2014-06-08 63 views
4

我在SCNScene中有一個SCNBox。一旦場景動畫SCNBox變化是可以通過檢查其presentationNode.orientation看到的方向。該值在SCNVector4中返回。我如何確定SCNBox的哪一面是SCNVector4中返回值的正面?從方向獲取SCNNode的「up」端

我曾嘗試以標準化數據,並用以下數據

Side 1 = (-x, 0, 0, +x) 
Side 2 = (0, -x, 0 +y) 
Side 3 = (-x, +x, -y, y) 
Side 4 = (x, x, y, y) 
Side 5 = (-x, 0, +y, 0) 
Side 6 = (-x, -y, +y, -x) 

想出了可惜,這並不總是正確的,並覈對它有時會返回無效雙方偶爾。

是否有一種可靠的方法來確定SCNBox的幾何方向屬性的正面?

編輯: 基於Toyos的答案,我想出了下面的代碼,這是行不通的。希望這將有助於接近最終目標。我還使用在Extracting vertices from scenekit找到的代碼來獲取我的SCNBoxNode的頂點。

- (NSNumber *)valueForRotation:(SCNVector4)rotation andGeometry:(SCNGeometry*)geometry { 
    SCNVector4 inverse = SCNVector4Make(rotation.x, rotation.y, rotation.z, -rotation.w); 

    CATransform3D transform = CATransform3DMakeRotation(inverse.w, inverse.x, inverse.y, inverse.z); 

    GLKMatrix4 matrix = GLKMatrix4Make(transform.m11, transform.m12, transform.m13, transform.m14, transform.m21, transform.m22, transform.m23, transform.m24, transform.m31, transform.m32, transform.m33, transform.m34, transform.m41, transform.m42, transform.m43, transform.m44); 

    GLKVector4 vector = GLKVector4Make(rotation.x, rotation.y, rotation.z, rotation.w); 

    GLKVector4 finalVector = GLKMatrix4MultiplyVector4(matrix, vector); 

    NSArray *vertexSources = [geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex]; 

    SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources 

    NSInteger stride = vertexSource.dataStride; // in bytes 
    NSInteger offset = vertexSource.dataOffset; // in bytes 

    NSInteger componentsPerVector = vertexSource.componentsPerVector; 
    NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent; 
    NSInteger vectorCount = vertexSource.vectorCount; 

    SCNVector3 vertices[vectorCount]; // A new array for vertices 

    // for each vector, read the bytes 
    NSLog(@"vetor count %i",vectorCount); 
    float highestProduct = 0; 
    int highestVector = -1; 
    NSMutableArray *highVectors; 
    for (NSInteger i=0; i<vectorCount; i++) { 

     // Assuming that bytes per component is 4 (a float) 
     // If it was 8 then it would be a double (aka CGFloat) 
     float vectorData[componentsPerVector]; 

     // The range of bytes for this vector 
     NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset 
             bytesPerVector); // and read the lenght of one vector 

     // Read into the vector data buffer 
     [vertexSource.data getBytes:&vectorData range:byteRange]; 

     // At this point you can read the data from the float array 
     float x = vectorData[0]; 
     float y = vectorData[1]; 
     float z = vectorData[2]; 

     // ... Maybe even save it as an SCNVector3 for later use ... 
     vertices[i] = SCNVector3Make(x, y, z); 

     // ... or just log it 
     NSLog(@"x:%f, y:%f, z:%f", x, y, z); 
     float product = (x * finalVector.x) + (y * finalVector.y) + (z * finalVector.z); 
     if (product > highestProduct) { 
      highestProduct = product; 
      highestVector = i; 
     } 

    } 

    NSLog(@"highestProduct = %f",highestProduct); 
    NSLog(@"highestVector = %i",highestVector); 
    NSLog(@"top verticy = %f, %f, %f",vertices[highestVector].x,vertices[highestVector].y,vertices[highestVector].z); 

    return [NSNumber numberWithInt:highestVector]; 
} 

回答

6

下面是返回真實朝上的臉的指標的方法。它假設「boxNode」是一個由6個面組成的框,具有以下(任意)順序:前/右/後/左/上/下。它返回面朝上的面的索引。 不要忘記導入。 對於任意網格,您必須使用面法線而不是「boxNormals」(由於SceneKit網格每個頂點只有一個法線,而不是每個面的一個法線,因此計算並不明顯,因此您必須計算法線面對自己)。

- (NSUInteger) boxUpIndex:(SCNNode *)boxNode 
{ 
    SCNVector4 rotation = boxNode.rotation; 
    SCNVector4 invRotation = rotation; invRotation.w = -invRotation.w; 

    SCNVector3 up = SCNVector3Make(0,1,0); 

    //rotate up by invRotation 
    SCNMatrix4 transform = SCNMatrix4MakeRotation(invRotation.w, invRotation.x, invRotation.y, invRotation.z); 
    GLKMatrix4 glkTransform = SCNMatrix4ToGLKMatrix4(transform); 
    GLKVector3 glkUp = SCNVector3ToGLKVector3(up); 
    GLKVector3 rotatedUp = GLKMatrix4MultiplyVector3(glkTransform, glkUp); 

    //build box normals (arbitrary order here) 
    GLKVector3 boxNormals[6] = {{{0,0,1}}, 
     {{1,0,0}}, 
     {{0,0,-1}}, 
     {{-1,0,0}}, 
     {{0,1,0}}, 
     {{0,-1,0}}, 
    }; 

    int bestIndex = 0; 
    float maxDot = -1; 

    for(int i=0; i<6; i++){ 
     float dot = GLKVector3DotProduct(boxNormals[i], rotatedUp); 
     if(dot > maxDot){ 
      maxDot = dot; 
      bestIndex = i; 
     } 
    } 

    return bestIndex; 
} 
+0

這不起作用,每個邊都返回4。這是我如何創建節點,也許我在這裏做錯了? 'SCNNode * block = [SCNNode node]; block.position = SCNVector3Make(20,0,0); 區塊。rotation = SCNVector4Make(0,0,0,0); block.geometry = [SCNBox boxWithWidth:8 height:8 length:8 chamferRadius:0]; SCNPhysicsBody * body = [SCNPhysicsBody dynamicBody]; body.mass = 5; body.restitution = .7; body.friction = 0.5; block.physicsBody = body; [[scene rootNode] addChildNode:block];' – Jeremy1026

+0

編輯:想出來,我需要改變'boxNode.rotation'爲'boxNode.presentationNode.node'.Thank you so much!我甚至沒有接近我的代碼:( – Jeremy1026

2

node.orientation會給你一個四元數。如果這使得您的數學更容易,您也可以使用node.eulerAngles或node.rotation(軸角度)。

在所有情況下,我認爲您需要對向量[0,1,0](向上)應用反向旋轉。結果向量(V)會給你上面的方向。 然後找到正面,比較方塊面和V的點積。最高點產品是面朝上的面。

(一種替代方法是,代替旋轉框的面和執行點積與[0,1,0])

+0

你能否提供一些關於我如何「應用反向旋轉矢量」? – Jeremy1026

+0

最簡單的就是使用軸角度(node.rotation)。 [x,y,z,angle]的反向旋轉是[x,y,z,-angle]。然後將這個旋轉應用到一個矢量上首先建立旋轉矩陣(通過使用GLKit,SIMD或CATransform3DMakeRotation(angle,x,y,z))。一旦你有了矩陣,用這個矩陣轉換矢量, (GLKit或SIMD在這裏最好,因爲CA沒有內置函數)請注意,SceneKit提供了將SCNVector3轉換爲GLKit,CA或SIMD的實用函數) – Toyos

+0

我將完全誠實,因爲我仍然完全丟失。但是,你清楚地知道你在說什麼,所以+1爲你的答案。感謝您花時間爲我進一步解釋它。 – Jeremy1026