mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Terrain as separate file
This commit is contained in:
parent
e998367f77
commit
bfab516832
@ -184,7 +184,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
// }
|
||||
}
|
||||
|
||||
private final float cameraSpeed = 100.0f;
|
||||
private final float cameraSpeed = 10.0f;
|
||||
private final Vector2 cameraVelocity = new Vector2();
|
||||
|
||||
@Override
|
||||
|
@ -10,8 +10,8 @@ import com.google.common.io.LittleEndianDataOutputStream;
|
||||
* A tile corner.
|
||||
*/
|
||||
public class Corner {
|
||||
private int groundHeight;
|
||||
private int waterHeight;
|
||||
private float groundHeight;
|
||||
private float waterHeight;
|
||||
private int mapEdge;
|
||||
private int ramp;
|
||||
private int blight;
|
||||
@ -24,10 +24,10 @@ public class Corner {
|
||||
private int layerHeight;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.groundHeight = (stream.readShort() - 8192) / 512;
|
||||
this.groundHeight = (stream.readShort() - 8192) / (float) 512;
|
||||
|
||||
final short waterAndEdge = stream.readShort();
|
||||
this.waterHeight = ((waterAndEdge & 0x3FFF) - 8192) / 512;
|
||||
this.waterHeight = ((waterAndEdge & 0x3FFF) - 8192) / (float) 512;
|
||||
this.mapEdge = waterAndEdge & 0x4000;
|
||||
|
||||
final short textureAndFlags = ParseUtils.readUInt8(stream);
|
||||
@ -52,19 +52,19 @@ public class Corner {
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeShort((this.groundHeight * 512) + 8192);
|
||||
stream.writeShort((this.waterHeight + 8192 + this.mapEdge) << 14);
|
||||
stream.writeShort((short) ((this.groundHeight * 512f) + 8192f));
|
||||
stream.writeShort((short) ((this.waterHeight * 512f) + 8192f + (this.mapEdge << 14)));
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.ramp << 4) | (this.blight << 5) | (this.water << 6)
|
||||
| (this.boundary << 7) | this.groundTexture));
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.cliffVariation << 5) | this.groundVariation));
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.cliffTexture << 4) + this.layerHeight));
|
||||
}
|
||||
|
||||
public int getGroundHeight() {
|
||||
public float getGroundHeight() {
|
||||
return this.groundHeight;
|
||||
}
|
||||
|
||||
public int getWaterHeight() {
|
||||
public float getWaterHeight() {
|
||||
return this.waterHeight;
|
||||
}
|
||||
|
||||
@ -107,4 +107,12 @@ public class Corner {
|
||||
public int getLayerHeight() {
|
||||
return this.layerHeight;
|
||||
}
|
||||
|
||||
public float computeFinalGroundHeight() {
|
||||
return (this.groundHeight + this.layerHeight) - 2.0f;
|
||||
}
|
||||
|
||||
public float computeFinalWaterHeight(final float waterOffset) {
|
||||
return this.waterHeight + waterOffset;
|
||||
}
|
||||
}
|
||||
|
@ -41,5 +41,258 @@ public class HiveWEShaders {
|
||||
"\r\n" + //
|
||||
" Normal = terrain_normal;\r\n" + //
|
||||
"}";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 0) uniform sampler2DArray cliff_textures;\r\n" + //
|
||||
"layout (binding = 2) uniform usampler2D pathing_map_static;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 1) uniform bool show_pathing_map_static;\r\n" + //
|
||||
"layout (location = 2) uniform bool show_lighting;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec3 UV;\r\n" + //
|
||||
"layout (location = 1) in vec3 Normal;\r\n" + //
|
||||
"layout (location = 2) in vec2 pathing_map_uv;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec4 color;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" color = texture(cliff_textures, UV);\r\n" + //
|
||||
"\r\n" + //
|
||||
" if (show_lighting) {\r\n" + //
|
||||
" vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
|
||||
" light_direction = normalize(light_direction);\r\n" + //
|
||||
"\r\n" + //
|
||||
" color.rgb *= clamp(dot(Normal, light_direction) + 0.45, 0, 1);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" uvec4 byte = texelFetch(pathing_map_static, ivec2(pathing_map_uv), 0);\r\n" + //
|
||||
" if (show_pathing_map_static) {\r\n" + //
|
||||
" vec4 pathing_color = vec4(min(byte.r & 2, 1), min(byte.r & 4, 1), min(byte.r & 8, 1), 0.25);\r\n"
|
||||
+ //
|
||||
" color = length(pathing_color.rgb) > 0 ? color * 0.75 + pathing_color * 0.5 : color;\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}";
|
||||
}
|
||||
|
||||
public static final class Terrain {
|
||||
private Terrain() {
|
||||
}
|
||||
|
||||
public static final String vert = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec2 vPosition;\r\n" + //
|
||||
"layout (location = 1) uniform mat4 MVP;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 0) uniform sampler2D height_texture;\r\n" + //
|
||||
"layout (binding = 1) uniform sampler2D height_cliff_texture;\r\n" + //
|
||||
"layout (binding = 2) uniform usampler2D terrain_texture_list;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec2 UV;\r\n" + //
|
||||
"layout (location = 1) out flat uvec4 texture_indices;\r\n" + //
|
||||
"layout (location = 2) out vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 3) out vec3 normal;\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" + //
|
||||
" 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" + //
|
||||
" gl_Position = ((texture_indices.a & 32768) == 0) ? MVP * vec4(vPosition + pos, height.r, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
|
||||
+ //
|
||||
"}";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 2) uniform bool show_pathing_map;\r\n" + //
|
||||
"layout (location = 3) uniform bool show_lighting;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 3) uniform sampler2DArray sample0;\r\n" + //
|
||||
"layout (binding = 4) uniform sampler2DArray sample1;\r\n" + //
|
||||
"layout (binding = 5) uniform sampler2DArray sample2;\r\n" + //
|
||||
"layout (binding = 6) uniform sampler2DArray sample3;\r\n" + //
|
||||
"layout (binding = 7) uniform sampler2DArray sample4;\r\n" + //
|
||||
"layout (binding = 8) uniform sampler2DArray sample5;\r\n" + //
|
||||
"layout (binding = 9) uniform sampler2DArray sample6;\r\n" + //
|
||||
"layout (binding = 10) uniform sampler2DArray sample7;\r\n" + //
|
||||
"layout (binding = 11) uniform sampler2DArray sample8;\r\n" + //
|
||||
"layout (binding = 12) uniform sampler2DArray sample9;\r\n" + //
|
||||
"layout (binding = 13) uniform sampler2DArray sample10;\r\n" + //
|
||||
"layout (binding = 14) uniform sampler2DArray sample11;\r\n" + //
|
||||
"layout (binding = 15) uniform sampler2DArray sample12;\r\n" + //
|
||||
"layout (binding = 16) uniform sampler2DArray sample13;\r\n" + //
|
||||
"layout (binding = 17) uniform sampler2DArray sample14;\r\n" + //
|
||||
"layout (binding = 18) uniform sampler2DArray sample15;\r\n" + //
|
||||
"layout (binding = 19) uniform sampler2DArray sample16;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 20) uniform usampler2D pathing_map_static;\r\n" + //
|
||||
"layout (binding = 21) uniform usampler2D pathing_map_dynamic;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec2 UV;\r\n" + //
|
||||
"layout (location = 1) in flat uvec4 texture_indices;\r\n" + //
|
||||
"layout (location = 2) in vec2 pathing_map_uv;\r\n" + //
|
||||
"layout (location = 3) in vec3 normal;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec4 color;\r\n" + //
|
||||
"layout (location = 1) out vec4 position;\r\n" + //
|
||||
"\r\n" + //
|
||||
"vec4 get_fragment(uint id, vec3 uv) {\r\n" + //
|
||||
" vec2 dx = dFdx(uv.xy);\r\n" + //
|
||||
" vec2 dy = dFdy(uv.xy);\r\n" + //
|
||||
"\r\n" + //
|
||||
" switch(id) {\r\n" + //
|
||||
" case 0:\r\n" + //
|
||||
" return textureGrad(sample0, uv, dx, dy);\r\n" + //
|
||||
" case 1:\r\n" + //
|
||||
" return textureGrad(sample1, uv, dx, dy);\r\n" + //
|
||||
" case 2:\r\n" + //
|
||||
" return textureGrad(sample2, uv, dx, dy);\r\n" + //
|
||||
" case 3:\r\n" + //
|
||||
" return textureGrad(sample3, uv, dx, dy);\r\n" + //
|
||||
" case 4:\r\n" + //
|
||||
" return textureGrad(sample4, uv, dx, dy);\r\n" + //
|
||||
" case 5:\r\n" + //
|
||||
" return textureGrad(sample5, uv, dx, dy);\r\n" + //
|
||||
" case 6:\r\n" + //
|
||||
" return textureGrad(sample6, uv, dx, dy);\r\n" + //
|
||||
" case 7:\r\n" + //
|
||||
" return textureGrad(sample7, uv, dx, dy);\r\n" + //
|
||||
" case 8:\r\n" + //
|
||||
" return textureGrad(sample8, uv, dx, dy);\r\n" + //
|
||||
" case 9:\r\n" + //
|
||||
" return textureGrad(sample9, uv, dx, dy);\r\n" + //
|
||||
" case 10:\r\n" + //
|
||||
" return textureGrad(sample10, uv, dx, dy);\r\n" + //
|
||||
" case 11:\r\n" + //
|
||||
" return textureGrad(sample11, uv, dx, dy);\r\n" + //
|
||||
" case 12:\r\n" + //
|
||||
" return textureGrad(sample12, uv, dx, dy);\r\n" + //
|
||||
" case 13:\r\n" + //
|
||||
" return textureGrad(sample13, uv, dx, dy);\r\n" + //
|
||||
" case 14:\r\n" + //
|
||||
" return textureGrad(sample14, uv, dx, dy);\r\n" + //
|
||||
" case 15:\r\n" + //
|
||||
" return textureGrad(sample15, uv, dx, dy);\r\n" + //
|
||||
" case 16:\r\n" + //
|
||||
" return textureGrad(sample16, uv, dx, dy);\r\n" + //
|
||||
" case 17:\r\n" + //
|
||||
" return vec4(0, 0, 0, 0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}\r\n" + //
|
||||
"\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" color = get_fragment(texture_indices.a & 31, vec3(UV, texture_indices.a >> 5));\r\n" + //
|
||||
" color = color * color.a + get_fragment(texture_indices.b & 31, vec3(UV, texture_indices.b >> 5)) * (1 - color.a);\r\n"
|
||||
+ //
|
||||
" color = color * color.a + get_fragment(texture_indices.g & 31, vec3(UV, texture_indices.g >> 5)) * (1 - color.a);\r\n"
|
||||
+ //
|
||||
" color = color * color.a + get_fragment(texture_indices.r & 31, vec3(UV, texture_indices.r >> 5)) * (1 - color.a);\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" if (show_lighting) {\r\n" + //
|
||||
" vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
|
||||
" light_direction = normalize(light_direction);\r\n" + //
|
||||
"\r\n" + //
|
||||
" color.rgb *= clamp(dot(normal, light_direction) + 0.45, 0, 1);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" uint byte_static = texelFetch(pathing_map_static, ivec2(pathing_map_uv), 0).r;\r\n" + //
|
||||
" uint byte_dynamic = texelFetch(pathing_map_dynamic, ivec2(pathing_map_uv), 0).r;\r\n" + //
|
||||
" if (show_pathing_map) {\r\n" + //
|
||||
" uint final = byte_static.r | byte_dynamic.r;\r\n" + //
|
||||
"\r\n" + //
|
||||
" vec4 pathing_static_color = vec4((final & 2) >> 1, (final & 4) >> 2, (final & 8) >> 3, 0.25);\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" color = length(pathing_static_color.rgb) > 0 ? color * 0.75 + pathing_static_color * 0.5 : color;\r\n"
|
||||
+ //
|
||||
" }\r\n" + //
|
||||
"}";
|
||||
}
|
||||
|
||||
public static final class Water {
|
||||
private Water() {
|
||||
}
|
||||
|
||||
public static final String vert = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec2 vPosition;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 0) uniform sampler2D water_height_texture;\r\n" + //
|
||||
"layout (binding = 1) uniform sampler2D ground_height_texture;\r\n" + //
|
||||
"layout (binding = 2) uniform sampler2D water_exists_texture;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) uniform mat4 MVP;\r\n" + //
|
||||
"layout (location = 1) uniform vec4 shallow_color_min;\r\n" + //
|
||||
"layout (location = 2) uniform vec4 shallow_color_max;\r\n" + //
|
||||
"layout (location = 3) uniform vec4 deep_color_min;\r\n" + //
|
||||
"layout (location = 4) uniform vec4 deep_color_max;\r\n" + //
|
||||
"layout (location = 5) uniform float water_offset;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec2 UV;\r\n" + //
|
||||
"out vec4 Color;\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" + //
|
||||
" gl_Position = is_water ? MVP * vec4(vPosition + pos, water_height, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" UV = vec2(vPosition.x, 1 - vPosition.y);\r\n" + //
|
||||
"\r\n" + //
|
||||
" float ground_height = texelFetch(ground_height_texture, height_pos, 0).r;\r\n" + //
|
||||
" float value = clamp(water_height - ground_height, 0.f, 1.f);\r\n" + //
|
||||
" 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" + //
|
||||
" }";
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 3) uniform sampler2DArray water_textures;\r\n" + //
|
||||
"layout (binding = 2) uniform sampler2D water_exists_texture;\r\n" + //
|
||||
"\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 6) uniform int current_texture;\r\n" + //
|
||||
"\r\n" + //
|
||||
"in vec2 UV;\r\n" + //
|
||||
"in vec4 Color;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec4 outColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" outColor = texture(water_textures, vec3(UV, current_texture)) * Color;\r\n" + //
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public class TerrainDoodad {
|
||||
|
||||
public TerrainDoodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.TerrainDoodad doodad) {
|
||||
final float[] centerOffset = map.centerOffset;
|
||||
final float[] centerOffset = map.terrain.centerOffset;
|
||||
final MdxSimpleInstance instance = (MdxSimpleInstance) model.addInstance(1);
|
||||
|
||||
locationHeap[0] = (doodad.getLocation()[0] * 128) + centerOffset[0] + 128;
|
||||
|
@ -6,16 +6,8 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.common.FetchDataTypeName;
|
||||
import com.etheller.warsmash.common.LoadGenericCallback;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
@ -23,18 +15,12 @@ import com.etheller.warsmash.parsers.w3x.War3Map;
|
||||
import com.etheller.warsmash.parsers.w3x.doo.War3MapDoo;
|
||||
import com.etheller.warsmash.parsers.w3x.objectdata.Warcraft3MapObjectData;
|
||||
import com.etheller.warsmash.parsers.w3x.unitsdoo.War3MapUnitsDoo;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.Corner;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.MappedData;
|
||||
import com.etheller.warsmash.util.MappedDataRow;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
import com.etheller.warsmash.viewer5.GenericResource;
|
||||
@ -43,36 +29,20 @@ import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain;
|
||||
|
||||
public class War3MapViewer extends ModelViewer {
|
||||
private static final float[] sizeHeap = new float[2];
|
||||
private static final War3ID sloc = War3ID.fromString("sloc");
|
||||
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
|
||||
private static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
|
||||
|
||||
private static final Vector3 normalHeap1 = new Vector3();
|
||||
private static final Vector3 normalHeap2 = new Vector3();
|
||||
public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
|
||||
|
||||
public PathSolver wc3PathSolver = PathSolver.DEFAULT;
|
||||
public SolverParams solverParams = new SolverParams();
|
||||
public ShaderProgram groundShader;
|
||||
public ShaderProgram waterShader;
|
||||
public ShaderProgram cliffShader;
|
||||
public Scene worldScene;
|
||||
public float waterIndex;
|
||||
public float waterIncreasePerFrame;
|
||||
public float waterHeightOffset;
|
||||
public List<Texture> waterTextures = new ArrayList<>();
|
||||
public float[] maxDeepColor = new float[4];
|
||||
public float[] minDeepColor = new float[4];
|
||||
public float[] maxShallowColor = new float[4];
|
||||
public float[] minShallowColor = new float[4];
|
||||
public boolean anyReady;
|
||||
public boolean terrainCliffsAndWaterLoaded;
|
||||
public MappedData terrainData = new MappedData();
|
||||
@ -92,28 +62,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
public MappedData unitMetaData = new MappedData();
|
||||
public List<Unit> units = new ArrayList<>();
|
||||
public boolean unitsReady;
|
||||
public List<Texture> tilesetTextures = new ArrayList<>();
|
||||
public List<Texture> cliffTextures = new ArrayList<>();
|
||||
public List<TerrainModel> cliffModels = new ArrayList<>();
|
||||
public War3Map mapMpq;
|
||||
public PathSolver mapPathSolver = PathSolver.DEFAULT;
|
||||
public Corner[][] corners;
|
||||
public float[] centerOffset = new float[2];
|
||||
public int[] mapSize = new int[2];
|
||||
public List<MappedDataRow> tilesets = new ArrayList<>(); // TODO
|
||||
public int blightTextureIndex = -1;
|
||||
public List<MappedDataRow> cliffTilesets = new ArrayList<>();
|
||||
public int columns;
|
||||
public int rows;
|
||||
public int vertexBuffer;
|
||||
public int faceBuffer;
|
||||
public int instanceBuffer;
|
||||
public int textureBuffer;
|
||||
public int variationBuffer;
|
||||
public int waterBuffer;
|
||||
public int heightMap;
|
||||
public int waterHeightMap;
|
||||
public int cliffHeightMap;
|
||||
|
||||
private final DataSource gameDataSource;
|
||||
|
||||
@ -127,9 +77,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
this.wc3PathSolver = PathSolver.DEFAULT;
|
||||
|
||||
this.groundShader = this.webGL.createShaderProgram(W3xShaders.Ground.vert, W3xShaders.Ground.frag);
|
||||
this.waterShader = this.webGL.createShaderProgram(W3xShaders.Water.vert, W3xShaders.Water.frag);
|
||||
this.cliffShader = this.webGL.createShaderProgram(W3xShaders.Cliffs.vert, W3xShaders.Cliffs.frag);
|
||||
this.terrain = new Terrain(webGL);
|
||||
|
||||
this.worldScene = this.addScene();
|
||||
|
||||
@ -219,9 +167,10 @@ public class War3MapViewer extends ModelViewer {
|
||||
final float[] centerOffset = terrainData.getCenterOffset();
|
||||
final int[] mapSize = terrainData.getMapSize();
|
||||
|
||||
this.corners = terrainData.getCorners();
|
||||
System.arraycopy(centerOffset, 0, this.centerOffset, 0, centerOffset.length);
|
||||
System.arraycopy(mapSize, 0, this.mapSize, 0, mapSize.length);
|
||||
this.terrain.load(terrainData, centerOffset, mapSize, this);
|
||||
this.terrainReady = true;
|
||||
this.anyReady = true;
|
||||
this.cliffsReady = true;
|
||||
|
||||
// Override the grid based on the map.
|
||||
this.worldScene.grid = new Grid(centerOffset[0], centerOffset[1], (mapSize[0] * 128) - 128,
|
||||
@ -252,248 +201,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
private void loadTerrainCliffsAndWater(final War3MapW3e w3e) {
|
||||
final String texturesExt = this.solverParams.reforged ? ".dds" : ".blp";
|
||||
final char tileset = w3e.getTileset();
|
||||
|
||||
for (final War3ID groundTile : w3e.getGroundTiles()) {
|
||||
final MappedDataRow row = this.terrainData.getRow(groundTile.asStringValue());
|
||||
|
||||
this.tilesets.add(row);
|
||||
this.tilesetTextures
|
||||
.add((Texture) this.load(row.get("dir").toString() + "\\" + row.get("file") + texturesExt,
|
||||
this.mapPathSolver, this.solverParams));
|
||||
}
|
||||
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(this.mapMpq.getCompoundDataSource());
|
||||
final DataTable worldEditData = standardObjectData.getWorldEditData();
|
||||
final Element tilesets = worldEditData.get("TileSets");
|
||||
|
||||
this.blightTextureIndex = this.tilesetTextures.size();
|
||||
this.tilesetTextures
|
||||
.add((Texture) this.load(tilesets.getField(Character.toString(tileset)).split(",")[1] + texturesExt,
|
||||
this.mapPathSolver, this.solverParams));
|
||||
|
||||
for (final War3ID cliffTile : w3e.getCliffTiles()) {
|
||||
final MappedDataRow row = this.cliffTypesData.getRow(cliffTile.asStringValue());
|
||||
|
||||
this.cliffTilesets.add(row);
|
||||
this.cliffTextures
|
||||
.add((Texture) this.load(row.get("texDir").toString() + "\\" + row.get("texFile") + texturesExt,
|
||||
this.mapPathSolver, this.solverParams));
|
||||
}
|
||||
|
||||
final MappedDataRow waterRow = this.waterData.getRow(tileset + "Sha");
|
||||
|
||||
this.waterHeightOffset = ((Number) waterRow.get("height")).floatValue();
|
||||
this.waterIncreasePerFrame = ((Number) waterRow.get("texRate")).intValue() / (float) 60;
|
||||
this.waterTextures.clear();
|
||||
this.maxDeepColor[0] = ((Number) waterRow.get("Dmax_R")).floatValue();
|
||||
this.maxDeepColor[1] = ((Number) waterRow.get("Dmax_G")).floatValue();
|
||||
this.maxDeepColor[2] = ((Number) waterRow.get("Dmax_B")).floatValue();
|
||||
this.maxDeepColor[3] = ((Number) waterRow.get("Dmax_A")).floatValue();
|
||||
this.minDeepColor[0] = ((Number) waterRow.get("Dmin_R")).floatValue();
|
||||
this.minDeepColor[1] = ((Number) waterRow.get("Dmin_G")).floatValue();
|
||||
this.minDeepColor[2] = ((Number) waterRow.get("Dmin_B")).floatValue();
|
||||
this.minDeepColor[3] = ((Number) waterRow.get("Dmin_A")).floatValue();
|
||||
this.maxShallowColor[0] = ((Number) waterRow.get("Smax_R")).floatValue();
|
||||
this.maxShallowColor[1] = ((Number) waterRow.get("Smax_G")).floatValue();
|
||||
this.maxShallowColor[2] = ((Number) waterRow.get("Smax_B")).floatValue();
|
||||
this.maxShallowColor[3] = ((Number) waterRow.get("Smax_A")).floatValue();
|
||||
this.minShallowColor[0] = ((Number) waterRow.get("Smin_R")).floatValue();
|
||||
this.minShallowColor[1] = ((Number) waterRow.get("Smin_G")).floatValue();
|
||||
this.minShallowColor[2] = ((Number) waterRow.get("Smin_B")).floatValue();
|
||||
this.minShallowColor[3] = ((Number) waterRow.get("Smin_A")).floatValue();
|
||||
|
||||
for (int i = 0, l = ((Number) waterRow.get("numTex")).intValue(); i < l; i++) {
|
||||
this.waterTextures.add(
|
||||
(Texture) this.load(waterRow.get("texFile").toString() + ((i < 10) ? "0" : "") + i + texturesExt,
|
||||
this.mapPathSolver, this.solverParams));
|
||||
}
|
||||
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
final Corner[][] corners = w3e.getCorners();
|
||||
final int columns = this.mapSize[0];
|
||||
final int rows = this.mapSize[1];
|
||||
final float[] centerOffset = this.centerOffset;
|
||||
final int instanceCount = (columns - 1) * (rows - 1);
|
||||
final float[] cliffHeights = new float[columns * rows];
|
||||
final float[] cornerHeights = new float[columns * rows];
|
||||
final float[] waterHeights = new float[columns * rows];
|
||||
final short[] cornerTextures = new short[instanceCount * 4];
|
||||
final short[] cornerVariations = new short[instanceCount * 4];
|
||||
final short[] waterFlags = new short[instanceCount];
|
||||
int instance = 0;
|
||||
final Map<String, CliffInfo> cliffs = new HashMap<>();
|
||||
|
||||
this.columns = columns - 1;
|
||||
this.rows = rows - 1;
|
||||
|
||||
for (int y = 0; y < rows; y++) {
|
||||
for (int x = 0; x < columns; x++) {
|
||||
final Corner bottomLeft = corners[y][x];
|
||||
final int index = (y * columns) + x;
|
||||
|
||||
cliffHeights[index] = bottomLeft.getGroundHeight();
|
||||
cornerHeights[index] = (bottomLeft.getGroundHeight() + bottomLeft.getLayerHeight()) - 2;
|
||||
waterHeights[index] = bottomLeft.getWaterHeight();
|
||||
|
||||
if ((y < (rows - 1)) && (x < (columns - 1))) {
|
||||
// Water can be used with cliffs and normal corners, so store water state
|
||||
// regardless.
|
||||
waterFlags[instance] = this.isWater(x, y);
|
||||
|
||||
// Is this a cliff, or a normal corner?
|
||||
if (this.isCliff(x, y)) {
|
||||
final int bottomLeftLayer = bottomLeft.getLayerHeight();
|
||||
final int bottomRightLayer = corners[y][x + 1].getLayerHeight();
|
||||
final int topLeftLayer = corners[y + 1][x].getLayerHeight();
|
||||
final int topRightLayer = corners[y + 1][x + 1].getLayerHeight();
|
||||
final int base = Math.min(Math.min(bottomLeftLayer, bottomRightLayer),
|
||||
Math.min(topLeftLayer, topRightLayer));
|
||||
final String fileName = this.cliffFileName(bottomLeftLayer, bottomRightLayer, topLeftLayer,
|
||||
topRightLayer, base);
|
||||
|
||||
if (!"AAAA".equals(fileName)) {
|
||||
int cliffTexture = bottomLeft.getCliffTexture();
|
||||
|
||||
// ?
|
||||
if (cliffTexture == 15) {
|
||||
cliffTexture = 1;
|
||||
}
|
||||
|
||||
final MappedDataRow cliffRow = this.cliffTilesets.get(cliffTexture);
|
||||
final String dir = cliffRow.get("cliffModelDir").toString();
|
||||
final String path = "Doodads\\Terrain\\" + dir + "\\" + dir + fileName
|
||||
+ Variations.getCliffVariation(dir, fileName, bottomLeft.getCliffVariation())
|
||||
+ ".mdx";
|
||||
|
||||
if (!cliffs.containsKey(path)) {
|
||||
cliffs.put(path, new CliffInfo());
|
||||
}
|
||||
|
||||
cliffs.get(path).locations.add(new float[] { ((x + 1) * 128) + centerOffset[0],
|
||||
(y * 128) + centerOffset[1], (base - 2) * 128 });
|
||||
cliffs.get(path).textures.add(cliffTexture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final int bottomLeftTexture = this.cornerTexture(x, y);
|
||||
final int bottomRightTexture = this.cornerTexture(x + 1, y);
|
||||
final int topLeftTexture = this.cornerTexture(x, y + 1);
|
||||
final int topRightTexture = this.cornerTexture(x + 1, y + 1);
|
||||
final LinkedHashSet<Integer> texturesUnique = new LinkedHashSet<>();
|
||||
texturesUnique.add(bottomLeftTexture);
|
||||
texturesUnique.add(bottomRightTexture);
|
||||
texturesUnique.add(topLeftTexture);
|
||||
texturesUnique.add(topRightTexture);
|
||||
final List<Integer> textures = new ArrayList<>(texturesUnique);
|
||||
Collections.sort(textures);
|
||||
|
||||
int texture = textures.remove(0);
|
||||
|
||||
cornerTextures[instance * 4] = (short) (texture + 1);
|
||||
cornerVariations[instance * 4] = this.getVariation(texture, bottomLeft.getGroundVariation());
|
||||
|
||||
for (int i = 0, l = textures.size(); i < l; i++) {
|
||||
int bitset = 0;
|
||||
|
||||
texture = textures.get(i);
|
||||
|
||||
if (bottomRightTexture == texture) {
|
||||
bitset |= 0b0001;
|
||||
}
|
||||
|
||||
if (bottomLeftTexture == texture) {
|
||||
bitset |= 0b0010;
|
||||
}
|
||||
|
||||
if (topRightTexture == texture) {
|
||||
bitset |= 0b0100;
|
||||
}
|
||||
|
||||
if (topLeftTexture == texture) {
|
||||
bitset |= 0b1000;
|
||||
}
|
||||
|
||||
cornerTextures[(instance * 4) + 1 + i] = (short) (texture + 1);
|
||||
cornerVariations[(instance * 4) + 1 + i] = (short) (bitset);
|
||||
}
|
||||
}
|
||||
|
||||
instance += 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.vertexBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, 8 * 4, RenderMathUtils.wrap(new float[] { 0, 0, 1, 0, 0, 1, 1, 1 }),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.faceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 6, RenderMathUtils.wrap(new byte[] { 0, 1, 2, 1, 3, 2 }),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.cliffHeightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffHeightMap);
|
||||
this.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(cliffHeights));
|
||||
|
||||
this.heightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
this.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(cornerHeights));
|
||||
|
||||
this.waterHeightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.waterHeightMap);
|
||||
this.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(waterHeights));
|
||||
|
||||
this.instanceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
final float[] instanceBufferData = new float[instanceCount];
|
||||
for (int i = 0; i < instanceBufferData.length; i++) {
|
||||
instanceBufferData[i] = i;
|
||||
}
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, instanceBufferData.length * 4, RenderMathUtils.wrap(instanceBufferData),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.textureBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.textureBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, cornerTextures.length, RenderMathUtils.wrap(cornerTextures),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.variationBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.variationBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, cornerVariations.length, RenderMathUtils.wrap(cornerVariations),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.waterBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.waterBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, waterFlags.length, RenderMathUtils.wrap(waterFlags), GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.terrainReady = true;
|
||||
this.anyReady = true;
|
||||
|
||||
final ShaderProgram cliffShader = this.cliffShader;
|
||||
this.cliffModels.clear();
|
||||
for (final Map.Entry<String, CliffInfo> entry : cliffs.entrySet()) {
|
||||
final String path = entry.getKey();
|
||||
final CliffInfo cliffInfo = entry.getValue();
|
||||
|
||||
final GenericResource resource = this.loadMapGeneric(path, FetchDataTypeName.ARRAY_BUFFER,
|
||||
streamDataCallback);
|
||||
|
||||
this.cliffModels.add(new TerrainModel(this, (InputStream) resource.data, cliffInfo.locations,
|
||||
cliffInfo.textures, cliffShader));
|
||||
}
|
||||
this.cliffsReady = true;
|
||||
|
||||
}
|
||||
|
||||
@ -629,11 +336,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
@Override
|
||||
public void update() {
|
||||
if (this.anyReady) {
|
||||
this.waterIndex += this.waterIncreasePerFrame;
|
||||
|
||||
if (this.waterIndex >= this.waterTextures.size()) {
|
||||
this.waterIndex = 0;
|
||||
}
|
||||
this.terrain.update();
|
||||
|
||||
super.update();
|
||||
|
||||
@ -656,325 +359,14 @@ public class War3MapViewer extends ModelViewer {
|
||||
final Scene worldScene = this.worldScene;
|
||||
|
||||
worldScene.startFrame();
|
||||
this.renderGround();
|
||||
this.renderCliffs();
|
||||
this.terrain.renderGround(this.gl, this.webGL, worldScene);
|
||||
this.terrain.renderCliffs(this.gl, this.webGL, worldScene);
|
||||
worldScene.renderOpaque();
|
||||
this.renderWater();
|
||||
this.terrain.renderWater(this.gl, this.webGL, worldScene);
|
||||
worldScene.renderTranslucent();
|
||||
}
|
||||
}
|
||||
|
||||
public void renderGround() {
|
||||
if (this.terrainReady) {
|
||||
final GL20 gl = this.gl;
|
||||
final WebGL webgl = this.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webgl.instancedArrays;
|
||||
final ShaderProgram shader = this.groundShader;
|
||||
final List<Texture> tilesetTextures = this.tilesetTextures;
|
||||
final int instanceAttrib = shader.getAttributeLocation("a_InstanceID");
|
||||
final int positionAttrib = shader.getAttributeLocation("a_position");
|
||||
final int texturesAttrib = shader.getAttributeLocation("a_textures");
|
||||
final int variationsAttrib = shader.getAttributeLocation("a_variations");
|
||||
final int tilesetCount = tilesetTextures.size();
|
||||
|
||||
gl.glEnable(GL20.GL_BLEND);
|
||||
gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
webgl.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", this.worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniform2fv("u_offset", this.centerOffset, 0, 2);
|
||||
sizeHeap[0] = this.columns;
|
||||
sizeHeap[1] = this.rows;
|
||||
shader.setUniform2fv("u_size", sizeHeap, 0, 2);
|
||||
shader.setUniformi("u_heightMap", 15);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE15);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
shader.setVertexAttribute(positionAttrib, 2, GL20.GL_FLOAT, false, 8, 0);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
shader.setVertexAttribute(instanceAttrib, 1, GL20.GL_FLOAT, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.textureBuffer);
|
||||
shader.setVertexAttribute(texturesAttrib, 4, GL20.GL_UNSIGNED_BYTE, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(texturesAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.variationBuffer);
|
||||
shader.setVertexAttribute(variationsAttrib, 4, GL20.GL_UNSIGNED_BYTE, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(variationsAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
|
||||
shader.setUniformi("u_baseTileset", 0);
|
||||
|
||||
for (int i = 0, l = Math.min(tilesetCount, 15); i < l; i++) {
|
||||
final int isExtended = (tilesetTextures.get(i).getWidth() > tilesetTextures.get(i).getHeight()) ? 1 : 0;
|
||||
|
||||
shader.setUniformf("u_extended[" + i + "]", isExtended);
|
||||
shader.setUniformi("u_tilesets[" + i + "]", i);
|
||||
|
||||
tilesetTextures.get(i).bind(i);
|
||||
}
|
||||
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
|
||||
if (tilesetCount > 15) {
|
||||
shader.setUniformi("u_baseTileset", 15);
|
||||
|
||||
for (int i = 0, l = tilesetCount - 15; i < l; i++) {
|
||||
final int isExtended = (tilesetTextures.get(i + 15).getWidth() > tilesetTextures.get(i + 15)
|
||||
.getHeight()) ? 1 : 0;
|
||||
|
||||
shader.setUniformf("u_extended[" + i + "]", isExtended);
|
||||
|
||||
tilesetTextures.get(i + 15).bind(i);
|
||||
}
|
||||
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
}
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(texturesAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(variationsAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void renderWater() {
|
||||
if (this.terrainReady) {
|
||||
final GL20 gl = this.gl;
|
||||
final WebGL webgl = this.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webgl.instancedArrays;
|
||||
final ShaderProgram shader = this.waterShader;
|
||||
final int instanceAttrib = shader.getAttributeLocation("a_InstanceID");
|
||||
final int positionAttrib = shader.getAttributeLocation("a_position");
|
||||
final int isWaterAttrib = shader.getAttributeLocation("a_isWater");
|
||||
|
||||
gl.glDepthMask(false);
|
||||
|
||||
gl.glEnable(GL20.GL_BLEND);
|
||||
gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
webgl.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", this.worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniform2fv("u_offset", this.centerOffset, 0, 2);
|
||||
sizeHeap[0] = this.columns;
|
||||
sizeHeap[1] = this.rows;
|
||||
shader.setUniform2fv("u_size", sizeHeap, 0, 2);
|
||||
shader.setUniformi("u_heightMap", 0);
|
||||
shader.setUniformi("u_waterHeightMap", 1);
|
||||
shader.setUniformi("u_waterTexture", 2);
|
||||
shader.setUniformf("u_offsetHeight", this.waterHeightOffset);
|
||||
shader.setUniform4fv("u_maxDeepColor", this.maxDeepColor, 0, 4);
|
||||
shader.setUniform4fv("u_minDeepColor", this.minDeepColor, 0, 4);
|
||||
shader.setUniform4fv("u_maxShallowColor", this.maxShallowColor, 0, 4);
|
||||
shader.setUniform4fv("u_minShallowColor", this.minShallowColor, 0, 4);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.waterHeightMap);
|
||||
|
||||
this.waterTextures.get((int) this.waterIndex).bind(2);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
shader.setVertexAttribute(positionAttrib, 2, GL20.GL_FLOAT, false, 8, 0);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
shader.setVertexAttribute(instanceAttrib, 1, GL20.GL_FLOAT, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.waterBuffer);
|
||||
shader.setVertexAttribute(isWaterAttrib, 1, GL20.GL_UNSIGNED_BYTE, false, 1, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(isWaterAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(isWaterAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void renderCliffs() {
|
||||
if (this.cliffsReady) {
|
||||
final GL20 gl = this.gl;
|
||||
final WebGL webGL = this.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webGL.instancedArrays;
|
||||
final ShaderProgram shader = this.cliffShader;
|
||||
|
||||
gl.glDisable(GL20.GL_BLEND);
|
||||
|
||||
webGL.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", this.worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniformi("u_heightMap", 0);
|
||||
shader.setUniformf("u_pixel[0]", 1 / (float) (this.columns + 1));
|
||||
shader.setUniformf("u_pixel[1]", 1 / (float) (this.rows + 1));
|
||||
shader.setUniform2fv("u_centerOffset", this.centerOffset, 0, 2);
|
||||
shader.setUniformi("u_texture1", 1);
|
||||
shader.setUniformi("u_texture2", 2);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffHeightMap);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffTextures.get(0).getGlTarget());
|
||||
|
||||
if (this.cliffTextures.size() > 1) {
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffTextures.get(1).getGlTarget());
|
||||
}
|
||||
|
||||
// Set instanced attributes.
|
||||
for (final TerrainModel cliff : this.cliffModels) {
|
||||
cliff.render(shader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String cliffFileName(final int bottomLeftLayer, final int bottomRightLayer, final int topLeftLayer,
|
||||
final int topRightLayer, final int base) {
|
||||
return Character.toString((char) ((65 + bottomLeftLayer) - base))
|
||||
+ Character.toString((char) ((65 + topLeftLayer) - base))
|
||||
+ Character.toString((char) ((65 + topRightLayer) - base))
|
||||
+ Character.toString((char) ((65 + bottomRightLayer) - base));
|
||||
}
|
||||
|
||||
public short getVariation(final int groundTexture, final int variation) {
|
||||
final Texture texture = this.tilesetTextures.get(groundTexture);
|
||||
|
||||
// Extended ?
|
||||
if (texture.getWidth() > texture.getHeight()) {
|
||||
if (variation < 16) {
|
||||
return (short) (16 + variation);
|
||||
}
|
||||
else if (variation == 16) {
|
||||
return 15;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (variation == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCliff(final int column, final int row) {
|
||||
if ((column < 1) || (column > (this.columns - 1)) || (row < 1) || (row > (this.rows - 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Corner[][] corners = this.corners;
|
||||
final int bottomLeft = corners[row][column].getLayerHeight();
|
||||
final int bottomRight = corners[row][column + 1].getLayerHeight();
|
||||
final int topLeft = corners[row + 1][column].getLayerHeight();
|
||||
final int topRight = corners[row + 1][column + 1].getLayerHeight();
|
||||
|
||||
return (bottomLeft != bottomRight) || (bottomLeft != topLeft) || (bottomLeft != topRight);
|
||||
}
|
||||
|
||||
public short isWater(final int column, final int row) {
|
||||
return ((this.corners[row][column].getWater() != 0) || (this.corners[row][column + 1].getWater() != 0)
|
||||
|| (this.corners[row + 1][column].getWater() != 0)
|
||||
|| (this.corners[row + 1][column + 1].getWater() != 0)) ? (short) 1 : (short) 0;
|
||||
}
|
||||
|
||||
public int cliffGroundIndex(final int whichCliff) {
|
||||
final String whichTile = this.cliffTilesets.get(whichCliff).get("groundTile").toString();
|
||||
final List<MappedDataRow> tilesets = this.tilesets;
|
||||
|
||||
for (int i = 0, l = tilesets.size(); i < l; i++) {
|
||||
if (tilesets.get(i).get("tileID").toString().equals(whichTile)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(Integer.toString(whichCliff));
|
||||
}
|
||||
|
||||
public int cornerTexture(final int column, final int row) {
|
||||
final Corner[][] corners = this.corners;
|
||||
final int columns = this.columns;
|
||||
final int rows = this.rows;
|
||||
|
||||
for (int y = -1; y < 1; y++) {
|
||||
for (int x = -1; x < 1; x++) {
|
||||
if (((column + x) > 0) && ((column + x) < (columns - 1)) && ((row + y) > 0)
|
||||
&& ((row + y) < (rows - 1))) {
|
||||
if (this.isCliff(column + x, row + y)) {
|
||||
int texture = corners[row + y][column + x].getCliffTexture();
|
||||
|
||||
if (texture == 15) {
|
||||
texture = 1;
|
||||
}
|
||||
|
||||
return this.cliffGroundIndex(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Corner corner = corners[row][column];
|
||||
if (corner.getBlight() != 0) {
|
||||
return this.blightTextureIndex;
|
||||
}
|
||||
return corner.getGroundTexture();
|
||||
}
|
||||
|
||||
public Vector3 groundNormal(final Vector3 out, int x, int y) {
|
||||
final float[] centerOffset = this.centerOffset;
|
||||
final int[] mapSize = this.mapSize;
|
||||
|
||||
x = (int) ((x - centerOffset[0]) / 128);
|
||||
y = (int) ((y - centerOffset[1]) / 128);
|
||||
|
||||
final int cellX = x;
|
||||
final int cellY = y;
|
||||
|
||||
// See if this coordinate is in the map
|
||||
|
||||
if ((cellX >= 0) && (cellX < (mapSize[0] - 1)) && (cellY >= 0) && (cellY < (mapSize[1] - 1))) {
|
||||
// See http://gamedev.stackexchange.com/a/24574
|
||||
final Corner[][] corners = this.corners;
|
||||
final int bottomLeft = corners[cellY][cellX].getGroundHeight();
|
||||
final int bottomRight = corners[cellY][cellX + 1].getGroundHeight();
|
||||
final int topLeft = corners[cellY + 1][cellX].getGroundHeight();
|
||||
final int topRight = corners[cellY + 1][cellX + 1].getGroundHeight();
|
||||
final int sqX = x - cellX;
|
||||
final int sqY = y - cellY;
|
||||
|
||||
if ((sqX + sqY) < 1) {
|
||||
normalHeap1.set(1, 0, bottomRight - bottomLeft);
|
||||
normalHeap2.set(0, 1, topLeft - bottomLeft);
|
||||
}
|
||||
else {
|
||||
normalHeap1.set(-1, 0, topRight - topLeft);
|
||||
normalHeap2.set(0, 1, topRight - bottomRight);
|
||||
}
|
||||
|
||||
out.set(normalHeap1.crs(normalHeap2)).nor();
|
||||
}
|
||||
else {
|
||||
out.set(0, 0, 1);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static final class MappedDataCallbackImplementation implements LoadGenericCallback {
|
||||
@Override
|
||||
public Object call(final InputStream data) {
|
||||
@ -1039,6 +431,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
private static final int MAXIMUM_ACCEPTED = 1 << 30;
|
||||
public final Terrain terrain;
|
||||
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
|
@ -0,0 +1,650 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.common.FetchDataTypeName;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.Corner;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.util.MappedDataRow;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
import com.etheller.warsmash.viewer5.GenericResource;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.TerrainModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.Variations;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.W3xShaders;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer.CliffInfo;
|
||||
|
||||
public class Terrain {
|
||||
private static final float[] sizeHeap = new float[2];
|
||||
private static final Vector3 normalHeap1 = new Vector3();
|
||||
private static final Vector3 normalHeap2 = new Vector3();
|
||||
|
||||
public ShaderProgram groundShader;
|
||||
public ShaderProgram waterShader;
|
||||
public ShaderProgram cliffShader;
|
||||
public float waterIndex;
|
||||
public float waterIncreasePerFrame;
|
||||
public float waterHeightOffset;
|
||||
public List<Texture> waterTextures = new ArrayList<>();
|
||||
public float[] maxDeepColor = new float[4];
|
||||
public float[] minDeepColor = new float[4];
|
||||
public float[] maxShallowColor = new float[4];
|
||||
public float[] minShallowColor = new float[4];
|
||||
|
||||
public List<Texture> tilesetTextures = new ArrayList<>();
|
||||
public List<Texture> cliffTextures = new ArrayList<>();
|
||||
public List<TerrainModel> cliffModels = new ArrayList<>();
|
||||
|
||||
public Corner[][] corners;
|
||||
public float[] centerOffset = new float[2];
|
||||
public int[] mapSize = new int[2];
|
||||
|
||||
public List<MappedDataRow> tilesets = new ArrayList<>(); // TODO
|
||||
public int blightTextureIndex = -1;
|
||||
public List<MappedDataRow> cliffTilesets = new ArrayList<>();
|
||||
public int columns;
|
||||
public int rows;
|
||||
|
||||
public int vertexBuffer;
|
||||
public int faceBuffer;
|
||||
public int instanceBuffer;
|
||||
public int textureBuffer;
|
||||
public int variationBuffer;
|
||||
public int waterBuffer;
|
||||
public int heightMap;
|
||||
public int waterHeightMap;
|
||||
public int cliffHeightMap;
|
||||
|
||||
public Terrain(final WebGL webGL, final DataSource dataSource, final WorldEditStrings worldEditStrings)
|
||||
throws IOException {
|
||||
|
||||
final DataTable terrainTable = new DataTable(worldEditStrings);
|
||||
try (InputStream terrainSlkStream = dataSource.getResourceAsStream("TerrainArt\\Terrain.slk")) {
|
||||
terrainTable.readSLK(terrainSlkStream);
|
||||
}
|
||||
final DataTable cliffTable = new DataTable(worldEditStrings);
|
||||
try (InputStream cliffSlkStream = dataSource.getResourceAsStream("TerrainArt\\CliffTypes.slk")) {
|
||||
cliffTable.readSLK(cliffSlkStream);
|
||||
}
|
||||
|
||||
this.groundShader = webGL.createShaderProgram(W3xShaders.Ground.vert, W3xShaders.Ground.frag);
|
||||
this.waterShader = webGL.createShaderProgram(W3xShaders.Water.vert, W3xShaders.Water.frag);
|
||||
this.cliffShader = webGL.createShaderProgram(W3xShaders.Cliffs.vert, W3xShaders.Cliffs.frag);
|
||||
|
||||
}
|
||||
|
||||
public void load(final War3MapW3e w3e, final float[] centerOffset, final int[] mapSize,
|
||||
final War3MapViewer viewer) {
|
||||
|
||||
this.corners = w3e.getCorners();
|
||||
System.arraycopy(centerOffset, 0, this.centerOffset, 0, centerOffset.length);
|
||||
System.arraycopy(mapSize, 0, this.mapSize, 0, mapSize.length);
|
||||
|
||||
final String texturesExt = viewer.solverParams.reforged ? ".dds" : ".blp";
|
||||
final char tileset = w3e.getTileset();
|
||||
|
||||
for (final War3ID groundTile : w3e.getGroundTiles()) {
|
||||
final MappedDataRow row = viewer.terrainData.getRow(groundTile.asStringValue());
|
||||
|
||||
this.tilesets.add(row);
|
||||
this.tilesetTextures
|
||||
.add((Texture) viewer.load(row.get("dir").toString() + "\\" + row.get("file") + texturesExt,
|
||||
viewer.mapPathSolver, viewer.solverParams));
|
||||
}
|
||||
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(viewer.mapMpq.getCompoundDataSource());
|
||||
final DataTable worldEditData = standardObjectData.getWorldEditData();
|
||||
final Element tilesets = worldEditData.get("TileSets");
|
||||
|
||||
this.blightTextureIndex = this.tilesetTextures.size();
|
||||
this.tilesetTextures
|
||||
.add((Texture) viewer.load(tilesets.getField(Character.toString(tileset)).split(",")[1] + texturesExt,
|
||||
viewer.mapPathSolver, viewer.solverParams));
|
||||
|
||||
for (final War3ID cliffTile : w3e.getCliffTiles()) {
|
||||
final MappedDataRow row = viewer.cliffTypesData.getRow(cliffTile.asStringValue());
|
||||
|
||||
this.cliffTilesets.add(row);
|
||||
this.cliffTextures
|
||||
.add((Texture) viewer.load(row.get("texDir").toString() + "\\" + row.get("texFile") + texturesExt,
|
||||
viewer.mapPathSolver, viewer.solverParams));
|
||||
}
|
||||
|
||||
final MappedDataRow waterRow = viewer.waterData.getRow(tileset + "Sha");
|
||||
|
||||
this.waterHeightOffset = ((Number) waterRow.get("height")).floatValue();
|
||||
this.waterIncreasePerFrame = ((Number) waterRow.get("texRate")).intValue() / (float) 60;
|
||||
this.waterTextures.clear();
|
||||
this.maxDeepColor[0] = ((Number) waterRow.get("Dmax_R")).floatValue();
|
||||
this.maxDeepColor[1] = ((Number) waterRow.get("Dmax_G")).floatValue();
|
||||
this.maxDeepColor[2] = ((Number) waterRow.get("Dmax_B")).floatValue();
|
||||
this.maxDeepColor[3] = ((Number) waterRow.get("Dmax_A")).floatValue();
|
||||
this.minDeepColor[0] = ((Number) waterRow.get("Dmin_R")).floatValue();
|
||||
this.minDeepColor[1] = ((Number) waterRow.get("Dmin_G")).floatValue();
|
||||
this.minDeepColor[2] = ((Number) waterRow.get("Dmin_B")).floatValue();
|
||||
this.minDeepColor[3] = ((Number) waterRow.get("Dmin_A")).floatValue();
|
||||
this.maxShallowColor[0] = ((Number) waterRow.get("Smax_R")).floatValue();
|
||||
this.maxShallowColor[1] = ((Number) waterRow.get("Smax_G")).floatValue();
|
||||
this.maxShallowColor[2] = ((Number) waterRow.get("Smax_B")).floatValue();
|
||||
this.maxShallowColor[3] = ((Number) waterRow.get("Smax_A")).floatValue();
|
||||
this.minShallowColor[0] = ((Number) waterRow.get("Smin_R")).floatValue();
|
||||
this.minShallowColor[1] = ((Number) waterRow.get("Smin_G")).floatValue();
|
||||
this.minShallowColor[2] = ((Number) waterRow.get("Smin_B")).floatValue();
|
||||
this.minShallowColor[3] = ((Number) waterRow.get("Smin_A")).floatValue();
|
||||
|
||||
for (int i = 0, l = ((Number) waterRow.get("numTex")).intValue(); i < l; i++) {
|
||||
this.waterTextures.add(
|
||||
(Texture) viewer.load(waterRow.get("texFile").toString() + ((i < 10) ? "0" : "") + i + texturesExt,
|
||||
viewer.mapPathSolver, viewer.solverParams));
|
||||
}
|
||||
|
||||
final GL20 gl = viewer.gl;
|
||||
|
||||
final Corner[][] corners = w3e.getCorners();
|
||||
final int columns = this.mapSize[0];
|
||||
final int rows = this.mapSize[1];
|
||||
final int instanceCount = (columns - 1) * (rows - 1);
|
||||
final float[] cliffHeights = new float[columns * rows];
|
||||
final float[] cornerHeights = new float[columns * rows];
|
||||
final float[] waterHeights = new float[columns * rows];
|
||||
final short[] cornerTextures = new short[instanceCount * 4];
|
||||
final short[] cornerVariations = new short[instanceCount * 4];
|
||||
final short[] waterFlags = new short[instanceCount];
|
||||
int instance = 0;
|
||||
final Map<String, CliffInfo> cliffs = new HashMap<>();
|
||||
|
||||
this.columns = columns - 1;
|
||||
this.rows = rows - 1;
|
||||
|
||||
for (int y = 0; y < rows; y++) {
|
||||
for (int x = 0; x < columns; x++) {
|
||||
final Corner bottomLeft = corners[y][x];
|
||||
final int index = (y * columns) + x;
|
||||
|
||||
cliffHeights[index] = bottomLeft.getGroundHeight();
|
||||
cornerHeights[index] = (bottomLeft.getGroundHeight() + bottomLeft.getLayerHeight()) - 2;
|
||||
waterHeights[index] = bottomLeft.getWaterHeight();
|
||||
|
||||
if ((y < (rows - 1)) && (x < (columns - 1))) {
|
||||
// Water can be used with cliffs and normal corners, so store water state
|
||||
// regardless.
|
||||
waterFlags[instance] = this.isWater(x, y);
|
||||
|
||||
// Is this a cliff, or a normal corner?
|
||||
if (this.isCliff(x, y)) {
|
||||
final int bottomLeftLayer = bottomLeft.getLayerHeight();
|
||||
final int bottomRightLayer = corners[y][x + 1].getLayerHeight();
|
||||
final int topLeftLayer = corners[y + 1][x].getLayerHeight();
|
||||
final int topRightLayer = corners[y + 1][x + 1].getLayerHeight();
|
||||
final int base = Math.min(Math.min(bottomLeftLayer, bottomRightLayer),
|
||||
Math.min(topLeftLayer, topRightLayer));
|
||||
final String fileName = this.cliffFileName(bottomLeftLayer, bottomRightLayer, topLeftLayer,
|
||||
topRightLayer, base);
|
||||
|
||||
if (!"AAAA".equals(fileName)) {
|
||||
int cliffTexture = bottomLeft.getCliffTexture();
|
||||
|
||||
// ?
|
||||
if (cliffTexture == 15) {
|
||||
cliffTexture = 1;
|
||||
}
|
||||
|
||||
final MappedDataRow cliffRow = this.cliffTilesets.get(cliffTexture);
|
||||
final String dir = cliffRow.get("cliffModelDir").toString();
|
||||
final String path = "Doodads\\Terrain\\" + dir + "\\" + dir + fileName
|
||||
+ Variations.getCliffVariation(dir, fileName, bottomLeft.getCliffVariation())
|
||||
+ ".mdx";
|
||||
|
||||
if (!cliffs.containsKey(path)) {
|
||||
cliffs.put(path, new CliffInfo());
|
||||
}
|
||||
|
||||
cliffs.get(path).locations.add(new float[] { ((x + 1) * 128) + centerOffset[0],
|
||||
(y * 128) + centerOffset[1], (base - 2) * 128 });
|
||||
cliffs.get(path).textures.add(cliffTexture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final int bottomLeftTexture = this.cornerTexture(x, y);
|
||||
final int bottomRightTexture = this.cornerTexture(x + 1, y);
|
||||
final int topLeftTexture = this.cornerTexture(x, y + 1);
|
||||
final int topRightTexture = this.cornerTexture(x + 1, y + 1);
|
||||
final LinkedHashSet<Integer> texturesUnique = new LinkedHashSet<>();
|
||||
texturesUnique.add(bottomLeftTexture);
|
||||
texturesUnique.add(bottomRightTexture);
|
||||
texturesUnique.add(topLeftTexture);
|
||||
texturesUnique.add(topRightTexture);
|
||||
final List<Integer> textures = new ArrayList<>(texturesUnique);
|
||||
Collections.sort(textures);
|
||||
|
||||
int texture = textures.remove(0);
|
||||
|
||||
cornerTextures[instance * 4] = (short) (texture + 1);
|
||||
cornerVariations[instance * 4] = this.getVariation(texture, bottomLeft.getGroundVariation());
|
||||
|
||||
for (int i = 0, l = textures.size(); i < l; i++) {
|
||||
int bitset = 0;
|
||||
|
||||
texture = textures.get(i);
|
||||
|
||||
if (bottomRightTexture == texture) {
|
||||
bitset |= 0b0001;
|
||||
}
|
||||
|
||||
if (bottomLeftTexture == texture) {
|
||||
bitset |= 0b0010;
|
||||
}
|
||||
|
||||
if (topRightTexture == texture) {
|
||||
bitset |= 0b0100;
|
||||
}
|
||||
|
||||
if (topLeftTexture == texture) {
|
||||
bitset |= 0b1000;
|
||||
}
|
||||
|
||||
cornerTextures[(instance * 4) + 1 + i] = (short) (texture + 1);
|
||||
cornerVariations[(instance * 4) + 1 + i] = (short) (bitset);
|
||||
}
|
||||
}
|
||||
|
||||
instance += 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.vertexBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, 8 * 4, RenderMathUtils.wrap(new float[] { 0, 0, 1, 0, 0, 1, 1, 1 }),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.faceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 6, RenderMathUtils.wrap(new byte[] { 0, 1, 2, 1, 3, 2 }),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.cliffHeightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffHeightMap);
|
||||
viewer.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(cliffHeights));
|
||||
|
||||
this.heightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
viewer.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(cornerHeights));
|
||||
|
||||
this.waterHeightMap = gl.glGenTexture();
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.waterHeightMap);
|
||||
viewer.webGL.setTextureMode(GL20.GL_CLAMP_TO_EDGE, GL20.GL_CLAMP_TO_EDGE, GL20.GL_NEAREST, GL20.GL_NEAREST);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_R32F, columns, rows, 0, GL30.GL_RED, GL20.GL_FLOAT,
|
||||
RenderMathUtils.wrap(waterHeights));
|
||||
|
||||
this.instanceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
final float[] instanceBufferData = new float[instanceCount];
|
||||
for (int i = 0; i < instanceBufferData.length; i++) {
|
||||
instanceBufferData[i] = i;
|
||||
}
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, instanceBufferData.length * 4, RenderMathUtils.wrap(instanceBufferData),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.textureBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.textureBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, cornerTextures.length, RenderMathUtils.wrap(cornerTextures),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.variationBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.variationBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, cornerVariations.length, RenderMathUtils.wrap(cornerVariations),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
this.waterBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.waterBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, waterFlags.length, RenderMathUtils.wrap(waterFlags), GL20.GL_STATIC_DRAW);
|
||||
|
||||
final ShaderProgram cliffShader = this.cliffShader;
|
||||
this.cliffModels.clear();
|
||||
for (final Map.Entry<String, CliffInfo> entry : cliffs.entrySet()) {
|
||||
final String path = entry.getKey();
|
||||
final CliffInfo cliffInfo = entry.getValue();
|
||||
|
||||
final GenericResource resource = viewer.loadMapGeneric(path, FetchDataTypeName.ARRAY_BUFFER,
|
||||
viewer.streamDataCallback);
|
||||
|
||||
this.cliffModels.add(new TerrainModel(viewer, (InputStream) resource.data, cliffInfo.locations,
|
||||
cliffInfo.textures, cliffShader));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCliff(final int column, final int row) {
|
||||
if ((column < 1) || (column > (this.columns - 1)) || (row < 1) || (row > (this.rows - 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Corner[][] corners = this.corners;
|
||||
final int bottomLeft = corners[row][column].getLayerHeight();
|
||||
final int bottomRight = corners[row][column + 1].getLayerHeight();
|
||||
final int topLeft = corners[row + 1][column].getLayerHeight();
|
||||
final int topRight = corners[row + 1][column + 1].getLayerHeight();
|
||||
|
||||
return (bottomLeft != bottomRight) || (bottomLeft != topLeft) || (bottomLeft != topRight);
|
||||
}
|
||||
|
||||
public short isWater(final int column, final int row) {
|
||||
return ((this.corners[row][column].getWater() != 0) || (this.corners[row][column + 1].getWater() != 0)
|
||||
|| (this.corners[row + 1][column].getWater() != 0)
|
||||
|| (this.corners[row + 1][column + 1].getWater() != 0)) ? (short) 1 : (short) 0;
|
||||
}
|
||||
|
||||
public String cliffFileName(final int bottomLeftLayer, final int bottomRightLayer, final int topLeftLayer,
|
||||
final int topRightLayer, final int base) {
|
||||
return Character.toString((char) ((65 + bottomLeftLayer) - base))
|
||||
+ Character.toString((char) ((65 + topLeftLayer) - base))
|
||||
+ Character.toString((char) ((65 + topRightLayer) - base))
|
||||
+ Character.toString((char) ((65 + bottomRightLayer) - base));
|
||||
}
|
||||
|
||||
public short getVariation(final int groundTexture, final int variation) {
|
||||
final Texture texture = this.tilesetTextures.get(groundTexture);
|
||||
|
||||
// Extended ?
|
||||
if (texture.getWidth() > texture.getHeight()) {
|
||||
if (variation < 16) {
|
||||
return (short) (16 + variation);
|
||||
}
|
||||
else if (variation == 16) {
|
||||
return 15;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (variation == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int cliffGroundIndex(final int whichCliff) {
|
||||
final String whichTile = this.cliffTilesets.get(whichCliff).get("groundTile").toString();
|
||||
final List<MappedDataRow> tilesets = this.tilesets;
|
||||
|
||||
for (int i = 0, l = tilesets.size(); i < l; i++) {
|
||||
if (tilesets.get(i).get("tileID").toString().equals(whichTile)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(Integer.toString(whichCliff));
|
||||
}
|
||||
|
||||
public int cornerTexture(final int column, final int row) {
|
||||
final Corner[][] corners = this.corners;
|
||||
final int columns = this.columns;
|
||||
final int rows = this.rows;
|
||||
|
||||
for (int y = -1; y < 1; y++) {
|
||||
for (int x = -1; x < 1; x++) {
|
||||
if (((column + x) > 0) && ((column + x) < (columns - 1)) && ((row + y) > 0)
|
||||
&& ((row + y) < (rows - 1))) {
|
||||
if (this.isCliff(column + x, row + y)) {
|
||||
int texture = corners[row + y][column + x].getCliffTexture();
|
||||
|
||||
if (texture == 15) {
|
||||
texture = 1;
|
||||
}
|
||||
|
||||
return this.cliffGroundIndex(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Corner corner = corners[row][column];
|
||||
if (corner.getBlight() != 0) {
|
||||
return this.blightTextureIndex;
|
||||
}
|
||||
return corner.getGroundTexture();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
this.waterIndex += this.waterIncreasePerFrame;
|
||||
|
||||
if (this.waterIndex >= this.waterTextures.size()) {
|
||||
this.waterIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void renderGround(final GL20 gl, final WebGL webgl, final Scene worldScene) {
|
||||
final ANGLEInstancedArrays instancedArrays = webgl.instancedArrays;
|
||||
final ShaderProgram shader = this.groundShader;
|
||||
final List<Texture> tilesetTextures = this.tilesetTextures;
|
||||
final int instanceAttrib = shader.getAttributeLocation("a_InstanceID");
|
||||
final int positionAttrib = shader.getAttributeLocation("a_position");
|
||||
final int texturesAttrib = shader.getAttributeLocation("a_textures");
|
||||
final int variationsAttrib = shader.getAttributeLocation("a_variations");
|
||||
final int tilesetCount = tilesetTextures.size();
|
||||
|
||||
gl.glEnable(GL20.GL_BLEND);
|
||||
gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
webgl.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniform2fv("u_offset", this.centerOffset, 0, 2);
|
||||
sizeHeap[0] = this.columns;
|
||||
sizeHeap[1] = this.rows;
|
||||
shader.setUniform2fv("u_size", sizeHeap, 0, 2);
|
||||
shader.setUniformi("u_heightMap", 15);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE15);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
shader.setVertexAttribute(positionAttrib, 2, GL20.GL_FLOAT, false, 8, 0);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
shader.setVertexAttribute(instanceAttrib, 1, GL20.GL_FLOAT, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.textureBuffer);
|
||||
shader.setVertexAttribute(texturesAttrib, 4, GL20.GL_UNSIGNED_BYTE, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(texturesAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.variationBuffer);
|
||||
shader.setVertexAttribute(variationsAttrib, 4, GL20.GL_UNSIGNED_BYTE, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(variationsAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
|
||||
shader.setUniformi("u_baseTileset", 0);
|
||||
|
||||
for (int i = 0, l = Math.min(tilesetCount, 15); i < l; i++) {
|
||||
final int isExtended = (tilesetTextures.get(i).getWidth() > tilesetTextures.get(i).getHeight()) ? 1 : 0;
|
||||
|
||||
shader.setUniformf("u_extended[" + i + "]", isExtended);
|
||||
shader.setUniformi("u_tilesets[" + i + "]", i);
|
||||
|
||||
tilesetTextures.get(i).bind(i);
|
||||
}
|
||||
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
|
||||
if (tilesetCount > 15) {
|
||||
shader.setUniformi("u_baseTileset", 15);
|
||||
|
||||
for (int i = 0, l = tilesetCount - 15; i < l; i++) {
|
||||
final int isExtended = (tilesetTextures.get(i + 15).getWidth() > tilesetTextures.get(i + 15)
|
||||
.getHeight()) ? 1 : 0;
|
||||
|
||||
shader.setUniformf("u_extended[" + i + "]", isExtended);
|
||||
|
||||
tilesetTextures.get(i + 15).bind(i);
|
||||
}
|
||||
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
}
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(texturesAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(variationsAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 0);
|
||||
}
|
||||
|
||||
public void renderWater(final GL20 gl, final WebGL webgl, final Scene worldScene) {
|
||||
final ANGLEInstancedArrays instancedArrays = webgl.instancedArrays;
|
||||
final ShaderProgram shader = this.waterShader;
|
||||
final int instanceAttrib = shader.getAttributeLocation("a_InstanceID");
|
||||
final int positionAttrib = shader.getAttributeLocation("a_position");
|
||||
final int isWaterAttrib = shader.getAttributeLocation("a_isWater");
|
||||
|
||||
gl.glDepthMask(false);
|
||||
|
||||
gl.glEnable(GL20.GL_BLEND);
|
||||
gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
webgl.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniform2fv("u_offset", this.centerOffset, 0, 2);
|
||||
sizeHeap[0] = this.columns;
|
||||
sizeHeap[1] = this.rows;
|
||||
shader.setUniform2fv("u_size", sizeHeap, 0, 2);
|
||||
shader.setUniformi("u_heightMap", 0);
|
||||
shader.setUniformi("u_waterHeightMap", 1);
|
||||
shader.setUniformi("u_waterTexture", 2);
|
||||
shader.setUniformf("u_offsetHeight", this.waterHeightOffset);
|
||||
shader.setUniform4fv("u_maxDeepColor", this.maxDeepColor, 0, 4);
|
||||
shader.setUniform4fv("u_minDeepColor", this.minDeepColor, 0, 4);
|
||||
shader.setUniform4fv("u_maxShallowColor", this.maxShallowColor, 0, 4);
|
||||
shader.setUniform4fv("u_minShallowColor", this.minShallowColor, 0, 4);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.heightMap);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.waterHeightMap);
|
||||
|
||||
this.waterTextures.get((int) this.waterIndex).bind(2);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
shader.setVertexAttribute(positionAttrib, 2, GL20.GL_FLOAT, false, 8, 0);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
shader.setVertexAttribute(instanceAttrib, 1, GL20.GL_FLOAT, false, 4, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.waterBuffer);
|
||||
shader.setVertexAttribute(isWaterAttrib, 1, GL20.GL_UNSIGNED_BYTE, false, 1, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(isWaterAttrib, 1);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, 6, GL20.GL_UNSIGNED_BYTE, 0,
|
||||
this.rows * this.columns);
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(isWaterAttrib, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(instanceAttrib, 0);
|
||||
}
|
||||
|
||||
public void renderCliffs(final GL20 gl, final WebGL webGL, final Scene worldScene) {
|
||||
final ANGLEInstancedArrays instancedArrays = webGL.instancedArrays;
|
||||
final ShaderProgram shader = this.cliffShader;
|
||||
|
||||
gl.glDisable(GL20.GL_BLEND);
|
||||
|
||||
webGL.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_VP", worldScene.camera.viewProjectionMatrix);
|
||||
shader.setUniformi("u_heightMap", 0);
|
||||
shader.setUniformf("u_pixel[0]", 1 / (float) (this.columns + 1));
|
||||
shader.setUniformf("u_pixel[1]", 1 / (float) (this.rows + 1));
|
||||
shader.setUniform2fv("u_centerOffset", this.centerOffset, 0, 2);
|
||||
shader.setUniformi("u_texture1", 1);
|
||||
shader.setUniformi("u_texture2", 2);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffHeightMap);
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffTextures.get(0).getGlTarget());
|
||||
|
||||
if (this.cliffTextures.size() > 1) {
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.cliffTextures.get(1).getGlTarget());
|
||||
}
|
||||
|
||||
// Set instanced attributes.
|
||||
for (final TerrainModel cliff : this.cliffModels) {
|
||||
cliff.render(shader);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 groundNormal(final Vector3 out, int x, int y) {
|
||||
final float[] centerOffset = this.centerOffset;
|
||||
final int[] mapSize = this.mapSize;
|
||||
|
||||
x = (int) ((x - centerOffset[0]) / 128);
|
||||
y = (int) ((y - centerOffset[1]) / 128);
|
||||
|
||||
final int cellX = x;
|
||||
final int cellY = y;
|
||||
|
||||
// See if this coordinate is in the map
|
||||
|
||||
if ((cellX >= 0) && (cellX < (mapSize[0] - 1)) && (cellY >= 0) && (cellY < (mapSize[1] - 1))) {
|
||||
// See http://gamedev.stackexchange.com/a/24574
|
||||
final Corner[][] corners = this.corners;
|
||||
final float bottomLeft = corners[cellY][cellX].getGroundHeight();
|
||||
final float bottomRight = corners[cellY][cellX + 1].getGroundHeight();
|
||||
final float topLeft = corners[cellY + 1][cellX].getGroundHeight();
|
||||
final float topRight = corners[cellY + 1][cellX + 1].getGroundHeight();
|
||||
final int sqX = x - cellX;
|
||||
final int sqY = y - cellY;
|
||||
|
||||
if ((sqX + sqY) < 1) {
|
||||
normalHeap1.set(1, 0, bottomRight - bottomLeft);
|
||||
normalHeap2.set(0, 1, topLeft - bottomLeft);
|
||||
}
|
||||
else {
|
||||
normalHeap1.set(-1, 0, topRight - topLeft);
|
||||
normalHeap2.set(0, 1, topRight - bottomRight);
|
||||
}
|
||||
|
||||
out.set(normalHeap1.crs(normalHeap2)).nor();
|
||||
}
|
||||
else {
|
||||
out.set(0, 0, 1);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user