Physics, toggle the collision mesh preview with F2, collision is hella buggy

This commit is contained in:
Halbear 2026-07-01 21:38:10 +01:00
parent 6b51c9b680
commit c02b9ed2ca
104 changed files with 1401 additions and 1720 deletions

View file

@ -1,6 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Logic.Collision;
import org.joml.Vector3f;
import org.joml.primitives.AABBf;
import java.util.ArrayList;
import java.util.HashMap;
@ -10,41 +11,77 @@ import java.util.Map;
public class CollisionManager {
public static Map<String,CollisionMesh> CollisionMeshes = new HashMap<>();
public static Map<String,List<String>> CollisionChunks = new HashMap<>();
public static List<String> AlwaysCheckMeshes = new ArrayList<>();
public static int ChunkWidth = 64;
public static void AddNewMesh(CollisionMesh mesh){
CollisionMeshes.put(mesh.ID, mesh);
Vector3f WorldPos = new Vector3f(mesh.WorldPosition).div(ChunkWidth);
WorldPos.x = (int)Math.floor(WorldPos.x);
WorldPos.y = (int)Math.floor(WorldPos.y);
WorldPos.z = (int)Math.floor(WorldPos.z);
String ChunkPosition = WorldPos.x + ":" + WorldPos.y + ":" + WorldPos.z;
if(!CollisionChunks.containsKey(ChunkPosition)){
List<String> newStrList = new ArrayList<>();
CollisionChunks.put(ChunkPosition, newStrList);
AABBf Bounds = mesh.GetVoxelCollider();
if(Bounds != null) {
Vector3f LowestPoints = new Vector3f(Bounds.minX, Bounds.minY, Bounds.minZ).div(ChunkWidth);
LowestPoints.x = (int) Math.floor(LowestPoints.x);
LowestPoints.y = (int) Math.floor(LowestPoints.y);
LowestPoints.z = (int) Math.floor(LowestPoints.z);
Vector3f LargestPoints = new Vector3f(Bounds.maxX, Bounds.maxY, Bounds.maxZ).div(ChunkWidth);
LargestPoints.x = (int) Math.ceil(LargestPoints.x);
LargestPoints.y = (int) Math.ceil(LargestPoints.y);
LargestPoints.z = (int) Math.ceil(LargestPoints.z);
for (int x = (int) LowestPoints.x; x < LargestPoints.x + 1; x++) {
for (int y = (int) LowestPoints.y; y < LargestPoints.y + 1; y++) {
for (int z = (int) LowestPoints.z; z < LargestPoints.z + 1; z++) {
String ChunkPosition = x + ":" + y + ":" + z;
if (!CollisionChunks.containsKey(ChunkPosition)) {
List<String> newStrList = new ArrayList<>();
CollisionChunks.put(ChunkPosition, newStrList);
}
CollisionChunks.get(ChunkPosition).add(mesh.ID);
}
}
}
} else{
AlwaysCheckMeshes.add(mesh.ID);
}
CollisionChunks.get(ChunkPosition).add(mesh.ID);
}
public static List<String> GetCollisionMeshesAtChunk(String Chunk){
return CollisionChunks.get(Chunk) == null ? new ArrayList<>() : CollisionChunks.get(Chunk);
}
public static List<CollisionMesh> GetAllCollisionMeshes(){return CollisionMeshes.values().stream().toList();}
public static void UpdateCollisionChunks(){
public static synchronized void UpdateCollisionChunks(){
Map<String,List<String>> newMap = new HashMap<>();
List<CollisionMesh> meshes = GetAllCollisionMeshes();
List<String> AlwaysShowMesh = new ArrayList<>();
for(int i = 0; i < meshes.size(); i++){
Vector3f WorldPos = new Vector3f(meshes.get(i).WorldPosition).div(ChunkWidth);
WorldPos.x = (int)Math.floor(WorldPos.x);
WorldPos.y = (int)Math.floor(WorldPos.y);
WorldPos.z = (int)Math.floor(WorldPos.z);
String ChunkPosition = WorldPos.x + ":" + WorldPos.y + ":" + WorldPos.z;
if(!newMap.containsKey(ChunkPosition)){
List<String> newStrList = new ArrayList<>();
newMap.put(ChunkPosition, newStrList);
CollisionMesh mesh = meshes.get(i);
AABBf Bounds = mesh.GetVoxelCollider();
if(Bounds != null) {
Vector3f LowestPoints = new Vector3f(Bounds.minX, Bounds.minY, Bounds.minZ).div(ChunkWidth);
LowestPoints.x = (int) Math.floor(LowestPoints.x);
LowestPoints.y = (int) Math.floor(LowestPoints.y);
LowestPoints.z = (int) Math.floor(LowestPoints.z);
Vector3f LargestPoints = new Vector3f(Bounds.maxX, Bounds.maxY, Bounds.maxZ).div(ChunkWidth);
LargestPoints.x = (int) Math.ceil(LargestPoints.x);
LargestPoints.y = (int) Math.ceil(LargestPoints.y);
LargestPoints.z = (int) Math.ceil(LargestPoints.z);
for (int x = (int) LowestPoints.x; x < LargestPoints.x + 1; x++) {
for (int y = (int) LowestPoints.y; y < LargestPoints.y + 1; y++) {
for (int z = (int) LowestPoints.z; z < LargestPoints.z + 1; z++) {
String ChunkPosition = x + ":" + y + ":" + z;
if (!CollisionChunks.containsKey(ChunkPosition)) {
List<String> newStrList = new ArrayList<>();
CollisionChunks.put(ChunkPosition, newStrList);
}
CollisionChunks.get(ChunkPosition).add(mesh.ID);
}
}
}
} else{
AlwaysShowMesh.add(mesh.ID);
}
newMap.get(ChunkPosition).add(meshes.get(i).ID);
}
CollisionChunks.clear();
CollisionChunks.putAll(newMap);
AlwaysCheckMeshes = (AlwaysShowMesh);
CollisionChunks = (newMap);
}
}

View file

@ -1,37 +1,57 @@
package net.halbear.Terrain4J.EngineCore.Logic.Collision;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VertexBufferStructure;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MeshData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanMesh;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanModel;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.joml.Intersectionf;
import org.joml.Quaternionf;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.system.MemoryUtil;
import org.joml.primitives.AABBf;
import java.io.*;
import java.nio.FloatBuffer;
import java.nio.LongBuffer;
import java.util.*;
public class CollisionMesh {
public final List<CollisionPoly> Polygons;
public Vector3f TopLeftFront = new Vector3f(0,0,0);
public Vector3f BottomRightBack = new Vector3f(0,0,0);
public Vector3f TopRightFront = new Vector3f(0,0,0);
public Vector3f BottomLeftBack = new Vector3f(0,0,0);
public Vector3f WorldPosition = new Vector3f(0,0,0);
public Quaternionf Rotation;
public Vector3f Scale;
public Quaternionf Rotation = new Quaternionf();
public Vector3f Scale = new Vector3f(1f,1f,1f);
public final String ID;
public CollisionMesh(String ID){
public final String ParentID;
public AABBf Voxel;
public CollisionMesh(String ID, String ActorID){
this.ID = ID;
if(ActorID != "NULL") {
Scale = new Vector3f(Actor.Actors.get(ActorID).GetScale());
Rotation = new Quaternionf(Actor.Actors.get(ActorID).GetRotation());
}
ParentID = ActorID;
Polygons = new ArrayList<>();
}
public void RegisterMesh(){
CollisionManager.AddNewMesh(this);
}
public AABBf GetVoxelCollider(){
if(TopRightFront == null || BottomLeftBack == null) return null;
if(Voxel == null){
Vector3f TLF = new Vector3f(TopRightFront);
Vector3f BRB = new Vector3f(BottomLeftBack);
TLF = TLF.mul(Scale).rotate(Rotation).add(WorldPosition);
BRB = BRB.mul(Scale).rotate(Rotation).add(WorldPosition);
AABBf ThisVoxel = new AABBf(BRB,TLF);
this.Voxel = ThisVoxel;
}
return Voxel;
}
public CollisionMesh SetWorldPosition(Vector3f newPos){
this.WorldPosition = new Vector3f(newPos);
return this;
@ -107,37 +127,71 @@ public class CollisionMesh {
}
public void CheckAABbounds(Vector3f v1, Vector3f v2, Vector3f v3){
float Bottom = Math.min(Math.min(v3.y, v1.y), Math.min(v3.y, v2.y));
float Left = Math.min(Math.min(v3.x, v1.x), Math.min(v3.x, v2.x));
float back = Math.min(Math.min(v3.z, v1.z), Math.min(v3.z, v2.z));
float Top = Math.max(Math.max(v3.y, v1.y), Math.max(v3.y, v2.y));
float Right = Math.max(Math.max(v3.x, v1.x), Math.max(v3.x, v2.x));
float Front = Math.max(Math.max(v3.z, v1.z), Math.max(v3.z, v2.z));
TopLeftFront.x = Math.min(TopLeftFront.x, Left);
TopLeftFront.y = Math.max(TopLeftFront.y, Top);
TopLeftFront.z = Math.max(TopLeftFront.z, Front);
BottomRightBack.x = Math.max(BottomRightBack.x, Right);
BottomRightBack.y = Math.min(BottomRightBack.y, Bottom);
BottomRightBack.z = Math.min(BottomRightBack.z, back);
float localMinX = Math.min(v1.x, Math.min(v2.x, v3.x));
float localMinY = Math.min(v1.y, Math.min(v2.y, v3.y));
float localMinZ = Math.min(v1.z, Math.min(v2.z, v3.z));
float localMaxX = Math.max(v1.x, Math.max(v2.x, v3.x));
float localMaxY = Math.max(v1.y, Math.max(v2.y, v3.y));
float localMaxZ = Math.max(v1.z, Math.max(v2.z, v3.z));
TopRightFront.x = Math.max(TopRightFront.x, localMaxX);
TopRightFront.y = Math.max(TopRightFront.y, localMaxY);
TopRightFront.z = Math.max(TopRightFront.z, localMaxZ);
BottomLeftBack.x = Math.min(BottomLeftBack.x, localMinX);
BottomLeftBack.y = Math.min(BottomLeftBack.y, localMinY);
BottomLeftBack.z = Math.min(BottomLeftBack.z, localMinZ);
}
public void AddNewCollisionPoly(Vector3f v1, Vector3f v2, Vector3f v3){
// if(Actor.Actors.get(ParentID) != null) {
// PhysicsController physicsController = Actor.Actors.get(ParentID).GetPhysicsController();
// if (physicsController != null) {
// physicsController.AddCollisionSensor(v1);
// physicsController.AddCollisionSensor(v2);
// physicsController.AddCollisionSensor(v3);
// }
// }
CollisionPoly newPoly = new CollisionPoly(v1,v2,v3,new Vector3f(0,0,0));
newPoly.CalculateCentre();
Polygons.add(newPoly);
CheckAABbounds(v1,v2,v3);
}
public boolean CheckAABBCollisionAgainstAABB(Vector3f inTopLeftFront, Vector3f inBottomRightBack){
AABBf OtherVoxel = new AABBf(inBottomRightBack,inTopLeftFront);
Vector3f TLF = new Vector3f(TopRightFront);
Vector3f BRB = new Vector3f(BottomLeftBack);
TLF = TLF.mul(Scale).rotate(Rotation).add(WorldPosition);
BRB = BRB.mul(Scale).rotate(Rotation).add(WorldPosition);
AABBf ThisVoxel = new AABBf(BRB,TLF);
this.Voxel = ThisVoxel;
return ThisVoxel.intersectsAABB(OtherVoxel);
}
public boolean CheckAABBCollisionAgainstAABB(AABBf OtherVoxel){
Vector3f TLF = new Vector3f(TopRightFront);
Vector3f BRB = new Vector3f(BottomLeftBack);
TLF = TLF.mul(Scale).rotate(Rotation).add(WorldPosition);
BRB = BRB.mul(Scale).rotate(Rotation).add(WorldPosition);
AABBf ThisVoxel = new AABBf(BRB,TLF);
this.Voxel = ThisVoxel;
return ThisVoxel.intersectsAABB(OtherVoxel);
}
public boolean CheckAABCollision(Vector3f Origin, Vector3f Sensor){
Vector3f LineDirection = new Vector3f(Sensor).sub(Origin);
Vector2f Result = new Vector2f();
return Intersectionf.intersectRayAab(Origin, LineDirection,BottomRightBack,TopLeftFront,Result) && Result.x <= 1.0f && Result.y >= 0.0f;
Vector3f TLF = new Vector3f(TopRightFront);
Vector3f BRB = new Vector3f(BottomLeftBack);
TLF = TLF.mul(Scale).rotate(Rotation).add(WorldPosition);
BRB = BRB.mul(Scale).rotate(Rotation).add(WorldPosition);
AABBf ThisVoxel = new AABBf(BRB,TLF);
return Intersectionf.intersectRayAab(Origin, LineDirection,BRB,TLF,Result) && Result.x <= 1.0f && Result.y >= 0.0f;
}
public boolean SimpleCheckCollision(Vector3f Origin, Vector3f Sensor){
Vector3f LineDirection = new Vector3f(Sensor).sub(Origin);
Vector2f Result = new Vector2f(0,0);
if(Intersectionf.intersectRayAab(Origin, LineDirection,BottomRightBack,TopLeftFront,Result) && Result.x <= 1.0f && Result.y >= 0.0f){
if(Intersectionf.intersectRayAab(Origin, LineDirection, BottomLeftBack, TopRightFront,Result) && Result.x <= 1.0f && Result.y >= 0.0f){
for(int i = 0; i < Polygons.size(); i++){
CollisionPoly polygon = Polygons.get(i);
if(T4Math.Math3D.DoesLineCollideWithPoly(Origin,Sensor,new Vector3f[]{polygon.v1(),polygon.v2(),polygon.v3()})){
@ -151,18 +205,20 @@ public class CollisionMesh {
public CollisionPoly[] GetAllPolygons(){
int Rows = Polygons.size();
CollisionPoly[] PolygonArr = new CollisionPoly[Rows * 3];
Vector3f PivotOffset = Actor.Actors.get(ParentID) != null ? new Vector3f(Actor.Actors.get(ParentID).GetPivotOffset()).add(Actor.Actors.get(ParentID).GetAditionalPivotOffset()) : new Vector3f(0,0,0);
for(int row = 0; row < Rows; row++){
PolygonArr[row] = Polygons.get(row).TransformScale(Scale).TransformRotation(Rotation).TransformPosition(WorldPosition);
PolygonArr[row] = Polygons.get(row).TransformScale(Scale).TransformPosition(PivotOffset).TransformRotation(Rotation).TransformPosition(PivotOffset.negate()).TransformPosition(WorldPosition);
}
return PolygonArr;
}
public Vector3f[] GetAllVertices(){
int Rows = Polygons.size();
Vector3f PivotOffset = Actor.Actors.get(ParentID) != null ? new Vector3f(Actor.Actors.get(ParentID).GetPivotOffset()).add(Actor.Actors.get(ParentID).GetAditionalPivotOffset()) : new Vector3f(0,0,0);
Vector3f[] Vertices = new Vector3f[Rows * 3];
for(int row = 0; row < Rows; row++){
int StartPos = row * 3;
CollisionPoly poly = Polygons.get(row).TransformScale(Scale).TransformRotation(Rotation).TransformPosition(WorldPosition);
CollisionPoly poly = Polygons.get(row).TransformScale(Scale).TransformPosition(PivotOffset).TransformRotation(Rotation).TransformPosition(PivotOffset.negate()).TransformPosition(WorldPosition);
Vertices[StartPos] = new Vector3f(poly.v1().x,poly.v1().y,poly.v1().z);
Vertices[StartPos + 1] = new Vector3f(poly.v2().x,poly.v2().y,poly.v2().z);
Vertices[StartPos + 2] = new Vector3f(poly.v3().x,poly.v3().y,poly.v3().z);

View file

@ -0,0 +1,29 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionPoly;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public record CollisionSensor(Vector3f Offset, String ActorID) {
public CollisionSensor TransformRotation(Quaternionf quaternionf){
if(quaternionf == null ) return this;
Vector3f newV1 = new Vector3f(Offset);
newV1.rotate(quaternionf);
CollisionSensor newPoly = new CollisionSensor(newV1,ActorID);
return newPoly;
}
public CollisionSensor TransformScale(Vector3f Scale){
if(Scale == null ) return this;
Vector3f newV1 = new Vector3f(Offset);
newV1.mul(Scale);
CollisionSensor newPoly = new CollisionSensor(newV1,ActorID);
return newPoly;
}
public CollisionSensor TransformPosition(Vector3f Position){
if(Position == null ) return this;
Vector3f newV1 = new Vector3f(Offset);
newV1.add(Position);
CollisionSensor newPoly = new CollisionSensor(newV1,ActorID);
return newPoly;
}
}

View file

@ -0,0 +1,132 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionManager;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionPoly;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Vector3f;
import org.tinylog.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionManager.ChunkWidth;
public class PhysicsController {
public List<CollisionSensor> collisionSensors;
public Vector3f Velocities = new Vector3f(0,0,0);
public Vector3f WeightBalance = new Vector3f(0,0,0);
public float MassKG = 10.0f;
public Vector3f TerminalVelocitiesMS = new Vector3f(36f,6.6f,36f);
public Vector3f Drag = new Vector3f(0.2f,0.2f,0.2f);
public Vector3f DragCoefficients = new Vector3f(0.1f,0.1f,0.1f);
public Vector3f CrossSectionAreas = new Vector3f(1.8f * 0.9f, 0.6f * 0.9f, 1.8f * 0.6f);
public Vector3f WeightDistribution = new Vector3f(2.0f,5.0f,7.0f);
public float FallingAcceleration = 0.0f;
public float Bounciness = 0.3f;
String CurrentChunkPosition = "NULL";
public void CalculateDragCoefficients(){
Vector3f dragCoefficients = WorldPhysicsManager.CalculateDragFromTerminalVelocities(TerminalVelocitiesMS,MassKG,CrossSectionAreas);
this.DragCoefficients = new Vector3f(dragCoefficients);
}
public void CalculateDrag(){
Vector3f drag = WorldPhysicsManager.CalculateRealDrag(Velocities,DragCoefficients,CrossSectionAreas);
this.Drag = new Vector3f(drag);
}
public void CalculateFallSpeed(){
FallingAcceleration = WorldPhysicsManager.CalculateGravityForce(MassKG, Drag);
}
public final String ID;
public final String ParentID;
public PhysicsController(String ActorID){
collisionSensors = new ArrayList<>();
ID = ActorID + "_PHYSICS_CONTROLLER";
ParentID = ActorID;
WorldPhysicsManager.PhysicsObjects.put(ID, this);
WorldPhysicsManager.ActiveControllers.add(ID);
Actor actor = Actor.Actors.get(ParentID);
Vector3f WorldPos = new Vector3f(actor.GetPosition()).div(ChunkWidth);
WorldPos.x = (int)Math.floor(WorldPos.x);
WorldPos.y = (int)Math.floor(WorldPos.y);
WorldPos.z = (int)Math.floor(WorldPos.z);
CurrentChunkPosition = WorldPos.x + ":" + WorldPos.y + ":" + WorldPos.z;
CalculateDragCoefficients();
}
public PhysicsController AddCollisionSensor(Vector3f RelativeOffset){
collisionSensors.add(new CollisionSensor(RelativeOffset, ParentID));
return this;
}
public void PhysicsTick(float DeltaTime, float TargetFrameRate){
Actor actor = Actor.Actors.get(ParentID);
Vector3f Position = new Vector3f(actor.GetPosition());
Vector3f NextPosition = new Vector3f(Position);
Vector3f Velocity = new Vector3f(Velocities);
Velocity = Velocity.mul(DeltaTime);
NextPosition.add(Velocity);
boolean ValidMovement = true;
CalculateDrag();
CalculateFallSpeed();
Velocities.y -= (FallingAcceleration/(TargetFrameRate*2)) * DeltaTime;
Velocities = Velocities.mul((1f - (WorldPhysicsManager.Drag * (DeltaTime))),1f,(1f - (WorldPhysicsManager.Drag * (DeltaTime))));
if(collisionSensors != null && !collisionSensors.isEmpty()) {
// Vector3f WorldPos = new Vector3f(NextPosition).div(ChunkWidth);
// WorldPos.x = (int) Math.floor(WorldPos.x);
// WorldPos.y = (int) Math.floor(WorldPos.y);
// WorldPos.z = (int) Math.floor(WorldPos.z);
// String NextChunkPosition = WorldPos.x + ":" + WorldPos.y + ":" + WorldPos.z;
// List<String> CollisionMeshesToCycleThrough = new ArrayList<>(CollisionManager.AlwaysCheckMeshes);
// CollisionMeshesToCycleThrough.addAll(CollisionManager.GetCollisionMeshesAtChunk(NextChunkPosition));
// if (!NextChunkPosition.equals(CurrentChunkPosition)) {
// CollisionMeshesToCycleThrough.addAll(CollisionManager.GetCollisionMeshesAtChunk(CurrentChunkPosition));
// CurrentChunkPosition = NextChunkPosition;
// }
// Logger.debug(CollisionMeshesToCycleThrough.size());
List<CollisionMesh> CollisionMeshesToCycleThrough = CollisionManager.GetAllCollisionMeshes();
//Logger.debug("Collision Mesh Count [{}]",CollisionMeshesToCycleThrough.size());
Vector3f PivotOffset = Actor.Actors.get(ParentID) != null ? new Vector3f(Actor.Actors.get(ParentID).GetPivotOffset()).add(Actor.Actors.get(ParentID).GetAditionalPivotOffset()) : new Vector3f(0,0,0);
List<CollisionSensor> CollidingSensors = new ArrayList<>();
for (int j = 0; j < collisionSensors.size(); j++) {
CollidingSensors.add(collisionSensors.get(j).TransformScale(actor.GetScale()).TransformPosition(PivotOffset).TransformRotation(actor.GetRotation()).TransformPosition(PivotOffset.negate()).TransformPosition(NextPosition));
}
if(CollidingSensors.size() > 0){
for(int i = 0; i < CollisionMeshesToCycleThrough.size(); i++){
CollisionMesh collisionMesh = CollisionMeshesToCycleThrough.get(i);
if(collisionMesh != null && !Objects.equals(collisionMesh.ID, actor.GetCollisionMesh().ID) && collisionMesh.CheckAABBCollisionAgainstAABB(collisionMesh.GetVoxelCollider())) {
CollisionPoly[] polygons = collisionMesh.GetAllPolygons();
for(int j = 0; j < polygons.length; j++){
CollisionPoly poly = polygons[j];
if(poly != null) {
for (int k = 0; k < CollidingSensors.size(); k++) {
Vector3f CollisionPoint = T4Math.Math3D.LineCollisionPointWithPoly(NextPosition, CollidingSensors.get(k).Offset(), new Vector3f[]{poly.v1(), poly.v2(), poly.v3()});
if (CollisionPoint != null) {
Vector3f subtraction = new Vector3f(NextPosition).sub(CollisionPoint);
if(Math.abs(subtraction.x) > 0.1f && Math.abs(subtraction.z) > 0.1f) {
NextPosition.sub(new Vector3f(NextPosition).sub(CollisionPoint));
} else {
NextPosition.sub(0,new Vector3f(NextPosition).sub(CollisionPoint).y,0);
}
Velocities.y = -Velocities.y * Bounciness;
if(Math.abs(Velocities.y) < 0.05f) Velocities.y = 0;
ValidMovement = false;
}
if(!ValidMovement)break;
}
}
if(!ValidMovement)break;
}
}
if(!ValidMovement)break;
}
}
}
actor.SetPosition(NextPosition);
}
}

View file

@ -0,0 +1,43 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WorldPhysicsManager {
public static float GravityMS = 9.8f;
public static float AirResistance = 0.5f;
public static float Drag = 0.02f;
public static float AirDensityKGM3 = 1.225f;
public static Vector3f CalculateRealDrag(Vector3f Velocity, Vector3f DragCoefficients, Vector3f CrossSectionAreas){
float XDrag = 0.5f * (AirDensityKGM3 * Velocity.x * Velocity.x * DragCoefficients.x * CrossSectionAreas.x);
float YDrag = 0.5f * (AirDensityKGM3 * Velocity.y * Velocity.y * DragCoefficients.y * CrossSectionAreas.y);
float ZDrag = 0.5f * (AirDensityKGM3 * Velocity.z * Velocity.z * DragCoefficients.z * CrossSectionAreas.z);
return new Vector3f(XDrag,YDrag,ZDrag);
}
public static float CalculateGravityForce(float Mass, Vector3f Drag){
return ((Mass * GravityMS) - Drag.y)/Mass;
}
public static Vector3f CalculateDragFromTerminalVelocities(Vector3f TerminalVelocities, float Mass, Vector3f CrossSectionAreas){
float Cdx = (2 * Mass * GravityMS) / (AirDensityKGM3 * TerminalVelocities.x * TerminalVelocities.x * CrossSectionAreas.x);
float Cdy = (2 * Mass * GravityMS) / (AirDensityKGM3 * TerminalVelocities.y * TerminalVelocities.y * CrossSectionAreas.y);
float Cdz = (2 * Mass * GravityMS) / (AirDensityKGM3 * TerminalVelocities.z * TerminalVelocities.z * CrossSectionAreas.z);
return new Vector3f(Cdx, Cdy, Cdz);
}
public static final Map<String, PhysicsController> PhysicsObjects = new HashMap<>();
public static List<String> ActiveControllers = new ArrayList<>();
public static void PhysicsTick(float DeltaTime, float TargetFrameTime){
for(int i = 0; i < ActiveControllers.size(); i++){
PhysicsObjects.get(ActiveControllers.get(i)).PhysicsTick(DeltaTime, TargetFrameTime);
}
}
}

View file

@ -12,11 +12,13 @@ import net.halbear.Terrain4J.EngineCore.Audio.SoundListener;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Input.KeyboardInput;
import net.halbear.Terrain4J.EngineCore.Input.MouseListener;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionManager;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.WorldPhysicsManager;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.ModelLoader;
import net.halbear.Terrain4J.EngineCore.Main.Scene.*;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.Gimbal;
@ -78,14 +80,32 @@ public class GameCore implements GameLogic {
MelonaData = ModelLoader.LoadModel("resources/models/melona/melona.json");
List<MaterialData> MelonaDataMat = ModelLoader.LoadMaterials("resources/models/melona/melona_mat.json");
var Melona = new Actor("Melona", MelonaData.ID(), new Vector3f(0,0f,-5f)).SetRotation(0,(float)Math.toRadians(180),0);
CollisionMesh mesh = new CollisionMesh("Super Collision Mesh");
mesh.AddNewCollisionPoly(new Vector3f(-100,-5,0),new Vector3f(-100,-5,-100),new Vector3f(0,-5,-100));
mesh.AddNewCollisionPoly(new Vector3f(0,-5,0),new Vector3f(0,-5,-100),new Vector3f(-100,-5,0));
Melona.CreatePhysicsController();
Melona.GetPhysicsController()
.AddCollisionSensor(new Vector3f(0,0,0))
.AddCollisionSensor(new Vector3f(0,1.8f,0))
.AddCollisionSensor(new Vector3f(0.5f,0,0))
.AddCollisionSensor(new Vector3f(-0.5f,0,0))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,0))
.AddCollisionSensor(new Vector3f(-5f,1.8f,0))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0.5f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0.5f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(0f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(-0f,0f,-0.5f))
.AddCollisionSensor(new Vector3f(0f,0f,0.5f));
CollisionMesh mesh = new CollisionMesh("Super Collision Mesh","NULL");
mesh.AddNewCollisionPoly(new Vector3f(-100,-5,100),new Vector3f(-100,-5.5f,-100),new Vector3f(100,-6,-100));
mesh.AddNewCollisionPoly(new Vector3f(100,-5.5f,100),new Vector3f(100,-6,-100),new Vector3f(-100,-5,100));
try {
Melona.GenerateCollisionMesh(MelonaData);
} catch(IOException e){
Logger.error("Could Not Generate Collison Mesh [{}]",e);
}
mesh.RegisterMesh();
scene.AddActor(Melona);
ActorEditorComponents.add(new EditorActorComponent(Melona,new Vector3f(0,0f,-5f),new Vector3f(1.0f,1.8f,1.0f)));
CubeActors.forEach(scene::AddActor);
@ -127,7 +147,26 @@ public class GameCore implements GameLogic {
public void SpawnNewActorEditorComponent(EngineInstance engineInstance){
var scene = engineInstance.scene();
var Melona = new Actor("Melona" + scene.GetActors().size(), MelonaData.ID(), new Vector3f(scene.GetCamera().GetPosition())).SetRotation(new Vector3f(scene.GetCamera().GetRotation()));
Camera camera = scene.GetCamera().CloneCamera();
camera.MoveForward(5);
var Melona = new Actor("MelonaSpawned" + scene.GetActors().size(), MelonaData.ID(), new Vector3f(camera.GetPosition()));
camera = null;
Melona.CreatePhysicsController();
Melona.GetPhysicsController()
.AddCollisionSensor(new Vector3f(0,0,0))
.AddCollisionSensor(new Vector3f(0,1.8f,0))
.AddCollisionSensor(new Vector3f(0.5f,0,0))
.AddCollisionSensor(new Vector3f(-0.5f,0,0))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,0))
.AddCollisionSensor(new Vector3f(-5f,1.8f,0))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0.5f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0.5f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(0.5f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(0f,1.8f,0.5f))
.AddCollisionSensor(new Vector3f(-0f,1.8f,-0.5f))
.AddCollisionSensor(new Vector3f(-0f,0f,-0.5f))
.AddCollisionSensor(new Vector3f(0f,0f,0.5f));
try {
Melona.GenerateCollisionMesh(MelonaData);
} catch(IOException e){
@ -196,7 +235,7 @@ public class GameCore implements GameLogic {
KeyboardInput input = window.getKeyboardInput();
float MovementDist = (float)(FrameDiffNanoSeconds / 1000000L) * MOVEMENT_SPEED;
Camera camera = scene.GetCamera();
if(input.keyPressed(GLFW_KEY_F3)){
if(input.keySinglePress(GLFW_KEY_F3)){
GUI_MODE = (GUI_MODE + 1) % 2;
}
if(input.keyPressed(GLFW_KEY_1)) {
@ -208,6 +247,14 @@ public class GameCore implements GameLogic {
if(input.keyPressed(GLFW_KEY_3)) {
Gimbal.universalType = Gimbal.GimbalType.Rotate;
}
if(input.keyPressed(GLFW_KEY_TAB)){
for(int i = 0; i < ActorEditorComponents.size(); i++){
ActorEditorComponents.get(i).UpdateAll();
}
}
if(input.keySinglePress(GLFW_KEY_F2)) {
EngineConfig.getInstance().SetRenderCollisionMesh(!EngineConfig.getInstance().RenderCollisionMeshes());
}
if(input.keyPressed(GLFW_KEY_W)){
camera.MoveForward(MovementDist);
}
@ -269,10 +316,18 @@ public class GameCore implements GameLogic {
@Override
public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
}
@Override
public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
float PhysicsFramerate = 24f;
float PhysicsFrameTime = 1000f/PhysicsFramerate;
float DeltaTime = (float)FrameDiffNanoSeconds/(PhysicsFrameTime*1000000f);
if(PrimaryRuntime.GetFastThread().Ticks(20)==0){
//CollisionManager.UpdateCollisionChunks();
}
WorldPhysicsManager.PhysicsTick(DeltaTime, PhysicsFramerate);
}
@Override

View file

@ -1,14 +1,19 @@
package net.halbear.Terrain4J.EngineCore.Main.Scene;
import imgui.extension.imguizmo.flag.Mode;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.PhysicsController;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Actor{
public static final Map<String, Actor> Actors = new HashMap<>();
private final String ID;
private final String ModelID;
private final Matrix4f ModelMatrix;
@ -16,6 +21,10 @@ public class Actor{
private final Quaternionf Rotation;
private CollisionMesh collisionMesh;
private Vector3f Scale;
private PhysicsController physicsController;
private final Vector3f PivotOffset;
private final Vector3f AdditionalPivotOffset;
private final Vector3f TemporaryVector;
public CollisionMesh GetCollisionMesh(){
return collisionMesh;
@ -25,15 +34,41 @@ public class Actor{
this.ID = ID;
this.ModelID = ModelID;
this.Position = Position;
TemporaryVector = new Vector3f();
PivotOffset = new Vector3f();
AdditionalPivotOffset = new Vector3f();
Scale = new Vector3f(1f,1f,1f);
Rotation = new Quaternionf();
ModelMatrix = new Matrix4f();
UpdateModelMatrix();
Actors.put(ID,this);
}
public Vector3f GetAditionalPivotOffset(){return AdditionalPivotOffset;}
public Actor SetAditionalPivotOffset(Vector3f offset){
AdditionalPivotOffset.set(offset);
return this;
}
public Vector3f GetPivotOffset(){return PivotOffset;}
public Actor SetPivotOffset(Vector3f offset){
PivotOffset.set(offset);
return this;
}
public PhysicsController GetPhysicsController(){return physicsController;}
public Actor CreatePhysicsController(){
physicsController = new PhysicsController(ID);
return this;
}
public Actor GenerateCollisionMesh(ModelData modelData) throws IOException {
collisionMesh = new CollisionMesh(ID + "_COLLISION_MESH");
collisionMesh = new CollisionMesh(ID + "_COLLISION_MESH", ID);
collisionMesh.GenerateCollisionMeshFromVulkanModel(modelData);
collisionMesh.RegisterMesh();
return this;
}
@ -59,7 +94,12 @@ public class Actor{
UpdateModelMatrix();
return this;
}
public Actor SetPosition(Vector3f position){
Position.set(position);
UpdateModelMatrix();
if(collisionMesh != null)collisionMesh.SetWorldPosition(Position);
return this;
}
public Actor SetPosition(float x, float y, float z){
Position.x = x;
Position.y = y;
@ -83,7 +123,10 @@ public class Actor{
}
public void UpdateModelMatrix(){
ModelMatrix.translateLocal(TemporaryVector.set(PivotOffset).add(AdditionalPivotOffset));
ModelMatrix.translationRotateScale(Position, Rotation, Scale);
ModelMatrix.translateLocal(TemporaryVector.negate());
}
public String GetID(){return ID;}

View file

@ -13,7 +13,18 @@ public class Camera {
private final Matrix4f ViewMatrix;
private float FOV = (float) (60 * EngineConfig.DegToRad);
private boolean Sprint;
int i = 0;
public Camera CloneCamera(){
Camera NewCam = new Camera();
NewCam.SetPosition(Position.x, Position.y, Position.z);
NewCam.SetRotation(Rotation.x, Rotation.y, Rotation.z);
NewCam.Right.set(new Vector3f(Right));
NewCam.Up.set(new Vector3f(Up));
NewCam.SetFOVrad(FOV);
NewCam.Sprint = Sprint;
NewCam.ViewMatrix.set(new Matrix4f(ViewMatrix));
return NewCam;
}
public Camera(){
Direction = new Vector3f();
@ -24,6 +35,12 @@ public class Camera {
ViewMatrix = new Matrix4f();
}
public Camera SetFOVrad(float FOVradians){
FOV = FOVradians;
return this;
}
public Camera SetFOV(float FOVdegrees){
FOV = (float) (FOVdegrees * EngineConfig.DegToRad);
return this;

View file

@ -3,7 +3,10 @@ package net.halbear.Terrain4J.EngineCore.Main.Scene;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.Gimbal;
import org.joml.Vector3f;
import java.util.HashMap;
public class EditorActorComponent implements IMoveableActorComponent{
public static final HashMap<String, IMoveableActorComponent> EditorComponents = new HashMap<>();
public static int ActorComponentCount = 0;
private Gimbal ActorGimbal;
private Actor actor;
@ -19,8 +22,12 @@ public class EditorActorComponent implements IMoveableActorComponent{
ActorGimbal = new Gimbal(Position, new Vector3f(1.0f,1.0f,1.0f),this).SetID(ID);
ActorComponentCount++;
UpdateAll();
EditorComponents.put(actor.GetID() + "_COMPONENT",this);
}
public IMoveableActorComponent GetEditorComponent(String ActorID){
return EditorComponents.get(ActorID + "_COMPONENT");
}
@Override
public void UpdateAll() {
@ -30,7 +37,7 @@ public class EditorActorComponent implements IMoveableActorComponent{
actor.GetCollisionMesh().SetRotationTransform(actor.GetRotation());
actor.GetCollisionMesh().SetScaleTransform(actor.GetScale());
}
ActorGimbal.SetPosition(new Vector3f(Position.x, Position.y + ActorDimensions.y/2, Position.z));
ActorGimbal.SetPosition(new Vector3f(Position.x, Position.y, Position.z).add(actor.GetPivotOffset()).add(actor.GetAditionalPivotOffset()));
}
@Override
@ -45,8 +52,8 @@ public class EditorActorComponent implements IMoveableActorComponent{
@Override
public void AddPosition(Vector3f addedPos) {
Vector3f Pos = actor.GetPosition();
actor.SetPosition(Pos.x + addedPos.x, Pos.y + addedPos.y, Pos.z + addedPos.z);
Vector3f Pos = Position;
actor.GetPosition().set(Pos.x + addedPos.x, Pos.y + addedPos.y, Pos.z + addedPos.z);
Position = new Vector3f(actor.GetPosition());
UpdateAll();
}

View file

@ -117,7 +117,7 @@ public class Gimbal implements GUIOverlay {
drawList.addLine(v1.x, v1.y, v2.x, v2.y, colour, 1f);
drawList.addLine(v2.x, v2.y, v3.x, v3.y, colour, 1f);
drawList.addLine(v3.x, v3.y, v1.x, v1.y, colour, 1f);
colour = ImGui.getColorU32(0.0f, 0.4f, 1.0f, 0.3f);
colour = ImGui.getColorU32(0.0f, 0.4f, 1.0f, 0.1f);
drawList.addTriangleFilled(
new ImVec2(v1.x,v1.y),
new ImVec2(v2.x,v2.y),

View file

@ -28,6 +28,17 @@ public class T4Math {
public static class Math3D {
public static float epsilon = 1e-6f;
public static boolean TestAabbAabb(
float minX1, float minY1, float minZ1, float maxX1, float maxY1, float maxZ1,
float minX2, float minY2, float minZ2, float maxX2, float maxY2, float maxZ2) {
return minX1 <= maxX2 && maxX1 >= minX2 &&
minY1 <= maxY2 && maxY1 >= minY2 &&
minZ1 <= maxZ2 && maxZ1 >= minZ2;
}
public static boolean DoesLineCollideWithPoly(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
Vector3f LineDirection = new Vector3f(Point).sub(Origin);
float Test = Intersectionf.intersectRayTriangle(Origin,LineDirection,
@ -35,36 +46,14 @@ public class T4Math {
return(Test >= 0.0f && Test <= 1.0f);
}
public static boolean ComplexLinePolyCollisionCheck(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
public static Vector3f LineCollisionPointWithPolyRelative(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
Vector3f LineDirection = new Vector3f(Point).sub(Origin);
float Test = Intersectionf.intersectRayTriangle(Origin,LineDirection,
Vertices[0],Vertices[1],Vertices[2], epsilon);
if(Test >= 0.0f && Test <= 1.0f) return true;
Vector3f edge1 = new Vector3f(Vertices[1]).sub(Vertices[0]);
Vector3f edge2 = new Vector3f(Vertices[2]).sub(Vertices[0]);
Vector3f normal = new Vector3f(edge1).cross(edge2).normalize();
Vector3f pAFromV0 = new Vector3f(Origin).sub(Vertices[0]);
Vector3f pBFromV0 = new Vector3f(Point).sub(Vertices[0]);
boolean pAOnPlane = Math.abs(pAFromV0.dot(normal)) < epsilon;
boolean pBOnPlane = Math.abs(pBFromV0.dot(normal)) < epsilon;
if (pAOnPlane && pBOnPlane) {
if (Intersectionf.testPointInTriangle(Origin.x,Origin.y,Origin.z,
Vertices[0].x,Vertices[0].y,Vertices[0].z,
Vertices[1].x,Vertices[1].y,Vertices[1].z,
Vertices[2].x,Vertices[2].y,Vertices[2].z) ||
Intersectionf.testPointInTriangle(Point.x,Point.y,Point.z,
Vertices[0].x,Vertices[0].y,Vertices[0].z,
Vertices[1].x,Vertices[1].y,Vertices[1].z,
Vertices[2].x,Vertices[2].y,Vertices[2].z)) {
return true;
}
if (Intersectionf.testLineSegmentTriangle(Origin, Point, Vertices[0], Vertices[1],Vertices[2], epsilon)){
return true;
}
if (Test >= 0.0f && Test <= 1.0f) {
return new Vector3f(LineDirection).mul(Test);
}
return false;
return null;
}
public static Vector3f LineCollisionPointWithPoly(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
@ -128,10 +117,41 @@ public class T4Math {
float ndcX = clipSpacePos.x / clipSpacePos.w;
float ndcY = clipSpacePos.y / clipSpacePos.w;
float ndcZ = clipSpacePos.z / clipSpacePos.w;
if (ndcZ < -1.0f || ndcZ > 1.0f) return null;
float screenX = ((ndcX + 1.0f) / 2.0f) * windowWidth;
float screenY = ((1.0f - ndcY) / 2.0f) * windowHeight;
return new Vector3f(screenX, screenY, ndcZ);
}
public static boolean ComplexLinePolyCollisionCheck(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
Vector3f LineDirection = new Vector3f(Point).sub(Origin);
float Test = Intersectionf.intersectRayTriangle(Origin,LineDirection,
Vertices[0],Vertices[1],Vertices[2], epsilon);
if(Test >= 0.0f && Test <= 1.0f) return true;
Vector3f edge1 = new Vector3f(Vertices[1]).sub(Vertices[0]);
Vector3f edge2 = new Vector3f(Vertices[2]).sub(Vertices[0]);
Vector3f normal = new Vector3f(edge1).cross(edge2).normalize();
Vector3f pAFromV0 = new Vector3f(Origin).sub(Vertices[0]);
Vector3f pBFromV0 = new Vector3f(Point).sub(Vertices[0]);
boolean pAOnPlane = Math.abs(pAFromV0.dot(normal)) < epsilon;
boolean pBOnPlane = Math.abs(pBFromV0.dot(normal)) < epsilon;
if (pAOnPlane && pBOnPlane) {
if (Intersectionf.testPointInTriangle(Origin.x,Origin.y,Origin.z,
Vertices[0].x,Vertices[0].y,Vertices[0].z,
Vertices[1].x,Vertices[1].y,Vertices[1].z,
Vertices[2].x,Vertices[2].y,Vertices[2].z) ||
Intersectionf.testPointInTriangle(Point.x,Point.y,Point.z,
Vertices[0].x,Vertices[0].y,Vertices[0].z,
Vertices[1].x,Vertices[1].y,Vertices[1].z,
Vertices[2].x,Vertices[2].y,Vertices[2].z)) {
return true;
}
if (Intersectionf.testLineSegmentTriangle(Origin, Point, Vertices[0], Vertices[1],Vertices[2], epsilon)){
return true;
}
}
return false;
}
}
}

View file

@ -8,6 +8,8 @@ import org.tinylog.Logger;
public class FastTickThread extends EngineThread {
private int Ticks = 0;
public FastTickThread(EngineInstance engineInstance, GameLogic appLogic){
super(engineInstance, appLogic, "Fast");
UpdateTickRateThreshold();
@ -17,6 +19,14 @@ public class FastTickThread extends EngineThread {
public void TickEvent(long nsTimeDiff){
gameLogic.UpdateFastThread(PrimaryRuntime.GetEngineInstance(), nsTimeDiff);
}
public int Ticks(int TickThreshold){
int TickCount = Ticks;
Ticks++;
if(Ticks >= TickThreshold) Ticks = 0;
return TickCount;
}
@Override
public void FrameEvent(long now, long InitialTime){