super janky rigid body collisions

This commit is contained in:
Halbear 2026-07-05 01:12:42 +01:00
parent c02b9ed2ca
commit b817940716
22 changed files with 2383 additions and 1129 deletions

View file

@ -78,6 +78,10 @@ public class EngineConfig {
public float Gamma = 0.8545f;
public int MaxVulkanCrashesAllowed = 10;
public boolean RenderCollisionMesh = true;
public float PhysicsSpeed = 1.0f;
public float PhysicsSpeed(){return PhysicsSpeed;}
public void SetPhysicsSpeed(float NewSpeed){PhysicsSpeed =NewSpeed;}
public boolean RenderCollisionMeshes(){
return RenderCollisionMesh;

View file

@ -1,10 +1,19 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import net.halbear.Terrain4J.EngineCore.Logic.Collision.CollisionPoly;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public record CollisionSensor(Vector3f Offset, String ActorID) {
public Vector3f GetTransformedCollisionSensor() {
Actor actor = Actor.Actors.get(ActorID);
if (actor != null) {
CollisionSensor newSensor = new CollisionSensor(new Vector3f().set(Offset), ActorID);
Vector3f PivotOffset = new Vector3f().set(actor.GetPivotOffset()).add(actor.GetAditionalPivotOffset());
return newSensor.TransformPosition(PivotOffset).TransformRotation(actor.GetRotation()).TransformPosition(PivotOffset.negate()).TransformScale(actor.GetScale()).TransformPosition(actor.GetPosition()).Offset;
}
return null;
}
public CollisionSensor TransformRotation(Quaternionf quaternionf){
if(quaternionf == null ) return this;
Vector3f newV1 = new Vector3f(Offset);

View file

@ -0,0 +1,16 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionMesh;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.List;
public interface IPhysicsController {
public String ID();
public String ActorID();
public String MeshID();
public void PhysicsTick(float DeltaTime, float TargetFrameRate);
public List<String> Collisions();
public void CollisionEvent(CollisionMesh collidingMesh, Vector3f PositionDifference, Vector2f CollisionScale);
}

View file

@ -0,0 +1,18 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision;
import org.joml.Vector3f;
public record CollisionIndexPoly(int Index1, int Index2, int Index3) {
public CollisionPoly GetCollisionPoly(CollisionMesh mesh){
Vector3f[] vertices = mesh.GetTransformedVertices(new int[]{Index1,Index2,Index3});
CollisionPoly polygon = new CollisionPoly(vertices[0],vertices[1],vertices[2],new Vector3f());
polygon.CalculateCentre();
return polygon;
}
public CollisionPoly GetRawCollisionPoly(CollisionMesh mesh){
Vector3f[] vertices = mesh.GetUntransformedVertices(new int[]{Index1,Index2,Index3});
CollisionPoly polygon = new CollisionPoly(vertices[0],vertices[1],vertices[2],new Vector3f());
polygon.CalculateCentre();
return polygon;
}
}

View file

@ -1,4 +1,4 @@
package net.halbear.Terrain4J.EngineCore.Logic.Collision;
package net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision;
import org.joml.Vector3f;
import org.joml.primitives.AABBf;

View file

@ -1,5 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Logic.Collision;
package net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyPhysicsController;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MeshData;
@ -15,7 +16,9 @@ import java.io.*;
import java.util.*;
public class CollisionMesh {
public final List<CollisionPoly> Polygons;
public final List<Vector3f> Vertices;
public final List<CollisionIndexPoly> Polygons;
public final Map<String, Integer> VertexLookup;
public Vector3f TopRightFront = new Vector3f(0,0,0);
public Vector3f BottomLeftBack = new Vector3f(0,0,0);
public Vector3f WorldPosition = new Vector3f(0,0,0);
@ -25,6 +28,8 @@ public class CollisionMesh {
public final String ParentID;
public AABBf Voxel;
public List<Vector3f> GetVertices(){return Vertices;}
public CollisionMesh(String ID, String ActorID){
this.ID = ID;
if(ActorID != "NULL") {
@ -32,7 +37,9 @@ public class CollisionMesh {
Rotation = new Quaternionf(Actor.Actors.get(ActorID).GetRotation());
}
ParentID = ActorID;
VertexLookup = new HashMap<>();
Polygons = new ArrayList<>();
Vertices = new ArrayList<>();
}
public void RegisterMesh(){
@ -40,15 +47,15 @@ public class CollisionMesh {
}
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;
}
//if(TopRightFront == null || BottomLeftBack == null) return null;
//if(Voxel == null){
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 TLF = new Vector3f(TopRightFront);
Vector3f BRB = new Vector3f(BottomLeftBack);
TLF = TLF.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
BRB = BRB.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
this.Voxel = new AABBf(BRB,TLF);
//}
return Voxel;
}
@ -93,7 +100,7 @@ public class CollisionMesh {
List<int[]> Indices = new ArrayList<>();
int IndexCount = 0;
int vertexOffset = 0;
int vertexStride = 14; // Your stride configuration
int vertexStride = 14;
for (MeshData meshData : vkModel.Meshes()) {
int intsToRead = meshData.IndexSize() / VulkanUtils.INT_SIZE;
int[] meshIndices = new int[intsToRead];
@ -141,17 +148,30 @@ public class CollisionMesh {
BottomLeftBack.z = Math.min(BottomLeftBack.z, localMinZ);
}
public void GenerateSensorsFromVertices(){
if(Actor.Actors.get(ParentID) != null &&Actor.Actors.get(ParentID).PhysicsType == Actor.PhysicsControllerType.T4JLegacyCollision) {
LegacyPhysicsController physicsController = (LegacyPhysicsController)Actor.Actors.get(ParentID).GetPhysicsController();
physicsController.collisionSensors.clear();
for(int i = 0; i < Vertices.size(); i++){
physicsController.AddCollisionSensor(Vertices.get(i));
}
}
}
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();
Vector3f[] vertices = new Vector3f[]{v1,v2,v3};
int[] Index = new int[3];
for(int i = 0; i < 3; i++) {
String Vertex = vertices[i].x + "_" + vertices[i].y + "_" + vertices[i].z;
Index[i] = Vertices.size();
if (VertexLookup.containsKey(Vertex)) {
Index[i] = VertexLookup.get(Vertex);
} else {
Vertices.add(new Vector3f(vertices[i]));
VertexLookup.put(Vertex, Index[i]);
}
}
CollisionIndexPoly newPoly = new CollisionIndexPoly(Index[0],Index[1],Index[2]);
Polygons.add(newPoly);
CheckAABbounds(v1,v2,v3);
}
@ -168,10 +188,11 @@ public class CollisionMesh {
}
public boolean CheckAABBCollisionAgainstAABB(AABBf OtherVoxel){
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 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);
TLF = TLF.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
BRB = BRB.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
AABBf ThisVoxel = new AABBf(BRB,TLF);
this.Voxel = ThisVoxel;
return ThisVoxel.intersectsAABB(OtherVoxel);
@ -191,10 +212,10 @@ public class CollisionMesh {
public boolean SimpleCheckCollision(Vector3f Origin, Vector3f Sensor){
Vector3f LineDirection = new Vector3f(Sensor).sub(Origin);
Vector2f Result = new Vector2f(0,0);
CollisionPoly[] polygons = GetAllPolygons();
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()})){
for (CollisionPoly polygon : polygons) {
if (T4Math.Math3D.DoesLineCollideWithPoly(Origin, Sensor, new Vector3f[]{polygon.v1(), polygon.v2(), polygon.v3()})) {
return true;
}
}
@ -202,27 +223,63 @@ public class CollisionMesh {
return false;
}
public Vector3f[] GetTransformedVertices(int[] index){
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[index.length];
Vector3f Vertex = new Vector3f();
for(int i = 0; i < vertices.length; i++) {
if (index[i] < Vertices.size() && Vertices.get(index[i]) != null) {
Vertex.set((Vertices.get(index[i])));
Vertex.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
vertices[i] = new Vector3f(Vertex);
}
}
return vertices;
}
public Vector3f[] GetUntransformedVertices(int[] index){
Vector3f[] vertices = new Vector3f[index.length];
Vector3f Vertex = new Vector3f();
for(int i = 0; i < vertices.length; i++) {
if (index[i] < Vertices.size() && Vertices.get(index[i]) != null) {
Vertex.set((Vertices.get(index[i])));
vertices[i] = new Vector3f(Vertex);
}
}
return vertices;
}
public Vector3f GetTransformedVertex(int index){
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);
if(index > Vertices.size() || index < 0) return null;
Vector3f Vertex = new Vector3f(Vertices.get(index));
Vertex.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
return Vertex;
}
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).TransformPosition(PivotOffset).TransformRotation(Rotation).TransformPosition(PivotOffset.negate()).TransformPosition(WorldPosition);
CollisionIndexPoly IndexPoly = Polygons.get(row);
CollisionPoly CollisionPoly = IndexPoly.GetRawCollisionPoly(this);
PolygonArr[row] = CollisionPoly.TransformPosition(PivotOffset).TransformRotation(Rotation).TransformPosition(PivotOffset.negate()).TransformScale(Scale).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).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);
Vector3f[] vertices = new Vector3f[Vertices.size()];
Vector3f Vertex = new Vector3f();
for(int i = 0; i < vertices.length; i++) {
if (Vertices.get(i) != null) {
Vertex.set((Vertices.get(i)));
Vertex.add(PivotOffset).rotate(Rotation).sub(PivotOffset).mul(Scale).add(WorldPosition);
vertices[i] = new Vector3f(Vertex);
}
}
return Vertices;
return vertices;
}
}

View file

@ -1,4 +1,4 @@
package net.halbear.Terrain4J.EngineCore.Logic.Collision;
package net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Math;
@ -16,6 +16,7 @@ public record CollisionPoly(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f Cent
Centre.set(new Vector3f(x,y,z));
return Centre;
}
public Vector3f[] GetVertices(){return new Vector3f[]{v1,v2,v3};}
public CollisionPoly TransformRotation(Quaternionf quaternionf){
if(quaternionf == null ) return this;
Vector3f newV1 = new Vector3f(v1);

View file

@ -0,0 +1,336 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionManager;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionPoly;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionManager.ChunkWidth;
import static net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math.Physics.*;
public class LegacyPhysicsController implements IPhysicsController {
public enum CollisionType{
PolyToPoly,
Sensor
}
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 CollisionType MeshCollision;
private final Vector3f Velocity = new Vector3f();
private final Vector3f Position = new Vector3f();
private final Vector3f NextPosition = new Vector3f();
private final Vector3f localVelocityStep = new Vector3f();
private final Vector3f displacement = new Vector3f();
private final Vector3f slidingCorrection = new Vector3f();
private final Vector3f sensorSubtraction = new Vector3f();
private final Vector3f pivotOffsetCache = new Vector3f();
private final List<Vector3f> transformedSensorsCache = new ArrayList<>();
public final List<String> Collisions = new ArrayList<>();
//public record CollisionResult(boolean Intersects,Vector3f StartPoint,Vector3f EndPoint){
//
//}
public static class CollisionResult{
public boolean Intersects = false;
public Vector3f StartPoint = null;
public Vector3f EndPoint = null;
public Vector3f CollisionNormal = null;
public float CollisionDepth;
}
public void SetCollisionType(CollisionType collisionType){
this.MeshCollision = collisionType;
}
public CollisionType GetCollisionType(){return MeshCollision;}
public final String ID;
public final String ParentID;
public void ImpulseVelocity(Vector3f Velocity){
Velocities.add(Velocity);
}
public LegacyPhysicsController(String ActorID, CollisionType collisionType){
MeshCollision = collisionType;
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;
DragCoefficients = CalculateDragCoefficients(TerminalVelocitiesMS, MassKG,CrossSectionAreas, actor.GetScale());
}
public LegacyPhysicsController AddCollisionSensor(Vector3f RelativeOffset){
collisionSensors.add(new CollisionSensor(RelativeOffset, ParentID));
return this;
}
@Override
public String ID() {
return "";
}
@Override
public String ActorID() {
return "";
}
@Override
public String MeshID() {
return "";
}
//aaaaand i give up on this system, time to attempt something completely different
public void PhysicsTick(float DeltaTime, float TargetFrameRate){
Actor actor = Actor.Actors.get(ParentID);
if (actor == null) return;
Position.set(actor.GetPosition());
NextPosition.set(Position);
localVelocityStep.set(Velocities).mul(DeltaTime);
NextPosition.add(localVelocityStep);
boolean ValidMovement = true;
Drag = CalculateDrag(TerminalVelocitiesMS, DragCoefficients,CrossSectionAreas, actor.GetScale());
FallingAcceleration = CalculateFallSpeed(MassKG, Drag, actor.GetScale());
Velocities.y -= (FallingAcceleration / (TargetFrameRate * 2f)) * DeltaTime;
float dragFactor = 1f - (WorldPhysicsManager.Drag * DeltaTime);
Velocities.set(Velocities.x * dragFactor, Velocities.y, Velocities.z * dragFactor);
List<CollisionMesh> CollisionMeshesToCycleThrough = CollisionManager.GetAllCollisionMeshes();
if (MeshCollision == CollisionType.Sensor && collisionSensors != null && !collisionSensors.isEmpty()) {
ValidMovement = CalculateSensorCollision(CollisionMeshesToCycleThrough, actor);
} else if (MeshCollision == CollisionType.PolyToPoly) {
ValidMovement = CalculateMeshCollision(CollisionMeshesToCycleThrough, actor);
}
actor.SetPosition(NextPosition);
}
@Override
public List<String> Collisions() {
return Collisions;
}
public boolean CalculateMeshCollision(List<CollisionMesh> CollisionMeshesToCycleThrough, Actor actor){
float skinThickness = 0.001f;
CollisionMesh thisCollisionMesh = actor.GetCollisionMesh();
// Logger.debug("POLY TO POLY");
if (thisCollisionMesh == null) return false;
CollisionPoly[] thisPolygons = thisCollisionMesh.GetAllPolygons();
boolean hasCollidedThisFrame = false;
for (int i = 0; i < CollisionMeshesToCycleThrough.size(); i++) {
CollisionMesh collisionMesh = CollisionMeshesToCycleThrough.get(i);
if (collisionMesh != null &&
!Objects.equals(collisionMesh.ID, thisCollisionMesh.ID) && !Collisions.contains(collisionMesh.ID) &&
collisionMesh.CheckAABBCollisionAgainstAABB(thisCollisionMesh.GetVoxelCollider())) {
CollisionPoly[] polygons = collisionMesh.GetAllPolygons();
for (int k = 0; k < thisPolygons.length; k++) {
for (int j = 0; j < polygons.length; j++) {
CollisionResult result = T4Math.ComplexCollision.ComputePolyToPolyIntersection(thisPolygons[k], polygons[j]);
if (result != null && result.Intersects) {
hasCollidedThisFrame = true;
Collisions.add(collisionMesh.ID);
float totalPushDistance = result.CollisionDepth + skinThickness;
displacement.set(result.CollisionNormal).mul(totalPushDistance);
if(result.CollisionDepth < 0.01f) {
NextPosition.set(Position).add(0,displacement.y,0);
}
float velocityAlongNormal = Velocities.dot(result.CollisionNormal);
// if (velocityAlongNormal < 0) {
// slidingCorrection.set(result.CollisionNormal).mul(velocityAlongNormal);
// Velocities.sub(slidingCorrection);
// Velocities.mul(0.98f); // Apply wall friction sliding damping
// }
Velocities.mul(result.CollisionNormal).mul(Bounciness);
// thisCollisionMesh.updatePositionContext(NextPosition);
}
}
}
}
}
return !hasCollidedThisFrame;
/*float skinThickness = 0.001f;
float totalPushDistance = 0;
Vector3f displacement = new Vector3f();
Vector3f slidingCorrection = new Vector3f();
CollisionMesh thisCollisionMesh = actor.GetCollisionMesh();
boolean ValidMovement = true;
if(thisCollisionMesh == null) return false;
CollisionPoly[] thisPolygons = thisCollisionMesh.GetAllPolygons();
for (int i = 0; i < CollisionMeshesToCycleThrough.size(); i++) {
CollisionMesh collisionMesh = CollisionMeshesToCycleThrough.get(i);
if (collisionMesh != null && !Objects.equals(collisionMesh.ID, thisCollisionMesh.ID) && collisionMesh.CheckAABBCollisionAgainstAABB(thisCollisionMesh.GetVoxelCollider())) {
CollisionPoly[] polygons = collisionMesh.GetAllPolygons();
for(int k = 0; k < thisPolygons.length; k++) {
for (int j = 0; j < polygons.length; j++) {
CollisionResult result = T4Math.ComplexCollision.ComputePolyToPolyIntersection(thisPolygons[k], polygons[j]);
if(result != null && result.Intersects){
totalPushDistance = result.CollisionDepth + skinThickness;
displacement.set(result.CollisionNormal).mul(totalPushDistance);
float velocityAlongNormal = Velocities.dot(result.CollisionNormal);
if(velocityAlongNormal < 0){
slidingCorrection.set(result.CollisionNormal).mul(velocityAlongNormal);
Velocities.sub(slidingCorrection);
Velocities.mul(0.98f);
}
//Velocities.y = -Velocities.y * Bounciness;//Velocities.mul(result.CollisionNormal).mul(Bounciness);
Logger.debug(displacement + "<- DISPLACEMENT " + NextPosition + "<- NEXTPOS " + Position + "<- POS");
NextPosition.add(displacement);
ValidMovement = false;
}
if (!ValidMovement) break;
}
if (!ValidMovement) break;
}
}
if (!ValidMovement) break;
}
return ValidMovement;*/
}
public void CollisionEvent(CollisionMesh collidingMesh, Vector3f PositionDifference, Vector2f CollisionScale){
Collisions.add(collidingMesh.ID);
if (Math.abs(PositionDifference.x) > 0.01f && Math.abs(PositionDifference.z) > 0.01f) {
NextPosition.add(PositionDifference);
}
}
public boolean CalculateSensorCollision(List<CollisionMesh> CollisionMeshesToCycleThrough, Actor actor){
boolean hasCollidedThisFrame = false;
Actor parentActor = Actor.Actors.get(ParentID);
if (parentActor != null) {
pivotOffsetCache.set(parentActor.GetPivotOffset()).add(parentActor.GetAditionalPivotOffset());
} else {
pivotOffsetCache.set(0, 0, 0);
}
while (transformedSensorsCache.size() < collisionSensors.size()) {
transformedSensorsCache.add(new Vector3f());
}
for (int j = 0; j < collisionSensors.size(); j++) {
transformedSensorsCache.get(j).set(collisionSensors.get(j).GetTransformedCollisionSensor());
}
Vector2f CollisionScale = new Vector2f();
for (int i = 0; i < CollisionMeshesToCycleThrough.size(); i++) {
CollisionMesh collisionMesh = CollisionMeshesToCycleThrough.get(i);
if (collisionMesh != null &&
!Objects.equals(collisionMesh.ID, actor.GetCollisionMesh().ID) && !Collisions.contains(collisionMesh.ID)&&
collisionMesh.CheckAABBCollisionAgainstAABB(actor.GetCollisionMesh().GetVoxelCollider())) {
CollisionPoly[] polygons = collisionMesh.GetAllPolygons();
for (int j = 0; j < polygons.length; j++) {
CollisionPoly poly = polygons[j];
if (poly == null) continue;
for (int k = 0; k < collisionSensors.size(); k++) {
Vector3f CollisionPoint = T4Math.Math3D.LineCollisionPointWithPoly(
NextPosition, transformedSensorsCache.get(k), poly.GetVertices(),CollisionScale);
if (CollisionPoint != null) {
hasCollidedThisFrame = true;
sensorSubtraction.set(NextPosition).sub(CollisionPoint);
if (Math.abs(sensorSubtraction.x) > 0.01f && Math.abs(sensorSubtraction.z) > 0.01f) {
NextPosition.sub(sensorSubtraction);
} else {
NextPosition.sub(0, sensorSubtraction.y, 0);
}
Collisions.add(collisionMesh.ID);
if(collisionMesh.ParentID != "NULL" && Actor.Actors.get(collisionMesh.ParentID) != null){
Actor.Actors.get(collisionMesh.ParentID).GetPhysicsController().CollisionEvent(Actor.Actors.get(ParentID).GetCollisionMesh(),sensorSubtraction,CollisionScale);
}
Velocities.y = -Velocities.y * Bounciness;
if (Math.abs(Velocities.y) < 0.05f) {
Velocities.y = 0;
}
}
}
}
}
}
return !hasCollidedThisFrame;
/*boolean ValidMovement = true;
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<Vector3f> CollidingSensors = new ArrayList<>();
for (int j = 0; j < collisionSensors.size(); j++) {
CollidingSensors.add(new Vector3f().set(collisionSensors.get(j).GetTransformedCollisionSensor()));
}
Vector3f Subtraction = new Vector3f();
if (!CollidingSensors.isEmpty()) {
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(actor.GetCollisionMesh().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), new Vector3f[]{poly.v1(), poly.v2(), poly.v3()});
if (CollisionPoint != null) {
Subtraction.set(NextPosition).sub(CollisionPoint);
if (Math.abs(Subtraction.x) > 0.1f && Math.abs(Subtraction.z) > 0.1f) {
NextPosition.sub(Subtraction);
} else {
NextPosition.sub(0, Subtraction.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;
}
}
return ValidMovement;*/
}
}

View file

@ -1,132 +0,0 @@
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,96 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ConvexMesh {
public static class Face{
public final int[] Indices;
public final Vector3f LocalNormal;
public Face(int[] Indices, Vector3f localNormal){
this.Indices = Indices;
this.LocalNormal = localNormal;
}
}
public Vector3f BottomLeftBack = new Vector3f(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE);
public Vector3f TopRightForward = new Vector3f(Float.MIN_VALUE,Float.MIN_VALUE,Float.MIN_VALUE);
public final List<Vector3f> LocalVertices = new ArrayList<>();
public final List<Face> Faces = new ArrayList<>();
public final List<int[]> Edges = new ArrayList<>();
public final Vector3f Position = new Vector3f();
public final Quaternionf Orientation = new Quaternionf();
public Vector3f WorldVertex(int i) {
Vector3f vertex = new Vector3f(LocalVertices.get(i));
Orientation.transform(vertex);
vertex.add(Position);
return vertex;
}
public Vector3f WorldFaceNormal(Face f) {
Vector3f normal = new Vector3f(f.LocalNormal);
Orientation.transform(normal);
return normal.normalize();
}
public List<Vector3f> WorldVertices() {
List<Vector3f> out = new ArrayList<>(LocalVertices.size());
for (int i = 0; i < LocalVertices.size(); i++) out.add(WorldVertex(i));
return out;
}
public Vector3f Support(Vector3f Direction) {
float best = -Float.MAX_VALUE;
Vector3f bestV = null;
for (int i = 0; i < LocalVertices.size(); i++) {
Vector3f w = WorldVertex(i);
float d = w.dot(Direction);
if (d > best) { best = d; bestV = w; }
}
return bestV;
}
public void CheckAABbounds(Vector3f v1){
TopRightForward.x = Math.max(TopRightForward.x, v1.x);
TopRightForward.y = Math.max(TopRightForward.y, v1.y);
TopRightForward.z = Math.max(TopRightForward.z, v1.z);
BottomLeftBack.x = Math.min(BottomLeftBack.x, v1.x);
BottomLeftBack.y = Math.min(BottomLeftBack.y, v1.y);
BottomLeftBack.z = Math.min(BottomLeftBack.z, v1.z);
}
//Creates a basic AABB, that said, its not really axis aligned so idk why im referring to is as one
public static ConvexMesh Box(float hx, float hy, float hz) {
ConvexMesh mesh = new ConvexMesh();
mesh.LocalVertices.add(new Vector3f(-hx, -hy, -hz)); // 0
mesh.LocalVertices.add(new Vector3f( hx, -hy, -hz)); // 1
mesh.LocalVertices.add(new Vector3f( hx, hy, -hz)); // 2
mesh.LocalVertices.add(new Vector3f(-hx, hy, -hz)); // 3
mesh.LocalVertices.add(new Vector3f(-hx, -hy, hz)); // 4
mesh.LocalVertices.add(new Vector3f( hx, -hy, hz)); // 5
mesh.LocalVertices.add(new Vector3f( hx, hy, hz)); // 6
mesh.LocalVertices.add(new Vector3f(-hx, hy, hz)); // 7
for(int i = 0; i < mesh.LocalVertices.size(); i++){
mesh.CheckAABbounds(mesh.LocalVertices.get(i));
}
mesh.Faces.add(new Face(new int[]{0, 3, 2, 1}, new Vector3f(0, 0, -1))); // -Z
mesh.Faces.add(new Face(new int[]{4, 5, 6, 7}, new Vector3f(0, 0, 1))); // +Z
mesh.Faces.add(new Face(new int[]{0, 1, 5, 4}, new Vector3f(0, -1, 0))); // -Y
mesh.Faces.add(new Face(new int[]{3, 7, 6, 2}, new Vector3f(0, 1, 0))); // +Y
mesh.Faces.add(new Face(new int[]{0, 4, 7, 3}, new Vector3f(-1, 0, 0))); // -X
mesh.Faces.add(new Face(new int[]{1, 2, 6, 5}, new Vector3f( 1, 0, 0))); // +X
int[][] e = {{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}};
mesh.Edges.addAll(Arrays.asList(e));
return mesh;
}
}

View file

@ -0,0 +1,143 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.IPhysicsController;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RigidBody {
public static final Map<String, RigidBody> RigidBodyRegister = new HashMap<>();
public static final List<String> RigidBodies = new ArrayList<>();
public final ConvexMesh shape;
public final Vector3f Position;
public final Quaternionf Rotation;
public Vector3f WeightBalance = new Vector3f(0,0,0);
public float MassKG = 10.0f;
public float InverseMassKG = -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;
public final Vector3f LinearVelocity = new Vector3f();
public final Vector3f AngularVelocity = new Vector3f();
public final Matrix3f InverseInertiaLocal = new Matrix3f();
public final Matrix3f InverseInertiaWorld = new Matrix3f();
public float Restitution = 0.3f; // 0 = fully inelastic, 1 = perfectly elastic
public float Friction = 0.5f;
public boolean IsStatic = false;
public String PhysicsControllerID = "NULL";
public final String RigidBodyID;
public RigidBody(String RigidBodyID, ConvexMesh Shape, float MassKG){
this.RigidBodyID = RigidBodyID;
RigidBodyRegister.put(RigidBodyID,this);
RigidBodies.add(RigidBodyID);
this.shape = Shape;
this.Position = shape.Position;
this.Rotation = shape.Orientation;
SetMass(MassKG);
}
public void SetMass(float mass) {
this.MassKG = mass;
if (mass <= 0f) {
InverseMassKG = 0f;
InverseInertiaLocal.zero();
IsStatic = true;
} else {
InverseMassKG = 1.0f / mass;
IsStatic = false;
}
}
public void SetRigidBoxInertia(float x, float y, float z){
if(IsStatic) return;
float width = 2 * x, height = 2 * y, depth = 2 * z;
float ix = (MassKG/12.0f) * (height * height + depth * depth);
float iy = (MassKG/12.0f) * (width * width + depth * depth);
float iz = (MassKG/12.0f) * (width * width + height * height);
InverseInertiaLocal.identity();
InverseInertiaLocal.m00(1.0f/ix);
InverseInertiaLocal.m11(1.0f/iy);
InverseInertiaLocal.m22(1.0f/iz);
UpdateInertiaWorld();
}
public void UpdateInertiaWorld(){
if(IsStatic) {
InverseInertiaWorld.zero();
return;
}
Matrix3f rotation = new Matrix3f();
Rotation.get(rotation);
Matrix3f rotationT = new Matrix3f(rotation).transpose();
InverseInertiaWorld.set(rotation).mul(InverseInertiaLocal).mul(rotationT);
}
private final Vector3f r = new Vector3f();
public Vector3f VecolityAtPoint(Vector3f worldPoint){
r.set(worldPoint).sub(Position);
Vector3f AngularPart = new Vector3f(AngularVelocity).cross(r);
return new Vector3f(LinearVelocity).add(AngularPart);
}
private final Vector3f ImpulseVelocity = new Vector3f();
public void ApplyImpulse(Vector3f impulse, Vector3f WorldPoint){
if(IsStatic) return;
LinearVelocity.add(ImpulseVelocity.set(impulse).mul(InverseMassKG));
r.set(WorldPoint).sub(Position);
Vector3f AngleImpulse = new Vector3f(r).cross(impulse);
Vector3f DeltaAngular = new Vector3f();
InverseInertiaWorld.transform(AngleImpulse,DeltaAngular);
AngularVelocity.add(DeltaAngular);
}
private final Vector3f GravityToAdd = new Vector3f();
private final Vector3f VelocityToAdd = new Vector3f();
public void Integrate(float DeltaTime, Vector3f Gravity){
if(IsStatic) return;
LinearVelocity.add(GravityToAdd.set(Gravity).mul(DeltaTime));
Position.add(VelocityToAdd.set(LinearVelocity).mul(DeltaTime));
Quaternionf Spin = new Quaternionf(
AngularVelocity.x * DeltaTime * 0.5f,
AngularVelocity.y * DeltaTime * 0.5f,
AngularVelocity.z * DeltaTime * 0.5f,
0
);
Quaternionf DeltaRotation = Spin.mul(Rotation);
Rotation.set(Rotation.x + DeltaRotation.x,
Rotation.y + DeltaRotation.y,
Rotation.z + DeltaRotation.z,
Rotation.w + DeltaRotation.w
).normalize();
UpdateInertiaWorld();
}
public RigidBody AttachPhysicsController(String PhysicsControllerID){
this.PhysicsControllerID = PhysicsControllerID;
return this;
}
public String GetID(){return RigidBodyID;}
}

View file

@ -0,0 +1,98 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Vector3f;
//Resolves the Separating Axis Theorem Test into reaction forces in impulses and position correction
public class RigidCollision {
private static final float CORRECTION_THRESHOLD = 1.0f; //how much it gets corrected positionally per physics step
private static final float SLOP = 0.001f; // amount of overlap can happen before collision gets corrected
public synchronized static void ResolveCollision(RigidBody body1, RigidBody body2, SeparatingAxisTheoremTester.CollisionResult collisionResult){
Vector3f Normal = collisionResult.Normal;
for(SeparatingAxisTheoremTester.Contact contact : collisionResult.Contacts){
ResolveContact(body1, body2, Normal, contact);
}
CorrectPositioning(body1,body2,Normal,collisionResult.Penetration);
}
private static final Vector3f Dist1 = new Vector3f();
private static final Vector3f Dist2 = new Vector3f();
private static final Vector3f Impulse = new Vector3f();
private static final Vector3f NegatedImpulse = new Vector3f();
private static final Vector3f RelativeVelocity = new Vector3f();
private synchronized static void ResolveContact(RigidBody body1, RigidBody body2, Vector3f normal, SeparatingAxisTheoremTester.Contact contact){
Vector3f Point = contact.Point;
Dist1.set(Point).sub(body1.Position);
Dist2.set(Point).sub(body2.Position);
Vector3f Velocity1 = body1.VecolityAtPoint(Point);
Vector3f Velocity2 = body2.VecolityAtPoint(Point);
RelativeVelocity.set(Velocity2).sub(Velocity1);
float VelocityAlongNormal = RelativeVelocity.dot(normal);
if(VelocityAlongNormal > 0) return;
float Restitution = Math.min(body1.Restitution, body2.Restitution);
float InverseMassSum = body1.InverseMassKG + body2.InverseMassKG + AngularTerm(body1, Dist1, normal) + AngularTerm(body2,Dist2,normal);
if(InverseMassSum <= T4Math.Math3D.epsilon) return;
float j = -(1 + Restitution) * VelocityAlongNormal / InverseMassSum;
Impulse.set(normal).mul(j);
body1.ApplyImpulse(NegatedImpulse.set(Impulse).negate(), Point);
body2.ApplyImpulse(Impulse,Point);
ApplyFriction(body1, body2, normal, Point, Dist1, Dist2, j);
}
private static final Vector3f DistanceCrossAxis = new Vector3f();
private static final Vector3f Angular = new Vector3f();
private static synchronized float AngularTerm(RigidBody body, Vector3f distance, Vector3f axis){
DistanceCrossAxis.set(distance).cross(axis);
Angular.zero();
body.InverseInertiaWorld.transform(DistanceCrossAxis,Angular);
return Angular.cross(distance).dot(axis);
}
private static final Vector3f relativeVelocity = new Vector3f();
private static final Vector3f Tangent = new Vector3f();
private static synchronized void ApplyFriction(RigidBody body1, RigidBody body2, Vector3f Normal, Vector3f Point, Vector3f Dist1, Vector3f Dist2, float NormalImpulseMagnitude){
Vector3f Velocity1 = body1.VecolityAtPoint(Point);
Vector3f Velocity2 = body2.VecolityAtPoint(Point);
relativeVelocity.set(Velocity2).sub(Velocity1);
Tangent.set(relativeVelocity).sub(new Vector3f(Normal).mul(relativeVelocity.dot(Normal)));
float TangentLengthSquared = Tangent.lengthSquared();
if(TangentLengthSquared <= 1e-10f) return;
Tangent.mul(1.0f/(float)Math.sqrt(TangentLengthSquared));
float InverseMassSumT = body1.InverseMassKG + body2.InverseMassKG + AngularTerm(body1, Dist1, Tangent) + AngularTerm(body2,Dist2,Tangent);
if(InverseMassSumT <= T4Math.Math3D.epsilon) return;
float jt = -relativeVelocity.dot(Tangent) / InverseMassSumT;
float mu = (body1.Friction + body2.Friction) * 0.5f;
float MaxFriction = mu * Math.abs(NormalImpulseMagnitude);
jt = Math.max(-MaxFriction, Math.min(MaxFriction,jt));
Vector3f FrictionResults = new Vector3f(Tangent).mul(jt);
body1.ApplyImpulse(new Vector3f(FrictionResults).negate(),Point);
body2.ApplyImpulse((FrictionResults),Point);
}
private static final Vector3f Correction = new Vector3f();
private static void CorrectPositioning(RigidBody body1, RigidBody body2, Vector3f Normal, float Penetration){
float InverseMassSum = body1.InverseMassKG + body2.InverseMassKG;
if(InverseMassSum <= T4Math.Math3D.epsilon) return;
float CorrectionMagnitude = Math.max(Penetration - SLOP, 0f) / InverseMassSum * CORRECTION_THRESHOLD;
Correction.set(Normal).mul(CorrectionMagnitude);
if(!body1.IsStatic) body1.Position.sub(new Vector3f(Correction).mul(body1.InverseMassKG));
if(!body2.IsStatic) body2.Position.add(new Vector3f(Correction).mul(body2.InverseMassKG));
}
}

View file

@ -0,0 +1,110 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.IPhysicsController;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.WorldPhysicsManager;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.*;
import static net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidBody.RigidBodies;
import static net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidBody.RigidBodyRegister;
import static net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math.Physics.CalculateDrag;
import static net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math.Physics.CalculateFallSpeed;
public class RigidPhysicsController implements IPhysicsController {
public static final Map<String,IPhysicsController> RigidControllerRegister = new HashMap<>();
public static final List<String> RigidControllers = new ArrayList<>();
public static IPhysicsController GetPhysicsController(String ID){
return RigidControllerRegister.get(ID);
}
public final List<String> Collisions = new ArrayList<>();
public static RigidBody Ground = new RigidBody("FLOOR",ConvexMesh.Box(100,100f,100),0);
static {
Ground.Position.set(0,-100f,0);
}
public String RigidBodyID = "NULL";
public String ActorID = "NULL";
public String ControllerID = "NOTSET";
public RigidPhysicsController(String ID, String ActorID){
RigidControllerRegister.put(ID, this);
RigidControllers.add(ID);
ControllerID = ID;
this.ActorID = ActorID;
}
public RigidPhysicsController CreateRigidBody(ConvexMesh Shape, float MassKG){
RigidBodyID = ActorID + "_RIGID_BODY";
RigidBody newRigidBody = new RigidBody(RigidBodyID,Shape,MassKG);
newRigidBody.SetRigidBoxInertia(0.5f,0.5f,0.5f);
newRigidBody.Position.set(Actor.Actors.get(ActorID).GetPosition());
newRigidBody.Rotation.set(Actor.Actors.get(ActorID).GetRotation());
newRigidBody.UpdateInertiaWorld();
WorldPhysicsManager.PhysicsObjects.put(ControllerID, this);
WorldPhysicsManager.ActiveControllers.add(ControllerID);
return this;
}
public RigidPhysicsController SetRigidBody(RigidBody rigidBody){
RigidBodyID = rigidBody.GetID();
WorldPhysicsManager.PhysicsObjects.put(ControllerID, this);
WorldPhysicsManager.ActiveControllers.add(ControllerID);
return this;
}
@Override
public String ID() {
return ControllerID;
}
@Override
public String ActorID() {
return ActorID;
}
@Override
public String MeshID() {
return RigidBodyID;
}
@Override
public void PhysicsTick(float DeltaTime, float TargetFrameRate) {
DeltaTime/=10f;
if(DeltaTime >0.1f) DeltaTime = 0.1f;
if(RigidBodyID != null && RigidBodyRegister.get(RigidBodyID) != null && ActorID != "NULL") {
RigidBody rigidBody = RigidBodyRegister.get(RigidBodyID);
rigidBody.Integrate(DeltaTime, new Vector3f(0,-1.8f,0));
for(int i = 0; i < RigidBodies.size(); i++) {
RigidBody OtherRigidBody = RigidBodyRegister.get(RigidBodies.get(i));
if(OtherRigidBody != null && OtherRigidBody.RigidBodyID != null && !Objects.equals(OtherRigidBody.RigidBodyID, RigidBodyID)) {
SeparatingAxisTheoremTester.CollisionResult collisionResult = SeparatingAxisTheoremTester.TestCollision(OtherRigidBody.shape, rigidBody.shape);
if (collisionResult != null) {
RigidCollision.ResolveCollision(OtherRigidBody, rigidBody, collisionResult);
}
}
}
if(Actor.Actors.get(ActorID) != null) {
Actor.Actors.get(ActorID).SetPivotOffset(new Vector3f(0,(rigidBody.shape.TopRightForward.y - rigidBody.shape.BottomLeftBack.y)/2f,0));
Actor.Actors.get(ActorID).SetPosition(new Vector3f(rigidBody.Position));
Actor.Actors.get(ActorID).SetRotation(rigidBody.Rotation);
}
}
}
@Override
public List<String> Collisions() {
return Collisions;
}
@Override
public void CollisionEvent(CollisionMesh collidingMesh, Vector3f PositionDifference, Vector2f CollisionScale) {
}
}

View file

@ -0,0 +1,260 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
/* this is an alternative collision system because the original Collision Mesh system is fuck ass and sucks ass while MUNCHING on performance
and makes the fast thread not so fast
this is Separating Axis Theorem testing for two convex polyhedra or something idk im no rocket scientist, but i think this shi needs comments for once
say we have 2 faces, face A and face B,this thingy-ma-jig tests face normals of A, face normals of B,
and cross products of every edge pair (A x B) for edge-edge contacts, the axis with the smallest
overlap (least "penetration" or intersection amount) is the collision normal,
face contacts are "resolved into a full contact manifold"(whatever that means) by clipping the incident face
against the side planes of the reference face, this is what gives stable multipoint contact (e.g. a box resting flat on a floor gets 4 contact points, not 1)
I'm not gonna lie, I asked Google AI Overview for help on this one, and it did NOT help, but it *did* provide some pretty useful information on how to do it
For a good explanation, visit this https://dyn4j.org/2010/01/sat/*/
public class SeparatingAxisTheoremTester {
private static final float BIAS = 0.005f;
private static final Vector3f Distance1 = new Vector3f();
private static final Vector3f Distance2 = new Vector3f();
private static final Vector3f Axis = new Vector3f();
private static final Vector3f Normal = new Vector3f();
public static class Contact {
public Vector3f Point;
public float Penetration;
}
public static class CollisionResult {
public Vector3f Normal;
public float Penetration;
public final List<Contact> Contacts = new ArrayList<>();
}
//as much as i'd like to multithread the fuck out of this, im putting it as synchronised so it doesn't get messy, and so memory management is smoother, i want to minimize the amount of Vectors creating during loops
public synchronized static CollisionResult TestCollision(ConvexMesh Mesh1, ConvexMesh Mesh2){
float BestOverlap = Float.MAX_VALUE;
Vector3f BestAxis = null;
ConvexMesh.Face BestFace =null;
ConvexMesh BestFaceOwner = null;
for(ConvexMesh.Face face : Mesh1.Faces){
Vector3f Axis = Mesh1.WorldFaceNormal(face);
Float Overlap = AxisOverlap(Mesh1,Mesh2,Axis);
if(Overlap == null) return null; // Axis do not overlap, no collision is happening, return to whatever fuck ass code is running
if(Overlap < BestOverlap){
BestOverlap = Overlap;
BestAxis = Axis;
BestFace = face;
BestFaceOwner = Mesh1;
}
}
for(ConvexMesh.Face face : Mesh2.Faces){
Vector3f Axis = Mesh2.WorldFaceNormal(face);// /\
Float Overlap = AxisOverlap(Mesh1,Mesh2,Axis);// |
if(Overlap == null) return null; //what he said up there |
if(Overlap < BestOverlap){
BestOverlap = Overlap;
BestAxis = Axis;
BestFace = face;
BestFaceOwner = Mesh2;
}
}
Vector3f BestEdgeAxis = null;
float BestEdgeOverlap = Float.MAX_VALUE;
int[] BestEdge1 = null, BestEdge2 = null;
for(int[]Edge1 : Mesh1.Edges){
Vector3f Point11 = Mesh1.WorldVertex(Edge1[0]);
Vector3f Point12 = Mesh1.WorldVertex(Edge1[1]);
Distance1.set(Point12).sub(Point11);
for(int[]Edge2 : Mesh2.Edges){
Vector3f Point21 = Mesh2.WorldVertex(Edge2[0]);
Vector3f Point22 = Mesh2.WorldVertex(Edge2[1]);
Distance2.set(Point22).sub(Point21);
Axis.set(Distance1).cross(Distance2);
if(Axis.lengthSquared() < T4Math.Math3D.epsilon) continue; //if its parallel or close to parallel jump town (continue here skips it to the next item in the loop)
Axis.normalize();
Float Overlap = AxisOverlap(Mesh1, Mesh2, Axis);
if(Overlap == null) return null;
if(Overlap < BestEdgeOverlap){
BestEdgeOverlap = Overlap;
BestEdgeAxis = Axis;
BestEdge1 = Edge1;
BestEdge2 = Edge2;
}
}
}
Vector3f Center1 = MeshCenter(Mesh1);
Vector3f Center2 = MeshCenter(Mesh2);
CollisionResult result = new CollisionResult();
if(BestEdgeAxis != null && BestEdgeOverlap < BestOverlap - BIAS){ // they're furiously EDGING (edge-edge collision)
Vector3f Normal = new Vector3f(BestEdgeAxis);
if(new Vector3f(Center2).sub(Center1).dot(Normal) < 0) Normal.negate();
Vector3f Point11 = Mesh1.WorldVertex(BestEdge1[0]), Point12 = Mesh1.WorldVertex(BestEdge1[1]);
Vector3f Point21 = Mesh2.WorldVertex(BestEdge2[0]), Point22 = Mesh2.WorldVertex(BestEdge2[1]);
Vector3f[] Closest = ClosestPointsSegment2(Point11,Point12,Point21,Point22);
result.Normal = Normal;
result.Penetration = BestEdgeOverlap;
Contact contact = new Contact();
contact.Point = new Vector3f(Closest[0]).lerp(Closest[1],0.5f);
contact.Penetration = BestEdgeOverlap;
result.Contacts.add(contact);
return result;
}
//Face collision/clipping
ConvexMesh ReferenceMesh = BestFaceOwner;
ConvexMesh IncidentMesh = (ReferenceMesh == Mesh1) ? Mesh2 : Mesh1;
ConvexMesh.Face ReferenceFace = BestFace;
Vector3f ReferenceFaceNormalWorld = ReferenceMesh.WorldFaceNormal(ReferenceFace);
ConvexMesh.Face IncidentFace = FindIncidentFace(IncidentMesh, ReferenceFaceNormalWorld);
List<Vector3f> IncidentPolygon = new ArrayList<>();
for(int Index : IncidentFace.Indices) IncidentPolygon.add(IncidentMesh.WorldVertex(Index));
List<Vector3f> ReferencePolygon = new ArrayList<>();
for(int Index : ReferenceFace.Indices) ReferencePolygon.add(ReferenceMesh.WorldVertex(Index));
List<Vector3f> Clipped = ClipPolygonAgainstFace(IncidentPolygon, ReferencePolygon, ReferenceFaceNormalWorld);
Vector3f ReferencePoint = ReferencePolygon.get(0);
Vector3f DepthVector = new Vector3f();
for(Vector3f point : Clipped){
float Depth = DepthVector.set(ReferencePoint).sub(point).dot(ReferenceFaceNormalWorld);
if(Depth >= 0){
Contact contact = new Contact();
contact.Point = point;
contact.Penetration = Depth;
result.Contacts.add(contact);
}
}
if(result.Contacts.isEmpty())return null;
Normal.set(BestAxis);
if(new Vector3f(Center2).sub(Center1).dot(Normal) < 0) Normal.negate();
result.Normal = Normal;
result.Penetration = BestOverlap;
return result;
}
private synchronized static Float AxisOverlap(ConvexMesh mesh1, ConvexMesh mesh2, Vector3f axis) {
float[] Range1 = ProjectOntoAxis(mesh1, axis);
float[] Range2 = ProjectOntoAxis(mesh2, axis);
float Overlap = Math.min(Range1[1],Range2[1]) - Math.max(Range1[0],Range2[0]);
return (Overlap < 0) ? null : Overlap;
}
private synchronized static float[] ProjectOntoAxis(ConvexMesh mesh, Vector3f axis){
float minimum = Float.MAX_VALUE, maximum = -Float.MAX_VALUE;
for(int i = 0; i < mesh.LocalVertices.size(); i++){
float distance = mesh.WorldVertex(i).dot(axis);
minimum = Math.min(minimum, distance);
maximum = Math.max(maximum, distance);
}
return new float[]{minimum,maximum};
}
private synchronized static Vector3f MeshCenter(ConvexMesh mesh){
Vector3f Center = new Vector3f();
for(int i = 0; i < mesh.LocalVertices.size(); i++) Center.add(mesh.WorldVertex(i));
Center.div(mesh.LocalVertices.size());
return Center;
}
private synchronized static ConvexMesh.Face FindIncidentFace(ConvexMesh mesh, Vector3f ReferenceNormal){
ConvexMesh.Face Best = null;
float BestDot = Float.MAX_VALUE;
for(ConvexMesh.Face face : mesh.Faces){
float d = mesh.WorldFaceNormal(face).dot(ReferenceNormal);
if(d < BestDot) {
BestDot = d;
Best = face;
}
}
return Best;
}
private synchronized static List<Vector3f> ClipPolygonAgainstFace(List<Vector3f> Incident, List<Vector3f> ReferencePoly, Vector3f ReferenceNormal){
List<Vector3f> Output = new ArrayList<>(Incident);
int Count = ReferencePoly.size();
for(int i = 0; i < Count; i++){
if(Output.isEmpty())break;
Vector3f Point1 = ReferencePoly.get(i);
Vector3f Point2 = ReferencePoly.get((i + 1) % Count);
Vector3f Edge = new Vector3f(Point2).sub(Point1);
Vector3f SidePlaneNormal = new Vector3f(Edge).cross(ReferenceNormal).normalize();
float Offset = SidePlaneNormal.dot(Point1);
List<Vector3f> Input=Output;
Output = new ArrayList<>();
for(int j = 0; j < Input.size(); j++){
Vector3f Current = Input.get(j);
Vector3f Previous = Input.get((j - 1 + Input.size()) % Input.size());
float dCurrent = SidePlaneNormal.dot(Current) - Offset;
float dPrev = SidePlaneNormal.dot(Previous) - Offset;
if (dCurrent <= 0) {
if (dPrev > 0) Output.add(IntersectEdge(Previous, Current, SidePlaneNormal, Offset));
Output.add(Current);
} else if (dPrev <= 0) {
Output.add(IntersectEdge(Previous, Current, SidePlaneNormal, Offset));
}
}
}
return Output;
}
private synchronized static Vector3f IntersectEdge(Vector3f Vertex1, Vector3f Vertex2, Vector3f Normal, float Offset) {
Vector3f Distance = new Vector3f(Vertex2).sub(Vertex1);
float Denominator = Normal.dot(Distance);
float t = (Math.abs(Denominator) < T4Math.Math3D.epsilon) ? 0 : (Offset - Normal.dot(Vertex1)) / Denominator;
return new Vector3f(Vertex1).add(Distance.mul(t));
}
private synchronized static Vector3f[] ClosestPointsSegment2(Vector3f p1, Vector3f q1, Vector3f p2, Vector3f q2) {
Vector3f d1 = new Vector3f(q1).sub(p1);
Vector3f d2 = new Vector3f(q2).sub(p2);
Vector3f r = new Vector3f(p1).sub(p2);
float a = d1.dot(d1), e = d2.dot(d2), f = d2.dot(r);
float s, t;
if (a <= 1e-8f && e <= 1e-8f) {
s = 0; t = 0;
} else if (a <= 1e-8f) {
s = 0; t = clamp(f / e, 0, 1);
} else {
float c = d1.dot(r);
if (e <= 1e-8f) {
t = 0; s = clamp(-c / a, 0, 1);
} else {
float b = d1.dot(d2);
float denom = a * e - b * b;
s = (Math.abs(denom) > 1e-8f) ? clamp((b * f - c * e) / denom, 0, 1) : 0;
t = (b * s + f) / e;
if (t < 0) { t = 0; s = clamp(-c / a, 0, 1); }
else if (t > 1) { t = 1; s = clamp((b - c) / a, 0, 1); }
}
}
Vector3f c1 = new Vector3f(p1).add(new Vector3f(d1).mul(s));
Vector3f c2 = new Vector3f(p2).add(new Vector3f(d2).mul(t));
return new Vector3f[]{c1, c2};
}
private static float clamp(float v, float lo, float hi) {
return Math.max(lo, Math.min(hi, v));
}
}

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Logic.Physics;
import org.joml.Vector3f;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import java.util.ArrayList;
import java.util.HashMap;
@ -13,30 +13,52 @@ public class WorldPhysicsManager {
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 int PhysicsTick = 0;
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 final Map<String, IPhysicsController> PhysicsObjects = new HashMap<>();
public static List<String> ActiveControllers = new ArrayList<>();
public static float FixedTimeStep = 1f / 60f;
public static int MaxSubStepsPerFrame = 8;
private static float Accumulator = 0f;
public static void SteppedPhysicsTick(float DeltaTime, float TargetFrameTime){
if(EngineConfig.getInstance().IsEngineThrottled()) return;
DeltaTime *= EngineConfig.getInstance().PhysicsSpeed();
Accumulator += DeltaTime;
float maxAccumulated = FixedTimeStep * MaxSubStepsPerFrame;
if (Accumulator > maxAccumulated) Accumulator = maxAccumulated;
int stepsRun = 0;
while (Accumulator >= FixedTimeStep && stepsRun < MaxSubStepsPerFrame) {
PhysicsTick++;
for (int i = 0; i < ActiveControllers.size(); i++) {
if (PhysicsObjects.get(ActiveControllers.get(i)) != null) {
PhysicsObjects.get(ActiveControllers.get(i)).PhysicsTick(FixedTimeStep, TargetFrameTime);
}
}
Accumulator -= FixedTimeStep;
stepsRun++;
}
for (int i = 0; i < ActiveControllers.size(); i++) {
PhysicsObjects.get(ActiveControllers.get(i)).Collisions().clear();
}
}
public static void PhysicsTick(float DeltaTime, float TargetFrameTime){
for(int i = 0; i < ActiveControllers.size(); i++){
PhysicsObjects.get(ActiveControllers.get(i)).PhysicsTick(DeltaTime, TargetFrameTime);
DeltaTime *= EngineConfig.getInstance().PhysicsSpeed();
if(!EngineConfig.getInstance().IsEngineThrottled()) {
PhysicsTick++;
for (int i = 0; i < ActiveControllers.size(); i++) {
if(PhysicsObjects.get(ActiveControllers.get(i)) != null) {
PhysicsObjects.get(ActiveControllers.get(i)).PhysicsTick(DeltaTime, TargetFrameTime);
}
}
for (int i = 0; i < ActiveControllers.size(); i++) {
PhysicsObjects.get(ActiveControllers.get(i)).Collisions().clear();
}
}
}

View file

@ -6,30 +6,29 @@ import imgui.ImGuiViewport;
import imgui.flag.ImGuiCond;
import imgui.flag.ImGuiWindowFlags;
import net.halbear.Terrain4J.EngineCore.Audio.AudioManager;
import net.halbear.Terrain4J.EngineCore.Audio.AudioSource;
import net.halbear.Terrain4J.EngineCore.Audio.SoundBuffer;
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.Physics.LegacyCollision.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.LegacyPhysicsController;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.ConvexMesh;
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;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.LoadingScreen;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.PerformanceOverlay;
import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor;
import net.halbear.Terrain4J.EngineCore.Profiling.GPUProfiler;
import net.halbear.Terrain4J.EngineCore.Vulkan.GUI.GuiTexture;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MaterialData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.ForwardSceneRender;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.openal.AL11;
@ -80,14 +79,14 @@ 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);
Melona.CreatePhysicsController();
Melona.GetPhysicsController()
Melona.CreatePhysicsController(LegacyPhysicsController.CollisionType.Sensor);
/* Melona.GetPhysicsController()
.AddCollisionSensor(new Vector3f(0,0,0))
.AddCollisionSensor(new Vector3f(0,1.8f,0))
.AddCollisionSensor(new Vector3f(0,2.4f,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))
.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))
@ -95,13 +94,15 @@ public class GameCore implements GameLogic {
.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));
.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);
Melona.SetPhysicsType(Actor.PhysicsControllerType.T4JLegacyCollision);
Melona.GetCollisionMesh().GenerateSensorsFromVertices();
} catch(IOException e){
Logger.error("Could Not Generate Collison Mesh [{}]",e);
}
@ -145,33 +146,41 @@ public class GameCore implements GameLogic {
return new InitData(models,materials,guiTextures);
}
private static final Vector3f SpawnPosition = new Vector3f();
private static final Vector3f SpawnDirection = new Vector3f();
private static final Matrix4f rotationMatrix = new Matrix4f();
public void SpawnNewActorEditorComponent(EngineInstance engineInstance){
var scene = engineInstance.scene();
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){
Logger.error("Could Not Generate Collison Mesh [{}]",e);
}
var scene = engineInstance.scene();
Camera camera = scene.GetCamera();
SpawnNewActorEditorComponent(engineInstance, new Vector3f(camera.GetPosition()),camera.GetRotation(),camera.GetDirection());
}
public void SpawnNewActor(EngineInstance engineInstance, Vector3f Position, Vector3f Rotation, Vector3f Direction){
var scene = engineInstance.scene();
Camera camera = scene.GetCamera();
SpawnPosition.set(Position);
rotationMatrix.set(new Matrix4f().rotateX(Rotation.x).rotateY(Rotation.y).rotateZ(Rotation.z));
SpawnDirection.set(new Vector3f(Direction));
rotationMatrix.positiveZ(SpawnDirection).negate().mul(5);
SpawnPosition.add(SpawnDirection);
var Melona = new Actor("MelonaSpawned" + scene.GetActors().size(), MelonaData.ID(), new Vector3f(SpawnPosition));
Melona.CreatePhysicsController( ConvexMesh.Box(0.75f,1,0.5f),20);
scene.AddActor(Melona);
}
public void SpawnNewActorEditorComponent(EngineInstance engineInstance, Vector3f Position, Vector3f Rotation, Vector3f Direction){
var scene = engineInstance.scene();
Camera camera = scene.GetCamera();
SpawnPosition.set(Position);
rotationMatrix.set(new Matrix4f().rotateX(Rotation.x).rotateY(Rotation.y).rotateZ(Rotation.z));
SpawnDirection.set(new Vector3f(Direction));
rotationMatrix.positiveZ(SpawnDirection).negate().mul(5);
SpawnPosition.add(SpawnDirection);
var Melona = new Actor("MelonaSpawned" + scene.GetActors().size(), MelonaData.ID(), new Vector3f(SpawnPosition));
Melona.CreatePhysicsController( ConvexMesh.Box(0.75f,1.2f,0.5f),20);
scene.AddActor(Melona);
ActorEditorComponents.add(new EditorActorComponent(Melona,new Vector3f(scene.GetCamera().GetPosition()),new Vector3f(1.0f,1.8f,1.0f)));
}
@ -235,8 +244,8 @@ public class GameCore implements GameLogic {
KeyboardInput input = window.getKeyboardInput();
float MovementDist = (float)(FrameDiffNanoSeconds / 1000000L) * MOVEMENT_SPEED;
Camera camera = scene.GetCamera();
if(input.keySinglePress(GLFW_KEY_F3)){
GUI_MODE = (GUI_MODE + 1) % 2;
if(input.keyPressed(GLFW_KEY_ENTER)) {
SpawnNewActor(engineInstance, new Vector3f( (float)(2 * Math.random()),20 + (float)(10 * Math.random()), (float)(2 * Math.random())),new Vector3f(-1f + (float)(2 * Math.random()),-1f + (float)(2 * Math.random()),-1f + (float)(2 * Math.random())),new Vector3f((float)Math.random(),(float)Math.random(),(float)Math.random()));
}
if(input.keyPressed(GLFW_KEY_1)) {
Gimbal.universalType = Gimbal.GimbalType.Move;

View file

@ -1,19 +1,30 @@
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.Logic.Physics.LegacyCollision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.IPhysicsController;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyPhysicsController;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.ConvexMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidCollision;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidPhysicsController;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Actor{
public enum PhysicsControllerType{
RigidBodySeparatingAxisTheory,
T4JLegacyCollision
}
public static final Map<String, Actor> Actors = new HashMap<>();
public static final List<String> ActorNames = new ArrayList<>();
private final String ID;
private final String ModelID;
private final Matrix4f ModelMatrix;
@ -21,10 +32,15 @@ public class Actor{
private final Quaternionf Rotation;
private CollisionMesh collisionMesh;
private Vector3f Scale;
private PhysicsController physicsController;
private IPhysicsController physicsController;
private final Vector3f PivotOffset;
private final Vector3f AdditionalPivotOffset;
private final Vector3f TemporaryVector;
public PhysicsControllerType PhysicsType = PhysicsControllerType.RigidBodySeparatingAxisTheory;
public void SetPhysicsType(PhysicsControllerType type){
this.PhysicsType = type;
}
public CollisionMesh GetCollisionMesh(){
return collisionMesh;
@ -32,6 +48,7 @@ public class Actor{
public Actor(String ID, String ModelID, Vector3f Position){
this.ID = ID;
ActorNames.add(ID);
this.ModelID = ModelID;
this.Position = Position;
TemporaryVector = new Vector3f();
@ -58,10 +75,15 @@ public class Actor{
return this;
}
public PhysicsController GetPhysicsController(){return physicsController;}
public IPhysicsController GetPhysicsController(){return physicsController;}
public Actor CreatePhysicsController(){
physicsController = new PhysicsController(ID);
public Actor CreatePhysicsController(ConvexMesh mesh, float Mass){
physicsController = new RigidPhysicsController(ID + "_RIGID_CONTROLLER", ID).CreateRigidBody(mesh, Mass);
return this;
}
public Actor CreatePhysicsController(LegacyPhysicsController.CollisionType collisionType){
physicsController = new LegacyPhysicsController(ID, collisionType);
return this;
}
@ -79,7 +101,11 @@ public class Actor{
Rotation.w = 1.0f;
return this;
}
public Actor SetRotation(Quaternionf rot){
Rotation.set(rot);
UpdateModelMatrix();
return this;
}
public Actor SetRotation(Vector3f rot){
Rotation.rotateX(rot.x);
Rotation.rotateY(rot.y);
@ -101,16 +127,12 @@ public class Actor{
return this;
}
public Actor SetPosition(float x, float y, float z){
Position.x = x;
Position.y = y;
Position.z = z;
Position.set(x,y,z);
UpdateModelMatrix();
return this;
}
public Actor SetScale(float x, float y, float z){
Scale.x = x;
Scale.y = y;
Scale.z = z;
Scale.set(x,y,z);
UpdateModelMatrix();
return this;
}

View file

@ -14,6 +14,9 @@ public class Camera {
private float FOV = (float) (60 * EngineConfig.DegToRad);
private boolean Sprint;
public Vector3f GetDirection(){
return Direction;
}
public Camera CloneCamera(){
Camera NewCam = new Camera();
NewCam.SetPosition(Position.x, Position.y, Position.z);

View file

@ -5,19 +5,21 @@ import imgui.ImGui;
import imgui.ImVec2;
import imgui.flag.ImGuiCol;
import imgui.flag.ImGuiWindowFlags;
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.Logic.Physics.LegacyCollision.CollisionManager;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionMesh;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionPoly;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.CollisionSensor;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyPhysicsController;
import net.halbear.Terrain4J.EngineCore.Main.GameCore;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIOverlay;
import net.halbear.Terrain4J.EngineCore.Main.Scene.IMoveableActorComponent;
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.T4Math;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.tinylog.Logger;
import java.util.HashMap;
import java.util.List;
@ -61,6 +63,17 @@ public class Gimbal implements GUIOverlay {
for(int j = 0; j < polygons.length; j++){
DrawCollisionPolygon(engineInstance.scene().GetCamera().GetViewMatrix(), engineInstance.scene().GetProjection().GetProjectionMatrix(),winWidth,winHeight,polygons[j]);
}
if(mesh.ParentID != "NULL" && Actor.Actors.get(mesh.ParentID) != null) {
Actor actor = Actor.Actors.get(mesh.ParentID);
if(actor.PhysicsType == Actor.PhysicsControllerType.T4JLegacyCollision) {
LegacyPhysicsController physicsController = (LegacyPhysicsController) actor.GetPhysicsController();
List<CollisionSensor> RawCollisionSesnors = physicsController.collisionSensors;
// Vector3f[] CollisionSensors = new Vector3f[RawCollisionSesnors.size()];
for (int j = 0; j < RawCollisionSesnors.size(); j++) {
DrawCollisionSensor(engineInstance.scene().GetCamera().GetViewMatrix(), engineInstance.scene().GetProjection().GetProjectionMatrix(), winWidth, winHeight, RawCollisionSesnors.get(j).GetTransformedCollisionSensor(), actor.GetPosition());
}
}
}
}
}
for(int i = 0; i < gimbals.size(); i++){
@ -106,6 +119,17 @@ public class Gimbal implements GUIOverlay {
DrawGimbalInWorld(engineInstance.scene().GetCamera().GetViewMatrix(), engineInstance.scene().GetProjection().GetProjectionMatrix(),engineInstance.window().getWidth(),engineInstance.window().getHeight());
}
public static void DrawCollisionSensor(Matrix4f viewMatrix, Matrix4f projMatrix, int winWidth, int winHeight, Vector3f Point, Vector3f Origin){
if(Point == null) return;
Vector3f v1 = T4Math.Math3D.GetScreenCoordFromWorldSpace(Point, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f v2 = T4Math.Math3D.GetScreenCoordFromWorldSpace(Origin, viewMatrix, projMatrix, winWidth, winHeight);
if(v1 == null || v2 == null) return;
ImDrawList drawList = ImGui.getBackgroundDrawList();
int colour = ImGui.getColorU32(1.0f, 0.0f, 0.0f, 0.5f);
drawList.addLine(v1.x, v1.y, v2.x, v2.y, colour, 1f);
drawList.addCircle(new ImVec2(v1.x,v1.y),6,colour,5f);
}
public static void DrawCollisionPolygon(Matrix4f viewMatrix, Matrix4f projMatrix, int winWidth, int winHeight, CollisionPoly polygon){
if(polygon == null || polygon.v1() == null || polygon.v2() == null || polygon.v3() == null) return;
Vector3f v1 = T4Math.Math3D.GetScreenCoordFromWorldSpace(polygon.v1(), viewMatrix, projMatrix, winWidth, winHeight);
@ -133,6 +157,7 @@ public class Gimbal implements GUIOverlay {
Vector3f ZYPoint = new Vector3f(Position.x,Position.y + (GimbalArmLengths.y/2.5f),Position.z + (GimbalArmLengths.z/2.5f));
Vector3f XPoint = new Vector3f(Position.x + GimbalArmLengths.x,Position.y,Position.z);
Vector3f XYPoint = new Vector3f(Position.x + (GimbalArmLengths.x/2.5f),Position.y+ (GimbalArmLengths.y/2.5f),Position.z);
Vector3f XYZPoint = new Vector3f(Position.x + (GimbalArmLengths.x/1.5f),Position.y+ (GimbalArmLengths.y/1.5f),Position.z+ (GimbalArmLengths.y/1.5f));
Vector3f XZPoint = new Vector3f(Position.x + (GimbalArmLengths.x/2.5f),Position.y,Position.z + (GimbalArmLengths.z/2.5f));
Vector3f YPoint = new Vector3f(Position.x,Position.y + GimbalArmLengths.y,Position.z);
Vector3f ZPoint = new Vector3f(Position.x,Position.y,Position.z + GimbalArmLengths.z);
@ -143,6 +168,7 @@ public class Gimbal implements GUIOverlay {
Vector3f ZCScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(ZCPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f ZYScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(ZYPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f XYScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(XYPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f XYZScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(XYZPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f XZScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(XZPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f XPointScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(XPoint, viewMatrix, projMatrix, winWidth, winHeight);
Vector3f YPointScreenPos = T4Math.Math3D.GetScreenCoordFromWorldSpace(YPoint, viewMatrix, projMatrix, winWidth, winHeight);
@ -232,11 +258,10 @@ public class Gimbal implements GUIOverlay {
if (ImGui.isItemActive()) {
switch(universalType) {
case Rotate:
parent.AddRotation(new Vector3f((float) ((MouseDiff.x + MouseDiff.y) / (float) (winWidth / 10.0f)),0,0));
parent.AddRotation(new Vector3f((float) ((MouseDiff.x + MouseDiff.y) / (float) (winHeight / 10.0f)),0,0));
break;
case Resize:
parent.AddScale(new Vector3f(0, (MouseDiff.x + MouseDiff.y) / (float) (winWidth / 10.0f), 0));
parent.AddScale(new Vector3f(0, (MouseDiff.x + MouseDiff.y) / (float) (winHeight / 10.0f), 0));
break;
case Move:
default:
@ -303,6 +328,20 @@ public class Gimbal implements GUIOverlay {
new ImVec2(YCScreenPos.x,YCScreenPos.y),
},4,color);
}
if(universalType == GimbalType.Resize && XYZScreenPos != null) {
color = ImGui.getColorU32(1.0f, 0.8f, 0.0f, 1.0f);
drawList.addLine(CentreScreenPos.x, CentreScreenPos.y, XYZScreenPos.x, XYZScreenPos.y, color, thickness);
float adjustedX = XYZScreenPos.x - (buttonWidth / 2.0f);
float adjustedY = XYZScreenPos.y - (buttonHeight / 2.0f);
ImGui.setCursorScreenPos(adjustedX, adjustedY);
ImGui.button("XYZ##" + ID, buttonWidth, buttonHeight);
drawList.addRectFilled(XYZScreenPos.x- (thickness * ButtonMultiplier/2.0f), XYZScreenPos.y- (thickness * ButtonMultiplier/2.0f), XYZScreenPos.x+ (thickness * ButtonMultiplier/2.0f), XYZScreenPos.y + (thickness * ButtonMultiplier/2.0f), color);
if (ImGui.isItemActive()) {
parent.AddScale(new Vector3f((MouseDiff.x + MouseDiff.y) / (float) (winWidth / 10.0f), (MouseDiff.x + MouseDiff.y) / (float) (winWidth / 10.0f), (MouseDiff.x + MouseDiff.y) / (float) (winWidth / 10.0f)));
parent.UpdateAll();
}
}
ImGui.popStyleColor(3);
}
}

View file

@ -148,8 +148,17 @@ public class Scene implements IScene {
public void SetActiveCameraFrame(String frameName){SetCameraFrame = frameName;}
public String GetActiveCameraFrame(){return ActiveCameraFrame;}
public Camera GetCamera(){return camera;}
public void AddActor(Actor NewActor){Actors.put(NewActor.GetID(), NewActor);}
public List<Actor> GetActors(){return Actors.values().stream().toList();}
public void AddActor(Actor NewActor){
Actors.put(NewActor.GetID(), NewActor);
ActiveActors.add(NewActor.GetID());
}
public List<Actor> GetActors(){
List<Actor> actors = new ArrayList<>();
for(int i = 0; i < ActiveActors.size(); i++){
actors.add(Actors.get(ActiveActors.get(i)));
}
return actors;
}
public Project3D GetProjection(){return Projection;}
public void RemoveAllActors(){Actors.clear();}
public Actor RemoveActor(Actor actor){return Actors.remove(actor.GetID());}
@ -158,6 +167,8 @@ public class Scene implements IScene {
}
public int GUI_MODE = 0;
@Override
public void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
IScene scene = engineInstance.scene();
@ -166,14 +177,8 @@ public class Scene implements IScene {
KeyboardInput input = window.getKeyboardInput();
float MovementDist = (float)(FrameDiffNanoSeconds / 1000000L) * MOVEMENT_SPEED;
Camera camera = scene.GetCamera();
if(input.keyPressed(GLFW_KEY_1)){
}
if(input.keyPressed(GLFW_KEY_2)){
}
if(input.keyPressed(GLFW_KEY_3)){
if(input.keySinglePress(GLFW_KEY_F3)){
GUI_MODE = (GUI_MODE + 1) % 2;
}
if(input.keyPressed(GLFW_KEY_W)){
camera.MoveForward(MovementDist);
@ -196,6 +201,8 @@ public class Scene implements IScene {
if(input.keyPressed(GLFW_KEY_LEFT_CONTROL)){
camera.Sprint(true);
} else camera.Sprint(false);
if(GUI_MODE == 0 && !ActiveGUIs.contains("PerformanceOverlay")) ActiveGUIs.add("PerformanceOverlay");
else if (GUI_MODE != 0) ActiveGUIs.remove("PerformanceOverlay");
}
@Override

View file

@ -1,10 +1,14 @@
package net.halbear.Terrain4J.EngineCore.Main.Util.Maths;
import imgui.ImVec2;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyCollision.CollisionPoly;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.LegacyPhysicsController;
import org.joml.*;
import java.lang.Math;
import static net.halbear.Terrain4J.EngineCore.Logic.Physics.WorldPhysicsManager.AirDensityKGM3;
import static net.halbear.Terrain4J.EngineCore.Logic.Physics.WorldPhysicsManager.GravityMS;
public class T4Math {
public static double Log2(int n){
return Math.log(n) / Math.log(n);
@ -25,9 +29,140 @@ public class T4Math {
}
}
public static class Math3D {
public static float epsilon = 1e-6f;
public static class Physics{
public static Vector3f CalculateDragCoefficients(Vector3f TerminalVelocitiesMS, float MassKG, Vector3f CrossSectionAreas, Vector3f Scale){
return (CalculateDragFromTerminalVelocities(TerminalVelocitiesMS,MassKG *Math.abs((Scale.x + Scale.y + Scale.z)/3),new Vector3f(CrossSectionAreas)));
}
public static Vector3f CalculateDrag(Vector3f Velocities, Vector3f DragCoefficients, Vector3f CrossSectionAreas, Vector3f Scale){
return (CalculateRealDrag(Velocities,DragCoefficients,new Vector3f(CrossSectionAreas)));
}
public static float CalculateFallSpeed(float MassKG, Vector3f Drag, Vector3f Scale){
return CalculateGravityForce(MassKG *Math.abs((Scale.x + Scale.y + Scale.z)/3), Drag);
}
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 class ComplexCollision{
public static LegacyPhysicsController.CollisionResult ComputePolyToPolyIntersection(CollisionPoly poly1, CollisionPoly poly2){
LegacyPhysicsController.CollisionResult result = new LegacyPhysicsController.CollisionResult();
if(poly1 == null || poly2 == null) return null;
Vector3f[] vertices1 = new Vector3f[]{new Vector3f().set(poly1.v1()),new Vector3f().set(poly1.v2()),new Vector3f().set(poly1.v3())};
Vector3f[] vertices2 = new Vector3f[]{new Vector3f().set(poly2.v1()),new Vector3f().set(poly2.v2()),new Vector3f().set(poly2.v3())};
Vector3f e1 = new Vector3f().set(poly1.v2()).sub(poly1.v1());
Vector3f e2 = new Vector3f().set(poly1.v3()).sub(poly1.v1());
Vector3f Normal1 = new Vector3f().set(e1).cross(e2).normalize();
float Distance1 = -Normal1.dot(poly1.v1());
float[] du = new float[3];
for(int i = 0; i < 3; i++) du[i] = Math.abs(Normal1.dot(vertices2[i]) + Distance1) < Math3D.epsilon ? 0 : Normal1.dot(vertices2[i]) + Distance1;
if ((du[0] > 0 && du[1] > 0 && du[2] > 0) || (du[0] < 0 && du[1] < 0 && du[2] < 0)) return result;
Vector3f f1 = new Vector3f().set(poly2.v2()).sub(poly2.v1());
Vector3f f2 = new Vector3f().set(poly2.v3()).sub(poly2.v1());
Vector3f Normal2 = new Vector3f().set(f1).cross(f2).normalize();
float Distance2 = -Normal2.dot(poly2.v1());
float[] dv = new float[3];
for(int i = 0; i < 3; i++) dv[i] = Math.abs(Normal2.dot(vertices1[i]) + Distance2) < Math3D.epsilon ? 0 : Normal2.dot(vertices1[i]) + Distance2;
if ((dv[0] > 0 && dv[1] > 0 && dv[2] > 0) || (dv[0] < 0 && dv[1] < 0 && dv[2] < 0)) return result;
Vector3f IntersectionDirection = new Vector3f().set(Normal1).cross(Normal2);
if (IntersectionDirection.lengthSquared() < Math3D.epsilon) {
return result;
}
IntersectionDirection.normalize();
Vector3f[] Segment1 = GetIntersectionPoints(poly1, dv);
Vector3f[] Segment2 = GetIntersectionPoints(poly2, du);
if (Segment1 == null || Segment2 == null) return result;
return ComputerSegmentOverlap(Segment1, Segment2, IntersectionDirection,Normal1);
}
public static Vector3f[] GetIntersectionPoints(CollisionPoly poly, float[] d){
Vector3f[] vertices1 = new Vector3f[]{new Vector3f().set(poly.v1()),new Vector3f().set(poly.v2()),new Vector3f().set(poly.v3())};
Vector3f[] Points = new Vector3f[2];
int Count = 0;
for(int i = 0; i < 3; i++) {
int next = (i + 1) % 3;
if (d[i] * d[next] < 0 || (d[i] == 0 && d[next] != 0) || (d[i] != 0 && d[next] == 0)) {
float Factor = d[i] / (d[i] - d[next]);
if (Float.isNaN(Factor) || Float.isInfinite(Factor)) Factor = 0.0f;
Vector3f Intersect = new Vector3f().set(vertices1[next]).sub(vertices1[i])
.mul(Factor).add(vertices1[i]);
if(Count < 2){
Points[Count++] = Intersect;
}
}
}
return Count == 2 ? Points : null;
}
public static LegacyPhysicsController.CollisionResult ComputerSegmentOverlap(Vector3f[] Segment1, Vector3f[] Segment2, Vector3f Direction, Vector3f CollisionNormal){
LegacyPhysicsController.CollisionResult result = new LegacyPhysicsController.CollisionResult();
float Poly1Min = Segment1[0].dot(Direction);
float Poly1Max = Segment1[1].dot(Direction);
Vector3f point1Min = Segment1[0], point1Max = Segment1[1];
if(Poly1Min > Poly1Max){
float temp = Poly1Max;
Poly1Max = Poly1Min;
Poly1Min = temp;
Vector3f pTemp = point1Min;
point1Min = point1Max;
point1Max = pTemp;
}
float Poly2Min = Segment2[0].dot(Direction);
float Poly2Max = Segment2[1].dot(Direction);
Vector3f point2Min = Segment2[0], point2Max = Segment2[1];
if(Poly2Min > Poly2Max){
float temp = Poly2Max;
Poly2Max = Poly2Min;
Poly2Min = temp;
Vector3f pTemp = point2Min;
point2Min = point2Max;
point2Max = pTemp;
}
if (Poly1Max < Poly2Min - Math3D.epsilon || Poly2Max < Poly1Min - Math3D.epsilon) return result;
float overlapMin = Math.max(Poly1Min, Poly2Min);
float overlapMax = Math.min(Poly1Max, Poly2Max);
float segmentLength = overlapMax - overlapMin;
result.Intersects = true;
result.StartPoint = new Vector3f((Poly1Min > Poly2Min) ? point1Min : point2Min);
result.EndPoint = new Vector3f((Poly1Max < Poly2Max) ? point1Max : point2Max);
result.CollisionNormal = new Vector3f(CollisionNormal);
result.CollisionDepth = segmentLength;
return result;
}
}
public static class Math3D {
public static float epsilon = 1e-8f;
public static boolean TestAabbAabb(
@ -56,11 +191,12 @@ public class T4Math {
return null;
}
public static Vector3f LineCollisionPointWithPoly(Vector3f Origin, Vector3f Point, Vector3f[] Vertices){
public static Vector3f LineCollisionPointWithPoly(Vector3f Origin, Vector3f Point, Vector3f[] Vertices, Vector2f ReturnTestValue){
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) {
ReturnTestValue.set(Test,1-Test);
return new Vector3f(LineDirection).mul(Test).add(Origin);
}
return null;