ARKitSceneKit学习ARKIT+SCENEKIT

SceneKit绘制模型与骨骼动画的实现

2018-03-01  本文已影响251人  小沛2016
研究目的
sceneKit里可以绘制几种几何模型,但那些不规律的形状如果不想使用模型,那么就要自己绘制了
demo效果
1.gif Untitled.gif
原理和步骤
1.定义 模型的  顶点坐标  纹理坐标  法线  骨骼顶点  骨骼动画  等数据

2.调用以下方法画出模型 

根据顶点坐标和模型类型画出模型,模型类型可以是点、线、三角形
为什么是三角形呢?因为三角形是最小边几何图形

+ (instancetype)geometrySourceWithVertices:(const SCNVector3 *)vertices count:(NSInteger)count;

3.调用以下方法设置纹理
+ (instancetype)geometrySourceWithNormals:(const SCNVector3 *)normals count:(NSInteger)count;
用以下方法设置模型图片 就是设置node的纹理
SCNGeometry *geo = [SCNGeometry geometryWithSources:sources elements:elements];
UIImage * image  = [UIImage imageNamed:@"xy.jpg"];
SCNMaterial *mat = [SCNMaterial material];
mat.diffuse.contents = image;
geo.firstMaterial = mat;
geo.firstMaterial.doubleSided = YES;

4.调用以下方法画出法线
+ (instancetype)geometrySourceWithTextureCoordinates:(const CGPoint *)texcoord count:(NSInteger)count;

5.调用以下方法画出骨骼顶点
+ (instancetype)geometrySourceWithData:(NSData *)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride;

6.调用以下方法设置与骨骼顶点向连接的部分
+ (instancetype)geometrySourceWithData:(NSData *)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride;

7.调用以下方法设置骨骼动画皮肤那方面的
+ (instancetype)geometrySourceWithData:(NSData *)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride

8.调用以下方法设置把骨骼相信存放到一个SCNSkinner类
+ (instancetype)skinnerWithBaseGeometry:(nullable SCNGeometry *)baseGeometry bones:(NSArray<SCNNode *> *)bones boneInverseBindTransforms:(nullable NSArray<NSValue *> *)boneInverseBindTransforms boneWeights:(SCNGeometrySource *)boneWeights boneIndices:(SCNGeometrySource *)boneIndices API_AVAILABLE(macos(10.10));

9.调用以下方法设置骨骼动画的值和类型
+ (SCNAction *)repeatActionForever:(SCNAction *)action;

10.调用以下方法运行骨骼动画
- (void)runAction:(SCNAction *)action API_AVAILABLE(macos(10.10));

关键性代码--模型本体和纹理
- (void)addNode1 {
    
    typedef struct {
        float x, y, z;    // position
        float nx, ny, nz; // normal
        float s, t;       // texture coordinates
    } MyVertex;
    
    MyVertex vertices[] = {
        // Z轴0.5处的平面
        -0.5,   0.5,  0.5,   0,  0,  1,  0, 0,
        -0.5,  -0.5,  0.5,  0,  0,  1,   0, 1,
        0.5,   -0.5,  0.5,   0,  0,  1,  1, 1,
        0.5,   -0.5,  0.5,   0,  0,  1,  1, 1,
        0.5,    0.5,  0.5,    0,  0,  1, 1, 0,
        -0.5,   0.5,  0.5,  0,  0,  1,   0, 0,
        
        // X轴-0.5处的平面
        -0.5,  0.5,   -0.5, -1,  0,  0, 0, 0,
        -0.5,  -0.5,  -0.5, -1,  0,  0, 0, 1,
        -0.5,  -0.5,    0.5, -1,  0,  0, 1, 1,
        -0.5,  -0.5,   0.5, -1,  0,  0, 1, 1,
        -0.5,  0.5,    0.5, -1,  0,  0, 1, 0,
        -0.5,  0.5,    -0.5, -1,  0,  0, 0, 0,
        
        // Z轴-0.5处的平面
        0.5,   -0.5,  -0.5,  0,  0,  -1, 0, 1,
        -0.5,  -0.5,  -0.5,  0,  0,  -1, 1, 1,
        -0.5,   0.5,  -0.5,   0,  0,  -1, 1, 0,
        -0.5,   0.5,  -0.5,  0,  0,  -1, 1, 0,
        0.5,    0.5,  -0.5,    0,  0,  -1, 0, 0,
        0.5,   -0.5,  -0.5,   0,  0,  -1, 0, 1,
        
        // X轴0.5处的平面
        0.5,  -0.5,    0.5, 1,  0,  0, 0, 1,
        0.5,  -0.5,  -0.5, 1,  0,  0, 1, 1,
        0.5,  0.5,   -0.5, 1,  0,  0, 1, 0,
        0.5,  0.5,    -0.5, 1,  0,  0, 1, 0,
        0.5,  0.5,    0.5, 1,  0,  0, 0, 0,
        0.5,  -0.5,   0.5, 1,  0,  0, 0, 1,
        
        // Y轴0.5处的平面
        0.5, 0.5,  -0.5, 0,  1,  0, 1, 0,
        -0.5, 0.5, -0.5, 0,  1,  0, 0, 0,
        -0.5,  0.5,  0.5, 0,  1,  0, 0, 1,
        -0.5, 0.5,  0.5, 0,  1,  0, 0, 1,
        0.5, 0.5,   0.5, 0,  1,  0, 1, 1,
        0.5,  0.5,  -0.5, 0,  1,  0, 1, 0,
        
        // Y轴-0.5处的平面
        -0.5, -0.5,   0.5, 0,  -1,  0, 0, 0,
        -0.5, -0.5, -0.5, 0,  -1,  0, 0, 1,
        0.5, -0.5,  -0.5, 0,  -1,  0, 1, 1,
        0.5, -0.5,  -0.5, 0,  -1,  0, 1, 1,
        0.5, -0.5,   0.5, 0,  -1,  0, 1, 0,
        -0.5, -0.5,  0.5, 0,  -1,  0, 0, 0,
    };
   
    
    NSData *data = [NSData dataWithBytes:vertices length:sizeof(vertices)];
    
    SCNGeometrySource *vertexSource, *normalSource, *tcoordSource;
    
    vertexSource = [SCNGeometrySource geometrySourceWithData:data
                                                    semantic:SCNGeometrySourceSemanticVertex
                                                 vectorCount:6
                                             floatComponents:YES
                                         componentsPerVector:3 // x, y, z
                                           bytesPerComponent:sizeof(float)
                                                  dataOffset:offsetof(MyVertex, x)
                                                  dataStride:sizeof(MyVertex)];
    
    normalSource = [SCNGeometrySource geometrySourceWithData:data
                                                    semantic:SCNGeometrySourceSemanticNormal
                                                 vectorCount:6
                                             floatComponents:YES
                                         componentsPerVector:3 // nx, ny, nz
                                           bytesPerComponent:sizeof(float)
                                                  dataOffset:offsetof(MyVertex, nx)
                                                  dataStride:sizeof(MyVertex)];
    
    tcoordSource = [SCNGeometrySource geometrySourceWithData:data
                                                    semantic:SCNGeometrySourceSemanticTexcoord
                                                 vectorCount:6
                                             floatComponents:YES
                                         componentsPerVector:2 // s, t
                                           bytesPerComponent:sizeof(float)
                                                  dataOffset:offsetof(MyVertex, s)
                                                  dataStride:sizeof(MyVertex)];
    
    
    int indices[] = {
        0,1,2,3,4,5,
        6,7,8,9,10,11,
        12,13,14,15,16,17,
        18,19,20,21,22,23,
        24,25,26,27,28,29,
        30,31,32,33,34,35
    };
    
    
    NSMutableArray * elements = [[NSMutableArray alloc]init];
    
    for (int i = 0; i<36; i+=6) {
        
        int indiceChild[] = {indices[i],indices[i+1],indices[i+2], indices[i+3],indices[i+4],indices[i+5]};
        
        NSData * indexData = [NSData dataWithBytes:indiceChild length:sizeof(indiceChild)];
        
        SCNGeometryElement * element = [SCNGeometryElement geometryElementWithData:indexData
                                                                     primitiveType:SCNGeometryPrimitiveTypeTriangles
                                                                    primitiveCount:2
                                                                     bytesPerIndex:sizeof(int)];
        [elements addObject:element];
        
    }
    
    
    
    SCNGeometry * geometry = [SCNGeometry geometryWithSources:@[vertexSource,normalSource,tcoordSource]
                                                     elements:elements];
    
    
    UIImage * image  = [UIImage imageNamed:@"xy.jpg"];
    SCNMaterial * material = [[SCNMaterial alloc]init];
    material.diffuse.contents = image;
    
    geometry.materials = @[material];
    
    SCNNode * node = [SCNNode nodeWithGeometry:geometry];
    
    node.position = SCNVector3Make(0, 0, -1);
    
    [self.scnView.scene.rootNode addChildNode:node];
    
}

关键性代码--骨骼动画
-(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 redColor];
    baseGeometry.firstMaterial.doubleSided  = YES;
    baseGeometry.firstMaterial.transparency = 0.6;
    
    SCNNode * mNode = [SCNNode nodeWithGeometry:baseGeometry];
    
    mNode.position = SCNVector3Make(15, 0, 9);
    
    int vectorCount = (int)[(SCNGeometrySource *)[mNode.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(0, 0.75, 0);
        
        //add a sphere to each bone, to visually check its position etc.
        SCNSphere *boneSphereGeom = [SCNSphere sphereWithRadius:0.1];
        boneSphereGeom.firstMaterial.emission.contents = [UIColor redColor];
        boneNode.geometry = boneSphereGeom;
        
        [bonesArray addObject:boneNode];
        
    }
    
    [mNode addChildNode:bonesArray[0]];
    
    
    //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.5)+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:YES
                                                                          componentsPerVector:3
                                                                            bytesPerComponent:sizeof(short)
                                                                                   dataOffset:offsetof(IndexVectors, k)
                                                                                  dataStride:sizeof(IndexVectors)];
    
    SCNSkinner * mNodeSkinner = [SCNSkinner skinnerWithBaseGeometry:baseGeometry
                                                                    bones:bonesArray
                                                boneInverseBindTransforms:bibtArray
                                                              boneWeights:boneWeightsGeometrySource
                                                              boneIndices:boneIndicesGeometrySource];
    
    mNode.skinner = mNodeSkinner;
    [[bonesArray objectAtIndex:1] runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:0 z:2 duration:2]]];
    
    return mNode;
}

代码

上一篇下一篇

猜你喜欢

热点阅读