diff --git a/core/src/com/etheller/warsmash/WarsmashGdxFDFTestRenderScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxFDFTestRenderScreen.java index 45fdf21..5277f72 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxFDFTestRenderScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxFDFTestRenderScreen.java @@ -821,6 +821,12 @@ public class WarsmashGdxFDFTestRenderScreen implements InputProcessor, Screen, S } + @Override + public void setReplaceableTextureHD(final int replaceableTextureId, final String replaceableTextureFile) { + // TODO Auto-generated method stub + + } + } private class LibGDXContentLayerModel extends Model { diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java index 8033840..3ca6c6f 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java @@ -452,6 +452,12 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen { } + @Override + public void setReplaceableTextureHD(final int replaceableTextureId, final String replaceableTextureFile) { + // TODO Auto-generated method stub + + } + } private class LibGDXContentLayerModel extends Model { diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java index 08cd05f..29c0ad6 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java @@ -857,6 +857,12 @@ public class WarsmashGdxMenuScreen implements InputProcessor, Screen, SingleMode } + @Override + public void setReplaceableTextureHD(final int replaceableTextureId, final String replaceableTextureFile) { + // TODO Auto-generated method stub + + } + } private class LibGDXContentLayerModel extends Model { diff --git a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java index 9c7f1b7..5b9734a 100644 --- a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java +++ b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java @@ -3112,48 +3112,6 @@ public class Jass2 { throw new JassException(globalScope, "Needs to sleep " + time, null); } }); - jassProgramVisitor.getJassNativeManager().createNative("GetPlayerNeutralAggressive", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 4); - } - }); - jassProgramVisitor.getJassNativeManager().createNative("GetBJPlayerNeutralVictim", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 3); - } - }); - jassProgramVisitor.getJassNativeManager().createNative("GetBJPlayerNeutralExtra", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 2); - } - }); - jassProgramVisitor.getJassNativeManager().createNative("GetPlayerNeutralPassive", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 1); - } - }); - jassProgramVisitor.getJassNativeManager().createNative("GetBJMaxPlayers", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 4); - } - }); - jassProgramVisitor.getJassNativeManager().createNative("GetBJMaxPlayerSlots", new JassFunction() { - @Override - public JassValue call(final List arguments, final GlobalScope globalScope, - final TriggerExecutionScope triggerScope) { - return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS); - } - }); jassProgramVisitor.getJassNativeManager().createNative("AddSpecialEffectTarget", new JassFunction() { @Override public JassValue call(final List arguments, final GlobalScope globalScope, @@ -4890,6 +4848,49 @@ public class Jass2 { return new IntegerJassValue(whichPlayer.getId()); } }); + + jassProgramVisitor.getJassNativeManager().createNative("GetPlayerNeutralAggressive", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 4); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetBJPlayerNeutralVictim", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 3); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetBJPlayerNeutralExtra", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 2); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetPlayerNeutralPassive", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 1); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetBJMaxPlayers", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS - 4); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetBJMaxPlayerSlots", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new IntegerJassValue(WarsmashConstants.MAX_PLAYERS); + } + }); } public static void registerTypingNatives(final JassProgramVisitor jassProgramVisitor, final HandleJassType raceType, diff --git a/core/src/com/etheller/warsmash/viewer5/Camera.java b/core/src/com/etheller/warsmash/viewer5/Camera.java index 1a97cd6..a5dae66 100644 --- a/core/src/com/etheller/warsmash/viewer5/Camera.java +++ b/core/src/com/etheller/warsmash/viewer5/Camera.java @@ -38,7 +38,7 @@ public class Camera { /** * World -> View. */ - private final Matrix4 viewMatrix; + public final Matrix4 viewMatrix; /** * View -> Clip. */ diff --git a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java index c145516..69b0e42 100644 --- a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java @@ -225,5 +225,7 @@ public abstract class ModelInstance extends Node { public abstract void setReplaceableTexture(int replaceableTextureId, String replaceableTextureFile); + public abstract void setReplaceableTextureHD(int replaceableTextureId, String replaceableTextureFile); + protected abstract void removeLights(Scene scene2); } diff --git a/core/src/com/etheller/warsmash/viewer5/Shaders.java b/core/src/com/etheller/warsmash/viewer5/Shaders.java index a4798c3..1dcc4ac 100644 --- a/core/src/com/etheller/warsmash/viewer5/Shaders.java +++ b/core/src/com/etheller/warsmash/viewer5/Shaders.java @@ -22,6 +22,63 @@ public class Shaders { " texture2D(u_boneMap, vec2(column + u_vectorSize * 2.0, row)),\r\n" + // " texture2D(u_boneMap, vec2(column + u_vectorSize * 3.0, row)));\r\n" + // " }"; + public static final String transforms = "#ifdef SKIN\r\n" + // + "attribute vec4 a_bones;\r\n" + // + "attribute vec4 a_weights;\r\n" + // + "void transformSkin(inout vec3 position, inout vec3 normal, inout vec3 tangent, inout vec3 binormal) {\r\n" + + // + " mat4 bone = mat4(0);\r\n" + // + " bone += fetchMatrix(a_bones[0], 0.0) * a_weights[0];\r\n" + // + " bone += fetchMatrix(a_bones[1], 0.0) * a_weights[1];\r\n" + // + " bone += fetchMatrix(a_bones[2], 0.0) * a_weights[2];\r\n" + // + " bone += fetchMatrix(a_bones[3], 0.0) * a_weights[3];\r\n" + // + " mat3 rotation = mat3(bone);\r\n" + // + " position = vec3(bone * vec4(position, 1.0));\r\n" + // + " normal = rotation * normal;\r\n" + // + " tangent = rotation * tangent;\r\n" + // + " binormal = rotation * binormal;\r\n" + // + "}\r\n" + // + "#else\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" + // + "mat4 getVertexGroupMatrix() {\r\n" + // + " mat4 bone;\r\n" + // + " // For the broken models out there, since the game supports this.\r\n" + // + " if (a_boneNumber > 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" + // + " }\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" + // + " }\r\n" + // + " }\r\n" + // + " #endif\r\n" + // + " }\r\n" + // + " return bone / a_boneNumber;\r\n" + // + "}\r\n" + // + "void transformVertexGroups(inout vec3 position, inout vec3 normal) {\r\n" + // + " mat4 bone = getVertexGroupMatrix();\r\n" + // + " mat3 rotation = mat3(bone);\r\n" + // + " position = vec3(bone * vec4(position, 1.0));\r\n" + // + " normal = normalize(rotation * normal);\r\n" + // + "}\r\n" + // + "void transformVertexGroupsHD(inout vec3 position, inout vec3 normal, inout vec3 tangent, inout vec3 binormal) {\r\n" + + // + " mat4 bone = getVertexGroupMatrix();\r\n" + // + " mat3 rotation = mat3(bone);\r\n" + // + " position = vec3(bone * vec4(position, 1.0));\r\n" + // + " normal = normalize(rotation * normal);\r\n" + // + " tangent = normalize(rotation * tangent);\r\n" + // + " binormal = normalize(rotation * binormal);\r\n" + // + "}\r\n" + // + "#endif"; public static final String decodeFloat = "\r\n" + // " vec2 decodeFloat2(float f) {\r\n" + // diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Batch.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Batch.java index 02dc3b1..5db6471 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Batch.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Batch.java @@ -4,13 +4,26 @@ public class Batch implements GenericIndexed { public int index; public Geoset geoset; public Layer layer; - public boolean isExtended; + public Material material; + public final SkinningType skinningType; + public final boolean hd; - public Batch(final int index, final Geoset geoset, final Layer layer, final boolean isExtended) { + public Batch(final int index, final Geoset geoset, final Layer layer, final SkinningType skinningType) { this.index = index; this.geoset = geoset; this.layer = layer; - this.isExtended = isExtended; + this.material = null; + this.skinningType = skinningType; + this.hd = false; + } + + public Batch(final int index, final Geoset geoset, final Material material, final SkinningType skinningType) { + this.index = index; + this.geoset = geoset; + this.material = material; + this.layer = material.layers.get(0); + this.skinningType = skinningType; + this.hd = true; } @Override 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 74aa956..a3c8cb3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java @@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.Matrix4; import com.etheller.warsmash.util.WarsmashConstants; +import com.etheller.warsmash.viewer5.Camera; import com.etheller.warsmash.viewer5.ModelViewer; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.Texture; @@ -15,18 +16,22 @@ import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager; import com.etheller.warsmash.viewer5.handlers.w3x.W3xSceneLightManager; public class BatchGroup extends GenericGroup { + private static float[] tempFloat3Array = new float[3]; private final MdxModel model; - public final boolean isExtended; + public final SkinningType skinningType; + public final boolean hd; - public BatchGroup(final MdxModel model, final boolean isExtended) { + public BatchGroup(final MdxModel model, final SkinningType skinningType, final boolean hd) { this.model = model; - this.isExtended = isExtended; + this.skinningType = skinningType; + this.hd = hd; } @Override public void render(final MdxComplexInstance instance, final Matrix4 mvp) { final Scene scene = instance.scene; + final Camera camera = scene.camera; final MdxModel model = this.model; final List textures = model.getTextures(); final MdxHandler handler = model.handler; @@ -35,11 +40,15 @@ public class BatchGroup extends GenericGroup { final ModelViewer viewer = model.viewer; final GL20 gl = viewer.gl; final WebGL webGL = viewer.webGL; - final boolean isExtended = this.isExtended; + final SkinningType skinningType = this.skinningType; + final boolean hd = this.hd; final ShaderProgram shader; final W3xSceneLightManager lightManager = (W3xSceneLightManager) scene.getLightManager(); - if (isExtended) { + if (hd) { + shader = handler.shaders.hd; + } + else if (skinningType == SkinningType.ExtendedVertexGroups) { if (DynamicShadowManager.IS_SHADOW_MAPPING) { shader = handler.shaders.extendedShadowMap; } @@ -58,7 +67,7 @@ public class BatchGroup extends GenericGroup { webGL.useShaderProgram(shader); - shader.setUniformMatrix("u_mvp", mvp); + shader.setUniformMatrix(hd ? "u_VP" : "u_mvp", mvp); final DataTexture boneTexture = instance.boneTexture; final DataTexture unitLightsTexture = lightManager.getUnitLightsTexture(); @@ -81,79 +90,185 @@ public class BatchGroup extends GenericGroup { shader.setUniformf("u_hasBones", 0); } - shader.setUniformi("u_texture", 0); - gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, model.arrayBuffer); gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, model.elementBuffer); - shader.setUniform4fv("u_vertexColor", instance.vertexColor, 0, instance.vertexColor.length); + if (hd) { + shader.setUniformi("u_diffuseMap", 0); + shader.setUniformi("u_normalsMap", 1); + shader.setUniformi("u_ormMap", 2); + shader.setUniformi("u_emissiveMap", 3); + shader.setUniformi("u_teamColorMap", 4); + shader.setUniformi("u_environmentMap", 5); - for (final int index : this.objects) { - final Batch batch = batches.get(index); - final Geoset geoset = batch.geoset; - final Layer layer = batch.layer; - final int geosetIndex = geoset.index; - final int layerIndex = layer.index; - final float[] geosetColor = instance.geosetColors[geosetIndex]; - final float layerAlpha = instance.layerAlphas[layerIndex]; + gl.glEnable(GL20.GL_BLEND); + gl.glEnable(GL20.GL_DEPTH_TEST); + gl.glDepthMask(true); - if ((geosetColor[3] > 0.01) && (layerAlpha > 0.01)) { - // BELOW: I updated it to "Math.max(0," because MDL and MDX parser for PRSCMOD - // menu screen behaved differently, - // the MDL case was getting "no data" for default value when unanimated, and "no - // data" resolved to -1, - // whereas MDX binary contained an "unused" 0 value. - final int layerTexture = Math.max(0, instance.layerTextures[layerIndex]); - final float[] uvAnim = instance.uvAnims[layerIndex]; + shader.setUniformMatrix("u_MV", camera.viewMatrix); - shader.setUniform4fv("u_geosetColor", geosetColor, 0, geosetColor.length); + tempFloat3Array[0] = camera.location.x; + tempFloat3Array[1] = camera.location.y; + tempFloat3Array[2] = camera.location.z; + shader.setUniform3fv("u_eyePos", tempFloat3Array, 0, 3); - shader.setUniformf("u_layerAlpha", layerAlpha); - shader.setUniformf("u_unshaded", layer.unshaded); + for (final int index : this.objects) { + final Batch batch = batches.get(index); + final Geoset geoset = batch.geoset; + final Material material = batch.material; + final Layer diffuseLayer = material.layers.get(0); + final Layer normalsLayer = material.layers.get(1); + final Layer ormLayer = material.layers.get(2); + final Layer emissiveLayer = material.layers.get(3); + final Layer teamColorLayer = material.layers.get(4); + final Layer environmentMapLayer = material.layers.get(5); + final float layerAlpha = instance.layerAlphas[diffuseLayer.index]; - shader.setUniform2fv("u_uvTrans", uvAnim, 0, 2); - shader.setUniform2fv("u_uvRot", uvAnim, 2, 2); - shader.setUniform1fv("u_uvScale", uvAnim, 4, 1); + if (layerAlpha > 0) { + shader.setUniformf("u_layerAlpha", layerAlpha); + shader.setUniformf("u_filterMode", diffuseLayer.filterMode); - if (instance.additiveOverrideMeshMode) { - layer.bindBlended(shader); - gl.glBlendFunc(FilterMode.ADDITIVE_ALPHA[0], FilterMode.ADDITIVE_ALPHA[1]); - } - else if (instance.vertexColor[3] < 1.0f) { - layer.bindBlended(shader); - } - else { - layer.bind(shader); - } + final int diffuseId = Math.max(0, instance.layerTextures[diffuseLayer.index]); + final int normalsId = Math.max(0, instance.layerTextures[normalsLayer.index]); + final int ormId = Math.max(0, instance.layerTextures[ormLayer.index]); + final int emissiveId = Math.max(0, instance.layerTextures[emissiveLayer.index]); + final int teamColorId = Math.max(0, instance.layerTextures[teamColorLayer.index]); + final int environmentMapId = Math.max(0, instance.layerTextures[environmentMapLayer.index]); - final Integer replaceable = replaceables.get(layerTexture); // TODO is this OK? - Texture texture; + final Texture diffuseTexture; + final Texture normalsTexture; + final Texture ormTexture; + final Texture emissiveTexture = textures.get(emissiveId); + final Texture teamColorTexture; + final Texture environmentMapTexture = textures.get(environmentMapId); - if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) - && (instance.replaceableTextures[replaceable] != null)) { - texture = instance.replaceableTextures[replaceable]; - } - else { - texture = textures.get(layerTexture); - - Texture textureLookup = instance.textureMapper.get(texture); - if (textureLookup == null) { - textureLookup = texture; + { + final Integer replaceable = replaceables.get(diffuseId); + if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) + && (instance.replaceableTextures_diffuse[replaceable] != null)) { + diffuseTexture = instance.replaceableTextures_diffuse[replaceable]; + } + else { + diffuseTexture = textures.get(diffuseId); + } } - texture = textureLookup; - } - viewer.webGL.bindTexture(texture, 0); + { + final Integer replaceable = replaceables.get(normalsId); + if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) + && (instance.replaceableTextures_normal[replaceable] != null)) { + normalsTexture = instance.replaceableTextures_normal[replaceable]; + } + else { + normalsTexture = textures.get(normalsId); + } + } - if (isExtended) { - geoset.bindExtended(shader, layer.coordId); - } - else { - geoset.bind(shader, layer.coordId); - } + { + final Integer replaceable = replaceables.get(ormId); + if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) + && (instance.replaceableTextures_orm[replaceable] != null)) { + ormTexture = instance.replaceableTextures_orm[replaceable]; + } + else { + ormTexture = textures.get(ormId); + } + } - geoset.render(); + final Integer replaceable = replaceables.get(teamColorId); + if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) + && (instance.replaceableTextures[replaceable] != null)) { + teamColorTexture = instance.replaceableTextures[replaceable]; + } + else { + teamColorTexture = textures.get(teamColorId); + } + + webGL.bindTexture(diffuseTexture, 0); + webGL.bindTexture(normalsTexture, 1); + webGL.bindTexture(ormTexture, 2); + webGL.bindTexture(emissiveTexture, 3); + webGL.bindTexture(teamColorTexture, 4); + webGL.bindTexture(environmentMapTexture, 5); + + geoset.bindHd(shader, batch.skinningType, diffuseLayer.coordId); + geoset.render(); + } } } + else { + shader.setUniformi("u_texture", 0); + + shader.setUniform4fv("u_vertexColor", instance.vertexColor, 0, instance.vertexColor.length); + + for (final int index : this.objects) { + final Batch batch = batches.get(index); + final Geoset geoset = batch.geoset; + final Layer layer = batch.layer; + final int geosetIndex = geoset.index; + final int layerIndex = layer.index; + final float[] geosetColor = instance.geosetColors[geosetIndex]; + final float layerAlpha = instance.layerAlphas[layerIndex]; + + if ((geosetColor[3] > 0.01) && (layerAlpha > 0.01)) { + // BELOW: I updated it to "Math.max(0," because MDL and MDX parser for PRSCMOD + // menu screen behaved differently, + // the MDL case was getting "no data" for default value when unanimated, and "no + // data" resolved to -1, + // whereas MDX binary contained an "unused" 0 value. + final int layerTexture = Math.max(0, instance.layerTextures[layerIndex]); + final float[] uvAnim = instance.uvAnims[layerIndex]; + + shader.setUniform4fv("u_geosetColor", geosetColor, 0, geosetColor.length); + + shader.setUniformf("u_layerAlpha", layerAlpha); + shader.setUniformf("u_unshaded", layer.unshaded); + + shader.setUniform2fv("u_uvTrans", uvAnim, 0, 2); + shader.setUniform2fv("u_uvRot", uvAnim, 2, 2); + shader.setUniform1fv("u_uvScale", uvAnim, 4, 1); + + if (instance.additiveOverrideMeshMode) { + layer.bindBlended(shader); + gl.glBlendFunc(FilterMode.ADDITIVE_ALPHA[0], FilterMode.ADDITIVE_ALPHA[1]); + } + else if (instance.vertexColor[3] < 1.0f) { + layer.bindBlended(shader); + } + else { + layer.bind(shader); + } + + final Integer replaceable = replaceables.get(layerTexture); // TODO is this OK? + Texture texture; + + if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT) + && (instance.replaceableTextures[replaceable] != null)) { + texture = instance.replaceableTextures[replaceable]; + } + else { + texture = textures.get(layerTexture); + + Texture textureLookup = instance.textureMapper.get(texture); + if (textureLookup == null) { + textureLookup = texture; + } + texture = textureLookup; + } + + viewer.webGL.bindTexture(texture, 0); + + if (skinningType == SkinningType.ExtendedVertexGroups) { + geoset.bindExtended(shader, layer.coordId); + } + else { + geoset.bind(shader, layer.coordId); + } + + geoset.render(); + } + } + } + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java index 9e8bdd8..2cb3121 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java @@ -13,6 +13,7 @@ public class Geoset { public int positionOffset; public int normalOffset; public int uvOffset; + public final int tangentOffset; public int skinOffset; public int faceOffset; public int vertices; @@ -29,14 +30,15 @@ public class Geoset { public final MdlxGeoset mdlxGeoset; public Geoset(final MdxModel model, final int index, final int positionOffset, final int normalOffset, - final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements, - final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes, final boolean unselectable, - final MdlxGeoset mdlxGeoset) { + final int uvOffset, final int tangentOffset, final int skinOffset, final int faceOffset, final int vertices, + final int elements, final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes, + final boolean unselectable, final MdlxGeoset mdlxGeoset) { this.model = model; this.index = index; this.positionOffset = positionOffset; this.normalOffset = normalOffset; this.uvOffset = uvOffset; + this.tangentOffset = tangentOffset; this.skinOffset = skinOffset; this.faceOffset = faceOffset; this.vertices = vertices; @@ -143,12 +145,16 @@ public class Geoset { this.faceOffset, instances); } - public void bindHd(final ShaderProgram shader, final int coordId) { + public void bindHd(final ShaderProgram shader, final SkinningType skinningType, final int coordId) { shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset); - shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.positionOffset); + shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset); shader.setVertexAttribute("a_uv", 2, GL20.GL_FLOAT, false, 0, this.uvOffset + (coordId * this.vertices * 8)); + + shader.setVertexAttribute("a_tangent", 4, GL20.GL_FLOAT, false, 0, this.tangentOffset); + + // TODO ghostwolf splits here and allows HD with non-skin, or SD with skin shader.setVertexAttribute("a_bones", 4, GL20.GL_UNSIGNED_BYTE, false, 8, this.skinOffset); - shader.setVertexAttribute("a_weights", 4, GL20.GL_UNSIGNED_BYTE, false, 8, this.skinOffset + 4); + shader.setVertexAttribute("a_weights", 4, GL20.GL_UNSIGNED_BYTE, true, 8, this.skinOffset + 4); } private static final class Variants { 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 02cd82f..4b715bb 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -70,6 +70,9 @@ public class MdxComplexInstance extends ModelInstance { public FloatBuffer worldMatricesCopyHeap; public DataTexture boneTexture; public Texture[] replaceableTextures = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT]; + public Texture[] replaceableTextures_diffuse = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT]; + public Texture[] replaceableTextures_normal = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT]; + public Texture[] replaceableTextures_orm = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT]; private float animationSpeed = 1.0f; private float blendTime; private float blendTimeRemaining; @@ -681,6 +684,16 @@ public class MdxComplexInstance extends ModelInstance { PathSolver.DEFAULT, null); } + @Override + public void setReplaceableTextureHD(final int replaceableTextureId, final String replaceableTextureFile) { + this.replaceableTextures_diffuse[replaceableTextureId] = (Texture) this.model.viewer + .load(replaceableTextureFile + "_diffuse.dds", PathSolver.DEFAULT, null); + this.replaceableTextures_normal[replaceableTextureId] = (Texture) this.model.viewer + .load(replaceableTextureFile + "_normal.dds", PathSolver.DEFAULT, null); + this.replaceableTextures_orm[replaceableTextureId] = (Texture) this.model.viewer + .load(replaceableTextureFile + "_orm.dds", PathSolver.DEFAULT, null); + } + /** * Set the vertex color of this instance. */ 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 9d4d6e4..4712d1f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxHandler.java @@ -44,7 +44,7 @@ public class MdxHandler extends ModelHandler { 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); + this.shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd()); // TODO HD reforged // If a shader failed to compile, don't allow the handler to be registered, and diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java index f5b88e8..905d2d3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java @@ -144,9 +144,9 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model { this.layers.add(vLayer); } - this.materials.add(new Material(this, "" /* material.shader */, layers)); + this.materials.add(new Material(this, material.shader, layers)); - if (false /* !"".equals(material.shader) */) { + if (!"".equals(material.shader)) { this.hd = true; } } 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 194d012..329ff6d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java @@ -1,59 +1,353 @@ package com.etheller.warsmash.viewer5.handlers.mdx; import com.etheller.warsmash.viewer5.Shaders; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentType; public class MdxShaders { - public static final String vsHd = Shaders.boneTexture + "\r\n" + // - " uniform mat4 u_mvp;\r\n" + // + public static final String vsHd = "#version 120\r\n" + Shaders.boneTexture + "\r\n" + // + " uniform mat4 u_VP;\r\n" + // + " uniform mat4 u_MV;\r\n" + // + " uniform vec3 u_eyePos;\r\n" + // + " uniform sampler2D u_lightTexture;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // " uniform float u_layerAlpha;\r\n" + // + " uniform bool u_hasBones;\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" + // - " attribute vec4 a_weights;\r\n" + // - " varying vec3 v_normal;\r\n" + // + " attribute vec4 a_tangent;\r\n" + // + // TODO ONLY_TANGENTS + " \r\n" + // + " \r\n" + // + "#define SKIN\r\n" + // TODO make this conditional + Shaders.transforms + // + " \r\n" + // + " \r\n" + // + "vec3 TBN(vec3 vector, vec3 tangent, vec3 binormal, vec3 normal) {\r\n" + // + " return vec3(dot(vector, tangent), dot(vector, binormal), dot(vector, normal));\r\n" + // + "}\r\n" + // + " \r\n" + // + " \r\n" + // " varying vec2 v_uv;\r\n" + // " varying float v_layerAlpha;\r\n" + // - " void transform(inout vec3 position, inout vec3 normal) {\r\n" + // - " mat4 bone;\r\n" + // - " bone += fetchMatrix(a_bones[0], 0.0) * a_weights[0];\r\n" + // - " bone += fetchMatrix(a_bones[1], 0.0) * a_weights[1];\r\n" + // - " bone += fetchMatrix(a_bones[2], 0.0) * a_weights[2];\r\n" + // - " bone += fetchMatrix(a_bones[3], 0.0) * a_weights[3];\r\n" + // - " position = vec3(bone * vec4(position, 1.0));\r\n" + // - " normal = mat3(bone) * normal;\r\n" + // - " }\r\n" + // + " varying vec3 v_lightDir;\r\n" + // + " varying vec3 v_eyeVec;\r\n" + // + " varying vec3 v_normal;\r\n" + // + " \r\n" + // + " \r\n" + // " void main() {\r\n" + // " vec3 position = a_position;\r\n" + // " vec3 normal = a_normal;\r\n" + // - " transform(position, normal);\r\n" + // - " v_normal = normal;\r\n" + // + " vec3 tangent = a_tangent.xyz;\r\n" + // + " \r\n" + // + // Re-orthogonalize the tangent in case it wasnt normalized. + // See "One last thing" at + // https://learnopengl.com/Advanced-Lighting/Normal-Mapping + " \r\n" + // + " tangent = normalize(tangent - dot(tangent, normal) * normal);\r\n" + // + " \r\n" + // + " vec3 binormal = cross(normal, tangent) * a_tangent.w;\r\n" + // + " \r\n" + // + " if (u_hasBones) {\r\n" + // + " #ifdef SKIN\r\n" + // + " transformSkin(position, normal, tangent, binormal);\r\n" + // + " #else\r\n" + // + " transformVertexGroupsHD(position, normal, tangent, binormal);\r\n" + // + " #endif\r\n" + // + " }\r\n" + // + " \r\n" + // + " vec3 position_mv = vec3(u_MV * vec4(position, 1));\r\n" + // + " \r\n" + // + " mat3 mv = mat3(u_MV);\r\n" + // + " vec3 t = normalize(mv * tangent);\r\n" + // + " vec3 b = normalize(mv * binormal);\r\n" + // + " vec3 n = normalize(mv * normal);\r\n" + // + " \r\n" + // + " v_eyeVec = normalize(u_eyePos - position_mv);\r\n" + // + " \r\n" + // + // TODO fix giant hack on lighting + " float rowPos = (0.5) / u_lightTextureHeight;\r\n" + // + " vec4 lightPosition = texture2D(u_lightTexture, vec2(0.125, rowPos));\r\n" + // + " vec3 u_lightPos = lightPosition.xyz;\r\n" + // + " vec3 lightDir = normalize(u_lightPos - position_mv);\r\n" + // + " v_lightDir = normalize(TBN(lightDir, t, b, n));\r\n" + // + " \r\n" + // " v_uv = a_uv;\r\n" + // " v_layerAlpha = u_layerAlpha;\r\n" + // - " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // + " \r\n" + // + " v_normal = normal;\r\n" + // + " // v_lightDirWorld = normalize(lightDir);\r\n" + // + " \r\n" + // + // TODO ONLY_TANGENTS + " gl_Position = u_VP * vec4(position, 1.0);\r\n" + // " }"; - public static final String fsHd = "\r\n" + // - " uniform sampler2D u_diffuseMap;\r\n" + // - " uniform sampler2D u_ormMap;\r\n" + // - " uniform sampler2D u_teamColorMap;\r\n" + // - " uniform float u_filterMode;\r\n" + // - " varying vec3 v_normal;\r\n" + // - " varying vec2 v_uv;\r\n" + // - " varying float v_layerAlpha;\r\n" + // - " void main() {\r\n" + // - " vec4 texel = texture2D(u_diffuseMap, v_uv);\r\n" + // - " vec4 color = vec4(texel.rgb, texel.a * v_layerAlpha);\r\n" + // - " vec4 orma = texture2D(u_ormMap, v_uv);\r\n" + // - " if (orma.a > 0.1) {\r\n" + // - " color *= texture2D(u_teamColorMap, v_uv) * orma.a;\r\n" + // - " }\r\n" + // - " // 1bit Alpha\r\n" + // - " if (u_filterMode == 1.0 && color.a < 0.75) {\r\n" + // - " discard;\r\n" + // - " }\r\n" + // - " gl_FragColor = color;\r\n" + // - " }"; + public static final String fsHd() { + return "#version 120\r\n" + // + "\r\n" + // + "\r\n" + // + "\r\n" + // + "uniform sampler2D u_diffuseMap;\r\n" + // + "uniform sampler2D u_normalsMap;\r\n" + // + "uniform sampler2D u_ormMap;\r\n" + // + "uniform sampler2D u_emissiveMap;\r\n" + // + "uniform sampler2D u_teamColorMap;\r\n" + // + "uniform sampler2D u_environmentMap;\r\n" + // + "uniform float u_filterMode;\r\n" + // + "// uniform sampler2D u_lutMap;\r\n" + // + "// uniform sampler2D u_envDiffuseMap;\r\n" + // + "// uniform sampler2D u_envSpecularMap;\r\n" + // + "varying vec2 v_uv;\r\n" + // + "varying float v_layerAlpha;\r\n" + // + "varying vec3 v_lightDir;\r\n" + // + "varying vec3 v_eyeVec;\r\n" + // + "varying vec3 v_normal;\r\n" + // + "// varying vec3 v_lightDirWorld;\r\n" + // + "#if defined(ONLY_TANGENTS)\r\n" + // + "varying vec3 v_tangent;\r\n" + // + "#endif\r\n" + // + "vec3 decodeNormal() {\r\n" + // + " vec2 xy = texture2D(u_normalsMap, v_uv).xy * 2.0 - 1.0;\r\n" + // + " \r\n" + // + " return vec3(xy, sqrt(1.0 - dot(xy, xy)));\r\n" + // + "}\r\n" + // + "const vec2 invAtan = vec2(0.1591, 0.3183);\r\n" + // + "vec2 sampleEnvironmentMap(vec3 normal) {\r\n" + // + " vec2 uv = vec2(atan(normal.x, normal.y), -asin(normal.z));\r\n" + // + " uv *= invAtan;\r\n" + // + " uv += 0.5;\r\n" + // + " return uv;\r\n" + // + "}\r\n" + // + "vec4 getDiffuseColor() {\r\n" + // + " vec4 color = texture2D(u_diffuseMap, v_uv);\r\n" + // + " // 1bit Alpha\r\n" + // + " if (u_filterMode == 1.0 && color.a < 0.75) {\r\n" + // + " discard;\r\n" + // + " }\r\n" + // + " return color;\r\n" + // + "}\r\n" + // + "vec4 getOrmColor() {\r\n" + // + " return texture2D(u_ormMap, v_uv);\r\n" + // + "}\r\n" + // + "vec3 getEmissiveColor() {\r\n" + // + " return texture2D(u_emissiveMap, v_uv).rgb;\r\n" + // + "}\r\n" + // + "vec3 getTeamColor() {\r\n" + // + " return texture2D(u_teamColorMap, v_uv).rgb;\r\n" + // + "}\r\n" + // + "// const float PI = 3.14159265359;\r\n" + // + "// const float RECIPROCAL_PI = 0.31830988618;\r\n" + // + "// const float RECIPROCAL_PI2 = 0.15915494;\r\n" + // + "// const float LN2 = 0.6931472;\r\n" + // + "// const float ENV_LODS = 6.0;\r\n" + // + "// vec4 SRGBtoLinear(vec4 srgb) {\r\n" + // + "// vec3 linOut = pow(srgb.xyz, vec3(2.2));\r\n" + // + "// return vec4(linOut, srgb.w);;\r\n" + // + "// }\r\n" + // + "// vec4 RGBMToLinear(in vec4 value) {\r\n" + // + "// float maxRange = 6.0;\r\n" + // + "// return vec4(value.xyz * value.w * maxRange, 1.0);\r\n" + // + "// }\r\n" + // + "// vec3 linearToSRGB(vec3 color) {\r\n" + // + "// return pow(color, vec3(1.0 / 2.2));\r\n" + // + "// }\r\n" + // + "// // vec3 getNormal() {\r\n" + // + "// // vec3 pos_dx = dFdx(vMPos.xyz);\r\n" + // + "// // vec3 pos_dy = dFdy(vMPos.xyz);\r\n" + // + "// // vec2 tex_dx = dFdx(vUv);\r\n" + // + "// // vec2 tex_dy = dFdy(vUv);\r\n" + // + "// // vec3 t = normalize(pos_dx * tex_dy.t - pos_dy * tex_dx.t);\r\n" + // + "// // vec3 b = normalize(-pos_dx * tex_dy.s + pos_dy * tex_dx.s);\r\n" + // + "// // mat3 tbn = mat3(t, b, normalize(vNormal));\r\n" + // + "// // vec3 n = texture2D(tNormal, vUv * uNormalUVScale).rgb * 2.0 - 1.0;\r\n" + // + "// // n.xy *= uNormalScale;\r\n" + // + "// // vec3 normal = normalize(tbn * n);\r\n" + // + "// // // Get world normal from view normal (normalMatrix * normal)\r\n" + // + "// // return normalize((vec4(normal, 0.0) * viewMatrix).xyz);\r\n" + // + "// // }\r\n" + // + "// vec3 specularReflection(vec3 specularEnvR0, vec3 specularEnvR90, float VdH) {\r\n" + // + "// return specularEnvR0 + (specularEnvR90 - specularEnvR0) * pow(clamp(1.0 - VdH, 0.0, 1.0), 5.0);\r\n" + + // + "// }\r\n" + // + "// float geometricOcclusion(float NdL, float NdV, float roughness) {\r\n" + // + "// float r = roughness;\r\n" + // + "// float attenuationL = 2.0 * NdL / (NdL + sqrt(r * r + (1.0 - r * r) * (NdL * NdL)));\r\n" + // + "// float attenuationV = 2.0 * NdV / (NdV + sqrt(r * r + (1.0 - r * r) * (NdV * NdV)));\r\n" + // + "// return attenuationL * attenuationV;\r\n" + // + "// }\r\n" + // + "// float microfacetDistribution(float roughness, float NdH) {\r\n" + // + "// float roughnessSq = roughness * roughness;\r\n" + // + "// float f = (NdH * roughnessSq - NdH) * NdH + 1.0;\r\n" + // + "// return roughnessSq / (PI * f * f);\r\n" + // + "// }\r\n" + // + "// vec2 cartesianToPolar(vec3 n) {\r\n" + // + "// vec2 uv;\r\n" + // + "// uv.x = atan(n.z, n.x) * RECIPROCAL_PI2 + 0.5;\r\n" + // + "// uv.y = asin(n.y) * RECIPROCAL_PI + 0.5;\r\n" + // + "// return uv;\r\n" + // + "// }\r\n" + // + "// void getIBLContribution(inout vec3 diffuse, inout vec3 specular, float NdV, float roughness, vec3 n, vec3 reflection, vec3 diffuseColor, vec3 specularColor) {\r\n" + + // + "// vec3 brdf = SRGBtoLinear(texture2D(u_lutMap, vec2(NdV, roughness))).rgb;\r\n" + // + "// vec3 diffuseLight = RGBMToLinear(texture2D(u_envDiffuseMap, sampleEnvironmentMap(n))).rgb;\r\n" + // + "// // Sample 2 levels and mix between to get smoother degradation\r\n" + // + "// float blend = roughness * ENV_LODS;\r\n" + // + "// float level0 = floor(blend);\r\n" + // + "// float level1 = min(ENV_LODS, level0 + 1.0);\r\n" + // + "// blend -= level0;\r\n" + // + " \r\n" + // + "// // Sample the specular env map atlas depending on the roughness value\r\n" + // + "// vec2 uvSpec = sampleEnvironmentMap(reflection);\r\n" + // + "// uvSpec.y /= 2.0;\r\n" + // + "// vec2 uv0 = uvSpec;\r\n" + // + "// vec2 uv1 = uvSpec;\r\n" + // + "// uv0 /= pow(2.0, level0);\r\n" + // + "// uv0.y += 1.0 - exp(-LN2 * level0);\r\n" + // + "// uv1 /= pow(2.0, level1);\r\n" + // + "// uv1.y += 1.0 - exp(-LN2 * level1);\r\n" + // + "// vec3 specular0 = RGBMToLinear(texture2D(u_envSpecularMap, uv0)).rgb;\r\n" + // + "// vec3 specular1 = RGBMToLinear(texture2D(u_envSpecularMap, uv1)).rgb;\r\n" + // + "// vec3 specularLight = mix(specular0, specular1, blend);\r\n" + // + "// diffuse = diffuseLight * diffuseColor;\r\n" + // + " \r\n" + // + "// // Bit of extra reflection for smooth materials\r\n" + // + "// float reflectivity = pow((1.0 - roughness), 2.0) * 0.05;\r\n" + // + "// specular = specularLight * (specularColor * brdf.x + brdf.y + reflectivity);\r\n" + // + "// // specular *= uEnvSpecular;\r\n" + // + "// }\r\n" + // + "// void PBR() {\r\n" + // + "// vec4 baseDiffuseColor = getDiffuseColor();\r\n" + // + "// vec3 baseColor = baseDiffuseColor.rgb;\r\n" + // + "// vec4 orm = getOrmColor();\r\n" + // + "// vec3 tc = getTeamColor();\r\n" + // + "// float tcFactor = getOrmColor().a;\r\n" + // + "// if (tcFactor > 0.1) {\r\n" + // + "// baseColor *= tc * tcFactor;\r\n" + // + "// }\r\n" + // + "// float roughness = clamp(orm.g, 0.04, 1.0);\r\n" + // + "// float metallic = clamp(orm.b, 0.04, 1.0);\r\n" + // + "// vec3 f0 = vec3(0.04);\r\n" + // + "// vec3 diffuseColor = baseColor * (vec3(1.0) - f0) * (1.0 - metallic);\r\n" + // + "// vec3 specularColor = mix(f0, baseColor, metallic);\r\n" + // + "// vec3 specularEnvR0 = specularColor;\r\n" + // + "// vec3 specularEnvR90 = vec3(clamp(max(max(specularColor.r, specularColor.g), specularColor.b) * 25.0, 0.0, 1.0));\r\n" + + // + "// vec3 N = v_normal;\r\n" + // + "// vec3 V = normalize(v_eyeVec);\r\n" + // + "// vec3 L = normalize(v_lightDirWorld);\r\n" + // + "// vec3 H = normalize(L + V);\r\n" + // + "// vec3 reflection = normalize(reflect(-V, N));\r\n" + // + "// float NdL = clamp(dot(N, L), 0.001, 1.0);\r\n" + // + "// float NdV = clamp(abs(dot(N, V)), 0.001, 1.0);\r\n" + // + "// float NdH = clamp(dot(N, H), 0.0, 1.0);\r\n" + // + "// float LdH = clamp(dot(L, H), 0.0, 1.0);\r\n" + // + "// float VdH = clamp(dot(V, H), 0.0, 1.0);\r\n" + // + "// vec3 F = specularReflection(specularEnvR0, specularEnvR90, VdH);\r\n" + // + "// float G = geometricOcclusion(NdL, NdV, roughness);\r\n" + // + "// float D = microfacetDistribution(roughness, NdH);\r\n" + // + "// vec3 diffuseContrib = (1.0 - F) * (diffuseColor / PI);\r\n" + // + "// vec3 specContrib = F * G * D / (4.0 * NdL * NdV);\r\n" + // + " \r\n" + // + "// // Shading based off lights\r\n" + // + "// // vec3 color = NdL * uLightColor * (diffuseContrib + specContrib);\r\n" + // + "// vec3 color = NdL * (diffuseContrib + specContrib);\r\n" + // + "// // Calculate IBL lighting\r\n" + // + "// vec3 diffuseIBL;\r\n" + // + "// vec3 specularIBL;\r\n" + // + "// getIBLContribution(diffuseIBL, specularIBL, NdV, roughness, N, reflection, diffuseColor, specularColor);\r\n" + + // + "// // Add IBL on top of color\r\n" + // + "// color += specularIBL;\r\n" + // + "// color *= orm.r;\r\n" + // + "// color += getEmissiveColor();\r\n" + // + "// // Convert to sRGB to display\r\n" + // + "// gl_FragColor.rgb = color;\r\n" + // + "// gl_FragColor.a = baseDiffuseColor.a;\r\n" + // + "// }\r\n" + // + "void onlyDiffuse() {\r\n" + // + " vec4 baseColor = getDiffuseColor();\r\n" + // + " vec3 tc = getTeamColor();\r\n" + // + " float tcFactor = getOrmColor().a;\r\n" + // + " if (tcFactor > 0.1) {\r\n" + // + " baseColor.rgb *= tc * tcFactor;\r\n" + // + " }\r\n" + // + " gl_FragColor = baseColor;\r\n" + // + "}\r\n" + // + "void onlyNormalMap() {\r\n" + // + " gl_FragColor = vec4(decodeNormal(), 1.0);\r\n" + // + "}\r\n" + // + "void onlyOcclusion() {\r\n" + // + " gl_FragColor = vec4(getOrmColor().rrr, 1.0);\r\n" + // + "}\r\n" + // + "void onlyRoughness() {\r\n" + // + " gl_FragColor = vec4(getOrmColor().ggg, 1.0);\r\n" + // + "}\r\n" + // + "void onlyMetallic() {\r\n" + // + " gl_FragColor = vec4(getOrmColor().bbb, 1.0);\r\n" + // + "}\r\n" + // + "void onlyTeamColorFactor() {\r\n" + // + " gl_FragColor = vec4(getOrmColor().aaa, 1.0);\r\n" + // + "}\r\n" + // + "void onlyEmissiveMap() {\r\n" + // + " gl_FragColor = vec4(getEmissiveColor(), 1.0);\r\n" + // + "}\r\n" + // + "void onlyTexCoords() {\r\n" + // + " gl_FragColor = vec4(v_uv, 0.0, 1.0);\r\n" + // + "}\r\n" + // + "void onlyNormals() {\r\n" + // + " gl_FragColor = vec4(v_normal, 1.0);\r\n" + // + "}\r\n" + // + "#if defined(ONLY_TANGENTS)\r\n" + // + "void onlyTangents() {\r\n" + // + " gl_FragColor = vec4(v_tangent, 1.0);\r\n" + // + "}\r\n" + // + "#endif\r\n" + // + "void lambert() {\r\n" + // + " vec4 baseColor = getDiffuseColor();\r\n" + // + " vec3 normal = decodeNormal();\r\n" + // + " vec4 orm = getOrmColor();\r\n" + // + " vec3 emissive = getEmissiveColor();\r\n" + // + " vec3 tc = getTeamColor();\r\n" + // + " float aoFactor = orm.r;\r\n" + // + " float tcFactor = orm.a;\r\n" + // + " float lambertFactor = clamp(dot(normal, v_lightDir), 0.0, 1.0);\r\n" + // + " vec3 color = baseColor.rgb;\r\n" + // + (MdxHandler.CURRENT_SHADER_TYPE != ShaderEnvironmentType.MENU ? " if (tcFactor > 0.1) {\r\n" + // + " color = color * (1.0 - tcFactor) + color * tc * tcFactor;\r\n" + // + " }\r\n" : "\r\n") + + // + " \r\n" + // + " color *= clamp(lambertFactor * aoFactor + 0.1, 0.0, 1.0);\r\n" + // + " color += emissive;\r\n" + // + " gl_FragColor = vec4(color, baseColor.a);\r\n" + // + "}\r\n" + // + "void main() {\r\n" + // + " #if defined(ONLY_DIFFUSE)\r\n" + // + " onlyDiffuse();\r\n" + // + " #elif defined(ONLY_NORMAL_MAP)\r\n" + // + " onlyNormalMap();\r\n" + // + " #elif defined(ONLY_OCCLUSION)\r\n" + // + " onlyOcclusion();\r\n" + // + " #elif defined(ONLY_ROUGHNESS)\r\n" + // + " onlyRoughness();\r\n" + // + " #elif defined(ONLY_METALLIC)\r\n" + // + " onlyMetallic();\r\n" + // + " #elif defined(ONLY_TC_FACTOR)\r\n" + // + " onlyTeamColorFactor();\r\n" + // + " #elif defined(ONLY_EMISSIVE)\r\n" + // + " onlyEmissiveMap();\r\n" + // + " #elif defined(ONLY_TEXCOORDS)\r\n" + // + " onlyTexCoords();\r\n" + // + " #elif defined(ONLY_NORMALS)\r\n" + // + " onlyNormals();\r\n" + // + " #elif defined(ONLY_TANGENTS)\r\n" + // + " onlyTangents();\r\n" + // + " #else\r\n" + // + " lambert();\r\n" + // + " #endif\r\n" + // + "}"; + } public static final String vsSimple = "\r\n" + // " uniform mat4 u_VP;\r\n" + // diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java index f56d476..7ac047e 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java @@ -55,4 +55,9 @@ public class MdxSimpleInstance extends BatchedInstance { this.replaceableTextures[replaceableTextureId] = (Texture) this.model.viewer.load(replaceableTextureFile, PathSolver.DEFAULT, null); } + + @Override + public void setReplaceableTextureHD(final int replaceableTextureId, final String replaceableTextureFile) { + throw new UnsupportedOperationException(); + } } 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 6911611..5b5e184 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxViewer.java @@ -11,13 +11,14 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentTy import com.etheller.warsmash.viewer5.handlers.w3x.W3xScenePortraitLightManager; public class MdxViewer extends AbstractMdxModelViewer { + public static ShaderEnvironmentType DEFAULT_SHADER_ENV = ShaderEnvironmentType.MENU; private final WorldEditStrings worldEditStrings; private final Vector3 defaultLighting; public MdxViewer(final DataSource dataSource, final CanvasProvider canvas, final Vector3 defaultLighting) { super(dataSource, canvas); - MdxHandler.CURRENT_SHADER_TYPE = ShaderEnvironmentType.MENU; + MdxHandler.CURRENT_SHADER_TYPE = DEFAULT_SHADER_ENV; this.defaultLighting = defaultLighting; this.worldEditStrings = new WorldEditStrings(this.dataSource); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java index 76485b2..a005aca 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java @@ -9,9 +9,6 @@ import com.etheller.warsmash.util.RenderMathUtils; import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset; public class SetupGeosets { - private static final int NORMAL_BATCH = 0; - private static final int EXTENDED_BATCH = 1; - private static final int REFORGED_BATCH = 2; public static void setupGeosets(final MdxModel model, final List geosets, final boolean bigNodeSpace) { if (geosets.size() > 0) { @@ -19,9 +16,10 @@ public class SetupGeosets { int positionBytes = 0; int normalBytes = 0; int uvBytes = 0; + int tangentBytes = 0; int skinBytes = 0; int faceBytes = 0; - final int[] batchTypes = new int[geosets.size()]; + final SkinningType[] batchTypes = new SkinningType[geosets.size()]; final int extendedBatchStride = bigNodeSpace ? 36 : 9; final int normalBatchStride = bigNodeSpace ? 20 : 5; @@ -32,17 +30,21 @@ public class SetupGeosets { for (int i = 0, l = geosets.size(); i < l; i++) { final MdlxGeoset geoset = geosets.get(i); - if (true /* geoset.getLod() == 0 */) { + if ((geoset.lod == 0) || (geoset.lod == -1)) { final int vertices = geoset.getVertices().length / 3; positionBytes += vertices * 12; normalBytes += vertices * 12; uvBytes += geoset.getUvSets().length * vertices * 8; - if (false /* geoset.skin.length */) { + if (geoset.tangents != null) { + tangentBytes += vertices * 16; + } + + if (geoset.skin != null) { skinBytes += vertices * 8; - batchTypes[i] = REFORGED_BATCH; + batchTypes[i] = SkinningType.Skin; } else { long biggestGroup = 0; @@ -56,12 +58,12 @@ public class SetupGeosets { if (biggestGroup > 4) { skinBytes += vertices * extendedBatchStride; - batchTypes[i] = EXTENDED_BATCH; + batchTypes[i] = SkinningType.ExtendedVertexGroups; } else { skinBytes += vertices * normalBatchStride; - batchTypes[i] = NORMAL_BATCH; + batchTypes[i] = SkinningType.VertexGroups; } } @@ -72,7 +74,8 @@ public class SetupGeosets { int positionOffset = 0; int normalOffset = positionOffset + positionBytes; int uvOffset = normalOffset + normalBytes; - int skinOffset = uvOffset + uvBytes; + int tangentOffset = uvOffset + uvBytes; + int skinOffset = tangentOffset + tangentBytes; int faceOffset = 0; model.arrayBuffer = gl.glGenBuffer(); @@ -86,11 +89,12 @@ public class SetupGeosets { for (int i = 0, l = geosets.size(); i < l; i++) { final MdlxGeoset geoset = geosets.get(i); - final int batchType = batchTypes[i]; - if (true /* geoset.lod == 0 */) { + final SkinningType batchType = batchTypes[i]; + if ((geoset.lod == 0) || (geoset.lod == -1)) { final float[] positions = geoset.getVertices(); final float[] normals = geoset.getNormals(); final float[][] uvSets = geoset.getUvSets(); + final float[] tangents = geoset.getTangents(); final int[] faces = geoset.getFaces(); int[] skin = null; final int vertices = geoset.getVertices().length / 3; @@ -98,7 +102,7 @@ public class SetupGeosets { int maxBones; int skinStride; int boneCountOffsetBytes; - if (batchType == EXTENDED_BATCH) { + if (batchType == SkinningType.ExtendedVertexGroups) { maxBones = 8; skinStride = extendedBatchStride; boneCountOffsetBytes = extendedBatchBoneCountOffsetBytes; @@ -109,8 +113,11 @@ public class SetupGeosets { boneCountOffsetBytes = normalBatchBoneCountOffsetBytes; } - if (batchType == REFORGED_BATCH) { - // skin = geoset.skin; // THIS IS NOT IMPLEMENTED + if (batchType == SkinningType.Skin) { + skin = new int[geoset.skin.length]; + for (int j = 0; j < geoset.skin.length; j++) { + skin[j] = geoset.skin[j]; + } } else { final long[] matrixIndices = geoset.getMatrixIndices(); @@ -157,20 +164,20 @@ public class SetupGeosets { final boolean unselectable = geoset.getSelectionFlags() == 4; final Geoset vGeoset = new Geoset(model, model.getGeosets().size(), positionOffset, normalOffset, - uvOffset, skinOffset, faceOffset, vertices, faces.length, openGLSkinType, skinStride, - boneCountOffsetBytes, unselectable, geoset); + uvOffset, tangentOffset, skinOffset, faceOffset, vertices, faces.length, openGLSkinType, + skinStride, boneCountOffsetBytes, unselectable, geoset); model.getGeosets().add(vGeoset); - if (batchType == REFORGED_BATCH) { - throw new UnsupportedOperationException("NYI"); -// model.batches.add(new Reforged) + final Material material = model.materials.get((int) geoset.getMaterialId()); + final boolean isHd = "Shader_HD_DefaultUnit".equals(material.shader); + + if (isHd) { + model.batches.add(new Batch(model.batches.size(), vGeoset, material, batchType)); } else { - final boolean isExtended = batchType == EXTENDED_BATCH; - for (final Layer layer : model.getMaterials().get((int) geoset.getMaterialId()).layers) { - model.batches.add(new Batch(model.batches.size(), vGeoset, layer, isExtended)); + model.batches.add(new Batch(model.batches.size(), vGeoset, layer, batchType)); } } @@ -190,6 +197,12 @@ public class SetupGeosets { uvOffset += uvSet.length * 4; } + if (tangents != null) { + gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, tangentOffset, tangents.length, + RenderMathUtils.wrap(tangents)); + tangentOffset += tangents.length * 4; + } + // Skin. gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, bigNodeSpace ? RenderMathUtils.wrap(skin) : RenderMathUtils.wrapAsBytes(skin)); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGroups.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGroups.java index 0c82145..5366083 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGroups.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGroups.java @@ -64,7 +64,8 @@ public class SetupGroups { public static boolean matchingGroup(final Object group, final Object object) { if (group instanceof BatchGroup) { - return (object instanceof Batch) && (((Batch) object).isExtended == ((BatchGroup) group).isExtended); + return (object instanceof Batch) && (((Batch) object).skinningType == ((BatchGroup) group).skinningType) + && (((Batch) object).hd == ((BatchGroup) group).hd); // } else if(group instanceof ReforgedBatch) { TODO // return (object instanceof ReforgedBatch) && (object.material.shader === group.shader); } @@ -76,7 +77,7 @@ public class SetupGroups { public static GenericGroup createMatchingGroup(final MdxModel model, final Object object) { if (object instanceof Batch) { - return new BatchGroup(model, ((Batch) object).isExtended); + return new BatchGroup(model, ((Batch) object).skinningType, ((Batch) object).hd); // } else if(object instanceof ReforgedBatch) { TODO // return new ReforgedBatchGroup(model, ((ReforgedBatch)object).material.shader); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupSimpleGroups.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupSimpleGroups.java index ebb45ce..ddf753b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupSimpleGroups.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupSimpleGroups.java @@ -43,7 +43,7 @@ public class SetupSimpleGroups { GenericGroup simpleGroup; if (group instanceof BatchGroup) { - simpleGroup = new BatchGroup(model, ((BatchGroup) group).isExtended); + simpleGroup = new BatchGroup(model, ((BatchGroup) group).skinningType, ((BatchGroup) group).hd); } else { throw new IllegalStateException("reforged?"); // TODO diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SkinningType.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SkinningType.java new file mode 100644 index 0000000..c78aae0 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SkinningType.java @@ -0,0 +1,7 @@ +package com.etheller.warsmash.viewer5.handlers.mdx; + +public enum SkinningType { + Skin, + ExtendedVertexGroups, + VertexGroups; +} 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 8b442fa..6ee2ba5 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 @@ -55,6 +55,7 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget { this.replaceableTextureFile += "Blight"; } this.instance.setReplaceableTexture(this.replaceableTextureId, this.replaceableTextureFile + ".blp"); + this.instance.setReplaceableTextureHD(this.replaceableTextureId, this.replaceableTextureFile); } this.selectionScale *= row.getFieldAsFloat(SEL_CIRCLE_SIZE, 0); this.unitAnimationListenerImpl = new UnitAnimationListenerImpl((MdxComplexInstance) this.instance, 0, 0); diff --git a/desktop/src/com/etheller/warsmash/desktop/editor/mdx/MdxEditorMain.java b/desktop/src/com/etheller/warsmash/desktop/editor/mdx/MdxEditorMain.java index ae7430b..6081dce 100644 --- a/desktop/src/com/etheller/warsmash/desktop/editor/mdx/MdxEditorMain.java +++ b/desktop/src/com/etheller/warsmash/desktop/editor/mdx/MdxEditorMain.java @@ -6,11 +6,14 @@ import javax.swing.UIManager; import com.etheller.warsmash.desktop.DesktopLauncher; import com.etheller.warsmash.desktop.editor.mdx.ui.YseraFrame; import com.etheller.warsmash.units.DataTable; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler.ShaderEnvironmentType; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer; public class MdxEditorMain { public static void main(final String[] args) { DesktopLauncher.loadExtensions(); + MdxViewer.DEFAULT_SHADER_ENV = ShaderEnvironmentType.GAME; try { // UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"); diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java index ca26561..37bd1f6 100644 --- a/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java @@ -38,7 +38,7 @@ public class JassArrayedAssignmentStatement implements JassStatement { if (arrayValue != null) { final Integer indexInt = index.visit(IntegerJassValueVisitor.getInstance()); if ((indexInt != null) && (indexInt >= 0)) { - arrayValue.set(indexInt, this.expression.evaluate(globalScope, localScope, triggerScope)); + arrayValue.set(globalScope, indexInt, this.expression.evaluate(globalScope, localScope, triggerScope)); } else { throw new JassException(globalScope, diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java index 58b302c..bd914b5 100644 --- a/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java @@ -1,7 +1,6 @@ package com.etheller.interpreter.ast.statement; import com.etheller.interpreter.ast.Assignable; -import com.etheller.interpreter.ast.debug.JassException; import com.etheller.interpreter.ast.expression.JassExpression; import com.etheller.interpreter.ast.scope.GlobalScope; import com.etheller.interpreter.ast.scope.LocalScope; @@ -11,7 +10,6 @@ import com.etheller.interpreter.ast.value.JassValue; public class JassSetStatement implements JassStatement { private final String identifier; private final JassExpression expression; - private static JassException zeroGuy = null; public JassSetStatement(final String identifier, final JassExpression expression) { this.identifier = identifier; diff --git a/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java index 5516e93..7365c37 100644 --- a/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java +++ b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java @@ -2,6 +2,8 @@ package com.etheller.interpreter.ast.value; import java.util.Arrays; +import com.etheller.interpreter.ast.debug.JassException; +import com.etheller.interpreter.ast.scope.GlobalScope; import com.etheller.interpreter.ast.util.JassSettings; import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor; @@ -33,7 +35,7 @@ public class ArrayJassValue implements JassValue { return visitor.accept(this); } - public void set(final int index, JassValue value) { + public void set(final GlobalScope globalScope, final int index, JassValue value) { final JassType primitiveType = this.type.getPrimitiveType(); if (value == null) { if (primitiveType.isNullable()) { @@ -49,6 +51,10 @@ public class ArrayJassValue implements JassValue { throw new IllegalStateException("Illegal type for assignment to " + primitiveType.getName() + " array: " + (valueType == null ? "null" : valueType.getName())); } + if (index >= this.data.length) { + throw new JassException(globalScope, "Max jass array size exceeded", + new ArrayIndexOutOfBoundsException(index)); + } this.data[index] = value; }