mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-04-25 20:21:50 +00:00
pathedinterior stuff
This commit is contained in:
parent
435c606927
commit
38bcab6c62
10 changed files with 816 additions and 21 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import src.PathedInterior;
|
||||
import h3d.Vector;
|
||||
import collision.CollisionSurface;
|
||||
import collision.CollisionEntity;
|
||||
|
|
@ -249,4 +250,217 @@ class DifBuilder {
|
|||
|
||||
return ig;
|
||||
}
|
||||
|
||||
public static function loadDifAsPI(path:String, loader:Loader) {
|
||||
var dif = Dif.Load(path);
|
||||
|
||||
var geo = dif.interiors[0];
|
||||
|
||||
var hulls = geo.convexHulls;
|
||||
|
||||
var triangles = [];
|
||||
var textures = [];
|
||||
|
||||
var collider = new CollisionEntity();
|
||||
|
||||
for (i in 0...hulls.length) {
|
||||
var hullTris = [];
|
||||
var hull = hulls[i];
|
||||
|
||||
for (j in hull.surfaceStart...(hull.surfaceStart + hull.surfaceCount)) {
|
||||
var surfaceindex = geo.hullSurfaceIndices[j];
|
||||
var surface = geo.surfaces[surfaceindex];
|
||||
var planeindex = surface.planeIndex;
|
||||
|
||||
var planeFlipped = (planeindex & 0x8000) == 0x8000;
|
||||
if (planeFlipped)
|
||||
planeindex &= ~0x8000;
|
||||
|
||||
var plane = geo.planes[planeindex];
|
||||
var normal = geo.normals[plane.normalIndex];
|
||||
|
||||
if (planeFlipped)
|
||||
normal = normal.scalar(-1);
|
||||
|
||||
var texture = geo.materialList[surface.textureIndex];
|
||||
if (!textures.contains(texture))
|
||||
textures.push(texture);
|
||||
|
||||
var points = geo.points;
|
||||
|
||||
var colliderSurface = new CollisionSurface();
|
||||
colliderSurface.points = [];
|
||||
colliderSurface.normals = [];
|
||||
colliderSurface.indices = [];
|
||||
|
||||
for (k in (surface.windingStart + 2)...(surface.windingStart + surface.windingCount)) {
|
||||
var p1, p2, p3;
|
||||
if ((k - (surface.windingStart + 2)) % 2 == 0) {
|
||||
p1 = points[geo.windings[k]];
|
||||
p2 = points[geo.windings[k - 1]];
|
||||
p3 = points[geo.windings[k - 2]];
|
||||
} else {
|
||||
p1 = points[geo.windings[k - 2]];
|
||||
p2 = points[geo.windings[k - 1]];
|
||||
p3 = points[geo.windings[k]];
|
||||
}
|
||||
|
||||
var texgen = geo.texGenEQs[surface.texGenIndex];
|
||||
|
||||
var uv1 = new Point2F(p1.x * texgen.planeX.x
|
||||
+ p1.y * texgen.planeX.y
|
||||
+ p1.z * texgen.planeX.z
|
||||
+ texgen.planeX.d,
|
||||
p1.x * texgen.planeY.x
|
||||
+ p1.y * texgen.planeY.y
|
||||
+ p1.z * texgen.planeY.z
|
||||
+ texgen.planeY.d);
|
||||
var uv2 = new Point2F(p2.x * texgen.planeX.x
|
||||
+ p2.y * texgen.planeX.y
|
||||
+ p2.z * texgen.planeX.z
|
||||
+ texgen.planeX.d,
|
||||
p2.x * texgen.planeY.x
|
||||
+ p2.y * texgen.planeY.y
|
||||
+ p2.z * texgen.planeY.z
|
||||
+ texgen.planeY.d);
|
||||
var uv3 = new Point2F(p3.x * texgen.planeX.x
|
||||
+ p3.y * texgen.planeX.y
|
||||
+ p3.z * texgen.planeX.z
|
||||
+ texgen.planeX.d,
|
||||
p3.x * texgen.planeY.x
|
||||
+ p3.y * texgen.planeY.y
|
||||
+ p3.z * texgen.planeY.z
|
||||
+ texgen.planeY.d);
|
||||
|
||||
var tri = new DifBuilderTriangle();
|
||||
tri.texture = texture;
|
||||
tri.normal1 = normal;
|
||||
tri.normal2 = normal;
|
||||
tri.normal3 = normal;
|
||||
tri.p1 = p1;
|
||||
tri.p2 = p2;
|
||||
tri.p3 = p3;
|
||||
tri.uv1 = uv1;
|
||||
tri.uv2 = uv2;
|
||||
tri.uv3 = uv3;
|
||||
triangles.push(tri);
|
||||
hullTris.push(tri);
|
||||
|
||||
colliderSurface.points.push(new Vector(-p1.x, p1.y, p1.z));
|
||||
colliderSurface.points.push(new Vector(-p2.x, p2.y, p2.z));
|
||||
colliderSurface.points.push(new Vector(-p3.x, p3.y, p3.z));
|
||||
colliderSurface.normals.push(new Vector(-normal.x, normal.y, normal.z));
|
||||
colliderSurface.normals.push(new Vector(-normal.x, normal.y, normal.z));
|
||||
colliderSurface.normals.push(new Vector(-normal.x, normal.y, normal.z));
|
||||
colliderSurface.indices.push(colliderSurface.indices.length);
|
||||
colliderSurface.indices.push(colliderSurface.indices.length);
|
||||
colliderSurface.indices.push(colliderSurface.indices.length);
|
||||
}
|
||||
|
||||
colliderSurface.generateBoundingBox();
|
||||
collider.addSurface(colliderSurface);
|
||||
}
|
||||
}
|
||||
|
||||
var mats = new Map<String, Array<DifBuilderTriangle>>();
|
||||
|
||||
for (index => value in triangles) {
|
||||
if (mats.exists(value.texture)) {
|
||||
mats[value.texture].push(value);
|
||||
} else {
|
||||
mats.set(value.texture, [value]);
|
||||
}
|
||||
}
|
||||
|
||||
collider.generateBoundingBox();
|
||||
var ig = new PathedInterior();
|
||||
ig.collider = collider;
|
||||
|
||||
function canFindTex(tex:String) {
|
||||
if (tex.indexOf('/') != -1) {
|
||||
tex = tex.split('/')[1];
|
||||
}
|
||||
|
||||
if (File.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
||||
return true;
|
||||
}
|
||||
if (File.exists(Path.directory(path) + "/" + tex + ".png")) {
|
||||
return true;
|
||||
}
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
|
||||
if (File.exists(prevDir + "/" + tex + ".jpg")) {
|
||||
return true;
|
||||
}
|
||||
if (File.exists(prevDir + "/" + tex + ".png")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function tex(tex:String):String {
|
||||
if (tex.indexOf('/') != -1) {
|
||||
tex = tex.split('/')[1];
|
||||
}
|
||||
|
||||
if (File.exists(Path.directory(path) + "/" + tex + ".jpg")) {
|
||||
return Path.directory(path) + "/" + tex + ".jpg";
|
||||
}
|
||||
if (File.exists(Path.directory(path) + "/" + tex + ".png")) {
|
||||
return Path.directory(path) + "/" + tex + ".png";
|
||||
}
|
||||
|
||||
var prevDir = Path.directory(Path.directory(path));
|
||||
|
||||
if (File.exists(prevDir + "/" + tex + ".jpg")) {
|
||||
return prevDir + "/" + tex + ".jpg";
|
||||
}
|
||||
if (File.exists(prevDir + "/" + tex + ".png")) {
|
||||
return prevDir + "/" + tex + ".png";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
for (grp => tris in mats) {
|
||||
var points = [];
|
||||
var normals = [];
|
||||
var uvs = [];
|
||||
|
||||
for (tri in tris) {
|
||||
var p1 = new Point(-tri.p1.x, tri.p1.y, tri.p1.z);
|
||||
var p2 = new Point(-tri.p2.x, tri.p2.y, tri.p2.z);
|
||||
var p3 = new Point(-tri.p3.x, tri.p3.y, tri.p3.z);
|
||||
var n1 = new Point(-tri.normal1.x, tri.normal1.y, tri.normal1.z);
|
||||
var n2 = new Point(-tri.normal2.x, tri.normal2.y, tri.normal2.z);
|
||||
var n3 = new Point(-tri.normal3.x, tri.normal3.y, tri.normal3.z);
|
||||
var uv1 = new UV(tri.uv1.x, tri.uv1.y);
|
||||
var uv2 = new UV(tri.uv2.x, tri.uv2.y);
|
||||
var uv3 = new UV(tri.uv3.x, tri.uv3.y);
|
||||
points.push(p3);
|
||||
points.push(p2);
|
||||
points.push(p1);
|
||||
normals.push(n3);
|
||||
normals.push(n2);
|
||||
normals.push(n1);
|
||||
uvs.push(uv3);
|
||||
uvs.push(uv2);
|
||||
uvs.push(uv1);
|
||||
}
|
||||
|
||||
var prim = new Polygon(points);
|
||||
prim.uvs = uvs;
|
||||
prim.normals = normals;
|
||||
|
||||
var texture:Texture = loader.load(tex(grp)).toImage().toTexture();
|
||||
texture.wrap = Wrap.Repeat;
|
||||
var material = h3d.mat.Material.create(texture);
|
||||
// material.mainPass.wireframe = true;
|
||||
|
||||
var mesh = new Mesh(prim, material, ig);
|
||||
}
|
||||
|
||||
return ig;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
src/Main.hx
39
src/Main.hx
|
|
@ -1,5 +1,8 @@
|
|||
package;
|
||||
|
||||
import h3d.Quat;
|
||||
import src.PathedInteriorMarker;
|
||||
import src.PathedInterior;
|
||||
import src.MarbleWorld;
|
||||
import collision.CollisionWorld;
|
||||
import src.Marble;
|
||||
|
|
@ -31,6 +34,38 @@ class Main extends hxd.App {
|
|||
var db = DifBuilder.loadDif("interiors/beginner/beginner_finish.dif", loader);
|
||||
world.addInterior(db);
|
||||
|
||||
var pi = DifBuilder.loadDifAsPI("interiors/addon/smallplatform.dif", loader);
|
||||
var pim = pi.getTransform();
|
||||
pim.setPosition(new Vector(5, 0, 0));
|
||||
pi.setTransform(pim);
|
||||
|
||||
var cube = new Cube();
|
||||
cube.addUVs();
|
||||
cube.addNormals();
|
||||
var mat = Material.create();
|
||||
|
||||
var m1 = new PathedInteriorMarker();
|
||||
m1.msToNext = 5;
|
||||
m1.position = new Vector(5, 0, 0);
|
||||
m1.smoothingType = "";
|
||||
m1.rotation = new Quat();
|
||||
|
||||
var m2 = new PathedInteriorMarker();
|
||||
m2.msToNext = 3;
|
||||
m2.position = new Vector(5, 0, 5);
|
||||
m2.smoothingType = "";
|
||||
m2.rotation = new Quat();
|
||||
|
||||
var m3 = new PathedInteriorMarker();
|
||||
m3.msToNext = 5;
|
||||
m3.position = new Vector(5, 0, 0);
|
||||
m3.smoothingType = "";
|
||||
m3.rotation = new Quat();
|
||||
|
||||
pi.markerData = [m1, m2, m3];
|
||||
|
||||
world.addPathedInterior(pi);
|
||||
|
||||
// for (surf in db.collider.surfaces) {
|
||||
// var surfmin = new CustomObject(cube, mat, s3d);
|
||||
// var bound = surf.boundingBox;
|
||||
|
|
@ -40,8 +75,6 @@ class Main extends hxd.App {
|
|||
// surfmax.setPosition(bound.xMax, bound.yMax, bound.zMax);
|
||||
// }
|
||||
|
||||
s3d.addChild(db);
|
||||
|
||||
// var mat = Material.create();
|
||||
// var so = new CustomObject(cube, mat);
|
||||
// so.setPosition(0, 0, 0);
|
||||
|
|
@ -60,7 +93,7 @@ class Main extends hxd.App {
|
|||
var marble = new Marble();
|
||||
marble.controllable = true;
|
||||
world.addMarble(marble);
|
||||
marble.setPosition(0, 0, 5);
|
||||
marble.setPosition(6, 0, 5);
|
||||
// marble.setPosition(-10, -5, 5);
|
||||
}
|
||||
|
||||
|
|
|
|||
306
src/Marble.hx
306
src/Marble.hx
|
|
@ -1,5 +1,9 @@
|
|||
package src;
|
||||
|
||||
import dif.math.Point3F;
|
||||
import dif.math.PlaneF;
|
||||
import collision.CollisionSurface;
|
||||
import src.PathedInterior;
|
||||
import collision.SphereCollisionEntity;
|
||||
import hxd.Key;
|
||||
import collision.CollisionInfo;
|
||||
|
|
@ -76,9 +80,9 @@ class Marble extends Object {
|
|||
this.collider = new SphereCollisionEntity(cast this);
|
||||
}
|
||||
|
||||
function findContacts(collisiomWorld:CollisionWorld) {
|
||||
function findContacts(collisiomWorld:CollisionWorld, dt:Float) {
|
||||
this.contacts = queuedContacts;
|
||||
var c = collisiomWorld.sphereIntersection(this.collider);
|
||||
var c = collisiomWorld.sphereIntersection(this.collider, dt);
|
||||
contacts = contacts.concat(c);
|
||||
}
|
||||
|
||||
|
|
@ -366,8 +370,296 @@ class Marble extends Object {
|
|||
this._bounceNormal = normal;
|
||||
}
|
||||
|
||||
function testMove(velocity:Vector, position:Vector, deltaT:Float, radius:Float, testPIs:Bool, collisionWorld:CollisionWorld) {
|
||||
var velLen = velocity.length();
|
||||
if (velLen < 0.001)
|
||||
return false;
|
||||
|
||||
var velocityDir = velocity.normalized();
|
||||
|
||||
var deltaPosition = velocity.multiply(deltaT);
|
||||
var finalPosition = position.add(deltaPosition);
|
||||
|
||||
var expandedcollider = new SphereCollisionEntity(cast this);
|
||||
expandedcollider.transform = Matrix.T(position.x, position.y, position.z);
|
||||
expandedcollider.radius = this.getAbsPos().getPosition().distance(position) + radius;
|
||||
|
||||
var foundObjs = collisionWorld.radiusSearch(position, expandedcollider.radius);
|
||||
|
||||
var finalT = deltaT;
|
||||
var marbleCollisionTime = finalT;
|
||||
var marbleCollisionNormal = new Vector(0, 0, 1);
|
||||
|
||||
var lastContactPos = new Vector();
|
||||
|
||||
function toDifPoint(vec:Vector) {
|
||||
return new Point3F(vec.x, vec.y, vec.z);
|
||||
}
|
||||
function fromDifPoint(vec:Point3F) {
|
||||
return new Vector(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
var contactPoly:{v0:Vector, v:Vector, v2:Vector};
|
||||
|
||||
for (obj in foundObjs) {
|
||||
if (obj.velocity.length() != 0) { // Its an MP so bruh
|
||||
|
||||
var invMatrix = obj.transform.clone();
|
||||
invMatrix.invert();
|
||||
var localpos = position.clone();
|
||||
localpos.transform(invMatrix);
|
||||
var surfaces = obj.octree.radiusSearch(localpos, expandedcollider.radius);
|
||||
|
||||
for (surf in surfaces) {
|
||||
var surface:CollisionSurface = cast obj;
|
||||
|
||||
var i = 0;
|
||||
while (i < surface.indices.length) {
|
||||
var v0 = surface.points[surface.indices[i]].transformed(obj.transform);
|
||||
var v = surface.points[surface.indices[i + 1]].transformed(obj.transform);
|
||||
var v2 = surface.points[surface.indices[i + 2]].transformed(obj.transform);
|
||||
|
||||
var polyPlane = PlaneF.ThreePoints(toDifPoint(v0), toDifPoint(v), toDifPoint(v2));
|
||||
|
||||
// If we're going the wrong direction or not going to touch the plane, ignore...
|
||||
if (!(polyPlane.getNormal().dot(toDifPoint(velocityDir)) > -0.001
|
||||
|| polyPlane.getNormal().dot(toDifPoint(finalPosition)) + polyPlane.d > radius)) {
|
||||
// Time until collision with the plane
|
||||
var collisionTime = (radius
|
||||
- (polyPlane.getNormal().dot(toDifPoint(position)) + polyPlane.d)) / polyPlane.getNormal().dot(toDifPoint(velocity));
|
||||
|
||||
// Are we going to touch the plane during this time step?
|
||||
if (collisionTime >= 0.0 && finalT >= collisionTime) {
|
||||
var lastVertIndex = surface.indices[surface.indices.length - 1];
|
||||
var lastVert = surface.points[lastVertIndex];
|
||||
|
||||
var collisionPos = velocity.multiply(collisionTime).add(position);
|
||||
|
||||
var isOnEdge:Bool = false;
|
||||
|
||||
for (i in 0...surface.indices.length) {
|
||||
{
|
||||
var thisVert = surface.points[surface.indices[i]];
|
||||
if (thisVert != lastVert) {
|
||||
var edgePlane = PlaneF.ThreePoints(toDifPoint(thisVert).add(polyPlane.getNormal()), toDifPoint(thisVert),
|
||||
toDifPoint(lastVert));
|
||||
lastVert = thisVert;
|
||||
|
||||
// if we are on the far side of the edge
|
||||
if (edgePlane.getNormal().dot(toDifPoint(collisionPos)) + edgePlane.d < 0.0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isOnEdge = i != surface.indices.length;
|
||||
}
|
||||
|
||||
// If we're inside the poly, just get the position
|
||||
if (!isOnEdge) {
|
||||
finalT = collisionTime;
|
||||
finalPosition = collisionPos;
|
||||
lastContactPos = fromDifPoint(polyPlane.project(toDifPoint(collisionPos)));
|
||||
contactPoly = {v0: v0, v: v, v2: v2};
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// We *might* be colliding with an edge
|
||||
|
||||
var lastVert = surface.points[surface.indices[surface.indices.length - 1]];
|
||||
|
||||
if (surface.indices.length == 0) {
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
var radSq = radius * radius;
|
||||
for (iter in 0...surface.indices.length) {
|
||||
var thisVert = surface.points[surface.indices[i]];
|
||||
|
||||
var vertDiff = lastVert.sub(thisVert);
|
||||
var posDiff = position.sub(thisVert);
|
||||
|
||||
var velRejection = vertDiff.cross(velocity);
|
||||
var posRejection = vertDiff.cross(posDiff);
|
||||
|
||||
// Build a quadratic equation to solve for the collision time
|
||||
var a = velRejection.lengthSq();
|
||||
var halfB = posRejection.dot(velRejection);
|
||||
var b = halfB + halfB;
|
||||
|
||||
var discriminant = b * b - (posRejection.lengthSq() - vertDiff.lengthSq() * radSq) * (a * 4.0);
|
||||
|
||||
// If it's not quadratic or has no solution, ignore this edge.
|
||||
if (a == 0.0 || discriminant < 0.0) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
var oneOverTwoA = 0.5 / a;
|
||||
var discriminantSqrt = Math.sqrt(discriminant);
|
||||
|
||||
// Solve using the quadratic formula
|
||||
var edgeCollisionTime = (discriminantSqrt - b) * oneOverTwoA;
|
||||
var edgeCollisionTime2 = (-b - discriminantSqrt) * oneOverTwoA;
|
||||
|
||||
// Make sure the 2 times are in ascending order
|
||||
if (edgeCollisionTime2 < edgeCollisionTime) {
|
||||
var temp = edgeCollisionTime2;
|
||||
edgeCollisionTime2 = edgeCollisionTime;
|
||||
edgeCollisionTime = temp;
|
||||
}
|
||||
|
||||
// If the collision doesn't happen on this time step, ignore this edge.
|
||||
if (edgeCollisionTime2 <= 0.0001 || finalT <= edgeCollisionTime) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the collision hasn't already happened
|
||||
if (edgeCollisionTime >= 0.0) {
|
||||
var edgeLen = vertDiff.length();
|
||||
|
||||
var relativeCollisionPos = velocity.multiply(edgeCollisionTime).add(position).sub(thisVert);
|
||||
|
||||
var distanceAlongEdge = relativeCollisionPos.dot(vertDiff) / edgeLen;
|
||||
|
||||
// If the collision happens outside the boundaries of the edge, ignore this edge.
|
||||
if (-radius > distanceAlongEdge || edgeLen + radius < distanceAlongEdge) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the collision is within the edge, resolve the collision and continue.
|
||||
if (distanceAlongEdge >= 0.0 && distanceAlongEdge <= edgeLen) {
|
||||
finalT = edgeCollisionTime;
|
||||
finalPosition = velocity.multiply(edgeCollisionTime).add(position);
|
||||
|
||||
lastContactPos = vertDiff.multiply(distanceAlongEdge / edgeLen).add(thisVert);
|
||||
contactPoly = {v0: v0, v: v, v2: v2};
|
||||
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This is what happens when we collide with a corner
|
||||
|
||||
var speedSq = velocity.lengthSq();
|
||||
|
||||
// Build a quadratic equation to solve for the collision time
|
||||
var posVertDiff = position.sub(thisVert);
|
||||
var halfCornerB = posVertDiff.dot(velocity);
|
||||
var cornerB = halfCornerB + halfCornerB;
|
||||
|
||||
var fourA = speedSq * 4.0;
|
||||
|
||||
var cornerDiscriminant = cornerB * cornerB - (posVertDiff.lengthSq() - radSq) * fourA;
|
||||
|
||||
// If it's quadratic and has a solution ...
|
||||
if (speedSq != 0.0 && cornerDiscriminant >= 0.0) {
|
||||
var oneOver2A = 0.5 / speedSq;
|
||||
var cornerDiscriminantSqrt = Math.sqrt(cornerDiscriminant);
|
||||
|
||||
// Solve using the quadratic formula
|
||||
var cornerCollisionTime = (cornerDiscriminantSqrt - cornerB) * oneOver2A;
|
||||
var cornerCollisionTime2 = (-cornerB - cornerDiscriminantSqrt) * oneOver2A;
|
||||
|
||||
// Make sure the 2 times are in ascending order
|
||||
if (cornerCollisionTime2 < cornerCollisionTime) {
|
||||
var temp = cornerCollisionTime2;
|
||||
cornerCollisionTime2 = cornerCollisionTime;
|
||||
cornerCollisionTime = temp;
|
||||
}
|
||||
|
||||
// If the collision doesn't happen on this time step, ignore this corner
|
||||
if (cornerCollisionTime2 > 0.0001 && finalT > cornerCollisionTime) {
|
||||
// Adjust to make sure very small negative times are counted as zero
|
||||
if (cornerCollisionTime <= 0.0 && cornerCollisionTime > -0.0001)
|
||||
cornerCollisionTime = 0.0;
|
||||
|
||||
// Check if the collision hasn't already happened
|
||||
if (cornerCollisionTime >= 0.0) {
|
||||
// Resolve it and continue
|
||||
finalT = cornerCollisionTime;
|
||||
contactPoly = {v0: v0, v: v, v2: v2};
|
||||
finalPosition = velocity.multiply(cornerCollisionTime).add(position);
|
||||
lastContactPos = thisVert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We still need to check the other corner ...
|
||||
// Build one last quadratic equation to solve for the collision time
|
||||
var lastVertDiff = position.sub(lastVert);
|
||||
var lastCornerHalfB = lastVertDiff.dot(velocity);
|
||||
var lastCornerB = lastCornerHalfB + lastCornerHalfB;
|
||||
var lastCornerDiscriminant = lastCornerB * lastCornerB - (lastVertDiff.lengthSq() - radSq) * fourA;
|
||||
|
||||
// If it's not quadratic or has no solution, then skip this corner
|
||||
if (speedSq == 0.0 || lastCornerDiscriminant < 0.0) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
var lastCornerOneOver2A = 0.5 / speedSq;
|
||||
var lastCornerDiscriminantSqrt = Math.sqrt(lastCornerDiscriminant);
|
||||
|
||||
// Solve using the quadratic formula
|
||||
var lastCornerCollisionTime = (lastCornerDiscriminantSqrt - lastCornerB) * lastCornerOneOver2A;
|
||||
var lastCornerCollisionTime2 = (-lastCornerB - lastCornerDiscriminantSqrt) * lastCornerOneOver2A;
|
||||
|
||||
// Make sure the 2 times are in ascending order
|
||||
if (lastCornerCollisionTime2 < lastCornerCollisionTime) {
|
||||
var temp = lastCornerCollisionTime2;
|
||||
lastCornerCollisionTime2 = lastCornerCollisionTime;
|
||||
lastCornerCollisionTime = temp;
|
||||
}
|
||||
|
||||
// If the collision doesn't happen on this time step, ignore this corner
|
||||
if (lastCornerCollisionTime2 <= 0.0001 || finalT <= lastCornerCollisionTime) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust to make sure very small negative times are counted as zero
|
||||
if (lastCornerCollisionTime <= 0.0 && lastCornerCollisionTime > -0.0001)
|
||||
lastCornerCollisionTime = 0.0;
|
||||
|
||||
// Check if the collision hasn't already happened
|
||||
if (lastCornerCollisionTime < 0.0) {
|
||||
lastVert = thisVert;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Resolve it and continue
|
||||
finalT = lastCornerCollisionTime;
|
||||
finalPosition = velocity.multiply(lastCornerCollisionTime).add(position);
|
||||
lastContactPos = lastVert;
|
||||
contactPoly = {v0: v0, v: v, v2: v2};
|
||||
|
||||
lastVert = thisVert;
|
||||
}
|
||||
}
|
||||
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
position = finalPosition;
|
||||
|
||||
var contacted = false;
|
||||
if (deltaT > finalT) {
|
||||
contacted = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function advancePhysics(m:Move, dt:Float, collisionWorld:CollisionWorld) {
|
||||
this.findContacts(collisionWorld);
|
||||
this.findContacts(collisionWorld, dt);
|
||||
var cmf = this.computeMoveForces(m);
|
||||
var isCentered:Bool = cmf.result;
|
||||
var aControl = cmf.aControl;
|
||||
|
|
@ -387,7 +679,7 @@ class Marble extends Object {
|
|||
this.queuedContacts = [];
|
||||
}
|
||||
|
||||
public function update(dt:Float, collisionWorld:CollisionWorld) {
|
||||
public function update(currentTime:Float, dt:Float, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
|
||||
var move = new Move();
|
||||
move.d = new Vector();
|
||||
if (this.controllable) {
|
||||
|
|
@ -430,6 +722,12 @@ class Marble extends Object {
|
|||
it++;
|
||||
} while (it <= 10);
|
||||
|
||||
if (this.controllable) {
|
||||
for (interior in pathedInteriors) {
|
||||
interior.update(currentTime, dt);
|
||||
}
|
||||
}
|
||||
|
||||
this.camera.target.load(this.getAbsPos().getPosition().toPoint());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import src.PathedInterior;
|
||||
import hxd.Key;
|
||||
import h3d.Vector;
|
||||
import src.InteriorGeometry;
|
||||
|
|
@ -12,8 +13,11 @@ class MarbleWorld {
|
|||
var collisionWorld:CollisionWorld;
|
||||
|
||||
public var interiors:Array<InteriorGeometry> = [];
|
||||
public var pathedInteriors:Array<PathedInterior> = [];
|
||||
public var marbles:Array<Marble> = [];
|
||||
|
||||
public var currentTime:Float = 0;
|
||||
|
||||
var scene:Scene;
|
||||
|
||||
public function new(scene:Scene) {
|
||||
|
|
@ -27,6 +31,13 @@ class MarbleWorld {
|
|||
this.scene.addChild(obj);
|
||||
}
|
||||
|
||||
public function addPathedInterior(obj:PathedInterior) {
|
||||
this.pathedInteriors.push(obj);
|
||||
this.collisionWorld.addMovingEntity(obj.collider);
|
||||
this.scene.addChild(obj);
|
||||
obj.init();
|
||||
}
|
||||
|
||||
public function addMarble(marble:Marble) {
|
||||
this.marbles.push(marble);
|
||||
if (marble.controllable) {
|
||||
|
|
@ -38,7 +49,8 @@ class MarbleWorld {
|
|||
|
||||
public function update(dt:Float) {
|
||||
for (marble in marbles) {
|
||||
marble.update(dt, collisionWorld);
|
||||
marble.update(currentTime, dt, collisionWorld, this.pathedInteriors);
|
||||
}
|
||||
currentTime += dt;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
150
src/PathedInterior.hx
Normal file
150
src/PathedInterior.hx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package src;
|
||||
|
||||
import h3d.Matrix;
|
||||
import h3d.Vector;
|
||||
import src.Util;
|
||||
import src.PathedInteriorMarker;
|
||||
import src.InteriorGeometry;
|
||||
|
||||
class PathedInterior extends InteriorGeometry {
|
||||
public var markerData:Array<PathedInteriorMarker> = [];
|
||||
|
||||
var duration:Float;
|
||||
var currentTime:Float;
|
||||
var targetTime:Float;
|
||||
var changeTime:Float;
|
||||
|
||||
var prevPosition:Vector;
|
||||
var currentPosition:Vector;
|
||||
|
||||
var velocity:Vector;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
}
|
||||
|
||||
public function init() {
|
||||
this.computeDuration();
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public function update(currentTime:Float, dt:Float) {
|
||||
var transform = this.getTransformAtTime(this.getInternalTime(currentTime));
|
||||
this.updatePosition();
|
||||
|
||||
var position = transform.getPosition();
|
||||
this.prevPosition = this.currentPosition;
|
||||
this.currentPosition = position;
|
||||
|
||||
velocity = position.sub(this.prevPosition).multiply(1 / dt);
|
||||
}
|
||||
|
||||
function computeDuration() {
|
||||
var total = 0.0;
|
||||
for (marker in markerData) {
|
||||
total += marker.msToNext;
|
||||
}
|
||||
this.duration = total;
|
||||
}
|
||||
|
||||
public function setTargetTime(now:Float, target:Float) {
|
||||
var currentInternalTime = this.getInternalTime(now);
|
||||
this.currentTime = currentInternalTime; // Start where the interior currently is
|
||||
this.targetTime = target;
|
||||
this.changeTime = now;
|
||||
}
|
||||
|
||||
public function getInternalTime(externalTime:Float) {
|
||||
if (this.targetTime < 0) {
|
||||
var direction = (this.targetTime == -1) ? 1 : (this.targetTime == -2) ? -1 : 0;
|
||||
return Util.adjustedMod(this.currentTime + (externalTime - this.changeTime) * direction, this.duration);
|
||||
} else {
|
||||
var dur = Math.abs(this.currentTime - this.targetTime);
|
||||
var compvarion = Util.clamp(dur > 0 ? (externalTime - this.changeTime) / dur : 1, 0, 1);
|
||||
return Util.clamp(Util.lerp(this.currentTime, this.targetTime, compvarion), 0, this.duration);
|
||||
}
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
var tform = this.collider.transform;
|
||||
tform.setPosition(this.currentPosition);
|
||||
this.setTransform(tform);
|
||||
this.collider.setTransform(tform);
|
||||
this.collider.velocity = this.velocity;
|
||||
}
|
||||
|
||||
function getTransformAtTime(time:Float) {
|
||||
var m1:PathedInteriorMarker = this.markerData[0];
|
||||
var m2:PathedInteriorMarker = this.markerData[1];
|
||||
if (m1 == null) {
|
||||
// Incase there are no markers at all
|
||||
var mat = this.getTransform();
|
||||
return mat;
|
||||
} else {
|
||||
m1 = this.markerData[0];
|
||||
}
|
||||
// Find the two markers in question
|
||||
var currentEndTime = m1.msToNext;
|
||||
var i = 2;
|
||||
while (currentEndTime < time && i < this.markerData.length) {
|
||||
m1 = m2;
|
||||
m2 = this.markerData[i++];
|
||||
|
||||
currentEndTime += m1.msToNext;
|
||||
}
|
||||
if (m2 == null)
|
||||
m2 = m1;
|
||||
|
||||
var m1Time = currentEndTime - m1.msToNext;
|
||||
var m2Time = currentEndTime;
|
||||
var duration = m2Time - m1Time;
|
||||
var position:Vector = null;
|
||||
var compvarion = Util.clamp(duration > 0 ? (time - m1Time) / duration : 1, 0, 1);
|
||||
if (m1.smoothingType == "Accelerate") {
|
||||
// A simple easing function
|
||||
compvarion = Math.sin(compvarion * Math.PI - (Math.PI / 2)) * 0.5 + 0.5;
|
||||
} else if (m1.smoothingType == "Spline") {
|
||||
// Smooth the path like it's a Catmull-Rom spline.
|
||||
var preStart = (i - 2) - 1;
|
||||
var postEnd = (i - 1) + 1;
|
||||
if (postEnd >= this.markerData.length)
|
||||
postEnd = 0;
|
||||
if (preStart < 0)
|
||||
preStart = this.markerData.length - 1;
|
||||
var p0 = this.markerData[preStart].position;
|
||||
var p1 = m1.position;
|
||||
var p2 = m2.position;
|
||||
var p3 = this.markerData[postEnd].position;
|
||||
position = new Vector();
|
||||
position.x = Util.catmullRom(compvarion, p0.x, p1.x, p2.x, p3.x);
|
||||
position.y = Util.catmullRom(compvarion, p0.y, p1.y, p2.y, p3.y);
|
||||
position.z = Util.catmullRom(compvarion, p0.z, p1.z, p2.z, p3.z);
|
||||
}
|
||||
if (position == null) {
|
||||
var p1 = m1.position;
|
||||
var p2 = m2.position;
|
||||
position = Util.lerpThreeVectors(p1, p2, compvarion);
|
||||
}
|
||||
// Offset by the position of the first marker
|
||||
var firstPosition = this.markerData[0].position;
|
||||
position.sub(firstPosition);
|
||||
var tform = this.getTransform().clone();
|
||||
var basePosition = tform.getPosition();
|
||||
position.add(basePosition); // Add the base position
|
||||
tform.setPosition(position);
|
||||
return tform;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
this.currentTime = 0;
|
||||
this.targetTime = -1;
|
||||
this.changeTime = 0;
|
||||
// Reset the position
|
||||
var transform = this.getTransformAtTime(this.getInternalTime(0));
|
||||
var position = transform.getPosition();
|
||||
this.prevPosition = position.clone();
|
||||
this.currentPosition = position;
|
||||
this.velocity = new Vector();
|
||||
updatePosition();
|
||||
}
|
||||
}
|
||||
22
src/PathedInteriorMarker.hx
Normal file
22
src/PathedInteriorMarker.hx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package src;
|
||||
|
||||
import h3d.Quat;
|
||||
import h3d.Vector;
|
||||
|
||||
class PathedInteriorMarker {
|
||||
public var msToNext:Float;
|
||||
public var smoothingType:String;
|
||||
public var position:Vector;
|
||||
public var rotation:Quat;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function clone() {
|
||||
var ret = new PathedInteriorMarker();
|
||||
ret.msToNext = msToNext;
|
||||
ret.smoothingType = smoothingType;
|
||||
ret.position = position;
|
||||
ret.rotation = rotation;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
35
src/Util.hx
Normal file
35
src/Util.hx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package src;
|
||||
|
||||
import h3d.Vector;
|
||||
|
||||
class Util {
|
||||
public static function adjustedMod(a:Float, n:Float) {
|
||||
var r1 = a % n;
|
||||
var r2 = (r1 + n) % n;
|
||||
return r2;
|
||||
}
|
||||
|
||||
public static function clamp(value:Float, min:Float, max:Float) {
|
||||
if (value < min)
|
||||
return min;
|
||||
if (value > max)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static function lerp(a:Float, b:Float, t:Float) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
public static function catmullRom(t:Float, p0:Float, p1:Float, p2:Float, p3:Float) {
|
||||
var point = t * t * t * ((-1) * p0 + 3 * p1 - 3 * p2 + p3) / 2;
|
||||
point += t * t * (2 * p0 - 5 * p1 + 4 * p2 - p3) / 2;
|
||||
point += t * ((-1) * p0 + p2) / 2;
|
||||
point += p1;
|
||||
return point;
|
||||
}
|
||||
|
||||
public static function lerpThreeVectors(v1:Vector, v2:Vector, t:Float) {
|
||||
return new Vector(lerp(v1.x, v2.x, t), lerp(v1.y, v2.y, t), lerp(v1.z, v2.z, t));
|
||||
}
|
||||
}
|
||||
|
|
@ -13,12 +13,13 @@ import h3d.col.Bounds;
|
|||
class CollisionEntity implements IOctreeObject {
|
||||
public var boundingBox:Bounds;
|
||||
|
||||
var octree:Octree;
|
||||
public var octree:Octree;
|
||||
|
||||
public var surfaces:Array<CollisionSurface>;
|
||||
|
||||
public var priority:Int;
|
||||
public var position:Int;
|
||||
public var velocity:Vector = new Vector();
|
||||
|
||||
public var transform:Matrix;
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ class CollisionEntity implements IOctreeObject {
|
|||
this.priority = priority;
|
||||
}
|
||||
|
||||
public function sphereIntersection(collisionEntity:SphereCollisionEntity) {
|
||||
public function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float) {
|
||||
var position = collisionEntity.transform.getPosition();
|
||||
var velocity = collisionEntity.velocity;
|
||||
var radius = collisionEntity.radius;
|
||||
|
|
@ -72,6 +73,9 @@ class CollisionEntity implements IOctreeObject {
|
|||
localpos.transform(invMatrix);
|
||||
var surfaces = octree.radiusSearch(localpos, radius * 1.1);
|
||||
|
||||
var tform = transform.clone();
|
||||
// tform.setPosition(tform.getPosition().add(velocity.multiply(dt)));
|
||||
|
||||
var contacts = [];
|
||||
|
||||
for (obj in surfaces) {
|
||||
|
|
@ -79,11 +83,11 @@ class CollisionEntity implements IOctreeObject {
|
|||
|
||||
var i = 0;
|
||||
while (i < surface.indices.length) {
|
||||
var v0 = surface.points[surface.indices[i]].transformed(transform);
|
||||
var v = surface.points[surface.indices[i + 1]].transformed(transform);
|
||||
var v2 = surface.points[surface.indices[i + 2]].transformed(transform);
|
||||
var v0 = surface.points[surface.indices[i]].transformed(tform);
|
||||
var v = surface.points[surface.indices[i + 1]].transformed(tform);
|
||||
var v2 = surface.points[surface.indices[i + 2]].transformed(tform);
|
||||
|
||||
var surfacenormal = surface.normals[surface.indices[i]].transformed(transform);
|
||||
var surfacenormal = surface.normals[surface.indices[i]].transformed3x3(transform);
|
||||
|
||||
var res = Collision.IntersectTriangleSphere(v0, v, v2, surfacenormal, position, radius);
|
||||
var closest = res.point;
|
||||
|
|
@ -99,7 +103,7 @@ class CollisionEntity implements IOctreeObject {
|
|||
cinfo.normal = res.normal; // surface.normals[surface.indices[i]];
|
||||
cinfo.point = closest;
|
||||
// cinfo.collider = this;
|
||||
cinfo.velocity = new Vector();
|
||||
cinfo.velocity = this.velocity;
|
||||
cinfo.penetration = radius - (position.sub(closest).dot(normal));
|
||||
cinfo.restitution = 1;
|
||||
cinfo.friction = 1;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package collision;
|
||||
|
||||
import h3d.col.Bounds;
|
||||
import h3d.col.Sphere;
|
||||
import h3d.Vector;
|
||||
import octree.Octree;
|
||||
|
|
@ -13,11 +14,11 @@ class CollisionWorld {
|
|||
this.octree = new Octree();
|
||||
}
|
||||
|
||||
public function sphereIntersection(spherecollision:SphereCollisionEntity) {
|
||||
public function sphereIntersection(spherecollision:SphereCollisionEntity, dt:Float) {
|
||||
var position = spherecollision.transform.getPosition();
|
||||
var radius = spherecollision.radius;
|
||||
var velocity = spherecollision.velocity;
|
||||
var searchdist = velocity.length() + radius;
|
||||
var searchdist = (velocity.length() * dt) + radius;
|
||||
var intersections = this.octree.radiusSearch(position, searchdist);
|
||||
|
||||
var contacts = [];
|
||||
|
|
@ -25,17 +26,44 @@ class CollisionWorld {
|
|||
for (obj in intersections) {
|
||||
var entity:CollisionEntity = cast obj;
|
||||
|
||||
contacts = contacts.concat(entity.sphereIntersection(spherecollision));
|
||||
contacts = contacts.concat(entity.sphereIntersection(spherecollision, dt));
|
||||
}
|
||||
|
||||
for (obj in dynamicEntities) {
|
||||
if (obj != spherecollision) {
|
||||
contacts = contacts.concat(obj.sphereIntersection(spherecollision));
|
||||
contacts = contacts.concat(obj.sphereIntersection(spherecollision, dt));
|
||||
}
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public function radiusSearch(center:Vector, radius:Float) {
|
||||
var intersections = this.octree.radiusSearch(center, radius);
|
||||
|
||||
var box = new Bounds();
|
||||
box.xMin = center.x - radius;
|
||||
box.yMin = center.y - radius;
|
||||
box.zMin = center.z - radius;
|
||||
box.xMax = center.x - radius;
|
||||
box.yMax = center.y - radius;
|
||||
box.zMax = center.z - radius;
|
||||
|
||||
var contacts:Array<CollisionEntity> = [];
|
||||
|
||||
for (obj in intersections) {
|
||||
var entity:CollisionEntity = cast obj;
|
||||
|
||||
contacts = contacts.concat(entity);
|
||||
}
|
||||
|
||||
for (obj in dynamicEntities) {
|
||||
if (obj.boundingBox.collide(box))
|
||||
contacts = contacts.concat(obj);
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public function addEntity(entity:CollisionEntity) {
|
||||
this.octree.insert(entity);
|
||||
this.entities.push(entity);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ 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) {
|
||||
|
|
@ -31,7 +30,7 @@ class SphereCollisionEntity extends CollisionEntity {
|
|||
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) {
|
||||
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float) {
|
||||
var contacts = [];
|
||||
var thispos = transform.getPosition();
|
||||
var position = collisionEntity.transform.getPosition();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue