SceneKit繪制模型與骨骼動畫的實現(xiàn)

研究目的
sceneKit里可以繪制幾種幾何模型,但那些不規(guī)律的形狀如果不想使用模型,那么就要自己繪制了
demo效果
1.gif
Untitled.gif
原理和步驟
1.定義 模型的  頂點坐標  紋理坐標  法線  骨骼頂點  骨骼動畫  等數(shù)據(jù)

2.調(diào)用以下方法畫出模型 

根據(jù)頂點坐標和模型類型畫出模型,模型類型可以是點、線、三角形
為什么是三角形呢?因為三角形是最小邊幾何圖形

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

3.調(diào)用以下方法設(shè)置紋理
+ (instancetype)geometrySourceWithNormals:(const SCNVector3 *)normals count:(NSInteger)count;
用以下方法設(shè)置模型圖片 就是設(shè)置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.調(diào)用以下方法畫出法線
+ (instancetype)geometrySourceWithTextureCoordinates:(const CGPoint *)texcoord count:(NSInteger)count;

5.調(diào)用以下方法畫出骨骼頂點
+ (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.調(diào)用以下方法設(shè)置與骨骼頂點向連接的部分
+ (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.調(diào)用以下方法設(shè)置骨骼動畫皮膚那方面的
+ (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.調(diào)用以下方法設(shè)置把骨骼相信存放到一個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.調(diào)用以下方法設(shè)置骨骼動畫的值和類型
+ (SCNAction *)repeatActionForever:(SCNAction *)action;

10.調(diào)用以下方法運行骨骼動畫
- (void)runAction:(SCNAction *)action API_AVAILABLE(macos(10.10));

關(guān)鍵性代碼--模型本體和紋理
- (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];
    
}

關(guān)鍵性代碼--骨骼動畫
-(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;
}

代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,635評論 25 708
  • 111. [動畫系統(tǒng)]如何將其他類型的動畫轉(zhuǎn)換成關(guān)鍵幀動畫? 動畫->點緩存->關(guān)鍵幀 112. [動畫]Unit...
    胤醚貔貅閱讀 13,108評論 3 90
  • 感覺自己被太多條條框框所局限 可能我需要認識到?jīng)]有什么真正有趣的我喜歡的工作 我認為我討厭快消 但這只是我人生的一...
    角落蜷縮閱讀 263評論 0 0
  • Share沒有任何一個女生樂意跟一個新的男生從頭開始,從完全陌生到開始熟悉,再經(jīng)歷一次從尷尬到坦然的過程,跟他分享...
    阿騫啊閱讀 186評論 0 0
  • 為什么大學(xué)生總懷念過去的高中? 幾天前,我在朋友圈看到一位大一妹紙發(fā)了這么一條說說: 大學(xué)上到現(xiàn)在終于明白,為什么...
    Tockey閱讀 3,136評論 1 13