mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
More work towards prototype
This commit is contained in:
parent
d39976fa18
commit
015b04c371
@ -8,28 +8,38 @@ import java.util.Arrays;
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSource;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.Camera;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SolvedPath;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxSimpleInstance;
|
||||
|
||||
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
||||
private static final boolean SPIN = false;
|
||||
private static final boolean ADVANCE_ANIMS = true;
|
||||
private DataSource codebase;
|
||||
private ModelViewer viewer;
|
||||
private MdxModel model;
|
||||
private CameraManager cameraManager;
|
||||
private static int VAO;
|
||||
private final Rectangle tempRect = new Rectangle();
|
||||
|
||||
private BitmapFont font;
|
||||
private SpriteBatch batch;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
@ -46,58 +56,320 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
final String renderer = Gdx.gl.glGetString(GL20.GL_RENDERER);
|
||||
System.err.println("Renderer: " + renderer);
|
||||
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\War3.mpq\\war3.mpq");
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127");
|
||||
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
|
||||
this.codebase = new CompoundDataSource(Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder));
|
||||
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
|
||||
this.codebase = new CompoundDataSourceDescriptor(
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
||||
this.viewer = new ModelViewer(this.codebase, this);
|
||||
|
||||
this.viewer.addHandler(new MdxHandler());
|
||||
this.viewer.enableAudio();
|
||||
|
||||
final Scene scene = this.viewer.addScene();
|
||||
scene.enableAudio();
|
||||
|
||||
this.cameraManager = new CameraManager();
|
||||
this.cameraManager.setupCamera(scene);
|
||||
|
||||
this.model = (MdxModel) this.viewer.load("units\\human\\footman\\footman.mdx", new PathSolver() {
|
||||
// this.model = (MdxModel) this.viewer.load("Cube.mdx", new PathSolver() {
|
||||
this.mainModel = (MdxModel) this.viewer.load("Buildings\\Undead\\Necropolis\\Necropolis.mdx", new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
|
||||
final MdxSimpleInstance instance = (MdxSimpleInstance) this.model.addInstance(1);
|
||||
this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0);
|
||||
|
||||
instance.setScene(scene);
|
||||
this.mainInstance.setScene(scene);
|
||||
|
||||
// instance.setSequence(1);
|
||||
//
|
||||
// instance.setSequenceLoopMode(2);
|
||||
int animIndex = 0;
|
||||
for (final Sequence s : this.mainModel.getSequences()) {
|
||||
if (s.getName().toLowerCase().startsWith("walk")) {
|
||||
animIndex = this.mainModel.getSequences().indexOf(s);
|
||||
}
|
||||
}
|
||||
this.mainInstance.setSequence(animIndex);
|
||||
|
||||
this.mainInstance.setSequenceLoopMode(0);
|
||||
|
||||
// acolytesHarvestingSceneJoke2(scene);
|
||||
|
||||
System.out.println("Loaded");
|
||||
// Gdx.gl30.glClearColor(0.5f, 0.5f, 0.5f, 1); // TODO remove white background
|
||||
Gdx.gl30.glClearColor(0.5f, 0.5f, 0.5f, 1); // TODO remove white background
|
||||
|
||||
this.font = new BitmapFont();
|
||||
this.batch = new SpriteBatch();
|
||||
}
|
||||
|
||||
private void makeDruidSquare(final Scene scene) {
|
||||
final MdxModel model2 = (MdxModel) this.viewer.load("units\\nightelf\\druidoftheclaw\\druidoftheclaw.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
makePerfectSquare(scene, model2, 15);
|
||||
}
|
||||
|
||||
private void singleAcolyteScene(final Scene scene) {
|
||||
final MdxModel model2 = (MdxModel) this.viewer.load("units\\undead\\acolyte\\acolyte.mdx", new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
|
||||
final MdxComplexInstance instance3 = (MdxComplexInstance) model2.addInstance(0);
|
||||
|
||||
instance3.setScene(scene);
|
||||
|
||||
int animIndex = 0;
|
||||
for (final Sequence s : model2.getSequences()) {
|
||||
if (s.getName().toLowerCase().startsWith("stand work")) {
|
||||
animIndex = model2.getSequences().indexOf(s);
|
||||
}
|
||||
}
|
||||
instance3.setSequence(animIndex);
|
||||
|
||||
instance3.setSequenceLoopMode(2);
|
||||
}
|
||||
|
||||
private void singleModelScene(final Scene scene, final String path, final String animName) {
|
||||
final MdxModel model2 = (MdxModel) this.viewer.load(path, new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
|
||||
final MdxComplexInstance instance3 = (MdxComplexInstance) model2.addInstance(0);
|
||||
|
||||
instance3.setScene(scene);
|
||||
|
||||
int animIndex = 0;
|
||||
for (final Sequence s : model2.getSequences()) {
|
||||
if (s.getName().toLowerCase().startsWith(animName)) {
|
||||
animIndex = model2.getSequences().indexOf(s);
|
||||
}
|
||||
}
|
||||
instance3.setSequence(animIndex);
|
||||
|
||||
instance3.setSequenceLoopMode(2);
|
||||
}
|
||||
|
||||
private void acolytesHarvestingScene(final Scene scene) {
|
||||
|
||||
final MdxModel acolyteModel = (MdxModel) this.viewer.load("units\\undead\\acolyte\\acolyte.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
final MdxModel mineEffectModel = (MdxModel) this.viewer
|
||||
.load("abilities\\spells\\undead\\undeadmine\\undeadminecircle.mdx", new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final MdxComplexInstance acolyteInstance = (MdxComplexInstance) acolyteModel.addInstance(0);
|
||||
|
||||
acolyteInstance.setScene(scene);
|
||||
|
||||
int animIndex = i % acolyteModel.getSequences().size();
|
||||
for (final Sequence s : acolyteModel.getSequences()) {
|
||||
if (s.getName().toLowerCase().startsWith("stand work")) {
|
||||
animIndex = acolyteModel.getSequences().indexOf(s);
|
||||
}
|
||||
}
|
||||
acolyteInstance.setSequence(animIndex);
|
||||
|
||||
acolyteInstance.setSequenceLoopMode(2);
|
||||
|
||||
final double angle = ((Math.PI * 2) / 5) * i;
|
||||
acolyteInstance.localLocation.x = (float) Math.cos(angle) * 256;
|
||||
acolyteInstance.localLocation.y = (float) Math.sin(angle) * 256;
|
||||
acolyteInstance.localRotation.setFromAxisRad(0, 0, 1, (float) (angle + Math.PI));
|
||||
|
||||
final MdxComplexInstance effectInstance = (MdxComplexInstance) mineEffectModel.addInstance(0);
|
||||
|
||||
effectInstance.setScene(scene);
|
||||
|
||||
effectInstance.setSequence(1);
|
||||
|
||||
effectInstance.setSequenceLoopMode(2);
|
||||
effectInstance.localLocation.x = (float) Math.cos(angle) * 256;
|
||||
effectInstance.localLocation.y = (float) Math.sin(angle) * 256;
|
||||
effectInstance.localRotation.setFromAxisRad(0, 0, 1, (float) (angle));
|
||||
|
||||
}
|
||||
final MdxModel mineModel = (MdxModel) this.viewer.load("buildings\\undead\\hauntedmine\\hauntedmine.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
final MdxComplexInstance mineInstance = (MdxComplexInstance) mineModel.addInstance(0);
|
||||
|
||||
mineInstance.setScene(scene);
|
||||
|
||||
mineInstance.setSequence(2);
|
||||
|
||||
mineInstance.setSequenceLoopMode(2);
|
||||
}
|
||||
|
||||
private void acolytesHarvestingSceneJoke2(final Scene scene) {
|
||||
|
||||
final MdxModel acolyteModel = (MdxModel) this.viewer.load("units\\undead\\acolyte\\acolyte.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
final MdxModel mineEffectModel = (MdxModel) this.viewer
|
||||
.load("abilities\\spells\\undead\\undeadmine\\undeadminecircle.mdx", new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final MdxComplexInstance acolyteInstance = (MdxComplexInstance) acolyteModel.addInstance(0);
|
||||
|
||||
acolyteInstance.setScene(scene);
|
||||
|
||||
int animIndex = i % acolyteModel.getSequences().size();
|
||||
for (final Sequence s : acolyteModel.getSequences()) {
|
||||
if (s.getName().toLowerCase().startsWith("stand work")) {
|
||||
animIndex = acolyteModel.getSequences().indexOf(s);
|
||||
}
|
||||
}
|
||||
acolyteInstance.setSequence(animIndex);
|
||||
|
||||
acolyteInstance.setSequenceLoopMode(2);
|
||||
|
||||
final double angle = ((Math.PI * 2) / 5) * i;
|
||||
acolyteInstance.localLocation.x = (float) Math.cos(angle) * 256;
|
||||
acolyteInstance.localLocation.y = (float) Math.sin(angle) * 256;
|
||||
acolyteInstance.localRotation.setFromAxisRad(0, 0, 1, (float) (angle + Math.PI));
|
||||
|
||||
final MdxComplexInstance effectInstance = (MdxComplexInstance) mineEffectModel.addInstance(0);
|
||||
|
||||
effectInstance.setScene(scene);
|
||||
|
||||
effectInstance.setSequence(1);
|
||||
|
||||
effectInstance.setSequenceLoopMode(2);
|
||||
effectInstance.localLocation.x = (float) Math.cos(angle) * 256;
|
||||
effectInstance.localLocation.y = (float) Math.sin(angle) * 256;
|
||||
effectInstance.localRotation.setFromAxisRad(0, 0, 1, (float) (angle));
|
||||
|
||||
}
|
||||
final MdxModel mineModel = (MdxModel) this.viewer.load("units\\orc\\spiritwolf\\spiritwolf.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
final MdxComplexInstance mineInstance = (MdxComplexInstance) mineModel.addInstance(0);
|
||||
|
||||
mineInstance.setScene(scene);
|
||||
|
||||
mineInstance.setSequence(0);
|
||||
mineInstance.localScale.x = 2;
|
||||
mineInstance.localScale.y = 2;
|
||||
mineInstance.localScale.z = 2;
|
||||
|
||||
mineInstance.setSequenceLoopMode(2);
|
||||
final MdxModel mineModel2 = (MdxModel) this.viewer
|
||||
.load("abilities\\spells\\undead\\unsummon\\unsummontarget.mdx", new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
}, null);
|
||||
final MdxComplexInstance mineInstance2 = (MdxComplexInstance) mineModel2.addInstance(0);
|
||||
|
||||
mineInstance2.setScene(scene);
|
||||
|
||||
mineInstance2.setSequence(0);
|
||||
|
||||
mineInstance2.setSequenceLoopMode(2);
|
||||
}
|
||||
|
||||
private void makeFourHundred(final Scene scene, final MdxModel model2) {
|
||||
for (int i = 0; i < 400; i++) {
|
||||
final MdxComplexInstance instance3 = (MdxComplexInstance) model2.addInstance(0);
|
||||
instance3.localLocation.x = (((i % 20) - 10) * 128);
|
||||
instance3.localLocation.y = (((i / 20) - 10) * 128);
|
||||
|
||||
instance3.setScene(scene);
|
||||
|
||||
final int animIndex = i % model2.getSequences().size();
|
||||
instance3.setSequence(animIndex);
|
||||
|
||||
instance3.setSequenceLoopMode(2);
|
||||
}
|
||||
}
|
||||
|
||||
private void makePerfectSquare(final Scene scene, final MdxModel model2, final int n) {
|
||||
final int n2 = n * n;
|
||||
for (int i = 0; i < n2; i++) {
|
||||
final MdxComplexInstance instance3 = (MdxComplexInstance) model2.addInstance(0);
|
||||
instance3.localLocation.x = (((i % n) - (n / 2)) * 128);
|
||||
instance3.localLocation.y = (((i / n) - (n / 2)) * 128);
|
||||
|
||||
instance3.setScene(scene);
|
||||
|
||||
final int animIndex = i % model2.getSequences().size();
|
||||
instance3.setSequence(animIndex);
|
||||
|
||||
instance3.setSequenceLoopMode(2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindDefaultVertexArray() {
|
||||
Gdx.gl30.glBindVertexArray(VAO);
|
||||
}
|
||||
|
||||
private int frame = 0;
|
||||
private MdxComplexInstance mainInstance;
|
||||
private MdxModel mainModel;
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
// this.cameraManager.verticalAngle += 0.01;
|
||||
// if (this.cameraManager.verticalAngle >= (Math.PI)) {
|
||||
// this.cameraManager.verticalAngle = 0;
|
||||
// }
|
||||
this.cameraManager.horizontalAngle += 0.01;
|
||||
if (this.cameraManager.horizontalAngle > (2 * Math.PI)) {
|
||||
this.cameraManager.horizontalAngle = 0;
|
||||
Gdx.gl30.glBindVertexArray(VAO);
|
||||
if (SPIN) {
|
||||
this.cameraManager.horizontalAngle += 0.01;
|
||||
if (this.cameraManager.horizontalAngle > (2 * Math.PI)) {
|
||||
this.cameraManager.horizontalAngle = 0;
|
||||
}
|
||||
}
|
||||
this.cameraManager.updateCamera();
|
||||
this.viewer.updateAndRender();
|
||||
|
||||
// gl.glDrawElements(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT, this.faceOffset);
|
||||
|
||||
// this.batch.begin();
|
||||
// this.font.draw(this.batch, Integer.toString(Gdx.graphics.getFramesPerSecond()), 0, 0);
|
||||
// this.batch.end();
|
||||
|
||||
this.frame++;
|
||||
if ((this.frame % 1000) == 0) {
|
||||
System.out.println(Integer.toString(Gdx.graphics.getFramesPerSecond()));
|
||||
}
|
||||
|
||||
if (ADVANCE_ANIMS && this.mainInstance.sequenceEnded) {
|
||||
this.mainInstance.setSequence((this.mainInstance.sequence + 1) % this.mainModel.getSequences().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,6 +386,13 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
return Gdx.graphics.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
this.tempRect.width = width;
|
||||
this.tempRect.height = height;
|
||||
this.cameraManager.camera.viewport(this.tempRect);
|
||||
}
|
||||
|
||||
class CameraManager {
|
||||
private CanvasProvider canvas;
|
||||
private Camera camera;
|
||||
@ -143,9 +422,9 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
this.zoomFactor = 0.1f;
|
||||
this.horizontalAngle = (float) (Math.PI / 2);
|
||||
this.verticalAngle = (float) (Math.PI / 4);
|
||||
this.distance = 5;
|
||||
this.distance = 1000;
|
||||
this.position = new Vector3();
|
||||
this.target = new Vector3();
|
||||
this.target = new Vector3(0, 0, 50);
|
||||
this.worldUp = new Vector3(0, 0, 1);
|
||||
this.vecHeap = new Vector3();
|
||||
this.quatHeap = new Quaternion();
|
||||
|
163
core/src/com/etheller/warsmash/WarsmashTestGameAttributes.java
Normal file
163
core/src/com/etheller/warsmash/WarsmashTestGameAttributes.java
Normal file
@ -0,0 +1,163 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
|
||||
public class WarsmashTestGameAttributes extends ApplicationAdapter {
|
||||
private int arrayBuffer;
|
||||
private int elementBuffer;
|
||||
private int VAO;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // colour to use when clearing
|
||||
|
||||
final ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(4);
|
||||
tempByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final IntBuffer temp = tempByteBuffer.asIntBuffer();
|
||||
|
||||
Gdx.gl30.glGenVertexArrays(1, temp);
|
||||
this.VAO = temp.get(0);
|
||||
|
||||
Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
|
||||
this.shaderProgram = new ShaderProgram(vsSimple, fsSimple);
|
||||
if (!this.shaderProgram.isCompiled()) {
|
||||
throw new IllegalStateException(this.shaderProgram.getLog());
|
||||
}
|
||||
|
||||
this.arrayBuffer = Gdx.gl.glGenBuffer();
|
||||
this.elementBuffer = Gdx.gl.glGenBuffer();
|
||||
System.out.println("arrayBuffer: " + this.arrayBuffer + ", elementBuffer: " + this.elementBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
|
||||
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.vertexBuffer = vertexByteBuffer.asFloatBuffer();
|
||||
|
||||
this.vertexBuffer.put(0, -1f);
|
||||
this.vertexBuffer.put(1, -1f);
|
||||
this.vertexBuffer.put(2, 0);
|
||||
this.vertexBuffer.put(3, 1f);
|
||||
this.vertexBuffer.put(4, -1f);
|
||||
this.vertexBuffer.put(5, 0);
|
||||
this.vertexBuffer.put(6, 0f);
|
||||
this.vertexBuffer.put(7, 1f);
|
||||
this.vertexBuffer.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ARRAY_BUFFER, ((9 * 4) * 2) + 3, null, GL20.GL_STATIC_DRAW);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, 9 * 4, this.vertexBuffer);
|
||||
|
||||
final ByteBuffer vertex2ByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertex2ByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final FloatBuffer vertexBuffer2 = vertex2ByteBuffer.asFloatBuffer();
|
||||
|
||||
vertexBuffer2.put(0, -1f);
|
||||
vertexBuffer2.put(1, -1f);
|
||||
vertexBuffer2.put(2, 0);
|
||||
vertexBuffer2.put(3, 1f);
|
||||
vertexBuffer2.put(4, -1f);
|
||||
vertexBuffer2.put(5, 0);
|
||||
vertexBuffer2.put(6, 0f);
|
||||
vertexBuffer2.put(7, 1f);
|
||||
vertexBuffer2.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4, 9 * 4, vertexBuffer2);
|
||||
|
||||
final ByteBuffer skinByteBuffer = ByteBuffer.allocateDirect(3);
|
||||
skinByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
skinByteBuffer.put((byte) 34);
|
||||
skinByteBuffer.put((byte) 35);
|
||||
skinByteBuffer.put((byte) 36);
|
||||
skinByteBuffer.clear();
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4 * 2, 3, skinByteBuffer);
|
||||
|
||||
// this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position");
|
||||
// this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
// this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
// this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
|
||||
final ByteBuffer faceByteBuffer = ByteBuffer.allocateDirect(6);
|
||||
faceByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.faceBuffer = faceByteBuffer.asShortBuffer();
|
||||
|
||||
this.faceBuffer.put(0, (short) 0);
|
||||
this.faceBuffer.put(1, (short) 1);
|
||||
this.faceBuffer.put(2, (short) 2);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 3 * 2, null, GL20.GL_STATIC_DRAW);
|
||||
|
||||
final int glGetError = Gdx.gl.glGetError();
|
||||
System.out.println(glGetError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
this.shaderProgram.begin();
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.shaderProgram.enableVertexAttribute("a_position");
|
||||
this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, 3 * 2, this.faceBuffer);
|
||||
Gdx.gl.glDrawElements(GL20.GL_TRIANGLES, 3, GL20.GL_UNSIGNED_SHORT, 0);
|
||||
// Gdx.gl.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);
|
||||
this.shaderProgram.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
}
|
||||
|
||||
public static final String vsSimple = "\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec3 a_position2;\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" gl_Position = vec4(a_position2.x, a_position2.y, a_position2.z, 1.0);\r\n" + //
|
||||
" fragNumber = a_boneNumber;\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String fsSimple = "\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if( fragNumber > 35.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 34.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 33.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" gl_FragColor = vec4(fragNumber*100.0, fragNumber, fragNumber, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n";
|
||||
private ShaderProgram shaderProgram;
|
||||
private FloatBuffer vertexBuffer;
|
||||
private ShortBuffer faceBuffer;
|
||||
|
||||
}
|
214
core/src/com/etheller/warsmash/WarsmashTestGameAttributes2.java
Normal file
214
core/src/com/etheller/warsmash/WarsmashTestGameAttributes2.java
Normal file
@ -0,0 +1,214 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
|
||||
public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
||||
private int arrayBuffer;
|
||||
private int elementBuffer;
|
||||
private int VAO;
|
||||
private DataSource codebase;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\War3.mpq\\war3.mpq");
|
||||
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
|
||||
this.codebase = new CompoundDataSourceDescriptor(Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder))
|
||||
.createDataSource();
|
||||
|
||||
final MdlxModel model;
|
||||
try (InputStream modelStream = this.codebase.getResourceAsStream("Buildings\\Other\\TempArtB\\TempArtB.mdx")) {
|
||||
model = new MdlxModel(modelStream);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // colour to use when clearing
|
||||
|
||||
final ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(4);
|
||||
tempByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final IntBuffer temp = tempByteBuffer.asIntBuffer();
|
||||
|
||||
Gdx.gl30.glGenVertexArrays(1, temp);
|
||||
this.VAO = temp.get(0);
|
||||
|
||||
Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
|
||||
this.shaderProgram = new ShaderProgram(vsSimple, fsSimple);
|
||||
if (!this.shaderProgram.isCompiled()) {
|
||||
throw new IllegalStateException(this.shaderProgram.getLog());
|
||||
}
|
||||
|
||||
this.arrayBuffer = Gdx.gl.glGenBuffer();
|
||||
this.elementBuffer = Gdx.gl.glGenBuffer();
|
||||
System.out.println("arrayBuffer: " + this.arrayBuffer + ", elementBuffer: " + this.elementBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
|
||||
final Geoset geoset0 = model.getGeosets().get(0);
|
||||
final float[] vertices = geoset0.getVertices();
|
||||
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.vertexBuffer = vertexByteBuffer.asFloatBuffer();
|
||||
|
||||
this.vertexBuffer.put(0, -1f);
|
||||
this.vertexBuffer.put(1, -1f);
|
||||
this.vertexBuffer.put(2, 0);
|
||||
this.vertexBuffer.put(3, 1f);
|
||||
this.vertexBuffer.put(4, -1f);
|
||||
this.vertexBuffer.put(5, 0);
|
||||
this.vertexBuffer.put(6, 0f);
|
||||
this.vertexBuffer.put(7, 1f);
|
||||
this.vertexBuffer.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ARRAY_BUFFER, ((9 * 4) * 2) + 3, null, GL20.GL_STATIC_DRAW);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, 9 * 4, this.vertexBuffer);
|
||||
|
||||
final ByteBuffer vertex2ByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertex2ByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final FloatBuffer vertexBuffer2 = vertex2ByteBuffer.asFloatBuffer();
|
||||
|
||||
vertexBuffer2.put(0, -1f);
|
||||
vertexBuffer2.put(1, -1f);
|
||||
vertexBuffer2.put(2, 0);
|
||||
vertexBuffer2.put(3, 1f);
|
||||
vertexBuffer2.put(4, -1f);
|
||||
vertexBuffer2.put(5, 0);
|
||||
vertexBuffer2.put(6, 0f);
|
||||
vertexBuffer2.put(7, 1f);
|
||||
vertexBuffer2.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4, 9 * 4, vertexBuffer2);
|
||||
|
||||
final ByteBuffer skinByteBuffer = ByteBuffer.allocateDirect(3);
|
||||
skinByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
skinByteBuffer.put((byte) 34);
|
||||
skinByteBuffer.put((byte) 35);
|
||||
skinByteBuffer.put((byte) 36);
|
||||
skinByteBuffer.clear();
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4 * 2, 3, skinByteBuffer);
|
||||
|
||||
// this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position");
|
||||
// this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
// this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
// this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
|
||||
final ByteBuffer faceByteBuffer = ByteBuffer.allocateDirect(6);
|
||||
faceByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.faceBuffer = faceByteBuffer.asShortBuffer();
|
||||
|
||||
this.faceBuffer.put(0, (short) 0);
|
||||
this.faceBuffer.put(1, (short) 1);
|
||||
this.faceBuffer.put(2, (short) 2);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 3 * 2, null, GL20.GL_STATIC_DRAW);
|
||||
|
||||
final int glGetError = Gdx.gl.glGetError();
|
||||
System.out.println(glGetError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
this.shaderProgram.begin();
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.shaderProgram.enableVertexAttribute("a_position");
|
||||
this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, 3 * 2, this.faceBuffer);
|
||||
Gdx.gl.glDrawElements(GL20.GL_TRIANGLES, 3, GL20.GL_UNSIGNED_SHORT, 0);
|
||||
// Gdx.gl.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);
|
||||
this.shaderProgram.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
}
|
||||
|
||||
public static final String vsSimple = "\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec3 a_position2;\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" gl_Position = vec4(a_position2.x, a_position2.y, a_position2.z, 1.0);\r\n" + //
|
||||
" fragNumber = a_boneNumber;\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String fsSimple = "\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if( fragNumber > 35.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 34.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 33.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" gl_FragColor = vec4(fragNumber*100.0, fragNumber, fragNumber, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n";
|
||||
private ShaderProgram shaderProgram;
|
||||
private FloatBuffer vertexBuffer;
|
||||
private ShortBuffer faceBuffer;
|
||||
|
||||
private static ShortBuffer wrapFaces(final int[] faces) {
|
||||
final ShortBuffer wrapper = ByteBuffer.allocateDirect(faces.length * 2).order(ByteOrder.nativeOrder())
|
||||
.asShortBuffer();
|
||||
for (final int face : faces) {
|
||||
wrapper.put((short) face);
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private static ByteBuffer wrap(final byte[] skin) {
|
||||
final ByteBuffer wrapper = ByteBuffer.allocateDirect(skin.length).order(ByteOrder.nativeOrder());
|
||||
wrapper.put(skin);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private static FloatBuffer wrap(final float[] positions) {
|
||||
final FloatBuffer wrapper = ByteBuffer.allocateDirect(positions.length * 4).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
wrapper.put(positions);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.Shaders;
|
||||
|
||||
public class WarsmashTestGameTextureBuffer extends ApplicationAdapter {
|
||||
private int arrayBuffer;
|
||||
private int elementBuffer;
|
||||
private int VAO;
|
||||
private ShaderProgram shaderProgram;
|
||||
private FloatBuffer vertexBuffer;
|
||||
private ShortBuffer faceBuffer;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
// ShaderProgram.pedantic = false;
|
||||
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // colour to use when clearing
|
||||
|
||||
final ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(4);
|
||||
tempByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final IntBuffer temp = tempByteBuffer.asIntBuffer();
|
||||
|
||||
Gdx.gl30.glGenVertexArrays(1, temp);
|
||||
this.VAO = temp.get(0);
|
||||
|
||||
Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
|
||||
System.out.println(vsSimple);
|
||||
this.shaderProgram = new ShaderProgram(vsSimple, fsSimple);
|
||||
if (!this.shaderProgram.isCompiled()) {
|
||||
throw new IllegalStateException(this.shaderProgram.getLog());
|
||||
}
|
||||
|
||||
this.arrayBuffer = Gdx.gl.glGenBuffer();
|
||||
this.elementBuffer = Gdx.gl.glGenBuffer();
|
||||
System.out.println("arrayBuffer: " + this.arrayBuffer + ", elementBuffer: " + this.elementBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
|
||||
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.vertexBuffer = vertexByteBuffer.asFloatBuffer();
|
||||
|
||||
this.vertexBuffer.put(0, -1f);
|
||||
this.vertexBuffer.put(1, -1f);
|
||||
this.vertexBuffer.put(2, 0);
|
||||
this.vertexBuffer.put(3, 1f);
|
||||
this.vertexBuffer.put(4, -1f);
|
||||
this.vertexBuffer.put(5, 0);
|
||||
this.vertexBuffer.put(6, 0f);
|
||||
this.vertexBuffer.put(7, 1f);
|
||||
this.vertexBuffer.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ARRAY_BUFFER, ((9 * 4) * 2) + 3, null, GL20.GL_STATIC_DRAW);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, 9 * 4, this.vertexBuffer);
|
||||
|
||||
final ByteBuffer vertex2ByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertex2ByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final FloatBuffer vertexBuffer2 = vertex2ByteBuffer.asFloatBuffer();
|
||||
|
||||
vertexBuffer2.put(0, -1f);
|
||||
vertexBuffer2.put(1, -1f);
|
||||
vertexBuffer2.put(2, 0);
|
||||
vertexBuffer2.put(3, 1f);
|
||||
vertexBuffer2.put(4, -1f);
|
||||
vertexBuffer2.put(5, 0);
|
||||
vertexBuffer2.put(6, 0f);
|
||||
vertexBuffer2.put(7, 1f);
|
||||
vertexBuffer2.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4, 9 * 4, vertexBuffer2);
|
||||
|
||||
final ByteBuffer skinByteBuffer = ByteBuffer.allocateDirect(3);
|
||||
skinByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
skinByteBuffer.put((byte) 34);
|
||||
skinByteBuffer.put((byte) 35);
|
||||
skinByteBuffer.put((byte) 36);
|
||||
skinByteBuffer.clear();
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4 * 2, 3, skinByteBuffer);
|
||||
|
||||
// this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position");
|
||||
// this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
// this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
// this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
// this.shaderProgram.setUniformi("u_boneMap", 15);
|
||||
|
||||
final ByteBuffer faceByteBuffer = ByteBuffer.allocateDirect(6);
|
||||
faceByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.faceBuffer = faceByteBuffer.asShortBuffer();
|
||||
|
||||
this.faceBuffer.put(0, (short) 0);
|
||||
this.faceBuffer.put(1, (short) 1);
|
||||
this.faceBuffer.put(2, (short) 2);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 3 * 2, null, GL20.GL_STATIC_DRAW);
|
||||
|
||||
final int glGetError = Gdx.gl.glGetError();
|
||||
System.out.println(glGetError);
|
||||
|
||||
final ByteBuffer vertex3ByteBuffer = ByteBuffer.allocateDirect(4 * 16);
|
||||
vertex3ByteBuffer.order(ByteOrder.nativeOrder());
|
||||
this.vertexTextureBuffer3 = vertex3ByteBuffer.asFloatBuffer();
|
||||
|
||||
this.vertexTextureBuffer3.put(0, 0.5f);
|
||||
this.vertexTextureBuffer3.put(1, 0);
|
||||
this.vertexTextureBuffer3.put(2, 0);
|
||||
this.vertexTextureBuffer3.put(3, 0);
|
||||
this.vertexTextureBuffer3.put(4, 0);
|
||||
this.vertexTextureBuffer3.put(5, 1);
|
||||
this.vertexTextureBuffer3.put(6, 0);
|
||||
this.vertexTextureBuffer3.put(7, 0);
|
||||
this.vertexTextureBuffer3.put(8, 0);
|
||||
this.vertexTextureBuffer3.put(9, 0);
|
||||
this.vertexTextureBuffer3.put(10, 1);
|
||||
this.vertexTextureBuffer3.put(11, 0);
|
||||
this.vertexTextureBuffer3.put(12, 0.0f);
|
||||
this.vertexTextureBuffer3.put(13, 0.0f);
|
||||
this.vertexTextureBuffer3.put(14, 0);
|
||||
this.vertexTextureBuffer3.put(15, 1);
|
||||
|
||||
// this.vertexTextureBuffer = Gdx.gl.glGenBuffer();
|
||||
this.vertexTexture = Gdx.gl.glGenTexture();
|
||||
|
||||
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE15);
|
||||
// Gdx.gl.glBindBuffer(GL20.GL_TEXTURE_2D, this.vertexTextureBuffer);
|
||||
// Gdx.gl.glBindBuffer(GL20.GL_TEXTURE_2D, 0);
|
||||
|
||||
Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, this.vertexTexture);
|
||||
Gdx.gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE);
|
||||
Gdx.gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE);
|
||||
Gdx.gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_NEAREST);
|
||||
Gdx.gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST);
|
||||
Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_RGBA, 4, 1, 0, GL30.GL_RGBA, GL20.GL_FLOAT,
|
||||
this.vertexTextureBuffer3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
this.shaderProgram.begin();
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.shaderProgram.enableVertexAttribute("a_position");
|
||||
this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
|
||||
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE15);
|
||||
Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, this.vertexTexture);
|
||||
Gdx.gl.glTexSubImage2D(GL20.GL_TEXTURE_2D, 0, 0, 0, 4, 1, GL30.GL_RGBA, GL20.GL_FLOAT,
|
||||
this.vertexTextureBuffer3);
|
||||
this.shaderProgram.setUniformi("u_boneMap", 15);
|
||||
this.shaderProgram.setUniformf("u_vectorSize", 1f / 4);
|
||||
this.shaderProgram.setUniformf("u_rowSize", 1);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, 3 * 2, this.faceBuffer);
|
||||
Gdx.gl.glDrawElements(GL20.GL_TRIANGLES, 3, GL20.GL_UNSIGNED_SHORT, 0);
|
||||
// Gdx.gl.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);
|
||||
this.shaderProgram.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
}
|
||||
|
||||
public static final String boneTexture = ""//
|
||||
+ " uniform samplerBuffer u_boneMap;\r\n" + //
|
||||
// " uniform uint u_vectorSize;\r\n" + //
|
||||
" uniform uint u_rowSize;\r\n" + //
|
||||
" mat4 fetchMatrix(uint column, uint row) {\r\n" + //
|
||||
// " column *= u_vectorSize * 4.0;\r\n" + //
|
||||
// " row *= u_rowSize;\r\n" + //
|
||||
" // Add in half texel to sample in the middle of the texel.\r\n" + //
|
||||
" // Otherwise, since the sample is directly on the boundry, small floating point errors can cause the sample to get the wrong pixel.\r\n"
|
||||
+ //
|
||||
" // This is mostly noticable with NPOT textures, which the bone maps are.\r\n" + //
|
||||
// " column += 0.5 * u_vectorSize;\r\n" + //
|
||||
// " row += 0.5 * u_rowSize;\r\n" + //
|
||||
" return mat4(texelFetch(u_boneMap, row * u_rowSize + column * 4),\r\n" + //
|
||||
" texelFetch(u_boneMap, row * u_rowSize + column * 4 + 1),\r\n" + //
|
||||
" texelFetch(u_boneMap, row * u_rowSize + column * 4 + 2),\r\n" + //
|
||||
" texelFetch(u_boneMap, row * u_rowSize + column * 4 + 3);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String vsSimple = "\r\n" + //
|
||||
"\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec3 a_position2;\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" mat4 bone = fetchMatrix(0.0, 0.0);\r\n" + //
|
||||
" if( a_boneNumber <= 34.5 ) {\r\n" + //
|
||||
" gl_Position = vec4(a_position2.x * bone[0][0], a_position2.y, a_position2.z, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" gl_Position = vec4(a_position2.x, a_position2.y, a_position2.z, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" fragNumber = a_boneNumber;\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String fsSimple = "\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" mat4 bone = fetchMatrix(0.0, 0.0);\r\n" + //
|
||||
" if( fragNumber > 35.5 ) {\r\n" + //
|
||||
" gl_FragColor = bone[0];//vec4(1.0, 0.0, 0.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 34.5 ) {\r\n" + //
|
||||
" gl_FragColor = bone[1];//vec4(0.0, 1.0, 1.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 33.5 ) {\r\n" + //
|
||||
" gl_FragColor = bone[2];//vec4(1.0, 0.0, 1.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" gl_FragColor = vec4(fragNumber*100.0, fragNumber, fragNumber, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n";
|
||||
private int vertexTexture;
|
||||
private int vertexTextureBuffer;
|
||||
private FloatBuffer vertexTextureBuffer3;
|
||||
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.Shaders;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
|
||||
public class WarsmashTestGameTextureBuffer2 extends ApplicationAdapter {
|
||||
private int arrayBuffer;
|
||||
private int elementBuffer;
|
||||
private int VAO;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
// ShaderProgram.pedantic = false;
|
||||
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // colour to use when clearing
|
||||
|
||||
final ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(4);
|
||||
tempByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final IntBuffer temp = tempByteBuffer.asIntBuffer();
|
||||
|
||||
Gdx.gl30.glGenVertexArrays(1, temp);
|
||||
this.VAO = temp.get(0);
|
||||
|
||||
Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
|
||||
System.out.println(vsSimple);
|
||||
this.shaderProgram = new ShaderProgram(vsSimple, fsSimple);
|
||||
if (!this.shaderProgram.isCompiled()) {
|
||||
throw new IllegalStateException(this.shaderProgram.getLog());
|
||||
}
|
||||
|
||||
this.arrayBuffer = Gdx.gl.glGenBuffer();
|
||||
this.elementBuffer = Gdx.gl.glGenBuffer();
|
||||
System.out.println("arrayBuffer: " + this.arrayBuffer + ", elementBuffer: " + this.elementBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
|
||||
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.vertexBuffer = vertexByteBuffer.asFloatBuffer();
|
||||
|
||||
this.vertexBuffer.put(0, -1f);
|
||||
this.vertexBuffer.put(1, -1f);
|
||||
this.vertexBuffer.put(2, 0);
|
||||
this.vertexBuffer.put(3, 1f);
|
||||
this.vertexBuffer.put(4, -1f);
|
||||
this.vertexBuffer.put(5, 0);
|
||||
this.vertexBuffer.put(6, 0f);
|
||||
this.vertexBuffer.put(7, 1f);
|
||||
this.vertexBuffer.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ARRAY_BUFFER, ((9 * 4) * 2) + 3, null, GL20.GL_STATIC_DRAW);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, 9 * 4, this.vertexBuffer);
|
||||
|
||||
final ByteBuffer vertex2ByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertex2ByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final FloatBuffer vertexBuffer2 = vertex2ByteBuffer.asFloatBuffer();
|
||||
|
||||
vertexBuffer2.put(0, -1f);
|
||||
vertexBuffer2.put(1, -1f);
|
||||
vertexBuffer2.put(2, 0);
|
||||
vertexBuffer2.put(3, 1f);
|
||||
vertexBuffer2.put(4, -1f);
|
||||
vertexBuffer2.put(5, 0);
|
||||
vertexBuffer2.put(6, 0f);
|
||||
vertexBuffer2.put(7, 1f);
|
||||
vertexBuffer2.put(8, 0);
|
||||
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4, 9 * 4, vertexBuffer2);
|
||||
|
||||
final ByteBuffer skinByteBuffer = ByteBuffer.allocateDirect(3);
|
||||
skinByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
skinByteBuffer.put((byte) 34);
|
||||
skinByteBuffer.put((byte) 35);
|
||||
skinByteBuffer.put((byte) 36);
|
||||
skinByteBuffer.clear();
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 9 * 4 * 2, 3, skinByteBuffer);
|
||||
|
||||
// this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position");
|
||||
// this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
// this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
// this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
// this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
// this.shaderProgram.setUniformi("u_boneMap", 15);
|
||||
|
||||
final ByteBuffer faceByteBuffer = ByteBuffer.allocateDirect(6);
|
||||
faceByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.faceBuffer = faceByteBuffer.asShortBuffer();
|
||||
|
||||
this.faceBuffer.put(0, (short) 0);
|
||||
this.faceBuffer.put(1, (short) 1);
|
||||
this.faceBuffer.put(2, (short) 2);
|
||||
|
||||
Gdx.gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, 3 * 2, null, GL20.GL_STATIC_DRAW);
|
||||
|
||||
final int glGetError = Gdx.gl.glGetError();
|
||||
System.out.println(glGetError);
|
||||
|
||||
final ByteBuffer vertex3ByteBuffer = ByteBuffer.allocateDirect(4 * 16);
|
||||
vertex3ByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final FloatBuffer vertexBuffer3 = vertex3ByteBuffer.asFloatBuffer();
|
||||
|
||||
vertexBuffer3.put(0, 1);
|
||||
vertexBuffer3.put(1, 0);
|
||||
vertexBuffer3.put(2, 0);
|
||||
vertexBuffer3.put(3, 0);
|
||||
vertexBuffer3.put(4, 0);
|
||||
vertexBuffer3.put(5, 1);
|
||||
vertexBuffer3.put(6, 0);
|
||||
vertexBuffer3.put(7, 0);
|
||||
vertexBuffer3.put(8, 0);
|
||||
vertexBuffer3.put(9, 0);
|
||||
vertexBuffer3.put(10, 1);
|
||||
vertexBuffer3.put(11, 0);
|
||||
vertexBuffer3.put(12, 0.0f);
|
||||
vertexBuffer3.put(13, 0.0f);
|
||||
vertexBuffer3.put(14, 0);
|
||||
vertexBuffer3.put(15, 1);
|
||||
|
||||
this.dataTexture = new DataTexture(Gdx.gl, 4, 4, 1);
|
||||
this.dataTexture.bindAndUpdate(vertexBuffer3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Gdx.gl30.glBindVertexArray(this.VAO);
|
||||
this.shaderProgram.begin();
|
||||
|
||||
this.dataTexture.bind(15);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.arrayBuffer);
|
||||
this.shaderProgram.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, 0);
|
||||
this.shaderProgram.enableVertexAttribute("a_position");
|
||||
this.shaderProgram.setVertexAttribute("a_position2", 3, GL20.GL_FLOAT, false, 0, 4 * 9);
|
||||
this.shaderProgram.enableVertexAttribute("a_position2");
|
||||
this.shaderProgram.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 1, 4 * 9 * 2);
|
||||
this.shaderProgram.enableVertexAttribute("a_boneNumber");
|
||||
|
||||
this.shaderProgram.setUniformi("u_boneMap", 15);
|
||||
this.shaderProgram.setUniformf("u_vectorSize", 1f / this.dataTexture.getWidth());
|
||||
this.shaderProgram.setUniformf("u_rowSize", 1);
|
||||
|
||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||
Gdx.gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, 3 * 2, this.faceBuffer);
|
||||
Gdx.gl.glDrawElements(GL20.GL_TRIANGLES, 3, GL20.GL_UNSIGNED_SHORT, 0);
|
||||
// Gdx.gl.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);
|
||||
this.shaderProgram.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final int width, final int height) {
|
||||
}
|
||||
|
||||
public static final String vsSimple = "\r\n" + //
|
||||
"\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec3 a_position2;\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" mat4 bone = fetchMatrix(0.0, 0.0);\r\n" + //
|
||||
" gl_Position = bone * vec4(a_position2.x, a_position2.y, a_position2.z, 1.0);\r\n" + //
|
||||
" fragNumber = a_boneNumber;\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String fsSimple = "\r\n" + //
|
||||
" varying float fragNumber;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if( fragNumber > 35.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 34.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);\r\n" + //
|
||||
" } else if( fragNumber > 33.5 ) {\r\n" + //
|
||||
" gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" gl_FragColor = vec4(fragNumber*100.0, fragNumber, fragNumber, 1.0);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n";
|
||||
private ShaderProgram shaderProgram;
|
||||
private FloatBuffer vertexBuffer;
|
||||
private ShortBuffer faceBuffer;
|
||||
private DataTexture dataTexture;
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
|
||||
public class WarsmashTestMyTextureGame extends ApplicationAdapter {
|
||||
|
||||
private DataSource codebase;
|
||||
private Texture texture;
|
||||
private SpriteBatch batch;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
|
||||
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127");
|
||||
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
|
||||
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
|
||||
this.codebase = new CompoundDataSourceDescriptor(
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
||||
|
||||
try {
|
||||
this.texture = ImageUtils
|
||||
.getTexture(ImageIO.read(this.codebase.getResourceAsStream("Textures\\Dust3x.blp")));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
this.batch = new SpriteBatch();
|
||||
this.batch.enableBlending();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
this.batch.begin();
|
||||
this.batch.draw(this.texture, 20, 20, 256, 256);
|
||||
this.batch.end();
|
||||
}
|
||||
|
||||
}
|
@ -15,10 +15,10 @@ import java.util.Set;
|
||||
public class CompoundDataSource implements DataSource {
|
||||
private final List<DataSource> mpqList = new ArrayList<>();
|
||||
|
||||
public CompoundDataSource(final List<DataSourceDescriptor> dataSourceDescriptors) {
|
||||
if (dataSourceDescriptors != null) {
|
||||
for (final DataSourceDescriptor descriptor : dataSourceDescriptors) {
|
||||
this.mpqList.add(descriptor.createDataSource());
|
||||
public CompoundDataSource(final List<DataSource> dataSources) {
|
||||
if (dataSources != null) {
|
||||
for (final DataSource dataSource : dataSources) {
|
||||
this.mpqList.add(dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.etheller.warsmash.datasources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CompoundDataSourceDescriptor implements DataSourceDescriptor {
|
||||
private final List<DataSourceDescriptor> dataSourceDescriptors;
|
||||
|
||||
public CompoundDataSourceDescriptor(final List<DataSourceDescriptor> dataSourceDescriptors) {
|
||||
this.dataSourceDescriptors = dataSourceDescriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource createDataSource() {
|
||||
final List<DataSource> dataSources = new ArrayList<>();
|
||||
for (final DataSourceDescriptor descriptor : this.dataSourceDescriptors) {
|
||||
dataSources.add(descriptor.createDataSource());
|
||||
}
|
||||
return new CompoundDataSource(dataSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "CompoundDataSourceDescriptor";
|
||||
}
|
||||
|
||||
}
|
143
core/src/com/etheller/warsmash/datasources/MpqDataSource.java
Normal file
143
core/src/com/etheller/warsmash/datasources/MpqDataSource.java
Normal file
@ -0,0 +1,143 @@
|
||||
package com.etheller.warsmash.datasources;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import mpq.ArchivedFile;
|
||||
import mpq.ArchivedFileExtractor;
|
||||
import mpq.ArchivedFileStream;
|
||||
import mpq.HashLookup;
|
||||
import mpq.MPQArchive;
|
||||
import mpq.MPQException;
|
||||
|
||||
public class MpqDataSource implements DataSource {
|
||||
|
||||
private final MPQArchive archive;
|
||||
private final SeekableByteChannel inputChannel;
|
||||
private final ArchivedFileExtractor extractor = new ArchivedFileExtractor();
|
||||
|
||||
public MpqDataSource(final MPQArchive archive, final SeekableByteChannel inputChannel) {
|
||||
this.archive = archive;
|
||||
this.inputChannel = inputChannel;
|
||||
}
|
||||
|
||||
public MPQArchive getArchive() {
|
||||
return this.archive;
|
||||
}
|
||||
|
||||
public SeekableByteChannel getInputChannel() {
|
||||
return this.inputChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
||||
ArchivedFile file = null;
|
||||
try {
|
||||
file = this.archive.lookupHash2(new HashLookup(filepath));
|
||||
}
|
||||
catch (final MPQException exc) {
|
||||
if (exc.getMessage().equals("lookup not found")) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new IOException(exc);
|
||||
}
|
||||
}
|
||||
final ArchivedFileStream stream = new ArchivedFileStream(this.inputChannel, this.extractor, file);
|
||||
final InputStream newInputStream = Channels.newInputStream(stream);
|
||||
return newInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile(final String filepath) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
// System.out.println("getting it from the outside: " +
|
||||
// filepath);
|
||||
ArchivedFile file = null;
|
||||
try {
|
||||
file = this.archive.lookupHash2(new HashLookup(filepath));
|
||||
}
|
||||
catch (final MPQException exc) {
|
||||
if (exc.getMessage().equals("lookup not found")) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new IOException(exc);
|
||||
}
|
||||
}
|
||||
final ArchivedFileStream stream = new ArchivedFileStream(this.inputChannel, this.extractor, file);
|
||||
final InputStream newInputStream = Channels.newInputStream(stream);
|
||||
String tmpdir = System.getProperty("java.io.tmpdir");
|
||||
if (!tmpdir.endsWith(File.separator)) {
|
||||
tmpdir += File.separator;
|
||||
}
|
||||
final String tempDir = tmpdir + "RMSExtract/";
|
||||
final File tempProduct = new File(tempDir + filepath.replace('\\', File.separatorChar));
|
||||
tempProduct.delete();
|
||||
tempProduct.getParentFile().mkdirs();
|
||||
Files.copy(newInputStream, tempProduct.toPath());
|
||||
tempProduct.deleteOnExit();
|
||||
return tempProduct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final String filepath) {
|
||||
try {
|
||||
this.archive.lookupPath(filepath);
|
||||
return true;
|
||||
}
|
||||
catch (final MPQException exc) {
|
||||
if (exc.getMessage().equals("lookup not found")) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException(exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getListfile() {
|
||||
try {
|
||||
final Set<String> listfile = new HashSet<>();
|
||||
ArchivedFile listfileContents;
|
||||
listfileContents = this.archive.lookupHash2(new HashLookup("(listfile)"));
|
||||
final ArchivedFileStream stream = new ArchivedFileStream(this.inputChannel, this.extractor,
|
||||
listfileContents);
|
||||
final InputStream newInputStream = Channels.newInputStream(stream);
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(newInputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
listfile.add(line);
|
||||
}
|
||||
}
|
||||
catch (final IOException exc) {
|
||||
throw new RuntimeException(exc);
|
||||
}
|
||||
return listfile;
|
||||
}
|
||||
catch (final MPQException exc) {
|
||||
if (exc.getMessage().equals("lookup not found")) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException(exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.inputChannel.close();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.etheller.warsmash.datasources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import mpq.MPQArchive;
|
||||
import mpq.MPQException;
|
||||
|
||||
public class MpqDataSourceDescriptor implements DataSourceDescriptor {
|
||||
/**
|
||||
* Generated serial id
|
||||
*/
|
||||
private static final long serialVersionUID = 8424254987711783598L;
|
||||
private final String mpqFilePath;
|
||||
|
||||
public MpqDataSourceDescriptor(final String mpqFilePath) {
|
||||
this.mpqFilePath = mpqFilePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource createDataSource() {
|
||||
try {
|
||||
SeekableByteChannel sbc;
|
||||
sbc = Files.newByteChannel(Paths.get(this.mpqFilePath), EnumSet.of(StandardOpenOption.READ));
|
||||
return new MpqDataSource(new MPQArchive(sbc), sbc);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final MPQException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "MPQ Archive: " + this.mpqFilePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((this.mpqFilePath == null) ? 0 : this.mpqFilePath.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final MpqDataSourceDescriptor other = (MpqDataSourceDescriptor) obj;
|
||||
if (this.mpqFilePath == null) {
|
||||
if (other.mpqFilePath != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.mpqFilePath.equals(other.mpqFilePath)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -88,4 +88,8 @@ public class Bone extends GenericObject {
|
||||
public int getGeosetAnimationId() {
|
||||
return this.geosetAnimationId;
|
||||
}
|
||||
|
||||
public int getGeosetId() {
|
||||
return this.geosetId;
|
||||
}
|
||||
}
|
||||
|
@ -109,4 +109,24 @@ public class Sequence implements MdlxBlock {
|
||||
public int getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public float getRarity() {
|
||||
return this.rarity;
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return this.moveSpeed;
|
||||
}
|
||||
|
||||
public long getSyncPoint() {
|
||||
return this.syncPoint;
|
||||
}
|
||||
|
||||
public Extent getExtent() {
|
||||
return this.extent;
|
||||
}
|
||||
}
|
||||
|
125
core/src/com/etheller/warsmash/parsers/w3x/War3Map.java
Normal file
125
core/src/com/etheller/warsmash/parsers/w3x/War3Map.java
Normal file
@ -0,0 +1,125 @@
|
||||
package com.etheller.warsmash.parsers.w3x;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
|
||||
|
||||
import com.etheller.warsmash.datasources.CompoundDataSource;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.MpqDataSource;
|
||||
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.google.common.io.LittleEndianDataInputStream;
|
||||
|
||||
import mpq.MPQArchive;
|
||||
import mpq.MPQException;
|
||||
|
||||
/**
|
||||
* Warcraft 3 map (W3X and W3M).
|
||||
*/
|
||||
public class War3Map implements DataSource {
|
||||
|
||||
private CompoundDataSource dataSource;
|
||||
private MpqDataSource internalMpqContentsDataSource;
|
||||
|
||||
public War3Map(final DataSource dataSource, final String mapFileName) {
|
||||
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 = dataSource.getResourceAsStream(mapFileName)) {
|
||||
final byte[] mapData = IOUtils.toByteArray(mapStream);
|
||||
sbc = new SeekableInMemoryByteChannel(mapData);
|
||||
this.internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc);
|
||||
this.dataSource = new CompoundDataSource(Arrays.asList(dataSource, this.internalMpqContentsDataSource));
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final MPQException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public War3MapW3i readMapInformation() throws IOException {
|
||||
War3MapW3i mapInfo;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
this.dataSource.getResourceAsStream("war3map.w3i"))) {
|
||||
mapInfo = new War3MapW3i(stream);
|
||||
}
|
||||
return mapInfo;
|
||||
}
|
||||
|
||||
public War3MapW3e readEnvironment() throws IOException {
|
||||
War3MapW3e environment;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
this.dataSource.getResourceAsStream("war3map.w3e"))) {
|
||||
environment = new War3MapW3e(stream);
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
|
||||
public War3MapDoo readDoodads() throws IOException {
|
||||
War3MapDoo doodadsFile;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
this.dataSource.getResourceAsStream("war3map.doo"))) {
|
||||
doodadsFile = new War3MapDoo(stream);
|
||||
}
|
||||
return doodadsFile;
|
||||
}
|
||||
|
||||
public War3MapUnitsDoo readUnits() throws IOException {
|
||||
War3MapUnitsDoo unitsFile;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
this.dataSource.getResourceAsStream("war3mapUnits.doo"))) {
|
||||
unitsFile = new War3MapUnitsDoo(stream);
|
||||
}
|
||||
return unitsFile;
|
||||
}
|
||||
|
||||
public Warcraft3MapObjectData readModifications() throws IOException {
|
||||
final Warcraft3MapObjectData changes = Warcraft3MapObjectData.load(this.dataSource, true);
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
||||
return this.dataSource.getResourceAsStream(filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile(final String filepath) throws IOException {
|
||||
return this.dataSource.getFile(filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final String filepath) {
|
||||
return this.dataSource.has(filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getListfile() {
|
||||
return this.internalMpqContentsDataSource.getListfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.dataSource.close();
|
||||
}
|
||||
|
||||
public CompoundDataSource getCompoundDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
}
|
157
core/src/com/etheller/warsmash/parsers/w3x/doo/Doodad.java
Normal file
157
core/src/com/etheller/warsmash/parsers/w3x/doo/Doodad.java
Normal file
@ -0,0 +1,157 @@
|
||||
package com.etheller.warsmash.parsers.w3x.doo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class Doodad {
|
||||
private War3ID id;
|
||||
private int variation;
|
||||
private final float[] location = new float[3];
|
||||
private float angle;
|
||||
private final float[] scale = { 1f, 1f, 1f };
|
||||
private short flags; // short to store unsigned byte, java problem
|
||||
private short life; // short to store unsigned byte, java problem
|
||||
private long itemTable = -1; // long to store unsigned int32, java problem
|
||||
private final List<RandomItemSet> itemSets = new ArrayList<>();
|
||||
private int editorId;
|
||||
private final short[] u1 = new short[8]; // short to store unsigned byte, java problem
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream, final int version) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.variation = stream.readInt();
|
||||
ParseUtils.readFloatArray(stream, this.location);
|
||||
this.angle = stream.readFloat();
|
||||
ParseUtils.readFloatArray(stream, this.scale);
|
||||
this.flags = ParseUtils.readUInt8(stream);
|
||||
this.life = ParseUtils.readUInt8(stream);
|
||||
|
||||
if (version > 7) {
|
||||
this.itemTable = ParseUtils.readUInt32(stream);
|
||||
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final RandomItemSet itemSet = new RandomItemSet();
|
||||
|
||||
itemSet.load(stream);
|
||||
|
||||
this.itemSets.add(itemSet);
|
||||
}
|
||||
}
|
||||
|
||||
this.editorId = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream, final int version) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
;
|
||||
stream.writeInt(this.variation);
|
||||
ParseUtils.writeFloatArray(stream, this.location);
|
||||
stream.writeFloat(this.angle);
|
||||
ParseUtils.writeFloatArray(stream, this.scale);
|
||||
ParseUtils.writeUInt8(stream, this.flags);
|
||||
ParseUtils.writeUInt8(stream, this.life);
|
||||
|
||||
if (version > 7) {
|
||||
ParseUtils.writeUInt32(stream, this.itemTable);
|
||||
ParseUtils.writeUInt32(stream, this.itemSets.size());
|
||||
|
||||
for (final RandomItemSet itemSet : this.itemSets) {
|
||||
itemSet.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
stream.writeInt(this.editorId);
|
||||
}
|
||||
|
||||
public int getByteLength(final int version) {
|
||||
int size = 42;
|
||||
|
||||
if (version > 7) {
|
||||
size += 8;
|
||||
|
||||
for (final RandomItemSet itemSet : this.itemSets) {
|
||||
size += itemSet.getByteLength();
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getVariation() {
|
||||
return this.variation;
|
||||
}
|
||||
|
||||
public void setVariation(final int variation) {
|
||||
this.variation = variation;
|
||||
}
|
||||
|
||||
public float getAngle() {
|
||||
return this.angle;
|
||||
}
|
||||
|
||||
public void setAngle(final float angle) {
|
||||
this.angle = angle;
|
||||
}
|
||||
|
||||
public short getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public void setFlags(final short flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public short getLife() {
|
||||
return this.life;
|
||||
}
|
||||
|
||||
public void setLife(final short life) {
|
||||
this.life = life;
|
||||
}
|
||||
|
||||
public long getItemTable() {
|
||||
return this.itemTable;
|
||||
}
|
||||
|
||||
public void setItemTable(final long itemTable) {
|
||||
this.itemTable = itemTable;
|
||||
}
|
||||
|
||||
public int getEditorId() {
|
||||
return this.editorId;
|
||||
}
|
||||
|
||||
public void setEditorId(final int editorId) {
|
||||
this.editorId = editorId;
|
||||
}
|
||||
|
||||
public float[] getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public float[] getScale() {
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
public List<RandomItemSet> getItemSets() {
|
||||
return this.itemSets;
|
||||
}
|
||||
|
||||
public short[] getU1() {
|
||||
return this.u1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.etheller.warsmash.parsers.w3x.doo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomItem {
|
||||
private War3ID id;
|
||||
private int chance;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.chance = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.chance);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.etheller.warsmash.parsers.w3x.doo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomItemSet {
|
||||
private final List<RandomItem> items = new ArrayList<>();
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final RandomItem item = new RandomItem();
|
||||
|
||||
item.load(stream);
|
||||
|
||||
this.items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.items.size());
|
||||
for (final RandomItem item : this.items) {
|
||||
item.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 4 + (this.items.size() * 8);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.etheller.warsmash.parsers.w3x.doo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* A terrain doodad.
|
||||
*
|
||||
* This type of doodad works much like cliffs. It uses the height of the
|
||||
* terrain, and gets affected by the ground heightmap. It cannot be manipulated
|
||||
* in any way in the World Editor once placed. Indeed, the only way to change it
|
||||
* is to remove it by changing cliffs around it.
|
||||
*/
|
||||
public class TerrainDoodad {
|
||||
private War3ID id;
|
||||
private long u1;
|
||||
private final long[] location = new long[2];
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream, final int version) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.u1 = ParseUtils.readUInt32(stream);
|
||||
ParseUtils.readUInt32Array(stream, this.location);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream, final int version) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
ParseUtils.writeUInt32(stream, this.u1);
|
||||
ParseUtils.writeUInt32Array(stream, this.location);
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public long getU1() {
|
||||
return this.u1;
|
||||
}
|
||||
|
||||
public long[] getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setU1(final long u1) {
|
||||
this.u1 = u1;
|
||||
}
|
||||
}
|
108
core/src/com/etheller/warsmash/parsers/w3x/doo/War3MapDoo.java
Normal file
108
core/src/com/etheller/warsmash/parsers/w3x/doo/War3MapDoo.java
Normal file
@ -0,0 +1,108 @@
|
||||
package com.etheller.warsmash.parsers.w3x.doo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* war3map.doo - the doodad and destructible file.
|
||||
*/
|
||||
public class War3MapDoo {
|
||||
private static final War3ID MAGIC_NUMBER = War3ID.fromString("W3do");
|
||||
private int version = 0;
|
||||
private final short[] u1 = new short[4];
|
||||
private final List<Doodad> doodads = new ArrayList<>();
|
||||
private final short[] u2 = new short[4];
|
||||
private final List<TerrainDoodad> terrainDoodads = new ArrayList<>();
|
||||
|
||||
public War3MapDoo(final LittleEndianDataInputStream stream) throws IOException {
|
||||
if (stream != null) {
|
||||
this.load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
final War3ID firstId = ParseUtils.readWar3ID(stream);
|
||||
if (!MAGIC_NUMBER.equals(firstId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.version = stream.readInt();
|
||||
ParseUtils.readUInt8Array(stream, this.u1);
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final Doodad doodad = new Doodad();
|
||||
|
||||
doodad.load(stream, this.version);
|
||||
|
||||
this.doodads.add(doodad);
|
||||
}
|
||||
|
||||
ParseUtils.readUInt8Array(stream, this.u2);
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final TerrainDoodad terrainDoodad = new TerrainDoodad();
|
||||
|
||||
terrainDoodad.load(stream, this.version);
|
||||
|
||||
this.terrainDoodads.add(terrainDoodad);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
|
||||
ParseUtils.writeWar3ID(stream, MAGIC_NUMBER);
|
||||
stream.writeInt(this.version);
|
||||
ParseUtils.writeUInt8Array(stream, this.u1);
|
||||
ParseUtils.writeUInt32(stream, this.doodads.size());
|
||||
|
||||
for (final Doodad doodad : this.doodads) {
|
||||
doodad.save(stream, this.version);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt8Array(stream, this.u2);
|
||||
ParseUtils.writeUInt32(stream, this.terrainDoodads.size());
|
||||
|
||||
for (final TerrainDoodad terrainDoodad : this.terrainDoodads) {
|
||||
terrainDoodad.save(stream, this.version);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
int size = 24 + (this.terrainDoodads.size() * 16);
|
||||
|
||||
for (final Doodad doodad : this.doodads) {
|
||||
size += doodad.getByteLength(this.version);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public short[] getU1() {
|
||||
return this.u1;
|
||||
}
|
||||
|
||||
public List<Doodad> getDoodads() {
|
||||
return this.doodads;
|
||||
}
|
||||
|
||||
public short[] getU2() {
|
||||
return this.u2;
|
||||
}
|
||||
|
||||
public List<TerrainDoodad> getTerrainDoodads() {
|
||||
return this.terrainDoodads;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package com.etheller.warsmash.parsers.w3x.objectdata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.units.StandardObjectData.WarcraftData;
|
||||
import com.etheller.warsmash.units.custom.WTSFile;
|
||||
import com.etheller.warsmash.units.custom.War3ObjectDataChangeset;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
|
||||
public final class Warcraft3MapObjectData {
|
||||
private final MutableObjectData units;
|
||||
private final MutableObjectData items;
|
||||
private final MutableObjectData destructibles;
|
||||
private final MutableObjectData doodads;
|
||||
private final MutableObjectData abilities;
|
||||
private final MutableObjectData buffs;
|
||||
private final MutableObjectData upgrades;
|
||||
private final List<MutableObjectData> datas;
|
||||
private transient Map<WorldEditorDataType, MutableObjectData> typeToData = new HashMap<>();
|
||||
|
||||
public Warcraft3MapObjectData(final MutableObjectData units, final MutableObjectData items,
|
||||
final MutableObjectData destructibles, final MutableObjectData doodads, final MutableObjectData abilities,
|
||||
final MutableObjectData buffs, final MutableObjectData upgrades) {
|
||||
this.units = units;
|
||||
this.items = items;
|
||||
this.destructibles = destructibles;
|
||||
this.doodads = doodads;
|
||||
this.abilities = abilities;
|
||||
this.buffs = buffs;
|
||||
this.upgrades = upgrades;
|
||||
this.datas = new ArrayList<>();
|
||||
this.datas.add(units);
|
||||
this.datas.add(items);
|
||||
this.datas.add(destructibles);
|
||||
this.datas.add(doodads);
|
||||
this.datas.add(abilities);
|
||||
this.datas.add(buffs);
|
||||
this.datas.add(upgrades);
|
||||
for (final MutableObjectData data : this.datas) {
|
||||
this.typeToData.put(data.getWorldEditorDataType(), data);
|
||||
}
|
||||
}
|
||||
|
||||
public MutableObjectData getDataByType(final WorldEditorDataType type) {
|
||||
return this.typeToData.get(type);
|
||||
}
|
||||
|
||||
public MutableObjectData getUnits() {
|
||||
return this.units;
|
||||
}
|
||||
|
||||
public MutableObjectData getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public MutableObjectData getDestructibles() {
|
||||
return this.destructibles;
|
||||
}
|
||||
|
||||
public MutableObjectData getDoodads() {
|
||||
return this.doodads;
|
||||
}
|
||||
|
||||
public MutableObjectData getAbilities() {
|
||||
return this.abilities;
|
||||
}
|
||||
|
||||
public MutableObjectData getBuffs() {
|
||||
return this.buffs;
|
||||
}
|
||||
|
||||
public MutableObjectData getUpgrades() {
|
||||
return this.upgrades;
|
||||
}
|
||||
|
||||
public List<MutableObjectData> getDatas() {
|
||||
return this.datas;
|
||||
}
|
||||
|
||||
public static Warcraft3MapObjectData load(final DataSource dataSource, final boolean inlineWTS) throws IOException {
|
||||
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(dataSource);
|
||||
final WarcraftData standardUnits = standardObjectData.getStandardUnits();
|
||||
final WarcraftData standardItems = standardObjectData.getStandardItems();
|
||||
final WarcraftData standardDoodads = standardObjectData.getStandardDoodads();
|
||||
final WarcraftData standardDestructables = standardObjectData.getStandardDestructables();
|
||||
final WarcraftData abilities = standardObjectData.getStandardAbilities();
|
||||
final WarcraftData standardAbilityBuffs = standardObjectData.getStandardAbilityBuffs();
|
||||
final WarcraftData standardUpgrades = standardObjectData.getStandardUpgrades();
|
||||
|
||||
final DataTable standardUnitMeta = standardObjectData.getStandardUnitMeta();
|
||||
final DataTable standardDoodadMeta = standardObjectData.getStandardDoodadMeta();
|
||||
final DataTable standardDestructableMeta = standardObjectData.getStandardDestructableMeta();
|
||||
final DataTable abilityMeta = standardObjectData.getStandardAbilityMeta();
|
||||
final DataTable standardAbilityBuffMeta = standardObjectData.getStandardAbilityBuffMeta();
|
||||
final DataTable standardUpgradeMeta = standardObjectData.getStandardUpgradeMeta();
|
||||
|
||||
final War3ObjectDataChangeset unitChangeset = new War3ObjectDataChangeset('u');
|
||||
final War3ObjectDataChangeset itemChangeset = new War3ObjectDataChangeset('t');
|
||||
final War3ObjectDataChangeset doodadChangeset = new War3ObjectDataChangeset('d');
|
||||
final War3ObjectDataChangeset destructableChangeset = new War3ObjectDataChangeset('b');
|
||||
final War3ObjectDataChangeset abilityChangeset = new War3ObjectDataChangeset('a');
|
||||
final War3ObjectDataChangeset buffChangeset = new War3ObjectDataChangeset('h');
|
||||
final War3ObjectDataChangeset upgradeChangeset = new War3ObjectDataChangeset('q');
|
||||
|
||||
final WTSFile wts = new WTSFile(dataSource.getResourceAsStream("war3map.wts"));
|
||||
if (dataSource.has("war3map.w3u")) {
|
||||
unitChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3u")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3t")) {
|
||||
itemChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3t")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3d")) {
|
||||
doodadChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3d")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3b")) {
|
||||
destructableChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3b")),
|
||||
wts, inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3a")) {
|
||||
abilityChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3a")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3h")) {
|
||||
buffChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3h")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
if (dataSource.has("war3map.w3q")) {
|
||||
upgradeChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3q")), wts,
|
||||
inlineWTS);
|
||||
}
|
||||
|
||||
final WorldEditStrings worldEditStrings = standardObjectData.getWorldEditStrings();
|
||||
final MutableObjectData unitData = new MutableObjectData(worldEditStrings, WorldEditorDataType.UNITS,
|
||||
standardUnits, standardUnitMeta, unitChangeset);
|
||||
final MutableObjectData itemData = new MutableObjectData(worldEditStrings, WorldEditorDataType.ITEM,
|
||||
standardItems, standardUnitMeta, itemChangeset);
|
||||
final MutableObjectData doodadData = new MutableObjectData(worldEditStrings, WorldEditorDataType.DOODADS,
|
||||
standardDoodads, standardDoodadMeta, doodadChangeset);
|
||||
final MutableObjectData destructableData = new MutableObjectData(worldEditStrings,
|
||||
WorldEditorDataType.DESTRUCTIBLES, standardDestructables, standardDestructableMeta,
|
||||
destructableChangeset);
|
||||
final MutableObjectData abilityData = new MutableObjectData(worldEditStrings, WorldEditorDataType.ABILITIES,
|
||||
abilities, abilityMeta, abilityChangeset);
|
||||
final MutableObjectData buffData = new MutableObjectData(worldEditStrings, WorldEditorDataType.BUFFS_EFFECTS,
|
||||
standardAbilityBuffs, standardAbilityBuffMeta, buffChangeset);
|
||||
final MutableObjectData upgradeData = new MutableObjectData(worldEditStrings, WorldEditorDataType.UPGRADES,
|
||||
standardUpgrades, standardUpgradeMeta, upgradeChangeset);
|
||||
|
||||
return new Warcraft3MapObjectData(unitData, itemData, destructableData, doodadData, abilityData, buffData,
|
||||
upgradeData);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* A dropped item.
|
||||
*/
|
||||
public class DroppedItem {
|
||||
private War3ID id;
|
||||
private int chance;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.chance = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.chance);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* A dropped item set.
|
||||
*/
|
||||
public class DroppedItemSet {
|
||||
private final List<DroppedItem> items = new ArrayList<>();
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final DroppedItem item = new DroppedItem();
|
||||
|
||||
item.load(stream);
|
||||
|
||||
this.items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.items.size());
|
||||
|
||||
for (final DroppedItem item : this.items) {
|
||||
item.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 4 + (this.items.size() * 8);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* An inventory item.
|
||||
*/
|
||||
public class InventoryItem {
|
||||
private int slot;
|
||||
private War3ID id;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.slot = stream.readInt();
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeInt(this.slot);
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class ModifiedAbility {
|
||||
private War3ID id;
|
||||
private int activeForAutocast = 0;
|
||||
private int heroLevel = 1;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.activeForAutocast = stream.readInt();
|
||||
this.heroLevel = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.activeForAutocast);
|
||||
stream.writeInt(this.heroLevel);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomUnit {
|
||||
private War3ID id;
|
||||
private int chance;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.chance = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.chance);
|
||||
}
|
||||
}
|
428
core/src/com/etheller/warsmash/parsers/w3x/unitsdoo/Unit.java
Normal file
428
core/src/com/etheller/warsmash/parsers/w3x/unitsdoo/Unit.java
Normal file
@ -0,0 +1,428 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class Unit {
|
||||
private War3ID id;
|
||||
private int variation;
|
||||
private final float[] location = new float[3];
|
||||
private float angle;
|
||||
private final float[] scale = new float[3];
|
||||
private short flags;
|
||||
private int player;
|
||||
private int unknown;
|
||||
private int hitpoints = -1;
|
||||
private int mana = -1;
|
||||
/**
|
||||
* @since 8
|
||||
*/
|
||||
private int droppedItemTable = 0;
|
||||
private final List<DroppedItemSet> droppedItemSets = new ArrayList<>();
|
||||
private int goldAmount;
|
||||
private float targetAcquisition;
|
||||
private int heroLevel;
|
||||
/**
|
||||
* @since 8
|
||||
*/
|
||||
private int heroStrength;
|
||||
/**
|
||||
* @since 8
|
||||
*/
|
||||
private int heroAgility;
|
||||
/**
|
||||
* @since 8
|
||||
*/
|
||||
private int heroIntelligence;
|
||||
private final List<InventoryItem> itemsInInventory = new ArrayList<>();
|
||||
private final List<ModifiedAbility> modifiedAbilities = new ArrayList<>();
|
||||
private int randomFlag;
|
||||
private final short[] level = new short[3];
|
||||
private short itemClass;
|
||||
private long unitGroup;
|
||||
private long positionInGroup;
|
||||
private final List<RandomUnit> randomUnitTables = new ArrayList<>();
|
||||
private int customTeamColor;
|
||||
private int waygate;
|
||||
private int creationNumber;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream, final int version) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.variation = stream.readInt();
|
||||
ParseUtils.readFloatArray(stream, this.location);
|
||||
this.angle = stream.readFloat();
|
||||
ParseUtils.readFloatArray(stream, this.scale);
|
||||
this.flags = ParseUtils.readUInt8(stream);
|
||||
this.player = stream.readInt();
|
||||
this.unknown = ParseUtils.readUInt16(stream);
|
||||
this.hitpoints = stream.readInt();
|
||||
this.mana = stream.readInt();
|
||||
|
||||
if (version > 7) {
|
||||
this.droppedItemTable = stream.readInt();
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final DroppedItemSet set = new DroppedItemSet();
|
||||
|
||||
set.load(stream);
|
||||
|
||||
this.droppedItemSets.add(set);
|
||||
}
|
||||
|
||||
this.goldAmount = stream.readInt();
|
||||
this.targetAcquisition = stream.readFloat();
|
||||
this.heroLevel = stream.readInt();
|
||||
|
||||
if (version > 7) {
|
||||
this.heroStrength = stream.readInt();
|
||||
this.heroAgility = stream.readInt();
|
||||
this.heroIntelligence = stream.readInt();
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final InventoryItem item = new InventoryItem();
|
||||
|
||||
item.load(stream);
|
||||
|
||||
this.itemsInInventory.add(item);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final ModifiedAbility modifiedAbility = new ModifiedAbility();
|
||||
|
||||
modifiedAbility.load(stream);
|
||||
|
||||
this.modifiedAbilities.add(modifiedAbility);
|
||||
}
|
||||
|
||||
this.randomFlag = stream.readInt();
|
||||
|
||||
if (this.randomFlag == 0) {
|
||||
ParseUtils.readUInt8Array(stream, this.level);
|
||||
this.itemClass = ParseUtils.readUInt8(stream);
|
||||
}
|
||||
else if (this.randomFlag == 1) {
|
||||
this.unitGroup = ParseUtils.readUInt32(stream);
|
||||
this.positionInGroup = ParseUtils.readUInt32(stream);
|
||||
}
|
||||
else if (this.randomFlag == 2) {
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final RandomUnit randomUnit = new RandomUnit();
|
||||
|
||||
randomUnit.load(stream);
|
||||
|
||||
this.randomUnitTables.add(randomUnit);
|
||||
}
|
||||
}
|
||||
|
||||
this.customTeamColor = stream.readInt();
|
||||
this.waygate = stream.readInt();
|
||||
this.creationNumber = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream, final int version) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.variation);
|
||||
ParseUtils.writeFloatArray(stream, this.location);
|
||||
stream.writeFloat(this.angle);
|
||||
ParseUtils.writeFloatArray(stream, this.scale);
|
||||
ParseUtils.writeUInt8(stream, this.flags);
|
||||
stream.writeInt(this.player);
|
||||
ParseUtils.writeUInt16(stream, this.unknown);
|
||||
stream.writeInt(this.hitpoints);
|
||||
stream.writeInt(this.mana);
|
||||
|
||||
if (version > 7) {
|
||||
stream.writeInt(this.droppedItemTable);
|
||||
}
|
||||
|
||||
stream.writeInt(this.droppedItemSets.size());
|
||||
|
||||
for (final DroppedItemSet droppedItemSet : this.droppedItemSets) {
|
||||
droppedItemSet.save(stream);
|
||||
}
|
||||
|
||||
stream.writeInt(this.goldAmount);
|
||||
stream.writeFloat(this.targetAcquisition);
|
||||
stream.writeInt(this.heroLevel);
|
||||
|
||||
if (version > 7) {
|
||||
stream.writeInt(this.heroStrength);
|
||||
stream.writeInt(this.heroAgility);
|
||||
stream.writeInt(this.heroIntelligence);
|
||||
}
|
||||
|
||||
stream.writeInt(this.itemsInInventory.size());
|
||||
|
||||
for (final InventoryItem itemInInventory : this.itemsInInventory) {
|
||||
itemInInventory.save(stream);
|
||||
}
|
||||
|
||||
stream.writeInt(this.modifiedAbilities.size());
|
||||
|
||||
for (final ModifiedAbility modifiedAbility : this.modifiedAbilities) {
|
||||
modifiedAbility.save(stream);
|
||||
}
|
||||
|
||||
stream.writeInt(this.randomFlag);
|
||||
|
||||
if (this.randomFlag == 0) {
|
||||
ParseUtils.writeUInt8Array(stream, this.level);
|
||||
ParseUtils.writeUInt8(stream, this.itemClass);
|
||||
}
|
||||
else if (this.randomFlag == 1) {
|
||||
ParseUtils.writeUInt32(stream, this.unitGroup);
|
||||
ParseUtils.writeUInt32(stream, this.positionInGroup);
|
||||
}
|
||||
else if (this.randomFlag == 2) {
|
||||
stream.writeInt(this.randomUnitTables.size());
|
||||
|
||||
for (final RandomUnit randomUnitTable : this.randomUnitTables) {
|
||||
randomUnitTable.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
stream.writeInt(this.customTeamColor);
|
||||
stream.writeInt(this.waygate);
|
||||
stream.writeInt(this.creationNumber);
|
||||
}
|
||||
|
||||
public int getByteLength(final int version) {
|
||||
int size = 91;
|
||||
|
||||
if (version > 7) {
|
||||
size += 16;
|
||||
}
|
||||
|
||||
for (final DroppedItemSet droppedItemSet : this.droppedItemSets) {
|
||||
size += droppedItemSet.getByteLength();
|
||||
}
|
||||
|
||||
size += this.itemsInInventory.size() * 8;
|
||||
|
||||
size += this.modifiedAbilities.size() * 12;
|
||||
|
||||
if (this.randomFlag == 0) {
|
||||
size += 4;
|
||||
}
|
||||
else if (this.randomFlag == 1) {
|
||||
size += 8;
|
||||
}
|
||||
else if (this.randomFlag == 2) {
|
||||
size += 4 + (this.randomUnitTables.size() * 8);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getVariation() {
|
||||
return this.variation;
|
||||
}
|
||||
|
||||
public float[] getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public float getAngle() {
|
||||
return this.angle;
|
||||
}
|
||||
|
||||
public float[] getScale() {
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
public short getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public int getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public int getUnknown() {
|
||||
return this.unknown;
|
||||
}
|
||||
|
||||
public int getHitpoints() {
|
||||
return this.hitpoints;
|
||||
}
|
||||
|
||||
public int getMana() {
|
||||
return this.mana;
|
||||
}
|
||||
|
||||
public int getDroppedItemTable() {
|
||||
return this.droppedItemTable;
|
||||
}
|
||||
|
||||
public List<DroppedItemSet> getDroppedItemSets() {
|
||||
return this.droppedItemSets;
|
||||
}
|
||||
|
||||
public int getGoldAmount() {
|
||||
return this.goldAmount;
|
||||
}
|
||||
|
||||
public float getTargetAcquisition() {
|
||||
return this.targetAcquisition;
|
||||
}
|
||||
|
||||
public int getHeroLevel() {
|
||||
return this.heroLevel;
|
||||
}
|
||||
|
||||
public int getHeroStrength() {
|
||||
return this.heroStrength;
|
||||
}
|
||||
|
||||
public int getHeroAgility() {
|
||||
return this.heroAgility;
|
||||
}
|
||||
|
||||
public int getHeroIntelligence() {
|
||||
return this.heroIntelligence;
|
||||
}
|
||||
|
||||
public List<InventoryItem> getItemsInInventory() {
|
||||
return this.itemsInInventory;
|
||||
}
|
||||
|
||||
public List<ModifiedAbility> getModifiedAbilities() {
|
||||
return this.modifiedAbilities;
|
||||
}
|
||||
|
||||
public int getRandomFlag() {
|
||||
return this.randomFlag;
|
||||
}
|
||||
|
||||
public short[] getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public short getItemClass() {
|
||||
return this.itemClass;
|
||||
}
|
||||
|
||||
public long getUnitGroup() {
|
||||
return this.unitGroup;
|
||||
}
|
||||
|
||||
public long getPositionInGroup() {
|
||||
return this.positionInGroup;
|
||||
}
|
||||
|
||||
public List<RandomUnit> getRandomUnitTables() {
|
||||
return this.randomUnitTables;
|
||||
}
|
||||
|
||||
public int getCustomTeamColor() {
|
||||
return this.customTeamColor;
|
||||
}
|
||||
|
||||
public int getWaygate() {
|
||||
return this.waygate;
|
||||
}
|
||||
|
||||
public int getCreationNumber() {
|
||||
return this.creationNumber;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setVariation(final int variation) {
|
||||
this.variation = variation;
|
||||
}
|
||||
|
||||
public void setAngle(final float angle) {
|
||||
this.angle = angle;
|
||||
}
|
||||
|
||||
public void setFlags(final short flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public void setPlayer(final int player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void setUnknown(final int unknown) {
|
||||
this.unknown = unknown;
|
||||
}
|
||||
|
||||
public void setHitpoints(final int hitpoints) {
|
||||
this.hitpoints = hitpoints;
|
||||
}
|
||||
|
||||
public void setMana(final int mana) {
|
||||
this.mana = mana;
|
||||
}
|
||||
|
||||
public void setDroppedItemTable(final int droppedItemTable) {
|
||||
this.droppedItemTable = droppedItemTable;
|
||||
}
|
||||
|
||||
public void setGoldAmount(final int goldAmount) {
|
||||
this.goldAmount = goldAmount;
|
||||
}
|
||||
|
||||
public void setTargetAcquisition(final float targetAcquisition) {
|
||||
this.targetAcquisition = targetAcquisition;
|
||||
}
|
||||
|
||||
public void setHeroLevel(final int heroLevel) {
|
||||
this.heroLevel = heroLevel;
|
||||
}
|
||||
|
||||
public void setHeroStrength(final int heroStrength) {
|
||||
this.heroStrength = heroStrength;
|
||||
}
|
||||
|
||||
public void setHeroAgility(final int heroAgility) {
|
||||
this.heroAgility = heroAgility;
|
||||
}
|
||||
|
||||
public void setHeroIntelligence(final int heroIntelligence) {
|
||||
this.heroIntelligence = heroIntelligence;
|
||||
}
|
||||
|
||||
public void setRandomFlag(final int randomFlag) {
|
||||
this.randomFlag = randomFlag;
|
||||
}
|
||||
|
||||
public void setItemClass(final short itemClass) {
|
||||
this.itemClass = itemClass;
|
||||
}
|
||||
|
||||
public void setUnitGroup(final long unitGroup) {
|
||||
this.unitGroup = unitGroup;
|
||||
}
|
||||
|
||||
public void setPositionInGroup(final long positionInGroup) {
|
||||
this.positionInGroup = positionInGroup;
|
||||
}
|
||||
|
||||
public void setCustomTeamColor(final int customTeamColor) {
|
||||
this.customTeamColor = customTeamColor;
|
||||
}
|
||||
|
||||
public void setWaygate(final int waygate) {
|
||||
this.waygate = waygate;
|
||||
}
|
||||
|
||||
public void setCreationNumber(final int creationNumber) {
|
||||
this.creationNumber = creationNumber;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.etheller.warsmash.parsers.w3x.unitsdoo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class War3MapUnitsDoo {
|
||||
private static final War3ID MAGIC_NUMBER = War3ID.fromString("W3do");
|
||||
private int version = 8;
|
||||
private long unknown = 11;
|
||||
private final List<Unit> units = new ArrayList<>();
|
||||
|
||||
public War3MapUnitsDoo(final LittleEndianDataInputStream stream) throws IOException {
|
||||
if (stream != null) {
|
||||
this.load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
final War3ID firstId = ParseUtils.readWar3ID(stream);
|
||||
if (!MAGIC_NUMBER.equals(firstId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.version = stream.readInt();
|
||||
this.unknown = ParseUtils.readUInt32(stream);
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final Unit unit = new Unit();
|
||||
|
||||
unit.load(stream, this.version);
|
||||
|
||||
this.units.add(unit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, MAGIC_NUMBER);
|
||||
stream.writeInt(this.version);
|
||||
ParseUtils.writeUInt32(stream, this.unknown);
|
||||
stream.writeInt(this.units.size());
|
||||
|
||||
for (final Unit unit : this.units) {
|
||||
unit.save(stream, this.version);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
int size = 16;
|
||||
|
||||
for (final Unit unit : this.units) {
|
||||
size += unit.getByteLength(this.version);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public List<Unit> getUnits() {
|
||||
return this.units;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(final int version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
110
core/src/com/etheller/warsmash/parsers/w3x/w3e/Corner.java
Normal file
110
core/src/com/etheller/warsmash/parsers/w3x/w3e/Corner.java
Normal file
@ -0,0 +1,110 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3e;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* A tile corner.
|
||||
*/
|
||||
public class Corner {
|
||||
private int groundHeight;
|
||||
private int waterHeight;
|
||||
private int mapEdge;
|
||||
private int ramp;
|
||||
private int blight;
|
||||
private int water;
|
||||
private int boundary;
|
||||
private int groundTexture;
|
||||
private int cliffVariation;
|
||||
private int groundVariation;
|
||||
private int cliffTexture;
|
||||
private int layerHeight;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.groundHeight = (stream.readShort() - 8192) / 512;
|
||||
|
||||
final short waterAndEdge = stream.readShort();
|
||||
this.waterHeight = ((waterAndEdge & 0x3FFF) - 8192) / 512;
|
||||
this.mapEdge = waterAndEdge & 0x4000;
|
||||
|
||||
final short textureAndFlags = ParseUtils.readUInt8(stream);
|
||||
|
||||
this.ramp = textureAndFlags & 0b00010000;
|
||||
this.blight = textureAndFlags & 0b00100000;
|
||||
this.water = textureAndFlags & 0b01000000;
|
||||
this.boundary = textureAndFlags & 0b10000000;
|
||||
|
||||
this.groundTexture = textureAndFlags & 0b00001111;
|
||||
|
||||
final short variation = ParseUtils.readUInt8(stream);
|
||||
|
||||
this.cliffVariation = (variation & 0b11100000) >>> 5;
|
||||
this.groundVariation = variation & 0b00011111;
|
||||
|
||||
final short cliffTextureAndLayer = ParseUtils.readUInt8(stream);
|
||||
|
||||
this.cliffTexture = (cliffTextureAndLayer & 0b11110000) >>> 4;
|
||||
this.layerHeight = cliffTextureAndLayer & 0b00001111;
|
||||
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeShort((this.groundHeight * 512) + 8192);
|
||||
stream.writeShort((this.waterHeight + 8192 + this.mapEdge) << 14);
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.ramp << 4) | (this.blight << 5) | (this.water << 6)
|
||||
| (this.boundary << 7) | this.groundTexture));
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.cliffVariation << 5) | this.groundVariation));
|
||||
ParseUtils.writeUInt8(stream, (short) ((this.cliffTexture << 4) + this.layerHeight));
|
||||
}
|
||||
|
||||
public int getGroundHeight() {
|
||||
return this.groundHeight;
|
||||
}
|
||||
|
||||
public int getWaterHeight() {
|
||||
return this.waterHeight;
|
||||
}
|
||||
|
||||
public int getMapEdge() {
|
||||
return this.mapEdge;
|
||||
}
|
||||
|
||||
public int getRamp() {
|
||||
return this.ramp;
|
||||
}
|
||||
|
||||
public int getBlight() {
|
||||
return this.blight;
|
||||
}
|
||||
|
||||
public int getWater() {
|
||||
return this.water;
|
||||
}
|
||||
|
||||
public int getBoundary() {
|
||||
return this.boundary;
|
||||
}
|
||||
|
||||
public int getGroundTexture() {
|
||||
return this.groundTexture;
|
||||
}
|
||||
|
||||
public int getCliffVariation() {
|
||||
return this.cliffVariation;
|
||||
}
|
||||
|
||||
public int getGroundVariation() {
|
||||
return this.groundVariation;
|
||||
}
|
||||
|
||||
public int getCliffTexture() {
|
||||
return this.cliffTexture;
|
||||
}
|
||||
|
||||
public int getLayerHeight() {
|
||||
return this.layerHeight;
|
||||
}
|
||||
}
|
148
core/src/com/etheller/warsmash/parsers/w3x/w3e/War3MapW3e.java
Normal file
148
core/src/com/etheller/warsmash/parsers/w3x/w3e/War3MapW3e.java
Normal file
@ -0,0 +1,148 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3e;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* war3map.w3e - the environment file.
|
||||
*/
|
||||
public class War3MapW3e {
|
||||
private static final War3ID MAGIC_NUMBER = War3ID.fromString("W3E!");
|
||||
private int version;
|
||||
private char tileset = 'A';
|
||||
private int hasCustomTileset;
|
||||
private final List<War3ID> groundTiles = new ArrayList<>();
|
||||
private final List<War3ID> cliffTiles = new ArrayList<>();
|
||||
private final int[] mapSize = new int[2];
|
||||
private final float[] centerOffset = new float[2];
|
||||
private Corner[][] corners;
|
||||
|
||||
public War3MapW3e(final LittleEndianDataInputStream stream) throws IOException {
|
||||
if (stream != null) {
|
||||
this.load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
final War3ID firstId = ParseUtils.readWar3ID(stream);
|
||||
if (!MAGIC_NUMBER.equals(firstId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.version = stream.readInt();
|
||||
this.tileset = (char) stream.read();
|
||||
this.hasCustomTileset = stream.readInt();
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
this.groundTiles.add(ParseUtils.readWar3ID(stream));
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
this.cliffTiles.add(ParseUtils.readWar3ID(stream));
|
||||
}
|
||||
|
||||
ParseUtils.readInt32Array(stream, this.mapSize);
|
||||
ParseUtils.readFloatArray(stream, this.centerOffset);
|
||||
|
||||
this.corners = new Corner[this.mapSize[1]][];
|
||||
for (int row = 0, rows = this.mapSize[1]; row < rows; row++) {
|
||||
this.corners[row] = new Corner[this.mapSize[0]];
|
||||
|
||||
for (int column = 0, columns = this.mapSize[0]; column < columns; column++) {
|
||||
final Corner corner = new Corner();
|
||||
|
||||
corner.load(stream);
|
||||
|
||||
this.corners[row][column] = corner;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, MAGIC_NUMBER);
|
||||
stream.writeInt(this.version);
|
||||
stream.write(this.tileset);
|
||||
stream.writeInt(this.hasCustomTileset);
|
||||
ParseUtils.writeUInt32(stream, this.groundTiles.size());
|
||||
|
||||
for (final War3ID groundTile : this.groundTiles) {
|
||||
ParseUtils.writeWar3ID(stream, groundTile);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.cliffTiles.size());
|
||||
|
||||
for (final War3ID cliffTile : this.cliffTiles) {
|
||||
ParseUtils.writeWar3ID(stream, cliffTile);
|
||||
}
|
||||
|
||||
ParseUtils.writeInt32Array(stream, this.mapSize);
|
||||
ParseUtils.writeFloatArray(stream, this.centerOffset);
|
||||
|
||||
for (final Corner[] row : this.corners) {
|
||||
for (final Corner corner : row) {
|
||||
corner.save(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 37 + (this.groundTiles.size() * 4) + (this.cliffTiles.size() * 4)
|
||||
+ (this.mapSize[0] * this.mapSize[1] * 7);
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public char getTileset() {
|
||||
return this.tileset;
|
||||
}
|
||||
|
||||
public int getHasCustomTileset() {
|
||||
return this.hasCustomTileset;
|
||||
}
|
||||
|
||||
public List<War3ID> getGroundTiles() {
|
||||
return this.groundTiles;
|
||||
}
|
||||
|
||||
public List<War3ID> getCliffTiles() {
|
||||
return this.cliffTiles;
|
||||
}
|
||||
|
||||
public int[] getMapSize() {
|
||||
return this.mapSize;
|
||||
}
|
||||
|
||||
public float[] getCenterOffset() {
|
||||
return this.centerOffset;
|
||||
}
|
||||
|
||||
public Corner[][] getCorners() {
|
||||
return this.corners;
|
||||
}
|
||||
|
||||
public void setVersion(final int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setTileset(final char tileset) {
|
||||
this.tileset = tileset;
|
||||
}
|
||||
|
||||
public void setHasCustomTileset(final int hasCustomTileset) {
|
||||
this.hasCustomTileset = hasCustomTileset;
|
||||
}
|
||||
|
||||
public void setCorners(final Corner[][] corners) {
|
||||
this.corners = corners;
|
||||
}
|
||||
}
|
29
core/src/com/etheller/warsmash/parsers/w3x/w3i/Force.java
Normal file
29
core/src/com/etheller/warsmash/parsers/w3x/w3i/Force.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class Force {
|
||||
private long flags;
|
||||
private long playerMasks;
|
||||
private String name;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.flags = ParseUtils.readUInt32(stream);
|
||||
this.playerMasks = ParseUtils.readUInt32(stream);
|
||||
this.name = ParseUtils.readUntilNull(stream);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.flags);
|
||||
ParseUtils.writeUInt32(stream, this.playerMasks);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.name);
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 9 + this.name.length();
|
||||
}
|
||||
}
|
48
core/src/com/etheller/warsmash/parsers/w3x/w3i/Player.java
Normal file
48
core/src/com/etheller/warsmash/parsers/w3x/w3i/Player.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* A player.
|
||||
*/
|
||||
public class Player {
|
||||
private War3ID id;
|
||||
private int type;
|
||||
private int race;
|
||||
private int isFixedStartPosition;
|
||||
private String name;
|
||||
private final float[] startLocation = new float[2];
|
||||
private long allyLowPriorities;
|
||||
private long allyHighPriorities;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.type = stream.readInt();
|
||||
this.race = stream.readInt();
|
||||
this.isFixedStartPosition = stream.readInt();
|
||||
this.name = ParseUtils.readUntilNull(stream);
|
||||
ParseUtils.readFloatArray(stream, this.startLocation);
|
||||
this.allyLowPriorities = ParseUtils.readUInt32(stream);
|
||||
this.allyHighPriorities = ParseUtils.readUInt32(stream);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.type);
|
||||
stream.writeInt(this.race);
|
||||
stream.writeInt(this.isFixedStartPosition);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.name);
|
||||
ParseUtils.writeFloatArray(stream, this.startLocation);
|
||||
ParseUtils.writeUInt32(stream, this.allyLowPriorities);
|
||||
ParseUtils.writeUInt32(stream, this.allyHighPriorities);
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 33 + this.name.length();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomItem {
|
||||
private int chance;
|
||||
private War3ID id;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.chance = stream.readInt();
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeInt(this.chance);
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
}
|
||||
|
||||
public int getChance() {
|
||||
return this.chance;
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setChance(final int chance) {
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomItemSet {
|
||||
private final List<RandomItem> items = new ArrayList<>();
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final RandomItem item = new RandomItem();
|
||||
|
||||
item.load(stream);
|
||||
|
||||
this.items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.items.size());
|
||||
|
||||
for (final RandomItem item : this.items) {
|
||||
item.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 4 + (this.items.size() * 8);
|
||||
}
|
||||
|
||||
public List<RandomItem> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomItemTable {
|
||||
private War3ID id;
|
||||
private String name;
|
||||
private final List<RandomItemSet> sets = new ArrayList<>();
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.name = ParseUtils.readUntilNull(stream);
|
||||
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final RandomItemSet set = new RandomItemSet();
|
||||
|
||||
set.load(stream);
|
||||
|
||||
this.sets.add(set);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.name);
|
||||
ParseUtils.writeUInt32(stream, this.sets.size());
|
||||
|
||||
for (final RandomItemSet set : this.sets) {
|
||||
set.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
int size = 9 + this.name.length();
|
||||
|
||||
for (final RandomItemSet set : this.sets) {
|
||||
size += set.getByteLength();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public List<RandomItemSet> getSets() {
|
||||
return this.sets;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomUnit {
|
||||
private int chance;
|
||||
private final List<War3ID> ids = new ArrayList<>();
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream, final int positions) throws IOException {
|
||||
this.chance = stream.readInt();
|
||||
|
||||
for (int i = 0; i < positions; i++) {
|
||||
this.ids.add(ParseUtils.readWar3ID(stream));
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeInt(this.chance);
|
||||
|
||||
for (final War3ID id : this.ids) {
|
||||
ParseUtils.writeWar3ID(stream, id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class RandomUnitTable {
|
||||
private int id;
|
||||
private String name;
|
||||
private int positions;
|
||||
private int[] columnTypes;
|
||||
private List<RandomUnit> units;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.id = stream.readInt(); // TODO is this a War3ID?
|
||||
this.name = ParseUtils.readUntilNull(stream);
|
||||
this.positions = stream.readInt();
|
||||
this.columnTypes = ParseUtils.readInt32Array(stream, this.positions);
|
||||
|
||||
for (long i = 0, l = ParseUtils.readUInt32(stream); i < l; i++) {
|
||||
final RandomUnit unit = new RandomUnit();
|
||||
|
||||
unit.load(stream, this.positions);
|
||||
|
||||
this.units.add(unit);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeInt(this.id);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.name);
|
||||
stream.writeInt(this.positions);
|
||||
ParseUtils.writeInt32Array(stream, this.columnTypes);
|
||||
ParseUtils.writeUInt32(stream, this.units.size());
|
||||
|
||||
for (final RandomUnit unit : this.units) {
|
||||
unit.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return 13 + this.name.length() + (this.columnTypes.length * 4)
|
||||
+ (this.units.size() * (4 + (4 * this.positions)));
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class TechAvailabilityChange {
|
||||
private long playerFlags;
|
||||
private War3ID id;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.playerFlags = ParseUtils.readUInt32(stream);
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.playerFlags);
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
public class UpgradeAvailabilityChange {
|
||||
private long playerFlags;
|
||||
private War3ID id;
|
||||
private int levelAffected;
|
||||
private int availability;
|
||||
|
||||
public void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.playerFlags = ParseUtils.readUInt32(stream);
|
||||
this.id = ParseUtils.readWar3ID(stream);
|
||||
this.levelAffected = stream.readInt();
|
||||
this.availability = stream.readInt();
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
ParseUtils.writeUInt32(stream, this.playerFlags);
|
||||
ParseUtils.writeWar3ID(stream, this.id);
|
||||
stream.writeInt(this.levelAffected);
|
||||
stream.writeInt(this.availability);
|
||||
}
|
||||
|
||||
}
|
440
core/src/com/etheller/warsmash/parsers/w3x/w3i/War3MapW3i.java
Normal file
440
core/src/com/etheller/warsmash/parsers/w3x/w3i/War3MapW3i.java
Normal file
@ -0,0 +1,440 @@
|
||||
package com.etheller.warsmash.parsers.w3x.w3i;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* war3map.w3i - the general map information file.
|
||||
*/
|
||||
public class War3MapW3i {
|
||||
private int version;
|
||||
private int saves;
|
||||
private int editorVersion;
|
||||
private final short[] unknown1 = new short[16];
|
||||
private String name;
|
||||
private String author;
|
||||
private String description;
|
||||
private String recommendedPlayers;
|
||||
private final float[] cameraBounds = new float[8];
|
||||
private final int[] cameraBoundsComplements = new int[4];
|
||||
private final int[] playableSize = new int[2];
|
||||
private long flags;
|
||||
private char tileset = 'A';
|
||||
private int campaignBackground;
|
||||
private String loadingScreenModel;
|
||||
private String loadingScreenText;
|
||||
private String loadingScreenTitle;
|
||||
private String loadingScreenSubtitle;
|
||||
private int loadingScreen;
|
||||
private String prologueScreenModel;
|
||||
private String prologueScreenText;
|
||||
private String prologueScreenTitle;
|
||||
private String prologueScreenSubtitle;
|
||||
private int useTerrainFog;
|
||||
private final float[] fogHeight = new float[2];
|
||||
private float fogDensity;
|
||||
private final short[] fogColor = new short[4];
|
||||
private int globalWeather;
|
||||
private String soundEnvironment;
|
||||
private char lightEnvironmentTileset;
|
||||
private final short[] waterVertexColor = new short[4];
|
||||
private final short[] unknown2 = new short[4];
|
||||
private final List<Player> players = new ArrayList<>();
|
||||
private final List<Force> forces = new ArrayList<>();
|
||||
private final List<UpgradeAvailabilityChange> upgradeAvailabilityChanges = new ArrayList<>();
|
||||
private final List<TechAvailabilityChange> techAvailabilityChanges = new ArrayList<>();
|
||||
private final List<RandomUnitTable> randomUnitTables = new ArrayList<>();
|
||||
private final List<RandomItemTable> randomItemTables = new ArrayList<>();
|
||||
|
||||
public War3MapW3i(final LittleEndianDataInputStream stream) throws IOException {
|
||||
if (stream != null) {
|
||||
load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private void load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
this.version = stream.readInt();
|
||||
this.saves = stream.readInt();
|
||||
this.editorVersion = stream.readInt();
|
||||
|
||||
if (this.version > 27) {
|
||||
ParseUtils.readUInt8Array(stream, this.unknown1);
|
||||
}
|
||||
|
||||
this.name = ParseUtils.readUntilNull(stream);
|
||||
this.author = ParseUtils.readUntilNull(stream);
|
||||
this.description = ParseUtils.readUntilNull(stream);
|
||||
this.recommendedPlayers = ParseUtils.readUntilNull(stream);
|
||||
ParseUtils.readFloatArray(stream, this.cameraBounds);
|
||||
ParseUtils.readInt32Array(stream, this.cameraBoundsComplements);
|
||||
ParseUtils.readInt32Array(stream, this.playableSize);
|
||||
this.flags = ParseUtils.readUInt32(stream);
|
||||
this.tileset = (char) stream.read();
|
||||
this.campaignBackground = stream.readInt();
|
||||
|
||||
if (this.version > 24) {
|
||||
this.loadingScreenModel = ParseUtils.readUntilNull(stream);
|
||||
}
|
||||
|
||||
this.loadingScreenText = ParseUtils.readUntilNull(stream);
|
||||
this.loadingScreenTitle = ParseUtils.readUntilNull(stream);
|
||||
this.loadingScreenSubtitle = ParseUtils.readUntilNull(stream);
|
||||
this.loadingScreen = stream.readInt();
|
||||
|
||||
if (this.version > 24) {
|
||||
this.prologueScreenModel = ParseUtils.readUntilNull(stream);
|
||||
}
|
||||
|
||||
this.prologueScreenText = ParseUtils.readUntilNull(stream);
|
||||
this.prologueScreenTitle = ParseUtils.readUntilNull(stream);
|
||||
this.prologueScreenSubtitle = ParseUtils.readUntilNull(stream);
|
||||
|
||||
if (this.version > 24) {
|
||||
this.useTerrainFog = stream.readInt();
|
||||
ParseUtils.readFloatArray(stream, this.fogHeight);
|
||||
this.fogDensity = stream.readFloat();
|
||||
ParseUtils.readUInt8Array(stream, this.fogColor);
|
||||
this.globalWeather = stream.readInt(); // TODO probably war3id, right?
|
||||
this.soundEnvironment = ParseUtils.readUntilNull(stream);
|
||||
this.lightEnvironmentTileset = (char) stream.read();
|
||||
ParseUtils.readUInt8Array(stream, this.waterVertexColor);
|
||||
}
|
||||
|
||||
if (this.version > 27) {
|
||||
ParseUtils.readUInt8Array(stream, this.unknown2);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final Player player = new Player();
|
||||
|
||||
player.load(stream);
|
||||
|
||||
this.players.add(player);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final Force force = new Force();
|
||||
|
||||
force.load(stream);
|
||||
|
||||
this.forces.add(force);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final UpgradeAvailabilityChange upgradeAvailabilityChange = new UpgradeAvailabilityChange();
|
||||
|
||||
upgradeAvailabilityChange.load(stream);
|
||||
|
||||
this.upgradeAvailabilityChanges.add(upgradeAvailabilityChange);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final TechAvailabilityChange techAvailabilityChange = new TechAvailabilityChange();
|
||||
|
||||
techAvailabilityChange.load(stream);
|
||||
|
||||
this.techAvailabilityChanges.add(techAvailabilityChange);
|
||||
}
|
||||
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final RandomUnitTable randomUnitTable = new RandomUnitTable();
|
||||
|
||||
randomUnitTable.load(stream);
|
||||
|
||||
this.randomUnitTables.add(randomUnitTable);
|
||||
}
|
||||
|
||||
if (this.version > 24) {
|
||||
for (int i = 0, l = stream.readInt(); i < l; i++) {
|
||||
final RandomItemTable randomItemTable = new RandomItemTable();
|
||||
|
||||
randomItemTable.load(stream);
|
||||
|
||||
this.randomItemTables.add(randomItemTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final LittleEndianDataOutputStream stream) throws IOException {
|
||||
stream.writeInt(this.version);
|
||||
stream.writeInt(this.saves);
|
||||
stream.writeInt(this.editorVersion);
|
||||
|
||||
if (this.version > 27) {
|
||||
ParseUtils.writeUInt8Array(stream, this.unknown1);
|
||||
}
|
||||
|
||||
ParseUtils.writeWithNullTerminator(stream, this.name);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.author);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.description);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.recommendedPlayers);
|
||||
ParseUtils.writeFloatArray(stream, this.cameraBounds);
|
||||
ParseUtils.writeInt32Array(stream, this.cameraBoundsComplements);
|
||||
ParseUtils.writeInt32Array(stream, this.playableSize);
|
||||
ParseUtils.writeUInt32(stream, this.flags);
|
||||
stream.write((byte) this.tileset);
|
||||
stream.writeInt(this.campaignBackground);
|
||||
|
||||
if (this.version > 24) {
|
||||
ParseUtils.writeWithNullTerminator(stream, this.loadingScreenModel);
|
||||
}
|
||||
|
||||
ParseUtils.writeWithNullTerminator(stream, this.loadingScreenText);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.loadingScreenTitle);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.loadingScreenSubtitle);
|
||||
stream.writeInt(this.loadingScreen);
|
||||
|
||||
if (this.version > 24) {
|
||||
ParseUtils.writeWithNullTerminator(stream, this.prologueScreenModel);
|
||||
}
|
||||
|
||||
ParseUtils.writeWithNullTerminator(stream, this.prologueScreenText);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.prologueScreenTitle);
|
||||
ParseUtils.writeWithNullTerminator(stream, this.prologueScreenSubtitle);
|
||||
|
||||
if (this.version > 24) {
|
||||
stream.writeInt(this.useTerrainFog);
|
||||
ParseUtils.writeFloatArray(stream, this.fogHeight);
|
||||
stream.writeFloat(this.fogDensity);
|
||||
ParseUtils.writeUInt8Array(stream, this.fogColor);
|
||||
stream.writeInt(this.globalWeather); // TODO War3ID???
|
||||
ParseUtils.writeWithNullTerminator(stream, this.soundEnvironment);
|
||||
stream.write((byte) this.lightEnvironmentTileset);
|
||||
ParseUtils.writeUInt8Array(stream, this.waterVertexColor);
|
||||
}
|
||||
|
||||
if (this.version > 27) {
|
||||
ParseUtils.writeUInt8Array(stream, this.unknown2);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.players.size());
|
||||
|
||||
for (final Player player : this.players) {
|
||||
player.save(stream);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.forces.size());
|
||||
|
||||
for (final Force force : this.forces) {
|
||||
force.save(stream);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.upgradeAvailabilityChanges.size());
|
||||
|
||||
for (final UpgradeAvailabilityChange change : this.upgradeAvailabilityChanges) {
|
||||
change.save(stream);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.techAvailabilityChanges.size());
|
||||
|
||||
for (final TechAvailabilityChange change : this.techAvailabilityChanges) {
|
||||
change.save(stream);
|
||||
}
|
||||
|
||||
ParseUtils.writeUInt32(stream, this.randomUnitTables.size());
|
||||
|
||||
for (final RandomUnitTable table : this.randomUnitTables) {
|
||||
table.save(stream);
|
||||
}
|
||||
|
||||
if (this.version > 24) {
|
||||
ParseUtils.writeUInt32(stream, this.randomItemTables.size());
|
||||
|
||||
for (final RandomItemTable table : this.randomItemTables) {
|
||||
table.save(stream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
int size = 111 + this.name.length() + this.author.length() + this.description.length()
|
||||
+ this.recommendedPlayers.length() + this.loadingScreenText.length() + this.loadingScreenTitle.length()
|
||||
+ this.loadingScreenSubtitle.length() + this.prologueScreenText.length()
|
||||
+ this.prologueScreenTitle.length() + this.prologueScreenSubtitle.length();
|
||||
|
||||
for (final Player player : this.players) {
|
||||
size += player.getByteLength();
|
||||
}
|
||||
|
||||
for (final Force force : this.forces) {
|
||||
size += force.getByteLength();
|
||||
}
|
||||
|
||||
size += this.upgradeAvailabilityChanges.size() * 16;
|
||||
|
||||
size += this.techAvailabilityChanges.size() * 8;
|
||||
|
||||
for (final RandomUnitTable table : this.randomUnitTables) {
|
||||
size += table.getByteLength();
|
||||
}
|
||||
|
||||
if (this.version > 24) {
|
||||
size += 36 + this.loadingScreenModel.length() + this.prologueScreenModel.length()
|
||||
+ this.soundEnvironment.length();
|
||||
|
||||
for (final RandomItemTable table : this.randomItemTables) {
|
||||
size += table.getByteLength();
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public int getSaves() {
|
||||
return this.saves;
|
||||
}
|
||||
|
||||
public int getEditorVersion() {
|
||||
return this.editorVersion;
|
||||
}
|
||||
|
||||
public short[] getUnknown1() {
|
||||
return this.unknown1;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return this.author;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public String getRecommendedPlayers() {
|
||||
return this.recommendedPlayers;
|
||||
}
|
||||
|
||||
public float[] getCameraBounds() {
|
||||
return this.cameraBounds;
|
||||
}
|
||||
|
||||
public int[] getCameraBoundsComplements() {
|
||||
return this.cameraBoundsComplements;
|
||||
}
|
||||
|
||||
public int[] getPlayableSize() {
|
||||
return this.playableSize;
|
||||
}
|
||||
|
||||
public long getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public char getTileset() {
|
||||
return this.tileset;
|
||||
}
|
||||
|
||||
public int getCampaignBackground() {
|
||||
return this.campaignBackground;
|
||||
}
|
||||
|
||||
public String getLoadingScreenModel() {
|
||||
return this.loadingScreenModel;
|
||||
}
|
||||
|
||||
public String getLoadingScreenText() {
|
||||
return this.loadingScreenText;
|
||||
}
|
||||
|
||||
public String getLoadingScreenTitle() {
|
||||
return this.loadingScreenTitle;
|
||||
}
|
||||
|
||||
public String getLoadingScreenSubtitle() {
|
||||
return this.loadingScreenSubtitle;
|
||||
}
|
||||
|
||||
public int getLoadingScreen() {
|
||||
return this.loadingScreen;
|
||||
}
|
||||
|
||||
public String getPrologueScreenModel() {
|
||||
return this.prologueScreenModel;
|
||||
}
|
||||
|
||||
public String getPrologueScreenText() {
|
||||
return this.prologueScreenText;
|
||||
}
|
||||
|
||||
public String getPrologueScreenTitle() {
|
||||
return this.prologueScreenTitle;
|
||||
}
|
||||
|
||||
public String getPrologueScreenSubtitle() {
|
||||
return this.prologueScreenSubtitle;
|
||||
}
|
||||
|
||||
public int getUseTerrainFog() {
|
||||
return this.useTerrainFog;
|
||||
}
|
||||
|
||||
public float[] getFogHeight() {
|
||||
return this.fogHeight;
|
||||
}
|
||||
|
||||
public float getFogDensity() {
|
||||
return this.fogDensity;
|
||||
}
|
||||
|
||||
public short[] getFogColor() {
|
||||
return this.fogColor;
|
||||
}
|
||||
|
||||
public int getGlobalWeather() {
|
||||
return this.globalWeather;
|
||||
}
|
||||
|
||||
public String getSoundEnvironment() {
|
||||
return this.soundEnvironment;
|
||||
}
|
||||
|
||||
public char getLightEnvironmentTileset() {
|
||||
return this.lightEnvironmentTileset;
|
||||
}
|
||||
|
||||
public short[] getWaterVertexColor() {
|
||||
return this.waterVertexColor;
|
||||
}
|
||||
|
||||
public short[] getUnknown2() {
|
||||
return this.unknown2;
|
||||
}
|
||||
|
||||
public List<Player> getPlayers() {
|
||||
return this.players;
|
||||
}
|
||||
|
||||
public List<Force> getForces() {
|
||||
return this.forces;
|
||||
}
|
||||
|
||||
public List<UpgradeAvailabilityChange> getUpgradeAvailabilityChanges() {
|
||||
return this.upgradeAvailabilityChanges;
|
||||
}
|
||||
|
||||
public List<TechAvailabilityChange> getTechAvailabilityChanges() {
|
||||
return this.techAvailabilityChanges;
|
||||
}
|
||||
|
||||
public List<RandomUnitTable> getRandomUnitTables() {
|
||||
return this.randomUnitTables;
|
||||
}
|
||||
|
||||
public List<RandomItemTable> getRandomItemTables() {
|
||||
return this.randomItemTables;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.etheller.warsmash.parsers.w3x.wpm;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
|
||||
public class War3MapWpm {
|
||||
private static final War3ID MAGIC_NUMBER = War3ID.fromString("MP3W");
|
||||
private int version;
|
||||
private final int[] size = new int[2];
|
||||
private short[] pathing;
|
||||
|
||||
public War3MapWpm(final LittleEndianDataInputStream stream) throws IOException {
|
||||
if (stream != null) {
|
||||
this.load(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean load(final LittleEndianDataInputStream stream) throws IOException {
|
||||
final War3ID firstId = ParseUtils.readWar3ID(stream);
|
||||
if (!MAGIC_NUMBER.equals(firstId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.version = stream.readInt();
|
||||
ParseUtils.readInt32Array(stream, this.size);
|
||||
this.pathing = ParseUtils.readUInt8Array(stream, this.size[0] * this.size[1]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public int[] getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public short[] getPathing() {
|
||||
return this.pathing;
|
||||
}
|
||||
|
||||
public void setVersion(final int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setPathing(final short[] pathing) {
|
||||
this.pathing = pathing;
|
||||
}
|
||||
|
||||
}
|
@ -339,6 +339,10 @@ public class StandardObjectData {
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public WorldEditStrings getWorldEditStrings() {
|
||||
return this.worldEditStrings;
|
||||
}
|
||||
|
||||
public static class WarcraftData implements ObjectData {
|
||||
WorldEditStrings worldEditStrings;
|
||||
List<DataTable> tables = new ArrayList<>();
|
||||
|
105
core/src/com/etheller/warsmash/units/custom/Change.java
Normal file
105
core/src/com/etheller/warsmash/units/custom/Change.java
Normal file
@ -0,0 +1,105 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public final class Change {
|
||||
private War3ID id;
|
||||
private int vartype, level, dataptr;
|
||||
private int longval;
|
||||
private float realval;
|
||||
private String strval;
|
||||
|
||||
private boolean boolval;
|
||||
private War3ID junkDNA;
|
||||
|
||||
public War3ID getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(final War3ID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getVartype() {
|
||||
return this.vartype;
|
||||
}
|
||||
|
||||
public void setVartype(final int vartype) {
|
||||
this.vartype = vartype;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public void setLevel(final int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getDataptr() {
|
||||
return this.dataptr;
|
||||
}
|
||||
|
||||
public void setDataptr(final int dataptr) {
|
||||
this.dataptr = dataptr;
|
||||
}
|
||||
|
||||
public int getLongval() {
|
||||
return this.longval;
|
||||
}
|
||||
|
||||
public void setLongval(final int longval) {
|
||||
this.longval = longval;
|
||||
}
|
||||
|
||||
public float getRealval() {
|
||||
return this.realval;
|
||||
}
|
||||
|
||||
public void setRealval(final float realval) {
|
||||
this.realval = realval;
|
||||
}
|
||||
|
||||
public String getStrval() {
|
||||
return this.strval;
|
||||
}
|
||||
|
||||
public void setStrval(final String strval) {
|
||||
this.strval = strval;
|
||||
}
|
||||
|
||||
public boolean isBoolval() {
|
||||
return this.boolval;
|
||||
}
|
||||
|
||||
public void setBoolval(final boolean boolval) {
|
||||
this.boolval = boolval;
|
||||
}
|
||||
|
||||
public void setJunkDNA(final War3ID junkDNA) {
|
||||
this.junkDNA = junkDNA;
|
||||
}
|
||||
|
||||
public War3ID getJunkDNA() {
|
||||
return this.junkDNA;
|
||||
}
|
||||
|
||||
public void copyFrom(final Change other) {
|
||||
this.id = other.id;
|
||||
this.level = other.level;
|
||||
this.dataptr = other.dataptr;
|
||||
this.vartype = other.vartype;
|
||||
this.longval = other.longval;
|
||||
this.realval = other.realval;
|
||||
this.strval = other.strval;
|
||||
this.boolval = other.boolval;
|
||||
this.junkDNA = other.junkDNA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Change clone() {
|
||||
final Change copy = new Change();
|
||||
copy.copyFrom(this);
|
||||
return copy;
|
||||
}
|
||||
}
|
55
core/src/com/etheller/warsmash/units/custom/ChangeMap.java
Normal file
55
core/src/com/etheller/warsmash/units/custom/ChangeMap.java
Normal file
@ -0,0 +1,55 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public final class ChangeMap implements Iterable<Map.Entry<War3ID, List<Change>>> {
|
||||
private final Map<War3ID, List<Change>> idToChanges = new LinkedHashMap<>();
|
||||
|
||||
public void add(final War3ID war3Id, final Change change) {
|
||||
List<Change> list = this.idToChanges.get(war3Id);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
this.idToChanges.put(war3Id, list);
|
||||
}
|
||||
list.add(change);
|
||||
}
|
||||
|
||||
public void add(final War3ID war3Id, final List<Change> changes) {
|
||||
for (final Change change : changes) {
|
||||
add(war3Id, change);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Change> get(final War3ID war3ID) {
|
||||
return this.idToChanges.get(war3ID);
|
||||
}
|
||||
|
||||
public void delete(final War3ID war3ID, final Change change) {
|
||||
if (this.idToChanges.containsKey(war3ID)) {
|
||||
final List<Change> changeList = this.idToChanges.get(war3ID);
|
||||
changeList.remove(change);
|
||||
if (changeList.isEmpty()) {
|
||||
this.idToChanges.remove(war3ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<War3ID, List<Change>>> iterator() {
|
||||
return this.idToChanges.entrySet().iterator();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.idToChanges.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.idToChanges.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public final class ObjectDataChangeEntry {
|
||||
private War3ID oldId;
|
||||
private War3ID newId;
|
||||
private final ChangeMap changes;
|
||||
|
||||
public ObjectDataChangeEntry(final War3ID oldId, final War3ID newId) {
|
||||
this.oldId = oldId;
|
||||
this.newId = newId;
|
||||
this.changes = new ChangeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectDataChangeEntry clone() {
|
||||
final ObjectDataChangeEntry objectDataChangeEntry = new ObjectDataChangeEntry(this.oldId, this.newId);
|
||||
for (final Map.Entry<War3ID, List<Change>> entry : this.changes) {
|
||||
objectDataChangeEntry.getChanges().add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return objectDataChangeEntry;
|
||||
}
|
||||
|
||||
public ChangeMap getChanges() {
|
||||
return this.changes;
|
||||
}
|
||||
|
||||
public War3ID getOldId() {
|
||||
return this.oldId;
|
||||
}
|
||||
|
||||
public void setOldId(final War3ID oldId) {
|
||||
this.oldId = oldId;
|
||||
}
|
||||
|
||||
public War3ID getNewId() {
|
||||
return this.newId;
|
||||
}
|
||||
|
||||
public void setNewId(final War3ID newId) {
|
||||
this.newId = newId;
|
||||
}
|
||||
}
|
89
core/src/com/etheller/warsmash/units/custom/ObjectMap.java
Normal file
89
core/src/com/etheller/warsmash/units/custom/ObjectMap.java
Normal file
@ -0,0 +1,89 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public final class ObjectMap implements Iterable<Map.Entry<War3ID, ObjectDataChangeEntry>> {
|
||||
private final Map<War3ID, ObjectDataChangeEntry> idToDataChangeEntry;
|
||||
private final Set<War3ID> lowerCaseKeySet;
|
||||
|
||||
public ObjectMap() {
|
||||
this.idToDataChangeEntry = new LinkedHashMap<>();
|
||||
this.lowerCaseKeySet = new HashSet<>();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.idToDataChangeEntry.clear();
|
||||
this.lowerCaseKeySet.clear();
|
||||
}
|
||||
|
||||
public ObjectDataChangeEntry remove(final War3ID key) {
|
||||
this.lowerCaseKeySet.remove(War3ID.fromString(key.toString().toLowerCase()));
|
||||
return this.idToDataChangeEntry.remove(key);
|
||||
}
|
||||
|
||||
public Set<War3ID> keySet() {
|
||||
return this.idToDataChangeEntry.keySet();
|
||||
}
|
||||
|
||||
public ObjectDataChangeEntry put(final War3ID key, final ObjectDataChangeEntry value) {
|
||||
this.lowerCaseKeySet.add(War3ID.fromString(key.toString().toLowerCase()));
|
||||
return this.idToDataChangeEntry.put(key, value);
|
||||
}
|
||||
|
||||
public Set<Map.Entry<War3ID, ObjectDataChangeEntry>> entrySet() {
|
||||
return this.idToDataChangeEntry.entrySet();
|
||||
}
|
||||
|
||||
public ObjectDataChangeEntry get(final War3ID key) {
|
||||
return this.idToDataChangeEntry.get(key);
|
||||
}
|
||||
|
||||
public boolean containsKey(final War3ID key) {
|
||||
return this.idToDataChangeEntry.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsKeyCaseInsensitive(final War3ID key) {
|
||||
return this.lowerCaseKeySet.contains(War3ID.fromString(key.toString().toLowerCase()));
|
||||
}
|
||||
|
||||
public boolean containsValue(final ObjectDataChangeEntry value) {
|
||||
return this.idToDataChangeEntry.containsValue(value);
|
||||
}
|
||||
|
||||
public Collection<ObjectDataChangeEntry> values() {
|
||||
return this.idToDataChangeEntry.values();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.idToDataChangeEntry.size();
|
||||
}
|
||||
|
||||
public void forEach(final BiConsumer<? super War3ID, ? super ObjectDataChangeEntry> forEach) {
|
||||
this.idToDataChangeEntry.forEach(forEach);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<War3ID, ObjectDataChangeEntry>> iterator() {
|
||||
return this.idToDataChangeEntry.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMap clone() {
|
||||
final ObjectMap clone = new ObjectMap();
|
||||
forEach(new BiConsumer<War3ID, ObjectDataChangeEntry>() {
|
||||
@Override
|
||||
public void accept(final War3ID key, final ObjectDataChangeEntry value) {
|
||||
clone.put(key, value);
|
||||
}
|
||||
});
|
||||
return clone;
|
||||
}
|
||||
}
|
5
core/src/com/etheller/warsmash/units/custom/WTS.java
Normal file
5
core/src/com/etheller/warsmash/units/custom/WTS.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
public interface WTS {
|
||||
String get(int key);
|
||||
}
|
94
core/src/com/etheller/warsmash/units/custom/WTSFile.java
Normal file
94
core/src/com/etheller/warsmash/units/custom/WTSFile.java
Normal file
@ -0,0 +1,94 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Deaod
|
||||
*
|
||||
*/
|
||||
public class WTSFile implements WTS {
|
||||
private final InputStream source;
|
||||
private final Map<Integer, String> trigStrings = new Hashtable<>();
|
||||
|
||||
private static enum ParseState {
|
||||
NEXT_TRIGSTR,
|
||||
START_OF_DATA,
|
||||
END_OF_DATA;
|
||||
}
|
||||
|
||||
private void parse() throws IOException {
|
||||
final BufferedReader sourceReader = new BufferedReader(
|
||||
new InputStreamReader(this.source, Charset.forName("utf-8")));
|
||||
ParseState state = ParseState.NEXT_TRIGSTR;
|
||||
|
||||
// WTS files may start with a Byte Order Mark, which we will have to skip.
|
||||
sourceReader.mark(4);
|
||||
if (sourceReader.read() != 0xFEFF) {
|
||||
// first character not a BOM, unread the character.
|
||||
sourceReader.reset();
|
||||
}
|
||||
|
||||
String currentLine = sourceReader.readLine();
|
||||
int id = 0;
|
||||
StringBuffer data = new StringBuffer();
|
||||
|
||||
while (currentLine != null) {
|
||||
switch (state) {
|
||||
case NEXT_TRIGSTR:
|
||||
if (currentLine.startsWith("STRING ")) {
|
||||
id = Integer.parseInt(currentLine.substring(7));
|
||||
state = ParseState.START_OF_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case START_OF_DATA:
|
||||
if (currentLine.startsWith("{")) {
|
||||
state = ParseState.END_OF_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case END_OF_DATA:
|
||||
if (currentLine.startsWith("}")) {
|
||||
this.trigStrings.put(id, data.toString());
|
||||
data = new StringBuffer();
|
||||
state = ParseState.NEXT_TRIGSTR;
|
||||
}
|
||||
else {
|
||||
data.append(currentLine);
|
||||
}
|
||||
break;
|
||||
}
|
||||
currentLine = sourceReader.readLine();
|
||||
}
|
||||
sourceReader.close();
|
||||
}
|
||||
|
||||
public WTSFile(final InputStream inputStream) throws IOException {
|
||||
this.source = inputStream;
|
||||
parse();
|
||||
}
|
||||
|
||||
public WTSFile(final Path source) throws IOException {
|
||||
this(Files.newInputStream(source));
|
||||
}
|
||||
|
||||
public WTSFile(final String sourcePath) throws IOException {
|
||||
this(Paths.get(sourcePath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(final int index) {
|
||||
return this.trigStrings.get(index);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,802 @@
|
||||
package com.etheller.warsmash.units.custom;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.etheller.warsmash.util.ParseUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.google.common.io.LittleEndianDataOutputStream;
|
||||
|
||||
/**
|
||||
* Inspired by PitzerMike's obj.h, without a lot of immediate focus on Java
|
||||
* conventions. I will probably get it converted over to Java conventions once I
|
||||
* have a working replica of his obj.h code.
|
||||
*
|
||||
* @author Eric
|
||||
*
|
||||
*/
|
||||
public final class War3ObjectDataChangeset {
|
||||
public static final int VAR_TYPE_INT = 0;
|
||||
public static final int VAR_TYPE_REAL = 1;
|
||||
public static final int VAR_TYPE_UNREAL = 2;
|
||||
public static final int VAR_TYPE_STRING = 3;
|
||||
public static final int VAR_TYPE_BOOLEAN = 4;
|
||||
public static final int MAX_STR_LEN = 1024;
|
||||
private static final Set<War3ID> UNIT_ID_SET;
|
||||
private static final Set<War3ID> ABILITY_ID_SET;
|
||||
static {
|
||||
final HashSet<War3ID> unitHashSet = new HashSet<>();
|
||||
unitHashSet.add(War3ID.fromString("ubpx"));
|
||||
unitHashSet.add(War3ID.fromString("ubpy"));
|
||||
unitHashSet.add(War3ID.fromString("ides"));
|
||||
unitHashSet.add(War3ID.fromString("uhot"));
|
||||
unitHashSet.add(War3ID.fromString("unam"));
|
||||
unitHashSet.add(War3ID.fromString("ureq"));
|
||||
unitHashSet.add(War3ID.fromString("urqa"));
|
||||
unitHashSet.add(War3ID.fromString("utip"));
|
||||
unitHashSet.add(War3ID.fromString("utub"));
|
||||
UNIT_ID_SET = unitHashSet;
|
||||
final HashSet<War3ID> abilHashSet = new HashSet<>();
|
||||
abilHashSet.add(War3ID.fromString("irc2"));
|
||||
abilHashSet.add(War3ID.fromString("irc3"));
|
||||
abilHashSet.add(War3ID.fromString("bsk1"));
|
||||
abilHashSet.add(War3ID.fromString("bsk2"));
|
||||
abilHashSet.add(War3ID.fromString("bsk3"));
|
||||
abilHashSet.add(War3ID.fromString("coau"));
|
||||
abilHashSet.add(War3ID.fromString("coa1"));
|
||||
abilHashSet.add(War3ID.fromString("coa2"));
|
||||
abilHashSet.add(War3ID.fromString("cyc1"));
|
||||
abilHashSet.add(War3ID.fromString("dcp1"));
|
||||
abilHashSet.add(War3ID.fromString("dcp2"));
|
||||
abilHashSet.add(War3ID.fromString("dvm1"));
|
||||
abilHashSet.add(War3ID.fromString("dvm2"));
|
||||
abilHashSet.add(War3ID.fromString("dvm3"));
|
||||
abilHashSet.add(War3ID.fromString("dvm4"));
|
||||
abilHashSet.add(War3ID.fromString("dvm5"));
|
||||
abilHashSet.add(War3ID.fromString("exh1"));
|
||||
abilHashSet.add(War3ID.fromString("exhu"));
|
||||
abilHashSet.add(War3ID.fromString("fak1"));
|
||||
abilHashSet.add(War3ID.fromString("fak2"));
|
||||
abilHashSet.add(War3ID.fromString("fak3"));
|
||||
abilHashSet.add(War3ID.fromString("hwdu"));
|
||||
abilHashSet.add(War3ID.fromString("inv1"));
|
||||
abilHashSet.add(War3ID.fromString("inv2"));
|
||||
abilHashSet.add(War3ID.fromString("inv3"));
|
||||
abilHashSet.add(War3ID.fromString("inv4"));
|
||||
abilHashSet.add(War3ID.fromString("inv5"));
|
||||
abilHashSet.add(War3ID.fromString("liq1"));
|
||||
abilHashSet.add(War3ID.fromString("liq2"));
|
||||
abilHashSet.add(War3ID.fromString("liq3"));
|
||||
abilHashSet.add(War3ID.fromString("liq4"));
|
||||
abilHashSet.add(War3ID.fromString("mim1"));
|
||||
abilHashSet.add(War3ID.fromString("mfl1"));
|
||||
abilHashSet.add(War3ID.fromString("mfl2"));
|
||||
abilHashSet.add(War3ID.fromString("mfl3"));
|
||||
abilHashSet.add(War3ID.fromString("mfl4"));
|
||||
abilHashSet.add(War3ID.fromString("mfl5"));
|
||||
abilHashSet.add(War3ID.fromString("tpi1"));
|
||||
abilHashSet.add(War3ID.fromString("tpi2"));
|
||||
abilHashSet.add(War3ID.fromString("spl1"));
|
||||
abilHashSet.add(War3ID.fromString("spl2"));
|
||||
abilHashSet.add(War3ID.fromString("irl1"));
|
||||
abilHashSet.add(War3ID.fromString("irl2"));
|
||||
abilHashSet.add(War3ID.fromString("irl3"));
|
||||
abilHashSet.add(War3ID.fromString("irl4"));
|
||||
abilHashSet.add(War3ID.fromString("irl5"));
|
||||
abilHashSet.add(War3ID.fromString("idc1"));
|
||||
abilHashSet.add(War3ID.fromString("idc2"));
|
||||
abilHashSet.add(War3ID.fromString("idc3"));
|
||||
abilHashSet.add(War3ID.fromString("imo1"));
|
||||
abilHashSet.add(War3ID.fromString("imo2"));
|
||||
abilHashSet.add(War3ID.fromString("imo3"));
|
||||
abilHashSet.add(War3ID.fromString("imou"));
|
||||
abilHashSet.add(War3ID.fromString("ict1"));
|
||||
abilHashSet.add(War3ID.fromString("ict2"));
|
||||
abilHashSet.add(War3ID.fromString("isr1"));
|
||||
abilHashSet.add(War3ID.fromString("isr2"));
|
||||
abilHashSet.add(War3ID.fromString("ipv1"));
|
||||
abilHashSet.add(War3ID.fromString("ipv2"));
|
||||
abilHashSet.add(War3ID.fromString("ipv3"));
|
||||
abilHashSet.add(War3ID.fromString("mec1"));
|
||||
abilHashSet.add(War3ID.fromString("spb1"));
|
||||
abilHashSet.add(War3ID.fromString("spb2"));
|
||||
abilHashSet.add(War3ID.fromString("spb3"));
|
||||
abilHashSet.add(War3ID.fromString("spb4"));
|
||||
abilHashSet.add(War3ID.fromString("spb5"));
|
||||
abilHashSet.add(War3ID.fromString("gra1"));
|
||||
abilHashSet.add(War3ID.fromString("gra2"));
|
||||
abilHashSet.add(War3ID.fromString("gra3"));
|
||||
abilHashSet.add(War3ID.fromString("gra4"));
|
||||
abilHashSet.add(War3ID.fromString("gra5"));
|
||||
abilHashSet.add(War3ID.fromString("ipmu"));
|
||||
abilHashSet.add(War3ID.fromString("flk1"));
|
||||
abilHashSet.add(War3ID.fromString("flk2"));
|
||||
abilHashSet.add(War3ID.fromString("flk3"));
|
||||
abilHashSet.add(War3ID.fromString("flk4"));
|
||||
abilHashSet.add(War3ID.fromString("flk5"));
|
||||
abilHashSet.add(War3ID.fromString("fbk1"));
|
||||
abilHashSet.add(War3ID.fromString("fbk2"));
|
||||
abilHashSet.add(War3ID.fromString("fbk3"));
|
||||
abilHashSet.add(War3ID.fromString("fbk4"));
|
||||
abilHashSet.add(War3ID.fromString("nca1"));
|
||||
abilHashSet.add(War3ID.fromString("pxf1"));
|
||||
abilHashSet.add(War3ID.fromString("pxf2"));
|
||||
abilHashSet.add(War3ID.fromString("mls1"));
|
||||
abilHashSet.add(War3ID.fromString("sla1"));
|
||||
abilHashSet.add(War3ID.fromString("sla2"));
|
||||
ABILITY_ID_SET = abilHashSet;
|
||||
}
|
||||
|
||||
private int version;
|
||||
private ObjectMap original = new ObjectMap();
|
||||
private final ObjectMap custom = new ObjectMap();
|
||||
private char expected;
|
||||
private War3ID lastused;
|
||||
|
||||
public char kind;
|
||||
public boolean detected;
|
||||
|
||||
public War3ID nameField;
|
||||
|
||||
public War3ObjectDataChangeset() {
|
||||
this.version = 2;
|
||||
this.kind = 'u';
|
||||
this.expected = 'u';
|
||||
this.detected = false;
|
||||
this.lastused = War3ID.fromString("u~~~");
|
||||
}
|
||||
|
||||
public War3ObjectDataChangeset(final char expectedkind) {
|
||||
this.version = 2;
|
||||
this.kind = 'u';
|
||||
this.expected = expectedkind;
|
||||
this.detected = false;
|
||||
this.lastused = War3ID.fromString("u~~~");
|
||||
}
|
||||
|
||||
public boolean detectKind(final War3ID chid) {
|
||||
if (UNIT_ID_SET.contains(chid)) {
|
||||
this.kind = 'u';
|
||||
return false;
|
||||
}
|
||||
else if (ABILITY_ID_SET.contains(chid)) {
|
||||
this.kind = 'a';
|
||||
}
|
||||
else {
|
||||
switch (chid.asStringValue().charAt(0)) {
|
||||
case 'f':
|
||||
this.kind = 'h';
|
||||
break;
|
||||
case 'i':
|
||||
this.kind = 't';
|
||||
break;
|
||||
case 'g':
|
||||
this.kind = 'q';
|
||||
break;
|
||||
case 'a':
|
||||
case 'u':
|
||||
case 'b':
|
||||
case 'd':
|
||||
this.kind = chid.asStringValue().charAt(0);
|
||||
break;
|
||||
default:
|
||||
this.kind = 'a';
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public char getExpectedKind() {
|
||||
return this.expected;
|
||||
}
|
||||
|
||||
public War3ID getNameField() {
|
||||
final War3ID field = War3ID.fromString("unam");
|
||||
char cmp = this.kind;
|
||||
if (!this.detected) {
|
||||
cmp = this.expected;
|
||||
}
|
||||
switch (cmp) {
|
||||
case 'h':
|
||||
this.nameField = field.set(0, 'f');
|
||||
break;
|
||||
case 't':
|
||||
this.nameField = field.set(0, 'u');
|
||||
break;
|
||||
case 'q':
|
||||
this.nameField = field.set(0, 'g');
|
||||
break;
|
||||
default:
|
||||
this.nameField = field.set(0, cmp);
|
||||
break;
|
||||
}
|
||||
return this.nameField;
|
||||
}
|
||||
|
||||
public boolean extended() {
|
||||
char cmp = this.kind;
|
||||
if (!this.detected) {
|
||||
cmp = this.expected;
|
||||
}
|
||||
switch (cmp) {
|
||||
case 'u':
|
||||
case 'h':
|
||||
case 'b':
|
||||
case 't':
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void renameids(final ObjectMap map, final boolean isOriginal) {
|
||||
final War3ID nameId = getNameField();
|
||||
final List<War3ID> idsToRemoveFromMap = new ArrayList<>();
|
||||
final Map<War3ID, ObjectDataChangeEntry> idsToObjectsForAddingToMap = new HashMap<>();
|
||||
for (final Iterator<Map.Entry<War3ID, ObjectDataChangeEntry>> iterator = map.iterator(); iterator.hasNext();) {
|
||||
final Map.Entry<War3ID, ObjectDataChangeEntry> entry = iterator.next();
|
||||
final ObjectDataChangeEntry current = entry.getValue();
|
||||
final List<Change> nameEntry = current.getChanges().get(nameId);
|
||||
if ((nameEntry != null) && !nameEntry.isEmpty()) {
|
||||
final Change firstNameChange = nameEntry.get(0);
|
||||
int pos = firstNameChange.getStrval().lastIndexOf("::");
|
||||
if ((pos != -1) && (firstNameChange.getStrval().length() > (pos + 2))) {
|
||||
String rest = firstNameChange.getStrval().substring(pos + 2);
|
||||
if (rest.length() == 4) {
|
||||
final War3ID newId = War3ID.fromString(rest);
|
||||
final ObjectDataChangeEntry existingObjectWithMatchingId = map.get(newId);
|
||||
if (isOriginal) {// obj.cpp: update id and name
|
||||
current.setOldId(newId);
|
||||
}
|
||||
else {
|
||||
current.setNewId(newId);
|
||||
}
|
||||
firstNameChange.setStrval(firstNameChange.getStrval().substring(0, pos));
|
||||
if (existingObjectWithMatchingId != null) {
|
||||
// obj.cpp: carry over all changes
|
||||
final Iterator<Map.Entry<War3ID, List<Change>>> changeIterator = current.getChanges()
|
||||
.iterator();
|
||||
while (changeIterator.hasNext()) {
|
||||
final Map.Entry<War3ID, List<Change>> changeIteratorNext = changeIterator.next();
|
||||
final War3ID copiedChangeId = changeIteratorNext.getKey();
|
||||
List<Change> changeListForFieldToOverwrite = existingObjectWithMatchingId.getChanges()
|
||||
.get(copiedChangeId);
|
||||
if (changeListForFieldToOverwrite == null) {
|
||||
changeListForFieldToOverwrite = new ArrayList<>();
|
||||
}
|
||||
for (final Change changeToCopy : changeIteratorNext.getValue()) {
|
||||
final Iterator<Change> replaceIterator = changeListForFieldToOverwrite.iterator();
|
||||
boolean didOverwrite = false;
|
||||
while (replaceIterator.hasNext()) {
|
||||
final Change changeToOverwrite = replaceIterator.next();
|
||||
if (changeToOverwrite.getLevel() != changeToCopy.getLevel()) {
|
||||
// obj.cpp: we can only replace
|
||||
// changes with the same
|
||||
// level/variation
|
||||
continue;
|
||||
}
|
||||
if (copiedChangeId.equals(nameId)) {
|
||||
// obj.cpp: carry over further
|
||||
// references
|
||||
pos = changeToOverwrite.getStrval().lastIndexOf("::");
|
||||
if ((pos != -1) && (changeToOverwrite.getStrval().length() > (pos + 2))) {
|
||||
rest = changeToOverwrite.getStrval().substring(pos + 2);
|
||||
if ((rest.length() == 4) || "REMOVE".equals(rest)) {
|
||||
changeToCopy.setStrval(changeToCopy.getStrval() + "::" + rest);
|
||||
// so if this is a peasant, whose name was "Peasant::hfoo"
|
||||
// and when we copied his data onto the footman, we found
|
||||
// that the footman was named "Footman::hkni", then at that
|
||||
// point we set the peasant's name to be "Peasant::hkni"
|
||||
// because we are about to copy it onto the footman.
|
||||
// And, we already set it to just "Peasant", so
|
||||
// appending the "::" and the 'rest' variable is enough.
|
||||
// Then, on a further loop iteration, in theory
|
||||
// we will copoy the footman who is named Peasant
|
||||
// onto the knight.
|
||||
//
|
||||
// TODO but what if we already copied the footman onto the knight?
|
||||
// did PitzerMike consider this in obj.cpp?
|
||||
}
|
||||
}
|
||||
}
|
||||
changeToOverwrite.copyFrom(changeToCopy);
|
||||
didOverwrite = true;
|
||||
break;
|
||||
}
|
||||
if (!didOverwrite) {
|
||||
changeListForFieldToOverwrite.add(changeToCopy);
|
||||
if (changeListForFieldToOverwrite.size() == 1) {
|
||||
existingObjectWithMatchingId.getChanges().add(copiedChangeId,
|
||||
changeListForFieldToOverwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // obj.cpp: an object with that id didn't exist
|
||||
idsToRemoveFromMap.add(entry.getKey());
|
||||
idsToObjectsForAddingToMap.put(newId, current.clone());
|
||||
}
|
||||
}
|
||||
else if ("REMOVE".equals(rest)) { // obj.cpp: want to remove the object
|
||||
idsToRemoveFromMap.add(entry.getKey());
|
||||
} // obj.cpp: in all other cases keep it untouched
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (final War3ID id : idsToRemoveFromMap) {
|
||||
map.remove(id);
|
||||
}
|
||||
for (final Map.Entry<War3ID, ObjectDataChangeEntry> entry : idsToObjectsForAddingToMap.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void renameIds() {
|
||||
renameids(this.original, true);
|
||||
renameids(this.custom, false);
|
||||
}
|
||||
|
||||
// ' ' - '/'
|
||||
// ':' - '@'
|
||||
// '[' - '`'
|
||||
// '{' - '~'
|
||||
public char nextchar(final char cur) {
|
||||
switch (cur) {
|
||||
case '&': // skip ' because often jass parsers don't handle escaped rawcodes like '\''
|
||||
return '(';
|
||||
case '/': // skip digits
|
||||
return ':';
|
||||
case '@': // skip capital letters
|
||||
return '['; // skip \ for the sam reason like ' ('\\')
|
||||
case '[':
|
||||
return ']';
|
||||
case '_': // skip <EFBFBD> and lower case letters (<EFBFBD> can't be seen very well)
|
||||
return '{';
|
||||
case '~': // close circle and restart at !
|
||||
return '!';
|
||||
default:
|
||||
return (char) ((short) cur + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// we use only special characters to avoid collisions with existing objects
|
||||
// the first character must remain unchanged though because it can have a
|
||||
// special meaning
|
||||
public War3ID getunusedid(final War3ID substitutefor) {
|
||||
this.lastused = this.lastused.set(0, substitutefor.charAt(0));
|
||||
this.lastused = this.lastused.set(3, nextchar(substitutefor.charAt(3)));
|
||||
if (this.lastused.charAt(3) == '!') {
|
||||
this.lastused = this.lastused.set(2, nextchar(substitutefor.charAt(2)));
|
||||
if (this.lastused.charAt(2) == '!') {
|
||||
this.lastused = this.lastused.set(1, nextchar(substitutefor.charAt(1)));
|
||||
}
|
||||
}
|
||||
return this.lastused;
|
||||
}
|
||||
|
||||
public void mergetable(final ObjectMap target, final ObjectMap targetCustom, final ObjectMap source,
|
||||
final CollisionHandling collisionHandling) {
|
||||
final Iterator<Map.Entry<War3ID, ObjectDataChangeEntry>> sourceObjectIterator = source.iterator();
|
||||
while (sourceObjectIterator.hasNext()) {
|
||||
final Map.Entry<War3ID, ObjectDataChangeEntry> sourceObject = sourceObjectIterator.next();
|
||||
if (target.containsKey(sourceObject.getKey())) {
|
||||
// obj.cpp: we have a collision
|
||||
War3ID oldId;
|
||||
War3ID replacementId;
|
||||
|
||||
switch (collisionHandling) {
|
||||
case CREATE_NEW_ID:
|
||||
oldId = sourceObject.getKey();
|
||||
// obj.cpp: get new id until we finally have one that isn't used yet, or we're
|
||||
// out of ids
|
||||
replacementId = getunusedid(oldId);
|
||||
while (!((oldId.charAt(1) == '~') && (oldId.charAt(2) == '~') && (oldId.charAt(3) == '~'))
|
||||
&& targetCustom.containsKey(replacementId)) {
|
||||
oldId = replacementId;
|
||||
replacementId = getunusedid(oldId);
|
||||
}
|
||||
if (!((oldId.charAt(1) == '~') && (oldId.charAt(2) == '~') && (oldId.charAt(3) == '~'))) {
|
||||
sourceObject.getValue().setNewId(replacementId);
|
||||
targetCustom.put(replacementId, sourceObject.getValue().clone());
|
||||
}
|
||||
break;
|
||||
case REPLACE:
|
||||
// final ObjectDataChangeEntry deleteObject = target.get(sourceObject.getKey());
|
||||
target.put(sourceObject.getKey(), sourceObject.getValue().clone());
|
||||
break;
|
||||
default:// merge
|
||||
final ObjectDataChangeEntry targetObject = target.get(sourceObject.getKey());
|
||||
for (final Map.Entry<War3ID, List<Change>> sourceUnitField : sourceObject.getValue().getChanges()) {
|
||||
for (final Change sourceChange : sourceUnitField.getValue()) {
|
||||
List<Change> targetChanges = targetObject.getChanges().get(sourceUnitField.getKey());
|
||||
if (targetChanges == null) {
|
||||
targetChanges = new ArrayList<>();
|
||||
}
|
||||
Change bestTargetChange = null;
|
||||
for (final Change targetChange : targetChanges) {
|
||||
if (targetChange.getLevel() == sourceChange.getLevel()) {
|
||||
bestTargetChange = targetChange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bestTargetChange != null) {
|
||||
bestTargetChange.copyFrom(sourceChange);
|
||||
}
|
||||
else {
|
||||
targetChanges.add(sourceChange.clone());
|
||||
if (targetChanges.size() == 1) {
|
||||
targetObject.getChanges().add(sourceUnitField.getKey(), targetChanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
targetCustom.put(sourceObject.getKey(), sourceObject.getValue().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static enum CollisionHandling {
|
||||
CREATE_NEW_ID,
|
||||
REPLACE,
|
||||
MERGE;
|
||||
}
|
||||
|
||||
public void merge(final War3ObjectDataChangeset obj, final CollisionHandling collisionHandling) {
|
||||
mergetable(this.original, this.custom, obj.original, collisionHandling);
|
||||
mergetable(this.original, this.custom, obj.custom, collisionHandling);
|
||||
}
|
||||
|
||||
public int getvartype(final String name) {
|
||||
if ("int".equals(name) || "bool".equals(name)) {
|
||||
return 0;
|
||||
}
|
||||
else if ("real".equals(name)) {
|
||||
return 1;
|
||||
}
|
||||
else if ("unreal".equals(name)) {
|
||||
return 2;
|
||||
}
|
||||
return 3; // string
|
||||
}
|
||||
|
||||
public boolean loadtable(final LittleEndianDataInputStream stream, final ObjectMap map, final boolean isOriginal,
|
||||
final WTS wts, final boolean inlineWTS) throws IOException {
|
||||
final War3ID noid = new War3ID(0);
|
||||
final ByteBuffer stringByteBuffer = ByteBuffer.allocate(1024); // TODO check max len?
|
||||
final CharsetDecoder decoder = Charset.forName("utf-8").newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
int ptr;
|
||||
final int count = stream.readInt();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final long nanoTime = System.nanoTime();
|
||||
War3ID origid;
|
||||
War3ID newid = null;
|
||||
origid = readWar3ID(stream);
|
||||
ObjectDataChangeEntry existingObject;
|
||||
if (isOriginal) {
|
||||
if (noid.equals(origid)) {
|
||||
throw new IOException("the input stream might be screwed");
|
||||
}
|
||||
existingObject = map.get(origid);
|
||||
if (existingObject == null) {
|
||||
existingObject = new ObjectDataChangeEntry(origid, noid);
|
||||
}
|
||||
existingObject.setNewId(readWar3ID(stream));
|
||||
}
|
||||
else {
|
||||
newid = readWar3ID(stream);
|
||||
if (noid.equals(origid) || noid.equals(newid)) {
|
||||
throw new IOException("the input stream might be screwed");
|
||||
}
|
||||
existingObject = map.get(newid);
|
||||
if (existingObject == null) {
|
||||
existingObject = new ObjectDataChangeEntry(origid, newid);
|
||||
}
|
||||
}
|
||||
final int ccount = stream.readInt();// Retera: I assume this is change count?
|
||||
if ((ccount == 0) && isOriginal) {
|
||||
// throw new IOException("we seem to have reached the end of the stream and get
|
||||
// zeroes");
|
||||
System.err.println("we seem to have reached the end of the stream and get zeroes");
|
||||
}
|
||||
if (isOriginal) {
|
||||
debugprint("StandardUnit \"" + origid + "\" " + ccount + " {");
|
||||
}
|
||||
else {
|
||||
debugprint("CustomUnit \"" + origid + ":" + newid + "\" " + ccount + " {");
|
||||
}
|
||||
for (int j = 0; j < ccount; j++) {
|
||||
final War3ID chid = readWar3ID(stream);
|
||||
if (noid.equals(chid)) {
|
||||
throw new IOException("the input stream might be screwed");
|
||||
}
|
||||
if (!this.detected) {
|
||||
this.detected = detectKind(chid);
|
||||
}
|
||||
|
||||
final Change newlyReadChange = new Change();
|
||||
newlyReadChange.setId(chid);
|
||||
newlyReadChange.setVartype(stream.readInt());
|
||||
debugprint("\t\"" + chid + "\" {");
|
||||
debugprint("\t\tType " + newlyReadChange.getVartype() + ",");
|
||||
if (extended()) {
|
||||
newlyReadChange.setLevel(stream.readInt());
|
||||
newlyReadChange.setDataptr(stream.readInt());
|
||||
debugprint("\t\tLevel " + newlyReadChange.getLevel() + ",");
|
||||
debugprint("\t\tData " + newlyReadChange.getDataptr() + ",");
|
||||
}
|
||||
|
||||
switch (newlyReadChange.getVartype()) {
|
||||
case 0:
|
||||
newlyReadChange.setLongval(stream.readInt());
|
||||
debugprint("\t\tValue " + newlyReadChange.getLongval() + ",");
|
||||
break;
|
||||
case 3:
|
||||
ptr = 0;
|
||||
stringByteBuffer.clear();
|
||||
byte charRead;
|
||||
while ((charRead = (byte) stream.read()) != 0) {
|
||||
stringByteBuffer.put(charRead);
|
||||
}
|
||||
stringByteBuffer.flip();
|
||||
newlyReadChange.setStrval(decoder.decode(stringByteBuffer).toString());
|
||||
if (inlineWTS && (newlyReadChange.getStrval().length() > 8)
|
||||
&& "TRIGSTR_".equals(newlyReadChange.getStrval().substring(0, 8))) {
|
||||
final int key = getWTSValue(newlyReadChange);
|
||||
newlyReadChange.setStrval(wts.get(key));
|
||||
if ((newlyReadChange.getStrval() != null)
|
||||
&& (newlyReadChange.getStrval().length() > MAX_STR_LEN)) {
|
||||
newlyReadChange.setStrval(newlyReadChange.getStrval().substring(0, MAX_STR_LEN - 1));
|
||||
}
|
||||
}
|
||||
debugprint("\t\tValue \"" + newlyReadChange.getStrval() + "\",");
|
||||
break;
|
||||
case 4:
|
||||
newlyReadChange.setBoolval(stream.readInt() == 1);
|
||||
debugprint("\t\tValue " + newlyReadChange.isBoolval() + ",");
|
||||
break;
|
||||
default:
|
||||
newlyReadChange.setRealval(stream.readFloat());
|
||||
debugprint("\t\tValue " + newlyReadChange.getRealval() + ",");
|
||||
break;
|
||||
}
|
||||
final War3ID crap = readWar3ID(stream);
|
||||
debugprint("\t\tExtra \"" + crap + "\",");
|
||||
newlyReadChange.setJunkDNA(crap);
|
||||
List<Change> existingChanges = existingObject.getChanges().get(chid);
|
||||
if (existingChanges == null) {
|
||||
existingChanges = new ArrayList<>();
|
||||
}
|
||||
Change bestTargetChange = null;
|
||||
for (final Change targetChange : existingChanges) {
|
||||
if (targetChange.getLevel() == newlyReadChange.getLevel()) {
|
||||
bestTargetChange = targetChange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bestTargetChange != null) {
|
||||
bestTargetChange.copyFrom(newlyReadChange);
|
||||
}
|
||||
else {
|
||||
existingChanges.add(newlyReadChange.clone());
|
||||
if (existingChanges.size() == 1) {
|
||||
existingObject.getChanges().add(chid, existingChanges);
|
||||
}
|
||||
}
|
||||
if (!crap.equals(existingObject.getOldId()) && !crap.equals(existingObject.getNewId())
|
||||
&& !crap.equals(noid)) {
|
||||
for (int charIndex = 0; charIndex < 4; charIndex++) {
|
||||
if ((crap.charAt(charIndex) < 32) || (crap.charAt(charIndex) > 126)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
debugprint("\t}");
|
||||
}
|
||||
debugprint("}");
|
||||
if ((newid == null) && !isOriginal) {
|
||||
throw new IllegalStateException("custom unit has no ID!");
|
||||
}
|
||||
map.put(isOriginal ? origid : newid, existingObject);
|
||||
final long endNanoTime = System.nanoTime();
|
||||
final long deltaNanoTime = endNanoTime - nanoTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private War3ID readWar3ID(final LittleEndianDataInputStream stream) throws IOException {
|
||||
return new War3ID(Integer.reverseBytes(stream.readInt()));
|
||||
}
|
||||
|
||||
private static int getWTSValue(final Change change) {
|
||||
String numberAsText = change.getStrval().substring(8);
|
||||
while ((numberAsText.length() > 0) && (numberAsText.charAt(0) == '0')) {
|
||||
numberAsText = numberAsText.substring(1);
|
||||
}
|
||||
if (numberAsText.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
while (!Character.isDigit(numberAsText.charAt(numberAsText.length() - 1))) {
|
||||
numberAsText = numberAsText.substring(0, numberAsText.length() - 1);
|
||||
}
|
||||
return Integer.parseInt(numberAsText);
|
||||
}
|
||||
|
||||
public boolean load(final LittleEndianDataInputStream stream, final WTS wts, final boolean inlineWTS)
|
||||
throws IOException {
|
||||
this.detected = false;
|
||||
this.version = stream.readInt();
|
||||
if ((this.version != 1) && (this.version != 2)) {
|
||||
return false;
|
||||
}
|
||||
ObjectMap backup = this.original.clone();
|
||||
if (!loadtable(stream, this.original, true, wts, inlineWTS)) {
|
||||
this.original = backup;
|
||||
return false;
|
||||
}
|
||||
backup = this.custom.clone();
|
||||
if (!loadtable(stream, this.custom, false, wts, inlineWTS)) {
|
||||
this.original = backup;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean load(final File file, final WTS wts, final boolean inlineWTS) throws IOException {
|
||||
try (LittleEndianDataInputStream inputStream = new LittleEndianDataInputStream(new FileInputStream(file))) {
|
||||
final boolean result = load(inputStream, wts, inlineWTS);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static void inlineWTSTable(final ObjectMap map, final WTS wts) {
|
||||
for (final Map.Entry<War3ID, ObjectDataChangeEntry> entry : map.entrySet()) {
|
||||
for (final Map.Entry<War3ID, List<Change>> changes : entry.getValue().getChanges()) {
|
||||
for (final Change change : changes.getValue()) {
|
||||
if ((change.getStrval().length() > 8) && "TRIGSTR_".equals(change.getStrval().substring(0, 8))) {
|
||||
final int key = getWTSValue(change);
|
||||
change.setStrval(wts.get(key));
|
||||
if (change.getStrval().length() > MAX_STR_LEN) {
|
||||
change.setStrval(change.getStrval().substring(0, MAX_STR_LEN - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void inlineWTS(final WTS wts) {
|
||||
inlineWTSTable(this.original, wts);
|
||||
inlineWTSTable(this.custom, wts);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
reset('u');
|
||||
}
|
||||
|
||||
public void reset(final char expectedkind) {
|
||||
this.detected = false;
|
||||
this.kind = 'u';
|
||||
this.lastused = War3ID.fromString("u~~~");
|
||||
this.expected = expectedkind;
|
||||
this.original.clear();
|
||||
this.custom.clear();
|
||||
}
|
||||
|
||||
public boolean saveTable(final LittleEndianDataOutputStream outputStream, final ObjectMap map,
|
||||
final boolean isOriginal) throws IOException {
|
||||
final CharsetEncoder encoder = Charset.forName("utf-8").newEncoder().onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
final CharBuffer charBuffer = CharBuffer.allocate(1024);
|
||||
final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
|
||||
final War3ID noid = new War3ID(0);
|
||||
int count;
|
||||
count = map.size();
|
||||
outputStream.writeInt(count);
|
||||
for (final Map.Entry<War3ID, ObjectDataChangeEntry> entry : map) {
|
||||
final ObjectDataChangeEntry cl = entry.getValue();
|
||||
int totalSize = 0;
|
||||
for (final Map.Entry<War3ID, List<Change>> changeEntry : cl.getChanges()) {
|
||||
totalSize += changeEntry.getValue().size();
|
||||
}
|
||||
if ((totalSize > 0) || !isOriginal) {
|
||||
ParseUtils.writeWar3ID(outputStream, cl.getOldId());
|
||||
ParseUtils.writeWar3ID(outputStream, cl.getNewId());
|
||||
count = totalSize;// cl.getChanges().size();
|
||||
outputStream.writeInt(count);
|
||||
for (final Map.Entry<War3ID, List<Change>> changes : entry.getValue().getChanges()) {
|
||||
for (final Change change : changes.getValue()) {
|
||||
ParseUtils.writeWar3ID(outputStream, change.getId());
|
||||
outputStream.writeInt(change.getVartype());
|
||||
if (extended()) {
|
||||
outputStream.writeInt(change.getLevel());
|
||||
outputStream.writeInt(change.getDataptr());
|
||||
}
|
||||
switch (change.getVartype()) {
|
||||
case 0:
|
||||
outputStream.writeInt(change.getLongval());
|
||||
break;
|
||||
case 3:
|
||||
charBuffer.clear();
|
||||
byteBuffer.clear();
|
||||
charBuffer.put(change.getStrval());
|
||||
charBuffer.flip();
|
||||
encoder.encode(charBuffer, byteBuffer, false);
|
||||
byteBuffer.flip();
|
||||
final byte[] stringBytes = new byte[byteBuffer.remaining() + 1];
|
||||
int i = 0;
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
stringBytes[i++] = byteBuffer.get();
|
||||
}
|
||||
stringBytes[i] = 0;
|
||||
outputStream.write(stringBytes);
|
||||
break;
|
||||
case 4:
|
||||
outputStream.writeInt(change.isBoolval() ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
outputStream.writeFloat(change.getRealval());
|
||||
break;
|
||||
}
|
||||
// if (change.getJunkDNA() == null) {
|
||||
// saveWriteChars(outputStream, cl.getNewId().asStringValue().toCharArray());
|
||||
// } else {
|
||||
// saveWriteChars(outputStream,
|
||||
// change.getJunkDNA().asStringValue().toCharArray());
|
||||
// }
|
||||
// saveWriteChars(outputStream, cl.getNewId().asStringValue().toCharArray());
|
||||
ParseUtils.writeWar3ID(outputStream, noid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean save(final LittleEndianDataOutputStream outputStream, final boolean generateWTS) throws IOException {
|
||||
if (generateWTS) {
|
||||
throw new UnsupportedOperationException("FAIL cannot generate WTS, needs more code");
|
||||
}
|
||||
this.version = 2;
|
||||
outputStream.writeInt(this.version);
|
||||
if (!saveTable(outputStream, this.original, true)) {
|
||||
throw new RuntimeException("Failed to save standard unit custom data");
|
||||
}
|
||||
if (!saveTable(outputStream, this.custom, false)) {
|
||||
throw new RuntimeException("Failed to save custom unit custom data");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ObjectMap getOriginal() {
|
||||
return this.original;
|
||||
}
|
||||
|
||||
public ObjectMap getCustom() {
|
||||
return this.custom;
|
||||
}
|
||||
|
||||
private static void debugprint(final String s) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,922 @@
|
||||
package com.etheller.warsmash.units.manager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.etheller.warsmash.units.GameObject;
|
||||
import com.etheller.warsmash.units.ObjectData;
|
||||
import com.etheller.warsmash.units.custom.Change;
|
||||
import com.etheller.warsmash.units.custom.ChangeMap;
|
||||
import com.etheller.warsmash.units.custom.ObjectDataChangeEntry;
|
||||
import com.etheller.warsmash.units.custom.War3ObjectDataChangeset;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
|
||||
public final class MutableObjectData {
|
||||
private static final War3ID ROC_SUPPORT_URAC = War3ID.fromString("urac");
|
||||
private static final War3ID ROC_SUPPORT_UCAM = War3ID.fromString("ucam");
|
||||
private static final War3ID ROC_SUPPORT_USPE = War3ID.fromString("uspe");
|
||||
private static final War3ID ROC_SUPPORT_UBDG = War3ID.fromString("ubdg");
|
||||
|
||||
private final WorldEditorDataType worldEditorDataType;
|
||||
private final ObjectData sourceSLKData;
|
||||
private final ObjectData sourceSLKMetaData;
|
||||
private final War3ObjectDataChangeset editorData;
|
||||
private Set<War3ID> cachedKeySet;
|
||||
private final Map<String, War3ID> metaNameToMetaId;
|
||||
private final Map<War3ID, MutableGameObject> cachedKeyToGameObject;
|
||||
private final MutableObjectDataChangeNotifier changeNotifier;
|
||||
private final WorldEditStrings worldEditStrings;
|
||||
|
||||
public MutableObjectData(final WorldEditStrings worldEditStrings, final WorldEditorDataType worldEditorDataType,
|
||||
final ObjectData sourceSLKData, final ObjectData sourceSLKMetaData,
|
||||
final War3ObjectDataChangeset editorData) {
|
||||
this.worldEditStrings = worldEditStrings;
|
||||
this.worldEditorDataType = worldEditorDataType;
|
||||
resolveStringReferencesInNames(sourceSLKData);
|
||||
this.sourceSLKData = sourceSLKData;
|
||||
this.sourceSLKMetaData = sourceSLKMetaData;
|
||||
this.editorData = editorData;
|
||||
this.metaNameToMetaId = new HashMap<>();
|
||||
for (final String metaKeyString : sourceSLKMetaData.keySet()) {
|
||||
final War3ID metaKey = War3ID.fromString(metaKeyString);
|
||||
this.metaNameToMetaId.put(sourceSLKMetaData.get(metaKeyString).getField("field"), metaKey);
|
||||
}
|
||||
this.cachedKeyToGameObject = new HashMap<>();
|
||||
this.changeNotifier = new MutableObjectDataChangeNotifier();
|
||||
}
|
||||
|
||||
// TODO remove this hack
|
||||
public War3ObjectDataChangeset getEditorData() {
|
||||
return this.editorData;
|
||||
}
|
||||
|
||||
private void resolveStringReferencesInNames(final ObjectData sourceSLKData) {
|
||||
for (final String key : sourceSLKData.keySet()) {
|
||||
final GameObject gameObject = sourceSLKData.get(key);
|
||||
String name = gameObject.getField("Name");
|
||||
final String suffix = gameObject.getField("EditorSuffix");
|
||||
if (name.startsWith("WESTRING")) {
|
||||
if (!name.contains(" ")) {
|
||||
name = this.worldEditStrings.getString(name);
|
||||
}
|
||||
else {
|
||||
final String[] names = name.split(" ");
|
||||
name = "";
|
||||
for (final String subName : names) {
|
||||
if (name.length() > 0) {
|
||||
name += " ";
|
||||
}
|
||||
if (subName.startsWith("WESTRING")) {
|
||||
name += this.worldEditStrings.getString(subName);
|
||||
}
|
||||
else {
|
||||
name += subName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.startsWith("\"") && name.endsWith("\"")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
gameObject.setField("Name", name);
|
||||
}
|
||||
if (suffix.startsWith("WESTRING")) {
|
||||
gameObject.setField("EditorSuffix", this.worldEditStrings.getString(suffix));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeChangset(final War3ObjectDataChangeset changeset) {
|
||||
final List<War3ID> newObjects = new ArrayList<>();
|
||||
final Map<War3ID, War3ID> previousAliasToNewAlias = new HashMap<>();
|
||||
for (final Map.Entry<War3ID, ObjectDataChangeEntry> entry : changeset.getCustom()) {
|
||||
|
||||
// final String newId = JOptionPane.showInputDialog("Choose UNIT ID");
|
||||
final War3ID nextDefaultEditorId = /* War3ID.fromString(newId); */getNextDefaultEditorId(
|
||||
War3ID.fromString(entry.getKey().charAt(0) + "000"));
|
||||
;
|
||||
System.out.println("Merging " + nextDefaultEditorId + " for " + entry.getKey());
|
||||
// createNew API will notifier the changeNotifier
|
||||
final MutableGameObject newObject = createNew(nextDefaultEditorId, entry.getValue().getOldId(), false);
|
||||
for (final Map.Entry<War3ID, List<Change>> changeList : entry.getValue().getChanges()) {
|
||||
newObject.customUnitData.getChanges().add(changeList.getKey(), changeList.getValue());
|
||||
}
|
||||
newObjects.add(nextDefaultEditorId);
|
||||
previousAliasToNewAlias.put(entry.getKey(), nextDefaultEditorId);
|
||||
}
|
||||
final War3ID[] fieldsToCheck = this.worldEditorDataType == WorldEditorDataType.UNITS
|
||||
? new War3ID[] { War3ID.fromString("utra"), War3ID.fromString("uupt"), War3ID.fromString("ubui") }
|
||||
: new War3ID[] {};
|
||||
for (final War3ID unitId : newObjects) {
|
||||
final MutableGameObject unit = get(unitId);
|
||||
for (final War3ID field : fieldsToCheck) {
|
||||
final String techtreeString = unit.getFieldAsString(field, 0);
|
||||
final java.util.List<String> techList = Arrays.asList(techtreeString.split(","));
|
||||
final ArrayList<String> resultingTechList = new ArrayList<>();
|
||||
for (final String tech : techList) {
|
||||
if (tech.length() == 4) {
|
||||
final War3ID newTechId = previousAliasToNewAlias.get(War3ID.fromString(tech));
|
||||
if (newTechId != null) {
|
||||
resultingTechList.add(newTechId.toString());
|
||||
}
|
||||
else {
|
||||
resultingTechList.add(tech);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultingTechList.add(tech);
|
||||
}
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (final String tech : resultingTechList) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(tech);
|
||||
}
|
||||
unit.setField(field, 0, sb.toString());
|
||||
}
|
||||
}
|
||||
this.changeNotifier.objectsCreated(newObjects.toArray(new War3ID[newObjects.size()]));
|
||||
}
|
||||
|
||||
public War3ObjectDataChangeset copySelectedObjects(final List<MutableGameObject> objectsToCopy) {
|
||||
final War3ObjectDataChangeset changeset = new War3ObjectDataChangeset(this.editorData.getExpectedKind());
|
||||
final War3ID[] fieldsToCheck = this.worldEditorDataType == WorldEditorDataType.UNITS
|
||||
? new War3ID[] { War3ID.fromString("utra"), War3ID.fromString("uupt"), War3ID.fromString("ubui") }
|
||||
: new War3ID[] {};
|
||||
final Map<War3ID, War3ID> previousAliasToNewAlias = new HashMap<>();
|
||||
for (final MutableGameObject gameObject : objectsToCopy) {
|
||||
final ObjectDataChangeEntry gameObjectUserDataToCopy;
|
||||
final ObjectDataChangeEntry gameObjectUserData;
|
||||
final War3ID alias = gameObject.getAlias();
|
||||
if (this.editorData.getOriginal().containsKey(alias)) {
|
||||
gameObjectUserDataToCopy = this.editorData.getOriginal().get(alias);
|
||||
final War3ID newAlias = getNextDefaultEditorId(
|
||||
War3ID.fromString(gameObject.getCode().charAt(0) + "000"), changeset, this.sourceSLKData);
|
||||
gameObjectUserData = new ObjectDataChangeEntry(gameObjectUserDataToCopy.getOldId(), newAlias);
|
||||
}
|
||||
else if (this.editorData.getCustom().containsKey(alias)) {
|
||||
gameObjectUserDataToCopy = this.editorData.getCustom().get(alias);
|
||||
gameObjectUserData = new ObjectDataChangeEntry(gameObjectUserDataToCopy.getOldId(),
|
||||
gameObjectUserDataToCopy.getNewId());
|
||||
}
|
||||
else {
|
||||
gameObjectUserDataToCopy = null;
|
||||
final War3ID newAlias = getNextDefaultEditorId(
|
||||
War3ID.fromString(gameObject.getCode().charAt(0) + "000"), changeset, this.sourceSLKData);
|
||||
gameObjectUserData = new ObjectDataChangeEntry(
|
||||
gameObject.isCustom() ? gameObject.getCode() : gameObject.getAlias(), newAlias);
|
||||
}
|
||||
if (gameObjectUserDataToCopy != null) {
|
||||
for (final Map.Entry<War3ID, List<Change>> changeEntry : gameObjectUserDataToCopy.getChanges()) {
|
||||
for (final Change change : changeEntry.getValue()) {
|
||||
final Change newChange = new Change();
|
||||
newChange.copyFrom(change);
|
||||
gameObjectUserData.getChanges().add(change.getId(), newChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
previousAliasToNewAlias.put(gameObject.getAlias(), gameObjectUserData.getNewId());
|
||||
changeset.getCustom().put(gameObjectUserData.getNewId(), gameObjectUserData);
|
||||
}
|
||||
final MutableObjectData changeEditManager = new MutableObjectData(this.worldEditStrings,
|
||||
this.worldEditorDataType, this.sourceSLKData, this.sourceSLKMetaData, changeset);
|
||||
for (final War3ID unitId : changeEditManager.keySet()) {
|
||||
final MutableGameObject unit = changeEditManager.get(unitId);
|
||||
for (final War3ID field : fieldsToCheck) {
|
||||
final String techtreeString = unit.getFieldAsString(field, 0);
|
||||
final java.util.List<String> techList = Arrays.asList(techtreeString.split(","));
|
||||
final ArrayList<String> resultingTechList = new ArrayList<>();
|
||||
for (final String tech : techList) {
|
||||
if (tech.length() == 4) {
|
||||
final War3ID newTechId = previousAliasToNewAlias.get(War3ID.fromString(tech));
|
||||
if (newTechId != null) {
|
||||
resultingTechList.add(newTechId.toString());
|
||||
}
|
||||
else {
|
||||
resultingTechList.add(tech);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultingTechList.add(tech);
|
||||
}
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (final String tech : resultingTechList) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(tech);
|
||||
}
|
||||
unit.setField(field, 0, sb.toString());
|
||||
}
|
||||
}
|
||||
return changeset;
|
||||
|
||||
}
|
||||
|
||||
public WorldEditorDataType getWorldEditorDataType() {
|
||||
return this.worldEditorDataType;
|
||||
}
|
||||
|
||||
public ObjectData getSourceSLKMetaData() {
|
||||
return this.sourceSLKMetaData;
|
||||
}
|
||||
|
||||
public void addChangeListener(final MutableObjectDataChangeListener listener) {
|
||||
this.changeNotifier.subscribe(listener);
|
||||
}
|
||||
|
||||
public void removeChangeListener(final MutableObjectDataChangeListener listener) {
|
||||
this.changeNotifier.unsubscribe(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of all Unit IDs in the map, at the cost of a lot of time to
|
||||
* go find them all.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public Set<War3ID> keySet() {
|
||||
if (this.cachedKeySet == null) {
|
||||
final Set<War3ID> customUnitKeys = this.editorData.getCustom().keySet();
|
||||
final Set<War3ID> customKeys = new HashSet<>(customUnitKeys);
|
||||
for (final String standardUnitKey : this.sourceSLKData.keySet()) {
|
||||
customKeys.add(War3ID.fromString(standardUnitKey));
|
||||
}
|
||||
this.cachedKeySet = customKeys;
|
||||
}
|
||||
return this.cachedKeySet;
|
||||
}
|
||||
|
||||
public void dropCachesHack() {
|
||||
this.cachedKeySet = null;
|
||||
this.cachedKeyToGameObject.clear();
|
||||
}
|
||||
|
||||
public MutableGameObject get(final War3ID id) {
|
||||
MutableGameObject mutableGameObject = this.cachedKeyToGameObject.get(id);
|
||||
if (mutableGameObject == null) {
|
||||
if (this.editorData.getCustom().containsKey(id)) {
|
||||
final ObjectDataChangeEntry customUnitData = this.editorData.getCustom().get(id);
|
||||
mutableGameObject = new MutableGameObject(
|
||||
this.sourceSLKData.get(customUnitData.getOldId().asStringValue()), customUnitData);
|
||||
this.cachedKeyToGameObject.put(id, mutableGameObject);
|
||||
}
|
||||
else if (this.editorData.getOriginal().containsKey(id)) {
|
||||
final ObjectDataChangeEntry customUnitData = this.editorData.getOriginal().get(id);
|
||||
mutableGameObject = new MutableGameObject(
|
||||
this.sourceSLKData.get(customUnitData.getOldId().asStringValue()),
|
||||
this.editorData.getOriginal().get(id));
|
||||
this.cachedKeyToGameObject.put(id, mutableGameObject);
|
||||
}
|
||||
else if (this.sourceSLKData.get(id.asStringValue()) != null) {
|
||||
mutableGameObject = new MutableGameObject(this.sourceSLKData.get(id.asStringValue()), null);
|
||||
this.cachedKeyToGameObject.put(id, mutableGameObject);
|
||||
}
|
||||
}
|
||||
return mutableGameObject;
|
||||
}
|
||||
|
||||
public MutableGameObject createNew(final War3ID id, final War3ID parent) {
|
||||
return createNew(id, parent, true);
|
||||
}
|
||||
|
||||
private MutableGameObject createNew(final War3ID id, final War3ID parent, final boolean fireListeners) {
|
||||
this.editorData.getCustom().put(id, new ObjectDataChangeEntry(parent, id));
|
||||
if (this.cachedKeySet != null) {
|
||||
this.cachedKeySet.add(id);
|
||||
}
|
||||
if (fireListeners) {
|
||||
this.changeNotifier.objectCreated(id);
|
||||
}
|
||||
return get(id);
|
||||
}
|
||||
|
||||
public void remove(final War3ID id) {
|
||||
remove(id, true);
|
||||
}
|
||||
|
||||
public void remove(final List<MutableGameObject> objects) {
|
||||
final List<War3ID> removedIds = new ArrayList<>();
|
||||
for (final MutableGameObject object : objects) {
|
||||
if (object.isCustom()) {
|
||||
remove(object.getAlias(), false);
|
||||
removedIds.add(object.getAlias());
|
||||
}
|
||||
}
|
||||
this.changeNotifier.objectsRemoved(removedIds.toArray(new War3ID[removedIds.size()]));
|
||||
}
|
||||
|
||||
private MutableGameObject remove(final War3ID id, final boolean fireListeners) {
|
||||
final ObjectDataChangeEntry removedObject = this.editorData.getCustom().remove(id);
|
||||
final MutableGameObject removedMutableObj = this.cachedKeyToGameObject.remove(id);
|
||||
if (this.cachedKeySet != null) {
|
||||
this.cachedKeySet.remove(id);
|
||||
}
|
||||
if (fireListeners) {
|
||||
this.changeNotifier.objectRemoved(id);
|
||||
}
|
||||
return removedMutableObj /* might be null based on cache, don't use */;
|
||||
}
|
||||
|
||||
private static boolean goodForId(final char c) {
|
||||
return Character.isDigit(c) || ((c >= 'A') && (c <= 'Z'));
|
||||
}
|
||||
|
||||
public War3ID getNextDefaultEditorId(final War3ID startingId) {
|
||||
War3ID newId = startingId;
|
||||
while (this.editorData.getCustom().containsKeyCaseInsensitive(newId)
|
||||
|| (this.sourceSLKData.get(newId.toString()) != null) || !goodForId(newId.charAt(1))
|
||||
|| !goodForId(newId.charAt(2)) || !goodForId(newId.charAt(3))) {
|
||||
// TODO good code general solution
|
||||
if (newId.charAt(3) == 'Z') {
|
||||
if (newId.charAt(2) == 'Z') {
|
||||
if (newId.charAt(1) == 'Z') {
|
||||
newId = new War3ID(((newId.getValue() / (256 * 256 * 256)) * 256 * 256 * 256)
|
||||
+ (256 * 256 * 256) + '0' + ('0' * 256) + ('0' * 256 * 256));
|
||||
}
|
||||
else {
|
||||
newId = new War3ID(
|
||||
((newId.getValue() / (256 * 256)) * 256 * 256) + (256 * 256) + '0' + ('0' * 256));
|
||||
}
|
||||
}
|
||||
else {
|
||||
newId = new War3ID(((newId.getValue() / 256) * 256) + 256 + '0');
|
||||
}
|
||||
}
|
||||
else {
|
||||
newId = new War3ID(newId.getValue() + 1);
|
||||
}
|
||||
}
|
||||
return newId;
|
||||
}
|
||||
|
||||
public static War3ID getNextDefaultEditorId(final War3ID startingId, final War3ObjectDataChangeset editorData,
|
||||
final ObjectData sourceSLKData) {
|
||||
War3ID newId = startingId;
|
||||
while (editorData.getCustom().containsKeyCaseInsensitive(newId) || (sourceSLKData.get(newId.toString()) != null)
|
||||
|| !goodForId(newId.charAt(1)) || !goodForId(newId.charAt(2)) || !goodForId(newId.charAt(3))) {
|
||||
newId = new War3ID(newId.getValue() + 1);
|
||||
}
|
||||
return newId;
|
||||
}
|
||||
|
||||
private static final War3ID BUFF_EDITOR_NAME = War3ID.fromString("fnam");
|
||||
private static final War3ID BUFF_BUFFTIP = War3ID.fromString("ftip");
|
||||
private static final War3ID UNIT_CAMPAIGN = War3ID.fromString("ucam");
|
||||
private static final War3ID UNIT_EDITOR_SUFFIX = War3ID.fromString("unsf");
|
||||
private static final War3ID ABIL_EDITOR_SUFFIX = War3ID.fromString("ansf");
|
||||
private static final War3ID DESTRUCTABLE_EDITOR_SUFFIX = War3ID.fromString("bsuf");
|
||||
private static final War3ID BUFF_EDITOR_SUFFIX = War3ID.fromString("fnsf");
|
||||
private static final War3ID UPGRADE_EDITOR_SUFFIX = War3ID.fromString("gnsf");
|
||||
private static final War3ID HERO_PROPER_NAMES = War3ID.fromString("upro");
|
||||
|
||||
private static final Set<War3ID> CATEGORY_FIELDS = new HashSet<>();
|
||||
private static final Set<War3ID> TEXT_FIELDS = new HashSet<>();
|
||||
private static final Set<War3ID> ICON_FIELDS = new HashSet<>();
|
||||
private static final Set<War3ID> FIELD_SETTINGS_FIELDS = new HashSet<>();
|
||||
|
||||
static {
|
||||
// categorizing - I thought these would be changeFlags value "c", but no luck
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("ubdg")); // is a building
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("uspe")); // categorize special
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("ucam")); // categorize campaign
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("urac")); // race
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("uine")); // in editor
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("ucls")); // sort string (not a real field, fanmade)
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("icla")); // item class
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("bcat")); // destructible category
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("dcat")); // doodad category
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("aher")); // hero ability
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("aite")); // item ability
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("arac")); // ability race
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("frac")); // buff race
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("feff")); // is effect
|
||||
|
||||
CATEGORY_FIELDS.add(War3ID.fromString("grac")); // upgrade race
|
||||
// field structure fields - doesn't seem to be changeFlags 's' like you might
|
||||
// hope
|
||||
FIELD_SETTINGS_FIELDS.add(War3ID.fromString("ubdg")); // unit is a builder
|
||||
FIELD_SETTINGS_FIELDS.add(War3ID.fromString("dvar")); // doodad variations
|
||||
FIELD_SETTINGS_FIELDS.add(War3ID.fromString("alev")); // ability level
|
||||
FIELD_SETTINGS_FIELDS.add(War3ID.fromString("glvl")); // upgrade max level
|
||||
}
|
||||
|
||||
public final class MutableGameObject {
|
||||
private final GameObject parentWC3Object;
|
||||
private ObjectDataChangeEntry customUnitData;
|
||||
|
||||
private void fireChangedEvent(final War3ID field, final int level) {
|
||||
final String changeFlags = MutableObjectData.this.sourceSLKMetaData.get(field.toString())
|
||||
.getField("changeFlags");
|
||||
if (CATEGORY_FIELDS.contains(field)) {
|
||||
MutableObjectData.this.changeNotifier.categoriesChanged(getAlias());
|
||||
}
|
||||
else if (changeFlags.contains("t")) {
|
||||
MutableObjectData.this.changeNotifier.textChanged(getAlias());
|
||||
}
|
||||
else if (changeFlags.contains("m")) {
|
||||
MutableObjectData.this.changeNotifier.modelChanged(getAlias());
|
||||
}
|
||||
else if (changeFlags.contains("i")) {
|
||||
MutableObjectData.this.changeNotifier.iconsChanged(getAlias());
|
||||
}
|
||||
else if (FIELD_SETTINGS_FIELDS.contains(field)) {
|
||||
MutableObjectData.this.changeNotifier.fieldsChanged(getAlias());
|
||||
}
|
||||
}
|
||||
|
||||
public MutableGameObject(final GameObject parentWC3Object, final ObjectDataChangeEntry customUnitData) {
|
||||
this.parentWC3Object = parentWC3Object;
|
||||
if (parentWC3Object == null) {
|
||||
System.err.println(
|
||||
"Parent object is null for " + customUnitData.getNewId() + ":" + customUnitData.getOldId());
|
||||
throw new AssertionError("parentWC3Object cannot be null");
|
||||
// this.parentWC3Object = new Element("", new DataTable());
|
||||
}
|
||||
this.customUnitData = customUnitData;
|
||||
}
|
||||
|
||||
public boolean hasCustomField(final War3ID field, final int level) {
|
||||
return getMatchingChange(field, level) != null;
|
||||
}
|
||||
|
||||
public boolean hasEditorData() {
|
||||
return (this.customUnitData != null) && (this.customUnitData.getChanges().size() > 0);
|
||||
}
|
||||
|
||||
public boolean isCustom() {
|
||||
return MutableObjectData.this.editorData.getCustom().containsKey(getAlias());
|
||||
}
|
||||
|
||||
public void setField(final War3ID field, final int level, final String value) {
|
||||
if (value.equals(getFieldStringFromSLKs(field, level))) {
|
||||
if (!value.equals(getFieldAsString(field, level))) {
|
||||
fireChangedEvent(field, level);
|
||||
System.out.println("field was reset");
|
||||
}
|
||||
else {
|
||||
System.out.println("field was unmodified");
|
||||
}
|
||||
resetFieldToDefaults(field, level);
|
||||
return;
|
||||
}
|
||||
final Change matchingChange = getOrCreateMatchingChange(field, level);
|
||||
matchingChange.setStrval(value);
|
||||
matchingChange.setVartype(War3ObjectDataChangeset.VAR_TYPE_STRING);
|
||||
System.out.println("field created change");
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
|
||||
public void setField(final War3ID field, final int level, final boolean value) {
|
||||
if (value == (asInt(getFieldStringFromSLKs(field, level).trim()) == 1)) {
|
||||
if (value != getFieldAsBoolean(field, level)) {
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
resetFieldToDefaults(field, level);
|
||||
return;
|
||||
}
|
||||
final Change matchingChange = getOrCreateMatchingChange(field, level);
|
||||
matchingChange.setBoolval(value);
|
||||
matchingChange.setVartype(War3ObjectDataChangeset.VAR_TYPE_BOOLEAN);
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
|
||||
public void setField(final War3ID field, final int level, final int value) {
|
||||
if (value == asInt(getFieldStringFromSLKs(field, level).trim())) {
|
||||
if (value != getFieldAsInteger(field, level)) {
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
resetFieldToDefaults(field, level);
|
||||
return;
|
||||
}
|
||||
final Change matchingChange = getOrCreateMatchingChange(field, level);
|
||||
matchingChange.setLongval(value);
|
||||
matchingChange.setVartype(War3ObjectDataChangeset.VAR_TYPE_INT);
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
|
||||
public void resetFieldToDefaults(final War3ID field, final int level) {
|
||||
final Change existingChange = getMatchingChange(field, level);
|
||||
if ((existingChange != null) && (this.customUnitData != null)) {
|
||||
this.customUnitData.getChanges().delete(field, existingChange);
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void setField(final War3ID field, final int level, final float value) {
|
||||
if (Math.abs(value - asFloat(getFieldStringFromSLKs(field, level).trim())) < 0.00001f) {
|
||||
if (Math.abs(value - getFieldAsFloat(field, level)) > 0.00001f) {
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
resetFieldToDefaults(field, level);
|
||||
return;
|
||||
}
|
||||
final Change matchingChange = getOrCreateMatchingChange(field, level);
|
||||
matchingChange.setRealval(value);
|
||||
final boolean unsigned = MutableObjectData.this.sourceSLKMetaData.get(field.asStringValue())
|
||||
.getField("type").equals("unreal");
|
||||
matchingChange.setVartype(
|
||||
unsigned ? War3ObjectDataChangeset.VAR_TYPE_UNREAL : War3ObjectDataChangeset.VAR_TYPE_REAL);
|
||||
fireChangedEvent(field, level);
|
||||
}
|
||||
|
||||
private Change getOrCreateMatchingChange(final War3ID field, final int level) {
|
||||
if (this.customUnitData == null) {
|
||||
final War3ID war3Id = War3ID.fromString(this.parentWC3Object.getId());
|
||||
final ObjectDataChangeEntry newCustomUnitData = new ObjectDataChangeEntry(war3Id, War3ID.NONE);
|
||||
MutableObjectData.this.editorData.getOriginal().put(war3Id, newCustomUnitData);
|
||||
this.customUnitData = newCustomUnitData;
|
||||
}
|
||||
Change matchingChange = getMatchingChange(field, level);
|
||||
if (matchingChange == null) {
|
||||
final ChangeMap changeMap = this.customUnitData.getChanges();
|
||||
final List<Change> changeList = changeMap.get(field);
|
||||
matchingChange = new Change();
|
||||
matchingChange.setId(field);
|
||||
matchingChange.setLevel(level);
|
||||
if (MutableObjectData.this.editorData.extended()) {
|
||||
// dunno why, but Blizzard sure likes those dataptrs in the ability data
|
||||
// my code should grab 0 when the metadata lacks this field
|
||||
matchingChange.setDataptr(
|
||||
MutableObjectData.this.sourceSLKMetaData.get(field.asStringValue()).getFieldValue("data"));
|
||||
}
|
||||
if (changeList == null) {
|
||||
changeMap.add(field, matchingChange);
|
||||
}
|
||||
else {
|
||||
boolean insertedChange = false;
|
||||
for (int i = 0; i < changeList.size(); i++) {
|
||||
if (changeList.get(i).getLevel() > level) {
|
||||
insertedChange = true;
|
||||
changeList.add(i, matchingChange);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!insertedChange) {
|
||||
changeList.add(changeList.size(), matchingChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchingChange;
|
||||
}
|
||||
|
||||
public String getFieldAsString(final War3ID field, final int level) {
|
||||
final Change matchingChange = getMatchingChange(field, level);
|
||||
if (matchingChange != null) {
|
||||
if (matchingChange.getVartype() != War3ObjectDataChangeset.VAR_TYPE_STRING) {
|
||||
throw new IllegalStateException(
|
||||
"Requested string value of '" + field + "' from '" + this.parentWC3Object.getId()
|
||||
+ "', but this field was not a string! vartype=" + matchingChange.getVartype());
|
||||
}
|
||||
return matchingChange.getStrval();
|
||||
}
|
||||
// no luck with custom data, look at the standard data
|
||||
int slkLevel = level;
|
||||
if (MutableObjectData.this.worldEditorDataType == WorldEditorDataType.UPGRADES) {
|
||||
slkLevel -= 1;
|
||||
}
|
||||
return getFieldStringFromSLKs(field, slkLevel);
|
||||
}
|
||||
|
||||
private Change getMatchingChange(final War3ID field, final int level) {
|
||||
Change matchingChange = null;
|
||||
if (this.customUnitData == null) {
|
||||
return null;
|
||||
}
|
||||
final List<Change> changeList = this.customUnitData.getChanges().get(field);
|
||||
if (changeList != null) {
|
||||
for (final Change change : changeList) {
|
||||
if (change.getLevel() == level) {
|
||||
matchingChange = change;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchingChange;
|
||||
}
|
||||
|
||||
public String readSLKTag(final String key) {
|
||||
if (MutableObjectData.this.metaNameToMetaId.containsKey(key)) {
|
||||
return getFieldAsString(MutableObjectData.this.metaNameToMetaId.get(key), 0);
|
||||
}
|
||||
return this.parentWC3Object.getField(key);
|
||||
}
|
||||
|
||||
public boolean readSLKTagBoolean(final String key) {
|
||||
if (MutableObjectData.this.metaNameToMetaId.containsKey(key)) {
|
||||
return getFieldAsBoolean(MutableObjectData.this.metaNameToMetaId.get(key), 0);
|
||||
}
|
||||
return this.parentWC3Object.getFieldValue(key) == 1;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String name = getFieldAsString(MutableObjectData.this.editorData.getNameField(),
|
||||
MutableObjectData.this.worldEditorDataType == WorldEditorDataType.UPGRADES ? 1 : 0);
|
||||
boolean nameKnown = name.length() >= 1;
|
||||
if (!nameKnown && !readSLKTag("code").equals(getAlias().toString()) && (readSLKTag("code").length() >= 4)
|
||||
&& !isCustom()) {
|
||||
final MutableGameObject codeObject = get(War3ID.fromString(readSLKTag("code").substring(0, 4)));
|
||||
if (codeObject != null) {
|
||||
name = codeObject.getName();
|
||||
nameKnown = true;
|
||||
}
|
||||
}
|
||||
String suf = "";
|
||||
switch (MutableObjectData.this.worldEditorDataType) {
|
||||
case ABILITIES:
|
||||
suf = getFieldAsString(ABIL_EDITOR_SUFFIX, 0);
|
||||
break;
|
||||
case BUFFS_EFFECTS:
|
||||
final String editorName = getFieldAsString(BUFF_EDITOR_NAME, 0);
|
||||
if (!nameKnown && (editorName.length() > 1)) {
|
||||
name = editorName;
|
||||
nameKnown = true;
|
||||
}
|
||||
final String buffTip = getFieldAsString(BUFF_BUFFTIP, 0);
|
||||
if (!nameKnown && (buffTip.length() > 1)) {
|
||||
name = buffTip;
|
||||
nameKnown = true;
|
||||
}
|
||||
suf = getFieldAsString(BUFF_EDITOR_SUFFIX, 0);
|
||||
break;
|
||||
case DESTRUCTIBLES:
|
||||
suf = getFieldAsString(DESTRUCTABLE_EDITOR_SUFFIX, 0);
|
||||
break;
|
||||
case DOODADS:
|
||||
break;
|
||||
case ITEM:
|
||||
break;
|
||||
case UNITS:
|
||||
if (getFieldAsBoolean(UNIT_CAMPAIGN, 0) && Character.isUpperCase(getAlias().charAt(0))) {
|
||||
name = getFieldAsString(HERO_PROPER_NAMES, 0);
|
||||
if (name.contains(",")) {
|
||||
name = name.split(",")[0];
|
||||
}
|
||||
}
|
||||
suf = getFieldAsString(UNIT_EDITOR_SUFFIX, 0);
|
||||
break;
|
||||
case UPGRADES:
|
||||
suf = getFieldAsString(UPGRADE_EDITOR_SUFFIX, 1);
|
||||
break;
|
||||
}
|
||||
if (nameKnown/* && name.startsWith("WESTRING") */) {
|
||||
if (!name.contains(" ")) {
|
||||
// name = WEString.getString(name);
|
||||
}
|
||||
else {
|
||||
final String[] names = name.split(" ");
|
||||
name = "";
|
||||
for (final String subName : names) {
|
||||
if (name.length() > 0) {
|
||||
name += " ";
|
||||
}
|
||||
// if (subName.startsWith("WESTRING")) {
|
||||
// name += WEString.getString(subName);
|
||||
// } else {
|
||||
name += subName;
|
||||
// }
|
||||
}
|
||||
}
|
||||
if (name.startsWith("\"") && name.endsWith("\"")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
}
|
||||
if (!nameKnown) {
|
||||
name = MutableObjectData.this.worldEditStrings.getString("WESTRING_UNKNOWN") + " '"
|
||||
+ getAlias().toString() + "'";
|
||||
}
|
||||
if ((suf.length() > 0) && !suf.equals("_")) {
|
||||
// if (suf.startsWith("WESTRING")) {
|
||||
// suf = WEString.getString(suf);
|
||||
// }
|
||||
if (!suf.startsWith(" ")) {
|
||||
name += " ";
|
||||
}
|
||||
name += suf;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private String getFieldStringFromSLKs(final War3ID field, final int level) {
|
||||
final GameObject metaData = MutableObjectData.this.sourceSLKMetaData.get(field.asStringValue());
|
||||
if (metaData == null) {
|
||||
if (MutableObjectData.this.worldEditorDataType == WorldEditorDataType.UNITS) {
|
||||
if (ROC_SUPPORT_URAC.equals(field)) {
|
||||
return this.parentWC3Object.getField("race");
|
||||
}
|
||||
else if (ROC_SUPPORT_UCAM.equals(field)) {
|
||||
return "0";
|
||||
}
|
||||
else if (ROC_SUPPORT_USPE.equals(field)) {
|
||||
return this.parentWC3Object.getField("special");
|
||||
}
|
||||
else if (ROC_SUPPORT_UBDG.equals(field)) {
|
||||
return this.parentWC3Object.getField("isbldg");
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Program requested " + field.toString() + " from "
|
||||
+ MutableObjectData.this.worldEditorDataType);
|
||||
}
|
||||
if (this.parentWC3Object == null) {
|
||||
throw new IllegalStateException("corrupted unit, no parent unit id");
|
||||
}
|
||||
int index = metaData.getFieldValue("index");
|
||||
final String upgradeHack = metaData.getField("appendIndex");
|
||||
if ("0".equals(upgradeHack)) {
|
||||
// Engage magic upgrade hack to replace index with level
|
||||
if (!field.toString().equals("gbpx") && !field.toString().equals("gbpy")) {
|
||||
index = level;
|
||||
}
|
||||
}
|
||||
else if ((index != -1) && (level > 0)) {
|
||||
index = level - 1;
|
||||
}
|
||||
if (index != -1) {
|
||||
final String fieldStringValue = this.parentWC3Object
|
||||
.getField(getEditorMetaDataDisplayKey(level, metaData), index);
|
||||
return fieldStringValue;
|
||||
}
|
||||
final String fieldStringValue = this.parentWC3Object.getField(getEditorMetaDataDisplayKey(level, metaData));
|
||||
return fieldStringValue;
|
||||
}
|
||||
|
||||
public int getFieldAsInteger(final War3ID field, final int level) {
|
||||
final Change matchingChange = getMatchingChange(field, level);
|
||||
if (matchingChange != null) {
|
||||
if (matchingChange.getVartype() != War3ObjectDataChangeset.VAR_TYPE_INT) {
|
||||
throw new IllegalStateException(
|
||||
"Requested integer value of '" + field + "' from '" + this.parentWC3Object.getId()
|
||||
+ "', but this field was not an int! vartype=" + matchingChange.getVartype());
|
||||
}
|
||||
return matchingChange.getLongval();
|
||||
}
|
||||
// no luck with custom data, look at the standard data
|
||||
try {
|
||||
return Integer.parseInt(getFieldStringFromSLKs(field, level));
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getFieldAsBoolean(final War3ID field, final int level) {
|
||||
final Change matchingChange = getMatchingChange(field, level);
|
||||
if (matchingChange != null) {
|
||||
if (matchingChange.getVartype() != War3ObjectDataChangeset.VAR_TYPE_BOOLEAN) {
|
||||
if (matchingChange.getVartype() == War3ObjectDataChangeset.VAR_TYPE_INT) {
|
||||
return matchingChange.getLongval() == 1;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Requested boolean value of '" + field + "' from '" + this.parentWC3Object.getId()
|
||||
+ "', but this field was not a bool! vartype=" + matchingChange.getVartype());
|
||||
}
|
||||
}
|
||||
return matchingChange.isBoolval();
|
||||
}
|
||||
// no luck with custom data, look at the standard data
|
||||
try {
|
||||
return Integer.parseInt(getFieldStringFromSLKs(field, level)) == 1;
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public float getFieldAsFloat(final War3ID field, final int level) {
|
||||
final Change matchingChange = getMatchingChange(field, level);
|
||||
if (matchingChange != null) {
|
||||
if ((matchingChange.getVartype() != War3ObjectDataChangeset.VAR_TYPE_REAL)
|
||||
&& (matchingChange.getVartype() != War3ObjectDataChangeset.VAR_TYPE_UNREAL)) {
|
||||
throw new IllegalStateException(
|
||||
"Requested float value of '" + field + "' from '" + this.parentWC3Object.getId()
|
||||
+ "', but this field was not a float! vartype=" + matchingChange.getVartype());
|
||||
}
|
||||
return matchingChange.getRealval();
|
||||
}
|
||||
// no luck with custom data, look at the standard data
|
||||
try {
|
||||
return Float.parseFloat(getFieldStringFromSLKs(field, level));
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public War3ID getAlias() {
|
||||
if (this.customUnitData == null) {
|
||||
return War3ID.fromString(this.parentWC3Object.getId());
|
||||
}
|
||||
if (War3ID.NONE.equals(this.customUnitData.getNewId())) {
|
||||
return this.customUnitData.getOldId();
|
||||
}
|
||||
return this.customUnitData.getNewId();
|
||||
}
|
||||
|
||||
public War3ID getCode() {
|
||||
if (this.customUnitData == null) {
|
||||
if ((MutableObjectData.this.worldEditorDataType == WorldEditorDataType.ABILITIES)
|
||||
|| (MutableObjectData.this.worldEditorDataType == WorldEditorDataType.BUFFS_EFFECTS)) {
|
||||
return War3ID.fromString(this.parentWC3Object.getField("code"));
|
||||
}
|
||||
else {
|
||||
return War3ID.fromString(this.parentWC3Object.getId());
|
||||
}
|
||||
}
|
||||
if (War3ID.NONE.equals(this.customUnitData.getNewId())) {
|
||||
if ((MutableObjectData.this.worldEditorDataType == WorldEditorDataType.ABILITIES)
|
||||
|| (MutableObjectData.this.worldEditorDataType == WorldEditorDataType.BUFFS_EFFECTS)) {
|
||||
return War3ID.fromString(this.parentWC3Object.getField("code"));
|
||||
}
|
||||
else {
|
||||
return this.customUnitData.getOldId();
|
||||
}
|
||||
}
|
||||
return this.customUnitData.getOldId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int asInt(final String text) {
|
||||
return text == null ? 0
|
||||
: "".equals(text) ? 0 : "-".equals(text) ? 0 : "_".equals(text) ? 0 : Integer.parseInt(text);
|
||||
}
|
||||
|
||||
private static float asFloat(final String text) {
|
||||
return text == null ? 0
|
||||
: "".equals(text) ? 0 : "-".equals(text) ? 0 : "_".equals(text) ? 0 : Float.parseFloat(text);
|
||||
}
|
||||
|
||||
public enum WorldEditorDataType {
|
||||
UNITS("w3u"),
|
||||
ITEM("w3t"),
|
||||
DESTRUCTIBLES("w3b"),
|
||||
DOODADS("w3d"),
|
||||
ABILITIES("w3a"),
|
||||
BUFFS_EFFECTS("w3h"),
|
||||
UPGRADES("w3q");
|
||||
|
||||
private String extension;
|
||||
|
||||
private WorldEditorDataType(final String extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return this.extension;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getEditorMetaDataDisplayKey(int level, final GameObject metaData) {
|
||||
final int index = metaData.getFieldValue("index");
|
||||
String metaDataName = metaData.getField("field");
|
||||
final int repeatCount = metaData.getFieldValue("repeat");
|
||||
final String upgradeHack = metaData.getField("appendIndex");
|
||||
final boolean repeats = (repeatCount > 0) && !"0".equals(upgradeHack);
|
||||
final int data = metaData.getFieldValue("data");
|
||||
if (data > 0) {
|
||||
metaDataName += (char) ('A' + (data - 1));
|
||||
}
|
||||
if ("1".equals(upgradeHack)) {
|
||||
final int upgradeExtensionLevel = level - 1;
|
||||
if (upgradeExtensionLevel > 0) {
|
||||
metaDataName += Integer.toString(upgradeExtensionLevel);
|
||||
}
|
||||
}
|
||||
else if (repeats && (index == -1)) {
|
||||
if (level == 0) {
|
||||
level = 1;
|
||||
}
|
||||
if (repeatCount >= 10) {
|
||||
metaDataName += String.format("%2d", level).replace(' ', '0');
|
||||
}
|
||||
else {
|
||||
metaDataName += Integer.toString(level);
|
||||
}
|
||||
}
|
||||
return metaDataName;
|
||||
}
|
||||
|
||||
public static String getDisplayAsRawDataName(final MutableGameObject gameObject) {
|
||||
String aliasString = gameObject.getAlias().toString();
|
||||
if (!gameObject.getAlias().equals(gameObject.getCode())) {
|
||||
aliasString += ":" + gameObject.getCode().toString();
|
||||
}
|
||||
return aliasString + " (" + gameObject.getName() + ")";
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.etheller.warsmash.units.manager;
|
||||
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public interface MutableObjectDataChangeListener {
|
||||
void textChanged(War3ID changedObject);
|
||||
|
||||
void iconsChanged(War3ID changedObject);
|
||||
|
||||
void categoriesChanged(War3ID changedObject);
|
||||
|
||||
void fieldsChanged(War3ID changedObject);
|
||||
|
||||
void modelChanged(War3ID changedObject);
|
||||
|
||||
void objectCreated(War3ID newObject);
|
||||
|
||||
void objectsCreated(War3ID[] newObject);
|
||||
|
||||
void objectRemoved(War3ID removedObject);
|
||||
|
||||
void objectsRemoved(War3ID[] removedObject);
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.etheller.warsmash.units.manager;
|
||||
|
||||
import com.etheller.warsmash.util.SubscriberSetNotifier;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
|
||||
public final class MutableObjectDataChangeNotifier extends SubscriberSetNotifier<MutableObjectDataChangeListener>
|
||||
implements MutableObjectDataChangeListener {
|
||||
|
||||
@Override
|
||||
public void textChanged(final War3ID changedObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.textChanged(changedObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void categoriesChanged(final War3ID changedObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.categoriesChanged(changedObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iconsChanged(final War3ID changedObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.iconsChanged(changedObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldsChanged(final War3ID changedObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.fieldsChanged(changedObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modelChanged(final War3ID changedObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.modelChanged(changedObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void objectCreated(final War3ID newObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.objectCreated(newObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void objectsCreated(final War3ID[] newObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.objectsCreated(newObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void objectRemoved(final War3ID newObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.objectRemoved(newObject);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void objectsRemoved(final War3ID[] newObject) {
|
||||
for (final MutableObjectDataChangeListener listener : this.set) {
|
||||
listener.objectsRemoved(newObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -8,6 +7,7 @@ import java.awt.image.ColorModel;
|
||||
import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.Pixmap;
|
||||
import com.badlogic.gdx.graphics.Pixmap.Format;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
@ -30,14 +30,20 @@ public final class ImageUtils {
|
||||
// for
|
||||
// RGB
|
||||
|
||||
final Pixmap pixmap = new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888);
|
||||
final Pixmap pixmap = new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888) {
|
||||
@Override
|
||||
public int getGLInternalFormat() {
|
||||
return GL30.GL_SRGB8_ALPHA8;
|
||||
}
|
||||
};
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
final int pixel = pixels[(y * image.getWidth()) + x];
|
||||
pixmap.drawPixel(x, y, (pixel << 8) | (pixel >>> 24));
|
||||
}
|
||||
}
|
||||
return new Texture(pixmap);
|
||||
final Texture texture = new Texture(pixmap);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,12 +72,10 @@ public final class ImageUtils {
|
||||
DataBuffer.TYPE_BYTE);
|
||||
final BufferedImage lRGB = new BufferedImage(lRGBModel,
|
||||
lRGBModel.createCompatibleWritableRaster(in.getWidth(), in.getHeight()), false, null);
|
||||
final Graphics2D graphic = lRGB.createGraphics();
|
||||
try {
|
||||
graphic.drawImage(in, 0, 0, null);
|
||||
}
|
||||
finally {
|
||||
graphic.dispose();
|
||||
for (int i = 0; i < in.getWidth(); i++) {
|
||||
for (int j = 0; j < in.getHeight(); j++) {
|
||||
lRGB.setRGB(i, j, in.getRGB(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to sRGB.
|
||||
|
@ -13,6 +13,10 @@ import java.util.Map;
|
||||
public class MappedData {
|
||||
private final Map<String, MappedDataRow> map = new HashMap<>();
|
||||
|
||||
public MappedData() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public MappedData(final String buffer) {
|
||||
if (buffer != null) {
|
||||
this.load(buffer);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@ -49,6 +50,18 @@ public class ParseUtils {
|
||||
return array;
|
||||
}
|
||||
|
||||
public static void readInt32Array(final LittleEndianDataInputStream stream, final int[] array) throws IOException {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = stream.readInt();
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] readInt32Array(final LittleEndianDataInputStream stream, final int length) throws IOException {
|
||||
final int[] array = new int[length];
|
||||
readInt32Array(stream, array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static void readUInt16Array(final LittleEndianDataInputStream stream, final int[] array) throws IOException {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = readUInt16(stream);
|
||||
@ -115,6 +128,13 @@ public class ParseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeInt32Array(final LittleEndianDataOutputStream stream, final int[] array)
|
||||
throws IOException {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
stream.writeInt(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeUInt16Array(final LittleEndianDataOutputStream stream, final int[] array)
|
||||
throws IOException {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
@ -138,4 +158,21 @@ public class ParseUtils {
|
||||
final String name = new String(recycleByteArray, 0, i, ParseUtils.UTF8);
|
||||
return name;
|
||||
}
|
||||
|
||||
public static String readUntilNull(final LittleEndianDataInputStream stream) throws IOException {
|
||||
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int c;
|
||||
while (((c = stream.read()) != 0) && (c != -1)) {
|
||||
baos.write(c);
|
||||
}
|
||||
return new String(baos.toByteArray(), ParseUtils.UTF8);
|
||||
}
|
||||
|
||||
public static void writeWithNullTerminator(final LittleEndianDataOutputStream stream, final String name)
|
||||
throws IOException {
|
||||
final byte[] nameBytes = name.getBytes(ParseUtils.UTF8);
|
||||
stream.write(nameBytes);
|
||||
stream.write(0);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
@ -9,6 +14,8 @@ import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public enum RenderMathUtils {
|
||||
;
|
||||
public static final float[] EMPTY_FLOAT_ARRAY = new float[0];
|
||||
public static final long[] EMPTY_LONG_ARRAY = new long[0];
|
||||
public static final Quaternion QUAT_DEFAULT = new Quaternion(0, 0, 0, 1);
|
||||
public static final Vector3 VEC3_ONE = new Vector3(1, 1, 1);
|
||||
public static final Vector3 VEC3_UNIT_X = new Vector3(1, 0, 0);
|
||||
@ -42,22 +49,22 @@ public enum RenderMathUtils {
|
||||
final float sy = s.y;
|
||||
final float sz = s.z;
|
||||
out.val[Matrix4.M00] = (1 - (yy + zz)) * sx;
|
||||
out.val[Matrix4.M01] = (xy + wz) * sx;
|
||||
out.val[Matrix4.M02] = (xz - wy) * sx;
|
||||
out.val[Matrix4.M03] = 0;
|
||||
out.val[Matrix4.M10] = (xy - wz) * sy;
|
||||
out.val[Matrix4.M10] = (xy + wz) * sx;
|
||||
out.val[Matrix4.M20] = (xz - wy) * sx;
|
||||
out.val[Matrix4.M30] = 0;
|
||||
out.val[Matrix4.M01] = (xy - wz) * sy;
|
||||
out.val[Matrix4.M11] = (1 - (xx + zz)) * sy;
|
||||
out.val[Matrix4.M12] = (yz + wx) * sy;
|
||||
out.val[Matrix4.M13] = 0;
|
||||
out.val[Matrix4.M20] = (xz + wy) * sz;
|
||||
out.val[Matrix4.M21] = (yz - wx) * sz;
|
||||
out.val[Matrix4.M21] = (yz + wx) * sy;
|
||||
out.val[Matrix4.M31] = 0;
|
||||
out.val[Matrix4.M02] = (xz + wy) * sz;
|
||||
out.val[Matrix4.M12] = (yz - wx) * sz;
|
||||
out.val[Matrix4.M22] = (1 - (xx + yy)) * sz;
|
||||
out.val[Matrix4.M23] = 0;
|
||||
out.val[Matrix4.M30] = (v.x + pivot.x) - ((out.val[Matrix4.M00] * pivot.x) + (out.val[Matrix4.M10] * pivot.y)
|
||||
+ (out.val[Matrix4.M20] * pivot.z));
|
||||
out.val[Matrix4.M31] = (v.y + pivot.y) - ((out.val[Matrix4.M01] * pivot.x) + (out.val[Matrix4.M11] * pivot.y)
|
||||
+ (out.val[Matrix4.M21] * pivot.z));
|
||||
out.val[Matrix4.M32] = (v.z + pivot.z) - ((out.val[Matrix4.M02] * pivot.x) + (out.val[Matrix4.M12] * pivot.y)
|
||||
out.val[Matrix4.M32] = 0;
|
||||
out.val[Matrix4.M03] = (v.x + pivot.x) - ((out.val[Matrix4.M00] * pivot.x) + (out.val[Matrix4.M01] * pivot.y)
|
||||
+ (out.val[Matrix4.M02] * pivot.z));
|
||||
out.val[Matrix4.M13] = (v.y + pivot.y) - ((out.val[Matrix4.M10] * pivot.x) + (out.val[Matrix4.M11] * pivot.y)
|
||||
+ (out.val[Matrix4.M12] * pivot.z));
|
||||
out.val[Matrix4.M23] = (v.z + pivot.z) - ((out.val[Matrix4.M20] * pivot.x) + (out.val[Matrix4.M21] * pivot.y)
|
||||
+ (out.val[Matrix4.M22] * pivot.z));
|
||||
out.val[Matrix4.M33] = 1;
|
||||
}
|
||||
@ -86,20 +93,20 @@ public enum RenderMathUtils {
|
||||
final float sy = s.y;
|
||||
final float sz = s.z;
|
||||
out.val[Matrix4.M00] = (1 - (yy + zz)) * sx;
|
||||
out.val[Matrix4.M01] = (xy + wz) * sx;
|
||||
out.val[Matrix4.M02] = (xz - wy) * sx;
|
||||
out.val[Matrix4.M03] = 0;
|
||||
out.val[Matrix4.M10] = (xy - wz) * sy;
|
||||
out.val[Matrix4.M10] = (xy + wz) * sx;
|
||||
out.val[Matrix4.M20] = (xz - wy) * sx;
|
||||
out.val[Matrix4.M30] = 0;
|
||||
out.val[Matrix4.M01] = (xy - wz) * sy;
|
||||
out.val[Matrix4.M11] = (1 - (xx + zz)) * sy;
|
||||
out.val[Matrix4.M12] = (yz + wx) * sy;
|
||||
out.val[Matrix4.M13] = 0;
|
||||
out.val[Matrix4.M20] = (xz + wy) * sz;
|
||||
out.val[Matrix4.M21] = (yz - wx) * sz;
|
||||
out.val[Matrix4.M21] = (yz + wx) * sy;
|
||||
out.val[Matrix4.M31] = 0;
|
||||
out.val[Matrix4.M02] = (xz + wy) * sz;
|
||||
out.val[Matrix4.M12] = (yz - wx) * sz;
|
||||
out.val[Matrix4.M22] = (1 - (xx + yy)) * sz;
|
||||
out.val[Matrix4.M23] = 0;
|
||||
out.val[Matrix4.M30] = v.x;
|
||||
out.val[Matrix4.M31] = v.y;
|
||||
out.val[Matrix4.M32] = v.z;
|
||||
out.val[Matrix4.M32] = 0;
|
||||
out.val[Matrix4.M03] = v.x;
|
||||
out.val[Matrix4.M13] = v.y;
|
||||
out.val[Matrix4.M23] = v.z;
|
||||
out.val[Matrix4.M33] = 1;
|
||||
}
|
||||
|
||||
@ -158,27 +165,27 @@ public enum RenderMathUtils {
|
||||
final float far) {
|
||||
final float f = 1.0f / (float) Math.tan(fovy / 2), nf;
|
||||
out.val[Matrix4.M00] = f / aspect;
|
||||
out.val[Matrix4.M01] = 0;
|
||||
out.val[Matrix4.M02] = 0;
|
||||
out.val[Matrix4.M03] = 0;
|
||||
out.val[Matrix4.M10] = 0;
|
||||
out.val[Matrix4.M11] = f;
|
||||
out.val[Matrix4.M12] = 0;
|
||||
out.val[Matrix4.M13] = 0;
|
||||
out.val[Matrix4.M20] = 0;
|
||||
out.val[Matrix4.M21] = 0;
|
||||
out.val[Matrix4.M23] = -1;
|
||||
out.val[Matrix4.M30] = 0;
|
||||
out.val[Matrix4.M01] = 0;
|
||||
out.val[Matrix4.M11] = f;
|
||||
out.val[Matrix4.M21] = 0;
|
||||
out.val[Matrix4.M31] = 0;
|
||||
out.val[Matrix4.M02] = 0;
|
||||
out.val[Matrix4.M12] = 0;
|
||||
out.val[Matrix4.M32] = -1;
|
||||
out.val[Matrix4.M03] = 0;
|
||||
out.val[Matrix4.M13] = 0;
|
||||
out.val[Matrix4.M33] = 0;
|
||||
if (!Double.isNaN(far) && !Double.isInfinite(far)) {
|
||||
nf = 1 / (near - far);
|
||||
out.val[Matrix4.M22] = (far + near) * nf;
|
||||
out.val[Matrix4.M32] = (2 * far * near) * nf;
|
||||
out.val[Matrix4.M23] = (2 * far * near) * nf;
|
||||
}
|
||||
else {
|
||||
out.val[Matrix4.M22] = -1;
|
||||
out.val[Matrix4.M32] = -2 * near;
|
||||
out.val[Matrix4.M23] = -2 * near;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -189,32 +196,32 @@ public enum RenderMathUtils {
|
||||
final float bt = 1 / (bottom - top);
|
||||
final float nf = 1 / (near - far);
|
||||
out.val[Matrix4.M00] = -2 * lr;
|
||||
out.val[Matrix4.M01] = 0;
|
||||
out.val[Matrix4.M02] = 0;
|
||||
out.val[Matrix4.M03] = 0;
|
||||
|
||||
out.val[Matrix4.M10] = 0;
|
||||
out.val[Matrix4.M11] = -2 * bt;
|
||||
out.val[Matrix4.M12] = 0;
|
||||
out.val[Matrix4.M13] = 0;
|
||||
|
||||
out.val[Matrix4.M20] = 0;
|
||||
out.val[Matrix4.M21] = 0;
|
||||
out.val[Matrix4.M22] = 2 * nf;
|
||||
out.val[Matrix4.M23] = 0;
|
||||
out.val[Matrix4.M30] = 0;
|
||||
|
||||
out.val[Matrix4.M30] = (left + right) * lr;
|
||||
out.val[Matrix4.M31] = (top + bottom) * bt;
|
||||
out.val[Matrix4.M32] = (far + near) * nf;
|
||||
out.val[Matrix4.M01] = 0;
|
||||
out.val[Matrix4.M11] = -2 * bt;
|
||||
out.val[Matrix4.M21] = 0;
|
||||
out.val[Matrix4.M31] = 0;
|
||||
|
||||
out.val[Matrix4.M02] = 0;
|
||||
out.val[Matrix4.M12] = 0;
|
||||
out.val[Matrix4.M22] = 2 * nf;
|
||||
out.val[Matrix4.M32] = 0;
|
||||
|
||||
out.val[Matrix4.M03] = (left + right) * lr;
|
||||
out.val[Matrix4.M13] = (top + bottom) * bt;
|
||||
out.val[Matrix4.M23] = (far + near) * nf;
|
||||
out.val[Matrix4.M33] = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void unpackPlanes(final Vector4[] planes, final Matrix4 m) {
|
||||
final float a00 = m.val[Matrix4.M00], a01 = m.val[Matrix4.M01], a02 = m.val[Matrix4.M02],
|
||||
a03 = m.val[Matrix4.M03], a10 = m.val[Matrix4.M10], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M12],
|
||||
a13 = m.val[Matrix4.M13], a20 = m.val[Matrix4.M20], a21 = m.val[Matrix4.M21], a22 = m.val[Matrix4.M22],
|
||||
a23 = m.val[Matrix4.M23], a30 = m.val[Matrix4.M30], a31 = m.val[Matrix4.M31], a32 = m.val[Matrix4.M32],
|
||||
final float a00 = m.val[Matrix4.M00], a01 = m.val[Matrix4.M10], a02 = m.val[Matrix4.M20],
|
||||
a03 = m.val[Matrix4.M30], a10 = m.val[Matrix4.M01], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M21],
|
||||
a13 = m.val[Matrix4.M31], a20 = m.val[Matrix4.M02], a21 = m.val[Matrix4.M12], a22 = m.val[Matrix4.M22],
|
||||
a23 = m.val[Matrix4.M32], a30 = m.val[Matrix4.M03], a31 = m.val[Matrix4.M13], a32 = m.val[Matrix4.M23],
|
||||
a33 = m.val[Matrix4.M33];
|
||||
|
||||
// Left clipping plane
|
||||
@ -443,4 +450,37 @@ public enum RenderMathUtils {
|
||||
return out;
|
||||
}
|
||||
|
||||
public static ShortBuffer wrapFaces(final int[] faces) {
|
||||
final ShortBuffer wrapper = ByteBuffer.allocateDirect(faces.length * 2).order(ByteOrder.nativeOrder())
|
||||
.asShortBuffer();
|
||||
for (final int face : faces) {
|
||||
wrapper.put((short) face);
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static ByteBuffer wrap(final byte[] skin) {
|
||||
final ByteBuffer wrapper = ByteBuffer.allocateDirect(skin.length).order(ByteOrder.nativeOrder());
|
||||
wrapper.put(skin);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static FloatBuffer wrap(final float[] positions) {
|
||||
final FloatBuffer wrapper = ByteBuffer.allocateDirect(positions.length * 4).order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
wrapper.put(positions);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static Buffer wrap(final short[] cornerTextures) {
|
||||
final ByteBuffer wrapper = ByteBuffer.allocateDirect(cornerTextures.length).order(ByteOrder.nativeOrder());
|
||||
for (final short face : cornerTextures) {
|
||||
wrapper.put((byte) face);
|
||||
}
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class SubscriberSetNotifier<LISTENER_TYPE> {
|
||||
protected final Set<LISTENER_TYPE> set; // bad for iteration but there
|
||||
// should never be a dude subscribed
|
||||
// 2x
|
||||
|
||||
public SubscriberSetNotifier() {
|
||||
this.set = new HashSet<>();
|
||||
}
|
||||
|
||||
public final void subscribe(final LISTENER_TYPE listener) {
|
||||
this.set.add(listener);
|
||||
}
|
||||
|
||||
public final void unsubscribe(final LISTENER_TYPE listener) {
|
||||
this.set.remove(listener);
|
||||
}
|
||||
}
|
@ -564,10 +564,10 @@ public class Vector4 implements Serializable, Vector<Vector4> {
|
||||
public static Vector4 transformMat4(final Vector4 out, final Vector4 a, final Matrix4 matrix) {
|
||||
final float x = a.x, y = a.y, z = a.z, w = a.w;
|
||||
final float[] m = matrix.val;
|
||||
out.x = (m[Matrix4.M00] * x) + (m[Matrix4.M10] * y) + (m[Matrix4.M20] * z) + (m[Matrix4.M30] * w);
|
||||
out.y = (m[Matrix4.M01] * x) + (m[Matrix4.M11] * y) + (m[Matrix4.M21] * z) + (m[Matrix4.M31] * w);
|
||||
out.z = (m[Matrix4.M02] * x) + (m[Matrix4.M12] * y) + (m[Matrix4.M22] * z) + (m[Matrix4.M32] * w);
|
||||
out.w = (m[Matrix4.M03] * x) + (m[Matrix4.M13] * y) + (m[Matrix4.M23] * z) + (m[Matrix4.M33] * w);
|
||||
out.x = (m[Matrix4.M00] * x) + (m[Matrix4.M01] * y) + (m[Matrix4.M02] * z) + (m[Matrix4.M03] * w);
|
||||
out.y = (m[Matrix4.M10] * x) + (m[Matrix4.M11] * y) + (m[Matrix4.M12] * z) + (m[Matrix4.M13] * w);
|
||||
out.z = (m[Matrix4.M20] * x) + (m[Matrix4.M21] * y) + (m[Matrix4.M22] * z) + (m[Matrix4.M23] * w);
|
||||
out.w = (m[Matrix4.M30] * x) + (m[Matrix4.M31] * y) + (m[Matrix4.M32] * z) + (m[Matrix4.M33] * w);
|
||||
return out;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
public class WarsmashConstants {
|
||||
public static final int MAX_PLAYERS = 16;
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class BoundingShape extends SceneNode {
|
||||
private final float[] min = new float[] { -1, -1, -1 };
|
||||
private final float[] max = new float[] { 1, 1, 1 };
|
||||
private float radius = (float) Math.sqrt(2);
|
||||
|
||||
public void fromBounds(final float[] min, final float[] max) {
|
||||
System.arraycopy(min, 0, this.min, 0, this.min.length);
|
||||
System.arraycopy(max, 0, this.max, 0, this.max.length);
|
||||
|
||||
final float dX = max[0] - min[0];
|
||||
final float dY = max[1] - min[1];
|
||||
final float dZ = max[2] - min[2];
|
||||
|
||||
this.radius = (float) Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) / 2;
|
||||
}
|
||||
|
||||
public void fromRadius(final float radius) {
|
||||
final float s = (float) (radius * Math.cos(radius));
|
||||
this.min[0] = this.min[1] = this.min[2] = s;
|
||||
this.max[0] = this.max[1] = this.max[2] = s;
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
public void fromVertices(final float[] vertices) {
|
||||
final float[] min = new float[] { 1E9f, 1E9f, 1E9f };
|
||||
final float[] max = new float[] { -1E9f, -1E9f, -1E9f };
|
||||
|
||||
for (int i = 0, l = vertices.length; i < l; i += 3) {
|
||||
final float x = vertices[i];
|
||||
final float y = vertices[i + 1];
|
||||
final float z = vertices[i + 2];
|
||||
|
||||
if (x > max[0]) {
|
||||
max[0] = x;
|
||||
}
|
||||
if (x < min[0]) {
|
||||
min[0] = x;
|
||||
}
|
||||
if (y > max[1]) {
|
||||
max[1] = y;
|
||||
}
|
||||
if (y < min[1]) {
|
||||
min[1] = y;
|
||||
}
|
||||
if (z > max[2]) {
|
||||
max[2] = z;
|
||||
}
|
||||
if (z < min[2]) {
|
||||
min[2] = z;
|
||||
}
|
||||
}
|
||||
|
||||
fromBounds(min, max);
|
||||
}
|
||||
|
||||
public Vector3 getPositiveVertex(final Vector3 out, final Vector3 normal) {
|
||||
if (normal.x >= 0) {
|
||||
out.x = this.max[0];
|
||||
}
|
||||
else {
|
||||
out.x = this.min[0];
|
||||
}
|
||||
if (normal.y >= 0) {
|
||||
out.y = this.max[1];
|
||||
}
|
||||
else {
|
||||
out.y = this.min[1];
|
||||
}
|
||||
if (normal.z >= 0) {
|
||||
out.z = this.max[2];
|
||||
}
|
||||
else {
|
||||
out.z = this.min[2];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public Vector3 getNegativeVertex(final Vector3 out, final Vector3 normal) {
|
||||
if (normal.x >= 0) {
|
||||
out.x = this.min[0];
|
||||
}
|
||||
else {
|
||||
out.x = this.max[0];
|
||||
}
|
||||
if (normal.y >= 0) {
|
||||
out.y = this.min[1];
|
||||
}
|
||||
else {
|
||||
out.y = this.max[1];
|
||||
}
|
||||
if (normal.z >= 0) {
|
||||
out.z = this.min[2];
|
||||
}
|
||||
else {
|
||||
out.z = this.max[2];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateObject(final Scene scene) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void convertBasis(final Quaternion computedRotation) {
|
||||
// TODO ???
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.etheller.warsmash.viewer.ModelView.SceneData;
|
||||
|
||||
public class Bucket {
|
||||
private final ModelView modelView;
|
||||
private final Model model;
|
||||
private final int count;
|
||||
|
||||
public Bucket(final ModelView modelView) {
|
||||
final Model model = modelView.model;
|
||||
final GL20 gl = model.getViewer().gl;
|
||||
|
||||
this.modelView = modelView;
|
||||
this.model = model;
|
||||
this.count = 0;
|
||||
|
||||
// this.instanceIdBuffer =
|
||||
}
|
||||
|
||||
public int fill(final SceneData data, final int baseInstance, final Scene scene) {
|
||||
// Make believe the bucket is now filled with data for all instances.
|
||||
// This is because if a non-specific bucket implementation is supplied,
|
||||
// instancing isn't used, so batching is irrelevant.
|
||||
return data.instances.size();
|
||||
}
|
||||
}
|
@ -1,314 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.Vector4;
|
||||
|
||||
public class Camera {
|
||||
private static final Vector3 vectorHeap = new Vector3();
|
||||
private static final Vector3 vectorHeap2 = new Vector3();
|
||||
private static final Vector3 vectorHeap3 = new Vector3();
|
||||
private static final Quaternion quatHeap = new Quaternion();
|
||||
private static final Matrix4 matHeap = new Matrix4();
|
||||
|
||||
private final Rectangle rect;
|
||||
|
||||
private boolean isPerspective;
|
||||
private float fov;
|
||||
private float aspect;
|
||||
|
||||
private boolean isOrtho;
|
||||
private float leftClipPlane;
|
||||
private float rightClipPlane;
|
||||
private float bottomClipPlane;
|
||||
private float topClipPlane;
|
||||
|
||||
private float nearClipPlane;
|
||||
private float farClipPlane;
|
||||
|
||||
private final Vector3 location;
|
||||
private final Quaternion rotation;
|
||||
|
||||
public Quaternion inverseRotation;
|
||||
private final Matrix4 worldMatrix;
|
||||
private final Matrix4 projectionMatrix;
|
||||
private final Matrix4 worldProjectionMatrix;
|
||||
private final Matrix4 inverseWorldMatrix;
|
||||
private final Matrix4 inverseRotationMatrix;
|
||||
private final Matrix4 inverseWorldProjectionMatrix;
|
||||
private final Vector3 directionX;
|
||||
private final Vector3 directionY;
|
||||
private final Vector3 directionZ;
|
||||
private final Vector3[] vectors;
|
||||
private final Vector3[] billboardedVectors;
|
||||
|
||||
private final Vector4[] planes;
|
||||
private boolean dirty;
|
||||
|
||||
public Camera() {
|
||||
// rencered viewport
|
||||
this.rect = new Rectangle();
|
||||
|
||||
// perspective values
|
||||
this.isPerspective = true;
|
||||
this.fov = 0;
|
||||
this.aspect = 0;
|
||||
|
||||
// Orthogonal values
|
||||
this.isOrtho = false;
|
||||
this.leftClipPlane = 0f;
|
||||
this.rightClipPlane = 0f;
|
||||
this.bottomClipPlane = 0f;
|
||||
this.topClipPlane = 0f;
|
||||
|
||||
// Shared values
|
||||
this.nearClipPlane = 0f;
|
||||
this.farClipPlane = 0f;
|
||||
|
||||
// World values
|
||||
this.location = new Vector3();
|
||||
this.rotation = new Quaternion();
|
||||
|
||||
// Derived values.
|
||||
this.inverseRotation = new Quaternion();
|
||||
this.worldMatrix = new Matrix4();
|
||||
this.projectionMatrix = new Matrix4();
|
||||
this.worldProjectionMatrix = new Matrix4();
|
||||
this.inverseWorldMatrix = new Matrix4();
|
||||
this.inverseRotationMatrix = new Matrix4();
|
||||
this.inverseWorldProjectionMatrix = new Matrix4();
|
||||
this.directionX = new Vector3();
|
||||
this.directionY = new Vector3();
|
||||
this.directionZ = new Vector3();
|
||||
|
||||
// First four vectors are the corners of a 2x2 rectangle, the last three vectors
|
||||
// are the unit axes
|
||||
this.vectors = new Vector3[] { new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0),
|
||||
new Vector3(1, -1, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1) };
|
||||
|
||||
// First four vectors are the corners of a 2x2 rectangle billboarded to the
|
||||
// camera, the last three vectors are the unit axes billboarded
|
||||
this.billboardedVectors = new Vector3[] { new Vector3(), new Vector3(), new Vector3(), new Vector3(),
|
||||
new Vector3(), new Vector3(), new Vector3() };
|
||||
|
||||
// Left, right, top, bottom, near, far
|
||||
this.planes = new Vector4[] { new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4(),
|
||||
new Vector4() };
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void perspective(final float fov, final float aspect, final float near, final float far) {
|
||||
this.isPerspective = true;
|
||||
this.isOrtho = false;
|
||||
this.fov = fov;
|
||||
this.aspect = aspect;
|
||||
this.nearClipPlane = near;
|
||||
this.farClipPlane = far;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void ortho(final float left, final float right, final float bottom, final float top, final float near,
|
||||
final float far) {
|
||||
this.isPerspective = false;
|
||||
this.isOrtho = true;
|
||||
this.leftClipPlane = left;
|
||||
this.rightClipPlane = right;
|
||||
this.bottomClipPlane = bottom;
|
||||
this.topClipPlane = top;
|
||||
this.nearClipPlane = near;
|
||||
this.farClipPlane = far;
|
||||
}
|
||||
|
||||
public void viewport(final Rectangle viewport) {
|
||||
this.rect.set(viewport);
|
||||
|
||||
this.aspect = viewport.width / viewport.height;
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void setLocation(final Vector3 location) {
|
||||
this.location.set(location);
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void move(final Vector3 offset) {
|
||||
this.location.add(offset);
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void setRotation(final Quaternion rotation) {
|
||||
this.rotation.set(rotation);
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void rotate(final Quaternion rotation) {
|
||||
this.rotation.mul(rotation);
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void setRotationAngles(final float horizontalAngle, final float verticalAngle) {
|
||||
this.rotation.idt();
|
||||
// this.rotateAngles(horizontalAngle, verticalAngle);
|
||||
throw new UnsupportedOperationException(
|
||||
"Ghostwolf called a function that does not exist, so I did not know what to do here");
|
||||
}
|
||||
|
||||
public void rotateAround(final Quaternion rotation, final Vector3 point) {
|
||||
this.rotate(rotation);
|
||||
|
||||
quatHeap.conjugate(); // TODO ?????????
|
||||
vectorHeap.set(this.location);
|
||||
vectorHeap.sub(point);
|
||||
rotation.transform(vectorHeap);
|
||||
vectorHeap.add(point);
|
||||
this.location.set(vectorHeap);
|
||||
}
|
||||
|
||||
public void setRotationAround(final Quaternion rotation, final Vector3 point) {
|
||||
this.setRotation(rotation);
|
||||
;
|
||||
|
||||
final float length = vectorHeap.set(this.location).sub(point).len();
|
||||
|
||||
quatHeap.conjugate(); // TODO ?????????
|
||||
vectorHeap.set(RenderMathUtils.VEC3_UNIT_Z);
|
||||
quatHeap.transform(vectorHeap);
|
||||
vectorHeap.scl(length);
|
||||
this.location.set(vectorHeap.add(point));
|
||||
}
|
||||
|
||||
public void setRotationAroundAngles(final float horizontalAngle, final float verticalAngle, final Vector3 point) {
|
||||
quatHeap.idt();
|
||||
RenderMathUtils.rotateX(quatHeap, quatHeap, verticalAngle);
|
||||
RenderMathUtils.rotateY(quatHeap, quatHeap, horizontalAngle);
|
||||
|
||||
this.setRotationAround(quatHeap, point);
|
||||
}
|
||||
|
||||
public void face(final Vector3 point, final Vector3 worldUp) {
|
||||
matHeap.setToLookAt(this.location, point, worldUp);
|
||||
matHeap.getRotation(this.rotation);
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void moveToAndFace(final Vector3 location, final Vector3 target, final Vector3 worldUp) {
|
||||
this.location.set(location);
|
||||
this.face(target, worldUp);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.location.set(0, 0, 0);
|
||||
this.rotation.idt();
|
||||
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (this.dirty) {
|
||||
this.dirty = true;
|
||||
|
||||
final Vector3 location = this.location;
|
||||
final Quaternion rotation = this.rotation;
|
||||
final Quaternion inverseRotation = this.inverseRotation;
|
||||
final Matrix4 worldMatrix = this.worldMatrix;
|
||||
final Matrix4 projectionMatrix = this.projectionMatrix;
|
||||
final Matrix4 worldProjectionMatrix = this.worldProjectionMatrix;
|
||||
final Vector3[] vectors = this.vectors;
|
||||
final Vector3[] billboardedVectors = this.billboardedVectors;
|
||||
|
||||
if (this.isPerspective) {
|
||||
RenderMathUtils.perspective(projectionMatrix, this.fov, this.aspect, this.nearClipPlane,
|
||||
this.farClipPlane);
|
||||
}
|
||||
else {
|
||||
RenderMathUtils.ortho(projectionMatrix, this.leftClipPlane, this.rightClipPlane, this.bottomClipPlane,
|
||||
this.topClipPlane, this.nearClipPlane, this.farClipPlane);
|
||||
}
|
||||
|
||||
rotation.toMatrix(projectionMatrix.val);
|
||||
worldMatrix.translate(vectorHeap.set(location).scl(-1));
|
||||
inverseRotation.set(rotation).conjugate();
|
||||
|
||||
// World projection matrix
|
||||
// World space -> NDC space
|
||||
worldProjectionMatrix.set(projectionMatrix).mul(worldMatrix);
|
||||
|
||||
// Recalculate the camera's frustum planes
|
||||
RenderMathUtils.unpackPlanes(this.planes, worldProjectionMatrix);
|
||||
|
||||
// Inverse world matrix
|
||||
// Camera space -> world space
|
||||
this.inverseWorldMatrix.set(worldMatrix).inv();
|
||||
|
||||
this.directionX.set(RenderMathUtils.VEC3_UNIT_X);
|
||||
inverseRotation.transform(this.directionX);
|
||||
this.directionY.set(RenderMathUtils.VEC3_UNIT_Y);
|
||||
inverseRotation.transform(this.directionY);
|
||||
this.directionZ.set(RenderMathUtils.VEC3_UNIT_Z);
|
||||
inverseRotation.transform(this.directionZ);
|
||||
|
||||
// Inverse world projection matrix
|
||||
// NDC space -> World space
|
||||
this.inverseWorldProjectionMatrix.set(worldProjectionMatrix);
|
||||
this.inverseWorldProjectionMatrix.inv();
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
billboardedVectors[i].set(vectors[i]);
|
||||
inverseRotation.transform(billboardedVectors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean testSphere(final Vector3 center, final float radius) {
|
||||
for (final Vector4 plane : this.planes) {
|
||||
if (RenderMathUtils.distanceToPlane(plane, center) <= -radius) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Vector3 cameraToWorld(final Vector3 out, final Vector3 v) {
|
||||
return out.set(v).prj(this.inverseWorldMatrix);
|
||||
}
|
||||
|
||||
public Vector3 worldToCamera(final Vector3 out, final Vector3 v) {
|
||||
return out.set(v).prj(this.worldMatrix);
|
||||
}
|
||||
|
||||
public float[] screenToWorldRay(final float[] out, final Vector2 v) {
|
||||
final Vector3 a = vectorHeap;
|
||||
final Vector3 b = vectorHeap2;
|
||||
final Vector3 c = vectorHeap3;
|
||||
final float x = v.x;
|
||||
final float y = v.y;
|
||||
final Rectangle viewport = this.rect;
|
||||
|
||||
// Intersection on the near-plane
|
||||
RenderMathUtils.unproject(a, c.set(x, y, 0), this.inverseWorldProjectionMatrix, viewport);
|
||||
|
||||
// Intersection on the far-plane
|
||||
RenderMathUtils.unproject(b, c.set(x, y, 1), this.inverseWorldProjectionMatrix, viewport);
|
||||
|
||||
out[0] = a.x;
|
||||
out[1] = a.y;
|
||||
out[2] = a.z;
|
||||
out[3] = b.x;
|
||||
out[4] = b.y;
|
||||
out[5] = b.z;
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
public abstract class Model {
|
||||
private ModelView modelView;
|
||||
|
||||
public boolean ok;
|
||||
|
||||
public abstract Viewer getViewer();
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
public class ModelInstance {
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ModelView {
|
||||
protected final Model model;
|
||||
protected final HashSet<Object> instanceSet;
|
||||
protected final HashMap<Scene, SceneData> sceneData;
|
||||
protected final int renderedInstances;
|
||||
protected final int renderedParticles;
|
||||
protected final int renderedBuckets;
|
||||
protected final int renderedCalls;
|
||||
|
||||
public ModelView(final Model model) {
|
||||
this.model = model;
|
||||
|
||||
this.instanceSet = new HashSet<>();
|
||||
this.sceneData = new HashMap<>();
|
||||
|
||||
this.renderedInstances = 0;
|
||||
this.renderedParticles = 0;
|
||||
this.renderedBuckets = 0;
|
||||
this.renderedCalls = 0;
|
||||
}
|
||||
|
||||
public abstract Object getShallowCopy();
|
||||
|
||||
public abstract void applyShallowCopy(final Object view);
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object view);
|
||||
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
// public boo
|
||||
public void addSceneData(final ModelInstance instance, final Scene scene) {
|
||||
if (this.model.ok && (scene != null)) {
|
||||
SceneData data = this.sceneData.get(scene);
|
||||
|
||||
if (data == null) {
|
||||
data = this.createSceneData(scene);
|
||||
|
||||
this.sceneData.put(scene, data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private SceneData createSceneData(final Scene scene) {
|
||||
return new SceneData(scene, this);
|
||||
}
|
||||
|
||||
public static final class SceneData {
|
||||
public final Scene scene;
|
||||
public final ModelView modelView;
|
||||
public final int baseIndex = 0;
|
||||
public final List<ModelInstance> instances = new ArrayList<>();
|
||||
public final List<Bucket> buckets = new ArrayList<>();
|
||||
public final int usedBuckets = 0;
|
||||
|
||||
public SceneData(final Scene scene, final ModelView modelView) {
|
||||
this.scene = scene;
|
||||
this.modelView = modelView;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
public abstract class Scene {
|
||||
public Camera camera;
|
||||
}
|
@ -1,245 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
|
||||
public abstract class SceneNode extends ViewerNode {
|
||||
|
||||
public SceneNode() {
|
||||
}
|
||||
|
||||
public SceneNode setPivot(final float[] pivot) {
|
||||
this.pivot.set(pivot);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setLocation(final float[] location) {
|
||||
this.localLocation.set(location);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setRotation(final float[] rotation) {
|
||||
this.localRotation.set(rotation[0], rotation[1], rotation[2], rotation[3]);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setScale(final float[] varying) {
|
||||
this.localScale.set(varying);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setUniformScale(final float uniform) {
|
||||
this.localScale.set(uniform, uniform, uniform);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setTransformation(final Vector3 location, final Quaternion rotation, final Vector3 scale) {
|
||||
// TODO for performance, Ghostwolf did a direct field write on everything here.
|
||||
// I'm hoping we can get Java's JIT to just figure it out and do it on its own
|
||||
this.localLocation.set(location);
|
||||
this.localRotation.set(rotation);
|
||||
this.localScale.set(scale);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode resetTransformation() {
|
||||
this.pivot.set(Vector3.Zero);
|
||||
this.localLocation.set(Vector3.Zero);
|
||||
this.localRotation.set(RenderMathUtils.QUAT_DEFAULT);
|
||||
this.localScale.set(RenderMathUtils.VEC3_ONE);
|
||||
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode movePivot(final float[] offset) {
|
||||
this.pivot.add(offset[0], offset[1], offset[2]);
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode move(final float[] offset) {
|
||||
this.localLocation.add(offset[0], offset[1], offset[2]);
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode rotate(final Quaternion rotation) {
|
||||
RenderMathUtils.mul(this.localRotation, this.localRotation, rotation);
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode rotateLocal(final Quaternion rotation) {
|
||||
RenderMathUtils.mul(this.localRotation, rotation, this.localRotation);
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode scale(final float[] scale) {
|
||||
this.localScale.x *= scale[0];
|
||||
this.localScale.y *= scale[1];
|
||||
this.localScale.z *= scale[2];
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode uniformScale(final float scale) {
|
||||
this.localScale.x *= scale;
|
||||
this.localScale.y *= scale;
|
||||
this.localScale.z *= scale;
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneNode setParent(final ViewerNode parent) {
|
||||
if (this.parent != null) {
|
||||
this.parent.children.remove(this);
|
||||
}
|
||||
|
||||
this.parent = parent;
|
||||
|
||||
if (parent != null) {
|
||||
parent.children.add(this);
|
||||
}
|
||||
|
||||
this.dirty = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void recalculateTransformation() {
|
||||
boolean dirty = this.dirty;
|
||||
final ViewerNode parent = this.parent;
|
||||
|
||||
this.wasDirty = this.dirty;
|
||||
|
||||
if (parent != null) {
|
||||
dirty = dirty || parent.wasDirty;
|
||||
}
|
||||
|
||||
this.wasDirty = dirty;
|
||||
|
||||
if (dirty) {
|
||||
this.dirty = false;
|
||||
|
||||
if (parent != null) {
|
||||
Vector3 computedLocation;
|
||||
Vector3 computedScaling;
|
||||
|
||||
final Vector3 parentPivot = parent.pivot;
|
||||
|
||||
computedLocation = locationHeap;
|
||||
computedLocation.x = this.localLocation.x + parentPivot.x;
|
||||
computedLocation.y = this.localLocation.y + parentPivot.y;
|
||||
computedLocation.z = this.localLocation.z + parentPivot.z;
|
||||
|
||||
if (this.dontInheritScaling) {
|
||||
computedScaling = scalingHeap;
|
||||
|
||||
final Vector3 parentInverseScale = parent.inverseWorldScale;
|
||||
computedScaling.x = parentInverseScale.x * this.localScale.x;
|
||||
computedScaling.y = parentInverseScale.y * this.localScale.y;
|
||||
computedScaling.z = parentInverseScale.z * this.localScale.z;
|
||||
|
||||
this.worldScale.x = this.localScale.x;
|
||||
this.worldScale.y = this.localScale.y;
|
||||
this.worldScale.z = this.localScale.z;
|
||||
}
|
||||
else {
|
||||
computedScaling = this.localScale;
|
||||
|
||||
final Vector3 parentScale = parent.worldScale;
|
||||
this.worldScale.x = parentScale.x * this.localScale.x;
|
||||
this.worldScale.y = parentScale.y * this.localScale.y;
|
||||
this.worldScale.z = parentScale.z * this.localScale.z;
|
||||
}
|
||||
|
||||
RenderMathUtils.fromRotationTranslationScale(this.localRotation, computedLocation, computedScaling,
|
||||
this.localMatrix);
|
||||
|
||||
RenderMathUtils.mul(this.worldMatrix, parent.worldMatrix, this.localMatrix);
|
||||
|
||||
RenderMathUtils.mul(this.worldRotation, parent.worldRotation, this.localRotation);
|
||||
}
|
||||
else {
|
||||
RenderMathUtils.fromRotationTranslationScale(this.localRotation, this.localLocation, this.localScale,
|
||||
this.localMatrix);
|
||||
|
||||
this.worldMatrix.set(this.localMatrix);
|
||||
|
||||
this.worldRotation.set(this.localRotation);
|
||||
|
||||
this.worldScale.set(this.localScale);
|
||||
}
|
||||
}
|
||||
|
||||
// Inverse world rotation
|
||||
this.inverseWorldRotation.x = -this.worldRotation.x;
|
||||
this.inverseWorldRotation.y = -this.worldRotation.y;
|
||||
this.inverseWorldRotation.z = -this.worldRotation.z;
|
||||
this.inverseWorldRotation.w = this.worldRotation.w;
|
||||
|
||||
// Inverse world scale
|
||||
this.inverseWorldScale.x = 1 / this.worldScale.x;
|
||||
this.inverseWorldScale.y = 1 / this.worldScale.y;
|
||||
this.inverseWorldScale.z = 1 / this.worldScale.z;
|
||||
|
||||
// World location
|
||||
this.worldLocation.x = this.worldMatrix.val[Matrix4.M30];
|
||||
this.worldLocation.y = this.worldMatrix.val[Matrix4.M31];
|
||||
this.worldLocation.z = this.worldMatrix.val[Matrix4.M32];
|
||||
|
||||
// Inverse world location
|
||||
this.inverseWorldLocation.x = -this.worldLocation.x;
|
||||
this.inverseWorldLocation.y = -this.worldLocation.y;
|
||||
this.inverseWorldLocation.z = -this.worldLocation.z;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final Scene scene) {
|
||||
if (this.dirty || ((this.parent != null) && this.parent.wasDirty)) {
|
||||
this.dirty = true;
|
||||
this.wasDirty = true;
|
||||
this.recalculateTransformation();
|
||||
}
|
||||
else {
|
||||
this.wasDirty = false;
|
||||
}
|
||||
|
||||
this.updateObject(scene);
|
||||
this.updateChildren(scene);
|
||||
}
|
||||
|
||||
protected abstract void updateObject(Scene scene);
|
||||
|
||||
protected void updateChildren(final Scene scene) {
|
||||
for (int i = 0, l = this.children.size(); i < l; i++) {
|
||||
this.children.get(i).update(scene);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void convertBasis(Quaternion computedRotation);
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.Descriptor;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
|
||||
public abstract class SkeletalNode extends ViewerNode {
|
||||
|
||||
private final Object object;
|
||||
|
||||
private final boolean billboarded = false;
|
||||
private final boolean billboardedX = false;
|
||||
private final boolean billboardedY = false;
|
||||
private final boolean billboardedZ = false;
|
||||
|
||||
public SkeletalNode() {
|
||||
this.object = null;
|
||||
}
|
||||
|
||||
public void recalculateTransformation(final Scene scene) {
|
||||
final Quaternion computedRotation;
|
||||
Vector3 computedScaling;
|
||||
|
||||
if (this.dontInheritScaling) {
|
||||
computedScaling = scalingHeap;
|
||||
|
||||
final Vector3 parentInverseScale = this.parent.inverseWorldScale;
|
||||
computedScaling.x = parentInverseScale.x * this.localScale.x;
|
||||
computedScaling.y = parentInverseScale.y * this.localScale.y;
|
||||
computedScaling.z = parentInverseScale.z * this.localScale.z;
|
||||
|
||||
this.worldScale.x = this.localScale.x;
|
||||
this.worldScale.y = this.localScale.y;
|
||||
this.worldScale.z = this.localScale.z;
|
||||
}
|
||||
else {
|
||||
computedScaling = this.localScale;
|
||||
|
||||
final Vector3 parentScale = this.parent.worldScale;
|
||||
this.worldScale.x = parentScale.x * this.worldScale.x;
|
||||
this.worldScale.y = parentScale.y * this.worldScale.y;
|
||||
this.worldScale.z = parentScale.z * this.worldScale.z;
|
||||
}
|
||||
|
||||
if (this.billboarded) {
|
||||
computedRotation = rotationHeap;
|
||||
|
||||
computedRotation.set(this.parent.inverseWorldRotation);
|
||||
computedRotation.mul(scene.camera.inverseRotation);
|
||||
|
||||
this.convertBasis(computedRotation);
|
||||
}
|
||||
else {
|
||||
computedRotation = this.localRotation;
|
||||
}
|
||||
|
||||
RenderMathUtils.fromRotationTranslationScaleOrigin(computedRotation, this.localLocation, computedScaling,
|
||||
this.localMatrix, this.pivot);
|
||||
|
||||
RenderMathUtils.mul(this.worldMatrix, this.parent.worldMatrix, this.localMatrix);
|
||||
|
||||
RenderMathUtils.mul(this.worldRotation, this.parent.worldRotation, computedRotation);
|
||||
|
||||
// Inverse world rotation
|
||||
this.inverseWorldRotation.x = -this.worldRotation.x;
|
||||
this.inverseWorldRotation.y = -this.worldRotation.y;
|
||||
this.inverseWorldRotation.z = -this.worldRotation.z;
|
||||
this.inverseWorldRotation.w = this.worldRotation.w;
|
||||
|
||||
// Inverse world scale
|
||||
this.inverseWorldScale.x = 1 / this.worldScale.x;
|
||||
this.inverseWorldScale.y = 1 / this.worldScale.y;
|
||||
this.inverseWorldScale.z = 1 / this.worldScale.z;
|
||||
|
||||
// World location
|
||||
final float x = this.pivot.x;
|
||||
final float y = this.pivot.y;
|
||||
final float z = this.pivot.z;
|
||||
this.worldLocation.x = (this.worldMatrix.val[Matrix4.M00] * x) + (this.worldMatrix.val[Matrix4.M10] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M20] * z) + this.worldMatrix.val[Matrix4.M30];
|
||||
this.worldLocation.y = (this.worldMatrix.val[Matrix4.M01] * x) + (this.worldMatrix.val[Matrix4.M11] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M21] * z) + this.worldMatrix.val[Matrix4.M31];
|
||||
this.worldLocation.z = (this.worldMatrix.val[Matrix4.M02] * x) + (this.worldMatrix.val[Matrix4.M12] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M22] * z) + this.worldMatrix.val[Matrix4.M32];
|
||||
|
||||
// Inverse world location
|
||||
this.inverseWorldLocation.x = -this.worldLocation.x;
|
||||
this.inverseWorldLocation.y = -this.worldLocation.y;
|
||||
this.inverseWorldLocation.z = -this.worldLocation.z;
|
||||
}
|
||||
|
||||
protected void updateChildren(final Scene scene) {
|
||||
for (int i = 0, l = this.children.size(); i < l; i++) {
|
||||
this.children.get(i).update(scene);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void convertBasis(Quaternion computedRotation);
|
||||
|
||||
public static <NODE extends SkeletalNode> Object[] createSkeletalNodes(final int count,
|
||||
final Descriptor<NODE> nodeDescriptor) {
|
||||
final List<NODE> nodes = new ArrayList<>();
|
||||
final List<Matrix4> worldMatrices = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final NODE node = nodeDescriptor.create();
|
||||
nodes.add(node);
|
||||
worldMatrices.add(node.worldMatrix);
|
||||
}
|
||||
final Object[] data = { nodes, worldMatrices };
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public abstract class Viewer {
|
||||
public GL20 gl;
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package com.etheller.warsmash.viewer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public abstract class ViewerNode {
|
||||
protected static final Vector3 locationHeap = new Vector3();
|
||||
protected static final Quaternion rotationHeap = new Quaternion();
|
||||
protected static final Vector3 scalingHeap = new Vector3();
|
||||
|
||||
protected final Vector3 pivot;
|
||||
protected final Vector3 localLocation;
|
||||
protected final Quaternion localRotation;
|
||||
protected final Vector3 localScale;
|
||||
protected final Vector3 worldLocation;
|
||||
protected final Quaternion worldRotation;
|
||||
protected final Vector3 worldScale;
|
||||
protected final Vector3 inverseWorldLocation;
|
||||
protected final Quaternion inverseWorldRotation;
|
||||
protected final Vector3 inverseWorldScale;
|
||||
protected final Matrix4 localMatrix;
|
||||
protected final Matrix4 worldMatrix;
|
||||
protected final boolean dontInheritTranslation;
|
||||
protected final boolean dontInheritRotation;
|
||||
protected final boolean dontInheritScaling;
|
||||
protected boolean visible;
|
||||
protected boolean wasDirty;
|
||||
protected boolean dirty;
|
||||
|
||||
protected ViewerNode parent;
|
||||
|
||||
protected final List<ViewerNode> children;
|
||||
|
||||
public ViewerNode() {
|
||||
this.pivot = new Vector3();
|
||||
this.localLocation = new Vector3();
|
||||
this.localRotation = new Quaternion(0, 0, 0, 1);
|
||||
this.localScale = new Vector3(1, 1, 1);
|
||||
this.worldLocation = new Vector3();
|
||||
this.worldRotation = new Quaternion();
|
||||
this.worldScale = new Vector3();
|
||||
this.inverseWorldLocation = new Vector3();
|
||||
this.inverseWorldRotation = new Quaternion();
|
||||
this.inverseWorldScale = new Vector3();
|
||||
this.localMatrix = new Matrix4();
|
||||
this.localMatrix.val[0] = 1;
|
||||
this.localMatrix.val[5] = 1;
|
||||
this.localMatrix.val[10] = 1;
|
||||
this.localMatrix.val[15] = 1;
|
||||
this.worldMatrix = new Matrix4();
|
||||
this.dontInheritTranslation = false;
|
||||
this.dontInheritRotation = false;
|
||||
this.dontInheritScaling = false;
|
||||
this.visible = true;
|
||||
this.wasDirty = false;
|
||||
this.dirty = true;
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
|
||||
public abstract void update(Scene scene);
|
||||
}
|
@ -230,8 +230,6 @@ public class Camera {
|
||||
|
||||
public void update() {
|
||||
if (this.dirty) {
|
||||
this.dirty = true;
|
||||
|
||||
final Vector3 location = this.location;
|
||||
final Quaternion rotation = this.rotation;
|
||||
final Quaternion inverseRotation = this.inverseRotation;
|
||||
@ -281,6 +279,7 @@ public class Camera {
|
||||
billboardedVectors[i].set(vectors[i]);
|
||||
inverseRotation.transform(billboardedVectors[i]);
|
||||
}
|
||||
this.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,9 @@ public class EmittedObjectUpdater {
|
||||
this.objects.set(i, this.objects.remove(this.alive));
|
||||
i -= 1;
|
||||
}
|
||||
else {
|
||||
this.objects.remove(this.alive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,10 @@ public abstract class Emitter<MODEL_INSTANCE extends ModelInstance, EMITTED_OBJE
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
public void update(final float dt, final boolean objectVisible) {
|
||||
if (!objectVisible) {
|
||||
return;
|
||||
}
|
||||
this.updateEmission(dt);
|
||||
|
||||
final float currentEmission = this.currentEmission;
|
||||
@ -54,6 +57,9 @@ public abstract class Emitter<MODEL_INSTANCE extends ModelInstance, EMITTED_OBJE
|
||||
this.alive -= 1;
|
||||
|
||||
final EMITTED_OBJECT otherObject = this.objects.get(this.alive);
|
||||
if (object.index == -1) {
|
||||
System.err.println("bad");
|
||||
}
|
||||
this.objects.set(object.index, otherObject);
|
||||
this.objects.set(this.alive, object);
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture.TextureWrap;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public abstract class GdxTextureResource extends Texture {
|
||||
private com.badlogic.gdx.graphics.Texture gdxTexture;
|
||||
|
||||
public GdxTextureResource(final ModelViewer viewer, final ResourceHandler handler, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
}
|
||||
|
||||
public void setGdxTexture(final com.badlogic.gdx.graphics.Texture gdxTexture) {
|
||||
this.gdxTexture = gdxTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(final int unit) {
|
||||
this.viewer.webGL.bindTexture(this, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalBind() {
|
||||
this.gdxTexture.bind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return this.gdxTexture.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.gdxTexture.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGlTarget() {
|
||||
return this.gdxTexture.glTarget;
|
||||
}
|
||||
|
||||
public void setWrapS(final boolean wrapS) {
|
||||
this.gdxTexture.setWrap(wrapS ? TextureWrap.Repeat : TextureWrap.ClampToEdge, this.gdxTexture.getVWrap());
|
||||
}
|
||||
|
||||
public void setWrapT(final boolean wrapT) {
|
||||
this.gdxTexture.setWrap(this.gdxTexture.getUWrap(), wrapT ? TextureWrap.Repeat : TextureWrap.ClampToEdge);
|
||||
}
|
||||
|
||||
}
|
@ -3,8 +3,8 @@ package com.etheller.warsmash.viewer5;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class Grid {
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final float x;
|
||||
private final float y;
|
||||
private final int width;
|
||||
private final int depth;
|
||||
private final int cellWidth;
|
||||
@ -13,7 +13,7 @@ public class Grid {
|
||||
private final int rows;
|
||||
final GridCell[] cells;
|
||||
|
||||
public Grid(final int x, int y, final int width, final int depth, final int cellWidth, final int cellDepth) {
|
||||
public Grid(final float x, float y, final int width, final int depth, final int cellWidth, final int cellDepth) {
|
||||
final int columns = width / cellWidth;
|
||||
final int rows = depth / cellDepth;
|
||||
|
||||
|
@ -93,9 +93,6 @@ public abstract class ModelInstance extends Node {
|
||||
}
|
||||
|
||||
public boolean isVisible(final Camera camera) {
|
||||
if (true) {
|
||||
return true;
|
||||
}
|
||||
final float x = this.worldLocation.x;
|
||||
final float y = this.worldLocation.y;
|
||||
final float z = this.worldLocation.z;
|
||||
|
@ -21,7 +21,7 @@ import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
|
||||
public class ModelViewer {
|
||||
private final DataSource dataSource;
|
||||
private DataSource dataSource;
|
||||
public final CanvasProvider canvas;
|
||||
public List<Resource> resources;
|
||||
public Map<String, Resource> fetchCache;
|
||||
@ -45,7 +45,7 @@ public class ModelViewer {
|
||||
this.resources = new ArrayList<>();
|
||||
this.fetchCache = new HashMap<>();
|
||||
this.handlers = new HashSet<ResourceHandler>();
|
||||
this.frameTime = 1000 / 60;
|
||||
this.frameTime = 1000 / 6;
|
||||
this.gl = Gdx.gl;
|
||||
this.webGL = new WebGL(this.gl);
|
||||
this.scenes = new ArrayList<>();
|
||||
@ -71,6 +71,10 @@ public class ModelViewer {
|
||||
this.textureMappers = new HashMap<Model, List<TextureMapper>>();
|
||||
}
|
||||
|
||||
public void setDataSource(final DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public boolean enableAudio() {
|
||||
this.audioEnabled = true;
|
||||
return this.audioEnabled;
|
||||
@ -200,6 +204,11 @@ public class ModelViewer {
|
||||
return this.fetchCache.get(key);
|
||||
}
|
||||
|
||||
public GenericResource loadGeneric(final String path, final FetchDataTypeName dataType,
|
||||
final LoadGenericCallback callback) {
|
||||
return loadGeneric(path, dataType, callback, this.dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load something generic.
|
||||
*
|
||||
@ -217,7 +226,7 @@ public class ModelViewer {
|
||||
* promise resolved to.
|
||||
*/
|
||||
public GenericResource loadGeneric(final String path, final FetchDataTypeName dataType,
|
||||
final LoadGenericCallback callback) {
|
||||
final LoadGenericCallback callback, final DataSource dataSource) {
|
||||
final Resource cachedResource = this.fetchCache.get(path);
|
||||
|
||||
if (cachedResource != null) {
|
||||
@ -235,7 +244,7 @@ public class ModelViewer {
|
||||
|
||||
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
||||
try {
|
||||
resource.loadData(this.dataSource.getResourceAsStream(path), null);
|
||||
resource.loadData(dataSource.getResourceAsStream(path), null);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new IllegalStateException("Unable to load data: " + path);
|
||||
|
@ -21,7 +21,7 @@ public abstract class Node extends GenericNode {
|
||||
this.localScale = new Vector3(1, 1, 1);
|
||||
this.worldLocation = new Vector3();
|
||||
this.worldRotation = new Quaternion();
|
||||
this.worldScale = new Vector3();
|
||||
this.worldScale = new Vector3(1, 1, 1);
|
||||
this.inverseWorldLocation = new Vector3();
|
||||
this.inverseWorldRotation = new Quaternion();
|
||||
this.inverseWorldScale = new Vector3();
|
||||
@ -249,9 +249,9 @@ public abstract class Node extends GenericNode {
|
||||
this.inverseWorldScale.z = 1 / this.worldScale.z;
|
||||
|
||||
// World location
|
||||
this.worldLocation.x = this.worldMatrix.val[Matrix4.M30];
|
||||
this.worldLocation.y = this.worldMatrix.val[Matrix4.M31];
|
||||
this.worldLocation.z = this.worldMatrix.val[Matrix4.M32];
|
||||
this.worldLocation.x = this.worldMatrix.val[Matrix4.M03];
|
||||
this.worldLocation.y = this.worldMatrix.val[Matrix4.M13];
|
||||
this.worldLocation.z = this.worldMatrix.val[Matrix4.M23];
|
||||
|
||||
// Inverse world location
|
||||
this.inverseWorldLocation.x = -this.worldLocation.x;
|
||||
|
@ -2,4 +2,16 @@ package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface PathSolver {
|
||||
SolvedPath solve(String src, Object solverParams);
|
||||
|
||||
// We generally just use the default path solver.
|
||||
// These things were apparently meant to work as the Ghostwolf's JavaScript's
|
||||
// equivalent of the DataSource interface you will find in this Java repo.
|
||||
// But I did not know that and wasn't sure what it was for, so I kept it in the
|
||||
// port of his code. Eventually it should be removed.
|
||||
public static final PathSolver DEFAULT = new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
return new SolvedPath(src, src.substring(src.lastIndexOf('.')), true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,138 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
/**
|
||||
* Similar to GdxTextureResource, but now I'm probably replacing use of that one
|
||||
* with this one. I'm trying to fight the system here and avoid porting
|
||||
* Ghostwolf's BLP parser to java, and just use the Java BLP parser that I
|
||||
* already had, but the libraries are not playing nicely with each other, so
|
||||
* this class is written to be a lower level solution (OpenGL calls instead of
|
||||
* LibGDX api) that will work.
|
||||
*
|
||||
* My theory is that because doing it THIS way works on Retera Model Studio,
|
||||
* therefore it should work here as well.
|
||||
*/
|
||||
public abstract class RawOpenGLTextureResource extends Texture {
|
||||
private static final int BYTES_PER_PIXEL = 4;
|
||||
private final int target;
|
||||
protected int handle;
|
||||
private int width;
|
||||
private int height;
|
||||
private int wrapS = GL20.GL_CLAMP_TO_EDGE;
|
||||
private int wrapT = GL20.GL_CLAMP_TO_EDGE;
|
||||
private final int magFilter = GL20.GL_LINEAR;
|
||||
private final int minFilter = GL20.GL_LINEAR;
|
||||
|
||||
public RawOpenGLTextureResource(final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl, final ResourceHandler handler) {
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
final GL20 gl = this.viewer.gl;
|
||||
this.handle = gl.glGenTexture();
|
||||
this.target = GL20.GL_TEXTURE_2D;
|
||||
gl.glBindTexture(this.target, this.handle);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, this.minFilter);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, this.magFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(final int unit) {
|
||||
this.viewer.webGL.bindTexture(this, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalBind() {
|
||||
this.viewer.gl.glBindTexture(this.target, this.handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGlTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrapS(final boolean wrapS) {
|
||||
this.wrapS = wrapS ? GL20.GL_REPEAT : GL20.GL_CLAMP_TO_EDGE;
|
||||
final GL20 gl = this.viewer.gl;
|
||||
|
||||
gl.glBindTexture(this.target, this.handle);
|
||||
gl.glTexParameteri(this.target, GL20.GL_TEXTURE_WRAP_S, this.wrapS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrapT(final boolean wrapT) {
|
||||
this.wrapT = wrapT ? GL20.GL_REPEAT : GL20.GL_CLAMP_TO_EDGE;
|
||||
final GL20 gl = this.viewer.gl;
|
||||
|
||||
gl.glBindTexture(this.target, this.handle);
|
||||
gl.glTexParameteri(this.target, GL20.GL_TEXTURE_WRAP_T, this.wrapT);
|
||||
|
||||
}
|
||||
|
||||
public void update(final BufferedImage image) {
|
||||
final GL20 gl = this.viewer.gl;
|
||||
|
||||
final int imageWidth = image.getWidth();
|
||||
final int imageHeight = image.getHeight();
|
||||
final int[] pixels = new int[imageWidth * imageHeight];
|
||||
image.getRGB(0, 0, imageWidth, imageHeight, pixels, 0, imageWidth);
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(imageWidth * imageHeight * BYTES_PER_PIXEL)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
// 4
|
||||
// for
|
||||
// RGBA,
|
||||
// 3
|
||||
// for
|
||||
// RGB
|
||||
|
||||
for (int y = 0; y < imageHeight; y++) {
|
||||
for (int x = 0; x < imageWidth; x++) {
|
||||
final int pixel = pixels[(y * imageWidth) + x];
|
||||
buffer.put((byte) ((pixel >> 16) & 0xFF)); // Red component
|
||||
buffer.put((byte) ((pixel >> 8) & 0xFF)); // Green component
|
||||
buffer.put((byte) (pixel & 0xFF)); // Blue component
|
||||
buffer.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component.
|
||||
// Only for RGBA
|
||||
}
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.handle);
|
||||
|
||||
// if ((this.width == imageWidth) && (this.height == imageHeight)) {
|
||||
// gl.glTexSubImage2D(GL20.GL_TEXTURE_2D, 0, 0, 0, imageWidth, imageHeight, GL20.GL_RGBA,
|
||||
// GL20.GL_UNSIGNED_BYTE, buffer);
|
||||
// }
|
||||
// else {
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_SRGB8_ALPHA8, imageWidth, imageHeight, 0, GL20.GL_RGBA,
|
||||
GL20.GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
this.width = imageWidth;
|
||||
this.height = imageHeight;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,7 @@ public class Scene {
|
||||
|
||||
public final ModelViewer viewer;
|
||||
public final Camera camera;
|
||||
public final Grid grid;
|
||||
public Grid grid;
|
||||
public int visibleCells;
|
||||
public int visibleInstances;
|
||||
public int updatedParticles;
|
||||
@ -185,7 +185,8 @@ public class Scene {
|
||||
if (cell.isVisible(this.camera)) {
|
||||
this.visibleCells += 1;
|
||||
|
||||
for (final ModelInstance instance : cell.instances) {
|
||||
for (int i = 0, l = cell.instances.size(); i < l; i++) {
|
||||
final ModelInstance instance = cell.instances.get(i);
|
||||
if (instance.rendered && (instance.cullFrame < frame) && instance.isVisible(this.camera)) {
|
||||
instance.cullFrame = frame;
|
||||
|
||||
|
@ -10,6 +10,7 @@ import com.etheller.warsmash.util.RenderMathUtils;
|
||||
public abstract class SkeletalNode extends GenericNode {
|
||||
protected static final Vector3 locationHeap = new Vector3();
|
||||
protected static final Quaternion rotationHeap = new Quaternion();
|
||||
protected static final Quaternion rotationHeap2 = new Quaternion();
|
||||
protected static final Vector3 scalingHeap = new Vector3();
|
||||
|
||||
public UpdatableObject object;
|
||||
@ -26,7 +27,7 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
this.localScale = new Vector3(1, 1, 1);
|
||||
this.worldLocation = new Vector3();
|
||||
this.worldRotation = new Quaternion();
|
||||
this.worldScale = new Vector3();
|
||||
this.worldScale = new Vector3(1, 1, 1);
|
||||
this.inverseWorldLocation = new Vector3();
|
||||
this.inverseWorldRotation = new Quaternion();
|
||||
this.inverseWorldScale = new Vector3();
|
||||
@ -123,12 +124,12 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
final float x = this.pivot.x;
|
||||
final float y = this.pivot.y;
|
||||
final float z = this.pivot.z;
|
||||
this.worldLocation.x = (this.worldMatrix.val[Matrix4.M00] * x) + (this.worldMatrix.val[Matrix4.M10] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M20] * z) + this.worldMatrix.val[Matrix4.M30];
|
||||
this.worldLocation.y = (this.worldMatrix.val[Matrix4.M01] * x) + (this.worldMatrix.val[Matrix4.M11] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M21] * z) + this.worldMatrix.val[Matrix4.M31];
|
||||
this.worldLocation.z = (this.worldMatrix.val[Matrix4.M02] * x) + (this.worldMatrix.val[Matrix4.M12] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M22] * z) + this.worldMatrix.val[Matrix4.M32];
|
||||
this.worldLocation.x = (this.worldMatrix.val[Matrix4.M00] * x) + (this.worldMatrix.val[Matrix4.M01] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M02] * z) + this.worldMatrix.val[Matrix4.M03];
|
||||
this.worldLocation.y = (this.worldMatrix.val[Matrix4.M10] * x) + (this.worldMatrix.val[Matrix4.M11] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M12] * z) + this.worldMatrix.val[Matrix4.M13];
|
||||
this.worldLocation.z = (this.worldMatrix.val[Matrix4.M20] * x) + (this.worldMatrix.val[Matrix4.M21] * y)
|
||||
+ (this.worldMatrix.val[Matrix4.M22] * z) + this.worldMatrix.val[Matrix4.M23];
|
||||
|
||||
// Inverse world location
|
||||
this.inverseWorldLocation.x = -this.worldLocation.x;
|
||||
|
@ -1,51 +1,26 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture.TextureWrap;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public abstract class Texture extends HandlerResource<ResourceHandler> {
|
||||
private com.badlogic.gdx.graphics.Texture gdxTexture;
|
||||
|
||||
public Texture(final ModelViewer viewer, final ResourceHandler handler, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
public Texture(final ModelViewer viewer, final String extension, final PathSolver pathSolver, final String fetchUrl,
|
||||
final ResourceHandler handler) {
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
}
|
||||
|
||||
public void setGdxTexture(final com.badlogic.gdx.graphics.Texture gdxTexture) {
|
||||
this.gdxTexture = gdxTexture;
|
||||
}
|
||||
public abstract void bind(final int unit);
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
public abstract void internalBind();
|
||||
|
||||
public void bind(final int unit) {
|
||||
this.viewer.webGL.bindTexture(this, unit);
|
||||
}
|
||||
public abstract int getWidth();
|
||||
|
||||
public void internalBind() {
|
||||
this.gdxTexture.bind();
|
||||
}
|
||||
public abstract int getHeight();
|
||||
|
||||
public int getWidth() {
|
||||
return this.gdxTexture.getWidth();
|
||||
}
|
||||
public abstract int getGlTarget();
|
||||
|
||||
public int getHeight() {
|
||||
return this.gdxTexture.getHeight();
|
||||
}
|
||||
public abstract void setWrapS(final boolean wrapS);
|
||||
|
||||
public int getGlTarget() {
|
||||
return this.gdxTexture.glTarget;
|
||||
}
|
||||
|
||||
public void setWrapS(final boolean wrapS) {
|
||||
this.gdxTexture.setWrap(wrapS ? TextureWrap.Repeat : TextureWrap.ClampToEdge, this.gdxTexture.getVWrap());
|
||||
}
|
||||
|
||||
public void setWrapT(final boolean wrapT) {
|
||||
this.gdxTexture.setWrap(this.gdxTexture.getUWrap(), wrapT ? TextureWrap.Repeat : TextureWrap.ClampToEdge);
|
||||
}
|
||||
public abstract void setWrapT(final boolean wrapT);
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface UpdatableObject {
|
||||
void update(float dt);
|
||||
void update(float dt, boolean visible);
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ package com.etheller.warsmash.viewer5.gl;
|
||||
import java.nio.Buffer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
|
||||
public class DataTexture {
|
||||
public GL20 gl;
|
||||
public int texture;
|
||||
public int format;
|
||||
public int internalFormat;
|
||||
public int width = 0;
|
||||
public int height = 0;
|
||||
|
||||
@ -15,11 +17,12 @@ public class DataTexture {
|
||||
this.gl = gl;
|
||||
this.texture = gl.glGenTexture();
|
||||
this.format = (channels == 3 ? GL20.GL_RGB : GL20.GL_RGBA);
|
||||
this.internalFormat = (channels == 3 ? GL20.GL_RGB : GL30.GL_RGBA32F);
|
||||
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.texture);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_NEAREST);
|
||||
gl.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST);
|
||||
|
||||
this.reserve(width, height);
|
||||
@ -33,8 +36,8 @@ public class DataTexture {
|
||||
this.height = Math.max(this.height, height);
|
||||
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.texture);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, this.format, this.width, this.height, 0, this.format, GL20.GL_FLOAT,
|
||||
null);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, this.internalFormat, this.width, this.height, 0, this.format,
|
||||
GL20.GL_FLOAT, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,18 +6,16 @@ import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture.TextureFilter;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.RawOpenGLTextureResource;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public class BlpTexture extends Texture {
|
||||
public class BlpTexture extends RawOpenGLTextureResource {
|
||||
|
||||
public BlpTexture(final ModelViewer viewer, final ResourceHandler handler, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
super(viewer, handler, extension, pathSolver, fetchUrl);
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -30,10 +28,7 @@ public class BlpTexture extends Texture {
|
||||
BufferedImage img;
|
||||
try {
|
||||
img = ImageIO.read(src);
|
||||
final com.badlogic.gdx.graphics.Texture texture = ImageUtils
|
||||
.getTexture(ImageUtils.forceBufferedImagesRGB(img));
|
||||
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
|
||||
setGdxTexture(texture);
|
||||
update(img);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -24,29 +24,33 @@ public class AttachmentInstance implements UpdatableObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
public void update(final float dt, final boolean objectVisible) {
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
|
||||
if (internalInstance.model.ok) {
|
||||
this.attachment.getVisibility(visbilityHeap, this.instance.sequence, this.instance.frame,
|
||||
this.instance.counter);
|
||||
|
||||
if (visbilityHeap[0] > 0.1) {
|
||||
// The parent instance might not actually be in a scene.
|
||||
// This happens if loading a local model, where loading is instant and adding to
|
||||
// a scene always comes afterwards.
|
||||
// Therefore, do it here dynamically.
|
||||
this.instance.scene.addInstance(internalInstance);
|
||||
|
||||
if (internalInstance.hidden()) {
|
||||
internalInstance.show();
|
||||
|
||||
// Every time the attachment becomes visible again, restart its first sequence.
|
||||
internalInstance.setSequence(0);
|
||||
}
|
||||
if (!objectVisible) {
|
||||
internalInstance.hide();
|
||||
}
|
||||
else {
|
||||
internalInstance.hide();
|
||||
this.attachment.getVisibility(visbilityHeap, this.instance.sequence, this.instance.frame,
|
||||
this.instance.counter);
|
||||
|
||||
if (visbilityHeap[0] > 0.1) {
|
||||
// The parent instance might not actually be in a scene.
|
||||
// This happens if loading a local model, where loading is instant and adding to
|
||||
// a scene always comes afterwards.
|
||||
// Therefore, do it here dynamically.
|
||||
this.instance.scene.addInstance(internalInstance);
|
||||
|
||||
if (internalInstance.hidden()) {
|
||||
internalInstance.show();
|
||||
|
||||
// Every time the attachment becomes visible again, restart its first sequence.
|
||||
internalInstance.setSequence(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
internalInstance.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
|
||||
public class BatchGroup extends GenericGroup {
|
||||
|
||||
@ -31,6 +32,7 @@ public class BatchGroup extends GenericGroup {
|
||||
final List<Integer> replaceables = model.replaceables;
|
||||
final ModelViewer viewer = model.viewer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final WebGL webGL = viewer.webGL;
|
||||
final boolean isExtended = this.isExtended;
|
||||
final ShaderProgram shader;
|
||||
|
||||
@ -41,7 +43,7 @@ public class BatchGroup extends GenericGroup {
|
||||
shader = MdxHandler.Shaders.complex;
|
||||
}
|
||||
|
||||
shader.begin();
|
||||
webGL.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.viewProjectionMatrix);
|
||||
|
||||
@ -52,7 +54,7 @@ public class BatchGroup extends GenericGroup {
|
||||
boneTexture.bind(15);
|
||||
|
||||
shader.setUniformf("u_hasBones", 1);
|
||||
shader.setUniformf("u_boneMap", 15);
|
||||
shader.setUniformi("u_boneMap", 15);
|
||||
shader.setUniformf("u_vectorSize", 1f / boneTexture.getWidth());
|
||||
shader.setUniformf("u_rowSize", 1);
|
||||
}
|
||||
@ -86,7 +88,7 @@ public class BatchGroup extends GenericGroup {
|
||||
|
||||
shader.setUniform2fv("u_uvTrans", uvAnim, 0, 2);
|
||||
shader.setUniform2fv("u_uvRot", uvAnim, 2, 2);
|
||||
shader.setUniform1fv("u_uvRot", uvAnim, 4, 1);
|
||||
shader.setUniform1fv("u_uvScale", uvAnim, 4, 1);
|
||||
|
||||
layer.bind(shader);
|
||||
|
||||
@ -101,13 +103,15 @@ public class BatchGroup extends GenericGroup {
|
||||
}
|
||||
else {
|
||||
texture = textures.get(layerTexture);
|
||||
|
||||
Texture textureLookup = instance.textureMapper.get(texture);
|
||||
if (textureLookup == null) {
|
||||
textureLookup = texture;
|
||||
}
|
||||
texture = textureLookup;
|
||||
}
|
||||
|
||||
Texture textureLookup = instance.textureMapper.get(texture);
|
||||
if (textureLookup == null) {
|
||||
textureLookup = texture;
|
||||
}
|
||||
viewer.webGL.bindTexture(textureLookup, 0);
|
||||
viewer.webGL.bindTexture(texture, 0);
|
||||
|
||||
if (isExtended) {
|
||||
geoset.bindExtended(shader, layer.coordId);
|
||||
|
@ -7,13 +7,21 @@ public class Bone extends GenericObject {
|
||||
public Bone(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Bone bone, final int index) {
|
||||
super(model, bone, index);
|
||||
|
||||
final int geosetAnimationId = bone.getGeosetAnimationId();
|
||||
if (geosetAnimationId != -1) {
|
||||
this.geosetAnimation = model.getGeosetAnimations().get(geosetAnimationId);
|
||||
}
|
||||
else {
|
||||
this.geosetAnimation = null;
|
||||
GeosetAnimation geosetAnimation = null;
|
||||
final int geosetId = bone.getGeosetId();
|
||||
if (geosetId != -1) {
|
||||
final Geoset geoset = model.getGeosets().get(geosetId);
|
||||
if (geoset.geosetAnimation != null) {
|
||||
geosetAnimation = geoset.geosetAnimation;
|
||||
}
|
||||
else {
|
||||
final int geosetAnimationId = bone.getGeosetAnimationId();
|
||||
if (geosetAnimationId != -1) {
|
||||
geosetAnimation = model.getGeosetAnimations().get(geosetAnimationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.geosetAnimation = geosetAnimation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,7 +30,7 @@ public class EmitterGroup extends GenericGroup {
|
||||
gl.glDisable(GL20.GL_CULL_FACE);
|
||||
gl.glEnable(GL20.GL_DEPTH_TEST);
|
||||
|
||||
shader.begin();
|
||||
viewer.webGL.useShaderProgram(shader);
|
||||
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.viewProjectionMatrix);
|
||||
shader.setUniformf("u_texture", 0);
|
||||
|
@ -79,7 +79,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
private float pitch;
|
||||
private float pitchVariance;
|
||||
private float volume;
|
||||
public List<Sound> decodedBuffers;
|
||||
public List<Sound> decodedBuffers = new ArrayList<>();
|
||||
/**
|
||||
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
|
||||
* are loaded.
|
||||
@ -111,6 +111,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
else if ("UBR".equals(type)) {
|
||||
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
|
||||
}
|
||||
else if ("SPN".equals(type)) {
|
||||
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
@ -219,6 +222,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
this.columns = getInt(row, "Columns");
|
||||
this.rows = getInt(row, "Rows");
|
||||
this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay");
|
||||
this.intervalTimes = new float[] { getFloat(row, "Lifespan"), getFloat(row, "Decay") };
|
||||
this.intervals = new float[][] {
|
||||
{ getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"),
|
||||
getFloat(row, "LifespanRepeat") },
|
||||
@ -262,10 +266,14 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
|
||||
final GenericResource[] resources = new GenericResource[fileNames.length];
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
resources[i] = viewer.loadGeneric(
|
||||
final GenericResource genericResource = viewer.loadGeneric(
|
||||
pathSolver.solve(((String) animSoundsRow.get("DirectoryBase")) + fileNames[i],
|
||||
model.solverParams).finalSrc,
|
||||
FetchDataTypeName.ARRAY_BUFFER, decodedDataCallback);
|
||||
if (genericResource == null) {
|
||||
throw new IllegalStateException("Null sound: " + fileNames[i]);
|
||||
}
|
||||
resources[i] = genericResource;
|
||||
}
|
||||
|
||||
// TODO JS async removed
|
||||
|
@ -6,8 +6,8 @@ public class FilterMode {
|
||||
private static final int[] ERROR_DEFAULT = new int[] { 0, 0 };
|
||||
private static final int[] MODULATE_2X = new int[] { GL20.GL_DST_COLOR, GL20.GL_SRC_COLOR };
|
||||
private static final int[] MODULATE = new int[] { GL20.GL_ZERO, GL20.GL_SRC_COLOR };
|
||||
private static final int[] ADDITIVE_ALPHA = new int[] { GL20.GL_ALPHA, GL20.GL_ONE };
|
||||
private static final int[] BLEND = new int[] { GL20.GL_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA };
|
||||
private static final int[] ADDITIVE_ALPHA = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE };
|
||||
private static final int[] BLEND = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA };
|
||||
|
||||
public static int[] layerFilterMode(final com.etheller.warsmash.parsers.mdlx.Layer.FilterMode filterMode) {
|
||||
switch (filterMode) {
|
||||
@ -38,7 +38,7 @@ public class FilterMode {
|
||||
case MODULATE2X:
|
||||
return MODULATE_2X; // Modulate 2x
|
||||
case ALPHAKEY:
|
||||
return ADDITIVE_ALPHA; // Add alpha
|
||||
return BLEND; // Add alpha
|
||||
default:
|
||||
return ERROR_DEFAULT;
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ public class GeometryEmitterFuncs {
|
||||
public static final int EMITTER_RIBBON = 1;
|
||||
public static final int EMITTER_SPLAT = 2;
|
||||
public static final int EMITTER_UBERSPLAT = 3;
|
||||
public static final int EMITTER_SPN = 4; // added by Retera because reasons
|
||||
|
||||
private static final Vector3 locationHeap = new Vector3();
|
||||
private static final Vector3 startHeap = new Vector3();
|
||||
@ -79,7 +80,8 @@ public class GeometryEmitterFuncs {
|
||||
final int teamColor = instance.teamColor;
|
||||
int offset = 0;
|
||||
|
||||
for (final Particle2 object : objects) {
|
||||
for (int objectIndex = 0; objectIndex < emitter.alive; objectIndex++) {
|
||||
final Particle2 object = objects.get(objectIndex);
|
||||
final int byteOffset = offset * BYTES_PER_OBJECT;
|
||||
final int floatOffset = offset * FLOATS_PER_OBJECT;
|
||||
final int p0Offset = floatOffset + FLOAT_OFFSET_P0;
|
||||
@ -127,6 +129,7 @@ public class GeometryEmitterFuncs {
|
||||
floatView.put(p0Offset + 8, scale.z);
|
||||
|
||||
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);
|
||||
|
||||
@ -188,9 +191,9 @@ public class GeometryEmitterFuncs {
|
||||
shader.setUniform3fv("u_intervals[2]", intervals[2], 0, 3);
|
||||
shader.setUniform3fv("u_intervals[3]", intervals[3], 0, 3);
|
||||
|
||||
shader.setUniform4fv("u_colors[0]", colors[0], 0, 3);
|
||||
shader.setUniform4fv("u_colors[1]", colors[1], 0, 3);
|
||||
shader.setUniform4fv("u_colors[2]", colors[2], 0, 3);
|
||||
shader.setUniform4fv("u_colors[0]", colors[0], 0, 4);
|
||||
shader.setUniform4fv("u_colors[1]", colors[1], 0, 4);
|
||||
shader.setUniform4fv("u_colors[2]", colors[2], 0, 4);
|
||||
|
||||
shader.setUniform3fv("u_scaling", emitterObject.scaling, 0, 3);
|
||||
|
||||
@ -377,9 +380,6 @@ public class GeometryEmitterFuncs {
|
||||
}
|
||||
|
||||
public static void renderEmitter(final MdxEmitter<?, ?, ?> emitter, final ShaderProgram shader) {
|
||||
if (emitter == null) {
|
||||
System.err.println("NULL EMITTER");
|
||||
}
|
||||
int alive = emitter.alive;
|
||||
final EmitterObject emitterObject = emitter.emitterObject;
|
||||
final int emitterType = emitterObject.getGeometryEmitterType();
|
||||
@ -387,6 +387,9 @@ public class GeometryEmitterFuncs {
|
||||
if (emitterType == EMITTER_RIBBON) {
|
||||
alive -= 1;
|
||||
}
|
||||
else if (emitterType == EMITTER_SPN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (alive > 0) {
|
||||
final ModelViewer viewer = emitter.instance.model.viewer;
|
||||
@ -395,6 +398,8 @@ public class GeometryEmitterFuncs {
|
||||
final GL20 gl = viewer.gl;
|
||||
final int size = alive * BYTES_PER_OBJECT;
|
||||
|
||||
buffer.reserve(size);
|
||||
|
||||
switch (emitterType) {
|
||||
case EMITTER_PARTICLE2:
|
||||
bindParticleEmitter2Buffer((ParticleEmitter2) emitter, buffer);
|
||||
|
@ -94,7 +94,7 @@ public class Geoset {
|
||||
public void bind(final ShaderProgram shader, final int coordId) {
|
||||
// TODO use indices instead of strings for attributes
|
||||
shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset);
|
||||
shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset);
|
||||
// shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset);
|
||||
shader.setVertexAttribute("a_uv", 2, GL20.GL_FLOAT, false, 0, this.uvOffset + (coordId * this.vertices * 8));
|
||||
shader.setVertexAttribute("a_bones", 4, GL20.GL_UNSIGNED_BYTE, false, 5, this.skinOffset);
|
||||
shader.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 5, this.skinOffset + 4);
|
||||
|
@ -90,10 +90,10 @@ public class Layer extends AnimatedObject {
|
||||
}
|
||||
|
||||
if (this.twoSided != 0) {
|
||||
gl.glEnable(GL20.GL_CULL_FACE);
|
||||
gl.glDisable(GL20.GL_CULL_FACE);
|
||||
}
|
||||
else {
|
||||
gl.glDisable(GL20.GL_CULL_FACE);
|
||||
gl.glEnable(GL20.GL_CULL_FACE);
|
||||
}
|
||||
|
||||
if (this.noDepthTest != 0) {
|
||||
|
@ -345,8 +345,8 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
// This includes attachments and emitters.
|
||||
final UpdatableObject object = node.object;
|
||||
|
||||
if ((object != null) && objectVisible) {
|
||||
object.update(dt);
|
||||
if (object != null) {
|
||||
object.update(dt, objectVisible);
|
||||
}
|
||||
|
||||
// Update all of the node's non-skeletal children, which will update their
|
||||
@ -462,20 +462,20 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
for (int i = 0, l = this.worldMatrices.length; i < l; i++) {
|
||||
final Matrix4 worldMatrix = this.worldMatrices[i];
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 0, worldMatrix.val[Matrix4.M00]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 1, worldMatrix.val[Matrix4.M01]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 2, worldMatrix.val[Matrix4.M02]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 3, worldMatrix.val[Matrix4.M03]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 4, worldMatrix.val[Matrix4.M10]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 1, worldMatrix.val[Matrix4.M10]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 2, worldMatrix.val[Matrix4.M20]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 3, worldMatrix.val[Matrix4.M30]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 4, worldMatrix.val[Matrix4.M01]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 5, worldMatrix.val[Matrix4.M11]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 6, worldMatrix.val[Matrix4.M12]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 7, worldMatrix.val[Matrix4.M13]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 8, worldMatrix.val[Matrix4.M20]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 9, worldMatrix.val[Matrix4.M21]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 6, worldMatrix.val[Matrix4.M21]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 7, worldMatrix.val[Matrix4.M31]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 8, worldMatrix.val[Matrix4.M02]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 9, worldMatrix.val[Matrix4.M12]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 10, worldMatrix.val[Matrix4.M22]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 11, worldMatrix.val[Matrix4.M23]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 12, worldMatrix.val[Matrix4.M30]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 13, worldMatrix.val[Matrix4.M31]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 14, worldMatrix.val[Matrix4.M32]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 11, worldMatrix.val[Matrix4.M32]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 12, worldMatrix.val[Matrix4.M03]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 13, worldMatrix.val[Matrix4.M13]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 14, worldMatrix.val[Matrix4.M23]);
|
||||
this.worldMatricesCopyHeap.put((i * 16) + 15, worldMatrix.val[Matrix4.M33]);
|
||||
}
|
||||
this.boneTexture.bindAndUpdate(this.worldMatricesCopyHeap);
|
||||
@ -515,7 +515,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
this.allowParticleSpawn = true;
|
||||
|
||||
if (this.frame >= interval[1]) {
|
||||
if ((this.sequenceLoopMode == 2) || ((this.sequenceLoopMode == 0) && (sequence.getFlags() == 0))) {
|
||||
if ((this.sequenceLoopMode == 2) || ((this.sequenceLoopMode == 1) && (sequence.getFlags() == 0))) {
|
||||
this.frame = (int) interval[0]; // TODO not cast
|
||||
|
||||
this.resetEventEmitters();
|
||||
|
@ -17,9 +17,12 @@ public abstract class MdxEmitter<MODEL_INSTANCE extends ModelInstance, EMITTER_O
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
public void update(final float dt, final boolean objectVisible) {
|
||||
if (!objectVisible) {
|
||||
return;
|
||||
}
|
||||
if (this.emitterObject.ok()) {
|
||||
super.update(dt);
|
||||
super.update(dt, objectVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ public class MdxHandler extends ModelHandler {
|
||||
public boolean load(final ModelViewer viewer) {
|
||||
viewer.addHandler(new BlpHandler());
|
||||
|
||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex);
|
||||
Shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex,
|
||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplexUnshaded, MdxShaders.fsComplex);
|
||||
Shaders.extended = viewer.webGL.createShaderProgram("#define EXTENDED_BONES\r\n" + MdxShaders.vsComplexUnshaded,
|
||||
MdxShaders.fsComplex);
|
||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
||||
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
|
@ -9,6 +9,7 @@ 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;
|
||||
@ -56,6 +57,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
super(handler, viewer, extension, pathSolver, fetchUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelInstance createInstance(final int type) {
|
||||
if (type == 1) {
|
||||
return new MdxSimpleInstance(this);
|
||||
@ -173,7 +175,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
final List<Texture> teamGlows = reforged ? MdxHandler.reforgedTeamGlows : MdxHandler.teamGlows;
|
||||
|
||||
if (teamColors.isEmpty()) {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
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,
|
||||
|
@ -10,8 +10,8 @@ public class MdxNode extends SkeletalNode {
|
||||
|
||||
@Override
|
||||
protected void convertBasis(final Quaternion computedRotation) {
|
||||
computedRotation.mulLeft(HALF_PI_Y);
|
||||
computedRotation.mulLeft(HALF_PI_X);
|
||||
computedRotation.mul(HALF_PI_Y);
|
||||
computedRotation.mul(HALF_PI_X);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,16 +40,16 @@ public class MdxRenderBatch extends RenderBatch {
|
||||
final int offset = i * 12;
|
||||
|
||||
floatView.put(offset + 0, worldMatrix.val[Matrix4.M00]);
|
||||
floatView.put(offset + 1, worldMatrix.val[Matrix4.M01]);
|
||||
floatView.put(offset + 2, worldMatrix.val[Matrix4.M02]);
|
||||
floatView.put(offset + 3, worldMatrix.val[Matrix4.M03]);
|
||||
floatView.put(offset + 4, worldMatrix.val[Matrix4.M10]);
|
||||
floatView.put(offset + 5, worldMatrix.val[Matrix4.M11]);
|
||||
floatView.put(offset + 6, worldMatrix.val[Matrix4.M12]);
|
||||
floatView.put(offset + 7, worldMatrix.val[Matrix4.M13]);
|
||||
floatView.put(offset + 8, worldMatrix.val[Matrix4.M20]);
|
||||
floatView.put(offset + 9, worldMatrix.val[Matrix4.M21]);
|
||||
floatView.put(offset + 10, worldMatrix.val[Matrix4.M22]);
|
||||
floatView.put(offset + 1, worldMatrix.val[Matrix4.M10]);
|
||||
floatView.put(offset + 2, worldMatrix.val[Matrix4.M20]);
|
||||
floatView.put(offset + 3, worldMatrix.val[Matrix4.M01]);
|
||||
floatView.put(offset + 4, worldMatrix.val[Matrix4.M11]);
|
||||
floatView.put(offset + 5, worldMatrix.val[Matrix4.M21]);
|
||||
floatView.put(offset + 6, worldMatrix.val[Matrix4.M02]);
|
||||
floatView.put(offset + 7, worldMatrix.val[Matrix4.M12]);
|
||||
floatView.put(offset + 8, worldMatrix.val[Matrix4.M22]);
|
||||
floatView.put(offset + 9, worldMatrix.val[Matrix4.M03]);
|
||||
floatView.put(offset + 10, worldMatrix.val[Matrix4.M13]);
|
||||
floatView.put(offset + 11, worldMatrix.val[Matrix4.M23]);
|
||||
}
|
||||
|
||||
@ -88,7 +88,8 @@ public class MdxRenderBatch extends RenderBatch {
|
||||
|
||||
transposeHeap.set(this.scene.camera.viewProjectionMatrix);
|
||||
transposeHeap.tra();
|
||||
shader.setUniformMatrix4fv("u_VP", transposeHeap.val, 0, transposeHeap.val.length);
|
||||
shader.setUniformMatrix4fv("u_VP", this.scene.camera.viewProjectionMatrix.val, 0,
|
||||
this.scene.camera.viewProjectionMatrix.val.length);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, model.arrayBuffer);
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, model.elementBuffer);
|
||||
|
@ -66,8 +66,7 @@ public class MdxShaders {
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
// " gl_Position = u_VP * mat4(a_m0, 0.0, a_m1, 0.0, a_m2, 0.0, a_m3, 1.0) * vec4(a_position, 1.0);\r\n" + //
|
||||
" gl_Position = u_VP * vec4(a_position, 1.0);\r\n" + //
|
||||
" gl_Position = u_VP * mat4(a_m0, 0.0, a_m1, 0.0, a_m2, 0.0, a_m3, 1.0) * vec4(a_position, 1.0);\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String fsSimple = "\r\n" + //
|
||||
@ -84,8 +83,7 @@ public class MdxShaders {
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }\r\n";
|
||||
|
||||
public static final String vsComplex = Shaders.boneTexture + "\r\n" + //
|
||||
" uniform mat4 u_mvp;\r\n" + //
|
||||
public static final String vsComplex = " uniform mat4 u_mvp;\r\n" + //
|
||||
" uniform vec4 u_vertexColor;\r\n" + //
|
||||
" uniform vec4 u_geosetColor;\r\n" + //
|
||||
" uniform float u_layerAlpha;\r\n" + //
|
||||
@ -105,6 +103,7 @@ public class MdxShaders {
|
||||
" varying vec4 v_color;\r\n" + //
|
||||
" varying vec4 v_uvTransRot;\r\n" + //
|
||||
" varying float v_uvScale;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void transform(inout vec3 position, inout vec3 normal) {\r\n" + //
|
||||
" // For the broken models out there, since the game supports this.\r\n" + //
|
||||
" if (a_boneNumber > 0.0) {\r\n" + //
|
||||
@ -132,6 +131,7 @@ public class MdxShaders {
|
||||
" position = p.xyz / a_boneNumber;\r\n" + //
|
||||
" normal = normalize(n.xyz);\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" }\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec3 position = a_position;\r\n" + //
|
||||
@ -146,6 +146,65 @@ public class MdxShaders {
|
||||
" gl_Position = u_mvp * vec4(position, 1.0);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String vsComplexUnshaded = " uniform mat4 u_mvp;\r\n" + //
|
||||
" uniform vec4 u_vertexColor;\r\n" + //
|
||||
" uniform vec4 u_geosetColor;\r\n" + //
|
||||
" uniform float u_layerAlpha;\r\n" + //
|
||||
" uniform vec2 u_uvTrans;\r\n" + //
|
||||
" uniform vec2 u_uvRot;\r\n" + //
|
||||
" uniform float u_uvScale;\r\n" + //
|
||||
" uniform bool u_hasBones;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
" attribute vec4 a_bones;\r\n" + //
|
||||
" #ifdef EXTENDED_BONES\r\n" + //
|
||||
" attribute vec4 a_extendedBones;\r\n" + //
|
||||
" #endif\r\n" + //
|
||||
" attribute float a_boneNumber;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec4 v_color;\r\n" + //
|
||||
" varying vec4 v_uvTransRot;\r\n" + //
|
||||
" varying float v_uvScale;\r\n" + //
|
||||
Shaders.boneTexture + "\r\n" + //
|
||||
" void transform(inout vec3 position) {\r\n" + //
|
||||
" // For the broken models out there, since the game supports this.\r\n" + //
|
||||
" if (a_boneNumber > 0.0) {\r\n" + //
|
||||
" vec4 position4 = vec4(position, 1.0);\r\n" + //
|
||||
" mat4 bone;\r\n" + //
|
||||
" vec4 p = vec4(0.0,0.0,0.0,0.0);\r\n" + //
|
||||
" for (int i = 0; i < 4; i++) {\r\n" + //
|
||||
" if (a_bones[i] > 0.0) {\r\n" + //
|
||||
" bone = fetchMatrix(a_bones[i] - 1.0, 0.0);\r\n" + //
|
||||
" p += bone * position4;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n" + //
|
||||
" #ifdef EXTENDED_BONES\r\n" + //
|
||||
" for (int i = 0; i < 4; i++) {\r\n" + //
|
||||
" if (a_extendedBones[i] > 0.0) {\r\n" + //
|
||||
" bone = fetchMatrix(a_extendedBones[i] - 1.0, 0.0);\r\n" + //
|
||||
" p += bone * position4;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" }\r\n" + //
|
||||
" #endif\r\n" + //
|
||||
" position = p.xyz / a_boneNumber;\r\n" + //
|
||||
// " position.x *= fetchMatrix(0.0, 0.0)[0][0];\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" position.x += 100.0;\r\n" + //
|
||||
" }\r\n" + //
|
||||
"\r\n" + //
|
||||
" }\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec3 position = a_position;\r\n" + //
|
||||
" if (u_hasBones) {\r\n" + //
|
||||
" transform(position);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_color = u_vertexColor * u_geosetColor.bgra * vec4(1.0, 1.0, 1.0, u_layerAlpha);\r\n" + //
|
||||
" v_uvTransRot = vec4(u_uvTrans, u_uvRot);\r\n" + //
|
||||
" v_uvScale = u_uvScale;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(position, 1.0);\r\n" + //
|
||||
" }";
|
||||
|
||||
public static final String fsComplex = Shaders.quatTransform + "\r\n\r\n" + //
|
||||
" uniform sampler2D u_texture;\r\n" + //
|
||||
" uniform float u_filterMode;\r\n" + //
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user