Tengo una aplicación Swift que usa SceneKit para iOS 8. Carga una escena de un archivo .dae que contiene una malla controlada por un esqueleto. En tiempo de ejecución, necesito modificar las coordenadas de textura. Usar una transformación no es una opción: necesito calcular un UV diferente y completamente nuevo para cada vértice de la malla.
Sé que la geometría es inmutable en SceneKit y leí que el enfoque sugerido es hacer una copia manualmente. Estoy tratando de hacer eso, pero siempre termino fallando cuando intento volver a crear el SCNSkinner
código en. El choque es un EXC_BAD_ACCESS
interior C3DSourceAccessorGetMutableValuePtrAtIndex
. Desafortunadamente, no hay un código fuente para esto, por lo que no estoy seguro de por qué se bloquea exactamente. Lo he reducido al SCNSkinner
objeto adjunto al nodo de malla. Si no configuro eso, no obtengo un bloqueo y las cosas parecen estar funcionando.
EDITAR: Aquí hay una pila de llamadas más completa del bloqueo:
C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...
No he encontrado ninguna documentación o código de ejemplo sobre cómo crear un SCNSkinner
objeto manualmente. Como lo estoy creando basándome en una malla que funcionaba anteriormente, no debería ser demasiado difícil. Estoy creando el de SCNSkinner
acuerdo con la documentación de Swift, pasando todas las cosas correctas al init. Sin embargo, hay una propiedad de esqueleto en el SCNSkinner
que no estoy seguro de cómo configurar. Lo configuré en el esqueleto que estaba en el original SCNSkinner
de la malla que estoy copiando, que creo que debería funcionar ... pero no funciona. Al configurar la propiedad del esqueleto, no parece estar asignando. Verificarlo inmediatamente después de la asignación muestra que todavía es nulo. Como prueba, intenté establecer la propiedad de esqueleto de la malla original en otra cosa, y después de la asignación tampoco se modificó.
¿Alguien puede arrojar algo de luz sobre lo que está sucediendo? ¿O cómo crear y configurar correctamente un SCNSkinner
objeto manualmente?
Aquí está el código que estoy usando para clonar manualmente una malla y reemplazarla por una nueva (no he modificado ninguno de los datos de origen aquí; simplemente estoy tratando de asegurarme de poder crear una copia en este momento) :
// 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)
Respuestas:
Estos tres métodos pueden ayudarlo a encontrar la solución:
Vea este enlace también.
fuente
No sé específicamente qué causa que su código se bloquee, pero aquí hay una forma de generar una malla, huesos y pelar esa malla, todo desde el código. Swift4 y iOS 12.
En el ejemplo, hay una malla que representa la concatenación de dos cilindros, con uno de los cilindros ramificándose en un ángulo de 45 grados, así:
Los cilindros son solo triángulos extruidos, es decir,
radialSegmentCount = 3.
(tenga en cuenta que hay 12 vértices, no 9, ya que los dos cilindros no están realmente unidos. Los triángulos están ordenados así:Hay 3 huesos, correspondientes a las cabezas y pies de los cilindros, donde el hueso del medio corresponde a la cabeza del cilindro inferior y simultáneamente al pie del cilindro superior. Así, por ejemplo, los vértices
v0
,v2
yv4
corresponden abone0
;v1
,v3
,v5
Corresponden abone1
, y así sucesivamente. Eso explica por québoneIndices
(ver más abajo) tiene el valor que tiene.Las posiciones de reposo de los huesos corresponden a las posiciones de reposo de los cilindros en la geometría (
bone2
brota en un ángulo de 45 gradosbone1
, al igual que la geometría del cilindro).Con eso como contexto, el siguiente código crea todo lo necesario para revestir la geometría:
Nota: en la mayoría de los casos, debe utilizar
UInt16
noUInt8
.fuente