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

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

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

根據(jù)頂點(diǎn)坐標(biāo)和模型類型畫出模型苔咪,模型類型可以是點(diǎn)、線廊谓、三角形
為什么是三角形呢只洒?因?yàn)槿切问亲钚∵厧缀螆D形

+ (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)用以下方法畫出骨骼頂點(diǎn)
+ (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è)置與骨骼頂點(diǎn)向連接的部分
+ (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è)置把骨骼相信存放到一個(gè)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)用以下方法運(yùn)行骨骼動畫
- (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)系作者
  • 序言:七十年代末镊靴,一起剝皮案震驚了整個(gè)濱河市没龙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唱遭,老刑警劉巖戳寸,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拷泽,居然都是意外死亡疫鹊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門司致,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拆吆,“玉大人,你說我怎么就攤上這事脂矫≡嬉” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵庭再,是天一觀的道長捞奕。 經(jīng)常有香客問我,道長拄轻,這世上最難降的妖魔是什么颅围? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮恨搓,結(jié)果婚禮上院促,老公的妹妹穿的比我還像新娘。我一直安慰自己斧抱,他們只是感情好常拓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辉浦,像睡著了一般弄抬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宪郊,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天眉睹,我揣著相機(jī)與錄音,去河邊找鬼废膘。 笑死竹海,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丐黄。 我是一名探鬼主播斋配,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了艰争?” 一聲冷哼從身側(cè)響起坏瞄,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甩卓,沒想到半個(gè)月后鸠匀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逾柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年缀棍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片机错。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爬范,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弱匪,到底是詐尸還是另有隱情青瀑,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布萧诫,位于F島的核電站斥难,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏帘饶。R本人自食惡果不足惜哑诊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尖奔。 院中可真熱鬧,春花似錦穷当、人聲如沸提茁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茴扁。三九已至,卻和暖如春汪疮,著一層夾襖步出監(jiān)牢的瞬間峭火,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工智嚷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卖丸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓盏道,卻偏偏與公主長得像稍浆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

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

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