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

研究目的
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;
}

代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,799评论 25 707
  • 111. [动画系统]如何将其他类型的动画转换成关键帧动画? 动画->点缓存->关键帧 112. [动画]Unit...
    胤醚貔貅阅读 12,982评论 3 90
  • 感觉自己被太多条条框框所局限 可能我需要认识到没有什么真正有趣的我喜欢的工作 我认为我讨厌快消 但这只是我人生的一...
    角落蜷缩阅读 258评论 0 0
  • Share没有任何一个女生乐意跟一个新的男生从头开始,从完全陌生到开始熟悉,再经历一次从尴尬到坦然的过程,跟他分享...
    阿骞啊阅读 182评论 0 0
  • 为什么大学生总怀念过去的高中? 几天前,我在朋友圈看到一位大一妹纸发了这么一条说说: 大学上到现在终于明白,为什么...
    Tockey阅读 3,113评论 1 13