2014-12-07 21 views
8

Ho un po 'più di problemi matematici con la programmazione 3D e spero che tu possa aiutarmi!Scenekit Pan Traduzione 2D in 3D ortogonale solo orizzontale

Sto provando a creare un gioco 3D utilizzando Scenekit con un angolo isometrico.

Questo codice crea la mia macchina fotografica ortografica:

var cameraNode = SCNNode() 
cameraNode.camera = SCNCamera() 
cameraNode.name = "Camera" 
cameraNode.position = SCNVector3Make(-5.0, -5.0, 10.0) 
cameraNode.eulerAngles = SCNVector3Make(PI/3.0, 0.0, -PI/4.0) 
cameraNode.camera?.usesOrthographicProjection = true 
cameraNode.camera?.orthographicScale = 7.0 
scene.rootNode.addChildNode(cameraNode) 

Ora voglio spostare la telecamera utilizzando un gesto padella, producendo una sensazione di scorrimento. Per rendere ciò possibile, la fotocamera non dovrebbe muoversi verticalmente, ma solo orizzontalmente. La posizione tattile sullo schermo e la posizione non proiettata nel mondo 3D dovrebbero rimanere gli stessi durante lo spostamento.

Ho pensato di calcolare la traduzione 2D in 3D e di ignorare la componente verticale. Questo codice funziona davvero e produce quasi il risultato desiderato, ma la velocità non è corretta. Se io Pan, la fotocamera sembra accelerare e non reagire in modo corretto:

var previousTranslation = CGPointMake(0.0, 0.0) 

func pan(gesture: UIPanGestureRecognizer) 
{ 
    let view = self.view as SCNView 
    let translation = gesture.translationInView(view) 
    let location = gesture.locationInView(view) 

    let diffTrans = translation - previousTranslation 
    previousTranslation = translation 

    let cameraNode = scene.rootNode.childNodeWithName("Camera", recursively: false) 

    let worldPointTrans = view.unprojectPoint(SCNVector3Make(-Float(diffTrans.x), -Float(diffTrans.y), 0.0)) 
    let worldPoint0 = view.unprojectPoint(SCNVector3Make(0.0, 0.0, 0.0)) 

    var diff = worldPointTrans - worldPoint0 
    diff.x = diff.x/Float(cameraNode!.camera!.orthographicScale) 
    diff.y = diff.y/Float(cameraNode!.camera!.orthographicScale) 
    diff.z = 0 
    cameraNode?.position += diff 
} 

Qualcuno sa un modo sofisticato di calcolo di una traduzione schermo in traduzione 3D orizzontale, ignorando l'asse verticale?

Grazie in anticipo :)

EDIT: La vaschetta lavora per traslazione orizzontale ora. Ma non per verticale, perché ho impostato la differenza sull'asse z su zero.

+0

non si utilizza mai "diffTrans". Non vuoi usare "diffTrans" quando non proteggi (invece che "traduzione")? Altrimenti si accumulerebbero le traduzioni ad ogni iterazione. – Toyos

+0

Grazie per aver segnalato questo! Ho provato così tanti approcci diversi che non ho visto quell'errore.Risolve il problema con il panning orizzontale ma non verticale, a causa della componente verticale indesiderata della traduzione mondiale –

+0

Controlla la risposta del rickster della risposta qui: http://stackoverflow.com/questions/25150737/how-to-use- ios-swift-scenekit-scnscenerenderer-unprojectpoint-proper Alla fine ha una nota sulla gestione della non proiezione quando il piano della telecamera non è lo stesso di "mondo". Potrebbe essere un utile punto di partenza. –

risposta

9

Ho trovato la mia soluzione.!

Sto calcolando un raggio nella posizione iniziale del gesto (P1-P2) e un raggio nella posizione tradotta (Q1-Q2). Ora ho due raggi e lascio che entrambi si incrocino con il piano XY per ricevere i punti P0 e Q0

La differenza di P0 e Q0 è la traduzione non proiettata.

Questa tecnica dovrebbe funzionare anche con una fotocamera non ortogonale, ma non l'ho ancora testata.

Mi sembra che funziona, ma se qualcuno potrebbe matematicamente confermare questa ipotesi, io sarà felice di leggere che :)

Ecco il codice:

var previousLocation = SCNVector3(x: 0, y: 0, z: 0) 

func pan(gesture: UIPanGestureRecognizer) 
{ 
    let view = self.view as SCNView 
    let translation = gesture.translationInView(view) 

    let location = gesture.locationInView(view) 
    let secLocation = location + translation 

    let P1 = view.unprojectPoint(SCNVector3(x: Float(location.x), y: Float(location.y), z: 0.0)) 
    let P2 = view.unprojectPoint(SCNVector3(x: Float(location.x), y: Float(location.y), z: 1.0)) 

    let Q1 = view.unprojectPoint(SCNVector3(x: Float(secLocation.x), y: Float(secLocation.y), z: 0.0)) 
    let Q2 = view.unprojectPoint(SCNVector3(x: Float(secLocation.x), y: Float(secLocation.y), z: 1.0)) 

    let t1 = -P1.z/(P2.z - P1.z) 
    let t2 = -Q1.z/(Q2.z - Q1.z) 

    let x1 = P1.x + t1 * (P2.x - P1.x) 
    let y1 = P1.y + t1 * (P2.y - P1.y) 

    let P0 = SCNVector3Make(x1, y1,0) 

    let x2 = Q1.x + t1 * (Q2.x - Q1.x) 
    let y2 = Q1.y + t1 * (Q2.y - Q1.y) 

    let Q0 = SCNVector3Make(x2, y2, 0) 

    var diffR = Q0 - P0 
    diffR *= -1 

    let cameraNode = view.scene!.rootNode.childNodeWithName("Camera", recursively: false) 

    switch gesture.state { 
    case .Began: 
     previousLocation = cameraNode!.position 
     break; 
    case .Changed: 
     cameraNode?.position = previousLocation + diffR 
     break; 
    default: 
     break; 
    } 
} 

Red is screen translation, Blue is world translation

1

Ho calcolato le equazioni per il panning isometrico, il codice è sotto.

//camera pan ISOMETRIC logic 
func pan(gesture: UIPanGestureRecognizer) { 
    let view = self.sceneView as SCNView 
    let cameraNode = view.scene!.rootNode.childNode(withName: "Camera", recursively: false) 
    let translation = gesture.translation(in: view) 

    let constant: Float = 30.0 
    var translateX = Float(translation.y)*sin(.pi/4.0)/cos(.pi/3.0)-Float(translation.x)*cos(.pi/4.0) 
    var translateY = Float(translation.y)*cos(.pi/4.0)/cos(.pi/3.0)+Float(translation.x)*sin(.pi/4.0) 
    translateX = translateX/constant 
    translateY = translateY/constant 

    switch gesture.state { 
    case .began: 
     previousLocation = cameraNode!.position 
     break; 
    case .changed: 
     cameraNode?.position = SCNVector3Make((previousLocation.x + translateX), (previousLocation.y + translateY), (previousLocation.z)) 
     break; 
    default: 
     break; 
    } 
} 

e per ottenere il ridimensionamento destra, è necessario utilizzare ScreenHeight come variabile per orthographicScale. Il ridimensionamento che ho usato qui è 30 ingrandimenti, nota che il 30 è usato anche per la costante nel codice sopra.

let screenSize: CGRect = UIScreen.main.bounds 
    let screenHeight = screenSize.height 
    let cameraNode = SCNNode() 
    cameraNode.camera = SCNCamera() 
    cameraNode.name = "Camera" 
    let cameraDist = Float(20.0) 
    let cameraPosX = cameraDist*(-1.0)*cos(.pi/4.0)*cos(.pi/6.0) 
    let cameraPosY = cameraDist*(-1.0)*sin(.pi/4.0)*cos(.pi/6.0) 
    let cameraPosZ = cameraDist*sin(.pi/6) 
    cameraNode.position = SCNVector3Make(cameraPosX, cameraPosY, cameraPosZ) 
    cameraNode.eulerAngles = SCNVector3Make(.pi/3.0, 0.0, -.pi/4.0) 
    cameraNode.camera?.usesOrthographicProjection = true 
    cameraNode.camera?.orthographicScale = Double(screenHeight)/(2.0*30.0) //30x magnification constant. larger number = larger object 
    scene.rootNode.addChildNode(cameraNode)