I have a Swift app using SceneKit for iOS 8. I load a scene from a .dae file that contains a mesh controlled by a skeleton. At runtime, I need to modify the texture coordinates. Using a transform is not an option -- I need to compute a different, completely new UV for each vertex in the mesh.
I know geometry is immutable in SceneKit, and I've read that the suggested approach is to make a copy manually. I'm trying to do that, but I always end up crashing when trying to re-create the SCNSkinner
in code. The crash is an EXC_BAD_ACCESS
inside C3DSourceAccessorGetMutableValuePtrAtIndex
. Unfortunately, there is no source code for this, so I'm not sure why exactly it's crashing. I've narrowed it down to the SCNSkinner
object attached to the mesh node. If I do not set that, I don't get a crash and things appear to be working.
EDIT: Here is a more complete call stack of the crash:
C3DSourceAccessorGetMutableValuePtrAtIndex C3DSkinPrepareMeshForGPUIfNeeded C3DSkinnerMakeCurrentMesh C3DSkinnerUpdateCurrentMesh __CFSetApplyFunction_block_invoke CFBasicHashApply CFSetApplyFunction C3DAppleEngineRenderScene ...
I've not found any documentation or example code about how to create an SCNSkinner
object manually. Since I'm just creating it based on a previously working mesh, it shouldn't be too difficult. I'm creating the SCNSkinner
according to the Swift documentation, passing all of the correct things into the init. However, there is a skeleton property in the SCNSkinner
that I'm not sure how to set. I set it to the skeleton that was on the original SCNSkinner
of the mesh I'm copying, which I think should work... but it doesn't. When setting the skeleton property, it does not appear to be assigning. Checking it immediately after the assignment shows that it is still nil. As a test, I tried to set the original mesh's skeleton property to something else, and after the assignment it was left untouched as well.
Can anyone shed any light on what is happening? Or how to correctly create and set up an SCNSkinner
object manually?
Here is the code I'm using to manually clone a mesh and replace it with a new one (I have not modified any of the source data here -- I'm simply trying to make sure I can create a copy at this point):
// 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)
This three methods may help you to find the solution:
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); }
See this link as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With