mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
More work on handler code
This commit is contained in:
parent
005dd375ad
commit
c132b0d984
@ -1,8 +1,8 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
@ -13,20 +13,20 @@ import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.Texture.TextureFilter;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.FolderDataSource;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.hiveworkshop.wc3.mpq.Codebase;
|
||||
import com.hiveworkshop.wc3.mpq.FileCodebase;
|
||||
|
||||
public class WarsmashGdxGame extends ApplicationAdapter {
|
||||
private SpriteBatch batch;
|
||||
private BitmapFont font;
|
||||
private Codebase codebase;
|
||||
private DataSource codebase;
|
||||
private Texture texture;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
this.codebase = new FileCodebase(new File("C:/MPQBuild/War3.mpq/war3.mpq"));
|
||||
this.codebase = new FolderDataSource(Paths.get("C:/MPQBuild/War3.mpq/war3.mpq"));
|
||||
|
||||
final War3ID id = War3ID.fromString("ipea");
|
||||
try {
|
||||
|
13
core/src/com/etheller/warsmash/common/FetchDataTypeName.java
Normal file
13
core/src/com/etheller/warsmash/common/FetchDataTypeName.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.etheller.warsmash.common;
|
||||
|
||||
/**
|
||||
* These will probably change the further I get from the source material I am
|
||||
* copying from.
|
||||
*/
|
||||
public enum FetchDataTypeName {
|
||||
IMAGE,
|
||||
TEXT,
|
||||
SLK,
|
||||
ARRAY_BUFFER,
|
||||
BLOB;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.etheller.warsmash.common;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface LoadGenericCallback {
|
||||
Object call(InputStream data); // TODO typing
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package com.etheller.warsmash.datasources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
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;
|
||||
|
||||
public class CompoundDataSource implements DataSource {
|
||||
private final List<DataSource> mpqList = new ArrayList<>();
|
||||
|
||||
public CompoundDataSource(final List<DataSourceDescriptor> dataSourceDescriptors) {
|
||||
if (dataSourceDescriptors != null) {
|
||||
for (final DataSourceDescriptor descriptor : dataSourceDescriptors) {
|
||||
this.mpqList.add(descriptor.createDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, File> cache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public File getFile(final String filepath) {
|
||||
if (this.cache.containsKey(filepath)) {
|
||||
return this.cache.get(filepath);
|
||||
}
|
||||
try {
|
||||
for (int i = this.mpqList.size() - 1; i >= 0; i--) {
|
||||
final DataSource mpq = this.mpqList.get(i);
|
||||
final File tempProduct = mpq.getFile(filepath);
|
||||
if (tempProduct != null) {
|
||||
this.cache.put(filepath, tempProduct);
|
||||
return tempProduct;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (filepath.toLowerCase(Locale.US).endsWith(".blp")) {
|
||||
return getFile(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
||||
}
|
||||
if (filepath.toLowerCase(Locale.US).endsWith(".tif")) {
|
||||
return getFile(filepath.substring(0, filepath.lastIndexOf(".")) + ".dds");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) {
|
||||
try {
|
||||
for (int i = this.mpqList.size() - 1; i >= 0; i--) {
|
||||
final DataSource mpq = this.mpqList.get(i);
|
||||
final InputStream resourceAsStream = mpq.getResourceAsStream(filepath);
|
||||
if (resourceAsStream != null) {
|
||||
return resourceAsStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final String filepath) {
|
||||
if (this.cache.containsKey(filepath)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = this.mpqList.size() - 1; i >= 0; i--) {
|
||||
final DataSource mpq = this.mpqList.get(i);
|
||||
if (mpq.has(filepath)) {
|
||||
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;
|
||||
}
|
||||
|
||||
public void refresh(final List<DataSourceDescriptor> dataSourceDescriptors) {
|
||||
for (final DataSource dataSource : this.mpqList) {
|
||||
try {
|
||||
dataSource.close();
|
||||
}
|
||||
catch (final NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.cache.clear();
|
||||
this.mpqList.clear();
|
||||
if (dataSourceDescriptors != null) {
|
||||
for (final DataSourceDescriptor descriptor : dataSourceDescriptors) {
|
||||
this.mpqList.add(descriptor.createDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface LoadedMPQ {
|
||||
void unload();
|
||||
|
||||
boolean hasListfile();
|
||||
|
||||
boolean has(String path);
|
||||
}
|
||||
|
||||
public Set<String> getMergedListfile() {
|
||||
final Set<String> listfile = new HashSet<>();
|
||||
for (final DataSource mpqGuy : this.mpqList) {
|
||||
final Collection<String> dataSourceListfile = mpqGuy.getListfile();
|
||||
if (dataSourceListfile != null) {
|
||||
for (final String element : dataSourceListfile) {
|
||||
listfile.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
return listfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getListfile() {
|
||||
return getMergedListfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
for (final DataSource mpqGuy : this.mpqList) {
|
||||
mpqGuy.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -316,4 +316,61 @@ public class Geoset implements MdlxBlock, Chunk {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -163,4 +163,29 @@ public class Light extends GenericObject {
|
||||
public long getByteLength() {
|
||||
return 48 + super.getByteLength();
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public float[] getAttenuation() {
|
||||
return this.attenuation;
|
||||
}
|
||||
|
||||
public float[] getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
public float getIntensity() {
|
||||
return this.intensity;
|
||||
}
|
||||
|
||||
public float[] getAmbientColor() {
|
||||
return this.ambientColor;
|
||||
}
|
||||
|
||||
public float getAmbientIntensity() {
|
||||
return this.ambientIntensity;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -190,4 +190,33 @@ public class ParticleEmitter extends GenericObject {
|
||||
public long getByteLength() {
|
||||
return 288 + super.getByteLength();
|
||||
}
|
||||
|
||||
public float getEmissionRate() {
|
||||
return this.emissionRate;
|
||||
}
|
||||
|
||||
public float getGravity() {
|
||||
return this.gravity;
|
||||
}
|
||||
|
||||
public float getLongitude() {
|
||||
return this.longitude;
|
||||
}
|
||||
|
||||
public float getLatitude() {
|
||||
return this.latitude;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public float getLifeSpan() {
|
||||
return this.lifeSpan;
|
||||
}
|
||||
|
||||
public float getSpeed() {
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
}
|
||||
|
303
core/src/com/etheller/warsmash/units/DataTable.java
Normal file
303
core/src/com/etheller/warsmash/units/DataTable.java
Normal file
@ -0,0 +1,303 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
|
||||
public class DataTable implements ObjectData {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
Map<StringKey, Element> dataTable = new LinkedHashMap<>();
|
||||
|
||||
private final WorldEditStrings worldEditStrings;
|
||||
|
||||
public DataTable(final WorldEditStrings worldEditStrings) {
|
||||
this.worldEditStrings = worldEditStrings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedString(final String key) {
|
||||
return this.worldEditStrings.getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
final Set<String> outputKeySet = new HashSet<>();
|
||||
final Set<StringKey> internalKeySet = this.dataTable.keySet();
|
||||
for (final StringKey key : internalKeySet) {
|
||||
outputKeySet.add(key.getString());
|
||||
}
|
||||
return outputKeySet;
|
||||
}
|
||||
|
||||
public void readTXT(final InputStream inputStream) {
|
||||
try {
|
||||
readTXT(inputStream, false);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void readTXT(final File f) {
|
||||
readTXT(f, false);
|
||||
}
|
||||
|
||||
public void readTXT(final File f, final boolean canProduce) {
|
||||
try {
|
||||
readTXT(new FileInputStream(f), canProduce);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void readSLK(final File f) {
|
||||
try {
|
||||
readSLK(new FileInputStream(f));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void readTXT(final InputStream txt, final boolean canProduce) throws IOException {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(txt, "utf-8"));
|
||||
// BOM marker will only appear on the very beginning
|
||||
reader.mark(4);
|
||||
if ('\ufeff' != reader.read()) {
|
||||
reader.reset(); // not the BOM marker
|
||||
}
|
||||
|
||||
String input = "";
|
||||
Element currentUnit = null;
|
||||
final boolean first = true;
|
||||
while ((input = reader.readLine()) != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println(input);
|
||||
}
|
||||
if (input.startsWith("//")) {
|
||||
continue;
|
||||
}
|
||||
if (input.startsWith("[") && input.contains("]")) {
|
||||
final int start = input.indexOf("[") + 1;
|
||||
final int end = input.indexOf("]");
|
||||
final String newKey = input.substring(start, end);
|
||||
final String newKeyBase = newKey;
|
||||
currentUnit = this.dataTable.get(new StringKey(newKey));
|
||||
if (currentUnit == null) {
|
||||
currentUnit = new Element(newKey, this);
|
||||
if (canProduce) {
|
||||
currentUnit = new LMUnit(newKey, this);
|
||||
this.dataTable.put(new StringKey(newKey), currentUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input.contains("=")) {
|
||||
final int eIndex = input.indexOf("=");
|
||||
final String fieldValue = input.substring(eIndex + 1);
|
||||
int fieldIndex = 0;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
boolean withinQuotedString = false;
|
||||
final String fieldName = input.substring(0, eIndex);
|
||||
for (int i = 0; i < fieldValue.length(); i++) {
|
||||
final char c = fieldValue.charAt(i);
|
||||
if (c == '\"') {
|
||||
withinQuotedString = !withinQuotedString;
|
||||
}
|
||||
else if (!withinQuotedString && (c == ',')) {
|
||||
currentUnit.setField(fieldName, builder.toString().trim(), fieldIndex++);
|
||||
builder.setLength(0); // empty buffer
|
||||
}
|
||||
else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
if (currentUnit == null) {
|
||||
System.out.println("null for " + input);
|
||||
}
|
||||
currentUnit.setField(fieldName, builder.toString().trim(), fieldIndex++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
}
|
||||
|
||||
public void readSLK(final InputStream txt) throws IOException {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(txt, "utf-8"));
|
||||
|
||||
String input = "";
|
||||
Element currentUnit = null;
|
||||
input = reader.readLine();
|
||||
if (!input.contains("ID")) {
|
||||
System.err.println("Formatting of SLK is unusual.");
|
||||
}
|
||||
input = reader.readLine();
|
||||
while (input.startsWith("P;") || input.startsWith("F;")) {
|
||||
input = reader.readLine();
|
||||
}
|
||||
final int yIndex = input.indexOf("Y") + 1;
|
||||
final int xIndex = input.indexOf("X") + 1;
|
||||
int colCount = 0;
|
||||
int rowCount = 0;
|
||||
boolean flipMode = false;
|
||||
if (xIndex > yIndex) {
|
||||
colCount = Integer.parseInt(input.substring(xIndex, input.lastIndexOf(";")));
|
||||
rowCount = Integer.parseInt(input.substring(yIndex, xIndex - 2));
|
||||
}
|
||||
else {
|
||||
rowCount = Integer.parseInt(input.substring(yIndex, input.lastIndexOf(";")));
|
||||
colCount = Integer.parseInt(input.substring(xIndex, yIndex - 2));
|
||||
flipMode = true;
|
||||
}
|
||||
int rowStartCount = 0;
|
||||
final String[] dataNames = new String[colCount];
|
||||
int col = 0;
|
||||
int lastFieldId = 0;
|
||||
while ((input = reader.readLine()) != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println(input);
|
||||
}
|
||||
if (input.startsWith("E")) {
|
||||
break;
|
||||
}
|
||||
if (input.startsWith("O;")) {
|
||||
continue;
|
||||
}
|
||||
if (input.contains("X1;")) {
|
||||
rowStartCount++;
|
||||
col = 0;
|
||||
}
|
||||
else {
|
||||
col++;
|
||||
}
|
||||
String kInput;
|
||||
if (input.startsWith("F;")) {
|
||||
kInput = reader.readLine();
|
||||
if (DEBUG) {
|
||||
System.out.println(kInput);
|
||||
}
|
||||
}
|
||||
else {
|
||||
kInput = input;
|
||||
}
|
||||
if (rowStartCount <= 1) {
|
||||
final int subXIndex = input.indexOf("X");
|
||||
final int subYIndex = input.indexOf("Y");
|
||||
if ((subYIndex >= 0) && (subYIndex < subXIndex)) {
|
||||
final int eIndex = kInput.indexOf("K");
|
||||
final int fieldIdEndIndex = kInput != input ? input.length() : eIndex - 1;
|
||||
if ((eIndex == -1) || (kInput.charAt(eIndex - 1) != ';')) {
|
||||
continue;
|
||||
}
|
||||
final int fieldId;
|
||||
if (subXIndex < 0) {
|
||||
if (lastFieldId == 0) {
|
||||
rowStartCount++;
|
||||
}
|
||||
fieldId = lastFieldId + 1;
|
||||
}
|
||||
else {
|
||||
fieldId = Integer.parseInt(input.substring(subXIndex + 1, fieldIdEndIndex));
|
||||
}
|
||||
|
||||
final int quotationIndex = kInput.indexOf("\"");
|
||||
if (quotationIndex == -1) {
|
||||
dataNames[fieldId - 1] = kInput.substring(eIndex + 1);
|
||||
}
|
||||
else {
|
||||
dataNames[fieldId - 1] = kInput.substring(quotationIndex + 1, kInput.lastIndexOf("\""));
|
||||
}
|
||||
lastFieldId = fieldId;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
int eIndex = kInput.indexOf("K");
|
||||
if ((eIndex == -1) || (kInput.charAt(eIndex - 1) != ';')) {
|
||||
continue;
|
||||
}
|
||||
final int fieldId;
|
||||
if (subXIndex < 0) {
|
||||
if (lastFieldId == 0) {
|
||||
rowStartCount++;
|
||||
}
|
||||
fieldId = lastFieldId + 1;
|
||||
}
|
||||
else {
|
||||
if (flipMode && input.contains("Y") && (input == kInput)) {
|
||||
eIndex = Math.min(subYIndex, eIndex);
|
||||
}
|
||||
final int fieldIdEndIndex = kInput != input ? input.length() : eIndex - 1;
|
||||
fieldId = Integer.parseInt(input.substring(subXIndex + 1, fieldIdEndIndex));
|
||||
}
|
||||
|
||||
final int quotationIndex = kInput.indexOf("\"");
|
||||
if (quotationIndex == -1) {
|
||||
dataNames[fieldId - 1] = kInput.substring(eIndex + 1);
|
||||
}
|
||||
else {
|
||||
dataNames[fieldId - 1] = kInput.substring(quotationIndex + 1, kInput.lastIndexOf("\""));
|
||||
}
|
||||
lastFieldId = fieldId;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (input.contains("X1;") || ((input != kInput) && input.endsWith("X1"))) {
|
||||
final int start = kInput.indexOf("\"") + 1;
|
||||
final int end = kInput.lastIndexOf("\"");
|
||||
if ((start - 1) != end) {
|
||||
final String newKey = kInput.substring(start, end);
|
||||
currentUnit = this.dataTable.get(new StringKey(newKey));
|
||||
if (currentUnit == null) {
|
||||
currentUnit = new Element(newKey, this);
|
||||
this.dataTable.put(new StringKey(newKey), currentUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (kInput.contains("K")) {
|
||||
final int subXIndex = input.indexOf("X");
|
||||
int eIndex = kInput.indexOf("K");
|
||||
if (flipMode && kInput.contains("Y")) {
|
||||
eIndex = Math.min(kInput.indexOf("Y"), eIndex);
|
||||
}
|
||||
final int fieldIdEndIndex = kInput != input ? input.length() : eIndex - 1;
|
||||
final int fieldId = (subXIndex == -1) || (subXIndex > fieldIdEndIndex) ? 1
|
||||
: Integer.parseInt(input.substring(subXIndex + 1, fieldIdEndIndex));
|
||||
String fieldValue = kInput.substring(eIndex + 1);
|
||||
if ((fieldValue.length() > 1) && fieldValue.startsWith("\"") && fieldValue.endsWith("\"")) {
|
||||
fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
|
||||
}
|
||||
if (dataNames[fieldId - 1] != null) {
|
||||
currentUnit.setField(dataNames[fieldId - 1], fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element get(final String id) {
|
||||
return this.dataTable.get(new StringKey(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(final String id, final String field, final String value) {
|
||||
get(id).setField(field, value);
|
||||
}
|
||||
|
||||
public void put(final String id, final Element e) {
|
||||
this.dataTable.put(new StringKey(id), e);
|
||||
}
|
||||
}
|
217
core/src/com/etheller/warsmash/units/Element.java
Normal file
217
core/src/com/etheller/warsmash/units/Element.java
Normal file
@ -0,0 +1,217 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class Element extends HashedGameObject {
|
||||
|
||||
public Element(final String id, final DataTable table) {
|
||||
super(id, table);
|
||||
}
|
||||
|
||||
public List<GameObject> builds() {
|
||||
return getFieldAsList("Builds", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> requires() {
|
||||
final List<GameObject> requirements = getFieldAsList("Requires", this.parentTable);
|
||||
final List<Integer> reqLvls = requiresLevels();
|
||||
return requirements;
|
||||
}
|
||||
|
||||
public List<Integer> requiresLevels() {
|
||||
final String stringList = getField("Requiresamount");
|
||||
final String[] listAsArray = stringList.split(",");
|
||||
final LinkedList<Integer> output = new LinkedList<>();
|
||||
if ((listAsArray != null) && (listAsArray.length > 0) && !listAsArray[0].equals("")) {
|
||||
for (final String levelString : listAsArray) {
|
||||
final Integer level = Integer.parseInt(levelString);
|
||||
if (level != null) {
|
||||
output.add(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public List<GameObject> parents() {
|
||||
return getFieldAsList("Parents", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> children() {
|
||||
return getFieldAsList("Children", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> requiredBy() {
|
||||
return getFieldAsList("RequiredBy", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> trains() {
|
||||
return getFieldAsList("Trains", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> upgrades() {
|
||||
return getFieldAsList("Upgrade", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> researches() {
|
||||
return getFieldAsList("Researches", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> dependencyOr() {
|
||||
return getFieldAsList("DependencyOr", this.parentTable);
|
||||
}
|
||||
|
||||
public List<GameObject> abilities() {
|
||||
return getFieldAsList("abilList", this.parentTable);
|
||||
}
|
||||
|
||||
HashMap<String, List<Element>> hashedLists = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getField("Name");
|
||||
}
|
||||
|
||||
public int getTechTier() {
|
||||
final String tier = getField("Custom Field: TechTier");
|
||||
if (tier == null) {
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(tier);
|
||||
}
|
||||
|
||||
public void setTechTier(final int i) {
|
||||
setField("Custom Field: TechTier", i + "");
|
||||
}
|
||||
|
||||
public int getTechDepth() {
|
||||
final String tier = getField("Custom Field: TechDepth");
|
||||
if (tier == null) {
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(tier);
|
||||
}
|
||||
|
||||
public void setTechDepth(final int i) {
|
||||
setField("Custom Field: TechDepth", i + "");
|
||||
}
|
||||
|
||||
public String getIconPath() {
|
||||
String artField = getField("Art");
|
||||
if (artField.indexOf(',') != -1) {
|
||||
artField = artField.substring(0, artField.indexOf(','));
|
||||
}
|
||||
return artField;
|
||||
}
|
||||
|
||||
public String getUnitId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = getField("Name");
|
||||
boolean nameKnown = name.length() >= 1;
|
||||
if (!nameKnown && !getField("code").equals(this.id) && (getField("code").length() >= 4)) {
|
||||
final Element other = (Element) this.parentTable.get(getField("code").substring(0, 4));
|
||||
if (other != null) {
|
||||
name = other.getName();
|
||||
nameKnown = true;
|
||||
}
|
||||
}
|
||||
if (!nameKnown && (getField("EditorName").length() > 1)) {
|
||||
name = getField("EditorName");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Editorname").length() > 1)) {
|
||||
name = getField("Editorname");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("BuffTip").length() > 1)) {
|
||||
name = getField("BuffTip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Bufftip").length() > 1)) {
|
||||
name = getField("Bufftip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (nameKnown && name.startsWith("WESTRING")) {
|
||||
if (!name.contains(" ")) {
|
||||
name = this.parentTable.getLocalizedString(name);
|
||||
}
|
||||
else {
|
||||
final String[] names = name.split(" ");
|
||||
name = "";
|
||||
for (final String subName : names) {
|
||||
if (name.length() > 0) {
|
||||
name += " ";
|
||||
}
|
||||
if (subName.startsWith("WESTRING")) {
|
||||
name += this.parentTable.getLocalizedString(subName);
|
||||
}
|
||||
else {
|
||||
name += subName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.startsWith("\"") && name.endsWith("\"")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
setField("Name", name);
|
||||
}
|
||||
if (!nameKnown) {
|
||||
name = this.parentTable.getLocalizedString("WESTRING_UNKNOWN") + " '" + getUnitId() + "'";
|
||||
}
|
||||
if (getField("campaign").startsWith("1") && Character.isUpperCase(getUnitId().charAt(0))) {
|
||||
name = getField("Propernames");
|
||||
if (name.contains(",")) {
|
||||
name = name.split(",")[0];
|
||||
}
|
||||
}
|
||||
String suf = getField("EditorSuffix");
|
||||
if ((suf.length() > 0) && !suf.equals("_")) {
|
||||
if (suf.startsWith("WESTRING")) {
|
||||
suf = this.parentTable.getLocalizedString(suf);
|
||||
}
|
||||
if (!suf.startsWith(" ")) {
|
||||
name += " ";
|
||||
}
|
||||
name += suf;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public void addParent(final String parentId) {
|
||||
String parentField = getField("Parents");
|
||||
if (!parentField.contains(parentId)) {
|
||||
parentField = parentField + "," + parentId;
|
||||
setField("Parents", parentField);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChild(final String parentId) {
|
||||
String parentField = getField("Children");
|
||||
if (!parentField.contains(parentId)) {
|
||||
parentField = parentField + "," + parentId;
|
||||
setField("Children", parentField);
|
||||
}
|
||||
}
|
||||
|
||||
public void addRequiredBy(final String parentId) {
|
||||
String parentField = getField("RequiredBy");
|
||||
if (!parentField.contains(parentId)) {
|
||||
parentField = parentField + "," + parentId;
|
||||
setField("RequiredBy", parentField);
|
||||
}
|
||||
}
|
||||
|
||||
public void addResearches(final String parentId) {
|
||||
String parentField = getField("Researches");
|
||||
if (!parentField.contains(parentId)) {
|
||||
parentField = parentField + "," + parentId;
|
||||
setField("Researches", parentField);
|
||||
}
|
||||
}
|
||||
}
|
30
core/src/com/etheller/warsmash/units/GameObject.java
Normal file
30
core/src/com/etheller/warsmash/units/GameObject.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface GameObject {
|
||||
|
||||
public void setField(String field, String value);
|
||||
|
||||
public void setField(String field, String value, int index);
|
||||
|
||||
public String getField(String field);
|
||||
|
||||
public String getField(String field, int index);
|
||||
|
||||
public int getFieldValue(String field);
|
||||
|
||||
public int getFieldValue(String field, int index);
|
||||
|
||||
public List<? extends GameObject> getFieldAsList(String field, ObjectData objectData);
|
||||
|
||||
public String getId();
|
||||
|
||||
public ObjectData getTable();
|
||||
|
||||
public String getName();
|
||||
|
||||
public Set<String> keySet();
|
||||
|
||||
}
|
241
core/src/com/etheller/warsmash/units/HashedGameObject.java
Normal file
241
core/src/com/etheller/warsmash/units/HashedGameObject.java
Normal file
@ -0,0 +1,241 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class HashedGameObject implements GameObject {
|
||||
HashMap<StringKey, List<String>> fields = new HashMap<>();
|
||||
String id;
|
||||
ObjectData parentTable;
|
||||
|
||||
transient HashMap<String, List<GameObject>> hashedLists = new HashMap<>();
|
||||
|
||||
public HashedGameObject(final String id, final ObjectData table) {
|
||||
this.id = id;
|
||||
this.parentTable = table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(final String field, final String value) {
|
||||
final StringKey key = new StringKey(field);
|
||||
List<String> list = this.fields.get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
this.fields.put(key, list);
|
||||
list.add(value);
|
||||
}
|
||||
else {
|
||||
list.set(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField(final String field) {
|
||||
final String value = "";
|
||||
if (this.fields.get(new StringKey(field)) != null) {
|
||||
final List<String> list = this.fields.get(new StringKey(field));
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (list != null) {
|
||||
for (final String str : list) {
|
||||
if (sb.length() != 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(str);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean hasField(final String field) {
|
||||
return this.fields.containsKey(new StringKey(field));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldValue(final String field) {
|
||||
int i = 0;
|
||||
try {
|
||||
i = Integer.parseInt(getField(field));
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(final String field, final String value, final int index) {
|
||||
final StringKey key = new StringKey(field);
|
||||
List<String> list = this.fields.get(key);
|
||||
if (list == null) {
|
||||
if (index == 0) {
|
||||
list = new ArrayList<>();
|
||||
this.fields.put(key, list);
|
||||
list.add(value);
|
||||
}
|
||||
else {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (list.size() == index) {
|
||||
list.add(value);
|
||||
}
|
||||
else {
|
||||
list.set(index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField(final String field, final int index) {
|
||||
String value = "";
|
||||
if (this.fields.get(new StringKey(field)) != null) {
|
||||
final List<String> list = this.fields.get(new StringKey(field));
|
||||
if (list != null) {
|
||||
if (list.size() > index) {
|
||||
value = list.get(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldValue(final String field, final int index) {
|
||||
int i = 0;
|
||||
try {
|
||||
i = Integer.parseInt(getField(field, index));
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GameObject> getFieldAsList(final String field, final ObjectData parentTable) {
|
||||
List<GameObject> fieldAsList;
|
||||
fieldAsList = new ArrayList<>();
|
||||
final String stringList = getField(field);
|
||||
final String[] listAsArray = stringList.split(",");
|
||||
if ((listAsArray != null) && (listAsArray.length > 0)) {
|
||||
for (final String buildingId : listAsArray) {
|
||||
final GameObject referencedUnit = parentTable.get(buildingId);
|
||||
if (referencedUnit != null) {
|
||||
fieldAsList.add(referencedUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldAsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getField("Name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = getField("Name");
|
||||
boolean nameKnown = name.length() >= 1;
|
||||
if (!nameKnown && !getField("code").equals(this.id) && (getField("code").length() >= 4)) {
|
||||
final GameObject other = this.parentTable.get(getField("code").substring(0, 4));
|
||||
if (other != null) {
|
||||
name = other.getName();
|
||||
nameKnown = true;
|
||||
}
|
||||
}
|
||||
if (!nameKnown && (getField("EditorName").length() > 1)) {
|
||||
name = getField("EditorName");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Editorname").length() > 1)) {
|
||||
name = getField("Editorname");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("BuffTip").length() > 1)) {
|
||||
name = getField("BuffTip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Bufftip").length() > 1)) {
|
||||
name = getField("Bufftip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (nameKnown && name.startsWith("WESTRING")) {
|
||||
if (!name.contains(" ")) {
|
||||
name = this.parentTable.getLocalizedString(name);
|
||||
}
|
||||
else {
|
||||
final String[] names = name.split(" ");
|
||||
name = "";
|
||||
for (final String subName : names) {
|
||||
if (name.length() > 0) {
|
||||
name += " ";
|
||||
}
|
||||
if (subName.startsWith("WESTRING")) {
|
||||
name += this.parentTable.getLocalizedString(subName);
|
||||
}
|
||||
else {
|
||||
name += subName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.startsWith("\"") && name.endsWith("\"")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
setField("Name", name);
|
||||
}
|
||||
if (!nameKnown) {
|
||||
name = this.parentTable.getLocalizedString("WESTRING_UNKNOWN") + " '" + getId() + "'";
|
||||
}
|
||||
if (getField("campaign").startsWith("1") && Character.isUpperCase(getId().charAt(0))) {
|
||||
name = getField("Propernames");
|
||||
if (name.contains(",")) {
|
||||
name = name.split(",")[0];
|
||||
}
|
||||
}
|
||||
String suf = getField("EditorSuffix");
|
||||
if ((suf.length() > 0) && !suf.equals("_")) {
|
||||
if (suf.startsWith("WESTRING")) {
|
||||
suf = this.parentTable.getLocalizedString(suf);
|
||||
}
|
||||
if (!suf.startsWith(" ")) {
|
||||
name += " ";
|
||||
}
|
||||
name += suf;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public void addToList(final String parentId, final String list) {
|
||||
String parentField = getField(list);
|
||||
if (!parentField.contains(parentId)) {
|
||||
parentField = parentField + "," + parentId;
|
||||
setField(list, parentField);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectData getTable() {
|
||||
return this.parentTable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
final Set<String> keySet = new HashSet<>();
|
||||
for (final StringKey key : this.fields.keySet()) {
|
||||
keySet.add(key.getString());
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
}
|
12
core/src/com/etheller/warsmash/units/LMUnit.java
Normal file
12
core/src/com/etheller/warsmash/units/LMUnit.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class LMUnit extends Element {
|
||||
|
||||
public LMUnit(final String id, final DataTable table) {
|
||||
super(id, table);
|
||||
this.fields = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
}
|
13
core/src/com/etheller/warsmash/units/ObjectData.java
Normal file
13
core/src/com/etheller/warsmash/units/ObjectData.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface ObjectData {
|
||||
GameObject get(String id);
|
||||
|
||||
void setValue(String id, String field, String value);
|
||||
|
||||
Set<String> keySet();
|
||||
|
||||
String getLocalizedString(String key);
|
||||
}
|
610
core/src/com/etheller/warsmash/units/StandardObjectData.java
Normal file
610
core/src/com/etheller/warsmash/units/StandardObjectData.java
Normal file
@ -0,0 +1,610 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
|
||||
public class StandardObjectData {
|
||||
private WorldEditStrings worldEditStrings;
|
||||
private DataSource source;
|
||||
|
||||
public StandardObjectData(final DataSource dataSource) {
|
||||
this.source = dataSource;
|
||||
this.worldEditStrings = new WorldEditStrings(dataSource);
|
||||
}
|
||||
|
||||
public WarcraftData getStandardUnits() {
|
||||
|
||||
final DataTable profile = new DataTable(this.worldEditStrings);
|
||||
final DataTable unitAbilities = new DataTable(this.worldEditStrings);
|
||||
final DataTable unitBalance = new DataTable(this.worldEditStrings);
|
||||
final DataTable unitData = new DataTable(this.worldEditStrings);
|
||||
final DataTable unitUI = new DataTable(this.worldEditStrings);
|
||||
final DataTable unitWeapons = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignUnitStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanUnitStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralUnitStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfUnitStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcUnitStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadUnitFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadUnitStrings.txt"), true);
|
||||
|
||||
unitAbilities.readSLK(this.source.getResourceAsStream("Units\\UnitAbilities.slk"));
|
||||
|
||||
unitBalance.readSLK(this.source.getResourceAsStream("Units\\UnitBalance.slk"));
|
||||
|
||||
unitData.readSLK(this.source.getResourceAsStream("Units\\UnitData.slk"));
|
||||
|
||||
unitUI.readSLK(this.source.getResourceAsStream("Units\\UnitUI.slk"));
|
||||
|
||||
unitWeapons.readSLK(this.source.getResourceAsStream("Units\\UnitWeapons.slk"));
|
||||
final InputStream unitSkin = this.source.getResourceAsStream("Units\\UnitSkin.txt");
|
||||
if (unitSkin != null) {
|
||||
profile.readTXT(unitSkin, true);
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData units = new WarcraftData();
|
||||
|
||||
units.add(profile, "Profile", false);
|
||||
units.add(unitAbilities, "UnitAbilities", true);
|
||||
units.add(unitBalance, "UnitBalance", true);
|
||||
units.add(unitData, "UnitData", true);
|
||||
units.add(unitUI, "UnitUI", true);
|
||||
units.add(unitWeapons, "UnitWeapons", true);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardItems() {
|
||||
final DataTable profile = new DataTable(this.worldEditStrings);
|
||||
final DataTable itemData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemStrings.txt"), true);
|
||||
itemData.readSLK(this.source.getResourceAsStream("Units\\ItemData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData units = new WarcraftData();
|
||||
|
||||
units.add(profile, "Profile", false);
|
||||
units.add(itemData, "ItemData", true);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardDestructables() {
|
||||
final DataTable destructableData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
destructableData.readSLK(this.source.getResourceAsStream("Units\\DestructableData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData units = new WarcraftData();
|
||||
|
||||
units.add(destructableData, "DestructableData", true);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardDoodads() {
|
||||
|
||||
final DataTable destructableData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
destructableData.readSLK(this.source.getResourceAsStream("Doodads\\Doodads.slk"));
|
||||
final InputStream unitSkin = this.source.getResourceAsStream("Doodads\\DoodadSkins.txt");
|
||||
if (unitSkin != null) {
|
||||
destructableData.readTXT(unitSkin, true);
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData units = new WarcraftData();
|
||||
|
||||
units.add(destructableData, "DoodadData", true);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public DataTable getStandardUnitMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\UnitMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getStandardDestructableMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\DestructableMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getStandardDoodadMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Doodads\\DoodadMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardAbilities() {
|
||||
|
||||
final DataTable profile = new DataTable(this.worldEditStrings);
|
||||
final DataTable abilityData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CommonAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CommonAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityStrings.txt"), true);
|
||||
|
||||
abilityData.readSLK(this.source.getResourceAsStream("Units\\AbilityData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData abilities = new WarcraftData();
|
||||
|
||||
abilities.add(profile, "Profile", false);
|
||||
abilities.add(abilityData, "AbilityData", true);
|
||||
|
||||
return abilities;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardAbilityBuffs() {
|
||||
final DataTable profile = new DataTable(this.worldEditStrings);
|
||||
final DataTable abilityData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CommonAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CommonAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadAbilityStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\ItemAbilityStrings.txt"), true);
|
||||
|
||||
abilityData.readSLK(this.source.getResourceAsStream("Units\\AbilityBuffData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData abilities = new WarcraftData();
|
||||
|
||||
abilities.add(profile, "Profile", false);
|
||||
abilities.add(abilityData, "AbilityData", true);
|
||||
|
||||
return abilities;
|
||||
}
|
||||
|
||||
public WarcraftData getStandardUpgrades() {
|
||||
final DataTable profile = new DataTable(this.worldEditStrings);
|
||||
final DataTable upgradeData = new DataTable(this.worldEditStrings);
|
||||
|
||||
try {
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\CampaignUpgradeStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\HumanUpgradeStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NeutralUpgradeStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\NightElfUpgradeStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\OrcUpgradeStrings.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadUpgradeFunc.txt"), true);
|
||||
profile.readTXT(this.source.getResourceAsStream("Units\\UndeadUpgradeStrings.txt"), true);
|
||||
|
||||
upgradeData.readSLK(this.source.getResourceAsStream("Units\\UpgradeData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final WarcraftData units = new WarcraftData();
|
||||
|
||||
units.add(profile, "Profile", false);
|
||||
units.add(upgradeData, "UpgradeData", true);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public DataTable getStandardUpgradeMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\UpgradeMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getStandardUpgradeEffectMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\UpgradeEffectMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getStandardAbilityMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\AbilityMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getStandardAbilityBuffMeta() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readSLK(this.source.getResourceAsStream("Units\\AbilityBuffMetaData.slk"));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getUnitEditorData() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readTXT(this.source.getResourceAsStream("UI\\UnitEditorData.txt"), true);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public DataTable getWorldEditData() {
|
||||
final DataTable unitMetaData = new DataTable(this.worldEditStrings);
|
||||
try {
|
||||
unitMetaData.readTXT(this.source.getResourceAsStream("UI\\WorldEditData.txt"), true);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return unitMetaData;
|
||||
}
|
||||
|
||||
public static class WarcraftData implements ObjectData {
|
||||
WorldEditStrings worldEditStrings;
|
||||
List<DataTable> tables = new ArrayList<>();
|
||||
Map<StringKey, DataTable> tableMap = new HashMap<>();
|
||||
Map<StringKey, WarcraftObject> units = new HashMap<>();
|
||||
|
||||
public WarcraftData(final WorldEditStrings worldEditStrings) {
|
||||
this.worldEditStrings = worldEditStrings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedString(final String key) {
|
||||
return this.worldEditStrings.getString(key);
|
||||
}
|
||||
|
||||
public void add(final DataTable data, final String name, final boolean canMake) {
|
||||
this.tableMap.put(new StringKey(name), data);
|
||||
this.tables.add(data);
|
||||
if (canMake) {
|
||||
for (final String id : data.keySet()) {
|
||||
if (!this.units.containsKey(new StringKey(id))) {
|
||||
this.units.put(new StringKey(id), new WarcraftObject(data.get(id).getId(), this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WarcraftData() {
|
||||
}
|
||||
|
||||
public List<DataTable> getTables() {
|
||||
return this.tables;
|
||||
}
|
||||
|
||||
public void setTables(final List<DataTable> tables) {
|
||||
this.tables = tables;
|
||||
}
|
||||
|
||||
public DataTable getTable(final String tableName) {
|
||||
return this.tableMap.get(new StringKey(tableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameObject get(final String id) {
|
||||
return this.units.get(new StringKey(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(final String id, final String field, final String value) {
|
||||
get(id).setField(field, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
final Set<String> keySet = new HashSet<>();
|
||||
for (final StringKey key : this.units.keySet()) {
|
||||
keySet.add(key.getString());
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
|
||||
public void cloneUnit(final String parentId, final String cloneId) {
|
||||
for (final DataTable table : this.tables) {
|
||||
final Element parentEntry = table.get(parentId);
|
||||
final LMUnit cloneUnit = new LMUnit(cloneId, table);
|
||||
for (final String key : parentEntry.keySet()) {
|
||||
cloneUnit.setField(key, parentEntry.getField(key));
|
||||
}
|
||||
table.put(cloneId, cloneUnit);
|
||||
}
|
||||
this.units.put(new StringKey(cloneId), new WarcraftObject(cloneId, this));
|
||||
}
|
||||
}
|
||||
|
||||
public static class WarcraftObject implements GameObject {
|
||||
String id;
|
||||
WarcraftData dataSource;
|
||||
|
||||
public WarcraftObject(final String id, final WarcraftData dataSource) {
|
||||
this.id = id;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(final String field, final String value, final int index) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
element.setField(field, value, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField(final String field, final int index) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getField(field, index);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldValue(final String field, final int index) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getFieldValue(field, index);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(final String field, final String value) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
element.setField(field, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("no field");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getField(final String field) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getField(field);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldValue(final String field) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getFieldValue(field);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc) I'm not entirely sure this is still safe to use
|
||||
*
|
||||
* @see com.hiveworkshop.wc3.units.GameObject#getFieldAsList(java.lang. String)
|
||||
*/
|
||||
@Override
|
||||
public List<? extends GameObject> getFieldAsList(final String field, final ObjectData objectData) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getFieldAsList(field, objectData);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();// empty list if not found
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectData getTable() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String getName() {
|
||||
// return dataSource.profile.get(id).getName();
|
||||
// }
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = getField("Name");
|
||||
boolean nameKnown = name.length() >= 1;
|
||||
if (!nameKnown && !getField("code").equals(this.id) && (getField("code").length() >= 4)) {
|
||||
final WarcraftObject other = (WarcraftObject) this.dataSource.get(getField("code").substring(0, 4));
|
||||
if (other != null) {
|
||||
name = other.getName();
|
||||
nameKnown = true;
|
||||
}
|
||||
}
|
||||
if (!nameKnown && (getField("EditorName").length() > 1)) {
|
||||
name = getField("EditorName");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Editorname").length() > 1)) {
|
||||
name = getField("Editorname");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("BuffTip").length() > 1)) {
|
||||
name = getField("BuffTip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (!nameKnown && (getField("Bufftip").length() > 1)) {
|
||||
name = getField("Bufftip");
|
||||
nameKnown = true;
|
||||
}
|
||||
if (nameKnown && name.startsWith("WESTRING")) {
|
||||
if (!name.contains(" ")) {
|
||||
name = this.dataSource.getLocalizedString(name);
|
||||
}
|
||||
else {
|
||||
final String[] names = name.split(" ");
|
||||
name = "";
|
||||
for (final String subName : names) {
|
||||
if (name.length() > 0) {
|
||||
name += " ";
|
||||
}
|
||||
if (subName.startsWith("WESTRING")) {
|
||||
name += this.dataSource.getLocalizedString(subName);
|
||||
}
|
||||
else {
|
||||
name += subName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.startsWith("\"") && name.endsWith("\"")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
setField("Name", name);
|
||||
}
|
||||
if (!nameKnown) {
|
||||
name = this.dataSource.getLocalizedString("WESTRING_UNKNOWN") + " '" + getId() + "'";
|
||||
}
|
||||
if (getField("campaign").startsWith("1") && Character.isUpperCase(getId().charAt(0))) {
|
||||
name = getField("Propernames");
|
||||
if (name.contains(",")) {
|
||||
name = name.split(",")[0];
|
||||
}
|
||||
}
|
||||
String suf = getField("EditorSuffix");
|
||||
if ((suf.length() > 0) && !suf.equals("_")) {
|
||||
if (suf.startsWith("WESTRING")) {
|
||||
suf = this.dataSource.getLocalizedString(suf);
|
||||
}
|
||||
if (!suf.startsWith(" ")) {
|
||||
name += " ";
|
||||
}
|
||||
name += suf;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
BufferedImage storedImage = null;
|
||||
String storedImagePath = null;
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
final Set<String> keySet = new HashSet<>();
|
||||
for (final DataTable table : this.dataSource.tables) {
|
||||
keySet.addAll(table.get(this.id).keySet());
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
}
|
||||
|
||||
private StandardObjectData() {
|
||||
}
|
||||
}
|
54
core/src/com/etheller/warsmash/units/StringKey.java
Normal file
54
core/src/com/etheller/warsmash/units/StringKey.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.etheller.warsmash.units;
|
||||
|
||||
/**
|
||||
* A hashable wrapper object for a String that can be used as the key in a
|
||||
* hashtable, but which disregards case as a key -- except that it will remember
|
||||
* case if directly asked for its value. The game needs this to be able to show
|
||||
* the original case of a string to the user in the editor, while still doing
|
||||
* map lookups in a case insensitive way.
|
||||
*
|
||||
* @author Eric
|
||||
*
|
||||
*/
|
||||
public final class StringKey {
|
||||
private final String string;
|
||||
|
||||
public StringKey(final String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return this.string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((this.string.toLowerCase() == null) ? 0 : this.string.toLowerCase().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 StringKey other = (StringKey) obj;
|
||||
if (this.string == null) {
|
||||
if (other.string != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.string.equalsIgnoreCase(other.string)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
84
core/src/com/etheller/warsmash/util/IniFile.java
Normal file
84
core/src/com/etheller/warsmash/util/IniFile.java
Normal file
@ -0,0 +1,84 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class IniFile {
|
||||
private static final Pattern NAME_PATTERN = Pattern.compile("^\\[(.+?)\\].*");
|
||||
private static final Pattern DATA_PATTERN = Pattern.compile("^(.+?)=(.*?)$");
|
||||
public final Map<String, String> properties = new HashMap<>();
|
||||
public final Map<String, Map<String, String>> sections = new HashMap<>();
|
||||
|
||||
public IniFile(final String buffer) {
|
||||
if (buffer != null) {
|
||||
this.load(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(final String buffer) {
|
||||
// All properties added until a section is reached are added to the properties
|
||||
// map.
|
||||
// Once a section is reached, any further properties will be added to it until
|
||||
// matching another section, etc.
|
||||
Map<String, String> section = this.properties;
|
||||
|
||||
// Below: using \n instead of \r\n because its not reading directly from the
|
||||
// actual file, but instead from a Java translated thing
|
||||
for (final String line : buffer.split("\n")) {
|
||||
// INI defines comments as starting with a semicolon ';'.
|
||||
// However, Warcraft 3 INI files use normal C comments '//'.
|
||||
// In addition, Warcraft 3 files have empty lines.
|
||||
// Therefore, ignore any line matching any of these conditions.
|
||||
|
||||
if ((line.length() != 0) && !line.startsWith("//") && !line.startsWith(";")) {
|
||||
final Matcher matcher = NAME_PATTERN.matcher(line);
|
||||
|
||||
if (matcher.matches()) {
|
||||
final String name = matcher.group(1).trim().toLowerCase();
|
||||
|
||||
section = this.sections.get(name);
|
||||
|
||||
if (section == null) {
|
||||
section = new HashMap<>();
|
||||
|
||||
this.sections.put(name, section);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final Matcher dataMatcher = DATA_PATTERN.matcher(line);
|
||||
if (dataMatcher.matches()) {
|
||||
section.put(dataMatcher.group(1).toLowerCase(), dataMatcher.group(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String save() {
|
||||
final List<String> lines = new ArrayList<>();
|
||||
|
||||
for (final Map.Entry<String, String> entry : this.properties.entrySet()) {
|
||||
lines.add(entry.getKey() + "=" + entry.getValue());
|
||||
}
|
||||
|
||||
for (final Map.Entry<String, Map<String, String>> sectionData : this.sections.entrySet()) {
|
||||
lines.add("[" + sectionData.getKey() + "]");
|
||||
|
||||
for (final Map.Entry<String, String> entry : sectionData.getValue().entrySet()) {
|
||||
lines.add(entry.getKey() + "=" + entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return String.join("\r\n", lines);
|
||||
}
|
||||
|
||||
public Map<String, String> getSection(final String name) {
|
||||
return this.sections.get(name.toLowerCase());
|
||||
}
|
||||
}
|
92
core/src/com/etheller/warsmash/util/MappedData.java
Normal file
92
core/src/com/etheller/warsmash/util/MappedData.java
Normal file
@ -0,0 +1,92 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A structure that holds mapped data from INI and SLK files.
|
||||
*
|
||||
* In the case of SLK files, the first row is expected to hold the names of the
|
||||
* columns.
|
||||
*/
|
||||
public class MappedData {
|
||||
private final Map<String, MappedDataRow> map = new HashMap<>();
|
||||
|
||||
public MappedData(final String buffer) {
|
||||
if (buffer != null) {
|
||||
this.load(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from an SLK file or an INI file.
|
||||
*
|
||||
* Note that this may override previous properties!
|
||||
*/
|
||||
public void load(final String buffer) {
|
||||
if (buffer.startsWith("ID;")) {
|
||||
final SlkFile file = new SlkFile(buffer);
|
||||
final List<List<Object>> rows = file.rows;
|
||||
final List<Object> header = rows.get(0);
|
||||
|
||||
for (int i = 1, l = rows.size(); i < l; i++) {
|
||||
final List<Object> row = rows.get(i);
|
||||
if (row != null) {
|
||||
String name = (String) row.get(0);
|
||||
|
||||
if (name != null) {
|
||||
name = name.toLowerCase();
|
||||
|
||||
if (!this.map.containsKey(name)) {
|
||||
this.map.put(name, new MappedDataRow());
|
||||
}
|
||||
|
||||
final MappedDataRow mapped = this.map.get(name);
|
||||
|
||||
for (int j = 0, k = header.size(); j < k; j++) {
|
||||
String key = (String) header.get(j);
|
||||
|
||||
// UnitBalance.slk doesn't define the name of one row.
|
||||
if (key == null) {
|
||||
key = "column" + j;
|
||||
}
|
||||
|
||||
mapped.put(key, row.get(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final IniFile file = new IniFile(buffer);
|
||||
final Map<String, Map<String, String>> sections = file.sections;
|
||||
|
||||
for (final Map.Entry<String, Map<String, String>> rowAndProperties : sections.entrySet()) {
|
||||
final String row = rowAndProperties.getKey();
|
||||
|
||||
if (!this.map.containsKey(row)) {
|
||||
this.map.put(row, new MappedDataRow());
|
||||
}
|
||||
|
||||
final MappedDataRow mapped = this.map.get(row);
|
||||
|
||||
for (final Map.Entry<String, String> nameAndProperty : rowAndProperties.getValue().entrySet()) {
|
||||
mapped.put(nameAndProperty.getKey(), nameAndProperty.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MappedDataRow getRow(final String key) {
|
||||
return this.map.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
public Object getProperty(final String key, final String name) {
|
||||
return this.map.get(key.toLowerCase()).get(name);
|
||||
}
|
||||
|
||||
public void setRow(final String key, final MappedDataRow values) {
|
||||
this.map.put(key.toLowerCase(), values);
|
||||
}
|
||||
}
|
7
core/src/com/etheller/warsmash/util/MappedDataRow.java
Normal file
7
core/src/com/etheller/warsmash/util/MappedDataRow.java
Normal file
@ -0,0 +1,7 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MappedDataRow extends HashMap<String, Object> {
|
||||
|
||||
}
|
70
core/src/com/etheller/warsmash/util/SlkFile.java
Normal file
70
core/src/com/etheller/warsmash/util/SlkFile.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SlkFile {
|
||||
public List<List<Object>> rows;
|
||||
|
||||
public SlkFile(final String buffer) {
|
||||
if (buffer != null) {
|
||||
this.load(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(final String buffer) {
|
||||
if (!buffer.startsWith("ID")) {
|
||||
throw new RuntimeException("WrongMagicNumber");
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
for (final String line : buffer.split("\n")) {
|
||||
// The B command is supposed to define the total number of columns and rows,
|
||||
// however in UbetSplatData.slk it gives wrong information
|
||||
// Therefore, just ignore it, since JavaScript arrays grow as they want either
|
||||
// way
|
||||
if (line.charAt(0) != 'B') {
|
||||
for (final String token : line.split(";")) {
|
||||
final char op = token.charAt(0);
|
||||
final String valueString = token.substring(1).trim();
|
||||
final Object value;
|
||||
|
||||
if (op == 'X') {
|
||||
x = Integer.parseInt(valueString, 10) - 1;
|
||||
}
|
||||
else if (op == 'Y') {
|
||||
y = Integer.parseInt(valueString, 10) - 1;
|
||||
}
|
||||
else if (op == 'K') {
|
||||
while (y >= this.rows.size()) {
|
||||
this.rows.add(null);
|
||||
}
|
||||
if (this.rows.get(y) == null) {
|
||||
this.rows.set(y, new ArrayList<>());
|
||||
}
|
||||
|
||||
if (valueString.charAt('0') == '"') {
|
||||
value = valueString.substring(1, valueString.length() - 1);
|
||||
}
|
||||
else if ("TRUE".equals(valueString)) {
|
||||
value = true;
|
||||
}
|
||||
else if ("FALSE".equals(valueString)) {
|
||||
value = false;
|
||||
}
|
||||
else {
|
||||
value = Float.parseFloat(valueString);
|
||||
}
|
||||
|
||||
final List<Object> row = this.rows.get(y);
|
||||
while (x >= row.size()) {
|
||||
row.add(null);
|
||||
}
|
||||
row.set(x, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
core/src/com/etheller/warsmash/util/Test.java
Normal file
20
core/src/com/etheller/warsmash/util/Test.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Test {
|
||||
|
||||
public static void main(final String[] args) {
|
||||
final Pattern pattern = Pattern.compile("^\\[(.+?)\\]");
|
||||
final Matcher matcher = pattern.matcher("[boat] // ocean");
|
||||
if (matcher.matches()) {
|
||||
final String name = matcher.group(1).trim().toLowerCase();
|
||||
System.out.println(name);
|
||||
}
|
||||
else {
|
||||
System.out.println("no match");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
71
core/src/com/etheller/warsmash/util/WorldEditStrings.java
Normal file
71
core/src/com/etheller/warsmash/util/WorldEditStrings.java
Normal file
@ -0,0 +1,71 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
|
||||
public class WorldEditStrings {
|
||||
private ResourceBundle bundle;
|
||||
private ResourceBundle bundlegs;
|
||||
|
||||
public WorldEditStrings(final DataSource dataSource) {
|
||||
try (InputStream fis = dataSource.getResourceAsStream("UI\\WorldEditStrings.txt");
|
||||
InputStreamReader reader = new InputStreamReader(fis, "utf-8")) {
|
||||
this.bundle = new PropertyResourceBundle(reader);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try (InputStream fis = dataSource.getResourceAsStream("UI\\WorldEditGameStrings.txt");
|
||||
InputStreamReader reader = new InputStreamReader(fis, "utf-8")) {
|
||||
this.bundlegs = new PropertyResourceBundle(reader);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String string) {
|
||||
try {
|
||||
while (string.toUpperCase().startsWith("WESTRING")) {
|
||||
string = internalGetString(string);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
catch (final MissingResourceException exc) {
|
||||
try {
|
||||
return this.bundlegs.getString(string.toUpperCase());
|
||||
}
|
||||
catch (final MissingResourceException exc2) {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String internalGetString(final String key) {
|
||||
try {
|
||||
String string = this.bundle.getString(key.toUpperCase());
|
||||
if ((string.charAt(0) == '"') && (string.length() >= 2) && (string.charAt(string.length() - 1) == '"')) {
|
||||
string = string.substring(1, string.length() - 1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
catch (final MissingResourceException exc) {
|
||||
return this.bundlegs.getString(key.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringCaseSensitive(final String key) {
|
||||
try {
|
||||
return this.bundle.getString(key);
|
||||
}
|
||||
catch (final MissingResourceException exc) {
|
||||
return this.bundlegs.getString(key);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.badlogic.gdx.audio.Sound;
|
||||
|
||||
public class AudioBufferSource {
|
||||
public Sound buffer;
|
||||
|
||||
public void connect(final AudioPanner panner) {
|
||||
|
||||
}
|
||||
|
||||
public void start(final int value) {
|
||||
if (this.buffer != null) {
|
||||
this.buffer.play(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package com.etheller.warsmash.viewer5;
|
||||
public class AudioContext {
|
||||
private boolean running = false;
|
||||
public Listener listener = new Listener();
|
||||
public AudioDestination destination = new AudioDestination() {
|
||||
};
|
||||
|
||||
public void suspend() {
|
||||
this.running = false;
|
||||
@ -44,4 +46,22 @@ public class AudioContext {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public AudioPanner createPanner() {
|
||||
return new AudioPanner() {
|
||||
@Override
|
||||
public void setPosition(final float x, final float y, final float z) {
|
||||
System.err.println("audio panner set position not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(final AudioDestination destination) {
|
||||
System.err.println("audio panner connect dest not implemented");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AudioBufferSource createBufferSource() {
|
||||
return new AudioBufferSource();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface AudioDestination {
|
||||
|
||||
}
|
10
core/src/com/etheller/warsmash/viewer5/AudioPanner.java
Normal file
10
core/src/com/etheller/warsmash/viewer5/AudioPanner.java
Normal file
@ -0,0 +1,10 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public abstract class AudioPanner {
|
||||
public abstract void setPosition(float x, float y, float z);
|
||||
|
||||
public float maxDistance;
|
||||
public float refDistance;
|
||||
|
||||
public abstract void connect(AudioDestination destination);
|
||||
}
|
@ -7,5 +7,9 @@ public abstract class EmittedObject<MODEL_INSTANCE extends ModelInstance, EMITTE
|
||||
public EMITTER emitter;
|
||||
public int index;
|
||||
|
||||
public EmittedObject(final EMITTER emitter) {
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
protected abstract void bind(int flags);
|
||||
}
|
||||
|
35
core/src/com/etheller/warsmash/viewer5/GenericResource.java
Normal file
35
core/src/com/etheller/warsmash/viewer5/GenericResource.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.etheller.warsmash.common.LoadGenericCallback;
|
||||
|
||||
public final class GenericResource extends Resource {
|
||||
|
||||
public Object data; // TODO this likely won't work, just brainstorming until I get to the part of
|
||||
// using the data
|
||||
private final LoadGenericCallback callback;
|
||||
|
||||
public GenericResource(final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl, final LoadGenericCallback callback) {
|
||||
super(viewer, extension, pathSolver, fetchUrl);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void lateLoad() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load(final InputStream src, final Object options) {
|
||||
this.data = this.callback.call(src);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(final Exception e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
14
core/src/com/etheller/warsmash/viewer5/HandlerResource.java
Normal file
14
core/src/com/etheller/warsmash/viewer5/HandlerResource.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public abstract class HandlerResource<HANDLER extends ResourceHandler> extends Resource {
|
||||
public final HANDLER handler;
|
||||
|
||||
public HandlerResource(final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl, final HANDLER handler) {
|
||||
super(viewer, extension, pathSolver, fetchUrl);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
}
|
@ -6,13 +6,13 @@ import java.util.List;
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelInstanceDescriptor;
|
||||
|
||||
public abstract class Model<HANDLER extends ModelHandler> extends Resource<HANDLER> {
|
||||
public abstract class Model<HANDLER extends ModelHandler> extends HandlerResource<HANDLER> {
|
||||
public Bounds bounds;
|
||||
public List<ModelInstance> preloadedInstances;
|
||||
|
||||
public Model(final HANDLER handler, final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl) {
|
||||
super(viewer, handler, extension, pathSolver, fetchUrl);
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
this.bounds = new Bounds();
|
||||
this.preloadedInstances = new ArrayList<>();
|
||||
}
|
||||
|
@ -11,7 +11,10 @@ import java.util.Set;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.etheller.warsmash.common.FetchDataTypeName;
|
||||
import com.etheller.warsmash.common.LoadGenericCallback;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.viewer5.gl.ClientBuffer;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
@ -19,8 +22,8 @@ import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
public class ModelViewer {
|
||||
private final DataSource dataSource;
|
||||
public final CanvasProvider canvas;
|
||||
public List<Resource<?>> resources;
|
||||
public Map<String, Resource<?>> fetchCache;
|
||||
public List<Resource> resources;
|
||||
public Map<String, Resource> fetchCache;
|
||||
public int frameTime;
|
||||
public GL20 gl;
|
||||
public WebGL webGL;
|
||||
@ -30,7 +33,8 @@ public class ModelViewer {
|
||||
private int updatedParticles;
|
||||
public int frame;
|
||||
public final int rectBuffer;
|
||||
private final boolean enableAudio;
|
||||
public ClientBuffer buffer;
|
||||
public boolean audioEnabled;
|
||||
private final Map<Model, List<TextureMapper>> textureMappers;
|
||||
private final Set<ResourceHandler> handlers;
|
||||
|
||||
@ -51,6 +55,7 @@ public class ModelViewer {
|
||||
this.frame = 0;
|
||||
|
||||
this.rectBuffer = this.gl.glGenBuffer();
|
||||
this.buffer = new ClientBuffer(this.gl);
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.rectBuffer);
|
||||
final ByteBuffer temp = ByteBuffer.allocate(6);
|
||||
temp.put((byte) 0);
|
||||
@ -61,10 +66,15 @@ public class ModelViewer {
|
||||
temp.put((byte) 3);
|
||||
temp.clear();
|
||||
this.gl.glBufferData(GL20.GL_ARRAY_BUFFER, temp.capacity(), temp, GL20.GL_STATIC_DRAW);
|
||||
this.enableAudio = false;
|
||||
this.audioEnabled = false;
|
||||
this.textureMappers = new HashMap<Model, List<TextureMapper>>();
|
||||
}
|
||||
|
||||
public boolean enableAudio() {
|
||||
this.audioEnabled = true;
|
||||
return this.audioEnabled;
|
||||
}
|
||||
|
||||
public boolean addHandler(ResourceHandler handler) {
|
||||
if (handler != null) {
|
||||
|
||||
@ -115,7 +125,7 @@ public class ModelViewer {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Resource<?> load(final String src, final PathSolver pathSolver, final Object solverParams) {
|
||||
public Resource load(final String src, final PathSolver pathSolver, final Object solverParams) {
|
||||
String finalSrc = src;
|
||||
String extension = "";
|
||||
boolean isFetch = false;
|
||||
@ -139,7 +149,7 @@ public class ModelViewer {
|
||||
// Is there a handler for this file type?
|
||||
if (handlerAndDataType != null) {
|
||||
if (isFetch) {
|
||||
final Resource<?> resource = this.fetchCache.get(finalSrc);
|
||||
final Resource resource = this.fetchCache.get(finalSrc);
|
||||
|
||||
if (resource != null) {
|
||||
return resource;
|
||||
@ -147,7 +157,7 @@ public class ModelViewer {
|
||||
}
|
||||
|
||||
final ResourceHandler handler = (ResourceHandler) handlerAndDataType[0];
|
||||
final Resource<?> resource = handler.construct(new ResourceHandlerConstructionParams(this, handler,
|
||||
final Resource resource = handler.construct(new ResourceHandlerConstructionParams(this, handler,
|
||||
extension, pathSolver, isFetch ? finalSrc : ""));
|
||||
|
||||
this.resources.add(resource);
|
||||
@ -179,10 +189,55 @@ public class ModelViewer {
|
||||
return this.fetchCache.containsKey(key);
|
||||
}
|
||||
|
||||
public Resource<?> get(final String key) {
|
||||
public Resource get(final String key) {
|
||||
return this.fetchCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load something generic.
|
||||
*
|
||||
* Unlike load(), this does not use handlers or construct any internal objects.
|
||||
*
|
||||
* `dataType` can be one of: `"image"`, `"string"`, `"arrayBuffer"`, `"blob"`.
|
||||
*
|
||||
* If `callback` isn't given, the resource's `data` is the fetch data, according
|
||||
* to `dataType`.
|
||||
*
|
||||
* If `callback` is given, the resource's `data` is the value returned by it
|
||||
* when called with the fetch data.
|
||||
*
|
||||
* If `callback` returns a promise, the resource's `data` will be whatever the
|
||||
* promise resolved to.
|
||||
*/
|
||||
public GenericResource loadGeneric(final String path, final FetchDataTypeName dataType,
|
||||
final LoadGenericCallback callback) {
|
||||
final Resource cachedResource = this.fetchCache.get(path);
|
||||
|
||||
if (cachedResource != null) {
|
||||
// Technically also non-generic resources can be returned here, since the fetch
|
||||
// cache is shared.
|
||||
// That being said, this should be used for generic resources, and it makes the
|
||||
// typing a lot easier.
|
||||
return (GenericResource) cachedResource;
|
||||
}
|
||||
|
||||
final GenericResource resource = new GenericResource(this, null, null, path, callback);
|
||||
|
||||
this.resources.add(resource);
|
||||
this.fetchCache.put(path, resource);
|
||||
|
||||
// TODO this is a synchronous hack, skipped some Ghostwolf code
|
||||
try {
|
||||
resource.loadData(this.dataSource.getResourceAsStream(path), null);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new IllegalStateException("Unable to load data: " + path);
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
||||
}
|
||||
|
||||
public void updateAndRender() {
|
||||
this.update();
|
||||
this.startFrame();
|
||||
@ -191,7 +246,7 @@ public class ModelViewer {
|
||||
|
||||
// public Resource loadGeneric(String path, String dataType, )
|
||||
|
||||
public boolean unload(final Resource<?> resource) {
|
||||
public boolean unload(final Resource resource) {
|
||||
// TODO Auto-generated method stub
|
||||
final String fetchUrl = resource.fetchUrl;
|
||||
if (!"".equals(fetchUrl)) {
|
||||
|
@ -2,11 +2,8 @@ package com.etheller.warsmash.viewer5;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public abstract class Resource<HANDLER extends ResourceHandler> {
|
||||
public abstract class Resource {
|
||||
public final ModelViewer viewer;
|
||||
public final HANDLER handler;
|
||||
public final String extension;
|
||||
public final String fetchUrl;
|
||||
public boolean ok;
|
||||
@ -14,10 +11,9 @@ public abstract class Resource<HANDLER extends ResourceHandler> {
|
||||
public final PathSolver pathSolver;
|
||||
public final Object solverParams = null;
|
||||
|
||||
public Resource(final ModelViewer viewer, final HANDLER handler, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
public Resource(final ModelViewer viewer, final String extension, final PathSolver pathSolver,
|
||||
final String fetchUrl) {
|
||||
this.viewer = viewer;
|
||||
this.handler = handler;
|
||||
this.extension = extension;
|
||||
this.pathSolver = pathSolver;
|
||||
this.fetchUrl = fetchUrl;
|
||||
|
@ -35,8 +35,8 @@ public class Scene {
|
||||
public int visibleCells;
|
||||
public int visibleInstances;
|
||||
public int updatedParticles;
|
||||
private boolean audioEnabled;
|
||||
private AudioContext audioContext;
|
||||
public boolean audioEnabled;
|
||||
public AudioContext audioContext;
|
||||
|
||||
private final List<ModelInstance> instances;
|
||||
private final int currentInstance;
|
||||
|
@ -2,12 +2,12 @@ package com.etheller.warsmash.viewer5;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
|
||||
public abstract class Texture extends Resource<ResourceHandler> {
|
||||
public abstract class Texture extends HandlerResource<ResourceHandler> {
|
||||
private com.badlogic.gdx.graphics.Texture gdxTexture;
|
||||
|
||||
public Texture(final ModelViewer viewer, final ResourceHandler handler, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
super(viewer, handler, extension, pathSolver, fetchUrl);
|
||||
super(viewer, extension, pathSolver, fetchUrl, handler);
|
||||
}
|
||||
|
||||
public void setGdxTexture(final com.badlogic.gdx.graphics.Texture gdxTexture) {
|
||||
|
54
core/src/com/etheller/warsmash/viewer5/gl/ClientBuffer.java
Normal file
54
core/src/com/etheller/warsmash/viewer5/gl/ClientBuffer.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.etheller.warsmash.viewer5.gl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class ClientBuffer {
|
||||
private final GL20 gl;
|
||||
private final int buffer;
|
||||
private int size;
|
||||
private ByteBuffer arrayBuffer;
|
||||
public ByteBuffer byteView;
|
||||
public FloatBuffer floatView;
|
||||
|
||||
public ClientBuffer(final GL20 gl) {
|
||||
this(gl, 4);
|
||||
}
|
||||
|
||||
public ClientBuffer(final GL20 gl, final int size) {
|
||||
this.gl = gl;
|
||||
this.buffer = gl.glGenBuffer();
|
||||
this.arrayBuffer = null;
|
||||
|
||||
this.reserve(size);
|
||||
}
|
||||
|
||||
public void reserve(final int size) {
|
||||
if (this.size < size) {
|
||||
|
||||
// Ensure the size is on a 4 byte boundary.
|
||||
this.size = (int) Math.ceil(size / 4.) * 4;
|
||||
|
||||
this.gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.buffer);
|
||||
|
||||
this.arrayBuffer = ByteBuffer.allocate(this.size);
|
||||
this.gl.glBufferData(GL20.GL_ARRAY_BUFFER, this.size, this.arrayBuffer, GL20.GL_DYNAMIC_DRAW);
|
||||
this.byteView = this.arrayBuffer;
|
||||
this.floatView = this.arrayBuffer.asFloatBuffer();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void bindAndUpdate() {
|
||||
bindAndUpdate(this.size);
|
||||
}
|
||||
|
||||
public void bindAndUpdate(final int size) {
|
||||
final GL20 gl = this.gl;
|
||||
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, this.buffer);
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, size, this.byteView);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package com.etheller.warsmash.viewer5.handlers;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Resource;
|
||||
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||
|
||||
public abstract class ResourceHandler {
|
||||
public ResourceHandler handler;
|
||||
@ -12,5 +12,5 @@ public abstract class ResourceHandler {
|
||||
|
||||
public abstract boolean load(ModelViewer modelViewer);
|
||||
|
||||
public abstract Resource<?> construct(ResourceHandlerConstructionParams params);
|
||||
public abstract HandlerResource<?> construct(ResourceHandlerConstructionParams params);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.etheller.warsmash.viewer5.handlers.blp;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Resource;
|
||||
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
|
||||
@ -20,7 +20,7 @@ public class BlpHandler extends ResourceHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource<?> construct(final ResourceHandlerConstructionParams params) {
|
||||
public HandlerResource<?> construct(final ResourceHandlerConstructionParams params) {
|
||||
return new BlpTexture(params.getViewer(), params.getHandler(), params.getExtension(), params.getPathSolver(),
|
||||
params.getFetchUrl());
|
||||
}
|
||||
|
@ -12,22 +12,26 @@ import com.etheller.warsmash.util.War3ID;
|
||||
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) {
|
||||
this.model = model;
|
||||
this.timelines = new HashMap<>();
|
||||
this.variants = new HashMap<>();
|
||||
|
||||
for (final Timeline<?> timeline : object.getTimelines()) {
|
||||
this.timelines.put(timeline.getName(), createTypedSd(model, timeline));
|
||||
}
|
||||
}
|
||||
|
||||
public int getScalarValue(final float[] out, final War3ID name, final MdxComplexInstance instance,
|
||||
final float defaultValue) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
public int getScalarValue(final float[] out, final War3ID name, final int sequence, final int frame,
|
||||
final int counter, final float defaultValue) {
|
||||
if (sequence != -1) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
|
||||
if (animation instanceof ScalarSd) {
|
||||
return ((ScalarSd) animation).getValue(out, instance);
|
||||
if (animation instanceof ScalarSd) {
|
||||
return ((ScalarSd) animation).getValue(out, sequence, frame, counter);
|
||||
}
|
||||
}
|
||||
|
||||
out[0] = defaultValue;
|
||||
@ -35,12 +39,14 @@ public class AnimatedObject {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getScalarValue(final long[] out, final War3ID name, final MdxComplexInstance instance,
|
||||
final long defaultValue) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
public int getScalarValue(final long[] out, final War3ID name, final int sequence, final int frame,
|
||||
final int counter, final long defaultValue) {
|
||||
if (sequence != -1) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
|
||||
if (animation instanceof UInt32Sd) {
|
||||
return ((UInt32Sd) animation).getValue(out, instance);
|
||||
if (animation instanceof UInt32Sd) {
|
||||
return ((UInt32Sd) animation).getValue(out, sequence, frame, counter);
|
||||
}
|
||||
}
|
||||
|
||||
out[0] = defaultValue;
|
||||
@ -48,12 +54,14 @@ public class AnimatedObject {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getVectorValue(final float[] out, final War3ID name, final MdxComplexInstance instance,
|
||||
final float[] defaultValue) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
public int getVectorValue(final float[] out, final War3ID name, final int sequence, final int frame,
|
||||
final int counter, final float[] defaultValue) {
|
||||
if (sequence != -1) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
|
||||
if (animation instanceof VectorSd) {
|
||||
return ((VectorSd) animation).getValue(out, instance);
|
||||
if (animation instanceof VectorSd) {
|
||||
return ((VectorSd) animation).getValue(out, sequence, frame, counter);
|
||||
}
|
||||
}
|
||||
|
||||
System.arraycopy(defaultValue, 0, out, 0, 3);
|
||||
@ -61,12 +69,14 @@ public class AnimatedObject {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getQuadValue(final float[] out, final War3ID name, final MdxComplexInstance instance,
|
||||
final float[] defaultValue) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
public int getQuadValue(final float[] out, final War3ID name, final int sequence, final int frame,
|
||||
final int counter, final float[] defaultValue) {
|
||||
if (sequence != -1) {
|
||||
final Sd<?> animation = this.timelines.get(name);
|
||||
|
||||
if (animation instanceof QuaternionSd) {
|
||||
return ((QuaternionSd) animation).getValue(out, instance);
|
||||
if (animation instanceof QuaternionSd) {
|
||||
return ((QuaternionSd) animation).getValue(out, sequence, frame, counter);
|
||||
}
|
||||
}
|
||||
|
||||
System.arraycopy(defaultValue, 0, out, 0, 4);
|
||||
@ -74,6 +84,38 @@ public class AnimatedObject {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void addVariants(final War3ID name, final String variantName) {
|
||||
final Sd<?> timeline = this.timelines.get(name);
|
||||
final int sequences = this.model.getSequences().size();
|
||||
final byte[] variants = new byte[sequences];
|
||||
|
||||
if (timeline != null) {
|
||||
for (int i = 0; i < sequences; i++) {
|
||||
if (timeline.isVariant(i)) {
|
||||
variants[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.variants.put(variantName, variants);
|
||||
}
|
||||
|
||||
public void addVariantIntersection(final String[] names, final String variantName) {
|
||||
final int sequences = this.model.getSequences().size();
|
||||
final byte[] variants = new byte[sequences];
|
||||
|
||||
for (int i = 0; i < sequences; i++) {
|
||||
for (final String name : names) {
|
||||
final byte[] variantsAtName = this.variants.get(name);
|
||||
if ((variantsAtName != null) && (variantsAtName[i] != 0)) {
|
||||
variants[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.variants.put(variantName, variants);
|
||||
}
|
||||
|
||||
public boolean isVariant(final War3ID name, final int sequence) {
|
||||
final Sd<?> timeline = this.timelines.get(name);
|
||||
|
||||
|
@ -24,7 +24,7 @@ public class Attachment extends GenericObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KATV.getWar3id(), instance, 1);
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KATV.getWar3id(), sequence, frame, counter, 1);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ public class AttachmentInstance {
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
|
||||
if (internalInstance.model.ok) {
|
||||
this.attachment.getVisibility(visbilityHeap, this.instance);
|
||||
this.attachment.getVisibility(visbilityHeap, this.instance.sequence, this.instance.frame,
|
||||
this.instance.counter);
|
||||
|
||||
if (visbilityHeap[0] > 0.1) {
|
||||
// The parent instance might not actually be in a scene.
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class Batch {
|
||||
public class Batch implements GenericIndexed {
|
||||
public int index;
|
||||
public Geoset geoset;
|
||||
public Layer layer;
|
||||
@ -13,4 +13,9 @@ public class Batch {
|
||||
this.isExtended = isExtended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
@ -9,16 +8,14 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
|
||||
public class BatchGroup {
|
||||
public class BatchGroup extends GenericGroup {
|
||||
|
||||
private final MdxModel model;
|
||||
private final boolean isExtended;
|
||||
private final List<Integer> objects;
|
||||
public final boolean isExtended;
|
||||
|
||||
public BatchGroup(final MdxModel model, final boolean isExtended) {
|
||||
this.model = model;
|
||||
this.isExtended = isExtended;
|
||||
this.objects = new ArrayList<>(); // TODO IntArrayList
|
||||
}
|
||||
|
||||
public void render(final MdxComplexInstance instance) {
|
||||
|
@ -11,9 +11,9 @@ public class Bone extends GenericObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final MdxComplexInstance instance) {
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
if (this.geosetAnimation != null) {
|
||||
return this.geosetAnimation.getAlpha(out, instance);
|
||||
return this.geosetAnimation.getAlpha(out, sequence, frame, counter);
|
||||
}
|
||||
|
||||
out[0] = 1;
|
||||
|
@ -22,16 +22,16 @@ public class Camera extends AnimatedObject {
|
||||
this.targetPosition = camera.getTargetPosition();
|
||||
}
|
||||
|
||||
public int getPositionTranslation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KCTR.getWar3id(), instance, this.position);
|
||||
public int getPositionTranslation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KCTR.getWar3id(), sequence, frame, counter, this.position);
|
||||
}
|
||||
|
||||
public int getTargetTranslation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KTTR.getWar3id(), instance, this.targetPosition);
|
||||
public int getTargetTranslation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KTTR.getWar3id(), sequence, frame, counter, this.targetPosition);
|
||||
}
|
||||
|
||||
public int getRotation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KCRL.getWar3id(), instance, 0);
|
||||
public int getRotation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KCRL.getWar3id(), sequence, frame, counter, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
@ -10,15 +7,12 @@ import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SkeletalNode;
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
public class EmitterGroup {
|
||||
public class EmitterGroup extends GenericGroup {
|
||||
private final MdxModel model;
|
||||
private final List<Integer> objects;
|
||||
|
||||
public EmitterGroup(final MdxModel model) {
|
||||
this.model = model;
|
||||
this.objects = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void render(final MdxComplexInstance instance) {
|
||||
@ -55,10 +49,17 @@ public class EmitterGroup {
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_leftRightTop"), 1);
|
||||
|
||||
for (final int index : this.objects) {
|
||||
|
||||
GeometryEmitterFuncs.renderEmitter((MdxEmitter<?, ?, ?>) nodes[index].object, shader);
|
||||
}
|
||||
|
||||
}
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_leftRightTop"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_tail"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_color"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_health"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p3"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p2"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p1"), 0);
|
||||
Gdx.gl30.glVertexAttribDivisor(shader.getAttributeLocation("a_p0"), 0);
|
||||
|
||||
protected abstract void renderEmitter(EmitterObject emitter, ShaderProgram shader);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.viewer5.EmittedObject;
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
public abstract class EventObjectEmitter<EMITTER_OBJECT extends EventObjectEmitterObject, EMITTED_OBJECT extends EmittedObject<MdxComplexInstance, ? extends MdxEmitter<MdxComplexInstance, EMITTER_OBJECT, EMITTED_OBJECT>>>
|
||||
extends MdxEmitter<MdxComplexInstance, EMITTER_OBJECT, EMITTED_OBJECT> {
|
||||
private final int number = 0;
|
||||
private static final long[] valueHeap = { 0L };
|
||||
|
||||
private long lastValue = 0;
|
||||
|
||||
public EventObjectEmitter(final MdxComplexInstance instance, final EMITTER_OBJECT emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
@ -18,7 +19,15 @@ public abstract class EventObjectEmitter<EMITTER_OBJECT extends EventObjectEmitt
|
||||
if (instance.allowParticleSpawn) {
|
||||
final EMITTER_OBJECT emitterObject = this.emitterObject;
|
||||
|
||||
emitterObject.getV
|
||||
emitterObject.getValue(valueHeap, instance);
|
||||
|
||||
final long value = valueHeap[0];
|
||||
|
||||
if ((value == 1) && (value != this.lastValue)) {
|
||||
this.currentEmission += 1;
|
||||
}
|
||||
|
||||
this.lastValue = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,34 +1,85 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.audio.Sound;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.etheller.warsmash.common.FetchDataTypeName;
|
||||
import com.etheller.warsmash.common.LoadGenericCallback;
|
||||
import com.etheller.warsmash.util.MappedData;
|
||||
import com.etheller.warsmash.util.MappedDataRow;
|
||||
import com.etheller.warsmash.viewer5.GenericResource;
|
||||
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;
|
||||
|
||||
public abstract class EventObjectEmitterObject extends GenericObject implements EmitterObject {
|
||||
private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() {
|
||||
|
||||
@Override
|
||||
public Object call(final InputStream data) {
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
}
|
||||
catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new MappedData(stringBuilder.toString());
|
||||
}
|
||||
};
|
||||
private static final LoadGenericCallback decodedDataCallback = new LoadGenericCallback() {
|
||||
@Override
|
||||
public Object call(final InputStream data) {
|
||||
final FileHandle temp = new FileHandle("sound.wav") {
|
||||
@Override
|
||||
public InputStream read() {
|
||||
return data;
|
||||
};
|
||||
};
|
||||
return Gdx.audio.newSound(temp);
|
||||
}
|
||||
};
|
||||
|
||||
private int geometryEmitterType = -1;
|
||||
private final String type;
|
||||
private final String id;
|
||||
private final long[] keyFrames;
|
||||
private long globalSequence;
|
||||
private final long[] defval = { 1 };
|
||||
private MdxModel internalModel;
|
||||
private Texture internalTexture;
|
||||
private float[][] colors;
|
||||
private float[] intervalTimes;
|
||||
private float scale;
|
||||
private int columns;
|
||||
private int rows;
|
||||
private float lifeSpan;
|
||||
private int blendSrc;
|
||||
private int blendDst;
|
||||
private float[][] intervals;
|
||||
private float distanceCutoff;
|
||||
public MdxModel internalModel;
|
||||
public Texture internalTexture;
|
||||
public float[][] colors;
|
||||
public float[] intervalTimes;
|
||||
public float scale;
|
||||
public int columns;
|
||||
public int rows;
|
||||
public float lifeSpan;
|
||||
public int blendSrc;
|
||||
public int blendDst;
|
||||
public float[][] intervals;
|
||||
public float distanceCutoff;
|
||||
private float maxDistance;
|
||||
private float minDistance;
|
||||
public float minDistance;
|
||||
private float pitch;
|
||||
private float pitchVariance;
|
||||
private float volume;
|
||||
// private AudioBuffer[] decodedBuffers;
|
||||
public List<Sound> decodedBuffers;
|
||||
/**
|
||||
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
|
||||
* are loaded.
|
||||
@ -38,7 +89,7 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
*
|
||||
* The particles will simply be black.
|
||||
*/
|
||||
private final boolean ok = false;
|
||||
private boolean ok = false;
|
||||
|
||||
public EventObjectEmitterObject(final MdxModel model,
|
||||
final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) {
|
||||
@ -64,6 +115,189 @@ public abstract class EventObjectEmitterObject extends GenericObject implements
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.keyFrames = eventObject.getKeyFrames();
|
||||
|
||||
final int globalSequenceId = eventObject.getGlobalSequenceId();
|
||||
if (globalSequenceId != -1) {
|
||||
this.globalSequence = model.getGlobalSequences().get(globalSequenceId);
|
||||
}
|
||||
|
||||
final List<GenericResource> tables = new ArrayList<>();
|
||||
final PathSolver pathSolver = model.pathSolver;
|
||||
final Object solverParams = model.solverParams;
|
||||
|
||||
if ("SPN".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("SPL".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("UBR".equals(type)) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else if ("SND".equals(type)) {
|
||||
if (!model.reforged) {
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
|
||||
FetchDataTypeName.SLK, mappedDataCallback));
|
||||
}
|
||||
else {
|
||||
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
|
||||
// "Point01".
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO I am scrapping some async stuff with promises here from the JS and
|
||||
// calling load
|
||||
this.load(tables);
|
||||
}
|
||||
|
||||
private void load(final List<GenericResource> tables) {
|
||||
final MappedData firstTable = (MappedData) tables.get(0).data;
|
||||
final MappedDataRow row = firstTable.getRow(this.id);
|
||||
|
||||
if (row != null) {
|
||||
final MdxModel model = this.model;
|
||||
final ModelViewer viewer = model.viewer;
|
||||
final PathSolver pathSolver = model.pathSolver;
|
||||
|
||||
if ("SPN".equals(this.type)) {
|
||||
this.internalModel = (MdxModel) viewer.load(((String) row.get("Model")).replace(".mdl", ".mdx"),
|
||||
pathSolver, model.solverParams);
|
||||
|
||||
if (this.internalModel != null) {
|
||||
// TODO javascript async code removed here
|
||||
// this.internalModel.whenLoaded((model) => this.ok = model.ok)
|
||||
this.ok = this.internalModel.ok;
|
||||
}
|
||||
}
|
||||
else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
|
||||
final String texturesExt = model.reforged ? ".dds" : ".blp";
|
||||
|
||||
this.internalTexture = (Texture) viewer.load(
|
||||
"ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver,
|
||||
model.solverParams);
|
||||
|
||||
this.scale = (Float) row.get("Scale");
|
||||
this.colors = new float[][] {
|
||||
{ ((Float) row.get("StartR")).floatValue(), ((Float) row.get("StartG")).floatValue(),
|
||||
((Float) row.get("StartB")).floatValue(), ((Float) row.get("StartA")).floatValue() },
|
||||
{ ((Float) row.get("MiddleR")).floatValue(), ((Float) row.get("MiddleG")).floatValue(),
|
||||
((Float) row.get("MiddleB")).floatValue(), ((Float) row.get("MiddleA")).floatValue() },
|
||||
{ ((Float) row.get("EndR")).floatValue(), ((Float) row.get("EndG")).floatValue(),
|
||||
((Float) row.get("EndB")).floatValue(), ((Float) row.get("EndA")).floatValue() } };
|
||||
|
||||
if ("SPL".equals(this.type)) {
|
||||
this.columns = ((Number) row.get("Columns")).intValue();
|
||||
this.rows = ((Number) row.get("Rows")).intValue();
|
||||
this.lifeSpan = ((Number) row.get("Lifespan")).floatValue()
|
||||
+ ((Number) row.get("Decay")).floatValue();
|
||||
this.intervals = new float[][] {
|
||||
{ ((Float) row.get("UVLifespanStart")).floatValue(),
|
||||
((Float) row.get("UVLifespanEnd")).floatValue(),
|
||||
((Float) row.get("LifespanRepeat")).floatValue() },
|
||||
{ ((Float) row.get("UVDecayStart")).floatValue(),
|
||||
((Float) row.get("UVDecayEnd")).floatValue(),
|
||||
((Float) row.get("DecayRepeat")).floatValue() }, };
|
||||
}
|
||||
else {
|
||||
this.columns = 1;
|
||||
this.rows = 1;
|
||||
this.lifeSpan = ((Number) row.get("BirthTime")).floatValue()
|
||||
+ ((Number) row.get("PauseTime")).floatValue() + ((Number) row.get("Decay")).floatValue();
|
||||
this.intervalTimes = new float[] { ((Number) row.get("BirthTime")).floatValue(),
|
||||
((Number) row.get("PauseTime")).floatValue(), ((Number) row.get("Decay")).floatValue() };
|
||||
}
|
||||
|
||||
final int[] blendModes = FilterMode
|
||||
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
|
||||
.fromId(((Number) row.get("BlendMode")).intValue()));
|
||||
|
||||
this.blendSrc = blendModes[0];
|
||||
this.blendDst = blendModes[1];
|
||||
|
||||
this.ok = true;
|
||||
}
|
||||
else if ("SND".equals(this.type)) {
|
||||
// Only load sounds if audio is enabled.
|
||||
// This is mostly to save on bandwidth and loading time, especially when loading
|
||||
// full maps.
|
||||
if (viewer.audioEnabled) {
|
||||
final MappedData animSounds = (MappedData) tables.get(1).data;
|
||||
|
||||
final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel"));
|
||||
|
||||
if (animSoundsRow != null) {
|
||||
this.distanceCutoff = ((Number) animSoundsRow.get("DistanceCutoff")).floatValue();
|
||||
this.maxDistance = ((Number) animSoundsRow.get("MaxDistance")).floatValue();
|
||||
this.minDistance = ((Number) animSoundsRow.get("MinDistance")).floatValue();
|
||||
this.pitch = ((Number) animSoundsRow.get("Pitch")).floatValue();
|
||||
this.pitchVariance = ((Number) animSoundsRow.get("PitchVariance")).floatValue();
|
||||
this.volume = ((Number) animSoundsRow.get("Volume")).floatValue();
|
||||
|
||||
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
|
||||
final GenericResource[] resources = new GenericResource[fileNames.length];
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
resources[i] = viewer.loadGeneric(
|
||||
pathSolver.solve(((String) animSoundsRow.get("DirectoryBase")) + fileNames[i],
|
||||
model.solverParams).finalSrc,
|
||||
FetchDataTypeName.ARRAY_BUFFER, decodedDataCallback);
|
||||
}
|
||||
|
||||
// TODO JS async removed
|
||||
for (final GenericResource resource : resources) {
|
||||
this.decodedBuffers.add((Sound) resource.data);
|
||||
}
|
||||
this.ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown event object ID: " + this.type + this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getValue(final long[] out, final MdxComplexInstance instance) {
|
||||
if (this.globalSequence != -1) {
|
||||
|
||||
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
|
||||
}
|
||||
else if (instance.sequence != -1) {
|
||||
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
|
||||
|
||||
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
|
||||
}
|
||||
else {
|
||||
out[0] = this.defval[0];
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int getValueAtTime(final long[] out, final long frame, final long start, final long end) {
|
||||
if ((frame >= start) && (frame <= end)) {
|
||||
for (int i = this.keyFrames.length - 1; i > -1; i--) {
|
||||
if (this.keyFrames[i] < start) {
|
||||
out[0] = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
else if (this.keyFrames[i] <= frame) {
|
||||
out[0] = 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out[0] = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.audio.Sound;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.viewer5.AudioBufferSource;
|
||||
import com.etheller.warsmash.viewer5.AudioContext;
|
||||
import com.etheller.warsmash.viewer5.AudioPanner;
|
||||
import com.etheller.warsmash.viewer5.EmittedObject;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
|
||||
public class EventObjectSnd extends EmittedObject<MdxComplexInstance, EventObjectSndEmitter> {
|
||||
public EventObjectSnd(final EventObjectSndEmitter emitter) {
|
||||
super(emitter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(final int flags) {
|
||||
final EventObjectSndEmitter emitter = this.emitter;
|
||||
final MdxComplexInstance instance = emitter.instance;
|
||||
final ModelViewer viewer = instance.model.viewer;
|
||||
final Scene scene = instance.scene;
|
||||
|
||||
// Is audio enabled both viewer-wide and in this scene?
|
||||
if (viewer.audioEnabled && scene.audioEnabled) {
|
||||
final EventObjectEmitterObject emitterObject = emitter.emitterObject;
|
||||
final MdxNode node = instance.nodes[emitterObject.index];
|
||||
final AudioContext audioContext = scene.audioContext;
|
||||
final List<Sound> decodedBuffers = emitterObject.decodedBuffers;
|
||||
final AudioPanner panner = audioContext.createPanner();
|
||||
final AudioBufferSource source = audioContext.createBufferSource();
|
||||
final Vector3 location = node.worldLocation;
|
||||
|
||||
// Panner settings
|
||||
panner.setPosition(location.x, location.y, location.z);
|
||||
panner.maxDistance = emitterObject.distanceCutoff;
|
||||
panner.refDistance = emitterObject.minDistance;
|
||||
panner.connect(audioContext.destination);
|
||||
|
||||
// Source.
|
||||
source.buffer = decodedBuffers.get((int) (Math.random() * decodedBuffers.size()));
|
||||
source.connect(panner);
|
||||
|
||||
// Make a sound.
|
||||
source.start(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class EventObjectSndEmitter extends EventObjectEmitter<EventObjectEmitterObject, EventObjectSnd> {
|
||||
|
||||
public EventObjectSndEmitter(final MdxComplexInstance instance, final EventObjectEmitterObject emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventObjectSnd createObject() {
|
||||
return new EventObjectSnd(this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class EventObjectSplEmitter extends EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr> {
|
||||
public EventObjectSplEmitter(final MdxComplexInstance instance, final EventObjectEmitterObject emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventObjectSplUbr createObject() {
|
||||
return new EventObjectSplUbr(this);
|
||||
}
|
||||
}
|
@ -1,12 +1,63 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.viewer5.EmittedObject;
|
||||
|
||||
public class EventObjectSplUbr<EMITTER extends MdxEmitter<?, ?, ?>> extends EmittedObject<MdxComplexInstance, EMITTER> {
|
||||
private final float[] vertices = new float[12];
|
||||
public class EventObjectSplUbr
|
||||
extends EmittedObject<MdxComplexInstance, EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr>> {
|
||||
private static final Vector3 vertexHeap = new Vector3();
|
||||
|
||||
public final float[] vertices = new float[12];
|
||||
|
||||
public EventObjectSplUbr(final EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr> emitter) {
|
||||
super(emitter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(final int flags) {
|
||||
final EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr> emitter = this.emitter;
|
||||
final MdxComplexInstance instance = emitter.instance;
|
||||
final EventObjectEmitterObject emitterObject = emitter.emitterObject;
|
||||
final float[] vertices = this.vertices;
|
||||
final float scale = emitterObject.scale;
|
||||
final MdxNode node = instance.nodes[emitterObject.index];
|
||||
final Matrix4 worldMatrix = node.worldMatrix;
|
||||
|
||||
this.health = emitterObject.lifeSpan;
|
||||
|
||||
vertexHeap.x = scale;
|
||||
vertexHeap.y = scale;
|
||||
vertexHeap.prj(worldMatrix);
|
||||
vertices[0] = vertexHeap.x;
|
||||
vertices[1] = vertexHeap.y;
|
||||
vertices[2] = vertexHeap.z;
|
||||
|
||||
vertexHeap.x = -scale;
|
||||
vertexHeap.y = scale;
|
||||
vertexHeap.prj(worldMatrix);
|
||||
vertices[3] = vertexHeap.x;
|
||||
vertices[4] = vertexHeap.y;
|
||||
vertices[5] = vertexHeap.z;
|
||||
|
||||
vertexHeap.x = -scale;
|
||||
vertexHeap.y = -scale;
|
||||
vertexHeap.prj(worldMatrix);
|
||||
vertices[6] = vertexHeap.x;
|
||||
vertices[7] = vertexHeap.y;
|
||||
vertices[8] = vertexHeap.z;
|
||||
|
||||
vertexHeap.x = scale;
|
||||
vertexHeap.y = -scale;
|
||||
vertexHeap.prj(worldMatrix);
|
||||
vertices[9] = vertexHeap.x;
|
||||
vertices[10] = vertexHeap.y;
|
||||
vertices[11] = vertexHeap.z;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
this.health -= dt;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.viewer5.EmittedObject;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
|
||||
public class EventObjectSpn extends EmittedObject<MdxComplexInstance, EventObjectSpnEmitter> {
|
||||
private final MdxComplexInstance internalInstance;
|
||||
|
||||
public EventObjectSpn(final EventObjectSpnEmitter emitter) {
|
||||
super(emitter);
|
||||
|
||||
final EventObjectEmitterObject emitterObject = emitter.emitterObject;
|
||||
final MdxModel internalModel = emitterObject.internalModel;
|
||||
|
||||
this.internalInstance = (MdxComplexInstance) internalModel.addInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(final int flags) {
|
||||
final EventObjectSpnEmitter emitter = this.emitter;
|
||||
final MdxComplexInstance instance = emitter.instance;
|
||||
final Scene scene = instance.scene;
|
||||
final MdxNode node = instance.nodes[emitter.emitterObject.index];
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
|
||||
internalInstance.setSequence(0);
|
||||
internalInstance.setTransformation(node.worldLocation, node.worldRotation, node.worldScale);
|
||||
internalInstance.show();
|
||||
|
||||
scene.addInstance(internalInstance);
|
||||
|
||||
this.health = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
final MdxComplexInstance instance = this.internalInstance;
|
||||
final MdxModel model = (MdxModel) instance.model;
|
||||
|
||||
// Once the sequence finishes, this event object dies
|
||||
if (instance.frame >= model.getSequences().get(0).getInterval()[1]) {
|
||||
this.health = 0;
|
||||
|
||||
instance.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class EventObjectSpnEmitter extends EventObjectEmitter<EventObjectEmitterObject, EventObjectSpn> {
|
||||
|
||||
public EventObjectSpnEmitter(final MdxComplexInstance instance, final EventObjectEmitterObject emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventObjectSpn createObject() {
|
||||
return new EventObjectSpn(this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class EventObjectUbrEmitter extends EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr> {
|
||||
public EventObjectUbrEmitter(final MdxComplexInstance instance, final EventObjectEmitterObject emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventObjectSplUbr createObject() {
|
||||
return new EventObjectSplUbr(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GenericGroup {
|
||||
public final List<Integer> objects;
|
||||
|
||||
public GenericGroup() {
|
||||
this.objects = new ArrayList<>(); // TODO IntArrayList
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public interface GenericIndexed {
|
||||
public int getIndex();
|
||||
}
|
@ -3,7 +3,7 @@ package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
|
||||
public class GenericObject extends AnimatedObject {
|
||||
public class GenericObject extends AnimatedObject implements GenericIndexed {
|
||||
|
||||
public final int index;
|
||||
public final String name;
|
||||
@ -113,21 +113,24 @@ public class GenericObject extends AnimatedObject {
|
||||
* Many of the generic objects have animated visibilities. This is a generic
|
||||
* getter to allow the code to be consistent.
|
||||
*/
|
||||
public int getVisibility(final float[] out, final MdxComplexInstance instance) {
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
out[0] = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getTranslation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KGTR.getWar3id(), instance, RenderMathUtils.FLOAT_VEC3_ZERO);
|
||||
public int getTranslation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KGTR.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_VEC3_ZERO);
|
||||
}
|
||||
|
||||
public int getRotation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getQuadValue(out, AnimationMap.KGRT.getWar3id(), instance, RenderMathUtils.FLOAT_QUAT_DEFAULT);
|
||||
public int getRotation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getQuadValue(out, AnimationMap.KGRT.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_QUAT_DEFAULT);
|
||||
}
|
||||
|
||||
public int getScale(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KGSC.getWar3id(), instance, RenderMathUtils.FLOAT_VEC3_ONE);
|
||||
public int getScale(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KGSC.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_VEC3_ONE);
|
||||
}
|
||||
|
||||
public boolean isTranslationVariant(final int sequence) {
|
||||
@ -156,4 +159,9 @@ public class GenericObject extends AnimatedObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.viewer5.Camera;
|
||||
@ -12,6 +14,8 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
import com.etheller.warsmash.viewer5.gl.ClientBuffer;
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
//The total storage that emitted objects can use.
|
||||
//This is enough to support all of the MDX geometry emitters.
|
||||
@ -65,11 +69,11 @@ public class GeometryEmitterFuncs {
|
||||
private static final Vector3 endHeap = new Vector3();
|
||||
private static final float[] vectorTemp = new float[3];
|
||||
|
||||
public static void bindParticleEmitter2Buffer(final ParticleEmitter2 emitter, final ByteBuffer buffer) {
|
||||
public static void bindParticleEmitter2Buffer(final ParticleEmitter2 emitter, final ClientBuffer buffer) {
|
||||
final MdxComplexInstance instance = emitter.instance;
|
||||
final List<Particle2> objects = emitter.objects;
|
||||
final ByteBuffer byteView = buffer;
|
||||
final FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
final ByteBuffer byteView = buffer.byteView;
|
||||
final FloatBuffer floatView = buffer.floatView;
|
||||
final ParticleEmitter2Object emitterObject = emitter.emitterObject;
|
||||
final int modelSpace = emitterObject.modelSpace;
|
||||
final float tailLength = emitterObject.tailLength;
|
||||
@ -203,10 +207,10 @@ public class GeometryEmitterFuncs {
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindRibbonEmitterBuffer(final RibbonEmitter emitter, final ByteBuffer buffer) {
|
||||
public static void bindRibbonEmitterBuffer(final RibbonEmitter emitter, final ClientBuffer buffer) {
|
||||
Ribbon object = emitter.first;
|
||||
final ByteBuffer byteView = buffer;
|
||||
final FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
final ByteBuffer byteView = buffer.byteView;
|
||||
final FloatBuffer floatView = buffer.floatView;
|
||||
final RibbonEmitterObject emitterObject = emitter.emitterObject;
|
||||
final long columns = emitterObject.columns;
|
||||
final int alive = emitter.alive;
|
||||
@ -277,6 +281,154 @@ public class GeometryEmitterFuncs {
|
||||
shader.setUniformf("u_rows", emitterObject.rows);
|
||||
}
|
||||
|
||||
public static void bindEventObjectEmitterBuffer(
|
||||
final EventObjectEmitter<EventObjectEmitterObject, EventObjectSplUbr> emitter, final ClientBuffer buffer) {
|
||||
final List<EventObjectSplUbr> objects = emitter.objects;
|
||||
final FloatBuffer floatView = buffer.floatView;
|
||||
int offset = 0;
|
||||
|
||||
for (final EventObjectSplUbr object : objects) {
|
||||
final int floatOffset = offset * FLOATS_PER_OBJECT;
|
||||
final int p0Offset = floatOffset + FLOAT_OFFSET_P0;
|
||||
final float[] vertices = object.vertices;
|
||||
|
||||
floatView.put(p0Offset + 0, vertices[0]);
|
||||
floatView.put(p0Offset + 1, vertices[1]);
|
||||
floatView.put(p0Offset + 2, vertices[2]);
|
||||
floatView.put(p0Offset + 3, vertices[3]);
|
||||
floatView.put(p0Offset + 4, vertices[4]);
|
||||
floatView.put(p0Offset + 5, vertices[5]);
|
||||
floatView.put(p0Offset + 6, vertices[6]);
|
||||
floatView.put(p0Offset + 7, vertices[7]);
|
||||
floatView.put(p0Offset + 8, vertices[8]);
|
||||
floatView.put(p0Offset + 9, vertices[9]);
|
||||
floatView.put(p0Offset + 10, vertices[10]);
|
||||
floatView.put(p0Offset + 11, vertices[11]);
|
||||
|
||||
floatView.put(floatOffset + FLOAT_OFFSET_HEALTH, object.health);
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindEventObjectSplEmitterShader(final EventObjectSplEmitter emitter,
|
||||
final ShaderProgram shader) {
|
||||
final TextureMapper textureMapper = emitter.instance.textureMapper;
|
||||
final EventObjectEmitterObject emitterObject = emitter.emitterObject;
|
||||
final float[] intervalTimes = emitterObject.intervalTimes;
|
||||
final float[][] intervals = emitterObject.intervals;
|
||||
final float[][] colors = emitterObject.colors;
|
||||
final MdxModel model = emitterObject.model;
|
||||
final GL20 gl = model.viewer.gl;
|
||||
final Texture texture = emitterObject.internalTexture;
|
||||
|
||||
gl.glBlendFunc(emitterObject.blendSrc, emitterObject.blendDst);
|
||||
|
||||
Texture finalTexture = textureMapper.get(texture);
|
||||
if (finalTexture == null) {
|
||||
finalTexture = texture;
|
||||
}
|
||||
model.viewer.webGL.bindTexture(finalTexture, 0);
|
||||
|
||||
shader.setUniformf("u_lifeSpan", emitterObject.lifeSpan);
|
||||
shader.setUniformf("u_columns", emitterObject.columns);
|
||||
shader.setUniformf("rows", emitterObject.rows);
|
||||
|
||||
// 3 because the uniform is shared with UBR, which has 3 values.
|
||||
vectorTemp[0] = intervalTimes[0];
|
||||
vectorTemp[1] = intervalTimes[1];
|
||||
vectorTemp[2] = 0;
|
||||
shader.setUniform3fv("u_intervalTimes", vectorTemp, 0, 3);
|
||||
|
||||
shader.setUniform3fv("u_intervals[0]", intervals[0], 0, 3);
|
||||
shader.setUniform3fv("u_intervals[1]", intervals[1], 0, 3);
|
||||
|
||||
shader.setUniform3fv("u_colors[0]", colors[0], 0, 3);
|
||||
shader.setUniform3fv("u_colors[1]", colors[1], 0, 3);
|
||||
shader.setUniform3fv("u_colors[2]", colors[2], 0, 3);
|
||||
}
|
||||
|
||||
public static void bindEventObjectUbrEmitterShader(final EventObjectUbrEmitter emitter,
|
||||
final ShaderProgram shader) {
|
||||
final TextureMapper textureMapper = emitter.instance.textureMapper;
|
||||
final EventObjectEmitterObject emitterObject = emitter.emitterObject;
|
||||
final float[] intervalTimes = emitterObject.intervalTimes;
|
||||
final float[][] colors = emitterObject.colors;
|
||||
final MdxModel model = emitterObject.model;
|
||||
final GL20 gl = model.viewer.gl;
|
||||
final Texture texture = emitterObject.internalTexture;
|
||||
|
||||
gl.glBlendFunc(emitterObject.blendSrc, emitterObject.blendDst);
|
||||
|
||||
Texture finalTexture = textureMapper.get(texture);
|
||||
if (finalTexture == null) {
|
||||
finalTexture = texture;
|
||||
}
|
||||
model.viewer.webGL.bindTexture(finalTexture, 0);
|
||||
|
||||
shader.setUniformf("u_lifeSpan", emitterObject.lifeSpan);
|
||||
shader.setUniformf("u_columns", emitterObject.columns);
|
||||
shader.setUniformf("rows", emitterObject.rows);
|
||||
|
||||
shader.setUniform3fv("u_intervalTimes", intervalTimes, 0, 3);
|
||||
|
||||
shader.setUniform3fv("u_colors[0]", colors[0], 0, 3);
|
||||
shader.setUniform3fv("u_colors[1]", colors[1], 0, 3);
|
||||
shader.setUniform3fv("u_colors[2]", colors[2], 0, 3);
|
||||
}
|
||||
|
||||
public static void renderEmitter(final MdxEmitter<?, ?, ?> emitter, final ShaderProgram shader) {
|
||||
int alive = emitter.alive;
|
||||
final EmitterObject emitterObject = emitter.emitterObject;
|
||||
final int emitterType = emitterObject.getGeometryEmitterType();
|
||||
|
||||
if (emitterType == EMITTER_RIBBON) {
|
||||
alive -= 1;
|
||||
}
|
||||
|
||||
if (alive > 0) {
|
||||
final ModelViewer viewer = emitter.instance.model.viewer;
|
||||
final ClientBuffer buffer = viewer.buffer;
|
||||
final GL20 gl = viewer.gl;
|
||||
final int size = alive * BYTES_PER_OBJECT;
|
||||
|
||||
switch (emitterType) {
|
||||
case EMITTER_PARTICLE2:
|
||||
bindParticleEmitter2Buffer((ParticleEmitter2) emitter, buffer);
|
||||
bindParticleEmitter2Shader((ParticleEmitter2) emitter, shader);
|
||||
break;
|
||||
case EMITTER_RIBBON:
|
||||
bindRibbonEmitterBuffer((RibbonEmitter) emitter, buffer);
|
||||
bindRibbonEmitterShader((RibbonEmitter) emitter, shader);
|
||||
break;
|
||||
case EMITTER_SPLAT:
|
||||
bindEventObjectEmitterBuffer((EventObjectSplEmitter) emitter, buffer);
|
||||
bindEventObjectSplEmitterShader((EventObjectSplEmitter) emitter, shader);
|
||||
break;
|
||||
default:
|
||||
bindEventObjectEmitterBuffer((EventObjectUbrEmitter) emitter, buffer);
|
||||
bindEventObjectUbrEmitterShader((EventObjectUbrEmitter) emitter, shader);
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.bindAndUpdate(size);
|
||||
|
||||
shader.setUniformi("u_emitter", emitterType);
|
||||
|
||||
shader.setVertexAttribute("a_p0", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P0);
|
||||
shader.setVertexAttribute("a_p1", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P1);
|
||||
shader.setVertexAttribute("a_p2", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P2);
|
||||
shader.setVertexAttribute("a_p3", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P3);
|
||||
shader.setVertexAttribute("a_health", 1, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_HEALTH);
|
||||
shader.setVertexAttribute("a_color", 4, GL20.GL_UNSIGNED_BYTE, true, BYTES_PER_OBJECT, BYTE_OFFSET_COLOR);
|
||||
shader.setVertexAttribute("a_tail", 1, GL20.GL_UNSIGNED_BYTE, false, BYTES_PER_OBJECT, BYTE_OFFSET_TAIL);
|
||||
shader.setVertexAttribute("a_leftRightTop", 3, GL20.GL_UNSIGNED_BYTE, false, BYTES_PER_OBJECT,
|
||||
BYTE_OFFSET_LEFT_RIGHT_TOP);
|
||||
|
||||
Gdx.gl30.glDrawArraysInstanced(GL30.GL_TRIANGLES, 0, 6, alive);
|
||||
}
|
||||
}
|
||||
|
||||
private static final float[] asFloatArray(final Vector3 vec) {
|
||||
vectorTemp[0] = vec.x;
|
||||
vectorTemp[1] = vec.y;
|
||||
|
@ -74,18 +74,18 @@ public class Geoset {
|
||||
this.hasObjectAnim = hasAlphaAnim || hasColorAnim;
|
||||
}
|
||||
|
||||
public int getAlpha(final float[] out, final MdxComplexInstance instance) {
|
||||
public int getAlpha(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
if (this.geosetAnimation != null) {
|
||||
return this.geosetAnimation.getAlpha(out, instance);
|
||||
return this.geosetAnimation.getAlpha(out, sequence, frame, counter);
|
||||
}
|
||||
|
||||
out[0] = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getColor(final float[] out, final MdxComplexInstance instance) {
|
||||
public int getColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
if (this.geosetAnimation != null) {
|
||||
return this.geosetAnimation.getAlpha(out, instance);
|
||||
return this.geosetAnimation.getAlpha(out, sequence, frame, counter);
|
||||
}
|
||||
|
||||
Arrays.fill(out, 1);
|
||||
|
@ -19,12 +19,12 @@ public class GeosetAnimation extends AnimatedObject {
|
||||
this.geosetId = geosetAnimation.getGeosetId();
|
||||
}
|
||||
|
||||
public int getAlpha(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KGAO.getWar3id(), instance, this.alpha);
|
||||
public int getAlpha(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KGAO.getWar3id(), sequence, frame, counter, this.alpha);
|
||||
}
|
||||
|
||||
public int getColor(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KGAC.getWar3id(), instance, this.color);
|
||||
public int getColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KGAC.getWar3id(), sequence, frame, counter, this.color);
|
||||
}
|
||||
|
||||
public boolean isAlphaVariant(final int sequence) {
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
/**
|
||||
* An MDX helper.
|
||||
*/
|
||||
public class Helper extends GenericObject {
|
||||
public Helper(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.GenericObject object,
|
||||
final int index) {
|
||||
super(model, object, index);
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +1,121 @@
|
||||
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.Layer.FilterMode;
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
|
||||
public class Layer {
|
||||
public MdxModel model;
|
||||
public com.etheller.warsmash.parsers.mdlx.Layer layer;
|
||||
public int layerId;
|
||||
/**
|
||||
* An MDX layer.
|
||||
*/
|
||||
public class Layer extends AnimatedObject {
|
||||
public int index;
|
||||
public int priorityPlane;
|
||||
|
||||
public int filterMode;
|
||||
public int textureId;
|
||||
public int coordId;
|
||||
public float alpha;
|
||||
|
||||
public int index = -666;
|
||||
public int unshaded;
|
||||
public int sphereEnvironmentMap;
|
||||
public int twoSided;
|
||||
public int unfogged;
|
||||
public int noDepthTest;
|
||||
public int noDepthSet;
|
||||
public boolean depthMaskValue;
|
||||
public int blendSrc;
|
||||
public int blendDst;
|
||||
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) {
|
||||
super(model, layer);
|
||||
this.model = model;
|
||||
this.layer = layer;
|
||||
this.layerId = layerId;
|
||||
this.priorityPlane = priorityPlane;
|
||||
|
||||
final FilterMode filterMode2 = layer.getFilterMode();
|
||||
this.filterMode = filterMode2.ordinal();
|
||||
final com.etheller.warsmash.parsers.mdlx.Layer.FilterMode filterMode = layer.getFilterMode();
|
||||
final int textureAnimationId = layer.getTextureAnimationId();
|
||||
final GL20 gl = model.viewer.gl;
|
||||
|
||||
this.index = layerId;
|
||||
this.priorityPlane = priorityPlane;
|
||||
this.filterMode = filterMode.ordinal();
|
||||
this.textureId = layer.getTextureId();
|
||||
// this.coo
|
||||
this.index
|
||||
this.coordId = (int) layer.getCoordId();
|
||||
this.alpha = layer.getAlpha();
|
||||
|
||||
final int flags = layer.getFlags();
|
||||
|
||||
this.unshaded = flags & 0x1;
|
||||
this.sphereEnvironmentMap = flags & 0x2;
|
||||
this.twoSided = flags & 0x10;
|
||||
this.unfogged = flags & 0x20;
|
||||
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.blendSrc = 0;
|
||||
this.blendDst = 0;
|
||||
this.blended = (filterMode.ordinal() > 1);
|
||||
|
||||
if (this.blended) {
|
||||
final int[] result = FilterMode.layerFilterMode(filterMode);
|
||||
this.blendSrc = result[0];
|
||||
this.blendDst = result[1];
|
||||
}
|
||||
|
||||
if (textureAnimationId != -1) {
|
||||
final TextureAnimation textureAnimation = model.getTextureAnimations().get(textureAnimationId);
|
||||
|
||||
if (textureAnimation != null) {
|
||||
this.textureAnimation = textureAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
this.addVariants(AnimationMap.KMTA.getWar3id(), "alpha");
|
||||
this.addVariants(AnimationMap.KMTF.getWar3id(), "textureId");
|
||||
}
|
||||
|
||||
public void bind(final ShaderProgram shader) {
|
||||
// TODO Auto-generated method stub
|
||||
final GL20 gl = this.model.viewer.gl;
|
||||
|
||||
// gl.uniform1f(shader.uniforms.u_unshaded, this.unshaded);
|
||||
shader.setUniformf("u_filterMode", this.filterMode);
|
||||
|
||||
if (this.blended) {
|
||||
gl.glEnable(GL20.GL_BLEND);
|
||||
gl.glBlendFunc(this.blendSrc, this.blendDst);
|
||||
}
|
||||
else {
|
||||
gl.glDisable(GL20.GL_BLEND);
|
||||
}
|
||||
|
||||
if (this.twoSided != 0) {
|
||||
gl.glEnable(GL20.GL_CULL_FACE);
|
||||
}
|
||||
else {
|
||||
gl.glDisable(GL20.GL_CULL_FACE);
|
||||
}
|
||||
|
||||
if (this.noDepthTest != 0) {
|
||||
gl.glDisable(GL20.GL_DEPTH_TEST);
|
||||
}
|
||||
else {
|
||||
gl.glEnable(GL20.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
if (this.noDepthSet != 0) {
|
||||
gl.glDepthMask(false);
|
||||
}
|
||||
else {
|
||||
gl.glDepthMask(this.depthMaskValue);
|
||||
}
|
||||
}
|
||||
|
||||
public int getAlpha(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KMTA.getWar3id(), sequence, frame, counter, this.alpha);
|
||||
}
|
||||
|
||||
public int getTextureId(final long[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KMTF.getWar3id(), sequence, frame, counter, this.textureId);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
|
||||
public class Light extends GenericObject {
|
||||
|
||||
private final int type;
|
||||
private final float[] attenuation;
|
||||
private final float[] color;
|
||||
private final float intensity;
|
||||
private final float[] ambientColor;
|
||||
private final float ambientIntensity;
|
||||
|
||||
public Light(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Light light, final int index) {
|
||||
super(model, light, index);
|
||||
|
||||
this.type = light.getType();
|
||||
this.attenuation = light.getAttenuation();
|
||||
this.color = light.getColor();
|
||||
this.intensity = light.getIntensity();
|
||||
this.ambientColor = light.getAmbientColor();
|
||||
this.ambientIntensity = light.getAmbientIntensity();
|
||||
}
|
||||
|
||||
public int getAttenuationStart(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLAS.getWar3id(), sequence, frame, counter, this.attenuation[0]);
|
||||
}
|
||||
|
||||
public int getAttenuationEnd(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLAE.getWar3id(), sequence, frame, counter, this.attenuation[1]);
|
||||
}
|
||||
|
||||
public int getIntensity(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLAI.getWar3id(), sequence, frame, counter, this.intensity);
|
||||
}
|
||||
|
||||
public int getColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KLAC.getWar3id(), sequence, frame, counter, this.color);
|
||||
}
|
||||
|
||||
public int getAmbientIntensity(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLBI.getWar3id(), sequence, frame, counter, this.ambientIntensity);
|
||||
}
|
||||
|
||||
public int getAmbientColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KLBC.getWar3id(), sequence, frame, counter, this.ambientColor);
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import com.etheller.warsmash.viewer5.HandlerResource;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.Resource;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.handlers.ModelHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
@ -45,7 +45,7 @@ public class MdxHandler extends ModelHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource<?> construct(final ResourceHandlerConstructionParams params) {
|
||||
public HandlerResource<?> construct(final ResourceHandlerConstructionParams params) {
|
||||
return new MdxModel((MdxHandler) params.getHandler(), params.getViewer(), params.getExtension(),
|
||||
params.getPathSolver(), params.getFetchUrl());
|
||||
}
|
||||
|
@ -11,6 +11,31 @@ import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
|
||||
public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
public boolean reforged = false;
|
||||
public boolean hd = false;
|
||||
public SolverParams solverParams = new SolverParams();
|
||||
public String name = "";
|
||||
public List<Sequence> sequences = new ArrayList<>();
|
||||
public List<Integer> globalSequences = new ArrayList<>();
|
||||
public List<Material> materials = new ArrayList<>();
|
||||
public List<Layer> layers = new ArrayList<>();
|
||||
public List<Integer> replaceables = new ArrayList<>();
|
||||
public List<Texture> textures = new ArrayList<>();
|
||||
public List<TextureAnimation> textureAnimations = new ArrayList<>();
|
||||
public List<Geoset> geosets = new ArrayList<>();
|
||||
public List<GeosetAnimation> geosetAnimations = new ArrayList<>();
|
||||
public List<Bone> bones = new ArrayList<>();
|
||||
public List<Light> lights = new ArrayList<>();
|
||||
public List<Helper> helpers = new ArrayList<>();
|
||||
public List<Attachment> attachments = new ArrayList<>();
|
||||
public List<float[]> pivotPoints = new ArrayList<>();
|
||||
public List<ParticleEmitterObject> particleEmitters = new ArrayList<>();
|
||||
public List<ParticleEmitter2Object> particleEmitters2 = new ArrayList<>();
|
||||
public List<RibbonEmitterObject> ribbonEmitters = new ArrayList<>();
|
||||
public List<Camera> cameras = new ArrayList<>();
|
||||
public List<EventObjectEmitterObject> eventObjects = new ArrayList<>();
|
||||
public
|
||||
|
||||
private MdlxModel model;
|
||||
|
||||
public int arrayBuffer;
|
||||
@ -18,9 +43,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
|
||||
public List<Batch> batches = new ArrayList<>(); // TODO??
|
||||
|
||||
public List<Integer> replaceables = new ArrayList<>();
|
||||
|
||||
public boolean reforged = false;
|
||||
public List<Object> opaqueGroups;
|
||||
public List<Object> translucentGroups;
|
||||
|
||||
public MdxModel(final MdxHandler handler, final ModelViewer viewer, final String extension,
|
||||
final PathSolver pathSolver, final String fetchUrl) {
|
||||
@ -70,4 +94,18 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public List<TextureAnimation> getTextureAnimations() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public List<Geoset> getGeosets() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
private static final class SolverParams {
|
||||
public boolean reforged;
|
||||
public boolean hd;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.EmittedObject;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
|
||||
/**
|
||||
* A spawned model particle.
|
||||
*/
|
||||
public class Particle extends EmittedObject<MdxComplexInstance, ParticleEmitter> {
|
||||
private static final Quaternion rotationHeap = new Quaternion();
|
||||
private static final Quaternion rotationHeap2 = new Quaternion();
|
||||
private static final Vector3 velocityHeap = new Vector3();
|
||||
private static final float[] latitudeHeap = new float[1];
|
||||
// private static final float[] longitudeHeap = new float[1];
|
||||
private static final float[] lifeSpanHeap = new float[1];
|
||||
private static final float[] gravityHeap = new float[1];
|
||||
private static final float[] speedHeap = new float[1];
|
||||
private static final float[] tempVector = new float[3];
|
||||
|
||||
private final MdxComplexInstance internalInstance;
|
||||
private final Vector3 velocity = new Vector3();
|
||||
private float gravity;
|
||||
|
||||
public Particle(final ParticleEmitter emitter) {
|
||||
super(emitter);
|
||||
|
||||
final ParticleEmitterObject emitterObject = emitter.emitterObject;
|
||||
|
||||
this.internalInstance = (MdxComplexInstance) emitterObject.internalModel.addInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(final int flags) {
|
||||
final ParticleEmitter emitter = this.emitter;
|
||||
final MdxComplexInstance instance = emitter.instance;
|
||||
final int sequence = instance.sequence;
|
||||
final int frame = instance.frame;
|
||||
final int counter = instance.counter;
|
||||
final Scene scene = instance.scene;
|
||||
final ParticleEmitterObject emitterObject = emitter.emitterObject;
|
||||
final MdxNode node = instance.nodes[emitterObject.index];
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
final Vector3 scale = node.worldScale;
|
||||
final Vector3 velocity = this.velocity;
|
||||
|
||||
emitterObject.getLatitude(latitudeHeap, sequence, frame, counter);
|
||||
// longitude?? commented in ghostwolf JS
|
||||
emitterObject.getLifeSpan(lifeSpanHeap, sequence, frame, counter);
|
||||
emitterObject.getGravity(gravityHeap, sequence, frame, counter);
|
||||
emitterObject.getSpeed(speedHeap, sequence, frame, counter);
|
||||
|
||||
this.health = lifeSpanHeap[0];
|
||||
this.gravity = gravityHeap[0] * scale.z;
|
||||
|
||||
// Local rotation
|
||||
rotationHeap.idt();
|
||||
rotationHeap.mulLeft(rotationHeap2.setFromAxisRad(0, 0, 1,
|
||||
RenderMathUtils.randomInRange((float) -Math.PI, (float) Math.PI)));
|
||||
rotationHeap.mulLeft(rotationHeap2.setFromAxisRad(0, 1, 0,
|
||||
RenderMathUtils.randomInRange(-latitudeHeap[0], latitudeHeap[0])));
|
||||
velocity.set(RenderMathUtils.VEC3_UNIT_Z);
|
||||
rotationHeap.transform(velocity);
|
||||
|
||||
// World rotation
|
||||
node.worldRotation.transform(velocity);
|
||||
|
||||
// Apply speed
|
||||
velocity.scl(speedHeap[0]);
|
||||
|
||||
// Apply the parent's scale
|
||||
velocity.scl(scale);
|
||||
|
||||
scene.addInstance(internalInstance);
|
||||
|
||||
internalInstance.setTransformation(node.worldLocation, rotationHeap.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z,
|
||||
RenderMathUtils.randomInRange(0, (float) Math.PI * 2)), node.worldScale);
|
||||
internalInstance.setSequence(0);
|
||||
internalInstance.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
final MdxComplexInstance internalInstance = this.internalInstance;
|
||||
|
||||
internalInstance.paused = false; /// Why is this here?
|
||||
|
||||
this.health -= dt;
|
||||
|
||||
if (this.health > 0) {
|
||||
final Vector3 velocity = this.velocity;
|
||||
|
||||
velocity.z -= this.gravity * dt;
|
||||
|
||||
tempVector[0] = velocity.x * dt;
|
||||
tempVector[1] = velocity.y * dt;
|
||||
tempVector[2] = velocity.z * dt;
|
||||
internalInstance.move(tempVector);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ public class Particle2 extends EmittedObject<MdxComplexInstance, ParticleEmitter
|
||||
public final Vector3 location = new Vector3();
|
||||
public final Vector3 velocity = new Vector3();
|
||||
public final Vector3 scale = new Vector3();
|
||||
private final ParticleEmitter2 emitter2;
|
||||
|
||||
private static final Quaternion rotationHeap = new Quaternion();
|
||||
private static final Quaternion rotationHeap2 = new Quaternion();
|
||||
@ -24,7 +23,7 @@ public class Particle2 extends EmittedObject<MdxComplexInstance, ParticleEmitter
|
||||
private static final float[] gravityHeap = new float[1];
|
||||
|
||||
public Particle2(final ParticleEmitter2 emitter) {
|
||||
this.emitter2 = emitter;
|
||||
super(emitter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -32,12 +31,12 @@ public class Particle2 extends EmittedObject<MdxComplexInstance, ParticleEmitter
|
||||
final MdxComplexInstance instance = this.emitter.instance;
|
||||
final ParticleEmitter2Object emitterObject = this.emitter.emitterObject;
|
||||
|
||||
emitterObject.getWidth(widthHeap, instance);
|
||||
emitterObject.getLength(lengthHeap, instance);
|
||||
emitterObject.getLatitude(latitudeHeap, instance);
|
||||
emitterObject.getVariation(variationHeap, instance);
|
||||
emitterObject.getSpeed(speedHeap, instance);
|
||||
emitterObject.getGravity(gravityHeap, instance);
|
||||
emitterObject.getWidth(widthHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getLength(lengthHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getLatitude(latitudeHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getVariation(variationHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getSpeed(speedHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getGravity(gravityHeap, instance.sequence, instance.frame, instance.counter);
|
||||
|
||||
final MdxNode node = this.emitter.node;
|
||||
final Vector3 pivot = node.pivot;
|
||||
|
@ -0,0 +1,34 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
public class ParticleEmitter extends MdxEmitter<MdxComplexInstance, ParticleEmitterObject, Particle> {
|
||||
|
||||
private static final float[] emissionRateHeap = new float[1];
|
||||
|
||||
public ParticleEmitter(final MdxComplexInstance instance, final ParticleEmitterObject emitterObject) {
|
||||
super(instance, emitterObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateEmission(final float dt) {
|
||||
final MdxComplexInstance instance = this.instance;
|
||||
|
||||
if (instance.allowParticleSpawn) {
|
||||
final ParticleEmitterObject emitterObject = this.emitterObject;
|
||||
|
||||
emitterObject.getEmissionRate(emissionRateHeap, instance.sequence, instance.frame, instance.counter);
|
||||
|
||||
this.currentEmission += emissionRateHeap[0] * dt;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void emit() {
|
||||
this.emitObject(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Particle createObject() {
|
||||
return new Particle(this);
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,8 @@ public class ParticleEmitter2 extends MdxEmitter<MdxComplexInstance, ParticleEmi
|
||||
|
||||
if (instance.allowParticleSpawn) {
|
||||
final ParticleEmitter2Object emitterObject = this.emitterObject;
|
||||
final int keyframe = emitterObject.getEmissionRate(emissionRateHeap, instance);
|
||||
final int keyframe = emitterObject.getEmissionRate(emissionRateHeap, instance.sequence, instance.frame,
|
||||
instance.counter);
|
||||
|
||||
if (emitterObject.squirt != 0) {
|
||||
if (keyframe != this.lastEmissionKey) {
|
||||
|
@ -105,37 +105,37 @@ public class ParticleEmitter2Object extends GenericObject implements EmitterObje
|
||||
this.priorityPlane = emitter.getPriorityPlane();
|
||||
}
|
||||
|
||||
public int getWidth(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2N.getWar3id(), instance, this.width);
|
||||
public int getWidth(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2N.getWar3id(), sequence, frame, counter, this.width);
|
||||
}
|
||||
|
||||
public int getLength(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2W.getWar3id(), instance, this.length);
|
||||
public int getLength(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2W.getWar3id(), sequence, frame, counter, this.length);
|
||||
}
|
||||
|
||||
public int getSpeed(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2S.getWar3id(), instance, this.speed);
|
||||
public int getSpeed(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2S.getWar3id(), sequence, frame, counter, this.speed);
|
||||
}
|
||||
|
||||
public int getLatitude(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2L.getWar3id(), instance, this.latitude);
|
||||
public int getLatitude(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2L.getWar3id(), sequence, frame, counter, this.latitude);
|
||||
}
|
||||
|
||||
public int getGravity(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2G.getWar3id(), instance, this.gravity);
|
||||
public int getGravity(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2G.getWar3id(), sequence, frame, counter, this.gravity);
|
||||
}
|
||||
|
||||
public int getEmissionRate(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2E.getWar3id(), instance, this.emissionRate);
|
||||
public int getEmissionRate(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2E.getWar3id(), sequence, frame, counter, this.emissionRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2V.getWar3id(), instance, 1);
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2V.getWar3id(), sequence, frame, counter, 1);
|
||||
}
|
||||
|
||||
public int getVariation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2R.getWar3id(), instance, this.variation);
|
||||
public int getVariation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KP2R.getWar3id(), sequence, frame, counter, this.variation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,83 @@
|
||||
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;
|
||||
|
||||
public class ParticleEmitterObject extends GenericObject implements EmitterObject {
|
||||
public MdxModel internalModel;
|
||||
public float speed;
|
||||
public float latitude;
|
||||
public float longitude;
|
||||
public float lifeSpan;
|
||||
public float gravity;
|
||||
public float emissionRate;
|
||||
|
||||
/**
|
||||
* No need to create instances of the internal model if it didn't load.
|
||||
*
|
||||
* Such instances won't actually render, and who knows if the model will ever
|
||||
* load?
|
||||
*/
|
||||
public boolean ok = false;
|
||||
|
||||
public ParticleEmitterObject(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.ParticleEmitter emitter,
|
||||
final int index) {
|
||||
super(model, emitter, index);
|
||||
|
||||
this.internalModel = (MdxModel) model.viewer.load(
|
||||
emitter.getPath().replace("\\", "/").toLowerCase(Locale.US).replace(".mdl", ".mdx"), model.pathSolver,
|
||||
model.solverParams);
|
||||
this.speed = emitter.getSpeed();
|
||||
this.latitude = emitter.getLatitude();
|
||||
this.longitude = emitter.getLongitude();
|
||||
this.lifeSpan = emitter.getLifeSpan();
|
||||
this.gravity = emitter.getGravity();
|
||||
this.emissionRate = emitter.getEmissionRate();
|
||||
|
||||
// Activate emitters based on this emitter object only when and if the internal
|
||||
// model loads successfully.
|
||||
// TODO async removed here
|
||||
this.ok = this.internalModel.ok;
|
||||
}
|
||||
|
||||
public int getSpeed(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPES.getWar3id(), sequence, frame, counter, this.speed);
|
||||
}
|
||||
|
||||
public int getLatitude(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPLT.getWar3id(), sequence, frame, counter, this.latitude);
|
||||
}
|
||||
|
||||
public int getLongitude(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPLN.getWar3id(), sequence, frame, counter, this.longitude);
|
||||
}
|
||||
|
||||
public int getLifeSpan(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPEL.getWar3id(), sequence, frame, counter, this.lifeSpan);
|
||||
}
|
||||
|
||||
public int getGravity(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPEG.getWar3id(), sequence, frame, counter, this.gravity);
|
||||
}
|
||||
|
||||
public int getEmissionRate(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPEE.getWar3id(), sequence, frame, counter, this.emissionRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KPEV.getWar3id(), sequence, frame, counter, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGeometryEmitterType() {
|
||||
throw new UnsupportedOperationException("ghostwolf doesnt have this in the JS");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ok() {
|
||||
return this.ok;
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@ public class Ribbon extends EmittedObject<MdxComplexInstance, RibbonEmitter> {
|
||||
public Ribbon prev;
|
||||
public Ribbon next;
|
||||
|
||||
public Ribbon(final RibbonEmitter emitter) {
|
||||
super(emitter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(final int flags) {
|
||||
final RibbonEmitter emitter = this.emitter;
|
||||
@ -31,9 +35,9 @@ public class Ribbon extends EmittedObject<MdxComplexInstance, RibbonEmitter> {
|
||||
|
||||
this.health = emitter.emitterObject.lifeSpan;
|
||||
|
||||
emitterObject.getHeightBelow(vectorHeap, instance);
|
||||
emitterObject.getHeightBelow(vectorHeap, instance.sequence, instance.frame, instance.counter);
|
||||
belowHeap.set(vectorHeap);
|
||||
emitterObject.getHeightAbove(vectorHeap, instance);
|
||||
emitterObject.getHeightAbove(vectorHeap, instance.sequence, instance.frame, instance.counter);
|
||||
aboveHeap.set(vectorHeap);
|
||||
|
||||
belowHeap.y = y - belowHeap.x;
|
||||
@ -55,9 +59,6 @@ public class Ribbon extends EmittedObject<MdxComplexInstance, RibbonEmitter> {
|
||||
vertices[5] = belowHeap.z;
|
||||
}
|
||||
|
||||
public Ribbon(final RibbonEmitter emitter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt) {
|
||||
this.health -= dt;
|
||||
@ -70,9 +71,9 @@ public class Ribbon extends EmittedObject<MdxComplexInstance, RibbonEmitter> {
|
||||
final float[] vertices = this.vertices;
|
||||
final float gravity = emitterObject.gravity * dt * dt;
|
||||
|
||||
emitterObject.getColor(colorHeap, instance);
|
||||
emitterObject.getAlpha(alphaHeap, instance);
|
||||
emitterObject.getTextureSlot(slotHeap, instance);
|
||||
emitterObject.getColor(colorHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getAlpha(alphaHeap, instance.sequence, instance.frame, instance.counter);
|
||||
emitterObject.getTextureSlot(slotHeap, instance.sequence, instance.frame, instance.counter);
|
||||
|
||||
vertices[1] -= gravity;
|
||||
vertices[4] -= gravity;
|
||||
|
@ -40,29 +40,29 @@ public class RibbonEmitterObject extends GenericObject implements EmitterObject
|
||||
this.rows = emitter.getRows();
|
||||
}
|
||||
|
||||
public int getHeightBelow(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KRHB.getWar3id(), instance, this.heightBelow);
|
||||
public int getHeightBelow(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KRHB.getWar3id(), sequence, frame, counter, this.heightBelow);
|
||||
}
|
||||
|
||||
public int getHeightAbove(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KRHA.getWar3id(), instance, this.heightAbove);
|
||||
public int getHeightAbove(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KRHA.getWar3id(), sequence, frame, counter, this.heightAbove);
|
||||
}
|
||||
|
||||
public int getTextureSlot(final long[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KRTX.getWar3id(), instance, 0);
|
||||
public int getTextureSlot(final long[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KRTX.getWar3id(), sequence, frame, counter, 0);
|
||||
}
|
||||
|
||||
public int getColor(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KRCO.getWar3id(), instance, this.color);
|
||||
public int getColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KRCO.getWar3id(), sequence, frame, counter, this.color);
|
||||
}
|
||||
|
||||
public int getAlpha(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KRAL.getWar3id(), instance, this.alpha);
|
||||
public int getAlpha(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KRAL.getWar3id(), sequence, frame, counter, this.alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getScalarValue(out, AnimationMap.KRVS.getWar3id(), instance, 1f);
|
||||
public int getVisibility(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KRVS.getWar3id(), sequence, frame, counter, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,12 +116,12 @@ public abstract class Sd<TYPE> {
|
||||
}
|
||||
}
|
||||
|
||||
public int getValue(final TYPE out, final MdxComplexInstance instance) {
|
||||
public int getValue(final TYPE out, final int sequence, final int frame, final int counter) {
|
||||
if (this.globalSequence != null) {
|
||||
return this.globalSequence.getValue(out, instance.counter % this.globalSequence.end);
|
||||
return this.globalSequence.getValue(out, counter % this.globalSequence.end);
|
||||
}
|
||||
else if (instance.sequence != -1) {
|
||||
return this.sequences.get(instance.sequence).getValue(out, instance.frame);
|
||||
else if (sequence != -1) {
|
||||
return this.sequences.get(sequence).getValue(out, frame);
|
||||
}
|
||||
else {
|
||||
this.copy(out, this.defval);
|
||||
|
@ -0,0 +1,186 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class SetupGeosets {
|
||||
private static final int NORMAL_BATCH = 0;
|
||||
private static final int EXTENDED_BATCH = 0;
|
||||
private static final int REFORGED_BATCH = 0;
|
||||
|
||||
public static void setupGeosets(final MdxModel model,
|
||||
final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets) {
|
||||
if (geosets.size() > 0) {
|
||||
final GL20 gl = model.viewer.gl;
|
||||
int positionBytes = 0;
|
||||
int normalBytes = 0;
|
||||
int uvBytes = 0;
|
||||
int skinBytes = 0;
|
||||
int faceBytes = 0;
|
||||
final int[] batchTypes = new int[geosets.size()];
|
||||
|
||||
for (int i = 0, l = geosets.size(); i < l; i++) {
|
||||
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
|
||||
|
||||
if (true /* geoset.getLod() == 0 */) {
|
||||
final int vertices = geoset.getVertices().length / 3;
|
||||
|
||||
positionBytes += vertices * 12;
|
||||
normalBytes += vertices * 12;
|
||||
uvBytes += geoset.getUvSets().length * vertices * 8;
|
||||
|
||||
if (false /* geoset.skin.length */) {
|
||||
skinBytes += vertices * 8;
|
||||
|
||||
batchTypes[i] = REFORGED_BATCH;
|
||||
}
|
||||
else {
|
||||
long biggestGroup = 0;
|
||||
|
||||
for (final long group : geoset.getMatrixGroups()) {
|
||||
if (group > biggestGroup) {
|
||||
biggestGroup = group;
|
||||
}
|
||||
}
|
||||
|
||||
if (biggestGroup > 4) {
|
||||
skinBytes += vertices * 9;
|
||||
|
||||
batchTypes[i] = EXTENDED_BATCH;
|
||||
}
|
||||
else {
|
||||
batchTypes[i] = NORMAL_BATCH;
|
||||
}
|
||||
}
|
||||
|
||||
faceBytes += geoset.getFaces().length * 4;
|
||||
}
|
||||
}
|
||||
|
||||
int positionOffset = 0;
|
||||
int normalOffset = positionOffset + positionBytes;
|
||||
int uvOffset = normalOffset + normalBytes;
|
||||
int skinOffset = uvOffset + uvBytes;
|
||||
int faceOffset = 0;
|
||||
|
||||
model.arrayBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, model.arrayBuffer);
|
||||
gl.glBufferData(GL20.GL_ARRAY_BUFFER, skinOffset + skinBytes, null, GL20.GL_STATIC_DRAW);
|
||||
|
||||
model.elementBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, model.elementBuffer);
|
||||
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);
|
||||
|
||||
if (true /* geoset.lod == 0 */) {
|
||||
final float[] positions = geoset.getVertices();
|
||||
final float[] normals = geoset.getNormals();
|
||||
final float[][] uvSets = geoset.getUvSets();
|
||||
final int[] faces = geoset.getFaces();
|
||||
byte[] skin = null;
|
||||
final int vertices = geoset.getVertices().length / 3;
|
||||
final int batchType = batchTypes[i];
|
||||
|
||||
if (batchType == REFORGED_BATCH) {
|
||||
// skin = geoset.skin;
|
||||
}
|
||||
else {
|
||||
final long[] matrixIndices = geoset.getMatrixIndices();
|
||||
final short[] vertexGroups = geoset.getVertexGroups();
|
||||
final List<long[]> matrixGroups = new ArrayList<>();
|
||||
int offset = 0;
|
||||
// Normally the shader supports up to 4 bones per vertex.
|
||||
// This is enough for almost every existing Warcraft 3 model.
|
||||
// That being said, there are a few models with geosets that need more, for
|
||||
// example the Water Elemental.
|
||||
// These geosets use a different shader, which support up to 8 bones per vertex.
|
||||
int maxBones = 4;
|
||||
if (batchType == EXTENDED_BATCH) {
|
||||
maxBones = 8;
|
||||
}
|
||||
|
||||
skin = new byte[vertices * (maxBones + 1)];
|
||||
|
||||
// Slice the matrix groups
|
||||
for (final long size : geoset.getMatrixGroups()) {
|
||||
matrixGroups.add(Arrays.copyOfRange(matrixIndices, offset, (int) (offset + size)));
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// Parse the skinning.
|
||||
for (int si = 0; si < vertices; si++) {
|
||||
final short vertexGroup = vertexGroups[si];
|
||||
final long[] matrixGroup = (vertexGroup >= matrixGroups.size()) ? null
|
||||
: matrixGroups.get(vertexGroup);
|
||||
|
||||
offset = si * (maxBones + 1);
|
||||
|
||||
// Somehow in some bad models a vertex group index refers to an invalid matrix
|
||||
// group.
|
||||
// Such models are still loaded by the game.
|
||||
if (matrixGroup != null) {
|
||||
final int bones = Math.min(matrixGroup.length, maxBones);
|
||||
|
||||
for (int j = 0; j < bones; j++) {
|
||||
skin[offset + j] = (byte) (matrixGroup[j] + 1); // 1 is added to diffrentiate
|
||||
// between matrix 0, and no matrix.
|
||||
}
|
||||
|
||||
skin[offset + maxBones] = (byte) bones;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Geoset vGeoset = new Geoset(model, model.getGeosets().size(), positionOffset, normalOffset,
|
||||
uvOffset, skinOffset, faceOffset, vertices, faces.length);
|
||||
|
||||
model.getGeosets().add(vGeoset);
|
||||
|
||||
if (batchType == REFORGED_BATCH) {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
// model.batches.add(new Reforged)
|
||||
}
|
||||
else {
|
||||
final boolean isExtended = batchType == EXTENDED_BATCH;
|
||||
|
||||
for (final Layer layer : model.getMaterials().get((int) geoset.getMaterialId()).layers) {
|
||||
model.batches.add(new Batch(model.batches.size(), vGeoset, layer, isExtended));
|
||||
}
|
||||
}
|
||||
|
||||
// Positions.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, positionOffset, positions.length,
|
||||
FloatBuffer.wrap(positions));
|
||||
positionOffset += positions.length * 4;
|
||||
|
||||
// Normals.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, normalOffset, normals.length, FloatBuffer.wrap(normals));
|
||||
normalOffset += normals.length * 4;
|
||||
|
||||
// Texture coordinates.
|
||||
for (final float[] uvSet : uvSets) {
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, uvOffset, uvSet.length, FloatBuffer.wrap(uvSet));
|
||||
uvOffset += uvSet.length * 4;
|
||||
}
|
||||
|
||||
// Skin.
|
||||
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, ByteBuffer.wrap(skin));
|
||||
skinOffset += skin.length * 1;
|
||||
|
||||
// Faces.
|
||||
gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length, IntBuffer.wrap(faces));
|
||||
faceOffset += faces.length * 4;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
|
||||
|
||||
public class SetupGroups {
|
||||
public static int getPrio(final Batch object) {
|
||||
return object.layer.priorityPlane;
|
||||
}
|
||||
|
||||
public static int getPrio(final ParticleEmitter2Object object) {
|
||||
return object.priorityPlane;
|
||||
}
|
||||
|
||||
public static int getPrio(final RibbonEmitterObject object) {
|
||||
return object.layer.priorityPlane;
|
||||
}
|
||||
|
||||
public static int getPrio(final Object object) {
|
||||
if (object instanceof Batch) {
|
||||
return getPrio((Batch) object);
|
||||
}
|
||||
else if (object instanceof RibbonEmitterObject) {
|
||||
return getPrio((RibbonEmitterObject) object);
|
||||
}
|
||||
else if (object instanceof ParticleEmitter2Object) {
|
||||
return getPrio((ParticleEmitter2Object) object);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(object.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matchingGroup(final Object group, final Object object) {
|
||||
if (group instanceof BatchGroup) {
|
||||
return (object instanceof Batch) && (((Batch) object).isExtended == ((BatchGroup) group).isExtended);
|
||||
// } else if(group instanceof ReforgedBatch) { TODO
|
||||
// return (object instanceof ReforgedBatch) && (object.material.shader === group.shader);
|
||||
}
|
||||
else {
|
||||
// All of the emitter objects are generic objects.
|
||||
return (object instanceof GenericObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static GenericGroup createMatchingGroup(final MdxModel model, final Object object) {
|
||||
if (object instanceof Batch) {
|
||||
return new BatchGroup(model, ((Batch) object).isExtended);
|
||||
// } else if(object instanceof ReforgedBatch) { TODO
|
||||
// return new ReforgedBatchGroup(model, ((ReforgedBatch)object).material.shader);
|
||||
}
|
||||
else {
|
||||
return new EmitterGroup(model);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupGroups(final MdxModel model) {
|
||||
final List<Batch> opaqueBatches = new ArrayList<>();
|
||||
final List<Batch> translucentBatches = new ArrayList<>();
|
||||
|
||||
for (final Batch batch : model.batches) {// TODO reforged
|
||||
if (/* batch instanceof ReforgedBatch || */batch.layer.filterMode < 2) {
|
||||
opaqueBatches.add(batch);
|
||||
}
|
||||
else {
|
||||
translucentBatches.add(batch);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Object> opaqueGroups = model.opaqueGroups;
|
||||
final List<Object> translucentGroups = model.translucentGroups;
|
||||
GenericGroup currentGroup = null;
|
||||
|
||||
for (final Batch object : opaqueBatches) {
|
||||
if ((currentGroup == null) || !matchingGroup(currentGroup, object)) {
|
||||
currentGroup = createMatchingGroup(model, object);
|
||||
|
||||
opaqueGroups.add(currentGroup);
|
||||
}
|
||||
|
||||
currentGroup.objects.add(object.index);
|
||||
}
|
||||
|
||||
// Sort between all of the translucent batches and emitters that have priority
|
||||
// planes
|
||||
final List<Object> sorted = new ArrayList<>();
|
||||
sorted.addAll(translucentBatches);
|
||||
sorted.addAll(model.particleEmitters2);
|
||||
sorted.addAll(model.ribbonEmitters);
|
||||
Collections.sort(sorted, new Comparator<Object>() {
|
||||
@Override
|
||||
public int compare(final Object o1, final Object o2) {
|
||||
return getPrio(o1) - getPrio(o2);
|
||||
}
|
||||
});
|
||||
|
||||
// Event objects have no priority planes, so they might as well always be last.
|
||||
final List<Object> objects = new ArrayList<>();
|
||||
objects.addAll(sorted);
|
||||
objects.addAll(model.eventObjects);
|
||||
|
||||
currentGroup = null;
|
||||
|
||||
for (final Object object : objects) { // TODO reforged
|
||||
if ((object instanceof Batch /* || object instanceof ReforgedBatch */)
|
||||
|| (object instanceof EmitterObject)) {
|
||||
if ((currentGroup == null) || !matchingGroup(currentGroup, objects)) {
|
||||
currentGroup = createMatchingGroup(model, objects);
|
||||
|
||||
translucentGroups.add(currentGroup);
|
||||
}
|
||||
|
||||
currentGroup.objects.add(((GenericIndexed) object).getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,16 +10,19 @@ public class TextureAnimation extends AnimatedObject {
|
||||
super(model, textureAnimation);
|
||||
}
|
||||
|
||||
public int getTranslation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAT.getWar3id(), instance, RenderMathUtils.FLOAT_VEC3_ZERO);
|
||||
public int getTranslation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAT.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_VEC3_ZERO);
|
||||
}
|
||||
|
||||
public int getRotation(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAR.getWar3id(), instance, RenderMathUtils.FLOAT_QUAT_DEFAULT);
|
||||
public int getRotation(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAR.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_QUAT_DEFAULT);
|
||||
}
|
||||
|
||||
public int getScale(final float[] out, final MdxComplexInstance instance) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAS.getWar3id(), instance, RenderMathUtils.FLOAT_VEC3_ONE);
|
||||
public int getScale(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KTAS.getWar3id(), sequence, frame, counter,
|
||||
RenderMathUtils.FLOAT_VEC3_ONE);
|
||||
}
|
||||
|
||||
public boolean isTranslationVariant(final int sequence) {
|
||||
|
@ -1,12 +0,0 @@
|
||||
package com.hiveworkshop.wc3.mpq;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface Codebase {
|
||||
InputStream getResourceAsStream(String filepath);
|
||||
|
||||
File getFile(String filepath);
|
||||
|
||||
boolean has(String filepath);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package com.hiveworkshop.wc3.mpq;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileCodebase implements Codebase {
|
||||
private final File sourceDirectory;
|
||||
|
||||
public FileCodebase(final File sourceDirectory) {
|
||||
this.sourceDirectory = sourceDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String filepath) {
|
||||
try {
|
||||
return new FileInputStream(new File(this.sourceDirectory.getPath() + File.separatorChar + filepath));
|
||||
}
|
||||
catch (final FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile(final String filepath) {
|
||||
return new File(this.sourceDirectory.getPath() + File.separatorChar + filepath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final String filepath) {
|
||||
return new File(this.sourceDirectory.getPath() + File.separatorChar + filepath).exists();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user