Mam aplikację Swift używającą SceneKit dla iOS 8. Ładuję scenę z pliku .dae, który zawiera siatkę kontrolowaną przez szkielet. W czasie wykonywania muszę zmodyfikować współrzędne tekstury. Korzystanie z transformacji nie wchodzi w grę - muszę obliczyć inne, całkowicie nowe UV dla każdego wierzchołka siatki.
Wiem, że geometria jest niezmienna w SceneKit i przeczytałem, że sugerowanym podejściem jest ręczne wykonanie kopii. Próbuję to zrobić, ale zawsze kończy mi się awaria, gdy próbuję odtworzyć SCNSkinner
kod. Katastrofa jest w EXC_BAD_ACCESS
środku C3DSourceAccessorGetMutableValuePtrAtIndex
. Niestety nie ma do tego kodu źródłowego, więc nie jestem pewien, dlaczego dokładnie się zawiesza. Zawęziłem to do SCNSkinner
obiektu przymocowanego do węzła siatki. Jeśli tego nie ustawię, nie ma awarii i wydaje się, że wszystko działa.
EDYCJA: Oto bardziej kompletny stos wywołań awarii:
C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...
Nie znalazłem żadnej dokumentacji ani przykładowego kodu o tym, jak SCNSkinner
ręcznie utworzyć obiekt. Ponieważ tworzę go tylko w oparciu o wcześniej działającą siatkę, nie powinno to być zbyt trudne. Tworzę SCNSkinner
zgodnie z dokumentacją Swift, przekazując wszystkie poprawne rzeczy do init. Jednak istnieje właściwość szkieletu SCNSkinner
, której nie jestem pewien, jak ustawić. Ustawiłem go na szkielecie, który był na oryginale SCNSkinner
siatki, którą kopiuję, co moim zdaniem powinno działać ... ale tak nie jest. Podczas ustawiania właściwości szkieletu nie wygląda na to, że jest przypisywana. Sprawdzenie go natychmiast po przypisaniu pokazuje, że nadal jest zerowy. W ramach testu próbowałem ustawić właściwość szkieletu oryginalnej siatki na coś innego i po przypisaniu również ona została nietknięta.
Czy ktoś może rzucić światło na to, co się dzieje? Albo jak poprawnie SCNSkinner
ręcznie stworzyć i skonfigurować obiekt?
Oto kod, którego używam do ręcznego sklonowania siatki i zastąpienia jej nową (nie modyfikowałem tutaj żadnych danych źródłowych - po prostu próbuję się upewnić, że mogę w tym miejscu utworzyć kopię) :
// 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)
Odpowiedzi:
Te trzy metody mogą pomóc w znalezieniu rozwiązania:
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); }
Zobacz również ten link .
źródło
Nie wiem konkretnie, co powoduje awarię kodu, ale oto sposób generowania siatki, kości i skórowania tej siatki - wszystko z kodu. Swift4 oraz iOS 12.
W tym przykładzie jest siatka reprezentująca połączenie dwóch cylindrów, przy czym jeden z cylindrów rozgałęzia się pod kątem 45 stopni, na przykład:
Cylindry są po prostu wytłoczonymi trójkątami, tj.
radialSegmentCount = 3.
(Zauważ, że jest 12 wierzchołków, a nie 9, ponieważ oba cylindry nie są tak naprawdę połączone. Trójkąty są uporządkowane w następujący sposób:v5 ^ v3 /__|__\ v1 | | | | v4 | v2 |/___\| v0
Istnieją 3 kości, odpowiadające głowom i stopom cylindrów, gdzie środkowa kość odpowiada główce dolnego cylindra i jednocześnie stopie górnego cylindra. Tak na przykład wierzchołki
v0
,v2
iv4
odpowiadająbone0
;v1
,v3
,v5
Odpowiadająbone1
, i tak dalej. To wyjaśnia, dlaczegoboneIndices
(patrz poniżej) ma taką wartość.Położenia spoczynkowe kości odpowiadają położeniom spoczynkowym cylindrów w geometrii (
bone2
wyrastają pod kątem 45 stopni odbone1
, podobnie jak geometria cylindra).W tym kontekście poniższy kod tworzy wszystko, co jest potrzebne do skórowania geometrii:
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
Uwaga: W większości przypadków należy użyć
UInt16
nieUInt8
.źródło