कोड में SceneKit SCNSkinner ऑब्जेक्ट कैसे बनाएं?


89

मेरे पास iOS के लिए SceneKit का उपयोग करके एक स्विफ्ट ऐप है। मैं एक .dae फ़ाइल से एक दृश्य लोड करता हूं जिसमें एक कंकाल द्वारा नियंत्रित एक जाल होता है। रनटाइम पर, मुझे बनावट के निर्देशांक को संशोधित करने की आवश्यकता है। एक ट्रांसफॉर्मेशन का उपयोग करना एक विकल्प नहीं है - मुझे मेष में प्रत्येक शीर्ष के लिए एक अलग, पूरी तरह से नए यूवी की गणना करने की आवश्यकता है।

मुझे पता है कि ज्यामिति SceneKit में अपरिवर्तनीय है, और मैंने पढ़ा है कि सुझाए गए दृष्टिकोण को मैन्युअल रूप से प्रतिलिपि बनाना है। मैं ऐसा करने की कोशिश कर रहा हूं, लेकिन SCNSkinnerकोड में फिर से बनाने की कोशिश करने पर मैं हमेशा दुर्घटनाग्रस्त हो जाता हूं । दुर्घटना EXC_BAD_ACCESSअंदर की है C3DSourceAccessorGetMutableValuePtrAtIndex। दुर्भाग्य से, इसके लिए कोई स्रोत कोड नहीं है, इसलिए मुझे यकीन नहीं है कि वास्तव में यह दुर्घटनाग्रस्त क्यों है। मैंने इसे SCNSkinnerजाल नोड से जुड़ी वस्तु तक सीमित कर दिया है । अगर मैं वह सेट नहीं करता हूं, तो मुझे कोई दुर्घटना नहीं होती है और चीजें काम करती दिखाई देती हैं।

संपादित करें: यहां दुर्घटना का अधिक पूर्ण कॉल स्टैक है:

C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...

मैंने SCNSkinnerमैन्युअल रूप से कोई ऑब्जेक्ट बनाने के बारे में कोई दस्तावेज़ या उदाहरण कोड नहीं पाया है । चूंकि मैं इसे पहले से काम कर रहे जाल के आधार पर बना रहा हूं, इसलिए यह बहुत मुश्किल नहीं होना चाहिए। मैं SCNSkinnerस्विफ्ट डॉक्यूमेंट के अनुसार, सभी सही चीजों को इनिट में पास कर रहा हूं । हालाँकि, वहाँ एक कंकाल संपत्ति है 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)

वर्कअराउंड के लिए मेरी वर्तमान योजना नई बनावट निर्देशांक की गणना करने के लिए है, .dae फ़ाइल को अधिलेखित करें, दृश्य को फ्लश करें, और .dae फ़ाइल को पुनः लोड करें। यह पूरी तरह से अनावश्यक होना चाहिए, लेकिन Apple के प्रलेखन से, मैं यह पता नहीं लगा सकता कि समस्या क्या है। मैं या तो गलत तरीके से कुछ कर रहा हूं, कुछ महत्वपूर्ण कदम छोड़ रहा हूं, या एसईसीकेटी .dae फाइल से लोड होने पर SCNSkinner को सही ढंग से सेट करने के लिए कुछ निजी APIs का उपयोग करता है। यह दिलचस्प है कि .dae से SceneKit लोड होने के बाद कंकाल की संपत्ति कोई बच्चों के साथ एक नोड है , फिर भी कंकाल एनीमेशन स्पष्ट रूप से काम करता है।
jcr

.Dae फ़ाइल को ओवरराइट करना दुर्भाग्य से एक विकल्प नहीं है। एक अनुकूलन और संपीड़न कदम है जो तब होता है जब Xcode आपके .dae फ़ाइल को डिवाइस में कॉपी करता है। ऐप के भीतर डिवाइस पर एक नई .dae फ़ाइल को सहेजना काम नहीं करता है क्योंकि iOS पर SceneKit एक .dae फ़ाइल को लोड नहीं करेगा जो Xcode की स्क्रिप्ट के माध्यम से नहीं चलाया गया है।
jcr

क्या आपको यह काम मिला?
μολ17ν.λαβέ 20

नहीं, मैंने अपना स्वयं का इंजन लिखना समाप्त कर दिया है और तब से SceneKit का उपयोग नहीं किया है।
jcr

प्रभावशाली। क्या आप opengl या धातु के साथ गए थे?
μολ17ν.λαβέ

जवाबों:


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. इस लिंक को भी देखें ।


0

मैं विशेष रूप से नहीं जानता कि आपके कोड के दुर्घटनाग्रस्त होने का कारण क्या है, लेकिन यहां एक जाल, हड्डियां और उस जाल को चमका देने का एक तरीका है - सभी कोड से। स्विफ्ट 4 और आईओएस 12।

उदाहरण में, दो सिलेंडरों के संघनन का प्रतिनिधित्व करने वाला जाल है, जिसमें से एक सिलेंडर 45 डिग्री के कोण पर बंद होता है, जैसे:

 \
 |

सिलेंडर केवल एक्सट्रूडेड त्रिकोण हैं, अर्थात radialSegmentCount = 3.(ध्यान दें कि 12 कोने हैं, 9 नहीं, क्योंकि दो सिलेंडर वास्तव में संयुक्त नहीं हैं। त्रिकोण इस तरह के आदेश दिए गए हैं:

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

सिलिंडर के सिर और पैरों के समान 3 हड्डियां होती हैं, जहां मध्य हड्डी नीचे के सिलेंडर के सिर से मेल खाती है और साथ ही साथ शीर्ष सिलेंडर के पैर। उदाहरण के लिए, कोने v0, v2और v4पत्र व्यवहार करने के bone0; v1, v3, v5के अनुरूप bone1बहुत आगे है, और। यह बताता है कि क्यों boneIndices(नीचे देखें) का मूल्य है जो वह करता है।

हड्डियों के आराम करने वाले स्थान ज्यामिति में सिलेंडर के आराम करने की स्थिति से मेल खाते हैं ( सिलेंडर ज्यामिति की तरह bone245 डिग्री के कोण पर अंकुरित होते हैं bone1)।

उस संदर्भ के साथ, निम्नलिखित कोड ज्यामिति को त्वचा करने के लिए आवश्यक सब कुछ बनाता है:

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

नोट: ज्यादातर मामलों में, आपको उपयोग UInt16नहीं करना चाहिए UInt8

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.