refactor world logic
This commit is contained in:
@@ -49,6 +49,9 @@ public class GameScreen implements Screen {
|
|||||||
private TextureRegion frameRegion;
|
private TextureRegion frameRegion;
|
||||||
private SpriteBatch screenBatch;
|
private SpriteBatch screenBatch;
|
||||||
|
|
||||||
|
// world
|
||||||
|
private ModelLibrary modelLibrary;
|
||||||
|
|
||||||
|
|
||||||
private static final int RETRO_WIDTH = 320;
|
private static final int RETRO_WIDTH = 320;
|
||||||
private static final int RETRO_HEIGHT = 240;
|
private static final int RETRO_HEIGHT = 240;
|
||||||
@@ -94,11 +97,14 @@ public class GameScreen implements Screen {
|
|||||||
// create the cycle controller
|
// create the cycle controller
|
||||||
dayNightCycle = new DayNightCycle(shadowLight, ambientLight);
|
dayNightCycle = new DayNightCycle(shadowLight, ambientLight);
|
||||||
// optional: start at morning/noon/etc
|
// optional: start at morning/noon/etc
|
||||||
dayNightCycle.setTimeOfDay(0.25f); // sunrise
|
dayNightCycle.setTimeOfDay(0.4f); // good sun
|
||||||
|
dayNightCycle.setAxialTilt(30f);
|
||||||
|
dayNightCycle.setDayLength(60f * 5f); // 20 minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initWorld() {
|
private void initWorld() {
|
||||||
world = new World3D();
|
modelLibrary = new ModelLibrary();
|
||||||
|
world = new World3D(modelLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMenus() {
|
private void initMenus() {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.math.Vector3;
|
||||||
|
|
||||||
|
public class Collider {
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
NONE,
|
||||||
|
BOX // axis-aligned box
|
||||||
|
// later: SPHERE, CAPSULE, MESH, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type type = Type.NONE;
|
||||||
|
|
||||||
|
// Local-space center + extents relative to ModelInstance transform
|
||||||
|
public final Vector3 center = new Vector3(0f, 0f, 0f);
|
||||||
|
public final Vector3 halfExtents = new Vector3(1f, 1f, 1f);
|
||||||
|
|
||||||
|
public static Collider none() {
|
||||||
|
return new Collider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collider box(float hx, float hy, float hz) {
|
||||||
|
Collider c = new Collider();
|
||||||
|
c.type = Type.BOX;
|
||||||
|
c.halfExtents.set(hx, hy, hz);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ public class DayNightCycle {
|
|||||||
|
|
||||||
// Rotate around Z so the sun path is tilted relative to world up.
|
// Rotate around Z so the sun path is tilted relative to world up.
|
||||||
private final Vector3 tiltAxis = new Vector3(0f, 0f, 1f).nor();
|
private final Vector3 tiltAxis = new Vector3(0f, 0f, 1f).nor();
|
||||||
public float axialTiltDeg = 30f; // tweak to taste
|
private float axialTilt = 30f; // in degrees
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If false (default), simple equator-style model:
|
* If false (default), simple equator-style model:
|
||||||
@@ -43,7 +43,7 @@ public class DayNightCycle {
|
|||||||
* If true, brightness and day length depend on the final tilted direction,
|
* If true, brightness and day length depend on the final tilted direction,
|
||||||
* so axialTiltDeg affects how long days are and how high the sun gets.
|
* so axialTiltDeg affects how long days are and how high the sun gets.
|
||||||
*/
|
*/
|
||||||
public boolean seasonal = false;
|
private boolean seasonal = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0–1: 0 = midnight, 0.25 = sunrise, 0.5 = noon, 0.75 = sunset.
|
* 0–1: 0 = midnight, 0.25 = sunrise, 0.5 = noon, 0.75 = sunset.
|
||||||
@@ -51,7 +51,7 @@ public class DayNightCycle {
|
|||||||
private float timeOfDay = 0.25f;
|
private float timeOfDay = 0.25f;
|
||||||
|
|
||||||
/** Seconds for one full 24h cycle. */
|
/** Seconds for one full 24h cycle. */
|
||||||
public float dayLengthSeconds = 60 * 5f;
|
private float dayLengthSeconds = 60 * 5f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Daylight / altitude factor in [0, 1].
|
* Daylight / altitude factor in [0, 1].
|
||||||
@@ -98,7 +98,7 @@ public class DayNightCycle {
|
|||||||
|
|
||||||
// Visual direction with axial tilt
|
// Visual direction with axial tilt
|
||||||
tmpDir.set(baseX, baseY, baseZ).nor();
|
tmpDir.set(baseX, baseY, baseZ).nor();
|
||||||
tmpDir.rotate(tiltAxis, axialTiltDeg);
|
tmpDir.rotate(tiltAxis, axialTilt);
|
||||||
sunDirection.set(tmpDir);
|
sunDirection.set(tmpDir);
|
||||||
|
|
||||||
// Sun color / intensity
|
// Sun color / intensity
|
||||||
@@ -116,7 +116,7 @@ public class DayNightCycle {
|
|||||||
// === Seasonal / physical mode ===
|
// === Seasonal / physical mode ===
|
||||||
|
|
||||||
tmpDir.set(baseX, baseY, baseZ).nor();
|
tmpDir.set(baseX, baseY, baseZ).nor();
|
||||||
tmpDir.rotate(tiltAxis, axialTiltDeg);
|
tmpDir.rotate(tiltAxis, axialTilt);
|
||||||
sunDirection.set(tmpDir);
|
sunDirection.set(tmpDir);
|
||||||
|
|
||||||
// True altitude from final direction: 0..1
|
// True altitude from final direction: 0..1
|
||||||
@@ -166,4 +166,24 @@ public class DayNightCycle {
|
|||||||
public boolean isSunAboveHorizon() {
|
public boolean isSunAboveHorizon() {
|
||||||
return sunAltitude > 0f; // strictly > 0; you can use >= 0.01f if you want a tiny cutoff
|
return sunAltitude > 0f; // strictly > 0; you can use >= 0.01f if you want a tiny cutoff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getAxialTilt()
|
||||||
|
{
|
||||||
|
return axialTilt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAxialTilt(float degrees)
|
||||||
|
{
|
||||||
|
axialTilt = degrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDayLength()
|
||||||
|
{
|
||||||
|
return dayLengthSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDayLength(float seconds)
|
||||||
|
{
|
||||||
|
dayLengthSeconds = seconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
|
import com.badlogic.gdx.graphics.VertexAttributes;
|
||||||
|
import com.badlogic.gdx.graphics.g3d.*;
|
||||||
|
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
|
||||||
|
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
|
||||||
|
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
|
||||||
|
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
|
||||||
|
|
||||||
|
public class ModelLibrary {
|
||||||
|
|
||||||
|
private final ModelBuilder builder = new ModelBuilder();
|
||||||
|
|
||||||
|
public final Texture groundTexture;
|
||||||
|
public final Model groundModel;
|
||||||
|
public final Model unitCubeModel;
|
||||||
|
|
||||||
|
public ModelLibrary() {
|
||||||
|
// ground texture
|
||||||
|
groundTexture = new Texture(Gdx.files.internal("textures/paving.png"));
|
||||||
|
groundTexture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
||||||
|
groundTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
|
||||||
|
|
||||||
|
// ground model (32x32 plane)
|
||||||
|
float halfSize = 32f;
|
||||||
|
float tileScale = 16f;
|
||||||
|
|
||||||
|
builder.begin();
|
||||||
|
Material groundMat = new Material(
|
||||||
|
ColorAttribute.createDiffuse(1f, 1f, 1f, 1f),
|
||||||
|
TextureAttribute.createDiffuse(groundTexture)
|
||||||
|
);
|
||||||
|
|
||||||
|
MeshPartBuilder mpb = builder.part(
|
||||||
|
"ground",
|
||||||
|
GL20.GL_TRIANGLES,
|
||||||
|
VertexAttributes.Usage.Position
|
||||||
|
| VertexAttributes.Usage.Normal
|
||||||
|
| VertexAttributes.Usage.TextureCoordinates,
|
||||||
|
groundMat
|
||||||
|
);
|
||||||
|
mpb.setUVRange(0f, 0f, tileScale, tileScale);
|
||||||
|
mpb.rect(
|
||||||
|
-halfSize, 0f, halfSize,
|
||||||
|
halfSize, 0f, halfSize,
|
||||||
|
halfSize, 0f, -halfSize,
|
||||||
|
-halfSize, 0f, -halfSize,
|
||||||
|
0f, 1f, 0f
|
||||||
|
);
|
||||||
|
groundModel = builder.end();
|
||||||
|
|
||||||
|
// a generic 2x2x2 cube model (can be reused for buildings, crates, etc.)
|
||||||
|
unitCubeModel = builder.createBox(
|
||||||
|
2f, 2f, 2f,
|
||||||
|
new Material(ColorAttribute.createDiffuse(1f, 1f, 1f, 1f)),
|
||||||
|
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
groundModel.dispose();
|
||||||
|
unitCubeModel.dispose();
|
||||||
|
groundTexture.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
package wtf.beatrice.retrorender.engine;
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.graphics.g3d.Environment;
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.g3d.ModelBatch;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.math.Matrix4;
|
||||||
import com.badlogic.gdx.graphics.VertexAttributes;
|
|
||||||
import com.badlogic.gdx.graphics.g3d.*;
|
|
||||||
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
|
|
||||||
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
|
|
||||||
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
|
|
||||||
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
|
|
||||||
import com.badlogic.gdx.math.Vector3;
|
import com.badlogic.gdx.math.Vector3;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -16,134 +10,121 @@ import java.util.List;
|
|||||||
|
|
||||||
public class World3D {
|
public class World3D {
|
||||||
|
|
||||||
// textures
|
private final ModelLibrary models;
|
||||||
private final Texture groundTexture;
|
|
||||||
|
|
||||||
// models (base mesh)
|
private final WorldObject ground;
|
||||||
private final Model groundModel;
|
private final List<WorldObject> objects = new ArrayList<>();
|
||||||
private final Model cubeModel;
|
|
||||||
|
|
||||||
// instances
|
|
||||||
private final ModelInstance groundInstance;
|
|
||||||
private final List<ModelInstance> cubeInstances = new ArrayList<>();
|
|
||||||
|
|
||||||
public World3D() {
|
private final Vector3 tmpWorld = new Vector3();
|
||||||
ModelBuilder modelBuilder = new ModelBuilder();
|
private final Vector3 tmpFeet = new Vector3();
|
||||||
|
private final Vector3 tmpHead = new Vector3();
|
||||||
|
private final Vector3 tmpLocalFeet = new Vector3();
|
||||||
|
private final Vector3 tmpLocalHead = new Vector3();
|
||||||
|
|
||||||
// --- load floor texture
|
private final Matrix4 tmpMat = new Matrix4();
|
||||||
groundTexture = new Texture(Gdx.files.internal("textures/paving.png"));
|
private final Matrix4 tmpInv = new Matrix4();
|
||||||
groundTexture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
|
|
||||||
groundTexture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
|
|
||||||
|
|
||||||
// --- create ground material
|
private final Vector3 tmp = new Vector3();
|
||||||
Material groundMat = new Material(
|
|
||||||
ColorAttribute.createDiffuse(1f, 1f, 1f, 1f),
|
|
||||||
TextureAttribute.createDiffuse(groundTexture)
|
|
||||||
);
|
|
||||||
|
|
||||||
// plane size (halfSize in X/Z)
|
public World3D(ModelLibrary models) {
|
||||||
float halfSize = 32f;
|
this.models = models;
|
||||||
|
|
||||||
// times the texture repeats across the whole plane
|
// --- ground ---
|
||||||
float tileScale = 16f;
|
ground = new WorldObject(
|
||||||
|
|
||||||
modelBuilder.begin();
|
|
||||||
|
|
||||||
MeshPartBuilder mpb = modelBuilder.part(
|
|
||||||
"ground",
|
"ground",
|
||||||
GL20.GL_TRIANGLES,
|
new com.badlogic.gdx.graphics.g3d.ModelInstance(models.groundModel),
|
||||||
VertexAttributes.Usage.Position
|
Collider.none() // treat plane as baseHeight = 0, not as a collider
|
||||||
| VertexAttributes.Usage.Normal
|
|
||||||
| VertexAttributes.Usage.TextureCoordinates,
|
|
||||||
groundMat
|
|
||||||
);
|
);
|
||||||
|
ground.staticObject = true;
|
||||||
|
|
||||||
// set UV range so texture repeats tileScale times
|
// --- some cubes (temporary test geometry) ---
|
||||||
mpb.setUVRange(0f, 0f, tileScale, tileScale);
|
addCube("center", 0f, 1f, 0f);
|
||||||
|
addCube("cube-ne", 8f, 1f, 8f);
|
||||||
|
addCube("cube-nw", -8f, 1f, 8f);
|
||||||
|
addCube("cube-se", 8f, 1f, -8f);
|
||||||
|
addCube("cube-sw", -8f, 1f, -8f);
|
||||||
|
|
||||||
// build the quad
|
WorldObject pillar = addCube("pillar", 0f, 3f, -10f);
|
||||||
mpb.rect(
|
// scale pillar’s transform (purely visual)
|
||||||
-halfSize, 0f, halfSize, // top-left
|
pillar.instance.transform
|
||||||
halfSize, 0f, halfSize, // top-right
|
|
||||||
halfSize, 0f, -halfSize, // bottom-right
|
|
||||||
-halfSize, 0f, -halfSize, // bottom-left
|
|
||||||
0f, 1f, 0f // normal up
|
|
||||||
);
|
|
||||||
|
|
||||||
groundModel = modelBuilder.end();
|
|
||||||
groundInstance = new ModelInstance(groundModel);
|
|
||||||
|
|
||||||
// --- create reusable cube model
|
|
||||||
cubeModel = modelBuilder.createBox(
|
|
||||||
2f, 2f, 2f,
|
|
||||||
new Material(ColorAttribute.createDiffuse(1f, 1f, 1f, 1f)),
|
|
||||||
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- place random cubes in the world
|
|
||||||
// center
|
|
||||||
cubeInstances.add(new ModelInstance(cubeModel)); // at origin
|
|
||||||
|
|
||||||
// four around
|
|
||||||
cubeInstances.add(new ModelInstance(cubeModel));
|
|
||||||
cubeInstances.get(1).transform.setToTranslation(8f, 1f, 8f);
|
|
||||||
|
|
||||||
cubeInstances.add(new ModelInstance(cubeModel));
|
|
||||||
cubeInstances.get(2).transform.setToTranslation(-8f, 1f, 8f);
|
|
||||||
|
|
||||||
cubeInstances.add(new ModelInstance(cubeModel));
|
|
||||||
cubeInstances.get(3).transform.setToTranslation(8f, 1f, -8f);
|
|
||||||
|
|
||||||
cubeInstances.add(new ModelInstance(cubeModel));
|
|
||||||
cubeInstances.get(4).transform.setToTranslation(-8f, 1f, -8f);
|
|
||||||
|
|
||||||
// one tall pillar in front of you
|
|
||||||
ModelInstance pillar = new ModelInstance(cubeModel);
|
|
||||||
pillar.transform
|
|
||||||
.setToScaling(1f, 3f, 1f)
|
.setToScaling(1f, 3f, 1f)
|
||||||
.translate(0f, 3f, -10f);
|
.translate(0f, 3f, -10f);
|
||||||
cubeInstances.add(pillar);
|
// collider is still 1x1x1, you can adjust collider.halfExtents here if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldObject addCube(String id, float x, float y, float z) {
|
||||||
|
var instance = new com.badlogic.gdx.graphics.g3d.ModelInstance(models.unitCubeModel);
|
||||||
|
instance.transform.setToTranslation(x, y, z);
|
||||||
|
|
||||||
|
// cube is 2x2x2, so half extents = 1
|
||||||
|
Collider collider = Collider.box(1f, 1f, 1f);
|
||||||
|
|
||||||
|
WorldObject obj = new WorldObject(id, instance, collider);
|
||||||
|
obj.staticObject = true;
|
||||||
|
objects.add(obj);
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(float delta) {
|
public void update(float delta) {
|
||||||
// rotate cubes around y axis
|
// here you can update objects that animate / move
|
||||||
for (ModelInstance instance : cubeInstances) {
|
for (WorldObject obj : objects) {
|
||||||
instance.transform.rotate(Vector3.Y, 20f * delta);
|
obj.update(delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(ModelBatch batch, Environment environment) {
|
public void render(ModelBatch batch, Environment environment) {
|
||||||
// draw ground
|
// draw ground
|
||||||
batch.render(groundInstance, environment);
|
batch.render(ground.instance, environment);
|
||||||
// draw cubes
|
|
||||||
for (ModelInstance instance : cubeInstances) {
|
// draw objects
|
||||||
batch.render(instance, environment);
|
for (WorldObject obj : objects) {
|
||||||
|
batch.render(obj.instance, environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ground height (y) at a given XZ position.
|
* Returns the ground height (y) at a given XZ position.
|
||||||
* Starts from baseHeight (e.g. 0) and checks if you're standing on top of any cube.
|
* Starts from baseHeight (e.g. 0) and checks if you're standing on top of any BOX collider.
|
||||||
*/
|
*/
|
||||||
public float getGroundHeightAt(float x, float z, float baseHeight) {
|
public float getGroundHeightAt(float x, float z, float baseHeight) {
|
||||||
float maxY = baseHeight; // ground plane at y = 0
|
float maxY = baseHeight; // plane at y = 0
|
||||||
|
|
||||||
// a cube is 2 units tall, from y = 0 to y = 2
|
for (WorldObject obj : objects) {
|
||||||
float cubeHalfSize = 1f;
|
if (obj.collider.type != Collider.Type.BOX) continue;
|
||||||
float cubeTopY = 2f;
|
|
||||||
|
|
||||||
Vector3 tmp = new Vector3();
|
Collider col = obj.collider;
|
||||||
for (ModelInstance cube : cubeInstances) {
|
|
||||||
cube.transform.getTranslation(tmp);
|
|
||||||
|
|
||||||
float minX = tmp.x - cubeHalfSize;
|
// Inverse transform: world → object local
|
||||||
float maxX = tmp.x + cubeHalfSize;
|
tmpInv.set(obj.instance.transform);
|
||||||
float minZ = tmp.z - cubeHalfSize;
|
tmpInv.inv();
|
||||||
float maxZ = tmp.z + cubeHalfSize;
|
|
||||||
|
|
||||||
if (x >= minX && x <= maxX && z >= minZ && z <= maxZ) {
|
// Player XZ at ground level in world space
|
||||||
if (cubeTopY > maxY) {
|
tmpWorld.set(x, 0f, z);
|
||||||
maxY = cubeTopY;
|
tmpWorld.mul(tmpInv); // now in local space
|
||||||
}
|
|
||||||
|
float px = tmpWorld.x;
|
||||||
|
float pz = tmpWorld.z;
|
||||||
|
|
||||||
|
float minX = col.center.x - col.halfExtents.x;
|
||||||
|
float maxX = col.center.x + col.halfExtents.x;
|
||||||
|
float minZ = col.center.z - col.halfExtents.z;
|
||||||
|
float maxZ = col.center.z + col.halfExtents.z;
|
||||||
|
|
||||||
|
// inside horizontal footprint in *local* space
|
||||||
|
if (px < minX || px > maxX || pz < minZ || pz > maxZ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local top Y of the box
|
||||||
|
float localTopY = col.center.y + col.halfExtents.y;
|
||||||
|
|
||||||
|
// Convert that top point back to world space
|
||||||
|
tmpWorld.set(col.center.x, localTopY, col.center.z);
|
||||||
|
tmpWorld.mul(obj.instance.transform);
|
||||||
|
|
||||||
|
if (tmpWorld.y > maxY) {
|
||||||
|
maxY = tmpWorld.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,36 +132,50 @@ public class World3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple horizontal collision check between a player and cube sides.
|
* Simple horizontal collision check between a player and BOX colliders.
|
||||||
* Player is approximated as a capsule: radius in XZ, height = eyeHeight.
|
* Player is approximated as a capsule: radius in XZ, height = eyeHeight.
|
||||||
*/
|
*/
|
||||||
public boolean collidesAt(float x, float y, float z, float radius, float eyeHeight) {
|
public boolean collidesAt(float x, float y, float z, float radius, float eyeHeight) {
|
||||||
float feetY = y - eyeHeight;
|
float feetY = y - eyeHeight;
|
||||||
float headY = y;
|
float headY = y;
|
||||||
|
|
||||||
float cubeHalfSize = 1f;
|
for (WorldObject obj : objects) {
|
||||||
float cubeBottomY = 0f;
|
if (obj.collider.type != Collider.Type.BOX) continue;
|
||||||
float cubeTopY = 2f;
|
|
||||||
|
|
||||||
Vector3 tmp = new Vector3();
|
Collider col = obj.collider;
|
||||||
for (ModelInstance cube : cubeInstances) {
|
|
||||||
cube.transform.getTranslation(tmp);
|
|
||||||
|
|
||||||
float minX = tmp.x - cubeHalfSize;
|
// world → local
|
||||||
float maxX = tmp.x + cubeHalfSize;
|
tmpInv.set(obj.instance.transform);
|
||||||
float minZ = tmp.z - cubeHalfSize;
|
tmpInv.inv();
|
||||||
float maxZ = tmp.z + cubeHalfSize;
|
|
||||||
|
|
||||||
// no vertical overlap -> ignore
|
tmpFeet.set(x, feetY, z).mul(tmpInv);
|
||||||
if (headY <= cubeBottomY || feetY >= cubeTopY) {
|
tmpHead.set(x, headY, z).mul(tmpInv);
|
||||||
|
|
||||||
|
// For horizontal test, use the "average" x/z of feet/head
|
||||||
|
float px = 0.5f * (tmpFeet.x + tmpHead.x);
|
||||||
|
float pz = 0.5f * (tmpFeet.z + tmpHead.z);
|
||||||
|
|
||||||
|
float boxMinY = col.center.y - col.halfExtents.y;
|
||||||
|
float boxMaxY = col.center.y + col.halfExtents.y;
|
||||||
|
|
||||||
|
// vertical overlap in local space
|
||||||
|
if (tmpHead.y <= boxMinY || tmpFeet.y >= boxMaxY) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// circle (player) vs AABB (cube) in XZ
|
float minX = col.center.x - col.halfExtents.x;
|
||||||
boolean overlapX = (x + radius > minX) && (x - radius < maxX);
|
float maxX = col.center.x + col.halfExtents.x;
|
||||||
boolean overlapZ = (z + radius > minZ) && (z - radius < maxZ);
|
float minZ = col.center.z - col.halfExtents.z;
|
||||||
|
float maxZ = col.center.z + col.halfExtents.z;
|
||||||
|
|
||||||
if (overlapX && overlapZ) {
|
// circle (player) vs AABB in local XZ
|
||||||
|
float closestX = Math.max(minX, Math.min(px, maxX));
|
||||||
|
float closestZ = Math.max(minZ, Math.min(pz, maxZ));
|
||||||
|
|
||||||
|
float dx = px - closestX;
|
||||||
|
float dz = pz - closestZ;
|
||||||
|
|
||||||
|
if (dx * dx + dz * dz <= radius * radius) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,8 +184,6 @@ public class World3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
groundModel.dispose();
|
models.dispose(); // or let GameScreen own/dispose the library
|
||||||
cubeModel.dispose();
|
|
||||||
groundTexture.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package wtf.beatrice.retrorender.engine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.g3d.ModelInstance;
|
||||||
|
import com.badlogic.gdx.math.Vector3;
|
||||||
|
|
||||||
|
public class WorldObject {
|
||||||
|
|
||||||
|
public final String id;
|
||||||
|
public final ModelInstance instance;
|
||||||
|
public final Collider collider;
|
||||||
|
public boolean staticObject = true;
|
||||||
|
public boolean castsShadow = true;
|
||||||
|
|
||||||
|
// Cached translation for faster queries
|
||||||
|
private final Vector3 tmpPos = new Vector3();
|
||||||
|
|
||||||
|
public WorldObject(String id, ModelInstance instance, Collider collider) {
|
||||||
|
this.id = id;
|
||||||
|
this.instance = instance;
|
||||||
|
this.collider = collider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(float delta) {
|
||||||
|
instance.transform.rotate(Vector3.Y, delta * 20f);
|
||||||
|
// default: do nothing
|
||||||
|
// you can subclass or add strategies later for rotating, animating, AI, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 getPosition(Vector3 out) {
|
||||||
|
instance.transform.getTranslation(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 getPosition() {
|
||||||
|
return getPosition(tmpPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user