Weiter am Editor gearbeitet, unter anderem LOD System, Items, Trees, Modelle
This commit is contained in:
254
blight-common/src/main/java/de/blight/common/ChunkTerrainIO.java
Normal file
254
blight-common/src/main/java/de/blight/common/ChunkTerrainIO.java
Normal file
@@ -0,0 +1,254 @@
|
||||
package de.blight.common;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
/**
|
||||
* Konstanten, Datei-I/O und Hilfsmethoden für das chunk-basierte Terrain-System.
|
||||
*
|
||||
* Die Welt (4096 × 4096 m) wird in CHUNKS_PER_AXIS² = 32² = 1024 quadratische
|
||||
* Chunks à CHUNK_SIZE = 128 m unterteilt. Jeder Chunk speichert CHUNK_VERTS² = 129²
|
||||
* Höhenwerte bei 1 m Auflösung (native Editor-Auflösung).
|
||||
*
|
||||
* LOD-Stufen (3):
|
||||
* LOD 0 – 1 m/Vertex (129×129) – nah, Chebyshev-Dist ≤ LOD0_RANGE
|
||||
* LOD 1 – 4 m/Vertex (33×33) – mittel, Chebyshev-Dist ≤ LOD1_RANGE
|
||||
* LOD 2 – 16 m/Vertex (9×9) – fern, Rest
|
||||
*
|
||||
* Kanten-IDs (EDGE_*): werden für Seam-Stitching verwendet.
|
||||
*/
|
||||
public final class ChunkTerrainIO {
|
||||
|
||||
// ── Welt / Chunk-Konfiguration ─────────────────────────────────────────────
|
||||
public static final int CHUNK_SIZE = 128;
|
||||
public static final int WORLD_SIZE = 4096;
|
||||
public static final int CHUNKS_PER_AXIS = WORLD_SIZE / CHUNK_SIZE; // 32
|
||||
public static final int CHUNK_COUNT = CHUNKS_PER_AXIS * CHUNKS_PER_AXIS; // 1024
|
||||
/** Vertices pro Kante bei nativer 1-m-Auflösung (inklusiv). */
|
||||
public static final int CHUNK_VERTS = CHUNK_SIZE + 1; // 129
|
||||
|
||||
// ── LOD-Konfiguration ──────────────────────────────────────────────────────
|
||||
public static final int LOD_COUNT = 3;
|
||||
/** Meter pro Vertex je LOD-Stufe. */
|
||||
public static final float[] LOD_SPACING = { 1f, 4f, 16f };
|
||||
/** Vertices pro Kante je LOD-Stufe. */
|
||||
public static final int[] LOD_VERTS = { 129, 33, 9 };
|
||||
/** Chebyshev-Distanz (in Chunks) bis inkl. dieser Stufe aktiv ist. */
|
||||
public static final int LOD0_RANGE = 1;
|
||||
public static final int LOD1_RANGE = 3;
|
||||
/** Chebyshev-Distanz ≤ PHYSICS_RANGE: Physik-Collider aktiv. */
|
||||
public static final int PHYSICS_RANGE = 1;
|
||||
|
||||
// ── Kanten-Konstanten ──────────────────────────────────────────────────────
|
||||
public static final int EDGE_NORTH = 0; // höchste Zeile (row = VERTS-1)
|
||||
public static final int EDGE_SOUTH = 1; // unterste Zeile (row = 0)
|
||||
public static final int EDGE_EAST = 2; // rechteste Spalte (col = VERTS-1)
|
||||
public static final int EDGE_WEST = 3; // linkeste Spalte (col = 0)
|
||||
|
||||
private static final int MAGIC = 0x424C4354; // "BLCT"
|
||||
private static final int VERSION = 1;
|
||||
|
||||
private ChunkTerrainIO() {}
|
||||
|
||||
// ── Dateipfade ─────────────────────────────────────────────────────────────
|
||||
|
||||
public static Path chunksDir() {
|
||||
return MapIO.getMapPath().resolveSibling("chunks");
|
||||
}
|
||||
|
||||
public static Path getChunkPath(int cx, int cz) {
|
||||
return chunksDir().resolve(String.format("chunk_%02d_%02d.blc", cx, cz));
|
||||
}
|
||||
|
||||
public static boolean chunkExists(int cx, int cz) {
|
||||
return Files.exists(getChunkPath(cx, cz));
|
||||
}
|
||||
|
||||
public static boolean allChunksExist() {
|
||||
for (int cz = 0; cz < CHUNKS_PER_AXIS; cz++)
|
||||
for (int cx = 0; cx < CHUNKS_PER_AXIS; cx++)
|
||||
if (!chunkExists(cx, cz)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── I/O ───────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void saveChunk(int cx, int cz, float[] heights) throws IOException {
|
||||
if (heights.length != CHUNK_VERTS * CHUNK_VERTS)
|
||||
throw new IllegalArgumentException("heights.length muss " + (CHUNK_VERTS * CHUNK_VERTS) + " sein");
|
||||
Path p = getChunkPath(cx, cz);
|
||||
Path tmp = p.resolveSibling(p.getFileName() + ".tmp");
|
||||
Files.createDirectories(p.getParent());
|
||||
try (DataOutputStream out = new DataOutputStream(
|
||||
new BufferedOutputStream(new GZIPOutputStream(Files.newOutputStream(tmp))))) {
|
||||
out.writeInt(MAGIC);
|
||||
out.writeInt(VERSION);
|
||||
out.writeInt(cx);
|
||||
out.writeInt(cz);
|
||||
for (float h : heights) out.writeFloat(h);
|
||||
}
|
||||
try {
|
||||
Files.move(tmp, p, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.move(tmp, p, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] loadChunk(int cx, int cz) throws IOException {
|
||||
try (DataInputStream in = new DataInputStream(
|
||||
new BufferedInputStream(new GZIPInputStream(
|
||||
Files.newInputStream(getChunkPath(cx, cz)))))) {
|
||||
if (in.readInt() != MAGIC) throw new IOException("Ungültiges Chunk-Format");
|
||||
int ver = in.readInt();
|
||||
if (ver != VERSION) throw new IOException("Unbekannte Chunk-Version: " + ver);
|
||||
in.readInt(); // cx (ignoriert, Dateiname ist maßgeblich)
|
||||
in.readInt(); // cz
|
||||
float[] h = new float[CHUNK_VERTS * CHUNK_VERTS];
|
||||
for (int i = 0; i < h.length; i++) h[i] = in.readFloat();
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Export ────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Teilt die Editor-Heightmap (4097 × 4097, 1 m/Vertex) in 1024 Chunk-Dateien auf.
|
||||
* Direkte Kopie ohne Interpolation – jeder Chunk enthält exakt die Editor-Vertices
|
||||
* seines Bereichs.
|
||||
*/
|
||||
public static void exportFromEditorHeightMap(float[] editorH, int editorVerts) throws IOException {
|
||||
for (int cz = 0; cz < CHUNKS_PER_AXIS; cz++) {
|
||||
for (int cx = 0; cx < CHUNKS_PER_AXIS; cx++) {
|
||||
float[] chunk = new float[CHUNK_VERTS * CHUNK_VERTS];
|
||||
for (int row = 0; row < CHUNK_VERTS; row++) {
|
||||
int srcRow = cz * CHUNK_SIZE + row;
|
||||
for (int col = 0; col < CHUNK_VERTS; col++) {
|
||||
chunk[row * CHUNK_VERTS + col] = editorH[srcRow * editorVerts + cx * CHUNK_SIZE + col];
|
||||
}
|
||||
}
|
||||
saveChunk(cx, cz, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migriert eine alte MapData (16385 × 16385 Höhenwerte, 0,25 m/Vertex)
|
||||
* in das Chunk-Format. Jeder Chunk enthält 129 × 129 Werte (jeder 4. Quell-Vertex).
|
||||
*/
|
||||
public static void exportFromMapData(MapData data) throws IOException {
|
||||
int srcVerts = MapData.TERRAIN_VERTS; // 16385
|
||||
int srcPerChunk = (srcVerts - 1) / CHUNKS_PER_AXIS; // 512
|
||||
int step = srcPerChunk / (CHUNK_VERTS - 1); // 4
|
||||
for (int cz = 0; cz < CHUNKS_PER_AXIS; cz++) {
|
||||
for (int cx = 0; cx < CHUNKS_PER_AXIS; cx++) {
|
||||
float[] chunk = new float[CHUNK_VERTS * CHUNK_VERTS];
|
||||
for (int row = 0; row < CHUNK_VERTS; row++) {
|
||||
int srcRow = Math.min(cz * srcPerChunk + row * step, srcVerts - 1);
|
||||
for (int col = 0; col < CHUNK_VERTS; col++) {
|
||||
int srcCol = Math.min(cx * srcPerChunk + col * step, srcVerts - 1);
|
||||
chunk[row * CHUNK_VERTS + col] = data.terrainHeight[srcRow * srcVerts + srcCol];
|
||||
}
|
||||
}
|
||||
saveChunk(cx, cz, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Erzeugt leere (flache) Chunk-Dateien mit Höhe 1.0 für alle 1024 Chunks. */
|
||||
public static void exportBlankChunks() throws IOException {
|
||||
float[] flat = new float[CHUNK_VERTS * CHUNK_VERTS];
|
||||
java.util.Arrays.fill(flat, 1f);
|
||||
for (int cz = 0; cz < CHUNKS_PER_AXIS; cz++)
|
||||
for (int cx = 0; cx < CHUNKS_PER_AXIS; cx++)
|
||||
saveChunk(cx, cz, flat);
|
||||
}
|
||||
|
||||
// ── Downsample ────────────────────────────────────────────────────────────
|
||||
|
||||
/** Bilineare Verkleinerung eines quadratischen srcVerts²-Arrays auf dstVerts². */
|
||||
public static float[] downsample(float[] src, int srcVerts, int dstVerts) {
|
||||
if (srcVerts == dstVerts) return src.clone();
|
||||
float[] dst = new float[dstVerts * dstVerts];
|
||||
float step = (float) (srcVerts - 1) / (dstVerts - 1);
|
||||
for (int dz = 0; dz < dstVerts; dz++) {
|
||||
float sz = dz * step;
|
||||
int sz0 = Math.min((int) sz, srcVerts - 2), sz1 = sz0 + 1;
|
||||
float fz = sz - sz0;
|
||||
for (int dx = 0; dx < dstVerts; dx++) {
|
||||
float sx = dx * step;
|
||||
int sx0 = Math.min((int) sx, srcVerts - 2), sx1 = sx0 + 1;
|
||||
float fx = sx - sx0;
|
||||
dst[dz * dstVerts + dx] =
|
||||
(src[sz0 * srcVerts + sx0] * (1 - fx) + src[sz0 * srcVerts + sx1] * fx) * (1 - fz)
|
||||
+ (src[sz1 * srcVerts + sx0] * (1 - fx) + src[sz1 * srcVerts + sx1] * fx) * fz;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// ── Seam-Stitching ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Extrahiert den Rand eines quadratischen height-Arrays (verts × verts).
|
||||
* Werte in Leserichtung: West→Ost für NORTH/SOUTH, Nord→Süd für EAST/WEST.
|
||||
*/
|
||||
public static float[] extractEdge(float[] heights, int verts, int edge) {
|
||||
float[] e = new float[verts];
|
||||
switch (edge) {
|
||||
case EDGE_NORTH -> { for (int i = 0; i < verts; i++) e[i] = heights[(verts - 1) * verts + i]; }
|
||||
case EDGE_SOUTH -> { System.arraycopy(heights, 0, e, 0, verts); }
|
||||
case EDGE_EAST -> { for (int i = 0; i < verts; i++) e[i] = heights[i * verts + (verts - 1)]; }
|
||||
case EDGE_WEST -> { for (int i = 0; i < verts; i++) e[i] = heights[i * verts]; }
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passt die Randwerte in {@code heights} (fineVerts × fineVerts) an die gröbere
|
||||
* Nachbar-Kante an: nicht-ausgerichtete Zwischenvertices werden auf die lineare
|
||||
* Interpolation der Anker-Vertices gesetzt → eliminiert Höhenrisse an LOD-Grenzen.
|
||||
*
|
||||
* @param heights height-Array des feinen Chunks (in-place modifiziert)
|
||||
* @param fineVerts Vertices pro Kante (fein)
|
||||
* @param coarseEdge Randwerte des groben Nachbarn (Länge coarseVerts)
|
||||
* @param coarseVerts Länge von coarseEdge
|
||||
* @param edge welche Kante dieses Chunks (EDGE_*)
|
||||
*/
|
||||
public static void stitchEdge(float[] heights, int fineVerts,
|
||||
float[] coarseEdge, int coarseVerts, int edge) {
|
||||
int ratio = (fineVerts - 1) / (coarseVerts - 1);
|
||||
for (int i = 0; i < fineVerts; i++) {
|
||||
if (i % ratio == 0) continue;
|
||||
int k0 = i / ratio;
|
||||
float t = (float) (i % ratio) / ratio;
|
||||
float snapped = coarseEdge[k0] + (coarseEdge[k0 + 1] - coarseEdge[k0]) * t;
|
||||
int idx = switch (edge) {
|
||||
case EDGE_NORTH -> (fineVerts - 1) * fineVerts + i;
|
||||
case EDGE_SOUTH -> i;
|
||||
case EDGE_EAST -> i * fineVerts + (fineVerts - 1);
|
||||
default -> i * fineVerts; // EDGE_WEST
|
||||
};
|
||||
heights[idx] = snapped;
|
||||
}
|
||||
}
|
||||
|
||||
// ── LOD-Hilfsmethoden ─────────────────────────────────────────────────────
|
||||
|
||||
/** Gibt den LOD-Level für eine gegebene Chebyshev-Distanz zurück. */
|
||||
public static int lodForDistance(int chebyshev) {
|
||||
if (chebyshev <= LOD0_RANGE) return 0;
|
||||
if (chebyshev <= LOD1_RANGE) return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/** Chebyshev-Distanz (in Chunks) zwischen zwei Chunk-Koordinaten. */
|
||||
public static int chebyshev(int ax, int az, int bx, int bz) {
|
||||
return Math.max(Math.abs(ax - bx), Math.abs(az - bz));
|
||||
}
|
||||
|
||||
/** Flacher Chunk-Index aus Chunk-Koordinaten. */
|
||||
public static int chunkIndex(int cx, int cz) {
|
||||
return cz * CHUNKS_PER_AXIS + cx;
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,17 @@ public record ModelMeta(
|
||||
boolean castShadow,
|
||||
boolean receiveShadow,
|
||||
float randomScaleMin,
|
||||
float randomScaleMax
|
||||
float randomScaleMax,
|
||||
String lod1Path, // relativer Asset-Pfad; "" = nicht gesetzt
|
||||
String lod2Path,
|
||||
float lod1Distance, // ab dieser Distanz LOD1 anzeigen
|
||||
float lod2Distance, // ab dieser Distanz LOD2 anzeigen
|
||||
float cullDistance // ab dieser Distanz ausblenden
|
||||
) {
|
||||
public static ModelMeta defaults(String j3oFileName) {
|
||||
String name = j3oFileName.replaceFirst("\\.j3o$", "");
|
||||
return new ModelMeta(name, "", "", 1f, 1f, 1f, true, 0f, 0f,
|
||||
false, true, true, 1f, 1f);
|
||||
false, true, true, 1f, 1f,
|
||||
"", "", 30f, 80f, 120f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public final class ModelMetaIO {
|
||||
p.setProperty("receiveShadow", String.valueOf(m.receiveShadow()));
|
||||
p.setProperty("randomScaleMin", String.valueOf(m.randomScaleMin()));
|
||||
p.setProperty("randomScaleMax", String.valueOf(m.randomScaleMax()));
|
||||
p.setProperty("lod1Path", m.lod1Path());
|
||||
p.setProperty("lod2Path", m.lod2Path());
|
||||
p.setProperty("lod1Distance", String.valueOf(m.lod1Distance()));
|
||||
p.setProperty("lod2Distance", String.valueOf(m.lod2Distance()));
|
||||
p.setProperty("cullDistance", String.valueOf(m.cullDistance()));
|
||||
try (Writer w = Files.newBufferedWriter(metaPath(j3oPath))) {
|
||||
p.store(w, null);
|
||||
}
|
||||
@@ -57,7 +62,12 @@ public final class ModelMetaIO {
|
||||
Boolean.parseBoolean(p.getProperty("castShadow", "true")),
|
||||
Boolean.parseBoolean(p.getProperty("receiveShadow", "true")),
|
||||
parseFloat(p, "randomScaleMin", 1f),
|
||||
parseFloat(p, "randomScaleMax", 1f)
|
||||
parseFloat(p, "randomScaleMax", 1f),
|
||||
p.getProperty("lod1Path", ""),
|
||||
p.getProperty("lod2Path", ""),
|
||||
parseFloat(p, "lod1Distance", 30f),
|
||||
parseFloat(p, "lod2Distance", 80f),
|
||||
parseFloat(p, "cullDistance", 120f)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package de.blight.common;
|
||||
|
||||
/** Ein Item-Pickup das auf der Spielwelt liegt (Kartendaten). */
|
||||
public record PlacedItem(String itemId, float x, float y, float z) {}
|
||||
@@ -0,0 +1,51 @@
|
||||
package de.blight.common;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Liest und schreibt auf der Karte liegende Items (Pickups).
|
||||
* Datei: {@code blight_placed_items.bpi} neben {@code blight_map.blm}.
|
||||
* Format: {@code itemId\tx\ty\tz}
|
||||
*/
|
||||
public final class PlacedItemIO {
|
||||
|
||||
private PlacedItemIO() {}
|
||||
|
||||
public static Path getPath() {
|
||||
return MapIO.getMapPath().resolveSibling("blight_placed_items.bpi");
|
||||
}
|
||||
|
||||
public static void save(List<PlacedItem> items) throws IOException {
|
||||
Path p = getPath();
|
||||
Files.createDirectories(p.getParent());
|
||||
try (BufferedWriter w = Files.newBufferedWriter(p)) {
|
||||
w.write("# itemId\tx\ty\tz");
|
||||
w.newLine();
|
||||
for (PlacedItem it : items) {
|
||||
w.write(String.format(Locale.ROOT, "%s\t%.5f\t%.5f\t%.5f%n",
|
||||
it.itemId(), it.x(), it.y(), it.z()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<PlacedItem> load() throws IOException {
|
||||
Path p = getPath();
|
||||
if (!Files.exists(p)) return List.of();
|
||||
List<PlacedItem> list = new ArrayList<>();
|
||||
for (String line : Files.readAllLines(p)) {
|
||||
line = line.strip();
|
||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
||||
String[] f = line.split("\t", -1);
|
||||
if (f.length < 4) continue;
|
||||
try {
|
||||
list.add(new PlacedItem(f[0],
|
||||
Float.parseFloat(f[1]),
|
||||
Float.parseFloat(f[2]),
|
||||
Float.parseFloat(f[3])));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,10 @@ public record PlacedModel(
|
||||
/** AnimationLibrary-Clip-Key (z. B. "walk/Run"); "" = keine Animation. */
|
||||
String animClip,
|
||||
boolean castShadow,
|
||||
boolean receiveShadow
|
||||
boolean receiveShadow,
|
||||
String lod1Path, // relativer Asset-Pfad; "" = nicht gesetzt
|
||||
String lod2Path,
|
||||
float lod1Distance, // ab dieser Distanz LOD1 anzeigen
|
||||
float lod2Distance, // ab dieser Distanz LOD2 anzeigen
|
||||
float cullDistance // ab dieser Distanz ausblenden
|
||||
) {}
|
||||
|
||||
@@ -8,10 +8,11 @@ import java.util.*;
|
||||
* Liest und schreibt platzierte Modelle als tab-separierte Textdatei
|
||||
* ({@code blight_objects.blo}) neben der Kartendatei.
|
||||
*
|
||||
* Spalten (seit v3):
|
||||
* Spalten (seit v4):
|
||||
* modelPath x y z rotY scale rotX rotZ solid texPath nmPath matPath meshFile animClip castShadow receiveShadow
|
||||
* lod1Path lod2Path lod1Distance lod2Distance cullDistance
|
||||
*
|
||||
* Alte Dateien mit 6 Spalten (v1/v2) werden gelesen; fehlende Felder erhalten Standardwerte.
|
||||
* Alte Dateien mit 6 Spalten (v1/v2/v3) werden gelesen; fehlende Felder erhalten Standardwerte.
|
||||
*/
|
||||
public final class PlacedModelIO {
|
||||
|
||||
@@ -25,11 +26,11 @@ public final class PlacedModelIO {
|
||||
Path p = getPath();
|
||||
Files.createDirectories(p.getParent());
|
||||
try (BufferedWriter w = Files.newBufferedWriter(p)) {
|
||||
w.write("# modelPath\tx\ty\tz\trotY\tscale\trotX\trotZ\tsolid\ttexPath\tnmPath\tmatPath\tmeshFile\tanimClip\tcastShadow\treceiveShadow");
|
||||
w.write("# modelPath\tx\ty\tz\trotY\tscale\trotX\trotZ\tsolid\ttexPath\tnmPath\tmatPath\tmeshFile\tanimClip\tcastShadow\treceiveShadow\tlod1Path\tlod2Path\tlod1Distance\tlod2Distance\tcullDistance");
|
||||
w.newLine();
|
||||
for (PlacedModel m : models) {
|
||||
w.write(String.format(Locale.ROOT,
|
||||
"%s\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%b\t%s\t%s\t%s\t%s\t%s\t%b\t%b%n",
|
||||
"%s\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%.5f\t%b\t%s\t%s\t%s\t%s\t%s\t%b\t%b\t%s\t%s\t%.5f\t%.5f\t%.5f%n",
|
||||
m.modelPath(),
|
||||
m.x(), m.y(), m.z(),
|
||||
m.rotY(), m.scale(),
|
||||
@@ -37,7 +38,9 @@ public final class PlacedModelIO {
|
||||
m.solid(),
|
||||
nvl(m.texturePath()), nvl(m.normalMapPath()), nvl(m.materialPath()),
|
||||
nvl(m.meshFile()), nvl(m.animClip()),
|
||||
m.castShadow(), m.receiveShadow()));
|
||||
m.castShadow(), m.receiveShadow(),
|
||||
nvl(m.lod1Path()), nvl(m.lod2Path()),
|
||||
m.lod1Distance(), m.lod2Distance(), m.cullDistance()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,14 +71,25 @@ public final class PlacedModelIO {
|
||||
String animClip = f.length > 13 ? f[13] : "";
|
||||
boolean castShadow = f.length > 14 ? Boolean.parseBoolean(f[14]) : true;
|
||||
boolean receiveShadow = f.length > 15 ? Boolean.parseBoolean(f[15]) : true;
|
||||
String lod1Path = f.length > 16 ? f[16] : "";
|
||||
String lod2Path = f.length > 17 ? f[17] : "";
|
||||
float lod1Distance = f.length > 18 ? parseFloat(f[18], 30f) : 30f;
|
||||
float lod2Distance = f.length > 19 ? parseFloat(f[19], 80f) : 80f;
|
||||
float cullDistance = f.length > 20 ? parseFloat(f[20], 120f) : 120f;
|
||||
list.add(new PlacedModel(modelPath, x, y, z,
|
||||
rotY, rotX, rotZ, scale, solid,
|
||||
texPath, nmPath, matPath, meshFile, animClip,
|
||||
castShadow, receiveShadow));
|
||||
castShadow, receiveShadow,
|
||||
lod1Path, lod2Path, lod1Distance, lod2Distance, cullDistance));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String nvl(String s) { return s != null ? s : ""; }
|
||||
|
||||
private static float parseFloat(String s, float def) {
|
||||
try { return Float.parseFloat(s); }
|
||||
catch (NumberFormatException e) { return def; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user