implement marble-marble collision

This commit is contained in:
RandomityGuy 2021-05-28 22:23:52 +05:30
parent d67bc00810
commit 5bed26560f
7 changed files with 175 additions and 37 deletions

3
.gitignore vendored
View file

@ -2,4 +2,5 @@ interiors
*.hl
*.js
*.js.map
.vscode
.vscode
native

8
compile-c.hxml Normal file
View file

@ -0,0 +1,8 @@
-cp src
-lib headbutt
-lib heaps
-lib hlsdl
-lib polygonal-ds
-hl native/marblegame.c
--main Main
-debug

View file

@ -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() {

View file

@ -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<CollisionInfo> = [];
var queuedContacts:Array<CollisionInfo> = [];
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<CollisionInfo>) {
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<CollisionInfo>) {
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<CollisionInfo>) {
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++;

View file

@ -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();

View file

@ -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<CollisionEntity> = [];
public var dynamicEntities:Array<CollisionEntity> = [];
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);
}
}

View file

@ -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;
}
}