研究目的
sceneKit里可以繪制幾種幾何模型,但那些不規(guī)律的形狀如果不想使用模型炕舵,那么就要自己繪制了
demo效果
原理和步驟
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;
}
代碼