自定义SceneKit字符无法正确克隆

时间:2015-06-07 13:01:15

标签: ios objective-c scenekit scnnode

我以编程方式创建了一个完全自定义的绑定SCNNode。此节点包含自定义几何,骨架装备作为子节点的层次结构,SCNSkinner对象将骨架和几何结合在一起。问题是这样的:一旦克隆我的自定义节点,它就会失去几何和骨架之间的绑定。

为了找到这个问题的根源,我将我相当复杂的角色节点与我能想到的最简单的几何装备组合交换:一个由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)。 我创建了一个新节点,并为其添加了原始几何体的副本。然后我克隆了骨架根节点,并将其添加到新节点。最后我将原始皮肤设置为新鲜节点皮肤。不幸的是无济于事。结果与直接克隆的节点完全相同。

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;

1 个答案:

答案 0 :(得分:0)

答案在Apple SCNNode文档中。 尝试使用// The code Expression<Func<TEntity, bool>> filter means //the caller will provide a lambda expression based on the TEntity type, //and this expression will return a Boolean value. public virtual IEnumerable<TEntity> Get( Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") { IQueryable<TEntity> query = DbSet; if (filter != null) { query = query.AsExpandable().Where(filter); } // applies the eager-loading expressions after parsing the comma-delimited list foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { return orderBy(query).ToList(); } else { return query.ToList(); } } 方法而不是flattenedClone()。还有一件事,它不需要任何陈述。这是Swift中的一个小例子:

clone()

现在,nodeB包含nodeA的所有子节点