mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Add splats and camera target height based on terrain and other stuff
This commit is contained in:
parent
67e37244bd
commit
960f6b9738
29
core/src/com/etheller/warsmash/TestMain.java
Normal file
29
core/src/com/etheller/warsmash/TestMain.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.MpqDataSourceDescriptor;
|
||||
|
||||
public class TestMain {
|
||||
public static void main(final String[] args) {
|
||||
final MpqDataSourceDescriptor desc = new MpqDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127\\Z.mpq");
|
||||
final DataSource createDataSource = desc.createDataSource();
|
||||
try {
|
||||
final InputStream cliffZ = createDataSource.getResourceAsStream("ReplaceableTextures\\Cliff\\Cliff0.blp");
|
||||
final BufferedImage img = ImageIO.read(cliffZ);
|
||||
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -74,7 +74,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
this.cameraManager.setupCamera(scene);
|
||||
|
||||
// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx",
|
||||
this.mainModel = (MdxModel) this.viewer.load("Units\\NightElf\\DruidOfTheClaw\\DruidOfTheClaw_Portrait.mdx",
|
||||
this.mainModel = (MdxModel) this.viewer.load("Doodads\\Cinematic\\RisingWaterDoodad\\RisingWaterDoodad.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
@ -82,7 +82,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
}
|
||||
}, null);
|
||||
|
||||
this.modelCamera = this.mainModel.cameras.get(0);
|
||||
// this.modelCamera = this.mainModel.cameras.get(0);
|
||||
|
||||
this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0);
|
||||
|
||||
|
@ -60,21 +60,21 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
final String renderer = Gdx.gl.glGetString(GL20.GL_RENDERER);
|
||||
System.err.println("Renderer: " + renderer);
|
||||
|
||||
// final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127");
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\War3.mpq\\war3.mpq");
|
||||
final FolderDataSourceDescriptor war3xLocalmpq = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\War3xLocal.mpq\\enus-war3local.mpq");
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127");
|
||||
// final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor(
|
||||
// "D:\\NEEDS_ORGANIZING\\MPQBuild\\War3.mpq\\war3.mpq");
|
||||
// final FolderDataSourceDescriptor war3xLocalmpq = new FolderDataSourceDescriptor(
|
||||
// "D:\\NEEDS_ORGANIZING\\MPQBuild\\War3xLocal.mpq\\enus-war3local.mpq");
|
||||
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
|
||||
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
|
||||
this.codebase = new CompoundDataSourceDescriptor(
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, war3xLocalmpq, testingFolder, currentFolder))
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, /* war3xLocalmpq, */ testingFolder, currentFolder))
|
||||
.createDataSource();
|
||||
this.viewer = new War3MapViewer(this.codebase, this);
|
||||
|
||||
try {
|
||||
this.viewer.loadMap("ReforgedGeorgeVacation.w3x");
|
||||
this.viewer.loadMap("(2)BootyBay.w3m");
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -101,7 +101,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
}, null);
|
||||
|
||||
this.portraitCameraManager.modelCamera = this.portraitModel.cameras.get(0);
|
||||
this.portraitScene.camera.viewport(new Rectangle(100, 0, 100, 100));
|
||||
this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48));
|
||||
|
||||
this.portraitInstance = (MdxComplexInstance) this.portraitModel.addInstance(0);
|
||||
|
||||
@ -120,10 +120,15 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
Gdx.input.setInputProcessor(this);
|
||||
}
|
||||
|
||||
private int frame = 0;
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
final float deltaTime = Gdx.graphics.getDeltaTime();
|
||||
Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO);
|
||||
this.cameraManager.target.add(this.cameraVelocity.x, this.cameraVelocity.y, 0);
|
||||
this.cameraManager.target.add(this.cameraVelocity.x * deltaTime, this.cameraVelocity.y * deltaTime, 0);
|
||||
this.cameraManager.target.z = this.viewer.terrain.getGroundHeight(this.cameraManager.target.x,
|
||||
this.cameraManager.target.y);
|
||||
this.cameraManager.updateCamera();
|
||||
this.portraitCameraManager.updateCamera();
|
||||
this.viewer.updateAndRender();
|
||||
@ -138,6 +143,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.portraitInstance
|
||||
.setSequence((this.portraitInstance.sequence + 1) % this.portraitModel.getSequences().size());
|
||||
}
|
||||
|
||||
if ((this.frame++ % 1000) == 0) {
|
||||
System.out.println(Gdx.graphics.getFramesPerSecond());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,6 +165,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
super.resize(width, height);
|
||||
this.tempRect.x = 0;
|
||||
this.tempRect.y = 0;
|
||||
this.tempRect.width = width;
|
||||
@ -246,9 +256,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]);
|
||||
this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0],
|
||||
WarsmashGdxMapGame.this.cameraTargetTemp[1], WarsmashGdxMapGame.this.cameraTargetTemp[2]);
|
||||
this.camera.perspective(this.modelCamera.fieldOfView,
|
||||
Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(), this.modelCamera.nearClippingPlane,
|
||||
this.modelCamera.farClippingPlane);
|
||||
this.camera.perspective(this.modelCamera.fieldOfView, this.camera.getAspect(),
|
||||
this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane);
|
||||
}
|
||||
|
||||
this.camera.moveToAndFace(this.position, this.target, this.worldUp);
|
||||
@ -259,7 +268,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
// }
|
||||
}
|
||||
|
||||
private final float cameraSpeed = 10.0f;
|
||||
private final float cameraSpeed = 4096.0f; // per second
|
||||
private final Vector2 cameraVelocity = new Vector2();
|
||||
private Scene portraitScene;
|
||||
|
||||
@ -304,6 +313,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
@Override
|
||||
public boolean touchDown(final int screenX, final int screenY, final int pointer, final int button) {
|
||||
System.out.println(screenX + "," + screenY);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,8 @@ public class FolderDataSource implements DataSource {
|
||||
if ("".equals(filepath)) {
|
||||
return false; // special case for folder data source, dont do this
|
||||
}
|
||||
return Files.exists(this.folderPath.resolve(filepath));
|
||||
final Path resolvedPath = this.folderPath.resolve(filepath);
|
||||
return Files.exists(resolvedPath) && !Files.isDirectory(resolvedPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.etheller.warsmash.datasources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class SubdirDataSource implements DataSource {
|
||||
private final DataSource dataSource;
|
||||
private final String subdir;
|
||||
|
||||
public SubdirDataSource(final DataSource dataSource, final String subdir) {
|
||||
this.dataSource = dataSource;
|
||||
this.subdir = subdir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile(final String filepath) throws IOException {
|
||||
return this.dataSource.getFile(this.subdir + filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
||||
return this.dataSource.getResourceAsStream(this.subdir + filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final String filepath) {
|
||||
return this.dataSource.has(this.subdir + filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getListfile() {
|
||||
final List<String> results = new ArrayList<>();
|
||||
for (final String x : this.dataSource.getListfile()) {
|
||||
if (x.startsWith(this.subdir)) {
|
||||
results.add(x.substring(this.subdir.length()));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.dataSource.close();
|
||||
}
|
||||
}
|
@ -451,6 +451,11 @@ public enum RenderMathUtils {
|
||||
return out;
|
||||
}
|
||||
|
||||
// ==== All of the following "wrap" calls are horribly inefficient. Eventually
|
||||
// they should be removed entirely with better design.
|
||||
// Until that happens, be sure to only call them during setup and not while the
|
||||
// simulation is live. Otherwise you'll probably get some
|
||||
// bad lag (and memory leaks?).
|
||||
public static ShortBuffer wrapFaces(final int[] faces) {
|
||||
final ShortBuffer wrapper = ByteBuffer.allocateDirect(faces.length * 2).order(ByteOrder.nativeOrder())
|
||||
.asShortBuffer();
|
||||
@ -517,4 +522,36 @@ public enum RenderMathUtils {
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static Buffer wrap(final List<float[]> vertices) {
|
||||
if (vertices.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final int expectedNumberOfFloats = vertices.get(0).length;
|
||||
final FloatBuffer wrapper = ByteBuffer.allocateDirect(vertices.size() * expectedNumberOfFloats * 4)
|
||||
.order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
for (final float[] subArray : vertices) {
|
||||
for (final float f : subArray) {
|
||||
wrapper.put(f);
|
||||
}
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static Buffer wrapFaces(final List<int[]> indices) {
|
||||
if (indices.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final int expectedNumberOfValues = indices.get(0).length;
|
||||
final ShortBuffer wrapper = ByteBuffer.allocateDirect(indices.size() * expectedNumberOfValues * 2)
|
||||
.order(ByteOrder.nativeOrder()).asShortBuffer();
|
||||
for (final int[] subArray : indices) {
|
||||
for (final int value : subArray) {
|
||||
wrapper.put((short) value);
|
||||
}
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ package com.etheller.warsmash.util;
|
||||
|
||||
public class WarsmashConstants {
|
||||
public static final int MAX_PLAYERS = 28;
|
||||
public static final int REPLACEABLE_TEXTURE_LIMIT = 64;
|
||||
}
|
||||
|
@ -146,6 +146,10 @@ public class Camera {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public float getAspect() {
|
||||
return this.aspect;
|
||||
}
|
||||
|
||||
public void setLocation(final Vector3 location) {
|
||||
this.location.set(location);
|
||||
|
||||
|
@ -124,4 +124,6 @@ public abstract class ModelInstance extends Node {
|
||||
public abstract void load();
|
||||
|
||||
protected abstract RenderBatch getBatch(TextureMapper textureMapper2);
|
||||
|
||||
public abstract void setReplaceableTexture(int replaceableTextureId, String replaceableTextureFile);
|
||||
}
|
||||
|
@ -177,6 +177,9 @@ public class ModelViewer {
|
||||
|
||||
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
||||
try {
|
||||
if (!this.dataSource.has(finalSrc)) {
|
||||
System.err.println("Attempting to load non-existant file: " + finalSrc);
|
||||
}
|
||||
resource.loadData(this.dataSource.getResourceAsStream(finalSrc), null);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
|
@ -12,10 +12,20 @@ public interface PathSolver {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
final int dotIndex = src.lastIndexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
if ((dotIndex == -1)) {
|
||||
throw new IllegalStateException("unable to resolve: " + src);
|
||||
}
|
||||
return new SolvedPath(src, src.substring(dotIndex), true);
|
||||
}
|
||||
};
|
||||
public static final PathSolver NOFETCH = new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
final int dotIndex = src.lastIndexOf('.');
|
||||
if ((dotIndex == -1)) {
|
||||
throw new IllegalStateException("unable to resolve: " + src);
|
||||
}
|
||||
return new SolvedPath(src, src.substring(dotIndex), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
||||
private int wrapT = GL20.GL_CLAMP_TO_EDGE;
|
||||
private final int magFilter = GL20.GL_LINEAR;
|
||||
private final int minFilter = GL20.GL_LINEAR;
|
||||
private ByteBuffer data;
|
||||
|
||||
public RawOpenGLTextureResource(final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl, final ResourceHandler handler) {
|
||||
@ -124,6 +125,7 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
this.data = buffer;
|
||||
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.handle);
|
||||
|
||||
@ -140,4 +142,21 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* I really don't like holding the reference to the original buffer like this.
|
||||
* Seems wasteful. It's already on the GPU. However, while porting some code for
|
||||
* shadow maps I hit a point where I really finally felt obligated to add this
|
||||
* (there is some code in the Terrain stuff that should've had this, but
|
||||
* doesn't, and does its own texture management as a result).
|
||||
*
|
||||
* So, as a note to future authors, please reinvent the system such that this
|
||||
* cached buffer data is only stored for shadow maps and terrain textures or
|
||||
* whatever. Right now, this holds a reference to these guys on every texture,
|
||||
* on every unit, on every doodad, etc. Java will not be able to garbage collect
|
||||
* them because we hold on to the buffer in "update()".
|
||||
*/
|
||||
public ByteBuffer getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,4 @@ package com.etheller.warsmash.viewer5.gl;
|
||||
|
||||
public class Extensions {
|
||||
public static ANGLEInstancedArrays angleInstancedArrays;
|
||||
|
||||
public static int GL_BGRA;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
@ -26,8 +27,6 @@ public class BatchGroup extends GenericGroup {
|
||||
final MdxModel model = this.model;
|
||||
final List<Texture> textures = model.getTextures();
|
||||
final MdxHandler handler = model.handler;
|
||||
final List<Texture> teamColors = MdxHandler.teamColors;
|
||||
final List<Texture> teamGlows = MdxHandler.teamGlows;
|
||||
final List<Batch> batches = model.batches;
|
||||
final List<Integer> replaceables = model.replaceables;
|
||||
final ModelViewer viewer = model.viewer;
|
||||
@ -95,11 +94,9 @@ public class BatchGroup extends GenericGroup {
|
||||
final Integer replaceable = replaceables.get(layerTexture); // TODO is this OK?
|
||||
Texture texture;
|
||||
|
||||
if (replaceable == 1) {
|
||||
texture = teamColors.get(instance.teamColor);
|
||||
}
|
||||
else if (replaceable == 2) {
|
||||
texture = teamGlows.get(instance.teamColor);
|
||||
if ((replaceable > 0) && (replaceable < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT)
|
||||
&& (instance.replaceableTextures[replaceable] != null)) {
|
||||
texture = instance.replaceableTextures[replaceable];
|
||||
}
|
||||
else {
|
||||
texture = textures.get(layerTexture);
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.Camera;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
@ -77,7 +78,6 @@ public class GeometryEmitterFuncs {
|
||||
final ParticleEmitter2Object emitterObject = emitter.emitterObject;
|
||||
final int modelSpace = emitterObject.modelSpace;
|
||||
final float tailLength = emitterObject.tailLength;
|
||||
final int teamColor = instance.teamColor;
|
||||
int offset = 0;
|
||||
|
||||
for (int objectIndex = 0; objectIndex < emitter.alive; objectIndex++) {
|
||||
@ -131,7 +131,7 @@ public class GeometryEmitterFuncs {
|
||||
floatView.put(floatOffset + FLOAT_OFFSET_HEALTH, object.health);
|
||||
|
||||
byteView.put(byteOffset + BYTE_OFFSET_TAIL, (byte) tail);
|
||||
byteView.put(byteOffset + BYTE_OFFSET_TEAM_COLOR, (byte) teamColor);
|
||||
byteView.put(byteOffset + BYTE_OFFSET_TEAM_COLOR, (byte) 0);
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
@ -154,15 +154,9 @@ public class GeometryEmitterFuncs {
|
||||
|
||||
gl.glBlendFunc(emitterObject.blendSrc, emitterObject.blendDst);
|
||||
|
||||
if (replaceableId == 1) {
|
||||
final List<Texture> teamColors = model.reforged ? MdxHandler.reforgedTeamColors : MdxHandler.teamColors;
|
||||
|
||||
texture = teamColors.get(instance.teamColor);
|
||||
}
|
||||
else if (replaceableId == 2) {
|
||||
final List<Texture> teamGlows = model.reforged ? MdxHandler.reforgedTeamGlows : MdxHandler.teamGlows;
|
||||
|
||||
texture = teamGlows.get(instance.teamColor);
|
||||
if ((replaceableId > 0) && (replaceableId < WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT)
|
||||
&& (instance.replaceableTextures[(int) replaceableId] != null)) {
|
||||
texture = instance.replaceableTextures[(int) replaceableId];
|
||||
}
|
||||
else {
|
||||
texture = emitterObject.internalTexture;
|
||||
|
@ -10,9 +10,11 @@ import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.GenericNode;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.Node;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SkeletalNode;
|
||||
@ -43,7 +45,6 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
public int sequence = -1;
|
||||
public int sequenceLoopMode = 0;
|
||||
public boolean sequenceEnded = false;
|
||||
public int teamColor = 0;
|
||||
public float[] vertexColor = { 1, 1, 1, 1 };
|
||||
// Particles do not spawn when the sequence is -1, or when the sequence finished
|
||||
// and it's not repeating
|
||||
@ -59,7 +60,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
public Matrix4[] worldMatrices;
|
||||
public FloatBuffer worldMatricesCopyHeap;
|
||||
public DataTexture boneTexture;
|
||||
public Texture[] replaceableTextures = new Texture[64];
|
||||
public Texture[] replaceableTextures = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT];
|
||||
|
||||
public MdxComplexInstance(final MdxModel model) {
|
||||
super(model);
|
||||
@ -572,11 +573,21 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
* Set the team color of this instance.
|
||||
*/
|
||||
public MdxComplexInstance setTeamColor(final int id) {
|
||||
this.teamColor = id;
|
||||
|
||||
this.replaceableTextures[1] = (Texture) this.model.viewer.load(
|
||||
"ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(id) + ".blp",
|
||||
PathSolver.DEFAULT, null);
|
||||
this.replaceableTextures[2] = (Texture) this.model.viewer.load(
|
||||
"ReplaceableTextures\\" + ReplaceableIds.getPathString(2) + ReplaceableIds.getIdString(id) + ".blp",
|
||||
PathSolver.DEFAULT, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReplaceableTexture(final int replaceableTextureId, final String replaceableTextureFile) {
|
||||
this.replaceableTextures[replaceableTextureId] = (Texture) this.model.viewer.load(replaceableTextureFile,
|
||||
PathSolver.DEFAULT, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the vertex color of this instance.
|
||||
*/
|
||||
|
@ -1,26 +1,16 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
import com.etheller.warsmash.viewer5.handlers.blp.BlpHandler;
|
||||
|
||||
public class MdxHandler extends ModelHandler {
|
||||
|
||||
// Team color/glow textures, shared between all models, but loaded with the
|
||||
// first model that uses them.
|
||||
public static final List<Texture> teamColors = new ArrayList<>();
|
||||
public static final List<Texture> teamGlows = new ArrayList<>();
|
||||
|
||||
public static final List<Texture> reforgedTeamColors = new ArrayList<>();
|
||||
public static final List<Texture> reforgedTeamGlows = new ArrayList<>();
|
||||
|
||||
public MdxHandler() {
|
||||
this.extensions = new ArrayList<>();
|
||||
this.extensions.add(new String[] { ".mdx", "arrayBuffer" });
|
||||
|
@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.GL20;
|
||||
import com.etheller.warsmash.parsers.mdlx.Extent;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
@ -131,7 +130,6 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
}
|
||||
|
||||
final GL20 gl = viewer.gl;
|
||||
boolean usingTeamTextures = false;
|
||||
|
||||
// Textures.
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Texture texture : parser.getTextures()) {
|
||||
@ -140,16 +138,20 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
final int flags = texture.getFlags();
|
||||
|
||||
if (replaceableId != 0) {
|
||||
path = "ReplaceableTextures\\" + ReplaceableIds.getPathString(replaceableId) + ".blp";
|
||||
|
||||
if ((replaceableId == 1) || (replaceableId == 2)) {
|
||||
usingTeamTextures = true;
|
||||
}
|
||||
// TODO This uses dumb, stupid, terrible, no-good hardcoded replaceable IDs
|
||||
// instead of the real system, because currently MdxSimpleInstance is not
|
||||
// supporting it correctly.
|
||||
final String idString = ((replaceableId == 1) || (replaceableId == 2)) ? ReplaceableIds.getIdString(0)
|
||||
: "";
|
||||
path = "ReplaceableTextures\\" + ReplaceableIds.getPathString(replaceableId) + idString + ".blp";
|
||||
}
|
||||
|
||||
if (reforged && !path.endsWith(".dds")) {
|
||||
path = path.substring(0, path.length() - 4) + ".dds";
|
||||
}
|
||||
else if ("".equals(path)) {
|
||||
path = "Textures\\white.blp";
|
||||
}
|
||||
|
||||
final Texture viewerTexture = (Texture) viewer.load(path, pathSolver, solverParams);
|
||||
|
||||
@ -168,24 +170,6 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
this.textures.add(viewerTexture);
|
||||
}
|
||||
|
||||
// Start loading the team color and glow textures if this model uses them and
|
||||
// they weren't loaded previously.
|
||||
if (usingTeamTextures) {
|
||||
final List<Texture> teamColors = reforged ? MdxHandler.reforgedTeamColors : MdxHandler.teamColors;
|
||||
final List<Texture> teamGlows = reforged ? MdxHandler.reforgedTeamGlows : MdxHandler.teamGlows;
|
||||
|
||||
if (teamColors.isEmpty()) {
|
||||
for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) {
|
||||
final String id = ReplaceableIds.getIdString(i);
|
||||
|
||||
teamColors.add((Texture) viewer.load("ReplaceableTextures\\TeamColor\\TeamColor" + id + texturesExt,
|
||||
pathSolver, solverParams));
|
||||
teamGlows.add((Texture) viewer.load("ReplaceableTextures\\TeamGlow\\TeamGlow" + id + texturesExt,
|
||||
pathSolver, solverParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Geoset animations
|
||||
for (final com.etheller.warsmash.parsers.mdlx.GeosetAnimation geosetAnimation : parser.getGeosetAnimations()) {
|
||||
this.geosetAnimations.add(new GeosetAnimation(this, geosetAnimation));
|
||||
|
@ -1,11 +1,15 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.BatchedInstance;
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
|
||||
public class MdxSimpleInstance extends BatchedInstance {
|
||||
public Texture[] replaceableTextures = new Texture[WarsmashConstants.REPLACEABLE_TEXTURE_LIMIT];
|
||||
|
||||
public MdxSimpleInstance(final Model model) {
|
||||
super(model);
|
||||
@ -36,4 +40,9 @@ public class MdxSimpleInstance extends BatchedInstance {
|
||||
return new MdxRenderBatch(this.scene, this.model, textureMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReplaceableTexture(final int replaceableTextureId, final String replaceableTextureFile) {
|
||||
this.replaceableTextures[replaceableTextureId] = (Texture) this.model.viewer.load(replaceableTextureFile,
|
||||
PathSolver.DEFAULT, null);
|
||||
}
|
||||
}
|
||||
|
@ -106,11 +106,11 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
||||
}
|
||||
|
||||
public int getWidth(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2N.getWar3id(), sequence, frame, counter, this.width);
|
||||
return this.getScalarValue(out, AnimationMap.KP2N.getWar3id(), sequence, frame, counter, this.length);
|
||||
}
|
||||
|
||||
public int getLength(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2W.getWar3id(), sequence, frame, counter, this.length);
|
||||
return this.getScalarValue(out, AnimationMap.KP2W.getWar3id(), sequence, frame, counter, this.width);
|
||||
}
|
||||
|
||||
public int getSpeed(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
|
@ -13,8 +13,8 @@ public class ReplaceableIds {
|
||||
for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) {
|
||||
ID_TO_STR.put(Long.valueOf(i), String.format("%2d", i).replace(' ', '0'));
|
||||
}
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(1), "TeamColor\\TeamColor00");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(2), "TeamGlow\\TeamGlow00");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(1), "TeamColor\\TeamColor");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(2), "TeamGlow\\TeamGlow");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(11), "Cliff\\Cliff0");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(21), ""); // Used by all cursor models (HumanCursor, OrcCursor,
|
||||
// UndeadCursor, NightElfCursor)
|
||||
|
@ -4,10 +4,13 @@ import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
|
||||
public class Doodad {
|
||||
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
|
||||
private static final War3ID TEX_ID = War3ID.fromString("btxi");
|
||||
private final ModelInstance instance;
|
||||
private final MutableGameObject row;
|
||||
|
||||
@ -30,6 +33,17 @@ public class Doodad {
|
||||
final float defScale = row.readSLKTagFloat("defScale");
|
||||
instance.uniformScale(defScale);
|
||||
}
|
||||
if (type == WorldEditorDataType.DESTRUCTIBLES) {
|
||||
// TODO destructables need to be their own type, game simulation, etc
|
||||
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
|
||||
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
|
||||
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
|
||||
if (!replaceableTextureFile.toLowerCase().endsWith(".blp")) {
|
||||
replaceableTextureFile += ".blp";
|
||||
}
|
||||
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
|
||||
}
|
||||
}
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
this.instance = instance;
|
||||
|
@ -0,0 +1,138 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
|
||||
/**
|
||||
* TODO this is copied from RivSoft stuff.
|
||||
* https://github.com/d07RiV/wc3data/blob/3435e9728663825d892693318d0a0bb823dfad8c/src/mdx/viewer/handlers/w3x/splatmodel.js
|
||||
*
|
||||
* Shouldn't this just be a geomtry shader that takes X/Y/Texture as input and
|
||||
* renders a splat, so that we can simply change the X/Y attribute values and
|
||||
* move around the unit selection circles without memory allocations? For now I
|
||||
* plan to simply port the RivSoft stuff, and come back later.
|
||||
*/
|
||||
public class SplatModel {
|
||||
private static final int MAX_VERTICES = 65000;
|
||||
private final Texture texture;
|
||||
private final List<Batch> batches;
|
||||
public final float[] color;
|
||||
|
||||
public SplatModel(final GL30 gl, final Texture texture, final List<float[]> locations, final float[] centerOffset) {
|
||||
this.texture = texture;
|
||||
this.batches = new ArrayList<>();
|
||||
this.color = new float[] { 1, 1, 1, 1 };
|
||||
|
||||
final List<float[]> vertices = new ArrayList<>();
|
||||
final List<float[]> uvs = new ArrayList<>();
|
||||
final List<int[]> indices = new ArrayList<>();
|
||||
final int instances = locations.size();
|
||||
for (int idx = 0; idx < instances; ++idx) {
|
||||
final float[] locs = locations.get(idx);
|
||||
final float x0 = locs[0];
|
||||
final float y0 = locs[1];
|
||||
final float x1 = locs[2];
|
||||
final float y1 = locs[3];
|
||||
final float zoffs = locs[4];
|
||||
|
||||
final int ix0 = (int) Math.floor((x0 - centerOffset[0]) / 128.0);
|
||||
final int ix1 = (int) Math.ceil((x1 - centerOffset[0]) / 128.0);
|
||||
final int iy0 = (int) Math.floor((y0 - centerOffset[1]) / 128.0);
|
||||
final int iy1 = (int) Math.ceil((y1 - centerOffset[1]) / 128.0);
|
||||
|
||||
final float newVerts = ((iy1 - iy0) + 1) * ((ix1 - ix0) + 1);
|
||||
if (newVerts > MAX_VERTICES) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int start = vertices.size();
|
||||
final int step = (ix1 - ix0) + 1;
|
||||
if ((start + newVerts) > MAX_VERTICES) {
|
||||
this.addBatch(gl, vertices, uvs, indices);
|
||||
vertices.clear();
|
||||
uvs.clear();
|
||||
indices.clear();
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for (int iy = iy0; iy <= iy1; ++iy) {
|
||||
final float y = (iy * 128.0f) + centerOffset[1];
|
||||
for (int ix = ix0; ix <= ix1; ++ix) {
|
||||
final float x = (ix * 128.0f) + centerOffset[0];
|
||||
vertices.add(new float[] { x, y, zoffs });
|
||||
uvs.add(new float[] { (x - x0) / (x1 - x0), 1.0f - ((y - y0) / (y1 - y0)) });
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < (iy1 - iy0); ++i) {
|
||||
for (int j = 0; j < (ix1 - ix0); ++j) {
|
||||
final int i0 = start + (i * step) + j;
|
||||
indices.add(new int[] { i0, i0 + 1, i0 + step, i0 + 1, i0 + step + 1, i0 + step });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (indices.size() > 0) {
|
||||
this.addBatch(gl, vertices, uvs, indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addBatch(final GL30 gl, final List<float[]> vertices, final List<float[]> uvs,
|
||||
final List<int[]> indices) {
|
||||
final int uvsOffset = vertices.size() * 3 * 4;
|
||||
|
||||
final int vertexBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, vertexBuffer);
|
||||
gl.glBufferData(GL30.GL_ARRAY_BUFFER, uvsOffset + (uvs.size() * 4 * 2), null, GL30.GL_STATIC_DRAW);
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, 0, vertices.size() * 4 * 5, RenderMathUtils.wrap(vertices));
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, uvsOffset, uvs.size() * 4 * 2, RenderMathUtils.wrap(uvs));
|
||||
|
||||
final int faceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, faceBuffer);
|
||||
gl.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, indices.size() * 6 * 2, RenderMathUtils.wrapFaces(indices),
|
||||
GL30.GL_STATIC_DRAW);
|
||||
|
||||
this.batches.add(new Batch(uvsOffset, vertexBuffer, faceBuffer, indices.size() * 6));
|
||||
}
|
||||
|
||||
public void render(final GL30 gl, final ShaderProgram shader) {
|
||||
// Texture
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE1);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.texture.getGlHandle());
|
||||
shader.setUniform4fv("u_color", this.color, 0, 4);
|
||||
|
||||
for (final Batch b : this.batches) {
|
||||
// Vertices
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, b.vertexBuffer);
|
||||
shader.setVertexAttribute("a_position", 3, GL30.GL_FLOAT, false, 12, 0);
|
||||
shader.setVertexAttribute("a_uv", 2, GL30.GL_FLOAT, false, 8, b.uvsOffset);
|
||||
|
||||
// Faces.
|
||||
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, b.faceBuffer);
|
||||
|
||||
// Draw
|
||||
gl.glDrawElements(GL30.GL_TRIANGLES, b.elements, GL30.GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Batch {
|
||||
private final int uvsOffset;
|
||||
private final int vertexBuffer;
|
||||
private final int faceBuffer;
|
||||
private final int elements;
|
||||
|
||||
public Batch(final int uvsOffset, final int vertexBuffer, final int faceBuffer, final int elements) {
|
||||
this.uvsOffset = uvsOffset;
|
||||
this.vertexBuffer = vertexBuffer;
|
||||
this.faceBuffer = faceBuffer;
|
||||
this.elements = elements;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.WarsmashGdxGame;
|
||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
PuffTheMagicDragonIsNoMoreToday at 9:06 PM
|
||||
that being said I think we call the tiles corners or whatever, since we store the points rather than the quads
|
||||
but at the same time there's also per-quad data xDS
|
||||
|
||||
ReteraToday at 9:06 PM
|
||||
yeah, I've seen the corner class go by several times while transcribing this latest bit to java
|
||||
hmmm
|
||||
|
||||
PuffTheMagicDragonIsNoMoreToday at 9:07 PM
|
||||
some things are per-corner, some per-tile
|
||||
note that the existing code only somewhat takes care of cliff/terrain doodads, and it's not tested much
|
||||
|
||||
ReteraToday at 9:10 PM
|
||||
well, I'll probably rip some stuff off of HiveWE too
|
||||
that was what I was thinking I'd probably do if it was necessary
|
||||
|
||||
PuffTheMagicDragonIsNoMoreToday at 9:11 PM
|
||||
last time I checked it wasn't implemented there, but that was a long time ago
|
||||
basically you want to not have ground tiles where the doodads exist, and to know where that is you need the pathing texture used by the doodads
|
||||
|
||||
ReteraToday at 9:12 PM
|
||||
oh yea
|
||||
makes sense
|
||||
|
||||
PuffTheMagicDragonIsNoMoreToday at 9:13 PM
|
||||
they also can't be supported by my hacky TerrainModel or whatever it was called, since at least some of them require blending
|
||||
*
|
||||
*/
|
||||
public class TerrainModel {
|
||||
private static final IntBuffer GL_TEMP_BUFFER = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder())
|
||||
.asIntBuffer();
|
||||
private final War3MapViewer viewer;
|
||||
private final int vertexBuffer;
|
||||
private final int faceBuffer;
|
||||
private final int normalsOffset;
|
||||
private final int uvsOffset;
|
||||
private final int elements;
|
||||
private final int locationAndTextureBuffer;
|
||||
private final int texturesOffset;
|
||||
private final int instances;
|
||||
private final int vao;
|
||||
|
||||
public TerrainModel(final War3MapViewer viewer, final InputStream modelInput, final List<float[]> locations,
|
||||
final List<Integer> textures, final ShaderProgram shader) {
|
||||
final GL20 gl = viewer.gl;
|
||||
final WebGL webgl = viewer.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webgl.instancedArrays;
|
||||
final MdlxModel parser;
|
||||
try {
|
||||
parser = new MdlxModel(modelInput);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
final Geoset geoset = parser.getGeosets().get(0);
|
||||
final float[] vertices = geoset.getVertices();
|
||||
final float[] normals = geoset.getNormals();
|
||||
final float[] uvs = geoset.getUvSets()[0];
|
||||
final int[] faces = geoset.getFaces();
|
||||
final int normalsOffset = vertices.length * 4;
|
||||
final int uvsOffset = normalsOffset + (normals.length * 4);
|
||||
int vao;
|
||||
|
||||
GL_TEMP_BUFFER.clear();
|
||||
Gdx.gl30.glGenVertexArrays(1, GL_TEMP_BUFFER);
|
||||
vao = GL_TEMP_BUFFER.get(0);
|
||||
Gdx.gl30.glBindVertexArray(vao);
|
||||
|
||||
final int vertexBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, vertexBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, uvsOffset + (uvs.length * 4), null, GL20.GL_STATIC_DRAW);
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, vertices.length * 4, RenderMathUtils.wrap(vertices));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, normalsOffset, normals.length * 4, RenderMathUtils.wrap(normals));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, uvsOffset, uvs.length * 4, RenderMathUtils.wrap(uvs));
|
||||
|
||||
shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
shader.enableVertexAttribute("a_position");
|
||||
|
||||
shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, normalsOffset);
|
||||
shader.enableVertexAttribute("a_normal");
|
||||
|
||||
shader.setVertexAttribute("a_uv", 3, GL20.GL_FLOAT, false, 0, uvsOffset);
|
||||
shader.enableVertexAttribute("a_uv");
|
||||
|
||||
final int texturesOffset = locations.size() * 3 * 4;
|
||||
final int locationAndTextureBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, locationAndTextureBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, texturesOffset + textures.size(), null, GL20.GL_STATIC_DRAW);
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, locations.size() * 3 * 4, wrapVectors(locations));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, texturesOffset, textures.size(), wrapTexIndices(textures));
|
||||
|
||||
shader.setVertexAttribute("a_instancePosition", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
shader.enableVertexAttribute("a_instancePosition");
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_instancePosition"), 1);
|
||||
|
||||
shader.setVertexAttribute("a_instanceTexture", 1, GL20.GL_UNSIGNED_BYTE, false, 0, texturesOffset);
|
||||
shader.enableVertexAttribute("a_instanceTexture");
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_instanceTexture"), 1);
|
||||
|
||||
final int faceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, faceBuffer);
|
||||
gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, faces.length * 2, RenderMathUtils.wrapFaces(faces),
|
||||
GL20.GL_STATIC_DRAW);
|
||||
|
||||
WarsmashGdxGame.bindDefaultVertexArray();
|
||||
|
||||
this.viewer = viewer;
|
||||
this.vertexBuffer = vertexBuffer;
|
||||
this.faceBuffer = faceBuffer;
|
||||
this.normalsOffset = normalsOffset;
|
||||
this.uvsOffset = uvsOffset;
|
||||
this.elements = faces.length;
|
||||
this.locationAndTextureBuffer = locationAndTextureBuffer;
|
||||
this.texturesOffset = texturesOffset;
|
||||
this.instances = locations.size() / 3;
|
||||
this.vao = vao;
|
||||
}
|
||||
|
||||
private Buffer wrapTexIndices(final List<Integer> textures) {
|
||||
final ByteBuffer wrapper = ByteBuffer.allocateDirect(textures.size()).order(ByteOrder.nativeOrder());
|
||||
for (final Integer texture : textures) {
|
||||
wrapper.put(texture.byteValue());
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private Buffer wrapVectors(final List<float[]> locations) {
|
||||
final FloatBuffer wrapper = ByteBuffer.allocateDirect(locations.size() * 12).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
for (final float[] vector : locations) {
|
||||
wrapper.put(vector[0]);
|
||||
wrapper.put(vector[1]);
|
||||
wrapper.put(vector[2]);
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public void render(final ShaderProgram shader) {
|
||||
final War3MapViewer viewer = this.viewer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final WebGL webGL = viewer.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webGL.instancedArrays;
|
||||
|
||||
Gdx.gl30.glBindVertexArray(this.vao);
|
||||
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT, 0,
|
||||
this.instances);
|
||||
|
||||
WarsmashGdxGame.bindDefaultVertexArray();
|
||||
|
||||
}
|
||||
}
|
@ -2,22 +2,34 @@ package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
|
||||
public class Unit {
|
||||
private static final War3ID IS_BLDG = War3ID.fromString("ubdg");
|
||||
private static final War3ID RED = War3ID.fromString("uclr");
|
||||
private static final War3ID GREEN = War3ID.fromString("uclg");
|
||||
private static final War3ID BLUE = War3ID.fromString("uclb");
|
||||
private static final War3ID MODEL_SCALE = War3ID.fromString("usca");
|
||||
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
|
||||
private static final War3ID ITEM_MODEL_SCALE = War3ID.fromString("isca");
|
||||
private static final War3ID ITEM_RED = War3ID.fromString("iclr");
|
||||
private static final War3ID ITEM_GREEN = War3ID.fromString("iclg");
|
||||
private static final War3ID ITEM_BLUE = War3ID.fromString("iclb");
|
||||
private static final float[] heapZ = new float[3];
|
||||
public final MdxComplexInstance instance;
|
||||
public final MutableGameObject row;
|
||||
|
||||
public Unit(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit) {
|
||||
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final WorldEditorDataType type) {
|
||||
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
|
||||
|
||||
instance.move(unit.getLocation());
|
||||
float angle;
|
||||
if ((row != null) && row.readSLKTagBoolean("isBldg")) {
|
||||
if ((row != null) && row.getFieldAsBoolean(IS_BLDG, 0)) {
|
||||
angle = (float) Math.toRadians(270.0f);
|
||||
}
|
||||
else {
|
||||
@ -30,12 +42,28 @@ public class Unit {
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
if (row != null) {
|
||||
heapZ[2] = row.readSLKTagFloat("moveHeight");
|
||||
heapZ[2] = row.getFieldAsFloat(MOVE_HEIGHT, 0);
|
||||
|
||||
instance.move(heapZ);
|
||||
instance.setVertexColor(new float[] { (row.readSLKTagInt("red")) / 255f,
|
||||
(row.readSLKTagInt("green")) / 255f, (row.readSLKTagInt("blue")) / 255f });
|
||||
instance.uniformScale(row.readSLKTagFloat("modelScale"));
|
||||
War3ID red;
|
||||
War3ID green;
|
||||
War3ID blue;
|
||||
War3ID scale;
|
||||
if (type == WorldEditorDataType.UNITS) {
|
||||
scale = MODEL_SCALE;
|
||||
red = RED;
|
||||
green = GREEN;
|
||||
blue = BLUE;
|
||||
}
|
||||
else {
|
||||
scale = ITEM_MODEL_SCALE;
|
||||
red = ITEM_RED;
|
||||
green = ITEM_GREEN;
|
||||
blue = ITEM_BLUE;
|
||||
}
|
||||
instance.setVertexColor(new float[] { (row.getFieldAsInteger(red, 0)) / 255f,
|
||||
(row.getFieldAsInteger(green, 0)) / 255f, (row.getFieldAsInteger(blue, 0)) / 255f });
|
||||
instance.uniformScale(row.getFieldAsFloat(scale, 0));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,251 +1,60 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
public class W3xShaders {
|
||||
public static final class Cliffs {
|
||||
private Cliffs() {
|
||||
public static final class UberSplat {
|
||||
private UberSplat() {
|
||||
}
|
||||
|
||||
public static final String vert = "\r\n" + //
|
||||
"uniform mat4 u_VP;\r\n" + //
|
||||
"uniform sampler2D u_heightMap;\r\n" + //
|
||||
"uniform vec2 u_pixel;\r\n" + //
|
||||
"uniform vec2 u_centerOffset;\r\n" + //
|
||||
"attribute vec3 a_position;\r\n" + //
|
||||
// "attribute vec3 a_normal;\r\n" + //
|
||||
"attribute vec2 a_uv;\r\n" + //
|
||||
"attribute vec3 a_instancePosition;\r\n" + //
|
||||
"attribute float a_instanceTexture;\r\n" + //
|
||||
"varying vec3 v_normal;\r\n" + //
|
||||
"varying vec2 v_uv;\r\n" + //
|
||||
"varying float v_texture;\r\n" + //
|
||||
"varying vec3 v_position;\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" // Half of a pixel in the cliff height map.\r\n" + //
|
||||
" vec2 halfPixel = u_pixel * 0.5;\r\n" + //
|
||||
" // The bottom left corner of the map tile this vertex is on.\r\n" + //
|
||||
" vec2 corner = floor((a_instancePosition.xy - vec2(1.0, 0.0) - u_centerOffset.xy) / 128.0);\r\n" + //
|
||||
" // Get the 4 closest heights in the height map.\r\n" + //
|
||||
" float bottomLeft = texture2D(u_heightMap, corner * u_pixel + halfPixel).r;\r\n" + //
|
||||
" float bottomRight = texture2D(u_heightMap, (corner + vec2(1.0, 0.0)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" float topLeft = texture2D(u_heightMap, (corner + vec2(0.0, 1.0)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" float topRight = texture2D(u_heightMap, (corner + vec2(1.0, 1.0)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" \r\n" + //
|
||||
" // Do a bilinear interpolation between the heights to get the final value.\r\n" + //
|
||||
" float bottom = mix(bottomRight, bottomLeft, -a_position.x / 128.0);\r\n" + //
|
||||
" float top = mix(topRight, topLeft, -a_position.x / 128.0);\r\n" + //
|
||||
" float height = mix(bottom, top, a_position.y / 128.0);\r\n" + //
|
||||
// " v_normal = a_normal;\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_texture = a_instanceTexture;\r\n" + //
|
||||
" v_position = a_position + vec3(a_instancePosition.xy, a_instancePosition.z + height * 128.0);\r\n" + //
|
||||
" gl_Position = u_VP * vec4(v_position, 1.0);\r\n" + //
|
||||
"}\r\n" + //
|
||||
"";
|
||||
|
||||
public static final String frag = "\r\n" + //
|
||||
"// #extension GL_OES_standard_derivatives : enable\r\n" + //
|
||||
"precision mediump float;\r\n" + //
|
||||
"uniform sampler2D u_texture1;\r\n" + //
|
||||
"uniform sampler2D u_texture2;\r\n" + //
|
||||
// "varying vec3 v_normal;\r\n" + //
|
||||
"varying vec2 v_uv;\r\n" + //
|
||||
"varying float v_texture;\r\n" + //
|
||||
"varying vec3 v_position;\r\n" + //
|
||||
"// const vec3 lightDirection = normalize(vec3(-0.3, -0.3, 0.25));\r\n" + //
|
||||
"vec4 sample(int texture, vec2 uv) {\r\n" + //
|
||||
" if (texture == 0) {\r\n" + //
|
||||
" return texture2D(u_texture1, uv);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" return texture2D(u_texture2, uv);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" vec4 color = sample(int(v_texture), v_uv);\r\n" + //
|
||||
" // vec3 faceNormal = cross(dFdx(v_position), dFdy(v_position));\r\n" + //
|
||||
" // vec3 normal = normalize((faceNormal + v_normal) * 0.5);\r\n" + //
|
||||
" // color *= clamp(dot(normal, lightDirection) + 0.45, 0.1, 1.0);\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
"}\r\n" + //
|
||||
"";
|
||||
}
|
||||
|
||||
public static final class Ground {
|
||||
private Ground() {
|
||||
}
|
||||
|
||||
public static final String frag = "\r\n" + //
|
||||
"precision mediump float;\r\n" + //
|
||||
"uniform sampler2D u_tilesets[15];\r\n" + //
|
||||
"varying vec4 v_tilesets;\r\n" + //
|
||||
"varying vec2 v_uv[4];\r\n" + //
|
||||
"varying vec3 v_normal;\r\n" + //
|
||||
"const vec3 lightDirection = normalize(vec3(-0.3, -0.3, 0.25));\r\n" + //
|
||||
"vec4 sample(float tileset, vec2 uv) {\r\n" + //
|
||||
" if (tileset <= 0.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[0], uv);\r\n" + //
|
||||
" } else if (tileset <= 1.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[1], uv);\r\n" + //
|
||||
" } else if (tileset <= 2.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[2], uv);\r\n" + //
|
||||
" } else if (tileset <= 3.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[3], uv);\r\n" + //
|
||||
" } else if (tileset <= 4.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[4], uv);\r\n" + //
|
||||
" } else if (tileset <= 5.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[5], uv);\r\n" + //
|
||||
" } else if (tileset <= 6.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[6], uv);\r\n" + //
|
||||
" } else if (tileset <= 7.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[7], uv);\r\n" + //
|
||||
" } else if (tileset <= 8.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[8], uv);\r\n" + //
|
||||
" } else if (tileset <= 9.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[9], uv);\r\n" + //
|
||||
" } else if (tileset <= 10.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[10], uv);\r\n" + //
|
||||
" } else if (tileset <= 11.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[11], uv);\r\n" + //
|
||||
" } else if (tileset <= 12.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[12], uv);\r\n" + //
|
||||
" } else if (tileset <= 13.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[13], uv);\r\n" + //
|
||||
" } else if (tileset <= 14.5) {\r\n" + //
|
||||
" return texture2D(u_tilesets[14], uv);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}\r\n" + //
|
||||
"vec4 blend(vec4 color, float tileset, vec2 uv) {\r\n" + //
|
||||
" vec4 texel = sample(tileset, uv);\r\n" + //
|
||||
" return mix(color, texel, texel.a);\r\n" + //
|
||||
"}\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" vec4 color = sample(v_tilesets[0] - 1.0, v_uv[0]);\r\n" + //
|
||||
" if (v_tilesets[1] > 0.5) {\r\n" + //
|
||||
" color = blend(color, v_tilesets[1] - 1.0, v_uv[1]);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" if (v_tilesets[2] > 0.5) {\r\n" + //
|
||||
" color = blend(color, v_tilesets[2] - 1.0, v_uv[2]);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" if (v_tilesets[3] > 0.5) {\r\n" + //
|
||||
" color = blend(color, v_tilesets[3] - 1.0, v_uv[3]);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" // color *= clamp(dot(v_normal, lightDirection) + 0.45, 0.0, 1.0);\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
"}";
|
||||
|
||||
public static final String vert = "\r\n" + //
|
||||
"uniform mat4 u_VP;\r\n" + //
|
||||
"uniform sampler2D u_heightMap;\r\n" + //
|
||||
"uniform vec2 u_size;\r\n" + //
|
||||
"uniform vec2 u_offset;\r\n" + //
|
||||
"uniform bool u_extended[14];\r\n" + //
|
||||
"uniform float u_baseTileset;\r\n" + //
|
||||
"attribute vec2 a_position;\r\n" + //
|
||||
"attribute float a_InstanceID;\r\n" + //
|
||||
"attribute vec4 a_textures;\r\n" + //
|
||||
"attribute vec4 a_variations;\r\n" + //
|
||||
"varying vec4 v_tilesets;\r\n" + //
|
||||
"varying vec2 v_uv[4];\r\n" + //
|
||||
"varying vec3 v_normal;\r\n" + //
|
||||
"vec2 getCell(float variation) {\r\n" + //
|
||||
" if (variation < 16.0) {\r\n" + //
|
||||
" return vec2(mod(variation, 4.0), floor(variation / 4.0));\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" variation -= 16.0;\r\n" + //
|
||||
" return vec2(4.0 + mod(variation, 4.0), floor(variation / 4.0));\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}\r\n" + //
|
||||
"vec2 getUV(vec2 position, bool extended, float variation) {\r\n" + //
|
||||
" vec2 cell = getCell(variation);\r\n" + //
|
||||
" vec2 cellSize = vec2(extended ? 0.125 : 0.25, 0.25);\r\n" + //
|
||||
" vec2 uv = vec2(position.x, 1.0 - position.y);\r\n" + //
|
||||
" vec2 pixelSize = vec2(1.0 / 512.0, 1.0 / 256.0); /// Note: hardcoded to 512x256 for now.\r\n" + //
|
||||
" return clamp((cell + uv) * cellSize, cell * cellSize + pixelSize, (cell + 1.0) * cellSize - pixelSize); \r\n"
|
||||
" uniform mat4 u_mvp;\r\n" + //
|
||||
" uniform sampler2D u_heightMap;\r\n" + //
|
||||
" uniform vec2 u_pixel;\r\n" + //
|
||||
" uniform vec2 u_size;\r\n" + //
|
||||
" uniform vec2 u_shadowPixel;\r\n" + //
|
||||
" uniform vec2 u_centerOffset;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" const float normalDist = 0.25;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec2 halfPixel = u_pixel * 0.5;\r\n" + //
|
||||
" vec2 base = (a_position.xy - u_centerOffset) / 128.0;\r\n" + //
|
||||
" float height = texture2D(u_heightMap, base * u_pixel + halfPixel).r;\r\n" + //
|
||||
" float hL = texture2D(u_heightMap, vec2(base - vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
"}\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" vec4 textures = a_textures - u_baseTileset;\r\n" + //
|
||||
" \r\n" + //
|
||||
" if (textures[0] > 0.0 || textures[1] > 0.0 || textures[2] > 0.0 || textures[3] > 0.0) {\r\n" + //
|
||||
" v_tilesets = textures;\r\n" + //
|
||||
" v_uv[0] = getUV(a_position, u_extended[int(textures[0]) - 1], a_variations[0]);\r\n" + //
|
||||
" v_uv[1] = getUV(a_position, u_extended[int(textures[1]) - 1], a_variations[1]);\r\n" + //
|
||||
" v_uv[2] = getUV(a_position, u_extended[int(textures[2]) - 1], a_variations[2]);\r\n" + //
|
||||
" v_uv[3] = getUV(a_position, u_extended[int(textures[3]) - 1], a_variations[3]);\r\n" + //
|
||||
" vec2 corner = vec2(mod(a_InstanceID, u_size.x), floor(a_InstanceID / u_size.x));\r\n" + //
|
||||
" vec2 base = corner + a_position;\r\n" + //
|
||||
" float height = texture2D(u_heightMap, base / u_size).r;\r\n" + //
|
||||
" float hL = texture2D(u_heightMap, vec2(base - vec2(1.0, 0.0)) / (u_size)).r;\r\n" + //
|
||||
" float hR = texture2D(u_heightMap, vec2(base + vec2(1.0, 0.0)) / (u_size)).r;\r\n" + //
|
||||
" float hD = texture2D(u_heightMap, vec2(base - vec2(0.0, 1.0)) / (u_size)).r;\r\n" + //
|
||||
" float hU = texture2D(u_heightMap, vec2(base + vec2(0.0, 1.0)) / (u_size)).r;\r\n" + //
|
||||
" v_normal = normalize(vec3(hL - hR, hD - hU, 2.0));\r\n" + //
|
||||
" gl_Position = u_VP * vec4(base * 128.0 + u_offset, height * 128.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" v_tilesets = vec4(0.0);\r\n" + //
|
||||
" v_uv[0] = vec2(0.0);\r\n" + //
|
||||
" v_uv[1] = vec2(0.0);\r\n" + //
|
||||
" v_uv[2] = vec2(0.0);\r\n" + //
|
||||
" v_uv[3] = vec2(0.0);\r\n" + //
|
||||
" v_normal = vec3(0.0);\r\n" + //
|
||||
" gl_Position = vec4(0.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}";
|
||||
}
|
||||
|
||||
public static final class Water {
|
||||
private Water() {
|
||||
}
|
||||
" float hR = texture2D(u_heightMap, vec2(base + vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float hD = texture2D(u_heightMap, vec2(base - vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float hU = texture2D(u_heightMap, vec2(base + vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" v_normal = normalize(vec3(hL - hR, hD - hU, normalDist * 2.0));\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_suv = base / u_size;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(a_position.xy, height * 128.0 + a_position.z, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
||||
public static final String frag = "\r\n" + //
|
||||
"precision mediump float;\r\n" + //
|
||||
"uniform sampler2D u_waterTexture;\r\n" + //
|
||||
"varying vec2 v_uv;\r\n" + //
|
||||
"varying vec4 v_color;\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" gl_FragColor = texture2D(u_waterTexture, v_uv) * v_color;\r\n" + //
|
||||
"}\r\n" + //
|
||||
"";
|
||||
public static final String vert = "\r\n" + //
|
||||
"uniform mat4 u_VP;\r\n" + //
|
||||
"uniform sampler2D u_heightMap;\r\n" + //
|
||||
"uniform sampler2D u_waterHeightMap;\r\n" + //
|
||||
"uniform vec2 u_size;\r\n" + //
|
||||
"uniform vec2 u_offset;\r\n" + //
|
||||
"uniform float u_offsetHeight;\r\n" + //
|
||||
"uniform vec4 u_minDeepColor;\r\n" + //
|
||||
"uniform vec4 u_maxDeepColor;\r\n" + //
|
||||
"uniform vec4 u_minShallowColor;\r\n" + //
|
||||
"uniform vec4 u_maxShallowColor;\r\n" + //
|
||||
"attribute vec2 a_position;\r\n" + //
|
||||
"attribute float a_InstanceID;\r\n" + //
|
||||
"attribute float a_isWater;\r\n" + //
|
||||
"varying vec2 v_uv;\r\n" + //
|
||||
"varying vec4 v_color;\r\n" + //
|
||||
"const float minDepth = 10.0 / 128.0;\r\n" + //
|
||||
"const float deepLevel = 64.0 / 128.0;\r\n" + //
|
||||
"const float maxDepth = 72.0 / 128.0;\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" if (a_isWater > 0.5) {\r\n" + //
|
||||
" v_uv = a_position;\r\n" + //
|
||||
" vec2 corner = vec2(mod(a_InstanceID, u_size.x), floor(a_InstanceID / u_size.x));\r\n" + //
|
||||
" vec2 base = corner + a_position;\r\n" + //
|
||||
" float height = texture2D(u_heightMap, base / u_size).r;\r\n" + //
|
||||
" float waterHeight = texture2D(u_waterHeightMap, base / u_size).r + u_offsetHeight;\r\n" + //
|
||||
" float value = clamp(waterHeight - height, 0.0, 1.0);\r\n" + //
|
||||
" if (value <= deepLevel) {\r\n" + //
|
||||
" value = max(0.0, value - minDepth) / (deepLevel - minDepth);\r\n" + //
|
||||
" v_color = mix(u_minShallowColor, u_maxShallowColor, value) / 255.0;\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" value = clamp(value - deepLevel, 0.0, maxDepth - deepLevel) / (maxDepth - deepLevel);\r\n" + //
|
||||
" v_color = mix(u_minDeepColor, u_maxDeepColor, value) / 255.0;\r\n" + //
|
||||
" uniform sampler2D u_texture;\r\n" + //
|
||||
" uniform sampler2D u_shadowMap;\r\n" + //
|
||||
" uniform vec4 u_color;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" const vec3 lightDirection = normalize(vec3(-0.3, -0.3, 0.25));\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if (any(bvec4(lessThan(v_uv, vec2(0.0)), greaterThan(v_uv, vec2(1.0))))) {\r\n" + //
|
||||
" discard;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" vec4 color = texture2D(u_texture, clamp(v_uv, 0.0, 1.0)).rgba * u_color;\r\n" + //
|
||||
" float shadow = texture2D(u_shadowMap, v_suv).r;\r\n" + //
|
||||
" color.xyz *= clamp(dot(v_normal, lightDirection) + 0.45, 0.0, 1.0);\r\n" + //
|
||||
" color.xyz *= 1.0 - shadow;\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" gl_Position = u_VP * vec4(base * 128.0 + u_offset, waterHeight * 128.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" v_uv = vec2(0.0);\r\n" + //
|
||||
" v_color = vec4(0.0);\r\n" + //
|
||||
" gl_Position = vec4(0.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"}\r\n" + //
|
||||
"";
|
||||
" ";
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,14 @@ import com.etheller.warsmash.common.LoadGenericCallback;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSource;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.MpqDataSource;
|
||||
import com.etheller.warsmash.datasources.SubdirDataSource;
|
||||
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.War3MapW3e;
|
||||
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
@ -42,12 +44,20 @@ 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;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain.Splat;
|
||||
|
||||
import mpq.MPQArchive;
|
||||
import mpq.MPQException;
|
||||
|
||||
public class War3MapViewer extends ModelViewer {
|
||||
private static final War3ID UNIT_FILE = War3ID.fromString("umdl");
|
||||
private static final War3ID UBER_SPLAT = War3ID.fromString("uubs");
|
||||
private static final War3ID UNIT_SHADOW = War3ID.fromString("ushu");
|
||||
private static final War3ID UNIT_SHADOW_X = War3ID.fromString("ushx");
|
||||
private static final War3ID UNIT_SHADOW_Y = War3ID.fromString("ushy");
|
||||
private static final War3ID UNIT_SHADOW_W = War3ID.fromString("ushw");
|
||||
private static final War3ID UNIT_SHADOW_H = War3ID.fromString("ushh");
|
||||
private static final War3ID BUILDING_SHADOW = War3ID.fromString("ushb");
|
||||
private static final War3ID ITEM_FILE = War3ID.fromString("ifil");
|
||||
private static final War3ID sloc = War3ID.fromString("sloc");
|
||||
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
|
||||
@ -96,7 +106,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
this.worldScene = this.addScene();
|
||||
|
||||
loadSLKs();
|
||||
}
|
||||
|
||||
public void loadSLKs() {
|
||||
@ -161,6 +170,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
public void loadMap(final String mapFilePath) throws IOException {
|
||||
loadSLKs();
|
||||
final War3Map war3Map = new War3Map(this.gameDataSource, mapFilePath);
|
||||
|
||||
this.mapMpq = war3Map;
|
||||
@ -175,23 +185,31 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
tileset = w3iFile.getTileset();
|
||||
|
||||
final DataSource tilesetSource;
|
||||
DataSource tilesetSource;
|
||||
try {
|
||||
// Slightly complex. Here's the theory:
|
||||
// 1.) Copy map into RAM
|
||||
// 2.) Setup a Data Source that will read assets
|
||||
// from either the map or the game, giving the map priority.
|
||||
SeekableByteChannel sbc;
|
||||
try (InputStream mapStream = war3Map.getCompoundDataSource().getResourceAsStream(tileset + ".mpq")) {
|
||||
final byte[] mapData = IOUtils.toByteArray(mapStream);
|
||||
sbc = new SeekableInMemoryByteChannel(mapData);
|
||||
final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc);
|
||||
tilesetSource = new CompoundDataSource(
|
||||
Arrays.asList(war3Map.getCompoundDataSource(), internalMpqContentsDataSource));
|
||||
final CompoundDataSource compoundDataSource = war3Map.getCompoundDataSource();
|
||||
try (InputStream mapStream = compoundDataSource.getResourceAsStream(tileset + ".mpq")) {
|
||||
if (mapStream == null) {
|
||||
tilesetSource = new CompoundDataSource(Arrays.asList(compoundDataSource,
|
||||
new SubdirDataSource(compoundDataSource, tileset + ".mpq/")));
|
||||
}
|
||||
else {
|
||||
final byte[] mapData = IOUtils.toByteArray(mapStream);
|
||||
sbc = new SeekableInMemoryByteChannel(mapData);
|
||||
final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc);
|
||||
tilesetSource = new CompoundDataSource(
|
||||
Arrays.asList(compoundDataSource, internalMpqContentsDataSource));
|
||||
}
|
||||
}
|
||||
catch (final IOException exc) {
|
||||
tilesetSource = new CompoundDataSource(
|
||||
Arrays.asList(compoundDataSource, new SubdirDataSource(compoundDataSource, tileset + ".mpq/")));
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final MPQException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -202,8 +220,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
final War3MapW3e terrainData = this.mapMpq.readEnvironment();
|
||||
|
||||
this.terrain = new Terrain(terrainData, this.webGL, this.dataSource, new WorldEditStrings(this.dataSource),
|
||||
this);
|
||||
this.terrain = new Terrain(terrainData, w3iFile, this.webGL, this.dataSource,
|
||||
new WorldEditStrings(this.dataSource), this);
|
||||
|
||||
final float[] centerOffset = terrainData.getCenterOffset();
|
||||
final int[] mapSize = terrainData.getMapSize();
|
||||
@ -264,7 +282,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
String file = row.readSLKTag("file");
|
||||
final int numVar = row.readSLKTagInt("numVar");
|
||||
|
||||
if (file.endsWith(".mdx")) {
|
||||
if (file.endsWith(".mdx") || file.endsWith(".mdl")) {
|
||||
file = file.substring(0, file.length() - 4);
|
||||
}
|
||||
|
||||
@ -337,25 +355,28 @@ public class War3MapViewer extends ModelViewer {
|
||||
String path = null;
|
||||
|
||||
// Hardcoded?
|
||||
WorldEditorDataType type = null;
|
||||
if (sloc.equals(unit.getId())) {
|
||||
path = "Objects\\StartLocation\\StartLocation.mdx";
|
||||
type = null; /// ??????
|
||||
}
|
||||
else {
|
||||
row = modifications.getUnits().get(unit.getId());
|
||||
if (row == null) {
|
||||
row = modifications.getItems().get(unit.getId());
|
||||
path = row.getFieldAsString(ITEM_FILE, 0);
|
||||
if (row != null) {
|
||||
type = WorldEditorDataType.ITEM;
|
||||
path = row.getFieldAsString(ITEM_FILE, 0);
|
||||
|
||||
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
|
||||
path = path.substring(0, path.length() - 4);
|
||||
}
|
||||
if (row.readSLKTagInt("fileVerFlags") == 2) {
|
||||
path += "_V1";
|
||||
}
|
||||
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
|
||||
path = path.substring(0, path.length() - 4);
|
||||
}
|
||||
|
||||
path += ".mdx";
|
||||
path += ".mdx";
|
||||
}
|
||||
}
|
||||
else {
|
||||
type = WorldEditorDataType.UNITS;
|
||||
path = row.getFieldAsString(UNIT_FILE, 0);
|
||||
|
||||
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
|
||||
@ -366,19 +387,61 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
path += ".mdx";
|
||||
|
||||
final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0);
|
||||
if (uberSplat != null) {
|
||||
final Element uberSplatInfo = this.terrain.uberSplatTable.get(uberSplat);
|
||||
if (uberSplatInfo != null) {
|
||||
final String texturePath = uberSplatInfo.getField("Dir") + "\\"
|
||||
+ uberSplatInfo.getField("file") + ".blp";
|
||||
if (!this.terrain.splats.containsKey(texturePath)) {
|
||||
this.terrain.splats.put(texturePath, new Splat());
|
||||
}
|
||||
final float x = unit.getLocation()[0];
|
||||
final float y = unit.getLocation()[1];
|
||||
final float s = uberSplatInfo.getFieldFloatValue("Scale");
|
||||
this.terrain.splats.get(texturePath).locations
|
||||
.add(new float[] { x - s, y - s, x + s, y + s, 1 });
|
||||
}
|
||||
}
|
||||
|
||||
final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0);
|
||||
if ((unitShadow != null) && !"_".equals(unitShadow)) {
|
||||
final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp";
|
||||
final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0);
|
||||
final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0);
|
||||
final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0);
|
||||
final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0);
|
||||
if (!this.terrain.splats.containsKey(texture)) {
|
||||
final Splat splat = new Splat();
|
||||
splat.opacity = 0.5f;
|
||||
this.terrain.splats.put(texture, splat);
|
||||
}
|
||||
final float x = unit.getLocation()[0] - shadowX;
|
||||
final float y = unit.getLocation()[1] - shadowY;
|
||||
this.terrain.splats.get(texture).locations
|
||||
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
|
||||
}
|
||||
|
||||
final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0);
|
||||
if ((buildingShadow != null) && !"_".equals(buildingShadow)) {
|
||||
this.terrain.addShadow(buildingShadow, unit.getLocation()[0], unit.getLocation()[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
|
||||
|
||||
this.units.add(new Unit(this, model, row, unit));
|
||||
this.units.add(new Unit(this, model, row, unit, type));
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown unit ID: " + unit.getId());
|
||||
}
|
||||
}
|
||||
|
||||
this.terrain.loadSplats();
|
||||
|
||||
this.unitsReady = true;
|
||||
this.anyReady = true;
|
||||
}
|
||||
@ -410,8 +473,9 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
worldScene.startFrame();
|
||||
this.terrain.renderGround();
|
||||
// this.terrain.renderCliffs();
|
||||
this.terrain.renderCliffs();
|
||||
worldScene.renderOpaque();
|
||||
this.terrain.renderUberSplats();
|
||||
this.terrain.renderWater();
|
||||
worldScene.renderTranslucent();
|
||||
|
||||
|
@ -60,8 +60,10 @@ public class CliffMesh {
|
||||
|
||||
public void renderQueue(final float[] position) {
|
||||
if (this.renderJobs.remaining() < 4) {
|
||||
final FloatBuffer newRenderJobs = ByteBuffer.allocateDirect(this.renderJobs.capacity() * 2)
|
||||
.order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
final int newCapacity = this.renderJobs.capacity() * 2;
|
||||
final FloatBuffer newRenderJobs = ByteBuffer.allocateDirect(newCapacity * 4).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
newRenderJobs.clear();
|
||||
this.renderJobs.flip();
|
||||
newRenderJobs.put(this.renderJobs);
|
||||
this.renderJobs = newRenderJobs;
|
||||
@ -79,32 +81,24 @@ public class CliffMesh {
|
||||
this.gl.glBufferData(GL20.GL_ARRAY_BUFFER, this.renderJobs.remaining() * 4, this.renderJobs,
|
||||
GL20.GL_DYNAMIC_DRAW);
|
||||
|
||||
this.gl.glEnableVertexAttribArray(0);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
this.gl.glVertexAttribPointer(0, 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
|
||||
this.gl.glEnableVertexAttribArray(1);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.uvBuffer);
|
||||
this.gl.glVertexAttribPointer(1, 2, GL20.GL_FLOAT, false, 0, 0);
|
||||
|
||||
this.gl.glEnableVertexAttribArray(2);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.normalBuffer);
|
||||
this.gl.glVertexAttribPointer(2, 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
// this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.normalBuffer);
|
||||
// this.gl.glVertexAttribPointer(2, 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
|
||||
this.gl.glEnableVertexAttribArray(3);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.normalBuffer);
|
||||
this.gl.glVertexAttribPointer(3, 4, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.gl.glVertexAttribDivisor(3, 1);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.instanceBuffer);
|
||||
this.gl.glVertexAttribPointer(2, 4, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.gl.glVertexAttribDivisor(2, 1);
|
||||
|
||||
this.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
||||
this.gl.glDrawElementsInstanced(GL20.GL_TRIANGLES, this.indices, GL30.GL_UNSIGNED_SHORT, 0,
|
||||
this.renderJobs.remaining() / 4);
|
||||
|
||||
this.gl.glVertexAttribDivisor(3, 0); // ToDo use vao
|
||||
this.gl.glDisableVertexAttribArray(0);
|
||||
this.gl.glDisableVertexAttribArray(1);
|
||||
this.gl.glDisableVertexAttribArray(2);
|
||||
this.gl.glDisableVertexAttribArray(3);
|
||||
this.gl.glVertexAttribDivisor(2, 0); // ToDo use vao
|
||||
|
||||
this.renderJobs.clear();
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import javax.imageio.ImageIO;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||
|
||||
public class GroundTexture {
|
||||
public int id;
|
||||
@ -31,7 +30,7 @@ public class GroundTexture {
|
||||
this.id = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.id);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RGBA8, this.tileSize, this.tileSize,
|
||||
this.extended ? 32 : 16, 0, Extensions.GL_BGRA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
this.extended ? 32 : 16, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
|
@ -14,6 +14,8 @@ import java.util.TreeSet;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
@ -24,6 +26,7 @@ import com.badlogic.gdx.math.Vector3;
|
||||
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.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
@ -32,9 +35,13 @@ import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
import com.etheller.warsmash.viewer5.Camera;
|
||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.RawOpenGLTextureResource;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.Variations;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.W3xShaders;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
|
||||
public class Terrain {
|
||||
@ -92,8 +99,18 @@ public class Terrain {
|
||||
private final War3MapViewer viewer;
|
||||
public float[] centerOffset;
|
||||
private final WebGL webGL;
|
||||
private final ShaderProgram uberSplatShader;
|
||||
public final DataTable uberSplatTable;
|
||||
|
||||
public Terrain(final War3MapW3e w3eFile, final WebGL webGL, final DataSource dataSource,
|
||||
private final List<SplatModel> uberSplatModels;
|
||||
private int shadowMap;
|
||||
public final Map<String, Splat> splats = new HashMap<>();
|
||||
public final Map<String, List<float[]>> shadows = new HashMap<>();
|
||||
public final Map<String, Texture> shadowTextures = new HashMap<>();
|
||||
private final int[] mapBounds;
|
||||
private final int[] mapSize;
|
||||
|
||||
public Terrain(final War3MapW3e w3eFile, final War3MapW3i w3iFile, final WebGL webGL, final DataSource dataSource,
|
||||
final WorldEditStrings worldEditStrings, final War3MapViewer viewer) throws IOException {
|
||||
this.webGL = webGL;
|
||||
this.viewer = viewer;
|
||||
@ -136,6 +153,10 @@ public class Terrain {
|
||||
try (InputStream waterSlkStream = dataSource.getResourceAsStream("TerrainArt\\Water.slk")) {
|
||||
this.waterTable.readSLK(waterSlkStream);
|
||||
}
|
||||
this.uberSplatTable = new DataTable(worldEditStrings);
|
||||
try (InputStream uberSlkStream = dataSource.getResourceAsStream("Splats\\UberSplatData.slk")) {
|
||||
this.uberSplatTable.readSLK(uberSlkStream);
|
||||
}
|
||||
|
||||
final char tileset = w3eFile.getTileset();
|
||||
final Element waterInfo = this.waterTable.get(tileset + "Sha");
|
||||
@ -147,16 +168,31 @@ public class Terrain {
|
||||
loadWaterColor(this.maxShallowColor, "Smax", waterInfo);
|
||||
loadWaterColor(this.minDeepColor, "Dmin", waterInfo);
|
||||
loadWaterColor(this.maxDeepColor, "Dmax", waterInfo);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (this.minDeepColor[i] > this.maxDeepColor[i]) {
|
||||
this.maxDeepColor[i] = this.minDeepColor[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Cliff Meshes
|
||||
|
||||
final Map<String, Integer> cliffVars = Variations.CLIFF_VARS;
|
||||
Map<String, Integer> cliffVars = Variations.CLIFF_VARS;
|
||||
for (final Map.Entry<String, Integer> cliffVar : cliffVars.entrySet()) {
|
||||
final Integer maxVariations = cliffVar.getValue();
|
||||
for (int variation = 0; variation <= maxVariations; variation++) {
|
||||
final String fileName = "Doodads\\Terrain\\Cliffs\\Cliffs" + cliffVar.getKey() + variation + ".mdx";
|
||||
this.cliffMeshes.add(new CliffMesh(fileName, dataSource, Gdx.gl30));
|
||||
this.pathToCliff.put(cliffVar.getKey() + variation, this.cliffMeshes.size() - 1);
|
||||
this.pathToCliff.put("Cliffs" + cliffVar.getKey() + variation, this.cliffMeshes.size() - 1);
|
||||
}
|
||||
}
|
||||
cliffVars = Variations.CITY_CLIFF_VARS;
|
||||
for (final Map.Entry<String, Integer> cliffVar : cliffVars.entrySet()) {
|
||||
final Integer maxVariations = cliffVar.getValue();
|
||||
for (int variation = 0; variation <= maxVariations; variation++) {
|
||||
final String fileName = "Doodads\\Terrain\\CityCliffs\\CityCliffs" + cliffVar.getKey() + variation
|
||||
+ ".mdx";
|
||||
this.cliffMeshes.add(new CliffMesh(fileName, dataSource, Gdx.gl30));
|
||||
this.pathToCliff.put("CityCliffs" + cliffVar.getKey() + variation, this.cliffMeshes.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +201,7 @@ public class Terrain {
|
||||
final Element terrainTileInfo = this.terrainTable.get(groundTile.asStringValue());
|
||||
final String dir = terrainTileInfo.getField("dir");
|
||||
final String file = terrainTileInfo.getField("file");
|
||||
this.groundTextures.add(new GroundTexture(dir + "/" + file + texturesExt, dataSource, Gdx.gl30));
|
||||
this.groundTextures.add(new GroundTexture(dir + "\\" + file + texturesExt, dataSource, Gdx.gl30));
|
||||
this.groundTextureToId.put(groundTile.asStringValue(), this.groundTextures.size() - 1);
|
||||
}
|
||||
|
||||
@ -182,10 +218,11 @@ public class Terrain {
|
||||
final Element cliffInfo = this.cliffTable.get(cliffTile.asStringValue());
|
||||
final String texDir = cliffInfo.getField("texDir");
|
||||
final String texFile = cliffInfo.getField("texFile");
|
||||
try (InputStream imageStream = dataSource.getResourceAsStream(texDir + "/" + texFile + texturesExt)) {
|
||||
try (InputStream imageStream = dataSource.getResourceAsStream(texDir + "\\" + texFile + texturesExt)) {
|
||||
final BufferedImage image = ImageIO.read(imageStream);
|
||||
this.cliffTextures.add(new UnloadedTexture(image.getWidth(), image.getHeight(),
|
||||
ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image))));
|
||||
ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image)),
|
||||
cliffInfo.getField("cliffModelDir"), cliffInfo.getField("rampModelDir")));
|
||||
}
|
||||
this.cliffTexturesSize = Math.max(this.cliffTexturesSize,
|
||||
this.cliffTextures.get(this.cliffTextures.size() - 1).width);
|
||||
@ -222,8 +259,8 @@ public class Terrain {
|
||||
|
||||
this.groundHeight = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundHeight);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_NEAREST);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_NEAREST);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);
|
||||
|
||||
gl.glTexImage2D(GL30.GL_TEXTURE_2D, 0, GL30.GL_R16F, width, height, 0, GL30.GL_RED, GL30.GL_FLOAT,
|
||||
RenderMathUtils.wrap(this.groundHeights));
|
||||
@ -244,13 +281,13 @@ public class Terrain {
|
||||
this.cliffTextureArray = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.cliffTextureArray);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RGBA8, this.cliffTexturesSize, this.cliffTexturesSize,
|
||||
this.cliffTextures.size(), 0, Extensions.GL_BGRA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
this.cliffTextures.size(), 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR_MIPMAP_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_BASE_LEVEL, 0);
|
||||
|
||||
int sub = 0;
|
||||
for (final UnloadedTexture i : this.cliffTextures) {
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, sub, i.width, i.height, 1, Extensions.GL_BGRA,
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, sub, i.width, i.height, 1, GL30.GL_RGBA,
|
||||
GL30.GL_UNSIGNED_BYTE, i.data);
|
||||
sub += 1;
|
||||
}
|
||||
@ -276,8 +313,8 @@ public class Terrain {
|
||||
// Water textures
|
||||
this.waterTextureArray = gl.glGenTexture();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_RGBA8, 128, 128, this.waterTextureCount, 0,
|
||||
Extensions.GL_BGRA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_SRGB8_ALPHA8, 128, 128, this.waterTextureCount, 0,
|
||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_BASE_LEVEL, 0);
|
||||
@ -294,8 +331,7 @@ public class Terrain {
|
||||
}
|
||||
|
||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, image.getWidth(), image.getHeight(), 1,
|
||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE,
|
||||
ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image)));
|
||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, ImageUtils.getTextureBuffer(image));
|
||||
}
|
||||
}
|
||||
gl.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);
|
||||
@ -306,9 +342,14 @@ public class Terrain {
|
||||
this.cliffShader = webGL.createShaderProgram(TerrainShaders.Cliffs.vert, TerrainShaders.Cliffs.frag);
|
||||
this.waterShader = webGL.createShaderProgram(TerrainShaders.Water.vert, TerrainShaders.Water.frag);
|
||||
|
||||
this.uberSplatShader = webGL.createShaderProgram(W3xShaders.UberSplat.vert, W3xShaders.UberSplat.frag);
|
||||
|
||||
// TODO collision bodies (?)
|
||||
|
||||
this.centerOffset = w3eFile.getCenterOffset();
|
||||
this.uberSplatModels = new ArrayList<>();
|
||||
this.mapBounds = w3iFile.getCameraBoundsComplements();
|
||||
this.mapSize = w3eFile.getMapSize();
|
||||
}
|
||||
|
||||
private void updateGroundHeights(final Rectangle area) {
|
||||
@ -403,6 +444,10 @@ public class Terrain {
|
||||
final boolean facingLeft = (bottomRight.getLayerHeight() >= bottomLeft.getLayerHeight())
|
||||
&& (topRight.getLayerHeight() >= topLeft.getLayerHeight());
|
||||
|
||||
int bottomLeftCliffTex = bottomLeft.getCliffTexture();
|
||||
if (bottomLeftCliffTex == 15) {
|
||||
bottomLeftCliffTex -= 14;
|
||||
}
|
||||
if (!(facingDown && (j == 0)) && !(!facingDown && (j >= (this.rows - 2)))
|
||||
&& !(facingLeft && (i == 0)) && !(!facingLeft && (i >= (this.columns - 2)))) {
|
||||
final boolean br = ((bottomLeft.getRamp() != 0) != (bottomRight.getRamp() != 0))
|
||||
@ -424,7 +469,8 @@ public class Terrain {
|
||||
+ ((bottomRight.getLayerHeight() - base)
|
||||
* (bottomRight.getRamp() != 0 ? -4 : 1)));
|
||||
|
||||
fileName = "Doodads\\Terrain\\CliffTrans\\CliffTrans" + fileName + "0.mdx";
|
||||
final String rampModelDir = this.cliffTextures.get(bottomLeftCliffTex).rampModelDir;
|
||||
fileName = "Doodads\\Terrain\\" + rampModelDir + "\\" + rampModelDir + fileName + "0.mdx";
|
||||
|
||||
if (this.dataSource.has(fileName)) {
|
||||
if (!this.pathToCliff.containsKey(fileName)) {
|
||||
@ -441,7 +487,7 @@ public class Terrain {
|
||||
}
|
||||
}
|
||||
|
||||
this.cliffs.add(new IVec3((i + ((bo ? 1 : 0) * (facingDown ? 0 : 1))),
|
||||
this.cliffs.add(new IVec3((i + ((bo ? 1 : 0) * (facingLeft ? 0 : 1))),
|
||||
(j - ((br ? 1 : 0) * (facingDown ? 1 : 0))), this.pathToCliff.get(fileName)));
|
||||
bottomLeft.romp = true;
|
||||
|
||||
@ -475,7 +521,10 @@ public class Terrain {
|
||||
}
|
||||
|
||||
// Clamp to within max variations
|
||||
fileName += Variations.getCliffVariation("Cliffs", fileName, bottomLeft.getCliffVariation());
|
||||
|
||||
fileName = this.cliffTextures.get(bottomLeftCliffTex).cliffModelDir + fileName
|
||||
+ Variations.getCliffVariation(this.cliffTextures.get(bottomLeftCliffTex).cliffModelDir,
|
||||
fileName, bottomLeft.getCliffVariation());
|
||||
if (!this.pathToCliff.containsKey(fileName)) {
|
||||
throw new IllegalArgumentException("No such pathToCliff entry: " + fileName);
|
||||
}
|
||||
@ -693,12 +742,51 @@ public class Terrain {
|
||||
|
||||
}
|
||||
|
||||
public void renderUberSplats() {
|
||||
final GL30 gl = Gdx.gl30;
|
||||
final WebGL webGL = this.webGL;
|
||||
final ShaderProgram shader = this.uberSplatShader;
|
||||
|
||||
gl.glDepthMask(false);
|
||||
gl.glEnable(GL30.GL_BLEND);
|
||||
gl.glBlendFunc(GL30.GL_SRC_ALPHA, GL30.GL_ONE_MINUS_SRC_ALPHA);
|
||||
gl.glBlendEquation(GL30.GL_FUNC_ADD);
|
||||
|
||||
webGL.useShaderProgram(this.uberSplatShader);
|
||||
|
||||
shader.setUniformMatrix("u_mvp", this.camera.viewProjectionMatrix);
|
||||
shader.setUniformi("u_heightMap", 0);
|
||||
sizeHeap[0] = this.columns - 1;
|
||||
sizeHeap[1] = this.rows - 1;
|
||||
shader.setUniform2fv("u_size", sizeHeap, 0, 2);
|
||||
sizeHeap[0] = 1 / (float) this.columns;
|
||||
sizeHeap[1] = 1 / (float) this.rows;
|
||||
shader.setUniform2fv("u_pixel", sizeHeap, 0, 2);
|
||||
shader.setUniform2fv("u_centerOffset", this.centerOffset, 0, 2);
|
||||
shader.setUniformi("u_texture", 1);
|
||||
shader.setUniformi("u_shadowMap", 2);
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeight);
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
|
||||
|
||||
// Render the cliffs
|
||||
for (final SplatModel splat : this.uberSplatModels) {
|
||||
splat.render(gl, shader);
|
||||
}
|
||||
}
|
||||
|
||||
public void renderWater() {
|
||||
// Render water
|
||||
this.webGL.useShaderProgram(this.waterShader);
|
||||
|
||||
final GL30 gl = Gdx.gl30;
|
||||
gl.glDepthMask(false);
|
||||
gl.glDisable(GL30.GL_CULL_FACE);
|
||||
gl.glEnable(GL30.GL_BLEND);
|
||||
gl.glBlendFunc(GL30.GL_SRC_ALPHA, GL30.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
gl.glUniformMatrix4fv(0, 1, false, this.camera.viewProjectionMatrix.val, 0);
|
||||
gl.glUniform4fv(1, 1, this.minShallowColor, 0);
|
||||
@ -748,13 +836,12 @@ public class Terrain {
|
||||
this.cliffMeshes.get(i.z).renderQueue(fourComponentHeap);
|
||||
}
|
||||
|
||||
this.cliffShader.begin();
|
||||
this.webGL.useShaderProgram(this.cliffShader);
|
||||
|
||||
final GL30 gl = Gdx.gl30;
|
||||
|
||||
// WC3 models are 128x too large
|
||||
tempMatrix.set(this.camera.viewProjectionMatrix);
|
||||
tempMatrix.scale(1 / 128f, 1 / 128f, 1 / 128f);
|
||||
gl.glUniformMatrix4fv(0, 1, false, tempMatrix.val, 0);
|
||||
gl.glUniform1i(1, this.viewer.renderPathing);
|
||||
gl.glUniform1i(2, this.viewer.renderLighting);
|
||||
@ -765,10 +852,102 @@ public class Terrain {
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundHeight);
|
||||
// gl.glActiveTexture(GL30.GL_TEXTURE2);
|
||||
for (final CliffMesh i : this.cliffMeshes) {
|
||||
gl.glUniform1f(this.cliffShader.getUniformLocation("centerOffsetX"), this.centerOffset[0]);
|
||||
gl.glUniform1f(this.cliffShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
|
||||
i.render();
|
||||
}
|
||||
}
|
||||
|
||||
public void addShadow(final String file, final float x, final float y) {
|
||||
if (!this.shadows.containsKey(file)) {
|
||||
final String path = "ReplaceableTextures\\Shadows\\" + file + ".blp";
|
||||
this.shadows.put(file, new ArrayList<>());
|
||||
this.shadowTextures.put(file, (Texture) this.viewer.load(path, PathSolver.DEFAULT, null));
|
||||
}
|
||||
this.shadows.get(file).add(new float[] { x, y });
|
||||
}
|
||||
|
||||
public void initShadows() throws IOException {
|
||||
final GL30 gl = Gdx.gl30;
|
||||
final float[] centerOffset = this.centerOffset;
|
||||
final int columns = (this.columns - 1) * 4;
|
||||
final int rows = (this.rows - 1) * 4;
|
||||
|
||||
final int shadowSize = columns * rows;
|
||||
final byte[] shadowData = new byte[columns * rows];
|
||||
if (this.viewer.mapMpq.has("war3map.shd")) {
|
||||
final InputStream shadowSource = this.viewer.mapMpq.getResourceAsStream("war3map.shd");
|
||||
final byte[] buffer = IOUtils.toByteArray(shadowSource);
|
||||
for (int i = 0; i < shadowSize; i++) {
|
||||
shadowData[i] = (byte) (buffer[i] / 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (final Map.Entry<String, Texture> fileAndTexture : this.shadowTextures.entrySet()) {
|
||||
final String file = fileAndTexture.getKey();
|
||||
final Texture texture = fileAndTexture.getValue();
|
||||
|
||||
final int width = texture.getWidth();
|
||||
final int height = texture.getHeight();
|
||||
final int ox = (int) Math.round(width * 0.3);
|
||||
final int oy = (int) Math.round(height * 0.7);
|
||||
for (final float[] location : this.shadows.get(file)) {
|
||||
final int x0 = (int) Math.floor((location[0] - centerOffset[0]) / 32.0) - ox;
|
||||
final int y0 = (int) Math.floor((location[1] - centerOffset[1]) / 32.0) + oy;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
if (((y0 - y) < 0) || ((y0 - y) >= rows)) {
|
||||
continue;
|
||||
}
|
||||
for (int x = 0; x < width; ++x) {
|
||||
if (((x0 + x) < 0) || ((x0 + x) >= columns)) {
|
||||
continue;
|
||||
}
|
||||
if (((RawOpenGLTextureResource) texture).getData().get((((y * width) + x) * 4) + 3) != 0) {
|
||||
shadowData[((y0 - y) * columns) + x0 + x] = (byte) 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final byte outsideArea = (byte) 204;
|
||||
final int x0 = this.mapBounds[0] * 4, x1 = (this.mapSize[0] - this.mapBounds[1] - 1) * 4,
|
||||
y0 = this.mapBounds[2] * 4, y1 = (this.mapSize[1] - this.mapBounds[3] - 1) * 4;
|
||||
for (int y = y0; y < y1; ++y) {
|
||||
for (int x = x0; x < x1; ++x) {
|
||||
final RenderCorner c = this.corners[x >> 2][y >> 2];
|
||||
if (c.getBoundary() != 0) {
|
||||
shadowData[(y * columns) + x] = outsideArea;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < rows; ++y) {
|
||||
for (int x = 0; x < x0; ++x) {
|
||||
shadowData[(y * columns) + x] = outsideArea;
|
||||
}
|
||||
for (int x = x1; x < columns; ++x) {
|
||||
shadowData[(y * columns) + x] = outsideArea;
|
||||
}
|
||||
}
|
||||
for (int x = x0; x < x1; ++x) {
|
||||
for (int y = 0; y < y0; ++y) {
|
||||
shadowData[(y * columns) + x] = outsideArea;
|
||||
}
|
||||
for (int y = y1; y < rows; ++y) {
|
||||
shadowData[(y * columns) + x] = outsideArea;
|
||||
}
|
||||
}
|
||||
|
||||
this.shadowMap = gl.glGenBuffer();
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexImage2D(GL30.GL_TEXTURE_2D, 0, GL30.GL_R8, columns, rows, 0, GL30.GL_RED, GL30.GL_UNSIGNED_BYTE,
|
||||
RenderMathUtils.wrap(shadowData));
|
||||
}
|
||||
|
||||
// public Vector3 groundNormal(final Vector3 out, int x, int y) {
|
||||
// final float[] centerOffset = this.centerOffset;
|
||||
// final int[] mapSize = this.mapSize;
|
||||
@ -813,12 +992,62 @@ public class Terrain {
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final Buffer data;
|
||||
private final String cliffModelDir;
|
||||
private final String rampModelDir;
|
||||
|
||||
public UnloadedTexture(final int width, final int height, final Buffer data) {
|
||||
public UnloadedTexture(final int width, final int height, final Buffer data, final String cliffModelDir,
|
||||
final String rampModelDir) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.data = data;
|
||||
this.cliffModelDir = cliffModelDir;
|
||||
this.rampModelDir = rampModelDir;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public float getGroundHeight(final float x, final float y) {
|
||||
final float userCellSpaceX = (x - this.centerOffset[0]) / 128.0f;
|
||||
final float userCellSpaceY = (y - this.centerOffset[1]) / 128.0f;
|
||||
final int cellX = (int) userCellSpaceX;
|
||||
final int cellY = (int) userCellSpaceY;
|
||||
|
||||
if ((cellX >= 0) && (cellX < (this.mapSize[0] - 1)) && (cellY >= 0) && (cellY < (this.mapSize[1] - 1))) {
|
||||
final float bottomLeft = this.corners[cellX][cellY].computeFinalGroundHeight();
|
||||
final float bottomRight = this.corners[cellX][cellY].computeFinalGroundHeight();
|
||||
final float topLeft = this.corners[cellX][cellY].computeFinalGroundHeight();
|
||||
final float topRight = this.corners[cellX][cellY].computeFinalGroundHeight();
|
||||
final float sqX = userCellSpaceX - cellX;
|
||||
final float sqY = userCellSpaceY - cellY;
|
||||
float height;
|
||||
|
||||
if ((sqX + sqY) < 1) {
|
||||
height = bottomLeft + ((bottomRight - bottomLeft) * sqX) + ((topLeft - bottomLeft) * sqY);
|
||||
}
|
||||
else {
|
||||
height = topRight + ((bottomRight - topRight) * (1 - sqY)) + ((topLeft - topRight) * (1 - sqX));
|
||||
}
|
||||
|
||||
return height * 128.0f;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final class Splat {
|
||||
public List<float[]> locations = new ArrayList<>();
|
||||
public float opacity = 1;
|
||||
}
|
||||
|
||||
public void loadSplats() throws IOException {
|
||||
for (final Map.Entry<String, Splat> entry : this.splats.entrySet()) {
|
||||
final String path = entry.getKey();
|
||||
final Splat splat = entry.getValue();
|
||||
|
||||
final SplatModel splatModel = new SplatModel(Gdx.gl30,
|
||||
(Texture) this.viewer.load(path, PathSolver.DEFAULT, null), splat.locations, this.centerOffset);
|
||||
splatModel.color[3] = splat.opacity;
|
||||
this.uberSplatModels.add(splatModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||
|
||||
/**
|
||||
* Mostly copied from HiveWE!
|
||||
*/
|
||||
public class TerrainShaders {
|
||||
public static final class Cliffs {
|
||||
private Cliffs() {
|
||||
@ -9,12 +12,14 @@ public class TerrainShaders {
|
||||
"\r\n" + //
|
||||
"layout (location = 0) in vec3 vPosition;\r\n" + //
|
||||
"layout (location = 1) in vec2 vUV;\r\n" + //
|
||||
"layout (location = 2) in vec3 vNormal;\r\n" + //
|
||||
"layout (location = 3) in vec4 vOffset;\r\n" + //
|
||||
// "layout (location = 2) in vec3 vNormal;\r\n" + //
|
||||
"layout (location = 2) in vec4 vOffset;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) uniform mat4 MVP;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 1) uniform sampler2D height_texture;\r\n" + //
|
||||
"layout (location = 3) uniform float centerOffsetX;\r\n" + //
|
||||
"layout (location = 4) uniform float centerOffsetY;\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 0) out vec3 UV;\r\n" + //
|
||||
"layout (location = 1) out vec3 Normal;\r\n" + //
|
||||
@ -24,11 +29,14 @@ public class TerrainShaders {
|
||||
" pathing_map_uv = (vec2(vPosition.x + 128, vPosition.y) / 128 + vOffset.xy) * 4;\r\n" + //
|
||||
" \r\n" + //
|
||||
" ivec2 size = textureSize(height_texture, 0);\r\n" + //
|
||||
" float value = texture(height_texture, (vOffset.xy + vec2(vPosition.x + 192, vPosition.y + 64) / 128) / vec2(size)).r;\r\n"
|
||||
" float value = texture(height_texture, (vOffset.xy + vec2(vPosition.x + 192, vPosition.y + 64) / 128.0) / vec2(size)).r;\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" gl_Position = MVP * vec4(vPosition + vec3(vOffset.xy + vec2(1, 0), vOffset.z + value) * 128, 1);\r\n"
|
||||
" vec4 myposition = vec4((vPosition + vec3(vOffset.xy + vec2(1, 0), vOffset.z + value) * 128 ), 1);\r\n"
|
||||
+ //
|
||||
" myposition.x += centerOffsetX;\r\n" + //
|
||||
" myposition.y += centerOffsetY;\r\n" + //
|
||||
" gl_Position = MVP * myposition;\r\n" + //
|
||||
" UV = vec3(vUV, vOffset.a);\r\n" + //
|
||||
"\r\n" + //
|
||||
" ivec2 height_pos = ivec2(vOffset.xy + vec2(vPosition.x + 128, vPosition.y) / 128);\r\n" + //
|
||||
@ -273,7 +281,7 @@ public class TerrainShaders {
|
||||
" gl_Position = is_water ? MVP * vec4((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, water_height*128.0, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" UV = vec2(vPosition.x, vPosition.y);\r\n" + //
|
||||
" UV = vec2((vPosition.x + pos.x%2)/2.0, (vPosition.y + pos.y%2)/2.0);\r\n" + //
|
||||
"\r\n" + //
|
||||
" float ground_height = texelFetch(ground_height_texture, height_pos, 0).r;\r\n" + //
|
||||
" float value = clamp(water_height - ground_height, 0.f, 1.f);\r\n" + //
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.etheller.warsmash.desktop;
|
||||
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
@ -30,7 +29,6 @@ public class DesktopLauncher {
|
||||
GL31.glDrawArraysInstanced(mode, first, count, instanceCount);
|
||||
}
|
||||
};
|
||||
Extensions.GL_BGRA = GL12.GL_BGRA;
|
||||
final LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
config.useGL30 = true;
|
||||
config.gles30ContextMajorVersion = 3;
|
||||
|
Loading…
Reference in New Issue
Block a user