Weiter daran gearbeitet, die welt aus dem editor ins game zu bringen
This commit is contained in:
@@ -6,19 +6,24 @@ import com.jme3.app.state.BaseAppState;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.bullet.BulletAppState;
|
||||
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
|
||||
import com.jme3.bullet.control.CharacterControl;
|
||||
import com.jme3.bullet.control.RigidBodyControl;
|
||||
import com.jme3.bullet.util.CollisionShapeFactory;
|
||||
import com.jme3.light.*;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.scene.*;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.shape.*;
|
||||
import com.jme3.shadow.*;
|
||||
import com.jme3.terrain.geomipmap.*;
|
||||
import com.jme3.texture.*;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.SkyFactory;
|
||||
import java.nio.ByteBuffer;
|
||||
import de.blight.common.MapData;
|
||||
import de.blight.common.MapIO;
|
||||
import de.blight.game.config.KeyBindings;
|
||||
@@ -27,6 +32,8 @@ import de.blight.game.control.ThirdPersonCamera;
|
||||
import de.blight.game.state.GrassState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WorldScene extends BaseAppState {
|
||||
|
||||
@@ -73,9 +80,9 @@ public class WorldScene extends BaseAppState {
|
||||
protected void onEnable() {
|
||||
buildLighting();
|
||||
TerrainQuad terrain = buildTerrain();
|
||||
buildDecorations(terrain);
|
||||
|
||||
if (loadedMapData != null) {
|
||||
rootNode.attachChild(buildGebirge(loadedMapData));
|
||||
app.getStateManager().attach(new GrassState(loadedMapData, terrain));
|
||||
}
|
||||
|
||||
@@ -181,23 +188,31 @@ public class WorldScene extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
// Höhe in der Weltmitte als Spawn-Grundlage
|
||||
float centerHeight = heights[(GAME_VERTS / 2) * GAME_VERTS + (GAME_VERTS / 2)];
|
||||
spawnY = centerHeight + 3f;
|
||||
// Spawn über dem höchsten Punkt – Basis-Terrain UND Gebirge-Oberkante
|
||||
float minH = Float.MAX_VALUE, maxH = -Float.MAX_VALUE;
|
||||
for (float h : heights) { if (h < minH) minH = h; if (h > maxH) maxH = h; }
|
||||
float midH = (minH + maxH) * 0.5f;
|
||||
float maxUpperTop = maxH;
|
||||
for (float h : map.upperTop) { if (h > maxUpperTop) maxUpperTop = h; }
|
||||
spawnY = maxUpperTop + 20f;
|
||||
|
||||
TerrainQuad terrain = new TerrainQuad("terrain", 65, GAME_VERTS, heights);
|
||||
terrain.setLocalScale(8f, 1f, 8f); // 512 Zellen * 8 WE = 4096 WE pro Achse
|
||||
terrain.setLocalScale(8f, 1f, 8f);
|
||||
terrain.setShadowMode(RenderQueue.ShadowMode.Receive);
|
||||
applyTerrainMaterial(terrain, map);
|
||||
rootNode.attachChild(terrain);
|
||||
|
||||
applyTerrainMaterial(terrain, 32f);
|
||||
|
||||
RigidBodyControl terrainPhysics = new RigidBodyControl(
|
||||
CollisionShapeFactory.createMeshShape(terrain), 0f);
|
||||
// jBullet subtrahiert midH intern in getVertex() → Physics-Body bei midH
|
||||
// damit Kollisionsfläche und sichtbares Terrain übereinstimmen.
|
||||
HeightfieldCollisionShape hcs = new HeightfieldCollisionShape(
|
||||
heights, terrain.getLocalScale());
|
||||
RigidBodyControl terrainPhysics = new RigidBodyControl(hcs, 0f);
|
||||
terrain.addControl(terrainPhysics);
|
||||
bulletAppState.getPhysicsSpace().add(terrainPhysics);
|
||||
terrainPhysics.setPhysicsLocation(new Vector3f(0f, midH, 0f));
|
||||
|
||||
rootNode.attachChild(terrain);
|
||||
System.out.println("[WorldScene] Karte geladen, Spawn Y=" + spawnY);
|
||||
System.out.println("[WorldScene] Karte geladen, Spawn Y=" + spawnY
|
||||
+ " maxGebirgeH=" + maxUpperTop);
|
||||
return terrain;
|
||||
}
|
||||
|
||||
@@ -223,32 +238,234 @@ public class WorldScene extends BaseAppState {
|
||||
terrain.setLocalScale(0.5f, 0.5f, 0.5f);
|
||||
terrain.setShadowMode(RenderQueue.ShadowMode.Receive);
|
||||
|
||||
applyTerrainMaterial(terrain, 64f);
|
||||
applyTerrainMaterial(terrain, null);
|
||||
|
||||
rootNode.attachChild(terrain);
|
||||
|
||||
RigidBodyControl terrainPhysics = new RigidBodyControl(
|
||||
CollisionShapeFactory.createMeshShape(terrain), 0f);
|
||||
terrain.addControl(terrainPhysics);
|
||||
bulletAppState.getPhysicsSpace().add(terrainPhysics);
|
||||
|
||||
rootNode.attachChild(terrain);
|
||||
return terrain;
|
||||
}
|
||||
|
||||
private void applyTerrainMaterial(TerrainQuad terrain, float texScale) {
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
|
||||
// -----------------------------------------------------------------------
|
||||
// Gebirge (obere Gesteinsschicht)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private Node buildGebirge(MapData map) {
|
||||
final int VERTS = 513;
|
||||
final int CELLS = 512;
|
||||
final float CELL = 8f;
|
||||
final float ORIGIN = -2048f;
|
||||
final int CHUNK = 16;
|
||||
final int NCHUNK = CELLS / CHUNK;
|
||||
|
||||
Node node = new Node("gebirge");
|
||||
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
try {
|
||||
Texture rock = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
|
||||
rock.setWrap(Texture.WrapMode.Repeat);
|
||||
mat.setTexture("DiffuseMap", rock);
|
||||
mat.setColor("Diffuse", new ColorRGBA(0.45f, 0.32f, 0.25f, 1f));
|
||||
} catch (Exception e) {
|
||||
mat.setBoolean("UseMaterialColors", true);
|
||||
mat.setColor("Diffuse", new ColorRGBA(0.18f, 0.12f, 0.08f, 1f));
|
||||
}
|
||||
mat.setColor("Ambient", new ColorRGBA(0.10f, 0.07f, 0.05f, 1f));
|
||||
mat.setColor("Specular", new ColorRGBA(0.06f, 0.05f, 0.04f, 1f));
|
||||
mat.setFloat("Shininess", 6f);
|
||||
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
|
||||
mat.getAdditionalRenderState().setPolyOffset(2f, 2f);
|
||||
|
||||
for (int chunkZ = 0; chunkZ < NCHUNK; chunkZ++) {
|
||||
for (int chunkX = 0; chunkX < NCHUNK; chunkX++) {
|
||||
List<Float> pos = new ArrayList<>();
|
||||
List<Float> norm = new ArrayList<>();
|
||||
List<Float> uv = new ArrayList<>();
|
||||
List<Integer> idx = new ArrayList<>();
|
||||
int vtx = 0;
|
||||
|
||||
for (int lcz = 0; lcz < CHUNK; lcz++) {
|
||||
int cz = chunkZ * CHUNK + lcz;
|
||||
for (int lcx = 0; lcx < CHUNK; lcx++) {
|
||||
int cx = chunkX * CHUNK + lcx;
|
||||
if (map.upperHole[cz * CELLS + cx] != 0) continue;
|
||||
|
||||
float wx0 = ORIGIN + cx * CELL, wx1 = wx0 + CELL;
|
||||
float wz0 = ORIGIN + cz * CELL, wz1 = wz0 + CELL;
|
||||
float t00 = map.upperTop[cz*VERTS+cx], t10 = map.upperTop[cz*VERTS+cx+1];
|
||||
float t01 = map.upperTop[(cz+1)*VERTS+cx], t11 = map.upperTop[(cz+1)*VERTS+cx+1];
|
||||
float b00 = map.upperBottom[cz*VERTS+cx], b10 = map.upperBottom[cz*VERTS+cx+1];
|
||||
float b01 = map.upperBottom[(cz+1)*VERTS+cx], b11 = map.upperBottom[(cz+1)*VERTS+cx+1];
|
||||
|
||||
// UV: eine Textur-Kachel pro Zelle (8 WE)
|
||||
float u0 = cx, u1 = cx+1f, v0 = cz, v1 = cz+1f;
|
||||
|
||||
// Oben (XZ-Ebene)
|
||||
vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx0,t00,wz0, wx1,t10,wz0, wx1,t11,wz1, wx0,t01,wz1, 0,1,0,
|
||||
u0,v0, u1,v0, u1,v1, u0,v1);
|
||||
// Unten
|
||||
vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx0,b00,wz0, wx0,b01,wz1, wx1,b11,wz1, wx1,b10,wz0, 0,-1,0,
|
||||
u0,v0, u0,v1, u1,v1, u1,v0);
|
||||
// Seiten – UV: horizontal = Zellposition, vertikal = Höhe normiert
|
||||
if (gebirgeHole(map,cx,cz-1,CELLS)) vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx1,t10,wz0, wx0,t00,wz0, wx0,b00,wz0, wx1,b10,wz0, 0,0,-1,
|
||||
u1,t10, u0,t00, u0,b00, u1,b10);
|
||||
if (gebirgeHole(map,cx,cz+1,CELLS)) vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx0,t01,wz1, wx1,t11,wz1, wx1,b11,wz1, wx0,b01,wz1, 0,0,1,
|
||||
u0,t01, u1,t11, u1,b11, u0,b01);
|
||||
if (gebirgeHole(map,cx-1,cz,CELLS)) vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx0,t00,wz0, wx0,t01,wz1, wx0,b01,wz1, wx0,b00,wz0, -1,0,0,
|
||||
v0,t00, v1,t01, v1,b01, v0,b00);
|
||||
if (gebirgeHole(map,cx+1,cz,CELLS)) vtx = quad(pos,norm,uv,idx,vtx,
|
||||
wx1,t11,wz1, wx1,t10,wz0, wx1,b10,wz0, wx1,b11,wz1, 1,0,0,
|
||||
v1,t11, v0,t10, v0,b10, v1,b11);
|
||||
}
|
||||
}
|
||||
if (idx.isEmpty()) continue;
|
||||
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(toFA(pos)));
|
||||
mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(toFA(norm)));
|
||||
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(toFA(uv)));
|
||||
mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(toIA(idx)));
|
||||
mesh.updateBound();
|
||||
mesh.updateCounts();
|
||||
|
||||
Geometry geom = new Geometry("gebirge_" + chunkX + "_" + chunkZ, mesh);
|
||||
geom.setMaterial(mat);
|
||||
geom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
|
||||
RigidBodyControl rbc = new RigidBodyControl(
|
||||
CollisionShapeFactory.createMeshShape(geom), 0f);
|
||||
geom.addControl(rbc);
|
||||
bulletAppState.getPhysicsSpace().add(rbc);
|
||||
node.attachChild(geom);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private boolean gebirgeHole(MapData map, int cx, int cz, int CELLS) {
|
||||
if (cx < 0 || cx >= CELLS || cz < 0 || cz >= CELLS) return true;
|
||||
return map.upperHole[cz * CELLS + cx] != 0;
|
||||
}
|
||||
|
||||
/** Fügt ein Quad (2 Dreiecke) mit Position, Normal, UV und Index hinzu. */
|
||||
private int quad(List<Float> pos, List<Float> norm, List<Float> uv, List<Integer> idx, int v,
|
||||
float x0, float y0, float z0, float x1, float y1, float z1,
|
||||
float x2, float y2, float z2, float x3, float y3, float z3,
|
||||
float nx, float ny, float nz,
|
||||
float u0, float v0, float u1, float v1,
|
||||
float u2, float v2, float u3, float v3) {
|
||||
pos.add(x0); pos.add(y0); pos.add(z0);
|
||||
pos.add(x1); pos.add(y1); pos.add(z1);
|
||||
pos.add(x2); pos.add(y2); pos.add(z2);
|
||||
pos.add(x3); pos.add(y3); pos.add(z3);
|
||||
for (int i = 0; i < 4; i++) { norm.add(nx); norm.add(ny); norm.add(nz); }
|
||||
uv.add(u0); uv.add(v0);
|
||||
uv.add(u1); uv.add(v1);
|
||||
uv.add(u2); uv.add(v2);
|
||||
uv.add(u3); uv.add(v3);
|
||||
idx.add(v); idx.add(v+1); idx.add(v+2);
|
||||
idx.add(v); idx.add(v+2); idx.add(v+3);
|
||||
return v + 4;
|
||||
}
|
||||
|
||||
private float[] toFA(List<Float> l) {
|
||||
float[] a = new float[l.size()];
|
||||
for (int i = 0; i < l.size(); i++) a[i] = l.get(i);
|
||||
return a;
|
||||
}
|
||||
|
||||
private int[] toIA(List<Integer> l) {
|
||||
int[] a = new int[l.size()];
|
||||
for (int i = 0; i < l.size(); i++) a[i] = l.get(i);
|
||||
return a;
|
||||
}
|
||||
|
||||
private void applyTerrainMaterial(TerrainQuad terrain, MapData map) {
|
||||
if (map != null) {
|
||||
try {
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
|
||||
|
||||
Texture tex1 = loadTexOrFallback("Textures/Terrain/splat/grass.jpg",
|
||||
new ColorRGBA(0.28f, 0.58f, 0.18f, 1f));
|
||||
Texture tex2 = loadTexOrFallback("Textures/Terrain/splat/road.jpg",
|
||||
new ColorRGBA(0.55f, 0.50f, 0.40f, 1f));
|
||||
Texture tex3 = loadTexOrFallback("Textures/Terrain/splat/Gravel.jpg",
|
||||
new ColorRGBA(0.45f, 0.35f, 0.25f, 1f));
|
||||
tex1.setWrap(Texture.WrapMode.Repeat);
|
||||
tex2.setWrap(Texture.WrapMode.Repeat);
|
||||
tex3.setWrap(Texture.WrapMode.Repeat);
|
||||
mat.setTexture("Tex1", tex1); mat.setFloat("Tex1Scale", 512f);
|
||||
mat.setTexture("Tex2", tex2); mat.setFloat("Tex2Scale", 512f);
|
||||
mat.setTexture("Tex3", tex3); mat.setFloat("Tex3Scale", 512f);
|
||||
|
||||
// Ältere Maps haben splatR=0 → Gras (Tex1) wäre unsichtbar; auf 255 setzen.
|
||||
byte[] splatR = map.splatR;
|
||||
boolean rAllZero = true;
|
||||
for (byte b : splatR) { if (b != 0) { rAllZero = false; break; } }
|
||||
if (rAllZero) {
|
||||
splatR = new byte[splatR.length];
|
||||
java.util.Arrays.fill(splatR, (byte) 255);
|
||||
}
|
||||
|
||||
int sz = MapData.SPLAT_SIZE;
|
||||
ByteBuffer splatBuf = BufferUtils.createByteBuffer(sz * sz * 4);
|
||||
for (int i = 0; i < sz * sz; i++) {
|
||||
splatBuf.put(splatR[i]);
|
||||
splatBuf.put(map.splatG[i]);
|
||||
splatBuf.put(map.splatB[i]);
|
||||
splatBuf.put((byte) 0);
|
||||
}
|
||||
splatBuf.flip();
|
||||
Texture2D splatTex = new Texture2D(new Image(Image.Format.RGBA8, sz, sz, splatBuf));
|
||||
splatTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||
splatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||
splatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||
mat.setTexture("Alpha", splatTex);
|
||||
|
||||
terrain.setMaterial(mat);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
System.err.println("[WorldScene] Splat-Material fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: einfaches Gras-Material
|
||||
try {
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
|
||||
Texture grass = assetManager.loadTexture("Textures/gras.png");
|
||||
grass.setWrap(Texture.WrapMode.Repeat);
|
||||
mat.setTexture("Tex1", grass);
|
||||
mat.setFloat("Tex1Scale", texScale);
|
||||
mat.setTexture("DiffuseMap", grass);
|
||||
mat.setFloat("DiffuseMap_0_scale", 32f);
|
||||
mat.setBoolean("useTriPlanarMapping", false);
|
||||
terrain.setMaterial(mat);
|
||||
} catch (Exception e) {
|
||||
// Fallback: einfarbiges Material
|
||||
mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
mat.setBoolean("UseMaterialColors", true);
|
||||
mat.setColor("Diffuse", new ColorRGBA(0.28f, 0.58f, 0.18f, 1f));
|
||||
mat.setColor("Ambient", new ColorRGBA(0.15f, 0.30f, 0.09f, 1f));
|
||||
terrain.setMaterial(mat);
|
||||
}
|
||||
}
|
||||
|
||||
private Texture loadTexOrFallback(String path, ColorRGBA color) {
|
||||
try {
|
||||
return assetManager.loadTexture(path);
|
||||
} catch (Exception e) {
|
||||
ByteBuffer buf = BufferUtils.createByteBuffer(4);
|
||||
buf.put((byte)(color.r * 255)).put((byte)(color.g * 255))
|
||||
.put((byte)(color.b * 255)).put((byte)(color.a * 255));
|
||||
buf.flip();
|
||||
return new Texture2D(new Image(Image.Format.RGBA8, 1, 1, buf));
|
||||
}
|
||||
terrain.setMaterial(mat);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user