Eu tenho um aplicativo Swift usando SceneKit para iOS 8. Carrego uma cena de um arquivo .dae que contém uma malha controlada por um esqueleto. Em tempo de execução, preciso modificar as coordenadas de textura. Usar uma transformação não é uma opção - eu preciso calcular um UV diferente e completamente novo para cada vértice da malha.
Eu sei que a geometria é imutável no SceneKit, e li que a abordagem sugerida é fazer uma cópia manualmente. Estou tentando fazer isso, mas sempre acabo travando ao tentar recriar o SCNSkinner
código. O acidente é um EXC_BAD_ACCESS
interior C3DSourceAccessorGetMutableValuePtrAtIndex
. Infelizmente, não há código-fonte para isso, então não sei exatamente por que está falhando. Eu o reduzi ao SCNSkinner
objeto anexado ao nó da malha. Se eu não definir isso, não consigo travar e as coisas parecem estar funcionando.
EDIT: Aqui está uma pilha de chamadas mais completa da falha:
C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...
Não encontrei nenhuma documentação ou código de exemplo sobre como criar um SCNSkinner
objeto manualmente. Já que estou apenas criando com base em uma malha de trabalho anterior, não deve ser muito difícil. Estou criando o de SCNSkinner
acordo com a documentação do Swift, passando todas as coisas corretas para o init. No entanto, há uma propriedade de esqueleto na SCNSkinner
que não tenho certeza de como definir. Eu o configurei para o esqueleto que estava no original SCNSkinner
da malha que estou copiando, que acho que deveria funcionar ... mas não funciona. Ao definir a propriedade esqueleto, ela não parece estar sendo atribuída. Verificar imediatamente após a atribuição mostra que ainda é nulo. Como teste, tentei definir a propriedade de esqueleto da malha original para outra coisa e, após a atribuição, ela também foi deixada intacta.
Alguém pode lançar alguma luz sobre o que está acontecendo? Ou como criar e configurar corretamente um SCNSkinner
objeto manualmente?
Aqui está o código que estou usando para clonar manualmente uma malha e substituí-la por uma nova (não modifiquei nenhum dos dados de origem aqui - estou simplesmente tentando ter certeza de que posso criar uma cópia neste ponto) :
// 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)
Respostas:
Esses três métodos podem ajudá-lo a encontrar a solução:
Veja este link também.
fonte
Eu não sei especificamente o que causa o travamento do seu código, mas aqui está uma maneira de gerar uma malha, ossos e esfolar essa malha - tudo a partir do código. Swift4 e iOS 12.
No exemplo, há uma malha que representa a concatenação de dois cilindros, com um dos cilindros se ramificando em um ângulo de 45 graus, assim:
Os cilindros são apenas triângulos extrudados, ou seja,
radialSegmentCount = 3.
(Observe que existem 12 vértices, não 9, já que os dois cilindros não são realmente unidos. Os triângulos são ordenados assim:Existem 3 ossos, correspondendo às cabeças e pés dos cilindros, onde o osso do meio corresponde à cabeça do cilindro inferior e simultaneamente ao pé do cilindro superior. Assim, por exemplo, os vértices
v0
,v2
ev4
correspondem abone0
;v1
,v3
,v5
Corresponder abone1
, e assim por diante. Isso explica porqueboneIndices
(veja abaixo) tem o valor que tem.As posições de repouso dos ossos correspondem às posições de repouso dos cilindros na geometria (
bone2
brota em um ângulo de 45 grausbone1
, assim como a geometria do cilindro).Com isso como contexto, o código a seguir cria tudo o que é necessário para revestir a geometria:
Observação: na maioria dos casos, você
UInt16
não deve usarUInt8
.fonte