2015-06-07 4 views
1

Я создал полностью настраиваемый SCNNode с программным обеспечением. Этот узел дополнен пользовательской геометрией, скелетной установкой как иерархией дочерних узлов и объектом SCNSkinner для объединения скелета и геометрии. Проблема в том, что: Как только я клонирую свой пользовательский узел, он теряет привязку между геометрией и скелетом.Пользовательский символ SceneKit не будет клонировать правильно

Чтобы добраться до корня этой проблемы, я поменял свой довольно сложный узел символов с помощью самой простой комбинации геометрий-буровой установки, которую я мог бы подумать: блок геометрии из 12 вершин и скелетная установка из 3 суставов.

Я бы любил делиться изображение в этой точке, но не может этого сделать, так как не хватает репутации, но ...

-(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 как свежие узлы скиннера. К сожалению, безрезультатно. Результат ведет себя точно так же, как непосредственно клонированный узел.

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

Ответ представлен в документации Apple SCNNode. Попробуйте использовать метод flattenedClone() вместо clone(). И еще одно, это не нужно для утверждения. Вот маленький пример Swift:

nodeB = nodeA.flattenedClone() 

Теперь NodeB содержит все дочерние узлы узла А

+0

Я боюсь, '[узел flattenedClone]' не делает трюк. Я уже пробовал это раньше. Он только сбой приложения с EXC_BAD_ACCESS где-то в C3DMeshCopy. Но даже если это сработает, это не решит мою проблему. Я хочу скопировать/клонировать всю структуру узла, чтобы иметь возможность манипулировать скелетом и тем самым его сеткой впоследствии. – dthgs

Смежные вопросы