mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
More work but no prototype yet
This commit is contained in:
parent
c132b0d984
commit
c157b0e368
@ -1,65 +1,154 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
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.Texture;
|
||||
import com.badlogic.gdx.graphics.Texture.TextureFilter;
|
||||
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.Vector3;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSource;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.FolderDataSource;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||
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.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxSimpleInstance;
|
||||
|
||||
public class WarsmashGdxGame extends ApplicationAdapter {
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
||||
private DataSource codebase;
|
||||
private Texture texture;
|
||||
private ModelViewer viewer;
|
||||
private MdxModel model;
|
||||
private CameraManager cameraManager;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
this.codebase = new FolderDataSource(Paths.get("C:/MPQBuild/War3.mpq/war3.mpq"));
|
||||
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 testingFolder = new FolderDataSourceDescriptor(
|
||||
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
|
||||
this.codebase = new CompoundDataSource(Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder));
|
||||
this.viewer = new ModelViewer(this.codebase, this);
|
||||
|
||||
this.viewer.addHandler(new MdxHandler());
|
||||
|
||||
final Scene scene = this.viewer.addScene();
|
||||
|
||||
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() {
|
||||
@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);
|
||||
|
||||
instance.setScene(scene);
|
||||
|
||||
// instance.setSequence(1);
|
||||
//
|
||||
// instance.setSequenceLoopMode(2);
|
||||
|
||||
final War3ID id = War3ID.fromString("ipea");
|
||||
try {
|
||||
final String path = "terrainart\\lordaeronsummer\\lords_dirt.blp";
|
||||
final boolean has = this.codebase.has(path);
|
||||
final BufferedImage img = ImageIO.read(this.codebase.getResourceAsStream(path));
|
||||
this.texture = ImageUtils.getTexture(ImageUtils.forceBufferedImagesRGB(img));
|
||||
this.texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.batch = new SpriteBatch();
|
||||
this.font = new BitmapFont();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Gdx.gl.glClearColor(0, 1, 0, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
final int srcFunc = this.batch.getBlendSrcFunc();
|
||||
final int dstFunc = this.batch.getBlendDstFunc();
|
||||
this.viewer.updateAndRender();
|
||||
|
||||
this.batch.enableBlending();
|
||||
this.batch.begin();
|
||||
// this.font.draw(this.batch, "Hello World", 100, 100);
|
||||
this.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
this.batch.draw(this.texture, 0, 0);
|
||||
this.batch.end();
|
||||
this.batch.setBlendFunction(srcFunc, dstFunc);
|
||||
// gl.glDrawElements(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT, this.faceOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWidth() {
|
||||
return Gdx.graphics.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHeight() {
|
||||
return Gdx.graphics.getHeight();
|
||||
}
|
||||
|
||||
class CameraManager {
|
||||
private CanvasProvider canvas;
|
||||
private Camera camera;
|
||||
private float moveSpeed;
|
||||
private float rotationSpeed;
|
||||
private float zoomFactor;
|
||||
private float horizontalAngle;
|
||||
private float verticalAngle;
|
||||
private float distance;
|
||||
private Vector3 position;
|
||||
private Vector3 target;
|
||||
private Vector3 worldUp;
|
||||
private Vector3 vecHeap;
|
||||
private Quaternion quatHeap;
|
||||
private Quaternion quatHeap2;
|
||||
|
||||
// An orbit camera setup example.
|
||||
// Left mouse button controls the orbit itself.
|
||||
// The right mouse button allows to move the camera and the point it's looking
|
||||
// at on the XY plane.
|
||||
// Scrolling zooms in and out.
|
||||
private void setupCamera(final Scene scene) {
|
||||
this.canvas = scene.viewer.canvas;
|
||||
this.camera = scene.camera;
|
||||
this.moveSpeed = 2;
|
||||
this.rotationSpeed = (float) (Math.PI / 180);
|
||||
this.zoomFactor = 0.1f;
|
||||
this.horizontalAngle = (float) (Math.PI / 2);
|
||||
this.verticalAngle = (float) (Math.PI / 4);
|
||||
this.distance = 500;
|
||||
this.position = new Vector3();
|
||||
this.target = new Vector3();
|
||||
this.worldUp = new Vector3(0, 0, 1);
|
||||
this.vecHeap = new Vector3();
|
||||
this.quatHeap = new Quaternion();
|
||||
this.quatHeap2 = new Quaternion();
|
||||
|
||||
updateCamera();
|
||||
|
||||
// cameraUpdate();
|
||||
}
|
||||
|
||||
private void updateCamera() {
|
||||
// Limit the vertical angle so it doesn't flip.
|
||||
// Since the camera uses a quaternion, flips don't matter to it, but this feels
|
||||
// better.
|
||||
this.verticalAngle = (float) Math.min(Math.max(0.01, this.verticalAngle), Math.PI - 0.01);
|
||||
|
||||
this.quatHeap.idt();
|
||||
this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle);
|
||||
this.quatHeap2.idt();
|
||||
this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle);
|
||||
this.quatHeap.mul(this.quatHeap2);
|
||||
|
||||
this.position.set(0, 0, 1);
|
||||
this.quatHeap.transform(this.position);
|
||||
this.position.scl(this.distance);
|
||||
this.position = this.position.add(this.target);
|
||||
|
||||
this.camera.moveToAndFace(this.position, this.target, this.worldUp);
|
||||
}
|
||||
|
||||
// private void cameraUpdate() {
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -44,4 +44,12 @@ public class Extent {
|
||||
public float getBoundsRadius() {
|
||||
return this.boundsRadius;
|
||||
}
|
||||
|
||||
public float[] getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
public float[] getMax() {
|
||||
return this.max;
|
||||
}
|
||||
}
|
||||
|
@ -118,4 +118,24 @@ public class Material implements MdlxBlock, Chunk {
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getPriorityPlane() {
|
||||
return this.priorityPlane;
|
||||
}
|
||||
|
||||
public void setPriorityPlane(final int priorityPlane) {
|
||||
this.priorityPlane = priorityPlane;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public void setFlags(final int flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public List<Layer> getLayers() {
|
||||
return this.layers;
|
||||
}
|
||||
}
|
||||
|
@ -654,4 +654,88 @@ public class MdlxModel {
|
||||
public List<float[]> getPivotPoints() {
|
||||
return this.pivotPoints;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getAnimationFile() {
|
||||
return this.animationFile;
|
||||
}
|
||||
|
||||
public Extent getExtent() {
|
||||
return this.extent;
|
||||
}
|
||||
|
||||
public long getBlendTime() {
|
||||
return this.blendTime;
|
||||
}
|
||||
|
||||
public List<Material> getMaterials() {
|
||||
return this.materials;
|
||||
}
|
||||
|
||||
public List<Texture> getTextures() {
|
||||
return this.textures;
|
||||
}
|
||||
|
||||
public List<TextureAnimation> getTextureAnimations() {
|
||||
return this.textureAnimations;
|
||||
}
|
||||
|
||||
public List<Geoset> getGeosets() {
|
||||
return this.geosets;
|
||||
}
|
||||
|
||||
public List<GeosetAnimation> getGeosetAnimations() {
|
||||
return this.geosetAnimations;
|
||||
}
|
||||
|
||||
public List<Bone> getBones() {
|
||||
return this.bones;
|
||||
}
|
||||
|
||||
public List<Light> getLights() {
|
||||
return this.lights;
|
||||
}
|
||||
|
||||
public List<Helper> getHelpers() {
|
||||
return this.helpers;
|
||||
}
|
||||
|
||||
public List<Attachment> getAttachments() {
|
||||
return this.attachments;
|
||||
}
|
||||
|
||||
public List<ParticleEmitter> getParticleEmitters() {
|
||||
return this.particleEmitters;
|
||||
}
|
||||
|
||||
public List<ParticleEmitter2> getParticleEmitters2() {
|
||||
return this.particleEmitters2;
|
||||
}
|
||||
|
||||
public List<RibbonEmitter> getRibbonEmitters() {
|
||||
return this.ribbonEmitters;
|
||||
}
|
||||
|
||||
public List<Camera> getCameras() {
|
||||
return this.cameras;
|
||||
}
|
||||
|
||||
public List<EventObject> getEventObjects() {
|
||||
return this.eventObjects;
|
||||
}
|
||||
|
||||
public List<CollisionShape> getCollisionShapes() {
|
||||
return this.collisionShapes;
|
||||
}
|
||||
|
||||
public List<UnknownChunk> getUnknownChunks() {
|
||||
return this.unknownChunks;
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,10 @@ public class Sequence implements MdlxBlock {
|
||||
}
|
||||
|
||||
public long[] getInterval() {
|
||||
return interval;
|
||||
return this.interval;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
}
|
||||
|
@ -78,4 +78,16 @@ public class Texture implements MdlxBlock {
|
||||
stream.endBlock();
|
||||
}
|
||||
|
||||
public int getReplaceableId() {
|
||||
return this.replaceableId;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ public enum RenderMathUtils {
|
||||
return (plane.x * px) + (plane.y * py) + plane.w;
|
||||
}
|
||||
|
||||
public static int testSphere(final Vector4[] planes, final float x, final float y, final float z, final int r,
|
||||
public static int testSphere(final Vector4[] planes, final float x, final float y, final float z, final float r,
|
||||
int first) {
|
||||
if (first == -1) {
|
||||
first = 0;
|
||||
|
@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SlkFile {
|
||||
public List<List<Object>> rows;
|
||||
public List<List<Object>> rows = new ArrayList<>();
|
||||
|
||||
public SlkFile(final String buffer) {
|
||||
if (buffer != null) {
|
||||
@ -44,7 +44,7 @@ public class SlkFile {
|
||||
this.rows.set(y, new ArrayList<>());
|
||||
}
|
||||
|
||||
if (valueString.charAt('0') == '"') {
|
||||
if (valueString.charAt(0) == '"') {
|
||||
value = valueString.substring(1, valueString.length() - 1);
|
||||
}
|
||||
else if ("TRUE".equals(valueString)) {
|
||||
|
16
core/src/com/etheller/warsmash/viewer5/BatchedInstance.java
Normal file
16
core/src/com/etheller/warsmash/viewer5/BatchedInstance.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
/**
|
||||
* A batched model instance.
|
||||
*/
|
||||
public abstract class BatchedInstance extends ModelInstance {
|
||||
|
||||
public BatchedInstance(final Model model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatched() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,5 +1,19 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public class Bounds {
|
||||
public int x, y, r;
|
||||
public float x, y, z, r;
|
||||
|
||||
public void fromExtents(final float[] min, final float[] max) {
|
||||
final float x = min[0];
|
||||
final float y = min[1];
|
||||
final float z = min[2];
|
||||
final float w = max[0] - x;
|
||||
final float d = max[1] - y;
|
||||
final float h = max[2] - z;
|
||||
|
||||
this.x = x + (w / 2f);
|
||||
this.y = y + (d / 2f);
|
||||
this.z = z + (h / 2f);
|
||||
this.r = (float) (Math.max(Math.max(w, d), h) / 2.);
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,26 @@ public class Camera {
|
||||
public final Quaternion rotation;
|
||||
|
||||
public Quaternion inverseRotation;
|
||||
private final Matrix4 worldMatrix;
|
||||
/**
|
||||
* World -> View.
|
||||
*/
|
||||
private final Matrix4 viewMatrix;
|
||||
/**
|
||||
* View -> Clip.
|
||||
*/
|
||||
private final Matrix4 projectionMatrix;
|
||||
public final Matrix4 worldProjectionMatrix;
|
||||
private final Matrix4 inverseWorldMatrix;
|
||||
private final Matrix4 inverseRotationMatrix;
|
||||
private final Matrix4 inverseWorldProjectionMatrix;
|
||||
/**
|
||||
* World -> Clip.
|
||||
*/
|
||||
public final Matrix4 viewProjectionMatrix;
|
||||
/**
|
||||
* View -> World.
|
||||
*/
|
||||
private final Matrix4 inverseViewMatrix;
|
||||
/**
|
||||
* Clip -> World.
|
||||
*/
|
||||
private final Matrix4 inverseViewProjectionMatrix;
|
||||
public final Vector3 directionX;
|
||||
public final Vector3 directionY;
|
||||
public final Vector3 directionZ;
|
||||
@ -75,12 +89,11 @@ public class Camera {
|
||||
|
||||
// Derived values.
|
||||
this.inverseRotation = new Quaternion();
|
||||
this.worldMatrix = new Matrix4();
|
||||
this.viewMatrix = new Matrix4();
|
||||
this.projectionMatrix = new Matrix4();
|
||||
this.worldProjectionMatrix = new Matrix4();
|
||||
this.inverseWorldMatrix = new Matrix4();
|
||||
this.inverseRotationMatrix = new Matrix4();
|
||||
this.inverseWorldProjectionMatrix = new Matrix4();
|
||||
this.viewProjectionMatrix = new Matrix4();
|
||||
this.inverseViewMatrix = new Matrix4();
|
||||
this.inverseViewProjectionMatrix = new Matrix4();
|
||||
this.directionX = new Vector3();
|
||||
this.directionY = new Vector3();
|
||||
this.directionZ = new Vector3();
|
||||
@ -222,9 +235,9 @@ public class Camera {
|
||||
final Vector3 location = this.location;
|
||||
final Quaternion rotation = this.rotation;
|
||||
final Quaternion inverseRotation = this.inverseRotation;
|
||||
final Matrix4 worldMatrix = this.worldMatrix;
|
||||
final Matrix4 viewMatrix = this.viewMatrix;
|
||||
final Matrix4 projectionMatrix = this.projectionMatrix;
|
||||
final Matrix4 worldProjectionMatrix = this.worldProjectionMatrix;
|
||||
final Matrix4 viewProjectionMatrix = this.viewProjectionMatrix;
|
||||
final Vector3[] vectors = this.vectors;
|
||||
final Vector3[] billboardedVectors = this.billboardedVectors;
|
||||
|
||||
@ -238,19 +251,19 @@ public class Camera {
|
||||
}
|
||||
|
||||
rotation.toMatrix(projectionMatrix.val);
|
||||
worldMatrix.translate(vectorHeap.set(location).scl(-1));
|
||||
viewMatrix.translate(vectorHeap.set(location).scl(-1));
|
||||
inverseRotation.set(rotation).conjugate();
|
||||
|
||||
// World projection matrix
|
||||
// World space -> NDC space
|
||||
worldProjectionMatrix.set(projectionMatrix).mul(worldMatrix);
|
||||
viewProjectionMatrix.set(projectionMatrix).mul(viewMatrix);
|
||||
|
||||
// Recalculate the camera's frustum planes
|
||||
RenderMathUtils.unpackPlanes(this.planes, worldProjectionMatrix);
|
||||
RenderMathUtils.unpackPlanes(this.planes, viewProjectionMatrix);
|
||||
|
||||
// Inverse world matrix
|
||||
// Camera space -> world space
|
||||
this.inverseWorldMatrix.set(worldMatrix).inv();
|
||||
this.inverseViewMatrix.set(viewMatrix).inv();
|
||||
|
||||
this.directionX.set(RenderMathUtils.VEC3_UNIT_X);
|
||||
inverseRotation.transform(this.directionX);
|
||||
@ -261,8 +274,8 @@ public class Camera {
|
||||
|
||||
// Inverse world projection matrix
|
||||
// NDC space -> World space
|
||||
this.inverseWorldProjectionMatrix.set(worldProjectionMatrix);
|
||||
this.inverseWorldProjectionMatrix.inv();
|
||||
this.inverseViewProjectionMatrix.set(viewProjectionMatrix);
|
||||
this.inverseViewProjectionMatrix.inv();
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
billboardedVectors[i].set(vectors[i]);
|
||||
@ -281,18 +294,18 @@ public class Camera {
|
||||
}
|
||||
|
||||
public Vector3 cameraToWorld(final Vector3 out, final Vector3 v) {
|
||||
return out.set(v).prj(this.inverseWorldMatrix);
|
||||
return out.set(v).prj(this.inverseViewMatrix);
|
||||
}
|
||||
|
||||
public Vector3 worldToCamera(final Vector3 out, final Vector3 v) {
|
||||
return out.set(v).prj(this.worldMatrix);
|
||||
return out.set(v).prj(this.viewMatrix);
|
||||
}
|
||||
|
||||
public Vector2 worldToScreen(final Vector2 out, final Vector3 v) {
|
||||
final Rectangle viewport = this.rect;
|
||||
|
||||
vectorHeap.set(v);
|
||||
vectorHeap.prj(this.inverseWorldMatrix);
|
||||
vectorHeap.prj(this.inverseViewMatrix);
|
||||
|
||||
out.x = Math.round(((vectorHeap.x + 1) / 2) * viewport.width);
|
||||
out.y = Math.round(((vectorHeap.y + 1) / 2) * viewport.height);
|
||||
@ -309,10 +322,10 @@ public class Camera {
|
||||
final Rectangle viewport = this.rect;
|
||||
|
||||
// Intersection on the near-plane
|
||||
RenderMathUtils.unproject(a, c.set(x, y, 0), this.inverseWorldProjectionMatrix, viewport);
|
||||
RenderMathUtils.unproject(a, c.set(x, y, 0), this.inverseViewProjectionMatrix, viewport);
|
||||
|
||||
// Intersection on the far-plane
|
||||
RenderMathUtils.unproject(b, c.set(x, y, 1), this.inverseWorldProjectionMatrix, viewport);
|
||||
RenderMathUtils.unproject(b, c.set(x, y, 1), this.inverseViewProjectionMatrix, viewport);
|
||||
|
||||
out[0] = a.x;
|
||||
out[1] = a.y;
|
||||
|
@ -3,7 +3,8 @@ package com.etheller.warsmash.viewer5;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Emitter<MODEL_INSTANCE extends ModelInstance, EMITTED_OBJECT extends EmittedObject<MODEL_INSTANCE, ? extends Emitter<MODEL_INSTANCE, EMITTED_OBJECT>>> {
|
||||
public abstract class Emitter<MODEL_INSTANCE extends ModelInstance, EMITTED_OBJECT extends EmittedObject<MODEL_INSTANCE, ? extends Emitter<MODEL_INSTANCE, EMITTED_OBJECT>>>
|
||||
implements UpdatableObject {
|
||||
|
||||
public final MODEL_INSTANCE instance;
|
||||
public final List<EMITTED_OBJECT> objects;
|
||||
@ -36,6 +37,7 @@ public abstract class Emitter<MODEL_INSTANCE extends ModelInstance, EMITTED_OBJE
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
this.updateEmission(dt);
|
||||
|
||||
|
@ -8,26 +8,26 @@ import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public abstract class GenericNode {
|
||||
|
||||
protected Vector3 pivot;
|
||||
protected Vector3 localLocation;
|
||||
protected Quaternion localRotation;
|
||||
protected Vector3 localScale;
|
||||
protected Vector3 worldLocation;
|
||||
protected Quaternion worldRotation;
|
||||
protected Vector3 worldScale;
|
||||
protected Vector3 inverseWorldLocation;
|
||||
protected Quaternion inverseWorldRotation;
|
||||
protected Vector3 inverseWorldScale;
|
||||
protected Matrix4 localMatrix;
|
||||
protected Matrix4 worldMatrix;
|
||||
protected GenericNode parent;
|
||||
protected List<GenericNode> children;
|
||||
public Vector3 pivot;
|
||||
public Vector3 localLocation;
|
||||
public Quaternion localRotation;
|
||||
public Vector3 localScale;
|
||||
public Vector3 worldLocation;
|
||||
public Quaternion worldRotation;
|
||||
public Vector3 worldScale;
|
||||
public Vector3 inverseWorldLocation;
|
||||
public Quaternion inverseWorldRotation;
|
||||
public Vector3 inverseWorldScale;
|
||||
public Matrix4 localMatrix;
|
||||
public Matrix4 worldMatrix;
|
||||
public GenericNode parent;
|
||||
public List<Node> children;
|
||||
public boolean dontInheritTranslation;
|
||||
public boolean dontInheritRotation;
|
||||
public boolean dontInheritScaling;
|
||||
protected boolean visible;
|
||||
protected boolean wasDirty;
|
||||
protected boolean dirty;
|
||||
public boolean visible;
|
||||
public boolean wasDirty;
|
||||
public boolean dirty;
|
||||
|
||||
protected abstract void update(float dt, Scene scene);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public final class GenericResource extends Resource {
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ public class GridCell {
|
||||
}
|
||||
|
||||
public boolean isVisible(final Camera camera) {
|
||||
if (true) {
|
||||
return true;
|
||||
}
|
||||
this.plane = RenderMathUtils.testCell(camera.planes, this.left, this.right, this.bottom, this.top, this.plane);
|
||||
|
||||
return this.plane == -1;
|
||||
|
@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelInstanceDescriptor;
|
||||
|
||||
public abstract class Model<HANDLER extends ModelHandler> extends HandlerResource<HANDLER> {
|
||||
public Bounds bounds;
|
||||
@ -17,13 +16,14 @@ public abstract class Model<HANDLER extends ModelHandler> extends HandlerResourc
|
||||
this.preloadedInstances = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected abstract ModelInstance createInstance(int type);
|
||||
|
||||
public ModelInstance addInstance() {
|
||||
return addInstance(0);
|
||||
}
|
||||
|
||||
public ModelInstance addInstance(final int type) {
|
||||
final ModelInstanceDescriptor instanceDescriptor = this.handler.instanceDescriptor;
|
||||
final ModelInstance instance = instanceDescriptor.create(this);
|
||||
final ModelInstance instance = createInstance(type);
|
||||
|
||||
if (this.ok) {
|
||||
instance.load();
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.Vector4;
|
||||
|
||||
@ -19,8 +18,6 @@ public abstract class ModelInstance extends Node {
|
||||
public boolean paused;
|
||||
public boolean rendered;
|
||||
|
||||
public Vector3 worldLocation;
|
||||
public Vector3 worldScale;
|
||||
public Scene scene;
|
||||
|
||||
public ModelInstance(final Model model) {
|
||||
@ -96,6 +93,9 @@ 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;
|
||||
@ -122,4 +122,6 @@ public abstract class ModelInstance extends Node {
|
||||
public abstract void renderTranslucent();
|
||||
|
||||
public abstract void load();
|
||||
|
||||
protected abstract RenderBatch getBatch(TextureMapper textureMapper2);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class ModelViewer {
|
||||
this.rectBuffer = this.gl.glGenBuffer();
|
||||
this.buffer = new ClientBuffer(this.gl);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.rectBuffer);
|
||||
final ByteBuffer temp = ByteBuffer.allocate(6);
|
||||
final ByteBuffer temp = ByteBuffer.allocateDirect(6);
|
||||
temp.put((byte) 0);
|
||||
temp.put((byte) 1);
|
||||
temp.put((byte) 2);
|
||||
@ -129,7 +129,6 @@ public class ModelViewer {
|
||||
String finalSrc = src;
|
||||
String extension = "";
|
||||
boolean isFetch = false;
|
||||
final boolean resolved = false;
|
||||
|
||||
// If a given path solver, resolve.
|
||||
if (pathSolver != null) {
|
||||
@ -138,12 +137,17 @@ public class ModelViewer {
|
||||
finalSrc = solved.getFinalSrc();
|
||||
extension = solved.getExtension();
|
||||
isFetch = solved.isFetch();
|
||||
}
|
||||
|
||||
// Built-in texture sources
|
||||
// ---- TODO not using JS code here
|
||||
if (!(extension instanceof String)) {
|
||||
throw new IllegalStateException("The path solver did not return an extension!");
|
||||
}
|
||||
|
||||
if (extension.charAt(0) != '.') {
|
||||
extension = '.' + extension;
|
||||
}
|
||||
// Built-in texture sources
|
||||
// ---- TODO not using JS code here
|
||||
|
||||
if (resolved) {
|
||||
final Object[] handlerAndDataType = this.findHandler(extension.toLowerCase());
|
||||
|
||||
// Is there a handler for this file type?
|
||||
@ -181,8 +185,10 @@ public class ModelViewer {
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Load unresolved: " + finalSrc);
|
||||
throw new IllegalStateException(
|
||||
"Could not resolve " + finalSrc + ". Did you forget to pass a path solver?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean has(final String key) {
|
||||
@ -276,6 +282,7 @@ public class ModelViewer {
|
||||
public void startFrame() {
|
||||
this.gl.glDepthMask(true);
|
||||
this.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
|
||||
this.gl.glClearColor(0.5f, 0.5f, 0.5f, 1); // TODO remove white background
|
||||
}
|
||||
|
||||
public void render() {
|
||||
|
40
core/src/com/etheller/warsmash/viewer5/RenderBatch.java
Normal file
40
core/src/com/etheller/warsmash/viewer5/RenderBatch.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A render batch.
|
||||
*/
|
||||
public abstract class RenderBatch {
|
||||
public Scene scene;
|
||||
public Model<?> model;
|
||||
public TextureMapper textureMapper;
|
||||
public List<ModelInstance> instances = new ArrayList<>();
|
||||
public int count = 0;
|
||||
|
||||
public abstract void render();
|
||||
|
||||
public RenderBatch(final Scene scene, final Model<?> model, final TextureMapper textureMapper) {
|
||||
this.scene = scene;
|
||||
this.model = model;
|
||||
this.textureMapper = textureMapper;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
public void add(final ModelInstance instance) {
|
||||
if (this.count == this.instances.size()) {
|
||||
this.instances.add(instance);
|
||||
}
|
||||
else if (this.count > this.instances.size()) {
|
||||
throw new IllegalStateException("count > size");
|
||||
}
|
||||
else {
|
||||
this.instances.set(this.count, instance);
|
||||
}
|
||||
this.count++;
|
||||
}
|
||||
}
|
@ -8,8 +8,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.etheller.warsmash.viewer5.handlers.Batch;
|
||||
import com.etheller.warsmash.viewer5.handlers.BatchDescriptor;
|
||||
|
||||
/**
|
||||
* A scene.
|
||||
@ -29,22 +27,22 @@ import com.etheller.warsmash.viewer5.handlers.BatchDescriptor;
|
||||
*/
|
||||
public class Scene {
|
||||
|
||||
private final ModelViewer viewer;
|
||||
public final ModelViewer viewer;
|
||||
public final Camera camera;
|
||||
final Grid grid;
|
||||
public final Grid grid;
|
||||
public int visibleCells;
|
||||
public int visibleInstances;
|
||||
public int updatedParticles;
|
||||
public boolean audioEnabled;
|
||||
public AudioContext audioContext;
|
||||
|
||||
private final List<ModelInstance> instances;
|
||||
private final int currentInstance;
|
||||
private final List<ModelInstance> batchedInstances;
|
||||
private final int currentBatchedInstance;
|
||||
public final List<ModelInstance> instances;
|
||||
public final int currentInstance;
|
||||
public final List<ModelInstance> batchedInstances;
|
||||
public final int currentBatchedInstance;
|
||||
public final EmittedObjectUpdater emitterObjectUpdater;
|
||||
private final Map<TextureMapper, Batch> batches;
|
||||
private final Comparator<ModelInstance> instanceDepthComparator;
|
||||
public final Map<TextureMapper, RenderBatch> batches;
|
||||
public final Comparator<ModelInstance> instanceDepthComparator;
|
||||
|
||||
public Scene(final ModelViewer viewer) {
|
||||
final CanvasProvider canvas = viewer.canvas;
|
||||
@ -144,12 +142,10 @@ public class Scene {
|
||||
|
||||
public void addToBatch(final ModelInstance instance) {
|
||||
final TextureMapper textureMapper = instance.textureMapper;
|
||||
Batch batch = this.batches.get(textureMapper);
|
||||
RenderBatch batch = this.batches.get(textureMapper);
|
||||
|
||||
if (batch == null) {
|
||||
final Model<?> model = instance.model;
|
||||
final BatchDescriptor batchDescriptor = model.handler.batchDescriptor;
|
||||
batch = batchDescriptor.create(this, model, textureMapper);
|
||||
batch = instance.getBatch(textureMapper);
|
||||
|
||||
this.batches.put(textureMapper, batch);
|
||||
}
|
||||
@ -240,7 +236,7 @@ public class Scene {
|
||||
this.viewer.gl.glViewport((int) viewport.x, (int) viewport.y, (int) viewport.width, (int) viewport.height);
|
||||
|
||||
// Clear all of the batches.
|
||||
for (final Batch batch : this.batches.values()) {
|
||||
for (final RenderBatch batch : this.batches.values()) {
|
||||
batch.clear();
|
||||
}
|
||||
|
||||
@ -250,7 +246,7 @@ public class Scene {
|
||||
}
|
||||
|
||||
// Render all of the batches.
|
||||
for (final Batch batch : this.batches.values()) {
|
||||
for (final RenderBatch batch : this.batches.values()) {
|
||||
batch.render();
|
||||
}
|
||||
|
||||
|
@ -54,5 +54,5 @@ public class Shaders {
|
||||
" vec3 quat_transform(vec2 q, vec3 v) {\r\n" + //
|
||||
" return vec3(quat_transform(q, v.xy), v.z);\r\n" + //
|
||||
" }\r\n" + //
|
||||
" `,";
|
||||
" ";
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
@ -13,30 +12,9 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
protected static final Quaternion rotationHeap = new Quaternion();
|
||||
protected static final Vector3 scalingHeap = new Vector3();
|
||||
|
||||
public final Vector3 pivot;
|
||||
public final Vector3 localLocation;
|
||||
public final Quaternion localRotation;
|
||||
public final Vector3 localScale;
|
||||
public final Vector3 worldLocation;
|
||||
public final Quaternion worldRotation;
|
||||
public final Vector3 worldScale;
|
||||
public final Vector3 inverseWorldLocation;
|
||||
public final Quaternion inverseWorldRotation;
|
||||
public final Vector3 inverseWorldScale;
|
||||
public final Matrix4 localMatrix;
|
||||
public final Matrix4 worldMatrix;
|
||||
public SkeletalNode parent;
|
||||
public final List<Node> children;
|
||||
public final boolean dontInheritTranslation;
|
||||
public final boolean dontInheritRotation;
|
||||
public final boolean dontInheritScaling;
|
||||
public boolean visible;
|
||||
public boolean wasDirty;
|
||||
public boolean dirty;
|
||||
public UpdatableObject object;
|
||||
|
||||
public Object object;
|
||||
|
||||
public final boolean billboarded;
|
||||
public boolean billboarded;
|
||||
public final boolean billboardedX;
|
||||
public final boolean billboardedY;
|
||||
public final boolean billboardedZ;
|
||||
@ -158,7 +136,7 @@ public abstract class SkeletalNode extends GenericNode {
|
||||
this.inverseWorldLocation.z = -this.worldLocation.z;
|
||||
}
|
||||
|
||||
protected void updateChildren(final float dt, final Scene scene) {
|
||||
public void updateChildren(final float dt, final Scene scene) {
|
||||
for (int i = 0, l = this.children.size(); i < l; i++) {
|
||||
this.children.get(i).update(dt, scene);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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> {
|
||||
@ -16,7 +17,7 @@ public abstract class Texture extends HandlerResource<ResourceHandler> {
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
public void bind(final int unit) {
|
||||
@ -39,4 +40,12 @@ public abstract class Texture extends HandlerResource<ResourceHandler> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface UpdatableObject {
|
||||
void update(float dt);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.etheller.warsmash.viewer5.gl;
|
||||
|
||||
/**
|
||||
* TODO what is this?
|
||||
*/
|
||||
public interface ANGLEInstancedArrays {
|
||||
|
||||
void glVertexAttribDivisorANGLE(int index, int divisor);
|
||||
|
||||
void glDrawArraysInstancedANGLE(int mode, int first, int count, int instanceCount);
|
||||
|
||||
void glDrawElementsInstancedANGLE(int mode, int count, int type, int indicesOffset, int instanceCount);
|
||||
}
|
@ -33,7 +33,7 @@ public class ClientBuffer {
|
||||
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.buffer);
|
||||
|
||||
this.arrayBuffer = ByteBuffer.allocate(this.size);
|
||||
this.arrayBuffer = ByteBuffer.allocateDirect(this.size);
|
||||
this.gl.glBufferData(GL20.GL_ARRAY_BUFFER, this.size, this.arrayBuffer, GL20.GL_DYNAMIC_DRAW);
|
||||
this.byteView = this.arrayBuffer;
|
||||
this.floatView = this.arrayBuffer.asFloatBuffer();
|
||||
|
67
core/src/com/etheller/warsmash/viewer5/gl/DataTexture.java
Normal file
67
core/src/com/etheller/warsmash/viewer5/gl/DataTexture.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.etheller.warsmash.viewer5.gl;
|
||||
|
||||
import java.nio.Buffer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class DataTexture {
|
||||
public GL20 gl;
|
||||
public int texture;
|
||||
public int format;
|
||||
public int width = 0;
|
||||
public int height = 0;
|
||||
|
||||
public DataTexture(final GL20 gl, final int channels, final int width, final int height) {
|
||||
this.gl = gl;
|
||||
this.texture = gl.glGenTexture();
|
||||
this.format = (channels == 3 ? GL20.GL_RGB : GL20.GL_RGBA);
|
||||
|
||||
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_MAG_FILTER, GL20.GL_NEAREST);
|
||||
|
||||
this.reserve(width, height);
|
||||
}
|
||||
|
||||
private void reserve(final int width, final int height) {
|
||||
if ((this.width < width) || (this.height < height)) {
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
this.width = Math.max(this.width, width);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void bindAndUpdate(final Buffer buffer) {
|
||||
bindAndUpdate(buffer, this.width, this.height);
|
||||
}
|
||||
|
||||
public void bindAndUpdate(final Buffer buffer, final int width, final int height) {
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.texture);
|
||||
gl.glTexSubImage2D(GL20.GL_TEXTURE_2D, 0, 0, 0, width, height, this.format, GL20.GL_FLOAT, buffer);
|
||||
}
|
||||
|
||||
public void bind(final int unit) {
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
gl.glActiveTexture(GL20.GL_TEXTURE0 + unit);
|
||||
gl.glBindTexture(GL20.GL_TEXTURE_2D, this.texture);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5.gl;
|
||||
|
||||
public class Extensions {
|
||||
public static ANGLEInstancedArrays angleInstancedArrays;
|
||||
}
|
@ -20,6 +20,7 @@ public class WebGL {
|
||||
public ShaderProgram currentShaderProgram;
|
||||
public String floatPrecision;
|
||||
public final com.badlogic.gdx.graphics.Texture emptyTexture;
|
||||
public ANGLEInstancedArrays instancedArrays;
|
||||
|
||||
public WebGL(final GL20 gl) {
|
||||
gl.glDepthFunc(GL20.GL_LEQUAL);
|
||||
@ -43,6 +44,7 @@ public class WebGL {
|
||||
}
|
||||
}
|
||||
this.emptyTexture = new com.badlogic.gdx.graphics.Texture(imageData);
|
||||
this.instancedArrays = Extensions.angleInstancedArrays;
|
||||
}
|
||||
|
||||
public ShaderUnitDeprecated createShaderUnit(final String src, final int type) {
|
||||
@ -53,10 +55,13 @@ public class WebGL {
|
||||
return this.shaderUnits.get(hash);
|
||||
}
|
||||
|
||||
public ShaderProgram createShaderProgram(final String vertexSrc, final String fragmentSrc) {
|
||||
public ShaderProgram createShaderProgram(String vertexSrc, String fragmentSrc) {
|
||||
vertexSrc = vertexSrc.replace("mediump", "");
|
||||
fragmentSrc = fragmentSrc.replace("mediump", "");
|
||||
final Map<Integer, ShaderProgram> shaderPrograms = this.shaderPrograms;
|
||||
|
||||
final int hash = stringHash(vertexSrc + fragmentSrc);
|
||||
ShaderProgram.pedantic = false;
|
||||
if (!shaderPrograms.containsKey(hash)) {
|
||||
shaderPrograms.put(hash, new ShaderProgram(vertexSrc, fragmentSrc));
|
||||
}
|
||||
@ -66,6 +71,12 @@ public class WebGL {
|
||||
if (shaderProgram.isCompiled()) {
|
||||
return shaderProgram;
|
||||
}
|
||||
else {
|
||||
System.err.println(shaderProgram.getLog());
|
||||
if (true) {
|
||||
throw new IllegalStateException("Bad shader");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers;
|
||||
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
|
||||
public class Batch {
|
||||
|
||||
public void add(final ModelInstance instance) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void render() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers;
|
||||
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
|
||||
public interface BatchDescriptor {
|
||||
Batch create(Scene scene, Model model, TextureMapper textureMapper);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5.handlers;
|
||||
|
||||
public abstract class ModelHandler extends ResourceHandler {
|
||||
public BatchDescriptor batchDescriptor;
|
||||
public ModelInstanceDescriptor instanceDescriptor;
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class AttachmentInstance {
|
||||
import com.etheller.warsmash.viewer5.UpdatableObject;
|
||||
|
||||
public class AttachmentInstance implements UpdatableObject {
|
||||
private static final float[] visbilityHeap = new float[1];
|
||||
|
||||
private final MdxComplexInstance instance;
|
||||
private final Attachment attachment;
|
||||
private final MdxComplexInstance internalInstance;
|
||||
public final MdxComplexInstance internalInstance;
|
||||
|
||||
public AttachmentInstance(final MdxComplexInstance instance, final Attachment attachment) {
|
||||
final MdxModel internalModel = attachment.internalModel;
|
||||
@ -21,7 +23,8 @@ public class AttachmentInstance {
|
||||
this.internalInstance = internalInstance;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
|
||||
if (internalInstance.model.ok) {
|
||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
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;
|
||||
|
||||
public class BatchGroup extends GenericGroup {
|
||||
|
||||
@ -18,6 +19,7 @@ public class BatchGroup extends GenericGroup {
|
||||
this.isExtended = isExtended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final MdxComplexInstance instance) {
|
||||
final Scene scene = instance.scene;
|
||||
final MdxModel model = this.model;
|
||||
@ -41,9 +43,9 @@ public class BatchGroup extends GenericGroup {
|
||||
|
||||
shader.begin();
|
||||
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.worldProjectionMatrix);
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.viewProjectionMatrix);
|
||||
|
||||
final Texture boneTexture = instance.boneTexture;
|
||||
final DataTexture boneTexture = instance.boneTexture;
|
||||
|
||||
// Instances of models with no bones don't have a bone texture.
|
||||
if (boneTexture != null) {
|
||||
|
@ -7,7 +7,13 @@ 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);
|
||||
|
||||
this.geosetAnimation = model.getGeosetAnimations().get(bone.getGeosetAnimationId());
|
||||
final int geosetAnimationId = bone.getGeosetAnimationId();
|
||||
if (geosetAnimationId != -1) {
|
||||
this.geosetAnimation = model.getGeosetAnimations().get(geosetAnimationId);
|
||||
}
|
||||
else {
|
||||
this.geosetAnimation = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,12 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SkeletalNode;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
|
||||
public class EmitterGroup extends GenericGroup {
|
||||
private final MdxModel model;
|
||||
@ -15,12 +15,14 @@ public class EmitterGroup extends GenericGroup {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final MdxComplexInstance instance) {
|
||||
final Scene scene = instance.scene;
|
||||
final SkeletalNode[] nodes = instance.nodes;
|
||||
final Model<?> model = instance.model;
|
||||
final ModelViewer viewer = model.viewer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final ANGLEInstancedArrays instancedArrays = viewer.webGL.instancedArrays;
|
||||
final ShaderProgram shader = MdxHandler.Shaders.particles;
|
||||
|
||||
gl.glDepthMask(false);
|
||||
@ -30,36 +32,36 @@ public class EmitterGroup extends GenericGroup {
|
||||
|
||||
shader.begin();
|
||||
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.worldProjectionMatrix);
|
||||
shader.setUniformMatrix("u_mvp", scene.camera.viewProjectionMatrix);
|
||||
shader.setUniformf("u_texture", 0);
|
||||
|
||||
final int a_position = shader.getAttributeLocation("a_position");
|
||||
Gdx.gl30.glVertexAttribDivisor(a_position, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(a_position, 0);
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, viewer.rectBuffer);
|
||||
gl.glVertexAttribPointer(a_position, 1, GL20.GL_UNSIGNED_BYTE, false, 0, 0);
|
||||
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p0"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p1"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p2"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p3"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_health"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_color"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_tail"), 1);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_leftRightTop"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p0"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p1"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p2"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p3"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_health"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_color"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_tail"), 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_leftRightTop"), 1);
|
||||
|
||||
for (final int index : this.objects) {
|
||||
GeometryEmitterFuncs.renderEmitter((MdxEmitter<?, ?, ?>) nodes[index].object, shader);
|
||||
}
|
||||
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_leftRightTop"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_tail"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_color"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_health"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p3"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p2"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p1"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p0"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_leftRightTop"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_tail"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_color"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_health"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p3"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p2"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p1"), 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(shader.getAttributeLocation("a_p0"), 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,10 @@ public abstract class EventObjectEmitter<EMITTER_OBJECT extends EventObjectEmitt
|
||||
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.lastValue = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void emit() {
|
||||
this.emitObject(0);
|
||||
|
@ -21,7 +21,7 @@ import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
public abstract class EventObjectEmitterObject extends GenericObject implements EmitterObject {
|
||||
public class EventObjectEmitterObject extends GenericObject implements EmitterObject {
|
||||
private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() {
|
||||
|
||||
@Override
|
||||
@ -57,10 +57,10 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
};
|
||||
|
||||
private int geometryEmitterType = -1;
|
||||
private final String type;
|
||||
public final String type;
|
||||
private final String id;
|
||||
private final long[] keyFrames;
|
||||
private long globalSequence;
|
||||
private long globalSequence = -1;
|
||||
private final long[] defval = { 1 };
|
||||
public MdxModel internalModel;
|
||||
public Texture internalTexture;
|
||||
@ -130,7 +130,7 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("SPL".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
@ -156,6 +156,30 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
this.load(tables);
|
||||
}
|
||||
|
||||
private float getFloat(final MappedDataRow row, final String name) {
|
||||
final Float x = (Float) row.get(name);
|
||||
if (x == null) {
|
||||
return Float.NaN;
|
||||
}
|
||||
else {
|
||||
return x.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
private int getInt(final MappedDataRow row, final String name) {
|
||||
return getInt(row, name, Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
private int getInt(final MappedDataRow row, final String name, final int defaultValue) {
|
||||
final Number x = (Number) row.get(name);
|
||||
if (x == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
else {
|
||||
return x.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void load(final List<GenericResource> tables) {
|
||||
final MappedData firstTable = (MappedData) tables.get(0).data;
|
||||
final MappedDataRow row = firstTable.getRow(this.id);
|
||||
@ -182,40 +206,36 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
"ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver,
|
||||
model.solverParams);
|
||||
|
||||
this.scale = (Float) row.get("Scale");
|
||||
this.scale = getFloat(row, "Scale");
|
||||
this.colors = new float[][] {
|
||||
{ ((Float) row.get("StartR")).floatValue(), ((Float) row.get("StartG")).floatValue(),
|
||||
((Float) row.get("StartB")).floatValue(), ((Float) row.get("StartA")).floatValue() },
|
||||
{ ((Float) row.get("MiddleR")).floatValue(), ((Float) row.get("MiddleG")).floatValue(),
|
||||
((Float) row.get("MiddleB")).floatValue(), ((Float) row.get("MiddleA")).floatValue() },
|
||||
{ ((Float) row.get("EndR")).floatValue(), ((Float) row.get("EndG")).floatValue(),
|
||||
((Float) row.get("EndB")).floatValue(), ((Float) row.get("EndA")).floatValue() } };
|
||||
{ getFloat(row, "StartR"), getFloat(row, "StartG"), getFloat(row, "StartB"),
|
||||
getFloat(row, "StartA") },
|
||||
{ getFloat(row, "MiddleR"), getFloat(row, "MiddleG"), getFloat(row, "MiddleB"),
|
||||
getFloat(row, "MiddleA") },
|
||||
{ getFloat(row, "EndR"), getFloat(row, "EndG"), getFloat(row, "EndB"),
|
||||
getFloat(row, "EndA") } };
|
||||
|
||||
if ("SPL".equals(this.type)) {
|
||||
this.columns = ((Number) row.get("Columns")).intValue();
|
||||
this.rows = ((Number) row.get("Rows")).intValue();
|
||||
this.lifeSpan = ((Number) row.get("Lifespan")).floatValue()
|
||||
+ ((Number) row.get("Decay")).floatValue();
|
||||
this.columns = getInt(row, "Columns");
|
||||
this.rows = getInt(row, "Rows");
|
||||
this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay");
|
||||
this.intervals = new float[][] {
|
||||
{ ((Float) row.get("UVLifespanStart")).floatValue(),
|
||||
((Float) row.get("UVLifespanEnd")).floatValue(),
|
||||
((Float) row.get("LifespanRepeat")).floatValue() },
|
||||
{ ((Float) row.get("UVDecayStart")).floatValue(),
|
||||
((Float) row.get("UVDecayEnd")).floatValue(),
|
||||
((Float) row.get("DecayRepeat")).floatValue() }, };
|
||||
{ getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"),
|
||||
getFloat(row, "LifespanRepeat") },
|
||||
{ getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
|
||||
getFloat(row, "DecayRepeat") }, };
|
||||
}
|
||||
else {
|
||||
this.columns = 1;
|
||||
this.rows = 1;
|
||||
this.lifeSpan = ((Number) row.get("BirthTime")).floatValue()
|
||||
+ ((Number) row.get("PauseTime")).floatValue() + ((Number) row.get("Decay")).floatValue();
|
||||
this.intervalTimes = new float[] { ((Number) row.get("BirthTime")).floatValue(),
|
||||
((Number) row.get("PauseTime")).floatValue(), ((Number) row.get("Decay")).floatValue() };
|
||||
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
|
||||
this.intervalTimes = new float[] { getFloat(row, "BirthTime"), getFloat(row, "PauseTime"),
|
||||
getFloat(row, "Decay") };
|
||||
}
|
||||
|
||||
final int[] blendModes = FilterMode
|
||||
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
|
||||
.fromId(((Number) row.get("BlendMode")).intValue()));
|
||||
.fromId(getInt(row, "BlendMode")));
|
||||
|
||||
this.blendSrc = blendModes[0];
|
||||
this.blendDst = blendModes[1];
|
||||
@ -232,12 +252,12 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel"));
|
||||
|
||||
if (animSoundsRow != null) {
|
||||
this.distanceCutoff = ((Number) animSoundsRow.get("DistanceCutoff")).floatValue();
|
||||
this.maxDistance = ((Number) animSoundsRow.get("MaxDistance")).floatValue();
|
||||
this.minDistance = ((Number) animSoundsRow.get("MinDistance")).floatValue();
|
||||
this.pitch = ((Number) animSoundsRow.get("Pitch")).floatValue();
|
||||
this.pitchVariance = ((Number) animSoundsRow.get("PitchVariance")).floatValue();
|
||||
this.volume = ((Number) animSoundsRow.get("Volume")).floatValue();
|
||||
this.distanceCutoff = getFloat(animSoundsRow, "DistanceCutoff");
|
||||
this.maxDistance = getFloat(animSoundsRow, "MaxDistance");
|
||||
this.minDistance = getFloat(animSoundsRow, "MinDistance");
|
||||
this.pitch = getFloat(animSoundsRow, "Pitch");
|
||||
this.pitchVariance = getFloat(animSoundsRow, "PitchVariance");
|
||||
this.volume = getFloat(animSoundsRow, "Volume");
|
||||
|
||||
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
|
||||
final GenericResource[] resources = new GenericResource[fileNames.length];
|
||||
@ -300,4 +320,13 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ok() {
|
||||
return this.ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGeometryEmitterType() {
|
||||
return this.geometryEmitterType;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,11 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GenericGroup {
|
||||
public abstract class GenericGroup {
|
||||
public final List<Integer> objects;
|
||||
|
||||
public abstract void render(MdxComplexInstance instance);
|
||||
|
||||
public GenericGroup() {
|
||||
this.objects = new ArrayList<>(); // TODO IntArrayList
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public class GenericObject extends AnimatedObject implements GenericIndexed {
|
||||
return this.isVariant(AnimationMap.KGSC.getWar3id(), sequence);
|
||||
}
|
||||
|
||||
private static final class Variants {
|
||||
public static final class Variants {
|
||||
boolean[] translation;
|
||||
boolean[] rotation;
|
||||
boolean[] scale;
|
||||
|
@ -4,9 +4,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
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.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.viewer5.Camera;
|
||||
@ -14,6 +12,7 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.ClientBuffer;
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
@ -378,6 +377,9 @@ 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();
|
||||
@ -388,6 +390,7 @@ public class GeometryEmitterFuncs {
|
||||
|
||||
if (alive > 0) {
|
||||
final ModelViewer viewer = emitter.instance.model.viewer;
|
||||
final ANGLEInstancedArrays instancedArrays = viewer.webGL.instancedArrays;
|
||||
final ClientBuffer buffer = viewer.buffer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final int size = alive * BYTES_PER_OBJECT;
|
||||
@ -425,7 +428,7 @@ public class GeometryEmitterFuncs {
|
||||
shader.setVertexAttribute("a_leftRightTop", 3, GL20.GL_UNSIGNED_BYTE, false, BYTES_PER_OBJECT,
|
||||
BYTE_OFFSET_LEFT_RIGHT_TOP);
|
||||
|
||||
Gdx.gl30.glDrawArraysInstanced(GL30.GL_TRIANGLES, 0, 6, alive);
|
||||
instancedArrays.glDrawArraysInstancedANGLE(GL20.GL_TRIANGLES, 0, 6, alive);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,9 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
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.gl.ANGLEInstancedArrays;
|
||||
|
||||
public class Geoset {
|
||||
public MdxModel model;
|
||||
@ -123,8 +122,9 @@ public class Geoset {
|
||||
}
|
||||
|
||||
public void renderSimple(final int instances) {
|
||||
Gdx.gl30.glDrawElementsInstanced(GL30.GL_TRIANGLES, this.elements, GL30.GL_UNSIGNED_SHORT, this.faceOffset,
|
||||
instances);
|
||||
final ANGLEInstancedArrays instancedArrays = this.model.viewer.webGL.instancedArrays;
|
||||
instancedArrays.glDrawElementsInstancedANGLE(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT,
|
||||
this.faceOffset, instances);
|
||||
}
|
||||
|
||||
public void bindHd(final ShaderProgram shader, final int coordId) {
|
||||
|
@ -15,8 +15,12 @@ public class GeosetAnimation extends AnimatedObject {
|
||||
final float[] color = geosetAnimation.getColor();
|
||||
|
||||
this.alpha = geosetAnimation.getAlpha();
|
||||
this.color = new float[] { color[2], color[1], color[0] };
|
||||
this.color = new float[] { color[2], color[1], color[0] }; // Stored as RGB, but animated colors are stored as
|
||||
// BGR, so sizzle.
|
||||
this.geosetId = geosetAnimation.getGeosetId();
|
||||
|
||||
this.addVariants(AnimationMap.KGAO.getWar3id(), "alpha");
|
||||
this.addVariants(AnimationMap.KGAC.getWar3id(), "color");
|
||||
}
|
||||
|
||||
public int getAlpha(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
|
@ -1,70 +1,654 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
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.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.GenericNode;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.Node;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SkeletalNode;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
import com.etheller.warsmash.viewer5.UpdatableObject;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
|
||||
public class MdxComplexInstance extends ModelInstance {
|
||||
private static final float[] visibilityHeap = new float[1];
|
||||
private static final float[] translationHeap = new float[3];
|
||||
private static final float[] rotationHeap = new float[4];
|
||||
private static final float[] scaleHeap = new float[3];
|
||||
private static final float[] colorHeap = new float[3];
|
||||
private static final float[] alphaHeap = new float[1];
|
||||
private static final long[] textureIdHeap = new long[1];
|
||||
|
||||
public List<AttachmentInstance> attachments = new ArrayList<>();
|
||||
public List<ParticleEmitter> particleEmitters = new ArrayList<>();
|
||||
public List<ParticleEmitter2> particleEmitters2 = new ArrayList<>();
|
||||
public List<RibbonEmitter> ribbonEmitters = new ArrayList<>();
|
||||
public List<EventObjectEmitter<?, ?>> eventObjectEmitters = new ArrayList<>();
|
||||
public MdxNode[] nodes;
|
||||
public SkeletalNode[] sortedNodes;
|
||||
|
||||
public int frame;
|
||||
public int counter;
|
||||
public int sequence;
|
||||
public int sequenceLoopMode;
|
||||
public boolean sequenceEnded;
|
||||
public int teamColor;
|
||||
public Texture boneTexture;
|
||||
// TODO more fields, these few are to make related classes compile
|
||||
public float[] vertexColor;
|
||||
public int frame = 0;
|
||||
// Global sequences
|
||||
public int counter = 0;
|
||||
public int sequence = -1;
|
||||
public int sequenceLoopMode = 0;
|
||||
public boolean sequenceEnded = false;
|
||||
public int teamColor = 0;
|
||||
public float[] vertexColor = { 1, 1, 1, 1 };
|
||||
// Particles do not spawn when the sequence is -1, or when the sequence finished
|
||||
// and it's not repeating
|
||||
public boolean allowParticleSpawn = false;
|
||||
// If forced is true, everything will update regardless of variancy.
|
||||
// Any later non-forced update can then use variancy to skip updating things.
|
||||
// It is set to true every time the sequence is set with setSequence().
|
||||
public boolean forced = true;
|
||||
public float[][] geosetColors;
|
||||
public float[] layerAlphas;
|
||||
public int[] layerTextures;
|
||||
public float[][] uvAnims;
|
||||
public boolean allowParticleSpawn;
|
||||
public Matrix4[] worldMatrices;
|
||||
public FloatBuffer worldMatricesCopyHeap;
|
||||
public DataTexture boneTexture;
|
||||
|
||||
public MdxComplexInstance(final MdxModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAnimations(final float dt) {
|
||||
// TODO Auto-generated method stub
|
||||
public void load() {
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
|
||||
this.geosetColors = new float[model.geosets.size()][];
|
||||
for (int i = 0, l = model.geosets.size(); i < l; i++) {
|
||||
this.geosetColors[i] = new float[4];
|
||||
}
|
||||
|
||||
this.layerAlphas = new float[model.layers.size()];
|
||||
this.layerTextures = new int[model.layers.size()];
|
||||
this.uvAnims = new float[model.layers.size()][];
|
||||
for (int i = 0, l = model.layers.size(); i < l; i++) {
|
||||
this.layerAlphas[i] = 0;
|
||||
this.layerTextures[i] = 0;
|
||||
this.uvAnims[i] = new float[5];
|
||||
}
|
||||
|
||||
// Create the needed amount of shared nodes.
|
||||
final Object[] sharedNodeData = Node.createSkeletalNodes(model.genericObjects.size(),
|
||||
MdxNodeDescriptor.INSTANCE);
|
||||
final List<MdxNode> nodes = (List<MdxNode>) sharedNodeData[0];
|
||||
int nodeIndex = 0;
|
||||
this.nodes = nodes.toArray(new MdxNode[nodes.size()]);
|
||||
|
||||
// A shared typed array for all world matrices of the internal nodes.
|
||||
this.worldMatrices = ((List<Matrix4>) sharedNodeData[1]).toArray(new Matrix4[0]);
|
||||
this.worldMatricesCopyHeap = ByteBuffer.allocateDirect(16 * this.worldMatrices.length * 4).asFloatBuffer();
|
||||
|
||||
// And now initialize all of the nodes and objects
|
||||
for (final Bone bone : model.bones) {
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], bone);
|
||||
}
|
||||
|
||||
for (final Light light : model.lights) {
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], light);
|
||||
}
|
||||
|
||||
for (final Helper helper : model.helpers) {
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], helper);
|
||||
}
|
||||
|
||||
for (final Attachment attachment : model.attachments) {
|
||||
AttachmentInstance attachmentInstance = null;
|
||||
|
||||
// Attachments may have game models attached to them, such as Undead and
|
||||
// Nightelf building animations.
|
||||
if (attachment.internalModel != null) {
|
||||
attachmentInstance = new AttachmentInstance(this, attachment);
|
||||
|
||||
this.attachments.add(attachmentInstance);
|
||||
}
|
||||
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], attachment, attachmentInstance);
|
||||
}
|
||||
|
||||
for (final ParticleEmitterObject emitterObject : model.particleEmitters) {
|
||||
final ParticleEmitter emitter = new ParticleEmitter(this, emitterObject);
|
||||
|
||||
this.particleEmitters.add(emitter);
|
||||
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], emitterObject, emitter);
|
||||
}
|
||||
|
||||
for (final ParticleEmitter2Object emitterObject : model.particleEmitters2) {
|
||||
final ParticleEmitter2 emitter = new ParticleEmitter2(this, emitterObject);
|
||||
|
||||
this.particleEmitters2.add(emitter);
|
||||
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], emitterObject, emitter);
|
||||
}
|
||||
|
||||
for (final RibbonEmitterObject emitterObject : model.ribbonEmitters) {
|
||||
final RibbonEmitter emitter = new RibbonEmitter(this, emitterObject);
|
||||
|
||||
this.ribbonEmitters.add(emitter);
|
||||
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], emitterObject, emitter);
|
||||
}
|
||||
|
||||
for (final EventObjectEmitterObject emitterObject : model.eventObjects) {
|
||||
final String type = emitterObject.type;
|
||||
EventObjectEmitter<?, ?> emitter;
|
||||
|
||||
if ("SPN".equals(type)) {
|
||||
emitter = new EventObjectSpnEmitter(this, emitterObject);
|
||||
}
|
||||
else if ("SPL".equals(type)) {
|
||||
emitter = new EventObjectSplEmitter(this, emitterObject);
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
emitter = new EventObjectUbrEmitter(this, emitterObject);
|
||||
}
|
||||
else {
|
||||
emitter = new EventObjectSndEmitter(this, emitterObject);
|
||||
}
|
||||
|
||||
this.eventObjectEmitters.add(emitter);
|
||||
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], emitterObject, emitter);
|
||||
}
|
||||
|
||||
for (final CollisionShape collisionShape : model.collisionShapes) {
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], collisionShape);
|
||||
}
|
||||
|
||||
// Save a sorted array of all of the nodes, such that every child node comes
|
||||
// after its parent.
|
||||
// This allows for flat iteration when updating.
|
||||
final List<Integer> hierarchy = model.hierarchy;
|
||||
|
||||
this.sortedNodes = new SkeletalNode[nodes.size()];
|
||||
for (int i = 0, l = nodes.size(); i < l; i++) {
|
||||
this.sortedNodes[i] = this.nodes[hierarchy.get(i)];
|
||||
}
|
||||
|
||||
// If the sequence was changed before the model was loaded, reset it now that
|
||||
// the model loaded.
|
||||
this.setSequence(this.sequence);
|
||||
|
||||
if (model.bones.size() != 0) {
|
||||
this.boneTexture = new DataTexture(model.viewer.gl, 4, model.bones.size() * 4, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all of the emitted objects that belong to this instance.
|
||||
*/
|
||||
@Override
|
||||
public void clearEmittedObjects() {
|
||||
for (final ParticleEmitter emitter : this.particleEmitters) {
|
||||
emitter.clear();
|
||||
}
|
||||
|
||||
for (final ParticleEmitter2 emitter : this.particleEmitters2) {
|
||||
emitter.clear();
|
||||
}
|
||||
|
||||
for (final RibbonEmitter emitter : this.ribbonEmitters) {
|
||||
emitter.clear();
|
||||
}
|
||||
|
||||
for (final EventObjectEmitter<?, ?> emitter : this.eventObjectEmitters) {
|
||||
emitter.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void initNode(final MdxNode[] nodes, final SkeletalNode node, final GenericObject genericObject) {
|
||||
initNode(nodes, node, genericObject, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a skeletal node.
|
||||
*/
|
||||
private void initNode(final MdxNode[] nodes, final SkeletalNode node, final GenericObject genericObject,
|
||||
final UpdatableObject object) {
|
||||
node.pivot.set(genericObject.pivot);
|
||||
|
||||
if (genericObject.parentId == -1) {
|
||||
node.parent = this;
|
||||
}
|
||||
else {
|
||||
node.parent = nodes[genericObject.parentId];
|
||||
}
|
||||
|
||||
/// TODO: single-axis billboarding
|
||||
if (genericObject.billboarded != 0) {
|
||||
node.billboarded = true;
|
||||
} // else if (genericObject.billboardedX) {
|
||||
// node.billboardedX = true;
|
||||
// } else if (genericObject.billboardedY) {
|
||||
// node.billboardedY = true;
|
||||
// } else if (genericObject.billboardedZ) {
|
||||
// node.billboardedZ = true;
|
||||
// }
|
||||
|
||||
if (object != null) {
|
||||
node.object = object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Overriden to hide also attachment models.
|
||||
*/
|
||||
@Override
|
||||
public void clearEmittedObjects() {
|
||||
// TODO Auto-generated method stub
|
||||
public void hide() {
|
||||
super.hide();
|
||||
|
||||
for (final AttachmentInstance attachment : this.attachments) {
|
||||
attachment.internalInstance.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all of this instance internal nodes and objects. Nodes that are
|
||||
* determined to not be visible will not be updated, nor will any of their
|
||||
* children down the hierarchy.
|
||||
*/
|
||||
public void updateNodes(final float dt, final boolean forced) {
|
||||
final int sequence = this.sequence;
|
||||
final int frame = this.frame;
|
||||
final int counter = this.counter;
|
||||
final SkeletalNode[] sortedNodes = this.sortedNodes;
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
final List<GenericObject> sortedGenericObjects = model.sortedGenericObjects;
|
||||
final Scene scene = this.scene;
|
||||
|
||||
// Update the nodes
|
||||
for (int i = 0, l = sortedNodes.length; i < l; i++) {
|
||||
final GenericObject genericObject = sortedGenericObjects.get(i);
|
||||
final SkeletalNode node = sortedNodes[i];
|
||||
final GenericNode parent = node.parent;
|
||||
|
||||
genericObject.getVisibility(visibilityHeap, sequence, frame, counter);
|
||||
|
||||
final boolean objectVisible = visibilityHeap[0] > 0;
|
||||
final boolean nodeVisible = forced || (parent.visible && objectVisible);
|
||||
|
||||
node.visible = nodeVisible;
|
||||
|
||||
// Every node only needs to be updated if this is a forced update, or if both
|
||||
// the parent node and the generic object corresponding to this node are
|
||||
// visible.
|
||||
// Incoming messy code for optimizations!
|
||||
if (nodeVisible) {
|
||||
boolean wasDirty = false;
|
||||
final GenericObject.Variants variants = genericObject.variants;
|
||||
final Vector3 localLocation = node.localLocation;
|
||||
final Quaternion localRotation = node.localRotation;
|
||||
final Vector3 localScale = node.localScale;
|
||||
|
||||
// Only update the local node data if there is a need to
|
||||
if (forced || variants.generic[sequence]) {
|
||||
wasDirty = true;
|
||||
|
||||
// Translation
|
||||
if (forced || variants.translation[sequence]) {
|
||||
genericObject.getTranslation(translationHeap, sequence, frame, counter);
|
||||
|
||||
localLocation.x = translationHeap[0];
|
||||
localLocation.y = translationHeap[1];
|
||||
localLocation.z = translationHeap[2];
|
||||
}
|
||||
|
||||
// Rotation
|
||||
if (forced || variants.rotation[sequence]) {
|
||||
genericObject.getRotation(rotationHeap, sequence, frame, counter);
|
||||
|
||||
localRotation.x = rotationHeap[0];
|
||||
localRotation.y = rotationHeap[1];
|
||||
localRotation.z = rotationHeap[2];
|
||||
localRotation.w = rotationHeap[3];
|
||||
}
|
||||
|
||||
// Scale
|
||||
if (forced || variants.scale[sequence]) {
|
||||
genericObject.getScale(scaleHeap, sequence, frame, counter);
|
||||
|
||||
localScale.x = scaleHeap[0];
|
||||
localScale.y = scaleHeap[1];
|
||||
localScale.z = scaleHeap[2];
|
||||
}
|
||||
}
|
||||
|
||||
final boolean wasReallyDirty = forced || wasDirty || parent.wasDirty || genericObject.anyBillboarding;
|
||||
|
||||
node.wasDirty = wasReallyDirty;
|
||||
|
||||
// If this is a forced update, or this node's local data was updated, or the
|
||||
// parent node was updated, do a full world update.
|
||||
if (wasReallyDirty) {
|
||||
node.recalculateTransformation(scene);
|
||||
}
|
||||
|
||||
// If there is an instance object associated with this node, and the node is
|
||||
// visible (which might not be the case for a forced update!), update the
|
||||
// object.
|
||||
// This includes attachments and emitters.
|
||||
final UpdatableObject object = node.object;
|
||||
|
||||
if ((object != null) && objectVisible) {
|
||||
object.update(dt);
|
||||
}
|
||||
|
||||
// Update all of the node's non-skeletal children, which will update their
|
||||
// children, and so on.
|
||||
node.updateChildren(dt, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the batch data.
|
||||
*/
|
||||
public void updateBatches(final boolean forced) {
|
||||
final int sequence = this.sequence;
|
||||
final int frame = this.frame;
|
||||
final int counter = this.counter;
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
final List<Geoset> geosets = model.geosets;
|
||||
final List<Layer> layers = model.layers;
|
||||
final float[][] geosetColors = this.geosetColors;
|
||||
final float[] layerAlphas = this.layerAlphas;
|
||||
final int[] layerTextures = this.layerTextures;
|
||||
final float[][] uvAnims = this.uvAnims;
|
||||
|
||||
// Geoset
|
||||
for (int i = 0, l = geosets.size(); i < l; i++) {
|
||||
final Geoset geoset = geosets.get(i);
|
||||
final GeosetAnimation geosetAnimation = geoset.geosetAnimation;
|
||||
final float[] geosetColor = geosetColors[i];
|
||||
|
||||
if (geosetAnimation != null) {
|
||||
// Color
|
||||
if (forced || (geosetAnimation.variants.get("color")[sequence] != 0)) {
|
||||
geosetAnimation.getColor(colorHeap, sequence, frame, counter);
|
||||
|
||||
geosetColor[0] = colorHeap[0];
|
||||
geosetColor[1] = colorHeap[1];
|
||||
geosetColor[2] = colorHeap[2];
|
||||
}
|
||||
|
||||
// Alpha
|
||||
if (forced || (geosetAnimation.variants.get("alpha")[sequence] != 0)) {
|
||||
geosetAnimation.getAlpha(alphaHeap, sequence, frame, counter);
|
||||
|
||||
geosetColor[3] = alphaHeap[0];
|
||||
}
|
||||
}
|
||||
else if (forced) {
|
||||
geosetColor[0] = 1;
|
||||
geosetColor[1] = 1;
|
||||
geosetColor[2] = 1;
|
||||
geosetColor[3] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Layers
|
||||
for (int i = 0, l = layers.size(); i < l; i++) {
|
||||
final Layer layer = layers.get(i);
|
||||
final TextureAnimation textureAnimation = layer.textureAnimation;
|
||||
final float[] uvAnim = uvAnims[i];
|
||||
|
||||
// Alpha
|
||||
if (forced || (layer.variants.get("alpha")[sequence] != 0)) {
|
||||
layer.getAlpha(alphaHeap, sequence, frame, counter);
|
||||
|
||||
layerAlphas[i] = alphaHeap[0];
|
||||
}
|
||||
|
||||
// Sprite animation
|
||||
if (forced || (layer.variants.get("textureId")[sequence] != 0)) {
|
||||
layer.getTextureId(textureIdHeap, sequence, frame, counter);
|
||||
|
||||
layerTextures[i] = (int) textureIdHeap[0];
|
||||
}
|
||||
|
||||
if (textureAnimation != null) {
|
||||
// UV translation animation
|
||||
if (forced || (textureAnimation.variants.get("translation")[sequence] != 0)) {
|
||||
textureAnimation.getTranslation(translationHeap, sequence, frame, counter);
|
||||
|
||||
uvAnim[0] = translationHeap[0];
|
||||
uvAnim[1] = translationHeap[1];
|
||||
}
|
||||
|
||||
// UV rotation animation
|
||||
if (forced || (textureAnimation.variants.get("rotation")[sequence] != 0)) {
|
||||
textureAnimation.getRotation(rotationHeap, sequence, frame, counter);
|
||||
|
||||
uvAnim[2] = rotationHeap[2];
|
||||
uvAnim[3] = rotationHeap[3];
|
||||
}
|
||||
|
||||
// UV scale animation
|
||||
if (forced || (textureAnimation.variants.get("scale")[sequence] != 0)) {
|
||||
textureAnimation.getScale(scaleHeap, sequence, frame, counter);
|
||||
|
||||
uvAnim[4] = scaleHeap[0];
|
||||
}
|
||||
}
|
||||
else if (forced) {
|
||||
uvAnim[0] = 0;
|
||||
uvAnim[1] = 0;
|
||||
uvAnim[2] = 0;
|
||||
uvAnim[3] = 1;
|
||||
uvAnim[4] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBoneTexture() {
|
||||
if (this.boneTexture != null) {
|
||||
this.worldMatricesCopyHeap.clear();
|
||||
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) + 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) + 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) + 15, worldMatrix.val[Matrix4.M33]);
|
||||
}
|
||||
this.boneTexture.bindAndUpdate(this.worldMatricesCopyHeap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderOpaque() {
|
||||
// TODO Auto-generated method stub
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
|
||||
for (final GenericGroup group : model.opaqueGroups) {
|
||||
group.render(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTranslucent() {
|
||||
// TODO Auto-generated method stub
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
|
||||
for (final GenericGroup group : model.translucentGroups) {
|
||||
group.render(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// TODO Auto-generated method stub
|
||||
public void updateAnimations(final float dt) {
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
final int sequenceId = this.sequence;
|
||||
|
||||
if (sequenceId != -1) {
|
||||
final Sequence sequence = model.sequences.get(sequenceId);
|
||||
final long[] interval = sequence.getInterval();
|
||||
final int frameTime = model.viewer.frameTime;
|
||||
|
||||
this.frame += frameTime;
|
||||
this.counter += frameTime;
|
||||
this.allowParticleSpawn = true;
|
||||
|
||||
if (this.frame >= interval[1]) {
|
||||
if ((this.sequenceLoopMode == 2) || ((this.sequenceLoopMode == 0) && (sequence.getFlags() == 0))) {
|
||||
this.frame = (int) interval[0]; // TODO not cast
|
||||
|
||||
this.resetEventEmitters();
|
||||
}
|
||||
else {
|
||||
this.frame = (int) interval[1]; // TODO not cast
|
||||
this.counter -= frameTime;
|
||||
this.allowParticleSpawn = false;
|
||||
}
|
||||
|
||||
this.sequenceEnded = true;
|
||||
}
|
||||
else {
|
||||
this.sequenceEnded = false;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean forced = this.forced;
|
||||
|
||||
if (sequenceId == -1) {
|
||||
if (forced) {
|
||||
// Update the nodes
|
||||
this.updateNodes(dt, forced);
|
||||
|
||||
this.updateBoneTexture();
|
||||
|
||||
// Update the batches
|
||||
this.updateBatches(forced);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// let variants = model.variants;
|
||||
|
||||
// if (forced || variants.nodes[sequenceId]) {
|
||||
// Update the nodes
|
||||
this.updateNodes(dt, forced);
|
||||
|
||||
this.updateBoneTexture();
|
||||
// }
|
||||
|
||||
// if (forced || variants.batches[sequenceId]) {
|
||||
// Update the batches
|
||||
this.updateBatches(forced);
|
||||
// }
|
||||
}
|
||||
|
||||
this.forced = false;
|
||||
|
||||
}
|
||||
|
||||
public MdxComplexInstance setSequenceLoopMode(final int mode) {
|
||||
this.sequenceLoopMode = mode;
|
||||
/**
|
||||
* Set the team color of this instance.
|
||||
*/
|
||||
public MdxComplexInstance setTeamColor(final int id) {
|
||||
this.teamColor = id;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setSequence(final int sequence) {
|
||||
this.sequence = sequence;
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
/**
|
||||
* Set the vertex color of this instance.
|
||||
*/
|
||||
public MdxComplexInstance setVertexColor(final float[] color) {
|
||||
System.arraycopy(color, 0, this.vertexColor, 0, color.length);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sequence of this instance.
|
||||
*/
|
||||
public MdxComplexInstance setSequence(final int id) {
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
|
||||
this.sequence = id;
|
||||
|
||||
if (model.ok) {
|
||||
final List<Sequence> sequences = model.sequences;
|
||||
|
||||
if ((id < 0) || (id > (sequences.size() - 1))) {
|
||||
this.sequence = -1;
|
||||
this.frame = 0;
|
||||
this.allowParticleSpawn = false;
|
||||
}
|
||||
else {
|
||||
this.frame = (int) sequences.get(id).getInterval()[0]; // TODO not cast
|
||||
}
|
||||
|
||||
this.resetEventEmitters();
|
||||
|
||||
this.forced = true;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the seuqnece loop mode. 0 to never loop, 1 to loop based on the model,
|
||||
* and 2 to always loop.
|
||||
*/
|
||||
public MdxComplexInstance setSequenceLoopMode(final int mode) {
|
||||
this.sequenceLoopMode = mode;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attachment node.
|
||||
*/
|
||||
public MdxNode getAttachment(final int id) {
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
final Attachment attachment = model.attachments.get(id);
|
||||
|
||||
if (attachment != null) {
|
||||
return this.nodes[attachment.index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event emitters depend on keyframe index changes to emit, rather than only
|
||||
* values. To work, they need to check what the last keyframe was, and only if
|
||||
* it's a different one, do something. When changing sequences, these states
|
||||
* need to be reset, so they can immediately emit things if needed.
|
||||
*/
|
||||
private void resetEventEmitters() {
|
||||
/// TODO: Update this. Said Ghostwolf.
|
||||
for (final EventObjectEmitter<?, ?> eventObjectEmitter : this.eventObjectEmitters) {
|
||||
eventObjectEmitter.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RenderBatch getBatch(final TextureMapper textureMapper2) {
|
||||
throw new UnsupportedOperationException("NOT API");
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public class MdxHandler extends ModelHandler {
|
||||
this.extensions = new ArrayList<>();
|
||||
this.extensions.add(new String[] { ".mdx", "arrayBuffer" });
|
||||
this.extensions.add(new String[] { ".mdl", "text" });
|
||||
this.load = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -36,12 +37,13 @@ public class MdxHandler extends ModelHandler {
|
||||
MdxShaders.fsComplex);
|
||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
||||
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
|
||||
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
|
||||
// TODO HD reforged
|
||||
|
||||
// If a shader failed to compile, don't allow the handler to be registered, and
|
||||
// send an error instead.
|
||||
return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled()
|
||||
&& Shaders.simple.isCompiled() && Shaders.hd.isCompiled();
|
||||
&& Shaders.simple.isCompiled() /* && Shaders.hd.isCompiled() */;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,11 +1,15 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
@ -16,7 +20,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
public SolverParams solverParams = new SolverParams();
|
||||
public String name = "";
|
||||
public List<Sequence> sequences = new ArrayList<>();
|
||||
public List<Integer> globalSequences = new ArrayList<>();
|
||||
public List<Long> globalSequences = new ArrayList<>();
|
||||
public List<Material> materials = new ArrayList<>();
|
||||
public List<Layer> layers = new ArrayList<>();
|
||||
public List<Integer> replaceables = new ArrayList<>();
|
||||
@ -34,72 +38,305 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
public List<RibbonEmitterObject> ribbonEmitters = new ArrayList<>();
|
||||
public List<Camera> cameras = new ArrayList<>();
|
||||
public List<EventObjectEmitterObject> eventObjects = new ArrayList<>();
|
||||
public
|
||||
|
||||
private MdlxModel model;
|
||||
|
||||
public List<CollisionShape> collisionShapes = new ArrayList<>();
|
||||
public boolean hasLayerAnims = false;
|
||||
public boolean hasGeosetAnims = false;
|
||||
public List<Batch> batches = new ArrayList<>();
|
||||
public List<GenericObject> genericObjects = new ArrayList<>();
|
||||
public List<GenericObject> sortedGenericObjects = new ArrayList<>();
|
||||
public List<Integer> hierarchy = new ArrayList<>();
|
||||
public List<GenericGroup> opaqueGroups = new ArrayList<>();
|
||||
public List<GenericGroup> translucentGroups = new ArrayList<>();
|
||||
public List<GenericGroup> simpleGroups = new ArrayList<>();
|
||||
public int arrayBuffer;
|
||||
public int elementBuffer;
|
||||
|
||||
public List<Batch> batches = new ArrayList<>(); // TODO??
|
||||
|
||||
public List<Object> opaqueGroups;
|
||||
public List<Object> translucentGroups;
|
||||
|
||||
public MdxModel(final MdxHandler handler, final ModelViewer viewer, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
super(handler, viewer, extension, pathSolver, fetchUrl);
|
||||
}
|
||||
|
||||
public ModelInstance createInstance(final int type) {
|
||||
if (type == 1) {
|
||||
return new MdxSimpleInstance(this);
|
||||
}
|
||||
else {
|
||||
return new MdxComplexInstance(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(final Object bufferOrParser) throws IOException {
|
||||
MdlxModel parser;
|
||||
|
||||
if (bufferOrParser instanceof MdlxModel) {
|
||||
parser = (MdlxModel) bufferOrParser;
|
||||
}
|
||||
else {
|
||||
parser = new MdlxModel((InputStream) bufferOrParser);
|
||||
}
|
||||
|
||||
final ModelViewer viewer = this.viewer;
|
||||
final PathSolver pathSolver = this.pathSolver;
|
||||
final SolverParams solverParams = this.solverParams;
|
||||
final boolean reforged = parser.getVersion() > 800;
|
||||
final String texturesExt = reforged ? ".dds" : ".blp";
|
||||
|
||||
this.reforged = reforged;
|
||||
this.name = parser.getName();
|
||||
|
||||
// Initialize the bounds.
|
||||
final Extent extent = parser.getExtent();
|
||||
this.bounds.fromExtents(extent.getMin(), extent.getMax());
|
||||
|
||||
// Sequences
|
||||
this.sequences.addAll(parser.getSequences());
|
||||
|
||||
// Global sequences
|
||||
this.globalSequences.addAll(parser.getGlobalSequences());
|
||||
|
||||
// Texture animations
|
||||
for (final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation : parser
|
||||
.getTextureAnimations()) {
|
||||
this.textureAnimations.add(new TextureAnimation(this, textureAnimation));
|
||||
}
|
||||
|
||||
// Materials
|
||||
int layerId = 0;
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Material material : parser.getMaterials()) {
|
||||
final List<Layer> layers = new ArrayList<>();
|
||||
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Layer layer : material.getLayers()) {
|
||||
final Layer vLayer = new Layer(this, layer, layerId++, material.getPriorityPlane());
|
||||
|
||||
layers.add(vLayer);
|
||||
|
||||
this.layers.add(vLayer);
|
||||
}
|
||||
|
||||
this.materials.add(new Material(this, "" /* material.shader */, layers));
|
||||
|
||||
if (false /* !"".equals(material.shader) */) {
|
||||
this.hd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reforged) {
|
||||
solverParams.reforged = true;
|
||||
}
|
||||
|
||||
if (this.hd) {
|
||||
solverParams.hd = true;
|
||||
}
|
||||
|
||||
final GL20 gl = viewer.gl;
|
||||
boolean usingTeamTextures = false;
|
||||
|
||||
// Textures.
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Texture texture : parser.getTextures()) {
|
||||
String path = texture.getPath();
|
||||
final int replaceableId = texture.getReplaceableId();
|
||||
final int flags = texture.getFlags();
|
||||
|
||||
if (replaceableId != 0) {
|
||||
path = "ReplaceableTextures\\" + ReplaceableIds.getPathString(replaceableId) + ".blp";
|
||||
|
||||
if ((replaceableId == 1) || (replaceableId == 2)) {
|
||||
usingTeamTextures = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reforged && !path.endsWith(".dds")) {
|
||||
path = path.substring(0, path.length() - 4) + ".dds";
|
||||
}
|
||||
|
||||
final Texture viewerTexture = (Texture) viewer.load(path, pathSolver, solverParams);
|
||||
|
||||
// When the texture will load, it will apply its wrap modes.
|
||||
if (!viewerTexture.loaded) {
|
||||
if ((flags & 0x1) != 0) {
|
||||
viewerTexture.setWrapS(true);
|
||||
}
|
||||
|
||||
if ((flags & 0x2) != 0) {
|
||||
viewerTexture.setWrapT(true);
|
||||
}
|
||||
}
|
||||
|
||||
this.replaceables.add(replaceableId);
|
||||
this.textures.add(viewerTexture);
|
||||
}
|
||||
|
||||
// Start loading the team color and glow textures if this model uses them and
|
||||
// they weren't loaded previously.
|
||||
if (usingTeamTextures) {
|
||||
final List<Texture> teamColors = reforged ? MdxHandler.reforgedTeamColors : MdxHandler.teamColors;
|
||||
final List<Texture> teamGlows = reforged ? MdxHandler.reforgedTeamGlows : MdxHandler.teamGlows;
|
||||
|
||||
if (teamColors.isEmpty()) {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
final String id = ReplaceableIds.getIdString(i);
|
||||
|
||||
teamColors.add((Texture) viewer.load("ReplaceableTextures\\TeamColor\\TeamColor" + id + texturesExt,
|
||||
pathSolver, solverParams));
|
||||
teamGlows.add((Texture) viewer.load("ReplaceableTextures\\TeamGlow\\TeamGlow" + id + texturesExt,
|
||||
pathSolver, solverParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Geoset animations
|
||||
for (final com.etheller.warsmash.parsers.mdlx.GeosetAnimation geosetAnimation : parser.getGeosetAnimations()) {
|
||||
this.geosetAnimations.add(new GeosetAnimation(this, geosetAnimation));
|
||||
}
|
||||
|
||||
// Geosets
|
||||
SetupGeosets.setupGeosets(this, parser.getGeosets());
|
||||
|
||||
this.pivotPoints = parser.getPivotPoints();
|
||||
|
||||
// Tracks the IDs of all generic objects
|
||||
int objectId = 0;
|
||||
|
||||
// Bones
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Bone bone : parser.getBones()) {
|
||||
this.bones.add(new Bone(this, bone, objectId++));
|
||||
}
|
||||
|
||||
// Lights
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Light light : parser.getLights()) {
|
||||
this.lights.add(new Light(this, light, objectId++));
|
||||
}
|
||||
|
||||
// Helpers
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Helper helper : parser.getHelpers()) {
|
||||
this.helpers.add(new Helper(this, helper, objectId++));
|
||||
}
|
||||
|
||||
// Attachments
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Attachment attachment : parser.getAttachments()) {
|
||||
this.attachments.add(new Attachment(this, attachment, objectId++));
|
||||
}
|
||||
|
||||
// Particle Emitters
|
||||
for (final com.etheller.warsmash.parsers.mdlx.ParticleEmitter particleEmitter : parser.getParticleEmitters()) {
|
||||
this.particleEmitters.add(new ParticleEmitterObject(this, particleEmitter, objectId++));
|
||||
}
|
||||
|
||||
// Particle Emitters 2
|
||||
for (final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2 particleEmitter2 : parser
|
||||
.getParticleEmitters2()) {
|
||||
this.particleEmitters2.add(new ParticleEmitter2Object(this, particleEmitter2, objectId++));
|
||||
}
|
||||
|
||||
// Ribbon emitters
|
||||
for (final com.etheller.warsmash.parsers.mdlx.RibbonEmitter ribbonEmitter : parser.getRibbonEmitters()) {
|
||||
this.ribbonEmitters.add(new RibbonEmitterObject(this, ribbonEmitter, objectId++));
|
||||
}
|
||||
|
||||
// Camera
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Camera camera : parser.getCameras()) {
|
||||
this.cameras.add(new Camera(this, camera));
|
||||
}
|
||||
|
||||
// Event objects
|
||||
for (final com.etheller.warsmash.parsers.mdlx.EventObject eventObject : parser.getEventObjects()) {
|
||||
this.eventObjects.add(new EventObjectEmitterObject(this, eventObject, objectId++));
|
||||
}
|
||||
|
||||
// Collision shapes
|
||||
for (final com.etheller.warsmash.parsers.mdlx.CollisionShape collisionShape : parser.getCollisionShapes()) {
|
||||
this.collisionShapes.add(new CollisionShape(this, collisionShape, objectId++));
|
||||
}
|
||||
|
||||
// One array for all generic objects.
|
||||
this.genericObjects.addAll(this.bones);
|
||||
this.genericObjects.addAll(this.lights);
|
||||
this.genericObjects.addAll(this.helpers);
|
||||
this.genericObjects.addAll(this.attachments);
|
||||
this.genericObjects.addAll(this.particleEmitters);
|
||||
this.genericObjects.addAll(this.particleEmitters2);
|
||||
this.genericObjects.addAll(this.ribbonEmitters);
|
||||
this.genericObjects.addAll(this.eventObjects);
|
||||
this.genericObjects.addAll(this.collisionShapes);
|
||||
|
||||
// Render groups.
|
||||
SetupGroups.setupGroups(this);
|
||||
|
||||
// SimpleInstance render group.
|
||||
SetupSimpleGroups.setupSimpleGroups(this);
|
||||
|
||||
// Creates the sorted indices array of the generic objects
|
||||
this.setupHierarchy(-1);
|
||||
|
||||
// Keep a sorted array.
|
||||
for (int i = 0, l = this.genericObjects.size(); i < l; i++) {
|
||||
this.sortedGenericObjects.add(this.genericObjects.get(this.hierarchy.get(i)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setupHierarchy(final int parent) {
|
||||
for (int i = 0, l = this.genericObjects.size(); i < l; i++) {
|
||||
final GenericObject object = this.genericObjects.get(i);
|
||||
|
||||
if (object.parentId == parent) {
|
||||
this.hierarchy.add(i);
|
||||
|
||||
this.setupHierarchy(object.objectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void lateLoad() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load(final InputStream src, final Object options) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
try {
|
||||
this.load(src);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// TODO typing
|
||||
public List<Long> getGlobalSequences() {
|
||||
return this.model.getGlobalSequences();
|
||||
return this.globalSequences;
|
||||
}
|
||||
|
||||
public List<Sequence> getSequences() {
|
||||
return this.model.getSequences();
|
||||
return this.sequences;
|
||||
}
|
||||
|
||||
public List<float[]> getPivotPoints() {
|
||||
return this.model.getPivotPoints();
|
||||
return this.pivotPoints;
|
||||
}
|
||||
|
||||
public List<GeosetAnimation> getGeosetAnimations() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return this.geosetAnimations;
|
||||
}
|
||||
|
||||
public List<Texture> getTextures() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return this.textures;
|
||||
}
|
||||
|
||||
public List<Material> getMaterials() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return this.materials;
|
||||
}
|
||||
|
||||
public List<TextureAnimation> getTextureAnimations() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return this.textureAnimations;
|
||||
}
|
||||
|
||||
public List<Geoset> getGeosets() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
return this.geosets;
|
||||
}
|
||||
|
||||
private static final class SolverParams {
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.util.Descriptor;
|
||||
|
||||
public class MdxNodeDescriptor implements Descriptor<MdxNode> {
|
||||
public static final MdxNodeDescriptor INSTANCE = new MdxNodeDescriptor();
|
||||
|
||||
@Override
|
||||
public MdxNode create() {
|
||||
return new MdxNode();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.ClientBuffer;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
|
||||
public class MdxRenderBatch extends RenderBatch {
|
||||
|
||||
public MdxRenderBatch(final Scene scene, final Model<?> model, final TextureMapper textureMapper) {
|
||||
super(scene, model, textureMapper);
|
||||
}
|
||||
|
||||
private void bindAndUpdateBuffer(final ClientBuffer buffer) {
|
||||
final int count = this.count;
|
||||
final List<ModelInstance> instances = this.instances;
|
||||
|
||||
// Ensure there is enough memory for all of the instances data.
|
||||
buffer.reserve(count * 48);
|
||||
|
||||
final FloatBuffer floatView = buffer.floatView;
|
||||
|
||||
// "Copy" the instances into the buffer
|
||||
for (int i = 0; i < count; i++) {
|
||||
final ModelInstance instance = instances.get(i);
|
||||
final Matrix4 worldMatrix = instance.worldMatrix;
|
||||
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 + 11, worldMatrix.val[Matrix4.M23]);
|
||||
}
|
||||
|
||||
// Update the buffer.
|
||||
buffer.bindAndUpdate(count * 48);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
final int count = this.count;
|
||||
|
||||
if (count != 0) {
|
||||
final MdxModel model = (MdxModel) this.model;
|
||||
final List<Batch> batches = model.batches;
|
||||
final List<Texture> textures = model.textures;
|
||||
final ModelViewer viewer = model.viewer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final WebGL webGL = viewer.webGL;
|
||||
final ANGLEInstancedArrays instancedArrays = webGL.instancedArrays;
|
||||
final ShaderProgram shader = MdxHandler.Shaders.simple;
|
||||
final int m0 = shader.getAttributeLocation("a_m0");
|
||||
final int m1 = shader.getAttributeLocation("a_m1");
|
||||
final int m2 = shader.getAttributeLocation("a_m2");
|
||||
final int m3 = shader.getAttributeLocation("a_m3");
|
||||
final ClientBuffer buffer = viewer.buffer;
|
||||
final TextureMapper textureMapper = this.textureMapper;
|
||||
|
||||
webGL.useShaderProgram(shader);
|
||||
|
||||
this.bindAndUpdateBuffer(buffer);
|
||||
|
||||
shader.setVertexAttribute(m0, 3, GL20.GL_FLOAT, false, 48, 0);
|
||||
shader.setVertexAttribute(m1, 3, GL20.GL_FLOAT, false, 48, 12);
|
||||
shader.setVertexAttribute(m2, 3, GL20.GL_FLOAT, false, 48, 24);
|
||||
shader.setVertexAttribute(m3, 3, GL20.GL_FLOAT, false, 48, 36);
|
||||
|
||||
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);
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m0, 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m1, 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m2, 1);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m3, 1);
|
||||
|
||||
for (final GenericGroup group : model.simpleGroups) {
|
||||
for (final Integer object : group.objects) {
|
||||
final Batch batch = batches.get(object);
|
||||
final Geoset geoset = batch.geoset;
|
||||
final Layer layer = batch.layer;
|
||||
final Texture texture = textures.get(layer.textureId);
|
||||
|
||||
shader.setUniformi("u_texture", 0);
|
||||
|
||||
Texture mappedTexture = textureMapper.get(texture);
|
||||
if (mappedTexture == null) {
|
||||
mappedTexture = texture;
|
||||
}
|
||||
viewer.webGL.bindTexture(mappedTexture, 0);
|
||||
|
||||
layer.bind(shader);
|
||||
|
||||
geoset.bindSimple(shader);
|
||||
geoset.renderSimple(count);
|
||||
}
|
||||
}
|
||||
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m3, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m2, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m1, 0);
|
||||
instancedArrays.glVertexAttribDivisorANGLE(m0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -56,7 +56,7 @@ public class MdxShaders {
|
||||
" }";
|
||||
|
||||
public static final String vsSimple = "\r\n" + //
|
||||
" uniform mat4 u_mvp;\r\n" + //
|
||||
" uniform mat4 u_VP;\r\n" + //
|
||||
" attribute vec3 a_m0;\r\n" + //
|
||||
" attribute vec3 a_m1;\r\n" + //
|
||||
" attribute vec3 a_m2;\r\n" + //
|
||||
@ -66,22 +66,22 @@ public class MdxShaders {
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" gl_Position = u_mvp * 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 * 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" + //
|
||||
" precision mediump float;\r\n" + //
|
||||
" uniform sampler2D u_texture;\r\n" + //
|
||||
" uniform float u_filterMode;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec4 color = texture2D(u_texture, v_uv);\r\n" + //
|
||||
" // 1bit Alpha\r\n" + //
|
||||
" if (u_filterMode == 1.0 && color.a < 0.75) {\r\n" + //
|
||||
" discard;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" //if (u_filterMode == 1.0 && color.a < 0.75) {\r\n" + //
|
||||
" //discard;\r\n" + //
|
||||
" //}\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }";
|
||||
" }\r\n";
|
||||
|
||||
public static final String vsComplex = Shaders.boneTexture + "\r\n" + //
|
||||
" uniform mat4 u_mvp;\r\n" + //
|
||||
|
@ -1,19 +1,16 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.viewer5.BatchedInstance;
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
|
||||
public class MdxSimpleInstance extends ModelInstance {
|
||||
public class MdxSimpleInstance extends BatchedInstance {
|
||||
|
||||
public MdxSimpleInstance(final Model model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatched() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAnimations(final float dt) {
|
||||
}
|
||||
@ -34,4 +31,9 @@ public class MdxSimpleInstance extends ModelInstance {
|
||||
public void load() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderBatch getBatch(final TextureMapper textureMapper) {
|
||||
return new MdxRenderBatch(this.scene, this.model, textureMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
||||
}
|
||||
else {
|
||||
this.internalTexture = (Texture) model.viewer.load(
|
||||
"ReplaceableTextures\\" + ReplaceableIds.get(replaceableId) + ".blp", model.pathSolver,
|
||||
"ReplaceableTextures\\" + ReplaceableIds.getPathString(replaceableId) + ".blp", model.pathSolver,
|
||||
model.solverParams);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import com.etheller.warsmash.util.Interpolator;
|
||||
public class QuaternionSd extends Sd<float[]> {
|
||||
|
||||
public QuaternionSd(final MdxModel model, final Timeline<float[]> timeline) {
|
||||
super(model, timeline);
|
||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,18 +5,35 @@ import java.util.Map;
|
||||
|
||||
public class ReplaceableIds {
|
||||
private static final Map<Long, String> ID_TO_STR = new HashMap<>();
|
||||
private static final Map<Long, String> REPLACEABLE_ID_TO_STR = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
ID_TO_STR.put(Long.valueOf(i), String.format("%2d", i).replace(' ', '0'));
|
||||
}
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(1), "TeamColor\\TeamColor00");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(2), "TeamGlow\\TeamGlow00");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(11), "Cliff\\Cliff0");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(21), ""); // Used by all cursor models (HumanCursor, OrcCursor,
|
||||
// UndeadCursor, NightElfCursor)
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(31), "LordaeronTree\\LordaeronSummerTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(32), "AshenvaleTree\\AshenTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(33), "BarrensTree\\BarrensTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(34), "NorthrendTree\\NorthTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(35), "Mushroom\\MushroomTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(36), "RuinsTree\\RuinsTree");
|
||||
REPLACEABLE_ID_TO_STR.put(Long.valueOf(37), "OutlandMushroomTree\\MushroomTree");
|
||||
}
|
||||
|
||||
public static void main(final String[] args) {
|
||||
System.out.println(ID_TO_STR);
|
||||
}
|
||||
|
||||
public static String get(final long replaceableId) {
|
||||
public static String getIdString(final long replaceableId) {
|
||||
return ID_TO_STR.get(replaceableId);
|
||||
}
|
||||
|
||||
public static String getPathString(final long replaceableId) {
|
||||
return REPLACEABLE_ID_TO_STR.get(replaceableId);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import com.etheller.warsmash.util.RenderMathUtils;
|
||||
public class ScalarSd extends Sd<float[]> {
|
||||
|
||||
public ScalarSd(final MdxModel model, final Timeline<float[]> timeline) {
|
||||
super(model, timeline);
|
||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,7 +85,7 @@ public abstract class Sd<TYPE> {
|
||||
|
||||
}
|
||||
|
||||
public Sd(final MdxModel model, final Timeline<TYPE> timeline) {
|
||||
public Sd(final MdxModel model, final Timeline<TYPE> timeline, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
||||
final List<Long> globalSequences = model.getGlobalSequences();
|
||||
final int globalSequenceId = timeline.getGlobalSequenceId();
|
||||
final Integer forcedInterp = forcedInterpMap.get(timeline.getName());
|
||||
@ -105,13 +105,14 @@ public abstract class Sd<TYPE> {
|
||||
|
||||
if ((globalSequenceId != -1) && (globalSequences.size() > 0)) {
|
||||
this.globalSequence = new SdSequence<TYPE>(this, 0, globalSequences.get(globalSequenceId).longValue(),
|
||||
timeline, true);
|
||||
timeline, true, arrayDescriptor);
|
||||
}
|
||||
else {
|
||||
for (final Sequence sequence : model.getSequences()) {
|
||||
final long[] interval = sequence.getInterval();
|
||||
|
||||
this.sequences.add(new SdSequence<TYPE>(this, interval[0], interval[1], timeline, false));
|
||||
this.sequences
|
||||
.add(new SdSequence<TYPE>(this, interval[0], interval[1], timeline, false, arrayDescriptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public interface SdArrayDescriptor<TYPE> {
|
||||
SdArrayDescriptor<Object> GENERIC = new SdArrayDescriptor<Object>() {
|
||||
@Override
|
||||
public Object[] create(final int size) {
|
||||
return new Object[size];
|
||||
}
|
||||
};
|
||||
SdArrayDescriptor<float[]> FLOAT_ARRAY = new SdArrayDescriptor<float[]>() {
|
||||
@Override
|
||||
public float[][] create(final int size) {
|
||||
return new float[size][];
|
||||
}
|
||||
};
|
||||
SdArrayDescriptor<long[]> LONG_ARRAY = new SdArrayDescriptor<long[]>() {
|
||||
@Override
|
||||
public long[][] create(final int size) {
|
||||
return new long[size][];
|
||||
}
|
||||
};
|
||||
|
||||
TYPE[] create(int size);
|
||||
}
|
@ -18,7 +18,7 @@ public final class SdSequence<TYPE> {
|
||||
public boolean constant;
|
||||
|
||||
public SdSequence(final Sd<TYPE> sd, final long start, final long end, final Timeline<TYPE> timeline,
|
||||
final boolean isGlobalSequence) {
|
||||
final boolean isGlobalSequence, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
||||
this.sd = sd;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
@ -120,13 +120,17 @@ public final class SdSequence<TYPE> {
|
||||
}
|
||||
this.frames = new long[framesBuilder.size()];
|
||||
for (int i = 0; i < framesBuilder.size(); i++) {
|
||||
frames[i] = framesBuilder.get(i);
|
||||
this.frames[i] = framesBuilder.get(i);
|
||||
}
|
||||
this.values = valuesBuilder.toArray((TYPE[]) new Object[valuesBuilder.size()]);
|
||||
this.inTans = inTansBuilder.toArray((TYPE[]) new Object[inTansBuilder.size()]);
|
||||
this.outTans = outTansBuilder.toArray((TYPE[]) new Object[outTansBuilder.size()]);
|
||||
this.values = valuesBuilder.toArray(arrayDescriptor.create(valuesBuilder.size()));
|
||||
this.inTans = inTansBuilder.toArray(arrayDescriptor.create(inTansBuilder.size()));
|
||||
this.outTans = outTansBuilder.toArray(arrayDescriptor.create(outTansBuilder.size()));
|
||||
}
|
||||
|
||||
// private TYPE[] makeArray(final int size) {
|
||||
// return (TYPE[]) new Object[size];
|
||||
// }
|
||||
|
||||
public int getValue(final TYPE out, final long frame) {
|
||||
final int l = this.frames.length;
|
||||
|
||||
@ -145,7 +149,8 @@ public final class SdSequence<TYPE> {
|
||||
if (this.frames[i] > frame) {
|
||||
final long start = this.frames[i = 1];
|
||||
final long end = this.frames[i];
|
||||
final float t = RenderMathUtils.clamp((frame - start) / (end - start), 0, 1);
|
||||
final float t = RenderMathUtils.clamp(((end - start) == 0 ? 0 : ((frame - start) / (end - start))),
|
||||
0, 1);
|
||||
|
||||
this.sd.interpolate(out, this.values, this.inTans, this.outTans, i - 1, i, t);
|
||||
|
||||
|
@ -2,7 +2,7 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -11,8 +11,8 @@ import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class SetupGeosets {
|
||||
private static final int NORMAL_BATCH = 0;
|
||||
private static final int EXTENDED_BATCH = 0;
|
||||
private static final int REFORGED_BATCH = 0;
|
||||
private static final int EXTENDED_BATCH = 1;
|
||||
private static final int REFORGED_BATCH = 2;
|
||||
|
||||
public static void setupGeosets(final MdxModel model,
|
||||
final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets) {
|
||||
@ -157,30 +157,52 @@ public class SetupGeosets {
|
||||
}
|
||||
|
||||
// Positions.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, positionOffset, positions.length,
|
||||
FloatBuffer.wrap(positions));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, positionOffset, positions.length, wrap(positions));
|
||||
positionOffset += positions.length * 4;
|
||||
|
||||
// Normals.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, normalOffset, normals.length, FloatBuffer.wrap(normals));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, normalOffset, normals.length, wrap(normals));
|
||||
normalOffset += normals.length * 4;
|
||||
|
||||
// Texture coordinates.
|
||||
for (final float[] uvSet : uvSets) {
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, uvOffset, uvSet.length, FloatBuffer.wrap(uvSet));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, uvOffset, uvSet.length, wrap(uvSet));
|
||||
uvOffset += uvSet.length * 4;
|
||||
}
|
||||
|
||||
// Skin.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, ByteBuffer.wrap(skin));
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, wrap(skin));
|
||||
skinOffset += skin.length * 1;
|
||||
|
||||
// Faces.
|
||||
gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length, IntBuffer.wrap(faces));
|
||||
gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length, wrapFaces(faces));
|
||||
faceOffset += faces.length * 4;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ShortBuffer wrapFaces(final int[] faces) {
|
||||
final ShortBuffer wrapper = ByteBuffer.allocateDirect(faces.length * 2).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);
|
||||
wrapper.put(skin);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private static FloatBuffer wrap(final float[] positions) {
|
||||
final FloatBuffer wrapper = ByteBuffer.allocateDirect(positions.length * 4).asFloatBuffer();
|
||||
wrapper.put(positions);
|
||||
wrapper.clear();
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +71,8 @@ public class SetupGroups {
|
||||
}
|
||||
}
|
||||
|
||||
final List<Object> opaqueGroups = model.opaqueGroups;
|
||||
final List<Object> translucentGroups = model.translucentGroups;
|
||||
final List<GenericGroup> opaqueGroups = model.opaqueGroups;
|
||||
final List<GenericGroup> translucentGroups = model.translucentGroups;
|
||||
GenericGroup currentGroup = null;
|
||||
|
||||
for (final Batch object : opaqueBatches) {
|
||||
@ -82,7 +82,8 @@ public class SetupGroups {
|
||||
opaqueGroups.add(currentGroup);
|
||||
}
|
||||
|
||||
currentGroup.objects.add(object.index);
|
||||
final int index = object.index;
|
||||
currentGroup.objects.add(index);
|
||||
}
|
||||
|
||||
// Sort between all of the translucent batches and emitters that have priority
|
||||
@ -109,12 +110,13 @@ public class SetupGroups {
|
||||
if ((object instanceof Batch /* || object instanceof ReforgedBatch */)
|
||||
|| (object instanceof EmitterObject)) {
|
||||
if ((currentGroup == null) || !matchingGroup(currentGroup, objects)) {
|
||||
currentGroup = createMatchingGroup(model, objects);
|
||||
currentGroup = createMatchingGroup(model, object);
|
||||
|
||||
translucentGroups.add(currentGroup);
|
||||
}
|
||||
|
||||
currentGroup.objects.add(((GenericIndexed) object).getIndex());
|
||||
final int index = ((GenericIndexed) object).getIndex();
|
||||
currentGroup.objects.add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SetupSimpleGroups {
|
||||
private static final float[] alphaHeap = new float[1];
|
||||
|
||||
public static boolean isBatchSimple(final Batch batch) {
|
||||
final GeosetAnimation geosetAnimation = batch.geoset.geosetAnimation;
|
||||
|
||||
if (geosetAnimation != null) {
|
||||
geosetAnimation.getAlpha(alphaHeap, 0, 0, 0);
|
||||
|
||||
if (alphaHeap[0] <= 0.01) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Layer layer;
|
||||
|
||||
if (batch instanceof Batch) {
|
||||
layer = batch.layer;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("reforged?"); // TODO
|
||||
// layer = batch.material.layers[0];
|
||||
}
|
||||
|
||||
layer.getAlpha(alphaHeap, 0, 0, 0);
|
||||
|
||||
if (alphaHeap[0] < 0.01) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setupSimpleGroups(final MdxModel model) {
|
||||
final List<Batch> batches = model.batches;
|
||||
final List<GenericGroup> simpleGroups = model.simpleGroups;
|
||||
|
||||
for (final GenericGroup group : model.opaqueGroups) {
|
||||
GenericGroup simpleGroup;
|
||||
|
||||
if (group instanceof BatchGroup) {
|
||||
simpleGroup = new BatchGroup(model, ((BatchGroup) group).isExtended);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("reforged?"); // TODO
|
||||
// simpleGroup = new ReforgedBatchGroup(model, group.shader);
|
||||
}
|
||||
|
||||
for (final Integer object : group.objects) {
|
||||
if (isBatchSimple(batches.get(object))) {
|
||||
simpleGroup.objects.add(object);
|
||||
}
|
||||
}
|
||||
|
||||
simpleGroups.add(simpleGroup);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,10 @@ public class TextureAnimation extends AnimatedObject {
|
||||
public TextureAnimation(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation) {
|
||||
super(model, textureAnimation);
|
||||
|
||||
this.addVariants(AnimationMap.KTAT.getWar3id(), "translation");
|
||||
this.addVariants(AnimationMap.KTAR.getWar3id(), "rotation");
|
||||
this.addVariants(AnimationMap.KTAS.getWar3id(), "scale");
|
||||
}
|
||||
|
||||
public int getTranslation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
|
@ -6,7 +6,7 @@ import com.etheller.warsmash.util.RenderMathUtils;
|
||||
public class UInt32Sd extends Sd<long[]> {
|
||||
|
||||
public UInt32Sd(final MdxModel model, final Timeline<long[]> timeline) {
|
||||
super(model, timeline);
|
||||
super(model, timeline, SdArrayDescriptor.LONG_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,7 +6,7 @@ import com.etheller.warsmash.util.Interpolator;
|
||||
public class VectorSd extends Sd<float[]> {
|
||||
|
||||
public VectorSd(final MdxModel model, final Timeline<float[]> timeline) {
|
||||
super(model, timeline);
|
||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,12 +1,37 @@
|
||||
package com.etheller.warsmash.desktop;
|
||||
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.etheller.warsmash.WarsmashGdxGame;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||
|
||||
public class DesktopLauncher {
|
||||
public static void main (String[] arg) {
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
public static void main(final String[] arg) {
|
||||
Extensions.angleInstancedArrays = new ANGLEInstancedArrays() {
|
||||
@Override
|
||||
public void glVertexAttribDivisorANGLE(final int index, final int divisor) {
|
||||
GL33.glVertexAttribDivisor(index, divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDrawElementsInstancedANGLE(final int mode, final int count, final int type,
|
||||
final int indicesOffset, final int instanceCount) {
|
||||
GL31.glDrawElementsInstanced(mode, count, type, indicesOffset, instanceCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDrawArraysInstancedANGLE(final int mode, final int first, final int count,
|
||||
final int instanceCount) {
|
||||
GL31.glDrawArraysInstanced(mode, first, count, instanceCount);
|
||||
}
|
||||
};
|
||||
final LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
config.useGL30 = true;
|
||||
config.gles30ContextMinorVersion = 3;
|
||||
new LwjglApplication(new WarsmashGdxGame(), config);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user