Kodda SceneKit SCNSkinner nesnesi nasıl oluşturulur?


88

İOS 8 için SceneKit kullanan bir Swift uygulamam var. Bir iskelet tarafından kontrol edilen bir ağ içeren bir .dae dosyasından bir sahne yüklüyorum. Çalışma zamanında doku koordinatlarını değiştirmem gerekiyor. Dönüşüm kullanmak bir seçenek değil - Ağdaki her köşe için farklı, tamamen yeni bir UV hesaplamam gerekiyor.

SceneKit'te geometrinin değişmez olduğunu biliyorum ve önerilen yaklaşımın manuel olarak bir kopya yapmak olduğunu okudum. Bunu yapmaya çalışıyorum, ancak SCNSkinnerin kodunu yeniden oluşturmaya çalışırken her zaman çöküyorum . Çarpışma EXC_BAD_ACCESSiçeride C3DSourceAccessorGetMutableValuePtrAtIndex. Ne yazık ki, bunun için bir kaynak kodu yok, bu yüzden tam olarak neden çöktüğünden emin değilim. SCNSkinnerÖrgü düğüme bağlı nesneye kadar daralttım . Bunu ayarlamazsam, bir çökme yaşamam ve işler yolunda görünüyor.

DÜZENLEME: İşte çökmenin daha eksiksiz bir çağrı yığını:

C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...

Bir SCNSkinnernesnenin manuel olarak nasıl oluşturulacağına ilişkin herhangi bir belge veya örnek kod bulamadım . Daha önce çalışan bir ağa dayanarak oluşturduğum için, çok zor olmamalı. SCNSkinnerSwift dokümantasyonuna göre oluşturuyorum, tüm doğru şeyleri init'e geçiriyorum. Bununla birlikte, SCNSkinnernasıl ayarlanacağından emin olmadığım bir iskelet özelliği var . SCNSkinnerKopyaladığım ağın orijinalindeki iskelete ayarladım , işe yaraması gerektiğini düşünüyorum ... ama olmuyor. İskelet özelliğini ayarlarken, atanıyor gibi görünmüyor. Ödevden hemen sonra kontrol etmek, hala sıfır olduğunu gösterir. Bir test olarak, orijinal ağın iskelet özelliğini başka bir şeye ayarlamaya çalıştım ve atamadan sonra da dokunulmadan kaldı.

Olanlara ışık tutan var mı? Veya bir SCNSkinnernesneyi manuel olarak nasıl doğru bir şekilde oluşturabilir ve ayarlayabilirsiniz ?

İşte bir ağı el ile klonlamak ve yenisiyle değiştirmek için kullandığım kod (buradaki kaynak verilerden hiçbirini değiştirmedim - sadece bu noktada bir kopya oluşturabileceğimden emin olmaya çalışıyorum) :

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

Mevcut bir geçici çözüm için planım, yeni doku koordinatlarını hesaplamak, .dae dosyasının üzerine yazmak, sahneyi temizlemek ve .dae dosyasını yeniden yüklemek. Bu tamamen gereksiz olmalı, ancak Apple'ın belgelerine göre sorunun ne olduğunu anlayamıyorum. Ya yanlış bir şey yapıyorum, önemli bir adımı atlıyorum ya da SceneKit .dae dosyasından yüklerken SCNSkinner'ı doğru şekilde kurmak için bazı özel API'ler kullanıyor. .Dae gelen SceneKit yüklendikten sonra iskelet özellik ile bir düğüm olduğunu İlginç hiçbir çocuklar , henüz iskelet animasyon açıkça çalışır.
jcr

.Dae dosyasının üzerine yazmak maalesef bir seçenek değildir. Xcode .dae dosyanızı aygıta kopyaladığında gerçekleşen bir optimizasyon ve sıkıştırma adımı vardır. Uygulama içinde cihaza yeni bir .dae dosyası kaydetmek işe yaramaz çünkü iOS'taki SceneKit, Xcode'un komut dosyası aracılığıyla çalıştırılmamış bir .dae dosyasını yüklemeyecektir.
jcr

Bu işi aldın mı?
μολὼν.λαβέ

Hayır, sonunda kendi motorumu yazdım ve o zamandan beri SceneKit'i kullanmadım.
jcr

Etkileyici. Opengl veya metal ile gittin mi?
μολὼν.λαβέ

Yanıtlar:


1

Bu üç yöntem çözümü bulmanıza yardımcı olabilir:

  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. Bu bağlantıya da bakın .


0

Kodunuzun çökmesine neyin sebep olduğunu tam olarak bilmiyorum ama işte bir ağ, kemikler oluşturmanın ve o ağın dış görünümünü oluşturmanın bir yolu - hepsi koddan. Swift4 ve iOS 12.

Örnekte, aşağıdaki gibi 45 derecelik bir açıyla dallanan silindirlerden biri ile iki silindirin birleşimini temsil eden bir ağ vardır:

 \
 |

Silindirler sadece haddelenmiş üçgenlerdir, yani radialSegmentCount = 3.(9 değil, 12 köşe bulunduğuna dikkat edin, çünkü iki silindir gerçekten birleşik değildir. Üçgenler şu şekilde sıralanır:

      v5
      ^
v3 /__|__\ v1
   |  |  |
   |  v4 |
v2 |/___\| v0

Silindirlerin başlarına ve ayaklarına karşılık gelen 3 kemik vardır; burada orta kemik, alt silindirin başına ve aynı zamanda üst silindirin ayağına karşılık gelir. Yani, örneğin, köşe v0, v2ve v4tekabül etmek bone0; v1, v3, v5Karşılık bone1benzeri, vb. Bu, neden boneIndices(aşağıya bakın) sahip olduğu değere sahip olduğunu açıklar .

Kemiklerin dinlenme pozisyonları, geometride silindirlerin dinlenme pozisyonlarına karşılık gelir ( tıpkı silindir geometrisinde olduğu gibi bone245 derecelik bir açıyla filizlenir bone1).

Bununla bağlam olarak, aşağıdaki kod, geometriyi kaplamak için gereken her şeyi oluşturur:

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

Not: Çoğu durumda, kullanmak gerekir UInt16değil UInt8.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.