2014-12-01 11 views
62

Mam aplikację Swift używającą SceneKit dla systemu iOS 8. Załaduję scenę z pliku .dae, który zawiera siatkę kontrolowaną przez szkielet. W czasie wykonywania należy zmodyfikować współrzędne tekstury. Używanie transformacji nie jest opcją - muszę obliczyć inny, całkowicie nowy UV dla każdego wierzchołka w siatce.Jak utworzyć obiekt SCNSkinner SceneKit w kodzie?

Wiem, że geometria jest niezmienna w SceneKit i przeczytałem, że sugerowane podejście polega na ręcznym wykonaniu kopii. Próbuję to zrobić, ale zawsze mam awarię podczas próby odtworzenia kodu SCNSkinner. The crash jest EXC_BAD_ACCESS wewnątrz C3DSourceAccessorGetMutableValuePtrAtIndex. Niestety nie ma na to kodu źródłowego, więc nie jestem pewien, dlaczego właśnie się zawiesza. Zawęziłem go do obiektu SCNSkinner dołączonego do węzła siatki. Jeśli tego nie ustawię, nie dostanę awarii i wszystko wydaje się działać.

EDIT: Oto bardziej kompletny stos wywołań od katastrofy:

C3DSourceAccessorGetMutableValuePtrAtIndex 
C3DSkinPrepareMeshForGPUIfNeeded 
C3DSkinnerMakeCurrentMesh 
C3DSkinnerUpdateCurrentMesh 
__CFSetApplyFunction_block_invoke 
CFBasicHashApply 
CFSetApplyFunction 
C3DAppleEngineRenderScene 
... 

nie znalazłem żadnej dokumentacji lub przykładowy kod na temat sposobu tworzenia obiektu SCNSkinner ręcznie. Ponieważ właśnie go tworzę 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 w kodzie SCNSkinner istnieje szkielet, którego nie jestem pewien. Ustawiłem go na szkielecie, który był na oryginalnym SCNSkinner siatki, którą kopiuję, co moim zdaniem powinno działać ... ale tak nie jest. Podczas ustawiania właściwości szkieletu nie wydaje się, aby była ona przypisywana. Sprawdzenie go natychmiast po wykonaniu zadania pokazuje, że wciąż jest zerowe. Jako test próbowałem ustawić właściwość szkieletu pierwotnej siatki na coś innego, a po przypisaniu również nie została ona naruszona.

Czy ktoś może rzucić światło na to, co się dzieje? Albo jak poprawnie utworzyć i skonfigurować obiekt ręcznie?

Oto kod, którego używam do ręcznego klonowania siatki i zastąpienia go nowym (nie zmieniłem tutaj żadnych danych źródłowych - po prostu staram się upewnić, że mogę utworzyć kopię w tym punkcie):

// 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) 
+0

Mój obecny plan obejścia tego problemu polega na prostym obliczeniu nowych współrzędnych tekstury, zastąpieniu pliku .dae, przepłukaniu sceny i ponownym wczytaniu pliku .dae. To powinno być zupełnie niepotrzebne, ale z dokumentacji Apple nie mogę się zorientować, o co chodzi. Albo robię coś niepoprawnie, pomijając jakiś ważny krok, albo SceneKit używa prywatnych API do poprawnego skonfigurowania SCNSkinnera podczas ładowania z pliku .dae. Ciekawe, że właściwość szkieletu po wczytaniu SceneKit z .dae jest węzłem z _no children_, ale animacja szkieletu wyraźnie działa. – jcr

+0

Nadpisanie pliku .dae niestety nie jest opcją. Istnieje etap optymalizacji i kompresji, który ma miejsce, gdy Xcode kopiuje plik .dae na urządzenie. Zapisanie nowego pliku .dae na urządzeniu w aplikacji nie działa, ponieważ program SceneKit na iOS nie załaduje pliku.plik dae, który nie został uruchomiony przez skrypt Xcode. – jcr

+0

Dostałeś tę pracę? –

Odpowiedz

1

Te trzy metody mogą pomóc znaleźć rozwiązanie:

  1. SCNNode *hero = [SCNScene sceneNamed:@"Hero"].rootNode; 
    SCNNode *hat = [SCNScene sceneNamed:@"FancyFedora"].rootNode; 
    hat.skinner.skeleton = hero.skinner.skeleton; 
    
  2. [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); 
    } 
    
  3. Zobacz także this link.