Programing

코드에서 SceneKit SCNSkinner 객체를 만드는 방법은 무엇입니까?

lottogame 2020. 9. 12. 11:23
반응형

코드에서 SceneKit SCNSkinner 객체를 만드는 방법은 무엇입니까?


iOS 8 용 SceneKit을 사용하는 Swift 앱이 있습니다. 골격으로 제어되는 메시가 포함 된 .dae 파일에서 장면을로드합니다. 런타임에 텍스처 좌표를 수정해야합니다. 변형을 사용하는 것은 옵션이 아닙니다. 메시의 각 정점에 대해 완전히 새로운 UV를 계산해야합니다.

저는 SceneKit에서 지오메트리를 변경할 수 없다는 것을 알고 있으며 제안 된 접근 방식은 수동으로 복사하는 것이라고 읽었습니다. 나는 그렇게하려고 노력하고 있지만 SCNSkinner코드 를 다시 만들려고 할 때 항상 충돌이 발생 합니다. 충돌은 EXC_BAD_ACCESS내부 C3DSourceAccessorGetMutableValuePtrAtIndex입니다. 불행히도 이에 대한 소스 코드가 없으므로 정확히 충돌하는 이유를 모르겠습니다. SCNSkinner메쉬 노드에 연결된 개체 로 좁혔습니다 . 설정하지 않으면 충돌이 발생하지 않고 작동하는 것처럼 보입니다.

편집 : 다음은 충돌의 더 완전한 호출 스택입니다.

C3DSourceAccessorGetMutableValuePtrAtIndex
C3DSkinPrepareMeshForGPUIfNeeded
C3DSkinnerMakeCurrentMesh
C3DSkinnerUpdateCurrentMesh
__CFSetApplyFunction_block_invoke
CFBasicHashApply
CFSetApplyFunction
C3DAppleEngineRenderScene
...

SCNSkinner수동으로 개체 를 만드는 방법에 대한 문서 나 예제 코드를 찾지 못했습니다 . 이전에 작업했던 메시를 기반으로 생성하고 있기 때문에 그렇게 어렵지 않습니다. 내가 만드는거야 SCNSkinner초기화에 올바른 일을 모두 통과, 스위프트 문서에 따라. 그러나 SCNSkinner설정 방법을 잘 모르겠다 는 골격 속성이 있습니다. SCNSkinner복사중인 메시 의 원본에있는 스켈레톤으로 설정 했는데 작동해야한다고 생각합니다 ...하지만 그렇지 않습니다. 스켈레톤 속성을 설정할 때 할당되는 것처럼 보이지 않습니다. 할당 직후에 확인하면 여전히 nil임을 알 수 있습니다. 테스트로 원래 메시의 스켈레톤 속성을 다른 것으로 설정하려고했고 할당 후에도 그대로 두었습니다.

아무도 무슨 일이 일어나고 있는지에 대해 밝힐 수 있습니까? 또는 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)

이 세 가지 방법은 솔루션을 찾는 데 도움이 될 수 있습니다.

  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. 이 링크참조하십시오 .


코드가 충돌하는 원인이 무엇인지 구체적으로 알지 못하지만 여기에 메쉬, 뼈를 생성하고 해당 메쉬를 스키닝하는 방법이 있습니다. Swift4 및 iOS 12.

이 예에는 두 개의 원통을 연결하는 메시가 있으며 원통 중 하나는 다음과 같이 45도 각도로 분기됩니다.

 \
 |

The cylinders are just extruded triangles, i.e., radialSegmentCount = 3. (Note that there are 12 vertices, not 9, since the two cylinders aren't really conjoined. The triangles are ordered like this:

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

There are 3 bones, corresponding to the heads and feet of the cylinders, where the middle bone corresponds to the head of the bottom cylinder and simultaneously the foot of the top cylinder. So for example, vertices v0, v2, and v4 correspond to bone0; v1, v3, v5 correspond to bone1, and so forth. That explains why boneIndices (see below) has the value that it does.

The resting positions of the bones corresponds to the resting positions of the cylinders in the geometry (bone2 sprouts off at a 45 degree angle from bone1, just like the cylinder geometry).

With that as context, the following code creates everything needed to skin the geometry:

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

Note: In most cases, you should use UInt16 not UInt8.

참고URL : https://stackoverflow.com/questions/27233066/how-to-create-a-scenekit-scnskinner-object-in-code

반응형