Compare commits

..

29 Commits

Author SHA1 Message Date
63aa7aa104 Commit vor Änderung Umstellung auf eine einzelne Animation 2026-06-28 22:59:36 +02:00
6d061cd621 Sicherstellen des Zustands der passt 2026-06-28 21:31:00 +02:00
cd350a92fa Bank-Sitz: blockingAnimRemaining +1Frame, applyMotionKfOffset als Lerp
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 15:11:44 +02:00
b44d583dc3 Bank-Sitz-Fix: clearKfOffset-Timing, Approach-Distanz, Animationsübergänge
- clearKfOffset() erst nach get_up-Animation (Callback) statt sofort beim Start
  → kein Slide des Visuals während der Aufsteh-Animation
- Approach-Distanz zur Bank um 17.5cm verkürzt (läuft näher ran, sitzt tiefer)
- blockingAnimRemaining um 1 Frame (1/60s) gekürzt → verhindert Extra-Keyframe-Hold
  am Animationsende (noch zu beobachten)
- Diverses aus vorheriger Session: AnimSet-Editor, Navigation, Assets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:51:11 +02:00
ba0b80f524 Navigation, Bank-Setzen und AnimSet-Editor Keyframes
- CharacterNavigator: universelle Pfad-Navigation für Spieler und NPCs
  (PathFinder + Terrain-Slope-Check + Stuck-Erkennung, Walk/Run)
- PlayerInputControl: navigateTo/stopNavigation-API, Navigator hat Vorrang
  vor WASD; setNavigationSources für PathFinder + TerrainChunkState
- WorldInteractableState: Bank-Setzen komplett neu (< 5m, E-Taste),
  Navigator läuft zum Sitzpunkt, dreht Rücken zur Bank, spielt
  sit_down_bench / sitting / get_up_sitting; Bett weiterhin mit Rücklauf
- AnimSet-Editor: Kamera startet mit -45° Pitch; AnimKeyframe-Offset-Editor
- WorldScene: PathFinder + ObstacleRoot an PlayerInputControl übergeben

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 18:18:58 +02:00
79f9cf12a3 Animationen jetzt heil und Keyframes eingebaut 2026-06-23 17:44:22 +02:00
914bf6e673 Zwischenstand - animationen passen weitesgehend 2026-06-22 22:36:44 +02:00
b3b943e588 Charakter-Liegend-Fix: T-Pose im Editor, CullHint im Spiel, Strip in src+bin
Editor (AnimPreviewState):
- __tpose__ AnimClip mit Einzel-Frame-Track für Root-Joints (behebt NPE in
  ClipAction.doInterpolate; leerer Clip hatte null-tracks-Array)
- Beim Laden: eingebettete Clips automatisch strippt und in src+bin speichern;
  danach __tpose__ abspielen → Charakter steht in Bind-Pose
- stopAll(): zurück zur T-Pose statt SkinningControl deaktivieren
- Achsen-Indikator immer fest auf (0,0,0) statt bounding-box-Mitte

Spiel (WorldScene):
- CullHint.Always beim Laden, Inherit nach Animation-Setup (kein liegender Charakter)
- stripEmbeddedClips: speichert jetzt in src + bin + build (alle bekannten Pfade);
  besseres Logging bei fehlendem Pfad

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 22:25:23 +02:00
d3c6e8ed77 Zwischenstand: CullHint-Workaround gegen liegenden Charakter + stripEmbeddedClips-Fix
- WorldScene: Charakter wird beim Laden mit CullHint.Always versteckt, erst nach
  setupAnimationContext (idle läuft) wieder sichtbar
- WorldScene: stripEmbeddedClips nutzt jetzt AnimationLibrary.findAssetRoot() statt
  hartkodierter Pfad-Liste; besseres Logging wenn Datei nicht gefunden
- AnimPreviewState: Modell beim Laden versteckt (CullHint.Always), erst bei playClip
  sichtbar; stopAll versteckt Modell wieder
- PlayerInputControl: tryPlay setzt Action VOR SkinningControl-Aktivierung

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 22:03:48 +02:00
7a3b2b8733 snapRootBoneXZ: Achsen-Fix für Mixamo-Hips (X=0, Y=0/frei, Z=frei)
In diesen Mixamo-Exporten ist Local-Y die Vorwärts-Richtung (nicht Höhe),
Local-Z die Höhe. Bisheriger Code fror Z=0 ein → Charakter 1m zu tief.
Und Y war frei → Lauf-Drift blieb.

Neue Logik:
  X → 0 (kein Seiten-Drift)
  Y → 0 für Lauf-Clips (running/walking/sprinting/running_jump), normalisiert sonst
  Z → vollständig frei (Höhe und Setz/Aufsteh-Bewegung erhalten)

Nur der flachste Bone (Hips) wird modifiziert, alle anderen unberührt.
Clips vollständig neu importiert mit korrektem Snap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 18:28:51 +02:00
e669e29096 Zwischenstand 2026-06-21 23:04:40 +02:00
52c1fb1fe8 Revert Variante B – zurück zu snapRootBoneXZ mit frame0-XZ
RootDriftCompensatorControl entfernt, AnimationLibrary auf Stand 7dcf16f
zurückgesetzt (snapRootBoneXZ friert XZ auf frame0-Wert ein, Early-Return
bleibt, kein Clip-Clearing in applyAllTo).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:01:29 +02:00
197a820ef9 Option B: RootDriftCompensatorControl verhindert Mesh-Drift ohne Animation zu verändern
Neues Control auf dem Modell-Spatial (nach AnimComposer hinzugefügt):
- Läuft nach AnimComposer aber vor SkinningControl → liest aktuellen Frame
- Findet den flachsten Knochen mit nicht-nullem Model-Space XZ (= Hüft-Bone)
- Verschiebt den Modell-Spatial in die entgegengesetzte Richtung
- Mesh bleibt immer über der Physik-Kapsel; Y (sit_down/Jump) wird nicht berührt
- Animations-Daten, J3Os und AnimationLibrary bleiben unverändert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 11:35:12 +02:00
7dcf16fddf snapRootBoneXZ: flachsten Joint mit Translations snappen statt nur Tiefe-0
Rigs wo Hips auf Tiefe 1 liegt (Kind des Root) wurden bisher nicht gesnappt
weil nur Tiefe-0-Joints gefunden wurden. Jetzt wird die kleinste Tiefe unter
allen Joints mit Translation-Track gesucht und nur diese Ebene eingefroren.
Passt zu beiden Rig-Strukturen (Root-Translation und Hips-Translation).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 10:09:27 +02:00
71ba297657 Root-Bone XZ in-memory einfrieren beim Clip-Apply (kein Drift mehr ingame)
Beim Anwenden eines Clips auf ein Modell (AnimationLibrary.applyTo) wird der
Root-Joint-Track jetzt in-memory gepatcht: X und Z werden auf Frame-0-Wert
eingefroren, Y (Höhenachse) bleibt vollständig frei. Damit bleibt das Mesh
immer über der Physik-Kapsel, sit_down/Jump/Bounce laufen korrekt weiter.
J3O-Dateien werden nicht verändert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 10:01:37 +02:00
07a4c6a323 Animations neu importiert, SLF4J-Format-Bugs gefixt, stripRootXZ als tote Methode für später
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 09:58:15 +02:00
c8f1dd9432 Animations-Import, Massenimport-Queue, Asset-Archivierung, Voxel-Refactor
- Animations-Import: GLB wird direkt vom Ursprungspfad geladen (kein Zwischenkopieren), J3O in clips/ gespeichert
- RetargetingSystem: Translations-Tracks im Full-Retarget-Pfad erhalten (Hips-Y für sit_down)
- AnimationLibrary: lädt nur J3O, Clip-Name wird bei applyTo() auf Library-Key umbenannt
- SharedInput: animPreviewAddAnimPath → ConcurrentLinkedQueue animImportQueue (Massenimport-Fix)
- EditorApp: archiveOriginal() archiviert Originaldateien nach assets/imported/<assettyp>/
- EditorApp: Animations-Unterknoten im Asset-Baum zeigen enthaltene Clip-Namen
- Neue Animations-Clips: sit_down, get_up_sitting, sitting, pickup, sprinting u.a.
- Voxel: VoxelChunkState entfernt, VoxelChunkNode/MarchingCubes überarbeitet
- Map: Voxel-Chunks bereinigt, Terrain-Chunks aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 20:52:04 +02:00
a369647e9c Weiter gearbeitet 2026-06-16 07:03:08 +02:00
a80269e681 Commit vor Voxel Update für die Klippen 2026-06-11 21:52:00 +02:00
fe5dfc19b1 Commit vor dem Einfügen von Gebierge über Voxels 2026-06-10 12:56:29 +02:00
edb9cfc946 Weiter gebastelt 2026-06-09 19:25:30 +02:00
5e85051716 Weiter am Editor gearbeitet, unter anderem LOD System, Items, Trees, Modelle 2026-06-08 22:25:47 +02:00
1297869dfa Commit vor großem Terrain refactoring 2026-06-08 08:42:45 +02:00
7faed35287 Einmal den Fortschritt mit dem Wasser sichern 2026-06-06 09:05:25 +02:00
d56f2ea41f Weiter gearbeitet 2026-06-04 22:40:17 +02:00
875c39ab27 Alles was kloht nicht commiten wollte 2026-06-02 23:20:18 +02:00
ed1bc8f0a3 editor-assets vollständig nach blight-assets migriert
TreeGeneratorState, PalmGeneratorState und EzTreeState zeigten noch auf
editor-assets/ für Models, Texturen und Impostors. Alle Pfade auf
blight-assets/src/main/resources/ umgestellt:
- models/ → Models/
- textures/ → Textures/impostor/
- Palmen → trees/palm/ (konsistent mit EzTree)
- ASSET_ROOT/BLIGHT_ASSET_ROOT-Doppelung in EzTreeState bereinigt
- verbleibende System.out/err durch log.warn/error ersetzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 23:18:14 +02:00
3e7108954e .gitignore: logs/ und downloads/ ausschließen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 23:13:39 +02:00
50f496c864 Atmosphäre-Tools, EZ-Tree-Fixes, i18n, AnimSet, Baum-Export
- blight-lang: TextResolver + EN/DE Sprachpakete (TextReference i18n)
- AnimSet: Clips + ActionMap in .animset.json zusammengeführt
- EZ-Tree: Branch-Parameter-Fixes (length/radius/children/force nicht senden,
  twist Grad→Radiant, leaves.size ×5); Ordner-ComboBox mit Auto-Refresh
- Logging beim Baum-Export in allen drei Generatoren (EZ-Tree, Blight, Palme)
- Atmosphäre-Tools: Emitter, Licht, Wasser, Sound-/Musikbereiche, Spiel-Starten
- AnimPreviewState, RetargetingSystem, AnimationLibrary (Animations-Editor)
- Terrain-Transparenz-Fix, Schatten-Fix, ThirdPersonCamera-Fix
- DayNightState, WeatherState, CloudsNode, JmeConsole
- MapIO v6, neue blight-common Modell-Klassen (GameCharacter, NPC, Quests…)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 23:12:46 +02:00
1620 changed files with 50060 additions and 2183 deletions

12
.gitignore vendored
View File

@@ -10,3 +10,15 @@
.classpath
.settings/
bin/
# Laufzeit-Logs
**/logs/
# Lokale Downloads (nicht ins Repo)
downloads/
# Ingame-Karten-Sessions (vom Editor beim Spielstart erzeugt)
run/
# Spielstände
saves/

View File

@@ -0,0 +1,98 @@
ANIMATIONEN BLENDER RETARGETING WORKFLOW
==========================================
WARUM DIESER WORKFLOW?
----------------------
Das Mixamo-Skelett (stand.glb etc.) und das Tripo3D-Skelett (Charakter-Modell) haben
unterschiedliche Bone-Orientierungs-Konventionen. Das Runtime-Retargeting in Java kann
diesen Mismatch nicht zuverlässig beheben. Lösung: Animationen einmalig in Blender auf
das Tripo-Skelett "umrechnen" (baken) und als neue GLBs exportieren.
Diese Arbeit ist EINMALIG pro Animations-Clip. Danach funktioniert jedes weitere
Tripo3D-Modell mit denselben Bone-Namen automatisch über AnimationLibrary.
VORAUSSETZUNGEN
---------------
- Charakter-Modell als GLB-Datei (die Quelldatei, aus der die .j3o erzeugt wurde)
- Mixamo-Animations-GLB (z.B. stand.glb, twohand_idle.glb)
- Blender 3.x oder 4.x
WORKFLOW (für jede Animation einmal wiederholen)
------------------------------------------------
Schritt 1: Beide Armatures in Blender laden
File → Import → glTF 2.0 → Charakter-Modell .glb (= Custom-Armature)
File → Import → glTF 2.0 → stand.glb (= Mixamo-Armature + Animation)
Schritt 2: Custom-Armature vorbereiten
- Custom-Armature selektieren
- Pose Mode (Ctrl+Tab)
- Alle Bones selektieren (A)
Schritt 3: Copy-Rotation-Constraints setzen
Für jeden Bone in der Custom-Armature:
Bone-Properties → Constraints → Add Bone Constraint → Copy Rotation
Target: Mixamo-Armature
Bone: entsprechender Mixamo-Bone (siehe Mapping unten)
BONE-MAPPING (Custom → Mixamo):
┌─────────────────┬─────────────────────────┐
│ Custom │ Mixamo │
├─────────────────┼─────────────────────────┤
│ Pelvis │ mixamorig:Hips │
│ Waist │ mixamorig:Spine │
│ Spine01 │ mixamorig:Spine1 │
│ Spine02 │ mixamorig:Spine2 │
│ NeckTwist01 │ mixamorig:Neck │
│ L_Clavicle │ mixamorig:LeftShoulder │
│ L_Upperarm │ mixamorig:LeftArm │
│ L_Forearm │ mixamorig:LeftForeArm │
│ L_Hand │ mixamorig:LeftHand │
│ R_Clavicle │ mixamorig:RightShoulder │
│ R_Upperarm │ mixamorig:RightArm │
│ R_Forearm │ mixamorig:RightForeArm │
│ R_Hand │ mixamorig:RightHand │
│ L_Thigh │ mixamorig:LeftUpLeg │
│ L_Calf │ mixamorig:LeftLeg │
│ L_Foot │ mixamorig:LeftFoot │
│ R_Thigh │ mixamorig:RightUpLeg │
│ R_Calf │ mixamorig:RightLeg │
│ R_Foot │ mixamorig:RightFoot │
└─────────────────┴─────────────────────────┘
Schritt 4: Animation baken
Pose → Animation → Bake Action
✓ Only Selected Bones
✓ Visual Keying
✓ Clear Constraints
Bake Data: Pose
→ Erzeugt eine neue Action auf dem Custom-Skelett.
Schritt 5: Exportieren
- Mixamo-Armature aus der Szene löschen (X → Delete)
- Custom-Armature selektieren
- File → Export → glTF 2.0
Format: GLB
Include: Selected Objects only
Animation: ✓ (Export animations)
- Speichern als: blight-assets/src/main/resources/animations/stand.glb
(überschreibt das alte, fehlerhafte GLB)
→ Workflow für jede weitere Animation (twohand_idle.glb etc.) wiederholen.
WIE ES DANACH FUNKTIONIERT
---------------------------
Die neuen GLBs haben das Tripo-Skelett mit den korrekten vorgerechneten Animationen.
AnimationLibrary lädt sie beim Start automatisch und verteilt sie per applyAllTo()
auf alle Charaktere im Spiel.
Neue Tripo3D-Charaktere (gleiche Bone-Namen) bekommen alle Animationen automatisch —
kein weiteres Blender-Mapping nötig, weil das RetargetingSystem Bind-Pose-Unterschiede
(z.B. unterschiedliche Proportionen) selbst korrigiert.
Neuer Animations-Clip von Mixamo:
→ Einmal durch diesen Workflow → GLB in animations/ ablegen → fertig.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,3 +3,12 @@
// geteilten Assets (MatDefs, Shaders, Textures) auf dem Classpath zu erhalten.
//
// Editor-spezifische Assets (Tool-Icons etc.) verbleiben in blight-editor.
apply plugin: 'java'
sourceSets {
main {
java { srcDirs = [] }
resources { srcDirs = ['src/main/resources'] }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -1,13 +1,14 @@
MaterialDef TreeLeaf {
MaterialDef Fern {
MaterialParameters {
Color Diffuse (Color) : 0.18 0.60 0.10 1.0
Float WindStrength : 0.30
Float WindSpeed : 0.7
Texture2D LeafMap
Boolean HasLeafMap : false
Float WindStrength : 0.15
Float WindSpeed : 0.6
Texture2D DiffuseMap
Boolean HasDiffuseMap : false
Texture2D NormalMap -LINEAR
Boolean HasNormalMap : false
// Vom Shadow-Renderer befüllt (PostShadow-Pass) — vollständige Liste aus PostShadow.j3md
Int BoundDrawBuffer
Int FilterMode
Boolean HardwareShadows
@@ -15,8 +16,6 @@ MaterialDef TreeLeaf {
Texture2D ShadowMap1
Texture2D ShadowMap2
Texture2D ShadowMap3
Texture2D ShadowMap4
Texture2D ShadowMap5
Float ShadowIntensity : 1.0
Vector4 Splits
Vector2 FadeInfo
@@ -24,18 +23,15 @@ MaterialDef TreeLeaf {
Matrix4 LightViewProjectionMatrix1
Matrix4 LightViewProjectionMatrix2
Matrix4 LightViewProjectionMatrix3
Matrix4 LightViewProjectionMatrix4
Matrix4 LightViewProjectionMatrix5
Vector3 LightPos
Vector3 LightDir
Float PCFEdge
Float ShadowMapSize
Boolean BackfaceShadows : false
Boolean BackfaceShadows : false
}
Technique {
VertexShader GLSL150 : Shaders/Tree.vert
FragmentShader GLSL150 : Shaders/TreeLeaf.frag
VertexShader GLSL150 : Shaders/Fern.vert
FragmentShader GLSL150 : Shaders/Fern.frag
WorldParameters {
WorldViewProjectionMatrix
@@ -48,22 +44,6 @@ MaterialDef TreeLeaf {
}
}
Technique PostShadow {
VertexShader GLSL150 : Shaders/LeafPostShadow.vert
FragmentShader GLSL150 : Shaders/LeafPostShadow.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
ForcedRenderState {
Blend Modulate
FaceCull Off
DepthWrite Off
}
}
Technique PreShadow {
VertexShader GLSL150 : Shaders/LeafPreShadow.vert
FragmentShader GLSL150 : Shaders/LeafPreShadow.frag
@@ -80,4 +60,20 @@ MaterialDef TreeLeaf {
ColorWrite Off
}
}
Technique PostShadow {
VertexShader GLSL150 : Shaders/LeafPostShadow.vert
FragmentShader GLSL150 : Shaders/LeafPostShadow.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
ForcedRenderState {
Blend Modulate
FaceCull Off
DepthWrite Off
}
}
}

View File

@@ -0,0 +1,25 @@
MaterialDef Flowing Water {
MaterialParameters {
Texture2D NormalMap
Texture2D FoamMap
Texture2D DiffuseMap
Color Tint
Float UVScale : 6.0
Float NormalUVScale : 0.5
Float Time : 0.0
Float FlowSpeed : 1.0
Float FoamAmount : 0.0
}
Technique {
VertexShader GLSL150: Shaders/FlowingWater.vert
FragmentShader GLSL150: Shaders/FlowingWater.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
HAS_NORMALMAP : NormalMap
HAS_FOAMMAP : FoamMap
HAS_DIFFUSEMAP : DiffuseMap
}
}
}

View File

@@ -3,8 +3,11 @@ MaterialDef Grass {
MaterialParameters {
Color Color (Color) : 1.0 1.0 1.0 1.0
Texture2D ColorMap
Texture2D NormalMap
Float WindSpeed : 0.5
Float WindStrength : 0.12
Vector3 SunDir : 0.55 0.80 0.35
Color SunColor : 1.0 1.0 0.95 1.0
}
Technique {
@@ -15,6 +18,7 @@ MaterialDef Grass {
WorldViewProjectionMatrix
WorldMatrix
Time
AmbientLightColor
}
RenderState {
@@ -22,7 +26,8 @@ MaterialDef Grass {
}
Defines {
HAS_COLORMAP : ColorMap
HAS_COLORMAP : ColorMap
HAS_NORMALMAP : NormalMap
}
}
}

View File

@@ -0,0 +1,25 @@
MaterialDef GrassVertex {
MaterialParameters {
Float WindSpeed : 1.0
Float WindStrength : 0.15
Vector3 SunDir : 0.35 0.8 0.45
Color SunColor : 0.95 0.90 0.75 1.0
}
Technique {
VertexShader GLSL150: Shaders/GrassVertex.vert
FragmentShader GLSL150: Shaders/GrassVertex.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
Time
AmbientLightColor
}
RenderState {
FaceCull Off
}
}
}

View File

@@ -0,0 +1,40 @@
MaterialDef TerrainArray {
MaterialParameters {
TextureArray DiffuseArray
FloatArray DiffuseScales
TextureArray NormalArray
Float Shininess : 32.0
Vector3 LightDir
Vector3 SunColor
Vector3 AmbientColor
Boolean DebugNoLight
Boolean DebugSlot0Only
Texture2D DebugDirectTex
Texture2D AlphaMap
Texture2D AlphaMap_1
Texture2D AlphaMap_2
}
Technique {
VertexShader GLSL150 : Shaders/TerrainArray.vert
FragmentShader GLSL150 : Shaders/TerrainArray.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
ViewProjectionMatrix
}
Defines {
HAS_NORMAL_ARRAY : NormalArray
HAS_ALPHA_1 : AlphaMap_1
HAS_ALPHA_2 : AlphaMap_2
HAS_LIGHTDIR : LightDir
HAS_SCENE_LIGHT : SunColor
DEBUG_NO_LIGHT : DebugNoLight
DEBUG_SLOT0_ONLY : DebugSlot0Only
DEBUG_DIRECT_TEX : DebugDirectTex
}
}
}

View File

@@ -0,0 +1,19 @@
MaterialDef Topology {
MaterialParameters {
Float Interval : 10.0
Float LineWidth : 0.12
Float Opacity : 0.55
}
Technique {
VertexShader GLSL150: Shaders/Topology.vert
FragmentShader GLSL150: Shaders/Topology.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
RenderState {
Blend Alpha
DepthWrite Off
}
}
}

View File

@@ -6,6 +6,9 @@ MaterialDef Tree {
Float WindSpeed : 0.5
Texture2D BarkMap
Boolean HasBarkMap : false
Vector3 LightDir
Vector3 SunColor
Vector3 AmbientColor
}
Technique {
@@ -17,5 +20,9 @@ MaterialDef Tree {
WorldMatrix
Time
}
Defines {
HAS_SCENE_LIGHT : SunColor
}
}
}

View File

@@ -28,6 +28,8 @@ MaterialDef TreeLeaf {
Matrix4 LightViewProjectionMatrix5
Vector3 LightPos
Vector3 LightDir
Vector3 SunColor
Vector3 AmbientColor
Float PCFEdge
Float ShadowMapSize
Boolean BackfaceShadows : false
@@ -46,6 +48,10 @@ MaterialDef TreeLeaf {
RenderState {
FaceCull Off
}
Defines {
HAS_SCENE_LIGHT : SunColor
}
}
Technique PostShadow {

View File

@@ -0,0 +1,69 @@
MaterialDef Voxel {
MaterialParameters {
Texture2D TexFlat
Texture2D TexSteep
Texture2D NormalMapFlat
Texture2D NormalMapSteep
Texture2D DisplacementMapFlat
Texture2D DisplacementMapSteep
Float TexScale : 8.0
Float DisplacementScale : 0.3
Float TessellationLevel : 4.0
Vector3 LightDir
Vector3 SunColor
Vector3 AmbientColor
Boolean DebugNoLight
}
Technique {
VertexShader GLSL150 : Shaders/Voxel.vert
FragmentShader GLSL150 : Shaders/Voxel.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
HAS_NM_FLAT : NormalMapFlat
HAS_NM_STEEP : NormalMapSteep
HAS_LIGHTDIR : LightDir
HAS_SCENE_LIGHT : SunColor
DEBUG_NO_LIGHT : DebugNoLight
}
RenderState {
FaceCull Off
}
}
Technique Tessellation {
VertexShader GLSL400 : Shaders/VoxelTess.vert
TessellationControlShader GLSL400 : Shaders/Voxel.tsctrl
TessellationEvaluationShader GLSL400 : Shaders/Voxel.tseval
FragmentShader GLSL400 : Shaders/Voxel.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
ViewProjectionMatrix
CameraPosition
}
Defines {
HAS_NM_FLAT : NormalMapFlat
HAS_NM_STEEP : NormalMapSteep
HAS_DISP_FLAT : DisplacementMapFlat
HAS_DISP_STEEP : DisplacementMapSteep
HAS_LIGHTDIR : LightDir
HAS_SCENE_LIGHT : SunColor
}
RenderState {
FaceCull Off
}
}
}

View File

@@ -0,0 +1,80 @@
MaterialDef Advanced Water Polygon {
MaterialParameters {
Int BoundDrawBuffer
Int NumSamples
Int NumSamplesDepth
Texture2D FoamMap
Texture2D CausticsMap
Texture2D NormalMap -LINEAR
Texture2D ReflectionMap
Texture2D HeightMap -LINEAR
Texture2D Texture
Texture2D DepthTexture
Vector3 CameraPosition
Float Time
Vector3 frustumCorner
Matrix4 TextureProjMatrix
Float WaterHeight
Vector3 LightDir
Float WaterTransparency
Float NormalScale
Float R0
Float MaxAmplitude
Color LightColor
Float ShoreHardness
Float FoamHardness
Float RefractionStrength
Float WaveScale
Vector3 FoamExistence
Float SunScale
Vector3 ColorExtinction
Float Shininess
Color WaterColor
Color DeepWaterColor
Vector2 WindDirection
Float ReflectionDisplace
Float FoamIntensity
Float CausticsIntensity
Float UnderWaterFogDistance
Boolean UseRipples
Boolean UseHQShoreline
Boolean UseSpecular
Boolean UseFoam
Boolean UseCaustics
Boolean UseRefraction
Float Radius
Vector3 Center
Boolean SquareArea
Vector2Array Points
Int NumPoints : 0
}
Technique {
VertexShader GLSL310 GLSL300 GLSL150 GLSL120 : Common/MatDefs/Post/Post.vert
FragmentShader GLSL310 GLSL300 GLSL150 GLSL120 : Shaders/WaterPolygon.frag
WorldParameters {
ViewProjectionMatrixInverse
}
Defines {
BOUND_DRAW_BUFFER: BoundDrawBuffer
RESOLVE_MS : NumSamples
RESOLVE_DEPTH_MS : NumSamplesDepth
ENABLE_RIPPLES : UseRipples
ENABLE_HQ_SHORELINE : UseHQShoreline
ENABLE_SPECULAR : UseSpecular
ENABLE_FOAM : UseFoam
ENABLE_CAUSTICS : UseCaustics
ENABLE_REFRACTION : UseRefraction
ENABLE_AREA : Center
SQUARE_AREA : SquareArea
POLYGON_AREA : Points
}
}
}

View File

@@ -0,0 +1,23 @@
#Thu Jun 18 17:47:21 CEST 2026
attachedEmitters.count=0
attachedLight.0=0.00000|0.10000|0.00000|0.80000|1.00000|0.80000|12.00000|50.00000
attachedLights.count=1
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=Höhlenkristall1
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=1.0
scaleY=1.0
scaleZ=1.0
solid=true
tags=
uniformScale=true

View File

@@ -0,0 +1,27 @@
#Sat Jun 20 13:06:13 CEST 2026
attachedEmitters.count=0
attachedLights.count=0
castShadow=true
category=
cullDistance=120.0
interactableOffsetX=0.0
interactableOffsetY=0.6
interactableOffsetZ=0.0
interactableRotY=1.5707964
interactableType=BENCH
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=bank
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=1.0
scaleY=1.0
scaleZ=1.0
solid=true
tags=
uniformScale=true

View File

@@ -0,0 +1,27 @@
#Sat Jun 20 14:30:44 CEST 2026
attachedEmitters.count=0
attachedLights.count=0
castShadow=true
category=
cullDistance=120.0
interactableOffsetX=0.0
interactableOffsetY=0.5
interactableOffsetZ=0.0
interactableRotY=1.5707964
interactableType=BENCH
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=bank1
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=1.0
scaleY=1.0
scaleZ=1.0
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Mon Jun 08 22:18:33 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=kaktusfeige
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=2.5
scaleY=2.5
scaleZ=2.5
solid=true
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 10:41:26 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=blutagave
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.3
scaleY=0.3
scaleZ=0.3
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 09:59:57 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=erzmoss
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.33
scaleY=0.33
scaleZ=0.33
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 10:38:00 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=geisterfarn
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.3
scaleY=0.3
scaleZ=0.3
solid=true
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 10:42:24 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=quelllilie
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.3
scaleY=0.3
scaleZ=0.3
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 10:39:27 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=sonnenherz
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.3
scaleY=0.3
scaleZ=0.3
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,20 @@
#Tue Jun 09 10:40:05 CEST 2026
castShadow=true
category=
cullDistance=120.0
lod1Distance=30.0
lod1Path=
lod2Distance=80.0
lod2Path=
name=windschiff
pivotOffsetY=0.0
placementOffsetY=0.0
randomScaleMax=1.0
randomScaleMin=1.0
receiveShadow=true
scaleX=0.3
scaleY=0.3
scaleZ=0.3
solid=false
tags=
uniformScale=true

View File

@@ -0,0 +1,42 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform vec4 m_Diffuse;
uniform sampler2D m_DiffuseMap;
uniform bool m_HasDiffuseMap;
uniform sampler2D m_NormalMap;
uniform bool m_HasNormalMap;
in vec2 texCoord;
in vec3 vNormal;
in vec3 vTangent;
in float vHandedness;
void main() {
vec3 baseColor;
if (m_HasDiffuseMap) {
vec4 tex = texture2D(m_DiffuseMap, texCoord);
if (tex.a < 0.5) discard;
baseColor = tex.rgb * m_Diffuse.rgb;
} else {
vec2 uv = texCoord * 2.0 - 1.0;
if (dot(uv, uv) > 0.95) discard;
baseColor = m_Diffuse.rgb;
}
vec3 N = normalize(vNormal);
if (m_HasNormalMap) {
vec3 T = normalize(vTangent);
vec3 B = normalize(cross(N, T) * vHandedness);
mat3 TBN = mat3(T, B, N);
vec3 nm = texture2D(m_NormalMap, texCoord).rgb * 2.0 - 1.0;
N = normalize(TBN * nm);
}
vec3 sun = normalize(vec3(0.4, 1.0, 0.3));
float diff = max(0.0, dot(N, sun));
// two-sided: underside receives half brightness
diff = max(diff, max(0.0, dot(-N, sun)) * 0.5);
float lit = 0.45 + diff * 0.55;
gl_FragColor = vec4(baseColor * lit, 1.0);
}

View File

@@ -0,0 +1,34 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
uniform float g_Time;
uniform float m_WindStrength;
uniform float m_WindSpeed;
in vec3 inPosition;
in vec3 inNormal;
in vec2 inTexCoord;
in vec4 inColor; // R = wind weight (0=base, 1=tip)
in vec4 inTangent; // xyz=tangent, w=handedness
out vec2 texCoord;
out vec3 vNormal;
out vec3 vTangent;
out float vHandedness;
void main() {
float windW = inColor.r;
float t = g_Time * m_WindSpeed;
vec4 wp = g_WorldMatrix * vec4(inPosition, 1.0);
float phase = wp.x * 0.08 + wp.z * 0.06;
float swayX = sin(t + phase) * windW * m_WindStrength;
float swayZ = cos(t*0.73 + phase) * windW * m_WindStrength * 0.55;
vec3 anim = inPosition + vec3(swayX, 0.0, swayZ);
gl_Position = g_WorldViewProjectionMatrix * vec4(anim, 1.0);
texCoord = inTexCoord;
vNormal = normalize((g_WorldMatrix * vec4(inNormal, 0.0)).xyz);
vTangent = normalize((g_WorldMatrix * vec4(inTangent.xyz, 0.0)).xyz);
vHandedness = inTangent.w;
}

View File

@@ -0,0 +1,85 @@
#ifdef HAS_NORMALMAP
uniform sampler2D m_NormalMap;
#endif
#ifdef HAS_FOAMMAP
uniform sampler2D m_FoamMap;
#endif
#ifdef HAS_DIFFUSEMAP
uniform sampler2D m_DiffuseMap;
#endif
uniform vec4 m_Tint;
uniform float m_FoamAmount;
in vec2 vUV;
in vec2 vTex1;
in vec2 vTex2;
out vec4 outFragColor;
void main() {
// ── Randabstand: 0 = Mitte, 1 = Rand ─────────────────────────────────────
float edgeDist = abs(vUV.x * 2.0 - 1.0);
// ── Tiefengradient ────────────────────────────────────────────────────────
// Ränder heller/transparenter (flaches Ufer), Mitte dunkler (tief)
vec3 baseColor = m_Tint.rgb * (1.0 - edgeDist * 0.45);
float baseAlpha = m_Tint.a * smoothstep(0.0, 0.18, 1.0 - edgeDist);
// ── Dual-Layer Normal-Map: Specular + Wellenhelligkeit ────────────────────
float specular = 0.0;
float ripple = 0.0;
#ifdef HAS_NORMALMAP
vec3 raw1 = texture(m_NormalMap, vTex1).rgb * 2.0 - 1.0;
vec3 raw2 = texture(m_NormalMap, vTex2).rgb * 2.0 - 1.0;
// Tangent-Space → World-Space für horizontales Mesh (T=X, B=Z, N=Y):
// raw.xyz = (tangent-X, tangent-Y, depth) → world = (raw.x, raw.z, raw.y)
vec3 n1 = normalize(vec3(raw1.x, raw1.z, raw1.y));
vec3 n2 = normalize(vec3(raw2.x, raw2.z, raw2.y));
vec3 n = normalize(n1 + n2);
vec3 sunDir = normalize(vec3(0.5, 1.0, 0.3));
specular = pow(max(0.0, dot(n, sunDir)), 8.0);
// Ripple: Bereiche mit starker Oberflächenneigung (Wellenkämme) heller
ripple = (abs(n.x) + abs(n.z)) * 0.30;
#endif
// ── Schaum ────────────────────────────────────────────────────────────────
// Uferschaum wird stärker je näher am Rand; Wasserfälle extra
float foamEdge = smoothstep(0.55, 1.0, edgeDist);
float foamMask = clamp(foamEdge + m_FoamAmount * 0.75, 0.0, 1.0);
#ifdef HAS_FOAMMAP
float foamSample = texture(m_FoamMap, vTex1 * 0.4).r;
foamMask *= foamSample;
#else
// Prozeduraler Fallback wenn keine Schaumtextur geladen
float procFoam = (sin(vTex1.x * 6.283) * 0.5 + 0.5)
* (sin(vTex1.y * 3.141) * 0.5 + 0.5);
foamMask *= procFoam;
#endif
// ── Zusammenführen ────────────────────────────────────────────────────────
// specular-Anteil erhöht (0.55→0.75) damit Glanzlichter deutlich sichtbar sind
vec3 waterColor = baseColor + ripple + vec3(0.55, 0.65, 0.75) * specular;
vec3 finalColor = mix(waterColor, vec3(1.0), foamMask);
float finalAlpha = max(baseAlpha, foamMask * 0.9);
// ── Diffuse-Map: Fluss = Farbmodulation in Fließrichtung, Wasserfall = Gischt ──
#ifdef HAS_DIFFUSEMAP
float d1 = texture(m_DiffuseMap, vTex1 * 0.55).r;
float d2 = texture(m_DiffuseMap, vTex2 * 0.40).r;
float diffuseBlend = max(d1, d2 * 0.6);
// Fluss (FoamAmount=0): Textur moduliert die Wasserfarbe (dunkle/helle Strömungsmuster)
float riverMod = (diffuseBlend - 0.5) * 0.4 * (1.0 - m_FoamAmount);
finalColor = clamp(finalColor + riverMod, 0.0, 1.0);
// Wasserfall (FoamAmount>0): weiße Gischt-Überlagerung
float foamStr = diffuseBlend * m_FoamAmount * 0.85;
finalColor = mix(finalColor, vec3(1.0), foamStr);
finalAlpha = max(finalAlpha, foamStr * 0.9);
#endif
outFragColor = vec4(clamp(finalColor, 0.0, 1.0), finalAlpha);
}

View File

@@ -0,0 +1,23 @@
uniform mat4 g_WorldViewProjectionMatrix;
uniform float m_Time;
uniform float m_UVScale;
uniform float m_NormalUVScale;
uniform float m_FlowSpeed;
in vec3 inPosition;
in vec2 inTexCoord;
out vec2 vUV; // raw [0..1] UV für Tiefe / Rand-Logik
out vec2 vTex1; // scrollende Normal-Map-UV (flussabwärts)
out vec2 vTex2; // scrollende Normal-Map-UV (diagonal, gegenläufig)
void main() {
vUV = inTexCoord;
float ns = m_UVScale * m_NormalUVScale; // Normal-Map UV-Dichte (0.5 = 2x größeres Muster)
float t = m_Time * m_FlowSpeed * m_NormalUVScale; // Scroll-Geschwindigkeit skaliert mit
vTex1 = vec2(inTexCoord.x * ns,
inTexCoord.y * ns - t);
vTex2 = vec2(inTexCoord.x * ns * 0.7 - t * 0.25,
inTexCoord.y * ns * 0.7 + t * 0.55);
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

View File

@@ -1,10 +1,20 @@
uniform vec4 m_Color;
uniform vec3 m_SunDir;
uniform vec4 m_SunColor;
uniform vec4 g_AmbientLightColor;
#ifdef HAS_COLORMAP
uniform sampler2D m_ColorMap;
#endif
#ifdef HAS_NORMALMAP
uniform sampler2D m_NormalMap;
in vec3 wTangent;
in vec3 wBitangent;
#endif
in vec2 texCoord;
in vec3 wNormal;
out vec4 outFragColor;
void main() {
@@ -12,7 +22,20 @@ void main() {
#ifdef HAS_COLORMAP
color *= texture(m_ColorMap, texCoord);
#endif
// Alpha-Discard: transparente Pixel sofort verwerfen, Z-Buffer bleibt sauber
if (color.a < 0.5) discard;
vec3 n = normalize(wNormal);
#ifdef HAS_NORMALMAP
vec3 nm = texture(m_NormalMap, texCoord).rgb * 2.0 - 1.0;
mat3 tbn = mat3(normalize(wTangent), normalize(wBitangent), n);
n = normalize(tbn * nm);
#endif
// Beleuchtung: Sonne (two-sided via abs für Grashalme) + Ambient
float diffuse = abs(dot(n, normalize(m_SunDir)));
vec3 lit = g_AmbientLightColor.rgb + m_SunColor.rgb * diffuse;
color.rgb *= lit;
outFragColor = color;
}

View File

@@ -7,8 +7,16 @@ uniform float m_WindStrength;
in vec3 inPosition;
in vec2 inTexCoord;
in vec3 inNormal;
in vec4 inTangent;
out vec2 texCoord;
out vec3 wNormal;
#ifdef HAS_NORMALMAP
out vec3 wTangent;
out vec3 wBitangent;
#endif
void main() {
vec4 pos = vec4(inPosition, 1.0);
@@ -29,5 +37,14 @@ void main() {
}
texCoord = inTexCoord;
mat3 rot = mat3(g_WorldMatrix);
wNormal = normalize(rot * inNormal);
#ifdef HAS_NORMALMAP
wTangent = normalize(rot * inTangent.xyz);
wBitangent = cross(wNormal, wTangent) * inTangent.w;
#endif
gl_Position = g_WorldViewProjectionMatrix * pos;
}

View File

@@ -0,0 +1,34 @@
uniform vec4 g_AmbientLightColor;
uniform vec3 m_SunDir; // Richtung von der Fläche zur Sonne (world-space, normiert)
uniform vec4 m_SunColor; // Sonnenfarbe RGB
in vec4 varColor;
in vec3 varNormal;
out vec4 outFragColor;
void main() {
// Normale für Rück- und Vorderseite korrekt ausrichten
vec3 n = normalize(varNormal);
if (!gl_FrontFacing) n = -n;
vec3 L = normalize(m_SunDir);
// Diffuses Licht
float nDotL = max(dot(n, L), 0.0);
// Subsurface-Scatter-Approximation: etwas Licht scheint durch den Halm
float sss = max(dot(-n, L), 0.0) * 0.25;
float light = nDotL + sss;
vec3 ambient = g_AmbientLightColor.rgb * varColor.rgb;
vec3 diffuse = m_SunColor.rgb * varColor.rgb * light;
// Farbe nicht über die Vertex-Color-Helligkeit hinaus aufhellen
vec3 result = ambient + diffuse;
result = min(result, varColor.rgb * 1.5);
outFragColor = vec4(result, 1.0);
}

View File

@@ -0,0 +1,39 @@
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
uniform float g_Time;
uniform float m_WindSpeed;
uniform float m_WindStrength;
in vec3 inPosition;
in vec3 inNormal;
in vec4 inColor;
in vec2 inTexCoord; // .x = windFactor (0 = Wurzel, 1 = Spitze)
out vec4 varColor;
out vec3 varNormal;
void main() {
vec4 pos = vec4(inPosition, 1.0);
float wf = inTexCoord.x;
if (wf > 0.001) {
// Weltposition als Phasenbasis → jeder Halm schwingt anders
vec2 worldXZ = (g_WorldMatrix * pos).xz;
float t = g_Time * m_WindSpeed;
float sway = sin(t * 2.1 + worldXZ.x * 0.08 + worldXZ.y * 0.06) * 0.6
+ sin(t * 1.4 - worldXZ.x * 0.05 + worldXZ.y * 0.09) * 0.4;
// Quadratische Gewichtung: Spitze biegt sich mehr als Basis
float bend = sway * m_WindStrength * wf * wf;
pos.x += bend;
pos.z += bend * 0.3;
}
varColor = inColor;
// Normal in Weltkoordinaten (WorldMatrix ist für Gras typischerweise Identität)
varNormal = normalize(mat3(g_WorldMatrix) * inNormal);
gl_Position = g_WorldViewProjectionMatrix * pos;
}

View File

@@ -0,0 +1,133 @@
uniform sampler2DArray m_DiffuseArray;
#ifdef DEBUG_DIRECT_TEX
uniform sampler2D m_DebugDirectTex;
#endif
uniform float m_DiffuseScales[12];
uniform sampler2D m_AlphaMap;
#ifdef HAS_ALPHA_1
uniform sampler2D m_AlphaMap_1;
#endif
#ifdef HAS_ALPHA_2
uniform sampler2D m_AlphaMap_2;
#endif
#ifdef HAS_NORMAL_ARRAY
uniform sampler2DArray m_NormalArray;
#endif
#ifdef HAS_LIGHTDIR
uniform vec3 m_LightDir;
#endif
#ifdef HAS_SCENE_LIGHT
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
#endif
in vec2 vSplatUV;
in vec3 vWorldPos;
in vec3 vNormal;
in vec4 vTangent;
out vec4 outColor;
void main() {
vec4 a0 = texture(m_AlphaMap, vSplatUV);
vec4 a1 = vec4(0.0);
vec4 a2 = vec4(0.0);
#ifdef HAS_ALPHA_1
a1 = texture(m_AlphaMap_1, vSplatUV);
#endif
#ifdef HAS_ALPHA_2
a2 = texture(m_AlphaMap_2, vSplatUV);
#endif
// Slot-Mapping:
// Array[0] = Editor-Slot 1 (Basis, immer sichtbar — a0.r ist hardcoded 1.0)
// Array[1] = Editor-Slot 2, alpha = a0.g
// Array[2] = Editor-Slot 3, alpha = a0.b
// Array[3] = Editor-Slot 4, alpha = a0.a
// Array[4] = Editor-Slot 5, alpha = a1.r
// Array[5] = Editor-Slot 6, alpha = a1.g
// Array[6] = Editor-Slot 7, alpha = a1.b
// Array[7] = Editor-Slot 8, alpha = a1.a
// Array[8] = Editor-Slot 9, alpha = a2.r
// Array[9] = Editor-Slot 10, alpha = a2.g
// Array[10] = Editor-Slot 11, alpha = a2.b
// Array[11] = Editor-Slot 12, alpha = a2.a
//
// Blending: sequential mix — jeder Overlay ersetzt den Basis-Layer schrittweise.
// a0.r (Slot 1) wird vom Painting-Tool immer auf 1.0 gesetzt und dient nur als
// "Basis ist belegt"-Marker; er ist kein echter Alpha-Wert.
float overlays[11] = float[](a0.g, a0.b, a0.a,
a1.r, a1.g, a1.b, a1.a,
a2.r, a2.g, a2.b, a2.a);
#ifdef DEBUG_DIRECT_TEX
vec4 directCol = texture(m_DebugDirectTex, vWorldPos.xz / m_DiffuseScales[0]);
outColor = vec4(directCol.rgb, directCol.a);
return;
#endif
// Basis (Slot 1)
vec4 col = texture(m_DiffuseArray, vec3(vWorldPos.xz / m_DiffuseScales[0], 0.0));
#ifdef DEBUG_SLOT0_ONLY
// Debug: nur Slot 0 anzeigen, keine Alpha-Blending-Schleife
outColor = vec4(col.rgb, col.a);
return;
#endif
// Overlays (Slots 2-12) sequentiell darüber mischen
for (int i = 0; i < 11; i++) {
if (overlays[i] > 0.001) {
vec2 uv = vWorldPos.xz / m_DiffuseScales[i + 1];
col = mix(col, texture(m_DiffuseArray, vec3(uv, float(i + 1))), overlays[i]);
}
}
vec3 N = normalize(vNormal);
#ifdef HAS_NORMAL_ARRAY
vec3 T = normalize(vTangent.xyz);
vec3 B = cross(N, T) * vTangent.w;
mat3 TBN = mat3(T, B, N);
// Normal-Map Basis (Slot 1)
vec2 uv0 = vWorldPos.xz / m_DiffuseScales[0];
vec3 nm0 = texture(m_NormalArray, vec3(uv0, 0.0)).rgb * 2.0 - 1.0;
vec3 pertN = TBN * nm0;
// Normal-Maps Overlays (Slots 2-12) sequentiell mischen
for (int i = 0; i < 11; i++) {
if (overlays[i] > 0.001) {
vec2 uv = vWorldPos.xz / m_DiffuseScales[i + 1];
vec3 nm = texture(m_NormalArray, vec3(uv, float(i + 1))).rgb * 2.0 - 1.0;
pertN = mix(pertN, TBN * nm, overlays[i]);
}
}
N = normalize(pertN);
#endif
#ifdef HAS_LIGHTDIR
vec3 lightDir = normalize(m_LightDir);
#else
vec3 lightDir = normalize(vec3(0.6, 1.0, 0.4));
#endif
float diff = max(dot(N, lightDir), 0.0);
#ifdef HAS_SCENE_LIGHT
vec3 light = m_AmbientColor + m_SunColor * diff;
#else
vec3 light = vec3(diff * 0.65 + 0.35);
#endif
const float BRIGHTNESS = 0.80;
#ifdef DEBUG_NO_LIGHT
outColor = vec4(col.rgb * BRIGHTNESS, col.a);
#else
outColor = vec4(col.rgb * light * BRIGHTNESS, col.a);
#endif
}

View File

@@ -0,0 +1,27 @@
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
uniform mat4 g_ViewProjectionMatrix;
in vec3 inPosition;
in vec3 inNormal;
in vec2 inTexCoord;
in vec4 inTangent;
out vec2 vSplatUV;
out vec3 vWorldPos;
out vec3 vNormal;
out vec4 vTangent;
void main() {
vec4 worldPos4 = g_WorldMatrix * vec4(inPosition, 1.0);
mat3 worldMat3 = mat3(g_WorldMatrix);
vec3 worldPos = worldPos4.xyz;
vec3 worldNorm = normalize(worldMat3 * inNormal);
vSplatUV = inTexCoord;
vWorldPos = worldPos;
vNormal = worldNorm;
vTangent = vec4(normalize(worldMat3 * inTangent.xyz), inTangent.w);
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

View File

@@ -0,0 +1,38 @@
uniform float m_Interval;
uniform float m_LineWidth;
uniform float m_Opacity;
in float vWorldY;
out vec4 outColor;
// Maps height to a blue→green→yellow→red gradient
vec3 heightColor(float t) {
t = clamp(t, 0.0, 1.0);
vec3 c;
if (t < 0.25) {
c = mix(vec3(0.0, 0.0, 0.8), vec3(0.0, 0.7, 0.7), t * 4.0);
} else if (t < 0.5) {
c = mix(vec3(0.0, 0.7, 0.7), vec3(0.2, 0.8, 0.1), (t - 0.25) * 4.0);
} else if (t < 0.75) {
c = mix(vec3(0.2, 0.8, 0.1), vec3(0.9, 0.8, 0.0), (t - 0.5) * 4.0);
} else {
c = mix(vec3(0.9, 0.8, 0.0), vec3(0.8, 0.1, 0.0), (t - 0.75) * 4.0);
}
return c;
}
void main() {
float minH = -20.0;
float maxH = 300.0;
float t = (vWorldY - minH) / (maxH - minH);
vec3 band = heightColor(t);
// Contour lines: sharp pulse near each interval boundary
float phase = fract(vWorldY / m_Interval);
float fw = fwidth(vWorldY / m_Interval);
float line = 1.0 - smoothstep(m_LineWidth - fw, m_LineWidth + fw, min(phase, 1.0 - phase));
vec3 color = mix(band, vec3(0.0), line * 0.75);
outColor = vec4(color, m_Opacity);
}

View File

@@ -1,13 +1,12 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
in vec3 inPosition;
in vec2 inTexCoord;
out vec2 texCoord;
out float vWorldY;
void main() {
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
vWorldY = worldPos.y;
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
texCoord = inTexCoord;
}

View File

@@ -4,26 +4,35 @@ uniform vec4 m_Diffuse;
uniform sampler2D m_BarkMap;
uniform bool m_HasBarkMap;
#ifdef HAS_SCENE_LIGHT
uniform vec3 m_LightDir;
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
#endif
in vec2 texCoord;
in vec3 worldNormal;
void main() {
vec3 n = normalize(worldNormal);
// Sun at ~45° elevation from SE — good contrast on vertical cylinders
vec3 sunDir = normalize(vec3(0.6, 0.7, 0.4));
vec3 fillDir = normalize(vec3(-0.5, 0.3, -0.4));
vec3 rimDir = normalize(vec3(-0.3, 0.5, 0.7));
float sun = max(dot(n, sunDir), 0.0);
float fill = max(dot(n, fillDir), 0.0) * 0.22;
float rim = max(dot(n, rimDir), 0.0) * 0.14;
float sky = dot(n, vec3(0.0, 1.0, 0.0)) * 0.4 + 0.4; // [0.0, 0.8]
float light = sun * 0.75 + fill + rim + sky * 0.09 + 0.05;
vec3 baseColor = m_HasBarkMap
? texture2D(m_BarkMap, texCoord).rgb * m_Diffuse.rgb
: m_Diffuse.rgb;
gl_FragColor = vec4(baseColor * clamp(light, 0.0, 1.0), m_Diffuse.a);
#ifdef HAS_SCENE_LIGHT
float diff = max(dot(n, normalize(m_LightDir)), 0.0);
vec3 light = clamp(m_AmbientColor + m_SunColor * diff, 0.0, 1.0);
#else
vec3 sunDir = normalize(vec3(0.6, 0.7, 0.4));
vec3 fillDir = normalize(vec3(-0.5, 0.3, -0.4));
vec3 rimDir = normalize(vec3(-0.3, 0.5, 0.7));
float sun = max(dot(n, sunDir), 0.0);
float fill = max(dot(n, fillDir), 0.0) * 0.22;
float rim = max(dot(n, rimDir), 0.0) * 0.14;
float sky = dot(n, vec3(0.0, 1.0, 0.0)) * 0.4 + 0.4;
vec3 light = vec3(clamp(sun * 0.75 + fill + rim + sky * 0.09 + 0.05, 0.0, 1.0));
#endif
gl_FragColor = vec4(baseColor * light, m_Diffuse.a);
}

View File

@@ -4,6 +4,12 @@ uniform vec4 m_Diffuse;
uniform sampler2D m_LeafMap;
uniform bool m_HasLeafMap;
#ifdef HAS_SCENE_LIGHT
uniform vec3 m_LightDir;
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
#endif
in vec2 texCoord;
in vec3 worldNormal;
@@ -15,13 +21,18 @@ void main() {
if (tex.a < 0.5) discard;
baseColor = tex.rgb * m_Diffuse.rgb;
} else {
// Fallback: kreisförmiger Clip
vec2 uv = texCoord * 2.0 - 1.0;
if (dot(uv, uv) > 0.95) discard;
float edge = 1.0 - dot(uv, uv);
baseColor = m_Diffuse.rgb * (0.7 + 0.3 * edge);
}
// Leaves transmit light — no directional shading, uniform brightness
gl_FragColor = vec4(baseColor, 1.0);
#ifdef HAS_SCENE_LIGHT
// Blätter transmittieren Licht — abs() für doppelseitige Beleuchtung
float diff = abs(dot(normalize(worldNormal), normalize(m_LightDir)));
vec3 light = clamp(m_AmbientColor + m_SunColor * diff * 0.6, 0.0, 1.0);
gl_FragColor = vec4(baseColor * light, 1.0);
#else
gl_FragColor = vec4(baseColor * 0.8, 1.0);
#endif
}

View File

@@ -0,0 +1,112 @@
uniform sampler2D m_TexFlat;
uniform sampler2D m_TexSteep;
uniform float m_TexScale;
#ifdef HAS_NM_FLAT
uniform sampler2D m_NormalMapFlat;
#endif
#ifdef HAS_NM_STEEP
uniform sampler2D m_NormalMapSteep;
#endif
in vec3 vWorldPos;
in vec3 vNormal;
out vec4 outColor;
#ifdef HAS_LIGHTDIR
uniform vec3 m_LightDir;
#endif
#ifdef HAS_SCENE_LIGHT
uniform vec3 m_SunColor;
uniform vec3 m_AmbientColor;
#endif
vec4 triplanar(sampler2D tex, vec2 uvX, vec2 uvY, vec2 uvZ, vec3 bw) {
return texture(tex, uvX) * bw.x
+ texture(tex, uvY) * bw.y
+ texture(tex, uvZ) * bw.z;
}
// Triplanare Normal-Map-Mischung (Whiteout-Blending, world-space Ausgabe).
// N = geometrische Weltoberflächen-Normale, bw = triplanare Gewichte.
vec3 triplanarNormal(sampler2D nmap, vec2 uvX, vec2 uvY, vec2 uvZ, vec3 bw, vec3 N) {
vec3 tnX = texture(nmap, uvX).rgb * 2.0 - 1.0;
vec3 tnY = texture(nmap, uvY).rgb * 2.0 - 1.0;
vec3 tnZ = texture(nmap, uvZ).rgb * 2.0 - 1.0;
// Reorientiertes Normal-Mapping (RNM / Whiteout): Tangent → World
tnX = vec3(tnX.xy + N.zy, abs(tnX.z) * N.x);
tnY = vec3(tnY.xy + N.xz, abs(tnY.z) * N.y);
tnZ = vec3(tnZ.xy + N.xy, abs(tnZ.z) * N.z);
return normalize(tnX.zyx * bw.x + tnY.xzy * bw.y + tnZ.xyz * bw.z);
}
void main() {
vec3 bw = pow(abs(vNormal), vec3(4.0));
bw /= (bw.x + bw.y + bw.z + 0.001);
vec2 uvX = vWorldPos.yz / m_TexScale;
vec2 uvY = vWorldPos.xz / m_TexScale;
vec2 uvZ = vWorldPos.xy / m_TexScale;
// Flach ab ~11° Gefälle (20% grade, normal.y≈0.98); alles andere Fels.
float flatBlend = smoothstep(0.94, 0.99, vNormal.y);
float steepBlend = 1.0 - flatBlend;
// Flat: reines XZ-UV wie das Terrain (uvY = worldPos.xz / texScale), kein Triplanar.
// Steep: Triplanar für alle nicht-flachen Flächen inkl. Decken und Tunnelwände.
vec4 col = texture(m_TexFlat, uvY) * flatBlend
+ triplanar(m_TexSteep, uvX, uvY, uvZ, bw) * steepBlend;
// Geometrie-Normale für Beleuchtung, ggf. durch Normal-Map ersetzt.
vec3 N = normalize(vNormal);
#if defined(HAS_NM_FLAT) || defined(HAS_NM_STEEP)
vec3 pertN = vec3(0.0);
float totalBlend = 0.0;
#ifdef HAS_NM_FLAT
if (flatBlend > 0.001) {
vec3 nmFlat = texture(m_NormalMapFlat, uvY).rgb * 2.0 - 1.0;
nmFlat = vec3(nmFlat.xy + N.xz, abs(nmFlat.z) * N.y);
pertN += normalize(nmFlat.xzy) * flatBlend;
totalBlend += flatBlend;
}
#endif
#ifdef HAS_NM_STEEP
if (steepBlend > 0.001) {
pertN += triplanarNormal(m_NormalMapSteep, uvX, uvY, uvZ, bw, N) * steepBlend;
totalBlend += steepBlend;
}
#endif
if (totalBlend > 0.001) {
N = normalize(pertN / totalBlend);
}
#endif
// Flache Voxel-Flächen Richtung (0,1,0) korrigieren, um Abweichungen der
// Marching-Cubes-Normalen auszugleichen und das Terrain-Lighting zu matchen.
N = normalize(mix(N, vec3(0.0, 1.0, 0.0), flatBlend));
#ifdef HAS_LIGHTDIR
vec3 lightDir = normalize(m_LightDir);
#else
vec3 lightDir = normalize(vec3(0.6, 1.0, 0.4));
#endif
float diff = max(dot(N, lightDir), 0.0);
#ifdef HAS_SCENE_LIGHT
vec3 light = m_AmbientColor + m_SunColor * diff;
#else
vec3 light = vec3(diff * 0.65 + 0.35);
#endif
const float BRIGHTNESS = 0.80;
#ifdef DEBUG_NO_LIGHT
outColor = vec4(col.rgb * BRIGHTNESS, col.a);
#else
outColor = vec4(col.rgb * light * BRIGHTNESS, col.a);
#endif
if (outColor.a < 0.1) discard;
}

View File

@@ -0,0 +1,29 @@
layout(vertices = 3) out;
in vec3 vWorldPos[];
in vec3 vNormal[];
out vec3 tcWorldPos[];
out vec3 tcNormal[];
uniform float m_TessellationLevel;
uniform vec3 g_CameraPosition;
void main() {
tcWorldPos[gl_InvocationID] = vWorldPos[gl_InvocationID];
tcNormal[gl_InvocationID] = vNormal[gl_InvocationID];
if (gl_InvocationID == 0) {
vec3 center = (vWorldPos[0] + vWorldPos[1] + vWorldPos[2]) / 3.0;
float dist = distance(g_CameraPosition, center);
// Maximum subdivision close-up, falls to 1 at 64 m
float level = mix(m_TessellationLevel, 1.0, clamp(dist / 64.0, 0.0, 1.0));
level = max(1.0, level);
gl_TessLevelOuter[0] = level;
gl_TessLevelOuter[1] = level;
gl_TessLevelOuter[2] = level;
gl_TessLevelInner[0] = level;
}
}

View File

@@ -0,0 +1,82 @@
layout(triangles, equal_spacing, ccw) in;
in vec3 tcWorldPos[];
in vec3 tcNormal[];
out vec3 vWorldPos;
out vec3 vNormal;
uniform mat4 g_ViewProjectionMatrix;
uniform float m_TexScale;
uniform float m_DisplacementScale;
#ifdef HAS_DISP_FLAT
uniform sampler2D m_DisplacementMapFlat;
#endif
#ifdef HAS_DISP_STEEP
uniform sampler2D m_DisplacementMapSteep;
#endif
#ifdef HAS_DISP_CEIL
uniform sampler2D m_DisplacementMapCeil;
#endif
void main() {
vec3 bc = gl_TessCoord;
// Interpolate position and normal from patch vertices
vec3 pos = bc.x * tcWorldPos[0] + bc.y * tcWorldPos[1] + bc.z * tcWorldPos[2];
vec3 nor = normalize(bc.x * tcNormal[0] + bc.y * tcNormal[1] + bc.z * tcNormal[2]);
// Triplanar blend weights (same as fragment shader)
vec3 bw = pow(abs(nor), vec3(4.0));
bw /= (bw.x + bw.y + bw.z + 0.001);
vec2 uvX = pos.yz / m_TexScale;
vec2 uvY = pos.xz / m_TexScale;
vec2 uvZ = pos.xy / m_TexScale;
float flatBlend = smoothstep(0.94, 0.99, nor.y);
float ceilBlend = 1.0 - smoothstep(-0.6, -0.3, nor.y);
float steepBlend = max(0.0, 1.0 - flatBlend - ceilBlend);
#if defined(HAS_DISP_FLAT) || defined(HAS_DISP_STEEP) || defined(HAS_DISP_CEIL)
float disp = 0.0;
float totalW = 0.0;
#ifdef HAS_DISP_FLAT
if (flatBlend > 0.001) {
float d = texture(m_DisplacementMapFlat, uvX).r * bw.x
+ texture(m_DisplacementMapFlat, uvY).r * bw.y
+ texture(m_DisplacementMapFlat, uvZ).r * bw.z;
disp += d * flatBlend;
totalW += flatBlend;
}
#endif
#ifdef HAS_DISP_STEEP
if (steepBlend > 0.001) {
float d = texture(m_DisplacementMapSteep, uvX).r * bw.x
+ texture(m_DisplacementMapSteep, uvY).r * bw.y
+ texture(m_DisplacementMapSteep, uvZ).r * bw.z;
disp += d * steepBlend;
totalW += steepBlend;
}
#endif
#ifdef HAS_DISP_CEIL
if (ceilBlend > 0.001) {
float d = texture(m_DisplacementMapCeil, uvX).r * bw.x
+ texture(m_DisplacementMapCeil, uvY).r * bw.y
+ texture(m_DisplacementMapCeil, uvZ).r * bw.z;
disp += d * ceilBlend;
totalW += ceilBlend;
}
#endif
if (totalW > 0.001) disp /= totalW;
// 0.5 = neutral (no displacement), 0 = sink, 1 = raise
pos += nor * ((disp - 0.5) * m_DisplacementScale);
#endif
vWorldPos = pos;
vNormal = nor;
gl_Position = g_ViewProjectionMatrix * vec4(pos, 1.0);
}

View File

@@ -0,0 +1,15 @@
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
in vec3 inPosition;
in vec3 inNormal;
out vec3 vWorldPos;
out vec3 vNormal;
void main() {
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
vWorldPos = worldPos.xyz;
vNormal = normalize(mat3(g_WorldMatrix) * inNormal);
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

View File

@@ -0,0 +1,15 @@
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldMatrix;
in vec3 inPosition;
in vec3 inNormal;
out vec3 vWorldPos;
out vec3 vNormal;
void main() {
vec4 wpos = g_WorldMatrix * vec4(inPosition, 1.0);
vWorldPos = wpos.xyz;
vNormal = normalize(mat3(g_WorldMatrix) * inNormal);
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

View File

@@ -0,0 +1,483 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/MultiSample.glsllib"
#import "Common/ShaderLib/WaterUtil.glsllib"
// Water pixel shader
// Copyright (C) JMonkeyEngine 3.0
// by Remy Bouquet (nehon) for JMonkeyEngine 3.0
// original HLSL version by Wojciech Toman 2009
uniform COLORTEXTURE m_Texture;
uniform DEPTHTEXTURE m_DepthTexture;
uniform sampler2D m_HeightMap;
uniform sampler2D m_NormalMap;
uniform sampler2D m_FoamMap;
uniform sampler2D m_CausticsMap;
uniform sampler2D m_ReflectionMap;
uniform mat4 g_ViewProjectionMatrixInverse;
uniform mat4 m_TextureProjMatrix;
uniform vec3 m_CameraPosition;
uniform float m_WaterHeight;
uniform float m_Time;
uniform float m_WaterTransparency;
uniform float m_NormalScale;
uniform float m_R0;
uniform float m_MaxAmplitude;
uniform vec3 m_LightDir;
uniform vec4 m_LightColor;
uniform float m_ShoreHardness;
uniform float m_FoamHardness;
uniform float m_RefractionStrength;
uniform vec3 m_FoamExistence;
uniform vec3 m_ColorExtinction;
uniform float m_Shininess;
uniform vec4 m_WaterColor;
uniform vec4 m_DeepWaterColor;
uniform vec2 m_WindDirection;
uniform float m_SunScale;
uniform float m_WaveScale;
uniform float m_UnderWaterFogDistance;
uniform float m_CausticsIntensity;
#ifdef ENABLE_AREA
uniform vec3 m_Center;
uniform float m_Radius;
#endif
#ifdef POLYGON_AREA
uniform vec2 m_Points[64];
uniform int m_NumPoints;
bool polygonContains(float px, float pz) {
bool inside = false;
int j = m_NumPoints - 1;
for (int i = 0; i < m_NumPoints; i++) {
float xi = m_Points[i].x, zi = m_Points[i].y;
float xj = m_Points[j].x, zj = m_Points[j].y;
if ((zi > pz) != (zj > pz) &&
px < (xj - xi) * (pz - zi) / (zj - zi) + xi)
inside = !inside;
j = i;
}
return inside;
}
#endif
vec2 scale; // = vec2(m_WaveScale, m_WaveScale);
float refractionScale; // = m_WaveScale;
// Modifies 4 sampled normals. Increase first values to have more
// smaller "waves" or last to have more bigger "waves"
const vec4 normalModifier = vec4(3.0, 2.0, 4.0, 10.0);
// Strength of displacement along normal.
uniform float m_ReflectionDisplace;
// Water transparency along eye vector.
const float visibility = 3.0;
// foam intensity
uniform float m_FoamIntensity ;
vec2 m_FrustumNearFar; //=vec2(1.0,m_UnderWaterFogDistance);
const float LOG2 = 1.442695;
varying vec2 texCoord;
void setGlobals(){
scale = vec2(m_WaveScale, m_WaveScale);
refractionScale = m_WaveScale;
m_FrustumNearFar=vec2(1.0,m_UnderWaterFogDistance);
}
mat3 MatrixInverse(in mat3 inMatrix){
float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
mat3 T = transpose(inMatrix);
return mat3(cross(T[1], T[2]),
cross(T[2], T[0]),
cross(T[0], T[1])) / det;
}
mat3 computeTangentFrame(in vec3 N, in vec3 P, in vec2 UV) {
vec3 dp1 = dFdx(P);
vec3 dp2 = dFdy(P);
vec2 duv1 = dFdx(UV);
vec2 duv2 = dFdy(UV);
// solve the linear system
vec3 dp1xdp2 = cross(dp1, dp2);
mat2x3 inverseM = mat2x3(cross(dp2, dp1xdp2), cross(dp1xdp2, dp1));
vec3 T = inverseM * vec2(duv1.x, duv2.x);
vec3 B = inverseM * vec2(duv1.y, duv2.y);
// construct tangent frame
float maxLength = max(length(T), length(B));
T = T / maxLength;
B = B / maxLength;
return mat3(T, B, N);
}
float saturate(in float val){
return clamp(val,0.0,1.0);
}
vec3 saturate(in vec3 val){
return clamp(val,vec3(0.0),vec3(1.0));
}
vec3 getPosition(in float depth, in vec2 uv){
vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
pos = g_ViewProjectionMatrixInverse * pos;
return pos.xyz / pos.w;
}
// Function calculating fresnel term.
// - normal - normalized normal vector
// - eyeVec - normalized eye vector
float fresnelTerm(in vec3 normal,in vec3 eyeVec){
float angle = 1.0 - max(0.0, dot(normal, eyeVec));
float fresnel = angle * angle;
fresnel = fresnel * fresnel;
fresnel = fresnel * angle;
return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength);
}
vec4 underWater(int sampleNum){
float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r;
vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb;
vec3 position = getPosition(sceneDepth, texCoord);
float level = m_WaterHeight;
vec3 eyeVec = position - m_CameraPosition;
// Find intersection with water surface
vec3 eyeVecNorm = normalize(eyeVec);
float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
vec2 texC = vec2(0.0);
float cameraDepth = length(m_CameraPosition - surfacePoint);
texC = (surfacePoint.xz + eyeVecNorm.xz) * scale + m_Time * 0.03 * m_WindDirection;
float bias = texture2D(m_HeightMap, texC).r;
level += bias * m_MaxAmplitude;
t = (level - m_CameraPosition.y) / eyeVecNorm.y;
surfacePoint = m_CameraPosition + eyeVecNorm * t;
eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
#if __VERSION__ >= 130
// Find normal of water surface
float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1.0, 0.0)).r;
float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1.0, 0.0)).r;
float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0.0, -1.0)).r;
float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0.0, 1.0)).r;
#else
// Find normal of water surface
float normal1 = texture2D(m_HeightMap, (texC + vec2(-1.0, 0.0) / 256.0)).r;
float normal2 = texture2D(m_HeightMap, (texC + vec2(1.0, 0.0) / 256.0)).r;
float normal3 = texture2D(m_HeightMap, (texC + vec2(0.0, -1.0) / 256.0)).r;
float normal4 = texture2D(m_HeightMap, (texC + vec2(0.0, 1.0) / 256.0)).r;
#endif
vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
vec3 normal = myNormal*-1.0;
float fresnel = fresnelTerm(normal, eyeVecNorm);
vec3 refraction = color2;
#ifdef ENABLE_REFRACTION
texC = texCoord.xy *sin (fresnel+1.0);
texC = clamp(texC,0.0,1.0);
refraction = fetchTextureSample(m_Texture, texC, sampleNum).rgb;
#endif
float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
refraction = mix(mix(refraction, m_DeepWaterColor.rgb * waterCol, m_WaterTransparency), m_WaterColor.rgb* waterCol,m_WaterTransparency);
vec3 foam = vec3(0.0);
#ifdef ENABLE_FOAM
texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 *
saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
}
foam *= m_LightColor.rgb;
#endif
vec3 specular = vec3(0.0);
vec3 color ;
float fogFactor;
if(position.y>level){
#ifdef ENABLE_SPECULAR
if(step(0.9999,sceneDepth)==1.0){
vec3 lightDir=normalize(m_LightDir);
vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
specular += specular * 25.0 * saturate(m_Shininess - 0.05);
specular=specular * m_LightColor.rgb * 100.0;
}
#endif
float fogIntensity= 8.0 * m_WaterTransparency;
fogFactor = exp2( -fogIntensity * fogIntensity * cameraDepth * 0.03 * LOG2 );
fogFactor = clamp(fogFactor, 0.0, 1.0);
color =mix(m_DeepWaterColor.rgb,refraction,fogFactor);
specular=specular*fogFactor;
color = saturate(color + max(specular, foam ));
}else{
vec3 caustics = vec3(0.0);
#ifdef ENABLE_CAUSTICS
vec2 windDirection=m_WindDirection;
texC = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.x) * 0.01;
vec2 texCoord2 = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.z) * 0.01;
caustics += (texture2D(m_CausticsMap, texC)+ texture2D(m_CausticsMap, texCoord2)).rgb;
caustics=saturate(mix(m_WaterColor.rgb,caustics,m_CausticsIntensity));
color=mix(color2,caustics,m_CausticsIntensity);
#else
color=color2;
#endif
float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x));
float fogIntensity= 18.0 * m_WaterTransparency;
fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 );
fogFactor = clamp(fogFactor, 0.0, 1.0);
color =mix(m_DeepWaterColor.rgb,color,fogFactor);
}
return vec4(color, 1.0);
}
// NOTE: This will be called even for single-sampling
vec4 main_multiSample(int sampleNum){
// If we are underwater let's call the underwater function
if(m_WaterHeight >= m_CameraPosition.y){
#ifdef ENABLE_AREA
if(isOverExtent(m_CameraPosition, m_Center, m_Radius)){
return fetchTextureSample(m_Texture, texCoord, sampleNum);
}
#endif
#ifdef POLYGON_AREA
if(!polygonContains(m_CameraPosition.x, m_CameraPosition.z)){
return fetchTextureSample(m_Texture, texCoord, sampleNum);
}
#endif
return underWater(sampleNum);
}
float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r;
vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb;
vec3 color = color2;
vec3 position = getPosition(sceneDepth, texCoord);
#ifdef ENABLE_AREA
if(isOverExtent(position, m_Center, m_Radius)){
return vec4(color2, 1.0);
}
#endif
#ifdef POLYGON_AREA
if(!polygonContains(position.x, position.z)){
return vec4(color2, 1.0);
}
#endif
float level = m_WaterHeight;
float isAtFarPlane = step(0.99998, sceneDepth);
//#ifndef ENABLE_RIPPLES
// This optimization won't work on NVIDIA cards if ripples are enabled
if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){
return vec4(color2, 1.0);
}
//#endif
vec3 eyeVec = position - m_CameraPosition;
float cameraDepth = m_CameraPosition.y - position.y;
// Find intersection with water surface
vec3 eyeVecNorm = normalize(eyeVec);
float t = (level - m_CameraPosition.y) / eyeVecNorm.y;
vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t;
vec2 texC = vec2(0.0);
int samples = 1;
#ifdef ENABLE_HQ_SHORELINE
samples = 10;
#endif
float biasFactor = 1.0 / float(samples);
for (int i = 0; i < samples; i++){
texC = (surfacePoint.xz + eyeVecNorm.xz * biasFactor) * scale + m_Time * 0.03 * m_WindDirection;
float bias = texture2D(m_HeightMap, texC).r;
bias *= biasFactor;
level += bias * m_MaxAmplitude;
t = (level - m_CameraPosition.y) / eyeVecNorm.y;
surfacePoint = m_CameraPosition + eyeVecNorm * t;
}
float depth = length(position - surfacePoint);
float depth2 = surfacePoint.y - position.y;
// XXX: HACK ALERT: Increase water depth to infinity if at far plane
// Prevents "foam on horizon" issue
// For best results, replace the "100.0" below with the
// highest value in the m_ColorExtinction vec3
depth += isAtFarPlane * 100.0;
depth2 += isAtFarPlane * 100.0;
eyeVecNorm = normalize(m_CameraPosition - surfacePoint);
#if __VERSION__ >= 130
// Find normal of water surface
float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1.0, 0.0)).r;
float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1.0, 0.0)).r;
float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0.0, -1.0)).r;
float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0.0, 1.0)).r;
#else
// Find normal of water surface
float normal1 = texture2D(m_HeightMap, (texC + vec2(-1.0, 0.0) / 256.0)).r;
float normal2 = texture2D(m_HeightMap, (texC + vec2(1.0, 0.0) / 256.0)).r;
float normal3 = texture2D(m_HeightMap, (texC + vec2(0.0, -1.0) / 256.0)).r;
float normal4 = texture2D(m_HeightMap, (texC + vec2(0.0, 1.0) / 256.0)).r;
#endif
vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude));
vec3 normal = vec3(0.0);
#ifdef ENABLE_RIPPLES
texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6;
mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
vec3 normal0a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
texC = surfacePoint.xz * 0.4 + m_WindDirection * m_Time* 0.8;
tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
vec3 normal1a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
texC = surfacePoint.xz * 0.2 + m_WindDirection * m_Time * 0.4;
tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
vec3 normal2a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
texC = surfacePoint.xz * 0.1 + m_WindDirection * m_Time * 0.2;
tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC);
vec3 normal3a = normalize(tangentFrame*(2.0 * texture2D(m_NormalMap, texC).xyz - 1.0));
normal = normalize(normal0a * normalModifier.x + normal1a * normalModifier.y +normal2a * normalModifier.z + normal3a * normalModifier.w);
#if __VERSION__ >= 130 && !defined GL_ES
// XXX: Here's another way to fix the terrain edge issue,
// But it requires GLSL 1.3 and still looks kinda incorrect
// around edges
normal = isnan(normal.x) ? myNormal : normal;
#else
// To make the shader 1.2 compatible we use a trick :
// we clamp the x value of the normal and compare it to it's former value instead of using isnan.
normal = clamp(normal.x,0.0,1.0)!=normal.x ? myNormal : normal;
#endif
#else
normal = myNormal;
#endif
vec3 refraction = color2;
#ifdef ENABLE_REFRACTION
// texC = texCoord.xy+ m_ReflectionDisplace * normal.x;
texC = texCoord.xy;
texC += sin(m_Time*1.8 + 3.0 * abs(position.y))* (refractionScale * min(depth2, 1.0));
texC = clamp(texC,vec2(0.0),vec2(0.999));
refraction = fetchTextureSample(m_Texture, texC, sampleNum).rgb;
#endif
vec3 waterPosition = surfacePoint.xyz;
waterPosition.y -= (level - m_WaterHeight);
vec4 texCoordProj = m_TextureProjMatrix * vec4(waterPosition, 1.0);
texCoordProj.x = texCoordProj.x + m_ReflectionDisplace * normal.x;
texCoordProj.z = texCoordProj.z + m_ReflectionDisplace * normal.z;
texCoordProj /= texCoordProj.w;
texCoordProj.y = 1.0 - texCoordProj.y;
vec3 reflection = texture2D(m_ReflectionMap, texCoordProj.xy).rgb;
float fresnel = fresnelTerm(normal, eyeVecNorm);
float depthN = depth * m_WaterTransparency;
float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale);
refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)),
m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction));
vec3 foam = vec3(0.0);
#ifdef ENABLE_FOAM
texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005;
vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005;
vec4 foam1 = texture2D(m_FoamMap, texC);
vec4 foam2 = texture2D(m_FoamMap, texCoord2);
if(depth2 < m_FoamExistence.x){
foam = (foam1.r + foam2).rgb * vec3(m_FoamIntensity);
}else if(depth2 < m_FoamExistence.y){
foam = mix((foam1 + foam2) * m_FoamIntensity , vec4(0.0),
(depth2 - m_FoamExistence.x) / (m_FoamExistence.y - m_FoamExistence.x)).rgb;
}
if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){
foam += ((foam1 + foam2) * m_FoamIntensity * m_FoamIntensity * 0.3 *
saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb;
}
foam *= m_LightColor.rgb;
#endif
vec3 specular = vec3(0.0);
#ifdef ENABLE_SPECULAR
vec3 lightDir=normalize(m_LightDir);
vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm);
float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5);
specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2)));
specular += specular * 25.0 * saturate(m_Shininess - 0.05);
//foam does not shine
specular=specular * m_LightColor.rgb - (5.0 * foam);
#endif
color = mix(refraction, reflection, fresnel);
color = mix(refraction, color, saturate(depth * m_ShoreHardness));
color = saturate(color + max(specular, foam ));
color = mix(refraction, color, saturate(depth* m_FoamHardness));
// XXX: HACK ALERT:
// We trick the GeForces to think they have
// to calculate the derivatives for all these pixels by using step()!
// That way we won't get pixels around the edges of the terrain,
// Where the derivatives are undefined
return vec4(mix(color, color2, step(level, position.y)), 1.0);
}
void main(){
setGlobals();
#ifdef RESOLVE_MS
vec4 color = vec4(0.0);
for (int i = 0; i < m_NumSamples; i++){
color += main_multiSample(i);
}
gl_FragColor = color / float(m_NumSamples);
#else
gl_FragColor = main_multiSample(0);
#endif
}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/gravel1/Gravel040_1K-PNG_NormalGL.png","displacementMap":"Textures/gravel1/Gravel040_1K-PNG_Displacement.png","roughnessMap":"Textures/gravel1/Gravel040_1K-PNG_Roughness.png","aoMap":"Textures/gravel1/Gravel040_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/gras2/Ground003_1K-PNG_NormalGL.png","displacementMap":"Textures/gras2/Ground003_1K-PNG_Displacement.png","roughnessMap":"Textures/gras2/Ground003_1K-PNG_Roughness.png","aoMap":""}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/leaves2/Ground019_1K-PNG_NormalGL.png","displacementMap":"Textures/leaves2/Ground019_1K-PNG_Displacement.png","roughnessMap":"Textures/leaves2/Ground019_1K-PNG_Roughness.png","aoMap":"Textures/leaves2/Ground019_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/leaves1/Ground023_1K-PNG_NormalGL.png","displacementMap":"Textures/leaves1/Ground023_1K-PNG_Displacement.png","roughnessMap":"Textures/leaves1/Ground023_1K-PNG_Roughness.png","aoMap":"Textures/leaves1/Ground023_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/gras1/Ground037_1K-PNG_NormalGL.png","displacementMap":"Textures/gras1/Ground037_1K-PNG_Displacement.png","roughnessMap":"Textures/gras1/Ground037_1K-PNG_Roughness.png","aoMap":"Textures/gras1/Ground037_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/dirt1/Ground048_1K-PNG_NormalGL.png","displacementMap":"Textures/dirt1/Ground048_1K-PNG_Displacement.png","roughnessMap":"Textures/dirt1/Ground048_1K-PNG_Roughness.png","aoMap":"Textures/dirt1/Ground048_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/sand1/Ground054_1K-PNG_NormalGL.png","displacementMap":"Textures/sand1/Ground054_1K-PNG_Displacement.png","roughnessMap":"Textures/sand1/Ground054_1K-PNG_Roughness.png","aoMap":"Textures/sand1/Ground054_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/sand2/Ground060_1K-PNG_NormalGL.png","displacementMap":"Textures/sand2/Ground060_1K-PNG_Displacement.png","roughnessMap":"Textures/sand2/Ground060_1K-PNG_Roughness.png","aoMap":"Textures/sand2/Ground060_1K-PNG_AmbientOcclusion.png"}

View File

@@ -0,0 +1 @@
{"normalMap":"Textures/gras3/Ground068_1K-PNG_NormalGL.png","displacementMap":"Textures/gras3/Ground068_1K-PNG_Displacement.png","roughnessMap":"Textures/gras3/Ground068_1K-PNG_Roughness.png","aoMap":"Textures/gras3/Ground068_1K-PNG_AmbientOcclusion.png"}

Some files were not shown because too many files have changed in this diff Show More