Saya memiliki aplikasi Swift menggunakan SceneKit untuk iOS 8. Saya memuat adegan dari file .dae yang berisi mesh yang dikendalikan oleh kerangka. Saat runtime, saya perlu mengubah koordinat tekstur. Menggunakan transformasi bukanlah pilihan - saya perlu menghitung UV yang berbeda dan benar-benar baru untuk setiap simpul di mesh.
Saya tahu geometri tidak dapat diubah di SceneKit, dan saya telah membaca bahwa pendekatan yang disarankan adalah membuat salinan secara manual. Saya mencoba melakukan itu, tetapi saya selalu mengalami error saat mencoba membuat ulang SCNSkinner
kode in. Kecelakaan itu ada EXC_BAD_ACCESS
di dalam C3DSourceAccessorGetMutableValuePtrAtIndex
. Sayangnya, tidak ada kode sumber untuk ini, jadi saya tidak yakin mengapa sebenarnya itu macet. Saya telah mempersempitnya ke SCNSkinner
objek yang menempel pada node mesh. Jika saya tidak menyetelnya, saya tidak mengalami crash dan segala sesuatunya tampak berfungsi.
EDIT: Berikut adalah tumpukan panggilan crash yang lebih lengkap:
C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...
Saya belum menemukan dokumentasi atau kode contoh tentang cara membuat SCNSkinner
objek secara manual. Karena saya hanya membuatnya berdasarkan mesh yang berfungsi sebelumnya, seharusnya tidak terlalu sulit. Saya membuat SCNSkinner
sesuai dengan dokumentasi Swift, meneruskan semua hal yang benar ke init. Namun, ada properti kerangka di dalamnya SCNSkinner
yang saya tidak yakin cara menyetelnya. Saya mengaturnya ke kerangka yang ada di SCNSkinner
mesh asli yang saya salin, yang menurut saya harus berfungsi ... tetapi ternyata tidak. Saat menyetel properti kerangka, tampaknya properti tersebut tidak menetapkan. Memeriksa segera setelah penugasan menunjukkan bahwa masih nihil. Sebagai ujian, saya mencoba untuk mengatur properti kerangka mesh asli ke sesuatu yang lain, dan setelah penugasan itu dibiarkan tidak tersentuh juga.
Adakah yang bisa menjelaskan apa yang terjadi? Atau bagaimana cara membuat dan mengatur SCNSkinner
objek secara manual?
Berikut adalah kode yang saya gunakan untuk mengkloning mesh secara manual dan menggantinya dengan yang baru (Saya belum mengubah sumber data apa pun di sini - Saya hanya mencoba memastikan saya dapat membuat salinan pada saat ini) :
// This is at the start of the app, just so you can see how the scene is set up.
// I add the .dae contents into its own node in the scene. This seems to be the
// standard way to put multiple .dae models into the same scene. This doesn't seem to
// have any impact on the problem I'm having -- I've tried without this indirection
// and the same problem exists.
let scene = SCNScene()
let modelNode = SCNNode()
modelNode.name = "ModelNode"
scene.rootNode.addChildNode(modelNode)
let modelScene = SCNScene(named: "model.dae")
if modelScene != nil {
if let childNodes = modelScene?.rootNode.childNodes {
for childNode in childNodes {
modelNode.addChildNode(childNode as SCNNode)
}
}
}
// This happens later in the app after a tap from the user.
let modelNode = scnView.scene!.rootNode.childNodeWithName("ModelNode", recursively: true)
let modelMesh = modelNode?.childNodeWithName("MeshName", recursively: true)
let verts = modelMesh?.geometry!.geometrySourcesForSemantic(SCNGeometrySourceSemanticVertex)
let normals = modelMesh?.geometry!.geometrySourcesForSemantic(SCNGeometrySourceSemanticNormal)
let texcoords = modelMesh?.geometry!.geometrySourcesForSemantic(SCNGeometrySourceSemanticTexcoord)
let boneWeights = modelMesh?.geometry!.geometrySourcesForSemantic(SCNGeometrySourceSemanticBoneWeights)
let boneIndices = modelMesh?.geometry!.geometrySourcesForSemantic(SCNGeometrySourceSemanticBoneIndices)
let geometry = modelMesh?.geometry!.geometryElementAtIndex(0)
// Note: the vertex and normal data is shared.
let vertsData = NSData(data: verts![0].data)
let texcoordsData = NSData(data: texcoords![0].data)
let boneWeightsData = NSData(data: boneWeights![0].data)
let boneIndicesData = NSData(data: boneIndices![0].data)
let geometryData = NSData(data: geometry!.data!)
let newVerts = SCNGeometrySource(data: vertsData, semantic: SCNGeometrySourceSemanticVertex, vectorCount: verts![0].vectorCount, floatComponents: verts![0].floatComponents, componentsPerVector: verts![0].componentsPerVector, bytesPerComponent: verts![0].bytesPerComponent, dataOffset: verts![0].dataOffset, dataStride: verts![0].dataStride)
let newNormals = SCNGeometrySource(data: vertsData, semantic: SCNGeometrySourceSemanticNormal, vectorCount: normals![0].vectorCount, floatComponents: normals![0].floatComponents, componentsPerVector: normals![0].componentsPerVector, bytesPerComponent: normals![0].bytesPerComponent, dataOffset: normals![0].dataOffset, dataStride: normals![0].dataStride)
let newTexcoords = SCNGeometrySource(data: texcoordsData, semantic: SCNGeometrySourceSemanticTexcoord, vectorCount: texcoords![0].vectorCount, floatComponents: texcoords![0].floatComponents, componentsPerVector: texcoords![0].componentsPerVector, bytesPerComponent: texcoords![0].bytesPerComponent, dataOffset: texcoords![0].dataOffset, dataStride: texcoords![0].dataStride)
let newBoneWeights = SCNGeometrySource(data: boneWeightsData, semantic: SCNGeometrySourceSemanticBoneWeights, vectorCount: boneWeights![0].vectorCount, floatComponents: boneWeights![0].floatComponents, componentsPerVector: boneWeights![0].componentsPerVector, bytesPerComponent: boneWeights![0].bytesPerComponent, dataOffset: boneWeights![0].dataOffset, dataStride: boneWeights![0].dataStride)
let newBoneIndices = SCNGeometrySource(data: boneIndicesData, semantic: SCNGeometrySourceSemanticBoneIndices, vectorCount: boneIndices![0].vectorCount, floatComponents: boneIndices![0].floatComponents, componentsPerVector: boneIndices![0].componentsPerVector, bytesPerComponent: boneIndices![0].bytesPerComponent, dataOffset: boneIndices![0].dataOffset, dataStride: boneIndices![0].dataStride)
let newGeometry = SCNGeometryElement(data: geometryData, primitiveType: geometry!.primitiveType, primitiveCount: geometry!.primitiveCount, bytesPerIndex: geometry!.bytesPerIndex)
let newMeshGeometry = SCNGeometry(sources: [newVerts, newNormals, newTexcoords, newBoneWeights, newBoneIndices], elements: [newGeometry])
newMeshGeometry.firstMaterial = modelMesh?.geometry!.firstMaterial
let newModelMesh = SCNNode(geometry: newMeshGeometry)
let bones = modelMesh?.skinner?.bones
let boneInverseBindTransforms = modelMesh?.skinner?.boneInverseBindTransforms
let skeleton = modelMesh!.skinner!.skeleton!
let baseGeometryBindTransform = modelMesh!.skinner!.baseGeometryBindTransform
newModelMesh.skinner = SCNSkinner(baseGeometry: newMeshGeometry, bones: bones, boneInverseBindTransforms: boneInverseBindTransforms, boneWeights: newBoneWeights, boneIndices: newBoneIndices)
newModelMesh.skinner?.baseGeometryBindTransform = baseGeometryBindTransform
// Before this assignment, newModelMesh.skinner?.skeleton is nil.
newModelMesh.skinner?.skeleton = skeleton
// After, it is still nil... however, skeleton itself is completely valid.
modelMesh?.removeFromParentNode()
newModelMesh.name = "MeshName"
let meshParentNode = modelNode?.childNodeWithName("MeshParentNode", recursively: true)
meshParentNode?.addChildNode(newModelMesh)
Jawaban:
Ketiga metode ini dapat membantu Anda menemukan solusinya:
SCNNode *hero = [SCNScene sceneNamed:@"Hero"].rootNode; SCNNode *hat = [SCNScene sceneNamed:@"FancyFedora"].rootNode; hat.skinner.skeleton = hero.skinner.skeleton;
[Export ("initWithFrame:")] public UIView (System.Drawing.RectangleF frame) : base (NSObjectFlag.Empty) { // Invoke the init method now. var initWithFrame = new Selector ("initWithFrame:").Handle; if (IsDirectBinding) Handle = ObjCRuntime.Messaging.IntPtr_objc_msgSend_RectangleF (this.Handle, initWithFrame, frame); else Handle = ObjCRuntime.Messaging.IntPtr_objc_msgSendSuper_RectangleF (this.SuperHandle, initWithFrame, frame); }
Lihat tautan ini juga.
sumber
Saya tidak tahu secara spesifik apa yang menyebabkan kode Anda macet, tetapi berikut adalah cara menghasilkan mesh, tulang, dan skinning mesh itu - semuanya dari kode. Swift4 dan iOS 12.
Dalam contoh, terdapat jaring yang merepresentasikan rangkaian dua silinder, dengan salah satu silinder bercabang pada sudut 45 derajat, seperti:
Silinder hanyalah segitiga yang diekstrusi, yaitu
radialSegmentCount = 3.
(Perhatikan bahwa ada 12 simpul, bukan 9, karena kedua silinder tidak benar-benar terhubung. Segitiga tersebut disusun seperti ini:v5 ^ v3 /__|__\ v1 | | | | v4 | v2 |/___\| v0
Ada 3 tulang, sesuai dengan kepala dan kaki silinder, dimana tulang tengah sesuai dengan kepala silinder bawah dan sekaligus kaki silinder atas. Jadi misalnya, simpul
v0
,v2
danv4
sesuai denganbone0
;v1
,v3
,v5
Sesuai denganbone1
, dan sebagainya. Itu menjelaskan mengapaboneIndices
(lihat di bawah) memiliki nilai yang dimilikinya.Posisi istirahat tulang sesuai dengan posisi diam silinder dalam geometri (
bone2
bertunas pada sudut 45 derajat daribone1
, seperti geometri silinder).Dengan itu sebagai konteks, kode berikut membuat semua yang dibutuhkan untuk menguliti geometri:
let vertices = [float3(0.17841241, 0.0, 0.0), float3(0.17841241, 1.0, 0.0), float3(-0.089206174, 0.0, 0.1545097), float3(-0.089206174, 1.0, 0.1545097), float3(-0.089206256, 0.0, -0.15450965), float3(-0.089206256, 1.0, -0.15450965), float3(0.12615661, 1.1261566, 0.0), float3(-0.58094996, 1.8332633, 0.0), float3(-0.063078284, 0.9369217, 0.1545097), float3(-0.7701849, 1.6440284, 0.1545097), float3(-0.063078344, 0.93692166, -0.15450965), float3(-0.77018493, 1.6440284, -0.15450965)] let indices: [UInt8] = [0, 1, 2, 3, 4, 5, 0, 1, 1, 6, 6, 7, 8, 9, 10, 11, 6, 7] let geometrySource = SCNGeometrySource(vertices: vertices.map { SCNVector3($0) }) let geometryElement = SCNGeometryElement(indices: indices, primitiveType: .triangleStrip) let geometry = SCNGeometry(sources: [geometrySource], elements: [geometryElement]) let bone0 = SCNNode() bone0.simdPosition = float3(0,0,0) let bone1 = SCNNode() bone1.simdPosition = float3(0,1,0) let bone2 = SCNNode() bone2.simdPosition = float3(0,1,0) + normalize(float3(-1,1,0)) let bones = [bone0, bone1, bone2] let boneInverseBindTransforms: [NSValue]? = bones.map { NSValue(scnMatrix4: SCNMatrix4Invert($0.transform)) } var boneWeights: [Float] = vertices.map { _ in 1.0 } var boneIndices: [UInt8] = [ 0, 1, 0, 1, 0, 1, 1, 2, 1, 2, 1, 2, ] let boneWeightsData = Data(bytesNoCopy: &boneWeights, count: boneWeights.count * MemoryLayout<Float>.size, deallocator: .none) let boneIndicesData = Data(bytesNoCopy: &boneIndices, count: boneWeights.count * MemoryLayout<UInt8>.size, deallocator: .none) let boneWeightsGeometrySource = SCNGeometrySource(data: boneWeightsData, semantic: .boneWeights, vectorCount: boneWeights.count, usesFloatComponents: true, componentsPerVector: 1, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: 0, dataStride: MemoryLayout<Float>.size) let boneIndicesGeometrySource = SCNGeometrySource(data: boneIndicesData, semantic: .boneIndices, vectorCount: boneIndices.count, usesFloatComponents: false, componentsPerVector: 1, bytesPerComponent: MemoryLayout<UInt8>.size, dataOffset: 0, dataStride: MemoryLayout<UInt8>.size) let skinner = SCNSkinner(baseGeometry: geometry, bones: bones, boneInverseBindTransforms: boneInverseBindTransforms, boneWeights: boneWeightsGeometrySource, boneIndices: boneIndicesGeometrySource) let node = SCNNode(geometry: geometry) node.skinner = skinner
Catatan: Dalam banyak kasus, Anda sebaiknya
UInt16
tidak menggunakanUInt8
.sumber