Ich habe eine Swift-App mit SceneKit für iOS 8. Ich lade eine Szene aus einer .dae-Datei, die ein von einem Skelett gesteuertes Netz enthält. Zur Laufzeit muss ich die Texturkoordinaten ändern. Die Verwendung einer Transformation ist keine Option. Ich muss für jeden Scheitelpunkt im Netz eine andere, völlig neue UV-Strahlung berechnen.
Ich weiß, dass Geometrie in SceneKit unveränderlich ist, und ich habe gelesen, dass der vorgeschlagene Ansatz darin besteht, eine Kopie manuell zu erstellen. Ich versuche das zu tun, aber ich stürze immer ab, wenn ich versuche, den SCNSkinner
In-Code neu zu erstellen . Der Absturz ist ein EXC_BAD_ACCESS
Inneres C3DSourceAccessorGetMutableValuePtrAtIndex
. Leider gibt es dafür keinen Quellcode, daher bin ich mir nicht sicher, warum genau er abstürzt. Ich habe es auf das SCNSkinner
Objekt eingegrenzt, das an den Netzknoten angehängt ist. Wenn ich das nicht einstelle, bekomme ich keinen Absturz und die Dinge scheinen zu funktionieren.
EDIT: Hier ist ein vollständigerer Aufrufstapel des Absturzes:
C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...
Ich habe keine Dokumentation oder Beispielcode zum SCNSkinner
manuellen Erstellen eines Objekts gefunden. Da ich es nur auf der Grundlage eines zuvor funktionierenden Netzes erstelle, sollte es nicht zu schwierig sein. Ich erstelle das SCNSkinner
gemäß der Swift-Dokumentation und übergebe alle richtigen Dinge an den Init. Es gibt jedoch eine Skeletteigenschaft in der SCNSkinner
, deren Einstellung ich nicht sicher bin. Ich habe es auf das Skelett eingestellt, das sich auf dem Original SCNSkinner
des zu kopierenden Netzes befand, was meiner Meinung nach funktionieren sollte ... aber es funktioniert nicht. Wenn Sie die Skeleton-Eigenschaft festlegen, scheint sie nicht zugewiesen zu sein. Wenn Sie es unmittelbar nach der Zuweisung überprüfen, wird angezeigt, dass es immer noch Null ist. Als Test habe ich versucht, die Skeletteigenschaft des ursprünglichen Netzes auf etwas anderes zu setzen, und nach der Zuweisung blieb sie ebenfalls unberührt.
Kann jemand Licht ins Dunkel bringen, was passiert? Oder wie man ein SCNSkinner
Objekt manuell korrekt erstellt und einrichtet ?
Hier ist der Code, den ich verwende, um ein Netz manuell zu klonen und durch ein neues zu ersetzen (ich habe hier keine Quelldaten geändert - ich versuche lediglich sicherzustellen, dass ich an dieser Stelle eine Kopie erstellen kann). ::
// 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)
Antworten:
Diese drei Methoden können Ihnen helfen, die Lösung zu finden:
Siehe auch diesen Link .
quelle
Ich weiß nicht genau, was Ihren Code zum Absturz bringt, aber hier ist eine Möglichkeit, ein Netz, Knochen und das Enthäuten dieses Netzes zu generieren - alles aus Code. Swift4 und iOS 12.
In dem Beispiel gibt es ein Netz, das die Verkettung von zwei Zylindern darstellt, wobei einer der Zylinder in einem Winkel von 45 Grad abzweigt, wie folgt:
Die Zylinder sind nur extrudierte Dreiecke, dh
radialSegmentCount = 3.
(Beachten Sie, dass es 12 Eckpunkte gibt, nicht 9, da die beiden Zylinder nicht wirklich miteinander verbunden sind. Die Dreiecke sind wie folgt angeordnet:Es gibt 3 Knochen, die den Köpfen und Füßen der Zylinder entsprechen, wobei der mittlere Knochen dem Kopf des unteren Zylinders und gleichzeitig dem Fuß des oberen Zylinders entspricht. So zum Beispiel, Ecken
v0
,v2
undv4
entsprechenbone0
;v1
,v3
,v5
Entsprichtbone1
, und so weiter. Das erklärt, warumboneIndices
(siehe unten) den Wert hat, den es hat.Die Ruhepositionen der Knochen entsprechen den Ruhepositionen der Zylinder in der Geometrie (
bone2
sprießt in einem Winkel von 45 Grad vonbone1
, genau wie die Zylindergeometrie).Mit diesem Kontext erstellt der folgende Code alles, was zum Skinieren der Geometrie erforderlich ist:
Hinweis: In den meisten Fällen sollten Sie
UInt16
nicht verwendenUInt8
.quelle