Texturierung von Voxel Terrain angepasst
This commit is contained in:
@@ -80,6 +80,22 @@ public final class MapData {
|
|||||||
/** Splatmap Alpha-Kanal Dritte Gruppe [SPLAT_SIZE²]. */
|
/** Splatmap Alpha-Kanal Dritte Gruppe [SPLAT_SIZE²]. */
|
||||||
public final byte[] thirdSplatA;
|
public final byte[] thirdSplatA;
|
||||||
|
|
||||||
|
/** Voxel-Splatmap Kanal 1-4 (AlphaMap für Sculpted-Meshes, unabhängig vom Basis-Terrain) [SPLAT_SIZE²]. */
|
||||||
|
public final byte[] voxelSplatR;
|
||||||
|
public final byte[] voxelSplatG;
|
||||||
|
public final byte[] voxelSplatB;
|
||||||
|
public final byte[] voxelSplatA;
|
||||||
|
/** Voxel-Splatmap Kanal 5-8 (AlphaMap_1) [SPLAT_SIZE²]. */
|
||||||
|
public final byte[] voxelUpperSplatR;
|
||||||
|
public final byte[] voxelUpperSplatG;
|
||||||
|
public final byte[] voxelUpperSplatB;
|
||||||
|
public final byte[] voxelUpperSplatA;
|
||||||
|
/** Voxel-Splatmap Kanal 9-12 (AlphaMap_2) [SPLAT_SIZE²]. */
|
||||||
|
public final byte[] voxelThirdSplatR;
|
||||||
|
public final byte[] voxelThirdSplatG;
|
||||||
|
public final byte[] voxelThirdSplatB;
|
||||||
|
public final byte[] voxelThirdSplatA;
|
||||||
|
|
||||||
/** Texturpfade für dritte Gruppe (4 Slots, "" = Standard-Textur). */
|
/** Texturpfade für dritte Gruppe (4 Slots, "" = Standard-Textur). */
|
||||||
public final String[] thirdTextures = new String[]{"", "", "", ""};
|
public final String[] thirdTextures = new String[]{"", "", "", ""};
|
||||||
/** Normal-Map-Pfade für dritte Gruppe (4 Slots, "" = keine Normal-Map). */
|
/** Normal-Map-Pfade für dritte Gruppe (4 Slots, "" = keine Normal-Map). */
|
||||||
@@ -155,6 +171,18 @@ public final class MapData {
|
|||||||
thirdSplatG = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatG = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
thirdSplatB = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatB = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
thirdSplatA = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatA = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatR = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatG = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatB = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatA = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatR = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatG = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatB = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatA = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatR = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatG = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatB = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatA = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
grassDensity = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
grassDensity = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
grassHeightMap = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
grassHeightMap = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
grassTextureMap = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
grassTextureMap = new byte [SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.util.zip.*;
|
|||||||
* Altes Format (v≤9) wird beim Laden bilinear hochskaliert.
|
* Altes Format (v≤9) wird beim Laden bilinear hochskaliert.
|
||||||
* 16 – Kartengröße halbiert: TERRAIN_VERTS=8193, SPLAT_SIZE=1025, UPPER_CELLS=256.
|
* 16 – Kartengröße halbiert: TERRAIN_VERTS=8193, SPLAT_SIZE=1025, UPPER_CELLS=256.
|
||||||
* Format v10-v15 wird beim Laden bilinear auf neue Größe herunterskaliert.
|
* Format v10-v15 wird beim Laden bilinear auf neue Größe herunterskaliert.
|
||||||
|
* 17 – Voxel-Splatmaps (12 Kanäle RGBA×3) getrennt von Basis-Terrain-Splatmaps.
|
||||||
*/
|
*/
|
||||||
public final class MapIO {
|
public final class MapIO {
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ public final class MapIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final int MAGIC = 0x424C4947; // "BLIG"
|
private static final int MAGIC = 0x424C4947; // "BLIG"
|
||||||
private static final int VERSION = 16;
|
private static final int VERSION = 17;
|
||||||
|
|
||||||
// Größen alter Saves für Migration
|
// Größen alter Saves für Migration
|
||||||
private static final int V10_TERRAIN_VERTS = 16385; // v10-v15
|
private static final int V10_TERRAIN_VERTS = 16385; // v10-v15
|
||||||
@@ -172,6 +173,13 @@ public final class MapIO {
|
|||||||
writeStrings(out, data.upperDisplacementMaps);
|
writeStrings(out, data.upperDisplacementMaps);
|
||||||
writeStrings(out, data.thirdDisplacementMaps);
|
writeStrings(out, data.thirdDisplacementMaps);
|
||||||
writeFloats(out, data.diffuseScales);
|
writeFloats(out, data.diffuseScales);
|
||||||
|
// v17: voxel-splatmaps (unabhängig vom Basis-Terrain)
|
||||||
|
out.write(data.voxelSplatR); out.write(data.voxelSplatG);
|
||||||
|
out.write(data.voxelSplatB); out.write(data.voxelSplatA);
|
||||||
|
out.write(data.voxelUpperSplatR); out.write(data.voxelUpperSplatG);
|
||||||
|
out.write(data.voxelUpperSplatB); out.write(data.voxelUpperSplatA);
|
||||||
|
out.write(data.voxelThirdSplatR); out.write(data.voxelThirdSplatG);
|
||||||
|
out.write(data.voxelThirdSplatB); out.write(data.voxelThirdSplatA);
|
||||||
}
|
}
|
||||||
// Atomares Umbenennen: erst wenn die Datei vollständig ist ersetzen wir die alte.
|
// Atomares Umbenennen: erst wenn die Datei vollständig ist ersetzen wir die alte.
|
||||||
// ATOMIC_MOVE schlägt auf manchen Systemen cross-device fehl → Fallback auf REPLACE_EXISTING.
|
// ATOMIC_MOVE schlägt auf manchen Systemen cross-device fehl → Fallback auf REPLACE_EXISTING.
|
||||||
@@ -377,6 +385,14 @@ public final class MapIO {
|
|||||||
readStrings(in, data.thirdDisplacementMaps);
|
readStrings(in, data.thirdDisplacementMaps);
|
||||||
readFloats(in, data.diffuseScales);
|
readFloats(in, data.diffuseScales);
|
||||||
}
|
}
|
||||||
|
if (version >= 17) {
|
||||||
|
in.readFully(data.voxelSplatR); in.readFully(data.voxelSplatG);
|
||||||
|
in.readFully(data.voxelSplatB); in.readFully(data.voxelSplatA);
|
||||||
|
in.readFully(data.voxelUpperSplatR); in.readFully(data.voxelUpperSplatG);
|
||||||
|
in.readFully(data.voxelUpperSplatB); in.readFully(data.voxelUpperSplatA);
|
||||||
|
in.readFully(data.voxelThirdSplatR); in.readFully(data.voxelThirdSplatG);
|
||||||
|
in.readFully(data.voxelThirdSplatB); in.readFully(data.voxelThirdSplatA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -530,9 +530,9 @@ public class SculptedMeshEditorState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
com.jme3.texture.Texture2D a0 = tes.getSplatTex();
|
com.jme3.texture.Texture2D a0 = tes.getVoxelSplatTex();
|
||||||
com.jme3.texture.Texture2D a1 = tes.getUpperSplatTex();
|
com.jme3.texture.Texture2D a1 = tes.getVoxelUpperSplatTex();
|
||||||
com.jme3.texture.Texture2D a2 = tes.getThirdSplatTex();
|
com.jme3.texture.Texture2D a2 = tes.getVoxelThirdSplatTex();
|
||||||
if (a0 != null) { mat.setTexture("AlphaMap", a0); }
|
if (a0 != null) { mat.setTexture("AlphaMap", a0); }
|
||||||
if (a1 != null) { mat.setTexture("AlphaMap_1", a1); }
|
if (a1 != null) { mat.setTexture("AlphaMap_1", a1); }
|
||||||
if (a2 != null) { mat.setTexture("AlphaMap_2", a2); }
|
if (a2 != null) { mat.setTexture("AlphaMap_2", a2); }
|
||||||
|
|||||||
@@ -142,6 +142,22 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
private Image thirdSplatImage;
|
private Image thirdSplatImage;
|
||||||
private Texture2D thirdSplatTex;
|
private Texture2D thirdSplatTex;
|
||||||
|
|
||||||
|
// ── Voxel-Splatmaps (Sculpted-Mesh, unabhängig vom Basis-Terrain) ─────────
|
||||||
|
private byte[] voxelSplatR, voxelSplatG, voxelSplatB, voxelSplatA;
|
||||||
|
private ByteBuffer voxelSplatBuf;
|
||||||
|
private Image voxelSplatImage;
|
||||||
|
private Texture2D voxelSplatTex;
|
||||||
|
|
||||||
|
private byte[] voxelUpperSplatR, voxelUpperSplatG, voxelUpperSplatB, voxelUpperSplatA;
|
||||||
|
private ByteBuffer voxelUpperSplatBuf;
|
||||||
|
private Image voxelUpperSplatImage;
|
||||||
|
private Texture2D voxelUpperSplatTex;
|
||||||
|
|
||||||
|
private byte[] voxelThirdSplatR, voxelThirdSplatG, voxelThirdSplatB, voxelThirdSplatA;
|
||||||
|
private ByteBuffer voxelThirdSplatBuf;
|
||||||
|
private Image voxelThirdSplatImage;
|
||||||
|
private Texture2D voxelThirdSplatTex;
|
||||||
|
|
||||||
// ── Kameraposition ────────────────────────────────────────────────────────
|
// ── Kameraposition ────────────────────────────────────────────────────────
|
||||||
private static final float DEFAULT_CAM_Y = 50f;
|
private static final float DEFAULT_CAM_Y = 50f;
|
||||||
private static final float DEFAULT_PITCH = (float) (Math.PI / 4); // intern +45° → Kamera schaut 45° nach unten (JME 3.9: dir.y = -sin(pitch))
|
private static final float DEFAULT_PITCH = (float) (Math.PI / 4); // intern +45° → Kamera schaut 45° nach unten (JME 3.9: dir.y = -sin(pitch))
|
||||||
@@ -476,6 +492,19 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
input.thirdTexturePaths, 0, MapData.TEXTURE_SLOTS);
|
input.thirdTexturePaths, 0, MapData.TEXTURE_SLOTS);
|
||||||
System.arraycopy(loadedMapData.thirdNormalMaps, 0,
|
System.arraycopy(loadedMapData.thirdNormalMaps, 0,
|
||||||
input.thirdNormalMapPaths, 0, MapData.TEXTURE_SLOTS);
|
input.thirdNormalMapPaths, 0, MapData.TEXTURE_SLOTS);
|
||||||
|
// Voxel-Splatmaps (Sculpted-Meshes, seit Version 17)
|
||||||
|
voxelSplatR = loadedMapData.voxelSplatR.clone();
|
||||||
|
voxelSplatG = loadedMapData.voxelSplatG.clone();
|
||||||
|
voxelSplatB = loadedMapData.voxelSplatB.clone();
|
||||||
|
voxelSplatA = loadedMapData.voxelSplatA.clone();
|
||||||
|
voxelUpperSplatR = loadedMapData.voxelUpperSplatR.clone();
|
||||||
|
voxelUpperSplatG = loadedMapData.voxelUpperSplatG.clone();
|
||||||
|
voxelUpperSplatB = loadedMapData.voxelUpperSplatB.clone();
|
||||||
|
voxelUpperSplatA = loadedMapData.voxelUpperSplatA.clone();
|
||||||
|
voxelThirdSplatR = loadedMapData.voxelThirdSplatR.clone();
|
||||||
|
voxelThirdSplatG = loadedMapData.voxelThirdSplatG.clone();
|
||||||
|
voxelThirdSplatB = loadedMapData.voxelThirdSplatB.clone();
|
||||||
|
voxelThirdSplatA = loadedMapData.voxelThirdSplatA.clone();
|
||||||
if (loadedMapData.normalMapOrder != null && loadedMapData.normalMapOrder.length == 12)
|
if (loadedMapData.normalMapOrder != null && loadedMapData.normalMapOrder.length == 12)
|
||||||
input.normalMapOrder = loadedMapData.normalMapOrder.clone();
|
input.normalMapOrder = loadedMapData.normalMapOrder.clone();
|
||||||
if (loadedMapData.diffuseScales != null && loadedMapData.diffuseScales.length == 12)
|
if (loadedMapData.diffuseScales != null && loadedMapData.diffuseScales.length == 12)
|
||||||
@@ -507,6 +536,18 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
thirdSplatG = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatG = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
thirdSplatB = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatB = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
thirdSplatA = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
thirdSplatA = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatR = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatG = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatB = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelSplatA = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatR = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatG = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatB = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelUpperSplatA = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatR = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatG = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatB = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
|
voxelThirdSplatA = new byte[SPLAT_SIZE * SPLAT_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
// A-Kanal aus alten Saves bereinigen (war für Fluss-Textur; nicht mehr verwendet)
|
// A-Kanal aus alten Saves bereinigen (war für Fluss-Textur; nicht mehr verwendet)
|
||||||
@@ -545,6 +586,39 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
thirdSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
thirdSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
thirdSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
thirdSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
|
|
||||||
|
voxelSplatBuf = BufferUtils.createByteBuffer(SPLAT_SIZE * SPLAT_SIZE * 4);
|
||||||
|
for (int i = 0; i < SPLAT_SIZE * SPLAT_SIZE; i++) {
|
||||||
|
voxelSplatBuf.put(voxelSplatR[i]).put(voxelSplatG[i]).put(voxelSplatB[i]).put(voxelSplatA[i]);
|
||||||
|
}
|
||||||
|
voxelSplatBuf.flip();
|
||||||
|
voxelSplatImage = new Image(Image.Format.RGBA8, SPLAT_SIZE, SPLAT_SIZE, voxelSplatBuf, ColorSpace.Linear);
|
||||||
|
voxelSplatTex = new Texture2D(voxelSplatImage);
|
||||||
|
voxelSplatTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||||
|
voxelSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
|
voxelSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
|
|
||||||
|
voxelUpperSplatBuf = BufferUtils.createByteBuffer(SPLAT_SIZE * SPLAT_SIZE * 4);
|
||||||
|
for (int i = 0; i < SPLAT_SIZE * SPLAT_SIZE; i++) {
|
||||||
|
voxelUpperSplatBuf.put(voxelUpperSplatR[i]).put(voxelUpperSplatG[i]).put(voxelUpperSplatB[i]).put(voxelUpperSplatA[i]);
|
||||||
|
}
|
||||||
|
voxelUpperSplatBuf.flip();
|
||||||
|
voxelUpperSplatImage = new Image(Image.Format.RGBA8, SPLAT_SIZE, SPLAT_SIZE, voxelUpperSplatBuf, ColorSpace.Linear);
|
||||||
|
voxelUpperSplatTex = new Texture2D(voxelUpperSplatImage);
|
||||||
|
voxelUpperSplatTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||||
|
voxelUpperSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
|
voxelUpperSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
|
|
||||||
|
voxelThirdSplatBuf = BufferUtils.createByteBuffer(SPLAT_SIZE * SPLAT_SIZE * 4);
|
||||||
|
for (int i = 0; i < SPLAT_SIZE * SPLAT_SIZE; i++) {
|
||||||
|
voxelThirdSplatBuf.put(voxelThirdSplatR[i]).put(voxelThirdSplatG[i]).put(voxelThirdSplatB[i]).put(voxelThirdSplatA[i]);
|
||||||
|
}
|
||||||
|
voxelThirdSplatBuf.flip();
|
||||||
|
voxelThirdSplatImage = new Image(Image.Format.RGBA8, SPLAT_SIZE, SPLAT_SIZE, voxelThirdSplatBuf, ColorSpace.Linear);
|
||||||
|
voxelThirdSplatTex = new Texture2D(voxelThirdSplatImage);
|
||||||
|
voxelThirdSplatTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||||
|
voxelThirdSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
|
voxelThirdSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
|
|
||||||
// loadedMapData wird ab hier nicht mehr gebraucht (letzter Verbraucher war
|
// loadedMapData wird ab hier nicht mehr gebraucht (letzter Verbraucher war
|
||||||
// dieser Merge). Freigeben, weil terrainHeight allein ~1 GB (16385² floats)
|
// dieser Merge). Freigeben, weil terrainHeight allein ~1 GB (16385² floats)
|
||||||
// belegt und sonst die ganze Session über unnötig im Heap hängt.
|
// belegt und sonst die ganze Session über unnötig im Heap hängt.
|
||||||
@@ -954,6 +1028,139 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Malt Voxel-Basis-Texturen (Slots 1-4) auf die Voxel-Splatmap. */
|
||||||
|
private void applyVoxelTexturePaint(Vector3f contact, float strength, int textureIndex) {
|
||||||
|
float radius = (float) input.textureTool.brushRadius.getValue();
|
||||||
|
float softEdge = radius + Math.min(radius * 0.5f, 2f * SPLAT_WE_PER_PX);
|
||||||
|
int centerPX = Math.round((contact.x + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int centerPZ = (SPLAT_SIZE - 1) - Math.round((contact.z + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int pixR = (int) Math.ceil(softEdge / SPLAT_WE_PER_PX);
|
||||||
|
boolean changed = false;
|
||||||
|
for (int dz = -pixR; dz <= pixR; dz++) {
|
||||||
|
int pz = centerPZ + dz;
|
||||||
|
if (pz < 0 || pz >= SPLAT_SIZE) continue;
|
||||||
|
for (int dx = -pixR; dx <= pixR; dx++) {
|
||||||
|
int px = centerPX + dx;
|
||||||
|
if (px < 0 || px >= SPLAT_SIZE) continue;
|
||||||
|
float distWE = FastMath.sqrt(dx * dx + dz * dz) * SPLAT_WE_PER_PX;
|
||||||
|
if (distWE >= softEdge) continue;
|
||||||
|
float t = distWE / softEdge;
|
||||||
|
float falloff = (1f + FastMath.cos(FastMath.PI * t)) * 0.5f;
|
||||||
|
float blend = (textureIndex == 0) ? falloff : strength * falloff;
|
||||||
|
int idx = pz * SPLAT_SIZE + px;
|
||||||
|
float curG = (voxelSplatG[idx] & 0xFF) / 255f;
|
||||||
|
float curB = (voxelSplatB[idx] & 0xFF) / 255f;
|
||||||
|
float curA = (voxelSplatA[idx] & 0xFF) / 255f;
|
||||||
|
float tgG = (textureIndex == 1) ? 1f : 0f;
|
||||||
|
float tgB = (textureIndex == 2) ? 1f : 0f;
|
||||||
|
float tgA = (textureIndex == 3) ? 1f : 0f;
|
||||||
|
voxelSplatR[idx] = (byte) 255;
|
||||||
|
voxelSplatG[idx] = (byte) Math.round((curG + (tgG - curG) * blend) * 255f);
|
||||||
|
voxelSplatB[idx] = (byte) Math.round((curB + (tgB - curB) * blend) * 255f);
|
||||||
|
voxelSplatA[idx] = (byte) Math.round((curA + (tgA - curA) * blend) * 255f);
|
||||||
|
int bi = idx * 4;
|
||||||
|
voxelSplatBuf.put(bi, voxelSplatR[idx]);
|
||||||
|
voxelSplatBuf.put(bi + 1, voxelSplatG[idx]);
|
||||||
|
voxelSplatBuf.put(bi + 2, voxelSplatB[idx]);
|
||||||
|
voxelSplatBuf.put(bi + 3, voxelSplatA[idx]);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) { voxelSplatBuf.rewind(); voxelSplatImage.setUpdateNeeded(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Malt Voxel-Slots 5-8 auf die Voxel-Upper-Splatmap. */
|
||||||
|
private void applyVoxelUpperTexturePaint(Vector3f contact, float strength, int textureIndex) {
|
||||||
|
float radius = (float) input.textureTool.brushRadius.getValue();
|
||||||
|
float softEdge = radius + Math.min(radius * 0.5f, 2f * SPLAT_WE_PER_PX);
|
||||||
|
int centerPX = Math.round((contact.x + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int centerPZ = (SPLAT_SIZE - 1) - Math.round((contact.z + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int pixR = (int) Math.ceil(softEdge / SPLAT_WE_PER_PX);
|
||||||
|
boolean changed = false;
|
||||||
|
for (int dz = -pixR; dz <= pixR; dz++) {
|
||||||
|
int pz = centerPZ + dz;
|
||||||
|
if (pz < 0 || pz >= SPLAT_SIZE) continue;
|
||||||
|
for (int dx = -pixR; dx <= pixR; dx++) {
|
||||||
|
int px = centerPX + dx;
|
||||||
|
if (px < 0 || px >= SPLAT_SIZE) continue;
|
||||||
|
float distWE = FastMath.sqrt(dx * dx + dz * dz) * SPLAT_WE_PER_PX;
|
||||||
|
if (distWE >= softEdge) continue;
|
||||||
|
float t = distWE / softEdge;
|
||||||
|
float falloff = (1f + FastMath.cos(FastMath.PI * t)) * 0.5f;
|
||||||
|
int idx = pz * SPLAT_SIZE + px;
|
||||||
|
if (textureIndex < 0) {
|
||||||
|
voxelUpperSplatR[idx] = (byte) Math.round((voxelUpperSplatR[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelUpperSplatG[idx] = (byte) Math.round((voxelUpperSplatG[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelUpperSplatB[idx] = (byte) Math.round((voxelUpperSplatB[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelUpperSplatA[idx] = (byte) Math.round((voxelUpperSplatA[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
} else {
|
||||||
|
float blend = strength * falloff;
|
||||||
|
float curR = (voxelUpperSplatR[idx] & 0xFF) / 255f;
|
||||||
|
float curG = (voxelUpperSplatG[idx] & 0xFF) / 255f;
|
||||||
|
float curB = (voxelUpperSplatB[idx] & 0xFF) / 255f;
|
||||||
|
float curA = (voxelUpperSplatA[idx] & 0xFF) / 255f;
|
||||||
|
voxelUpperSplatR[idx] = (byte) Math.round((curR + ((textureIndex == 0 ? 1f : 0f) - curR) * blend) * 255f);
|
||||||
|
voxelUpperSplatG[idx] = (byte) Math.round((curG + ((textureIndex == 1 ? 1f : 0f) - curG) * blend) * 255f);
|
||||||
|
voxelUpperSplatB[idx] = (byte) Math.round((curB + ((textureIndex == 2 ? 1f : 0f) - curB) * blend) * 255f);
|
||||||
|
voxelUpperSplatA[idx] = (byte) Math.round((curA + ((textureIndex == 3 ? 1f : 0f) - curA) * blend) * 255f);
|
||||||
|
}
|
||||||
|
int bi = idx * 4;
|
||||||
|
voxelUpperSplatBuf.put(bi, voxelUpperSplatR[idx]);
|
||||||
|
voxelUpperSplatBuf.put(bi + 1, voxelUpperSplatG[idx]);
|
||||||
|
voxelUpperSplatBuf.put(bi + 2, voxelUpperSplatB[idx]);
|
||||||
|
voxelUpperSplatBuf.put(bi + 3, voxelUpperSplatA[idx]);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) { voxelUpperSplatBuf.rewind(); voxelUpperSplatImage.setUpdateNeeded(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Malt Voxel-Slots 9-12 auf die Voxel-Third-Splatmap. */
|
||||||
|
private void applyVoxelThirdTexturePaint(Vector3f contact, float strength, int textureIndex) {
|
||||||
|
float radius = (float) input.textureTool.brushRadius.getValue();
|
||||||
|
float softEdge = radius + Math.min(radius * 0.5f, 2f * SPLAT_WE_PER_PX);
|
||||||
|
int centerPX = Math.round((contact.x + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int centerPZ = (SPLAT_SIZE - 1) - Math.round((contact.z + WORLD_HALF) / SPLAT_WE_PER_PX);
|
||||||
|
int pixR = (int) Math.ceil(softEdge / SPLAT_WE_PER_PX);
|
||||||
|
boolean changed = false;
|
||||||
|
for (int dz = -pixR; dz <= pixR; dz++) {
|
||||||
|
int pz = centerPZ + dz;
|
||||||
|
if (pz < 0 || pz >= SPLAT_SIZE) continue;
|
||||||
|
for (int dx = -pixR; dx <= pixR; dx++) {
|
||||||
|
int px = centerPX + dx;
|
||||||
|
if (px < 0 || px >= SPLAT_SIZE) continue;
|
||||||
|
float distWE = FastMath.sqrt(dx * dx + dz * dz) * SPLAT_WE_PER_PX;
|
||||||
|
if (distWE >= softEdge) continue;
|
||||||
|
float t = distWE / softEdge;
|
||||||
|
float falloff = (1f + FastMath.cos(FastMath.PI * t)) * 0.5f;
|
||||||
|
int idx = pz * SPLAT_SIZE + px;
|
||||||
|
if (textureIndex < 0) {
|
||||||
|
voxelThirdSplatR[idx] = (byte) Math.round((voxelThirdSplatR[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelThirdSplatG[idx] = (byte) Math.round((voxelThirdSplatG[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelThirdSplatB[idx] = (byte) Math.round((voxelThirdSplatB[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
voxelThirdSplatA[idx] = (byte) Math.round((voxelThirdSplatA[idx] & 0xFF) / 255f * (1f - falloff) * 255f);
|
||||||
|
} else {
|
||||||
|
float blend = strength * falloff;
|
||||||
|
float curR = (voxelThirdSplatR[idx] & 0xFF) / 255f;
|
||||||
|
float curG = (voxelThirdSplatG[idx] & 0xFF) / 255f;
|
||||||
|
float curB = (voxelThirdSplatB[idx] & 0xFF) / 255f;
|
||||||
|
float curA = (voxelThirdSplatA[idx] & 0xFF) / 255f;
|
||||||
|
voxelThirdSplatR[idx] = (byte) Math.round((curR + ((textureIndex == 0 ? 1f : 0f) - curR) * blend) * 255f);
|
||||||
|
voxelThirdSplatG[idx] = (byte) Math.round((curG + ((textureIndex == 1 ? 1f : 0f) - curG) * blend) * 255f);
|
||||||
|
voxelThirdSplatB[idx] = (byte) Math.round((curB + ((textureIndex == 2 ? 1f : 0f) - curB) * blend) * 255f);
|
||||||
|
voxelThirdSplatA[idx] = (byte) Math.round((curA + ((textureIndex == 3 ? 1f : 0f) - curA) * blend) * 255f);
|
||||||
|
}
|
||||||
|
int bi = idx * 4;
|
||||||
|
voxelThirdSplatBuf.put(bi, voxelThirdSplatR[idx]);
|
||||||
|
voxelThirdSplatBuf.put(bi + 1, voxelThirdSplatG[idx]);
|
||||||
|
voxelThirdSplatBuf.put(bi + 2, voxelThirdSplatB[idx]);
|
||||||
|
voxelThirdSplatBuf.put(bi + 3, voxelThirdSplatA[idx]);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) { voxelThirdSplatBuf.rewind(); voxelThirdSplatImage.setUpdateNeeded(); }
|
||||||
|
}
|
||||||
|
|
||||||
// ── Update-Schleife ───────────────────────────────────────────────────────
|
// ── Update-Schleife ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1159,27 +1366,44 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
Vector3f far = cam.getWorldCoordinates(new Vector2f(jmeX, jmeY), 1f);
|
Vector3f far = cam.getWorldCoordinates(new Vector2f(jmeX, jmeY), 1f);
|
||||||
com.jme3.math.Ray ray = new com.jme3.math.Ray(near, far.subtract(near).normalizeLocal());
|
com.jme3.math.Ray ray = new com.jme3.math.Ray(near, far.subtract(near).normalizeLocal());
|
||||||
|
|
||||||
Vector3f contact = raycastSurface(ray);
|
SurfaceHit hit = raycastSurfaceWithType(ray);
|
||||||
if (contact == null) continue;
|
if (hit == null) continue;
|
||||||
if (ves != null && ves.terrainTypeAt(contact.x, contact.z) == VoxelEditorState.TerrainType.VOXEL_UNBAKED) continue;
|
Vector3f contact = hit.point;
|
||||||
|
if (!hit.voxelMesh && ves != null && ves.terrainTypeAt(contact.x, contact.z) == VoxelEditorState.TerrainType.VOXEL_UNBAKED) continue;
|
||||||
|
|
||||||
int selIdx = input.textureTool.textureIndex.getSelectedIndex();
|
int selIdx = input.textureTool.textureIndex.getSelectedIndex();
|
||||||
float str = (float) input.textureTool.brushStrength.getValue();
|
float str = (float) input.textureTool.brushStrength.getValue();
|
||||||
|
|
||||||
if (edit.action() > 0) {
|
if (hit.voxelMesh) {
|
||||||
// Linksklick: Slot malen
|
// Sculpted-Mesh-Treffer → Voxel-Splatmaps
|
||||||
if (selIdx >= 8) {
|
if (edit.action() > 0) {
|
||||||
applyThirdTexturePaint(contact, str, selIdx - 8); // 0=R(Slot9),1=G(Slot10),...
|
if (selIdx >= 8) {
|
||||||
} else if (selIdx >= 4) {
|
applyVoxelThirdTexturePaint(contact, str, selIdx - 8);
|
||||||
applyUpperTexturePaint(contact, str, selIdx - 4); // 0=R(Slot5),1=G(Slot6),...
|
} else if (selIdx >= 4) {
|
||||||
|
applyVoxelUpperTexturePaint(contact, str, selIdx - 4);
|
||||||
|
} else {
|
||||||
|
applyVoxelTexturePaint(contact, str, selIdx);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
applyTexturePaint(contact, str, selIdx);
|
applyVoxelTexturePaint(contact, str, 0);
|
||||||
|
applyVoxelUpperTexturePaint(contact, str, -1);
|
||||||
|
applyVoxelThirdTexturePaint(contact, str, -1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Rechtsklick: immer auf Basis (Slot 1) zurücksetzen
|
// Basis-Terrain-Treffer → normale Splatmaps
|
||||||
applyTexturePaint(contact, str, 0); // Base → Slot 1 (Gras)
|
if (edit.action() > 0) {
|
||||||
applyUpperTexturePaint(contact, str, -1); // Upper-Kanäle → 0
|
if (selIdx >= 8) {
|
||||||
applyThirdTexturePaint(contact, str, -1); // Third-Kanäle → 0
|
applyThirdTexturePaint(contact, str, selIdx - 8);
|
||||||
|
} else if (selIdx >= 4) {
|
||||||
|
applyUpperTexturePaint(contact, str, selIdx - 4);
|
||||||
|
} else {
|
||||||
|
applyTexturePaint(contact, str, selIdx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyTexturePaint(contact, str, 0);
|
||||||
|
applyUpperTexturePaint(contact, str, -1);
|
||||||
|
applyThirdTexturePaint(contact, str, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1194,22 +1418,38 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
public com.jme3.texture.Texture2D getUpperSplatTex() { return upperSplatTex; }
|
public com.jme3.texture.Texture2D getUpperSplatTex() { return upperSplatTex; }
|
||||||
public com.jme3.texture.Texture2D getThirdSplatTex() { return thirdSplatTex; }
|
public com.jme3.texture.Texture2D getThirdSplatTex() { return thirdSplatTex; }
|
||||||
|
|
||||||
|
public com.jme3.texture.Texture2D getVoxelSplatTex() { return voxelSplatTex; }
|
||||||
|
public com.jme3.texture.Texture2D getVoxelUpperSplatTex() { return voxelUpperSplatTex; }
|
||||||
|
public com.jme3.texture.Texture2D getVoxelThirdSplatTex() { return voxelThirdSplatTex; }
|
||||||
|
|
||||||
|
private static final class SurfaceHit {
|
||||||
|
final Vector3f point;
|
||||||
|
final boolean voxelMesh;
|
||||||
|
SurfaceHit(Vector3f p, boolean v) { point = p; voxelMesh = v; }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raycast gegen Basis-Terrain, ungebackenes Voxel-Terrain und gebackene Sculpted-Meshes.
|
* Raycast gegen Basis-Terrain, ungebackenes Voxel-Terrain und gebackene Sculpted-Meshes.
|
||||||
* Gibt den zur Kamera nächstgelegenen Treffer zurück, oder null wenn keines getroffen wurde.
|
* Gibt den zur Kamera nächstgelegenen Treffer zurück, oder null wenn keines getroffen wurde.
|
||||||
*/
|
*/
|
||||||
private Vector3f raycastSurface(com.jme3.math.Ray ray) {
|
private Vector3f raycastSurface(com.jme3.math.Ray ray) {
|
||||||
|
SurfaceHit h = raycastSurfaceWithType(ray);
|
||||||
|
return h != null ? h.point : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SurfaceHit raycastSurfaceWithType(com.jme3.math.Ray ray) {
|
||||||
CollisionResults hits = new CollisionResults();
|
CollisionResults hits = new CollisionResults();
|
||||||
terrain.collideWith(ray, hits);
|
terrain.collideWith(ray, hits);
|
||||||
Vector3f best = hits.size() > 0 ? hits.getClosestCollision().getContactPoint() : null;
|
Vector3f best = hits.size() > 0 ? hits.getClosestCollision().getContactPoint() : null;
|
||||||
float bestDistSq = best != null ? ray.getOrigin().distanceSquared(best) : Float.MAX_VALUE;
|
float bestDistSq = best != null ? ray.getOrigin().distanceSquared(best) : Float.MAX_VALUE;
|
||||||
|
boolean bestIsVoxelMesh = false;
|
||||||
|
|
||||||
VoxelEditorState ves = getStateManager().getState(VoxelEditorState.class);
|
VoxelEditorState ves = getStateManager().getState(VoxelEditorState.class);
|
||||||
if (ves != null) {
|
if (ves != null) {
|
||||||
Vector3f vp = ves.raycastVoxelGeometry(ray);
|
Vector3f vp = ves.raycastVoxelGeometry(ray);
|
||||||
if (vp != null) {
|
if (vp != null) {
|
||||||
float d = ray.getOrigin().distanceSquared(vp);
|
float d = ray.getOrigin().distanceSquared(vp);
|
||||||
if (d < bestDistSq) { best = vp; bestDistSq = d; }
|
if (d < bestDistSq) { best = vp; bestDistSq = d; bestIsVoxelMesh = false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1218,10 +1458,10 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
Vector3f sp = smes.raycastGeometry(ray);
|
Vector3f sp = smes.raycastGeometry(ray);
|
||||||
if (sp != null) {
|
if (sp != null) {
|
||||||
float d = ray.getOrigin().distanceSquared(sp);
|
float d = ray.getOrigin().distanceSquared(sp);
|
||||||
if (d < bestDistSq) { best = sp; }
|
if (d < bestDistSq) { best = sp; bestIsVoxelMesh = true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return best;
|
return best != null ? new SurfaceHit(best, bestIsVoxelMesh) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gibt die Terrain-Höhe (Welt-Y) an der angegebenen Welt-XZ-Position zurück. */
|
/** Gibt die Terrain-Höhe (Welt-Y) an der angegebenen Welt-XZ-Position zurück. */
|
||||||
@@ -1268,6 +1508,18 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
final byte[] thG = thirdSplatG != null ? thirdSplatG.clone() : null;
|
final byte[] thG = thirdSplatG != null ? thirdSplatG.clone() : null;
|
||||||
final byte[] thB = thirdSplatB != null ? thirdSplatB.clone() : null;
|
final byte[] thB = thirdSplatB != null ? thirdSplatB.clone() : null;
|
||||||
final byte[] thA = thirdSplatA != null ? thirdSplatA.clone() : null;
|
final byte[] thA = thirdSplatA != null ? thirdSplatA.clone() : null;
|
||||||
|
final byte[] vxR = voxelSplatR != null ? voxelSplatR.clone() : null;
|
||||||
|
final byte[] vxG = voxelSplatG != null ? voxelSplatG.clone() : null;
|
||||||
|
final byte[] vxB = voxelSplatB != null ? voxelSplatB.clone() : null;
|
||||||
|
final byte[] vxA = voxelSplatA != null ? voxelSplatA.clone() : null;
|
||||||
|
final byte[] vxUR = voxelUpperSplatR != null ? voxelUpperSplatR.clone() : null;
|
||||||
|
final byte[] vxUG = voxelUpperSplatG != null ? voxelUpperSplatG.clone() : null;
|
||||||
|
final byte[] vxUB = voxelUpperSplatB != null ? voxelUpperSplatB.clone() : null;
|
||||||
|
final byte[] vxUA = voxelUpperSplatA != null ? voxelUpperSplatA.clone() : null;
|
||||||
|
final byte[] vxTR = voxelThirdSplatR != null ? voxelThirdSplatR.clone() : null;
|
||||||
|
final byte[] vxTG = voxelThirdSplatG != null ? voxelThirdSplatG.clone() : null;
|
||||||
|
final byte[] vxTB = voxelThirdSplatB != null ? voxelThirdSplatB.clone() : null;
|
||||||
|
final byte[] vxTA = voxelThirdSplatA != null ? voxelThirdSplatA.clone() : null;
|
||||||
final String[] texPaths = input.terrainTexturePaths.clone();
|
final String[] texPaths = input.terrainTexturePaths.clone();
|
||||||
final String[] normalPaths = input.terrainNormalMapPaths.clone();
|
final String[] normalPaths = input.terrainNormalMapPaths.clone();
|
||||||
final String[] upperPaths = input.upperTexturePaths.clone();
|
final String[] upperPaths = input.upperTexturePaths.clone();
|
||||||
@@ -1345,6 +1597,20 @@ public class TerrainEditorState extends BaseAppState {
|
|||||||
System.arraycopy(thirdPaths, 0, data.thirdTextures, 0, MapData.TEXTURE_SLOTS);
|
System.arraycopy(thirdPaths, 0, data.thirdTextures, 0, MapData.TEXTURE_SLOTS);
|
||||||
System.arraycopy(thirdNormPaths, 0, data.thirdNormalMaps, 0, MapData.TEXTURE_SLOTS);
|
System.arraycopy(thirdNormPaths, 0, data.thirdNormalMaps, 0, MapData.TEXTURE_SLOTS);
|
||||||
}
|
}
|
||||||
|
if (vxR != null) {
|
||||||
|
System.arraycopy(vxR, 0, data.voxelSplatR, 0, data.voxelSplatR.length);
|
||||||
|
System.arraycopy(vxG, 0, data.voxelSplatG, 0, data.voxelSplatG.length);
|
||||||
|
System.arraycopy(vxB, 0, data.voxelSplatB, 0, data.voxelSplatB.length);
|
||||||
|
System.arraycopy(vxA, 0, data.voxelSplatA, 0, data.voxelSplatA.length);
|
||||||
|
System.arraycopy(vxUR, 0, data.voxelUpperSplatR, 0, data.voxelUpperSplatR.length);
|
||||||
|
System.arraycopy(vxUG, 0, data.voxelUpperSplatG, 0, data.voxelUpperSplatG.length);
|
||||||
|
System.arraycopy(vxUB, 0, data.voxelUpperSplatB, 0, data.voxelUpperSplatB.length);
|
||||||
|
System.arraycopy(vxUA, 0, data.voxelUpperSplatA, 0, data.voxelUpperSplatA.length);
|
||||||
|
System.arraycopy(vxTR, 0, data.voxelThirdSplatR, 0, data.voxelThirdSplatR.length);
|
||||||
|
System.arraycopy(vxTG, 0, data.voxelThirdSplatG, 0, data.voxelThirdSplatG.length);
|
||||||
|
System.arraycopy(vxTB, 0, data.voxelThirdSplatB, 0, data.voxelThirdSplatB.length);
|
||||||
|
System.arraycopy(vxTA, 0, data.voxelThirdSplatA, 0, data.voxelThirdSplatA.length);
|
||||||
|
}
|
||||||
data.normalMapOrder = input.normalMapOrder.clone();
|
data.normalMapOrder = input.normalMapOrder.clone();
|
||||||
data.diffuseScales = diffScaleSnap.clone();
|
data.diffuseScales = diffScaleSnap.clone();
|
||||||
data.voxelFlatSlot = voxelFlatSlot;
|
data.voxelFlatSlot = voxelFlatSlot;
|
||||||
|
|||||||
@@ -238,19 +238,22 @@ public class SculptedMeshState extends BaseAppState {
|
|||||||
private Material buildMaterial() {
|
private Material buildMaterial() {
|
||||||
Material mat = new Material(app.getAssetManager(), "MatDefs/BakedTerrain.j3md");
|
Material mat = new Material(app.getAssetManager(), "MatDefs/BakedTerrain.j3md");
|
||||||
|
|
||||||
// DiffuseArray + DiffuseScales + AlphaMaps vom Terrain-Material übernehmen,
|
// DiffuseArray + DiffuseScales vom Terrain-Material übernehmen (selbe Textur-Slots).
|
||||||
// damit die gemalten Splatmap-Texturen auch ingame sichtbar sind.
|
// AlphaMaps kommen aus den Voxel-spezifischen Splatmap-Arrays in MapData.
|
||||||
if (terrainMaterial != null) {
|
if (terrainMaterial != null) {
|
||||||
com.jme3.material.MatParam diffArr = terrainMaterial.getParam("DiffuseArray");
|
com.jme3.material.MatParam diffArr = terrainMaterial.getParam("DiffuseArray");
|
||||||
if (diffArr != null) mat.setParam("DiffuseArray", diffArr.getVarType(), diffArr.getValue());
|
if (diffArr != null) mat.setParam("DiffuseArray", diffArr.getVarType(), diffArr.getValue());
|
||||||
|
|
||||||
com.jme3.material.MatParam scales = terrainMaterial.getParam("DiffuseScales");
|
com.jme3.material.MatParam scales = terrainMaterial.getParam("DiffuseScales");
|
||||||
if (scales != null) mat.setParam("DiffuseScales", scales.getVarType(), scales.getValue());
|
if (scales != null) mat.setParam("DiffuseScales", scales.getVarType(), scales.getValue());
|
||||||
|
}
|
||||||
for (String key : new String[]{"AlphaMap", "AlphaMap_1", "AlphaMap_2"}) {
|
if (mapData != null) {
|
||||||
com.jme3.material.MatParam p = terrainMaterial.getParam(key);
|
com.jme3.texture.Texture2D a0 = buildVoxelAlphaMap(mapData.voxelSplatR, mapData.voxelSplatG, mapData.voxelSplatB, mapData.voxelSplatA);
|
||||||
if (p != null) mat.setTexture(key, (Texture) p.getValue());
|
com.jme3.texture.Texture2D a1 = buildVoxelAlphaMap(mapData.voxelUpperSplatR, mapData.voxelUpperSplatG, mapData.voxelUpperSplatB, mapData.voxelUpperSplatA);
|
||||||
}
|
com.jme3.texture.Texture2D a2 = buildVoxelAlphaMap(mapData.voxelThirdSplatR, mapData.voxelThirdSplatG, mapData.voxelThirdSplatB, mapData.voxelThirdSplatA);
|
||||||
|
mat.setTexture("AlphaMap", a0);
|
||||||
|
mat.setTexture("AlphaMap_1", a1);
|
||||||
|
mat.setTexture("AlphaMap_2", a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Klippen-Textur: steiler Slot aus MapData für Flat/Steep-Blending
|
// Klippen-Textur: steiler Slot aus MapData für Flat/Steep-Blending
|
||||||
@@ -261,4 +264,21 @@ public class SculptedMeshState extends BaseAppState {
|
|||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private com.jme3.texture.Texture2D buildVoxelAlphaMap(byte[] r, byte[] g, byte[] b, byte[] a) {
|
||||||
|
int size = MapData.SPLAT_SIZE;
|
||||||
|
java.nio.ByteBuffer buf = com.jme3.util.BufferUtils.createByteBuffer(size * size * 4);
|
||||||
|
for (int i = 0; i < size * size; i++) {
|
||||||
|
buf.put(r[i]).put(g[i]).put(b[i]).put(a[i]);
|
||||||
|
}
|
||||||
|
buf.flip();
|
||||||
|
com.jme3.texture.Image img = new com.jme3.texture.Image(
|
||||||
|
com.jme3.texture.Image.Format.RGBA8, size, size, buf,
|
||||||
|
com.jme3.texture.image.ColorSpace.Linear);
|
||||||
|
com.jme3.texture.Texture2D tex = new com.jme3.texture.Texture2D(img);
|
||||||
|
tex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||||
|
tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||||
|
tex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user