Post

Replies

Boosts

Views

Activity

SWIFTUI view not completely refreshed!
Hello fellow developers here is something that I don t fully grasp : 1/ I have a fake SceneKit with two nodes both having light 2/ I have a small widget to explore those lights and tweak some param -> in the small widget I can t update a toggle item when a new light is selected while other params are updated ! here is a short sample that illustrate what I am trying to resolve import SwiftUI import SceneKit class ShortScene { var scene = SCNScene() var lightNodes : [SCNNode] { get {scene.rootNode.childNodes(passingTest: { current, stop in current.light != nil} ) } } init() { let light1 = SCNLight() light1.castsShadow = false light1.type = .omni light1.intensity = 100 let nodelight1 = SCNNode() nodelight1.light = light1 nodelight1.name = "nodeLight1" scene.rootNode.addChildNode(nodelight1) let light2 = SCNLight() light2.castsShadow = false light2.type = .ambient light2.intensity = 300 let nodelight2 = SCNNode() nodelight2.light = light2 nodelight2.name = "nodeLight2" scene.rootNode.addChildNode(nodelight2) } } extension SCNLight : ObservableObject {} extension SCNNode : ObservableObject {} struct LightViewEx : View { @ObservedObject var lightParam : SCNLight @ObservedObject var lightNode : SCNNode var bindCol : Binding<Color> @State var castShadows : Bool init( _ _lightNode : SCNNode) { if let _light = _lightNode.light { lightParam = _light lightNode = _lightNode bindCol = Binding<Color>( get: { if let _lightcol = _lightNode.light!.color as! NSColor? { return Color(_lightcol)} else { return Color.red } }, set: { newCol in _lightNode.light!.color = NSColor(newCol) } ) castShadows = _lightNode.light!.castsShadow print( "For \(lightNode.name!) : CShadows \(castShadows)") } else { fatalError("No Light attached to Node") } } var body : some View { VStack(alignment: .leading) { Text("Light Params") Picker("Type",selection : $lightParam.type) { Text("IES").tag(SCNLight.LightType.IES) Text("Ambient").tag(SCNLight.LightType.ambient) Text("Directionnal").tag(SCNLight.LightType.directional) Text("Directionnal").tag(SCNLight.LightType.directional) Text("Omni").tag(SCNLight.LightType.omni) Text("Probe").tag(SCNLight.LightType.probe) Text("Spot").tag(SCNLight.LightType.spot) Text("Area").tag(SCNLight.LightType.area) } ColorPicker("Light Color", selection: bindCol) Text("Intensity") TextField("Intensity", value: $lightParam.intensity, formatter: NumberFormatter()) Divider() // Toggle("shadows", isOn: $lightParam.castsShadow ).onChange(of: lightParam.castsShadow, { lightParam.castsShadow.toggle() }) Toggle("CastShadows", isOn: $castShadows ) .onChange(of: castShadows) { lightParam.castsShadow = castShadows;print("castsShadows changed to \(castShadows)") } } } } struct sceneView : View { @State var _lightIdx : Int = 0 @State var shortScene = ShortScene() var body : some View { VStack(alignment: .leading) { if shortScene.lightNodes.isEmpty == false { Picker("Lights", selection: $_lightIdx) { ForEach(0..<shortScene.lightNodes.count, id: \.self) { index in Text(shortScene.lightNodes[index].name ?? "NoName" ).tag(index) } } GridRow(alignment: .top) { LightViewEx(shortScene.lightNodes[_lightIdx]) } } } } } struct testUIView: View { var body: some View { sceneView() } } #Preview { testUIView() } Something is obviously not right ! Anyone has some idea ?
1
1
674
Feb ’24
SceneKit / renderNode override/ Metal / Instancing
Hello I am a bit stuck with a silly challenge I set myself : I want to have a node with a simple geometry ( let's say a triangle ) and I want to render those triangles N time with passing in some information per triangle. Let s say an offset to apply at shader time. I understand that may be I should create a sourcegeo and create multiple nodes to reflect that but here the point is to implement some Metal stuff in the renderNode override ( SCNode.rendererDelegate / SCNRenderNodeDelegate ). so I set up some vertex shader like this : vertex VertexOut brush_vertex_main(const VertexIn vertexIn [[stage_in]], constant BrushNodeBuffer& scn_node [[buffer(BufferNode)]], constant BrushInstances *instances [[buffer(BufferBrush)]], uint instanceID [[instance_id]]) { float4 vertexOffset = float4(instances[instanceID].offset.xyz,1.0) + float4(vertexIn.position.xyz,1.0); // float4 vertexOffset = float4(vertexIn.position.xyz,1.0); VertexOut out = { .position = scn_node.modelViewProjectionTransform * vertexOffset, .color = vertexIn.color }; return out; } Did some binding as well to declare a pipelinerenderState like for eg let defLib = wd.device!.makeDefaultLibrary() let vertFunc = defLib?.makeFunction(name: vertexFunctionName) let fragFunc = defLib?.makeFunction(name: fragmentFunctionName) // add geo desc ( geometries should be doing that underhood anyway let vertexDescriptor = MTLVertexDescriptor() // pos (SCNVertexSemanticPosition) vertexDescriptor.attributes[0].format = .float3 vertexDescriptor.attributes[0].bufferIndex = 0 vertexDescriptor.attributes[0].offset = 0 // color ( SCNVertexSemanticColor) vertexDescriptor.attributes[3].format = .float3 vertexDescriptor.attributes[3].bufferIndex = 0 vertexDescriptor.attributes[3].offset = MemoryLayout<simd_float3>.stride vertexDescriptor.layouts[0].stride = MemoryLayout<simd_float3>.stride * 2 let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.vertexFunction = vertFunc pipelineDescriptor.fragmentFunction = fragFunc pipelineDescriptor.vertexDescriptor = vertexDescriptor did some buffers creation, setting them properly in the rendering loop rendererCmdEnc.setRenderPipelineState(brushRenderPipelineState!) rendererCmdEnc.setVertexBuffer(vertBuff!, offset: 0, index: 0) // node info rendererCmdEnc.setVertexBuffer(nodeBuff! , offset: 0, index: Int(BufferNode.rawValue)) // per instance info rendererCmdEnc.setVertexBuffer(primBuff! , offset: 0, index: Int(BufferBrush.rawValue)) rendererCmdEnc.drawIndexedPrimitives(type: .triangle, indexCount: primitiveIdx.count, indexType: .uint16, indexBuffer: indexBuff!, indexBufferOffset: 0, instanceCount: 6) and I keep banging my head when this executes : I have a miss match between my renderpipelineState vs the RenderPassDescriptor. Either it s the colorAttachment or the sample count/rastersamplecount that is invalid. -[MTLDebugRenderCommandEncoder setRenderPipelineState:]:1604: failed assertion `Set Render Pipeline State Validation For depth attachment, the texture sample count (1) does not match the renderPipelineState rasterSampleCount (4). The color sample count (1) does not match the renderPipelineState's color sample count (4) The raster sample count (1) does not match the renderPipelineState's raster sample count (4) I used the passdescriptor collorattachment to be a close as possible when describing the renderpipelinestate. changed the rastersamplecount. Tried without any specific for collorattachment etc... Alas, either the API validation will tell me I have the wrong colorAttachment info when I set up the renderpipelinestate in the renderloop and if I fixed the colorattach info at renderStatePipeline creation, some invalid sample count. In a nutshell : is there any way to do this kind of geo instancing in a single node using SceneKit ? thanks in advance for any support you would find interesting to provide!
2
0
1k
Jun ’23