mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Initial commit for experimental effort to parse 1.32 format
This commit is contained in:
parent
12ef127d0e
commit
600ec9536b
10
build.gradle
10
build.gradle
@ -1,6 +1,4 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
flatDir {
|
flatDir {
|
||||||
@ -9,13 +7,10 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||||
|
maven { url "https://maven.nikr.net/" }
|
||||||
jcenter()
|
jcenter()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -39,6 +34,7 @@ allprojects {
|
|||||||
google()
|
google()
|
||||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||||
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
|
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
|
||||||
|
maven { url "https://maven.nikr.net/" }
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,6 +50,7 @@ project(":desktop") {
|
|||||||
compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
|
||||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||||
compile "com.google.guava:guava:23.5-jre"
|
compile "com.google.guava:guava:23.5-jre"
|
||||||
|
compile "net.nikr:dds:1.0.0"
|
||||||
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
||||||
compile files(fileTree(dir:'../jars', includes: ['*.jar']))
|
compile files(fileTree(dir:'../jars', includes: ['*.jar']))
|
||||||
|
|
||||||
@ -71,6 +68,7 @@ project(":core") {
|
|||||||
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
|
||||||
compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
||||||
compile "com.google.guava:guava:23.5-jre"
|
compile "com.google.guava:guava:23.5-jre"
|
||||||
|
compile "net.nikr:dds:1.0.0"
|
||||||
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
||||||
compile files(fileTree(dir:'../jars', includes: ['*.jar']))
|
compile files(fileTree(dir:'../jars', includes: ['*.jar']))
|
||||||
|
|
||||||
|
41
core/assets/warsmashRF.ini
Normal file
41
core/assets/warsmashRF.ini
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
[DataSources]
|
||||||
|
Count=5
|
||||||
|
Type00=CASC
|
||||||
|
Path00="C:\Program Files\Warcraft III"
|
||||||
|
Prefixes00=war3.w3mod,war3.w3mod\_deprecated.w3mod,war3.w3mod\_locales\enus.w3mod
|
||||||
|
Type01=Folder
|
||||||
|
Path01="..\..\resources"
|
||||||
|
Type02=Folder
|
||||||
|
Path02="D:\Backups\Warsmash\Data"
|
||||||
|
Type03=Folder
|
||||||
|
Path03="D:\Games\Warcraft III Patch 1.22\Maps"
|
||||||
|
Type04=Folder
|
||||||
|
Path04="."
|
||||||
|
|
||||||
|
[Map]
|
||||||
|
//FilePath="CombatUnitTests.w3x"
|
||||||
|
//FilePath="PitchRoll.w3x"
|
||||||
|
FilePath="PeonStartingBase.w3x"
|
||||||
|
//FilePath="MyStromguarde.w3m"
|
||||||
|
//FilePath="ColdArrows.w3m"
|
||||||
|
//FilePath="DungeonGoldMine.w3m"
|
||||||
|
//FilePath="PlayerPeasants.w3m"
|
||||||
|
//FilePath="FireLord.w3x"
|
||||||
|
//FilePath="Maps\Campaign\NightElf03.w3m"
|
||||||
|
//FilePath="PhoenixAttack.w3x"
|
||||||
|
//FilePath="LightEnvironmentTest.w3x"
|
||||||
|
//FilePath="TorchLight2.w3x"
|
||||||
|
//FilePath="OrcAssault.w3x"
|
||||||
|
//FilePath="FrostyVsFarm.w3m"
|
||||||
|
//FilePath="ModelTest.w3x"
|
||||||
|
//FilePath="SpinningSample.w3x"
|
||||||
|
//FilePath="Maps\Campaign\Prologue02.w3m"
|
||||||
|
//FilePath="Pathing.w3x"
|
||||||
|
//FilePath="ItemFacing.w3x"
|
||||||
|
//FilePath=SomeParticleTests.w3x
|
||||||
|
//FilePath="PeonMiningMultiHall.w3x"
|
||||||
|
//FilePath="QuadtreeBugs.w3x"
|
||||||
|
//FilePath="test2.w3x"
|
||||||
|
//FilePath="FarseerHoldPositionTest.w3x"
|
||||||
|
//FilePath="Ramps.w3m"
|
||||||
|
//FilePath="V1\Farm.w3x"
|
@ -14,7 +14,6 @@ import com.badlogic.gdx.math.Quaternion;
|
|||||||
import com.badlogic.gdx.math.Rectangle;
|
import com.badlogic.gdx.math.Rectangle;
|
||||||
import com.badlogic.gdx.math.Vector3;
|
import com.badlogic.gdx.math.Vector3;
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.units.DataTable;
|
import com.etheller.warsmash.units.DataTable;
|
||||||
import com.etheller.warsmash.util.DataSourceFileHandle;
|
import com.etheller.warsmash.util.DataSourceFileHandle;
|
||||||
import com.etheller.warsmash.viewer5.Camera;
|
import com.etheller.warsmash.viewer5.Camera;
|
||||||
@ -27,6 +26,7 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
|||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||||
|
|
||||||
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
||||||
|
@ -6,6 +6,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.badlogic.gdx.ApplicationAdapter;
|
import com.badlogic.gdx.ApplicationAdapter;
|
||||||
@ -26,6 +27,7 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
|||||||
import com.badlogic.gdx.math.Matrix4;
|
import com.badlogic.gdx.math.Matrix4;
|
||||||
import com.badlogic.gdx.math.Rectangle;
|
import com.badlogic.gdx.math.Rectangle;
|
||||||
import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
||||||
|
import com.etheller.warsmash.datasources.CascDataSourceDescriptor;
|
||||||
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||||
@ -56,7 +58,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.SettableCommandErro
|
|||||||
|
|
||||||
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
|
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
|
||||||
private static final boolean ENABLE_AUDIO = true;
|
private static final boolean ENABLE_AUDIO = true;
|
||||||
private static final boolean ENABLE_MUSIC = false;
|
private static final boolean ENABLE_MUSIC = true;
|
||||||
private DataSource codebase;
|
private DataSource codebase;
|
||||||
private War3MapViewer viewer;
|
private War3MapViewer viewer;
|
||||||
private final Rectangle tempRect = new Rectangle();
|
private final Rectangle tempRect = new Rectangle();
|
||||||
@ -186,7 +188,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
|||||||
|
|
||||||
// this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
|
// this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
|
||||||
|
|
||||||
this.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource,
|
this.solidGreenTexture = ImageUtils.getAnyExtensionTexture(this.viewer.dataSource,
|
||||||
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
||||||
|
|
||||||
Gdx.input.setInputProcessor(this);
|
Gdx.input.setInputProcessor(this);
|
||||||
@ -260,6 +262,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
|||||||
dataSourcesList.add(new MpqDataSourceDescriptor(path));
|
dataSourcesList.add(new MpqDataSourceDescriptor(path));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "CASC": {
|
||||||
|
final String prefixes = dataSourcesConfig.getField("Prefixes" + (i < 10 ? "0" : "") + i);
|
||||||
|
dataSourcesList.add(new CascDataSourceDescriptor(path, Arrays.asList(prefixes.split(","))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unknown data source type: " + type);
|
throw new RuntimeException("Unknown data source type: " + type);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
|||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.parsers.fdf.GameUI;
|
import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||||
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
|
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.units.DataTable;
|
import com.etheller.warsmash.units.DataTable;
|
||||||
import com.etheller.warsmash.util.DataSourceFileHandle;
|
import com.etheller.warsmash.util.DataSourceFileHandle;
|
||||||
import com.etheller.warsmash.util.ImageUtils;
|
import com.etheller.warsmash.util.ImageUtils;
|
||||||
@ -46,6 +45,7 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
|||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.ui.MenuUI;
|
import com.etheller.warsmash.viewer5.handlers.w3x.ui.MenuUI;
|
||||||
@ -141,7 +141,7 @@ public class WarsmashGdxMenuTestGame extends ApplicationAdapter implements Canva
|
|||||||
|
|
||||||
// this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
|
// this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png"));
|
||||||
|
|
||||||
this.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource,
|
this.solidGreenTexture = ImageUtils.getAnyExtensionTexture(this.viewer.dataSource,
|
||||||
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
||||||
|
|
||||||
Gdx.input.setInputProcessor(this);
|
Gdx.input.setInputProcessor(this);
|
||||||
|
@ -14,7 +14,6 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
|||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
import com.badlogic.gdx.math.Rectangle;
|
import com.badlogic.gdx.math.Rectangle;
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
|
||||||
import com.etheller.warsmash.units.DataTable;
|
import com.etheller.warsmash.units.DataTable;
|
||||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||||
@ -26,6 +25,8 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
|||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.camera.PortraitCameraManager;
|
import com.etheller.warsmash.viewer5.handlers.w3x.camera.PortraitCameraManager;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.util.MdxUtils;
|
||||||
|
|
||||||
public class WarsmashPreviewApplication extends ApplicationAdapter implements CanvasProvider {
|
public class WarsmashPreviewApplication extends ApplicationAdapter implements CanvasProvider {
|
||||||
private DataSource codebase;
|
private DataSource codebase;
|
||||||
@ -138,7 +139,7 @@ public class WarsmashPreviewApplication extends ApplicationAdapter implements Ca
|
|||||||
this.mdxHandler, ".mdx", PathSolver.DEFAULT, filename));
|
this.mdxHandler, ".mdx", PathSolver.DEFAULT, filename));
|
||||||
final MdlxModel mdlxModel;
|
final MdlxModel mdlxModel;
|
||||||
try (FileInputStream stream = new FileInputStream(filename)) {
|
try (FileInputStream stream = new FileInputStream(filename)) {
|
||||||
mdlxModel = new MdlxModel(stream);
|
mdlxModel = MdxUtils.loadMdlx(stream);
|
||||||
mdx.load(mdlxModel);
|
mdx.load(mdlxModel);
|
||||||
mdx.ok = true;
|
mdx.ok = true;
|
||||||
// mdx.lateLoad();
|
// mdx.lateLoad();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.etheller.warsmash;
|
package com.etheller.warsmash;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
@ -17,8 +16,8 @@ import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
|||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
|
||||||
public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
||||||
private int arrayBuffer;
|
private int arrayBuffer;
|
||||||
@ -36,8 +35,8 @@ public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
|||||||
.createDataSource();
|
.createDataSource();
|
||||||
|
|
||||||
final MdlxModel model;
|
final MdlxModel model;
|
||||||
try (InputStream modelStream = this.codebase.getResourceAsStream("Buildings\\Other\\TempArtB\\TempArtB.mdx")) {
|
try {
|
||||||
model = new MdlxModel(modelStream);
|
model = new MdlxModel(this.codebase.read("Buildings\\Other\\TempArtB\\TempArtB.mdx"));
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -67,7 +66,7 @@ public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
|||||||
|
|
||||||
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
Gdx.gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, this.elementBuffer);
|
||||||
|
|
||||||
final Geoset geoset0 = model.getGeosets().get(0);
|
final MdlxGeoset geoset0 = model.getGeosets().get(0);
|
||||||
final float[] vertices = geoset0.getVertices();
|
final float[] vertices = geoset0.getVertices();
|
||||||
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
final ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package com.etheller.warsmash;
|
package com.etheller.warsmash;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.ApplicationAdapter;
|
import com.badlogic.gdx.ApplicationAdapter;
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
@ -32,13 +29,7 @@ public class WarsmashTestMyTextureGame extends ApplicationAdapter {
|
|||||||
this.codebase = new CompoundDataSourceDescriptor(
|
this.codebase = new CompoundDataSourceDescriptor(
|
||||||
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
||||||
|
|
||||||
try {
|
this.texture = ImageUtils.getAnyExtensionTexture(this.codebase, "Textures\\Dust3x.blp");
|
||||||
this.texture = ImageUtils
|
|
||||||
.getTexture(ImageIO.read(this.codebase.getResourceAsStream("Textures\\Dust3x.blp")));
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||||
this.batch = new SpriteBatch();
|
this.batch = new SpriteBatch();
|
||||||
this.batch.enableBlending();
|
this.batch.enableBlending();
|
||||||
|
225
core/src/com/etheller/warsmash/datasources/CascDataSource.java
Normal file
225
core/src/com/etheller/warsmash/datasources/CascDataSource.java
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
package com.etheller.warsmash.datasources;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.hiveworkshop.blizzard.casc.io.WarcraftIIICASC;
|
||||||
|
import com.hiveworkshop.blizzard.casc.io.WarcraftIIICASC.FileSystem;
|
||||||
|
import com.hiveworkshop.json.JSONArray;
|
||||||
|
import com.hiveworkshop.json.JSONObject;
|
||||||
|
import com.hiveworkshop.json.JSONTokener;
|
||||||
|
|
||||||
|
public class CascDataSource implements DataSource {
|
||||||
|
private final String[] prefixes;
|
||||||
|
private WarcraftIIICASC warcraftIIICASC;
|
||||||
|
private FileSystem rootFileSystem;
|
||||||
|
private List<String> listFile;
|
||||||
|
private Map<String, String> fileAliases;
|
||||||
|
|
||||||
|
public CascDataSource(final String warcraft3InstallPath, final String[] prefixes) {
|
||||||
|
this.prefixes = prefixes;
|
||||||
|
for (int i = 0; i < (prefixes.length / 2); i++) {
|
||||||
|
final String temp = prefixes[i];
|
||||||
|
prefixes[i] = prefixes[prefixes.length - i - 1];
|
||||||
|
prefixes[prefixes.length - i - 1] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.warcraftIIICASC = new WarcraftIIICASC(Paths.get(warcraft3InstallPath), true);
|
||||||
|
this.rootFileSystem = this.warcraftIIICASC.getRootFileSystem();
|
||||||
|
this.listFile = this.rootFileSystem.enumerateFiles();
|
||||||
|
this.fileAliases = new HashMap<>();
|
||||||
|
if (this.has("filealiases.json")) {
|
||||||
|
try (InputStream stream = this.getResourceAsStream("filealiases.json")) {
|
||||||
|
stream.mark(4);
|
||||||
|
if ('\ufeff' != stream.read()) {
|
||||||
|
stream.reset(); // not the BOM marker
|
||||||
|
}
|
||||||
|
final JSONArray jsonObject = new JSONArray(new JSONTokener(stream));
|
||||||
|
for (int i = 0; i < jsonObject.length(); i++) {
|
||||||
|
final JSONObject alias = jsonObject.getJSONObject(i);
|
||||||
|
final String src = alias.getString("src");
|
||||||
|
final String dest = alias.getString("dest");
|
||||||
|
this.fileAliases.put(src.toLowerCase(Locale.US).replace('/', '\\'),
|
||||||
|
dest.toLowerCase(Locale.US).replace('/', '\\'));
|
||||||
|
if ((src.toLowerCase(Locale.US).contains(".blp")
|
||||||
|
|| dest.toLowerCase(Locale.US).contains(".blp"))
|
||||||
|
&& (!alias.has("assetType") || "Texture".equals(alias.getString("assetType")))) {
|
||||||
|
// This case: I saw a texture that resolves in game but was failing in our code
|
||||||
|
// here, because of this entry:
|
||||||
|
// {"src":"Units/Human/WarWagon/SiegeEngine.blp",
|
||||||
|
// "dest":"Textures/Steamtank.blp", "assetType": "Texture"},
|
||||||
|
// Our repo here checks BLP then DDS at a high-up application level thing, and
|
||||||
|
// the problem is that this entry is written using .BLP but we must be able to
|
||||||
|
// resolve .DDS when we go to look it up. The actual model is .BLP so maybe
|
||||||
|
// that's how the game does it, but my alias mapping is happening after the
|
||||||
|
// .BLP->.DDS dynamic fix, and not before.
|
||||||
|
this.fileAliases.put(src.toLowerCase(Locale.US).replace('/', '\\').replace(".blp", ".dds"),
|
||||||
|
dest.toLowerCase(Locale.US).replace('/', '\\').replace(".blp", ".dds"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getResourceAsStream(String filepath) {
|
||||||
|
filepath = filepath.toLowerCase(Locale.US).replace('/', '\\').replace(':', '\\');
|
||||||
|
final String resolvedAlias = this.fileAliases.get(filepath);
|
||||||
|
if (resolvedAlias != null) {
|
||||||
|
filepath = resolvedAlias;
|
||||||
|
}
|
||||||
|
for (final String prefix : this.prefixes) {
|
||||||
|
final String tempFilepath = prefix + "\\" + filepath;
|
||||||
|
final InputStream stream = internalGetResourceAsStream(tempFilepath);
|
||||||
|
if (stream != null) {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return internalGetResourceAsStream(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream internalGetResourceAsStream(final String tempFilepath) {
|
||||||
|
try {
|
||||||
|
if (this.rootFileSystem.isFile(tempFilepath) && this.rootFileSystem.isFileAvailable(tempFilepath)) {
|
||||||
|
final ByteBuffer buffer = this.rootFileSystem.readFileData(tempFilepath);
|
||||||
|
if (buffer.hasArray()) {
|
||||||
|
return new ByteArrayInputStream(buffer.array());
|
||||||
|
}
|
||||||
|
final byte[] data = new byte[buffer.remaining()];
|
||||||
|
buffer.clear();
|
||||||
|
buffer.get(data);
|
||||||
|
return new ByteArrayInputStream(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException("CASC parser error for: " + tempFilepath, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(String path) {
|
||||||
|
path = path.toLowerCase(Locale.US).replace('/', '\\').replace(':', '\\');
|
||||||
|
final String resolvedAlias = this.fileAliases.get(path);
|
||||||
|
if (resolvedAlias != null) {
|
||||||
|
path = resolvedAlias;
|
||||||
|
}
|
||||||
|
for (final String prefix : this.prefixes) {
|
||||||
|
final String tempFilepath = prefix + "\\" + path;
|
||||||
|
final ByteBuffer stream = internalRead(tempFilepath);
|
||||||
|
if (stream != null) {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return internalRead(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteBuffer internalRead(final String tempFilepath) {
|
||||||
|
try {
|
||||||
|
if (this.rootFileSystem.isFile(tempFilepath) && this.rootFileSystem.isFileAvailable(tempFilepath)) {
|
||||||
|
final ByteBuffer buffer = this.rootFileSystem.readFileData(tempFilepath);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException("CASC parser error for: " + tempFilepath, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getFile(String filepath) {
|
||||||
|
filepath = filepath.toLowerCase(Locale.US).replace('/', '\\').replace(':', '\\');
|
||||||
|
final String resolvedAlias = this.fileAliases.get(filepath);
|
||||||
|
if (resolvedAlias != null) {
|
||||||
|
filepath = resolvedAlias;
|
||||||
|
}
|
||||||
|
for (final String prefix : this.prefixes) {
|
||||||
|
final String tempFilepath = prefix + "\\" + filepath;
|
||||||
|
final File file = internalGetFile(tempFilepath);
|
||||||
|
if (file != null) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return internalGetFile(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File internalGetFile(final String tempFilepath) {
|
||||||
|
try {
|
||||||
|
if (this.rootFileSystem.isFile(tempFilepath) && this.rootFileSystem.isFileAvailable(tempFilepath)) {
|
||||||
|
final ByteBuffer buffer = this.rootFileSystem.readFileData(tempFilepath);
|
||||||
|
String tmpdir = System.getProperty("java.io.tmpdir");
|
||||||
|
if (!tmpdir.endsWith(File.separator)) {
|
||||||
|
tmpdir += File.separator;
|
||||||
|
}
|
||||||
|
final String tempDir = tmpdir + "MatrixEaterExtract/";
|
||||||
|
final File tempProduct = new File(tempDir + tempFilepath.replace('\\', File.separatorChar));
|
||||||
|
tempProduct.delete();
|
||||||
|
tempProduct.getParentFile().mkdirs();
|
||||||
|
try (final FileChannel fileChannel = FileChannel.open(tempProduct.toPath(), StandardOpenOption.CREATE,
|
||||||
|
StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
fileChannel.write(buffer);
|
||||||
|
}
|
||||||
|
tempProduct.deleteOnExit();
|
||||||
|
return tempProduct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException("CASC parser error for: " + tempFilepath, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String filepath) {
|
||||||
|
filepath = filepath.toLowerCase(Locale.US).replace('/', '\\').replace(':', '\\');
|
||||||
|
final String resolvedAlias = this.fileAliases.get(filepath);
|
||||||
|
if (resolvedAlias != null) {
|
||||||
|
filepath = resolvedAlias;
|
||||||
|
}
|
||||||
|
for (final String prefix : this.prefixes) {
|
||||||
|
final String tempFilepath = prefix + "\\" + filepath;
|
||||||
|
try {
|
||||||
|
if (this.rootFileSystem.isFile(tempFilepath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException("CASC parser error for: " + tempFilepath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return this.rootFileSystem.isFile(filepath);
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException("CASC parser error for: " + filepath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getListfile() {
|
||||||
|
return this.listFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.warcraftIIICASC.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package com.etheller.warsmash.datasources;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CascDataSourceDescriptor implements DataSourceDescriptor {
|
||||||
|
/**
|
||||||
|
* Generated serial id
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 832549098549298820L;
|
||||||
|
private final String gameInstallPath;
|
||||||
|
private final List<String> prefixes;
|
||||||
|
|
||||||
|
public CascDataSourceDescriptor(final String gameInstallPath, final List<String> prefixes) {
|
||||||
|
this.gameInstallPath = gameInstallPath;
|
||||||
|
this.prefixes = prefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataSource createDataSource() {
|
||||||
|
return new CascDataSource(this.gameInstallPath, this.prefixes.toArray(new String[this.prefixes.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "CASC: " + this.gameInstallPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPrefix(final String prefix) {
|
||||||
|
this.prefixes.add(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePrefix(final int index) {
|
||||||
|
this.prefixes.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void movePrefixUp(final int index) {
|
||||||
|
if (index > 0) {
|
||||||
|
Collections.swap(this.prefixes, index, index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void movePrefixDown(final int index) {
|
||||||
|
if (index < (this.prefixes.size() - 1)) {
|
||||||
|
Collections.swap(this.prefixes, index, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameInstallPath() {
|
||||||
|
return this.gameInstallPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPrefixes() {
|
||||||
|
return this.prefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = (prime * result) + ((this.gameInstallPath == null) ? 0 : this.gameInstallPath.hashCode());
|
||||||
|
result = (prime * result) + ((this.prefixes == null) ? 0 : this.prefixes.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CascDataSourceDescriptor other = (CascDataSourceDescriptor) obj;
|
||||||
|
if (this.gameInstallPath == null) {
|
||||||
|
if (other.gameInstallPath != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!this.gameInstallPath.equals(other.gameInstallPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.prefixes == null) {
|
||||||
|
if (other.prefixes != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!this.prefixes.equals(other.prefixes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,12 @@ package com.etheller.warsmash.datasources;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -44,11 +44,23 @@ public class CompoundDataSource implements DataSource {
|
|||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".blp")) {
|
return null;
|
||||||
return getFile(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(final String path) throws IOException {
|
||||||
|
try {
|
||||||
|
for (int i = this.mpqList.size() - 1; i >= 0; i--) {
|
||||||
|
final DataSource mpq = this.mpqList.get(i);
|
||||||
|
final ByteBuffer buffer = mpq.read(path);
|
||||||
|
if (buffer != null) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".tif")) {
|
catch (final IOException e) {
|
||||||
return getFile(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -68,12 +80,6 @@ public class CompoundDataSource implements DataSource {
|
|||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".blp")) {
|
|
||||||
return getResourceAsStream(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
|
||||||
}
|
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".tif")) {
|
|
||||||
return getResourceAsStream(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,12 +94,6 @@ public class CompoundDataSource implements DataSource {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".blp")) {
|
|
||||||
return has(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
|
||||||
}
|
|
||||||
if (filepath.toLowerCase(Locale.US).endsWith(".tif")) {
|
|
||||||
return has(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.etheller.warsmash.datasources;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public interface DataSource {
|
public interface DataSource {
|
||||||
@ -27,6 +28,8 @@ public interface DataSource {
|
|||||||
*/
|
*/
|
||||||
File getFile(String filepath) throws IOException;
|
File getFile(String filepath) throws IOException;
|
||||||
|
|
||||||
|
ByteBuffer read(String path) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the data source contains a valid entry for a particular file.
|
* Returns true if the data source contains a valid entry for a particular file.
|
||||||
* Some data sources (MPQs) may contain files for which this returns true, even
|
* Some data sources (MPQs) may contain files for which this returns true, even
|
||||||
|
@ -3,8 +3,10 @@ package com.etheller.warsmash.datasources;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -34,7 +36,7 @@ public class FolderDataSource implements DataSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceAsStream(String filepath) throws IOException {
|
public InputStream getResourceAsStream(String filepath) throws IOException {
|
||||||
filepath=fixFilepath(filepath);
|
filepath = fixFilepath(filepath);
|
||||||
if (!has(filepath)) {
|
if (!has(filepath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -43,16 +45,25 @@ public class FolderDataSource implements DataSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getFile(String filepath) throws IOException {
|
public File getFile(String filepath) throws IOException {
|
||||||
filepath=fixFilepath(filepath);
|
filepath = fixFilepath(filepath);
|
||||||
if (!has(filepath)) {
|
if (!has(filepath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new File(this.folderPath.toString() + File.separatorChar + filepath);
|
return new File(this.folderPath.toString() + File.separatorChar + filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(String path) throws IOException {
|
||||||
|
path = fixFilepath(path);
|
||||||
|
if (!has(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ByteBuffer.wrap(Files.readAllBytes(Paths.get(path)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean has(String filepath) {
|
public boolean has(String filepath) {
|
||||||
filepath=fixFilepath(filepath);
|
filepath = fixFilepath(filepath);
|
||||||
if ("".equals(filepath)) {
|
if ("".equals(filepath)) {
|
||||||
return false; // special case for folder data source, dont do this
|
return false; // special case for folder data source, dont do this
|
||||||
}
|
}
|
||||||
@ -69,7 +80,8 @@ public class FolderDataSource implements DataSource {
|
|||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String fixFilepath(String filepath) {
|
private static String fixFilepath(final String filepath) {
|
||||||
return filepath.replace('\\', File.separatorChar).replace('/', File.separatorChar);
|
return filepath.replace('\\', File.separatorChar).replace('/', File.separatorChar).replace(':',
|
||||||
|
File.separatorChar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.SeekableByteChannel;
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -57,6 +58,28 @@ public class MpqDataSource implements DataSource {
|
|||||||
return newInputStream;
|
return newInputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(final String path) throws IOException {
|
||||||
|
ArchivedFile file = null;
|
||||||
|
try {
|
||||||
|
file = this.archive.lookupHash2(new HashLookup(path));
|
||||||
|
}
|
||||||
|
catch (final MPQException exc) {
|
||||||
|
if (exc.getMessage().equals("lookup not found")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IOException(exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (final ArchivedFileStream stream = new ArchivedFileStream(this.inputChannel, this.extractor, file)) {
|
||||||
|
final long size = stream.size();
|
||||||
|
final ByteBuffer buffer = ByteBuffer.allocate((int) size);
|
||||||
|
stream.read(buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getFile(final String filepath) throws IOException {
|
public File getFile(final String filepath) throws IOException {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
@ -72,4 +72,7 @@ public class MpqDataSourceDescriptor implements DataSourceDescriptor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMpqFilePath() {
|
||||||
|
return this.mpqFilePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.etheller.warsmash.datasources;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -21,6 +22,11 @@ public class SubdirDataSource implements DataSource {
|
|||||||
return this.dataSource.getFile(this.subdir + filepath);
|
return this.dataSource.getFile(this.subdir + filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(final String path) throws IOException {
|
||||||
|
return this.dataSource.read(this.subdir + path);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
||||||
return this.dataSource.getResourceAsStream(this.subdir + filepath);
|
return this.dataSource.getResourceAsStream(this.subdir + filepath);
|
||||||
|
@ -41,7 +41,6 @@ import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame;
|
|||||||
import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
|
import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
|
||||||
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
|
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
|
||||||
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
|
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
|
|
||||||
import com.etheller.warsmash.units.DataTable;
|
import com.etheller.warsmash.units.DataTable;
|
||||||
import com.etheller.warsmash.units.Element;
|
import com.etheller.warsmash.units.Element;
|
||||||
import com.etheller.warsmash.util.ImageUtils;
|
import com.etheller.warsmash.util.ImageUtils;
|
||||||
@ -49,6 +48,7 @@ import com.etheller.warsmash.util.StringBundle;
|
|||||||
import com.etheller.warsmash.viewer5.Scene;
|
import com.etheller.warsmash.viewer5.Scene;
|
||||||
import com.etheller.warsmash.viewer5.handlers.AbstractMdxModelViewer;
|
import com.etheller.warsmash.viewer5.handlers.AbstractMdxModelViewer;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode;
|
||||||
|
|
||||||
public final class GameUI extends AbstractUIFrame implements UIFrame {
|
public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
@ -658,7 +658,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
|||||||
Texture texture = this.pathToTexture.get(path);
|
Texture texture = this.pathToTexture.get(path);
|
||||||
if (texture == null) {
|
if (texture == null) {
|
||||||
try {
|
try {
|
||||||
texture = ImageUtils.getBLPTexture(this.dataSource, path);
|
texture = ImageUtils.getAnyExtensionTexture(this.dataSource, path);
|
||||||
this.pathToTexture.put(path, texture);
|
this.pathToTexture.put(path, texture);
|
||||||
}
|
}
|
||||||
catch (final Exception exc) {
|
catch (final Exception exc) {
|
||||||
|
@ -10,7 +10,8 @@ import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
|||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
import com.etheller.warsmash.datasources.DataSourceDescriptor;
|
||||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.util.MdxUtils;
|
||||||
|
|
||||||
public class ModelExport {
|
public class ModelExport {
|
||||||
|
|
||||||
@ -24,9 +25,9 @@ public class ModelExport {
|
|||||||
|
|
||||||
try (InputStream modelStream = dataSource
|
try (InputStream modelStream = dataSource
|
||||||
.getResourceAsStream("UI\\Glues\\MainMenu\\MainMenu3D\\MainMenu3D.mdx")) {
|
.getResourceAsStream("UI\\Glues\\MainMenu\\MainMenu3D\\MainMenu3D.mdx")) {
|
||||||
final MdlxModel model = new MdlxModel(modelStream);
|
final MdlxModel model = new MdlxModel(dataSource.read("UI\\Glues\\MainMenu\\MainMenu3D\\MainMenu3D.mdx"));
|
||||||
try (FileOutputStream fos = new FileOutputStream(new File("C:\\Temp\\MainMenu3D.mdl"))) {
|
try (FileOutputStream fos = new FileOutputStream(new File("C:\\Temp\\MainMenu3D.mdl"))) {
|
||||||
model.saveMdl(fos);
|
MdxUtils.saveMdl(model, fos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
|
@ -4,7 +4,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
|||||||
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
|
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
|
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode;
|
||||||
|
|
||||||
public class FilterModeTextureFrame extends TextureFrame {
|
public class FilterModeTextureFrame extends TextureFrame {
|
||||||
private int blendSrc;
|
private int blendSrc;
|
||||||
|
@ -6,13 +6,14 @@ import com.badlogic.gdx.utils.viewport.Viewport;
|
|||||||
import com.etheller.warsmash.parsers.fdf.GameUI;
|
import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||||
import com.etheller.warsmash.parsers.fdf.datamodel.BackdropCornerFlags;
|
import com.etheller.warsmash.parsers.fdf.datamodel.BackdropCornerFlags;
|
||||||
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
|
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Layer;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Material;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Texture;
|
|
||||||
import com.etheller.warsmash.viewer5.Scene;
|
import com.etheller.warsmash.viewer5.Scene;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxMaterial;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTexture;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTexture.WrapMode;
|
||||||
|
|
||||||
public class SmartBackdropFrame extends SpriteFrame {
|
public class SmartBackdropFrame extends SpriteFrame {
|
||||||
private final boolean decorateFileNames;
|
private final boolean decorateFileNames;
|
||||||
@ -49,21 +50,21 @@ public class SmartBackdropFrame extends SpriteFrame {
|
|||||||
final MdlxModel model = new MdlxModel();
|
final MdlxModel model = new MdlxModel();
|
||||||
final int edgeFileMaterialId = generateMaterial(model, this.edgeFileString, true);
|
final int edgeFileMaterialId = generateMaterial(model, this.edgeFileString, true);
|
||||||
final int backgroundMaterialId = generateMaterial(model, this.backgroundString, this.tileBackground);
|
final int backgroundMaterialId = generateMaterial(model, this.backgroundString, this.tileBackground);
|
||||||
final Geoset edgeGeoset = new Geoset();
|
final MdlxGeoset edgeGeoset = new MdlxGeoset();
|
||||||
final float[] edgeGeosetVertices = new float[32 * 4];
|
final float[] edgeGeosetVertices = new float[32 * 4];
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int generateMaterial(final MdlxModel model, final String path, final boolean wrap) {
|
private int generateMaterial(final MdlxModel model, final String path, final boolean wrap) {
|
||||||
final Texture edgeFileReference = new Texture();
|
final MdlxTexture edgeFileReference = new MdlxTexture();
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
edgeFileReference.setFlags(0x2 | 0x1);
|
edgeFileReference.setWrapMode(WrapMode.REPEAT_BOTH);
|
||||||
}
|
}
|
||||||
edgeFileReference.setPath(path);
|
edgeFileReference.setPath(path);
|
||||||
final int textureId = model.getTextures().size();
|
final int textureId = model.getTextures().size();
|
||||||
model.getTextures().add(edgeFileReference);
|
model.getTextures().add(edgeFileReference);
|
||||||
final Material edgeFileMaterial = new Material();
|
final MdlxMaterial edgeFileMaterial = new MdlxMaterial();
|
||||||
final Layer edgeFileMaterialLayer = new Layer();
|
final MdlxLayer edgeFileMaterialLayer = new MdlxLayer();
|
||||||
edgeFileMaterialLayer.setAlpha(1.0f);
|
edgeFileMaterialLayer.setAlpha(1.0f);
|
||||||
edgeFileMaterialLayer.setFilterMode(FilterMode.BLEND);
|
edgeFileMaterialLayer.setFilterMode(FilterMode.BLEND);
|
||||||
edgeFileMaterialLayer.setTextureId(textureId);
|
edgeFileMaterialLayer.setTextureId(textureId);
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map from MDX animation tags to their equivalent MDL tokens, and the
|
|
||||||
* implementation objects.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Based on the works of Chananya Freiman.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum AnimationMap {
|
|
||||||
// Layer
|
|
||||||
KMTF(MdlUtils.TOKEN_TEXTURE_ID, TimelineDescriptor.UINT32_TIMELINE),
|
|
||||||
KMTA(MdlUtils.TOKEN_ALPHA, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// TextureAnimation
|
|
||||||
KTAT(MdlUtils.TOKEN_TRANSLATION, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KTAR(MdlUtils.TOKEN_ROTATION, TimelineDescriptor.VECTOR4_TIMELINE),
|
|
||||||
KTAS(MdlUtils.TOKEN_SCALING, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
// GeosetAnimation
|
|
||||||
KGAO(MdlUtils.TOKEN_ALPHA, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KGAC(MdlUtils.TOKEN_COLOR, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
// Light
|
|
||||||
KLAS(MdlUtils.TOKEN_ATTENUATION_START, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KLAE(MdlUtils.TOKEN_ATTENUATION_END, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KLAC(MdlUtils.TOKEN_COLOR, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KLAI(MdlUtils.TOKEN_INTENSITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KLBI(MdlUtils.TOKEN_AMB_INTENSITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KLBC(MdlUtils.TOKEN_AMB_COLOR, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KLAV(MdlUtils.TOKEN_VISIBILITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// Attachment
|
|
||||||
KATV(MdlUtils.TOKEN_VISIBILITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// ParticleEmitter
|
|
||||||
KPEE(MdlUtils.TOKEN_EMISSION_RATE, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPEG(MdlUtils.TOKEN_GRAVITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPLN(MdlUtils.TOKEN_LONGITUDE, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPLT(MdlUtils.TOKEN_LATITUDE, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPEL(MdlUtils.TOKEN_LIFE_SPAN, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPES(MdlUtils.TOKEN_INIT_VELOCITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KPEV(MdlUtils.TOKEN_VISIBILITY, TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// ParticleEmitter2
|
|
||||||
KP2S("Speed", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2R("Variation", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2L("Latitude", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2G("Gravity", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2E("EmissionRate", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2N("Length", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2W("Width", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KP2V("Visibility", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// RibbonEmitter
|
|
||||||
KRHA("HeightAbove", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KRHB("HeightBelow", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KRAL("Alpha", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
KRCO("Color", TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KRTX("TextureSlot", TimelineDescriptor.UINT32_TIMELINE),
|
|
||||||
KRVS("Visibility", TimelineDescriptor.FLOAT_TIMELINE),
|
|
||||||
// Camera
|
|
||||||
KCTR(MdlUtils.TOKEN_TRANSLATION, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KTTR(MdlUtils.TOKEN_TRANSLATION, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KCRL(MdlUtils.TOKEN_ROTATION, TimelineDescriptor.UINT32_TIMELINE),
|
|
||||||
// GenericObject
|
|
||||||
KGTR(MdlUtils.TOKEN_TRANSLATION, TimelineDescriptor.VECTOR3_TIMELINE),
|
|
||||||
KGRT(MdlUtils.TOKEN_ROTATION, TimelineDescriptor.VECTOR4_TIMELINE),
|
|
||||||
KGSC(MdlUtils.TOKEN_SCALING, TimelineDescriptor.VECTOR3_TIMELINE);
|
|
||||||
private final String mdlToken;
|
|
||||||
private final TimelineDescriptor implementation;
|
|
||||||
private final War3ID war3id;
|
|
||||||
|
|
||||||
private AnimationMap(final String mdlToken, final TimelineDescriptor implementation) {
|
|
||||||
this.mdlToken = mdlToken;
|
|
||||||
this.implementation = implementation;
|
|
||||||
this.war3id = War3ID.fromString(this.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMdlToken() {
|
|
||||||
return this.mdlToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimelineDescriptor getImplementation() {
|
|
||||||
return this.implementation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public War3ID getWar3id() {
|
|
||||||
return this.war3id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Map<War3ID, AnimationMap> ID_TO_TAG = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (final AnimationMap tag : AnimationMap.values()) {
|
|
||||||
ID_TO_TAG.put(tag.getWar3id(), tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Attachment extends GenericObject {
|
|
||||||
private String path = "";
|
|
||||||
private int attachmentId;
|
|
||||||
|
|
||||||
public Attachment() {
|
|
||||||
super(0x800);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] PATH_BYTES_HEAP = new byte[260];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
super.readMdx(stream);
|
|
||||||
|
|
||||||
this.path = ParseUtils.readString(stream, PATH_BYTES_HEAP);
|
|
||||||
this.attachmentId = stream.readInt();
|
|
||||||
|
|
||||||
this.readTimelines(stream, size - this.getByteLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getByteLength());
|
|
||||||
|
|
||||||
super.writeMdx(stream);
|
|
||||||
|
|
||||||
final byte[] bytes = this.path.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (260 - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
stream.writeInt(this.attachmentId); // Used to be Int32 in JS
|
|
||||||
|
|
||||||
this.writeNonGenericAnimationChunks(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
for (final String token : super.readMdlGeneric(stream)) {
|
|
||||||
if (MdlUtils.TOKEN_ATTACHMENT_ID.equals(token)) {
|
|
||||||
this.attachmentId = stream.readInt();
|
|
||||||
}
|
|
||||||
else if (MdlUtils.TOKEN_PATH.equals(token)) {
|
|
||||||
this.path = stream.read();
|
|
||||||
}
|
|
||||||
else if (MdlUtils.TOKEN_VISIBILITY.equals(token)) {
|
|
||||||
this.readTimeline(stream, AnimationMap.KATV);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IOException("Unknown token in Attachment " + this.name + ": " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_ATTACHMENT, this.name);
|
|
||||||
this.writeGenericHeader(stream);
|
|
||||||
|
|
||||||
// flowtsohg asks in his JS:
|
|
||||||
// Is this needed? MDX supplies it, but MdlxConv does not use it.
|
|
||||||
// Retera:
|
|
||||||
// I tried to preserve it when it was shown, but omit it when it was omitted
|
|
||||||
// for MDL in Matrix Eater. Matrix Eater's MDL -> MDX is generating them
|
|
||||||
// and discarding what was read from the MDL. The Matrix Eater is notably
|
|
||||||
// buggy from a cursory read through, and would always omit AttachmentID 0
|
|
||||||
// in MDL output.
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_ATTACHMENT_ID, this.attachmentId);
|
|
||||||
|
|
||||||
if ((this.path != null) && (this.path.length() > 0)) {
|
|
||||||
stream.writeStringAttrib(MdlUtils.TOKEN_PATH, this.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeTimeline(stream, AnimationMap.KATV);
|
|
||||||
|
|
||||||
this.writeGenericTimelines(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 268 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath() {
|
|
||||||
return this.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAttachmentId() {
|
|
||||||
return this.attachmentId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Bone extends GenericObject {
|
|
||||||
private int geosetId = -1;
|
|
||||||
private int geosetAnimationId = -1;
|
|
||||||
|
|
||||||
public Bone() {
|
|
||||||
super(0x100);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
super.readMdx(stream);
|
|
||||||
|
|
||||||
this.geosetId = stream.readInt();
|
|
||||||
this.geosetAnimationId = stream.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
super.writeMdx(stream);
|
|
||||||
stream.writeInt(this.geosetId);
|
|
||||||
stream.writeInt(this.geosetAnimationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) {
|
|
||||||
for (String token : super.readMdlGeneric(stream)) {
|
|
||||||
if (MdlUtils.TOKEN_GEOSETID.equals(token)) {
|
|
||||||
token = stream.read();
|
|
||||||
|
|
||||||
if (MdlUtils.TOKEN_MULTIPLE.equals(token)) {
|
|
||||||
this.geosetId = -1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.geosetId = Integer.parseInt(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (MdlUtils.TOKEN_GEOSETANIMID.equals(token)) {
|
|
||||||
token = stream.read();
|
|
||||||
|
|
||||||
if (MdlUtils.TOKEN_NONE.equals(token)) {
|
|
||||||
this.geosetAnimationId = -1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.geosetAnimationId = Integer.parseInt(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new RuntimeException("Unknown token in Bone " + this.name + ": " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_BONE, this.name);
|
|
||||||
this.writeGenericHeader(stream);
|
|
||||||
|
|
||||||
if (this.geosetId == -1) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_GEOSETID, MdlUtils.TOKEN_MULTIPLE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_GEOSETID, this.geosetId);
|
|
||||||
}
|
|
||||||
if (this.geosetAnimationId == -1) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_GEOSETANIMID, MdlUtils.TOKEN_NONE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_GEOSETANIMID, this.geosetAnimationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeGenericTimelines(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 8 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGeosetAnimationId() {
|
|
||||||
return this.geosetAnimationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGeosetId() {
|
|
||||||
return this.geosetId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Camera extends AnimatedObject {
|
|
||||||
protected String name;
|
|
||||||
private final float[] position;
|
|
||||||
private float fieldOfView;
|
|
||||||
private float farClippingPlane;
|
|
||||||
private float nearClippingPlane;
|
|
||||||
private final float[] targetPosition;
|
|
||||||
|
|
||||||
public Camera() {
|
|
||||||
this.position = new float[3];
|
|
||||||
this.targetPosition = new float[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] NAME_BYTES_HEAP = new byte[80];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
this.name = ParseUtils.readString(stream, NAME_BYTES_HEAP);
|
|
||||||
ParseUtils.readFloatArray(stream, this.position);
|
|
||||||
this.fieldOfView = stream.readFloat();
|
|
||||||
this.farClippingPlane = stream.readFloat();
|
|
||||||
this.nearClippingPlane = stream.readFloat();
|
|
||||||
ParseUtils.readFloatArray(stream, this.targetPosition);
|
|
||||||
|
|
||||||
readTimelines(stream, size - 120);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getByteLength());
|
|
||||||
final byte[] bytes = this.name.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (80 - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
ParseUtils.writeFloatArray(stream, this.position);
|
|
||||||
stream.writeFloat(this.fieldOfView);
|
|
||||||
stream.writeFloat(this.farClippingPlane);
|
|
||||||
stream.writeFloat(this.nearClippingPlane);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.targetPosition);
|
|
||||||
|
|
||||||
writeTimelines(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
this.name = stream.read();
|
|
||||||
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_POSITION:
|
|
||||||
stream.readFloatArray(this.position);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TRANSLATION:
|
|
||||||
readTimeline(stream, AnimationMap.KCTR);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ROTATION:
|
|
||||||
readTimeline(stream, AnimationMap.KCRL);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_FIELDOFVIEW:
|
|
||||||
this.fieldOfView = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_FARCLIP:
|
|
||||||
this.farClippingPlane = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_NEARCLIP:
|
|
||||||
this.nearClippingPlane = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TARGET:
|
|
||||||
for (final String subToken : stream.readBlock()) {
|
|
||||||
switch (subToken) {
|
|
||||||
case MdlUtils.TOKEN_POSITION:
|
|
||||||
stream.readFloatArray(this.targetPosition);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TRANSLATION:
|
|
||||||
readTimeline(stream, AnimationMap.KTTR);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unknown token in Camera " + this.name + "'s Target: " + subToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Camera " + this.name + ": " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_CAMERA, this.name);
|
|
||||||
|
|
||||||
stream.writeFloatArrayAttrib(MdlUtils.TOKEN_POSITION, this.position);
|
|
||||||
writeTimeline(stream, AnimationMap.KCTR);
|
|
||||||
writeTimeline(stream, AnimationMap.KCRL);
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_FIELDOFVIEW, this.fieldOfView);
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_FARCLIP, this.farClippingPlane);
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_NEARCLIP, this.nearClippingPlane);
|
|
||||||
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_TARGET);
|
|
||||||
stream.writeFloatArrayAttrib(MdlUtils.TOKEN_POSITION, this.targetPosition);
|
|
||||||
writeTimeline(stream, AnimationMap.KTTR);
|
|
||||||
stream.endBlock();
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 120 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getPosition() {
|
|
||||||
return this.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFieldOfView() {
|
|
||||||
return this.fieldOfView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFarClippingPlane() {
|
|
||||||
return this.farClippingPlane;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getNearClippingPlane() {
|
|
||||||
return this.nearClippingPlane;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getTargetPosition() {
|
|
||||||
return this.targetPosition;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
public interface Chunk {
|
|
||||||
long getByteLength();
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Extent {
|
|
||||||
protected float boundsRadius = 0;
|
|
||||||
protected final float[] min = new float[3];
|
|
||||||
protected final float[] max = new float[3];
|
|
||||||
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
this.boundsRadius = stream.readFloat();
|
|
||||||
ParseUtils.readFloatArray(stream, this.min);
|
|
||||||
ParseUtils.readFloatArray(stream, this.max);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
stream.writeFloat(this.boundsRadius);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.min);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.max);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) {
|
|
||||||
if ((this.min[0] != 0) || (this.min[1] != 0) || (this.min[2] != 0)) {
|
|
||||||
stream.writeFloatArrayAttrib(MdlUtils.TOKEN_MINIMUM_EXTENT, this.min);
|
|
||||||
}
|
|
||||||
if ((this.max[0] != 0) || (this.max[1] != 0) || (this.max[2] != 0)) {
|
|
||||||
stream.writeFloatArrayAttrib(MdlUtils.TOKEN_MAXIMUM_EXTENT, this.max);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.boundsRadius != 0) {
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_BOUNDSRADIUS, this.boundsRadius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBoundsRadius(final float boundsRadius) {
|
|
||||||
this.boundsRadius = boundsRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getBoundsRadius() {
|
|
||||||
return this.boundsRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getMin() {
|
|
||||||
return this.min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getMax() {
|
|
||||||
return this.max;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,350 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic object.
|
|
||||||
*
|
|
||||||
* The parent class for all objects that exist in the world, and may contain
|
|
||||||
* spatial animations. This includes bones, particle emitters, and many other
|
|
||||||
* things.
|
|
||||||
*
|
|
||||||
* Based on the works of Chananya Freiman.
|
|
||||||
*/
|
|
||||||
public abstract class GenericObject extends AnimatedObject implements Chunk {
|
|
||||||
protected String name;
|
|
||||||
private int objectId;
|
|
||||||
private int parentId;
|
|
||||||
protected int flags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] NAME_BYTES_HEAP = new byte[80];
|
|
||||||
|
|
||||||
public GenericObject(final int flags) {
|
|
||||||
this.name = "";
|
|
||||||
this.objectId = -1;
|
|
||||||
this.parentId = -1;
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
this.name = ParseUtils.readString(stream, NAME_BYTES_HEAP);
|
|
||||||
this.objectId = stream.readInt();
|
|
||||||
this.parentId = stream.readInt();
|
|
||||||
this.flags = stream.readInt(); // Used to be Int32 in JS
|
|
||||||
readTimelines(stream, size - 96);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getGenericByteLength());
|
|
||||||
final byte[] bytes = this.name.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (80 - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
stream.writeInt(this.objectId);
|
|
||||||
stream.writeInt(this.parentId);
|
|
||||||
stream.writeInt(this.flags); // UInt32 in ghostwolf JS, shouldn't matter for Java
|
|
||||||
|
|
||||||
for (final Timeline<?> timeline : eachTimeline(true)) {
|
|
||||||
timeline.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeNonGenericAnimationChunks(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
for (final Timeline<?> timeline : eachTimeline(false)) {
|
|
||||||
timeline.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Iterable<String> readMdlGeneric(final MdlTokenInputStream stream) {
|
|
||||||
this.name = stream.read();
|
|
||||||
return new Iterable<String>() {
|
|
||||||
@Override
|
|
||||||
public Iterator<String> iterator() {
|
|
||||||
return new WrappedMdlTokenIterator(GenericObject.this.readAnimatedBlock(stream), GenericObject.this,
|
|
||||||
stream);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeGenericHeader(final MdlTokenOutputStream stream) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_OBJECTID, this.objectId);
|
|
||||||
|
|
||||||
if (this.parentId != -1) {
|
|
||||||
stream.writeAttrib("Parent", this.parentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x40) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_BILLBOARDED_LOCK_Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x20) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_BILLBOARDED_LOCK_Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x10) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_BILLBOARDED_LOCK_X);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x8) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_BILLBOARDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x80) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_CAMERA_ANCHORED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x2) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_DONT_INHERIT + " { " + MdlUtils.TOKEN_ROTATION + " }");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x1) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_DONT_INHERIT + " { " + MdlUtils.TOKEN_TRANSLATION + " }");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x4) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_DONT_INHERIT + " { " + MdlUtils.TOKEN_SCALING + " }");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeGenericTimelines(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
this.writeTimeline(stream, AnimationMap.KGTR);
|
|
||||||
this.writeTimeline(stream, AnimationMap.KGRT);
|
|
||||||
this.writeTimeline(stream, AnimationMap.KGSC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Timeline<?>> eachTimeline(final boolean generic) {
|
|
||||||
return new TimelineMaskingIterable(generic);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getGenericByteLength() {
|
|
||||||
long size = 96;
|
|
||||||
for (final Chunk animation : eachTimeline(true)) {
|
|
||||||
size += animation.getByteLength();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 96 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class TimelineMaskingIterable implements Iterable<Timeline<?>> {
|
|
||||||
private final boolean generic;
|
|
||||||
|
|
||||||
private TimelineMaskingIterable(final boolean generic) {
|
|
||||||
this.generic = generic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<Timeline<?>> iterator() {
|
|
||||||
return new TimelineMaskingIterator(this.generic, GenericObject.this.timelines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class TimelineMaskingIterator implements Iterator<Timeline<?>> {
|
|
||||||
private final boolean wantGeneric;
|
|
||||||
private final Iterator<Timeline<?>> delegate;
|
|
||||||
private boolean hasNext;
|
|
||||||
private Timeline<?> next;
|
|
||||||
|
|
||||||
public TimelineMaskingIterator(final boolean wantGeneric, final List<Timeline<?>> timelines) {
|
|
||||||
this.wantGeneric = wantGeneric;
|
|
||||||
this.delegate = timelines.iterator();
|
|
||||||
scanUntilNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isGeneric(final Timeline<?> timeline) {
|
|
||||||
final War3ID name = timeline.getName();
|
|
||||||
final boolean generic = AnimationMap.KGTR.getWar3id().equals(name)
|
|
||||||
|| AnimationMap.KGRT.getWar3id().equals(name) || AnimationMap.KGSC.getWar3id().equals(name);
|
|
||||||
return generic;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scanUntilNext() {
|
|
||||||
boolean hasNext = false;
|
|
||||||
if (hasNext = this.delegate.hasNext()) {
|
|
||||||
do {
|
|
||||||
this.next = this.delegate.next();
|
|
||||||
}
|
|
||||||
while ((isGeneric(this.next) != this.wantGeneric) && (hasNext = this.delegate.hasNext()));
|
|
||||||
}
|
|
||||||
if (!hasNext) {
|
|
||||||
this.next = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return this.next != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timeline<?> next() {
|
|
||||||
final Timeline<?> last = this.next;
|
|
||||||
scanUntilNext();
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException("Remove is not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class WrappedMdlTokenIterator implements Iterator<String> {
|
|
||||||
private final Iterator<String> delegate;
|
|
||||||
private final GenericObject updatingObject;
|
|
||||||
private final MdlTokenInputStream stream;
|
|
||||||
private String next;
|
|
||||||
private boolean hasLoaded = false;
|
|
||||||
|
|
||||||
public WrappedMdlTokenIterator(final Iterator<String> delegate, final GenericObject updatingObject,
|
|
||||||
final MdlTokenInputStream stream) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.updatingObject = updatingObject;
|
|
||||||
this.stream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (this.delegate.hasNext()) {
|
|
||||||
this.next = read();
|
|
||||||
this.hasLoaded = true;
|
|
||||||
return this.next != null;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String next() {
|
|
||||||
if (!this.hasLoaded) {
|
|
||||||
this.next = read();
|
|
||||||
}
|
|
||||||
this.hasLoaded = false;
|
|
||||||
return this.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String read() {
|
|
||||||
String token;
|
|
||||||
InteriorParsing: do {
|
|
||||||
token = this.delegate.next();
|
|
||||||
if (token == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_OBJECTID:
|
|
||||||
this.updatingObject.objectId = Integer.parseInt(this.delegate.next());
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_PARENT:
|
|
||||||
this.updatingObject.parentId = Integer.parseInt(this.delegate.next());
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BILLBOARDED_LOCK_Z:
|
|
||||||
this.updatingObject.flags |= 0x40;
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BILLBOARDED_LOCK_Y:
|
|
||||||
this.updatingObject.flags |= 0x20;
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BILLBOARDED_LOCK_X:
|
|
||||||
this.updatingObject.flags |= 0x10;
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BILLBOARDED:
|
|
||||||
this.updatingObject.flags |= 0x8;
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_CAMERA_ANCHORED:
|
|
||||||
this.updatingObject.flags |= 0x80;
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_DONT_INHERIT:
|
|
||||||
for (final String subToken : this.stream.readBlock()) {
|
|
||||||
switch (subToken) {
|
|
||||||
case MdlUtils.TOKEN_ROTATION:
|
|
||||||
this.updatingObject.flags |= 0x2;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TRANSLATION:
|
|
||||||
this.updatingObject.flags |= 0x1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SCALING:
|
|
||||||
this.updatingObject.flags |= 0x0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TRANSLATION:
|
|
||||||
try {
|
|
||||||
this.updatingObject.readTimeline(this.stream, AnimationMap.KGTR);
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ROTATION:
|
|
||||||
try {
|
|
||||||
this.updatingObject.readTimeline(this.stream, AnimationMap.KGRT);
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SCALING:
|
|
||||||
try {
|
|
||||||
this.updatingObject.readTimeline(this.stream, AnimationMap.KGSC);
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
token = null;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break InteriorParsing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (this.delegate.hasNext());
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getObjectId() {
|
|
||||||
return this.objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getParentId() {
|
|
||||||
return this.parentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return this.flags;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,428 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Geoset implements MdlxBlock, Chunk {
|
|
||||||
private static final War3ID VRTX = War3ID.fromString("VRTX");
|
|
||||||
private static final War3ID NRMS = War3ID.fromString("NRMS");
|
|
||||||
private static final War3ID PTYP = War3ID.fromString("PTYP");
|
|
||||||
private static final War3ID PCNT = War3ID.fromString("PCNT");
|
|
||||||
private static final War3ID PVTX = War3ID.fromString("PVTX");
|
|
||||||
private static final War3ID GNDX = War3ID.fromString("GNDX");
|
|
||||||
private static final War3ID MTGC = War3ID.fromString("MTGC");
|
|
||||||
private static final War3ID MATS = War3ID.fromString("MATS");
|
|
||||||
private static final War3ID UVAS = War3ID.fromString("UVAS");
|
|
||||||
private static final War3ID UVBS = War3ID.fromString("UVBS");
|
|
||||||
private float[] vertices;
|
|
||||||
private float[] normals;
|
|
||||||
private long[] faceTypeGroups; // unsigned int[]
|
|
||||||
private long[] faceGroups; // unsigned int[]
|
|
||||||
private int[] faces; // unsigned short[]
|
|
||||||
private short[] vertexGroups; // unsigned byte[]
|
|
||||||
private long[] matrixGroups; // unsigned int[]
|
|
||||||
private long[] matrixIndices; // unsigned int[]
|
|
||||||
private long materialId = 0;
|
|
||||||
private long selectionGroup = 0;
|
|
||||||
private long selectionFlags = 0;
|
|
||||||
private final Extent extent = new Extent();
|
|
||||||
private Extent[] sequenceExtents;
|
|
||||||
private float[][] uvSets;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long mySize = ParseUtils.readUInt32(stream);
|
|
||||||
final int vrtx = stream.readInt(); // skip VRTX
|
|
||||||
this.vertices = ParseUtils.readFloatArray(stream, (int) (ParseUtils.readUInt32(stream) * 3));
|
|
||||||
final int nrms = stream.readInt(); // skip NRMS
|
|
||||||
this.normals = ParseUtils.readFloatArray(stream, (int) (ParseUtils.readUInt32(stream) * 3));
|
|
||||||
final int ptyp = stream.readInt(); // skip PTYP
|
|
||||||
this.faceTypeGroups = ParseUtils.readUInt32Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
stream.readInt(); // skip PCNT
|
|
||||||
this.faceGroups = ParseUtils.readUInt32Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
stream.readInt(); // skip PVTX
|
|
||||||
this.faces = ParseUtils.readUInt16Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
stream.readInt(); // skip GNDX
|
|
||||||
this.vertexGroups = ParseUtils.readUInt8Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
stream.readInt(); // skip MTGC
|
|
||||||
this.matrixGroups = ParseUtils.readUInt32Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
stream.readInt(); // skip MATS
|
|
||||||
this.matrixIndices = ParseUtils.readUInt32Array(stream, (int) ParseUtils.readUInt32(stream));
|
|
||||||
this.materialId = ParseUtils.readUInt32(stream);
|
|
||||||
this.selectionGroup = ParseUtils.readUInt32(stream);
|
|
||||||
this.selectionFlags = ParseUtils.readUInt32(stream);
|
|
||||||
this.extent.readMdx(stream);
|
|
||||||
|
|
||||||
final long numExtents = ParseUtils.readUInt32(stream);
|
|
||||||
this.sequenceExtents = new Extent[(int) numExtents];
|
|
||||||
for (int i = 0; i < numExtents; i++) {
|
|
||||||
final Extent extent = new Extent();
|
|
||||||
extent.readMdx(stream);
|
|
||||||
this.sequenceExtents[i] = extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.readInt(); // skip UVAS
|
|
||||||
|
|
||||||
final long numUVLayers = ParseUtils.readUInt32(stream);
|
|
||||||
this.uvSets = new float[(int) numUVLayers][];
|
|
||||||
for (int i = 0; i < numUVLayers; i++) {
|
|
||||||
stream.readInt(); // skip UVBS
|
|
||||||
this.uvSets[i] = ParseUtils.readFloatArray(stream, (int) (ParseUtils.readUInt32(stream) * 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, this.getByteLength());
|
|
||||||
ParseUtils.writeWar3ID(stream, VRTX);
|
|
||||||
ParseUtils.writeUInt32(stream, this.vertices.length / 3);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.vertices);
|
|
||||||
ParseUtils.writeWar3ID(stream, NRMS);
|
|
||||||
ParseUtils.writeUInt32(stream, this.normals.length / 3);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.normals);
|
|
||||||
ParseUtils.writeWar3ID(stream, PTYP);
|
|
||||||
ParseUtils.writeUInt32(stream, this.faceTypeGroups.length);
|
|
||||||
ParseUtils.writeUInt32Array(stream, this.faceTypeGroups);
|
|
||||||
ParseUtils.writeWar3ID(stream, PCNT);
|
|
||||||
ParseUtils.writeUInt32(stream, this.faceGroups.length);
|
|
||||||
ParseUtils.writeUInt32Array(stream, this.faceGroups);
|
|
||||||
ParseUtils.writeWar3ID(stream, PVTX);
|
|
||||||
ParseUtils.writeUInt32(stream, this.faces.length);
|
|
||||||
ParseUtils.writeUInt16Array(stream, this.faces);
|
|
||||||
ParseUtils.writeWar3ID(stream, GNDX);
|
|
||||||
ParseUtils.writeUInt32(stream, this.vertexGroups.length);
|
|
||||||
ParseUtils.writeUInt8Array(stream, this.vertexGroups);
|
|
||||||
ParseUtils.writeWar3ID(stream, MTGC);
|
|
||||||
ParseUtils.writeUInt32(stream, this.matrixGroups.length);
|
|
||||||
ParseUtils.writeUInt32Array(stream, this.matrixGroups);
|
|
||||||
ParseUtils.writeWar3ID(stream, MATS);
|
|
||||||
ParseUtils.writeUInt32(stream, this.matrixIndices.length);
|
|
||||||
ParseUtils.writeUInt32Array(stream, this.matrixIndices);
|
|
||||||
ParseUtils.writeUInt32(stream, this.materialId);
|
|
||||||
ParseUtils.writeUInt32(stream, this.selectionGroup);
|
|
||||||
ParseUtils.writeUInt32(stream, this.selectionFlags);
|
|
||||||
this.extent.writeMdx(stream);
|
|
||||||
ParseUtils.writeUInt32(stream, this.sequenceExtents.length);
|
|
||||||
|
|
||||||
for (final Extent sequenceExtent : this.sequenceExtents) {
|
|
||||||
sequenceExtent.writeMdx(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseUtils.writeWar3ID(stream, UVAS);
|
|
||||||
ParseUtils.writeUInt32(stream, this.uvSets.length);
|
|
||||||
for (final float[] uvSet : this.uvSets) {
|
|
||||||
ParseUtils.writeWar3ID(stream, UVBS);
|
|
||||||
ParseUtils.writeUInt32(stream, uvSet.length / 2);
|
|
||||||
ParseUtils.writeFloatArray(stream, uvSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) {
|
|
||||||
this.uvSets = new float[0][];
|
|
||||||
final List<Extent> sequenceExtents = new ArrayList<>();
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_VERTICES:
|
|
||||||
this.vertices = stream.readVectorArray(new float[stream.readInt() * 3], 3);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_NORMALS:
|
|
||||||
this.normals = stream.readVectorArray(new float[stream.readInt() * 3], 3);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TVERTICES:
|
|
||||||
this.uvSets = Arrays.copyOf(this.uvSets, this.uvSets.length + 1);
|
|
||||||
this.uvSets[this.uvSets.length - 1] = stream.readVectorArray(new float[stream.readInt() * 2], 2);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_VERTEX_GROUP: {
|
|
||||||
// Vertex groups are stored in a block with no count, can't allocate the buffer
|
|
||||||
// yet.
|
|
||||||
final List<Short> vertexGroups = new ArrayList<>();
|
|
||||||
for (final String vertexGroup : stream.readBlock()) {
|
|
||||||
vertexGroups.add(Short.valueOf(vertexGroup));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.vertexGroups = new short[vertexGroups.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (final Short vertexGroup : vertexGroups) {
|
|
||||||
this.vertexGroups[i++] = vertexGroup.shortValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_FACES:
|
|
||||||
// For now hardcoded for triangles, until I see a model with something
|
|
||||||
// different.
|
|
||||||
this.faceTypeGroups = new long[] { 4L };
|
|
||||||
|
|
||||||
stream.readInt(); // number of groups
|
|
||||||
|
|
||||||
final int count = stream.readInt();
|
|
||||||
|
|
||||||
stream.read(); // {
|
|
||||||
stream.read(); // Triangles
|
|
||||||
stream.read(); // {
|
|
||||||
|
|
||||||
this.faces = stream.readUInt16Array(new int[count]);
|
|
||||||
this.faceGroups = new long[] { count };
|
|
||||||
|
|
||||||
stream.read(); // }
|
|
||||||
stream.read(); // }
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_GROUPS: {
|
|
||||||
final List<Integer> indices = new ArrayList<>();
|
|
||||||
final List<Integer> groups = new ArrayList<>();
|
|
||||||
|
|
||||||
stream.readInt(); // matrices count
|
|
||||||
stream.readInt(); // total indices
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
for (final String matrix : stream.readBlock()) {
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
for (final String index : stream.readBlock()) {
|
|
||||||
indices.add(Integer.valueOf(index));
|
|
||||||
size += 1;
|
|
||||||
}
|
|
||||||
groups.add(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.matrixIndices = new long[indices.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (final Integer index : indices) {
|
|
||||||
this.matrixIndices[i++] = index.intValue();
|
|
||||||
}
|
|
||||||
this.matrixGroups = new long[groups.size()];
|
|
||||||
i = 0;
|
|
||||||
for (final Integer group : groups) {
|
|
||||||
this.matrixGroups[i++] = group.intValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MINIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.min);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MAXIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.max);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BOUNDSRADIUS:
|
|
||||||
this.extent.boundsRadius = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ANIM:
|
|
||||||
final Extent extent = new Extent();
|
|
||||||
|
|
||||||
for (final String subToken : stream.readBlock()) {
|
|
||||||
switch (subToken) {
|
|
||||||
case MdlUtils.TOKEN_MINIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(extent.min);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MAXIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(extent.max);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BOUNDSRADIUS:
|
|
||||||
extent.boundsRadius = stream.readFloat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sequenceExtents.add(extent);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MATERIAL_ID:
|
|
||||||
this.materialId = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SELECTION_GROUP:
|
|
||||||
this.selectionGroup = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_UNSELECTABLE:
|
|
||||||
this.selectionFlags = 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown token in Geoset: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.sequenceExtents = sequenceExtents.toArray(new Extent[sequenceExtents.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_GEOSET);
|
|
||||||
|
|
||||||
stream.writeVectorArray(MdlUtils.TOKEN_VERTICES, this.vertices, 3);
|
|
||||||
stream.writeVectorArray(MdlUtils.TOKEN_NORMALS, this.normals, 3);
|
|
||||||
|
|
||||||
for (final float[] uvSet : this.uvSets) {
|
|
||||||
stream.writeVectorArray(MdlUtils.TOKEN_TVERTICES, uvSet, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_VERTEX_GROUP);
|
|
||||||
for (int i = 0; i < this.vertexGroups.length; i++) {
|
|
||||||
stream.writeLine(this.vertexGroups[i] + ",");
|
|
||||||
}
|
|
||||||
stream.endBlock();
|
|
||||||
|
|
||||||
// For now hardcoded for triangles, until I see a model with something
|
|
||||||
// different.
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_FACES, 1, this.faces.length);
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_TRIANGLES);
|
|
||||||
final StringBuffer facesBuffer = new StringBuffer();
|
|
||||||
for (final int faceValue : this.faces) {
|
|
||||||
if (facesBuffer.length() > 0) {
|
|
||||||
facesBuffer.append(", ");
|
|
||||||
}
|
|
||||||
facesBuffer.append(faceValue);
|
|
||||||
}
|
|
||||||
stream.writeLine("{ " + facesBuffer.toString() + " },");
|
|
||||||
stream.endBlock();
|
|
||||||
stream.endBlock();
|
|
||||||
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_GROUPS, this.matrixGroups.length, this.matrixIndices.length);
|
|
||||||
int index = 0;
|
|
||||||
for (final long groupSize : this.matrixGroups) {
|
|
||||||
stream.writeLongSubArrayAttrib(MdlUtils.TOKEN_MATRICES, this.matrixIndices, index,
|
|
||||||
(int) (index + groupSize));
|
|
||||||
index += groupSize;
|
|
||||||
}
|
|
||||||
stream.endBlock();
|
|
||||||
|
|
||||||
this.extent.writeMdl(stream);
|
|
||||||
|
|
||||||
for (final Extent sequenceExtent : this.sequenceExtents) {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_ANIM);
|
|
||||||
sequenceExtent.writeMdl(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeAttribUInt32("MaterialID", this.materialId);
|
|
||||||
stream.writeAttribUInt32("SelectionGroup", this.selectionGroup);
|
|
||||||
if (this.selectionFlags == 4) {
|
|
||||||
stream.writeFlag("Unselectable");
|
|
||||||
}
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
long size = 120 + (this.vertices.length * 4) + (this.normals.length * 4) + (this.faceTypeGroups.length * 4)
|
|
||||||
+ (this.faceGroups.length * 4) + (this.faces.length * 2) + this.vertexGroups.length
|
|
||||||
+ (this.matrixGroups.length * 4) + (this.matrixIndices.length * 4) + (this.sequenceExtents.length * 28);
|
|
||||||
for (final float[] uvSet : this.uvSets) {
|
|
||||||
size += 8 + (uvSet.length * 4);
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getVertices() {
|
|
||||||
return this.vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getNormals() {
|
|
||||||
return this.normals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getFaceTypeGroups() {
|
|
||||||
return this.faceTypeGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getFaceGroups() {
|
|
||||||
return this.faceGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getFaces() {
|
|
||||||
return this.faces;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short[] getVertexGroups() {
|
|
||||||
return this.vertexGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getMatrixGroups() {
|
|
||||||
return this.matrixGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getMatrixIndices() {
|
|
||||||
return this.matrixIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMaterialId() {
|
|
||||||
return this.materialId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSelectionGroup() {
|
|
||||||
return this.selectionGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSelectionFlags() {
|
|
||||||
return this.selectionFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Extent getExtent() {
|
|
||||||
return this.extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Extent[] getSequenceExtents() {
|
|
||||||
return this.sequenceExtents;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[][] getUvSets() {
|
|
||||||
return this.uvSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVertices(final float[] vertices) {
|
|
||||||
this.vertices = vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNormals(final float[] normals) {
|
|
||||||
this.normals = normals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFaceTypeGroups(final long[] faceTypeGroups) {
|
|
||||||
this.faceTypeGroups = faceTypeGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFaceGroups(final long[] faceGroups) {
|
|
||||||
this.faceGroups = faceGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFaces(final int[] faces) {
|
|
||||||
this.faces = faces;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVertexGroups(final short[] vertexGroups) {
|
|
||||||
this.vertexGroups = vertexGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMatrixGroups(final long[] matrixGroups) {
|
|
||||||
this.matrixGroups = matrixGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMatrixIndices(final long[] matrixIndices) {
|
|
||||||
this.matrixIndices = matrixIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaterialId(final long materialId) {
|
|
||||||
this.materialId = materialId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectionGroup(final long selectionGroup) {
|
|
||||||
this.selectionGroup = selectionGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectionFlags(final long selectionFlags) {
|
|
||||||
this.selectionFlags = selectionFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSequenceExtents(final Extent[] sequenceExtents) {
|
|
||||||
this.sequenceExtents = sequenceExtents;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUvSets(final float[][] uvSets) {
|
|
||||||
this.uvSets = uvSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class GeosetAnimation extends AnimatedObject {
|
|
||||||
private float alpha = 1;
|
|
||||||
private int flags = 0;
|
|
||||||
private final float[] color = { 1, 1, 1 };
|
|
||||||
private int geosetId = -1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
this.alpha = stream.readFloat();
|
|
||||||
this.flags = stream.readInt();// ParseUtils.readUInt32(stream);
|
|
||||||
ParseUtils.readFloatArray(stream, this.color);
|
|
||||||
this.geosetId = stream.readInt();
|
|
||||||
|
|
||||||
readTimelines(stream, size - 28);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getByteLength());
|
|
||||||
stream.writeFloat(this.alpha);
|
|
||||||
stream.writeInt(this.flags);// ParseUtils.writeUInt32(stream, this.flags);
|
|
||||||
ParseUtils.writeFloatArray(stream, this.color);
|
|
||||||
stream.writeInt(this.geosetId);
|
|
||||||
|
|
||||||
writeTimelines(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
final Iterator<String> blockIterator = readAnimatedBlock(stream);
|
|
||||||
while (blockIterator.hasNext()) {
|
|
||||||
final String token = blockIterator.next();
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_DROP_SHADOW:
|
|
||||||
this.flags |= 0x1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_STATIC_ALPHA:
|
|
||||||
this.alpha = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ALPHA:
|
|
||||||
this.readTimeline(stream, AnimationMap.KGAO);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_STATIC_COLOR:
|
|
||||||
this.flags |= 0x2;
|
|
||||||
stream.readColor(this.color);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_COLOR:
|
|
||||||
this.flags |= 0x2;
|
|
||||||
readTimeline(stream, AnimationMap.KGAC);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_GEOSETID:
|
|
||||||
this.geosetId = stream.readInt();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in GeosetAnimation: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_GEOSETANIM);
|
|
||||||
|
|
||||||
if ((this.flags & 0x1) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_DROP_SHADOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.writeTimeline(stream, AnimationMap.KGAO)) {
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_STATIC_ALPHA, this.alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x2) != 0) {
|
|
||||||
if (!this.writeTimeline(stream, AnimationMap.KGAC)
|
|
||||||
&& ((this.color[0] != 0) || (this.color[1] != 0) || (this.color[2] != 0))) {
|
|
||||||
stream.writeColor(MdlUtils.TOKEN_STATIC_COLOR + " ", this.color); // TODO why the space?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.geosetId != -1) { // TODO Retera added -1 check here, why wasn't it there before in JS???
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_GEOSETID, this.geosetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 28 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getColor() {
|
|
||||||
return this.color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
return this.alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGeosetId() {
|
|
||||||
return this.geosetId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
|
|
||||||
public class Helper extends GenericObject {
|
|
||||||
|
|
||||||
public Helper() {
|
|
||||||
super(0x0); // NOTE: ghostwolf JS didn't pass the 0x1 flag????
|
|
||||||
// ANOTHER NOTE: setting the 0x1 flag causes other fan programs to spam error
|
|
||||||
// messages
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) {
|
|
||||||
for (final String token : readMdlGeneric(stream)) {
|
|
||||||
throw new IllegalStateException("Unknown token in Helper: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_HELPER, this.name);
|
|
||||||
writeGenericHeader(stream);
|
|
||||||
writeGenericTimelines(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
public enum InterpolationType {
|
|
||||||
DONT_INTERP, LINEAR, BEZIER, HERMITE;
|
|
||||||
|
|
||||||
public static final InterpolationType[] VALUES = values();
|
|
||||||
|
|
||||||
public boolean tangential() {
|
|
||||||
return ordinal() > 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,244 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Layer extends AnimatedObject {
|
|
||||||
// 0: none
|
|
||||||
// 1: transparent
|
|
||||||
// 2: blend
|
|
||||||
// 3: additive
|
|
||||||
// 4: add alpha
|
|
||||||
// 5: modulate
|
|
||||||
// 6: modulate 2x
|
|
||||||
public static enum FilterMode {
|
|
||||||
NONE("None"),
|
|
||||||
TRANSPARENT("Transparent"),
|
|
||||||
BLEND("Blend"),
|
|
||||||
ADDITIVE("Additive"),
|
|
||||||
ADDALPHA("AddAlpha"),
|
|
||||||
MODULATE("Modulate"),
|
|
||||||
MODULATE2X("Modulate2x");
|
|
||||||
|
|
||||||
String mdlText;
|
|
||||||
|
|
||||||
FilterMode(final String str) {
|
|
||||||
this.mdlText = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMdlText() {
|
|
||||||
return this.mdlText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FilterMode fromId(final int id) {
|
|
||||||
return values()[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int nameToId(final String name) {
|
|
||||||
for (final FilterMode mode : values()) {
|
|
||||||
if (mode.getMdlText().equals(name)) {
|
|
||||||
return mode.ordinal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getMdlText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FilterMode filterMode;
|
|
||||||
private int flags = 0;
|
|
||||||
private int textureId = -1;
|
|
||||||
private int textureAnimationId = -1;
|
|
||||||
private long coordId = 0;
|
|
||||||
private float alpha = 1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
this.filterMode = FilterMode.fromId((int) ParseUtils.readUInt32(stream));
|
|
||||||
this.flags = stream.readInt(); // UInt32 in JS
|
|
||||||
this.textureId = stream.readInt();
|
|
||||||
this.textureAnimationId = stream.readInt();
|
|
||||||
this.coordId = ParseUtils.readUInt32(stream);
|
|
||||||
this.alpha = stream.readFloat();
|
|
||||||
|
|
||||||
readTimelines(stream, size - 28);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getByteLength());
|
|
||||||
ParseUtils.writeUInt32(stream, this.filterMode.ordinal());
|
|
||||||
ParseUtils.writeUInt32(stream, this.flags);
|
|
||||||
stream.writeInt(this.textureId);
|
|
||||||
stream.writeInt(this.textureAnimationId);
|
|
||||||
ParseUtils.writeUInt32(stream, this.coordId);
|
|
||||||
stream.writeFloat(this.alpha);
|
|
||||||
|
|
||||||
writeTimelines(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
final Iterator<String> iterator = readAnimatedBlock(stream);
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
final String token = iterator.next();
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_FILTER_MODE:
|
|
||||||
this.filterMode = FilterMode.fromId(FilterMode.nameToId(stream.read()));
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_UNSHADED:
|
|
||||||
this.flags |= 0x1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SPHERE_ENV_MAP:
|
|
||||||
this.flags |= 0x2;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TWO_SIDED:
|
|
||||||
this.flags |= 0x10;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_UNFOGGED:
|
|
||||||
this.flags |= 0x20;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_NO_DEPTH_TEST:
|
|
||||||
this.flags |= 0x40;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_NO_DEPTH_SET:
|
|
||||||
this.flags |= 0x100;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_STATIC_TEXTURE_ID:
|
|
||||||
this.textureId = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TEXTURE_ID:
|
|
||||||
readTimeline(stream, AnimationMap.KMTF);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TVERTEX_ANIM_ID:
|
|
||||||
this.textureAnimationId = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_COORD_ID:
|
|
||||||
this.coordId = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_STATIC_ALPHA:
|
|
||||||
this.alpha = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ALPHA:
|
|
||||||
readTimeline(stream, AnimationMap.KMTA);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Layer: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_LAYER);
|
|
||||||
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_FILTER_MODE, this.filterMode.getMdlText());
|
|
||||||
|
|
||||||
if ((this.flags & 0x1) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_UNSHADED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x2) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_SPHERE_ENV_MAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x10) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_TWO_SIDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x20) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_UNFOGGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x40) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_NO_DEPTH_TEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x100) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_NO_DEPTH_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!writeTimeline(stream, AnimationMap.KMTF)) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_STATIC_TEXTURE_ID, this.textureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.textureAnimationId != -1) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_TVERTEX_ANIM_ID, this.textureAnimationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.coordId != 0) {
|
|
||||||
stream.writeAttribUInt32(MdlUtils.TOKEN_COORD_ID, this.coordId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!writeTimeline(stream, AnimationMap.KMTA) && (this.alpha != 1)) {
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_STATIC_ALPHA, this.alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 28 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilterMode getFilterMode() {
|
|
||||||
return this.filterMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return this.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTextureId() {
|
|
||||||
return this.textureId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTextureAnimationId() {
|
|
||||||
return this.textureAnimationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCoordId() {
|
|
||||||
return this.coordId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
return this.alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilterMode(final FilterMode filterMode) {
|
|
||||||
this.filterMode = filterMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlags(final int flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextureId(final int textureId) {
|
|
||||||
this.textureId = textureId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextureAnimationId(final int textureAnimationId) {
|
|
||||||
this.textureAnimationId = textureAnimationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoordId(final long coordId) {
|
|
||||||
this.coordId = coordId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlpha(final float alpha) {
|
|
||||||
this.alpha = alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Material implements MdlxBlock, Chunk {
|
|
||||||
private static final War3ID LAYS = War3ID.fromString("LAYS");
|
|
||||||
private int priorityPlane = 0;
|
|
||||||
private int flags;
|
|
||||||
private final List<Layer> layers = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
ParseUtils.readUInt32(stream); // Don't care about the size
|
|
||||||
|
|
||||||
this.priorityPlane = stream.readInt();// ParseUtils.readUInt32(stream);
|
|
||||||
this.flags = stream.readInt();// ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
stream.readInt(); // skip LAYS
|
|
||||||
|
|
||||||
final long layerCount = ParseUtils.readUInt32(stream);
|
|
||||||
for (int i = 0; i < layerCount; i++) {
|
|
||||||
final Layer layer = new Layer();
|
|
||||||
layer.readMdx(stream);
|
|
||||||
this.layers.add(layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, getByteLength());
|
|
||||||
stream.writeInt(this.priorityPlane); // was UInt32 in JS, but I *really* thought I used -1 in a past model
|
|
||||||
stream.writeInt(this.flags); // UInt32 in JS
|
|
||||||
ParseUtils.writeWar3ID(stream, LAYS);
|
|
||||||
ParseUtils.writeUInt32(stream, this.layers.size());
|
|
||||||
|
|
||||||
for (final Layer layer : this.layers) {
|
|
||||||
layer.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_CONSTANT_COLOR:
|
|
||||||
this.flags |= 0x1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SORT_PRIMS_NEAR_Z:
|
|
||||||
this.flags |= 0x8;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SORT_PRIMS_FAR_Z:
|
|
||||||
this.flags |= 0x10;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_FULL_RESOLUTION:
|
|
||||||
this.flags |= 0x20;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_PRIORITY_PLANE:
|
|
||||||
this.priorityPlane = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_LAYER: {
|
|
||||||
final Layer layer = new Layer();
|
|
||||||
layer.readMdl(stream);
|
|
||||||
this.layers.add(layer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Material: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_MATERIAL);
|
|
||||||
|
|
||||||
if ((this.flags & 0x1) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_CONSTANT_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x8) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_SORT_PRIMS_NEAR_Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x10) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_SORT_PRIMS_FAR_Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x20) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_FULL_RESOLUTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.priorityPlane != 0) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_PRIORITY_PLANE, this.priorityPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Layer layer : this.layers) {
|
|
||||||
layer.writeMdl(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
long size = 20;
|
|
||||||
|
|
||||||
for (final Layer layer : this.layers) {
|
|
||||||
size += layer.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPriorityPlane() {
|
|
||||||
return this.priorityPlane;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPriorityPlane(final int priorityPlane) {
|
|
||||||
this.priorityPlane = priorityPlane;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return this.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlags(final int flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Layer> getLayers() {
|
|
||||||
return this.layers;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
public interface MdlTokenInputStream {
|
|
||||||
String read();
|
|
||||||
|
|
||||||
long readUInt32();
|
|
||||||
|
|
||||||
int readInt();
|
|
||||||
|
|
||||||
float readFloat();
|
|
||||||
|
|
||||||
void readIntArray(long[] values);
|
|
||||||
|
|
||||||
float[] readFloatArray(float[] values); // is this same as read keyframe???
|
|
||||||
|
|
||||||
void readKeyframe(float[] values);
|
|
||||||
|
|
||||||
float[] readVectorArray(float[] array, int vectorLength);
|
|
||||||
|
|
||||||
int[] readUInt16Array(int[] values);
|
|
||||||
|
|
||||||
short[] readUInt8Array(short[] values);
|
|
||||||
|
|
||||||
String peek();
|
|
||||||
|
|
||||||
// needs crazy generator function behavior that I can call this multiple times
|
|
||||||
// and it allocates a new iterator that is changing the same underlying
|
|
||||||
// stream position, and needs nesting of blocks within blocks
|
|
||||||
// (see crazy transcribed generator in GenericObject, only makes good sense
|
|
||||||
// in JS)
|
|
||||||
Iterable<String> readBlock();
|
|
||||||
|
|
||||||
void readColor(float[] color);
|
|
||||||
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
public interface MdlTokenOutputStream {
|
|
||||||
void writeKeyframe(String prefix, long uInt32Value);
|
|
||||||
|
|
||||||
void writeKeyframe(String prefix, float floatValue);
|
|
||||||
|
|
||||||
void writeKeyframe(String prefix, float[] floatArrayValues);
|
|
||||||
|
|
||||||
void indent();
|
|
||||||
|
|
||||||
void unindent();
|
|
||||||
|
|
||||||
void startObjectBlock(String name, String objectName);
|
|
||||||
|
|
||||||
void startBlock(String name, int blockSize);
|
|
||||||
|
|
||||||
void startBlock(String name);
|
|
||||||
|
|
||||||
void writeFlag(String token);
|
|
||||||
|
|
||||||
void writeFlagUInt32(long flag);
|
|
||||||
|
|
||||||
void writeAttrib(String string, int globalSequenceId);
|
|
||||||
|
|
||||||
void writeAttribUInt32(String attribName, long uInt);
|
|
||||||
|
|
||||||
void writeAttrib(String string, String value);
|
|
||||||
|
|
||||||
void writeFloatAttrib(String attribName, float value);
|
|
||||||
|
|
||||||
// if this matches writeAttrib(String,String),
|
|
||||||
// then remove it
|
|
||||||
void writeStringAttrib(String attribName, String value);
|
|
||||||
|
|
||||||
void writeFloatArrayAttrib(String attribName, float[] floatArray);
|
|
||||||
|
|
||||||
void writeLongSubArrayAttrib(String attribName, long[] array, int startIndexInclusive, int endIndexExclusive);
|
|
||||||
|
|
||||||
void writeFloatArray(float[] floatArray);
|
|
||||||
|
|
||||||
void writeVectorArray(String token, float[] vectors, int vectorLength);
|
|
||||||
|
|
||||||
void endBlock();
|
|
||||||
|
|
||||||
void endBlockComma();
|
|
||||||
|
|
||||||
void writeLine(String string);
|
|
||||||
|
|
||||||
void startBlock(String tokenFaces, int sizeNumberProbably, int length);
|
|
||||||
|
|
||||||
void writeColor(String tokenStaticColor, float[] color);
|
|
||||||
|
|
||||||
void writeArrayAttrib(String tokenAlpha, short[] uint8Array);
|
|
||||||
|
|
||||||
void writeArrayAttrib(String tokenAlpha, int[] uint16Array);
|
|
||||||
|
|
||||||
void writeArrayAttrib(String tokenAlpha, long[] uint32Array);
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public interface MdlxBlock {
|
|
||||||
void readMdx(final LittleEndianDataInputStream stream) throws IOException;
|
|
||||||
|
|
||||||
void writeMdx(final LittleEndianDataOutputStream stream) throws IOException;
|
|
||||||
|
|
||||||
void readMdl(final MdlTokenInputStream stream) throws IOException;
|
|
||||||
|
|
||||||
void writeMdl(final MdlTokenOutputStream stream) throws IOException;
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.Descriptor;
|
|
||||||
|
|
||||||
public interface MdlxBlockDescriptor<E> extends Descriptor<E> {
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Attachment> ATTACHMENT = new MdlxBlockDescriptor<Attachment>() {
|
|
||||||
@Override
|
|
||||||
public Attachment create() {
|
|
||||||
return new Attachment();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Bone> BONE = new MdlxBlockDescriptor<Bone>() {
|
|
||||||
@Override
|
|
||||||
public Bone create() {
|
|
||||||
return new Bone();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Camera> CAMERA = new MdlxBlockDescriptor<Camera>() {
|
|
||||||
@Override
|
|
||||||
public Camera create() {
|
|
||||||
return new Camera();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<CollisionShape> COLLISION_SHAPE = new MdlxBlockDescriptor<CollisionShape>() {
|
|
||||||
@Override
|
|
||||||
public CollisionShape create() {
|
|
||||||
return new CollisionShape();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<EventObject> EVENT_OBJECT = new MdlxBlockDescriptor<EventObject>() {
|
|
||||||
@Override
|
|
||||||
public EventObject create() {
|
|
||||||
return new EventObject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Geoset> GEOSET = new MdlxBlockDescriptor<Geoset>() {
|
|
||||||
@Override
|
|
||||||
public Geoset create() {
|
|
||||||
return new Geoset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<GeosetAnimation> GEOSET_ANIMATION = new MdlxBlockDescriptor<GeosetAnimation>() {
|
|
||||||
@Override
|
|
||||||
public GeosetAnimation create() {
|
|
||||||
return new GeosetAnimation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Helper> HELPER = new MdlxBlockDescriptor<Helper>() {
|
|
||||||
@Override
|
|
||||||
public Helper create() {
|
|
||||||
return new Helper();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Light> LIGHT = new MdlxBlockDescriptor<Light>() {
|
|
||||||
@Override
|
|
||||||
public Light create() {
|
|
||||||
return new Light();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Layer> LAYER = new MdlxBlockDescriptor<Layer>() {
|
|
||||||
@Override
|
|
||||||
public Layer create() {
|
|
||||||
return new Layer();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Material> MATERIAL = new MdlxBlockDescriptor<Material>() {
|
|
||||||
@Override
|
|
||||||
public Material create() {
|
|
||||||
return new Material();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<ParticleEmitter> PARTICLE_EMITTER = new MdlxBlockDescriptor<ParticleEmitter>() {
|
|
||||||
@Override
|
|
||||||
public ParticleEmitter create() {
|
|
||||||
return new ParticleEmitter();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<ParticleEmitter2> PARTICLE_EMITTER2 = new MdlxBlockDescriptor<ParticleEmitter2>() {
|
|
||||||
@Override
|
|
||||||
public ParticleEmitter2 create() {
|
|
||||||
return new ParticleEmitter2();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<RibbonEmitter> RIBBON_EMITTER = new MdlxBlockDescriptor<RibbonEmitter>() {
|
|
||||||
@Override
|
|
||||||
public RibbonEmitter create() {
|
|
||||||
return new RibbonEmitter();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Sequence> SEQUENCE = new MdlxBlockDescriptor<Sequence>() {
|
|
||||||
@Override
|
|
||||||
public Sequence create() {
|
|
||||||
return new Sequence();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<Texture> TEXTURE = new MdlxBlockDescriptor<Texture>() {
|
|
||||||
@Override
|
|
||||||
public Texture create() {
|
|
||||||
return new Texture();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MdlxBlockDescriptor<TextureAnimation> TEXTURE_ANIMATION = new MdlxBlockDescriptor<TextureAnimation>() {
|
|
||||||
@Override
|
|
||||||
public TextureAnimation create() {
|
|
||||||
return new TextureAnimation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,744 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.StreamUtils;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.mdl.GhostwolfTokenInputStream;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.mdl.GhostwolfTokenOutputStream;
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Warcraft 3 model. Supports loading from and saving to both the binary MDX
|
|
||||||
* and text MDL file formats.
|
|
||||||
*/
|
|
||||||
public class MdlxModel {
|
|
||||||
// Below, these can't call a function on a string to make their value
|
|
||||||
// because
|
|
||||||
// switch/case statements require the value to be compile-time defined in
|
|
||||||
// order
|
|
||||||
// to be legal, and it appears to only allow basic binary operators for
|
|
||||||
// that.
|
|
||||||
// I would love a clearer way to just type 'MDLX' in a character constant in
|
|
||||||
// Java for this
|
|
||||||
private static final int MDLX = ('M' << 24) | ('D' << 16) | ('L' << 8) | ('X');// War3ID.fromString("MDLX").getValue();
|
|
||||||
private static final int VERS = ('V' << 24) | ('E' << 16) | ('R' << 8) | ('S');// War3ID.fromString("VERS").getValue();
|
|
||||||
private static final int MODL = ('M' << 24) | ('O' << 16) | ('D' << 8) | ('L');// War3ID.fromString("MODL").getValue();
|
|
||||||
private static final int SEQS = ('S' << 24) | ('E' << 16) | ('Q' << 8) | ('S');// War3ID.fromString("SEQS").getValue();
|
|
||||||
private static final int GLBS = ('G' << 24) | ('L' << 16) | ('B' << 8) | ('S');// War3ID.fromString("GLBS").getValue();
|
|
||||||
private static final int MTLS = ('M' << 24) | ('T' << 16) | ('L' << 8) | ('S');// War3ID.fromString("MTLS").getValue();
|
|
||||||
private static final int TEXS = ('T' << 24) | ('E' << 16) | ('X' << 8) | ('S');// War3ID.fromString("TEXS").getValue();
|
|
||||||
private static final int TXAN = ('T' << 24) | ('X' << 16) | ('A' << 8) | ('N');// War3ID.fromString("TXAN").getValue();
|
|
||||||
private static final int GEOS = ('G' << 24) | ('E' << 16) | ('O' << 8) | ('S');// War3ID.fromString("GEOS").getValue();
|
|
||||||
private static final int GEOA = ('G' << 24) | ('E' << 16) | ('O' << 8) | ('A');// War3ID.fromString("GEOA").getValue();
|
|
||||||
private static final int BONE = ('B' << 24) | ('O' << 16) | ('N' << 8) | ('E');// War3ID.fromString("BONE").getValue();
|
|
||||||
private static final int LITE = ('L' << 24) | ('I' << 16) | ('T' << 8) | ('E');// War3ID.fromString("LITE").getValue();
|
|
||||||
private static final int HELP = ('H' << 24) | ('E' << 16) | ('L' << 8) | ('P');// War3ID.fromString("HELP").getValue();
|
|
||||||
private static final int ATCH = ('A' << 24) | ('T' << 16) | ('C' << 8) | ('H');// War3ID.fromString("ATCH").getValue();
|
|
||||||
private static final int PIVT = ('P' << 24) | ('I' << 16) | ('V' << 8) | ('T');// War3ID.fromString("PIVT").getValue();
|
|
||||||
private static final int PREM = ('P' << 24) | ('R' << 16) | ('E' << 8) | ('M');// War3ID.fromString("PREM").getValue();
|
|
||||||
private static final int PRE2 = ('P' << 24) | ('R' << 16) | ('E' << 8) | ('2');// War3ID.fromString("PRE2").getValue();
|
|
||||||
private static final int RIBB = ('R' << 24) | ('I' << 16) | ('B' << 8) | ('B');// War3ID.fromString("RIBB").getValue();
|
|
||||||
private static final int CAMS = ('C' << 24) | ('A' << 16) | ('M' << 8) | ('S');// War3ID.fromString("CAMS").getValue();
|
|
||||||
private static final int EVTS = ('E' << 24) | ('V' << 16) | ('T' << 8) | ('S');// War3ID.fromString("EVTS").getValue();
|
|
||||||
private static final int CLID = ('C' << 24) | ('L' << 16) | ('I' << 8) | ('D');// War3ID.fromString("CLID").getValue();
|
|
||||||
private int version = 800;
|
|
||||||
private String name = "";
|
|
||||||
/**
|
|
||||||
* (Comment copied from Ghostwolf JS) To the best of my knowledge, this should
|
|
||||||
* always be left empty. This is probably a leftover from the Warcraft 3 beta.
|
|
||||||
* (WS game note: No, I never saw any animation files in the RoC 2001-2002 Beta.
|
|
||||||
* So it must be from the Alpha)
|
|
||||||
*
|
|
||||||
* @member {string}
|
|
||||||
*/
|
|
||||||
private String animationFile = "";
|
|
||||||
private final Extent extent = new Extent();
|
|
||||||
private long blendTime = 0;
|
|
||||||
private final List<Sequence> sequences = new ArrayList<Sequence>();
|
|
||||||
private final List<Long /* UInt32 */> globalSequences = new ArrayList<>();
|
|
||||||
private final List<Material> materials = new ArrayList<>();
|
|
||||||
private final List<Texture> textures = new ArrayList<>();
|
|
||||||
private final List<TextureAnimation> textureAnimations = new ArrayList<>();
|
|
||||||
private final List<Geoset> geosets = new ArrayList<>();
|
|
||||||
private final List<GeosetAnimation> geosetAnimations = new ArrayList<>();
|
|
||||||
private final List<Bone> bones = new ArrayList<>();
|
|
||||||
private final List<Light> lights = new ArrayList<>();
|
|
||||||
private final List<Helper> helpers = new ArrayList<>();
|
|
||||||
private final List<Attachment> attachments = new ArrayList<>();
|
|
||||||
private final List<float[]> pivotPoints = new ArrayList<>();
|
|
||||||
private final List<ParticleEmitter> particleEmitters = new ArrayList<>();
|
|
||||||
private final List<ParticleEmitter2> particleEmitters2 = new ArrayList<>();
|
|
||||||
private final List<RibbonEmitter> ribbonEmitters = new ArrayList<>();
|
|
||||||
private final List<Camera> cameras = new ArrayList<>();
|
|
||||||
private final List<EventObject> eventObjects = new ArrayList<>();
|
|
||||||
private final List<CollisionShape> collisionShapes = new ArrayList<>();
|
|
||||||
private final List<UnknownChunk> unknownChunks = new ArrayList<>();
|
|
||||||
|
|
||||||
public MdlxModel(final InputStream buffer) throws IOException {
|
|
||||||
if (buffer != null) {
|
|
||||||
// In ghostwolf JS, this function called load()
|
|
||||||
// which decided whether the buffer was an MDL.
|
|
||||||
loadMdx(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MdlxModel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMdx(final InputStream buffer) throws IOException {
|
|
||||||
final LittleEndianDataInputStream stream = new LittleEndianDataInputStream(buffer);
|
|
||||||
if (Integer.reverseBytes(stream.readInt()) != MDLX) {
|
|
||||||
throw new IllegalStateException("WrongMagicNumber");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (stream.available() > 0) {
|
|
||||||
final int tag = Integer.reverseBytes(stream.readInt());
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
|
|
||||||
switch (tag) {
|
|
||||||
case VERS:
|
|
||||||
loadVersionChunk(stream);
|
|
||||||
break;
|
|
||||||
case MODL:
|
|
||||||
loadModelChunk(stream);
|
|
||||||
break;
|
|
||||||
case SEQS:
|
|
||||||
loadStaticObjects(this.sequences, MdlxBlockDescriptor.SEQUENCE, stream, size / 132);
|
|
||||||
break;
|
|
||||||
case GLBS:
|
|
||||||
loadGlobalSequenceChunk(stream, size);
|
|
||||||
break;
|
|
||||||
case MTLS:
|
|
||||||
loadDynamicObjects(this.materials, MdlxBlockDescriptor.MATERIAL, stream, size);
|
|
||||||
break;
|
|
||||||
case TEXS:
|
|
||||||
loadStaticObjects(this.textures, MdlxBlockDescriptor.TEXTURE, stream, size / 268);
|
|
||||||
break;
|
|
||||||
case TXAN:
|
|
||||||
loadDynamicObjects(this.textureAnimations, MdlxBlockDescriptor.TEXTURE_ANIMATION, stream, size);
|
|
||||||
break;
|
|
||||||
case GEOS:
|
|
||||||
loadDynamicObjects(this.geosets, MdlxBlockDescriptor.GEOSET, stream, size);
|
|
||||||
break;
|
|
||||||
case GEOA:
|
|
||||||
loadDynamicObjects(this.geosetAnimations, MdlxBlockDescriptor.GEOSET_ANIMATION, stream, size);
|
|
||||||
break;
|
|
||||||
case BONE:
|
|
||||||
loadDynamicObjects(this.bones, MdlxBlockDescriptor.BONE, stream, size);
|
|
||||||
break;
|
|
||||||
case LITE:
|
|
||||||
loadDynamicObjects(this.lights, MdlxBlockDescriptor.LIGHT, stream, size);
|
|
||||||
break;
|
|
||||||
case HELP:
|
|
||||||
loadDynamicObjects(this.helpers, MdlxBlockDescriptor.HELPER, stream, size);
|
|
||||||
break;
|
|
||||||
case ATCH:
|
|
||||||
loadDynamicObjects(this.attachments, MdlxBlockDescriptor.ATTACHMENT, stream, size);
|
|
||||||
break;
|
|
||||||
case PIVT:
|
|
||||||
loadPivotPointChunk(stream, size);
|
|
||||||
break;
|
|
||||||
case PREM:
|
|
||||||
loadDynamicObjects(this.particleEmitters, MdlxBlockDescriptor.PARTICLE_EMITTER, stream, size);
|
|
||||||
break;
|
|
||||||
case PRE2:
|
|
||||||
loadDynamicObjects(this.particleEmitters2, MdlxBlockDescriptor.PARTICLE_EMITTER2, stream, size);
|
|
||||||
break;
|
|
||||||
case RIBB:
|
|
||||||
loadDynamicObjects(this.ribbonEmitters, MdlxBlockDescriptor.RIBBON_EMITTER, stream, size);
|
|
||||||
break;
|
|
||||||
case CAMS:
|
|
||||||
loadDynamicObjects(this.cameras, MdlxBlockDescriptor.CAMERA, stream, size);
|
|
||||||
break;
|
|
||||||
case EVTS:
|
|
||||||
loadDynamicObjects(this.eventObjects, MdlxBlockDescriptor.EVENT_OBJECT, stream, size);
|
|
||||||
break;
|
|
||||||
case CLID:
|
|
||||||
loadDynamicObjects(this.collisionShapes, MdlxBlockDescriptor.COLLISION_SHAPE, stream, size);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.unknownChunks.add(new UnknownChunk(stream, size, new War3ID(tag)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadVersionChunk(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
this.version = (int) ParseUtils.readUInt32(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] NAME_BYTES_HEAP = new byte[80];
|
|
||||||
private static final byte[] ANIMATION_FILE_BYTES_HEAP = new byte[260];
|
|
||||||
|
|
||||||
private void loadModelChunk(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
this.name = ParseUtils.readString(stream, NAME_BYTES_HEAP);
|
|
||||||
this.animationFile = ParseUtils.readString(stream, ANIMATION_FILE_BYTES_HEAP);
|
|
||||||
this.extent.readMdx(stream);
|
|
||||||
this.blendTime = ParseUtils.readUInt32(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock> void loadStaticObjects(final List<E> out, final MdlxBlockDescriptor<E> constructor,
|
|
||||||
final LittleEndianDataInputStream stream, final long count) throws IOException {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final E object = constructor.create();
|
|
||||||
|
|
||||||
object.readMdx(stream);
|
|
||||||
|
|
||||||
out.add(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadGlobalSequenceChunk(final LittleEndianDataInputStream stream, final long size) throws IOException {
|
|
||||||
for (long i = 0, l = size / 4; i < l; i++) {
|
|
||||||
this.globalSequences.add(ParseUtils.readUInt32(stream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock & Chunk> void loadDynamicObjects(final List<E> out,
|
|
||||||
final MdlxBlockDescriptor<E> constructor, final LittleEndianDataInputStream stream, final long size)
|
|
||||||
throws IOException {
|
|
||||||
long totalSize = 0;
|
|
||||||
while (totalSize < size) {
|
|
||||||
final E object = constructor.create();
|
|
||||||
|
|
||||||
object.readMdx(stream);
|
|
||||||
|
|
||||||
totalSize += object.getByteLength();
|
|
||||||
|
|
||||||
out.add(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPivotPointChunk(final LittleEndianDataInputStream stream, final long size) throws IOException {
|
|
||||||
for (long i = 0, l = size / 12; i < l; i++) {
|
|
||||||
this.pivotPoints.add(ParseUtils.readFloatArray(stream, 3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveMdx(final OutputStream outputStream) throws IOException {
|
|
||||||
final LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(outputStream);
|
|
||||||
stream.writeInt(Integer.reverseBytes(MDLX));
|
|
||||||
this.saveVersionChunk(stream);
|
|
||||||
this.saveModelChunk(stream);
|
|
||||||
this.saveStaticObjectChunk(stream, SEQS, this.sequences, 132);
|
|
||||||
this.saveGlobalSequenceChunk(stream);
|
|
||||||
this.saveDynamicObjectChunk(stream, MTLS, this.materials);
|
|
||||||
this.saveStaticObjectChunk(stream, TEXS, this.textures, 268);
|
|
||||||
this.saveDynamicObjectChunk(stream, TXAN, this.textureAnimations);
|
|
||||||
this.saveDynamicObjectChunk(stream, GEOS, this.geosets);
|
|
||||||
this.saveDynamicObjectChunk(stream, GEOA, this.geosetAnimations);
|
|
||||||
this.saveDynamicObjectChunk(stream, BONE, this.bones);
|
|
||||||
this.saveDynamicObjectChunk(stream, LITE, this.lights);
|
|
||||||
this.saveDynamicObjectChunk(stream, HELP, this.helpers);
|
|
||||||
this.saveDynamicObjectChunk(stream, ATCH, this.attachments);
|
|
||||||
this.savePivotPointChunk(stream);
|
|
||||||
this.saveDynamicObjectChunk(stream, PREM, this.particleEmitters);
|
|
||||||
this.saveDynamicObjectChunk(stream, PRE2, this.particleEmitters2);
|
|
||||||
this.saveDynamicObjectChunk(stream, RIBB, this.ribbonEmitters);
|
|
||||||
this.saveDynamicObjectChunk(stream, CAMS, this.cameras);
|
|
||||||
this.saveDynamicObjectChunk(stream, EVTS, this.eventObjects);
|
|
||||||
this.saveDynamicObjectChunk(stream, CLID, this.collisionShapes);
|
|
||||||
|
|
||||||
for (final UnknownChunk chunk : this.unknownChunks) {
|
|
||||||
chunk.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveVersionChunk(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
stream.writeInt(Integer.reverseBytes(VERS));
|
|
||||||
ParseUtils.writeUInt32(stream, 4);
|
|
||||||
ParseUtils.writeUInt32(stream, this.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveModelChunk(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
stream.writeInt(Integer.reverseBytes(MODL));
|
|
||||||
ParseUtils.writeUInt32(stream, 372);
|
|
||||||
final byte[] bytes = this.name.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (NAME_BYTES_HEAP.length - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
final byte[] animationFileBytes = this.animationFile.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(animationFileBytes);
|
|
||||||
for (int i = 0; i < (ANIMATION_FILE_BYTES_HEAP.length - animationFileBytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
this.extent.writeMdx(stream);
|
|
||||||
ParseUtils.writeUInt32(stream, this.blendTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock> void saveStaticObjectChunk(final LittleEndianDataOutputStream stream, final int name,
|
|
||||||
final List<E> objects, final long size) throws IOException {
|
|
||||||
if (!objects.isEmpty()) {
|
|
||||||
stream.writeInt(Integer.reverseBytes(name));
|
|
||||||
ParseUtils.writeUInt32(stream, objects.size() * size);
|
|
||||||
|
|
||||||
for (final E object : objects) {
|
|
||||||
object.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveGlobalSequenceChunk(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
if (!this.globalSequences.isEmpty()) {
|
|
||||||
stream.writeInt(Integer.reverseBytes(GLBS));
|
|
||||||
ParseUtils.writeUInt32(stream, this.globalSequences.size() * 4);
|
|
||||||
|
|
||||||
for (final Long globalSequence : this.globalSequences) {
|
|
||||||
ParseUtils.writeUInt32(stream, globalSequence);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock & Chunk> void saveDynamicObjectChunk(final LittleEndianDataOutputStream stream,
|
|
||||||
final int name, final List<E> objects) throws IOException {
|
|
||||||
if (!objects.isEmpty()) {
|
|
||||||
stream.writeInt(Integer.reverseBytes(name));
|
|
||||||
ParseUtils.writeUInt32(stream, getObjectsByteLength(objects));
|
|
||||||
|
|
||||||
for (final E object : objects) {
|
|
||||||
object.writeMdx(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void savePivotPointChunk(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
if (this.pivotPoints.size() > 0) {
|
|
||||||
stream.writeInt(Integer.reverseBytes(PIVT));
|
|
||||||
ParseUtils.writeUInt32(stream, this.pivotPoints.size() * 12);
|
|
||||||
|
|
||||||
for (final float[] pivotPoint : this.pivotPoints) {
|
|
||||||
ParseUtils.writeFloatArray(stream, pivotPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMdl(final InputStream inputStream) throws IOException {
|
|
||||||
final byte[] array = StreamUtils.copyStreamToByteArray(inputStream);
|
|
||||||
loadMdl(ByteBuffer.wrap(array));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMdl(final ByteBuffer inputStream) throws IOException {
|
|
||||||
String token;
|
|
||||||
final MdlTokenInputStream stream = new GhostwolfTokenInputStream(inputStream);
|
|
||||||
|
|
||||||
while ((token = stream.read()) != null) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_VERSION:
|
|
||||||
this.loadVersionBlock(stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MODEL:
|
|
||||||
this.loadModelBlock(stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SEQUENCES:
|
|
||||||
this.loadNumberedObjectBlock(this.sequences, MdlxBlockDescriptor.SEQUENCE, MdlUtils.TOKEN_ANIM, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_GLOBAL_SEQUENCES:
|
|
||||||
this.loadGlobalSequenceBlock(stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TEXTURES:
|
|
||||||
this.loadNumberedObjectBlock(this.textures, MdlxBlockDescriptor.TEXTURE, MdlUtils.TOKEN_BITMAP, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MATERIALS:
|
|
||||||
this.loadNumberedObjectBlock(this.materials, MdlxBlockDescriptor.MATERIAL, MdlUtils.TOKEN_MATERIAL,
|
|
||||||
stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_TEXTURE_ANIMS:
|
|
||||||
this.loadNumberedObjectBlock(this.textureAnimations, MdlxBlockDescriptor.TEXTURE_ANIMATION,
|
|
||||||
MdlUtils.TOKEN_TVERTEX_ANIM, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_GEOSET:
|
|
||||||
this.loadObject(this.geosets, MdlxBlockDescriptor.GEOSET, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_GEOSETANIM:
|
|
||||||
this.loadObject(this.geosetAnimations, MdlxBlockDescriptor.GEOSET_ANIMATION, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BONE:
|
|
||||||
this.loadObject(this.bones, MdlxBlockDescriptor.BONE, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_LIGHT:
|
|
||||||
this.loadObject(this.lights, MdlxBlockDescriptor.LIGHT, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_HELPER:
|
|
||||||
this.loadObject(this.helpers, MdlxBlockDescriptor.HELPER, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ATTACHMENT:
|
|
||||||
this.loadObject(this.attachments, MdlxBlockDescriptor.ATTACHMENT, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_PIVOT_POINTS:
|
|
||||||
this.loadPivotPointBlock(stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_PARTICLE_EMITTER:
|
|
||||||
this.loadObject(this.particleEmitters, MdlxBlockDescriptor.PARTICLE_EMITTER, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_PARTICLE_EMITTER2:
|
|
||||||
this.loadObject(this.particleEmitters2, MdlxBlockDescriptor.PARTICLE_EMITTER2, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_RIBBON_EMITTER:
|
|
||||||
this.loadObject(this.ribbonEmitters, MdlxBlockDescriptor.RIBBON_EMITTER, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_CAMERA:
|
|
||||||
this.loadObject(this.cameras, MdlxBlockDescriptor.CAMERA, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_EVENT_OBJECT:
|
|
||||||
this.loadObject(this.eventObjects, MdlxBlockDescriptor.EVENT_OBJECT, stream);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_COLLISION_SHAPE:
|
|
||||||
this.loadObject(this.collisionShapes, MdlxBlockDescriptor.COLLISION_SHAPE, stream);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unsupported block: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadVersionBlock(final MdlTokenInputStream stream) {
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_FORMAT_VERSION:
|
|
||||||
this.version = stream.readInt();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Version: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadModelBlock(final MdlTokenInputStream stream) {
|
|
||||||
this.name = stream.read();
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
if (token.startsWith("Num")) {
|
|
||||||
/*-
|
|
||||||
* Don't care about the number of things, the arrays will grow as they wish.
|
|
||||||
* This includes:
|
|
||||||
* NumGeosets
|
|
||||||
* NumGeosetAnims
|
|
||||||
* NumHelpers
|
|
||||||
* NumLights
|
|
||||||
* NumBones
|
|
||||||
* NumAttachments
|
|
||||||
* NumParticleEmitters
|
|
||||||
* NumParticleEmitters2
|
|
||||||
* NumRibbonEmitters
|
|
||||||
* NumEvents
|
|
||||||
*/
|
|
||||||
stream.read();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_BLEND_TIME:
|
|
||||||
this.blendTime = stream.readUInt32();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MINIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.min);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MAXIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.max);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BOUNDSRADIUS:
|
|
||||||
this.extent.boundsRadius = stream.readFloat();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Model: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock> void loadNumberedObjectBlock(final List<E> out,
|
|
||||||
final MdlxBlockDescriptor<E> constructor, final String name, final MdlTokenInputStream stream)
|
|
||||||
throws IOException {
|
|
||||||
stream.read(); // Don't care about the number, the array will grow
|
|
||||||
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
if (token.equals(name)) {
|
|
||||||
final E object = constructor.create();
|
|
||||||
|
|
||||||
object.readMdl(stream);
|
|
||||||
|
|
||||||
out.add(object);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Unknown token in " + name + ": " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadGlobalSequenceBlock(final MdlTokenInputStream stream) {
|
|
||||||
stream.read(); // Don't care about the number, the array will grow
|
|
||||||
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
if (token.equals(MdlUtils.TOKEN_DURATION)) {
|
|
||||||
this.globalSequences.add(stream.readUInt32());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Unknown token in GlobalSequences: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends MdlxBlock> void loadObject(final List<E> out, final MdlxBlockDescriptor<E> descriptor,
|
|
||||||
final MdlTokenInputStream stream) throws IOException {
|
|
||||||
final E object = descriptor.create();
|
|
||||||
|
|
||||||
object.readMdl(stream);
|
|
||||||
|
|
||||||
out.add(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPivotPointBlock(final MdlTokenInputStream stream) {
|
|
||||||
final int count = stream.readInt();
|
|
||||||
|
|
||||||
stream.read(); // {
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
this.pivotPoints.add(stream.readFloatArray(new float[3]));
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.read(); // }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveMdl(final OutputStream outputStream) throws IOException {
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {
|
|
||||||
final MdlTokenOutputStream stream = new GhostwolfTokenOutputStream(writer);
|
|
||||||
this.saveVersionBlock(stream);
|
|
||||||
this.saveModelBlock(stream);
|
|
||||||
this.saveStaticObjectsBlock(stream, MdlUtils.TOKEN_SEQUENCES, this.sequences);
|
|
||||||
this.saveGlobalSequenceBlock(stream);
|
|
||||||
this.saveStaticObjectsBlock(stream, MdlUtils.TOKEN_TEXTURES, this.textures);
|
|
||||||
this.saveStaticObjectsBlock(stream, MdlUtils.TOKEN_MATERIALS, this.materials);
|
|
||||||
this.saveStaticObjectsBlock(stream, MdlUtils.TOKEN_TEXTURE_ANIMS, this.textureAnimations);
|
|
||||||
this.saveObjects(stream, this.geosets);
|
|
||||||
this.saveObjects(stream, this.geosetAnimations);
|
|
||||||
this.saveObjects(stream, this.bones);
|
|
||||||
this.saveObjects(stream, this.lights);
|
|
||||||
this.saveObjects(stream, this.helpers);
|
|
||||||
this.saveObjects(stream, this.attachments);
|
|
||||||
this.savePivotPointBlock(stream);
|
|
||||||
this.saveObjects(stream, this.particleEmitters);
|
|
||||||
this.saveObjects(stream, this.particleEmitters2);
|
|
||||||
this.saveObjects(stream, this.ribbonEmitters);
|
|
||||||
this.saveObjects(stream, this.cameras);
|
|
||||||
this.saveObjects(stream, this.eventObjects);
|
|
||||||
this.saveObjects(stream, this.collisionShapes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveVersionBlock(final MdlTokenOutputStream stream) {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_VERSION);
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_FORMAT_VERSION, this.version);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveModelBlock(final MdlTokenOutputStream stream) {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_MODEL, this.name);
|
|
||||||
stream.writeAttribUInt32(MdlUtils.TOKEN_BLEND_TIME, this.blendTime);
|
|
||||||
this.extent.writeMdl(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveStaticObjectsBlock(final MdlTokenOutputStream stream, final String name,
|
|
||||||
final List<? extends MdlxBlock> objects) throws IOException {
|
|
||||||
if (!objects.isEmpty()) {
|
|
||||||
stream.startBlock(name, objects.size());
|
|
||||||
|
|
||||||
for (final MdlxBlock object : objects) {
|
|
||||||
object.writeMdl(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveGlobalSequenceBlock(final MdlTokenOutputStream stream) {
|
|
||||||
if (!this.globalSequences.isEmpty()) {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_GLOBAL_SEQUENCES, this.globalSequences.size());
|
|
||||||
|
|
||||||
for (final Long globalSequence : this.globalSequences) {
|
|
||||||
stream.writeAttribUInt32(MdlUtils.TOKEN_DURATION, globalSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveObjects(final MdlTokenOutputStream stream, final List<? extends MdlxBlock> objects)
|
|
||||||
throws IOException {
|
|
||||||
for (final MdlxBlock object : objects) {
|
|
||||||
object.writeMdl(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void savePivotPointBlock(final MdlTokenOutputStream stream) {
|
|
||||||
if (!this.pivotPoints.isEmpty()) {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_PIVOT_POINTS, this.pivotPoints.size());
|
|
||||||
|
|
||||||
for (final float[] pivotPoint : this.pivotPoints) {
|
|
||||||
stream.writeFloatArray(pivotPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getByteLength() {
|
|
||||||
long size = 396;
|
|
||||||
|
|
||||||
size += getStaticObjectsChunkByteLength(this.sequences, 132);
|
|
||||||
size += this.getStaticObjectsChunkByteLength(this.globalSequences, 4);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.materials);
|
|
||||||
size += this.getStaticObjectsChunkByteLength(this.textures, 268);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.textureAnimations);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.geosets);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.geosetAnimations);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.bones);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.lights);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.helpers);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.attachments);
|
|
||||||
size += this.getStaticObjectsChunkByteLength(this.pivotPoints, 12);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.particleEmitters);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.particleEmitters2);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.ribbonEmitters);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.cameras);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.eventObjects);
|
|
||||||
size += this.getDynamicObjectsChunkByteLength(this.collisionShapes);
|
|
||||||
size += this.getObjectsByteLength(this.unknownChunks);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends Chunk> long getObjectsByteLength(final List<E> objects) {
|
|
||||||
long size = 0;
|
|
||||||
for (final E object : objects) {
|
|
||||||
size += object.getByteLength();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends Chunk> long getDynamicObjectsChunkByteLength(final List<E> objects) {
|
|
||||||
if (!objects.isEmpty()) {
|
|
||||||
return 8 + this.getObjectsByteLength(objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E> long getStaticObjectsChunkByteLength(final List<E> objects, final long size) {
|
|
||||||
if (!objects.isEmpty()) {
|
|
||||||
return 8 + (objects.size() * size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Long> getGlobalSequences() {
|
|
||||||
return this.globalSequences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Sequence> getSequences() {
|
|
||||||
return this.sequences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<float[]> getPivotPoints() {
|
|
||||||
return this.pivotPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVersion() {
|
|
||||||
return this.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAnimationFile() {
|
|
||||||
return this.animationFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Extent getExtent() {
|
|
||||||
return this.extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getBlendTime() {
|
|
||||||
return this.blendTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Material> getMaterials() {
|
|
||||||
return this.materials;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Texture> getTextures() {
|
|
||||||
return this.textures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TextureAnimation> getTextureAnimations() {
|
|
||||||
return this.textureAnimations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Geoset> getGeosets() {
|
|
||||||
return this.geosets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<GeosetAnimation> getGeosetAnimations() {
|
|
||||||
return this.geosetAnimations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Bone> getBones() {
|
|
||||||
return this.bones;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Light> getLights() {
|
|
||||||
return this.lights;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Helper> getHelpers() {
|
|
||||||
return this.helpers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Attachment> getAttachments() {
|
|
||||||
return this.attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ParticleEmitter> getParticleEmitters() {
|
|
||||||
return this.particleEmitters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ParticleEmitter2> getParticleEmitters2() {
|
|
||||||
return this.particleEmitters2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RibbonEmitter> getRibbonEmitters() {
|
|
||||||
return this.ribbonEmitters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Camera> getCameras() {
|
|
||||||
return this.cameras;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EventObject> getEventObjects() {
|
|
||||||
return this.eventObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CollisionShape> getCollisionShapes() {
|
|
||||||
return this.collisionShapes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<UnknownChunk> getUnknownChunks() {
|
|
||||||
return this.unknownChunks;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class MdlxTest {
|
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
|
||||||
try (FileInputStream stream = new FileInputStream(
|
|
||||||
new File("C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\ArcaneEpic13.mdx"))) {
|
|
||||||
final MdlxModel model = new MdlxModel(stream);
|
|
||||||
try (FileOutputStream mdlStream = new FileOutputStream(new File(
|
|
||||||
"C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\Test\\MyOutAutomated.mdl"))) {
|
|
||||||
|
|
||||||
model.saveMdl(mdlStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (final FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Created MDL, now reparsing to MDX");
|
|
||||||
|
|
||||||
try (FileInputStream stream = new FileInputStream(
|
|
||||||
new File("C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\Test\\MyOutAutomated.mdl"))) {
|
|
||||||
final MdlxModel model = new MdlxModel(null);
|
|
||||||
model.loadMdl(stream);
|
|
||||||
try (FileOutputStream mdlStream = new FileOutputStream(new File(
|
|
||||||
"C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\Test\\MyOutAutomatedMDX.mdx"))) {
|
|
||||||
|
|
||||||
model.saveMdx(mdlStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (final FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
try (FileInputStream stream = new FileInputStream(
|
|
||||||
new File("C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\Test\\MyOutAutomatedMDX.mdx"))) {
|
|
||||||
final MdlxModel model = new MdlxModel(stream);
|
|
||||||
try (FileOutputStream mdlStream = new FileOutputStream(new File(
|
|
||||||
"C:\\Users\\micro\\OneDrive\\Documents\\Warcraft III\\Models\\Test\\MyOutAutomatedMDXBack2MDL.mdl"))) {
|
|
||||||
|
|
||||||
model.saveMdl(mdlStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (final FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Sequence implements MdlxBlock {
|
|
||||||
private String name = "";
|
|
||||||
private final long[] interval = new long[2];
|
|
||||||
private float moveSpeed = 0;
|
|
||||||
private int flags = 0;
|
|
||||||
private float rarity = 0;
|
|
||||||
private long syncPoint = 0;
|
|
||||||
private final Extent extent = new Extent();
|
|
||||||
private final EnumSet<AnimationTokens.PrimaryTag> primaryTags = EnumSet.noneOf(AnimationTokens.PrimaryTag.class);
|
|
||||||
private final EnumSet<AnimationTokens.SecondaryTag> secondaryTags = EnumSet
|
|
||||||
.noneOf(AnimationTokens.SecondaryTag.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] NAME_BYTES_HEAP = new byte[80];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
this.name = ParseUtils.readString(stream, NAME_BYTES_HEAP);
|
|
||||||
ParseUtils.readUInt32Array(stream, this.interval);
|
|
||||||
this.moveSpeed = stream.readFloat();
|
|
||||||
this.flags = (int) ParseUtils.readUInt32(stream);
|
|
||||||
this.rarity = stream.readFloat();
|
|
||||||
this.syncPoint = ParseUtils.readUInt32(stream);
|
|
||||||
this.extent.readMdx(stream);
|
|
||||||
populateTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
final byte[] bytes = this.name.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (NAME_BYTES_HEAP.length - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
ParseUtils.writeUInt32Array(stream, this.interval);
|
|
||||||
stream.writeFloat(this.moveSpeed);
|
|
||||||
ParseUtils.writeUInt32(stream, this.flags);
|
|
||||||
stream.writeFloat(this.rarity);
|
|
||||||
ParseUtils.writeUInt32(stream, this.syncPoint);
|
|
||||||
this.extent.writeMdx(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) {
|
|
||||||
this.name = stream.read();
|
|
||||||
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_INTERVAL:
|
|
||||||
stream.readIntArray(this.interval);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_NONLOOPING:
|
|
||||||
this.flags = 1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MOVESPEED:
|
|
||||||
this.moveSpeed = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_RARITY:
|
|
||||||
this.rarity = stream.readFloat();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MINIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.min);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_MAXIMUM_EXTENT:
|
|
||||||
stream.readFloatArray(this.extent.max);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_BOUNDSRADIUS:
|
|
||||||
this.extent.boundsRadius = stream.readFloat();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Sequence \"" + this.name + "\": " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) {
|
|
||||||
stream.startObjectBlock(MdlUtils.TOKEN_ANIM, this.name);
|
|
||||||
stream.writeArrayAttrib(MdlUtils.TOKEN_INTERVAL, this.interval);
|
|
||||||
|
|
||||||
if (this.flags == 1) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_NONLOOPING);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.moveSpeed != 0) {
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_MOVESPEED, this.moveSpeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.rarity != 0) {
|
|
||||||
stream.writeFloatAttrib(MdlUtils.TOKEN_RARITY, this.rarity);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extent.writeMdl(stream);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populateTags() {
|
|
||||||
this.primaryTags.clear();
|
|
||||||
this.secondaryTags.clear();
|
|
||||||
TokenLoop: for (final String token : this.name.split("\\s+")) {
|
|
||||||
final String upperCaseToken = token.toUpperCase();
|
|
||||||
for (final PrimaryTag primaryTag : PrimaryTag.values()) {
|
|
||||||
if (upperCaseToken.equals(primaryTag.name())) {
|
|
||||||
this.primaryTags.add(primaryTag);
|
|
||||||
continue TokenLoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
|
|
||||||
if (upperCaseToken.equals(secondaryTag.name())) {
|
|
||||||
this.secondaryTags.add(secondaryTag);
|
|
||||||
continue TokenLoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getInterval() {
|
|
||||||
return this.interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return this.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getRarity() {
|
|
||||||
return this.rarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getMoveSpeed() {
|
|
||||||
return this.moveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSyncPoint() {
|
|
||||||
return this.syncPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Extent getExtent() {
|
|
||||||
return this.extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnumSet<AnimationTokens.PrimaryTag> getPrimaryTags() {
|
|
||||||
return this.primaryTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnumSet<AnimationTokens.SecondaryTag> getSecondaryTags() {
|
|
||||||
return this.secondaryTags;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class Texture implements MdlxBlock {
|
|
||||||
private int replaceableId = 0;
|
|
||||||
private String path = "";
|
|
||||||
private int flags = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restricts us to only be able to parse models on one thread at a time, in
|
|
||||||
* return for high performance.
|
|
||||||
*/
|
|
||||||
private static final byte[] PATH_BYTES_HEAP = new byte[260];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
this.replaceableId = (int) ParseUtils.readUInt32(stream);
|
|
||||||
this.path = ParseUtils.readString(stream, PATH_BYTES_HEAP);
|
|
||||||
this.flags = (int) ParseUtils.readUInt32(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, this.replaceableId);
|
|
||||||
final byte[] bytes = this.path.getBytes(ParseUtils.UTF8);
|
|
||||||
stream.write(bytes);
|
|
||||||
for (int i = 0; i < (PATH_BYTES_HEAP.length - bytes.length); i++) {
|
|
||||||
stream.write((byte) 0);
|
|
||||||
}
|
|
||||||
ParseUtils.writeUInt32(stream, this.flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_IMAGE:
|
|
||||||
this.path = stream.read();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_REPLACEABLE_ID:
|
|
||||||
this.replaceableId = stream.readInt();
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_WRAP_WIDTH:
|
|
||||||
this.flags |= 0x1;
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_WRAP_HEIGHT:
|
|
||||||
this.flags |= 0x2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in Texture: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_BITMAP);
|
|
||||||
stream.writeStringAttrib(MdlUtils.TOKEN_IMAGE, this.path);
|
|
||||||
|
|
||||||
if (this.replaceableId != 0) {
|
|
||||||
stream.writeAttrib(MdlUtils.TOKEN_REPLACEABLE_ID, this.replaceableId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x1) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_WRAP_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.flags & 0x2) != 0) {
|
|
||||||
stream.writeFlag(MdlUtils.TOKEN_WRAP_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getReplaceableId() {
|
|
||||||
return this.replaceableId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath() {
|
|
||||||
return this.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return this.flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReplaceableId(final int replaceableId) {
|
|
||||||
this.replaceableId = replaceableId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPath(final String path) {
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlags(final int flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.MdlUtils;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class TextureAnimation extends AnimatedObject {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdx(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
final long size = ParseUtils.readUInt32(stream);
|
|
||||||
this.readTimelines(stream, size - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, this.getByteLength());
|
|
||||||
this.writeTimelines(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readMdl(final MdlTokenInputStream stream) throws IOException {
|
|
||||||
for (final String token : stream.readBlock()) {
|
|
||||||
switch (token) {
|
|
||||||
case MdlUtils.TOKEN_TRANSLATION:
|
|
||||||
this.readTimeline(stream, AnimationMap.KTAT);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_ROTATION:
|
|
||||||
this.readTimeline(stream, AnimationMap.KTAR);
|
|
||||||
break;
|
|
||||||
case MdlUtils.TOKEN_SCALING:
|
|
||||||
this.readTimeline(stream, AnimationMap.KTAS);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown token in TextureAnimation: " + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMdl(final MdlTokenOutputStream stream) throws IOException {
|
|
||||||
stream.startBlock(MdlUtils.TOKEN_TVERTEX_ANIM_SPACE);
|
|
||||||
this.writeTimeline(stream, AnimationMap.KTAT);
|
|
||||||
this.writeTimeline(stream, AnimationMap.KTAR);
|
|
||||||
this.writeTimeline(stream, AnimationMap.KTAS);
|
|
||||||
stream.endBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 4 + super.getByteLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.FloatArrayTimeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.FloatTimeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.UInt32Timeline;
|
|
||||||
|
|
||||||
public interface TimelineDescriptor {
|
|
||||||
Timeline createTimeline();
|
|
||||||
|
|
||||||
public static final TimelineDescriptor UINT32_TIMELINE = new TimelineDescriptor() {
|
|
||||||
@Override
|
|
||||||
public Timeline createTimeline() {
|
|
||||||
return new UInt32Timeline();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final TimelineDescriptor FLOAT_TIMELINE = new TimelineDescriptor() {
|
|
||||||
@Override
|
|
||||||
public Timeline createTimeline() {
|
|
||||||
return new FloatTimeline();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final TimelineDescriptor VECTOR3_TIMELINE = new TimelineDescriptor() {
|
|
||||||
@Override
|
|
||||||
public Timeline createTimeline() {
|
|
||||||
return new FloatArrayTimeline(3);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final TimelineDescriptor VECTOR4_TIMELINE = new TimelineDescriptor() {
|
|
||||||
@Override
|
|
||||||
public Timeline createTimeline() {
|
|
||||||
return new FloatArrayTimeline(4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public class UnknownChunk implements Chunk {
|
|
||||||
private final short[] chunk;
|
|
||||||
private final War3ID tag;
|
|
||||||
|
|
||||||
public UnknownChunk(final LittleEndianDataInputStream stream, final long size, final War3ID tag)
|
|
||||||
throws IOException {
|
|
||||||
System.err.println("Loading unknown chunk: " + tag);
|
|
||||||
this.chunk = ParseUtils.readUInt8Array(stream, (int) size);
|
|
||||||
this.tag = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeMdx(final LittleEndianDataOutputStream stream) throws IOException {
|
|
||||||
ParseUtils.writeWar3ID(stream, this.tag);
|
|
||||||
// Below: Byte.BYTES used because it's mean as a UInt8 array. This is
|
|
||||||
// not using Short.BYTES, deliberately, despite using a short[] as the
|
|
||||||
// type for the array. This is a Java problem that did not exist in the original
|
|
||||||
// JavaScript implementation by Ghostwolf
|
|
||||||
ParseUtils.writeUInt32(stream, this.chunk.length * Byte.BYTES);
|
|
||||||
ParseUtils.writeUInt8Array(stream, this.chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getByteLength() {
|
|
||||||
return 8 + (this.chunk.length * Byte.BYTES);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx.timeline;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenInputStream;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenOutputStream;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public final class FloatArrayTimeline extends Timeline<float[]> {
|
|
||||||
private final int arraySize;
|
|
||||||
|
|
||||||
public FloatArrayTimeline(final int arraySize) {
|
|
||||||
this.arraySize = arraySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int size() {
|
|
||||||
return this.arraySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected float[] readMdxValue(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
return ParseUtils.readFloatArray(stream, this.arraySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected float[] readMdlValue(final MdlTokenInputStream stream) {
|
|
||||||
final float[] output = new float[this.arraySize];
|
|
||||||
stream.readKeyframe(output);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdxValue(final LittleEndianDataOutputStream stream, final float[] value) throws IOException {
|
|
||||||
ParseUtils.writeFloatArray(stream, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdlValue(final MdlTokenOutputStream stream, final String prefix, final float[] value) {
|
|
||||||
stream.writeKeyframe(prefix, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getArraySize() {
|
|
||||||
return this.arraySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx.timeline;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenInputStream;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenOutputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public final class FloatTimeline extends Timeline<float[]> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int size() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected float[] readMdxValue(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
return new float[] { stream.readFloat() };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected float[] readMdlValue(final MdlTokenInputStream stream) {
|
|
||||||
return new float[] { stream.readFloat() };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdxValue(final LittleEndianDataOutputStream stream, final float[] value) throws IOException {
|
|
||||||
stream.writeFloat(value[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdlValue(final MdlTokenOutputStream stream, final String prefix, final float[] value) {
|
|
||||||
stream.writeKeyframe(prefix, value[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package com.etheller.warsmash.parsers.mdlx.timeline;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenInputStream;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlTokenOutputStream;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
|
||||||
import com.google.common.io.LittleEndianDataOutputStream;
|
|
||||||
|
|
||||||
public final class UInt32Timeline extends Timeline<long[]> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int size() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long[] readMdxValue(final LittleEndianDataInputStream stream) throws IOException {
|
|
||||||
return new long[] { ParseUtils.readUInt32(stream) };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long[] readMdlValue(final MdlTokenInputStream stream) {
|
|
||||||
return new long[] { stream.readUInt32() };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdxValue(final LittleEndianDataOutputStream stream, final long[] uint32) throws IOException {
|
|
||||||
ParseUtils.writeUInt32(stream, uint32[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMdlValue(final MdlTokenOutputStream stream, final String prefix, final long[] uint32) {
|
|
||||||
stream.writeKeyframe(prefix, uint32[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package com.etheller.warsmash.parsers.w3x;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.SeekableByteChannel;
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -119,6 +120,11 @@ public class War3Map implements DataSource {
|
|||||||
return this.dataSource.has(filepath);
|
return this.dataSource.has(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer read(final String path) throws IOException {
|
||||||
|
return this.dataSource.read(path);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getListfile() {
|
public Collection<String> getListfile() {
|
||||||
return this.internalMpqContentsDataSource.getListfile();
|
return this.internalMpqContentsDataSource.getListfile();
|
||||||
|
@ -101,6 +101,10 @@ public class StandardObjectData {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
destructableData.readSLK(this.source.getResourceAsStream("Units\\DestructableData.slk"));
|
destructableData.readSLK(this.source.getResourceAsStream("Units\\DestructableData.slk"));
|
||||||
|
final InputStream unitSkin = this.source.getResourceAsStream("Units\\DestructableSkin.txt");
|
||||||
|
if (unitSkin != null) {
|
||||||
|
destructableData.readTXT(unitSkin, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -191,6 +195,11 @@ public class StandardObjectData {
|
|||||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityFunc.txt"), true);
|
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityFunc.txt"), true);
|
||||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityStrings.txt"), true);
|
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityStrings.txt"), true);
|
||||||
|
|
||||||
|
final InputStream unitSkin = this.source.getResourceAsStream("Units\\AbilitySkin.txt");
|
||||||
|
if (unitSkin != null) {
|
||||||
|
profile.readTXT(unitSkin, true);
|
||||||
|
}
|
||||||
|
|
||||||
abilityData.readSLK(this.source.getResourceAsStream("Units\\AbilityData.slk"));
|
abilityData.readSLK(this.source.getResourceAsStream("Units\\AbilityData.slk"));
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
|
@ -10,7 +10,7 @@ public class DataSourceFileHandle extends FileHandle {
|
|||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
|
|
||||||
public DataSourceFileHandle(final DataSource dataSource, final String path) {
|
public DataSourceFileHandle(final DataSource dataSource, final String path) {
|
||||||
super(path);
|
super(fixPath(dataSource, path));
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,4 +28,14 @@ public class DataSourceFileHandle extends FileHandle {
|
|||||||
throw new RuntimeException("Failed to load FileHandle from DataSource: " + path());
|
throw new RuntimeException("Failed to load FileHandle from DataSource: " + path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String fixPath(final DataSource dataSource, String path) {
|
||||||
|
if (!dataSource.has(path) && (path.toLowerCase().endsWith(".wav") || path.toLowerCase().endsWith(".mp3"))) {
|
||||||
|
final String otherPossiblePath = path.substring(0, path.lastIndexOf('.')) + ".flac";
|
||||||
|
if (dataSource.has(otherPossiblePath)) {
|
||||||
|
path = otherPossiblePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,79 @@ public final class ImageUtils {
|
|||||||
private static final int BYTES_PER_PIXEL = 4;
|
private static final int BYTES_PER_PIXEL = 4;
|
||||||
public static final String DEFAULT_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNTemp.blp";
|
public static final String DEFAULT_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNTemp.blp";
|
||||||
|
|
||||||
public static Texture getBLPTexture(final DataSource dataSource, final String path) {
|
public static Texture getAnyExtensionTexture(final DataSource dataSource, final String path) {
|
||||||
final BufferedImage image = getBLPImage(dataSource, path);
|
BufferedImage image;
|
||||||
if (image != null) {
|
try {
|
||||||
return ImageUtils.getTexture(image);
|
final AnyExtensionImage imageInfo = getAnyExtensionImageFixRGB(dataSource, path, "texture");
|
||||||
|
image = imageInfo.getImageData();
|
||||||
|
if (image != null) {
|
||||||
|
return ImageUtils.getTexture(image, imageInfo.isNeedsSRGBFix());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AnyExtensionImage getAnyExtensionImageFixRGB(final DataSource dataSource, final String path,
|
||||||
|
final String errorType) throws IOException {
|
||||||
|
if (path.toLowerCase().endsWith(".blp")) {
|
||||||
|
try (InputStream stream = dataSource.getResourceAsStream(path)) {
|
||||||
|
if (stream == null) {
|
||||||
|
final String tgaPath = path.substring(0, path.length() - 4) + ".tga";
|
||||||
|
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
||||||
|
if (tgaStream != null) {
|
||||||
|
final BufferedImage tgaData = TgaFile.readTGA(tgaPath, tgaStream);
|
||||||
|
return new AnyExtensionImage(false, tgaData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String ddsPath = path.substring(0, path.length() - 4) + ".dds";
|
||||||
|
try (final InputStream ddsStream = dataSource.getResourceAsStream(ddsPath)) {
|
||||||
|
if (ddsStream != null) {
|
||||||
|
final BufferedImage image = ImageIO.read(ddsStream);
|
||||||
|
return new AnyExtensionImage(false, image);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException("Missing " + errorType + ": " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final BufferedImage image = ImageIO.read(stream);
|
||||||
|
return new AnyExtensionImage(true, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException("Missing " + errorType + ": " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AnyExtensionImage {
|
||||||
|
private final boolean needsSRGBFix;
|
||||||
|
private final BufferedImage imageData;
|
||||||
|
|
||||||
|
public AnyExtensionImage(final boolean needsSRGBFix, final BufferedImage imageData) {
|
||||||
|
this.needsSRGBFix = needsSRGBFix;
|
||||||
|
this.imageData = imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getImageData() {
|
||||||
|
return this.imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getRGBCorrectImageData() {
|
||||||
|
return this.needsSRGBFix ? forceBufferedImagesRGB(this.imageData) : this.imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNeedsSRGBFix() {
|
||||||
|
return this.needsSRGBFix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage getBLPImage(final DataSource dataSource, final String path) {
|
public static BufferedImage getBLPImage(final DataSource dataSource, final String path) {
|
||||||
try {
|
try {
|
||||||
try (final InputStream resourceAsStream = dataSource.getResourceAsStream(path)) {
|
try (final InputStream resourceAsStream = dataSource.getResourceAsStream(path)) {
|
||||||
@ -64,7 +129,7 @@ public final class ImageUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Texture getTexture(final BufferedImage image) {
|
public static Texture getTexture(final BufferedImage image, final boolean sRGBFix) {
|
||||||
final int[] pixels = new int[image.getWidth() * image.getHeight()];
|
final int[] pixels = new int[image.getWidth() * image.getHeight()];
|
||||||
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
|
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
|
||||||
|
|
||||||
@ -75,12 +140,12 @@ public final class ImageUtils {
|
|||||||
// for
|
// for
|
||||||
// RGB
|
// RGB
|
||||||
|
|
||||||
final Pixmap pixmap = new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888) {
|
final Pixmap pixmap = sRGBFix ? new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888) {
|
||||||
@Override
|
@Override
|
||||||
public int getGLInternalFormat() {
|
public int getGLInternalFormat() {
|
||||||
return GL30.GL_SRGB8_ALPHA8;
|
return GL30.GL_SRGB8_ALPHA8;
|
||||||
}
|
}
|
||||||
};
|
} : new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888);
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
final int pixel = pixels[(y * image.getWidth()) + x];
|
final int pixel = pixels[(y * image.getWidth()) + x];
|
||||||
|
@ -148,6 +148,15 @@ public abstract class ModelViewer {
|
|||||||
final SolvedPath solved = pathSolver.solve(src, solverParams);
|
final SolvedPath solved = pathSolver.solve(src, solverParams);
|
||||||
|
|
||||||
finalSrc = solved.getFinalSrc();
|
finalSrc = solved.getFinalSrc();
|
||||||
|
if (!this.dataSource.has(finalSrc)) {
|
||||||
|
final String ddsPath = finalSrc.substring(0, finalSrc.lastIndexOf('.')) + ".dds";
|
||||||
|
if (this.dataSource.has(ddsPath)) {
|
||||||
|
finalSrc = ddsPath;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.err.println("Attempting to load non-existant file: " + finalSrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
extension = solved.getExtension();
|
extension = solved.getExtension();
|
||||||
isFetch = solved.isFetch();
|
isFetch = solved.isFetch();
|
||||||
|
|
||||||
@ -185,9 +194,6 @@ public abstract class ModelViewer {
|
|||||||
|
|
||||||
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
||||||
try {
|
try {
|
||||||
if (!this.dataSource.has(finalSrc)) {
|
|
||||||
System.err.println("Attempting to load non-existant file: " + finalSrc);
|
|
||||||
}
|
|
||||||
resource.loadData(this.dataSource.getResourceAsStream(finalSrc), null);
|
resource.loadData(this.dataSource.getResourceAsStream(finalSrc), null);
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
|
@ -91,7 +91,7 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
|||||||
final GL20 gl = this.viewer.gl;
|
final GL20 gl = this.viewer.gl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(final BufferedImage image) {
|
public void update(final BufferedImage image, final boolean sRGBFix) {
|
||||||
final GL20 gl = this.viewer.gl;
|
final GL20 gl = this.viewer.gl;
|
||||||
|
|
||||||
final int imageWidth = image.getWidth();
|
final int imageWidth = image.getWidth();
|
||||||
@ -129,8 +129,8 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
|||||||
// GL20.GL_UNSIGNED_BYTE, buffer);
|
// GL20.GL_UNSIGNED_BYTE, buffer);
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_SRGB8_ALPHA8, imageWidth, imageHeight, 0, GL20.GL_RGBA,
|
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, sRGBFix ? GL30.GL_SRGB8_ALPHA8 : GL30.GL_RGBA8, imageWidth, imageHeight,
|
||||||
GL20.GL_UNSIGNED_BYTE, buffer);
|
0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, buffer);
|
||||||
|
|
||||||
this.width = imageWidth;
|
this.width = imageWidth;
|
||||||
this.height = imageHeight;
|
this.height = imageHeight;
|
||||||
|
@ -29,7 +29,7 @@ public class BlpGdxTexture extends GdxTextureResource {
|
|||||||
BufferedImage img;
|
BufferedImage img;
|
||||||
try {
|
try {
|
||||||
img = ImageIO.read(src);
|
img = ImageIO.read(src);
|
||||||
setGdxTexture(ImageUtils.getTexture(img));
|
setGdxTexture(ImageUtils.getTexture(img, true));
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -2,8 +2,8 @@ package com.etheller.warsmash.viewer5.handlers.blp;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
|
||||||
import com.etheller.warsmash.viewer5.HandlerResource;
|
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||||
|
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ public class BlpTexture extends RawOpenGLTextureResource {
|
|||||||
BufferedImage img;
|
BufferedImage img;
|
||||||
try {
|
try {
|
||||||
img = ImageIO.read(src);
|
img = ImageIO.read(src);
|
||||||
update(img);
|
update(img, true);
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.etheller.warsmash.viewer5.handlers.blp;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||||
|
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||||
|
|
||||||
|
public class DdsHandler extends ResourceHandler {
|
||||||
|
|
||||||
|
public DdsHandler() {
|
||||||
|
this.extensions = new ArrayList<>();
|
||||||
|
this.extensions.add(new String[] { ".dds", "arrayBuffer" });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean load(final ModelViewer modelViewer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandlerResource<?> construct(final ResourceHandlerConstructionParams params) {
|
||||||
|
return new DdsTexture(params.getViewer(), params.getHandler(), params.getExtension(), params.getPathSolver(),
|
||||||
|
params.getFetchUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.etheller.warsmash.viewer5.handlers.blp;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||||
|
import com.etheller.warsmash.viewer5.PathSolver;
|
||||||
|
import com.etheller.warsmash.viewer5.RawOpenGLTextureResource;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||||
|
|
||||||
|
public class DdsTexture extends RawOpenGLTextureResource {
|
||||||
|
|
||||||
|
public DdsTexture(final ModelViewer viewer, final ResourceHandler handler, final String extension,
|
||||||
|
final PathSolver pathSolver, final String fetchUrl) {
|
||||||
|
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void lateLoad() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void load(final InputStream src, final Object options) {
|
||||||
|
BufferedImage img;
|
||||||
|
try {
|
||||||
|
img = ImageIO.read(src);
|
||||||
|
update(img, false);
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,23 +3,24 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.FloatArrayTimeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.FloatTimeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.UInt32Timeline;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
import com.etheller.warsmash.util.War3ID;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxAnimatedObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxFloatArrayTimeline;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxFloatTimeline;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxUInt32Timeline;
|
||||||
|
|
||||||
public class AnimatedObject {
|
public class AnimatedObject {
|
||||||
public MdxModel model;
|
public MdxModel model;
|
||||||
public Map<War3ID, Sd<?>> timelines;
|
public Map<War3ID, Sd<?>> timelines;
|
||||||
public Map<String, byte[]> variants;
|
public Map<String, byte[]> variants;
|
||||||
|
|
||||||
public AnimatedObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.AnimatedObject object) {
|
public AnimatedObject(final MdxModel model, final MdlxAnimatedObject object) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.timelines = new HashMap<>();
|
this.timelines = new HashMap<>();
|
||||||
this.variants = new HashMap<>();
|
this.variants = new HashMap<>();
|
||||||
|
|
||||||
for (final Timeline<?> timeline : object.getTimelines()) {
|
for (final MdlxTimeline<?> timeline : object.getTimelines()) {
|
||||||
this.timelines.put(timeline.getName(), createTypedSd(model, timeline));
|
this.timelines.put(timeline.getName(), createTypedSd(model, timeline));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,15 +127,15 @@ public class AnimatedObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Sd<?> createTypedSd(final MdxModel model, final Timeline<?> timeline) {
|
private Sd<?> createTypedSd(final MdxModel model, final MdlxTimeline<?> timeline) {
|
||||||
if (timeline instanceof UInt32Timeline) {
|
if (timeline instanceof MdlxUInt32Timeline) {
|
||||||
return new UInt32Sd(model, (UInt32Timeline) timeline);
|
return new UInt32Sd(model, (MdlxUInt32Timeline) timeline);
|
||||||
}
|
}
|
||||||
else if (timeline instanceof FloatTimeline) {
|
else if (timeline instanceof MdlxFloatTimeline) {
|
||||||
return new ScalarSd(model, (FloatTimeline) timeline);
|
return new ScalarSd(model, (MdlxFloatTimeline) timeline);
|
||||||
}
|
}
|
||||||
else if (timeline instanceof FloatArrayTimeline) {
|
else if (timeline instanceof MdlxFloatArrayTimeline) {
|
||||||
final FloatArrayTimeline faTimeline = (FloatArrayTimeline) timeline;
|
final MdlxFloatArrayTimeline faTimeline = (MdlxFloatArrayTimeline) timeline;
|
||||||
final int arraySize = faTimeline.getArraySize();
|
final int arraySize = faTimeline.getArraySize();
|
||||||
if (arraySize == 3) {
|
if (arraySize == 3) {
|
||||||
return new VectorSd(model, faTimeline);
|
return new VectorSd(model, faTimeline);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxAttachment;
|
||||||
|
|
||||||
public class Attachment extends GenericObject {
|
public class Attachment extends GenericObject {
|
||||||
protected String name;
|
protected String name;
|
||||||
@ -8,8 +9,7 @@ public class Attachment extends GenericObject {
|
|||||||
protected final int attachmentId;
|
protected final int attachmentId;
|
||||||
protected MdxModel internalModel;
|
protected MdxModel internalModel;
|
||||||
|
|
||||||
public Attachment(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Attachment attachment,
|
public Attachment(final MdxModel model, final MdlxAttachment attachment, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, attachment, index);
|
super(model, attachment, index);
|
||||||
|
|
||||||
final String path = attachment.getPath().toLowerCase().replace(".mdl", ".mdx");
|
final String path = attachment.getPath().toLowerCase().replace(".mdl", ".mdx");
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxBone;
|
||||||
|
|
||||||
public class Bone extends GenericObject {
|
public class Bone extends GenericObject {
|
||||||
|
|
||||||
private final GeosetAnimation geosetAnimation;
|
private final GeosetAnimation geosetAnimation;
|
||||||
|
|
||||||
public Bone(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Bone bone, final int index) {
|
public Bone(final MdxModel model, final MdlxBone bone, final int index) {
|
||||||
super(model, bone, index);
|
super(model, bone, index);
|
||||||
|
|
||||||
GeosetAnimation geosetAnimation = null;
|
GeosetAnimation geosetAnimation = null;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxCamera;
|
||||||
|
|
||||||
public class Camera extends AnimatedObject {
|
public class Camera extends AnimatedObject {
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ public class Camera extends AnimatedObject {
|
|||||||
public final float nearClippingPlane;
|
public final float nearClippingPlane;
|
||||||
public final float[] targetPosition;
|
public final float[] targetPosition;
|
||||||
|
|
||||||
public Camera(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Camera camera) {
|
public Camera(final MdxModel model, final MdlxCamera camera) {
|
||||||
super(model, camera);
|
super(model, camera);
|
||||||
|
|
||||||
this.name = camera.getName();
|
this.name = camera.getName();
|
||||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.math.collision.BoundingBox;
|
|||||||
import com.badlogic.gdx.math.collision.Ray;
|
import com.badlogic.gdx.math.collision.Ray;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
import com.etheller.warsmash.viewer5.GenericNode;
|
import com.etheller.warsmash.viewer5.GenericNode;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxCollisionShape;
|
||||||
|
|
||||||
public class CollisionShape extends GenericObject {
|
public class CollisionShape extends GenericObject {
|
||||||
private static Vector3 intersectHeap = new Vector3();
|
private static Vector3 intersectHeap = new Vector3();
|
||||||
@ -15,8 +16,7 @@ public class CollisionShape extends GenericObject {
|
|||||||
private static Ray intersectRayHeap = new Ray();
|
private static Ray intersectRayHeap = new Ray();
|
||||||
private Intersectable intersectable;
|
private Intersectable intersectable;
|
||||||
|
|
||||||
public CollisionShape(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.CollisionShape object,
|
public CollisionShape(final MdxModel model, final MdlxCollisionShape object, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, object, index);
|
super(model, object, index);
|
||||||
final float[][] vertices = object.getVertices();
|
final float[][] vertices = object.getVertices();
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
|||||||
import com.etheller.warsmash.viewer5.PathSolver;
|
import com.etheller.warsmash.viewer5.PathSolver;
|
||||||
import com.etheller.warsmash.viewer5.Texture;
|
import com.etheller.warsmash.viewer5.Texture;
|
||||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxEventObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2;
|
||||||
|
|
||||||
public class EventObjectEmitterObject extends GenericObject implements EmitterObject {
|
public class EventObjectEmitterObject extends GenericObject implements EmitterObject {
|
||||||
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
|
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
|
||||||
@ -106,8 +108,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
|||||||
*/
|
*/
|
||||||
private boolean ok = false;
|
private boolean ok = false;
|
||||||
|
|
||||||
public EventObjectEmitterObject(final MdxModel model,
|
public EventObjectEmitterObject(final MdxModel model, final MdlxEventObject eventObject, final int index) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) {
|
|
||||||
super(model, eventObject, index);
|
super(model, eventObject, index);
|
||||||
|
|
||||||
final ModelViewer viewer = model.viewer;
|
final ModelViewer viewer = model.viewer;
|
||||||
@ -256,8 +257,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
|||||||
}
|
}
|
||||||
|
|
||||||
final int[] blendModes = FilterMode
|
final int[] blendModes = FilterMode
|
||||||
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
|
.emitterFilterMode(MdlxParticleEmitter2.FilterMode.fromId(getInt(row, "BlendMode")));
|
||||||
.fromId(getInt(row, "BlendMode")));
|
|
||||||
|
|
||||||
this.blendSrc = blendModes[0];
|
this.blendSrc = blendModes[0];
|
||||||
this.blendDst = blendModes[1];
|
this.blendDst = blendModes[1];
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2;
|
||||||
|
|
||||||
public class FilterMode {
|
public class FilterMode {
|
||||||
private static final int[] ERROR_DEFAULT = new int[] { 0, 0 };
|
private static final int[] ERROR_DEFAULT = new int[] { 0, 0 };
|
||||||
@ -9,7 +11,7 @@ public class FilterMode {
|
|||||||
private static final int[] ADDITIVE_ALPHA = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE };
|
private static final int[] ADDITIVE_ALPHA = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE };
|
||||||
private static final int[] BLEND = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA };
|
private static final int[] BLEND = new int[] { GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA };
|
||||||
|
|
||||||
public static int[] layerFilterMode(final com.etheller.warsmash.parsers.mdlx.Layer.FilterMode filterMode) {
|
public static int[] layerFilterMode(final MdlxLayer.FilterMode filterMode) {
|
||||||
switch (filterMode) {
|
switch (filterMode) {
|
||||||
case BLEND:
|
case BLEND:
|
||||||
return BLEND; // Blend
|
return BLEND; // Blend
|
||||||
@ -26,8 +28,7 @@ public class FilterMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[] emitterFilterMode(
|
public static int[] emitterFilterMode(final MdlxParticleEmitter2.FilterMode filterMode) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode filterMode) {
|
|
||||||
switch (filterMode) {
|
switch (filterMode) {
|
||||||
case BLEND:
|
case BLEND:
|
||||||
return BLEND; // Blend
|
return BLEND; // Blend
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGenericObject;
|
||||||
|
|
||||||
public class GenericObject extends AnimatedObject implements GenericIndexed {
|
public class GenericObject extends AnimatedObject implements GenericIndexed {
|
||||||
|
|
||||||
@ -38,8 +39,7 @@ public class GenericObject extends AnimatedObject implements GenericIndexed {
|
|||||||
public final boolean hasScaleAnim;
|
public final boolean hasScaleAnim;
|
||||||
public final boolean hasGenericAnim;
|
public final boolean hasGenericAnim;
|
||||||
|
|
||||||
public GenericObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.GenericObject object,
|
public GenericObject(final MdxModel model, final MdlxGenericObject object, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, object);
|
super(model, object);
|
||||||
|
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
@ -5,6 +5,7 @@ import java.util.Arrays;
|
|||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
|
|
||||||
public class Geoset {
|
public class Geoset {
|
||||||
public MdxModel model;
|
public MdxModel model;
|
||||||
@ -25,12 +26,12 @@ public class Geoset {
|
|||||||
private final int skinStride;
|
private final int skinStride;
|
||||||
private final int boneCountOffsetBytes;
|
private final int boneCountOffsetBytes;
|
||||||
public final boolean unselectable;
|
public final boolean unselectable;
|
||||||
public final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset;
|
public final MdlxGeoset mdlxGeoset;
|
||||||
|
|
||||||
public Geoset(final MdxModel model, final int index, final int positionOffset, final int normalOffset,
|
public Geoset(final MdxModel model, final int index, final int positionOffset, final int normalOffset,
|
||||||
final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements,
|
final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements,
|
||||||
final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes, final boolean unselectable,
|
final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes, final boolean unselectable,
|
||||||
final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset) {
|
final MdlxGeoset mdlxGeoset) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.positionOffset = positionOffset;
|
this.positionOffset = positionOffset;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeosetAnimation;
|
||||||
|
|
||||||
public class GeosetAnimation extends AnimatedObject {
|
public class GeosetAnimation extends AnimatedObject {
|
||||||
|
|
||||||
@ -8,8 +9,7 @@ public class GeosetAnimation extends AnimatedObject {
|
|||||||
private final float[] color;
|
private final float[] color;
|
||||||
public final int geosetId;
|
public final int geosetId;
|
||||||
|
|
||||||
public GeosetAnimation(final MdxModel model,
|
public GeosetAnimation(final MdxModel model, final MdlxGeosetAnimation geosetAnimation) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.GeosetAnimation geosetAnimation) {
|
|
||||||
super(model, geosetAnimation);
|
super(model, geosetAnimation);
|
||||||
|
|
||||||
final float[] color = geosetAnimation.getColor();
|
final float[] color = geosetAnimation.getColor();
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGenericObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An MDX helper.
|
* An MDX helper.
|
||||||
*/
|
*/
|
||||||
public class Helper extends GenericObject {
|
public class Helper extends GenericObject {
|
||||||
public Helper(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.GenericObject object,
|
public Helper(final MdxModel model, final MdlxGenericObject object, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, object, index);
|
super(model, object, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
|||||||
|
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An MDX layer.
|
* An MDX layer.
|
||||||
@ -26,11 +27,10 @@ public class Layer extends AnimatedObject {
|
|||||||
public boolean blended;
|
public boolean blended;
|
||||||
public TextureAnimation textureAnimation;
|
public TextureAnimation textureAnimation;
|
||||||
|
|
||||||
public Layer(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Layer layer, final int layerId,
|
public Layer(final MdxModel model, final MdlxLayer layer, final int layerId, final int priorityPlane) {
|
||||||
final int priorityPlane) {
|
|
||||||
super(model, layer);
|
super(model, layer);
|
||||||
|
|
||||||
final com.etheller.warsmash.parsers.mdlx.Layer.FilterMode filterMode = layer.getFilterMode();
|
final MdlxLayer.FilterMode filterMode = layer.getFilterMode();
|
||||||
final int textureAnimationId = layer.getTextureAnimationId();
|
final int textureAnimationId = layer.getTextureAnimationId();
|
||||||
final GL20 gl = model.viewer.gl;
|
final GL20 gl = model.viewer.gl;
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ public class Layer extends AnimatedObject {
|
|||||||
this.noDepthTest = flags & 0x40;
|
this.noDepthTest = flags & 0x40;
|
||||||
this.noDepthSet = flags & 0x80;
|
this.noDepthSet = flags & 0x80;
|
||||||
|
|
||||||
this.depthMaskValue = ((filterMode == com.etheller.warsmash.parsers.mdlx.Layer.FilterMode.NONE)
|
this.depthMaskValue = ((filterMode == MdlxLayer.FilterMode.NONE)
|
||||||
|| (filterMode == com.etheller.warsmash.parsers.mdlx.Layer.FilterMode.TRANSPARENT));
|
|| (filterMode == MdlxLayer.FilterMode.TRANSPARENT));
|
||||||
|
|
||||||
this.blendSrc = 0;
|
this.blendSrc = 0;
|
||||||
this.blendDst = 0;
|
this.blendDst = 0;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLight;
|
||||||
|
|
||||||
public class Light extends GenericObject {
|
public class Light extends GenericObject {
|
||||||
|
|
||||||
@ -11,17 +12,17 @@ public class Light extends GenericObject {
|
|||||||
private final float[] ambientColor;
|
private final float[] ambientColor;
|
||||||
private final float ambientIntensity;
|
private final float ambientIntensity;
|
||||||
|
|
||||||
public Light(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Light light, final int index) {
|
public Light(final MdxModel model, final MdlxLight light, final int index) {
|
||||||
super(model, light, index);
|
super(model, light, index);
|
||||||
|
|
||||||
switch (light.getType()) {
|
switch (light.getType()) {
|
||||||
case 0:
|
case OMNIDIRECTIONAL:
|
||||||
this.type = Type.OMNIDIRECTIONAL;
|
this.type = Type.OMNIDIRECTIONAL;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case DIRECTIONAL:
|
||||||
this.type = Type.DIRECTIONAL;
|
this.type = Type.DIRECTIONAL;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case AMBIENT:
|
||||||
this.type = Type.AMBIENT;
|
this.type = Type.AMBIENT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -10,7 +10,6 @@ import com.badlogic.gdx.math.Matrix4;
|
|||||||
import com.badlogic.gdx.math.Quaternion;
|
import com.badlogic.gdx.math.Quaternion;
|
||||||
import com.badlogic.gdx.math.Vector3;
|
import com.badlogic.gdx.math.Vector3;
|
||||||
import com.badlogic.gdx.math.collision.Ray;
|
import com.badlogic.gdx.math.collision.Ray;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.util.WarsmashConstants;
|
import com.etheller.warsmash.util.WarsmashConstants;
|
||||||
import com.etheller.warsmash.viewer5.GenericNode;
|
import com.etheller.warsmash.viewer5.GenericNode;
|
||||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||||
@ -24,6 +23,7 @@ import com.etheller.warsmash.viewer5.TextureMapper;
|
|||||||
import com.etheller.warsmash.viewer5.UpdatableObject;
|
import com.etheller.warsmash.viewer5.UpdatableObject;
|
||||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
|
|
||||||
public class MdxComplexInstance extends ModelInstance {
|
public class MdxComplexInstance extends ModelInstance {
|
||||||
private static final float[] visibilityHeap = new float[1];
|
private static final float[] visibilityHeap = new float[1];
|
||||||
@ -764,7 +764,7 @@ public class MdxComplexInstance extends ModelInstance {
|
|||||||
if (!geoset.unselectable) {
|
if (!geoset.unselectable) {
|
||||||
geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter);
|
geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter);
|
||||||
if (alphaHeap[0] > 0) {
|
if (alphaHeap[0] > 0) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset = geoset.mdlxGeoset;
|
final MdlxGeoset mdlxGeoset = geoset.mdlxGeoset;
|
||||||
if (CollisionShape.intersectRayTriangles(ray, this, mdlxGeoset.getVertices(),
|
if (CollisionShape.intersectRayTriangles(ray, this, mdlxGeoset.getVertices(),
|
||||||
mdlxGeoset.getFaces(), 3, intersection)) {
|
mdlxGeoset.getFaces(), 3, intersection)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -8,6 +8,7 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
|||||||
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
||||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||||
import com.etheller.warsmash.viewer5.handlers.blp.BlpHandler;
|
import com.etheller.warsmash.viewer5.handlers.blp.BlpHandler;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.blp.DdsHandler;
|
||||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaHandler;
|
import com.etheller.warsmash.viewer5.handlers.tga.TgaHandler;
|
||||||
|
|
||||||
public class MdxHandler extends ModelHandler {
|
public class MdxHandler extends ModelHandler {
|
||||||
@ -22,6 +23,7 @@ public class MdxHandler extends ModelHandler {
|
|||||||
@Override
|
@Override
|
||||||
public boolean load(final ModelViewer viewer) {
|
public boolean load(final ModelViewer viewer) {
|
||||||
viewer.addHandler(new BlpHandler());
|
viewer.addHandler(new BlpHandler());
|
||||||
|
viewer.addHandler(new DdsHandler());
|
||||||
viewer.addHandler(new TgaHandler());
|
viewer.addHandler(new TgaHandler());
|
||||||
|
|
||||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex);
|
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex);
|
||||||
@ -32,14 +34,15 @@ public class MdxHandler extends ModelHandler {
|
|||||||
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
|
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
|
||||||
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
|
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
|
||||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
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);
|
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
|
||||||
// TODO HD reforged
|
// TODO HD reforged
|
||||||
|
|
||||||
// If a shader failed to compile, don't allow the handler to be registered, and
|
// If a shader failed to compile, don't allow the handler to be registered, and
|
||||||
// send an error instead.
|
// send an error instead.
|
||||||
return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled()
|
return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled()
|
||||||
/* && Shaders.simple.isCompiled() && Shaders.hd.isCompiled() */;
|
/* && Shaders.simple.isCompiled() && Shaders.hd.isCompiled() */;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,17 +2,36 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Extent;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||||
import com.etheller.warsmash.viewer5.PathSolver;
|
import com.etheller.warsmash.viewer5.PathSolver;
|
||||||
import com.etheller.warsmash.viewer5.Texture;
|
import com.etheller.warsmash.viewer5.Texture;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxAttachment;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxBone;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxCamera;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxCollisionShape;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxEventObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxExtent;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeosetAnimation;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxHelper;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLight;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxMaterial;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxRibbonEmitter;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxSequence;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTexture;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTexture.WrapMode;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTextureAnimation;
|
||||||
|
|
||||||
public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||||
public boolean reforged = false;
|
public boolean reforged = false;
|
||||||
@ -73,7 +92,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
parser = (MdlxModel) bufferOrParser;
|
parser = (MdlxModel) bufferOrParser;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
parser = new MdlxModel((InputStream) bufferOrParser);
|
System.err.println("Wasting memory with conversion from InputStream to buffer in MdxModel");
|
||||||
|
parser = new MdlxModel(ByteBuffer.wrap(IOUtils.toByteArray((InputStream) bufferOrParser)));
|
||||||
}
|
}
|
||||||
|
|
||||||
final ModelViewer viewer = this.viewer;
|
final ModelViewer viewer = this.viewer;
|
||||||
@ -86,7 +106,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
this.name = parser.getName();
|
this.name = parser.getName();
|
||||||
|
|
||||||
// Initialize the bounds.
|
// Initialize the bounds.
|
||||||
final Extent extent = parser.getExtent();
|
final MdlxExtent extent = parser.getExtent();
|
||||||
final float[] min = extent.getMin();
|
final float[] min = extent.getMin();
|
||||||
final float[] max = extent.getMax();
|
final float[] max = extent.getMax();
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
@ -97,23 +117,24 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
this.bounds.fromExtents(min, max);
|
this.bounds.fromExtents(min, max);
|
||||||
|
|
||||||
// Sequences
|
// Sequences
|
||||||
this.sequences.addAll(parser.getSequences());
|
for (final MdlxSequence sequence : parser.getSequences()) {
|
||||||
|
this.sequences.add(new Sequence(sequence));
|
||||||
|
}
|
||||||
|
|
||||||
// Global sequences
|
// Global sequences
|
||||||
this.globalSequences.addAll(parser.getGlobalSequences());
|
this.globalSequences.addAll(parser.getGlobalSequences());
|
||||||
|
|
||||||
// Texture animations
|
// Texture animations
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation : parser
|
for (final MdlxTextureAnimation textureAnimation : parser.getTextureAnimations()) {
|
||||||
.getTextureAnimations()) {
|
|
||||||
this.textureAnimations.add(new TextureAnimation(this, textureAnimation));
|
this.textureAnimations.add(new TextureAnimation(this, textureAnimation));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Materials
|
// Materials
|
||||||
int layerId = 0;
|
int layerId = 0;
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Material material : parser.getMaterials()) {
|
for (final MdlxMaterial material : parser.getMaterials()) {
|
||||||
final List<Layer> layers = new ArrayList<>();
|
final List<Layer> layers = new ArrayList<>();
|
||||||
|
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Layer layer : material.getLayers()) {
|
for (final MdlxLayer layer : material.getLayers()) {
|
||||||
final Layer vLayer = new Layer(this, layer, layerId++, material.getPriorityPlane());
|
final Layer vLayer = new Layer(this, layer, layerId++, material.getPriorityPlane());
|
||||||
|
|
||||||
layers.add(vLayer);
|
layers.add(vLayer);
|
||||||
@ -139,10 +160,10 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
final GL20 gl = viewer.gl;
|
final GL20 gl = viewer.gl;
|
||||||
|
|
||||||
// Textures.
|
// Textures.
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Texture texture : parser.getTextures()) {
|
for (final MdlxTexture texture : parser.getTextures()) {
|
||||||
String path = texture.getPath();
|
String path = texture.getPath();
|
||||||
final int replaceableId = texture.getReplaceableId();
|
final int replaceableId = texture.getReplaceableId();
|
||||||
final int flags = texture.getFlags();
|
final WrapMode wrapMode = texture.getWrapMode();
|
||||||
|
|
||||||
if (replaceableId != 0) {
|
if (replaceableId != 0) {
|
||||||
// TODO This uses dumb, stupid, terrible, no-good hardcoded replaceable IDs
|
// TODO This uses dumb, stupid, terrible, no-good hardcoded replaceable IDs
|
||||||
@ -163,11 +184,11 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
final Texture viewerTexture = (Texture) viewer.load(path, pathSolver, solverParams);
|
final Texture viewerTexture = (Texture) viewer.load(path, pathSolver, solverParams);
|
||||||
|
|
||||||
// When the texture will load, it will apply its wrap modes.
|
// When the texture will load, it will apply its wrap modes.
|
||||||
if ((flags & 0x1) != 0) {
|
if (wrapMode.isWrapWidth()) {
|
||||||
viewerTexture.setWrapS(true);
|
viewerTexture.setWrapS(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & 0x2) != 0) {
|
if (wrapMode.isWrapHeight()) {
|
||||||
viewerTexture.setWrapT(true);
|
viewerTexture.setWrapT(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +197,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Geoset animations
|
// Geoset animations
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.GeosetAnimation geosetAnimation : parser.getGeosetAnimations()) {
|
for (final MdlxGeosetAnimation geosetAnimation : parser.getGeosetAnimations()) {
|
||||||
this.geosetAnimations.add(new GeosetAnimation(this, geosetAnimation));
|
this.geosetAnimations.add(new GeosetAnimation(this, geosetAnimation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,53 +210,52 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
|||||||
int objectId = 0;
|
int objectId = 0;
|
||||||
|
|
||||||
// Bones
|
// Bones
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Bone bone : parser.getBones()) {
|
for (final MdlxBone bone : parser.getBones()) {
|
||||||
this.bones.add(new Bone(this, bone, objectId++));
|
this.bones.add(new Bone(this, bone, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lights
|
// Lights
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Light light : parser.getLights()) {
|
for (final MdlxLight light : parser.getLights()) {
|
||||||
this.lights.add(new Light(this, light, objectId++));
|
this.lights.add(new Light(this, light, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Helper helper : parser.getHelpers()) {
|
for (final MdlxHelper helper : parser.getHelpers()) {
|
||||||
this.helpers.add(new Helper(this, helper, objectId++));
|
this.helpers.add(new Helper(this, helper, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attachments
|
// Attachments
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Attachment attachment : parser.getAttachments()) {
|
for (final MdlxAttachment attachment : parser.getAttachments()) {
|
||||||
this.attachments.add(new Attachment(this, attachment, objectId++));
|
this.attachments.add(new Attachment(this, attachment, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Particle Emitters
|
// Particle Emitters
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.ParticleEmitter particleEmitter : parser.getParticleEmitters()) {
|
for (final MdlxParticleEmitter particleEmitter : parser.getParticleEmitters()) {
|
||||||
this.particleEmitters.add(new ParticleEmitterObject(this, particleEmitter, objectId++));
|
this.particleEmitters.add(new ParticleEmitterObject(this, particleEmitter, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Particle Emitters 2
|
// Particle Emitters 2
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2 particleEmitter2 : parser
|
for (final MdlxParticleEmitter2 particleEmitter2 : parser.getParticleEmitters2()) {
|
||||||
.getParticleEmitters2()) {
|
|
||||||
this.particleEmitters2.add(new ParticleEmitter2Object(this, particleEmitter2, objectId++));
|
this.particleEmitters2.add(new ParticleEmitter2Object(this, particleEmitter2, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ribbon emitters
|
// Ribbon emitters
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.RibbonEmitter ribbonEmitter : parser.getRibbonEmitters()) {
|
for (final MdlxRibbonEmitter ribbonEmitter : parser.getRibbonEmitters()) {
|
||||||
this.ribbonEmitters.add(new RibbonEmitterObject(this, ribbonEmitter, objectId++));
|
this.ribbonEmitters.add(new RibbonEmitterObject(this, ribbonEmitter, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.Camera camera : parser.getCameras()) {
|
for (final MdlxCamera camera : parser.getCameras()) {
|
||||||
this.cameras.add(new Camera(this, camera));
|
this.cameras.add(new Camera(this, camera));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event objects
|
// Event objects
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.EventObject eventObject : parser.getEventObjects()) {
|
for (final MdlxEventObject eventObject : parser.getEventObjects()) {
|
||||||
this.eventObjects.add(new EventObjectEmitterObject(this, eventObject, objectId++));
|
this.eventObjects.add(new EventObjectEmitterObject(this, eventObject, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collision shapes
|
// Collision shapes
|
||||||
for (final com.etheller.warsmash.parsers.mdlx.CollisionShape collisionShape : parser.getCollisionShapes()) {
|
for (final MdlxCollisionShape collisionShape : parser.getCollisionShapes()) {
|
||||||
this.collisionShapes.add(new CollisionShape(this, collisionShape, objectId++));
|
this.collisionShapes.add(new CollisionShape(this, collisionShape, objectId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.util.WarsmashConstants;
|
import com.etheller.warsmash.util.WarsmashConstants;
|
||||||
import com.etheller.warsmash.viewer5.Texture;
|
import com.etheller.warsmash.viewer5.Texture;
|
||||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2.HeadOrTail;
|
||||||
|
|
||||||
public class ParticleEmitter2Object extends GenericObject implements EmitterObject {
|
public class ParticleEmitter2Object extends GenericObject implements EmitterObject {
|
||||||
public float width;
|
public float width;
|
||||||
@ -33,8 +35,7 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
|||||||
public int blendDst;
|
public int blendDst;
|
||||||
public int priorityPlane;
|
public int priorityPlane;
|
||||||
|
|
||||||
public ParticleEmitter2Object(final MdxModel model,
|
public ParticleEmitter2Object(final MdxModel model, final MdlxParticleEmitter2 emitter, final int index) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2 emitter, final int index) {
|
|
||||||
super(model, emitter, index);
|
super(model, emitter, index);
|
||||||
|
|
||||||
this.width = emitter.getWidth();
|
this.width = emitter.getWidth();
|
||||||
@ -68,10 +69,10 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
|||||||
|
|
||||||
this.replaceableId = emitter.getReplaceableId();
|
this.replaceableId = emitter.getReplaceableId();
|
||||||
|
|
||||||
final long headOrTail = emitter.getHeadOrTail();
|
final HeadOrTail headOrTail = emitter.getHeadOrTail();
|
||||||
|
|
||||||
this.head = ((headOrTail == 0) || (headOrTail == 2));
|
this.head = headOrTail.isIncludesHead();
|
||||||
this.tail = ((headOrTail == 1) || (headOrTail == 2));
|
this.tail = headOrTail.isIncludesTail();
|
||||||
|
|
||||||
this.cellWidth = 1f / emitter.getColumns();
|
this.cellWidth = 1f / emitter.getColumns();
|
||||||
this.cellHeight = 1f / emitter.getRows();
|
this.cellHeight = 1f / emitter.getRows();
|
||||||
|
@ -2,8 +2,9 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter;
|
||||||
|
|
||||||
public class ParticleEmitterObject extends GenericObject implements EmitterObject {
|
public class ParticleEmitterObject extends GenericObject implements EmitterObject {
|
||||||
public MdxModel internalModel;
|
public MdxModel internalModel;
|
||||||
@ -22,8 +23,7 @@ public class ParticleEmitterObject extends GenericObject implements EmitterObjec
|
|||||||
*/
|
*/
|
||||||
public boolean ok = false;
|
public boolean ok = false;
|
||||||
|
|
||||||
public ParticleEmitterObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.ParticleEmitter emitter,
|
public ParticleEmitterObject(final MdxModel model, final MdlxParticleEmitter emitter, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, emitter, index);
|
super(model, emitter, index);
|
||||||
|
|
||||||
this.internalModel = (MdxModel) model.viewer.load(
|
this.internalModel = (MdxModel) model.viewer.load(
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.Interpolator;
|
import com.etheller.warsmash.util.Interpolator;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public class QuaternionSd extends Sd<float[]> {
|
public class QuaternionSd extends Sd<float[]> {
|
||||||
|
|
||||||
public QuaternionSd(final MdxModel model, final Timeline<float[]> timeline) {
|
public QuaternionSd(final MdxModel model, final MdlxTimeline<float[]> timeline) {
|
||||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxRibbonEmitter;
|
||||||
|
|
||||||
public class RibbonEmitterObject extends GenericObject implements EmitterObject {
|
public class RibbonEmitterObject extends GenericObject implements EmitterObject {
|
||||||
public Layer layer;
|
public Layer layer;
|
||||||
@ -23,8 +24,7 @@ public class RibbonEmitterObject extends GenericObject implements EmitterObject
|
|||||||
*/
|
*/
|
||||||
public boolean ok = true;
|
public boolean ok = true;
|
||||||
|
|
||||||
public RibbonEmitterObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.RibbonEmitter emitter,
|
public RibbonEmitterObject(final MdxModel model, final MdlxRibbonEmitter emitter, final int index) {
|
||||||
final int index) {
|
|
||||||
super(model, emitter, index);
|
super(model, emitter, index);
|
||||||
|
|
||||||
this.layer = model.getMaterials().get(emitter.getMaterialId()).layers.get(0);
|
this.layer = model.getMaterials().get(emitter.getMaterialId()).layers.get(0);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public class ScalarSd extends Sd<float[]> {
|
public class ScalarSd extends Sd<float[]> {
|
||||||
|
|
||||||
public ScalarSd(final MdxModel model, final Timeline<float[]> timeline) {
|
public ScalarSd(final MdxModel model, final MdlxTimeline<float[]> timeline) {
|
||||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.War3ID;
|
import com.etheller.warsmash.util.War3ID;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public abstract class Sd<TYPE> {
|
public abstract class Sd<TYPE> {
|
||||||
public MdxModel model;
|
public MdxModel model;
|
||||||
@ -85,7 +84,7 @@ public abstract class Sd<TYPE> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sd(final MdxModel model, final Timeline<TYPE> timeline, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
public Sd(final MdxModel model, final MdlxTimeline<TYPE> timeline, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
||||||
final List<Long> globalSequences = model.getGlobalSequences();
|
final List<Long> globalSequences = model.getGlobalSequences();
|
||||||
final int globalSequenceId = timeline.getGlobalSequenceId();
|
final int globalSequenceId = timeline.getGlobalSequenceId();
|
||||||
final Integer forcedInterp = forcedInterpMap.get(timeline.getName());
|
final Integer forcedInterp = forcedInterpMap.get(timeline.getName());
|
||||||
|
@ -3,9 +3,9 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.ParseUtils;
|
import com.etheller.warsmash.util.ParseUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public final class SdSequence<TYPE> {
|
public final class SdSequence<TYPE> {
|
||||||
private static boolean INJECT_FRAMES_GHOSTWOLF_STYLE = false;
|
private static boolean INJECT_FRAMES_GHOSTWOLF_STYLE = false;
|
||||||
@ -19,7 +19,7 @@ public final class SdSequence<TYPE> {
|
|||||||
public TYPE[] outTans;
|
public TYPE[] outTans;
|
||||||
public boolean constant;
|
public boolean constant;
|
||||||
|
|
||||||
public SdSequence(final Sd<TYPE> sd, final long start, final long end, final Timeline<TYPE> timeline,
|
public SdSequence(final Sd<TYPE> sd, final long start, final long end, final MdlxTimeline<TYPE> timeline,
|
||||||
final boolean isGlobalSequence, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
final boolean isGlobalSequence, final SdArrayDescriptor<TYPE> arrayDescriptor) {
|
||||||
this.sd = sd;
|
this.sd = sd;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
@ -136,22 +136,22 @@ public final class SdSequence<TYPE> {
|
|||||||
this.outTans = outTansBuilder.toArray(arrayDescriptor.create(outTansBuilder.size()));
|
this.outTans = outTansBuilder.toArray(arrayDescriptor.create(outTansBuilder.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TYPE[] getValues(final Timeline<TYPE> timeline) {
|
private TYPE[] getValues(final MdlxTimeline<TYPE> timeline) {
|
||||||
final TYPE[] values = timeline.getValues();
|
final TYPE[] values = timeline.getValues();
|
||||||
return fixTimelineArray(timeline, values);
|
return fixTimelineArray(timeline, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TYPE[] getOutTans(final Timeline<TYPE> timeline) {
|
private TYPE[] getOutTans(final MdlxTimeline<TYPE> timeline) {
|
||||||
final TYPE[] outTans = timeline.getOutTans();
|
final TYPE[] outTans = timeline.getOutTans();
|
||||||
return fixTimelineArray(timeline, outTans);
|
return fixTimelineArray(timeline, outTans);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TYPE[] getInTans(final Timeline<TYPE> timeline) {
|
private TYPE[] getInTans(final MdlxTimeline<TYPE> timeline) {
|
||||||
final TYPE[] inTans = timeline.getInTans();
|
final TYPE[] inTans = timeline.getInTans();
|
||||||
return fixTimelineArray(timeline, inTans);
|
return fixTimelineArray(timeline, inTans);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TYPE[] fixTimelineArray(final Timeline<TYPE> timeline, final TYPE[] values) {
|
private TYPE[] fixTimelineArray(final MdlxTimeline<TYPE> timeline, final TYPE[] values) {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxExtent;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxSequence;
|
||||||
|
|
||||||
|
public class Sequence {
|
||||||
|
private final MdlxSequence sequence;
|
||||||
|
private final EnumSet<AnimationTokens.PrimaryTag> primaryTags = EnumSet.noneOf(AnimationTokens.PrimaryTag.class);
|
||||||
|
private final EnumSet<AnimationTokens.SecondaryTag> secondaryTags = EnumSet
|
||||||
|
.noneOf(AnimationTokens.SecondaryTag.class);
|
||||||
|
|
||||||
|
public Sequence(final MdlxSequence sequence) {
|
||||||
|
this.sequence = sequence;
|
||||||
|
populateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateTags() {
|
||||||
|
this.primaryTags.clear();
|
||||||
|
this.secondaryTags.clear();
|
||||||
|
TokenLoop: for (final String token : this.sequence.name.split("\\s+")) {
|
||||||
|
final String upperCaseToken = token.toUpperCase();
|
||||||
|
for (final PrimaryTag primaryTag : PrimaryTag.values()) {
|
||||||
|
if (upperCaseToken.equals(primaryTag.name())) {
|
||||||
|
this.primaryTags.add(primaryTag);
|
||||||
|
continue TokenLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
|
||||||
|
if (upperCaseToken.equals(secondaryTag.name())) {
|
||||||
|
this.secondaryTags.add(secondaryTag);
|
||||||
|
continue TokenLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.sequence.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getInterval() {
|
||||||
|
return this.sequence.getInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getMoveSpeed() {
|
||||||
|
return this.sequence.getMoveSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFlags() {
|
||||||
|
return this.sequence.getFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRarity() {
|
||||||
|
return this.sequence.getRarity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSyncPoint() {
|
||||||
|
return this.sequence.getSyncPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MdlxExtent getExtent() {
|
||||||
|
return this.sequence.getExtent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumSet<AnimationTokens.PrimaryTag> getPrimaryTags() {
|
||||||
|
return this.primaryTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumSet<AnimationTokens.SecondaryTag> getSecondaryTags() {
|
||||||
|
return this.secondaryTags;
|
||||||
|
}
|
||||||
|
}
|
@ -6,14 +6,14 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
|
|
||||||
public class SetupGeosets {
|
public class SetupGeosets {
|
||||||
private static final int NORMAL_BATCH = 0;
|
private static final int NORMAL_BATCH = 0;
|
||||||
private static final int EXTENDED_BATCH = 1;
|
private static final int EXTENDED_BATCH = 1;
|
||||||
private static final int REFORGED_BATCH = 2;
|
private static final int REFORGED_BATCH = 2;
|
||||||
|
|
||||||
public static void setupGeosets(final MdxModel model, final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets,
|
public static void setupGeosets(final MdxModel model, final List<MdlxGeoset> geosets, final boolean bigNodeSpace) {
|
||||||
final boolean bigNodeSpace) {
|
|
||||||
if (geosets.size() > 0) {
|
if (geosets.size() > 0) {
|
||||||
final GL20 gl = model.viewer.gl;
|
final GL20 gl = model.viewer.gl;
|
||||||
int positionBytes = 0;
|
int positionBytes = 0;
|
||||||
@ -30,7 +30,7 @@ public class SetupGeosets {
|
|||||||
final int extendedBatchBoneCountOffsetBytes = bigNodeSpace ? 32 : 8;
|
final int extendedBatchBoneCountOffsetBytes = bigNodeSpace ? 32 : 8;
|
||||||
|
|
||||||
for (int i = 0, l = geosets.size(); i < l; i++) {
|
for (int i = 0, l = geosets.size(); i < l; i++) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
|
final MdlxGeoset geoset = geosets.get(i);
|
||||||
|
|
||||||
if (true /* geoset.getLod() == 0 */) {
|
if (true /* geoset.getLod() == 0 */) {
|
||||||
final int vertices = geoset.getVertices().length / 3;
|
final int vertices = geoset.getVertices().length / 3;
|
||||||
@ -84,7 +84,7 @@ public class SetupGeosets {
|
|||||||
gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceBytes, null, GL20.GL_STATIC_DRAW);
|
gl.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceBytes, null, GL20.GL_STATIC_DRAW);
|
||||||
|
|
||||||
for (int i = 0, l = geosets.size(); i < l; i++) {
|
for (int i = 0, l = geosets.size(); i < l; i++) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
|
final MdlxGeoset geoset = geosets.get(i);
|
||||||
|
|
||||||
final int batchType = batchTypes[i];
|
final int batchType = batchTypes[i];
|
||||||
if (true /* geoset.lod == 0 */) {
|
if (true /* geoset.lod == 0 */) {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.AnimationMap;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxTextureAnimation;
|
||||||
|
|
||||||
public class TextureAnimation extends AnimatedObject {
|
public class TextureAnimation extends AnimatedObject {
|
||||||
|
|
||||||
public TextureAnimation(final MdxModel model,
|
public TextureAnimation(final MdxModel model, final MdlxTextureAnimation textureAnimation) {
|
||||||
final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation) {
|
|
||||||
super(model, textureAnimation);
|
super(model, textureAnimation);
|
||||||
|
|
||||||
this.addVariants(AnimationMap.KTAT.getWar3id(), "translation");
|
this.addVariants(AnimationMap.KTAT.getWar3id(), "translation");
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public class UInt32Sd extends Sd<long[]> {
|
public class UInt32Sd extends Sd<long[]> {
|
||||||
|
|
||||||
public UInt32Sd(final MdxModel model, final Timeline<long[]> timeline) {
|
public UInt32Sd(final MdxModel model, final MdlxTimeline<long[]> timeline) {
|
||||||
super(model, timeline, SdArrayDescriptor.LONG_ARRAY);
|
super(model, timeline, SdArrayDescriptor.LONG_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
|
||||||
import com.etheller.warsmash.util.Interpolator;
|
import com.etheller.warsmash.util.Interpolator;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||||
|
|
||||||
public class VectorSd extends Sd<float[]> {
|
public class VectorSd extends Sd<float[]> {
|
||||||
|
|
||||||
public VectorSd(final MdxModel model, final Timeline<float[]> timeline) {
|
public VectorSd(final MdxModel model, final MdlxTimeline<float[]> timeline) {
|
||||||
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
super(model, timeline, SdArrayDescriptor.FLOAT_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ public class TgaTexture extends RawOpenGLTextureResource {
|
|||||||
BufferedImage img;
|
BufferedImage img;
|
||||||
try {
|
try {
|
||||||
img = TgaFile.readTGA(this.fetchUrl, src);
|
img = TgaFile.readTGA(this.fetchUrl, src);
|
||||||
update(img);
|
update(img, false);
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
|
|
||||||
public class IndexedSequence {
|
public class IndexedSequence {
|
||||||
public final Sequence sequence;
|
public final Sequence sequence;
|
||||||
|
@ -5,9 +5,9 @@ import java.util.Comparator;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
||||||
|
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import com.badlogic.gdx.audio.Sound;
|
|
||||||
import com.badlogic.gdx.utils.TimeUtils;
|
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
|
||||||
import com.etheller.warsmash.units.DataTable;
|
|
||||||
import com.etheller.warsmash.units.Element;
|
|
||||||
import com.etheller.warsmash.util.DataSourceFileHandle;
|
|
||||||
import com.etheller.warsmash.viewer5.AudioBufferSource;
|
|
||||||
import com.etheller.warsmash.viewer5.AudioContext;
|
|
||||||
import com.etheller.warsmash.viewer5.AudioPanner;
|
|
||||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
|
|
||||||
|
|
||||||
public final class UnitAckSound {
|
|
||||||
private static final UnitAckSound SILENT = new UnitAckSound(0, 0, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
private final List<Sound> sounds = new ArrayList<>();
|
|
||||||
private final float volume;
|
|
||||||
private final float pitch;
|
|
||||||
private final float pitchVariance;
|
|
||||||
private final float minDistance;
|
|
||||||
private final float maxDistance;
|
|
||||||
private final float distanceCutoff;
|
|
||||||
|
|
||||||
private Sound lastPlayedSound;
|
|
||||||
|
|
||||||
public static UnitAckSound create(final DataSource dataSource, final DataTable unitAckSounds,
|
|
||||||
final String soundName, final String soundType) {
|
|
||||||
final Element row = unitAckSounds.get(soundName + soundType);
|
|
||||||
if (row == null) {
|
|
||||||
return SILENT;
|
|
||||||
}
|
|
||||||
final String fileNames = row.getField("FileNames");
|
|
||||||
String directoryBase = row.getField("DirectoryBase");
|
|
||||||
if ((directoryBase.length() > 1) && !directoryBase.endsWith("\\")) {
|
|
||||||
directoryBase += "\\";
|
|
||||||
}
|
|
||||||
final float volume = row.getFieldFloatValue("Volume") / 127f;
|
|
||||||
final float pitch = row.getFieldFloatValue("Pitch");
|
|
||||||
float pitchVariance = row.getFieldFloatValue("PitchVariance");
|
|
||||||
if (pitchVariance == 1.0f) {
|
|
||||||
pitchVariance = 0.0f;
|
|
||||||
}
|
|
||||||
final float minDistance = row.getFieldFloatValue("MinDistance");
|
|
||||||
final float maxDistance = row.getFieldFloatValue("MaxDistance");
|
|
||||||
final float distanceCutoff = row.getFieldFloatValue("DistanceCutoff");
|
|
||||||
final UnitAckSound sound = new UnitAckSound(volume, pitch, pitchVariance, minDistance, maxDistance,
|
|
||||||
distanceCutoff);
|
|
||||||
for (final String fileName : fileNames.split(",")) {
|
|
||||||
String filePath = directoryBase + fileName;
|
|
||||||
if (!filePath.toLowerCase().endsWith(".wav")) {
|
|
||||||
filePath += ".wav";
|
|
||||||
}
|
|
||||||
if (dataSource.has(filePath)) {
|
|
||||||
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnitAckSound(final float volume, final float pitch, final float pitchVariation, final float minDistance,
|
|
||||||
final float maxDistance, final float distanceCutoff) {
|
|
||||||
this.volume = volume;
|
|
||||||
this.pitch = pitch;
|
|
||||||
this.pitchVariance = pitchVariation;
|
|
||||||
this.minDistance = minDistance;
|
|
||||||
this.maxDistance = maxDistance;
|
|
||||||
this.distanceCutoff = distanceCutoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean play(final AudioContext audioContext, final RenderUnit unit) {
|
|
||||||
return play(audioContext, unit, (int) (Math.random() * this.sounds.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean play(final AudioContext audioContext, final RenderUnit unit, final int index) {
|
|
||||||
if (this.sounds.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final long millisTime = TimeUtils.millis();
|
|
||||||
if (millisTime < unit.lastUnitResponseEndTimeMillis) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final AudioPanner panner = audioContext.createPanner();
|
|
||||||
final AudioBufferSource source = audioContext.createBufferSource();
|
|
||||||
|
|
||||||
// Panner settings
|
|
||||||
panner.setPosition(unit.location[0], unit.location[1], unit.location[2]);
|
|
||||||
panner.setDistances(this.distanceCutoff, this.minDistance);
|
|
||||||
panner.connect(audioContext.destination);
|
|
||||||
|
|
||||||
// Source.
|
|
||||||
source.buffer = this.sounds.get(index);
|
|
||||||
source.connect(panner);
|
|
||||||
|
|
||||||
// Make a sound.
|
|
||||||
source.start(0, this.volume,
|
|
||||||
(this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance);
|
|
||||||
this.lastPlayedSound = source.buffer;
|
|
||||||
final float duration = Extensions.audio.getDuration(this.lastPlayedSound);
|
|
||||||
unit.lastUnitResponseEndTimeMillis = millisTime + (long) (1000 * duration);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSoundCount() {
|
|
||||||
return this.sounds.size();
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,11 +52,12 @@ public final class UnitSound {
|
|||||||
final UnitSound sound = new UnitSound(volume, pitch, pitchVariance, minDistance, maxDistance, distanceCutoff);
|
final UnitSound sound = new UnitSound(volume, pitch, pitchVariance, minDistance, maxDistance, distanceCutoff);
|
||||||
for (final String fileName : fileNames.split(",")) {
|
for (final String fileName : fileNames.split(",")) {
|
||||||
String filePath = directoryBase + fileName;
|
String filePath = directoryBase + fileName;
|
||||||
if (!filePath.toLowerCase().endsWith(".wav")) {
|
final int lastDotIndex = filePath.lastIndexOf('.');
|
||||||
filePath += ".wav";
|
if (lastDotIndex != -1) {
|
||||||
|
filePath = filePath.substring(0, lastDotIndex);
|
||||||
}
|
}
|
||||||
if (dataSource.has(filePath)) {
|
if (dataSource.has(filePath + ".wav") || dataSource.has(filePath + ".flac")) {
|
||||||
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath)));
|
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath + ".wav")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sound;
|
return sound;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
@ -10,9 +9,9 @@ import com.badlogic.gdx.graphics.GL20;
|
|||||||
import com.badlogic.gdx.graphics.GL30;
|
import com.badlogic.gdx.graphics.GL30;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||||
|
|
||||||
public class CliffMesh {
|
public class CliffMesh {
|
||||||
public int vertexBuffer;
|
public int vertexBuffer;
|
||||||
@ -28,11 +27,8 @@ public class CliffMesh {
|
|||||||
public CliffMesh(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
public CliffMesh(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
||||||
this.gl = gl;
|
this.gl = gl;
|
||||||
if (path.endsWith(".mdx") || path.endsWith(".MDX")) {
|
if (path.endsWith(".mdx") || path.endsWith(".MDX")) {
|
||||||
MdlxModel model;
|
final MdlxModel model = new MdlxModel(dataSource.read(path));
|
||||||
try (InputStream stream = dataSource.getResourceAsStream(path)) {
|
final MdlxGeoset geoset = model.getGeosets().get(0);
|
||||||
model = new MdlxModel(stream);
|
|
||||||
}
|
|
||||||
final Geoset geoset = model.getGeosets().get(0);
|
|
||||||
|
|
||||||
this.vertexBuffer = gl.glGenBuffer();
|
this.vertexBuffer = gl.glGenBuffer();
|
||||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||||
|
@ -2,15 +2,12 @@ package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
|||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.GL30;
|
import com.badlogic.gdx.graphics.GL30;
|
||||||
import com.etheller.warsmash.datasources.DataSource;
|
import com.etheller.warsmash.datasources.DataSource;
|
||||||
import com.etheller.warsmash.util.ImageUtils;
|
import com.etheller.warsmash.util.ImageUtils;
|
||||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
import com.etheller.warsmash.util.ImageUtils.AnyExtensionImage;
|
||||||
|
|
||||||
public class GroundTexture {
|
public class GroundTexture {
|
||||||
public int id;
|
public int id;
|
||||||
@ -18,34 +15,15 @@ public class GroundTexture {
|
|||||||
public boolean extended;
|
public boolean extended;
|
||||||
|
|
||||||
public GroundTexture(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
public GroundTexture(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
||||||
if (path.toLowerCase().endsWith(".blp")) {
|
final AnyExtensionImage imageInfo = ImageUtils.getAnyExtensionImageFixRGB(dataSource, path, "ground texture");
|
||||||
try (InputStream stream = dataSource.getResourceAsStream(path)) {
|
loadImage(path, gl, imageInfo.getImageData(), imageInfo.isNeedsSRGBFix());
|
||||||
if (stream == null) {
|
|
||||||
final String tgaPath = path.substring(0, path.length() - 4) + ".tga";
|
|
||||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
|
||||||
if (tgaStream != null) {
|
|
||||||
final BufferedImage tgaData = TgaFile.readTGA(tgaPath, tgaStream);
|
|
||||||
loadImage(path, gl, tgaData);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Missing ground texture: " + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final BufferedImage image = ImageIO.read(stream);
|
|
||||||
loadImage(path, gl, image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadImage(final String path, final GL30 gl, final BufferedImage image) {
|
private void loadImage(final String path, final GL30 gl, final BufferedImage image, final boolean sRGBFix) {
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
throw new IllegalStateException("Missing ground texture: " + path);
|
throw new IllegalStateException("Missing ground texture: " + path);
|
||||||
}
|
}
|
||||||
final Buffer buffer = ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image));
|
final Buffer buffer = ImageUtils.getTextureBuffer(sRGBFix ? ImageUtils.forceBufferedImagesRGB(image) : image);
|
||||||
final int width = image.getWidth();
|
final int width = image.getWidth();
|
||||||
final int height = image.getHeight();
|
final int height = image.getHeight();
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@ import java.util.Map;
|
|||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import org.apache.commons.compress.utils.IOUtils;
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
@ -36,6 +34,7 @@ import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
|||||||
import com.etheller.warsmash.units.DataTable;
|
import com.etheller.warsmash.units.DataTable;
|
||||||
import com.etheller.warsmash.units.Element;
|
import com.etheller.warsmash.units.Element;
|
||||||
import com.etheller.warsmash.util.ImageUtils;
|
import com.etheller.warsmash.util.ImageUtils;
|
||||||
|
import com.etheller.warsmash.util.ImageUtils.AnyExtensionImage;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
import com.etheller.warsmash.util.War3ID;
|
import com.etheller.warsmash.util.War3ID;
|
||||||
import com.etheller.warsmash.util.WorldEditStrings;
|
import com.etheller.warsmash.util.WorldEditStrings;
|
||||||
@ -46,7 +45,6 @@ import com.etheller.warsmash.viewer5.Texture;
|
|||||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
|
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||||
@ -257,31 +255,12 @@ public class Terrain {
|
|||||||
}
|
}
|
||||||
final String texDir = cliffInfo.getField("texDir");
|
final String texDir = cliffInfo.getField("texDir");
|
||||||
final String texFile = cliffInfo.getField("texFile");
|
final String texFile = cliffInfo.getField("texFile");
|
||||||
try (InputStream imageStream = dataSource.getResourceAsStream(texDir + "\\" + texFile + texturesExt)) {
|
final AnyExtensionImage imageInfo = ImageUtils.getAnyExtensionImageFixRGB(dataSource,
|
||||||
final BufferedImage image;
|
texDir + "\\" + texFile + texturesExt, "cliff texture");
|
||||||
if (imageStream == null) {
|
final BufferedImage image = imageInfo.getRGBCorrectImageData();
|
||||||
final String tgaPath = texDir + "\\" + texFile + ".tga";
|
this.cliffTextures
|
||||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
.add(new UnloadedTexture(image.getWidth(), image.getHeight(), ImageUtils.getTextureBuffer(image),
|
||||||
if (tgaStream != null) {
|
cliffInfo.getField("cliffModelDir"), cliffInfo.getField("rampModelDir")));
|
||||||
image = TgaFile.readTGA(tgaPath, tgaStream);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Missing cliff texture: " + texDir + "\\" + texFile + texturesExt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
image = ImageIO.read(imageStream);
|
|
||||||
if (image == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Missing cliff texture: " + texDir + "\\" + texFile + texturesExt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.cliffTextures.add(new UnloadedTexture(image.getWidth(), image.getHeight(),
|
|
||||||
ImageUtils.getTextureBuffer(ImageUtils.forceBufferedImagesRGB(image)),
|
|
||||||
cliffInfo.getField("cliffModelDir"), cliffInfo.getField("rampModelDir")));
|
|
||||||
}
|
|
||||||
this.cliffTexturesSize = Math.max(this.cliffTexturesSize,
|
this.cliffTexturesSize = Math.max(this.cliffTexturesSize,
|
||||||
this.cliffTextures.get(this.cliffTextures.size() - 1).width);
|
this.cliffTextures.get(this.cliffTextures.size() - 1).width);
|
||||||
this.cliffToGroundTexture.add(this.groundTextureToId.get(cliffInfo.getField("groundTile")));
|
this.cliffToGroundTexture.add(this.groundTextureToId.get(cliffInfo.getField("groundTile")));
|
||||||
@ -381,26 +360,32 @@ public class Terrain {
|
|||||||
// Water textures
|
// Water textures
|
||||||
this.waterTextureArray = gl.glGenTexture();
|
this.waterTextureArray = gl.glGenTexture();
|
||||||
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
|
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
|
||||||
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, GL30.GL_SRGB8_ALPHA8, 128, 128, this.waterTextureCount, 0,
|
|
||||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
final String fileName = waterInfo.getField("texFile");
|
||||||
|
final List<BufferedImage> waterTextures = new ArrayList<>();
|
||||||
|
boolean anyWaterTextureNeedsSRGB = false;
|
||||||
|
for (int i = 0; i < this.waterTextureCount; i++) {
|
||||||
|
final AnyExtensionImage imageInfo = ImageUtils.getAnyExtensionImageFixRGB(dataSource,
|
||||||
|
fileName + (i < 10 ? "0" : "") + Integer.toString(i) + texturesExt, "water texture");
|
||||||
|
final BufferedImage image = imageInfo.getImageData();
|
||||||
|
if ((image.getWidth() != 128) || (image.getHeight() != 128)) {
|
||||||
|
System.err
|
||||||
|
.println("Odd water texture size detected of " + image.getWidth() + " x " + image.getHeight());
|
||||||
|
}
|
||||||
|
anyWaterTextureNeedsSRGB |= imageInfo.isNeedsSRGBFix();
|
||||||
|
waterTextures.add(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glTexImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, anyWaterTextureNeedsSRGB ? GL30.GL_SRGB8_ALPHA8 : GL30.GL_RGBA8,
|
||||||
|
128, 128, this.waterTextureCount, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, null);
|
||||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_S, GL30.GL_CLAMP_TO_EDGE);
|
||||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_WRAP_T, GL30.GL_CLAMP_TO_EDGE);
|
||||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_BASE_LEVEL, 0);
|
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
|
|
||||||
final String fileName = waterInfo.getField("texFile");
|
for (int i = 0; i < waterTextures.size(); i++) {
|
||||||
for (int i = 0; i < this.waterTextureCount; i++) {
|
final BufferedImage image = waterTextures.get(i);
|
||||||
|
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, image.getWidth(), image.getHeight(), 1,
|
||||||
try (InputStream imageStream = dataSource
|
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, ImageUtils.getTextureBuffer(image));
|
||||||
.getResourceAsStream(fileName + (i < 10 ? "0" : "") + Integer.toString(i) + texturesExt)) {
|
|
||||||
final BufferedImage image = ImageIO.read(imageStream);
|
|
||||||
if ((image.getWidth() != 128) || (image.getHeight() != 128)) {
|
|
||||||
System.err.println(
|
|
||||||
"Odd water texture size detected of " + image.getWidth() + " x " + image.getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.glTexSubImage3D(GL30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, image.getWidth(), image.getHeight(), 1,
|
|
||||||
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, ImageUtils.getTextureBuffer(image));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
gl.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);
|
gl.glGenerateMipmap(GL30.GL_TEXTURE_2D_ARRAY);
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
||||||
|
@ -3,9 +3,9 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Quaternion;
|
import com.badlogic.gdx.math.Quaternion;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
||||||
|
@ -6,12 +6,12 @@ import java.util.Queue;
|
|||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.math.Quaternion;
|
import com.badlogic.gdx.math.Quaternion;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
|
||||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||||
import com.etheller.warsmash.util.RenderMathUtils;
|
import com.etheller.warsmash.util.RenderMathUtils;
|
||||||
import com.etheller.warsmash.util.War3ID;
|
import com.etheller.warsmash.util.War3ID;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||||
|
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
||||||
|
@ -11,8 +11,6 @@ import java.util.List;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.Input;
|
import com.badlogic.gdx.Input;
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
@ -45,7 +43,6 @@ import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
|
|||||||
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
|
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
|
||||||
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
|
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
|
||||||
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
|
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
|
||||||
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
|
|
||||||
import com.etheller.warsmash.units.Element;
|
import com.etheller.warsmash.units.Element;
|
||||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||||
import com.etheller.warsmash.util.FastNumberFormat;
|
import com.etheller.warsmash.util.FastNumberFormat;
|
||||||
@ -133,6 +130,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.ClickableActionFram
|
|||||||
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandListener;
|
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandListener;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
|
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.QueueIconListener;
|
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.QueueIconListener;
|
||||||
|
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode;
|
||||||
|
|
||||||
public class MeleeUI implements CUnitStateListener, CommandButtonListener, CommandCardCommandListener,
|
public class MeleeUI implements CUnitStateListener, CommandButtonListener, CommandCardCommandListener,
|
||||||
QueueIconListener, CommandErrorListener, CPlayerStateListener {
|
QueueIconListener, CommandErrorListener, CPlayerStateListener {
|
||||||
@ -276,7 +274,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
|||||||
this.cameraManager.target.x = startLocation[0];
|
this.cameraManager.target.x = startLocation[0];
|
||||||
this.cameraManager.target.y = startLocation[1];
|
this.cameraManager.target.y = startLocation[1];
|
||||||
|
|
||||||
this.activeButtonTexture = ImageUtils.getBLPTexture(war3MapViewer.mapMpq,
|
this.activeButtonTexture = ImageUtils.getAnyExtensionTexture(war3MapViewer.mapMpq,
|
||||||
"UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp");
|
"UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp");
|
||||||
this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter();
|
this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter();
|
||||||
this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f;
|
this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f;
|
||||||
@ -303,18 +301,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (war3MapViewer.dataSource.has("war3mapMap.blp")) {
|
else if (war3MapViewer.dataSource.has("war3mapMap.blp")) {
|
||||||
try {
|
minimapTexture = ImageUtils.getAnyExtensionTexture(war3MapViewer.dataSource, "war3mapMap.blp");
|
||||||
minimapTexture = ImageUtils
|
|
||||||
.getTexture(ImageIO.read(war3MapViewer.dataSource.getResourceAsStream("war3mapMap.blp")));
|
|
||||||
}
|
|
||||||
catch (final IOException e) {
|
|
||||||
System.err.println("Could not load minimap BLP file");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS];
|
final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS];
|
||||||
for (int i = 0; i < teamColors.length; i++) {
|
for (int i = 0; i < teamColors.length; i++) {
|
||||||
teamColors[i] = ImageUtils.getBLPTexture(war3MapViewer.dataSource,
|
teamColors[i] = ImageUtils.getAnyExtensionTexture(war3MapViewer.dataSource,
|
||||||
"ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(i) + ".blp");
|
"ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(i) + ".blp");
|
||||||
}
|
}
|
||||||
final Rectangle playableMapArea = war3MapViewer.terrain.getPlayableMapArea();
|
final Rectangle playableMapArea = war3MapViewer.terrain.getPlayableMapArea();
|
||||||
|
53
core/src/com/hiveworkshop/ReteraCASCUtils.java
Normal file
53
core/src/com/hiveworkshop/ReteraCASCUtils.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package com.hiveworkshop;
|
||||||
|
|
||||||
|
public class ReteraCASCUtils {
|
||||||
|
|
||||||
|
public static boolean arraysEquals(final byte[] a, final int aFromIndex, final int aToIndex, final byte[] b,
|
||||||
|
final int bFromIndex, final int bToIndex) {
|
||||||
|
if (a == null) {
|
||||||
|
if (b == null) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((aToIndex - aFromIndex) != (bToIndex - bFromIndex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int j = bFromIndex;
|
||||||
|
for (int i = aFromIndex; i < aToIndex; i++) {
|
||||||
|
if (a[i] != b[j++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int arraysCompareUnsigned(final byte[] a, final int aFromIndex, final int aToIndex, final byte[] b,
|
||||||
|
final int bFromIndex, final int bToIndex) {
|
||||||
|
final int i = arraysMismatch(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
|
||||||
|
if ((i >= 0) && (i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex))) {
|
||||||
|
return byteCompareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
|
||||||
|
}
|
||||||
|
return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int byteCompareUnsigned(final byte b, final byte c) {
|
||||||
|
return Integer.compare(b & 0xFF, c & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int arraysMismatch(final byte[] a, final int aFromIndex, final int aToIndex, final byte[] b,
|
||||||
|
final int bFromIndex, final int bToIndex) {
|
||||||
|
final int aLength = aToIndex - aFromIndex;
|
||||||
|
final int bLength = bToIndex - bFromIndex;
|
||||||
|
for (int i = 0; (i < aLength) && (i < bLength); i++) {
|
||||||
|
if (a[aFromIndex + i] != b[bFromIndex + i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Math.min(aLength, bLength);
|
||||||
|
}
|
||||||
|
}
|
118
core/src/com/hiveworkshop/blizzard/casc/ConfigurationFile.java
Normal file
118
core/src/com/hiveworkshop/blizzard/casc/ConfigurationFile.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package com.hiveworkshop.blizzard.casc;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import com.hiveworkshop.blizzard.casc.nio.MalformedCASCStructureException;
|
||||||
|
import com.hiveworkshop.nio.ByteBufferInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File containing CASC configuration information. This is basically a
|
||||||
|
* collection of keys with their assigned value. What the values mean depends on
|
||||||
|
* the purpose of the key.
|
||||||
|
*/
|
||||||
|
public class ConfigurationFile {
|
||||||
|
/**
|
||||||
|
* The name of the data folder containing the configuration files.
|
||||||
|
*/
|
||||||
|
public static final String CONFIGURATION_FOLDER_NAME = "config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character encoding used by configuration files.
|
||||||
|
*/
|
||||||
|
public static final Charset FILE_ENCODING = Charset.forName("UTF8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the configuration bucket folder names.
|
||||||
|
*/
|
||||||
|
public static final int BUCKET_NAME_LENGTH = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of configuration bucket folder tiers.
|
||||||
|
*/
|
||||||
|
public static final int BUCKET_TIERS = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a configuration file from the data folder by its key.
|
||||||
|
*
|
||||||
|
* @param dataFolder Path of the CASC data folder.
|
||||||
|
* @param keyHex Key for configuration file as a hexadecimal string.
|
||||||
|
* @return The requested configuration file.
|
||||||
|
* @throws IOException If an exception occurs when retrieving the file.
|
||||||
|
*/
|
||||||
|
public static ConfigurationFile lookupConfigurationFile(final Path dataFolder, final String keyHex)
|
||||||
|
throws IOException {
|
||||||
|
Path file = dataFolder.resolve(CONFIGURATION_FOLDER_NAME);
|
||||||
|
for (int tier = 0; tier < BUCKET_TIERS; tier += 1) {
|
||||||
|
final int keyOffset = tier * BUCKET_NAME_LENGTH;
|
||||||
|
final String bucketFolderName = keyHex.substring(keyOffset, keyOffset + BUCKET_NAME_LENGTH);
|
||||||
|
file = file.resolve(bucketFolderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
file = file.resolve(keyHex);
|
||||||
|
|
||||||
|
final ByteBuffer fileBuffer = ByteBuffer.wrap(Files.readAllBytes(file));
|
||||||
|
|
||||||
|
return new ConfigurationFile(fileBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Underlying map holding the configuration data.
|
||||||
|
*/
|
||||||
|
private final Map<String, String> configuration = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a configuration file by decoding a file buffer.
|
||||||
|
*
|
||||||
|
* @param fileBuffer File buffer to decode from.
|
||||||
|
* @throws IOException If one or more IO errors occur.
|
||||||
|
*/
|
||||||
|
public ConfigurationFile(final ByteBuffer fileBuffer) throws IOException {
|
||||||
|
try (final ByteBufferInputStream fileStream = new ByteBufferInputStream(fileBuffer);
|
||||||
|
final Scanner lineScanner = new Scanner(new InputStreamReader(fileStream, FILE_ENCODING))) {
|
||||||
|
while (lineScanner.hasNextLine()) {
|
||||||
|
final String line = lineScanner.nextLine().trim();
|
||||||
|
final int lineLength = line.indexOf('#');
|
||||||
|
final String record;
|
||||||
|
if (lineLength != -1) {
|
||||||
|
record = line.substring(0, lineLength);
|
||||||
|
} else {
|
||||||
|
record = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!record.equals("")) {
|
||||||
|
final int assignmentIndex = record.indexOf('=');
|
||||||
|
if (assignmentIndex == -1) {
|
||||||
|
throw new MalformedCASCStructureException(
|
||||||
|
"configuration file line contains record with no assignment");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String key = record.substring(0, assignmentIndex).trim();
|
||||||
|
final String value = record.substring(assignmentIndex + 1).trim();
|
||||||
|
|
||||||
|
if (configuration.putIfAbsent(key, value) != null) {
|
||||||
|
throw new MalformedCASCStructureException(
|
||||||
|
"configuration file contains duplicate key declarations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configuration defined by the file.
|
||||||
|
*
|
||||||
|
* @return Configuration map.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getConfiguration() {
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user