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 {
|
||||
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
flatDir {
|
||||
@ -9,13 +7,10 @@ buildscript {
|
||||
mavenCentral()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
maven { url "https://maven.nikr.net/" }
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -39,6 +34,7 @@ allprojects {
|
||||
google()
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
|
||||
maven { url "https://maven.nikr.net/" }
|
||||
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-freetype-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.google.guava:guava:23.5-jre"
|
||||
compile "net.nikr:dds:1.0.0"
|
||||
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
||||
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-freetype:$gdxVersion"
|
||||
compile "com.google.guava:guava:23.5-jre"
|
||||
compile "net.nikr:dds:1.0.0"
|
||||
implementation 'com.github.inwc3:wc3libs:-SNAPSHOT'
|
||||
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.Vector3;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.util.DataSourceFileHandle;
|
||||
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.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
|
||||
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
||||
|
@ -6,6 +6,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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.Rectangle;
|
||||
import com.badlogic.gdx.utils.viewport.ExtendViewport;
|
||||
import com.etheller.warsmash.datasources.CascDataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
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 {
|
||||
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 War3MapViewer viewer;
|
||||
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.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource,
|
||||
this.solidGreenTexture = ImageUtils.getAnyExtensionTexture(this.viewer.dataSource,
|
||||
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
||||
|
||||
Gdx.input.setInputProcessor(this);
|
||||
@ -260,6 +262,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
dataSourcesList.add(new MpqDataSourceDescriptor(path));
|
||||
break;
|
||||
}
|
||||
case "CASC": {
|
||||
final String prefixes = dataSourcesConfig.getField("Prefixes" + (i < 10 ? "0" : "") + i);
|
||||
dataSourcesList.add(new CascDataSourceDescriptor(path, Arrays.asList(prefixes.split(","))));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
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.parsers.fdf.GameUI;
|
||||
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.util.DataSourceFileHandle;
|
||||
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.MdxModel;
|
||||
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.w3x.War3MapViewer;
|
||||
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.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource,
|
||||
this.solidGreenTexture = ImageUtils.getAnyExtensionTexture(this.viewer.dataSource,
|
||||
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
||||
|
||||
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.math.Rectangle;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
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.MdxViewer;
|
||||
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 {
|
||||
private DataSource codebase;
|
||||
@ -138,7 +139,7 @@ public class WarsmashPreviewApplication extends ApplicationAdapter implements Ca
|
||||
this.mdxHandler, ".mdx", PathSolver.DEFAULT, filename));
|
||||
final MdlxModel mdlxModel;
|
||||
try (FileInputStream stream = new FileInputStream(filename)) {
|
||||
mdlxModel = new MdlxModel(stream);
|
||||
mdlxModel = MdxUtils.loadMdlx(stream);
|
||||
mdx.load(mdlxModel);
|
||||
mdx.ok = true;
|
||||
// mdx.lateLoad();
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
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.DataSourceDescriptor;
|
||||
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
|
||||
import com.etheller.warsmash.parsers.mdlx.Geoset;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||
|
||||
public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
||||
private int arrayBuffer;
|
||||
@ -36,8 +35,8 @@ public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
||||
.createDataSource();
|
||||
|
||||
final MdlxModel model;
|
||||
try (InputStream modelStream = this.codebase.getResourceAsStream("Buildings\\Other\\TempArtB\\TempArtB.mdx")) {
|
||||
model = new MdlxModel(modelStream);
|
||||
try {
|
||||
model = new MdlxModel(this.codebase.read("Buildings\\Other\\TempArtB\\TempArtB.mdx"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -67,7 +66,7 @@ public class WarsmashTestGameAttributes2 extends ApplicationAdapter {
|
||||
|
||||
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 ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(4 * 9);
|
||||
vertexByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
@ -1,10 +1,7 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
@ -32,13 +29,7 @@ public class WarsmashTestMyTextureGame extends ApplicationAdapter {
|
||||
this.codebase = new CompoundDataSourceDescriptor(
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
||||
|
||||
try {
|
||||
this.texture = ImageUtils
|
||||
.getTexture(ImageIO.read(this.codebase.getResourceAsStream("Textures\\Dust3x.blp")));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.texture = ImageUtils.getAnyExtensionTexture(this.codebase, "Textures\\Dust3x.blp");
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
this.batch = new SpriteBatch();
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -44,11 +44,23 @@ public class CompoundDataSource implements DataSource {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (filepath.toLowerCase(Locale.US).endsWith(".blp")) {
|
||||
return getFile(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
||||
return null;
|
||||
}
|
||||
if (filepath.toLowerCase(Locale.US).endsWith(".tif")) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -68,12 +80,6 @@ public class CompoundDataSource implements DataSource {
|
||||
// TODO Auto-generated catch block
|
||||
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;
|
||||
}
|
||||
|
||||
@ -88,12 +94,6 @@ public class CompoundDataSource implements DataSource {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package com.etheller.warsmash.datasources;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface DataSource {
|
||||
@ -27,6 +28,8 @@ public interface DataSource {
|
||||
*/
|
||||
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.
|
||||
* 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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@ -34,7 +36,7 @@ public class FolderDataSource implements DataSource {
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String filepath) throws IOException {
|
||||
filepath=fixFilepath(filepath);
|
||||
filepath = fixFilepath(filepath);
|
||||
if (!has(filepath)) {
|
||||
return null;
|
||||
}
|
||||
@ -43,16 +45,25 @@ public class FolderDataSource implements DataSource {
|
||||
|
||||
@Override
|
||||
public File getFile(String filepath) throws IOException {
|
||||
filepath=fixFilepath(filepath);
|
||||
filepath = fixFilepath(filepath);
|
||||
if (!has(filepath)) {
|
||||
return null;
|
||||
}
|
||||
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
|
||||
public boolean has(String filepath) {
|
||||
filepath=fixFilepath(filepath);
|
||||
filepath = fixFilepath(filepath);
|
||||
if ("".equals(filepath)) {
|
||||
return false; // special case for folder data source, dont do this
|
||||
}
|
||||
@ -69,7 +80,8 @@ public class FolderDataSource implements DataSource {
|
||||
public void close() {
|
||||
}
|
||||
|
||||
private static String fixFilepath(String filepath) {
|
||||
return filepath.replace('\\', File.separatorChar).replace('/', File.separatorChar);
|
||||
private static String fixFilepath(final String filepath) {
|
||||
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.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
@ -57,6 +58,28 @@ public class MpqDataSource implements DataSource {
|
||||
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
|
||||
public File getFile(final String filepath) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
@ -72,4 +72,7 @@ public class MpqDataSourceDescriptor implements DataSourceDescriptor {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getMpqFilePath() {
|
||||
return this.mpqFilePath;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.etheller.warsmash.datasources;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -21,6 +22,11 @@ public class SubdirDataSource implements DataSource {
|
||||
return this.dataSource.getFile(this.subdir + filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer read(final String path) throws IOException {
|
||||
return this.dataSource.read(this.subdir + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) throws IOException {
|
||||
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.TextureFrame;
|
||||
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.Element;
|
||||
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.handlers.AbstractMdxModelViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode;
|
||||
|
||||
public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
private final DataSource dataSource;
|
||||
@ -658,7 +658,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
Texture texture = this.pathToTexture.get(path);
|
||||
if (texture == null) {
|
||||
try {
|
||||
texture = ImageUtils.getBLPTexture(this.dataSource, path);
|
||||
texture = ImageUtils.getAnyExtensionTexture(this.dataSource, path);
|
||||
this.pathToTexture.put(path, texture);
|
||||
}
|
||||
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.DataSourceDescriptor;
|
||||
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 {
|
||||
|
||||
@ -24,9 +25,9 @@ public class ModelExport {
|
||||
|
||||
try (InputStream modelStream = dataSource
|
||||
.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"))) {
|
||||
model.saveMdl(fos);
|
||||
MdxUtils.saveMdl(model, fos);
|
||||
}
|
||||
}
|
||||
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.SpriteBatch;
|
||||
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 {
|
||||
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.datamodel.BackdropCornerFlags;
|
||||
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.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 {
|
||||
private final boolean decorateFileNames;
|
||||
@ -49,21 +50,21 @@ public class SmartBackdropFrame extends SpriteFrame {
|
||||
final MdlxModel model = new MdlxModel();
|
||||
final int edgeFileMaterialId = generateMaterial(model, this.edgeFileString, true);
|
||||
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];
|
||||
return model;
|
||||
}
|
||||
|
||||
private int generateMaterial(final MdlxModel model, final String path, final boolean wrap) {
|
||||
final Texture edgeFileReference = new Texture();
|
||||
final MdlxTexture edgeFileReference = new MdlxTexture();
|
||||
if (wrap) {
|
||||
edgeFileReference.setFlags(0x2 | 0x1);
|
||||
edgeFileReference.setWrapMode(WrapMode.REPEAT_BOTH);
|
||||
}
|
||||
edgeFileReference.setPath(path);
|
||||
final int textureId = model.getTextures().size();
|
||||
model.getTextures().add(edgeFileReference);
|
||||
final Material edgeFileMaterial = new Material();
|
||||
final Layer edgeFileMaterialLayer = new Layer();
|
||||
final MdlxMaterial edgeFileMaterial = new MdlxMaterial();
|
||||
final MdlxLayer edgeFileMaterialLayer = new MdlxLayer();
|
||||
edgeFileMaterialLayer.setAlpha(1.0f);
|
||||
edgeFileMaterialLayer.setFilterMode(FilterMode.BLEND);
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -119,6 +120,11 @@ public class War3Map implements DataSource {
|
||||
return this.dataSource.has(filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer read(final String path) throws IOException {
|
||||
return this.dataSource.read(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getListfile() {
|
||||
return this.internalMpqContentsDataSource.getListfile();
|
||||
|
@ -101,6 +101,10 @@ public class StandardObjectData {
|
||||
|
||||
try {
|
||||
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) {
|
||||
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\\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"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
|
@ -10,7 +10,7 @@ public class DataSourceFileHandle extends FileHandle {
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DataSourceFileHandle(final DataSource dataSource, final String path) {
|
||||
super(path);
|
||||
super(fixPath(dataSource, path));
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@ -28,4 +28,14 @@ public class DataSourceFileHandle extends FileHandle {
|
||||
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;
|
||||
public static final String DEFAULT_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNTemp.blp";
|
||||
|
||||
public static Texture getBLPTexture(final DataSource dataSource, final String path) {
|
||||
final BufferedImage image = getBLPImage(dataSource, path);
|
||||
public static Texture getAnyExtensionTexture(final DataSource dataSource, final String path) {
|
||||
BufferedImage image;
|
||||
try {
|
||||
final AnyExtensionImage imageInfo = getAnyExtensionImageFixRGB(dataSource, path, "texture");
|
||||
image = imageInfo.getImageData();
|
||||
if (image != null) {
|
||||
return ImageUtils.getTexture(image);
|
||||
return ImageUtils.getTexture(image, imageInfo.isNeedsSRGBFix());
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
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) {
|
||||
try {
|
||||
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()];
|
||||
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
|
||||
|
||||
@ -75,12 +140,12 @@ public final class ImageUtils {
|
||||
// for
|
||||
// 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
|
||||
public int getGLInternalFormat() {
|
||||
return GL30.GL_SRGB8_ALPHA8;
|
||||
}
|
||||
};
|
||||
} : new Pixmap(image.getWidth(), image.getHeight(), Format.RGBA8888);
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < 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);
|
||||
|
||||
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();
|
||||
isFetch = solved.isFetch();
|
||||
|
||||
@ -185,9 +194,6 @@ public abstract class ModelViewer {
|
||||
|
||||
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
||||
try {
|
||||
if (!this.dataSource.has(finalSrc)) {
|
||||
System.err.println("Attempting to load non-existant file: " + finalSrc);
|
||||
}
|
||||
resource.loadData(this.dataSource.getResourceAsStream(finalSrc), null);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
|
@ -91,7 +91,7 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
||||
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 int imageWidth = image.getWidth();
|
||||
@ -129,8 +129,8 @@ public abstract class RawOpenGLTextureResource extends Texture {
|
||||
// GL20.GL_UNSIGNED_BYTE, buffer);
|
||||
// }
|
||||
// else {
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_SRGB8_ALPHA8, imageWidth, imageHeight, 0, GL20.GL_RGBA,
|
||||
GL20.GL_UNSIGNED_BYTE, buffer);
|
||||
gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, sRGBFix ? GL30.GL_SRGB8_ALPHA8 : GL30.GL_RGBA8, imageWidth, imageHeight,
|
||||
0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
this.width = imageWidth;
|
||||
this.height = imageHeight;
|
||||
|
@ -29,7 +29,7 @@ public class BlpGdxTexture extends GdxTextureResource {
|
||||
BufferedImage img;
|
||||
try {
|
||||
img = ImageIO.read(src);
|
||||
setGdxTexture(ImageUtils.getTexture(img));
|
||||
setGdxTexture(ImageUtils.getTexture(img, true));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -2,8 +2,8 @@ package com.etheller.warsmash.viewer5.handlers.blp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
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;
|
||||
|
||||
|
@ -28,7 +28,7 @@ public class BlpTexture extends RawOpenGLTextureResource {
|
||||
BufferedImage img;
|
||||
try {
|
||||
img = ImageIO.read(src);
|
||||
update(img);
|
||||
update(img, true);
|
||||
}
|
||||
catch (final IOException 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.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.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 MdxModel model;
|
||||
public Map<War3ID, Sd<?>> timelines;
|
||||
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.timelines = 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));
|
||||
}
|
||||
}
|
||||
@ -126,15 +127,15 @@ public class AnimatedObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Sd<?> createTypedSd(final MdxModel model, final Timeline<?> timeline) {
|
||||
if (timeline instanceof UInt32Timeline) {
|
||||
return new UInt32Sd(model, (UInt32Timeline) timeline);
|
||||
private Sd<?> createTypedSd(final MdxModel model, final MdlxTimeline<?> timeline) {
|
||||
if (timeline instanceof MdlxUInt32Timeline) {
|
||||
return new UInt32Sd(model, (MdlxUInt32Timeline) timeline);
|
||||
}
|
||||
else if (timeline instanceof FloatTimeline) {
|
||||
return new ScalarSd(model, (FloatTimeline) timeline);
|
||||
else if (timeline instanceof MdlxFloatTimeline) {
|
||||
return new ScalarSd(model, (MdlxFloatTimeline) timeline);
|
||||
}
|
||||
else if (timeline instanceof FloatArrayTimeline) {
|
||||
final FloatArrayTimeline faTimeline = (FloatArrayTimeline) timeline;
|
||||
else if (timeline instanceof MdlxFloatArrayTimeline) {
|
||||
final MdlxFloatArrayTimeline faTimeline = (MdlxFloatArrayTimeline) timeline;
|
||||
final int arraySize = faTimeline.getArraySize();
|
||||
if (arraySize == 3) {
|
||||
return new VectorSd(model, faTimeline);
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 {
|
||||
protected String name;
|
||||
@ -8,8 +9,7 @@ public class Attachment extends GenericObject {
|
||||
protected final int attachmentId;
|
||||
protected MdxModel internalModel;
|
||||
|
||||
public Attachment(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Attachment attachment,
|
||||
final int index) {
|
||||
public Attachment(final MdxModel model, final MdlxAttachment attachment, final int index) {
|
||||
super(model, attachment, index);
|
||||
|
||||
final String path = attachment.getPath().toLowerCase().replace(".mdl", ".mdx");
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxBone;
|
||||
|
||||
public class Bone extends GenericObject {
|
||||
|
||||
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);
|
||||
|
||||
GeosetAnimation geosetAnimation = null;
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
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 {
|
||||
|
||||
@ -12,7 +13,7 @@ public class Camera extends AnimatedObject {
|
||||
public final float nearClippingPlane;
|
||||
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);
|
||||
|
||||
this.name = camera.getName();
|
||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.math.collision.BoundingBox;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.GenericNode;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxCollisionShape;
|
||||
|
||||
public class CollisionShape extends GenericObject {
|
||||
private static Vector3 intersectHeap = new Vector3();
|
||||
@ -15,8 +16,7 @@ public class CollisionShape extends GenericObject {
|
||||
private static Ray intersectRayHeap = new Ray();
|
||||
private Intersectable intersectable;
|
||||
|
||||
public CollisionShape(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.CollisionShape object,
|
||||
final int index) {
|
||||
public CollisionShape(final MdxModel model, final MdlxCollisionShape object, final int index) {
|
||||
super(model, object, index);
|
||||
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.Texture;
|
||||
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 {
|
||||
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
|
||||
@ -106,8 +108,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
*/
|
||||
private boolean ok = false;
|
||||
|
||||
public EventObjectEmitterObject(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) {
|
||||
public EventObjectEmitterObject(final MdxModel model, final MdlxEventObject eventObject, final int index) {
|
||||
super(model, eventObject, index);
|
||||
|
||||
final ModelViewer viewer = model.viewer;
|
||||
@ -256,8 +257,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
}
|
||||
|
||||
final int[] blendModes = FilterMode
|
||||
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
|
||||
.fromId(getInt(row, "BlendMode")));
|
||||
.emitterFilterMode(MdlxParticleEmitter2.FilterMode.fromId(getInt(row, "BlendMode")));
|
||||
|
||||
this.blendSrc = blendModes[0];
|
||||
this.blendDst = blendModes[1];
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxParticleEmitter2;
|
||||
|
||||
public class FilterMode {
|
||||
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[] 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) {
|
||||
case BLEND:
|
||||
return BLEND; // Blend
|
||||
@ -26,8 +28,7 @@ public class FilterMode {
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] emitterFilterMode(
|
||||
final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode filterMode) {
|
||||
public static int[] emitterFilterMode(final MdlxParticleEmitter2.FilterMode filterMode) {
|
||||
switch (filterMode) {
|
||||
case BLEND:
|
||||
return BLEND; // Blend
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
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 {
|
||||
|
||||
@ -38,8 +39,7 @@ public class GenericObject extends AnimatedObject implements GenericIndexed {
|
||||
public final boolean hasScaleAnim;
|
||||
public final boolean hasGenericAnim;
|
||||
|
||||
public GenericObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.GenericObject object,
|
||||
final int index) {
|
||||
public GenericObject(final MdxModel model, final MdlxGenericObject object, final int index) {
|
||||
super(model, object);
|
||||
|
||||
this.index = index;
|
||||
|
@ -5,6 +5,7 @@ import java.util.Arrays;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||
|
||||
public class Geoset {
|
||||
public MdxModel model;
|
||||
@ -25,12 +26,12 @@ public class Geoset {
|
||||
private final int skinStride;
|
||||
private final int boneCountOffsetBytes;
|
||||
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,
|
||||
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 com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset) {
|
||||
final MdlxGeoset mdlxGeoset) {
|
||||
this.model = model;
|
||||
this.index = index;
|
||||
this.positionOffset = positionOffset;
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 {
|
||||
|
||||
@ -8,8 +9,7 @@ public class GeosetAnimation extends AnimatedObject {
|
||||
private final float[] color;
|
||||
public final int geosetId;
|
||||
|
||||
public GeosetAnimation(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.GeosetAnimation geosetAnimation) {
|
||||
public GeosetAnimation(final MdxModel model, final MdlxGeosetAnimation geosetAnimation) {
|
||||
super(model, geosetAnimation);
|
||||
|
||||
final float[] color = geosetAnimation.getColor();
|
||||
|
@ -1,11 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxGenericObject;
|
||||
|
||||
/**
|
||||
* An MDX helper.
|
||||
*/
|
||||
public class Helper extends GenericObject {
|
||||
public Helper(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.GenericObject object,
|
||||
final int index) {
|
||||
public Helper(final MdxModel model, final MdlxGenericObject object, final int 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.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.
|
||||
@ -26,11 +27,10 @@ public class Layer extends AnimatedObject {
|
||||
public boolean blended;
|
||||
public TextureAnimation textureAnimation;
|
||||
|
||||
public Layer(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Layer layer, final int layerId,
|
||||
final int priorityPlane) {
|
||||
public Layer(final MdxModel model, final MdlxLayer layer, final int layerId, final int priorityPlane) {
|
||||
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 GL20 gl = model.viewer.gl;
|
||||
|
||||
@ -50,8 +50,8 @@ public class Layer extends AnimatedObject {
|
||||
this.noDepthTest = flags & 0x40;
|
||||
this.noDepthSet = flags & 0x80;
|
||||
|
||||
this.depthMaskValue = ((filterMode == com.etheller.warsmash.parsers.mdlx.Layer.FilterMode.NONE)
|
||||
|| (filterMode == com.etheller.warsmash.parsers.mdlx.Layer.FilterMode.TRANSPARENT));
|
||||
this.depthMaskValue = ((filterMode == MdlxLayer.FilterMode.NONE)
|
||||
|| (filterMode == MdlxLayer.FilterMode.TRANSPARENT));
|
||||
|
||||
this.blendSrc = 0;
|
||||
this.blendDst = 0;
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 {
|
||||
|
||||
@ -11,17 +12,17 @@ public class Light extends GenericObject {
|
||||
private final float[] ambientColor;
|
||||
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);
|
||||
|
||||
switch (light.getType()) {
|
||||
case 0:
|
||||
case OMNIDIRECTIONAL:
|
||||
this.type = Type.OMNIDIRECTIONAL;
|
||||
break;
|
||||
case 1:
|
||||
case DIRECTIONAL:
|
||||
this.type = Type.DIRECTIONAL;
|
||||
break;
|
||||
case 2:
|
||||
case AMBIENT:
|
||||
this.type = Type.AMBIENT;
|
||||
break;
|
||||
default:
|
||||
|
@ -10,7 +10,6 @@ import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.GenericNode;
|
||||
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.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||
|
||||
public class MdxComplexInstance extends ModelInstance {
|
||||
private static final float[] visibilityHeap = new float[1];
|
||||
@ -764,7 +764,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
if (!geoset.unselectable) {
|
||||
geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter);
|
||||
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(),
|
||||
mdlxGeoset.getFaces(), 3, intersection)) {
|
||||
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.ResourceHandlerConstructionParams;
|
||||
import com.etheller.warsmash.viewer5.handlers.blp.BlpHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.blp.DdsHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaHandler;
|
||||
|
||||
public class MdxHandler extends ModelHandler {
|
||||
@ -22,6 +23,7 @@ public class MdxHandler extends ModelHandler {
|
||||
@Override
|
||||
public boolean load(final ModelViewer viewer) {
|
||||
viewer.addHandler(new BlpHandler());
|
||||
viewer.addHandler(new DdsHandler());
|
||||
viewer.addHandler(new TgaHandler());
|
||||
|
||||
Shaders.complex = viewer.webGL.createShaderProgram(MdxShaders.vsComplex, MdxShaders.fsComplex);
|
||||
@ -32,7 +34,8 @@ public class MdxHandler extends ModelHandler {
|
||||
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
|
||||
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
|
||||
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
|
||||
//Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
|
||||
// Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple,
|
||||
// MdxShaders.fsSimple);
|
||||
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
|
||||
// TODO HD reforged
|
||||
|
||||
|
@ -2,17 +2,36 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.etheller.warsmash.parsers.mdlx.Extent;
|
||||
import com.etheller.warsmash.parsers.mdlx.MdlxModel;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
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 boolean reforged = false;
|
||||
@ -73,7 +92,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
parser = (MdlxModel) bufferOrParser;
|
||||
}
|
||||
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;
|
||||
@ -86,7 +106,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
this.name = parser.getName();
|
||||
|
||||
// Initialize the bounds.
|
||||
final Extent extent = parser.getExtent();
|
||||
final MdlxExtent extent = parser.getExtent();
|
||||
final float[] min = extent.getMin();
|
||||
final float[] max = extent.getMax();
|
||||
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);
|
||||
|
||||
// Sequences
|
||||
this.sequences.addAll(parser.getSequences());
|
||||
for (final MdlxSequence sequence : parser.getSequences()) {
|
||||
this.sequences.add(new Sequence(sequence));
|
||||
}
|
||||
|
||||
// Global sequences
|
||||
this.globalSequences.addAll(parser.getGlobalSequences());
|
||||
|
||||
// Texture animations
|
||||
for (final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation : parser
|
||||
.getTextureAnimations()) {
|
||||
for (final MdlxTextureAnimation textureAnimation : parser.getTextureAnimations()) {
|
||||
this.textureAnimations.add(new TextureAnimation(this, textureAnimation));
|
||||
}
|
||||
|
||||
// Materials
|
||||
int layerId = 0;
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Material material : parser.getMaterials()) {
|
||||
for (final MdlxMaterial material : parser.getMaterials()) {
|
||||
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());
|
||||
|
||||
layers.add(vLayer);
|
||||
@ -139,10 +160,10 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
final GL20 gl = viewer.gl;
|
||||
|
||||
// Textures.
|
||||
for (final com.etheller.warsmash.parsers.mdlx.Texture texture : parser.getTextures()) {
|
||||
for (final MdlxTexture texture : parser.getTextures()) {
|
||||
String path = texture.getPath();
|
||||
final int replaceableId = texture.getReplaceableId();
|
||||
final int flags = texture.getFlags();
|
||||
final WrapMode wrapMode = texture.getWrapMode();
|
||||
|
||||
if (replaceableId != 0) {
|
||||
// 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);
|
||||
|
||||
// When the texture will load, it will apply its wrap modes.
|
||||
if ((flags & 0x1) != 0) {
|
||||
if (wrapMode.isWrapWidth()) {
|
||||
viewerTexture.setWrapS(true);
|
||||
}
|
||||
|
||||
if ((flags & 0x2) != 0) {
|
||||
if (wrapMode.isWrapHeight()) {
|
||||
viewerTexture.setWrapT(true);
|
||||
}
|
||||
|
||||
@ -176,7 +197,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
@ -189,53 +210,52 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
int objectId = 0;
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// Particle Emitters 2
|
||||
for (final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2 particleEmitter2 : parser
|
||||
.getParticleEmitters2()) {
|
||||
for (final MdlxParticleEmitter2 particleEmitter2 : parser.getParticleEmitters2()) {
|
||||
this.particleEmitters2.add(new ParticleEmitter2Object(this, particleEmitter2, objectId++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
// 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++));
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
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 float width;
|
||||
@ -33,8 +35,7 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
||||
public int blendDst;
|
||||
public int priorityPlane;
|
||||
|
||||
public ParticleEmitter2Object(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.ParticleEmitter2 emitter, final int index) {
|
||||
public ParticleEmitter2Object(final MdxModel model, final MdlxParticleEmitter2 emitter, final int index) {
|
||||
super(model, emitter, index);
|
||||
|
||||
this.width = emitter.getWidth();
|
||||
@ -68,10 +69,10 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
||||
|
||||
this.replaceableId = emitter.getReplaceableId();
|
||||
|
||||
final long headOrTail = emitter.getHeadOrTail();
|
||||
final HeadOrTail headOrTail = emitter.getHeadOrTail();
|
||||
|
||||
this.head = ((headOrTail == 0) || (headOrTail == 2));
|
||||
this.tail = ((headOrTail == 1) || (headOrTail == 2));
|
||||
this.head = headOrTail.isIncludesHead();
|
||||
this.tail = headOrTail.isIncludesTail();
|
||||
|
||||
this.cellWidth = 1f / emitter.getColumns();
|
||||
this.cellHeight = 1f / emitter.getRows();
|
||||
|
@ -2,8 +2,9 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
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 MdxModel internalModel;
|
||||
@ -22,8 +23,7 @@ public class ParticleEmitterObject extends GenericObject implements EmitterObjec
|
||||
*/
|
||||
public boolean ok = false;
|
||||
|
||||
public ParticleEmitterObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.ParticleEmitter emitter,
|
||||
final int index) {
|
||||
public ParticleEmitterObject(final MdxModel model, final MdlxParticleEmitter emitter, final int index) {
|
||||
super(model, emitter, index);
|
||||
|
||||
this.internalModel = (MdxModel) model.viewer.load(
|
||||
|
@ -1,12 +1,12 @@
|
||||
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.RenderMathUtils;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
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 Layer layer;
|
||||
@ -23,8 +24,7 @@ public class RibbonEmitterObject extends GenericObject implements EmitterObject
|
||||
*/
|
||||
public boolean ok = true;
|
||||
|
||||
public RibbonEmitterObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.RibbonEmitter emitter,
|
||||
final int index) {
|
||||
public RibbonEmitterObject(final MdxModel model, final MdlxRibbonEmitter emitter, final int index) {
|
||||
super(model, emitter, index);
|
||||
|
||||
this.layer = model.getMaterials().get(emitter.getMaterialId()).layers.get(0);
|
||||
|
@ -1,11 +1,11 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||
|
||||
public abstract class Sd<TYPE> {
|
||||
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 int globalSequenceId = timeline.getGlobalSequenceId();
|
||||
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.Arrays;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
||||
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> {
|
||||
private static boolean INJECT_FRAMES_GHOSTWOLF_STYLE = false;
|
||||
@ -19,7 +19,7 @@ public final class SdSequence<TYPE> {
|
||||
public TYPE[] outTans;
|
||||
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) {
|
||||
this.sd = sd;
|
||||
this.start = start;
|
||||
@ -136,22 +136,22 @@ public final class SdSequence<TYPE> {
|
||||
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();
|
||||
return fixTimelineArray(timeline, values);
|
||||
}
|
||||
|
||||
private TYPE[] getOutTans(final Timeline<TYPE> timeline) {
|
||||
private TYPE[] getOutTans(final MdlxTimeline<TYPE> timeline) {
|
||||
final TYPE[] outTans = timeline.getOutTans();
|
||||
return fixTimelineArray(timeline, outTans);
|
||||
}
|
||||
|
||||
private TYPE[] getInTans(final Timeline<TYPE> timeline) {
|
||||
private TYPE[] getInTans(final MdlxTimeline<TYPE> timeline) {
|
||||
final TYPE[] inTans = timeline.getInTans();
|
||||
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) {
|
||||
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.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||
|
||||
public class SetupGeosets {
|
||||
private static final int NORMAL_BATCH = 0;
|
||||
private static final int EXTENDED_BATCH = 1;
|
||||
private static final int REFORGED_BATCH = 2;
|
||||
|
||||
public static void setupGeosets(final MdxModel model, final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets,
|
||||
final boolean bigNodeSpace) {
|
||||
public static void setupGeosets(final MdxModel model, final List<MdlxGeoset> geosets, final boolean bigNodeSpace) {
|
||||
if (geosets.size() > 0) {
|
||||
final GL20 gl = model.viewer.gl;
|
||||
int positionBytes = 0;
|
||||
@ -30,7 +30,7 @@ public class SetupGeosets {
|
||||
final int extendedBatchBoneCountOffsetBytes = bigNodeSpace ? 32 : 8;
|
||||
|
||||
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 */) {
|
||||
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);
|
||||
|
||||
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];
|
||||
if (true /* geoset.lod == 0 */) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
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 TextureAnimation(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.TextureAnimation textureAnimation) {
|
||||
public TextureAnimation(final MdxModel model, final MdlxTextureAnimation textureAnimation) {
|
||||
super(model, textureAnimation);
|
||||
|
||||
this.addVariants(AnimationMap.KTAT.getWar3id(), "translation");
|
||||
|
@ -1,11 +1,11 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.timeline.Timeline;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
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.RenderMathUtils;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.timeline.MdlxTimeline;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ public class TgaTexture extends RawOpenGLTextureResource {
|
||||
BufferedImage img;
|
||||
try {
|
||||
img = TgaFile.readTGA(this.fetchUrl, src);
|
||||
update(img);
|
||||
update(img, false);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -1,6 +1,6 @@
|
||||
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 final Sequence sequence;
|
||||
|
@ -5,9 +5,9 @@ import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
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.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.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);
|
||||
for (final String fileName : fileNames.split(",")) {
|
||||
String filePath = directoryBase + fileName;
|
||||
if (!filePath.toLowerCase().endsWith(".wav")) {
|
||||
filePath += ".wav";
|
||||
final int lastDotIndex = filePath.lastIndexOf('.');
|
||||
if (lastDotIndex != -1) {
|
||||
filePath = filePath.substring(0, lastDotIndex);
|
||||
}
|
||||
if (dataSource.has(filePath)) {
|
||||
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath)));
|
||||
if (dataSource.has(filePath + ".wav") || dataSource.has(filePath + ".flac")) {
|
||||
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath + ".wav")));
|
||||
}
|
||||
}
|
||||
return sound;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
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.glutils.ShaderProgram;
|
||||
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.hiveworkshop.rms.parsers.mdlx.MdlxGeoset;
|
||||
import com.hiveworkshop.rms.parsers.mdlx.MdlxModel;
|
||||
|
||||
public class CliffMesh {
|
||||
public int vertexBuffer;
|
||||
@ -28,11 +27,8 @@ public class CliffMesh {
|
||||
public CliffMesh(final String path, final DataSource dataSource, final GL30 gl) throws IOException {
|
||||
this.gl = gl;
|
||||
if (path.endsWith(".mdx") || path.endsWith(".MDX")) {
|
||||
MdlxModel model;
|
||||
try (InputStream stream = dataSource.getResourceAsStream(path)) {
|
||||
model = new MdlxModel(stream);
|
||||
}
|
||||
final Geoset geoset = model.getGeosets().get(0);
|
||||
final MdlxModel model = new MdlxModel(dataSource.read(path));
|
||||
final MdlxGeoset geoset = model.getGeosets().get(0);
|
||||
|
||||
this.vertexBuffer = gl.glGenBuffer();
|
||||
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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.Buffer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
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 int id;
|
||||
@ -18,34 +15,15 @@ public class GroundTexture {
|
||||
public boolean extended;
|
||||
|
||||
public GroundTexture(final String path, final DataSource dataSource, final GL30 gl) 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);
|
||||
loadImage(path, gl, tgaData);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Missing ground texture: " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final BufferedImage image = ImageIO.read(stream);
|
||||
loadImage(path, gl, image);
|
||||
}
|
||||
}
|
||||
final AnyExtensionImage imageInfo = ImageUtils.getAnyExtensionImageFixRGB(dataSource, path, "ground texture");
|
||||
loadImage(path, gl, imageInfo.getImageData(), imageInfo.isNeedsSRGBFix());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
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 height = image.getHeight();
|
||||
|
||||
|
@ -16,8 +16,6 @@ import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
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.Element;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.ImageUtils.AnyExtensionImage;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
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.Extensions;
|
||||
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.SplatModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
@ -257,31 +255,12 @@ public class Terrain {
|
||||
}
|
||||
final String texDir = cliffInfo.getField("texDir");
|
||||
final String texFile = cliffInfo.getField("texFile");
|
||||
try (InputStream imageStream = dataSource.getResourceAsStream(texDir + "\\" + texFile + texturesExt)) {
|
||||
final BufferedImage image;
|
||||
if (imageStream == null) {
|
||||
final String tgaPath = texDir + "\\" + texFile + ".tga";
|
||||
try (final InputStream tgaStream = dataSource.getResourceAsStream(tgaPath)) {
|
||||
if (tgaStream != null) {
|
||||
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)),
|
||||
final AnyExtensionImage imageInfo = ImageUtils.getAnyExtensionImageFixRGB(dataSource,
|
||||
texDir + "\\" + texFile + texturesExt, "cliff texture");
|
||||
final BufferedImage image = imageInfo.getRGBCorrectImageData();
|
||||
this.cliffTextures
|
||||
.add(new UnloadedTexture(image.getWidth(), image.getHeight(), ImageUtils.getTextureBuffer(image),
|
||||
cliffInfo.getField("cliffModelDir"), cliffInfo.getField("rampModelDir")));
|
||||
}
|
||||
this.cliffTexturesSize = Math.max(this.cliffTexturesSize,
|
||||
this.cliffTextures.get(this.cliffTextures.size() - 1).width);
|
||||
this.cliffToGroundTexture.add(this.groundTextureToId.get(cliffInfo.getField("groundTile")));
|
||||
@ -381,27 +360,33 @@ public class Terrain {
|
||||
// Water textures
|
||||
this.waterTextureArray = gl.glGenTexture();
|
||||
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_T, GL30.GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL30.GL_TEXTURE_BASE_LEVEL, 0);
|
||||
|
||||
final String fileName = waterInfo.getField("texFile");
|
||||
for (int i = 0; i < this.waterTextureCount; i++) {
|
||||
|
||||
try (InputStream imageStream = dataSource
|
||||
.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());
|
||||
}
|
||||
|
||||
for (int i = 0; i < waterTextures.size(); i++) {
|
||||
final BufferedImage image = waterTextures.get(i);
|
||||
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);
|
||||
|
||||
updateGroundHeights(new Rectangle(0, 0, width - 1, height - 1));
|
||||
|
@ -2,9 +2,9 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
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.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
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 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.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
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.math.Quaternion;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
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.PrimaryTag;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
||||
|
@ -11,8 +11,6 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
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.UIFrame;
|
||||
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.manager.MutableObjectData;
|
||||
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.CommandErrorListener;
|
||||
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,
|
||||
QueueIconListener, CommandErrorListener, CPlayerStateListener {
|
||||
@ -276,7 +274,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
this.cameraManager.target.x = startLocation[0];
|
||||
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");
|
||||
this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter();
|
||||
this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f;
|
||||
@ -303,18 +301,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
}
|
||||
}
|
||||
else if (war3MapViewer.dataSource.has("war3mapMap.blp")) {
|
||||
try {
|
||||
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();
|
||||
}
|
||||
minimapTexture = ImageUtils.getAnyExtensionTexture(war3MapViewer.dataSource, "war3mapMap.blp");
|
||||
}
|
||||
final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS];
|
||||
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");
|
||||
}
|
||||
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