From 38bcab6c623a81800f1b7e7c75b6a79f7c1a891a Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Sat, 29 May 2021 21:40:19 +0530 Subject: [PATCH] pathedinterior stuff --- src/DifBuilder.hx | 214 +++++++++++++++++ src/Main.hx | 39 +++- src/Marble.hx | 306 ++++++++++++++++++++++++- src/MarbleWorld.hx | 14 +- src/PathedInterior.hx | 150 ++++++++++++ src/PathedInteriorMarker.hx | 22 ++ src/Util.hx | 35 +++ src/collision/CollisionEntity.hx | 18 +- src/collision/CollisionWorld.hx | 36 ++- src/collision/SphereCollisionEntity.hx | 3 +- 10 files changed, 816 insertions(+), 21 deletions(-) create mode 100644 src/PathedInterior.hx create mode 100644 src/PathedInteriorMarker.hx create mode 100644 src/Util.hx diff --git a/src/DifBuilder.hx b/src/DifBuilder.hx index 27384165..46df3671 100644 --- a/src/DifBuilder.hx +++ b/src/DifBuilder.hx @@ -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>(); + + 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; + } } diff --git a/src/Main.hx b/src/Main.hx index 734a98ca..95e852fc 100644 --- a/src/Main.hx +++ b/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); } diff --git a/src/Marble.hx b/src/Marble.hx index 1974fa03..00f55ae9 100644 --- a/src/Marble.hx +++ b/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) { 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()); } } diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index ce40b4c2..8d144259 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -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 = []; + public var pathedInteriors:Array = []; public var marbles:Array = []; + 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; } } diff --git a/src/PathedInterior.hx b/src/PathedInterior.hx new file mode 100644 index 00000000..bb2fecd2 --- /dev/null +++ b/src/PathedInterior.hx @@ -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 = []; + + 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(); + } +} diff --git a/src/PathedInteriorMarker.hx b/src/PathedInteriorMarker.hx new file mode 100644 index 00000000..bbbea57f --- /dev/null +++ b/src/PathedInteriorMarker.hx @@ -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; + } +} diff --git a/src/Util.hx b/src/Util.hx new file mode 100644 index 00000000..987e7f6c --- /dev/null +++ b/src/Util.hx @@ -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)); + } +} diff --git a/src/collision/CollisionEntity.hx b/src/collision/CollisionEntity.hx index 7efa6c6b..393c508c 100644 --- a/src/collision/CollisionEntity.hx +++ b/src/collision/CollisionEntity.hx @@ -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; 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; diff --git a/src/collision/CollisionWorld.hx b/src/collision/CollisionWorld.hx index 5d433c30..7ebca7fb 100644 --- a/src/collision/CollisionWorld.hx +++ b/src/collision/CollisionWorld.hx @@ -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 = []; + + 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); diff --git a/src/collision/SphereCollisionEntity.hx b/src/collision/SphereCollisionEntity.hx index 5f98bcda..d8535948 100644 --- a/src/collision/SphereCollisionEntity.hx +++ b/src/collision/SphereCollisionEntity.hx @@ -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();