2014-12-01 92 views
62

我有一個使用SceneKit for iOS 8的Swift應用程序。我從包含由骨架控制的網格的.dae文件加載場景。 在運行時,我需要修改紋理座標。使用變換不是一種選擇 - 我需要爲網格中的每個頂點計算不同的,全新的UV。如何在代碼中創建一個SceneKit SCNSkinner對象?

我知道幾何圖形在SceneKit中是不可變的,我讀過建議的方法是手動進行復制。我試圖做到這一點,但是當試圖在代碼中重新創建SCNSkinner時,我總是會崩潰。 C3DSourceAccessorGetMutableValuePtrAtIndex內部的崩潰是EXC_BAD_ACCESS。不幸的是,這裏沒有源代碼,所以我不確定它爲什麼會崩潰。我已經縮小到連接到網格節點的SCNSkinner對象。如果我不這樣做,我不會崩潰,而且事情似乎正在發揮作用。

編輯:這裏是崩潰的更完整的調用堆棧:

C3DSourceAccessorGetMutableValuePtrAtIndex 
C3DSkinPrepareMeshForGPUIfNeeded 
C3DSkinnerMakeCurrentMesh 
C3DSkinnerUpdateCurrentMesh 
__CFSetApplyFunction_block_invoke 
CFBasicHashApply 
CFSetApplyFunction 
C3DAppleEngineRenderScene 
... 

我還沒有發現有關如何手動創建一個SCNSkinner對象的任何文檔或示例代碼。由於我只是基於以前工作的網格創建它,所以它不應該太難。我根據Swift文檔創建SCNSkinner,將所有正確的東西傳入init。但是,SCNSkinner中有一個框架屬性,我不知道如何設置。我將它設置爲我正在複製的網格的原始SCNSkinner上的骨架,我認爲 應該可以工作......但它不會。當設置骨架屬性時,它似乎不是分配。分配後立即檢查它仍然爲零。作爲一項測試,我試圖將原始網格的骨架屬性設置爲其他屬性,並且在賦值之後它也保持不變。

任何人都可以闡明發生了什麼?或者如何手動正確創建並設置SCNSkinner對象?

這裏是我用來手動克隆一個網格的代碼,並用一個新的代替(我沒有修改任何源數據 - 我只是試圖確保我可以創建一個副本在這一點上):

// 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

我目前的解決方案是計算新的紋理座標,覆蓋.dae文件,刷新場景並重新加載.dae文件。這應該是完全不必要的,但從Apple的文檔中,我無法弄清楚問題所在。我要麼不正確地做某些事情,要麼丟掉一些關鍵步驟,要麼從.dae文件加載時,SceneKit使用一些私有API來正確設置SCNSkinner。 有趣的是,SceneKit從.dae加載後的skeleton屬性是帶有_no children_的節點,但骨架動畫清晰地起作用。 – jcr 2014-12-04 08:22:33

+0

不幸的是,覆蓋.dae文件不是一個選項。 Xcode將您的.dae文件複製到設備時會發生優化和壓縮步驟。在應用程序內的設備上保存新的.dae文件不起作用,因爲iOS上的SceneKit不會加載。dae文件沒有通過Xcode的腳本運行。 – jcr 2014-12-11 10:03:35

+0

你有這個工作嗎? – 2017-12-16 20:00:53

回答

1

這三種方法可以幫助你找到解決辦法:

  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. 另請參閱this link

相關問題