diff --git a/src/CameraController.hx b/src/CameraController.hx index 7954b94f..fc82ccc5 100644 --- a/src/CameraController.hx +++ b/src/CameraController.hx @@ -127,12 +127,20 @@ class CameraController extends Object { public function enableSpectate() { spectate = true; - @:privateAccess this.level.playGui.setSpectateMenu(true); + if (@:privateAccess this.level.playGui.setSpectateMenu(true)) { + if (Util.isTouchDevice()) { + MarbleGame.instance.touchInput.setSpectatorControls(true); + MarbleGame.instance.touchInput.setSpectatorControlsVisibility(false); + } + } } public function stopSpectate() { spectate = false; @:privateAccess this.level.playGui.setSpectateMenu(false); + if (Util.isTouchDevice()) { + MarbleGame.instance.touchInput.setSpectatorControls(false); + } } public function orbit(mouseX:Float, mouseY:Float, isTouch:Bool = false) { @@ -163,7 +171,7 @@ class CameraController extends Object { } var factor = isTouch ? Util.lerp(1 / 25, 1 / 15, - Settings.controlsSettings.cameraSensitivity) : Util.lerp(1 / 2500, 1 / 100, Settings.controlsSettings.cameraSensitivity); + Settings.controlsSettings.cameraSensitivity) : Util.lerp(1 / 1000, 1 / 200, Settings.controlsSettings.cameraSensitivity); // CameraPitch += deltaposY * factor; // CameraYaw += deltaposX * factor; @@ -264,8 +272,8 @@ class CameraController extends Object { nextCameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, nextCameraPitch)); - CameraYaw = Util.lerp(CameraYaw, nextCameraYaw, lerpt); - CameraPitch = Util.lerp(CameraPitch, nextCameraPitch, lerpt); + CameraYaw = nextCameraYaw; // Util.lerp(CameraYaw, nextCameraYaw, lerpt); + CameraPitch = nextCameraPitch; // Util.lerp(CameraPitch, nextCameraPitch, lerpt); CameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, CameraPitch)); // Util.clamp(CameraPitch, -Math.PI / 12, Math.PI / 2); @@ -331,7 +339,7 @@ class CameraController extends Object { if (MarbleGame.instance.touchInput.movementInput.pressed) { dx = -MarbleGame.instance.touchInput.movementInput.value.x * CameraSpeed * dt; - dy = MarbleGame.instance.touchInput.movementInput.value.y * CameraSpeed * dt; + dy = -MarbleGame.instance.touchInput.movementInput.value.y * CameraSpeed * dt; } if ((!Util.isTouchDevice() && Key.isDown(Settings.controlsSettings.powerup)) @@ -342,10 +350,12 @@ class CameraController extends Object { } if (Key.isPressed(Settings.controlsSettings.blast) - || (MarbleGame.instance.touchInput.blastbutton.pressed) + || (MarbleGame.instance.touchInput.blastbutton.pressed && MarbleGame.instance.touchInput.blastbutton.didPressIt) || Gamepad.isPressed(Settings.gamepadSettings.blast)) { var freeMarbleIndex = -1; + MarbleGame.instance.touchInput.blastbutton.didPressIt = false; + for (i in 0...level.marbles.length) { var marble = level.marbles[i]; @:privateAccess if ((marble.connection != null && !marble.connection.spectator)) { @@ -354,6 +364,7 @@ class CameraController extends Object { } } spectateMarbleIndex = freeMarbleIndex; + MarbleGame.instance.touchInput.setSpectatorControlsVisibility(true); return; } @@ -366,7 +377,9 @@ class CameraController extends Object { camera.target = camera.pos.add(directionVector); } else { @:privateAccess level.playGui.setSpectateMenuText(1); - if (Key.isPressed(Settings.controlsSettings.left)) { + if (Key.isPressed(Settings.controlsSettings.left) + || (MarbleGame.instance.touchInput.leftButton.pressed && MarbleGame.instance.touchInput.leftButton.didPressIt)) { + MarbleGame.instance.touchInput.leftButton.didPressIt = false; spectateMarbleIndex = (spectateMarbleIndex - 1 + level.marbles.length) % level.marbles.length; @:privateAccess while (level.marbles[spectateMarbleIndex].connection == null || level.marbles[spectateMarbleIndex].connection.spectator) { @@ -374,7 +387,9 @@ class CameraController extends Object { } } - if (Key.isPressed(Settings.controlsSettings.right)) { + if (Key.isPressed(Settings.controlsSettings.right) + || (MarbleGame.instance.touchInput.rightButton.pressed && MarbleGame.instance.touchInput.rightButton.didPressIt)) { + MarbleGame.instance.touchInput.rightButton.didPressIt = false; spectateMarbleIndex = (spectateMarbleIndex + 1 + level.marbles.length) % level.marbles.length; @:privateAccess while (level.marbles[spectateMarbleIndex].connection == null || level.marbles[spectateMarbleIndex].connection.spectator) { @@ -383,9 +398,16 @@ class CameraController extends Object { } if (Key.isPressed(Settings.controlsSettings.blast) - || (MarbleGame.instance.touchInput.blastbutton.pressed) + || (MarbleGame.instance.touchInput.blastbutton.pressed && MarbleGame.instance.touchInput.blastbutton.didPressIt) || Gamepad.isPressed(Settings.gamepadSettings.blast)) { + MarbleGame.instance.touchInput.blastbutton.didPressIt = false; spectateMarbleIndex = -1; + MarbleGame.instance.touchInput.setSpectatorControlsVisibility(false); + return; + } + if (@:privateAccess level.marbles.length <= spectateMarbleIndex) { + spectateMarbleIndex = -1; + MarbleGame.instance.touchInput.setSpectatorControlsVisibility(false); return; } @@ -479,7 +501,8 @@ class CameraController extends Object { var camera = level.scene.camera; - var lerpt = Math.pow(0.5, dt / 0.032); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600)); + var lerpt = hxd.Math.min(1, + 1 - Math.pow(0.6, dt * 600)); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600)); var cameraPitchDelta = (Key.isDown(Settings.controlsSettings.camBackward) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camForward) ? 1 : 0) @@ -495,8 +518,8 @@ class CameraController extends Object { nextCameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, nextCameraPitch)); - CameraYaw = Util.lerp(CameraYaw, nextCameraYaw, lerpt); - CameraPitch = Util.lerp(CameraPitch, nextCameraPitch, lerpt); + CameraYaw = nextCameraYaw; // Util.lerp(CameraYaw, nextCameraYaw, lerpt); + CameraPitch = nextCameraPitch; // Util.lerp(CameraPitch, nextCameraPitch, lerpt); CameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, CameraPitch)); // Util.clamp(CameraPitch, -Math.PI / 12, Math.PI / 2); diff --git a/src/DtsObject.hx b/src/DtsObject.hx index fb6e3c2c..f4813133 100644 --- a/src/DtsObject.hx +++ b/src/DtsObject.hx @@ -579,32 +579,99 @@ class DtsObject extends GameObject { hs.restitution = data.restitution; } - for (i in primitive.firstElement...(primitive.firstElement + primitive.numElements - 2)) { - var i1 = dtsMesh.indices[i]; - var i2 = dtsMesh.indices[i + 1]; - var i3 = dtsMesh.indices[i + 2]; + var drawType = primitive.matIndex & TSDrawPrimitive.TypeMask; - if (k % 2 == 0) { - // Swap the first and last index to mainting correct winding order - var temp = i1; - i1 = i3; - i3 = temp; + if (drawType == TSDrawPrimitive.Triangles) { + var i = primitive.firstElement; + while (i < primitive.firstElement + primitive.numElements) { + var i1 = dtsMesh.indices[i]; + var i2 = dtsMesh.indices[i + 1]; + var i3 = dtsMesh.indices[i + 2]; + + var t1 = vertices[i2].sub(vertices[i1]); + var t2 = vertices[i3].sub(vertices[i1]); + var tarea = Math.abs(t1.cross(t2).length()) / 2.0; + if (tarea < 0.00001) + continue; + + for (index in [i1, i2, i3]) { + var vertex = vertices[index]; + hs.addPoint(vertex.x, vertex.y, vertex.z); + hs.transformKeys.push(0); + + var normal = vertexNormals[index]; + hs.addNormal(normal.x, normal.y, normal.z); + } + + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + + i += 3; } + } else if (drawType == TSDrawPrimitive.Strip) { + var k = 0; + for (i in primitive.firstElement...(primitive.firstElement + primitive.numElements - 2)) { + var i1 = dtsMesh.indices[i]; + var i2 = dtsMesh.indices[i + 1]; + var i3 = dtsMesh.indices[i + 2]; - for (index in [i1, i2, i3]) { - var vertex = vertices[index]; - hs.addPoint(vertex.x, vertex.y, vertex.z); - hs.transformKeys.push(0); + if (k % 2 == 0) { + // Swap the first and last index to mainting correct winding order + var temp = i1; + i1 = i3; + i3 = temp; + } - var normal = vertexNormals[index]; - hs.addNormal(normal.x, normal.y, normal.z); + var t1 = vertices[i2].sub(vertices[i1]); + var t2 = vertices[i3].sub(vertices[i1]); + var tarea = Math.abs(t1.cross(t2).length()) / 2.0; + if (tarea < 0.00001) + continue; + + for (index in [i1, i2, i3]) { + var vertex = vertices[index]; + hs.addPoint(vertex.x, vertex.y, vertex.z); + hs.transformKeys.push(0); + + var normal = vertexNormals[index]; + hs.addNormal(normal.x, normal.y, normal.z); + } + + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + + k++; } + } else if (drawType == TSDrawPrimitive.Fan) { + var i = primitive.firstElement; + while (i < primitive.firstElement + primitive.numElements - 2) { + var i1 = dtsMesh.indices[primitive.firstElement]; + var i2 = dtsMesh.indices[i + 1]; + var i3 = dtsMesh.indices[i + 2]; - hs.indices.push(hs.indices.length); - hs.indices.push(hs.indices.length); - hs.indices.push(hs.indices.length); + var t1 = vertices[i2].sub(vertices[i1]); + var t2 = vertices[i3].sub(vertices[i1]); + var tarea = Math.abs(t1.cross(t2).length()) / 2.0; + if (tarea < 0.00001) + continue; - k++; + for (index in [i1, i2, i3]) { + var vertex = vertices[index]; + hs.addPoint(vertex.x, vertex.y, vertex.z); + hs.transformKeys.push(0); + + var normal = vertexNormals[index]; + hs.addNormal(normal.x, normal.y, normal.z); + } + + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + hs.indices.push(hs.indices.length); + + i++; + } } hs.generateBoundingBox(); diff --git a/src/Marble.hx b/src/Marble.hx index 40a11de0..2e2de86d 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -1276,6 +1276,8 @@ class Marble extends GameObject { if (obj.go != null && !obj.go.isCollideable) continue; + var isDts = obj.go is DtsObject; + var invMatrix = @:privateAccess obj.invTransform; if (obj.go is PathedInterior) invMatrix = obj.transform.getInverse(); @@ -1338,10 +1340,11 @@ class Marble extends GameObject { // var vT = v.transformed(obj.transform); // var v2T = v2.transformed(obj.transform); // var vN = surfaceNormal.transformed3x3(obj.transform); - testTriangles.push({ - v: [v0.clone(), v.clone(), v2.clone()], - n: surfaceNormal.clone(), - }); + if (!isDts) + testTriangles.push({ + v: [v0.clone(), v.clone(), v2.clone()], + n: surfaceNormal.clone(), + }); // Time until collision with the plane var collisionTime = (radius - position.dot(surfaceNormal) - surfaceD) / surfaceNormal.dot(relVel); diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 5fbdc8aa..99f51fc9 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -224,9 +224,9 @@ class MarbleWorld extends Scheduler { public var currentInputMoves:Array; // Multiplayer - public var isMultiplayer:Bool; + public var isMultiplayer:Bool = false; - public var serverStartTicks:Int; + public var serverStartTicks:Int = 0; public var startTime:Float = 1e8; public var multiplayerStarted:Bool = false; @@ -2762,7 +2762,8 @@ class MarbleWorld extends Scheduler { // this.oobCameraPosition = camera.position.clone(); if (marble == this.marble) { playGui.setCenterText('outofbounds'); - AudioManager.playSound(ResourceLoader.getResource('data/sound/whoosh.wav', ResourceLoader.getAudio, this.soundResources)); + if (@:privateAccess !this.marble.isNetUpdate) + AudioManager.playSound(ResourceLoader.getResource('data/sound/whoosh.wav', ResourceLoader.getAudio, this.soundResources)); // if (this.replay.mode != = 'playback') this.oobSchedule = this.schedule(this.timeState.currentAttemptTime + 2, () -> { playGui.setCenterText('none'); diff --git a/src/PathedInterior.hx b/src/PathedInterior.hx index 1df00841..a7d0e2f0 100644 --- a/src/PathedInterior.hx +++ b/src/PathedInterior.hx @@ -59,6 +59,7 @@ class PathedInterior extends InteriorObject { var savedVelocity:Vector; var savedStopped:Bool; var savedStoppedPosition:Vector; + var savedInvPosition:Vector; var savedTime:Float; var soundChannel:Channel; @@ -115,7 +116,14 @@ class PathedInterior extends InteriorObject { this.markerData = this.path.markers.map(x -> { var marker = new PathedInteriorMarker(); marker.msToNext = MisParser.parseNumber(x.mstonext) / 1000; - marker.smoothingType = x.smoothingtype; + marker.smoothingType = switch (x.smoothingtype) { + case "Accelerate": + PathedInteriorMarker.SMOOTHING_ACCELERATE; + case "Spline": + PathedInteriorMarker.SMOOTHING_SPLINE; + default: + PathedInteriorMarker.SMOOTHING_LINEAR; + }; marker.position = MisParser.parseVector3(x.position); marker.position.x = -marker.position.x; marker.rotation = MisParser.parseRotation(x.rotation); @@ -214,9 +222,10 @@ class PathedInterior extends InteriorObject { return; if (this.velocity.length() == 0) return; + static var tform = new Matrix(); velocity.w = 0; var newp = position.add(velocity.multiply(timeDelta)); - var tform = this.getAbsPos().clone(); + tform.load(this.getAbsPos()); // .clone(); tform.setPosition(newp); if (this.isCollideable) { @@ -251,6 +260,7 @@ class PathedInterior extends InteriorObject { public function pushTickState() { savedPosition = this.position.clone(); + savedInvPosition = @:privateAccess this.collider.invTransform.getPosition(); savedVelocity = this.velocity.clone(); savedStopped = this.stopped; savedStoppedPosition = this.stoppedPosition != null ? this.stoppedPosition.clone() : null; @@ -262,7 +272,17 @@ class PathedInterior extends InteriorObject { this.velocity.load(savedVelocity); this.stopped = savedStopped; this.stoppedPosition = savedStoppedPosition; + var oldtPos = this.collider.transform.getPosition(); this.collider.transform.setPosition(savedPosition); + @:privateAccess this.collider.invTransform.setPosition(savedInvPosition); + + this.collider.boundingBox.xMin += savedPosition.x - oldtPos.x; + this.collider.boundingBox.xMax += savedPosition.x - oldtPos.x; + this.collider.boundingBox.yMin += savedPosition.y - oldtPos.y; + this.collider.boundingBox.yMax += savedPosition.y - oldtPos.y; + this.collider.boundingBox.zMin += savedPosition.z - oldtPos.z; + this.collider.boundingBox.zMax += savedPosition.z - oldtPos.z; + collisionWorld.updateTransform(this.collider); this.currentTime = savedTime; @@ -301,7 +321,8 @@ class PathedInterior extends InteriorObject { if (m1 == null) { // Incase there are no markers at all var tmp = new Matrix(); - var mat = Matrix.S(this.baseScale.x, this.baseScale.y, this.baseScale.z); + var mat = new Matrix(); + mat.initScale(this.baseScale.x, this.baseScale.y, this.baseScale.z); this.baseOrientation.toMatrix(tmp); mat.multiply3x4(mat, tmp); mat.setPosition(this.basePosition); @@ -324,10 +345,10 @@ class PathedInterior extends InteriorObject { var duration = m2Time - m1Time; var position:Vector = null; var compvarion = Util.clamp(duration != 0 ? (time - m1Time) / duration : 1, 0, 1); - if (m1.smoothingType == "Accelerate") { + if (m1.smoothingType == PathedInteriorMarker.SMOOTHING_ACCELERATE) { // A simple easing function compvarion = Math.sin(compvarion * Math.PI - (Math.PI / 2)) * 0.5 + 0.5; - } else if (m1.smoothingType == "Spline") { + } else if (m1.smoothingType == PathedInteriorMarker.SMOOTHING_SPLINE) { // Smooth the path like it's a Catmull-Rom spline. var preStart = (i - 2) - 1; var postEnd = (i - 1) + 1; @@ -355,7 +376,8 @@ class PathedInterior extends InteriorObject { position = position.add(basePosition); // Add the base position var tmp = new Matrix(); - var mat = Matrix.S(this.baseScale.x, this.baseScale.y, this.baseScale.z); + var mat = new Matrix(); + mat.initScale(this.baseScale.x, this.baseScale.y, this.baseScale.z); this.baseOrientation.toMatrix(tmp); mat.multiply3x4(mat, tmp); mat.setPosition(position); diff --git a/src/PathedInteriorMarker.hx b/src/PathedInteriorMarker.hx index bbbea57f..5c1191d8 100644 --- a/src/PathedInteriorMarker.hx +++ b/src/PathedInteriorMarker.hx @@ -5,10 +5,14 @@ import h3d.Vector; class PathedInteriorMarker { public var msToNext:Float; - public var smoothingType:String; + public var smoothingType:Int; public var position:Vector; public var rotation:Quat; + public static var SMOOTHING_LINEAR = 0; + public static var SMOOTHING_ACCELERATE = 1; + public static var SMOOTHING_SPLINE = 2; + public function new() {} public function clone() { diff --git a/src/Renderer.hx b/src/Renderer.hx index b06e5aff..53c2d139 100644 --- a/src/Renderer.hx +++ b/src/Renderer.hx @@ -11,6 +11,7 @@ class Renderer extends h3d.scene.Renderer { super(); defaultPass = new h3d.pass.Default("default"); allPasses = [defaultPass, shadow]; + shadow.enabled = false; } inline function get_def() diff --git a/src/collision/Collision.hx b/src/collision/Collision.hx index 9bfefef9..6d3889c2 100644 --- a/src/collision/Collision.hx +++ b/src/collision/Collision.hx @@ -146,110 +146,14 @@ class Collision { } public static inline function TriangleSphereIntersection(v0:Vector, v1:Vector, v2:Vector, N:Vector, P:Vector, r:Float, point:Vector, normal:Vector) { - var A = v0.sub(P); - var B = v1.sub(P); - var C = v2.sub(P); - var ca = C.sub(A); - var ba = B.sub(A); - var radiusSq = r * r; - var cp = ba.cross(ca); - var aDotCp = A.dot(cp); - var cpLenSq = cp.lengthSq(); - if (aDotCp * aDotCp > radiusSq * cpLenSq) { - return false; - } - - var aSq = A.dot(A); - var aDotB = A.dot(B); - var aDotC = A.dot(C); - var bSq = B.dot(B); - var bDotC = B.dot(C); - var cSq = C.dot(C); - - if (aSq > radiusSq && aDotB > aSq && aDotC > aSq) { - return false; - } - if (bSq > radiusSq && aDotB > bSq && bDotC > bSq) { - return false; - } - if (cSq > radiusSq && aDotC > cSq && bDotC > cSq) { - return false; - } - - var cSubB = C.sub(B); - var aSubC = A.sub(C); - var baSq = ba.lengthSq(); - var cSubBSq = cSubB.lengthSq(); - var aSubCSq = aSubC.lengthSq(); - var aTest = A.multiply(baSq).sub(ba.multiply(aDotB - aSq)); - var bTest = B.multiply(cSubBSq).sub(cSubB.multiply(bDotC - bSq)); - var cTest = C.multiply(aSubCSq).sub(aSubC.multiply(aDotC - cSq)); - var rhs = C.multiply(baSq).sub(aTest); - var rhs2 = A.multiply(cSubBSq).sub(bTest); - var rhs3 = B.multiply(aSubCSq).sub(cTest); - - if (aTest.dot(aTest) > radiusSq * baSq * baSq && aTest.dot(rhs) > 0) { - return false; - } - if (bTest.dot(bTest) > radiusSq * cSubBSq * cSubBSq && bTest.dot(rhs2) > 0) { - return false; - } - if (cTest.dot(cTest) > radiusSq * aSubCSq * aSubCSq && cTest.dot(rhs3) > 0) { - return false; - } - - var lhs = P.sub(v0); - var baca = ba.dot(ca); - var caSq = ca.lengthSq(); - var lhsBa = lhs.dot(ba); - var lhsCa = lhs.dot(ca); - var len = baSq * caSq - baca * baca; - var d1 = (caSq * lhsBa - baca * lhsCa) / len; - var d2 = (baSq * lhsCa - baca * lhsBa) / len; - - if (1 - d1 - d2 >= 0 && d1 >= 0 && d2 >= 0) { - normal.load(N); - point.load(P.sub(N.multiply(P.sub(v0).dot(N)))); + ClosestPtPointTriangle(P, v0, v1, v2, point); + var v = point.sub(P); + if (v.dot(v) <= r * r) { + normal.load(P.sub(point)); + normal.normalize(); return true; } else { - var closestPt = P.sub(N.multiply(P.sub(v0).dot(N))); - var r1 = ClosestPointLine(v0, v1, closestPt); - var r2 = ClosestPointLine(v1, v2, closestPt); - var r3 = ClosestPointLine(v2, v0, closestPt); - - var chosenEdge = 0; // Bitfield - - var chosenPt:Vector = new Vector(); - if (r1.distanceSq(P) < r2.distanceSq(P)) { - chosenPt.load(r1); - chosenEdge = 1; - } else { - chosenPt.load(r2); - chosenEdge = 2; - } - if (chosenPt.distanceSq(P) < r3.distanceSq(P)) - point.load(chosenPt); - else { - chosenEdge = 4; - point.load(r3); - } - normal.load(P.sub(point).normalized()); - return true; - - // if (res.normal.dot(N) > 0.8) { - // // Internal edge - // if (chosenEdge & edgeData > 0) { - // chosenEdge -= 1; - // if (chosenEdge > 2) - // chosenEdge--; - // // if (edgeNormals[chosenEdge].length() < 0.5) { - // // res.normal = center.sub(res.point).normalized(); - // // } else - // if (edgeConcavities[chosenEdge]) { // Our edge is concave - // res.normal = N.clone(); - // } - // } - // } + return false; } } @@ -431,38 +335,65 @@ class Collision { return null; } - public static function ClosestPtPointTriangle(pt:Vector, radius:Float, p0:Vector, p1:Vector, p2:Vector, normal:Vector) { - var closest:Vector = null; - var ptDot = pt.dot(normal); - var triDot = p0.dot(normal); - if (Math.abs(ptDot - triDot) > radius * 1.1) { - return null; + public static inline function ClosestPtPointTriangle(p:Vector, a:Vector, b:Vector, c:Vector, outP:Vector) { + // Check if P in vertex region outside A + var ab = b.sub(a); + var ac = c.sub(a); + var ap = p.sub(a); + var d1 = ab.dot(ap); + var d2 = ac.dot(ap); + if (d1 <= 0.0 && d2 <= 0.0) { // barycentric coordinates (1,0,0) + outP.load(a); + return; } - closest = pt.add(normal.multiply(triDot - ptDot)); - if (Collision.PointInTriangle2(closest, p0, p1, p2)) { - return closest; + // Check if P in vertex region outside B + var bp = p.sub(b); + var d3 = ab.dot(bp); + var d4 = ac.dot(bp); + if (d3 >= 0.0 && d4 <= d3) { // barycentric coordinates (0,1,0 + outP.load(b); + return; } - var t = 10.0; - var r1 = Collision.IntersectSegmentCapsule(pt, pt, p0, p1, radius); - if (r1.result && r1.tSeg < t) { - closest = p0.add((p1.sub(p0).multiply(r1.tCap))); - t = r1.tSeg; + + // Check if P in edge region of AB, if so return projection of P onto AB + var vc = d1 * d4 - d3 * d2; + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + var v = d1 / (d1 - d3); + outP.load(a.add(ab.multiply(v))); + return; } - var r2 = Collision.IntersectSegmentCapsule(pt, pt, p1, p2, radius); - if (r2.result && r2.tSeg < t) { - closest = p1.add((p2.sub(p1).multiply(r2.tCap))); - t = r2.tSeg; + + // Check if P in vertex region outside C + var cp = p.sub(c); + var d5 = ab.dot(cp); + var d6 = ac.dot(cp); + if (d6 >= 0.0 && d5 <= d6) { // barycentric coordinates (0,0,1) + outP.load(c); + return; } - var r3 = Collision.IntersectSegmentCapsule(pt, pt, p2, p0, radius); - if (r3.result && r3.tSeg < t) { - closest = p2.add((p2.sub(p2).multiply(r3.tCap))); - t = r3.tSeg; + + // Check if P in edge region of AC, if so return projection of P onto AC + var vb = d5 * d2 - d1 * d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + var w = d2 / (d2 - d6); + outP.load(a.add(ac.multiply(w))); + return; } - var res = t < 1; - if (res) { - return closest; + + // Check if P in edge region of BC, if so return projection of P onto BC + var va = d3 * d6 - d5 * d4; + if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) { + var w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + outP.load(b.add((c.sub(b)).multiply(w))); + return; } - return null; + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + + var denom = 1.0 / (va + vb + vc); + var v = vb * denom; + var w = vc * denom; + outP.load(a.add(ab.multiply(v)).add(ac.multiply(w))); + return; } public static function capsuleSphereNearestOverlap(a0:Vector, a1:Vector, radA:Float, b:Vector, radB:Float) { diff --git a/src/collision/CollisionEntity.hx b/src/collision/CollisionEntity.hx index ac2f277a..496b40d8 100644 --- a/src/collision/CollisionEntity.hx +++ b/src/collision/CollisionEntity.hx @@ -96,26 +96,39 @@ class CollisionEntity implements IOctreeObject implements IBVHObject { if (this.transform.equal(transform)) return; // Speedup - // if (this.fastTransform && Util.mat3x3equal(this.transform, transform)) { - // var oldPos = this.transform.getPosition(); - // var newPos = transform.getPosition(); - // this.transform.setPosition(newPos); - // this.invTransform = this.transform.getInverse(); - // if (this.boundingBox == null) - // generateBoundingBox(); - // else { - // this.boundingBox.xMin += newPos.x - oldPos.x; - // this.boundingBox.xMax += newPos.x - oldPos.x; - // this.boundingBox.yMin += newPos.y - oldPos.y; - // this.boundingBox.yMax += newPos.y - oldPos.y; - // this.boundingBox.zMin += newPos.z - oldPos.z; - // this.boundingBox.zMax += newPos.z - oldPos.z; - // } - // } else { - this.transform.load(transform); - this.invTransform = transform.getInverse(); - generateBoundingBox(); - // } + if (this.fastTransform && Util.mat3x3equal(this.transform, transform)) { + var oldPos = this.transform.getPosition(); + var newPos = transform.getPosition(); + this.transform.setPosition(newPos); + this.invTransform.prependTranslation(oldPos.x - newPos.x, oldPos.y - newPos.y, oldPos.z - newPos.z); + if (this.boundingBox == null) + generateBoundingBox(); + else { + this.boundingBox.xMin += newPos.x - oldPos.x; + this.boundingBox.xMax += newPos.x - oldPos.x; + this.boundingBox.yMin += newPos.y - oldPos.y; + this.boundingBox.yMax += newPos.y - oldPos.y; + this.boundingBox.zMin += newPos.z - oldPos.z; + this.boundingBox.zMax += newPos.z - oldPos.z; + + if (Debug.drawBounds) { + if (_dbgEntity == null) { + _dbgEntity = cast this.boundingBox.makeDebugObj(); + _dbgEntity.getMaterials()[0].mainPass.wireframe = true; + MarbleGame.instance.scene.addChild(_dbgEntity); + } else { + _dbgEntity.remove(); + _dbgEntity = cast this.boundingBox.makeDebugObj(); + _dbgEntity.getMaterials()[0].mainPass.wireframe = true; + MarbleGame.instance.scene.addChild(_dbgEntity); + } + } + } + } else { + this.transform.load(transform); + this.invTransform = transform.getInverse(); + generateBoundingBox(); + } _transformKey++; } diff --git a/src/gui/MPPreGameDlg.hx b/src/gui/MPPreGameDlg.hx index 46b0f43b..057ee172 100644 --- a/src/gui/MPPreGameDlg.hx +++ b/src/gui/MPPreGameDlg.hx @@ -168,11 +168,11 @@ class MPPreGameDlg extends GuiControl { }; dialogImg.addChild(levelName); - var levelDesc = new GuiText(markerFelt18); + var levelDesc = new GuiMLText(markerFelt18, null); levelDesc.text.textColor = 0xFFFFFF; levelDesc.position = new Vector(60, 185); levelDesc.extent = new Vector(516, 63); - levelDesc.text.text = MarbleGame.instance.world.mission.description; + levelDesc.text.text = StringTools.htmlEscape(MarbleGame.instance.world.mission.description); levelDesc.text.dropShadow = { dx: 1, dy: 1, @@ -244,8 +244,8 @@ class MPPreGameDlg extends GuiControl { ready: Net.lobbyHostReady, spectate: Net.hostSpectate }); - spectateBtn.pressed = Net.hostSpectate; - readyBtn.pressed = Net.lobbyHostReady; + spectateBtn.anim.currentFrame = Net.hostSpectate ? 2 : 0; + readyBtn.anim.currentFrame = Net.lobbyHostReady ? 2 : 0; } if (Net.isClient) { playerListArr.push({ @@ -253,8 +253,8 @@ class MPPreGameDlg extends GuiControl { ready: Net.lobbyClientReady, spectate: Net.clientSpectate }); - spectateBtn.pressed = Net.clientSpectate; - readyBtn.pressed = Net.lobbyClientReady; + spectateBtn.anim.currentFrame = Net.clientSpectate ? 2 : 0; + readyBtn.anim.currentFrame = Net.lobbyClientReady ? 2 : 0; } if (Net.clientIdMap != null) { for (c => v in Net.clientIdMap) { diff --git a/src/gui/OptionsDlg.hx b/src/gui/OptionsDlg.hx index f93264dc..c7b7e050 100644 --- a/src/gui/OptionsDlg.hx +++ b/src/gui/OptionsDlg.hx @@ -346,8 +346,8 @@ class OptionsDlg extends GuiImage { makeSlider("Field of View:", (Settings.optionsSettings.fovX - 60) / (140 - 60), yPos, generalPanel, (val) -> { Settings.optionsSettings.fovX = cast(60 + val * (140 - 60)); }); - makeSlider("Mouse Speed:", (Settings.controlsSettings.cameraSensitivity - 0.2) / (3 - 0.2), yPos, generalPanel, (val) -> { - Settings.controlsSettings.cameraSensitivity = cast(0.2 + val * (3 - 0.2)); + makeSlider("Mouse Speed:", (Settings.controlsSettings.cameraSensitivity - 0.12) / (1.2 - 0.12), yPos, generalPanel, (val) -> { + Settings.controlsSettings.cameraSensitivity = cast(0.12 + val * (1.2 - 0.12)); }, true); function getConflictingBinding(bindingName:String, key:Int) { diff --git a/src/gui/PlayGui.hx b/src/gui/PlayGui.hx index 9d18d4ea..f5eec555 100644 --- a/src/gui/PlayGui.hx +++ b/src/gui/PlayGui.hx @@ -225,7 +225,7 @@ class PlayGui { } if (Util.isTouchDevice()) { - MarbleGame.instance.touchInput.showControls(this.playGuiCtrl, game == 'ultra'); + MarbleGame.instance.touchInput.showControls(this.playGuiCtrl, game == 'ultra' || MarbleGame.instance.world.isMultiplayer); } playGuiCtrl.render(scene2d); @@ -592,6 +592,10 @@ class PlayGui { } public function setBlastValue(value:Float) { + if (Net.clientSpectate || Net.hostSpectate) { + MarbleGame.instance.touchInput.blastbutton.setEnabled(true); + return; // Is not changed + } if (value <= 1) { if (blastFill.extent.y == 16) { // Was previously charged blastFrame.bmp.tile = ResourceLoader.getResource("data/ui/game/blastbar.png", ResourceLoader.getImage, this.imageResources).toTile(); @@ -862,9 +866,10 @@ class PlayGui { public function setSpectateMenu(enabled:Bool) { if (enabled && spectatorCtrl == null) { initSpectatorMenu(); - playGuiCtrl.render(MarbleGame.canvas.scene2d); + spectatorCtrl.render(MarbleGame.canvas.scene2d, @:privateAccess playGuiCtrl._flow); blastFill.bmp.visible = false; blastFrame.bmp.visible = false; + return true; } if (!enabled && spectatorCtrl != null) { spectatorCtrl.dispose(); @@ -872,7 +877,9 @@ class PlayGui { blastFill.bmp.visible = true; blastFrame.bmp.visible = true; spectatorTxtMode = -1; + return true; } + return false; } public function setSpectateMenuText(mode:Int) { diff --git a/src/modes/HuntMode.hx b/src/modes/HuntMode.hx index e7013f8c..e390da29 100644 --- a/src/modes/HuntMode.hx +++ b/src/modes/HuntMode.hx @@ -550,10 +550,6 @@ class HuntMode extends NullMode { var beam = gemToBeamMap.get(gem); beam.setHide(true); - if (!this.level.isMultiplayer || Net.isHost) { - spawnHuntGems(); - } - var incr = 0; switch (gem.gemColor.toLowerCase()) { case "red.gem": @@ -641,6 +637,9 @@ class HuntMode extends NullMode { if (this.level.isMultiplayer && Net.isClient) { gem.pickUpClient = @:privateAccess marble.connection == null ? Net.clientId : @:privateAccess marble.connection.id; } + if (!this.level.isMultiplayer || Net.isHost) { + spawnHuntGems(); + } } public function setCompetitiveTimerStartTicks(ticks:Int) { diff --git a/src/touch/BlastButton.hx b/src/touch/BlastButton.hx index 14f8b137..38be5a6c 100644 --- a/src/touch/BlastButton.hx +++ b/src/touch/BlastButton.hx @@ -8,10 +8,16 @@ import src.ResourceLoader; import src.Settings; class BlastButton extends TouchButton { + public var didPressIt:Bool = true; + public function new() { var mode = MarbleGame.instance.world != null ? @:privateAccess MarbleGame.instance.world.marble.camera.spectate : false; super(ResourceLoader.getImage(mode ? "data/ui/touch/video-camera.png" : "data/ui/touch/explosion.png").resource, new Vector(Settings.touchSettings.blastButtonPos[0], Settings.touchSettings.blastButtonPos[1]), Settings.touchSettings.blastButtonSize); this.setEnabled(false); + this.onClick = () -> { + this.pressed = true; + didPressIt = true; + } } } diff --git a/src/touch/CameraInput.hx b/src/touch/CameraInput.hx index c059eba6..3a05a495 100644 --- a/src/touch/CameraInput.hx +++ b/src/touch/CameraInput.hx @@ -87,9 +87,9 @@ class CameraInput { if (jumpcam) { scaleFactor /= Settings.touchSettings.buttonJoystickMultiplier; } - if (Math.abs(delta.x) < 0.03) + if (Math.abs(delta.x) < 0.05) delta.x = 0; - if (Math.abs(delta.y) < 0.03) + if (Math.abs(delta.y) < 0.05) delta.y = 0; MarbleGame.instance.world.marble.camera.orbit(applyNonlinearScale(delta.x / scaleFactor), applyNonlinearScale(delta.y / scaleFactor), true); if (delta.x != 0) diff --git a/src/touch/PauseButton.hx b/src/touch/PauseButton.hx index 3130fabd..c9a9f45c 100644 --- a/src/touch/PauseButton.hx +++ b/src/touch/PauseButton.hx @@ -12,7 +12,7 @@ class PauseButton extends TouchButton { this.guiElement.vertSizing = Bottom; this.onClick = () -> { - if (MarbleGame.instance.world != null) { + if (MarbleGame.instance.world != null && @:privateAccess !MarbleGame.instance.paused) { @:privateAccess MarbleGame.instance.paused = true; MarbleGame.instance.handlePauseGame(); } diff --git a/src/touch/SpectatorChangeTargetButton.hx b/src/touch/SpectatorChangeTargetButton.hx new file mode 100644 index 00000000..47b153d1 --- /dev/null +++ b/src/touch/SpectatorChangeTargetButton.hx @@ -0,0 +1,25 @@ +package touch; + +import src.MarbleGame; +import touch.TouchInput.Touch; +import h3d.Vector; +import hxd.Window; +import src.ResourceLoader; +import src.Settings; + +class SpectatorChangeTargetButton extends TouchButton { + public var didPressIt:Bool = true; + + public function new(rightFacing:Bool) { + super(ResourceLoader.getImage(rightFacing ? "data/ui/touch/right.png" : "data/ui/touch/left.png").resource, new Vector(rightFacing ? 560 : 70, 120), + 60); + if (!rightFacing) { + this.guiElement.horizSizing = Right; + } + this.setEnabled(false); + this.onClick = () -> { + this.pressed = true; + didPressIt = true; + } + } +} diff --git a/src/touch/TouchInput.hx b/src/touch/TouchInput.hx index f09b803e..9405a451 100644 --- a/src/touch/TouchInput.hx +++ b/src/touch/TouchInput.hx @@ -4,6 +4,8 @@ import gui.GuiControl; import src.MarbleWorld; import h3d.Vector; import src.Settings; +import src.MarbleGame; +import src.ResourceLoader; enum TouchState { Pressed; @@ -44,6 +46,8 @@ class TouchInput { public var pauseButton:PauseButton; public var rewindButton:RewindButton; public var restartButton:RestartButton; + public var leftButton:SpectatorChangeTargetButton; + public var rightButton:SpectatorChangeTargetButton; public var currentTouchState:TouchEventState; @@ -118,17 +122,19 @@ class TouchInput { this.movementInput = new MovementInput(); this.jumpButton = new JumpButton(); this.powerupButton = new PowerupButton(); - if (Settings.optionsSettings.rewindEnabled) + if (Settings.optionsSettings.rewindEnabled && !MarbleGame.instance.world.isMultiplayer) this.rewindButton = new RewindButton(); if (ultra) this.blastbutton = new BlastButton(); this.pauseButton = new PauseButton(); - this.restartButton = new RestartButton(); + if (!MarbleGame.instance.world.isMultiplayer) + this.restartButton = new RestartButton(); pauseButton.add(parentGui); - restartButton.add(parentGui); + if (!MarbleGame.instance.world.isMultiplayer) + restartButton.add(parentGui); jumpButton.add(parentGui); powerupButton.add(parentGui); - if (Settings.optionsSettings.rewindEnabled) + if (Settings.optionsSettings.rewindEnabled && !MarbleGame.instance.world.isMultiplayer) rewindButton.add(parentGui); if (ultra) blastbutton.add(parentGui); @@ -147,6 +153,10 @@ class TouchInput { this.restartButton.setVisible(false); if (this.rewindButton != null) this.rewindButton.setVisible(false); + if (this.leftButton != null) + this.leftButton.setVisible(false); + if (this.rightButton != null) + this.rightButton.setVisible(false); } } @@ -157,10 +167,15 @@ class TouchInput { this.blastbutton.setVisible(enabled); this.movementInput.setVisible(enabled); this.pauseButton.setVisible(enabled); - this.restartButton.setVisible(enabled); + if (this.restartButton != null) + this.restartButton.setVisible(enabled); if (this.rewindButton != null) this.rewindButton.setVisible(enabled); this.cameraInput.enabled = enabled; + if (this.leftButton != null) + this.leftButton.setVisible(enabled); + if (this.rightButton != null) + this.rightButton.setVisible(enabled); if (Settings.touchSettings.hideControls) { this.jumpButton.setVisible(false); @@ -170,6 +185,10 @@ class TouchInput { this.movementInput.setVisible(false); if (this.rewindButton != null) this.rewindButton.setVisible(false); + if (this.leftButton != null) + this.leftButton.setVisible(false); + if (this.rightButton != null) + this.rightButton.setVisible(false); } } @@ -180,10 +199,19 @@ class TouchInput { blastbutton.remove(parentGui); movementInput.remove(parentGui); pauseButton.remove(parentGui); - restartButton.remove(parentGui); + if (this.restartButton != null) + restartButton.remove(parentGui); cameraInput.remove(parentGui); if (this.rewindButton != null) rewindButton.remove(parentGui); + if (this.leftButton != null) { + leftButton.remove(parentGui); + leftButton.dispose(); + } + if (this.rightButton != null) { + rightButton.remove(parentGui); + rightButton.dispose(); + } jumpButton.dispose(); powerupButton.dispose(); movementInput.dispose(); @@ -193,4 +221,41 @@ class TouchInput { if (this.rewindButton != null) rewindButton.dispose(); } + + public function setSpectatorControls(enabled:Bool) { + var tile = ResourceLoader.getImage(enabled ? "data/ui/touch/video-camera.png" : "data/ui/touch/explosion.png").resource; + @:privateAccess this.blastbutton.guiElement.graphics.content.state.tail.texture = tile.toTexture(); + if (enabled) { + jumpButton.setVisible(false); + if (this.leftButton == null) { // both are added at same time so it doesnt matter + var par = jumpButton.guiElement.parent; + this.leftButton = new SpectatorChangeTargetButton(false); + this.rightButton = new SpectatorChangeTargetButton(true); + this.leftButton.add(par); + this.rightButton.add(par); + this.leftButton.guiElement.render(MarbleGame.canvas.scene2d, @:privateAccess par._flow); + this.rightButton.guiElement.render(MarbleGame.canvas.scene2d, @:privateAccess par._flow); + } + } else { + jumpButton.setVisible(true); + if (this.leftButton != null) { + this.leftButton.remove(this.leftButton.guiElement.parent); + this.leftButton.dispose(); + this.leftButton = null; + } + if (this.rightButton != null) { + this.rightButton.remove(this.rightButton.guiElement.parent); + this.rightButton.dispose(); + this.rightButton = null; + } + } + } + + public function setSpectatorControlsVisibility(enabled:Bool) { + if (this.leftButton != null) { + this.leftButton.setVisible(enabled); + this.rightButton.setVisible(enabled); + this.movementInput.setVisible(!enabled); + } + } }