VegetationEditor: EZ-Tree Windanimation + Palm-Textur-Rotation fix

EzTreeState: addWindWeights() setzt VertexBuffer.Color (R = Wind-Gewicht)
nach der Mesh-Erzeugung. Blätter bekommen 1.0 (volle Animation), Äste
werden per Y-Position normiert. Tree.vert liest windW aus inColor.r —
ohne diesen Buffer blieb windW=0 und kein Schwingen sichtbar.

PalmMeshBuilder/PalmGeneratorState: stemLeft immer true (Stiel entlang U),
leafTextureAspect immer h/w. Behebt die 90°-Drehung von palm.png.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 22:32:16 +02:00
parent dea1aa3d3e
commit 76a192df67
3 changed files with 50 additions and 3 deletions

View File

@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -120,10 +121,12 @@ public class EzTreeState extends BaseAppState {
Node hdNode = tryNodeJsGeneration(req);
if (hdNode == null) hdNode = javaFallback(req);
addWindWeights(hdNode);
hdNode.setLocalScale(1f / 3f);
hdNode.updateGeometricState();
Node ld1Node = buildLod1Node(req);
addWindWeights(ld1Node);
ld1Node.setLocalScale(1f / 3f);
BoundingBox bb = boundsOf(hdNode);
@@ -681,6 +684,51 @@ public class EzTreeState extends BaseAppState {
return g;
}
/**
* Setzt Vertex-Farben (R = Wind-Gewicht) für alle Geometrien eines Baum-Knotens.
* Blätter bekommen Gewicht 1.0 (volle Animation); Äste werden per Y-Position
* normiert (0 = Boden, 1 = Spitze), sodass höhere Äste stärker schwingen.
* Tree.vert liest den Wind-Weight aus inColor.r — ohne diesen Buffer bleibt windW=0.
*/
private static void addWindWeights(Node treeNode) {
for (Spatial child : treeNode.getChildren()) {
if (!(child instanceof Geometry g)) continue;
Mesh mesh = g.getMesh();
FloatBuffer pos = mesh.getFloatBuffer(VertexBuffer.Type.Position);
if (pos == null) continue;
pos.rewind();
int vCount = pos.limit() / 3;
float[] colors = new float[vCount * 4];
boolean isLeaf = g.getName().contains("leav") || g.getName().contains("leaf");
if (isLeaf) {
for (int i = 0; i < vCount; i++) {
colors[i * 4] = 1f;
colors[i * 4 + 3] = 1f;
}
} else {
float minY = Float.MAX_VALUE, maxY = -Float.MAX_VALUE;
for (int i = 0; i < vCount; i++) {
pos.get();
float y = pos.get();
pos.get();
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
float range = maxY - minY;
pos.rewind();
for (int i = 0; i < vCount; i++) {
pos.get();
float y = pos.get();
pos.get();
colors[i * 4] = range > 0f ? (y - minY) / range : 0f;
colors[i * 4 + 3] = 1f;
}
}
mesh.setBuffer(VertexBuffer.Type.Color, 4, BufferUtils.createFloatBuffer(colors));
}
}
private void exportTree(Node lodRoot, String fileName, String subPath) {
try {
Path baseDir = ASSET_ROOT.resolve("Models").resolve("trees").resolve(subPath);

View File

@@ -113,8 +113,7 @@ public class PalmGeneratorState extends BaseAppState {
Texture t = assets.loadTexture(opts.leafTexture);
int w = t.getImage().getWidth();
int h = t.getImage().getHeight();
boolean stemLeft = opts.leafTexture.contains("palm2");
opts.leafTextureAspect = stemLeft ? (float) h / w : (float) w / h;
opts.leafTextureAspect = (float) h / w; // Stiel entlang U → Aspekt = Höhe/Breite
} catch (Exception ignored) {}
}

View File

@@ -236,7 +236,7 @@ public class PalmMeshBuilder {
float halfW = opts.leafTextureAspect > 0f
? scaledLength * opts.leafTextureAspect * 0.5f
: opts.frondWidth * sizeScale * 0.5f;
boolean stemLeft = opts.leafTexture != null && opts.leafTexture.contains("palm2");
boolean stemLeft = true; // Stiel entlang U-Achse für alle Palmen-Texturen
float g = opts.gravity;
int base = acc.vertexCount;