//
// MeshLineCollider.swift
// Gangster Driveby
//
// Created by Mike Farrell on 10/30/14.
// Copyright (c) 2014 Mike Farrell. All rights reserved.
//
import Foundation
class MeshLineCollider
{
weak var entity: Entity!
var boundingBoxCollider: MeshLineCollider?
let verts: GrowableArray
let faceIndices: GrowableArray
init(meshEntity: Entity)
{
entity = meshEntity
verts = entity.editVerts.mutableCopy() as GrowableArray
entity.applyTransformToVerts(verts, andNorms: nil)
let s: SubMesh = entity.submeshes[0] as SubMesh
faceIndices = s.editInds.mutableCopy() as GrowableArray
generateBoundingBoxCollider()
}
init(verts: GrowableArray, faces: GrowableArray)
{
self.verts = verts
faceIndices = faces
boundingBoxCollider = nil
}
convenience init(var verts: [float3], var faces: [uint3])
{
let vBuffer = UnsafeMutableBufferPointer(start: &verts, count: verts.count)
let vgArray = GrowableArray(data: vBuffer.baseAddress, ofSize: Int32(verts.count), andElementSize:Int32(sizeof(float3)), andExtraCapacity: 0)
let fBuffer = UnsafeMutableBufferPointer<uint3>(start: &faces, count: faces.count)
let fArray = GrowableArray(data: fBuffer.baseAddress, ofSize: Int32(faces.count*3), andElementSize:Int32(sizeof(uint)), andExtraCapacity: 0)
self.init(verts: vgArray, faces: fArray)
}
private final func split(quad: uint4) -> (uint3, uint3)
{
return (uint3(x: quad.x, y: quad.y, z: quad.w), uint3(x: quad.w, y: quad.y, z: quad.z))
}
private func generateBoundingBoxCollider()
{
var bbSize = float3.zero
entity.getBBox(&bbSize)
let box = Box3D(dimensions: bbSize, andPosition: float3.zero)
let boxVerts = box.generateVertices()
entity.applyTransformToVerts(boxVerts, andNorms: nil)
var boxInds: [uint4] = [ ]
boxInds.append(uint4(x: 0, y: 1, z: 2, w: 3))
boxInds.append(uint4(x: 0, y: 4, z: 5, w: 1))
boxInds.append(uint4(x: 2, y: 6, z: 7, w: 3))
boxInds.append(uint4(x: 3, y: 7, z: 4, w: 0))
boxInds.append(uint4(x: 1, y: 5, z: 6, w: 2))
boxInds.append(uint4(x: 4, y: 5, z: 6, w: 7))
var triangles: [uint3] = [ ]
for quad in boxInds
{
let spl = split(quad)
triangles.append(spl.0)
triangles.append(spl.1)
}
let fBuffer = UnsafeMutableBufferPointer<uint3>(start: &triangles, count: triangles.count)
let fArray = GrowableArray(data: fBuffer.baseAddress, ofSize: Int32(triangles.count*3), andElementSize:Int32(sizeof(uint)), andExtraCapacity: 0)
boundingBoxCollider = MeshLineCollider(verts: boxVerts, faces: fArray)
}
func debugRender()
{
if boundingBoxCollider != nil
{
boundingBoxCollider?.debugRender()
return
}
glDisable(GL_CULL_FACE.e)
glDepthRange(0.0, 0.9999);
let vgl = VertoGLStateMachine.currentMachine()
vgl.vertexPointerOfSize(3, type: GL_FLOAT.e, stride: 0, pointer: verts.data, bufferLength: GLsizeiptr(Int32(sizeof(float3))*verts.count))
vgl.setPrimaryColor(makeFloat4(0.2, 0.2, 0.2, 1))
vgl.prepareToDraw()
vgl.drawElementsInMode(GL_TRIANGLES.e, andCount: GLsizei(faceIndices.count), type: GL_UNSIGNED_INT.e, indexPointer: faceIndices.data,
bufferLength: GLsizeiptr(Int32(sizeof(uint))*faceIndices.count))
glDepthRange(0.0, 1.0);
}
//MARK: - Collision with line
func rayIntersection(rayOrigin ro: float3, rayDirection rd: float3, inout intersectionPoint: float3) -> IntersectionPoint?
{
if let bbox = boundingBoxCollider
{
if bbox.rayIntersection(rayOrigin: ro, rayDirection: rd, intersectionPoint: &intersectionPoint) == nil
{
return nil
}
}
let pointSet = IntersectionPointSet()
for i in 0..<faceIndices.count/3
{
let result = triangleIntersection(triangleIndex: Int(i), rayOrigin: ro, rayDirection: rd, intersectionPoint: &intersectionPoint)
if result.0
{
pointSet.add(IntersectionPoint(distance: result.1, point: intersectionPoint))
}
}
if !pointSet.empty()
{
return pointSet.nearest()
}
return nil
}
private func triangleIntersection(triangleIndex i: Int, rayOrigin ro: float3, rayDirection rd: float3, inout intersectionPoint ip: float3) -> (Bool, Float)
{
let smallNum: Float = 0.00000001
var u: float3, v: float3, n: float3 // triangle vectors
var dir: float3, w0: float3, w: float3 // ray vectors
var r: Float, a: Float, b: Float // params to calc ray-plane intersect
let vData = UnsafeMutableBufferPointer<float3>(start: UnsafeMutablePointer<float3>(verts.data), count: Int(verts.count))
let iData = UnsafeMutableBufferPointer<uint3>(start: UnsafeMutablePointer<uint3>(faceIndices.data), count: Int(faceIndices.count/3))
let v0 = vData[Int(iData[i].x)], v1 = vData[Int(iData[i].y)], v2 = vData[Int(iData[i].z)]
// get triangle edge vectors and plane normal
u = v1 - v0
v = v2 - v0
n = u.cross(v) // cross product
if n == float3.zero // triangle is degenerate
{
// do not deal with this case
return (false, 0)
}
dir = rd
w0 = ro - v0
a = -n.dot(w0)
b = n.dot(dir)
if fabsf(b) < smallNum
{
return (false, 0)
}
// get intersect point of ray with triangle plane
r = a / b
if r < 0.0 // ray goes away from triangle
{
return (false, 0) // => no intersect
}
// for a segment, also test if (r > 1.0) => no intersect
// intersect point of ray and plane
ip = ro + dir*r
// is I inside T?
var uu: Float, uv: Float, vv: Float, wu: Float, wv: Float, D: Float
uu = u.dot(u)
uv = u.dot(v)
vv = v.dot(v)
w = ip - v0
wu = w.dot(u)
wv = w.dot(v)
D = uv * uv - uu * vv
// get and test parametric coords
var s: Float, t: Float
s = (uv * wv - vv * wu) / D
if s < 0.0 || s > 1.0 // I is outside T
{
return (false, 0)
}
t = (uv * wu - uu * wv) / D
if t < 0.0 || (s + t) > 1.0 // I is outside T
{
return (false, 0)
}
return (true, r) // I is in T
}
}