mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
implement marble-marble collision
This commit is contained in:
parent
d67bc00810
commit
5bed26560f
7 changed files with 175 additions and 37 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,4 +2,5 @@ interiors
|
|||
*.hl
|
||||
*.js
|
||||
*.js.map
|
||||
.vscode
|
||||
.vscode
|
||||
native
|
||||
8
compile-c.hxml
Normal file
8
compile-c.hxml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
-cp src
|
||||
-lib headbutt
|
||||
-lib heaps
|
||||
-lib hlsdl
|
||||
-lib polygonal-ds
|
||||
-hl native/marblegame.c
|
||||
--main Main
|
||||
-debug
|
||||
10
src/Main.hx
10
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() {
|
||||
|
|
|
|||
100
src/Marble.hx
100
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<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++;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
64
src/collision/SphereCollisionEntity.hx
Normal file
64
src/collision/SphereCollisionEntity.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue