SceneKit

RSS for tag

Create 3D games and add 3D content to apps using high-level scene descriptions using SceneKit.

SceneKit Documentation

Post

Replies

Boosts

Views

Activity

USD to scenekit conversion removes normals
As the title suggests, clicking the "Export to SceneKit" button indeed converts a USD to .scn but removes the normals in the process if the mesh has blendshapes. When I export the same file without any blendshapes / morphtargets, the normals stay on as expected. If I try to create normals in the scenekit editor (adding them as a new geometry source) Xcode crashes (no matter if there are blendshapes or not) I've tried loading the resulting scene with [SCNSceneSource.LoadingOption.createNormalsIfAbsent : true] but this doesn't change anything either. I suppose this is a bug? My last resort is to load my character without any blendshapes and then add the targets from a different scene. Thanks for any insight! seb
0
0
466
Jul ’24
How to add a sort of engraving type of text to a mesh in SceneKit?
i'm trying to figure out how to basically engrave some text into this ellipsoid mesh. so far the only thing i've learned that can sort of come close to what im looking for is SCNText but it floats above the ellipsoid and doesnt conform to the angular shape. let allocator = MTKMeshBufferAllocator(device: MTLCreateSystemDefaultDevice()!) let disc = MDLMesh.newEllipsoid( withRadii: vector_float3(Float(discDiameter/2), Float(discDiameter/2), Float(discThickness/2)), radialSegments: 64, verticalSegments: 64, geometryType: .triangles, inwardNormals: false, hemisphere: false, allocator: allocator ) let discGeometry = SCNGeometry(mdlMesh: disc) let material = createIridescentMaterial() discGeometry.materials = [material]
2
0
649
Jul ’24
Failing to draw a window correctly using SceneKit
HI guys, I'm integrating the RoomPlan framework into my app. I'm able to scan a room and extract the nodes from the CaptureStructure object. So far, I can rebuild the 3D object in the SceneView, but I can't render the openings and the windows correctly. I'm struggling to add these two objects correctly in the wall, in order to make the wall transparent where they are supposed to be. If I export the CaptureStructure into a usda file and then I load it directly in the SceneView, all the doors, windows and openings are correctly rendered, therefore I do believe that I'm doing something wrong. Could you please tell me what I'm doing wrong? I added here a screenshot of my problem: I have also a prototype, which you can run and see the problem I'm talking about: https://github.com/renanstig/3d-scenekit-prototype
1
0
526
Jul ’24
Crash on iOS18 Beta5 when adding a SCNAnimation animationDidStop closure with Swift6 selected
I'm trying to update my projects to use Swift6, if I change the project settings to use Swift6 then my app crashes when I add a closure to the SCNAnimation animationDidStop property. The error is inside the SceneKit renderingQueue and indicates that the callback is being called on the wring queue. Maybe I need to do something in the code to fix this but I can't seem to make it work, maybe a SceneKit bug? If you create a new game template in Xcode using SceneKit and replace the contents of GameViewController.swift with the following you will see the app crash after it is launched. import UIKit import SceneKit class GameViewController: UIViewController { let player: SCNAnimationPlayer = { let a = CABasicAnimation(keyPath: "opacity") return SCNAnimationPlayer(animation: SCNAnimation(caAnimation: a)) }() override func viewDidLoad() { super.viewDidLoad() let scnView = self.view as! SCNView scnView.scene = SCNScene() // Change the project settings to use Swift6 // Setting this closure will then cause a _dispatch_assert_queue_fail // EXC_BREAKPOINT error in the scenekit.renderingQueue.SCNView queue, // the only thing on the stack is: // "%sBlock was %sexpected to execute on queue [%s (%p)]" player.animation.animationDidStop = { (a: SCNAnimation, b: SCNAnimatable, c: Bool) in print("stopped") } scnView.scene?.rootNode.addAnimationPlayer(player, forKey: nil) player.play() } }
2
2
499
Aug ’24
SceneKit app randomly crashes with EXC_BAD_ACCESS in jet_context::set_fragment_texture
Every now and then my SceneKit game app crashes and I have no idea why. The SCNView has a overlaySKScene, so it might also be SpriteKit's fault. The stack trace is #0 0x0000000241c1470c in jet_context::set_fragment_texture(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, jet_texture*) () #27 0x000000010572fd40 in _pthread_wqthread () Does anyone have an idea where I could start debugging this, without being able to consistently reproduce it?
11
0
621
Sep ’24
Particle burst with exact amount of particles
Hello dear forum, I need to emit an exact amount of particles from a SCNParticleSystem in a burst (this is for an UI effect). This worked for me perfectly in the scene editor by setting the birthrate to the amount and emission duration to 0. Sadly when I either load such a particle system from a scene or creating it by code, the emitted particles are sometimes one less, or one more. The first time I run it in the simulator, it seems to work fine but then amount of particles varies as described. video: https://youtube.com/shorts/MRzqWBy2ypA?feature=share Does anybody know how to make this predictable? Thanks so much in advance, Seb
1
0
435
Sep ’24
Can SceneKit be used with Swift 6 Concurrency ?
I am trying to port SceneKit projects to Swift 6, and I just can't figure out how that's possible. I even start thinking SceneKit and Swift 6 concurrency just don't match together, and SceneKit projects should - hopefully for the time being only - stick to Swift 5. The SCNSceneRendererDelegate methods are called in the SceneKit Thread. If the delegate is a ViewController: class GameViewController: UIViewController { let aNode = SCNNode() func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) { aNode.position.x = 10 } } Then the compiler generates the error "Main actor-isolated instance method 'renderer(_:updateAtTime:)' cannot be used to satisfy nonisolated protocol requirement" Which is fully understandable. The compiler even tells you those methods can't be used for protocol conformance, unless: Conformance is declare as @preconcurrency SCNSceneRendererDelegate like this: class GameViewController: UIViewController, @preconcurrency SCNSceneRendererDelegate { But that just delays the check to runtime, and therefore, crash in the SceneKit Thread happens at runtime... Again, fully understandable. or the delegate method is declared nonisolated like this: nonisolated func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) { aNode.position.x = 10 } Which generates the compiler error: "Main actor-isolated property 'position' can not be mutated from a nonisolated context". Again fully understandable. If the delegate is not a ViewController but a nonisolated class, we also have the problem that SCNNode can't be used. Nearly 100% of the SCNSceneRendererDelegate I've seen do use SCNNode or similar MainActor bound types, because they are meant for that. So, where am I wrong ? What is the solution to use SceneKit SCNSceneRendererDelegate methods with full Swift 6 compilation ? Is that even possible for now ?
5
0
411
Oct ’24
No smooth animation transition in IOS 18
Hi, I’m facing an issue with SceneKit. I’m developing a 3D mobile game. I have a character 3D model and several skeletal animations CAAnimation. I import both the model and the animations from Maya in *.dae format. The character’s animations play continuously one after the other, with each new animation being triggered randomly. The transition between animations makes smoothly by setting the fadeInDuration and fadeOutDuration properties. Here’s an example of the code: import UIKit import QuartzCore import SceneKit //---------------------- class TestAnimationController: UIViewController { var bodyNode: SCNNode? override func viewDidLoad() { super.viewDidLoad() let scnView = SCNView(frame: self.view.bounds) scnView.backgroundColor = .black // Set your desired background color scnView.autoresizingMask = [.flexibleWidth, .flexibleHeight] let scene = SCNScene(named: "art.scnassets/scene/Base_room/ROOM5.scn")! bodyNode = collada2SCNNode(filepath: "art.scnassets/female/girl_body_races.dae")! bodyNode?.renderingOrder = 10 scene.rootNode.addChildNode(bodyNode!) playIdleAnimation() scnView.scene = scene // Assign the scene to the SCNView self.view.addSubview(scnView) // Add the SCNView to your main view) } func collada2SCNNode(filepath:String) -> SCNNode? { if let scene = SCNScene(named: filepath) { let node = scene.rootNode.childNodes[0] return node } else { return nil } } func playIdleAnimation() { let array = [ "art.scnassets/female/animations/idle/girl_idle_4.dae", "art.scnassets/female/animations/idle/girl_idle1.dae", "art.scnassets/female/animations/idle/girl_idle2.dae", "art.scnassets/female/animations/idle/Girl_idle3.dae", ] let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")! self.setAnimationAdd( fadeInDuration: 1.0, fadeOutDuration: 1.0, keyTime: 0.99, animation, isLooped: false ) { [weak self] in guard let self = self else { return } try? self.playBoringAnimations() } } func playBoringAnimations() { let array = [ "art.scnassets/female/animations/boring/girl_boring1.dae", "art.scnassets/female/animations/boring/girl_boring2.dae", "art.scnassets/female/animations/boring/girl_boring3.dae", "art.scnassets/female/animations/boring/girl_boring4.dae", "art.scnassets/female/animations/boring/girl_boring5.dae", "art.scnassets/female/animations/boring/girl_boring6.dae", "art.scnassets/female/animations/boring/girl_boring8.dae" ] let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")! self.setAnimationAdd( fadeInDuration: 1.0, fadeOutDuration: 1.0, keyTime: 0.99, animation, isLooped: false ) { [weak self] in guard let self = self else { return } try? self.playIdleAnimation() } } func setAnimationAdd(fadeInDuration : CGFloat, fadeOutDuration : CGFloat, keyTime : CGFloat, _ animation: CAAnimation, isLooped: Bool, completion: (() -> Void)?) { animation.fadeInDuration = fadeInDuration animation.fadeOutDuration = fadeOutDuration if !isLooped { animation.repeatCount = 1 } else { animation.repeatCount = Float.greatestFiniteMagnitude } animation.animationEvents = [ SCNAnimationEvent(keyTime: keyTime, block: { _, _, _ in completion?() }) ] bodyNode?.addAnimation(animation, forKey: "avatarAnimation") } } Everything worked perfectly until I updated to iOS 18. On a physical device, the animations now transition abruptly without the smooth blending that was present in earlier iOS versions. The switch between them is very noticeable, as if the fadeInDuration and fadeOutDuration parameters are being ignored. However, in the iOS 18 simulator, the animations still transition smoothly as before. Here two example videos - IOS 17.5 and IOS 18 https://youtube.com/shorts/jzoMRF4skAQ - IOS 17,5 smooth https://youtube.com/shorts/VJXrZzO9wl0 - IOS 18 not smooth
0
1
226
Oct ’24
SceneKit Animations Transition Abruptly on iOS 18 Device, but Smooth in Simulator
Hi Friends! I’m facing an issue with SceneKit. I’m developing a 3D mobile game. I have a character 3D model and several skeletal animations CAAnimation. I import both the model and the animations from Maya in *.dae format. The character’s animations play continuously one after the other, with each new animation being triggered randomly. The transition between animations makes smoothly by setting the fadeInDuration and fadeOutDuration properties. Here’s an example of the code: import UIKit import QuartzCore import SceneKit class TestAnimationController: UIViewController { var bodyNode: SCNNode? override func viewDidLoad() { super.viewDidLoad() let scnView = SCNView(frame: self.view.bounds) scnView.backgroundColor = .black // Set your desired background color scnView.autoresizingMask = [.flexibleWidth, .flexibleHeight] let scene = SCNScene(named: "art.scnassets/scene/Base_room/ROOM5.scn")! bodyNode = collada2SCNNode(filepath: "art.scnassets/female/girl_body_races.dae")! bodyNode?.renderingOrder = 10 scene.rootNode.addChildNode(bodyNode!) playIdleAnimation() scnView.scene = scene // Assign the scene to the SCNView self.view.addSubview(scnView) // Add the SCNView to your main view) } func collada2SCNNode(filepath:String) -> SCNNode? { if let scene = SCNScene(named: filepath) { let node = scene.rootNode.childNodes[0] return node } else { return nil } } func playIdleAnimation() { let array = [ "art.scnassets/female/animations/idle/girl_idle_4.dae", "art.scnassets/female/animations/idle/girl_idle1.dae", "art.scnassets/female/animations/idle/girl_idle2.dae", "art.scnassets/female/animations/idle/Girl_idle3.dae", ] let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")! self.setAnimationAdd( fadeInDuration: 1.0, fadeOutDuration: 1.0, keyTime: 0.99, animation, isLooped: false ) { [weak self] in guard let self = self else { return } try? self.playBoringAnimations() } } func playBoringAnimations() { let array = [ "art.scnassets/female/animations/boring/girl_boring1.dae", "art.scnassets/female/animations/boring/girl_boring2.dae", "art.scnassets/female/animations/boring/girl_boring3.dae", "art.scnassets/female/animations/boring/girl_boring4.dae", "art.scnassets/female/animations/boring/girl_boring5.dae", "art.scnassets/female/animations/boring/girl_boring6.dae", "art.scnassets/female/animations/boring/girl_boring8.dae" ] let animation = CAAnimation.animationWithSceneNamed(array.randomElement() ?? "")! self.setAnimationAdd( fadeInDuration: 1.0, fadeOutDuration: 1.0, keyTime: 0.99, animation, isLooped: false ) { [weak self] in guard let self = self else { return } try? self.playIdleAnimation() } } func setAnimationAdd(fadeInDuration : CGFloat, fadeOutDuration : CGFloat, keyTime : CGFloat, _ animation: CAAnimation, isLooped: Bool, completion: (() -> Void)?) { animation.fadeInDuration = fadeInDuration animation.fadeOutDuration = fadeOutDuration if !isLooped { animation.repeatCount = 1 } else { animation.repeatCount = Float.greatestFiniteMagnitude } animation.animationEvents = [ SCNAnimationEvent(keyTime: keyTime, block: { _, _, _ in completion?() }) ] bodyNode?.addAnimation(animation, forKey: "avatarAnimation") } } Everything worked perfectly until I updated to iOS 18. On a physical device, the animations now transition abruptly without the smooth blending that was present in earlier iOS versions. The switch between them is very noticeable, as if the fadeInDuration and fadeOutDuration parameters are being ignored. However, in the iOS 18 simulator, the animations still transition smoothly as before. Here two example videos - IOS 17.5 and IOS 18 https://youtube.com/shorts/jzoMRF4skAQ - IOS 17,5 smooth https://youtube.com/shorts/VJXrZzO9wl0 - IOS 18 not smooth I try this code in IOS 17.5, everything works fine Does anyone have any ideas on how to fix this issue?
0
0
271
Oct ’24
SKAction.removeFromParent() causes crash when run in SCNView.overlaySKScene on iOS
Even when the action is run on the main thread, the following code causes a crash on iOS, but not on macOS. The game launches with a simple yellow rectangle, and when it finishes fading out and should be removed from the overlay scene, the app crashes. The code can be pasted into the file GameController.swift of Xcode's default project for Multiplatform macOS and iOS game. import SceneKit import SpriteKit @MainActor class GameController: NSObject { let scene: SCNScene let sceneRenderer: SCNSceneRenderer init(sceneRenderer renderer: SCNSceneRenderer) { sceneRenderer = renderer scene = SCNScene(named: "Art.scnassets/ship.scn")! super.init() sceneRenderer.scene = scene renderer.overlaySKScene = SKScene(size: CGSize(width: 500, height: 500)) DispatchQueue.main.async { let node = SKShapeNode(rect: CGRect(x: 100, y: 100, width: 100, height: 100)) node.fillColor = .yellow node.run(.sequence([ .fadeOut(withDuration: 1), .removeFromParent() ])) renderer.overlaySKScene!.addChild(node) } } } The Xcode console shows this stacktrace: *** Assertion failure in -[UIApplication _performAfterCATransactionCommitsWithLegacyRunloopObserverBasedTiming:block:], UIApplication.m:3246 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Call must be made on main thread' *** First throw call stack: ( 0 CoreFoundation 0x00000001804ae0f8 __exceptionPreprocess + 172 1 libobjc.A.dylib 0x0000000180087db4 objc_exception_throw + 56 2 Foundation 0x0000000180d17058 _userInfoForFileAndLine + 0 3 UIKitCore 0x00000001853cf678 -[UIApplication _performAfterCATransactionCommitsWithLegacyRunloopObserverBasedTiming:block:] + 376 4 UIKitCore 0x000000018553f7a0 -[_UIFocusUpdateThrottle scheduleProgrammaticFocusUpdate] + 300 5 UIKitCore 0x0000000184e2e22c -[UIFocusSystem _requestFocusUpdate:] + 548 6 UIKitCore 0x0000000184e2dfa4 -[UIFocusSystem requestFocusUpdateToEnvironment:] + 76 7 UIKitCore 0x0000000184e2e864 -[UIFocusSystem _focusEnvironmentWillDisappear:] + 408 8 SpriteKit 0x00000001a3d472f4 _ZL12_removeChildP6SKNodeS0_P7SKScene + 240 9 SpriteKit 0x00000001a3d473b0 -[SKNode removeChild:] + 80 10 SpriteKit 0x00000001a3d466b8 -[SKNode removeFromParent] + 128 11 SpriteKit 0x00000001a3d1678c -[SKRemove updateWithTarget:forTime:] + 64 12 SpriteKit 0x00000001a3d1b740 _ZN11SKCSequence27cpp_updateWithTargetForTimeEP7SKCNoded + 84 13 SpriteKit 0x00000001a3d20e3c _ZN7SKCNode6updateEdf + 156 14 SpriteKit 0x00000001a3d20f20 _ZN7SKCNode6updateEdf + 384 15 SpriteKit 0x00000001a3d26fb8 -[SKScene _update:] + 464 16 SpriteKit 0x00000001a3cf3168 -[SKSCNRenderer _update:] + 80 17 SceneKit 0x000000019c932bf0 -[SCNMTLRenderContext renderSKSceneWithRenderer:overlay:atTime:] + 60 18 SceneKit 0x000000019c9ebd98 -[SCNRenderer _drawOverlaySceneAtTime:] + 204 19 SceneKit 0x000000019cb1a1c0 _ZN3C3D11OverlayPass7executeERKNS_10RenderArgsE + 60 20 SceneKit 0x000000019c8e05ec _ZN3C3D13__renderSliceEPNS_11RenderGraphEPNS_10RenderPassERtRKNS0_9GraphNodeERPNS0_5StageENS_10RenderArgsEbRPU27objcproto16MTLCommandBuffer11objc_object + 2660 21 SceneKit 0x000000019c8e18ac _ZN3C3D11RenderGraph7executeEv + 3808 22 SceneKit 0x000000019c9ed26c -[SCNRenderer _renderSceneWithEngineContext:sceneTime:] + 756 23 SceneKit 0x000000019c9ed544 -[SCNRenderer _drawSceneWithNewRenderer:] + 208 24 SceneKit 0x000000019c9ed9fc -[SCNRenderer _drawScene:] + 40 25 SceneKit 0x000000019c9edce4 -[SCNRenderer _drawAtTime:] + 500 26 SceneKit 0x000000019ca87950 -[SCNView _drawAtTime:] + 368 27 SceneKit 0x000000019c943b74 __83-[NSObject(SCN_DisplayLinkExtensions) SCN_setupDisplayLinkWithQueue:screen:policy:]_block_invoke + 44 28 SceneKit 0x000000019ca50600 -[SCNDisplayLink _displayLinkCallbackReturningImmediately] + 132 29 libdispatch.dylib 0x000000010239173c _dispatch_client_callout + 16 30 libdispatch.dylib 0x0000000102394c14 _dispatch_continuation_pop + 756 31 libdispatch.dylib 0x00000001023aa4e0 _dispatch_source_invoke + 1736 32 libdispatch.dylib 0x00000001023997f0 _dispatch_lane_serial_drain + 340 33 libdispatch.dylib 0x000000010239a774 _dispatch_lane_invoke + 420 34 libdispatch.dylib 0x00000001023a71a8 _dispatch_root_queue_drain_deferred_wlh + 324 35 libdispatch.dylib 0x00000001023a6604 _dispatch_workloop_worker_thread + 488 36 libsystem_pthread.dylib 0x000000010242bb74 _pthread_wqthread + 284 37 libsystem_pthread.dylib 0x000000010242a934 start_wqthread + 8 ) libc++abi: terminating due to uncaught exception of type NSException Am I doing something wrong?
6
0
304
Oct ’24
SceneKit custom physics fields using wrong position?
In the simplest case I can come up with, I create a scene (either fully or partially in code) with a single dynamic body, located slightly away from the origin. I give the body a charge as well as adding an electric field to the node. Body does nothing (as to be expected, since it's the source of the field). However if I replace that field with a custom field (does nothing except reports back the passed in position value) the position shown is the location of the body in the local space of its parent (in this case, the root node) rather than the node the field is attached to (i.e. itself). I've attached the code customising the SwiftUI app template. Hopefully someone can tell me what I'm doing wrong? ContentView customisation… struct ContentView: View { var body: some View { SceneView(scene: ElectricScene(), options: [.allowsCameraControl, .autoenablesDefaultLighting]) } } And the code to create the scene… import Foundation import SceneKit class ElectricScene: SCNScene { override init() { super.init() physicsWorld.gravity = SCNVector3(0, 0, 0) let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(0, 0, 10) rootNode.addChildNode(cameraNode) let ballNode = SCNNode(geometry: SCNSphere(radius: 0.5)) ballNode.position = SCNVector3(2, 0, 0) ballNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil) ballNode.physicsBody?.charge = -1 rootNode.addChildNode(ballNode) // ballNode.physicsField = SCNPhysicsField.electric() ballNode.physicsField = SCNPhysicsField .customField {position, _, _, _, _ in print(position) return SCNVector3Zero } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } This (repeatedly) prints out the following… SCNVector3(x: 2.0, y: 0.0, z: 0.0) …which is the position of the node relative to the root node, rather than relative to the source of the field (itself).
1
0
258
Oct ’24
SceneKit app seriously hangs when run in fullscreen
I've been running my SceneKit game for many weeks in Xcode without performance issues. The game itself is finished, so I thought I could go on with publishing it on the App Store, but when archiving it in Xcode and running the archived app, I noticed that it seriously hangs. The hangs only seem to happen when I run the game in fullscreen mode. I tried disabling game mode, but the hangs still happen. Only when I run in windowed mode the game runs smoothly. Instruments confirms that there are many serious hangs, but it also reports that CPU usage is quite low during those hangs, on average about 15%. From what I know, hangs happen when the main thread is busy, but how can that be when CPU usage is so low, and why does it only happen in fullscreen mode for release builds?
2
0
281
Oct ’24
SK3DNode hitTest not working in SpriteKit/SceneKit
I have this minimum repro code: import SpriteKit import GameplayKit class MyGameScene3D: SCNScene { weak var node3D: MyNode3D! override init() { super.init() background.contents = UIColor.green let playground = SCNNode() playground.boundingBox = ( min: SCNVector3(x: 0, y: 0, z: 0), max: SCNVector3(x: 10, y: 10, z: 10)) let box = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)) box.position = SCNVector3(x: 5, y: 5, z: 5) playground.addChildNode(box) playground.position = SCNVector3(x: 0, y: 0, z: 0) rootNode.addChildNode(playground) let light = SCNLight() light.type = .ambient let lightNode = SCNNode() lightNode.light = light rootNode.addChildNode(lightNode) let camera = SCNCamera() let cameraNode = SCNNode() cameraNode.camera = camera cameraNode.eulerAngles = SCNVector3(x: -3.14/2, y: 0, z: 0) cameraNode.position = SCNVector3(x: 5, y: 11, z: 5) rootNode.addChildNode(cameraNode) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func handleTouchBegan(_ location: CGPoint) { let res = node3D.hitTest(location) print(res) } } class MyNode3D: SK3DNode { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let touch = touches.first! let scene = scnScene as! MyGameScene3D let location = touch.location(in: self) print(location) scene.handleTouchBegan(location) } } class GameScene: SKScene { init() { super.init(size: CGSize(width: 500, height: 1000)) self.backgroundColor = .red let node3D = MyNode3D() let scene3D = MyGameScene3D() node3D.scnScene = scene3D scene3D.node3D = node3D node3D.isUserInteractionEnabled = true node3D.viewportSize = CGSize(width: 100, height: 200) node3D.position = CGPoint(x: 50, y: 100) addChild(node3D) let up = SKSpriteNode(color: .blue, size: CGSize(width: 500, height: 10)) up.anchorPoint = CGPoint(x: 0, y:0) up.position = CGPoint(x:0, y:200) addChild(up) let right = SKSpriteNode(color: .gray, size: CGSize(width: 10, height: 500)) right.anchorPoint = CGPoint(x:0,y: 0) right.position = CGPoint(x:100, y:0) addChild(right) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } Basically, I have a SK3DNode of size 100x200, positioned at lower left corner of the screen (see screenshot below). Then in this SK3DNode, I have a SCNScene, where I put a 10x10x10 Playground node at position (0, 0, 0). Then I put a camera node right at the top of the Playground at position (5, 11, 5), and the camera looks down along the -y axis, with euler angle = (-90, 0, 0). Then in this Playground, I put a small box of size 1x1x1, at the center of the Playground at (5, 5, 5). The 2 long bars (gray & blue) are just there to indicate the boundary of the SK3DNode. The result rendering is correct (see screenshot below). However, I can't get the hit test working. I tap on the center 1x1x1 box on screen, get the right coordinate printed out, but the hit test result is empty. I want to be get the center 1x1x1 box when hitting there. How can I do so? Update: I tried to loop through all the pixels from -2000 to 2000, and still no hit: func handleTouchBegan(_ location: CGPoint) { for x in -2000...2000 { print("handling x: \(x)") for y in -2000...2000 { let res = node3D.hitTest(location) if !res.isEmpty { print("\(x), \(y), \(res)") } } } print("Done") }
1
0
205
3w
SKTexture used for SceneKit object is rendered too bright
I would like to preload and use some images for both SpriteKit and SceneKit models (my game uses SceneKit with a SpriteKit overlay), and as far as I can see the only efficient way would be to create and preload SKTexture objects which can be supplied to SKSpriteNode(texture:) and SCNMaterial.diffuse.contents. The problem is that SKTexture are rendered too bright in SceneKit, for some unknown reason. Here a comparison between rendering an image (from URL) and a SKTexture: And the code that produces it: let url = Bundle.main.url(forResource: "art.scnassets/texture.png", withExtension: nil)! let plane1 = SCNPlane(width: 10, height: 10) plane1.firstMaterial!.diffuse.contents = url.path let node1 = SCNNode(geometry: plane1) node1.position.x = -5 scene.rootNode.addChildNode(node1) let plane2 = SCNPlane(width: 10, height: 10) plane2.firstMaterial!.diffuse.contents = SKTexture(image: NSImage(byReferencing: url)) let node2 = SCNNode(geometry: plane2) node2.position.x = 5 scene.rootNode.addChildNode(node2) This issue was already mentioned in this other post, but since I wasn't notified of the reply from Quinn asking about the feedback number I created at the time, it didn't make any progress.
5
0
262
3w
SceneView camera viewpoint restoration
SceneKit SCNScene MacOS 15.1 Xcode 16.0 SceneView(scene: , options:[autoenablesDefaultLighting, allowsCameraContol] there is: .rootNode.cameraNode .rootNode.camera .rootNode.nestedChildNodes each with its own animation when the object is animated and dragged by mouse to change the view point, I can't return the view to the previous view. I have reinstated a clone of the original cameraNode, positions of all childNodes, removed and re-activated all animations... in vain. I have also cloned, removed and replaced .rootNode.camera, in vain. The documentation states the camera is "attached" to an SCNNode but does not say how. I make no declaration to associate .rootNode.cameraNode to .rootNode.camera yet if either is absent there is no scene to view. What am I missing? Thanks
1
0
167
2w
SCNNode from MDLMesh not rendered
I am writing an app to create 3D objects with curved surfaces such as a metal cabinet knob using SceneKit and Model I/O. I want the surfaces to be smooth so that edges between adjacent polygon faces are not visible. According to the documentation for MDLMesh.addNormals(withAttributeNamed: creaseThreshold:), a positive creaseThreshold value lower than 1.0 will interpolate sharper angles between faces into smooth surfaces. I have not been able to get this to work, and I need help with it. The lines of code where the problem occurs are shown here. let mesh = MDLMesh(scnGeometry: surfaceGeometry) // mesh.addNormals(withAttributeNamed: "MDLVertexAttributeNormal", creaseThreshold: 0.9) surfaceGeometry = SCNGeometry(mdlMesh: mesh) When the code is executed with middle line commented out, the knob object is rendered as shown in the screenshot. When that line is not commented out, mesh is altered and the SCNNode for the knob is created with no errors, but the node is not rendered. The questions I have are: (1) What changes do I need the make to the code so that the node will be rendered with a smooth surface?, and (2) what is the recommended way of smoothing a curved surface so that edges between faces are not visible? The full code for the function and a screenshot of the faceted knob object are attached. ![]("https://developer.apple.com/forums/content/attachment/a17feca7-ed6f-440c-add6-760a1cbf8778" "title=Screenshot cabinet knob with faceted surface.png;width=790;height=568") code-block func cabinetKnob() -> SCNNode { let controlPoints: [(x: Float, y: Float)] = [ (0.728,-0.237), (0.176,-0.06), (0.202,0.475), (0.989,0.842), (-0.066,1.093), (-0.726,0.787) ] let pairs = bsplinePath(controlPoints) var knobProfile = [SCNVector3]() for (x,y) in pairs { knobProfile += [ SCNVector3(x: CGFloat(x), y: CGFloat(y), z: 0)] } let nProfiles = 64 // create knob by rotating knobProfile about y-axis let aIncrement: CGFloat = 2 * CGFloat.pi / CGFloat(nProfiles) // ~6 degrees var angle: CGFloat = 0 var knobVertices = knobProfile.map( { $0 } ) angle = 0 for _ in 1...nProfiles { angle += aIncrement // rotate knobProfile about y-axis knobVertices += knobProfile.map( { $0.rotate(about: .y, by: angle) } ) } let source = SCNGeometrySource(vertices: knobVertices) var indices = [[UInt16]]() var i: UInt16 = 0 var j: UInt16 = UInt16(knobProfile.count) // 1st vertex of next profile for k in 0...nProfiles { var stripIndices = [UInt16]() if k == nProfiles { j = 0 } for _ in 0...knobProfile.count-1 { stripIndices += [i, j] i += 1; j += 1 } indices += [stripIndices] } let elements: [SCNGeometryElement] = indices.map( { SCNGeometryElement(indices: $0, primitiveType: .triangleStrip) } ) var surfaceGeometry = SCNGeometry(sources: [source], elements: elements) let mesh = MDLMesh(scnGeometry: surfaceGeometry) // mesh.addNormals(withAttributeNamed: "MDLVertexAttributeNormal", creaseThreshold: 0.9) surfaceGeometry = SCNGeometry(mdlMesh: mesh) let aluminum = SCNMaterial() aluminum.lightingModel = SCNMaterial.LightingModel.physicallyBased aluminum.diffuse.contents = NSColor(srgbRed: 0.95, green: 0.95, blue: 0.95, alpha: 1.0) aluminum.roughness.contents = 0.2 aluminum.metalness.contents = 0.9 aluminum.isDoubleSided = true surfaceGeometry.materials = [ aluminum ] let node = SCNNode(geometry: surfaceGeometry) return node }
4
0
224
2w
ARSCNView ignores output of SCNTechnique (sometimes)
I am using SCNTechnique in combination with ARSCNView. The technique is doing so minor post-processing. I have written several filter variant for this post-processing, but I'm facing an issue when with one of the filters/fragment shaders, SCNTechnique discards my output and just presents the plain camera feed on screen instead. This is clearly visible in the Metal pipeline, using the GPU frame debugger. Let me stress that my setup works for 90% of my filters, but not this one and I want to know why. iOS 18.1, iPhone 13 Mini. Xcode 16.1. Encoder 0 & 1 are injected by the system. Render encoder 2 & 3 correspond to my SCNTechnique's render passes: one to manipulate pixel data (darken it in this case) and another to BLIT it back to the main texture. I know the separate buffer is not strictly for this particular operation, but it shouldn't matter. Note that the issue occurs in encoder 4 (not mine but ARKit's). In Render Encoder 4, scn_postprocess_AR_fragment handle my texture (#0, ending in f980) and another from the camera feed (Texture 2). I know this pass is typically used for grain because that's what it used to do before I disabled grain on ARSCNView (+ the buffer still contains grain paramaters). I have other post-processing filters that work just fine. By what magic is ARKit determining to use Texture 2 instead of my Texture 0? Sure, I could keep digging into the minute differences between my shaders to find out which LoC affects how some ARKit shader down the line operates, but it's awfully opaque so far.
0
0
144
1w
Why does the planeNode in SceneKit flicker when using class property instead of a local variable?
I am working on a SceneKit project where I use a CAShapeLayer as the content for SCNMaterial's diffuse.contents to display a progress bar. Here's my initial code: func setupProgressWithCAShapeLayer() { let progressLayer = createProgressLayer() progressBarPlane?.firstMaterial?.diffuse.contents = progressLayer DispatchQueue.main.async { var progress: CGFloat = 0.0 Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in progress += 0.01 if progress > 1.0 { progress = 0.0 } progressLayer.strokeEnd = progress // Update progress } } } // MARK: - ARSCNViewDelegate func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { progressBarPlane = SCNPlane(width: 0.2, height: 0.2) setupProgressWithCAShapeLayer() let planeNode = SCNNode(geometry: progressBarPlane) planeNode.position = SCNVector3(x: 0, y: 0.2, z: 0) node.addChildNode(planeNode) } This works fine, and the progress bar updates smoothly. However, when I change the code to use a class property (self.progressLayer) instead of a local variable, the rendering starts flickering on the screen: func setupProgressWithCAShapeLayer() { self.progressLayer = createProgressLayer() progressBarPlane?.firstMaterial?.diffuse.contents = progressLayer DispatchQueue.main.async { [weak self] in var progress: CGFloat = 0.0 Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in progress += 0.01 if progress > 1.0 { progress = 0.0 } self?.progressLayer?.strokeEnd = progress // Update progress } } } After this change, the progressBarPlane in SceneKit starts flickering while being rendered on the screen. My Question: Why does switching from a local variable (progressLayer) to a class property (self.progressLayer) cause the flickering issue in SceneKit rendering?
0
0
75
6d