From 24590f77831f6b71c1f9e8bba78e77810ce2f201 Mon Sep 17 00:00:00 2001 From: Retera Date: Sun, 6 Jun 2021 01:29:05 -0400 Subject: [PATCH] Invulnerable ability, first draft of multi select, colored selection circles --- .../warsmash/WarsmashGdxMapScreen.java | 2 +- .../etheller/warsmash/parsers/fdf/GameUI.java | 4 +- .../fdf/frames/SimpleStatusBarFrame.java | 23 +- .../parsers/fdf/frames/TextureFrame.java | 11 + .../warsmash/util/WarsmashConstants.java | 1 - .../etheller/warsmash/viewer5/Shaders.java | 21 +- .../viewer5/handlers/mdx/BatchGroup.java | 8 +- .../viewer5/handlers/mdx/CollisionShape.java | 11 +- .../viewer5/handlers/mdx/EmitterGroup.java | 2 +- .../handlers/mdx/GeometryEmitterFuncs.java | 15 +- .../handlers/mdx/MdxComplexInstance.java | 23 +- .../viewer5/handlers/mdx/MdxHandler.java | 37 +- .../viewer5/handlers/mdx/MdxRenderBatch.java | 2 +- .../viewer5/handlers/mdx/MdxShaders.java | 636 +++++++++--------- .../viewer5/handlers/mdx/MdxViewer.java | 2 + .../viewer5/handlers/w3x/W3xShaders.java | 112 +-- .../viewer5/handlers/w3x/War3MapViewer.java | 184 ++++- .../w3x/camera/GameCameraManager.java | 39 +- .../handlers/w3x/environment/Terrain.java | 12 +- .../w3x/environment/TerrainShaders.java | 383 +++++------ .../w3x/rendersim/RenderDestructable.java | 26 + .../handlers/w3x/rendersim/RenderItem.java | 21 + .../handlers/w3x/rendersim/RenderUnit.java | 43 +- .../handlers/w3x/rendersim/RenderWidget.java | 8 + .../w3x/simulation/CDestructable.java | 15 + .../handlers/w3x/simulation/CItem.java | 10 + .../handlers/w3x/simulation/CUnit.java | 32 +- .../handlers/w3x/simulation/CUnitType.java | 8 +- .../handlers/w3x/simulation/CWidget.java | 4 + .../w3x/simulation/CWorldCollision.java | 6 +- .../combat/CAbilityInvulnerable.java | 77 +++ .../abilities/hero/CAbilityHero.java | 3 +- .../CAbilityTypeDefinitionInvulnerable.java | 30 + .../types/impl/CAbilityTypeInvulnerable.java | 24 + .../attacks/CUnitAttackMissileSplash.java | 7 +- .../w3x/simulation/data/CAbilityData.java | 2 + .../w3x/simulation/data/CUnitData.java | 4 +- .../pathing/CPathfindingProcessor.java | 5 +- .../viewer5/handlers/w3x/ui/MeleeUI.java | 281 +++++++- .../handlers/w3x/ui/MenuCursorState.java | 24 + 40 files changed, 1472 insertions(+), 686 deletions(-) create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityInvulnerable.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/definitions/impl/CAbilityTypeDefinitionInvulnerable.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInvulnerable.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuCursorState.java diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java index 9f4c9b1..bc3601c 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java @@ -53,7 +53,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI; public class WarsmashGdxMapScreen implements InputProcessor, Screen { public static final boolean ENABLE_AUDIO = true; - private static final boolean ENABLE_MUSIC = true; + private static final boolean ENABLE_MUSIC = false; private final War3MapViewer viewer; private final Rectangle tempRect = new Rectangle(); diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index f42926b..d55e532 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -221,7 +221,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } } - public boolean hasSkinField(String file) { + public boolean hasSkinField(final String file) { return (file != null) && this.skin.hasField(file); } @@ -329,7 +329,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { || ((parentDefinitionIfAvailable != null) && parentDefinitionIfAvailable.has("DecorateFileNames")); final SimpleStatusBarFrame simpleStatusBarFrame = new SimpleStatusBarFrame(frameDefinition.getName(), - parent, decorateFileNames); + parent, decorateFileNames, false, 0.0f); for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { simpleStatusBarFrame.add(inflate(childDefinition, simpleStatusBarFrame, frameDefinition, inDecorateFileNames || childDefinition.has("DecorateFileNames"))); diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java index a7ea544..4fad3cc 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java @@ -7,19 +7,28 @@ public class SimpleStatusBarFrame extends AbstractUIFrame { private final boolean decorateFileNames; private final TextureFrame barFrame; private final TextureFrame borderFrame; + private final float barInset; - public SimpleStatusBarFrame(final String name, final UIFrame parent, final boolean decorateFileNames) { + public SimpleStatusBarFrame(final String name, final UIFrame parent, final boolean decorateFileNames, + final boolean borderBelow, final float barInset) { super(name, parent); this.decorateFileNames = decorateFileNames; + this.barInset = barInset; this.barFrame = new TextureFrame(name + "Bar", this, decorateFileNames, new Vector4Definition(0, 1, 0, 1)); this.borderFrame = new TextureFrame(name + "Border", this, decorateFileNames, new Vector4Definition(0, 1, 0, 1)); this.borderFrame.setSetAllPoints(true); - this.barFrame.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this, FramePoint.TOPLEFT, 0, 0)); - this.barFrame.addSetPoint(new SetPoint(FramePoint.BOTTOMLEFT, this, FramePoint.BOTTOMLEFT, 0, 0)); - this.barFrame.setSetAllPoints(true); - add(this.barFrame); - add(this.borderFrame); + this.barFrame.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this, FramePoint.TOPLEFT, barInset, -barInset)); + this.barFrame.addSetPoint(new SetPoint(FramePoint.BOTTOMLEFT, this, FramePoint.BOTTOMLEFT, barInset, barInset)); + this.barFrame.setSetAllPoints(true, barInset); + if (borderBelow) { + add(this.borderFrame); + add(this.barFrame); + } + else { + add(this.barFrame); + add(this.borderFrame); + } } public boolean isDecorateFileNames() { @@ -28,7 +37,7 @@ public class SimpleStatusBarFrame extends AbstractUIFrame { public void setValue(final float value) { this.barFrame.setTexCoord(0, value, 0, 1); - this.barFrame.setWidth(this.renderBounds.width * value); + this.barFrame.setWidth(((this.renderBounds.width - (this.barInset * 2)) * value)); } public TextureFrame getBarFrame() { diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java index 22b77a6..77945e4 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java @@ -46,6 +46,17 @@ public class TextureFrame extends AbstractRenderableFrame { this.color = color; } + public void setColor(final float r, final float g, final float b, final float a) { + if (this.color == null) { + this.color = new Color(); + } + this.color.r = r; + this.color.g = g; + this.color.b = b; + this.color.a = a; + + } + public void setTexture(String file, final GameUI gameUI) { if (this.decorateFileNames) { file = gameUI.trySkinField(file); diff --git a/core/src/com/etheller/warsmash/util/WarsmashConstants.java b/core/src/com/etheller/warsmash/util/WarsmashConstants.java index aad55e8..6b2e4ab 100644 --- a/core/src/com/etheller/warsmash/util/WarsmashConstants.java +++ b/core/src/com/etheller/warsmash/util/WarsmashConstants.java @@ -9,7 +9,6 @@ public class WarsmashConstants { public static int GAME_VERSION = 1; public static final int REPLACEABLE_TEXTURE_LIMIT = 64; public static final float SIMULATION_STEP_TIME = 1 / 20f; - public static final float PLAYER_UNIT_MOUSE_HIGHLIGHT_UPDATE_STEP_TIME = 1 / 20f; public static final int PORT_NUMBER = 6115; public static final float BUILDING_CONSTRUCT_START_LIFE = 0.1f; public static final int BUILD_QUEUE_SIZE = 7; diff --git a/core/src/com/etheller/warsmash/viewer5/Shaders.java b/core/src/com/etheller/warsmash/viewer5/Shaders.java index 6171fb3..54c7dc7 100644 --- a/core/src/com/etheller/warsmash/viewer5/Shaders.java +++ b/core/src/com/etheller/warsmash/viewer5/Shaders.java @@ -1,5 +1,8 @@ package com.etheller.warsmash.viewer5; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentType; + public class Shaders { public static final String boneTexture = ""// + " uniform sampler2D u_boneMap;\r\n" + // @@ -58,6 +61,7 @@ public class Shaders { public static String lightSystem(final String normalName, final String positionName, final String lightTexture, final String lightTextureHeight, final String lightCount, final boolean terrain) { + new RuntimeException("lightSystem").printStackTrace(); return " vec3 lightFactor = vec3(0.0,0.0,0.0);\r\n" + // " for(float lightIndex = 0.5; lightIndex < " + lightCount + "; lightIndex += 1.0) {\r\n" + // " float rowPos = (lightIndex) / " + lightTextureHeight + ";\r\n" + // @@ -103,13 +107,14 @@ public class Shaders { " lightFactor += lightFactorContribution + (lightAmbColor.a/(pow(dist, 2.0))) * lightAmbColor.rgb;\r\n" + // " }\r\n" + // - " }\r\n";// + // - -// " vec4 sRGB = vec4(lightFactor, 1.0);" + // -// " bvec4 cutoff = lessThan(sRGB, vec4(0.04045));" + // -// " vec4 higher = pow((sRGB + vec4(0.055))/vec4(1.055), vec4(2.4));" + // -// " vec4 lower = sRGB/vec4(12.92);" + // -// "" + // -// " lightFactor = (higher * (vec4(1.0) - vec4(cutoff)) + lower * vec4(cutoff)).xyz;"; + " }\r\n" + // + (MdxHandler.CURRENT_SHADER_TYPE == ShaderEnvironmentType.MENU + ? " vec4 sRGB = vec4(lightFactor, 1.0);" + // + " bvec4 cutoff = lessThan(sRGB, vec4(0.04045));" + // + " vec4 higher = pow((sRGB + vec4(0.055))/vec4(1.055), vec4(2.4));" + // + " vec4 lower = sRGB/vec4(12.92);" + // + "" + // + " lightFactor = (higher * (vec4(1.0) - vec4(cutoff)) + lower * vec4(cutoff)).xyz;" + : ""); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java index d80a550..d8072f4 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java @@ -41,18 +41,18 @@ public class BatchGroup extends GenericGroup { if (isExtended) { if (DynamicShadowManager.IS_SHADOW_MAPPING) { - shader = MdxHandler.Shaders.extendedShadowMap; + shader = handler.shaders.extendedShadowMap; } else { - shader = MdxHandler.Shaders.extended; + shader = handler.shaders.extended; } } else { if (DynamicShadowManager.IS_SHADOW_MAPPING) { - shader = MdxHandler.Shaders.complexShadowMap; + shader = handler.shaders.complexShadowMap; } else { - shader = MdxHandler.Shaders.complex; + shader = handler.shaders.complex; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/CollisionShape.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/CollisionShape.java index 8dc90d3..7d1635b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/CollisionShape.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/CollisionShape.java @@ -87,7 +87,10 @@ public class CollisionShape extends GenericObject { public boolean checkIntersect(final Ray ray, final MdxNode mdxNode, final Vector3 intersection) { intersectHeap.set(this.center); intersectHeap.prj(mdxNode.worldMatrix); - return Intersector.intersectRaySphere(ray, intersectHeap, this.radius, intersection); + if (Intersector.intersectRaySphere(ray, intersectHeap, this.radius, intersection)) { + return true; + } + return false; } } @@ -120,6 +123,10 @@ public class CollisionShape extends GenericObject { intersectHeap2.prj(intersectMatrixHeap); intersectHeap2.sub(intersectHeap); intersectRayHeap.set(intersectHeap, intersectHeap2); - return bounds.intersectRay(intersectRayHeap, intersection); + if (bounds.intersectRay(intersectRayHeap, intersection)) { + intersection.prj(worldMatrix); + return true; + } + return false; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java index 7fed7a8..0586672 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java @@ -29,7 +29,7 @@ public class EmitterGroup extends GenericGroup { final ModelViewer viewer = model.viewer; final GL20 gl = viewer.gl; final ANGLEInstancedArrays instancedArrays = viewer.webGL.instancedArrays; - final ShaderProgram shader = MdxHandler.Shaders.particles; + final ShaderProgram shader = ((MdxModel) model).handler.shaders.particles; gl.glDepthMask(false); gl.glEnable(GL20.GL_BLEND); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java index 378e5c7..978b2a6 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java @@ -15,7 +15,9 @@ import com.etheller.warsmash.viewer5.Texture; import com.etheller.warsmash.viewer5.TextureMapper; import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays; import com.etheller.warsmash.viewer5.gl.ClientBuffer; +import com.etheller.warsmash.viewer5.gl.DataTexture; import com.etheller.warsmash.viewer5.handlers.EmitterObject; +import com.etheller.warsmash.viewer5.handlers.w3x.W3xSceneLightManager; //The total storage that emitted objects can use. //This is enough to support all of the MDX geometry emitters. @@ -191,6 +193,15 @@ public class GeometryEmitterFuncs { shader.setUniformf("u_columns", emitterObject.columns); shader.setUniformf("u_rows", emitterObject.rows); shader.setUniformf("u_teamColored", emitterObject.teamColored); + shader.setUniformi("u_unshaded", emitterObject.emitterUsesMdlOrUnshaded != 0 ? 1 : 0); + + final W3xSceneLightManager lightManager = (W3xSceneLightManager) scene.getLightManager(); + final DataTexture unitLightsTexture = lightManager.getUnitLightsTexture(); + + unitLightsTexture.bind(14); + shader.setUniformi("u_lightTexture", 14); + shader.setUniformf("u_lightCount", lightManager.getUnitLightCount()); + shader.setUniformf("u_lightTextureHeight", unitLightsTexture.getHeight()); shader.setUniform3fv("u_intervals[0]", intervals[0], 0, 3); shader.setUniform3fv("u_intervals[1]", intervals[1], 0, 3); @@ -210,9 +221,7 @@ public class GeometryEmitterFuncs { shader.setUniform3fv("u_vertices[3]", asFloatArray(vectors[3]), 0, 3); } - if (emitterObject.tail) { - shader.setUniform3fv("u_cameraZ", asFloatArray(camera.billboardedVectors[6]), 0, 3); - } + shader.setUniform3fv("u_cameraZ", asFloatArray(camera.billboardedVectors[6]), 0, 3); } public static void bindRibbonEmitterBuffer(final RibbonEmitter emitter, final ClientBuffer buffer) { 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 73ab88b..0c16f66 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -36,6 +36,7 @@ public class MdxComplexInstance extends ModelInstance { private static final float[] colorHeap = new float[3]; private static final float[] alphaHeap = new float[1]; private static final long[] textureIdHeap = new long[1]; + private static final Vector3 intersectionHeap = new Vector3(); public List lights = new ArrayList<>(); public List attachments = new ArrayList<>(); @@ -778,13 +779,18 @@ public class MdxComplexInstance extends ModelInstance { public boolean intersectRayWithCollisionSimple(final Ray ray, final Vector3 intersection) { final MdxModel mdxModel = (MdxModel) this.model; final List collisionShapes = mdxModel.collisionShapes; + boolean intersected = false; + ray.getEndPoint(intersection, 99999); for (final CollisionShape collisionShape : collisionShapes) { final MdxNode mdxNode = this.nodes[collisionShape.index]; - if (collisionShape.checkIntersect(ray, mdxNode, intersection)) { - return true; + if (collisionShape.checkIntersect(ray, mdxNode, intersectionHeap)) { + if (intersectionHeap.dst2(ray.origin) < intersection.dst2(ray.origin)) { + intersection.set(intersectionHeap); + } + intersected = true; } } - return intersectRayBounds(ray, intersection); + return intersected || intersectRayBounds(ray, intersection); } /** @@ -796,19 +802,24 @@ public class MdxComplexInstance extends ModelInstance { */ public boolean intersectRayWithMeshSlow(final Ray ray, final Vector3 intersection) { final MdxModel mdxModel = (MdxModel) this.model; + boolean intersected = false; + ray.getEndPoint(intersection, 99999); for (final Geoset geoset : mdxModel.geosets) { if (!geoset.unselectable) { geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter); if (alphaHeap[0] > 0) { final MdlxGeoset mdlxGeoset = geoset.mdlxGeoset; if (CollisionShape.intersectRayTriangles(ray, this, mdlxGeoset.getVertices(), mdlxGeoset.getFaces(), - 3, intersection)) { - return true; + 3, intersectionHeap)) { + if (intersectionHeap.dst2(ray.origin) < intersection.dst2(ray.origin)) { + intersection.set(intersectionHeap); + } + intersected = true; } } } } - return false; + return intersected; } /** diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java index 5c94309..9d4d6e4 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java @@ -12,6 +12,14 @@ import com.etheller.warsmash.viewer5.handlers.blp.DdsHandler; import com.etheller.warsmash.viewer5.handlers.tga.TgaHandler; public class MdxHandler extends ModelHandler { + public final Shaders shaders = new Shaders(); + + public static enum ShaderEnvironmentType { + MENU, + GAME + }; + + public static ShaderEnvironmentType CURRENT_SHADER_TYPE; public MdxHandler() { this.extensions = new ArrayList<>(); @@ -26,14 +34,14 @@ public class MdxHandler extends ModelHandler { viewer.addHandler(new DdsHandler()); viewer.addHandler(new TgaHandler()); - Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex); - Shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, + this.shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex(), MdxShaders.fsComplex); + this.shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex(), MdxShaders.fsComplex); - Shaders.complexShadowMap = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, + this.shaders.complexShadowMap = viewer.webGL.createShaderProgram(MdxShaders.vsComplex(), MdxShaders.fsComplexShadowMap); - Shaders.extendedShadowMap = viewer.webGL.createShaderProgram( - "#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap); - Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles); + this.shaders.extendedShadowMap = viewer.webGL.createShaderProgram( + "#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex(), MdxShaders.fsComplexShadowMap); + this.shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles(), MdxShaders.fsParticles); // Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, // MdxShaders.fsSimple); // Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd); @@ -41,7 +49,8 @@ public class MdxHandler extends ModelHandler { // If a shader failed to compile, don't allow the handler to be registered, and // send an error instead. - return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled() + return this.shaders.complex.isCompiled() && this.shaders.extended.isCompiled() + && this.shaders.particles.isCompiled() /* && Shaders.simple.isCompiled() && Shaders.hd.isCompiled() */; } @@ -56,12 +65,12 @@ public class MdxHandler extends ModelHandler { } - public static ShaderProgram complex; - public static ShaderProgram complexShadowMap; - public static ShaderProgram extended; - public static ShaderProgram extendedShadowMap; - public static ShaderProgram simple; - public static ShaderProgram particles; - public static ShaderProgram hd; + public ShaderProgram complex; + public ShaderProgram complexShadowMap; + public ShaderProgram extended; + public ShaderProgram extendedShadowMap; + public ShaderProgram simple; + public ShaderProgram particles; + public ShaderProgram hd; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxRenderBatch.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxRenderBatch.java index 91f6586..66203c9 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxRenderBatch.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxRenderBatch.java @@ -73,7 +73,7 @@ public class MdxRenderBatch extends RenderBatch { final GL20 gl = viewer.gl; final WebGL webGL = viewer.webGL; final ANGLEInstancedArrays instancedArrays = webGL.instancedArrays; - final ShaderProgram shader = MdxHandler.Shaders.simple; + final ShaderProgram shader = model.handler.shaders.simple; final int m0 = shader.getAttributeLocation("a_m0"); final int m1 = shader.getAttributeLocation("a_m1"); final int m2 = shader.getAttributeLocation("a_m2"); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java index 7e7cb8c..194d012 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java @@ -83,81 +83,84 @@ public class MdxShaders { " gl_FragColor = color;\r\n" + // " }\r\n"; - public static final String vsComplex = "\r\n" + // - "\r\n" + // - " uniform mat4 u_mvp;\r\n" + // - " uniform vec4 u_vertexColor;\r\n" + // - " uniform vec4 u_geosetColor;\r\n" + // - " uniform float u_layerAlpha;\r\n" + // - " uniform vec2 u_uvTrans;\r\n" + // - " uniform vec2 u_uvRot;\r\n" + // - " uniform float u_uvScale;\r\n" + // - " uniform bool u_hasBones;\r\n" + // - " uniform bool u_unshaded;\r\n" + // - " attribute vec3 a_position;\r\n" + // - " attribute vec3 a_normal;\r\n" + // - " attribute vec2 a_uv;\r\n" + // - " attribute vec4 a_bones;\r\n" + // - " #ifdef EXTENDED_BONES\r\n" + // - " attribute vec4 a_extendedBones;\r\n" + // - " #endif\r\n" + // - " attribute float a_boneNumber;\r\n" + // - " varying vec2 v_uv;\r\n" + // - " varying vec4 v_color;\r\n" + // - " varying vec4 v_uvTransRot;\r\n" + // - " varying float v_uvScale;\r\n" + // - " uniform sampler2D u_lightTexture;\r\n" + // - " uniform float u_lightCount;\r\n" + // - " uniform float u_lightTextureHeight;\r\n" + // - Shaders.boneTexture + "\r\n" + // - " void transform(inout vec3 position, inout vec3 normal) {\r\n" + // - " // For the broken models out there, since the game supports this.\r\n" + // - " if (a_boneNumber > 0.0) {\r\n" + // - " vec4 position4 = vec4(position, 1.0);\r\n" + // - " vec4 normal4 = vec4(normal, 0.0);\r\n" + // - " mat4 bone;\r\n" + // - " vec4 p = vec4(0.0,0.0,0.0,0.0);\r\n" + // - " vec4 n = vec4(0.0,0.0,0.0,0.0);\r\n" + // - " for (int i = 0; i < 4; i++) {\r\n" + // - " if (a_bones[i] > 0.0) {\r\n" + // - " bone = fetchMatrix(a_bones[i] - 1.0, 0.0);\r\n" + // - " p += bone * position4;\r\n" + // - " n += bone * normal4;\r\n" + // - " }\r\n" + // - " }\r\n" + // - " #ifdef EXTENDED_BONES\r\n" + // - " for (int i = 0; i < 4; i++) {\r\n" + // - " if (a_extendedBones[i] > 0.0) {\r\n" + // - " bone = fetchMatrix(a_extendedBones[i] - 1.0, 0.0);\r\n" + // - " p += bone * position4;\r\n" + // - " n += bone * normal4;\r\n" + // - " }\r\n" + // - " }\r\n" + // - " #endif\r\n" + // - " position = p.xyz / a_boneNumber;\r\n" + // - " normal = normalize(n.xyz);\r\n" + // - " } else {\r\n" + // - " position.x += 100.0;\r\n" + // - " }\r\n" + // - "\r\n" + // - " }\r\n" + // - " void main() {\r\n" + // - " vec3 position = a_position;\r\n" + // - " vec3 normal = a_normal;\r\n" + // - " if (u_hasBones) {\r\n" + // - " transform(position, normal);\r\n" + // - " }\r\n" + // - " v_uv = a_uv;\r\n" + // - " v_color = u_vertexColor * u_geosetColor.bgra * vec4(1.0, 1.0, 1.0, u_layerAlpha);\r\n" + // - " v_uvTransRot = vec4(u_uvTrans, u_uvRot);\r\n" + // - " v_uvScale = u_uvScale;\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // - " if(!u_unshaded) {\r\n" + // - Shaders.lightSystem("normal", "position", "u_lightTexture", "u_lightTextureHeight", "u_lightCount", false) - + "\r\n" + // - " v_color.xyz *= clamp(lightFactor, 0.0, 1.0);\r\n" + // - " }\r\n" + // - " }"; + public static final String vsComplex() { + return "\r\n" + // + "\r\n" + // + " uniform mat4 u_mvp;\r\n" + // + " uniform vec4 u_vertexColor;\r\n" + // + " uniform vec4 u_geosetColor;\r\n" + // + " uniform float u_layerAlpha;\r\n" + // + " uniform vec2 u_uvTrans;\r\n" + // + " uniform vec2 u_uvRot;\r\n" + // + " uniform float u_uvScale;\r\n" + // + " uniform bool u_hasBones;\r\n" + // + " uniform bool u_unshaded;\r\n" + // + " attribute vec3 a_position;\r\n" + // + " attribute vec3 a_normal;\r\n" + // + " attribute vec2 a_uv;\r\n" + // + " attribute vec4 a_bones;\r\n" + // + " #ifdef EXTENDED_BONES\r\n" + // + " attribute vec4 a_extendedBones;\r\n" + // + " #endif\r\n" + // + " attribute float a_boneNumber;\r\n" + // + " varying vec2 v_uv;\r\n" + // + " varying vec4 v_color;\r\n" + // + " varying vec4 v_uvTransRot;\r\n" + // + " varying float v_uvScale;\r\n" + // + " uniform sampler2D u_lightTexture;\r\n" + // + " uniform float u_lightCount;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // + Shaders.boneTexture + "\r\n" + // + " void transform(inout vec3 position, inout vec3 normal) {\r\n" + // + " // For the broken models out there, since the game supports this.\r\n" + // + " if (a_boneNumber > 0.0) {\r\n" + // + " vec4 position4 = vec4(position, 1.0);\r\n" + // + " vec4 normal4 = vec4(normal, 0.0);\r\n" + // + " mat4 bone;\r\n" + // + " vec4 p = vec4(0.0,0.0,0.0,0.0);\r\n" + // + " vec4 n = vec4(0.0,0.0,0.0,0.0);\r\n" + // + " for (int i = 0; i < 4; i++) {\r\n" + // + " if (a_bones[i] > 0.0) {\r\n" + // + " bone = fetchMatrix(a_bones[i] - 1.0, 0.0);\r\n" + // + " p += bone * position4;\r\n" + // + " n += bone * normal4;\r\n" + // + " }\r\n" + // + " }\r\n" + // + " #ifdef EXTENDED_BONES\r\n" + // + " for (int i = 0; i < 4; i++) {\r\n" + // + " if (a_extendedBones[i] > 0.0) {\r\n" + // + " bone = fetchMatrix(a_extendedBones[i] - 1.0, 0.0);\r\n" + // + " p += bone * position4;\r\n" + // + " n += bone * normal4;\r\n" + // + " }\r\n" + // + " }\r\n" + // + " #endif\r\n" + // + " position = p.xyz / a_boneNumber;\r\n" + // + " normal = normalize(n.xyz);\r\n" + // + " } else {\r\n" + // + " position.x += 100.0;\r\n" + // + " }\r\n" + // + "\r\n" + // + " }\r\n" + // + " void main() {\r\n" + // + " vec3 position = a_position;\r\n" + // + " vec3 normal = a_normal;\r\n" + // + " if (u_hasBones) {\r\n" + // + " transform(position, normal);\r\n" + // + " }\r\n" + // + " v_uv = a_uv;\r\n" + // + " v_color = u_vertexColor * u_geosetColor.bgra * vec4(1.0, 1.0, 1.0, u_layerAlpha);\r\n" + // + " v_uvTransRot = vec4(u_uvTrans, u_uvRot);\r\n" + // + " v_uvScale = u_uvScale;\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " if(!u_unshaded) {\r\n" + // + Shaders.lightSystem("normal", "position", "u_lightTexture", "u_lightTextureHeight", "u_lightCount", + false) + + "\r\n" + // + " v_color.xyz *= clamp(lightFactor, 0.0, 1.0);\r\n" + // + " }\r\n" + // + " }"; + } public static final String fsComplex = Shaders.quatTransform + "\r\n\r\n" + // " uniform sampler2D u_texture;\r\n" + // @@ -219,239 +222,256 @@ public class MdxShaders { " gl_FragColor = vec4(0.0, 0, 0, 1.0);//gl_FragCoord.z;\r\n" + // " }"; - public static final String vsParticles = "\r\n" + // - " #define EMITTER_PARTICLE2 0.0\r\n" + // - " #define EMITTER_RIBBON 1.0\r\n" + // - " #define EMITTER_SPLAT 2.0\r\n" + // - " #define EMITTER_UBERSPLAT 3.0\r\n" + // - " #define HEAD 0.0\r\n" + // - " uniform mat4 u_mvp;\r\n" + // - " uniform mediump float u_emitter;\r\n" + // - " // Shared\r\n" + // - " uniform vec4 u_colors[3];\r\n" + // - " uniform vec3 u_vertices[4];\r\n" + // - " uniform vec3 u_intervals[4];\r\n" + // - " uniform float u_lifeSpan;\r\n" + // - " uniform float u_columns;\r\n" + // - " uniform float u_rows;\r\n" + // - " // Particle2\r\n" + // - " uniform vec3 u_scaling;\r\n" + // - " uniform vec3 u_cameraZ;\r\n" + // - " uniform float u_timeMiddle;\r\n" + // - " uniform bool u_teamColored;\r\n" + // - " // Splat and Uber.\r\n" + // - " uniform vec3 u_intervalTimes;\r\n" + // - " // Vertices\r\n" + // - " attribute float a_position;\r\n" + // - " // Instances\r\n" + // - " attribute vec3 a_p0;\r\n" + // - " attribute vec3 a_p1;\r\n" + // - " attribute vec3 a_p2;\r\n" + // - " attribute vec3 a_p3;\r\n" + // - " attribute float a_health;\r\n" + // - " attribute vec4 a_color;\r\n" + // - " attribute float a_tail;\r\n" + // - " attribute vec3 a_leftRightTop;\r\n" + // - " varying vec2 v_texcoord;\r\n" + // - " varying vec4 v_color;\r\n" + // - " float getCell(vec3 interval, float factor) {\r\n" + // - " float start = interval[0];\r\n" + // - " float end = interval[1];\r\n" + // - " float repeat = interval[2];\r\n" + // - " float spriteCount = end - start;\r\n" + // - " if (spriteCount > 0.0) {\r\n" + // - " // Repeating speeds up the sprite animation, which makes it effectively run N times in its interval.\r\n" - + // - " // E.g. if repeat is 4, the sprite animation will be seen 4 times, and thus also run 4 times as fast.\r\n" - + // - " // The sprite index is limited to the number of actual sprites.\r\n" + // - " return min(start + mod(floor(spriteCount * repeat * factor), spriteCount), u_columns * u_rows - 1.0);\r\n" - + // - " }\r\n" + // - " return 0.0;\r\n" + // - " }\r\n" + // - " void particle2() {\r\n" + // - " float factor = (u_lifeSpan - a_health) / u_lifeSpan;\r\n" + // - " int index = 0;\r\n" + // - " if (factor < u_timeMiddle) {\r\n" + // - " factor = factor / u_timeMiddle;\r\n" + // - " index = 0;\r\n" + // - " } else {\r\n" + // - " factor = (factor - u_timeMiddle) / (1.0 - u_timeMiddle);\r\n" + // - " index = 1;\r\n" + // - " }\r\n" + // - " factor = min(factor, 1.0);\r\n" + // - " float scale = mix(u_scaling[index], u_scaling[index + 1], factor);\r\n" + // - " vec4 color = mix(u_colors[index], u_colors[index + 1], factor);\r\n" + // - " float cell = 0.0;\r\n" + // - " if (u_teamColored) {\r\n" + // - " cell = a_leftRightTop[0];\r\n" + // - " } else {\r\n" + // - " vec3 interval;\r\n" + // - " if (a_tail == HEAD) {\r\n" + // - " interval = u_intervals[index];\r\n" + // - " } else {\r\n" + // - " interval = u_intervals[index + 2];\r\n" + // - " }\r\n" + // - " cell = getCell(interval, factor);\r\n" + // - " }\r\n" + // - " float left = floor(mod(cell, u_columns));\r\n" + // - " float top = floor(cell / u_columns);\r\n" + // - " float right = left + 1.0;\r\n" + // - " float bottom = top + 1.0;\r\n" + // - " left /= u_columns;\r\n" + // - " right /= u_columns;\r\n" + // - " top /= u_rows;\r\n" + // - " bottom /= u_rows;\r\n" + // - " if (a_position == 0.0) {\r\n" + // - " v_texcoord = vec2(right, top);\r\n" + // - " } else if (a_position == 1.0) {\r\n" + // - " v_texcoord = vec2(left, top);\r\n" + // - " } else if (a_position == 2.0) {\r\n" + // - " v_texcoord = vec2(left, bottom);\r\n" + // - " } else if (a_position == 3.0) {\r\n" + // - " v_texcoord = vec2(right, bottom);\r\n" + // - " }\r\n" + // - " v_color = color;\r\n" + // - " \r\n" + // - " if (a_tail == HEAD) {\r\n" + // - " vec3 vertices[4];\r\n" + // - " if(a_p1[0] != 0.0 || a_p1[1] != 0.0) {\r\n" + // - " vec3 vx;\r\n" + // - " vx[0] = a_p1[0];\r\n" + // - " vx[1] = a_p1[1];\r\n" + // - " vx[2] = 0.0;\r\n" + // - " vx = normalize(vx);\r\n" + // - " vec3 vy;\r\n" + // - " vy[0] = -vx[1];\r\n" + // - " vy[1] = vx[0];\r\n" + // - " vy[2] = 0.0;\r\n" + // - " vertices[2] = - vx - vy;\r\n" + // - " vertices[1] = vx - vy;\r\n" + // - " vertices[0] = -vertices[2];\r\n" + // - " vertices[3] = -vertices[1];\r\n" + // - " } else {\r\n" + // - " vertices[0] = u_vertices[0];\r\n" + // - " vertices[1] = u_vertices[1];\r\n" + // - " vertices[2] = u_vertices[2];\r\n" + // - " vertices[3] = u_vertices[3];\r\n" + // - " }\r\n" + // - " gl_Position = u_mvp * vec4(a_p0 + (vertices[int(a_position)] * scale), 1.0);\r\n" + // - " } else {\r\n" + // - " // Get the normal to the tail in camera space.\r\n" + // - " // This allows to build a 2D rectangle around the 3D tail.\r\n" + // - " vec3 normal = cross(u_cameraZ, normalize(a_p1 - a_p0));\r\n" + // - " vec3 boundary = normal * scale * a_p2[0];\r\n" + // - " vec3 position;\r\n" + // - " if (a_position == 0.0) {\r\n" + // - " position = a_p0 - boundary;\r\n" + // - " } else if (a_position == 1.0) {\r\n" + // - " position = a_p1 - boundary;\r\n" + // - " } else if (a_position == 2.0) {\r\n" + // - " position = a_p1 + boundary;\r\n" + // - " } else if (a_position == 3.0) {\r\n" + // - " position = a_p0 + boundary;\r\n" + // - " }\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // - " }\r\n" + // - " }\r\n" + // - " void ribbon() {\r\n" + // - " vec3 position;\r\n" + // - " float left = a_leftRightTop[0] / 255.0;\r\n" + // - " float right = a_leftRightTop[1] / 255.0;\r\n" + // - " float top = a_leftRightTop[2] / 255.0;\r\n" + // - " float bottom = top + 1.0;\r\n" + // - " if (a_position == 0.0) {\r\n" + // - " v_texcoord = vec2(right, top);\r\n" + // - " position = a_p0;\r\n" + // - " } else if (a_position == 1.0) {\r\n" + // - " v_texcoord = vec2(right, bottom);\r\n" + // - " position = a_p1;\r\n" + // - " } else if (a_position == 2.0) {\r\n" + // - " v_texcoord = vec2(left, bottom);\r\n" + // - " position = a_p2;\r\n" + // - " } else if (a_position == 3.0) {\r\n" + // - " v_texcoord = vec2(left, top);\r\n" + // - " position = a_p3;\r\n" + // - " }\r\n" + // - " v_texcoord[0] /= u_columns;\r\n" + // - " v_texcoord[1] /= u_rows;\r\n" + // - " v_color = a_color;\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // - " }\r\n" + // - " void splat() {\r\n" + // - " float factor = u_lifeSpan - a_health;\r\n" + // - " int index;\r\n" + // - " if (factor < u_intervalTimes[0]) {\r\n" + // - " factor = factor / u_intervalTimes[0];\r\n" + // - " index = 0;\r\n" + // - " } else {\r\n" + // - " factor = (factor - u_intervalTimes[0]) / u_intervalTimes[1];\r\n" + // - " index = 1;\r\n" + // - " }\r\n" + // - " float cell = getCell(u_intervals[index], factor);\r\n" + // - " float left = floor(mod(cell, u_columns));\r\n" + // - " float top = floor(cell / u_columns);\r\n" + // - " float right = left + 1.0;\r\n" + // - " float bottom = top + 1.0;\r\n" + // - " vec3 position;\r\n" + // - " if (a_position == 0.0) {\r\n" + // - " v_texcoord = vec2(left, top);\r\n" + // - " position = a_p0;\r\n" + // - " } else if (a_position == 1.0) {\r\n" + // - " v_texcoord = vec2(left, bottom);\r\n" + // - " position = a_p1;\r\n" + // - " } else if (a_position == 2.0) {\r\n" + // - " v_texcoord = vec2(right, bottom);\r\n" + // - " position = a_p2;\r\n" + // - " } else if (a_position == 3.0) {\r\n" + // - " v_texcoord = vec2(right, top);\r\n" + // - " position = a_p3;\r\n" + // - " }\r\n" + // - " v_texcoord[0] /= u_columns;\r\n" + // - " v_texcoord[1] /= u_rows;\r\n" + // - " v_color = mix(u_colors[index], u_colors[index + 1], factor) / 255.0;\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // - " }\r\n" + // - " void ubersplat() {\r\n" + // - " float factor = u_lifeSpan - a_health;\r\n" + // - " vec4 color;\r\n" + // - " if (factor < u_intervalTimes[0]) {\r\n" + // - " color = mix(u_colors[0], u_colors[1], factor / u_intervalTimes[0]);\r\n" + // - " } else if (factor < u_intervalTimes[0] + u_intervalTimes[1]) {\r\n" + // - " color = u_colors[1];\r\n" + // - " } else {\r\n" + // - " color = mix(u_colors[1], u_colors[2], (factor - u_intervalTimes[0] - u_intervalTimes[1]) / u_intervalTimes[2]);\r\n" - + // - " }\r\n" + // - " vec3 position;\r\n" + // - " if (a_position == 0.0) {\r\n" + // - " v_texcoord = vec2(0.0, 0.0);\r\n" + // - " position = a_p0;\r\n" + // - " } else if (a_position == 1.0) {\r\n" + // - " v_texcoord = vec2(0.0, 1.0);\r\n" + // - " position = a_p1;\r\n" + // - " } else if (a_position == 2.0) {\r\n" + // - " v_texcoord = vec2(1.0, 1.0);\r\n" + // - " position = a_p2;\r\n" + // - " } else if (a_position == 3.0) {\r\n" + // - " v_texcoord = vec2(1.0, 0.0);\r\n" + // - " position = a_p3;\r\n" + // - " }\r\n" + // - " v_color = color / 255.0;\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // - " }\r\n" + // - " void main() {\r\n" + // - " if (u_emitter == EMITTER_PARTICLE2) {\r\n" + // - " particle2();\r\n" + // - " } else if (u_emitter == EMITTER_RIBBON) {\r\n" + // - " ribbon();\r\n" + // - " } else if (u_emitter == EMITTER_SPLAT) {\r\n" + // - " splat();\r\n" + // - " } else if (u_emitter == EMITTER_UBERSPLAT) {\r\n" + // - " ubersplat();\r\n" + // - " }\r\n" + // - " }"; + public static final String vsParticles() { + return "\r\n" + // + " #define EMITTER_PARTICLE2 0.0\r\n" + // + " #define EMITTER_RIBBON 1.0\r\n" + // + " #define EMITTER_SPLAT 2.0\r\n" + // + " #define EMITTER_UBERSPLAT 3.0\r\n" + // + " #define HEAD 0.0\r\n" + // + " uniform mat4 u_mvp;\r\n" + // + " uniform mediump float u_emitter;\r\n" + // + " // Shared\r\n" + // + " uniform vec4 u_colors[3];\r\n" + // + " uniform vec3 u_vertices[4];\r\n" + // + " uniform vec3 u_intervals[4];\r\n" + // + " uniform float u_lifeSpan;\r\n" + // + " uniform float u_columns;\r\n" + // + " uniform float u_rows;\r\n" + // + " // Particle2\r\n" + // + " uniform vec3 u_scaling;\r\n" + // + " uniform vec3 u_cameraZ;\r\n" + // + " uniform float u_timeMiddle;\r\n" + // + " uniform bool u_teamColored;\r\n" + // + " uniform bool u_unshaded;\r\n" + // + " uniform sampler2D u_lightTexture;\r\n" + // + " uniform float u_lightCount;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // + " // Splat and Uber.\r\n" + // + " uniform vec3 u_intervalTimes;\r\n" + // + " // Vertices\r\n" + // + " attribute float a_position;\r\n" + // + " // Instances\r\n" + // + " attribute vec3 a_p0;\r\n" + // + " attribute vec3 a_p1;\r\n" + // + " attribute vec3 a_p2;\r\n" + // + " attribute vec3 a_p3;\r\n" + // + " attribute float a_health;\r\n" + // + " attribute vec4 a_color;\r\n" + // + " attribute float a_tail;\r\n" + // + " attribute vec3 a_leftRightTop;\r\n" + // + " varying vec2 v_texcoord;\r\n" + // + " varying vec4 v_color;\r\n" + // + " float getCell(vec3 interval, float factor) {\r\n" + // + " float start = interval[0];\r\n" + // + " float end = interval[1];\r\n" + // + " float repeat = interval[2];\r\n" + // + " float spriteCount = end - start;\r\n" + // + " if (spriteCount > 0.0) {\r\n" + // + " // Repeating speeds up the sprite animation, which makes it effectively run N times in its interval.\r\n" + + // + " // E.g. if repeat is 4, the sprite animation will be seen 4 times, and thus also run 4 times as fast.\r\n" + + // + " // The sprite index is limited to the number of actual sprites.\r\n" + // + " return min(start + mod(floor(spriteCount * repeat * factor), spriteCount), u_columns * u_rows - 1.0);\r\n" + + // + " }\r\n" + // + " return 0.0;\r\n" + // + " }\r\n" + // + " void particle2() {\r\n" + // + " float factor = (u_lifeSpan - a_health) / u_lifeSpan;\r\n" + // + " int index = 0;\r\n" + // + " if (factor < u_timeMiddle) {\r\n" + // + " factor = factor / u_timeMiddle;\r\n" + // + " index = 0;\r\n" + // + " } else {\r\n" + // + " factor = (factor - u_timeMiddle) / (1.0 - u_timeMiddle);\r\n" + // + " index = 1;\r\n" + // + " }\r\n" + // + " factor = min(factor, 1.0);\r\n" + // + " float scale = mix(u_scaling[index], u_scaling[index + 1], factor);\r\n" + // + " vec4 color = mix(u_colors[index], u_colors[index + 1], factor);\r\n" + // + " float cell = 0.0;\r\n" + // + " if (u_teamColored) {\r\n" + // + " cell = a_leftRightTop[0];\r\n" + // + " } else {\r\n" + // + " vec3 interval;\r\n" + // + " if (a_tail == HEAD) {\r\n" + // + " interval = u_intervals[index];\r\n" + // + " } else {\r\n" + // + " interval = u_intervals[index + 2];\r\n" + // + " }\r\n" + // + " cell = getCell(interval, factor);\r\n" + // + " }\r\n" + // + " float left = floor(mod(cell, u_columns));\r\n" + // + " float top = floor(cell / u_columns);\r\n" + // + " float right = left + 1.0;\r\n" + // + " float bottom = top + 1.0;\r\n" + // + " left /= u_columns;\r\n" + // + " right /= u_columns;\r\n" + // + " top /= u_rows;\r\n" + // + " bottom /= u_rows;\r\n" + // + " if (a_position == 0.0) {\r\n" + // + " v_texcoord = vec2(right, top);\r\n" + // + " } else if (a_position == 1.0) {\r\n" + // + " v_texcoord = vec2(left, top);\r\n" + // + " } else if (a_position == 2.0) {\r\n" + // + " v_texcoord = vec2(left, bottom);\r\n" + // + " } else if (a_position == 3.0) {\r\n" + // + " v_texcoord = vec2(right, bottom);\r\n" + // + " }\r\n" + // + " v_color = color;\r\n" + // + " \r\n" + // + " vec3 lightingNormal;\r\n" + // + " vec3 position;\r\n" + // + " if (a_tail == HEAD) {\r\n" + // + " vec3 vertices[4];\r\n" + // + " if(a_p1[0] != 0.0 || a_p1[1] != 0.0) {\r\n" + // + " lightingNormal = vec3(0.0, 0.0, 1.0);\r\n" + // + " vec3 vx;\r\n" + // + " vx[0] = a_p1[0];\r\n" + // + " vx[1] = a_p1[1];\r\n" + // + " vx[2] = 0.0;\r\n" + // + " vx = normalize(vx);\r\n" + // + " vec3 vy;\r\n" + // + " vy[0] = -vx[1];\r\n" + // + " vy[1] = vx[0];\r\n" + // + " vy[2] = 0.0;\r\n" + // + " vertices[2] = - vx - vy;\r\n" + // + " vertices[1] = vx - vy;\r\n" + // + " vertices[0] = -vertices[2];\r\n" + // + " vertices[3] = -vertices[1];\r\n" + // + " } else {\r\n" + // + " lightingNormal = normalize(u_cameraZ);\r\n" + // + " vertices[0] = u_vertices[0];\r\n" + // + " vertices[1] = u_vertices[1];\r\n" + // + " vertices[2] = u_vertices[2];\r\n" + // + " vertices[3] = u_vertices[3];\r\n" + // + " }\r\n" + // + " position = a_p0 + (vertices[int(a_position)] * scale);\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " } else {\r\n" + // + " // Get the normal to the tail in camera space.\r\n" + // + " // This allows to build a 2D rectangle around the 3D tail.\r\n" + // + " vec3 normal = cross(u_cameraZ, normalize(a_p1 - a_p0));\r\n" + // + " vec3 boundary = normal * scale * a_p2[0];\r\n" + // + " if (a_position == 0.0) {\r\n" + // + " position = a_p0 - boundary;\r\n" + // + " } else if (a_position == 1.0) {\r\n" + // + " position = a_p1 - boundary;\r\n" + // + " } else if (a_position == 2.0) {\r\n" + // + " position = a_p1 + boundary;\r\n" + // + " } else if (a_position == 3.0) {\r\n" + // + " position = a_p0 + boundary;\r\n" + // + " }\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " lightingNormal = normalize(u_cameraZ);\r\n" + // + " }\r\n" + // + " if(!u_unshaded) {\r\n" + // + Shaders.lightSystem("lightingNormal", "position", "u_lightTexture", "u_lightTextureHeight", + "u_lightCount", false) + + "\r\n" + // + " v_color.xyz *= clamp(lightFactor, 0.0, 1.0);\r\n" + // + " }\r\n" + // + " }\r\n" + // + " void ribbon() {\r\n" + // + " vec3 position;\r\n" + // + " float left = a_leftRightTop[0] / 255.0;\r\n" + // + " float right = a_leftRightTop[1] / 255.0;\r\n" + // + " float top = a_leftRightTop[2] / 255.0;\r\n" + // + " float bottom = top + 1.0;\r\n" + // + " if (a_position == 0.0) {\r\n" + // + " v_texcoord = vec2(right, top);\r\n" + // + " position = a_p0;\r\n" + // + " } else if (a_position == 1.0) {\r\n" + // + " v_texcoord = vec2(right, bottom);\r\n" + // + " position = a_p1;\r\n" + // + " } else if (a_position == 2.0) {\r\n" + // + " v_texcoord = vec2(left, bottom);\r\n" + // + " position = a_p2;\r\n" + // + " } else if (a_position == 3.0) {\r\n" + // + " v_texcoord = vec2(left, top);\r\n" + // + " position = a_p3;\r\n" + // + " }\r\n" + // + " v_texcoord[0] /= u_columns;\r\n" + // + " v_texcoord[1] /= u_rows;\r\n" + // + " v_color = a_color;\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " }\r\n" + // + " void splat() {\r\n" + // + " float factor = u_lifeSpan - a_health;\r\n" + // + " int index;\r\n" + // + " if (factor < u_intervalTimes[0]) {\r\n" + // + " factor = factor / u_intervalTimes[0];\r\n" + // + " index = 0;\r\n" + // + " } else {\r\n" + // + " factor = (factor - u_intervalTimes[0]) / u_intervalTimes[1];\r\n" + // + " index = 1;\r\n" + // + " }\r\n" + // + " float cell = getCell(u_intervals[index], factor);\r\n" + // + " float left = floor(mod(cell, u_columns));\r\n" + // + " float top = floor(cell / u_columns);\r\n" + // + " float right = left + 1.0;\r\n" + // + " float bottom = top + 1.0;\r\n" + // + " vec3 position;\r\n" + // + " if (a_position == 0.0) {\r\n" + // + " v_texcoord = vec2(left, top);\r\n" + // + " position = a_p0;\r\n" + // + " } else if (a_position == 1.0) {\r\n" + // + " v_texcoord = vec2(left, bottom);\r\n" + // + " position = a_p1;\r\n" + // + " } else if (a_position == 2.0) {\r\n" + // + " v_texcoord = vec2(right, bottom);\r\n" + // + " position = a_p2;\r\n" + // + " } else if (a_position == 3.0) {\r\n" + // + " v_texcoord = vec2(right, top);\r\n" + // + " position = a_p3;\r\n" + // + " }\r\n" + // + " v_texcoord[0] /= u_columns;\r\n" + // + " v_texcoord[1] /= u_rows;\r\n" + // + " v_color = mix(u_colors[index], u_colors[index + 1], factor) / 255.0;\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " }\r\n" + // + " void ubersplat() {\r\n" + // + " float factor = u_lifeSpan - a_health;\r\n" + // + " vec4 color;\r\n" + // + " if (factor < u_intervalTimes[0]) {\r\n" + // + " color = mix(u_colors[0], u_colors[1], factor / u_intervalTimes[0]);\r\n" + // + " } else if (factor < u_intervalTimes[0] + u_intervalTimes[1]) {\r\n" + // + " color = u_colors[1];\r\n" + // + " } else {\r\n" + // + " color = mix(u_colors[1], u_colors[2], (factor - u_intervalTimes[0] - u_intervalTimes[1]) / u_intervalTimes[2]);\r\n" + + // + " }\r\n" + // + " vec3 position;\r\n" + // + " if (a_position == 0.0) {\r\n" + // + " v_texcoord = vec2(0.0, 0.0);\r\n" + // + " position = a_p0;\r\n" + // + " } else if (a_position == 1.0) {\r\n" + // + " v_texcoord = vec2(0.0, 1.0);\r\n" + // + " position = a_p1;\r\n" + // + " } else if (a_position == 2.0) {\r\n" + // + " v_texcoord = vec2(1.0, 1.0);\r\n" + // + " position = a_p2;\r\n" + // + " } else if (a_position == 3.0) {\r\n" + // + " v_texcoord = vec2(1.0, 0.0);\r\n" + // + " position = a_p3;\r\n" + // + " }\r\n" + // + " v_color = color / 255.0;\r\n" + // + " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " }\r\n" + // + " void main() {\r\n" + // + " if (u_emitter == EMITTER_PARTICLE2) {\r\n" + // + " particle2();\r\n" + // + " } else if (u_emitter == EMITTER_RIBBON) {\r\n" + // + " ribbon();\r\n" + // + " } else if (u_emitter == EMITTER_SPLAT) {\r\n" + // + " splat();\r\n" + // + " } else if (u_emitter == EMITTER_UBERSPLAT) {\r\n" + // + " ubersplat();\r\n" + // + " }\r\n" + // + " }"; + } public static final String fsParticles = "\r\n" + // " #define EMITTER_RIBBON 1.0\r\n" + // diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java index ea4fe18..6911611 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java @@ -7,6 +7,7 @@ import com.etheller.warsmash.util.WorldEditStrings; import com.etheller.warsmash.viewer5.CanvasProvider; import com.etheller.warsmash.viewer5.SceneLightManager; import com.etheller.warsmash.viewer5.handlers.AbstractMdxModelViewer; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentType; import com.etheller.warsmash.viewer5.handlers.w3x.W3xScenePortraitLightManager; public class MdxViewer extends AbstractMdxModelViewer { @@ -16,6 +17,7 @@ public class MdxViewer extends AbstractMdxModelViewer { public MdxViewer(final DataSource dataSource, final CanvasProvider canvas, final Vector3 defaultLighting) { super(dataSource, canvas); + MdxHandler.CURRENT_SHADER_TYPE = ShaderEnvironmentType.MENU; this.defaultLighting = defaultLighting; this.worldEditStrings = new WorldEditStrings(this.dataSource); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java index 891120a..9a39735 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java @@ -7,59 +7,65 @@ public class W3xShaders { private UberSplat() { } - public static final String vert = "\r\n" + // - "\r\n" + // - " uniform mat4 u_mvp;\r\n" + // - " uniform sampler2D u_heightMap;\r\n" + // - " uniform vec2 u_pixel;\r\n" + // - " uniform vec2 u_size;\r\n" + // - " uniform vec2 u_shadowPixel;\r\n" + // - " uniform vec2 u_centerOffset;\r\n" + // - " uniform sampler2D u_lightTexture;\r\n" + // - " uniform float u_lightCount;\r\n" + // - " uniform float u_lightTextureHeight;\r\n" + // - " attribute vec3 a_position;\r\n" + // - " attribute vec2 a_uv;\r\n" + // - " attribute float a_absoluteHeight;\r\n" + // - " varying vec2 v_uv;\r\n" + // - " varying vec2 v_suv;\r\n" + // - " varying vec3 v_normal;\r\n" + // - " varying float a_positionHeight;\r\n" + // - " varying vec3 shadeColor;\r\n" + // - " const float normalDist = 0.25;\r\n" + // - " void main() {\r\n" + // - " vec2 halfPixel = u_pixel * 0.5;\r\n" + // - " vec2 base = (a_position.xy - u_centerOffset) / 128.0;\r\n" + // - " float height;\r\n" + // - " float hL;\r\n" + // - " float hR;\r\n" + // - " float hD;\r\n" + // - " float hU;\r\n" + // - " if (a_absoluteHeight < -256.0) {\r\n" + // - " height = texture2D(u_heightMap, base * u_pixel + halfPixel).r * 128.0;\r\n" + // - " hL = texture2D(u_heightMap, vec2(base - vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + // - " hR = texture2D(u_heightMap, vec2(base + vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + // - " hD = texture2D(u_heightMap, vec2(base - vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + // - " hU = texture2D(u_heightMap, vec2(base + vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + // - " } else {\r\n" + // - " height = a_absoluteHeight;\r\n" + // - " hL = a_absoluteHeight;\r\n" + // - " hR = a_absoluteHeight;\r\n" + // - " hD = a_absoluteHeight;\r\n" + // - " hU = a_absoluteHeight;\r\n" + // - " }\r\n" + // - " v_normal = normalize(vec3(hL - hR, hD - hU, normalDist * 2.0));\r\n" + // - " v_uv = a_uv;\r\n" + // - " v_suv = base / u_size;\r\n" + // - " vec3 myposition = vec3(a_position.xy, height + a_position.z);\r\n" + // - " gl_Position = u_mvp * vec4(myposition.xyz, 1.0);\r\n" + // - " a_positionHeight = a_position.z;\r\n" + // - Shaders.lightSystem("v_normal", "myposition", "u_lightTexture", "u_lightTextureHeight", "u_lightCount", - true) - + "\r\n" + // - " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // - " }\r\n" + // - " "; + public static final String vert() { + return "\r\n" + // + "\r\n" + // + " uniform mat4 u_mvp;\r\n" + // + " uniform sampler2D u_heightMap;\r\n" + // + " uniform vec2 u_pixel;\r\n" + // + " uniform vec2 u_size;\r\n" + // + " uniform vec2 u_shadowPixel;\r\n" + // + " uniform vec2 u_centerOffset;\r\n" + // + " uniform sampler2D u_lightTexture;\r\n" + // + " uniform float u_lightCount;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // + " attribute vec3 a_position;\r\n" + // + " attribute vec2 a_uv;\r\n" + // + " attribute float a_absoluteHeight;\r\n" + // + " varying vec2 v_uv;\r\n" + // + " varying vec2 v_suv;\r\n" + // + " varying vec3 v_normal;\r\n" + // + " varying float a_positionHeight;\r\n" + // + " varying vec3 shadeColor;\r\n" + // + " const float normalDist = 0.25;\r\n" + // + " void main() {\r\n" + // + " vec2 halfPixel = u_pixel * 0.5;\r\n" + // + " vec2 base = (a_position.xy - u_centerOffset) / 128.0;\r\n" + // + " float height;\r\n" + // + " float hL;\r\n" + // + " float hR;\r\n" + // + " float hD;\r\n" + // + " float hU;\r\n" + // + " if (a_absoluteHeight < -256.0) {\r\n" + // + " height = texture2D(u_heightMap, base * u_pixel + halfPixel).r * 128.0;\r\n" + // + " hL = texture2D(u_heightMap, vec2(base - vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + + // + " hR = texture2D(u_heightMap, vec2(base + vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + + // + " hD = texture2D(u_heightMap, vec2(base - vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + + // + " hU = texture2D(u_heightMap, vec2(base + vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + + // + " } else {\r\n" + // + " height = a_absoluteHeight;\r\n" + // + " hL = a_absoluteHeight;\r\n" + // + " hR = a_absoluteHeight;\r\n" + // + " hD = a_absoluteHeight;\r\n" + // + " hU = a_absoluteHeight;\r\n" + // + " }\r\n" + // + " v_normal = normalize(vec3(hL - hR, hD - hU, normalDist * 2.0));\r\n" + // + " v_uv = a_uv;\r\n" + // + " v_suv = base / u_size;\r\n" + // + " vec3 myposition = vec3(a_position.xy, height + a_position.z);\r\n" + // + " gl_Position = u_mvp * vec4(myposition.xyz, 1.0);\r\n" + // + " a_positionHeight = a_position.z;\r\n" + // + Shaders.lightSystem("v_normal", "myposition", "u_lightTexture", "u_lightTextureHeight", + "u_lightCount", true) + + "\r\n" + // + " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // + " }\r\n" + // + " "; + } public static final String frag = "\r\n" + // " uniform sampler2D u_texture;\r\n" + // 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 ee050e5..7da6d63 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -11,10 +11,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.function.Consumer; import javax.imageio.ImageIO; @@ -73,6 +75,7 @@ import com.etheller.warsmash.viewer5.handlers.AbstractMdxModelViewer; import com.etheller.warsmash.viewer5.handlers.mdx.Attachment; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentType; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; import com.etheller.warsmash.viewer5.handlers.mdx.MdxNode; import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode; @@ -188,6 +191,8 @@ public class War3MapViewer extends AbstractMdxModelViewer { public List selModels = new ArrayList<>(); public List selected = new ArrayList<>(); + private final Set mouseHighlightSplatModelKeys = new HashSet<>(); + private final List mouseHighlightWidgets = new ArrayList<>(); private DataTable unitAckSoundsTable; private DataTable unitCombatSoundsTable; public DataTable miscData; @@ -234,6 +239,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas, final War3MapConfig mapConfig) { super(dataSource, canvas); + MdxHandler.CURRENT_SHADER_TYPE = ShaderEnvironmentType.GAME; this.gameDataSource = dataSource; final WebGL webGL = this.webGL; @@ -369,6 +375,9 @@ public class War3MapViewer extends AbstractMdxModelViewer { } this.selectionCircleScaleFactor = selectionCircleData.getFieldFloatValue("ScaleFactor"); this.imageWalkableZOffset = selectionCircleData.getFieldValue("ImageWalkableZOffset"); + this.selectionCircleColorFriend = parseColor(selectionCircleData, "ColorFriend"); + this.selectionCircleColorNeutral = parseColor(selectionCircleData, "ColorNeutral"); + this.selectionCircleColorEnemy = parseColor(selectionCircleData, "ColorEnemy"); this.uiSoundsTable = new DataTable(worldEditStrings); try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UISounds.slk")) { @@ -379,6 +388,13 @@ public class War3MapViewer extends AbstractMdxModelViewer { } } + private Color parseColor(final Element selectionCircleData, final String field) { + return new Color(selectionCircleData.getFieldFloatValue(field, 1) / 255f, + selectionCircleData.getFieldFloatValue(field, 2) / 255f, + selectionCircleData.getFieldFloatValue(field, 3) / 255f, + selectionCircleData.getFieldFloatValue(field, 0) / 255f); + } + public GenericResource loadMapGeneric(final String path, final FetchDataTypeName dataType, final LoadGenericCallback callback) { if (this.mapMpq == null) { @@ -1583,22 +1599,22 @@ public class War3MapViewer extends AbstractMdxModelViewer { locations.locations, this.terrain.centerOffset, locations.unitMapping, true, false, true); switch (allyKey) { case "e:": - model.color[0] = 1; - model.color[1] = 0; - model.color[2] = 0; - model.color[3] = 1; + model.color[0] = this.selectionCircleColorEnemy.r; + model.color[1] = this.selectionCircleColorEnemy.g; + model.color[2] = this.selectionCircleColorEnemy.b; + model.color[3] = this.selectionCircleColorEnemy.a; break; case "f:": - model.color[0] = 0; - model.color[1] = 1; - model.color[2] = 0; - model.color[3] = 1; + model.color[0] = this.selectionCircleColorFriend.r; + model.color[1] = this.selectionCircleColorFriend.g; + model.color[2] = this.selectionCircleColorFriend.b; + model.color[3] = this.selectionCircleColorFriend.a; break; default: - model.color[0] = 1; - model.color[1] = 1; - model.color[2] = 0; - model.color[3] = 1; + model.color[0] = this.selectionCircleColorNeutral.r; + model.color[1] = this.selectionCircleColorNeutral.g; + model.color[2] = this.selectionCircleColorNeutral.b; + model.color[3] = this.selectionCircleColorNeutral.a; break; } this.selModels.add(model); @@ -1606,6 +1622,113 @@ public class War3MapViewer extends AbstractMdxModelViewer { } } + public void clearUnitMouseOverHighlight(final RenderWidget unit) { + this.mouseHighlightWidgets.remove(unit); + unit.getSelectionPreviewHighlight().destroy(Gdx.gl30, this.terrain.centerOffset); + unit.unassignSelectionPreviewHighlight(); + } + + public void clearUnitMouseOverHighlight() { + for (final String modelKey : this.mouseHighlightSplatModelKeys) { + this.terrain.removeSplatBatchModel(modelKey); + } + for (final RenderWidget widget : this.mouseHighlightWidgets) { + widget.unassignSelectionPreviewHighlight(); + } + this.mouseHighlightSplatModelKeys.clear(); + this.mouseHighlightWidgets.clear(); + } + + public void showUnitMouseOverHighlight(final RenderWidget unit) { + final Map splats = new HashMap(); + if (unit.getSelectionScale() > 0) { + String allyKey = "n:"; + final float selectionSize = unit.getSelectionScale(); + String path = null; + for (int i = 0; i < this.selectionCircleSizes.size(); i++) { + final SelectionCircleSize selectionCircleSize = this.selectionCircleSizes.get(i); + if ((selectionSize < selectionCircleSize.size) || (i == (this.selectionCircleSizes.size() - 1))) { + path = selectionCircleSize.texture; + break; + } + } + if (!path.toLowerCase().endsWith(".blp")) { + path += ".blp"; + } + if (unit instanceof RenderUnit) { + final int selectedUnitPlayerIndex = ((RenderUnit) unit).getSimulationUnit().getPlayerIndex(); + final CPlayer localPlayer = this.simulation.getPlayer(this.localPlayerIndex); + if (!localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.PASSIVE)) { + allyKey = "e:"; + } + else if (localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.HELP_REQUEST)) { + allyKey = "f:"; + } + } + path = allyKey + path; + final SplatModel splatModel = this.terrain.getSplatModel("mouseover:" + path); + if (splatModel != null) { + final float x = unit.getX(); + final float y = unit.getY(); + final SplatMover splatInstance = splatModel.add(x - (selectionSize / 2), y - (selectionSize / 2), + x + (selectionSize / 2), y + (selectionSize / 2), 4, this.terrain.centerOffset); + unit.assignSelectionPreviewHighlight(splatInstance); + if (unit.getInstance().hidden()) { + splatInstance.hide(); + } + } + else { + if (!splats.containsKey(path)) { + splats.put(path, new Splat()); + } + final float x = unit.getX(); + final float y = unit.getY(); + splats.get(path).locations.add(new float[] { x - (selectionSize / 2), y - (selectionSize / 2), + x + (selectionSize / 2), y + (selectionSize / 2), 4 }); + splats.get(path).unitMapping.add(new Consumer() { + @Override + public void accept(final SplatMover t) { + unit.assignSelectionPreviewHighlight(t); + if (unit.getInstance().hidden()) { + t.hide(); + } + } + }); + } + } + this.mouseHighlightWidgets.add(unit); + for (final Map.Entry entry : splats.entrySet()) { + final String path = entry.getKey(); + final String filePath = path.substring(2); + final String allyKey = path.substring(0, 2); + final Splat locations = entry.getValue(); + final SplatModel model = new SplatModel(Gdx.gl30, (Texture) load(filePath, PathSolver.DEFAULT, null), + locations.locations, this.terrain.centerOffset, locations.unitMapping, true, false, true); + switch (allyKey) { + case "e:": + model.color[0] = this.selectionCircleColorEnemy.r; + model.color[1] = this.selectionCircleColorEnemy.g; + model.color[2] = this.selectionCircleColorEnemy.b; + model.color[3] = this.selectionCircleColorEnemy.a * 0.5f; + break; + case "f:": + model.color[0] = this.selectionCircleColorFriend.r; + model.color[1] = this.selectionCircleColorFriend.g; + model.color[2] = this.selectionCircleColorFriend.b; + model.color[3] = this.selectionCircleColorFriend.a * 0.5f; + break; + default: + model.color[0] = this.selectionCircleColorNeutral.r; + model.color[1] = this.selectionCircleColorNeutral.g; + model.color[2] = this.selectionCircleColorNeutral.b; + model.color[3] = this.selectionCircleColorNeutral.a * 0.5f; + break; + } + this.mouseHighlightSplatModelKeys.add("mouseover:" + path); + this.terrain.addSplatBatchModel("mouseover:" + path, model); + } + } + public void getClickLocation(final Vector3 out, final int screenX, final int screenY) { final float[] ray = rayHeap; mousePosHeap.set(screenX, screenY); @@ -1678,19 +1801,22 @@ public class War3MapViewer extends AbstractMdxModelViewer { final MdxComplexInstance instance = unit.getInstance(); if (instance.shown() && instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollisionSimple(gdxRayHeap, intersectionHeap)) { - if (filter.call(unit.getSimulationWidget()) && (intersectionHeap.z > this.terrain - .getGroundHeight(intersectionHeap.x, intersectionHeap.y))) { - if (((entity == null) && !unit.isIntersectedOnMeshAlways())) { - entity = unit; - } - else { - if (instance.intersectRayWithMeshSlow(gdxRayHeap, intersectionHeap) - && (intersectionHeap.z > this.terrain.getGroundHeight(intersectionHeap.x, - intersectionHeap.y))) { - this.worldScene.camera.worldToCamera(intersectionHeap, intersectionHeap); - if ((entity == null) || (intersectionHeap.z > intersectionHeap2.z)) { - entity = unit; - intersectionHeap2.set(intersectionHeap); + if (filter.call(unit.getSimulationWidget())) { + final float groundHeight = this.terrain.getGroundHeight(intersectionHeap.x, intersectionHeap.y); + if (intersectionHeap.z > groundHeight) { + if (((entity == null) && !unit.isIntersectedOnMeshAlways())) { + entity = unit; + } + else { + if (instance.intersectRayWithMeshSlow(gdxRayHeap, intersectionHeap)) { + if (intersectionHeap.z > this.terrain.getGroundHeight(intersectionHeap.x, + intersectionHeap.y)) { + this.worldScene.camera.worldToCamera(intersectionHeap, intersectionHeap); + if ((entity == null) || (intersectionHeap.z > intersectionHeap2.z)) { + entity = unit; + intersectionHeap2.set(intersectionHeap); + } + } } } } @@ -1773,6 +1899,12 @@ public class War3MapViewer extends AbstractMdxModelViewer { public int imageWalkableZOffset; private WTS preloadedWTS; + private Color selectionCircleColorFriend; + + private Color selectionCircleColorNeutral; + + private Color selectionCircleColorEnemy; + /** * Returns a power of two size for the given target capacity. */ @@ -1921,7 +2053,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { return this.itemToRenderPeer.get(item); } - private RenderWidget getRenderPeer(final CWidget damagedDestructable) { + public RenderWidget getRenderPeer(final CWidget damagedDestructable) { RenderWidget damagedWidget = War3MapViewer.this.unitToRenderPeer.get(damagedDestructable); if (damagedWidget == null) { damagedWidget = War3MapViewer.this.destructableToRenderPeer.get(damagedDestructable); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java index 013b276..017deeb 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java @@ -1,13 +1,18 @@ package com.etheller.warsmash.viewer5.handlers.w3x.camera; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.math.Rectangle; public final class GameCameraManager extends CameraManager { + private static final CameraRates INFINITE_CAMERA_RATES = new CameraRates(Float.POSITIVE_INFINITY, + Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, + Float.POSITIVE_INFINITY); private final CameraPreset[] presets; private final CameraRates cameraRates; public final CameraPanControls cameraPanControls; private int currentPreset = 0; + private float fov; public GameCameraManager(final CameraPreset[] presets, final CameraRates cameraRates) { this.presets = presets; @@ -17,14 +22,22 @@ public final class GameCameraManager extends CameraManager { @Override public void updateCamera() { - this.quatHeap2.idt(); final CameraPreset cameraPreset = this.presets[this.currentPreset]; + final CameraRates cameraRate = this.cameraRates; + updateCamera(cameraPreset, cameraRate); + } + + private void updateCamera(final CameraPreset cameraPreset, final CameraRates cameraRate) { + this.quatHeap2.idt(); this.quatHeap.idt(); - this.horizontalAngle = (float) Math.toRadians( - cameraPreset.getRotation(this.cameraPanControls.insertDown, this.cameraPanControls.deleteDown) - 90); + this.horizontalAngle = applyAtRate(this.horizontalAngle, (float) Math.toRadians( + cameraPreset.getRotation(this.cameraPanControls.insertDown, this.cameraPanControls.deleteDown) - 90), + (float) Math.toRadians(cameraRate.rotation * 3)); this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); - this.distance = Math.max(1200, cameraPreset.getDistance()); - this.verticalAngle = (float) Math.toRadians(Math.min(335, cameraPreset.getAoa()) - 270); + this.distance = applyAtRate(this.distance, Math.max(1200, cameraPreset.getDistance()), cameraRate.distance); + this.verticalAngle = applyAtRate(this.verticalAngle, + (float) Math.toRadians(Math.min(335, cameraPreset.getAoa()) - 270), + (float) Math.toRadians(cameraRate.aoa)); this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle); this.quatHeap.mul(this.quatHeap2); @@ -33,12 +46,24 @@ public final class GameCameraManager extends CameraManager { this.position.nor(); this.position.scl(this.distance); this.position = this.position.add(this.target); - this.camera.perspective((float) Math.toRadians(cameraPreset.getFov() / 2), this.camera.getAspect(), - cameraPreset.getNearZ(), cameraPreset.getFarZ()); + this.fov = applyAtRate(this.fov, (float) Math.toRadians(cameraPreset.getFov() / 2), + (float) Math.toRadians(cameraRate.fov)); + this.camera.perspective(this.fov, this.camera.getAspect(), cameraPreset.getNearZ(), cameraPreset.getFarZ()); this.camera.moveToAndFace(this.position, this.target, this.worldUp); } + private static float applyAtRate(final float oldValue, final float newValue, float rate) { + rate *= Gdx.graphics.getDeltaTime(); + final float deltaDistance = newValue - oldValue; + if (Math.abs(deltaDistance) < rate) { + return newValue; + } + else { + return oldValue + (Math.signum(deltaDistance) * rate); + } + } + public void resize(final Rectangle viewport) { this.camera.viewport(viewport); } 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 3cdb5a7..9784c6a 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 @@ -391,11 +391,11 @@ public class Terrain { updateGroundHeights(new Rectangle(0, 0, width - 1, height - 1)); - 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); + 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); - this.uberSplatShader = webGL.createShaderProgram(W3xShaders.UberSplat.vert, W3xShaders.UberSplat.frag); + this.uberSplatShader = webGL.createShaderProgram(W3xShaders.UberSplat.vert(), W3xShaders.UberSplat.frag); // TODO collision bodies (?) @@ -1443,6 +1443,10 @@ public class Terrain { Collections.sort(this.uberSplatModelsList); } + public SplatModel getSplatModel(final String pathKey) { + return this.uberSplatModels.get(pathKey); + } + public SplatMover addUberSplat(final String path, final float x, final float y, final float z, final float scale, final boolean unshaded, final boolean noDepthTest, final boolean highPriority) { SplatModel splatModel = this.uberSplatModels.get(path); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java index f54e4e7..428328b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java @@ -10,76 +10,78 @@ public class TerrainShaders { private Cliffs() { } - public static final String vert = "#version 330 core\r\n" + // - "\r\n" + // - "in vec3 vPosition;\r\n" + // - "in vec2 vUV;\r\n" + // - "in vec3 vNormal;\r\n" + // - "in vec4 vOffset;\r\n" + // - "\r\n" + // - "uniform mat4 MVP;\r\n" + // - "\r\n" + // - "uniform sampler2D height_texture;\r\n" + // - "uniform sampler2D shadowMap;\r\n" + // - "uniform float centerOffsetX;\r\n" + // - "uniform float centerOffsetY;\r\n" + // - "uniform sampler2D lightTexture;\r\n" + // - "uniform float lightCount;\r\n" + // - "uniform float lightTextureHeight;\r\n" + // - "\r\n" + // - "out vec3 UV;\r\n" + // - "out vec3 Normal;\r\n" + // - "out vec2 pathing_map_uv;\r\n" + // - "out vec3 position;\r\n" + // - "out vec2 v_suv;\r\n" + // - "out vec3 shadeColor;\r\n" + // - "\r\n" + // - "void main() {\r\n" + // - " pathing_map_uv = (vec2(vPosition.y, -vPosition.x) / 128 + vOffset.xy) * 4;\r\n" + // - " \r\n" + // - " ivec2 size = textureSize(height_texture, 0);\r\n" + // - " ivec2 shadowSize = textureSize(shadowMap, 0);\r\n" + // - " v_suv = pathing_map_uv / shadowSize;\r\n" + // - " float value = texture(height_texture, (vOffset.xy + vec2(vPosition.y + 64, -vPosition.x + 64) / 128.0) / vec2(size)).r;\r\n" - + // - "\r\n" + // - " position = (vec3(vPosition.y, -vPosition.x, vPosition.z) + vec3(vOffset.xy, vOffset.z + value) * 128 );\r\n" - + // - " vec4 myposition = vec4(position, 1);\r\n" + // - " myposition.x += centerOffsetX;\r\n" + // - " myposition.y += centerOffsetY;\r\n" + // - " position.x /= (size.x * 128.0);\r\n" + // - " position.y /= (size.y * 128.0);\r\n" + // - " gl_Position = MVP * myposition;\r\n" + // - " UV = vec3(vUV, vOffset.a);\r\n" + // - "\r\n" + // - " ivec2 height_pos = ivec2(vOffset.xy + vec2(vPosition.y, -vPosition.x) / 128);\r\n" + // - " ivec3 off = ivec3(1, 1, 0);\r\n" + // - " float hL = texelFetch(height_texture, height_pos - off.xz, 0).r;\r\n" + // - " float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + // - " float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + // - " float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + // - " bool edgeX = (vPosition.y) == float((int(vPosition.y))/128*128);\r\n" + // - " bool edgeY = (vPosition.x) == float((int(vPosition.x))/128*128);\r\n" + // - " bool edgeZ = (vPosition.z) == float((int(vPosition.z))/128*128);\r\n" + // - " vec3 terrain_normal = vec3(vNormal.y, -vNormal.x, vNormal.z);\r\n" + // - " if(edgeX) {\r\n" + // - " terrain_normal.x = hL - hR;\r\n" + // - " }\r\n" + // - " if(edgeY) {\r\n" + // - " terrain_normal.y = hD - hU;\r\n" + // - " }\r\n" + // - " if(edgeZ) {\r\n" + // - " terrain_normal.z = 2.0;\r\n" + // - " }\r\n" + // - " terrain_normal = normalize(terrain_normal);\r\n" + // - "\r\n" + // - " Normal = terrain_normal;\r\n" + // - Shaders.lightSystem("terrain_normal", "myposition.xyz", "lightTexture", "lightTextureHeight", - "lightCount", true) - + "\r\n" + // - " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // - "}"; + public static final String vert() { + return "#version 330 core\r\n" + // + "\r\n" + // + "in vec3 vPosition;\r\n" + // + "in vec2 vUV;\r\n" + // + "in vec3 vNormal;\r\n" + // + "in vec4 vOffset;\r\n" + // + "\r\n" + // + "uniform mat4 MVP;\r\n" + // + "\r\n" + // + "uniform sampler2D height_texture;\r\n" + // + "uniform sampler2D shadowMap;\r\n" + // + "uniform float centerOffsetX;\r\n" + // + "uniform float centerOffsetY;\r\n" + // + "uniform sampler2D lightTexture;\r\n" + // + "uniform float lightCount;\r\n" + // + "uniform float lightTextureHeight;\r\n" + // + "\r\n" + // + "out vec3 UV;\r\n" + // + "out vec3 Normal;\r\n" + // + "out vec2 pathing_map_uv;\r\n" + // + "out vec3 position;\r\n" + // + "out vec2 v_suv;\r\n" + // + "out vec3 shadeColor;\r\n" + // + "\r\n" + // + "void main() {\r\n" + // + " pathing_map_uv = (vec2(vPosition.y, -vPosition.x) / 128 + vOffset.xy) * 4;\r\n" + // + " \r\n" + // + " ivec2 size = textureSize(height_texture, 0);\r\n" + // + " ivec2 shadowSize = textureSize(shadowMap, 0);\r\n" + // + " v_suv = pathing_map_uv / shadowSize;\r\n" + // + " float value = texture(height_texture, (vOffset.xy + vec2(vPosition.y + 64, -vPosition.x + 64) / 128.0) / vec2(size)).r;\r\n" + + // + "\r\n" + // + " position = (vec3(vPosition.y, -vPosition.x, vPosition.z) + vec3(vOffset.xy, vOffset.z + value) * 128 );\r\n" + + // + " vec4 myposition = vec4(position, 1);\r\n" + // + " myposition.x += centerOffsetX;\r\n" + // + " myposition.y += centerOffsetY;\r\n" + // + " position.x /= (size.x * 128.0);\r\n" + // + " position.y /= (size.y * 128.0);\r\n" + // + " gl_Position = MVP * myposition;\r\n" + // + " UV = vec3(vUV, vOffset.a);\r\n" + // + "\r\n" + // + " ivec2 height_pos = ivec2(vOffset.xy + vec2(vPosition.y, -vPosition.x) / 128);\r\n" + // + " ivec3 off = ivec3(1, 1, 0);\r\n" + // + " float hL = texelFetch(height_texture, height_pos - off.xz, 0).r;\r\n" + // + " float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + // + " float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + // + " float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + // + " bool edgeX = (vPosition.y) == float((int(vPosition.y))/128*128);\r\n" + // + " bool edgeY = (vPosition.x) == float((int(vPosition.x))/128*128);\r\n" + // + " bool edgeZ = (vPosition.z) == float((int(vPosition.z))/128*128);\r\n" + // + " vec3 terrain_normal = vec3(vNormal.y, -vNormal.x, vNormal.z);\r\n" + // + " if(edgeX) {\r\n" + // + " terrain_normal.x = hL - hR;\r\n" + // + " }\r\n" + // + " if(edgeY) {\r\n" + // + " terrain_normal.y = hD - hU;\r\n" + // + " }\r\n" + // + " if(edgeZ) {\r\n" + // + " terrain_normal.z = 2.0;\r\n" + // + " }\r\n" + // + " terrain_normal = normalize(terrain_normal);\r\n" + // + "\r\n" + // + " Normal = terrain_normal;\r\n" + // + Shaders.lightSystem("terrain_normal", "myposition.xyz", "lightTexture", "lightTextureHeight", + "lightCount", true) + + "\r\n" + // + " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // + "}"; + } public static final String frag = "#version 330 core\r\n" + // "\r\n" + // @@ -113,62 +115,65 @@ public class TerrainShaders { private Terrain() { } - public static final String vert = "#version 330 core\r\n" + // - "\r\n" + // - "in vec2 vPosition;\r\n" + // - "uniform mat4 MVP;\r\n" + // - "uniform mat4 DepthBiasMVP;\r\n" + // - "\r\n" + // - "uniform sampler2D height_texture;\r\n" + // - "uniform sampler2D height_cliff_texture;\r\n" + // - "uniform usampler2D terrain_texture_list;\r\n" + // - "uniform float centerOffsetX;\r\n" + // - "uniform float centerOffsetY;\r\n" + // - "uniform sampler2D lightTexture;\r\n" + // - "uniform float lightCount;\r\n" + // - "uniform float lightTextureHeight;\r\n" + // - "\r\n" + // - "out vec2 UV;\r\n" + // - "flat out uvec4 texture_indices;\r\n" + // - "out vec2 pathing_map_uv;\r\n" + // - "out vec3 position;\r\n" + // - "out vec3 ShadowCoord;\r\n" + // - "out vec2 v_suv;\r\n" + // - "out vec3 shadeColor;\r\n" + // - "\r\n" + // - "void main() { \r\n" + // - " ivec2 size = textureSize(terrain_texture_list, 0);\r\n" + // - " ivec2 pos = ivec2(gl_InstanceID % size.x, gl_InstanceID / size.x);\r\n" + // - "\r\n" + // - " ivec2 height_pos = ivec2(vPosition + pos);\r\n" + // - " vec4 height = texelFetch(height_cliff_texture, height_pos, 0);\r\n" + // - "\r\n" + // - " ivec3 off = ivec3(1, 1, 0);\r\n" + // - " float hL = texelFetch(height_texture, height_pos - off.xz, 0).r;\r\n" + // - " float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + // - " float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + // - " float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + // - " vec3 normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + // - "\r\n" + // - " UV = vec2(vPosition.x, 1 - vPosition.y);\r\n" + // - " texture_indices = texelFetch(terrain_texture_list, pos, 0);\r\n" + // - " pathing_map_uv = (vPosition + pos) * 4; \r\n" + // - "\r\n" + // - " // Cliff culling\r\n" + // - " vec3 positionWorld = vec3((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, height.r*128.0);\r\n" - + // - " position = positionWorld;\r\n" + // - " gl_Position = ((texture_indices.a & 32768u) == 0u) ? MVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n" - + // - " ShadowCoord = (((texture_indices.a & 32768u) == 0u) ? DepthBiasMVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0)).xyz;\r\n" - + // - " v_suv = (vPosition + pos) / size;\r\n" + // - " position.x = (position.x - centerOffsetX) / (size.x * 128.0);\r\n" + // - " position.y = (position.y - centerOffsetY) / (size.y * 128.0);\r\n" + // - Shaders.lightSystem("normal", "positionWorld", "lightTexture", "lightTextureHeight", "lightCount", true) - + "\r\n" + // - " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // - "}"; + public static final String vert() { + return "#version 330 core\r\n" + // + "\r\n" + // + "in vec2 vPosition;\r\n" + // + "uniform mat4 MVP;\r\n" + // + "uniform mat4 DepthBiasMVP;\r\n" + // + "\r\n" + // + "uniform sampler2D height_texture;\r\n" + // + "uniform sampler2D height_cliff_texture;\r\n" + // + "uniform usampler2D terrain_texture_list;\r\n" + // + "uniform float centerOffsetX;\r\n" + // + "uniform float centerOffsetY;\r\n" + // + "uniform sampler2D lightTexture;\r\n" + // + "uniform float lightCount;\r\n" + // + "uniform float lightTextureHeight;\r\n" + // + "\r\n" + // + "out vec2 UV;\r\n" + // + "flat out uvec4 texture_indices;\r\n" + // + "out vec2 pathing_map_uv;\r\n" + // + "out vec3 position;\r\n" + // + "out vec3 ShadowCoord;\r\n" + // + "out vec2 v_suv;\r\n" + // + "out vec3 shadeColor;\r\n" + // + "\r\n" + // + "void main() { \r\n" + // + " ivec2 size = textureSize(terrain_texture_list, 0);\r\n" + // + " ivec2 pos = ivec2(gl_InstanceID % size.x, gl_InstanceID / size.x);\r\n" + // + "\r\n" + // + " ivec2 height_pos = ivec2(vPosition + pos);\r\n" + // + " vec4 height = texelFetch(height_cliff_texture, height_pos, 0);\r\n" + // + "\r\n" + // + " ivec3 off = ivec3(1, 1, 0);\r\n" + // + " float hL = texelFetch(height_texture, height_pos - off.xz, 0).r;\r\n" + // + " float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + // + " float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + // + " float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + // + " vec3 normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + // + "\r\n" + // + " UV = vec2(vPosition.x, 1 - vPosition.y);\r\n" + // + " texture_indices = texelFetch(terrain_texture_list, pos, 0);\r\n" + // + " pathing_map_uv = (vPosition + pos) * 4; \r\n" + // + "\r\n" + // + " // Cliff culling\r\n" + // + " vec3 positionWorld = vec3((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, height.r*128.0);\r\n" + + // + " position = positionWorld;\r\n" + // + " gl_Position = ((texture_indices.a & 32768u) == 0u) ? MVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n" + + // + " ShadowCoord = (((texture_indices.a & 32768u) == 0u) ? DepthBiasMVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0)).xyz;\r\n" + + // + " v_suv = (vPosition + pos) / size;\r\n" + // + " position.x = (position.x - centerOffsetX) / (size.x * 128.0);\r\n" + // + " position.y = (position.y - centerOffsetY) / (size.y * 128.0);\r\n" + // + Shaders.lightSystem("normal", "positionWorld", "lightTexture", "lightTextureHeight", "lightCount", + true) + + "\r\n" + // + " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // + "}"; + } public static final String frag = "#version 330 core\r\n" + // "\r\n" + // @@ -291,68 +296,70 @@ public class TerrainShaders { private Water() { } - public static final String vert = "#version 330 core\r\n" + // - "\r\n" + // - "in vec2 vPosition;\r\n" + // - "\r\n" + // - "uniform sampler2D water_height_texture;\r\n" + // - "uniform sampler2D ground_height_texture;\r\n" + // - "uniform sampler2D water_exists_texture;\r\n" + // - "uniform float centerOffsetX;\r\n" + // - "uniform float centerOffsetY;\r\n" + // - "\r\n" + // - "uniform mat4 MVP;\r\n" + // - "uniform vec4 shallow_color_min;\r\n" + // - "uniform vec4 shallow_color_max;\r\n" + // - "uniform vec4 deep_color_min;\r\n" + // - "uniform vec4 deep_color_max;\r\n" + // - "uniform float water_offset;\r\n" + // - "uniform sampler2D lightTexture;\r\n" + // - "uniform float lightCount;\r\n" + // - "uniform float lightTextureHeight;\r\n" + // - "\r\n" + // - "out vec2 UV;\r\n" + // - "out vec4 Color;\r\n" + // - "out vec2 position;\r\n" + // - "out vec3 shadeColor;\r\n" + // - "\r\n" + // - "const float min_depth = 10.f / 128;\r\n" + // - "const float deeplevel = 64.f / 128;\r\n" + // - "const float maxdepth = 72.f / 128;\r\n" + // - "\r\n" + // - "void main() { \r\n" + // - " ivec2 size = textureSize(water_height_texture, 0) - 1;\r\n" + // - " ivec2 pos = ivec2(gl_InstanceID % size.x, gl_InstanceID / size.x);\r\n" + // - " ivec2 height_pos = ivec2(vPosition + pos);\r\n" + // - " float water_height = texelFetch(water_height_texture, height_pos, 0).r + water_offset;\r\n" + // - "\r\n" + // - " bool is_water = texelFetch(water_exists_texture, pos, 0).r > 0\r\n" + // - " || texelFetch(water_exists_texture, pos + ivec2(1, 0), 0).r > 0\r\n" + // - " || texelFetch(water_exists_texture, pos + ivec2(1, 1), 0).r > 0\r\n" + // - " || texelFetch(water_exists_texture, pos + ivec2(0, 1), 0).r > 0;\r\n" + // - "\r\n" + // - " position = vec2((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY);\r\n" - + // - " vec4 myposition = vec4(position.xy, water_height*128.0, 1);\r\n" + // - " vec3 Normal = vec3(0,0,1);\r\n" + // - " gl_Position = is_water ? MVP * myposition : vec4(2.0, 0.0, 0.0, 1.0);\r\n" + // - "\r\n" + // - " UV = vec2((vPosition.x + pos.x%2)/2.0, (vPosition.y + pos.y%2)/2.0);\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" + // - " if (value <= deeplevel) {\r\n" + // - " value = max(0.f, value - min_depth) / (deeplevel - min_depth);\r\n" + // - " Color = shallow_color_min * (1.f - value) + shallow_color_max * value;\r\n" + // - " } else {\r\n" + // - " value = clamp(value - deeplevel, 0.f, maxdepth - deeplevel) / (maxdepth - deeplevel);\r\n" + // - " Color = deep_color_min * (1.f - value) + deep_color_max * value;\r\n" + // - " }\r\n" + // - Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightTextureHeight", "lightCount", - true) - + "\r\n" + // - " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // - " }"; + public static final String vert() { + return "#version 330 core\r\n" + // + "\r\n" + // + "in vec2 vPosition;\r\n" + // + "\r\n" + // + "uniform sampler2D water_height_texture;\r\n" + // + "uniform sampler2D ground_height_texture;\r\n" + // + "uniform sampler2D water_exists_texture;\r\n" + // + "uniform float centerOffsetX;\r\n" + // + "uniform float centerOffsetY;\r\n" + // + "\r\n" + // + "uniform mat4 MVP;\r\n" + // + "uniform vec4 shallow_color_min;\r\n" + // + "uniform vec4 shallow_color_max;\r\n" + // + "uniform vec4 deep_color_min;\r\n" + // + "uniform vec4 deep_color_max;\r\n" + // + "uniform float water_offset;\r\n" + // + "uniform sampler2D lightTexture;\r\n" + // + "uniform float lightCount;\r\n" + // + "uniform float lightTextureHeight;\r\n" + // + "\r\n" + // + "out vec2 UV;\r\n" + // + "out vec4 Color;\r\n" + // + "out vec2 position;\r\n" + // + "out vec3 shadeColor;\r\n" + // + "\r\n" + // + "const float min_depth = 10.f / 128;\r\n" + // + "const float deeplevel = 64.f / 128;\r\n" + // + "const float maxdepth = 72.f / 128;\r\n" + // + "\r\n" + // + "void main() { \r\n" + // + " ivec2 size = textureSize(water_height_texture, 0) - 1;\r\n" + // + " ivec2 pos = ivec2(gl_InstanceID % size.x, gl_InstanceID / size.x);\r\n" + // + " ivec2 height_pos = ivec2(vPosition + pos);\r\n" + // + " float water_height = texelFetch(water_height_texture, height_pos, 0).r + water_offset;\r\n" + // + "\r\n" + // + " bool is_water = texelFetch(water_exists_texture, pos, 0).r > 0\r\n" + // + " || texelFetch(water_exists_texture, pos + ivec2(1, 0), 0).r > 0\r\n" + // + " || texelFetch(water_exists_texture, pos + ivec2(1, 1), 0).r > 0\r\n" + // + " || texelFetch(water_exists_texture, pos + ivec2(0, 1), 0).r > 0;\r\n" + // + "\r\n" + // + " position = vec2((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY);\r\n" + + // + " vec4 myposition = vec4(position.xy, water_height*128.0, 1);\r\n" + // + " vec3 Normal = vec3(0,0,1);\r\n" + // + " gl_Position = is_water ? MVP * myposition : vec4(2.0, 0.0, 0.0, 1.0);\r\n" + // + "\r\n" + // + " UV = vec2((vPosition.x + pos.x%2)/2.0, (vPosition.y + pos.y%2)/2.0);\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" + // + " if (value <= deeplevel) {\r\n" + // + " value = max(0.f, value - min_depth) / (deeplevel - min_depth);\r\n" + // + " Color = shallow_color_min * (1.f - value) + shallow_color_max * value;\r\n" + // + " } else {\r\n" + // + " value = clamp(value - deeplevel, 0.f, maxdepth - deeplevel) / (maxdepth - deeplevel);\r\n" + // + " Color = deep_color_min * (1.f - value) + deep_color_max * value;\r\n" + // + " }\r\n" + // + Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightTextureHeight", "lightCount", + true) + + "\r\n" + // + " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // + " }"; + } public static final String frag = "#version 330 core\r\n" + // "\r\n" + // diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderDestructable.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderDestructable.java index 6da29d4..2c8ea2d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderDestructable.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderDestructable.java @@ -24,9 +24,11 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget { public Rectangle walkableBounds; private final CDestructable simulationDestructable; private SplatMover selectionCircle; + private SplatMover selectionPreviewHighlight; private final UnitAnimationListenerImpl unitAnimationListenerImpl; private boolean dead; private BuildingShadow destructableShadow; + private final boolean selectable; public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type, @@ -50,6 +52,7 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget { this.unitAnimationListenerImpl = new UnitAnimationListenerImpl((MdxComplexInstance) this.instance); simulationDestructable.setUnitAnimationListener(this.unitAnimationListenerImpl); this.unitAnimationListenerImpl.playAnimation(true, getAnimation(), SequenceUtils.EMPTY, 1.0f, true); + this.selectable = row.readSLKTagBoolean("selectable"); } @Override @@ -86,6 +89,10 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget { this.selectionCircle.destroy(Gdx.gl30, war3MapViewer.terrain.centerOffset); this.selectionCircle = null; } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.destroy(Gdx.gl30, war3MapViewer.terrain.centerOffset); + this.selectionPreviewHighlight = null; + } } else if (!dead) { if (this.dead) { @@ -140,6 +147,25 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget { @Override public void assignSelectionCircle(final SplatMover selectionCircle) { this.selectionCircle = selectionCircle; + } + @Override + public void unassignSelectionPreviewHighlight() { + this.selectionPreviewHighlight = null; + } + + @Override + public void assignSelectionPreviewHighlight(final SplatMover t) { + this.selectionPreviewHighlight = t; + } + + @Override + public boolean isSelectable() { + return this.selectable; + } + + @Override + public SplatMover getSelectionPreviewHighlight() { + return this.selectionPreviewHighlight; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java index 2dc82bd..e197362 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java @@ -27,6 +27,7 @@ public class RenderItem implements RenderWidget { public final MdxModel portraitModel; public SplatMover shadow; public SplatMover selectionCircle; + public SplatMover selectionPreviewHighlight; private boolean hidden; private boolean dead; @@ -176,4 +177,24 @@ public class RenderItem implements RenderWidget { public void assignSelectionCircle(final SplatMover t) { this.selectionCircle = t; } + + @Override + public void unassignSelectionPreviewHighlight() { + this.selectionPreviewHighlight = null; + } + + @Override + public void assignSelectionPreviewHighlight(final SplatMover t) { + this.selectionPreviewHighlight = t; + } + + @Override + public boolean isSelectable() { + return true; + } + + @Override + public SplatMover getSelectionPreviewHighlight() { + return this.selectionPreviewHighlight; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java index 346ee7a..b8a8a1c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java @@ -52,6 +52,7 @@ public class RenderUnit implements RenderWidget { public SplatMover shadow; private BuildingShadow buildingShadowInstance; public SplatMover selectionCircle; + public SplatMover selectionPreviewHighlight; private float facing; @@ -161,6 +162,9 @@ public class RenderUnit implements RenderWidget { if (this.selectionCircle != null) { this.selectionCircle.hide(); } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.hide(); + } if (this.shadow != null) { this.shadow.hide(); } @@ -174,6 +178,9 @@ public class RenderUnit implements RenderWidget { if (this.selectionCircle != null) { this.selectionCircle.show(map.terrain.centerOffset); } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.show(map.terrain.centerOffset); + } if (this.shadow != null) { this.shadow.show(map.terrain.centerOffset); } @@ -276,6 +283,10 @@ public class RenderUnit implements RenderWidget { this.selectionCircle.destroy(Gdx.gl30, map.terrain.centerOffset); this.selectionCircle = null; } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.destroy(Gdx.gl30, map.terrain.centerOffset); + this.selectionPreviewHighlight = null; + } } if (boneCorpse && !this.boneCorpse) { this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE, @@ -404,6 +415,13 @@ public class RenderUnit implements RenderWidget { || ((movementType == MovementType.FLY) || (movementType == MovementType.HOVER)), selectionCircleHeight + map.imageWalkableZOffset); } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.move(dx, dy, map.terrain.centerOffset); + this.selectionPreviewHighlight.setHeightAbsolute( + (currentWalkableUnder != null) + || ((movementType == MovementType.FLY) || (movementType == MovementType.HOVER)), + selectionCircleHeight + map.imageWalkableZOffset); + } this.unitAnimationListenerImpl.update(); if (!dead && this.simulationUnit.isConstructing()) { this.instance.setFrameByRatio( @@ -447,6 +465,9 @@ public class RenderUnit implements RenderWidget { if (this.selectionCircle != null) { this.selectionCircle.move(dx, dy, map.terrain.centerOffset); } + if (this.selectionPreviewHighlight != null) { + this.selectionPreviewHighlight.move(dx, dy, map.terrain.centerOffset); + } this.location[0] = this.simulationUnit.getX(); this.location[1] = this.simulationUnit.getY(); } @@ -463,7 +484,7 @@ public class RenderUnit implements RenderWidget { @Override public boolean isIntersectedOnMeshAlways() { - return this.simulationUnit.getUnitType().isBuilding(); + return this.simulationUnit.isBuilding(); } @Override @@ -495,4 +516,24 @@ public class RenderUnit implements RenderWidget { public void assignSelectionCircle(final SplatMover t) { this.selectionCircle = t; } + + @Override + public void unassignSelectionPreviewHighlight() { + this.selectionPreviewHighlight = null; + } + + @Override + public void assignSelectionPreviewHighlight(final SplatMover t) { + this.selectionPreviewHighlight = t; + } + + @Override + public boolean isSelectable() { + return true; // later needs locust + } + + @Override + public SplatMover getSelectionPreviewHighlight() { + return this.selectionPreviewHighlight; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java index 3eeabd0..f1c6cd8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java @@ -37,6 +37,12 @@ public interface RenderWidget { void assignSelectionCircle(SplatMover t); + void unassignSelectionPreviewHighlight(); + + void assignSelectionPreviewHighlight(SplatMover t); + + boolean isSelectable(); + public static final class UnitAnimationListenerImpl implements CUnitAnimationListener { private final MdxComplexInstance instance; protected final EnumSet secondaryAnimationTags = EnumSet @@ -159,4 +165,6 @@ public interface RenderWidget { this.allowRarityVariations = allowRarityVariations; } } + + SplatMover getSelectionPreviewHighlight(); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CDestructable.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CDestructable.java index d5f1e8f..d9cd576 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CDestructable.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CDestructable.java @@ -14,6 +14,7 @@ public class CDestructable extends CWidget { private final RemovablePathingMapInstance pathingInstance; private final RemovablePathingMapInstance pathingInstanceDeath; private UnitAnimationListenerImpl unitAnimationListenerImpl; + private boolean invulnerable; public CDestructable(final int handleId, final float x, final float y, final float life, final CDestructableType destTypeInstance, final RemovablePathingMapInstance pathingInstance, @@ -80,4 +81,18 @@ public class CDestructable extends CWidget { public void setUnitAnimationListener(final UnitAnimationListenerImpl unitAnimationListenerImpl) { this.unitAnimationListenerImpl = unitAnimationListenerImpl; } + + @Override + public float getMaxLife() { + return this.destType.getLife(); + } + + public void setInvulnerable(final boolean invulnerable) { + this.invulnerable = invulnerable; + } + + @Override + public boolean isInvulnerable() { + return this.invulnerable; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java index 2548e4d..f07335d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java @@ -73,6 +73,11 @@ public class CItem extends CWidget { return this.hidden; } + @Override + public float getMaxLife() { + return this.itemType.getHitPoints(); + } + public void setPointAndCheckUnstuck(final float newX, final float newY, final CSimulation game) { final CWorldCollision collision = game.getWorldCollision(); final PathingGrid pathingGrid = game.getPathingGrid(); @@ -100,4 +105,9 @@ public class CItem extends CWidget { setY(outputY); } + @Override + public boolean isInvulnerable() { + return false; + } + } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java index 48fd227..ccc67b3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java @@ -201,14 +201,14 @@ public class CUnit extends CWidget { return this.mana; } - public int getMaximumLife() { - return this.maximumLife; - } - public int getMaximumMana() { return this.maximumMana; } + public int getMaximumLife() { + return this.maximumLife; + } + public void setTypeId(final War3ID typeId) { this.typeId = typeId; } @@ -430,7 +430,7 @@ public class CUnit extends CWidget { } public float getEndingDecayTime(final CSimulation game) { - if (this.unitType.isBuilding()) { + if (this.isBuilding()) { return game.getGameplayConstants().getStructureDecayTime(); } return game.getGameplayConstants().getBoneDecayTime(); @@ -630,7 +630,7 @@ public class CUnit extends CWidget { public void setX(final float newX, final CWorldCollision collision, final CRegionManager regionManager) { final float prevX = getX(); - if (!this.unitType.isBuilding()) { + if (!this.isBuilding()) { setX(newX); collision.translate(this, newX - prevX, 0); } @@ -639,7 +639,7 @@ public class CUnit extends CWidget { public void setY(final float newY, final CWorldCollision collision, final CRegionManager regionManager) { final float prevY = getY(); - if (!this.unitType.isBuilding()) { + if (!this.isBuilding()) { setY(newY); collision.translate(this, 0, newY - prevY); } @@ -692,7 +692,7 @@ public class CUnit extends CWidget { final float prevY = getY(); setX(newX); setY(newY); - if (!this.unitType.isBuilding()) { + if (!this.isBuilding()) { collision.translate(this, newX - prevX, newY - prevY); } checkRegionEvents(regionManager); @@ -867,7 +867,7 @@ public class CUnit extends CWidget { final CPlayer sourcePlayer = simulation.getPlayer(source.getPlayerIndex()); if (!sourcePlayer.hasAlliance(this.playerIndex, CAllianceType.PASSIVE)) { final CGameplayConstants gameplayConstants = simulation.getGameplayConstants(); - if (gameplayConstants.isBuildingKillsGiveExp() || !source.getUnitType().isBuilding()) { + if (gameplayConstants.isBuildingKillsGiveExp() || !source.isBuilding()) { final CUnit killedUnit = this; final CAbilityHero killedUnitHeroData = getHeroData(); final boolean killedUnitIsAHero = killedUnitHeroData != null; @@ -917,7 +917,7 @@ public class CUnit extends CWidget { if (target instanceof CUnit) { final CUnit targetUnit = (CUnit) target; final CUnitType targetUnitType = targetUnit.getUnitType(); - if (targetUnitType.isBuilding() && (targetUnitType.getBuildingPathingPixelMap() != null)) { + if (targetUnit.isBuilding() && (targetUnitType.getBuildingPathingPixelMap() != null)) { final BufferedImage buildingPathingPixelMap = targetUnitType.getBuildingPathingPixelMap(); final float targetX = target.getX(); final float targetY = target.getY(); @@ -1061,7 +1061,7 @@ public class CUnit extends CWidget { } public boolean isMovementDisabled() { - return this.unitType.isBuilding(); + return this.isBuilding(); } public float getAcquisitionRange() { @@ -1205,6 +1205,7 @@ public class CUnit extends CWidget { this.invulnerable = invulnerable; } + @Override public boolean isInvulnerable() { return this.invulnerable; } @@ -1537,6 +1538,11 @@ public class CUnit extends CWidget { return this.containingRegions.contains(region); } + @Override + public float getMaxLife() { + return this.maximumLife; + } + private static final class RegionCheckerImpl implements CRegionEnumFunction { private CUnit unit; @@ -1556,4 +1562,8 @@ public class CUnit extends CWidget { } } + + public boolean isBuilding() { + return this.unitType.isBuilding(); + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java index 50b27f9..01744a1 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java @@ -74,6 +74,7 @@ public class CUnitType { private final List heroProperNames; private final int properNamesCount; private final boolean canFlee; + private final int priority; public CUnitType(final String name, final String legacyName, final War3ID typeId, final int life, final int manaInitial, final int manaMaximum, final int speed, final int defense, final String abilityList, @@ -91,7 +92,7 @@ public class CUnitType { final float strengthPerLevel, final int agility, final float agilityPerLevel, final int intelligence, final float intelligencePerLevel, final CPrimaryAttribute primaryAttribute, final List heroAbilityList, final List heroProperNames, final int properNamesCount, - final boolean canFlee) { + final boolean canFlee, final int priority) { this.name = name; this.legacyName = legacyName; this.typeId = typeId; @@ -144,6 +145,7 @@ public class CUnitType { this.heroProperNames = heroProperNames; this.properNamesCount = properNamesCount; this.canFlee = canFlee; + this.priority = priority; } public String getName() { @@ -353,4 +355,8 @@ public class CUnitType { public boolean isCanFlee() { return this.canFlee; } + + public int getPriority() { + return this.priority; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java index 1ae17c3..428d9c6 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java @@ -39,6 +39,8 @@ public abstract class CWidget implements AbilityTarget { return this.life; } + public abstract float getMaxLife(); + protected void setX(final float x) { this.x = x; } @@ -70,4 +72,6 @@ public abstract class CWidget implements AbilityTarget { final double dy = Math.abs(target.getY() - getY()); return (dx * dx) + (dy * dy); } + + public abstract boolean isInvulnerable(); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java index 8f6600d..9cc9a24 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java @@ -37,7 +37,7 @@ public class CWorldCollision { collisionSize * 2); unit.setCollisionRectangle(bounds); } - if (unit.getUnitType().isBuilding()) { + if (unit.isBuilding()) { // buildings are here so that we can include them when enumerating all units in // a rect, but they don't really move dynamically, this is kind of pointless this.buildingUnitCollision.add(unit, bounds); @@ -73,7 +73,7 @@ public class CWorldCollision { public void removeUnit(final CUnit unit) { final Rectangle bounds = unit.getCollisionRectangle(); if (bounds != null) { - if (unit.getUnitType().isBuilding()) { + if (unit.isBuilding()) { this.buildingUnitCollision.remove(unit, bounds); } else { @@ -154,7 +154,7 @@ public class CWorldCollision { } public void translate(final CUnit unit, final float xShift, final float yShift) { - if (unit.getUnitType().isBuilding()) { + if (unit.isBuilding()) { throw new IllegalArgumentException("Cannot add building to the CWorldCollision"); } final MovementType movementType = unit.getUnitType().getMovementType(); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityInvulnerable.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityInvulnerable.java new file mode 100644 index 0000000..4d14ecd --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityInvulnerable.java @@ -0,0 +1,77 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.combat; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.AbstractGenericNoIconAbility; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; + +public class CAbilityInvulnerable extends AbstractGenericNoIconAbility { + + public CAbilityInvulnerable(final int handleId, final War3ID alias) { + super(handleId, alias); + } + + @Override + public void onAdd(final CSimulation game, final CUnit unit) { + unit.setInvulnerable(true); + } + + @Override + public void onRemove(final CSimulation game, final CUnit unit) { + unit.setInvulnerable(false); + } + + @Override + public void onTick(final CSimulation game, final CUnit unit) { + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { + return null; + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, + final AbilityPointTarget point) { + return null; + } + + @Override + public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) { + return null; + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, + final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityPointTarget target, final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + protected void innerCheckCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { + receiver.notAnActiveAbility(); + } + + @Override + public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) { + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java index 28e2e40..d7200e3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java @@ -78,7 +78,8 @@ public class CAbilityHero extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, + final AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/definitions/impl/CAbilityTypeDefinitionInvulnerable.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/definitions/impl/CAbilityTypeDefinitionInvulnerable.java new file mode 100644 index 0000000..2bbf746 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/definitions/impl/CAbilityTypeDefinitionInvulnerable.java @@ -0,0 +1,30 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl; + +import java.util.EnumSet; +import java.util.List; + +import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityTypeLevelData; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.CAbilityTypeDefinition; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.impl.CAbilityTypeInvulnerable; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; + +public class CAbilityTypeDefinitionInvulnerable extends AbstractCAbilityTypeDefinition + implements CAbilityTypeDefinition { + + @Override + protected CAbilityTypeLevelData createLevelData(final MutableGameObject abilityEditorData, final int level) { + final String targetsAllowedAtLevelString = abilityEditorData.getFieldAsString(TARGETS_ALLOWED, level); + final EnumSet targetsAllowedAtLevel = CTargetType.parseTargetTypeSet(targetsAllowedAtLevelString); + return new CAbilityTypeLevelData(targetsAllowedAtLevel); + } + + @Override + protected CAbilityType innerCreateAbilityType(final War3ID alias, final MutableGameObject abilityEditorData, + final List levelData) { + return new CAbilityTypeInvulnerable(alias, abilityEditorData.getCode(), levelData); + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInvulnerable.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInvulnerable.java new file mode 100644 index 0000000..b6cbdb8 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInvulnerable.java @@ -0,0 +1,24 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.impl; + +import java.util.List; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.combat.CAbilityInvulnerable; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityTypeLevelData; + +public class CAbilityTypeInvulnerable extends CAbilityType { + + public CAbilityTypeInvulnerable(final War3ID alias, final War3ID code, + final List levelData) { + super(alias, code, levelData); + } + + @Override + public CAbility createAbility(final int handleId) { + final CAbilityTypeLevelData levelData = getLevelData(0); + return new CAbilityInvulnerable(handleId, getAlias()); + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/combat/attacks/CUnitAttackMissileSplash.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/combat/attacks/CUnitAttackMissileSplash.java index 525efaf..b292ac1 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/combat/attacks/CUnitAttackMissileSplash.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/combat/attacks/CUnitAttackMissileSplash.java @@ -108,8 +108,11 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile { final CUnitAttackListener attackListener) { SplashDamageConsumer.INSTANCE.doDamage(cSimulation, source, target, this, x, y, damage, attackListener); if ((getWeaponType() != CWeaponType.ARTILLERY) && !SplashDamageConsumer.INSTANCE.hitTarget) { - super.doDamage(cSimulation, source, target, damage * this.damageFactorSmall, x, y, bounceIndex, - attackListener); + float originalTargetDamage = damage; + if (Math.abs(this.damageFactorSmall) > 0.0001) { + originalTargetDamage *= this.damageFactorSmall; + } + super.doDamage(cSimulation, source, target, originalTargetDamage, x, y, bounceIndex, attackListener); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java index efaa590..1cded16 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java @@ -15,6 +15,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.def import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionGoldMine; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionHarvest; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionInventory; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionInvulnerable; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionReturnResources; public class CAbilityData { @@ -36,6 +37,7 @@ public class CAbilityData { this.codeToAbilityTypeDefinition.put(War3ID.fromString("Ahar"), new CAbilityTypeDefinitionHarvest()); this.codeToAbilityTypeDefinition.put(War3ID.fromString("ANcl"), new CAbilityTypeDefinitionChannelTest()); this.codeToAbilityTypeDefinition.put(War3ID.fromString("AInv"), new CAbilityTypeDefinitionInventory()); + this.codeToAbilityTypeDefinition.put(War3ID.fromString("Avul"), new CAbilityTypeDefinitionInvulnerable()); } public CAbilityType getAbilityType(final War3ID alias) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java index 7d4f581..e558641 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java @@ -168,6 +168,7 @@ public class CUnitData { private static final War3ID PRIMARY_ATTRIBUTE = War3ID.fromString("upra"); private static final War3ID CAN_FLEE = War3ID.fromString("ufle"); + private static final War3ID PRIORITY = War3ID.fromString("upri"); private final CGameplayConstants gameplayConstants; private final MutableObjectData unitData; @@ -275,6 +276,7 @@ public class CUnitData { final String abilityList = unitType.getFieldAsString(ABILITIES_NORMAL, 0); final String heroAbilityListString = unitType.getFieldAsString(ABILITIES_HERO, 0); final int unitLevel = unitType.getFieldAsInteger(UNIT_LEVEL, 0); + final int priority = unitType.getFieldAsInteger(PRIORITY, 0); final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0); final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0); @@ -536,7 +538,7 @@ public class CUnitData { goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes, propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus, intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames, properNamesCount, - canFlee); + canFlee, priority); this.unitIdToUnitType.put(typeId, unitTypeInstance); this.jassLegacyNameToUnitId.put(legacyName, typeId); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/pathing/CPathfindingProcessor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/pathing/CPathfindingProcessor.java index baf97bd..0a54c39 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/pathing/CPathfindingProcessor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/pathing/CPathfindingProcessor.java @@ -429,7 +429,10 @@ public class CPathfindingProcessor { } workIterations++; this.totalIterations++; - if (workIterations >= 7500) { + if (this.totalIterations > 20000) { + break; + } + if (workIterations >= 500) { // breaking jobs loop will implicitly exit without calling pathFound() below break JobsLoop; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java index 4ff32a4..7afae22 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java @@ -5,10 +5,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.EnumSet; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Queue; +import java.util.Set; import java.util.concurrent.TimeUnit; import com.badlogic.gdx.Gdx; @@ -23,9 +26,12 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; import com.badlogic.gdx.graphics.glutils.PixmapTextureData; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.math.collision.BoundingBox; import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.viewport.ExtendViewport; import com.etheller.warsmash.datasources.DataSource; @@ -53,6 +59,7 @@ import com.etheller.warsmash.util.ImageUtils; import com.etheller.warsmash.util.RenderMathUtils; import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.util.WarsmashConstants; +import com.etheller.warsmash.viewer5.Bounds; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.ViewerTextureRenderable; import com.etheller.warsmash.viewer5.handlers.mdx.Attachment; @@ -91,6 +98,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListene import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit.QueueItemType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitEnumFunction; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; @@ -245,6 +253,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private long lastErrorMessageExpireTime; private long lastErrorMessageFadeTime; + private MenuCursorState cursorState; private CAbilityView activeCommand; private int activeCommandOrderId; private RenderUnit activeCommandUnit; @@ -278,6 +287,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private MdxModel waypointModel; private final List waypointModelInstances = new ArrayList<>(); private List selectedUnits; + private Set dragSelectPreviewUnits = new HashSet<>(); + private Set dragSelectPreviewUnitsUpcoming = new HashSet<>(); private BitmapFont textTagFont; private SetPoint uberTipNoResourcesSetPoint; private SetPoint uberTipWithResourcesSetPoint; @@ -296,6 +307,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private SimpleButtonFrame chatButton; private final Runnable exitGameRunnable; private SimpleFrame smashEscMenu; + private RenderWidget mouseOverUnit; + private final Vector3 lastMouseDragStart = new Vector3(); + private final Vector3 lastMouseClickLocation = new Vector3(); + + private final List hpBarFrames = new ArrayList<>(); + private int hpBarFrameIndex = 0; + private boolean allowDrag; + private int currentlyDraggingPointer; + private final ShapeRenderer shapeRenderer = new ShapeRenderer(); public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets, final CameraRates cameraRates, @@ -939,13 +959,13 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma @Override public void onClick(final int abilityHandleId, final int orderId, final boolean rightClick) { - // TODO not O(N) if (this.selectedUnit == null) { return; } if (orderId == 0) { return; } + // TODO not O(N) CAbilityView abilityToUse = null; for (final CAbility ability : this.selectedUnit.getSimulationUnit().getAbilities()) { if (ability.getHandleId() == abilityHandleId) { @@ -1050,6 +1070,53 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma if (Gdx.input.isCursorCatched()) { Gdx.input.setCursorPosition(mouseX, mouseY); } + this.hpBarFrameIndex = 0; + if (this.currentlyDraggingPointer == -1) { + if ((this.mouseOverUnit != null) && !this.mouseOverUnit.getSimulationWidget().isInvulnerable()) { + final SimpleStatusBarFrame simpleStatusBarFrame = getHpBar(); + positionHealthBar(simpleStatusBarFrame, this.mouseOverUnit, 1.0f); + } + } + else if (this.currentlyDraggingPointer == Input.Buttons.LEFT) { + final float minDragX = Math.min(this.lastMouseClickLocation.x, this.lastMouseDragStart.x); + final float minDragY = Math.min(this.lastMouseClickLocation.y, this.lastMouseDragStart.y); + final float maxDragX = Math.max(this.lastMouseClickLocation.x, this.lastMouseDragStart.x); + final float maxDragY = Math.max(this.lastMouseClickLocation.y, this.lastMouseDragStart.y); + this.tempRect.set(minDragX, minDragY, maxDragX - minDragX, maxDragY - minDragY); + this.dragSelectPreviewUnitsUpcoming.clear(); + this.war3MapViewer.simulation.getWorldCollision().enumUnitsInRect(this.tempRect, new CUnitEnumFunction() { + @Override + public boolean call(final CUnit unit) { + final RenderUnit renderUnit = MeleeUI.this.war3MapViewer.getRenderPeer(unit); + if (!unit.isInvulnerable() && !unit.isDead() && renderUnit.isSelectable() + && MeleeUI.this.dragSelectPreviewUnitsUpcoming.add(renderUnit)) { + final SimpleStatusBarFrame simpleStatusBarFrame = getHpBar(); + positionHealthBar(simpleStatusBarFrame, renderUnit, 1.0f); + if (!MeleeUI.this.dragSelectPreviewUnits.contains(renderUnit)) { + MeleeUI.this.war3MapViewer.showUnitMouseOverHighlight(renderUnit); + } + } + return false; + } + }); + for (final RenderUnit unit : this.dragSelectPreviewUnits) { + if (!this.dragSelectPreviewUnitsUpcoming.contains(unit)) { + this.war3MapViewer.clearUnitMouseOverHighlight(unit); + } + } + final Set temp = this.dragSelectPreviewUnits; + this.dragSelectPreviewUnits = this.dragSelectPreviewUnitsUpcoming; + this.dragSelectPreviewUnitsUpcoming = temp; + } + if ((this.selectedUnits != null) && false) { + for (final RenderUnit unit : this.selectedUnits) { + final SimpleStatusBarFrame simpleStatusBarFrame = getHpBar(); + positionHealthBar(simpleStatusBarFrame, unit, 1.0f); + } + } + for (int i = this.hpBarFrameIndex; i < this.hpBarFrames.size(); i++) { + this.hpBarFrames.get(i).setVisible(false); + } screenCoordsVector.set(mouseX, mouseY); this.uiViewport.unproject(screenCoordsVector); @@ -1058,9 +1125,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma if (this.activeCommand != null) { if (this.draggingItem != null) { - this.cursorFrame.setSequence("HoldItem"); + setCursorState(MenuCursorState.HOLD_ITEM); } else { + setCursorState(MenuCursorState.TARGET_CURSOR); this.activeCommand.visit(this.cursorTargetSetupVisitor.reset(baseMouseX, baseMouseY)); } } @@ -1081,34 +1149,34 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } if (down) { if (left) { - this.cursorFrame.setSequence("Scroll Down Left"); + setCursorState(MenuCursorState.SCROLL_DOWN_LEFT); } else if (right) { - this.cursorFrame.setSequence("Scroll Down Right"); + setCursorState(MenuCursorState.SCROLL_DOWN_RIGHT); } else { - this.cursorFrame.setSequence("Scroll Down"); + setCursorState(MenuCursorState.SCROLL_DOWN); } } else if (up) { if (left) { - this.cursorFrame.setSequence("Scroll Up Left"); + setCursorState(MenuCursorState.SCROLL_UP_LEFT); } else if (right) { - this.cursorFrame.setSequence("Scroll Up Right"); + setCursorState(MenuCursorState.SCROLL_UP_RIGHT); } else { - this.cursorFrame.setSequence("Scroll Up"); + setCursorState(MenuCursorState.SCROLL_UP); } } else if (left) { - this.cursorFrame.setSequence("Scroll Left"); + setCursorState(MenuCursorState.SCROLL_LEFT); } else if (right) { - this.cursorFrame.setSequence("Scroll Right"); + setCursorState(MenuCursorState.SCROLL_RIGHT); } else { - this.cursorFrame.setSequence("Normal"); + setCursorState(MenuCursorState.NORMAL); } } if (this.selectedUnit != null) { @@ -1142,6 +1210,59 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } } + private void positionHealthBar(final SimpleStatusBarFrame simpleStatusBarFrame, final RenderWidget unit, + final float alpha) { + simpleStatusBarFrame.setVisible(true); + clickLocationTemp.x = unit.getX(); + clickLocationTemp.y = unit.getY(); + clickLocationTemp.z = unit.getZ(); + final Bounds unitBounds = unit.getInstance().getBounds(); + if (unitBounds != null) { + final BoundingBox unitBoundsBox = unitBounds.getBoundingBox(); + if (unitBoundsBox != null) { + clickLocationTemp.z += unitBoundsBox.max.z; + } + } + this.war3MapViewer.worldScene.camera.worldToScreen(screenCoordsVector, clickLocationTemp); + simpleStatusBarFrame.getBarFrame().setTexture("SimpleHpBarConsole", this.rootFrame); + simpleStatusBarFrame.getBorderFrame().setTexture("Textures\\Black32.blp", this.rootFrame); + simpleStatusBarFrame.getBorderFrame().setColor(0f, 0f, 0f, alpha); + final float lifeRatioRemaining = unit.getSimulationWidget().getLife() / unit.getSimulationWidget().getMaxLife(); + simpleStatusBarFrame.getBarFrame().setColor(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)), + Math.min(1.0f, lifeRatioRemaining * 2), 0, alpha); + final Vector2 unprojected = this.uiViewport.unproject(screenCoordsVector); + simpleStatusBarFrame.setWidth(unit.getSelectionScale() * 1.5f); + simpleStatusBarFrame.setHeight(16); + simpleStatusBarFrame.addSetPoint( + new SetPoint(FramePoint.CENTER, this.rootFrame, FramePoint.BOTTOMLEFT, unprojected.x, unprojected.y)); + simpleStatusBarFrame.setValue(lifeRatioRemaining); + simpleStatusBarFrame.positionBounds(this.rootFrame, this.uiViewport); + } + + private SimpleStatusBarFrame getHpBar() { + final SimpleStatusBarFrame simpleStatusBarFrame; + if (this.hpBarFrameIndex >= this.hpBarFrames.size()) { + simpleStatusBarFrame = new SimpleStatusBarFrame("SmashHpBar" + this.hpBarFrameIndex, this.rootFrame, true, + true, 3.0f); + this.rootFrame.add(simpleStatusBarFrame); + this.hpBarFrames.add(simpleStatusBarFrame); + } + else { + simpleStatusBarFrame = this.hpBarFrames.get(this.hpBarFrameIndex); + } + this.hpBarFrameIndex++; + return simpleStatusBarFrame; + } + + private void setCursorState(final MenuCursorState state) { + if (state != this.cursorState) { + if (state.getAnimationName() != null) { + this.cursorFrame.setSequence(state.getAnimationName()); + } + } + this.cursorState = state; + } + public void render(final SpriteBatch batch, final GlyphLayout glyphLayout) { final BitmapFont font = this.rootFrame.getFont(); font.setColor(Color.YELLOW); @@ -1172,6 +1293,26 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma (unprojected.y - (glyphLayout.height / 2)) + textTag.getScreenCoordsZHeight()); } } + if (this.currentlyDraggingPointer == Input.Buttons.LEFT) { + batch.end(); + this.shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + this.shapeRenderer.setColor(Color.GREEN); + Gdx.gl.glLineWidth(2); + this.shapeRenderer.begin(ShapeType.Line); + this.cameraManager.camera.worldToScreen(screenCoordsVector, this.lastMouseDragStart); + final Vector2 unprojected = this.uiViewport.unproject(screenCoordsVector); + final float x = unprojected.x; + final float y = unprojected.y; + this.cameraManager.camera.worldToScreen(screenCoordsVector, this.lastMouseClickLocation); + final Vector2 unprojectedEnd = this.uiViewport.unproject(screenCoordsVector); + final float minX = Math.min(x, unprojectedEnd.x); + final float minY = Math.min(y, unprojectedEnd.y); + this.shapeRenderer.rect(minX, minY, Math.max(x, unprojectedEnd.x) - minX, + Math.max(y, unprojectedEnd.y) - minY); + this.shapeRenderer.end(); + Gdx.gl.glLineWidth(1); + batch.begin(); + } } public void portraitTalk() { @@ -1783,8 +1924,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private void reloadSelectedUnitUI(final RenderUnit unit) { final CUnit simulationUnit = unit.getSimulationUnit(); - this.rootFrame.setText(this.unitLifeText, - FastNumberFormat.formatWholeNumber(simulationUnit.getLife()) + " / " + simulationUnit.getMaximumLife()); + final float lifeRatioRemaining = simulationUnit.getLife() / simulationUnit.getMaxLife(); + this.rootFrame.setText(this.unitLifeText, FastNumberFormat.formatWholeNumber(simulationUnit.getLife()) + " / " + + FastNumberFormat.formatWholeNumber(simulationUnit.getMaxLife())); + this.unitLifeText.setColor(new Color(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)), + Math.min(1.0f, lifeRatioRemaining * 2), 0, 1.0f)); final int maximumMana = simulationUnit.getMaximumMana(); if (maximumMana > 0) { this.rootFrame.setText(this.unitManaText, @@ -1940,8 +2084,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.rootFrame.setText(this.simpleNameValue, unitTypeName); String classText = null; for (final CUnitClassification classification : simulationUnit.getClassifications()) { - if ((classification == CUnitClassification.MECHANICAL) - && simulationUnit.getUnitType().isBuilding()) { + if ((classification == CUnitClassification.MECHANICAL) && simulationUnit.isBuilding()) { // buildings dont display MECHANICAL continue; } @@ -2041,14 +2184,20 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } localArmorIconBackdrop.setTexture(defenseTexture); - String defenseDisplayString = Integer.toString(simulationUnit.getCurrentDefenseDisplay()); - final int temporaryDefenseBonus = simulationUnit.getTemporaryDefenseBonus(); - if (temporaryDefenseBonus != 0) { - if (temporaryDefenseBonus > 0) { - defenseDisplayString += "|cFF00FF00 (+" + temporaryDefenseBonus + ")"; - } - else { - defenseDisplayString += "|cFFFF0000 (+" + temporaryDefenseBonus + ")"; + String defenseDisplayString; + if (simulationUnit.isInvulnerable()) { + defenseDisplayString = this.rootFrame.getTemplates().getDecoratedString("INVULNERABLE"); + } + else { + defenseDisplayString = Integer.toString(simulationUnit.getCurrentDefenseDisplay()); + final int temporaryDefenseBonus = simulationUnit.getTemporaryDefenseBonus(); + if (temporaryDefenseBonus != 0) { + if (temporaryDefenseBonus > 0) { + defenseDisplayString += "|cFF00FF00 (+" + temporaryDefenseBonus + ")"; + } + else { + defenseDisplayString += "|cFFFF0000 (+" + temporaryDefenseBonus + ")"; + } } } this.rootFrame.setText(localArmorInfoPanelIconValue, defenseDisplayString); @@ -2165,9 +2314,13 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma selectUnit(null); } else { + final float lifeRatioRemaining = this.selectedUnit.getSimulationUnit().getLife() + / this.selectedUnit.getSimulationUnit().getMaxLife(); this.rootFrame.setText(this.unitLifeText, FastNumberFormat.formatWholeNumber(this.selectedUnit.getSimulationUnit().getLife()) + " / " - + this.selectedUnit.getSimulationUnit().getMaximumLife()); + + FastNumberFormat.formatWholeNumber(this.selectedUnit.getSimulationUnit().getMaxLife())); + this.unitLifeText.setColor(new Color(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)), + Math.min(1.0f, lifeRatioRemaining * 2), 0, 1.0f)); } } @@ -2277,6 +2430,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } public boolean touchDown(final int screenX, final int screenY, final float worldScreenY, final int button) { + this.allowDrag = false; screenCoordsVector.set(screenX, screenY); this.uiViewport.unproject(screenCoordsVector); if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) { @@ -2300,9 +2454,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma clearAndRepopulateCommandCard(); } else { + final boolean shiftDown = isShiftDown(); final RenderWidget rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY, this.activeCommandUnitTargetFilter); - final boolean shiftDown = isShiftDown(); if (rayPickUnit != null) { this.unitOrderListener.issueTargetOrder( this.activeCommandUnit.getSimulationUnit().getHandleId(), @@ -2442,11 +2596,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } } else { - final List selectedUnits = this.war3MapViewer.selectUnit(screenX, worldScreenY, - false); - if (!selectedUnits.isEmpty()) { - selectWidgets(selectedUnits); + if (this.mouseOverUnit != null) { + final List unitList = Arrays.asList(this.mouseOverUnit); + this.war3MapViewer.doSelectUnit(unitList); + selectWidgets(unitList); } + this.war3MapViewer.getClickLocation(this.lastMouseClickLocation, screenX, (int) worldScreenY); + this.lastMouseDragStart.set(this.lastMouseClickLocation); + this.allowDrag = true; } } } @@ -2564,6 +2721,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } public boolean touchUp(final int screenX, final int screenY, final float worldScreenY, final int button) { + this.currentlyDraggingPointer = -1; screenCoordsVector.set(screenX, screenY); this.uiViewport.unproject(screenCoordsVector); final UIFrame clickedUIFrame = this.rootFrame.touchUp(screenCoordsVector.x, screenCoordsVector.y, button); @@ -2581,6 +2739,34 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } this.mouseDownUIFrame.mouseUp(this.rootFrame, this.uiViewport); } + else { + if (!this.dragSelectPreviewUnits.isEmpty()) { + final List selectedWidgets = new ArrayList<>(); + boolean foundGoal = false; + for (final RenderUnit unit : this.dragSelectPreviewUnits) { + if ((unit.getSimulationUnit().getPlayerIndex() == this.war3MapViewer.getLocalPlayerIndex()) + && !unit.getSimulationUnit().isBuilding()) { + foundGoal = true; + selectedWidgets.add(unit); + } + } + if (!foundGoal) { + selectedWidgets.addAll(this.dragSelectPreviewUnits); + } + Collections.sort(selectedWidgets, new Comparator() { + @Override + public int compare(final RenderWidget widget1, final RenderWidget widget2) { + return ((RenderUnit) widget1).getSimulationUnit().getUnitType().getPriority() + - ((RenderUnit) widget2).getSimulationUnit().getUnitType().getPriority(); + } + }); + + this.war3MapViewer.clearUnitMouseOverHighlight(); + this.war3MapViewer.doSelectUnit(selectedWidgets); + selectWidgets(selectedWidgets); + this.dragSelectPreviewUnits.clear(); + } + } this.mouseDownUIFrame = null; return false; } @@ -2599,6 +2785,25 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.cameraManager.target.x = worldPoint.x; this.cameraManager.target.y = worldPoint.y; } + else { + if (this.allowDrag) { + if (null != this.mouseOverUnit) { + this.war3MapViewer.clearUnitMouseOverHighlight(); + this.dragSelectPreviewUnits.clear(); + this.mouseOverUnit = null; + } + + this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); + this.currentlyDraggingPointer = pointer; + if (pointer == Input.Buttons.MIDDLE) { + this.cameraManager.target.add(this.lastMouseClickLocation.sub(clickLocationTemp).scl(-1)); + } + else if (pointer == Input.Buttons.LEFT) { + // update mouseover + } + this.lastMouseClickLocation.set(clickLocationTemp); + } + } return false; } @@ -2625,6 +2830,24 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.tooltipFrame.setVisible(false); } } + if (mousedUIFrame == null) { + final RenderWidget newMouseOverUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY, + new CWidgetFilterFunction() { + @Override + public boolean call(final CWidget unit) { + final RenderWidget renderPeer = MeleeUI.this.war3MapViewer.getRenderPeer(unit); + return !unit.isDead() && renderPeer.isSelectable(); + } + }); + if (newMouseOverUnit != this.mouseOverUnit) { + this.war3MapViewer.clearUnitMouseOverHighlight(); + this.dragSelectPreviewUnits.clear(); + if (newMouseOverUnit != null) { + this.war3MapViewer.showUnitMouseOverHighlight(newMouseOverUnit); + } + this.mouseOverUnit = newMouseOverUnit; + } + } return false; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuCursorState.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuCursorState.java new file mode 100644 index 0000000..60baa30 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuCursorState.java @@ -0,0 +1,24 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.ui; + +public enum MenuCursorState { + NORMAL("Normal"), + SCROLL_LEFT("Scroll Left"), + SCROLL_RIGHT("Scroll Right"), + SCROLL_DOWN("Scroll Down"), + SCROLL_UP("Scroll Up"), + SCROLL_DOWN_LEFT("Scroll Down Left"), + SCROLL_DOWN_RIGHT("Scroll Down Right"), + SCROLL_UP_LEFT("Scroll Up Left"), + SCROLL_UP_RIGHT("Scroll Up Right"), + TARGET_CURSOR(null), // handled specially + HOLD_ITEM("HoldItem"); + private String animationName; + + private MenuCursorState(final String animationName) { + this.animationName = animationName; + } + + public String getAnimationName() { + return this.animationName; + } +}