Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // | |
| // Scene.swift | |
| // Focals | |
| // | |
| // Created by Caspar Wylie on 05/08/2016. | |
| // Copyright © 2016 Caspar Wylie. All rights reserved. | |
| // | |
| /* | |
| SCENE COMPONENT | |
| */ | |
| import Foundation | |
| import SceneKit | |
| import CoreMotion | |
| import MapKit | |
| import SwiftyJSON | |
| class Scene{ | |
| //MARK: Node initiation | |
| let lightNode = SCNNode(); | |
| let cameraNode = SCNNode(); | |
| let scene = SCNScene(); | |
| var floorNode: SCNNode!; | |
| var tempFocalNode: SCNNode!; | |
| var focals: [SCNNode] = []; | |
| var sceneView: SCNView!; | |
| var misc = Misc(); | |
| //MARK: Add scene view to view | |
| func renderSceneLayer(_ frameView: UIView) -> Void{ | |
| let frameRect = frameView.frame; | |
| sceneView = SCNView(frame: frameRect); | |
| sceneView.backgroundColor = UIColor(white: 1, alpha: 0.0); | |
| frameView.addSubview(sceneView); | |
| sceneView.scene = scene; | |
| sceneView.audioEngine.mainMixerNode.outputVolume = 0.0; | |
| } | |
| //MARK: values are approximate within tolerance bound | |
| func isApprox(_ value1: CGFloat, value2: CGFloat, tol: CGFloat) -> Bool{ | |
| if( ((value1 - tol) <= value2) && ((value1 + tol) >= value2 ) ){ | |
| return true; | |
| }else{ | |
| return false; | |
| } | |
| } | |
| //MARK: Node rendering | |
| func renderLight(){ | |
| let lightObj = SCNLight(); | |
| lightObj.type = SCNLight.LightType.omni; | |
| lightNode.light = lightObj; | |
| lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5); | |
| } | |
| func toDegrees(_ rad: Double) -> Double{ | |
| return rad*57.2958; | |
| } | |
| func renderCamera(){ | |
| let cameraObj = SCNCamera(); | |
| cameraNode.camera = cameraObj; | |
| cameraNode.camera!.zNear = 0.1; | |
| cameraNode.camera!.zFar = 1200.0; | |
| cameraNode.position = SCNVector3(x: 0.0, y: 15.0, z: 0.0); | |
| } | |
| func renderFloor(){ | |
| let size: CGFloat = 700.0; | |
| let floor = SCNCylinder(radius: size,height: 1.0); | |
| floorNode = SCNNode(geometry: floor); | |
| floorNode.position = SCNVector3(x: 0,y: 0, z: 0); | |
| floorNode.opacity = 0.001; | |
| floorNode.name = "floor"; | |
| } | |
| var limCheckParentCount = 0; | |
| var focalTapID = -1; | |
| func checkCorrectParentNodeFocal(node: SCNNode){ | |
| if(node.name?.characters.first == "f"){ | |
| let startIndex = node.name?.index((node.name?.startIndex)!, offsetBy: 2); | |
| focalTapID = Int((node.name?.substring(from: startIndex!))!)!; | |
| }else{ | |
| if(limCheckParentCount > 4){ | |
| limCheckParentCount = 0; | |
| focalTapID = -1; | |
| }else{ | |
| checkCorrectParentNodeFocal(node: node.parent!); | |
| } | |
| limCheckParentCount += 1; | |
| } | |
| } | |
| func DAEtoSCNNodeWithText(_ filepath:String, focalDisplayInfo: (comment: String, author: String)) -> SCNNode { | |
| var fontSize = 0; | |
| let chars = focalDisplayInfo.comment.characters.count; | |
| if(chars > 200){ | |
| fontSize = 16; | |
| }else if(chars >= 100 && chars <= 200){ | |
| fontSize = 20; | |
| }else if(chars >= 50 && chars <= 100){ | |
| fontSize = 25; | |
| }else if(chars >= 10 && chars <= 50){ | |
| fontSize = 30; | |
| }else if(chars < 10){ | |
| fontSize = 35; | |
| } | |
| //setup text nodes | |
| let singNode = SCNNode(); | |
| let localScene = SCNScene(named: filepath); | |
| let singNodeArray = localScene!.rootNode.childNodes; | |
| let displayText = focalDisplayInfo.comment; | |
| let textRenderFront = SCNText(string: displayText, extrusionDepth:1); | |
| let textRenderBack = SCNText(string: displayText, extrusionDepth:1); | |
| textRenderBack.font = UIFont(name: "STHeitiSC-Medium", size: CGFloat(fontSize)); | |
| textRenderFront.font = UIFont(name: "STHeitiSC-Medium", size: CGFloat(fontSize)); | |
| //attribute option setting | |
| let textContainerFrame = CGRect(x: 0,y: 0, width: 230, height: 150); | |
| let textIsWrapped = true; | |
| let textColor = UIColor.black; | |
| let textNodeScale = SCNVector3(0.07,0.07,0.07); | |
| //setting attributes | |
| textRenderFront.isWrapped = textIsWrapped; | |
| textRenderFront.firstMaterial?.diffuse.contents = textColor; | |
| textRenderFront.containerFrame = textContainerFrame; | |
| textRenderFront.alignmentMode = kCAAlignmentCenter; | |
| textRenderBack.isWrapped = textIsWrapped; | |
| textRenderBack.firstMaterial?.diffuse.contents = textColor; | |
| textRenderBack.containerFrame = textContainerFrame; | |
| textRenderBack.alignmentMode = kCAAlignmentCenter; | |
| //build DAE scene as node by each component | |
| for childNode in singNodeArray { | |
| let textNodeFront = SCNNode(geometry: textRenderFront); | |
| let textNodeBack = SCNNode(geometry: textRenderBack); | |
| textNodeFront.scale = textNodeScale; | |
| textNodeFront.position = SCNVector3(x: 1.9, y: 17, z: -4); //x = depth , z = lateral | |
| textNodeFront.eulerAngles = SCNVector3(x: 0, y: 1.5708, z: 0); | |
| textNodeBack.scale = textNodeScale; | |
| textNodeBack.position = SCNVector3(x: -0.9, y: 17, z: -21); | |
| textNodeBack.eulerAngles = SCNVector3(x: 0, y: -1.5708, z: 0); | |
| singNode.addChildNode(textNodeFront); | |
| singNode.addChildNode(textNodeBack); | |
| singNode.addChildNode(childNode as SCNNode); | |
| } | |
| return singNode; | |
| } | |
| func renderSingleFocal(_ renderID: Int, mapPoint: MKMapPoint, currMapPoint: MKMapPoint, focalDisplayInfo: (String, String), render: Bool, tempFocal: Bool, vec: SCNVector3, tapMoveTemp: Bool){ | |
| var focalVec: SCNVector3!; | |
| if(vec.x + vec.z + vec.y == 0){ | |
| var focalCoord = (x: mapPoint.x - currMapPoint.x, y: mapPoint.y - currMapPoint.y); | |
| focalCoord = misc.rotateAroundPoint(focalCoord, angle: -90); | |
| focalVec = SCNVector3(x: Float(focalCoord.x), y: 0, z: Float(focalCoord.y)); | |
| }else{ | |
| focalVec = vec; | |
| } | |
| if(render==true){ | |
| //initiate focals | |
| if(tempFocal == false){ | |
| let focal = DAEtoSCNNodeWithText("focalpost.dae", focalDisplayInfo: focalDisplayInfo); | |
| focal.name = "f_" + String(renderID); | |
| focal.position = focalVec; | |
| focals.append(focal); | |
| self.scene.rootNode.addChildNode(focals.last!); | |
| }else{ | |
| tempFocalNode = DAEtoSCNNodeWithText("tempfocalpost.dae", focalDisplayInfo: focalDisplayInfo); | |
| tempFocalNode.name = "f_" + String(renderID); | |
| tempFocalNode.position = focalVec; | |
| self.scene.rootNode.addChildNode(tempFocalNode); | |
| } | |
| }else{ | |
| //update focal position | |
| let moveToAction = SCNAction.move(to: focalVec, duration: 1); | |
| if(tempFocal == false){ | |
| //print(focals); | |
| if(focals.count > 0){ | |
| if(focals.indices.contains(renderID) == true){ | |
| focals[renderID].runAction(moveToAction); | |
| } | |
| } | |
| }else{ | |
| if(tapMoveTemp == true){ | |
| tempFocalNode.position = focalVec; | |
| }else{ | |
| tempFocalNode.runAction(moveToAction); | |
| } | |
| } | |
| } | |
| } | |
| func removeTempFocal(){ | |
| tempFocalNode?.removeFromParentNode(); | |
| } | |
| //MARK: render or update focal within 3D atmosphere | |
| func renderFocals(_ mapPoints: [MKMapPoint], currMapPoint: MKMapPoint, | |
| render: Bool, currentHeading: CLHeading, toHide: String, comments: JSON, tempFocalMapPoint: MKMapPoint){ | |
| let toHideAsArr = toHide.components(separatedBy: ","); | |
| //remove previous area / region data | |
| if(render==true){ | |
| if (focals.count != 0) { | |
| for oldFocalID in 0...focals.count-1{ | |
| focals[oldFocalID].removeFromParentNode(); | |
| } | |
| focals = []; | |
| } | |
| } | |
| if(tempFocalNode != nil){ | |
| let tempFocalDisplayInfo = (comment: " ", author: " "); | |
| renderSingleFocal(-1,mapPoint: tempFocalMapPoint, currMapPoint: currMapPoint, focalDisplayInfo: tempFocalDisplayInfo, render: false, tempFocal: true, vec: SCNVector3Zero,tapMoveTemp: false); | |
| } | |
| //render or move new focals | |
| var i = 0; | |
| for mPoint in mapPoints{ | |
| let focalDisplayInfo = (comment: comments[i]["c_text"].rawString()!, author: comments[i]["c_u_uname"].rawString()!); | |
| renderSingleFocal(i,mapPoint: mPoint, currMapPoint: currMapPoint, focalDisplayInfo: focalDisplayInfo, render: render, tempFocal: false, vec: SCNVector3Zero,tapMoveTemp: false); | |
| //hide non-street visible focals | |
| for hideID in toHideAsArr{ | |
| if (hideID == String(i)){ | |
| focals[i].isHidden = true; | |
| break; | |
| }else{ | |
| focals[i].isHidden = false; | |
| } | |
| } | |
| i += 1; | |
| } | |
| } | |
| //MARK: gyro to scene camera mapping, on new gyro/motion data (delegated call from ViewController) | |
| func rotateCamera(_ gyroData: CMAttitude){ | |
| let qData: CMQuaternion = gyroData.quaternion; | |
| //quaternion data to eulerAngles (prevention of gimbal lock!) | |
| let attitudeRoll = atan2((2 * qData.y * qData.w) - (2 * qData.x * qData.z), | |
| 1 - (2 * qData.y * qData.y) - (2 * qData.z * qData.z) ); | |
| let attitudePitch = atan2((2 * qData.x * qData.w) - (2 * qData.y * qData.z), | |
| 1 - (2 * qData.x * qData.x) - (2 * qData.z * qData.z) ); | |
| let attitudeYaw = asin((2 * qData.x * qData.y) + (2 * qData.z * qData.w)); | |
| cameraNode.eulerAngles = SCNVector3(x: Float(attitudePitch - 1.5708),y: Float(attitudeYaw),z: Float(-attitudeRoll)); | |
| } | |
| //MARK: Add all nodes to scene | |
| func renderSceneEssentials(){ | |
| renderLight(); | |
| renderCamera(); | |
| renderFloor(); | |
| scene.rootNode.addChildNode(lightNode); | |
| scene.rootNode.addChildNode(cameraNode); | |
| scene.rootNode.addChildNode(floorNode); | |
| } | |
| } |