mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Support for walkable destructables
This commit is contained in:
parent
e919f35904
commit
7cea3e3fcd
@ -38,6 +38,7 @@ import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.HashedGameObject;
|
||||
import com.etheller.warsmash.util.DataSourceFileHandle;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
@ -141,7 +142,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
}
|
||||
|
||||
final Element cameraData = this.viewer.miscData.get("Camera");
|
||||
final Element cameraListenerData = this.viewer.miscData.get("Listener");
|
||||
Element cameraListenerData = this.viewer.miscData.get("Listener");
|
||||
if(cameraListenerData==null) {
|
||||
cameraListenerData = new Element("Listener", new DataTable(null));
|
||||
}
|
||||
final CameraPreset[] cameraPresets = new CameraPreset[6];
|
||||
for (int i = 0; i < cameraPresets.length; i++) {
|
||||
cameraPresets[i] = new CameraPreset(cameraData.getFieldFloatValue("AOA", i),
|
||||
|
@ -75,6 +75,43 @@ public class Quadtree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean intersect(float x, float y, final QuadtreeIntersector<T> intersector) {
|
||||
if (this.leaf) {
|
||||
for (int i = 0; i < this.nodes.size; i++) {
|
||||
final Node<T> node = this.nodes.get(i);
|
||||
if (node.bounds.contains(x, y)) {
|
||||
if (intersector.onIntersect(node.object)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (this.northeast.bounds.contains(x, y)) {
|
||||
if (this.northeast.intersect(x, y, intersector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.northwest.bounds.contains(x, y)) {
|
||||
if (this.northwest.intersect(x, y, intersector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.southwest.bounds.contains(x, y)) {
|
||||
if (this.southwest.intersect(x, y, intersector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.southeast.bounds.contains(x, y)) {
|
||||
if (this.southeast.intersect(x, y, intersector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void add(final Node<T> node, final int depth) {
|
||||
if (this.leaf) {
|
||||
if ((this.nodes.size >= SPLIT_THRESHOLD) && (depth < MAX_DEPTH)) {
|
||||
|
@ -31,4 +31,8 @@ public class Bounds {
|
||||
public boolean intersectRayFast(final Ray ray) {
|
||||
return Intersector.intersectRayBoundsFast(ray, this.boundingBox);
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox() {
|
||||
return boundingBox;
|
||||
}
|
||||
}
|
||||
|
@ -35,12 +35,13 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
@Override
|
||||
public InputStream read() {
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
;
|
||||
};
|
||||
if (data != null) {
|
||||
return Gdx.audio.newSound(temp);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Warning: missing sound file: " + this.filename);
|
||||
return null;
|
||||
}
|
||||
@ -58,11 +59,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
catch (final UnsupportedEncodingException e) {
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new MappedData(stringBuilder.toString());
|
||||
@ -96,10 +95,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
/**
|
||||
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
|
||||
* are loaded.
|
||||
*
|
||||
* <p>
|
||||
* This is because, like the other geometry emitters, it is fine to use them
|
||||
* even if the textures don't load.
|
||||
*
|
||||
* <p>
|
||||
* The particles will simply be black.
|
||||
*/
|
||||
private boolean ok = false;
|
||||
@ -120,11 +119,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
|
||||
if ("SPL".equals(type)) {
|
||||
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT;
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
} else if ("UBR".equals(type)) {
|
||||
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
|
||||
}
|
||||
else if ("SPN".equals(type)) {
|
||||
} else if ("SPN".equals(type)) {
|
||||
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
|
||||
}
|
||||
|
||||
@ -144,24 +141,20 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
if ("SPN".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("SPL".equals(type)) {
|
||||
} else if ("SPL".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
} else if ("UBR".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("SND".equals(type)) {
|
||||
} else if ("SND".equals(type)) {
|
||||
if (!model.reforged) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
|
||||
// "Point01".
|
||||
return;
|
||||
@ -176,8 +169,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
final Float x = (Float) row.get(name);
|
||||
if (x == null) {
|
||||
return Float.NaN;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return x.floatValue();
|
||||
}
|
||||
}
|
||||
@ -190,14 +182,16 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
final Number x = (Number) row.get(name);
|
||||
if (x == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return x.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void load(final List<GenericResource> tables) {
|
||||
final MappedData firstTable = (MappedData) tables.get(0).data;
|
||||
if (firstTable == null) {
|
||||
return;
|
||||
}
|
||||
final MappedDataRow row = firstTable.getRow(this.id.trim());
|
||||
|
||||
if (row != null) {
|
||||
@ -214,8 +208,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
// this.internalModel.whenLoaded((model) => this.ok = model.ok)
|
||||
this.ok = this.internalModel.ok;
|
||||
}
|
||||
}
|
||||
else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
|
||||
} else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
|
||||
final String texturesExt = model.reforged ? ".dds" : ".blp";
|
||||
|
||||
this.internalTexture = (Texture) viewer.load(
|
||||
@ -241,8 +234,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
getFloat(row, "LifespanRepeat")},
|
||||
{getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
|
||||
getFloat(row, "DecayRepeat")},};
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.columns = 1;
|
||||
this.rows = 1;
|
||||
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
|
||||
@ -258,8 +250,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
this.blendDst = blendModes[1];
|
||||
|
||||
this.ok = true;
|
||||
}
|
||||
else if ("SND".equals(this.type)) {
|
||||
} else if ("SND".equals(this.type)) {
|
||||
// Only load sounds if audio is enabled.
|
||||
// This is mostly to save on bandwidth and loading time, especially when loading
|
||||
// full maps.
|
||||
@ -288,8 +279,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
System.err.println("Null sound: " + fileNames[i]);
|
||||
}
|
||||
resources[i] = genericResource;
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
} catch (final Exception exc) {
|
||||
System.err.println("Failed to load sound: " + path);
|
||||
exc.printStackTrace();
|
||||
}
|
||||
@ -304,12 +294,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
this.ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Unknown event object type: " + this.type + this.id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Unknown event object ID: " + this.type + this.id);
|
||||
}
|
||||
}
|
||||
@ -318,13 +306,11 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
if (this.globalSequence != -1) {
|
||||
|
||||
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
|
||||
}
|
||||
else if (instance.sequence != -1) {
|
||||
} else if (instance.sequence != -1) {
|
||||
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
|
||||
|
||||
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
out[0] = this.defval[0];
|
||||
|
||||
return -1;
|
||||
@ -338,8 +324,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
out[0] = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
else if (this.keyFrames[i] <= frame) {
|
||||
} else if (this.keyFrames[i] <= frame) {
|
||||
out[0] = 1;
|
||||
|
||||
return i;
|
||||
|
@ -160,14 +160,11 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
|
||||
if ("SPN".equals(type)) {
|
||||
emitter = new EventObjectSpnEmitter(this, emitterObject);
|
||||
}
|
||||
else if ("SPL".equals(type)) {
|
||||
} else if ("SPL".equals(type)) {
|
||||
emitter = new EventObjectSplEmitter(this, emitterObject);
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
} else if ("UBR".equals(type)) {
|
||||
emitter = new EventObjectUbrEmitter(this, emitterObject);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
emitter = new EventObjectSndEmitter(this, emitterObject);
|
||||
}
|
||||
|
||||
@ -234,22 +231,18 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
|
||||
if (genericObject.parentId == -1) {
|
||||
node.parent = this;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
node.parent = nodes[genericObject.parentId];
|
||||
}
|
||||
|
||||
/// TODO: single-axis billboarding
|
||||
if (genericObject.billboarded != 0) {
|
||||
node.billboarded = true;
|
||||
}
|
||||
else if (genericObject.billboardedX != 0) {
|
||||
} else if (genericObject.billboardedX != 0) {
|
||||
node.billboardedX = true;
|
||||
}
|
||||
else if (genericObject.billboardedY != 0) {
|
||||
} else if (genericObject.billboardedY != 0) {
|
||||
node.billboardedY = true;
|
||||
}
|
||||
else if (genericObject.billboardedZ != 0) {
|
||||
} else if (genericObject.billboardedZ != 0) {
|
||||
node.billboardedZ = true;
|
||||
}
|
||||
|
||||
@ -412,8 +405,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
|
||||
geosetColor[3] = alphaHeap[0];
|
||||
}
|
||||
}
|
||||
else if (forced) {
|
||||
} else if (forced) {
|
||||
geosetColor[0] = 1;
|
||||
geosetColor[1] = 1;
|
||||
geosetColor[2] = 1;
|
||||
@ -464,8 +456,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
|
||||
uvAnim[4] = scaleHeap[0];
|
||||
}
|
||||
}
|
||||
else if (forced) {
|
||||
} else if (forced) {
|
||||
uvAnim[0] = 0;
|
||||
uvAnim[1] = 0;
|
||||
uvAnim[2] = 0;
|
||||
@ -545,8 +536,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
this.floatingFrame = this.frame = (int) interval[0]; // TODO not cast
|
||||
|
||||
this.resetEventEmitters();
|
||||
}
|
||||
else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
|
||||
} else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
|
||||
// mode
|
||||
final float framesPast = this.floatingFrame - interval[1];
|
||||
|
||||
@ -557,8 +547,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
this.sequenceEnded = false;
|
||||
this.resetEventEmitters();
|
||||
this.forced = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.floatingFrame = this.frame = (int) interval[1]; // TODO not cast
|
||||
this.counter -= integerFrameTime;
|
||||
this.allowParticleSpawn = false;
|
||||
@ -568,8 +557,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
}
|
||||
|
||||
this.sequenceEnded = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.sequenceEnded = false;
|
||||
}
|
||||
}
|
||||
@ -586,8 +574,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
// Update the batches
|
||||
this.updateBatches(forced);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// let variants = model.variants;
|
||||
|
||||
// if (forced || variants.nodes[sequenceId]) {
|
||||
@ -665,8 +652,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
this.frame = 0;
|
||||
this.floatingFrame = 0;
|
||||
this.allowParticleSpawn = false;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.frame = (int) sequences.get(id).getInterval()[0]; // TODO not cast
|
||||
this.floatingFrame = this.frame;
|
||||
this.sequenceEnded = false;
|
||||
@ -734,15 +720,17 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
*
|
||||
* @param ray
|
||||
*/
|
||||
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh) {
|
||||
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh, final boolean onlyUseMesh) {
|
||||
final MdxModel mdxModel = (MdxModel) this.model;
|
||||
final List<CollisionShape> collisionShapes = mdxModel.collisionShapes;
|
||||
if (!onlyUseMesh) {
|
||||
for (final CollisionShape collisionShape : collisionShapes) {
|
||||
final MdxNode mdxNode = this.nodes[collisionShape.index];
|
||||
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collisionShapes.isEmpty() || alwaysUseMesh) {
|
||||
for (final Geoset geoset : mdxModel.geosets) {
|
||||
if (!geoset.unselectable) {
|
||||
|
@ -32,14 +32,14 @@ public class MdxHandler extends ModelHandler {
|
||||
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
|
||||
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
|
||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
||||
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
//Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
// 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
|
||||
|
@ -152,6 +152,9 @@ public final class SdSequence<TYPE> {
|
||||
}
|
||||
|
||||
private TYPE[] fixTimelineArray(final Timeline<TYPE> timeline, final TYPE[] values) {
|
||||
if(values == null) {
|
||||
return null;
|
||||
}
|
||||
if (timeline.getName().equals(AnimationMap.KLAC.getWar3id())
|
||||
|| timeline.getName().equals(AnimationMap.KLBC.getWar3id())) {
|
||||
final float[][] flippedColorData = new float[values.length][3];
|
||||
|
@ -2,10 +2,13 @@ package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
|
||||
public class RenderDestructable extends RenderDoodad {
|
||||
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
|
||||
private static final War3ID TEX_ID = War3ID.fromString("btxi");
|
||||
|
||||
private final float life;
|
||||
|
||||
@ -14,6 +17,16 @@ public class RenderDestructable extends RenderDoodad {
|
||||
final float maxPitch, final float maxRoll, final float life) {
|
||||
super(map, model, row, doodad, type, maxPitch, maxRoll);
|
||||
this.life = life;
|
||||
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
|
||||
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
|
||||
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
|
||||
int dotIndex = replaceableTextureFile.lastIndexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
replaceableTextureFile = replaceableTextureFile.substring(0, dotIndex);
|
||||
}
|
||||
replaceableTextureFile += ".blp";
|
||||
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -13,8 +13,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
|
||||
public class RenderDoodad {
|
||||
private static final int SAMPLE_RADIUS = 4;
|
||||
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
|
||||
private static final War3ID TEX_ID = War3ID.fromString("btxi");
|
||||
public final ModelInstance instance;
|
||||
private final MutableGameObject row;
|
||||
private final float maxPitch;
|
||||
@ -30,8 +28,7 @@ public class RenderDoodad {
|
||||
|
||||
if (isSimple && false) {
|
||||
instance = model.addInstance(1);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
instance = model.addInstance();
|
||||
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
|
||||
}
|
||||
@ -74,17 +71,6 @@ public class RenderDoodad {
|
||||
final float defScale = row.readSLKTagFloat("defScale");
|
||||
instance.uniformScale(defScale);
|
||||
}
|
||||
if (type == WorldEditorDataType.DESTRUCTIBLES) {
|
||||
// TODO destructables need to be their own type, game simulation, etc
|
||||
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
|
||||
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
|
||||
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
|
||||
if (!replaceableTextureFile.toLowerCase().endsWith(".blp")) {
|
||||
replaceableTextureFile += ".blp";
|
||||
}
|
||||
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
|
||||
}
|
||||
}
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
this.instance = instance;
|
||||
|
@ -19,6 +19,8 @@ import java.util.function.Consumer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.math.collision.BoundingBox;
|
||||
import com.etheller.warsmash.util.*;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
|
||||
|
||||
@ -47,11 +49,6 @@ import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.MappedData;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
import com.etheller.warsmash.viewer5.GenericResource;
|
||||
import com.etheller.warsmash.viewer5.Grid;
|
||||
@ -118,10 +115,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
private static final War3ID sloc = War3ID.fromString("sloc");
|
||||
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
|
||||
private static final float[] rayHeap = new float[6];
|
||||
private static final Ray gdxRayHeap = new Ray();
|
||||
public static final Ray gdxRayHeap = new Ray();
|
||||
private static final Vector2 mousePosHeap = new Vector2();
|
||||
private static final Vector3 normalHeap = new Vector3();
|
||||
private static final Vector3 intersectionHeap = new Vector3();
|
||||
public static final Vector3 intersectionHeap = new Vector3();
|
||||
private static final Rectangle rectangleHeap = new Rectangle();
|
||||
public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
|
||||
|
||||
public PathSolver wc3PathSolver = PathSolver.DEFAULT;
|
||||
@ -186,6 +184,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
private GameUI gameUI;
|
||||
private Vector3 lightDirection;
|
||||
|
||||
private Quadtree<MdxComplexInstance> walkableObjectsTree;
|
||||
private QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight();
|
||||
private QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint();
|
||||
private QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable();
|
||||
|
||||
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
|
||||
super(dataSource, canvas);
|
||||
this.gameDataSource = dataSource;
|
||||
@ -311,8 +314,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final LoadGenericCallback callback) {
|
||||
if (this.mapMpq == null) {
|
||||
return loadGeneric(path, dataType, callback);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return loadGeneric(path, dataType, callback, this.dataSource);
|
||||
}
|
||||
}
|
||||
@ -343,22 +345,19 @@ public class War3MapViewer extends ModelViewer {
|
||||
tilesetSource = new CompoundDataSource(Arrays.asList(compoundDataSource,
|
||||
new SubdirDataSource(compoundDataSource, tileset + ".mpq/"),
|
||||
new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/")));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
final byte[] mapData = IOUtils.toByteArray(mapStream);
|
||||
sbc = new SeekableInMemoryByteChannel(mapData);
|
||||
final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc);
|
||||
tilesetSource = new CompoundDataSource(
|
||||
Arrays.asList(compoundDataSource, internalMpqContentsDataSource));
|
||||
}
|
||||
}
|
||||
catch (final IOException exc) {
|
||||
} catch (final IOException exc) {
|
||||
tilesetSource = new CompoundDataSource(
|
||||
Arrays.asList(compoundDataSource, new SubdirDataSource(compoundDataSource, tileset + ".mpq/"),
|
||||
new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/")));
|
||||
}
|
||||
}
|
||||
catch (final MPQException e) {
|
||||
} catch (final MPQException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
setDataSource(tilesetSource);
|
||||
@ -432,8 +431,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
modelInstance.setScene(War3MapViewer.this.worldScene);
|
||||
if (bounceIndex == 0) {
|
||||
SequenceUtils.randomBirthSequence(modelInstance);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
SequenceUtils.randomStandSequence(modelInstance);
|
||||
}
|
||||
modelInstance.setLocation(x, y, height);
|
||||
@ -503,13 +501,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
War3MapViewer.this.worldScene.removeInstance(renderUnit.instance);
|
||||
}
|
||||
}, this.terrain.pathingGrid,
|
||||
new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128),
|
||||
terrain.getEntireMap(),
|
||||
this.seededRandom, w3iFile.getPlayers());
|
||||
|
||||
walkableObjectsTree = new Quadtree<>(terrain.getEntireMap());
|
||||
if (this.doodadsAndDestructiblesLoaded) {
|
||||
this.loadDoodadsAndDestructibles(this.allObjectData);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
|
||||
}
|
||||
|
||||
@ -530,8 +528,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
public void loadAfterUI() throws IOException {
|
||||
if (this.unitsAndItemsLoaded) {
|
||||
this.loadUnitsAndItems(this.allObjectData);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
|
||||
}
|
||||
|
||||
@ -601,8 +598,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
bufferedImage = TgaFile.readTGA(pathingTexture,
|
||||
this.mapMpq.getResourceAsStream(pathingTexture));
|
||||
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage);
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
} catch (final Exception exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
@ -620,23 +616,30 @@ public class War3MapViewer extends ModelViewer {
|
||||
String path;
|
||||
if (this.mapMpq.has(fileVar)) {
|
||||
path = fileVar;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
path = file;
|
||||
}
|
||||
MdxModel model;
|
||||
if (this.mapMpq.has(path)) {
|
||||
model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams);
|
||||
}
|
||||
|
||||
if (type == WorldEditorDataType.DESTRUCTIBLES) {
|
||||
this.doodads.add(new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
|
||||
doodad.getLife()));
|
||||
RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
|
||||
doodad.getLife());
|
||||
if (row.readSLKTagBoolean("walkable")) {
|
||||
float x = doodad.getLocation()[0];
|
||||
float y = doodad.getLocation()[1];
|
||||
BoundingBox boundingBox = model.bounds.getBoundingBox();
|
||||
float minX = boundingBox.min.x + x;
|
||||
float minY = boundingBox.min.y + y;
|
||||
Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(), boundingBox.getHeight());
|
||||
walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, renderDestructableBounds);
|
||||
}
|
||||
else {
|
||||
this.doodads.add(renderDestructable);
|
||||
} else {
|
||||
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
|
||||
}
|
||||
}
|
||||
@ -669,14 +672,12 @@ public class War3MapViewer extends ModelViewer {
|
||||
pathingTextureImage = TgaFile.readTGA(pathingTexture,
|
||||
this.mapMpq.getResourceAsStream(pathingTexture));
|
||||
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), pathingTextureImage);
|
||||
}
|
||||
catch (final Exception exc) {
|
||||
} catch (final Exception exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pathingTextureImage = null;
|
||||
}
|
||||
if (pathingTextureImage != null) {
|
||||
@ -734,8 +735,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
// path = "Objects\\StartLocation\\StartLocation.mdx";
|
||||
type = null; /// ??????
|
||||
this.startLocations[unit.getPlayer()] = new Vector2(unit.getLocation()[0], unit.getLocation()[1]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
row = modifications.getUnits().get(unit.getId());
|
||||
if (row == null) {
|
||||
row = modifications.getItems().get(unit.getId());
|
||||
@ -768,8 +768,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
path += ".mdx";
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
type = WorldEditorDataType.UNITS;
|
||||
path = row.getFieldAsString(UNIT_FILE, 0);
|
||||
|
||||
@ -831,8 +830,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
if (pathingTexture.toLowerCase().endsWith(".tga")) {
|
||||
buildingPathingPixelMap = TgaFile.readTGA(pathingTexture,
|
||||
this.mapMpq.getResourceAsStream(pathingTexture));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
try (InputStream stream = this.mapMpq.getResourceAsStream(pathingTexture)) {
|
||||
buildingPathingPixelMap = ImageIO.read(stream);
|
||||
System.out.println("LOADING BLP PATHING: " + pathingTexture);
|
||||
@ -861,8 +859,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx";
|
||||
if (this.dataSource.has(portraitPath)) {
|
||||
portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
portraitModel = model;
|
||||
}
|
||||
if (type == WorldEditorDataType.UNITS) {
|
||||
@ -882,8 +879,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.items.add(new RenderItem(this, model, row, unit, soundset, portraitModel)); // TODO store
|
||||
// somewhere
|
||||
if (unitShadowSplat != null) {
|
||||
@ -895,8 +891,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Unknown unit ID: " + unit.getId());
|
||||
}
|
||||
}
|
||||
@ -1081,6 +1076,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
gdxRayHeap.direction.nor();// needed for libgdx
|
||||
RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
|
||||
this.terrain.softwareGroundMesh.indices, 3, out);
|
||||
rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y), Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y));
|
||||
walkableObjectsTree.intersect(rectangleHeap, walkablesIntersectionFinder.reset(gdxRayHeap));
|
||||
if (walkablesIntersectionFinder.found) {
|
||||
out.set(walkablesIntersectionFinder.intersection);
|
||||
} else {
|
||||
out.z = Math.max(getWalkableRenderHeight(out.x, out.y), terrain.getGroundHeight(out.x, out.y));
|
||||
}
|
||||
}
|
||||
|
||||
public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) {
|
||||
@ -1106,7 +1108,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.isVisible(this.worldScene.camera)
|
||||
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
|
||||
unit.getSimulationUnit().getUnitType().isBuilding())
|
||||
unit.getSimulationUnit().getUnitType().isBuilding(), false)
|
||||
&& !unit.getSimulationUnit().isDead()) {
|
||||
entity = unit;
|
||||
}
|
||||
@ -1118,16 +1120,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
final int idx = sel.indexOf(entity);
|
||||
if (idx >= 0) {
|
||||
sel.remove(idx);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sel.add(entity);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sel = Arrays.asList(entity);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sel = Collections.emptyList();
|
||||
}
|
||||
this.doSelectUnit(sel);
|
||||
@ -1145,7 +1144,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
for (final RenderUnit unit : this.units) {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
|
||||
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding())) {
|
||||
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
|
||||
entity = unit;
|
||||
}
|
||||
}
|
||||
@ -1162,11 +1161,9 @@ public class War3MapViewer extends ModelViewer {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
catch (final UnsupportedEncodingException e) {
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new MappedData(stringBuilder.toString());
|
||||
@ -1186,11 +1183,9 @@ public class War3MapViewer extends ModelViewer {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
catch (final UnsupportedEncodingException e) {
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
@ -1252,16 +1247,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
|
||||
false);
|
||||
ordered = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Target not valid.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Ability not ok to use.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Ability not move.");
|
||||
}
|
||||
}
|
||||
@ -1285,16 +1277,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, targetWidget,
|
||||
false);
|
||||
ordered = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Target not valid.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Ability not ok to use.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.err.println("Ability not move.");
|
||||
}
|
||||
}
|
||||
@ -1354,8 +1343,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
public SceneLightManager createLightManager(final boolean simple) {
|
||||
if (simple) {
|
||||
return new W3xScenePortraitLightManager(this, this.lightDirection);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return new W3xSceneWorldLightManager(this);
|
||||
}
|
||||
}
|
||||
@ -1376,4 +1364,80 @@ public class War3MapViewer extends ModelViewer {
|
||||
public AbilityDataUI getAbilityDataUI() {
|
||||
return this.abilityDataUI;
|
||||
}
|
||||
|
||||
public float getWalkableRenderHeight(float x, float y) {
|
||||
walkableObjectsTree.intersect(x, y, walkablesIntersector.reset(x, y));
|
||||
return walkablesIntersector.z;
|
||||
}
|
||||
|
||||
public MdxComplexInstance getHighestWalkableUnder(float x, float y) {
|
||||
walkableObjectsTree.intersect(x, y, intersectorFindsHighestWalkable.reset(x, y));
|
||||
return intersectorFindsHighestWalkable.highestInstance;
|
||||
}
|
||||
|
||||
private static final class QuadtreeIntersectorFindsWalkableRenderHeight implements QuadtreeIntersector<MdxComplexInstance> {
|
||||
private float z;
|
||||
private Ray ray = new Ray();
|
||||
private Vector3 intersection = new Vector3();
|
||||
|
||||
private QuadtreeIntersectorFindsWalkableRenderHeight reset(float x, float y) {
|
||||
z = -Float.MAX_VALUE;
|
||||
ray.set(x, y, 4096, 0, 0, -8192);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onIntersect(MdxComplexInstance intersectingObject) {
|
||||
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
|
||||
z = Math.max(z, intersection.z);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class QuadtreeIntersectorFindsHighestWalkable implements QuadtreeIntersector<MdxComplexInstance> {
|
||||
private float z;
|
||||
private Ray ray = new Ray();
|
||||
private Vector3 intersection = new Vector3();
|
||||
private MdxComplexInstance highestInstance;
|
||||
|
||||
private QuadtreeIntersectorFindsHighestWalkable reset(float x, float y) {
|
||||
z = -Float.MAX_VALUE;
|
||||
ray.set(x, y, 4096, 0, 0, -8192);
|
||||
highestInstance = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onIntersect(MdxComplexInstance intersectingObject) {
|
||||
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
|
||||
if(intersection.z > z) {
|
||||
z = intersection.z;
|
||||
highestInstance = intersectingObject;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector<MdxComplexInstance> {
|
||||
private Ray ray;
|
||||
private Vector3 intersection = new Vector3();
|
||||
private boolean found;
|
||||
|
||||
private QuadtreeIntersectorFindsHitPoint reset(Ray ray) {
|
||||
this.ray = ray;
|
||||
this.found = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onIntersect(MdxComplexInstance intersectingObject) {
|
||||
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
|
||||
this.found = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,6 +411,7 @@ public class Terrain {
|
||||
this.shaderMapBoundsRectangle = new Rectangle(this.shaderMapBounds[0], this.shaderMapBounds[1],
|
||||
this.shaderMapBounds[2] - this.shaderMapBounds[0], this.shaderMapBounds[3] - this.shaderMapBounds[1]);
|
||||
this.mapSize = w3eFile.getMapSize();
|
||||
this.entireMapRectangle = new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128);
|
||||
this.softwareGroundMesh = new SoftwareGroundMesh(this.groundHeights, this.groundCornerHeights,
|
||||
this.centerOffset, width, height);
|
||||
|
||||
@ -1019,15 +1020,14 @@ public class Terrain {
|
||||
gl.glUniform1i(6, (int) this.waterIndex);
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetX"), this.centerOffset[0]);
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
|
||||
gl.glUniform4fv(9, 1, this.shaderMapBounds, 0);
|
||||
gl.glUniform4fv(11, 1, this.shaderMapBounds, 0);
|
||||
|
||||
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
|
||||
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
final DataTexture terrainLightsTexture = lightManager.getTerrainLightsTexture();
|
||||
|
||||
unitLightsTexture.bind(21);
|
||||
gl.glUniform1i(this.waterShader.getUniformLocation("lightTexture"), 21);
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount());
|
||||
gl.glUniform1f(this.waterShader.getUniformLocation("lightCountHeight"), unitLightsTexture.getHeight());
|
||||
terrainLightsTexture.bind(3);
|
||||
gl.glUniform1f(9, lightManager.getTerrainLightCount());
|
||||
gl.glUniform1f(10, terrainLightsTexture.getHeight());
|
||||
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE0);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight);
|
||||
@ -1035,7 +1035,7 @@ public class Terrain {
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeight);
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE2);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterExists);
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE3);
|
||||
gl.glActiveTexture(GL30.GL_TEXTURE4);
|
||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
|
||||
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, Shapes.INSTANCE.vertexBuffer);
|
||||
@ -1239,6 +1239,7 @@ public class Terrain {
|
||||
private final WaveBuilder waveBuilder;
|
||||
public PathingGrid pathingGrid;
|
||||
private final Rectangle shaderMapBoundsRectangle;
|
||||
private final Rectangle entireMapRectangle;
|
||||
|
||||
private static final class UnloadedTexture {
|
||||
private final int width;
|
||||
@ -1399,4 +1400,8 @@ public class Terrain {
|
||||
public Rectangle getPlayableMapArea() {
|
||||
return this.shaderMapBoundsRectangle;
|
||||
}
|
||||
|
||||
public Rectangle getEntireMap() {
|
||||
return entireMapRectangle;
|
||||
}
|
||||
}
|
||||
|
@ -428,9 +428,9 @@ public class TerrainShaders {
|
||||
"layout (location = 3) uniform vec4 deep_color_min;\r\n" + //
|
||||
"layout (location = 4) uniform vec4 deep_color_max;\r\n" + //
|
||||
"layout (location = 5) uniform float water_offset;\r\n" + //
|
||||
"layout (location = 10) uniform sampler2D lightTexture;\r\n" + //
|
||||
"layout (location = 11) uniform float lightCount;\r\n" + //
|
||||
"layout (location = 12) uniform float lightTextureHeight;\r\n" + //
|
||||
"layout (binding = 3) uniform sampler2D lightTexture;\r\n" + //
|
||||
"layout (location = 9) uniform float lightCount;\r\n" + //
|
||||
"layout (location = 10) uniform float lightTextureHeight;\r\n" + //
|
||||
"\r\n" + //
|
||||
"out vec2 UV;\r\n" + //
|
||||
"out vec4 Color;\r\n" + //
|
||||
@ -477,12 +477,12 @@ public class TerrainShaders {
|
||||
|
||||
public static final String frag = "#version 450 core\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (binding = 3) uniform sampler2DArray water_textures;\r\n" + //
|
||||
"layout (binding = 4) uniform sampler2DArray water_textures;\r\n" + //
|
||||
"layout (binding = 2) uniform sampler2D water_exists_texture;\r\n" + //
|
||||
"\r\n" + //
|
||||
"\r\n" + //
|
||||
"layout (location = 6) uniform int current_texture;\r\n" + //
|
||||
"layout (location = 9) uniform vec4 mapBounds;\r\n" + //
|
||||
"layout (location = 11) uniform vec4 mapBounds;\r\n" + //
|
||||
"\r\n" + //
|
||||
"in vec2 UV;\r\n" + //
|
||||
"in vec4 Color;\r\n" + //
|
||||
|
@ -89,7 +89,8 @@ public class RenderUnit {
|
||||
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl(instance);
|
||||
simulationUnit.setUnitAnimationListener(this.unitAnimationListenerImpl);
|
||||
final String requiredAnimationNames = row.getFieldAsString(ANIM_PROPS, 0);
|
||||
TokenLoop: for (final String animationName : requiredAnimationNames.split(",")) {
|
||||
TokenLoop:
|
||||
for (final String animationName : requiredAnimationNames.split(",")) {
|
||||
final String upperCaseToken = animationName.toUpperCase();
|
||||
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
|
||||
if (upperCaseToken.equals(secondaryTag.name())) {
|
||||
@ -152,8 +153,7 @@ public class RenderUnit {
|
||||
// where they actually are
|
||||
this.x += (speedDelta * simDx) / distanceToSimulation;
|
||||
this.y += (speedDelta * simDy) / distanceToSimulation;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.x = simulationX;
|
||||
this.y = simulationY;
|
||||
}
|
||||
@ -166,20 +166,42 @@ public class RenderUnit {
|
||||
final float groundHeight;
|
||||
final MovementType movementType = this.simulationUnit.getUnitType().getMovementType();
|
||||
final short terrainPathing = map.terrain.pathingGrid.getPathing(x, y);
|
||||
final boolean swimming = (movementType == MovementType.AMPHIBIOUS)
|
||||
boolean swimming = (movementType == MovementType.AMPHIBIOUS)
|
||||
&& PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.SWIMMABLE)
|
||||
&& !PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.WALKABLE);
|
||||
if ((swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|
||||
|| (movementType == MovementType.HOVER)) {
|
||||
groundHeight = Math.max(map.terrain.getGroundHeight(x, y), map.terrain.getWaterHeight(x, y));
|
||||
float groundHeightTerrain = map.terrain.getGroundHeight(x, y);
|
||||
float groundHeightTerrainAndWater;
|
||||
MdxComplexInstance currentWalkableUnder;
|
||||
boolean standingOnWater = (swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|
||||
|| (movementType == MovementType.HOVER);
|
||||
if (standingOnWater) {
|
||||
groundHeightTerrainAndWater = Math.max(groundHeightTerrain, map.terrain.getWaterHeight(x, y));
|
||||
} else {
|
||||
// land units will have their feet pass under the surface of the water
|
||||
groundHeightTerrainAndWater = groundHeightTerrain;
|
||||
}
|
||||
if(movementType == MovementType.FLOAT) {
|
||||
// boats cant go on bridges
|
||||
groundHeight = groundHeightTerrainAndWater;
|
||||
currentWalkableUnder = null;
|
||||
} else {
|
||||
currentWalkableUnder = map.getHighestWalkableUnder(x, y);
|
||||
if(currentWalkableUnder != null) {
|
||||
System.out.println("WALKABLE UNDER");
|
||||
}
|
||||
War3MapViewer.gdxRayHeap.set(x, y, 4096, 0, 0, -8192);
|
||||
if (currentWalkableUnder != null && currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, War3MapViewer.intersectionHeap, true, true)
|
||||
&& War3MapViewer.intersectionHeap.z > groundHeightTerrainAndWater) {
|
||||
groundHeight = War3MapViewer.intersectionHeap.z;
|
||||
swimming = false; // Naga Royal Guard should slither across a bridge, not swim in rock
|
||||
} else {
|
||||
groundHeight = groundHeightTerrainAndWater;
|
||||
currentWalkableUnder = null;
|
||||
}
|
||||
else {
|
||||
groundHeight = map.terrain.getGroundHeight(x, y);
|
||||
}
|
||||
if (swimming && !this.swimming) {
|
||||
this.unitAnimationListenerImpl.addSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
|
||||
}
|
||||
else if (!swimming && this.swimming) {
|
||||
} else if (!swimming && this.swimming) {
|
||||
this.unitAnimationListenerImpl.removeSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
|
||||
}
|
||||
this.swimming = swimming;
|
||||
@ -200,8 +222,7 @@ public class RenderUnit {
|
||||
if (boneCorpse && !this.boneCorpse) {
|
||||
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE,
|
||||
this.simulationUnit.getEndingDecayTime(map.simulation), true);
|
||||
}
|
||||
else if (corpse && !this.corpse) {
|
||||
} else if (corpse && !this.corpse) {
|
||||
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,
|
||||
map.simulation.getGameplayConstants().getDecayTime(), true);
|
||||
}
|
||||
@ -237,8 +258,7 @@ public class RenderUnit {
|
||||
- ((this.orientationInterpolation.getEndingAccelCutoff() - absoluteFacingDeltaRadians)
|
||||
/ this.orientationInterpolation.getEndingAccelCutoff()))
|
||||
* (this.orientationInterpolation.getMaxVelocity()) * turningSign;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
acceleration = this.orientationInterpolation.getStartingAcceleration() * turningSign;
|
||||
this.currentTurnVelocity = this.currentTurnVelocity + acceleration;
|
||||
}
|
||||
@ -263,17 +283,43 @@ public class RenderUnit {
|
||||
final float pitchSampleForwardY = y + (sampleRadius * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleBackwardX = x - (sampleRadius * (float) Math.cos(facingRadians));
|
||||
final float pitchSampleBackwardY = y - (sampleRadius * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
|
||||
pitch = Math.max(-maxPitch, Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2)));
|
||||
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
|
||||
final float rollSampleForwardX = x + (sampleRadius * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleForwardY = y + (sampleRadius * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleBackwardX = x - (sampleRadius * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleBackwardY = y - (sampleRadius * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
|
||||
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
|
||||
final float pitchSampleGroundHeight1;
|
||||
final float pitchSampleGroundHeight2;
|
||||
final float rollSampleGroundHeight1;
|
||||
final float rollSampleGroundHeight2;
|
||||
if (currentWalkableUnder != null) {
|
||||
pitchSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
pitchSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleForwardX, pitchSampleForwardY);
|
||||
rollSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleBackwardX, rollSampleBackwardY);
|
||||
rollSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleForwardX, rollSampleForwardY);
|
||||
} else {
|
||||
float pitchGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
float pitchGroundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
|
||||
float rollGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
|
||||
float rollGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
|
||||
if (standingOnWater) {
|
||||
pitchSampleGroundHeight1 = Math.max(pitchGroundHeight1,
|
||||
map.terrain.getWaterHeight(pitchSampleBackwardX, pitchSampleBackwardY));
|
||||
pitchSampleGroundHeight2 = Math.max(pitchGroundHeight2,
|
||||
map.terrain.getWaterHeight(pitchSampleForwardX, pitchSampleForwardY));
|
||||
rollSampleGroundHeight1 = Math.max(rollGroundHeight1,
|
||||
map.terrain.getWaterHeight(rollSampleBackwardX, rollSampleBackwardY));
|
||||
rollSampleGroundHeight2 = Math.max(rollGroundHeight2,
|
||||
map.terrain.getWaterHeight(rollSampleForwardX, rollSampleForwardY));
|
||||
} else {
|
||||
pitchSampleGroundHeight1 = pitchGroundHeight1;
|
||||
pitchSampleGroundHeight2 = pitchGroundHeight2;
|
||||
rollSampleGroundHeight1 = rollGroundHeight1;
|
||||
rollSampleGroundHeight2 = rollGroundHeight2;
|
||||
}
|
||||
}
|
||||
pitch = Math.max(-maxPitch, Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGroundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2)));
|
||||
roll = Math.max(-maxRoll, Math.min(maxRoll,
|
||||
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, sampleRadius * 2)));
|
||||
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
|
||||
@ -289,6 +335,18 @@ public class RenderUnit {
|
||||
this.unitAnimationListenerImpl.update();
|
||||
}
|
||||
|
||||
private float getGroundHeightSample(float groundHeight, MdxComplexInstance currentWalkableUnder, float sampleX, float sampleY) {
|
||||
final float sampleGroundHeight;
|
||||
War3MapViewer.gdxRayHeap.origin.x = sampleX;
|
||||
War3MapViewer.gdxRayHeap.origin.y = sampleY;
|
||||
if (currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, War3MapViewer.intersectionHeap, true, true)) {
|
||||
sampleGroundHeight = War3MapViewer.intersectionHeap.z;
|
||||
} else {
|
||||
sampleGroundHeight = groundHeight;
|
||||
}
|
||||
return sampleGroundHeight;
|
||||
}
|
||||
|
||||
public CUnit getSimulationUnit() {
|
||||
return this.simulationUnit;
|
||||
}
|
||||
@ -374,8 +432,7 @@ public class RenderUnit {
|
||||
// animation is a looping animation
|
||||
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
|
||||
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
final QueuedAnimation nextAnimation = this.animationQueue.poll();
|
||||
if (nextAnimation != null) {
|
||||
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
|
||||
|
@ -178,6 +178,7 @@ public class CUnitData {
|
||||
final List<CUnitAttack> attacks = new ArrayList<>();
|
||||
final int attacksEnabled = unitType.getFieldAsInteger(ATTACKS_ENABLED, 0);
|
||||
if ((attacksEnabled & 0x1) != 0) {
|
||||
try {
|
||||
// attack one
|
||||
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK1_BACKSWING_POINT, 0);
|
||||
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK1_DAMAGE_POINT, 0);
|
||||
@ -218,8 +219,12 @@ public class CUnitData {
|
||||
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
|
||||
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
|
||||
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
|
||||
} catch (Exception exc) {
|
||||
System.err.println("Attack 1 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
|
||||
}
|
||||
}
|
||||
if ((attacksEnabled & 0x2) != 0) {
|
||||
try {
|
||||
// attack two
|
||||
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK2_BACKSWING_POINT, 0);
|
||||
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK2_DAMAGE_POINT, 0);
|
||||
@ -260,6 +265,9 @@ public class CUnitData {
|
||||
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
|
||||
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
|
||||
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
|
||||
} catch (Exception exc) {
|
||||
System.err.println("Attack 2 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
|
||||
}
|
||||
}
|
||||
final int deathType = unitType.getFieldAsInteger(DEATH_TYPE, 0);
|
||||
final boolean raise = (deathType & 0x1) != 0;
|
||||
|
@ -138,7 +138,6 @@ public class CAttackOrder implements COrder {
|
||||
else {
|
||||
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
|
||||
}
|
||||
System.out.println(damage + " from " + minDamage + " to " + maxDamage);
|
||||
this.unitAttack.launch(simulation, this.unit, this.target, damage);
|
||||
this.damagePointLaunchTime = 0;
|
||||
}
|
||||
|
@ -177,7 +177,8 @@ public class CPathfindingProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
while (!openSet.isEmpty()) {
|
||||
int searchIterations = 0;
|
||||
while (!openSet.isEmpty() && searchIterations < 150000) {
|
||||
Node current = openSet.poll();
|
||||
if (isGoal(current)) {
|
||||
final LinkedList<Point2D.Float> totalPath = new LinkedList<>();
|
||||
@ -259,6 +260,7 @@ public class CPathfindingProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
searchIterations++;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import com.etheller.warsmash.viewer5.gl.SoundLengthExtension;
|
||||
import com.etheller.warsmash.viewer5.gl.WireframeExtension;
|
||||
|
||||
public class DesktopLauncher {
|
||||
public static void main(final String[] arg) {
|
||||
public static void main(String[] arg) {
|
||||
Extensions.angleInstancedArrays = new ANGLEInstancedArrays() {
|
||||
@Override
|
||||
public void glVertexAttribDivisorANGLE(final int index, final int divisor) {
|
||||
@ -83,10 +83,11 @@ public class DesktopLauncher {
|
||||
config.useGL30 = true;
|
||||
config.gles30ContextMajorVersion = 3;
|
||||
config.gles30ContextMinorVersion = 3;
|
||||
config.samples = 16;
|
||||
//config.samples = 16;
|
||||
// config.vSyncEnabled = false;
|
||||
// config.foregroundFPS = 0;
|
||||
// config.backgroundFPS = 0;
|
||||
arg = new String[]{"-windowed"};
|
||||
if ((arg.length > 0) && "-windowed".equals(arg[0])) {
|
||||
config.fullscreen = false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user