From 5bed26560fcf0e5bb85d73869aae9205bc99743e Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Fri, 28 May 2021 22:23:52 +0530 Subject: [PATCH] implement marble-marble collision --- .gitignore | 3 +- compile-c.hxml | 8 ++ src/Main.hx | 10 +++ src/Marble.hx | 100 +++++++++++++++++-------- src/collision/CollisionEntity.hx | 6 +- src/collision/CollisionWorld.hx | 21 +++++- src/collision/SphereCollisionEntity.hx | 64 ++++++++++++++++ 7 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 compile-c.hxml create mode 100644 src/collision/SphereCollisionEntity.hx diff --git a/.gitignore b/.gitignore index 2b062af4..9a001354 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ interiors *.hl *.js *.js.map -.vscode \ No newline at end of file +.vscode +native \ No newline at end of file diff --git a/compile-c.hxml b/compile-c.hxml new file mode 100644 index 00000000..48668aac --- /dev/null +++ b/compile-c.hxml @@ -0,0 +1,8 @@ +-cp src +-lib headbutt +-lib heaps +-lib hlsdl +-lib polygonal-ds +-hl native/marblegame.c +--main Main +-debug \ No newline at end of file diff --git a/src/Main.hx b/src/Main.hx index 7bd5a43d..dd1b8816 100644 --- a/src/Main.hx +++ b/src/Main.hx @@ -17,6 +17,7 @@ class Main extends hxd.App { var fileSystem:FileSystem; var marble:Marble; + var marble2:Marble; var collisionWorld:CollisionWorld; override function init() { @@ -72,15 +73,24 @@ class Main extends hxd.App { // s3d.camera. marble = new Marble(); + marble.controllable = true; s3d.addChild(marble); marble.setPosition(0, 0, 5); + + marble2 = new Marble(); + s3d.addChild(marble2); + marble2.setPosition(0, 5, 5); // marble.setPosition(-10, -5, 5); s3d.addChild(marble.camera); + + collisionWorld.addMovingEntity(marble.collider); + collisionWorld.addMovingEntity(marble2.collider); } override function update(dt:Float) { super.update(dt); marble.update(dt, this.collisionWorld); + marble2.update(dt, this.collisionWorld); } static function main() { diff --git a/src/Marble.hx b/src/Marble.hx index 9724ddd1..1974fa03 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -1,5 +1,6 @@ package src; +import collision.SphereCollisionEntity; import hxd.Key; import collision.CollisionInfo; import h3d.Matrix; @@ -23,12 +24,17 @@ class Move { class Marble extends Object { public var camera:CameraController; + public var controllable:Bool = false; + + public var collider:SphereCollisionEntity; + + public var velocity:Vector; + public var omega:Vector; - var velocity:Vector; - var omega:Vector; var gravityDir:Vector = new Vector(0, 0, -1); - var _radius = 0.2; + public var _radius = 0.2; + var _maxRollVelocity = 15; var _angularAcceleration = 75; var _jumpImpulse = 7.5; @@ -40,7 +46,9 @@ class Marble extends Object { var _maxDotSlide = 0.5; var _minBounceVel = 0.1; var _bounceKineticFriction = 0.2; - var _bounceRestitution = 0.5; + + public var _bounceRestitution = 0.5; + var _bounceYet:Bool; var _bounceSpeed:Float; var _bouncePos:Vector; @@ -49,6 +57,11 @@ class Marble extends Object { var _contactTime:Float; var _totalTime:Float; + public var _mass:Float = 1; + + var contacts:Array = []; + var queuedContacts:Array = []; + public function new() { super(); var geom = Sphere.defaultUnitSphere(); @@ -59,11 +72,18 @@ class Marble extends Object { this.velocity = new Vector(); this.omega = new Vector(); this.camera = new CameraController(20); + + this.collider = new SphereCollisionEntity(cast this); } function findContacts(collisiomWorld:CollisionWorld) { - var c = collisiomWorld.sphereIntersection(this.getAbsPos().getPosition(), this.velocity, _radius); - return c; + this.contacts = queuedContacts; + var c = collisiomWorld.sphereIntersection(this.collider); + contacts = contacts.concat(c); + } + + public function queueCollision(collisionInfo:CollisionInfo) { + this.queuedContacts.push(collisionInfo); } function getMarbleAxis() { @@ -82,7 +102,7 @@ class Marble extends Object { return [sidedir, motiondir, updir]; } - function getExternalForces(m:Move, dt:Float, contacts:Array) { + function getExternalForces(m:Move, dt:Float) { var gWorkGravityDir = gravityDir; var A = gWorkGravityDir.multiply(this._gravity); if (contacts.length == 0) { @@ -139,7 +159,7 @@ class Marble extends Object { return {result: true, aControl: aControl, desiredOmega: desiredOmega}; } - function velocityCancel(surfaceSlide:Bool, noBounce:Bool, contacts:Array) { + function velocityCancel(surfaceSlide:Bool, noBounce:Bool) { var SurfaceDotThreshold = 0.001; var looped = false; var itersIn = 0; @@ -157,11 +177,18 @@ class Marble extends Object { if (noBounce) { this.velocity = this.velocity.sub(surfaceVel); } else if (contacts[i].collider != null) { - var info = contacts[i]; - var bounce2 = 0.5; - var normV = info.normal.multiply(this.velocity.dot(info.normal)); - normV = normV.multiply(1 + bounce2); - this.velocity = this.velocity.sub(normV); + var otherMarble = (cast(contacts[i].collider, SphereCollisionEntity).marble); + var ourMass = this._mass; + var theirMass = otherMarble._mass; + + var bounce = Math.max(this._bounceRestitution, otherMarble._bounceRestitution); + + var dp = this.velocity.multiply(ourMass).sub(otherMarble.velocity.multiply(theirMass)); + var normP = contacts[i].normal.multiply(dp.dot(contacts[i].normal)); + normP = normP.multiply(bounce + 1); + + otherMarble.velocity = otherMarble.velocity.add(normP.multiply(1 / theirMass)); + contacts[i].velocity = otherMarble.velocity; } else { var velocity2 = contacts[i].velocity; if (velocity2.length() > 0.0001 && !surfaceSlide && surfaceDot > -this._maxDotSlide * velLen) { @@ -239,7 +266,7 @@ class Marble extends Object { } } - function applyContactForces(dt:Float, m:Move, isCentered:Bool, aControl:Vector, desiredOmega:Vector, A:Vector, contacts:Array) { + function applyContactForces(dt:Float, m:Move, isCentered:Bool, aControl:Vector, desiredOmega:Vector, A:Vector) { var a = new Vector(); this._slipAmount = 0; var gWorkGravityDir = new Vector(0, 0, -1); @@ -340,42 +367,45 @@ class Marble extends Object { } function advancePhysics(m:Move, dt:Float, collisionWorld:CollisionWorld) { - var contacts = this.findContacts(collisionWorld); + this.findContacts(collisionWorld); var cmf = this.computeMoveForces(m); var isCentered:Bool = cmf.result; var aControl = cmf.aControl; var desiredOmega = cmf.desiredOmega; - this.velocityCancel(isCentered, false, contacts); - var A = this.getExternalForces(m, dt, contacts); - var retf = this.applyContactForces(dt, m, isCentered, aControl, desiredOmega, A, contacts); + this.velocityCancel(isCentered, false); + var A = this.getExternalForces(m, dt); + var retf = this.applyContactForces(dt, m, isCentered, aControl, desiredOmega, A); A = retf[0]; var a = retf[1]; this.velocity = this.velocity.add(A.multiply(dt)); this.omega = this.omega.add(a.multiply(dt)); - this.velocityCancel(isCentered, true, contacts); + this.velocityCancel(isCentered, true); this._totalTime += dt; if (contacts.length != 0) { this._contactTime += dt; } + this.queuedContacts = []; } public function update(dt:Float, collisionWorld:CollisionWorld) { var move = new Move(); move.d = new Vector(); - if (Key.isDown(Key.W)) { - move.d.x -= 1; - } - if (Key.isDown(Key.S)) { - move.d.x += 1; - } - if (Key.isDown(Key.A)) { - move.d.y += 1; - } - if (Key.isDown(Key.D)) { - move.d.y -= 1; - } - if (Key.isDown(Key.SPACE)) { - move.jump = true; + if (this.controllable) { + if (Key.isDown(Key.W)) { + move.d.x -= 1; + } + if (Key.isDown(Key.S)) { + move.d.x += 1; + } + if (Key.isDown(Key.A)) { + move.d.y += 1; + } + if (Key.isDown(Key.D)) { + move.d.y -= 1; + } + if (Key.isDown(Key.SPACE)) { + move.jump = true; + } } var timeRemaining = dt; @@ -391,6 +421,10 @@ class Marble extends Object { advancePhysics(move, timeStep, collisionWorld); var newPos = this.getAbsPos().getPosition().add(this.velocity.multiply(timeStep)); this.setPosition(newPos.x, newPos.y, newPos.z); + var tform = this.collider.transform; + tform.setPosition(new Vector(newPos.x, newPos.y, newPos.z)); + this.collider.setTransform(tform); + this.collider.velocity = this.velocity; timeRemaining -= timeStep; it++; diff --git a/src/collision/CollisionEntity.hx b/src/collision/CollisionEntity.hx index 104a75d0..7efa6c6b 100644 --- a/src/collision/CollisionEntity.hx +++ b/src/collision/CollisionEntity.hx @@ -61,7 +61,11 @@ class CollisionEntity implements IOctreeObject { this.priority = priority; } - public function sphereIntersection(position:Vector, velocity:Vector, radius:Float) { + public function sphereIntersection(collisionEntity:SphereCollisionEntity) { + var position = collisionEntity.transform.getPosition(); + var velocity = collisionEntity.velocity; + var radius = collisionEntity.radius; + var invMatrix = transform.clone(); invMatrix.invert(); var localpos = position.clone(); diff --git a/src/collision/CollisionWorld.hx b/src/collision/CollisionWorld.hx index 89c6b0bb..5d433c30 100644 --- a/src/collision/CollisionWorld.hx +++ b/src/collision/CollisionWorld.hx @@ -1,16 +1,22 @@ package collision; +import h3d.col.Sphere; import h3d.Vector; import octree.Octree; class CollisionWorld { public var octree:Octree; + public var entities:Array = []; + public var dynamicEntities:Array = []; public function new() { this.octree = new Octree(); } - public function sphereIntersection(position:Vector, velocity:Vector, radius:Float) { + public function sphereIntersection(spherecollision:SphereCollisionEntity) { + var position = spherecollision.transform.getPosition(); + var radius = spherecollision.radius; + var velocity = spherecollision.velocity; var searchdist = velocity.length() + radius; var intersections = this.octree.radiusSearch(position, searchdist); @@ -19,12 +25,23 @@ class CollisionWorld { for (obj in intersections) { var entity:CollisionEntity = cast obj; - contacts = contacts.concat(entity.sphereIntersection(position, velocity, radius)); + contacts = contacts.concat(entity.sphereIntersection(spherecollision)); + } + + for (obj in dynamicEntities) { + if (obj != spherecollision) { + contacts = contacts.concat(obj.sphereIntersection(spherecollision)); + } } return contacts; } public function addEntity(entity:CollisionEntity) { this.octree.insert(entity); + this.entities.push(entity); + } + + public function addMovingEntity(entity:CollisionEntity) { + this.dynamicEntities.push(entity); } } diff --git a/src/collision/SphereCollisionEntity.hx b/src/collision/SphereCollisionEntity.hx new file mode 100644 index 00000000..5f98bcda --- /dev/null +++ b/src/collision/SphereCollisionEntity.hx @@ -0,0 +1,64 @@ +package collision; + +import src.Marble; +import h3d.col.Ray; +import h3d.Vector; +import h3d.col.Sphere; +import h3d.col.Bounds; + +class SphereCollisionEntity extends CollisionEntity { + public var radius:Float; + public var velocity:Vector; + public var marble:Marble; + + public function new(marble:Marble) { + super(); + this.marble = marble; + this.velocity = marble.velocity; + this.radius = marble._radius; + this.generateBoundingBox(); + } + + public override function generateBoundingBox() { + var boundingBox = new Bounds(); + var pos = transform.getPosition(); + boundingBox.addSpherePos(pos.x, pos.y, pos.z, radius); + this.boundingBox = boundingBox; + } + + public override function isIntersectedByRay(rayOrigin:Vector, rayDirection:Vector, intersectionPoint:Vector):Bool { + // TEMP cause bruh + return boundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), true) != -1; + } + + public override function sphereIntersection(collisionEntity:SphereCollisionEntity) { + var contacts = []; + var thispos = transform.getPosition(); + var position = collisionEntity.transform.getPosition(); + var velocity = collisionEntity.velocity; + var radius = collisionEntity.radius; + + if (position.distanceSq(thispos) < (radius + this.radius) * (radius + this.radius)) { + var contact = new CollisionInfo(); + contact.collider = this; + contact.friction = 1; + contact.restitution = 1; + contact.velocity = this.velocity; + contact.point = position.add(thispos).multiply(0.5); + contact.normal = contact.point.sub(thispos).normalized(); + contact.penetration = radius - (position.sub(contact.point).dot(contact.normal)); + contacts.push(contact); + + var othercontact = new CollisionInfo(); + othercontact.collider = collisionEntity; + othercontact.friction = 1; + othercontact.restitution = 1; + othercontact.velocity = this.velocity; + othercontact.point = thispos.add(position).multiply(0.5); + othercontact.normal = contact.point.sub(position).normalized(); + othercontact.penetration = this.radius - (thispos.sub(othercontact.point).dot(othercontact.normal)); + this.marble.queueCollision(othercontact); + } + return contacts; + } +}