2015-06-07 21 views
1

我以編程方式創建了一個完全自定義的操作SCNNode。該節點具有自定義幾何體,骨架裝置作爲子節點的層次結構以及將骨架和幾何體綁定在一起的SCNSkinner對象。問題是這樣的:只要我克隆我的自定義節點,它就會失去幾何和骨架之間的綁定。爲了找到這個問題的根源,我用我能想到的最簡單的幾何 - 鑽機組合交換了我相當複雜的字符節點:一個12個頂點的幾何塊和一個3個關節的骨架鑽機。自定義SceneKit字符不會正確克隆

我曾經愛過在這一點上分享圖片,但不能這樣做,因爲我沒有足夠的聲譽,但...

-(SCNNode *)createCustomRigBlock { 

    // baseGeometry 
    SCNVector3 positions[] = { 
     SCNVector3Make(0, 0, 0), 
     SCNVector3Make(0, 0, 1), 
     SCNVector3Make(1, 0, 1), 
     SCNVector3Make(1, 0, 0), 
     SCNVector3Make(0, 1, 0), 
     SCNVector3Make(0, 1, 1), 
     SCNVector3Make(1, 1, 1), 
     SCNVector3Make(1, 1, 0), 
     SCNVector3Make(0, 2, 0), 
     SCNVector3Make(0, 2, 1), 
     SCNVector3Make(1, 2, 1), 
     SCNVector3Make(1, 2, 0) 
    }; 
    SCNGeometrySource * baseGeometrySource = [SCNGeometrySource geometrySourceWithVertices:positions count:12]; 

    typedef struct { 
     uint16_t a, b, c; 
    } Triangles; 

    Triangles tVectors[20] = { 
     0,1,2, 
     0,2,3, 
     0,1,5, 
     0,4,5, 
     4,5,9, 
     4,8,9, 
     1,2,6, 
     1,5,6, 
     5,6,10, 
     5,9,10, 
     2,3,7, 
     2,6,7, 
     6,7,11, 
     6,10,11, 
     3,0,4, 
     3,4,7, 
     7,4,8, 
     7,8,11, 
     8,9,10, 
     8,10,11 
    }; 

    NSData *triangleData = [NSData dataWithBytes:tVectors length:sizeof(tVectors)]; 
    SCNGeometryElement * baseGeometryElement = [SCNGeometryElement geometryElementWithData:triangleData primitiveType:SCNGeometryPrimitiveTypeTriangles primitiveCount:20 bytesPerIndex:sizeof(uint16_t)]; 
    SCNGeometry * baseGeometry = [SCNGeometry geometryWithSources:[NSArray arrayWithObject:baseGeometrySource] elements:[NSArray arrayWithObject:baseGeometryElement]]; 

    baseGeometry.firstMaterial.emission.contents = [UIColor greenColor]; 
    baseGeometry.firstMaterial.doubleSided = YES; 
    baseGeometry.firstMaterial.transparency = 0.5; 
    SCNNode *customBlock = [SCNNode nodeWithGeometry:baseGeometry]; 

    int stageSize = 30; 
    customBlock.position = SCNVector3Make(stageSize/2, 0, stageSize/2-6); 

    int vectorCount = (int)[(SCNGeometrySource *)[customBlock.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex].firstObject vectorCount]; 

    //bones ... the bones of the rig 
    NSMutableArray * bonesArray = [NSMutableArray new]; 
    for (int i = 0; i < 3; i++) { 
     SCNNode * boneNode = [SCNNode new]; 
     boneNode.name = [NSString stringWithFormat:@"bone_%i",i]; 
     if (bonesArray.count > 0) { 
      [bonesArray.lastObject addChildNode:boneNode]; 
     } 
     boneNode.position = SCNVector3Make((i>0 ? 0 : 0.5), (i>0 ? 0.75 : 0.25), (i>0 ? 0 : 0.5)); 

     //add a sphere to each bone, to visually check its position etc. 
     SCNSphere *boneSphereGeom = [SCNSphere sphereWithRadius:0.1]; 
     boneSphereGeom.firstMaterial.emission.contents = [UIColor redColor]; 
     SCNNode * boneSphere = [SCNNode nodeWithGeometry:boneSphereGeom]; 
     [boneNode addChildNode:boneSphere]; 

     [bonesArray addObject:boneNode]; 
    } 

    [customBlock addChildNode:bonesArray.firstObject]; 

    //boneInverseBindTransforms ... this defines the geometries transformation in the default pose! 
    NSMutableArray * bibtArray = [NSMutableArray new]; 
    for (int i = 0; i < 3; i++) { 
     SCNMatrix4 initialPositionMatrix = SCNMatrix4MakeTranslation(0.5, (i*0.75)+0.25, 0.5); 
     SCNMatrix4 inverseFinalMatrix = SCNMatrix4Invert(initialPositionMatrix); 
     NSValue * bibtValue = [NSValue valueWithSCNMatrix4:inverseFinalMatrix]; 
     [bibtArray addObject:bibtValue]; 
    } 

    //boneWeights ... the weights, at which each vertex is influenced by certain bones (which bones is defined by "boneIndices") 
    typedef struct { 
     float a, b, c; 
    } WeightVectors; 

    WeightVectors vectors[vectorCount]; 
    for (int i = 0; i < vectorCount; i++) { 
     // set the same boneWeights for every vertex 
     vectors[i].a = 1; 
     vectors[i].b = 0; 
     vectors[i].c = 0; 
    } 

    NSData *weightData = [NSData dataWithBytes:vectors length:sizeof(vectors)]; 
    SCNGeometrySource * boneWeightsGeometrySource = [SCNGeometrySource geometrySourceWithData:weightData 
                        semantic:SCNGeometrySourceSemanticBoneWeights 
                        vectorCount:vectorCount 
                       floatComponents:YES 
                      componentsPerVector:3 
                      bytesPerComponent:sizeof(float) 
                        dataOffset:offsetof(WeightVectors, a) 
                        dataStride:sizeof(WeightVectors)]; 

    //boneIndices 
    typedef struct { 
     short k, l, m; // boneWeight 
    } IndexVectors; 

    IndexVectors iVectors[vectorCount]; 
    for (int i = 0; i < vectorCount; i++) { 
     if (i > 7) { 
      iVectors[i].k = 1; 
      iVectors[i].l = 0; 
      iVectors[i].m = 0; 
     } else { 
      iVectors[i].k = 0; 
      iVectors[i].l = 0; 
      iVectors[i].m = 0; 
     } 
    } 

    NSData *indexData = [NSData dataWithBytes:iVectors length:sizeof(iVectors)]; 
    SCNGeometrySource * boneIndicesGeometrySource = [SCNGeometrySource geometrySourceWithData:indexData 
                        semantic:SCNGeometrySourceSemanticBoneIndices 
                        vectorCount:vectorCount 
                       floatComponents:NO 
                      componentsPerVector:3 
                      bytesPerComponent:sizeof(short) 
                        dataOffset:offsetof(IndexVectors, k) 
                        dataStride:sizeof(IndexVectors)]; 

    SCNSkinner * customBlockSkinner = [SCNSkinner skinnerWithBaseGeometry:baseGeometry 
                    bones:bonesArray 
               boneInverseBindTransforms:bibtArray 
                   boneWeights:boneWeightsGeometrySource 
                   boneIndices:boneIndicesGeometrySource]; 

    customBlock.skinner = customBlockSkinner; 
    [[bonesArray objectAtIndex:1] runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:0 z:2 duration:2]]]; 

    return customBlock; 
} 

這種習俗SCNNode完美的作品,如只要我直接將其添加到我的場景中即可。現在,在我當前的應用程序中,我需要多次將此節點添加到場景中。這就是爲什麼我需要克隆節點並將生成的克隆添加到場景中。但克隆自定義節點的結果是非常錯誤的。正如我上面提到的,幾何和骨架之間的綁定似乎已經丟失。克隆節點中的骨架仍然可以放置在我的場景中,但幾何體不再遵循骨架位置或變形。

我已經試過:

1) 我創建了一個新的節點,並添加原始幾何的副本吧。然後我克隆了骨架根節點,並將其添加到新節點。最後,我將原始skinner設置爲新鮮節點skinner。不幸的是,無濟於事。結果的行爲與直接克隆的節點完全相同。

SCNNode * newNode = [SCNNode node]; 
newNode.geometry = [node.geometry copy]; 
for (SCNNode * childNode in node.childNodes) { 
    [newNode addChildNode:[childNode clone]]; 
} 
newNode.skinner = node.skinner; 

2.) 在試圖重現的幾何形狀和我創建的SCNSkinner的深層副本,並使其新鮮節點的斯金納骨架之間的結合。但是這隻會導致C3DSourceAccessorGetMutableValuePtrAtIndex崩潰,我無法修復。

NSArray * newBones = [newNode childNodesPassingTest:^(SCNNode *child, BOOL *stop) 
         { 
          if (![child.name isEqualToString:@"manipulator"]) { 
           return YES; 
          } 
          return NO; 
         }]; 
NSArray * newBoneInverseBindTransforms = [node.skinner.boneInverseBindTransforms copy]; 
SCNGeometrySource * newBoneWeights = [SCNGeometrySource geometrySourceWithData:[node.skinner.boneWeights.data copy] 
                     semantic:SCNGeometrySourceSemanticBoneWeights 
                    vectorCount:node.skinner.boneWeights.vectorCount 
                   floatComponents:node.skinner.boneWeights.floatComponents 
                  componentsPerVector:node.skinner.boneWeights.componentsPerVector 
                  bytesPerComponent:node.skinner.boneWeights.bytesPerComponent 
                    dataOffset:node.skinner.boneWeights.dataOffset 
                    dataStride:node.skinner.boneWeights.dataStride]; 

SCNGeometrySource * newBoneIndices = [SCNGeometrySource geometrySourceWithData:[node.skinner.boneIndices.data copy] 
                     semantic:SCNGeometrySourceSemanticBoneIndices 
                    vectorCount:node.skinner.boneIndices.vectorCount 
                   floatComponents:node.skinner.boneIndices.floatComponents 
                  componentsPerVector:node.skinner.boneIndices.componentsPerVector 
                  bytesPerComponent:node.skinner.boneIndices.bytesPerComponent 
                    dataOffset:node.skinner.boneIndices.dataOffset 
                    dataStride:node.skinner.boneIndices.dataStride]; 

SCNSkinner * newSkinner = [SCNSkinner skinnerWithBaseGeometry:newNode.geometry 
               bones:newBones 
          boneInverseBindTransforms:newBoneInverseBindTransforms 
              boneWeights:newBoneWeights 
              boneIndices:newBoneIndices]; 
newNode.skinner = newSkinner; 

回答

0

答案是在蘋果的SCNNode文檔中。 嘗試使用flattenedClone()方法而不是clone()。還有一件事,它不需要任何陳述。這裏是斯威夫特一個小例子:

nodeB = nodeA.flattenedClone() 

現在,節點B包含nodeA上

+0

的所有子節點恐怕'[節點flattenedClone]'不會做的伎倆。我以前已經嘗試過。它只會在C3DMeshCopy中的某處使用EXC_BAD_ACCESS來崩潰應用程序。但即使它能工作,這也不能解決我的問題。我想複製/克隆完整的節點結構,以便能夠操作骨架,從而對其網格進行操作。 – dthgs