diff --git a/core/src/com/etheller/warsmash/WarsmashGdxGame.java b/core/src/com/etheller/warsmash/WarsmashGdxGame.java index 6f96dcf..02a2916 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxGame.java @@ -73,12 +73,16 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide this.cameraManager = new CameraManager(); this.cameraManager.setupCamera(scene); - this.mainModel = (MdxModel) this.viewer.load("Buildings\\Undead\\Necropolis\\Necropolis.mdx", new PathSolver() { - @Override - public SolvedPath solve(final String src, final Object solverParams) { - return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true); - } - }, null); +// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx", + this.mainModel = (MdxModel) this.viewer.load("Units\\NightElf\\DruidOfTheClaw\\DruidOfTheClaw_Portrait.mdx", + new PathSolver() { + @Override + public SolvedPath solve(final String src, final Object solverParams) { + return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true); + } + }, null); + + this.modelCamera = this.mainModel.cameras.get(0); this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0); @@ -343,6 +347,9 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide private int frame = 0; private MdxComplexInstance mainInstance; private MdxModel mainModel; + private com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera; + private final float[] cameraPositionTemp = new float[3]; + private final float[] cameraTargetTemp = new float[3]; @Override public void render() { @@ -406,6 +413,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide private Vector3 target; private Vector3 worldUp; private Vector3 vecHeap; + private Vector3 vecHeap2; private Quaternion quatHeap; private Quaternion quatHeap2; @@ -427,6 +435,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide this.target = new Vector3(0, 0, 50); this.worldUp = new Vector3(0, 0, 1); this.vecHeap = new Vector3(); + this.vecHeap2 = new Vector3(); this.quatHeap = new Quaternion(); this.quatHeap2 = new Quaternion(); @@ -451,6 +460,34 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide this.quatHeap.transform(this.position); this.position.scl(this.distance); this.position = this.position.add(this.target); + if (WarsmashGdxGame.this.modelCamera != null) { + WarsmashGdxGame.this.modelCamera.getPositionTranslation(WarsmashGdxGame.this.cameraPositionTemp, + WarsmashGdxGame.this.mainInstance.sequence, WarsmashGdxGame.this.mainInstance.frame, + WarsmashGdxGame.this.mainInstance.counter); + WarsmashGdxGame.this.modelCamera.getTargetTranslation(WarsmashGdxGame.this.cameraTargetTemp, + WarsmashGdxGame.this.mainInstance.sequence, WarsmashGdxGame.this.mainInstance.frame, + WarsmashGdxGame.this.mainInstance.counter); + + this.position.set(WarsmashGdxGame.this.modelCamera.position); + this.target.set(WarsmashGdxGame.this.modelCamera.targetPosition); +// this.vecHeap2.set(this.target); +// this.vecHeap2.sub(this.position); +// this.vecHeap.set(this.vecHeap2); +// this.vecHeap.crs(this.worldUp); +// this.vecHeap.crs(this.vecHeap2); +// this.vecHeap.nor(); +// this.vecHeap.scl(this.camera.rect.height / 2f); +// this.position.add(this.vecHeap); + + this.position.add(WarsmashGdxGame.this.cameraPositionTemp[0], + WarsmashGdxGame.this.cameraPositionTemp[1], WarsmashGdxGame.this.cameraPositionTemp[2]); + this.target.add(WarsmashGdxGame.this.cameraTargetTemp[0], WarsmashGdxGame.this.cameraTargetTemp[1], + WarsmashGdxGame.this.cameraTargetTemp[2]); + this.camera.perspective(WarsmashGdxGame.this.modelCamera.fieldOfView, + Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(), + WarsmashGdxGame.this.modelCamera.nearClippingPlane, + WarsmashGdxGame.this.modelCamera.farClippingPlane); + } this.camera.moveToAndFace(this.position, this.target, this.worldUp); } diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java index 9fe4927..5e58942 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java @@ -11,6 +11,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.GL30; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Quaternion; @@ -23,7 +24,11 @@ import com.etheller.warsmash.datasources.DataSourceDescriptor; import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; import com.etheller.warsmash.viewer5.Camera; import com.etheller.warsmash.viewer5.CanvasProvider; +import com.etheller.warsmash.viewer5.PathSolver; import com.etheller.warsmash.viewer5.Scene; +import com.etheller.warsmash.viewer5.SolvedPath; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor { @@ -34,6 +39,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv private BitmapFont font; private SpriteBatch batch; + private CameraManager portraitCameraManager; + private MdxModel portraitModel; + private MdxComplexInstance portraitInstance; + private final float[] cameraPositionTemp = new float[3]; + private final float[] cameraTargetTemp = new float[3]; @Override public void create() { @@ -74,7 +84,35 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.cameraManager.setupCamera(this.viewer.worldScene); System.out.println("Loaded"); - Gdx.gl30.glClearColor(0.5f, 0.5f, 0.5f, 1); // TODO remove white background + Gdx.gl30.glClearColor(0.0f, 0.0f, 0.0f, 1); // TODO remove white background + Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST); + + this.portraitScene = this.viewer.addScene(); + this.portraitCameraManager = new CameraManager(); + this.portraitCameraManager.setupCamera(this.portraitScene); + +// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx", + this.portraitModel = (MdxModel) this.viewer.load("Units\\NightElf\\Runner\\Runner_Portrait.mdx", + new PathSolver() { + @Override + public SolvedPath solve(final String src, final Object solverParams) { + return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true); + } + }, null); + + this.portraitCameraManager.modelCamera = this.portraitModel.cameras.get(0); + this.portraitScene.camera.viewport(new Rectangle(100, 0, 100, 100)); + + this.portraitInstance = (MdxComplexInstance) this.portraitModel.addInstance(0); + + this.portraitInstance.setTeamColor(1); + + this.portraitInstance.setScene(this.portraitScene); + + final int animIndex = 0; + this.portraitInstance.setSequence(animIndex); + + this.portraitInstance.setSequenceLoopMode(0); this.font = new BitmapFont(); this.batch = new SpriteBatch(); @@ -87,6 +125,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO); this.cameraManager.target.add(this.cameraVelocity.x, this.cameraVelocity.y, 0); this.cameraManager.updateCamera(); + this.portraitCameraManager.updateCamera(); this.viewer.updateAndRender(); // gl.glDrawElements(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT, this.faceOffset); @@ -94,6 +133,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv // this.batch.begin(); // this.font.draw(this.batch, Integer.toString(Gdx.graphics.getFramesPerSecond()), 0, 0); // this.batch.end(); + + if (this.portraitInstance.sequenceEnded) { + this.portraitInstance + .setSequence((this.portraitInstance.sequence + 1) % this.portraitModel.getSequences().size()); + } } @Override @@ -112,12 +156,22 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv @Override public void resize(final int width, final int height) { + this.tempRect.x = 0; + this.tempRect.y = 0; this.tempRect.width = width; this.tempRect.height = height; this.cameraManager.camera.viewport(this.tempRect); + final float portraitTestWidth = (100 / 640f) * width; + final float portraitTestHeight = (100 / 480f) * height; + this.tempRect.x = portraitTestWidth; + this.tempRect.y = 0; + this.tempRect.width = portraitTestWidth; + this.tempRect.height = portraitTestHeight; + this.portraitScene.camera.viewport(this.tempRect); } class CameraManager { + public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera; private CanvasProvider canvas; private Camera camera; private float moveSpeed; @@ -175,6 +229,27 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.quatHeap.transform(this.position); this.position.scl(this.distance); this.position = this.position.add(this.target); + if (this.modelCamera != null) { + this.modelCamera.getPositionTranslation(WarsmashGdxMapGame.this.cameraPositionTemp, + WarsmashGdxMapGame.this.portraitInstance.sequence, + WarsmashGdxMapGame.this.portraitInstance.frame, + WarsmashGdxMapGame.this.portraitInstance.counter); + this.modelCamera.getTargetTranslation(WarsmashGdxMapGame.this.cameraTargetTemp, + WarsmashGdxMapGame.this.portraitInstance.sequence, + WarsmashGdxMapGame.this.portraitInstance.frame, + WarsmashGdxMapGame.this.portraitInstance.counter); + + this.position.set(this.modelCamera.position); + this.target.set(this.modelCamera.targetPosition); + + this.position.add(WarsmashGdxMapGame.this.cameraPositionTemp[0], + WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]); + this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0], + WarsmashGdxMapGame.this.cameraTargetTemp[1], WarsmashGdxMapGame.this.cameraTargetTemp[2]); + this.camera.perspective(this.modelCamera.fieldOfView, + Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(), this.modelCamera.nearClippingPlane, + this.modelCamera.farClippingPlane); + } this.camera.moveToAndFace(this.position, this.target, this.worldUp); } @@ -186,6 +261,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv private final float cameraSpeed = 10.0f; private final Vector2 cameraVelocity = new Vector2(); + private Scene portraitScene; @Override public boolean keyDown(final int keycode) { diff --git a/core/src/com/etheller/warsmash/viewer5/Scene.java b/core/src/com/etheller/warsmash/viewer5/Scene.java index 5d0c038..02315d4 100644 --- a/core/src/com/etheller/warsmash/viewer5/Scene.java +++ b/core/src/com/etheller/warsmash/viewer5/Scene.java @@ -255,7 +255,7 @@ public class Scene { // If this scene doesn't want alpha, clear it. if (!this.alpha) { gl.glDepthMask(true); - gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); + gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Camera.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Camera.java index d074e4b..697c138 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Camera.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Camera.java @@ -1,15 +1,16 @@ package com.etheller.warsmash.viewer5.handlers.mdx; import com.etheller.warsmash.parsers.mdlx.AnimationMap; +import com.etheller.warsmash.util.RenderMathUtils; public class Camera extends AnimatedObject { - private final String name; - private final float[] position; - private final float fieldOfView; - private final float farClippingPlane; - private final float nearClippingPlane; - private final float[] targetPosition; + public final String name; + public final float[] position; + public final float fieldOfView; + public final float farClippingPlane; + public final float nearClippingPlane; + public final float[] targetPosition; public Camera(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Camera camera) { super(model, camera); @@ -23,11 +24,13 @@ public class Camera extends AnimatedObject { } public int getPositionTranslation(final float[] out, final int sequence, final int frame, final int counter) { - return this.getVectorValue(out, AnimationMap.KCTR.getWar3id(), sequence, frame, counter, this.position); + return this.getVectorValue(out, AnimationMap.KCTR.getWar3id(), sequence, frame, counter, + RenderMathUtils.FLOAT_VEC3_ZERO); } public int getTargetTranslation(final float[] out, final int sequence, final int frame, final int counter) { - return this.getVectorValue(out, AnimationMap.KTTR.getWar3id(), sequence, frame, counter, this.targetPosition); + return this.getVectorValue(out, AnimationMap.KTTR.getWar3id(), sequence, frame, counter, + RenderMathUtils.FLOAT_VEC3_ZERO); } public int getRotation(final float[] out, final int sequence, final int frame, final int counter) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java index ff51f8f..4b77b29 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -16,6 +16,7 @@ import com.etheller.warsmash.viewer5.Node; import com.etheller.warsmash.viewer5.RenderBatch; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.SkeletalNode; +import com.etheller.warsmash.viewer5.Texture; import com.etheller.warsmash.viewer5.TextureMapper; import com.etheller.warsmash.viewer5.UpdatableObject; import com.etheller.warsmash.viewer5.gl.DataTexture; @@ -58,6 +59,7 @@ public class MdxComplexInstance extends ModelInstance { public Matrix4[] worldMatrices; public FloatBuffer worldMatricesCopyHeap; public DataTexture boneTexture; + public Texture[] replaceableTextures = new Texture[64]; public MdxComplexInstance(final MdxModel model) { super(model); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java index 656afeb..4a19552 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java @@ -347,4 +347,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model { } + public List getCameras() { + return this.cameras; + } + } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/Doodad.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/Doodad.java index e6a0bd4..eb1c28b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/Doodad.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/Doodad.java @@ -2,6 +2,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x; import com.badlogic.gdx.math.Quaternion; import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; +import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType; import com.etheller.warsmash.util.RenderMathUtils; import com.etheller.warsmash.viewer5.ModelInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; @@ -11,7 +12,7 @@ public class Doodad { private final MutableGameObject row; public Doodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row, - final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad) { + final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type) { final boolean isSimple = row.readSLKTagBoolean("lightweight"); ModelInstance instance; @@ -25,6 +26,10 @@ public class Doodad { instance.move(doodad.getLocation()); instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, doodad.getAngle())); instance.scale(doodad.getScale()); + if (type == WorldEditorDataType.DOODADS) { + final float defScale = row.readSLKTagFloat("defScale"); + instance.uniformScale(defScale); + } instance.setScene(map.worldScene); this.instance = instance; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java index be773b1..02524db 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -47,6 +47,8 @@ import mpq.MPQArchive; import mpq.MPQException; public class War3MapViewer extends ModelViewer { + private static final War3ID UNIT_FILE = War3ID.fromString("umdl"); + private static final War3ID ITEM_FILE = War3ID.fromString("ifil"); private static final War3ID sloc = War3ID.fromString("sloc"); private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation(); public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation(); @@ -253,10 +255,11 @@ public class War3MapViewer extends ModelViewer { final War3MapDoo doo = this.mapMpq.readDoodads(); for (final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad : doo.getDoodads()) { - + WorldEditorDataType type = WorldEditorDataType.DOODADS; MutableGameObject row = modifications.getDoodads().get(doodad.getId()); if (row == null) { row = modifications.getDestructibles().get(doodad.getId()); + type = WorldEditorDataType.DESTRUCTIBLES; } String file = row.readSLKTag("file"); final int numVar = row.readSLKTagInt("numVar"); @@ -294,7 +297,7 @@ public class War3MapViewer extends ModelViewer { model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams); } - this.doodads.add(new Doodad(this, model, row, doodad)); + this.doodads.add(new Doodad(this, model, row, doodad, type)); } // Cliff/Terrain doodads. @@ -341,10 +344,19 @@ public class War3MapViewer extends ModelViewer { row = modifications.getUnits().get(unit.getId()); if (row == null) { row = modifications.getItems().get(unit.getId()); - } + path = row.getFieldAsString(ITEM_FILE, 0); - if (row != null) { - path = row.readSLKTag("file"); + if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { + path = path.substring(0, path.length() - 4); + } + if (row.readSLKTagInt("fileVerFlags") == 2) { + path += "_V1"; + } + + path += ".mdx"; + } + else { + path = row.getFieldAsString(UNIT_FILE, 0); if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { path = path.substring(0, path.length() - 4); @@ -402,6 +414,15 @@ public class War3MapViewer extends ModelViewer { worldScene.renderOpaque(); this.terrain.renderWater(); worldScene.renderTranslucent(); + + final List scenes = this.scenes; + for (final Scene scene : scenes) { + if (scene != worldScene) { + scene.startFrame(); + scene.renderOpaque(); + scene.renderTranslucent(); + } + } } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java index a71fb70..67ba5ea 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java @@ -302,9 +302,9 @@ public class Terrain { updateGroundHeights(new Rectangle(0, 0, width - 1, height - 1)); - this.groundShader = webGL.createShaderProgram(HiveWEShaders.Terrain.vert, HiveWEShaders.Terrain.frag); - this.cliffShader = webGL.createShaderProgram(HiveWEShaders.Cliffs.vert, HiveWEShaders.Cliffs.frag); - this.waterShader = webGL.createShaderProgram(HiveWEShaders.Water.vert, HiveWEShaders.Water.frag); + this.groundShader = webGL.createShaderProgram(TerrainShaders.Terrain.vert, TerrainShaders.Terrain.frag); + this.cliffShader = webGL.createShaderProgram(TerrainShaders.Cliffs.vert, TerrainShaders.Cliffs.frag); + this.waterShader = webGL.createShaderProgram(TerrainShaders.Water.vert, TerrainShaders.Water.frag); // TODO collision bodies (?) @@ -649,6 +649,7 @@ public class Terrain { this.webGL.useShaderProgram(this.groundShader); final GL30 gl = Gdx.gl30; + gl.glDisable(GL30.GL_CULL_FACE); gl.glDisable(GL30.GL_BLEND); gl.glUniformMatrix4fv(this.groundShader.getUniformLocation("MVP"), 1, false, diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/HiveWEShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java similarity index 99% rename from core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/HiveWEShaders.java rename to core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java index 16d7d21..311a955 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/HiveWEShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java @@ -1,6 +1,6 @@ package com.etheller.warsmash.viewer5.handlers.w3x.environment; -public class HiveWEShaders { +public class TerrainShaders { public static final class Cliffs { private Cliffs() { } @@ -273,7 +273,7 @@ public class HiveWEShaders { " gl_Position = is_water ? MVP * vec4((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, water_height*128.0, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n" + // "\r\n" + // - " UV = vec2(vPosition.x, 1 - vPosition.y);\r\n" + // + " UV = vec2(vPosition.x, vPosition.y);\r\n" + // "\r\n" + // " float ground_height = texelFetch(ground_height_texture, height_pos, 0).r;\r\n" + // " float value = clamp(water_height - ground_height, 0.f, 1.f);\r\n" + //