Update with waves

This commit is contained in:
Retera 2020-05-16 14:36:41 -04:00
parent fabc3f4e61
commit 2ac6a5fa1d
20 changed files with 851 additions and 89 deletions

View File

@ -24,7 +24,6 @@ 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.EventObjectEmitterObject;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
@ -58,8 +57,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
System.err.println("Renderer: " + renderer);
final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127");
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor("E:\\Backups\\Warsmash\\Data");
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
this.codebase = new CompoundDataSourceDescriptor(
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
@ -74,8 +72,8 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
this.cameraManager = new CameraManager();
this.cameraManager.setupCamera(scene);
// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx",
this.mainModel = (MdxModel) this.viewer.load("Units\\Human\\HeroPaladinBoss\\HeroPaladinBoss.mdx",
this.mainModel = (MdxModel) this.viewer.load("Buildings\\Other\\TempArtB\\TempArtB.mdx",
// this.mainModel = (MdxModel) this.viewer.load("Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdx",
new PathSolver() {
@Override
public SolvedPath solve(final String src, final Object solverParams) {
@ -83,12 +81,12 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
}
}, null);
final EventObjectEmitterObject evt = this.mainModel.getEventObjects().get(1);
for (final Sequence seq : this.mainModel.getSequences()) {
System.out.println(seq.getName() + ": " + Arrays.toString(seq.getInterval()));
}
System.out.println(Arrays.toString(evt.keyFrames));
System.out.println(evt.name);
// final EventObjectEmitterObject evt = this.mainModel.getEventObjects().get(1);
// for (final Sequence seq : this.mainModel.getSequences()) {
// System.out.println(seq.getName() + ": " + Arrays.toString(seq.getInterval()));
// }
// System.out.println(Arrays.toString(evt.keyFrames));
// System.out.println(evt.name);
// this.modelCamera = this.mainModel.cameras.get(0);

View File

@ -5,6 +5,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.ImageIO;
@ -13,6 +14,7 @@ import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
@ -23,6 +25,8 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
@ -85,6 +89,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS];
private Texture solidGreenTexture;
private ShapeRenderer shapeRenderer;
private boolean showTalentTree;
private final List<Message> messages = new LinkedList<>();
@Override
public void create() {
@ -107,8 +116,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
// "D:\\NEEDS_ORGANIZING\\MPQBuild\\War3xLocal.mpq\\enus-war3local.mpq");
// final FolderDataSourceDescriptor rebirth = new FolderDataSourceDescriptor(
// "E:\\Games\\Warcraft III Patch 1.31 Rebirth");
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor(
"D:\\NEEDS_ORGANIZING\\MPQBuild\\Test");
final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor("E:\\Backups\\Warsmash\\Data");
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
this.codebase = new CompoundDataSourceDescriptor(
Arrays.<DataSourceDescriptor>asList(war3mpq, /* war3xLocalmpq, */ testingFolder, currentFolder))
@ -118,8 +126,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.viewer.worldScene.enableAudio();
this.viewer.enableAudio();
try {
// "Maps\\Campaign\\NightElf03.w3m"
this.viewer.loadMap("ProjectileTest.w3x");
this.viewer.loadMap("ReforgedGeorgeVacation.w3x");
}
catch (final IOException e) {
throw new RuntimeException(e);
@ -170,7 +177,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.batch = new SpriteBatch();
this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
// this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
if (this.viewer.dataSource.has("war3mapMap.tga")) {
try {
this.minimapTexture = ImageUtils.getTextureNoColorCorrection(TgaFile.readTGA("war3mapMap.tga",
@ -204,12 +211,12 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
Gdx.input.setInputProcessor(this);
//
// final Music music = Gdx.audio
// .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\OrcTheme.mp3"));
// music.setVolume(0.2f);
// music.setLooping(true);
// music.play();
final Music music = Gdx.audio
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3"));
music.setVolume(0.2f);
music.setLooping(true);
music.play();
this.minimap = new Rectangle(35, 7, 305, 272);
final float worldWidth = (this.viewer.terrain.columns - 1);
@ -224,6 +231,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.cameraManager.target.x = this.viewer.startLocations[0].x;
this.cameraManager.target.y = this.viewer.startLocations[0].y;
this.shapeRenderer = new ShapeRenderer();
this.talentTreeWindow = new Rectangle(100, 300, 1400, 800);
}
@Override
@ -265,7 +275,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.glyphLayout.setText(this.font, fpsString);
this.font.draw(this.batch, fpsString, (this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2, 1100);
this.batch.draw(this.consoleUITexture, 0, 0, this.uiViewport.getWorldWidth(), 320);
// this.batch.draw(this.consoleUITexture, 0, 0, this.uiViewport.getWorldWidth(), 320);
this.batch.draw(this.minimapTexture, 35, 7, 305, 272);
if (this.selectedUnit != null) {
@ -281,6 +291,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.font20.draw(this.batch, "Defense:", 600, 98);
this.font20.draw(this.batch, "Speed:", 600, 76);
this.font20.setColor(Color.WHITE);
int messageIndex = 0;
for (final Message message : this.messages) {
this.font20.draw(this.batch, message.text, 100, 400 + (25 * (messageIndex++)));
}
this.font20.setColor(Color.WHITE);
final int dmgMin = this.viewer.simulation.getUnitData()
.getA1MinDamage(this.selectedUnit.getSimulationUnit().getTypeId());
final int dmgMax = this.viewer.simulation.getUnitData()
@ -325,6 +340,33 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
4, 4);
}
this.batch.end();
if (this.showTalentTree) {
this.shapeRenderer.setProjectionMatrix(this.uiCamera.combined);
this.shapeRenderer.setColor(Color.BLACK);
this.shapeRenderer.begin(ShapeType.Filled);
this.shapeRenderer.rect(this.talentTreeWindow.x, this.talentTreeWindow.y, this.talentTreeWindow.width,
this.talentTreeWindow.height);
this.shapeRenderer.end();
this.shapeRenderer.setColor(Color.YELLOW);
this.shapeRenderer.begin(ShapeType.Line);
this.shapeRenderer.rect(100, 300, 1400, 800);
this.shapeRenderer.end();
this.batch.begin();
this.font.setColor(Color.YELLOW);
final String title = "Mage Talent Tree";
this.glyphLayout.setText(this.font, title);
this.font.draw(this.batch, title,
this.talentTreeWindow.x + ((this.talentTreeWindow.width - this.glyphLayout.width) / 2),
(this.talentTreeWindow.y + this.talentTreeWindow.height) - 45);
this.batch.end();
}
}
@Override
@ -466,6 +508,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private final Vector2 cameraVelocity = new Vector2();
private Scene portraitScene;
private Texture minimapTexture;
private Rectangle talentTreeWindow;
@Override
public boolean keyDown(final int keycode) {
@ -509,9 +552,25 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
@Override
public boolean touchDown(final int screenX, final int screenY, final int pointer, final int button) {
System.out.println(screenX + "," + screenY);
clickLocationTemp2.x = screenX;
clickLocationTemp2.y = screenY;
this.uiViewport.unproject(clickLocationTemp2);
if (this.selectedUnit != null) {
for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) {
if (new Rectangle(1225 + (70 * commandCardIcon.getX()), 160 - (70 * commandCardIcon.getY()), 64, 64)
.contains(clickLocationTemp2)) {
if (button == Input.Buttons.RIGHT) {
this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Right mouse click"));
}
else {
this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Left mouse click"));
}
return true;
}
}
}
if (this.minimapFilledArea.contains(clickLocationTemp2.x, clickLocationTemp2.y)) {
final float clickX = (clickLocationTemp2.x - this.minimapFilledArea.x) / this.minimapFilledArea.width;
final float clickY = (clickLocationTemp2.y - this.minimapFilledArea.y) / this.minimapFilledArea.height;
@ -604,4 +663,14 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
}
return true;
}
private static class Message {
private final float time;
private final String text;
public Message(final float time, final String text) {
this.time = time;
this.text = text;
}
}
}

View File

@ -9,8 +9,8 @@ import com.google.common.io.LittleEndianDataOutputStream;
public class CollisionShape extends GenericObject {
public static enum Type {
PLANE,
BOX,
PLANE,
SPHERE,
CYLINDER;
@ -77,12 +77,12 @@ public class CollisionShape extends GenericObject {
public void readMdl(final MdlTokenInputStream stream) {
for (final String token : super.readMdlGeneric(stream)) {
switch (token) {
case MdlUtils.TOKEN_PLANE:
this.type = Type.PLANE;
break;
case MdlUtils.TOKEN_BOX:
this.type = Type.BOX;
break;
case MdlUtils.TOKEN_PLANE:
this.type = Type.PLANE;
break;
case MdlUtils.TOKEN_SPHERE:
this.type = Type.SPHERE;
break;
@ -116,12 +116,12 @@ public class CollisionShape extends GenericObject {
String type;
int vertices = 2;
switch (this.type) {
case PLANE:
type = MdlUtils.TOKEN_PLANE;
break;
case BOX:
type = MdlUtils.TOKEN_BOX;
break;
case PLANE:
type = MdlUtils.TOKEN_PLANE;
break;
case SPHERE:
type = MdlUtils.TOKEN_SPHERE;
vertices = 1;
@ -164,4 +164,16 @@ public class CollisionShape extends GenericObject {
return size;
}
public float[][] getVertices() {
return this.vertices;
}
public CollisionShape.Type getType() {
return this.type;
}
public float getBoundsRadius() {
return this.boundsRadius;
}
}

View File

@ -1,7 +1,13 @@
package com.etheller.warsmash.viewer5;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.collision.Ray;
public class Bounds {
public float x, y, z, r;
private BoundingBox boundingBox;
public void fromExtents(final float[] min, final float[] max) {
final float x = min[0];
@ -15,5 +21,14 @@ public class Bounds {
this.y = y + (d / 2f);
this.z = z + (h / 2f);
this.r = (float) (Math.max(Math.max(w, d), h) / 2.);
this.boundingBox = new BoundingBox(new Vector3(min), new Vector3(max));
}
public void intersectRay(final Ray ray, final Vector3 intersection) {
Intersector.intersectRayBounds(ray, this.boundingBox, intersection);
}
public boolean intersectRayFast(final Ray ray) {
return Intersector.intersectRayBoundsFast(ray, this.boundingBox);
}
}

View File

@ -4,4 +4,9 @@ public class Extensions {
public static ANGLEInstancedArrays angleInstancedArrays;
public static DynamicShadowExtension dynamicShadowExtension;
public static WireframeExtension wireframeExtension;
public static int GL_LINE = 0;
public static int GL_FILL = 0;
}

View File

@ -0,0 +1,5 @@
package com.etheller.warsmash.viewer5.gl;
public interface WireframeExtension {
void glPolygonMode(int face, int mode);
}

View File

@ -1,10 +1,90 @@
package com.etheller.warsmash.viewer5.handlers.mdx;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.collision.Ray;
public class CollisionShape extends GenericObject {
private static Vector3 intersectHeap = new Vector3();
private static Vector3 intersectHeap2 = new Vector3();
private static Matrix4 intersectMatrixHeap = new Matrix4();
private static Ray intersectRayHeap = new Ray();
private Intersectable intersectable;
public CollisionShape(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.CollisionShape object,
final int index) {
super(model, object, index);
final float[][] vertices = object.getVertices();
switch (object.getType()) {
case BOX:
this.intersectable = new IntersectableBox(vertices[0], vertices[1]);
break;
case CYLINDER:
this.intersectable = null; // TODO
break;
case PLANE:
this.intersectable = null; // TODO
break;
case SPHERE:
this.intersectable = new IntersectableSphere(vertices[0], object.getBoundsRadius());
break;
}
}
public boolean checkIntersect(final Ray ray, final MdxNode mdxNode, final Vector3 intersection) {
if (this.intersectable != null) {
return this.intersectable.checkIntersect(ray, mdxNode, intersection);
}
return false;
}
private static interface Intersectable {
boolean checkIntersect(final Ray ray, final MdxNode mdxNode, final Vector3 intersection);
}
private static final class IntersectableBox implements Intersectable {
private final BoundingBox boundingBox;
public IntersectableBox(final float[] vertex1, final float[] vertex2) {
this.boundingBox = new BoundingBox(new Vector3(vertex1), new Vector3(vertex2));
}
@Override
public boolean checkIntersect(final Ray ray, final MdxNode mdxNode, final Vector3 intersection) {
intersectMatrixHeap.set(mdxNode.worldMatrix);
Matrix4.inv(intersectMatrixHeap.val);
intersectHeap.set(ray.origin);
intersectHeap2.set(ray.direction);
intersectHeap2.add(ray.origin);
intersectHeap.prj(intersectMatrixHeap);
intersectHeap2.prj(intersectMatrixHeap);
intersectHeap2.sub(intersectHeap);
intersectRayHeap.set(intersectHeap, intersectHeap2);
if (Intersector.intersectRayBounds(intersectRayHeap, this.boundingBox, intersection)) {
intersection.prj(mdxNode.worldMatrix);
return true;
}
return false;
}
}
private static final class IntersectableSphere implements Intersectable {
private final Vector3 center;
private final float radius;
public IntersectableSphere(final float[] center, final float radius) {
this.center = new Vector3(center);
this.radius = radius;
}
@Override
public boolean checkIntersect(final Ray ray, final MdxNode mdxNode, final Vector3 intersection) {
intersectHeap.set(this.center);
intersectHeap.prj(mdxNode.worldMatrix);
return Intersector.intersectRaySphere(ray, intersectHeap, this.radius, intersection);
}
}
}

View File

@ -22,6 +22,31 @@ import com.etheller.warsmash.viewer5.Texture;
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
public class EventObjectEmitterObject extends GenericObject implements EmitterObject {
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
private final String filename;
public LoadGenericSoundCallback(final String filename) {
this.filename = filename;
}
@Override
public Object call(final InputStream data) {
final FileHandle temp = new FileHandle(this.filename) {
@Override
public InputStream read() {
return data;
};
};
if (data != null) {
return Gdx.audio.newSound(temp);
}
else {
System.err.println("Warning: missing sound file: " + this.filename);
return null;
}
}
}
private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() {
@Override
@ -43,18 +68,6 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
return new MappedData(stringBuilder.toString());
}
};
private static final LoadGenericCallback decodedDataCallback = new LoadGenericCallback() {
@Override
public Object call(final InputStream data) {
final FileHandle temp = new FileHandle("sound.wav") {
@Override
public InputStream read() {
return data;
};
};
return Gdx.audio.newSound(temp);
}
};
private int geometryEmitterType = -1;
public final String type;
@ -266,10 +279,11 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
final GenericResource[] resources = new GenericResource[fileNames.length];
for (int i = 0; i < fileNames.length; i++) {
final GenericResource genericResource = viewer.loadGeneric(
pathSolver.solve(((String) animSoundsRow.get("DirectoryBase")) + fileNames[i],
model.solverParams).finalSrc,
FetchDataTypeName.ARRAY_BUFFER, decodedDataCallback);
final String pathString = pathSolver.solve(
((String) animSoundsRow.get("DirectoryBase")) + fileNames[i],
model.solverParams).finalSrc;
final GenericResource genericResource = viewer.loadGeneric(pathString,
FetchDataTypeName.ARRAY_BUFFER, new LoadGenericSoundCallback(pathString));
if (genericResource == null) {
throw new IllegalStateException("Null sound: " + fileNames[i]);
}

View File

@ -69,6 +69,8 @@ public class GeometryEmitterFuncs {
private static final Vector3 startHeap = new Vector3();
private static final Vector3 endHeap = new Vector3();
private static final float[] vectorTemp = new float[3];
private static final Vector3[] vector3Heap = { new Vector3(), new Vector3(), new Vector3(), new Vector3(),
new Vector3(), new Vector3() };
public static void bindParticleEmitter2Buffer(final ParticleEmitter2 emitter, final ClientBuffer buffer) {
final MdxComplexInstance instance = emitter.instance;
@ -99,6 +101,16 @@ public class GeometryEmitterFuncs {
floatView.put(p0Offset + 0, location.x);
floatView.put(p0Offset + 1, location.y);
floatView.put(p0Offset + 2, location.z);
if (emitterObject.xYQuad != 0) {
final Vector3 velocity = object.velocity;
floatView.put(p0Offset + 3, velocity.x);
floatView.put(p0Offset + 4, velocity.y);
floatView.put(p0Offset + 5, velocity.z);
}
else {
floatView.put(p0Offset + 3, 0);
floatView.put(p0Offset + 4, 0);
}
}
else {
final Vector3 velocity = object.velocity;

View File

@ -9,6 +9,7 @@ import java.util.List;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.Ray;
import com.etheller.warsmash.parsers.mdlx.Sequence;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.GenericNode;
@ -681,4 +682,25 @@ public class MdxComplexInstance extends ModelInstance {
protected RenderBatch getBatch(final TextureMapper textureMapper2) {
throw new UnsupportedOperationException("NOT API");
}
public void intersectRayBounds(final Ray ray, final Vector3 intersection) {
this.model.bounds.intersectRay(ray, intersection);
}
/**
* Intersects a world ray with the model's CollisionShapes. Only ever call this
* function on the Gdx thread because it uses static variables to hold state
* while processing.
*
* @param ray
*/
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection) {
for (final CollisionShape collisionShape : ((MdxModel) this.model).collisionShapes) {
final MdxNode mdxNode = this.nodes[collisionShape.index];
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
return true;
}
}
return false;
}
}

View File

@ -363,7 +363,28 @@ public class MdxShaders {
" v_color = color;\r\n" + //
" \r\n" + //
" if (a_tail == HEAD) {\r\n" + //
" gl_Position = u_mvp * vec4(a_p0 + (u_vertices[int(a_position)] * scale), 1.0);\r\n" + //
" vec3 vertices[4];\r\n" + //
" if(a_p1[0] != 0.0 || a_p1[1] != 0.0) {\r\n" + //
" vec3 vx;\r\n" + //
" vx[0] = a_p1[0];\r\n" + //
" vx[1] = a_p1[1];\r\n" + //
" vx[2] = 0.0;\r\n" + //
" vx = normalize(vx);\r\n" + //
" vec3 vy;\r\n" + //
" vy[0] = -vx[1];\r\n" + //
" vy[1] = vx[0];\r\n" + //
" vy[2] = 0.0;\r\n" + //
" vertices[2] = - vx - vy;\r\n" + //
" vertices[1] = vx - vy;\r\n" + //
" vertices[0] = -vertices[2];\r\n" + //
" vertices[3] = -vertices[1];\r\n" + //
" } else {\r\n" + //
" vertices[0] = u_vertices[0];\r\n" + //
" vertices[1] = u_vertices[1];\r\n" + //
" vertices[2] = u_vertices[2];\r\n" + //
" vertices[3] = u_vertices[3];\r\n" + //
" }\r\n" + //
" gl_Position = u_mvp * vec4(a_p0 + (vertices[int(a_position)] * scale), 1.0);\r\n" + //
" } else {\r\n" + //
" // Get the normal to the tail in camera space.\r\n" + //
" // This allows to build a 2D rectangle around the 3D tail.\r\n" + //

View File

@ -0,0 +1,71 @@
package com.etheller.warsmash.viewer5.handlers.w3x;
public class AnimationTokens {
public static enum PrimaryTag {
ATTACK,
BIRTH,
CINEMATIC,
DEATH,
DECAY,
DISSIPATE,
MORPH,
PORTRAIT,
SLEEP,
SPELL,
STAND,
WALK;
}
public static enum SecondaryTag {
ALTERNATE,
ALTERNATEEX,
CHAIN,
CHANNEL,
COMPLETE,
CRITICAL,
DEFEND,
DRAIN,
EATTREE,
FAST,
FILL,
FLAIL,
FLESH,
FIFTH,
FIRE,
FIRST,
FIVE,
FOUR,
FOURTH,
GOLD,
HIT,
LARGE,
LEFT,
LIGHT,
LOOPING,
LUMBER,
MEDIUM,
MODERATE,
OFF,
ONE,
PUKE,
READY,
RIGHT,
SECOND,
SEVERE,
SLAM,
SMALL,
SPIKED,
SPIN,
SWIM,
TALK,
THIRD,
THREE,
THROW,
TWO,
TURN,
VICTORY,
WORK,
WOUNDED,
UPGRADE;
}
}

View File

@ -47,20 +47,29 @@ public class SplatModel {
final float y1 = locs[3];
final float zoffs = locs[4];
final int ix0 = (int) Math.floor((x0 - centerOffset[0]) / 128.0);
final int ix1 = (int) Math.ceil((x1 - centerOffset[0]) / 128.0);
final int iy0 = (int) Math.floor((y0 - centerOffset[1]) / 128.0);
final int iy1 = (int) Math.ceil((y1 - centerOffset[1]) / 128.0);
final float centerOffsetX = centerOffset[0];
final float centerOffsetY = centerOffset[1];
final int ix0 = (int) Math.floor((x0 - centerOffsetX) / 128.0);
final int ix1 = (int) Math.ceil((x1 - centerOffsetX) / 128.0);
final int iy0 = (int) Math.floor((y0 - centerOffsetY) / 128.0);
final int iy1 = (int) Math.ceil((y1 - centerOffsetY) / 128.0);
final float newVerts = ((iy1 - iy0) + 1) * ((ix1 - ix0) + 1);
if (newVerts > MAX_VERTICES) {
final int newVerts = ((iy1 - iy0) + 1) * ((ix1 - ix0) + 1);
final int maxPossibleVerts = ((int) Math.ceil((y1 - y0) / 128.0) + 2)
* ((int) Math.ceil((x1 - x0) / 128.0) + 2);
final int maxPossibleFaces = ((int) Math.ceil((y1 - y0) / 128.0) + 1)
* ((int) Math.ceil((x1 - x0) / 128.0) + 1);
int start = vertices.size();
final SplatMover splatMover = unit == null ? null : new SplatMover(start * 3 * 4, indices.size() * 6 * 2);
final int numVertsToCrate = splatMover == null ? newVerts : maxPossibleVerts;
if (numVertsToCrate > MAX_VERTICES) {
continue;
}
int start = vertices.size();
final SplatMover splatMover = unit == null ? null : new SplatMover(start * 3 * 4);
final int step = (ix1 - ix0) + 1;
if ((start + newVerts) > MAX_VERTICES) {
if ((start + numVertsToCrate) > MAX_VERTICES) {
this.addBatch(gl, vertices, uvs, indices, batchRenderUnits);
vertices.clear();
uvs.clear();
@ -69,27 +78,62 @@ public class SplatModel {
start = 0;
}
final float uvXScale = x1 - x0;
final float uvYScale = y1 - y0;
for (int iy = iy0; iy <= iy1; ++iy) {
final float y = (iy * 128.0f) + centerOffset[1];
final float y = (iy * 128.0f) + centerOffsetY;
for (int ix = ix0; ix <= ix1; ++ix) {
final float x = (ix * 128.0f) + centerOffset[0];
final float x = (ix * 128.0f) + centerOffsetX;
final float[] vertex = new float[] { x, y, zoffs };
vertices.add(vertex);
uvs.add(new float[] { (x - x0) / (x1 - x0), 1.0f - ((y - y0) / (y1 - y0)) });
final float[] uv = new float[] { (x - x0) / uvXScale, 1.0f - ((y - y0) / uvYScale) };
uvs.add(uv);
if (splatMover != null) {
splatMover.vertices.add(vertex);
splatMover.uvs.add(uv);
}
}
}
if (splatMover != null) {
splatMover.uvXScale = uvXScale;
splatMover.uvYScale = uvYScale;
splatMover.locs = locs;
splatMover.ix0 = ix0;
splatMover.iy0 = iy0;
splatMover.ix1 = ix1;
splatMover.iy1 = iy1;
final float y = (iy1 * 128.0f) + centerOffsetY;
final float x = (ix1 * 128.0f) + centerOffsetX;
while (splatMover.vertices.size() < maxPossibleVerts) {
final float[] vertex = new float[] { x, y, zoffs };
vertices.add(vertex);
final float[] uv = new float[] { (x - x0) / uvXScale, 1.0f - ((y - y0) / uvYScale) };
uvs.add(uv);
splatMover.vertices.add(vertex);
splatMover.uvs.add(uv);
}
}
for (int i = 0; i < (iy1 - iy0); ++i) {
for (int j = 0; j < (ix1 - ix0); ++j) {
final int i0 = start + (i * step) + j;
indices.add(new int[] { i0, i0 + 1, i0 + step, i0 + 1, i0 + step + 1, i0 + step });
final int[] indexArray = new int[] { i0, i0 + 1, i0 + step, i0 + 1, i0 + step + 1, i0 + step };
indices.add(indexArray);
if (splatMover != null) {
splatMover.indices.add(indexArray);
}
}
}
if (unit != null) {
unit.accept(splatMover);
batchRenderUnits.add(splatMover);
while (splatMover.indices.size() < maxPossibleFaces) {
final int i0 = start;
final int[] indexArray = new int[] { i0, i0, i0, i0, i0, i0 };
indices.add(indexArray);
splatMover.indices.add(indexArray);
}
}
}
@ -117,6 +161,8 @@ public class SplatModel {
this.batches.add(new Batch(uvsOffset, vertexBuffer, faceBuffer, indices.size() * 6));
for (final SplatMover mover : batchRenderUnits) {
mover.vertexBuffer = vertexBuffer;
mover.uvsOffset = uvsOffset;
mover.faceBuffer = faceBuffer;
}
}
@ -157,24 +203,127 @@ public class SplatModel {
}
public static final class SplatMover {
public int faceBuffer;
public int uvsOffset;
public int iy1;
public int ix1;
public int iy0;
public int ix0;
public float[] locs;
public float uvYScale;
public float uvXScale;
private int vertexBuffer;
private final int startOffset;
private final int start;
private final List<float[]> vertices = new ArrayList<>();
private final List<float[]> uvs = new ArrayList<>();
private final List<int[]> indices = new ArrayList<>();
private final int indicesStartOffset;
private SplatMover(final int i) {
private SplatMover(final int i, final int indicesStartOffset) {
this.startOffset = i;
this.indicesStartOffset = indicesStartOffset;
this.start = i / 12;
}
public void move(final float deltaX, final float deltaY) {
for (final float[] vertex : this.vertices) {
vertex[0] += deltaX;
vertex[1] += deltaY;
}
public void move(final float deltaX, final float deltaY, final float[] centerOffset) {
this.locs[0] += deltaX;
this.locs[2] += deltaX;
this.locs[1] += deltaY;
this.locs[3] += deltaY;
final float x0 = this.locs[0];
final float y0 = this.locs[1];
final float x1 = this.locs[2];
final float y1 = this.locs[3];
final float centerOffsetX = centerOffset[0];
final float centerOffsetY = centerOffset[1];
final int ix0 = (int) Math.floor((x0 - centerOffsetX) / 128.0);
final int ix1 = (int) Math.ceil((x1 - centerOffsetX) / 128.0);
final int iy0 = (int) Math.floor((y0 - centerOffsetY) / 128.0);
final int iy1 = (int) Math.ceil((y1 - centerOffsetY) / 128.0);
final GL30 gl = Gdx.gl30;
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, this.vertexBuffer);
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.startOffset, 4 * 3 * this.vertices.size(),
RenderMathUtils.wrap(this.vertices));
if ((ix0 != this.ix0) || (iy0 != this.iy0) || (ix1 != this.ix1) || (iy1 != this.iy1)) {
// splat geometry has moved, difficult case
final float newVerts = ((iy1 - iy0) + 1) * ((ix1 - ix0) + 1);
if (newVerts <= this.uvs.size()) {
}
int vertexIndex = 0;
float y = 0;
float x = 0;
for (int iy = iy0; iy <= iy1; ++iy) {
y = (iy * 128.0f) + centerOffsetY;
for (int ix = ix0; ix <= ix1; ++ix) {
x = (ix * 128.0f) + centerOffsetX;
final float[] vertexToUpdate = this.vertices.get(vertexIndex);
vertexToUpdate[0] = x;
vertexToUpdate[1] = y;
final float[] uvItem = this.uvs.get(vertexIndex);
uvItem[0] = (x - x0) / this.uvXScale;
uvItem[1] = 1.0f - ((y - y0) / this.uvYScale);
vertexIndex++;
}
}
for (; vertexIndex < this.vertices.size(); vertexIndex++) {
final float[] vertexToUpdate = this.vertices.get(vertexIndex);
vertexToUpdate[0] = x;
vertexToUpdate[1] = y;
final float[] uvItem = this.uvs.get(vertexIndex);
uvItem[0] = (x - x0) / this.uvXScale;
uvItem[1] = 1.0f - ((y - y0) / this.uvYScale);
}
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.startOffset, 4 * 3 * this.vertices.size(),
RenderMathUtils.wrap(this.vertices));
final int step = (ix1 - ix0) + 1;
int faceIndicesIndex = 0;
for (int i = 0; i < (iy1 - iy0); ++i) {
for (int j = 0; j < (ix1 - ix0); ++j) {
final int i0 = this.start + (i * step) + j;
final int[] indexArr = this.indices.get(faceIndicesIndex++);
indexArr[0] = i0;
indexArr[1] = i0 + 1;
indexArr[2] = i0 + step;
indexArr[3] = i0 + 1;
indexArr[4] = i0 + step + 1;
indexArr[5] = i0 + step;
}
}
for (; faceIndicesIndex < this.indices.size(); faceIndicesIndex++) {
final int i0 = this.start;
final int[] indexArr = this.indices.get(faceIndicesIndex);
for (int i = 0; i < indexArr.length; i++) {
indexArr[i] = i0;
}
}
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.faceBuffer);
gl.glBufferSubData(GL30.GL_ELEMENT_ARRAY_BUFFER, this.indicesStartOffset, 6 * 2 * this.indices.size(),
RenderMathUtils.wrapFaces(this.indices));
this.ix0 = ix0;
this.iy0 = iy0;
this.ix1 = ix1;
this.iy1 = iy1;
}
else {
// splat will use same geometry, easy case, just update the UVs
int index = 0;
for (int iy = iy0; iy <= iy1; ++iy) {
final float y = (iy * 128.0f) + centerOffsetY;
for (int ix = ix0; ix <= ix1; ++ix) {
final float x = (ix * 128.0f) + centerOffsetX;
final float[] uvItem = this.uvs.get(index++);
uvItem[0] = (x - x0) / this.uvXScale;
uvItem[1] = 1.0f - ((y - y0) / this.uvYScale);
}
}
}
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.uvsOffset + ((this.startOffset / 3) * 2),
4 * 2 * this.uvs.size(), RenderMathUtils.wrap(this.uvs));
}
}
}

View File

@ -93,6 +93,7 @@ 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();
private static final Vector2 mousePosHeap = new Vector2();
private static final Vector3 normalHeap = new Vector3();
private static final Vector3 intersectionHeap = new Vector3();
@ -370,6 +371,9 @@ public class War3MapViewer extends ModelViewer {
else {
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
}
this.terrain.initShadows();
this.terrain.createWaves();
}
private void loadTerrainCliffsAndWater(final War3MapW3e w3e) {
@ -411,6 +415,13 @@ public class War3MapViewer extends ModelViewer {
fileVar += ".mdx";
if (type == WorldEditorDataType.DESTRUCTIBLES) {
final String shadowString = row.readSLKTag("shadow");
if ((shadowString != null) && (shadowString.length() > 0) && !"_".equals(shadowString)) {
this.terrain.addShadow(shadowString, doodad.getLocation()[0], doodad.getLocation()[1]);
}
}
// First see if the model is local.
// Doodads referring to local models may have invalid variations, so if the
// variation doesn't exist, try without a variation.
@ -750,9 +761,9 @@ public class War3MapViewer extends ModelViewer {
final float[] ray = rayHeap;
mousePosHeap.set(screenX, screenY);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
final Ray gdxRay = new Ray(new Vector3(ray[0], ray[1], ray[2]),
new Vector3(ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]));
Terrain.intersectRayTriangles(gdxRay, this.terrain.softwareGroundMesh.vertices,
gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]);
gdxRayHeap.direction.nor();// needed for libgdx
Terrain.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
this.terrain.softwareGroundMesh.indices, 3, out);
}
@ -766,6 +777,43 @@ public class War3MapViewer extends ModelViewer {
}
public List<RenderUnit> selectUnit(final float x, final float y, final boolean toggle) {
final float[] ray = rayHeap;
mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]);
gdxRayHeap.direction.nor();// needed for libgdx
RenderUnit entity = null;
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)) {
entity = unit;
}
}
List<RenderUnit> sel;
if (entity != null) {
if (toggle) {
sel = new ArrayList<>(this.selected);
final int idx = sel.indexOf(entity);
if (idx >= 0) {
sel.remove(idx);
}
else {
sel.add(entity);
}
}
else {
sel = Arrays.asList(entity);
}
}
else {
sel = Collections.emptyList();
}
this.doSelectUnit(sel);
return sel;
}
public List<RenderUnit> selectUnitOld(final float x, final float y, final boolean toggle) {
final float[] ray = rayHeap;
mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
@ -1020,4 +1068,9 @@ public class War3MapViewer extends ModelViewer {
return ordered;
}
public void standOnRepeat(final MdxComplexInstance instance) {
instance.setSequenceLoopMode(2);
StandSequence.randomStandSequence(instance);
}
}

View File

@ -6,6 +6,7 @@ public class RenderCorner extends Corner {
public boolean cliff;
public boolean romp;
public float rampAdjust;
public float depth;
public RenderCorner(final Corner corner) {
super(corner);

View File

@ -4,6 +4,7 @@ import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -18,6 +19,7 @@ import javax.imageio.ImageIO;
import org.apache.commons.compress.utils.IOUtils;
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.Intersector;
@ -40,6 +42,7 @@ import com.etheller.warsmash.viewer5.Camera;
import com.etheller.warsmash.viewer5.PathSolver;
import com.etheller.warsmash.viewer5.RawOpenGLTextureResource;
import com.etheller.warsmash.viewer5.Texture;
import com.etheller.warsmash.viewer5.gl.Extensions;
import com.etheller.warsmash.viewer5.gl.WebGL;
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
@ -55,6 +58,7 @@ public class Terrain {
private static final Vector3 normalHeap2 = new Vector3();
private static final float[] fourComponentHeap = new float[4];
private static final Matrix4 tempMatrix = new Matrix4();
private static final boolean WIREFRAME_TERRAIN = false;
public ShaderProgram groundShader;
public ShaderProgram waterShader;
@ -114,6 +118,7 @@ public class Terrain {
public final Map<String, List<float[]>> shadows = new HashMap<>();
public final Map<String, Texture> shadowTextures = new HashMap<>();
private final int[] mapBounds;
private final float[] shaderMapBounds;
private final int[] mapSize;
public final SoftwareGroundMesh softwareGroundMesh;
private final int testArrayBuffer;
@ -369,6 +374,10 @@ public class Terrain {
this.centerOffset = w3eFile.getCenterOffset();
this.uberSplatModels = new ArrayList<>();
this.mapBounds = w3iFile.getCameraBoundsComplements();
this.shaderMapBounds = new float[] { (this.mapBounds[0] * 128.0f) + this.centerOffset[0],
(this.mapBounds[2] * 128.0f) + this.centerOffset[1],
((this.columns - this.mapBounds[1] - 1) * 128.0f) + this.centerOffset[0],
((this.rows - this.mapBounds[3] - 1) * 128.0f) + this.centerOffset[1] };
this.mapSize = w3eFile.getMapSize();
this.softwareGroundMesh = new SoftwareGroundMesh(this.groundHeights, this.groundCornerHeights,
this.centerOffset, width, height);
@ -382,6 +391,13 @@ public class Terrain {
// gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.testElementBuffer);
// gl.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, this.softwareGroundMesh.indices.length,
// RenderMathUtils.wrap(this.softwareGroundMesh.indices), GL30.GL_STATIC_DRAW);
this.waveBuilder = new WaveBuilder(this.mapSize, this.waterTable, viewer, this.corners, this.centerOffset,
this.waterHeightOffset, w3eFile, w3iFile);
}
public void createWaves() {
this.waveBuilder.createWaves(this);
}
private void updateGroundHeights(final Rectangle area) {
@ -415,8 +431,12 @@ public class Terrain {
}
}
this.groundCornerHeights[(j * this.columns) + i] = this.corners[i][j].computeFinalGroundHeight()
+ rampHeight;
final RenderCorner corner = this.corners[i][j];
final float newGroundCornerHeight = corner.computeFinalGroundHeight() + rampHeight;
this.groundCornerHeights[(j * this.columns) + i] = newGroundCornerHeight;
corner.depth = (corner.getWater() != 0)
? (this.waterHeightOffset + corner.getWaterHeight()) - newGroundCornerHeight
: 0;
}
}
updateGroundHeights();
@ -430,9 +450,13 @@ public class Terrain {
}
private void updateCornerHeights() {
final FloatBuffer groundCornerHeightsWrapped = RenderMathUtils.wrap(this.groundCornerHeights);
Gdx.gl30.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeight);
Gdx.gl30.glTexSubImage2D(GL30.GL_TEXTURE_2D, 0, 0, 0, this.columns, this.rows, GL30.GL_RED, GL30.GL_FLOAT,
RenderMathUtils.wrap(this.groundCornerHeights));
groundCornerHeightsWrapped);
Gdx.gl30.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeightLinear);
Gdx.gl30.glTexSubImage2D(GL30.GL_TEXTURE_2D, 0, 0, 0, this.columns, this.rows, GL30.GL_RED, GL30.GL_FLOAT,
groundCornerHeightsWrapped);
}
/**
@ -819,15 +843,21 @@ public class Terrain {
// gl.glActiveTexture(GL30.GL_TEXTURE21, /*pathingMap.getTextureDynamic()*/);
gl.glActiveTexture(GL30.GL_TEXTURE20);
gl.glBindTexture(GL30.GL_TEXTURE_2D, dynamicShadowManager.getDepthTexture());
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
// gl.glEnableVertexAttribArray(0);
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, Shapes.INSTANCE.vertexBuffer);
gl.glVertexAttribPointer(0, 2, GL30.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, Shapes.INSTANCE.indexBuffer);
if (WIREFRAME_TERRAIN) {
Extensions.wireframeExtension.glPolygonMode(GL20.GL_FRONT_AND_BACK, Extensions.GL_LINE);
}
gl.glDrawElementsInstanced(GL30.GL_TRIANGLES, Shapes.INSTANCE.quadIndices.length * 3, GL30.GL_UNSIGNED_INT, 0,
(this.columns - 1) * (this.rows - 1));
if (WIREFRAME_TERRAIN) {
Extensions.wireframeExtension.glPolygonMode(GL20.GL_FRONT_AND_BACK, Extensions.GL_FILL);
}
// gl.glDisableVertexAttribArray(0);
@ -910,6 +940,7 @@ 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.glActiveTexture(GL30.GL_TEXTURE0);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight);
@ -960,6 +991,10 @@ public class Terrain {
gl.glUniform1i(1, this.viewer.renderPathing);
gl.glUniform1i(2, this.viewer.renderLighting);
this.cliffShader.setUniformi("shadowMap", 2);
gl.glActiveTexture(GL30.GL_TEXTURE2);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
gl.glActiveTexture(GL30.GL_TEXTURE0);
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.cliffTextureArray);
gl.glActiveTexture(GL30.GL_TEXTURE1);
@ -1052,7 +1087,7 @@ public class Terrain {
}
}
this.shadowMap = gl.glGenBuffer();
this.shadowMap = gl.glGenTexture();
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.shadowMap);
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);
@ -1107,6 +1142,7 @@ public class Terrain {
static Vector3 tmp1 = new Vector3();
static Vector3 tmp2 = new Vector3();
static Vector3 tmp3 = new Vector3();
private final WaveBuilder waveBuilder;
/**
* Intersects the given ray with list of triangles. Returns the nearest
@ -1185,10 +1221,10 @@ public class Terrain {
final int cellY = (int) userCellSpaceY;
if ((cellX >= 0) && (cellX < (this.mapSize[0] - 1)) && (cellY >= 0) && (cellY < (this.mapSize[1] - 1))) {
final float bottomLeft = this.corners[cellX][cellY].computeFinalGroundHeight();
final float bottomRight = this.corners[cellX + 1][cellY].computeFinalGroundHeight();
final float topLeft = this.corners[cellX][cellY + 1].computeFinalGroundHeight();
final float topRight = this.corners[cellX + 1][cellY + 1].computeFinalGroundHeight();
final float bottomLeft = this.groundCornerHeights[(cellY * this.columns) + cellX];
final float bottomRight = this.groundCornerHeights[(cellY * this.columns) + cellX + 1];
final float topLeft = this.groundCornerHeights[((cellY + 1) * this.columns) + cellX];
final float topRight = this.groundCornerHeights[((cellY + 1) * this.columns) + cellX + 1];
final float sqX = userCellSpaceX - cellX;
final float sqY = userCellSpaceY - cellY;
float height;
@ -1269,4 +1305,22 @@ public class Terrain {
}
}
}
public boolean inPlayableArea(float x, float y) {
x = (x - this.centerOffset[0]) / 128.0f;
y = (y - this.centerOffset[1]) / 128.0f;
if (x < this.mapBounds[0]) {
return false;
}
if (x >= (this.mapSize[0] - this.mapBounds[1] - 1)) {
return false;
}
if (y < this.mapBounds[2]) {
return false;
}
if (y >= (this.mapSize[1] - this.mapBounds[3] - 1)) {
return false;
} // TODO why do we use floor if we can use int cast?
return this.corners[(int) Math.floor(x)][(int) Math.floor(y)].getBoundary() == 0;
}
}

View File

@ -18,6 +18,7 @@ public class TerrainShaders {
"layout (location = 0) uniform mat4 MVP;\r\n" + //
"\r\n" + //
"layout (binding = 1) uniform sampler2D height_texture;\r\n" + //
"layout (binding = 3) uniform sampler2D shadowMap;\r\n" + //
"layout (location = 3) uniform float centerOffsetX;\r\n" + //
"layout (location = 4) uniform float centerOffsetY;\r\n" + //
"\r\n" + //
@ -25,11 +26,14 @@ public class TerrainShaders {
"layout (location = 1) out vec3 Normal;\r\n" + //
"layout (location = 2) out vec2 pathing_map_uv;\r\n" + //
"layout (location = 3) out vec3 position;\r\n" + //
"layout (location = 4) out vec2 v_suv;\r\n" + //
"\r\n" + //
"void main() {\r\n" + //
" pathing_map_uv = (vec2(vPosition.x + 128, vPosition.y) / 128 + vOffset.xy) * 4;\r\n" + //
" \r\n" + //
" ivec2 size = textureSize(height_texture, 0);\r\n" + //
" ivec2 shadowSize = textureSize(shadowMap, 0);\r\n" + //
" v_suv = pathing_map_uv / shadowSize;\r\n" + //
" float value = texture(height_texture, (vOffset.xy + vec2(vPosition.x + 192, vPosition.y + 64) / 128.0) / vec2(size)).r;\r\n"
+ //
"\r\n" + //
@ -38,7 +42,7 @@ public class TerrainShaders {
" myposition.x += centerOffsetX;\r\n" + //
" myposition.y += centerOffsetY;\r\n" + //
" position.x /= (size.x * 128.0);\r\n" + //
" position.y /= (size.x * 128.0);\r\n" + //
" position.y /= (size.y * 128.0);\r\n" + //
" gl_Position = MVP * myposition;\r\n" + //
" UV = vec3(vUV, vOffset.a);\r\n" + //
"\r\n" + //
@ -57,6 +61,7 @@ public class TerrainShaders {
"\r\n" + //
"layout (binding = 0) uniform sampler2DArray cliff_textures;\r\n" + //
"layout (binding = 2) uniform usampler2D pathing_map_static;\r\n" + //
"layout (binding = 3) uniform sampler2D shadowMap;\r\n" + //
"\r\n" + //
"layout (location = 1) uniform bool show_pathing_map_static;\r\n" + //
"layout (location = 2) uniform bool show_lighting;\r\n" + //
@ -64,12 +69,15 @@ public class TerrainShaders {
"layout (location = 0) in vec3 UV;\r\n" + //
"layout (location = 1) in vec3 Normal;\r\n" + //
"layout (location = 2) in vec2 pathing_map_uv;\r\n" + //
"layout (location = 4) in vec2 v_suv;\r\n" + //
"\r\n" + //
"out vec4 color;\r\n" + //
"\r\n" + //
"void main() {\r\n" + //
" color = texture(cliff_textures, UV);\r\n" + //
"\r\n" + //
" float shadow = texture2D(shadowMap, v_suv).r;\r\n" + //
" color.rgb *= (1.0 - shadow);\r\n" + //
" if (show_lighting) {\r\n" + //
" vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
" light_direction = normalize(light_direction);\r\n" + //
@ -127,6 +135,7 @@ public class TerrainShaders {
"layout (location = 3) out vec3 normal;\r\n" + //
"layout (location = 4) out vec3 position;\r\n" + //
"layout (location = 5) out vec3 ShadowCoord;\r\n" + //
"layout (location = 6) out vec2 v_suv;\r\n" + //
"\r\n" + //
"void main() { \r\n" + //
" ivec2 size = textureSize(terrain_texture_list, 0);\r\n" + //
@ -153,6 +162,7 @@ public class TerrainShaders {
+ //
" ShadowCoord = (((texture_indices.a & 32768) == 0) ? DepthBiasMVP * vec4(position.xyz, 1) : vec4(2.0, 0.0, 0.0, 1.0)).xyz;\r\n"
+ //
" v_suv = (vPosition + pos) / size;\r\n" + //
" position.x = (position.x - centerOffsetX) / (size.x * 128.0);\r\n" + //
" position.y = (position.y - centerOffsetY) / (size.y * 128.0);\r\n" + //
"}";
@ -190,6 +200,7 @@ public class TerrainShaders {
"layout (location = 3) in vec3 normal;\r\n" + //
"layout (location = 4) in vec3 position;\r\n" + //
"layout (location = 5) in vec3 ShadowCoord;\r\n" + //
"layout (location = 6) in vec2 v_suv;\r\n" + //
"\r\n" + //
"layout (location = 0) out vec4 color;\r\n" + //
// "layout (location = 1) out vec4 position;\r\n" + //
@ -247,11 +258,12 @@ public class TerrainShaders {
+ //
" color = color * color.a + get_fragment(texture_indices.r & 31, vec3(UV, texture_indices.r >> 5)) * (1 - color.a);\r\n"
+ //
" float visibility = 1.0;\r\n" + //
" float shadow = texture2D(shadowMap, v_suv).r;\r\n" + //
// " float visibility = 1.0;\r\n" + //
// " if ( texture2D(shadowMap, ShadowCoord.xy).z > ShadowCoord.z ) {\r\n" + //
// " visibility = 0.5;\r\n" + //
// " }\r\n" + //
" color = vec4(color.xyz * visibility, 1.0);\r\n" + //
" color = vec4(color.xyz * (1.0 - shadow), 1.0);\r\n" + //
"\r\n" + //
// " if (show_lighting) {\r\n" + //
// " vec3 light_direction = vec3(-0.3, -0.3, 0.25);\r\n" + //
@ -406,6 +418,7 @@ public class TerrainShaders {
"\r\n" + //
"out vec2 UV;\r\n" + //
"out vec4 Color;\r\n" + //
"out vec2 position;\r\n" + //
"\r\n" + //
"const float min_depth = 10.f / 128;\r\n" + //
"const float deeplevel = 64.f / 128;\r\n" + //
@ -422,7 +435,9 @@ public class TerrainShaders {
" || texelFetch(water_exists_texture, pos + ivec2(1, 1), 0).r > 0\r\n" + //
" || texelFetch(water_exists_texture, pos + ivec2(0, 1), 0).r > 0;\r\n" + //
"\r\n" + //
" gl_Position = is_water ? MVP * vec4((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY, water_height*128.0, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
" position = vec2((vPosition.x + pos.x)*128.0 + centerOffsetX, (vPosition.y + pos.y)*128.0 + centerOffsetY);\r\n"
+ //
" gl_Position = is_water ? MVP * vec4(position.xy, water_height*128.0, 1) : vec4(2.0, 0.0, 0.0, 1.0);\r\n"
+ //
"\r\n" + //
" UV = vec2((vPosition.x + pos.x%2)/2.0, (vPosition.y + pos.y%2)/2.0);\r\n" + //
@ -445,14 +460,19 @@ public class TerrainShaders {
"\r\n" + //
"\r\n" + //
"layout (location = 6) uniform int current_texture;\r\n" + //
"layout (location = 9) uniform vec4 mapBounds;\r\n" + //
"\r\n" + //
"in vec2 UV;\r\n" + //
"in vec4 Color;\r\n" + //
"in vec2 position;\r\n" + //
"\r\n" + //
"out vec4 outColor;\r\n" + //
"\r\n" + //
"void main() {\r\n" + //
" outColor = texture(water_textures, vec3(UV, current_texture)) * Color;\r\n" + //
" vec2 d2 = min(position - mapBounds.xy, mapBounds.zw - position);\r\n" + //
" float d1 = clamp(min(d2.x, d2.y) / 64.0 + 1.0, 0.0, 1.0) * 0.8 + 0.2;;\r\n" + //
" outColor = texture(water_textures, vec3(UV, current_texture)) * vec4(Color.rgb * d1, Color.a);\r\n"
+ //
"}";
}
}

View File

@ -0,0 +1,152 @@
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
import java.util.HashMap;
import java.util.Map;
import com.badlogic.gdx.math.Quaternion;
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
public class WaveBuilder {
private final int[] mapSize;
private final DataTable waterTable;
private final War3MapViewer viewer;
private final RenderCorner[][] corners;
private final float[] centerOffset;
private final float waterHeightOffset;
private float[] locations;
private final Map<String, MdxModel> models;
private final War3MapW3e w3eFile;
private final War3MapW3i w3iFile;
public WaveBuilder(final int[] mapSize, final DataTable waterTable, final War3MapViewer viewer,
final RenderCorner[][] corners, final float[] centerOffset, final float waterHeightOffset,
final War3MapW3e w3eFile, final War3MapW3i w3iFile) {
this.mapSize = mapSize;
this.waterTable = waterTable;
this.viewer = viewer;
this.corners = corners;
this.centerOffset = centerOffset;
this.waterHeightOffset = waterHeightOffset;
this.w3eFile = w3eFile;
this.w3iFile = w3iFile;
this.models = new HashMap<>();
}
public void createWaves(final Terrain terrain) {
final int columns = this.mapSize[0];
final int rows = this.mapSize[1];
final float wavesDepth = 25f / 128f;
final char tileset = this.w3eFile.getTileset();
final Element waterRow = this.waterTable.get(tileset + "Sha");
final long wavesCliff = (this.w3iFile.getFlags() & 0x0800);
final long wavesRolling = (this.w3iFile.getFlags() & 0x1000);
final String shoreline = waterRow.getField("shoreDir") + "\\" + waterRow.getField("shoreSFile") + "\\"
+ waterRow.getField("shoreSFile") + "0.mdx";
final String outsideCorner = waterRow.getField("shoreDir") + "\\" + waterRow.getField("shoreOCFile") + "\\"
+ waterRow.getField("shoreOCFile") + "0.mdx";
final String insideCorner = waterRow.getField("shoreDir") + "\\" + waterRow.getField("shoreICFile") + "\\"
+ waterRow.getField("shoreICFile") + "0.mdx";
// final String shoreline = "Buildings\\Other\\TempArtB\\TempArtB.mdx";
// final String outsideCorner = "Buildings\\Other\\TempArtB\\TempArtB.mdx";
// final String insideCorner = "Buildings\\Other\\TempArtB\\TempArtB.mdx";
this.locations = new float[3];
for (int y = 0; y < (rows - 1); ++y) {
for (int x = 0; x < (columns - 1); ++x) {
final RenderCorner a = this.corners[x][y];
final RenderCorner b = this.corners[x + 1][y];
final RenderCorner c = this.corners[x + 1][y + 1];
final RenderCorner d = this.corners[x][y + 1];
if ((a.getWater() != 0) || (b.getWater() != 0) || (c.getWater() != 0) || (d.getWater() != 0)) {
final boolean isCliff = (a.getLayerHeight() != b.getLayerHeight())
|| (a.getLayerHeight() != c.getLayerHeight()) || (a.getLayerHeight() != d.getLayerHeight());
if (isCliff && (wavesCliff == 0)) {
continue;
}
if (!isCliff && (wavesRolling == 0)) {
continue;
}
final int ad = (a.depth > wavesDepth) ? 1 : 0;
final int bd = (b.depth > wavesDepth) ? 1 : 0;
final int cd = (c.depth > wavesDepth) ? 1 : 0;
final int dd = (d.depth > wavesDepth) ? 1 : 0;
final int count = ad + bd + cd + dd;
this.locations[0] = (x * 128.0f) + this.centerOffset[0] + 64.0f;
this.locations[1] = (y * 128.0f) + this.centerOffset[1] + 64.0f;
this.locations[2] = ((((a.getWaterHeight() + b.getWaterHeight() + c.getWaterHeight()
+ d.getWaterHeight()) / 4f) + this.waterHeightOffset) * 128.0f) + 1.0f;
if (count == 1) {
addModelInstance(terrain, insideCorner, rotation(ad, bd, cd/* , dd */) - ((3 * Math.PI) / 4));
}
else if (count == 2) {
final double rot = rotation2(ad, bd, cd, dd);
if (!Double.isNaN(rot)) {
addModelInstance(terrain, shoreline, rot);
}
}
else if (count == 3) {
addModelInstance(terrain, outsideCorner,
rotation(1 ^ ad, 1 ^ bd, 1 ^ cd/* , 1 ^ dd */) + ((5 * Math.PI) / 4));
}
}
}
}
}
private void addModelInstance(final Terrain terrain, final String path, final double rotation) {
if (!this.models.containsKey(path)) {
this.models.put(path,
(MdxModel) this.viewer.load(path, this.viewer.wc3PathSolver, this.viewer.solverParams));
}
final MdxModel model = this.models.get(path);
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
instance.setLocation(this.locations);
instance.setLocalRotation(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, (float) rotation));
instance.setScene(this.viewer.worldScene);
if (!terrain.inPlayableArea(this.locations[0], this.locations[1])) {
instance.setVertexColor(new float[] { 51 / 255f, 51 / 255f, 51 / 255f, 1.0f });
}
this.viewer.standOnRepeat(instance);
}
private static double rotation(final int a, final int b, final int c) {
if (a != 0) {
return (-3 * Math.PI) / 4;
}
if (b != 0) {
return -Math.PI / 4;
}
if (c != 0) {
return Math.PI / 4;
}
return (3 * Math.PI) / 4;
}
private static double rotation2(final int a, final int b, final int c, final int d) {
if ((a != 0) && (b != 0)) {
return -Math.PI / 2;
}
if ((b != 0) && (c != 0)) {
return 0;
}
if ((c != 0) && (d != 0)) {
return Math.PI / 2;
}
if ((a != 0) && (d != 0)) {
return Math.PI;
}
return Double.NaN;
}
}

View File

@ -105,7 +105,7 @@ public class RenderUnit {
ability.getOrderId()));
}
else if (ability instanceof CAbilityAttack) {
this.commandCardIcons.add(new CommandCardIcon(-2, -2,
this.commandCardIcons.add(new CommandCardIcon(3, 0,
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNAttack.blp"),
ability.getOrderId()));
}
@ -117,7 +117,7 @@ public class RenderUnit {
ability.getOrderId()));
}
else if (ability instanceof CAbilityPatrol) {
this.commandCardIcons.add(new CommandCardIcon(3, 0,
this.commandCardIcons.add(new CommandCardIcon(0, 1,
ImageUtils.getBLPTexture(map.dataSource, "ReplaceableTextures\\CommandButtons\\BTNPatrol.blp"),
ability.getOrderId()));
}
@ -202,10 +202,10 @@ public class RenderUnit {
}
this.lastOrder = currentOrder;
if (this.shadow != null) {
this.shadow.move(dx, dy);
this.shadow.move(dx, dy, map.terrain.centerOffset);
}
if (this.selectionCircle != null) {
this.selectionCircle.move(dx, dy);
this.selectionCircle.move(dx, dy, map.terrain.centerOffset);
}
}

View File

@ -11,6 +11,7 @@ import com.etheller.warsmash.WarsmashGdxMapGame;
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
import com.etheller.warsmash.viewer5.gl.DynamicShadowExtension;
import com.etheller.warsmash.viewer5.gl.Extensions;
import com.etheller.warsmash.viewer5.gl.WireframeExtension;
public class DesktopLauncher {
public static void main(final String[] arg) {
@ -44,6 +45,14 @@ public class DesktopLauncher {
GL11.glDrawBuffer(mode);
}
};
Extensions.wireframeExtension = new WireframeExtension() {
@Override
public void glPolygonMode(final int face, final int mode) {
GL11.glPolygonMode(face, mode);
}
};
Extensions.GL_LINE = GL11.GL_LINE;
Extensions.GL_FILL = GL11.GL_FILL;
final LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.useGL30 = true;
config.gles30ContextMajorVersion = 3;