So I am trying to create a certain amount of spheres in a SceneKit scene based on the number of objects in a list. So I think I would put an addChild in a for loop, but how would I assign them all to the same type of model? Especially because sometimes the list will be empty, so the model would not need to show up at all.
SceneKit
RSS for tagCreate 3D games and add 3D content to apps using high-level scene descriptions using SceneKit.
Post
Replies
Boosts
Views
Activity
I am trying to make an app that uses SceneKit to display some 3D models, but started it up from the regular app format. When I try to build I get this error: shell script build rule for "/somePath/Scene.scn' must declare at least one output file.
The .scn is being provided to a view, is that not an output? Or is there some formatting issue that I need to solve.
How to ensure current SKScene has fully loaded before engaging it with the GamePad Controller?
MAJOR REWRITE FOR THE SAKE OF HOPEFULLY (?) INCREASED CLARITY
The problem is this = when stopping sound is involved when I do switch SKScenes, if I press the buttons of the GamePad Controller (which cycle thru these other SKScenes) too fast, the movement of the Game Pieces fails to resume when I return to the Game Scene after the above cycling.
This problem occurs only with the os(tvOS) version, but not with the iPad version. And the reason for this distinction is that each SKScene for the iPad has to fully load due to the fact that the button I press to switch SKScenes is at the top-left corner of the iPad -- so, by definition, by the time I have access to this button, the current SKScene has fully loaded.
By definition, there is no such button for os(iOS).
Given this button’s absence, I need the Swift version of jQuery’s
$(document).ready (function() {.
Any help will be appreciated to the rafters ...
We have requirement adding usdz file to UIView and showing the it’s content and archive the data and save to file. When user open the file, we need to unarchive that usdz content and binding with UIView and showing it to user. Initially, we created SCNScene object passing usdz file url like below.
do {
usdzScene = try SCNScene(url: usdzUrl)
} catch let error as NSError {
print(error)
}
Since SCNScene support to NSSecureCoding protocol , we directly archive that object and save it file and load back it from file and created SCNScene object using NSKeyedUnarchiver process.
But for some files, we realised high memory consumption while archiving the SCNScene object using below line.
func encode(with coder: NSCoder) {
coder.encode(self.scnScene, forKey: "scnScene")
}
File referene link : toy_drummer_idle.usdz
When we analyse apple documentation (check discussion section) , it said, scn file extension is the fastest format for processing than the usdz.
So we used SCNSecne write to feature for creating scn file from given usdz file.
After that, When we do the archive SCNScene object that was created by sun file url, the archive process is more faster and it will not take high memory as well. It is really faster previous case now.
But unfortunately, SCNScene write method will take lot of time for this conversion and memory meter is also going high and it will be caused to app crash as well.
I check the output file size as well. The given usdz file size is 18MB and generated scn file size is 483 MB. But SCNScene archive process is so fast.
Please, analyse this case and please, provide some guideline how we can optimise this behaviour. I really appreciate your feedback.
Full Code:
import UIKit
import SceneKit
class ViewController: UIViewController {
var scnView: SCNView?
var usdzScene: SCNScene?
var scnScene: SCNScene?
lazy var exportButton: UIButton = {
let btn = UIButton(type: UIButton.ButtonType.system)
btn.tag = 1
btn.backgroundColor = UIColor.blue
btn.addTarget(self, action: #selector(buttonPressed(_:)), for: .touchUpInside)
btn.setTitle("USDZ to SCN", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.layer.borderColor = UIColor.gray.cgColor
btn.titleLabel?.font = .systemFont(ofSize: 20)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
func deleteTempDirectory(directoryName: String) {
let tempDirectoryUrl = URL(fileURLWithPath: NSTemporaryDirectory())
let tempDirectory = tempDirectoryUrl.appendingPathComponent(directoryName, isDirectory: true)
if FileManager.default.fileExists(atPath: URL(string: tempDirectory.absoluteString)!.path) {
do{
try FileManager.default.removeItem(at: tempDirectory)
}
catch let error as NSError {
print(error)
}
}
}
func createTempDirectory(directoryName: String) -> URL? {
let tempDirectoryUrl = URL(fileURLWithPath: NSTemporaryDirectory())
let toBeCreatedDirectoryUrl = tempDirectoryUrl.appendingPathComponent(directoryName, isDirectory: true)
if !FileManager.default.fileExists(atPath: URL(string: toBeCreatedDirectoryUrl.absoluteString)!.path) {
do{
try FileManager.default.createDirectory(at: toBeCreatedDirectoryUrl, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError {
print(error)
return nil
}
}
return toBeCreatedDirectoryUrl
}
@IBAction func buttonPressed(_ sender: UIButton){
let scnFolderName = "SCN"
let scnFileName = "3D"
deleteTempDirectory(directoryName: scnFolderName)
guard let scnDirectoryUrl = createTempDirectory(directoryName: scnFolderName) else {return}
let scnFileUrl = scnDirectoryUrl.appendingPathComponent(scnFileName).appendingPathExtension("scn")
guard let usdzScene else {return}
let result = usdzScene.write(to: scnFileUrl, options: nil, delegate: nil, progressHandler: nil)
if (result) {
print("exporting process is success.")
} else {
print("exporting process is failed.")
}
}
override func viewDidLoad() {
super.viewDidLoad()
let usdzUrl: URL? = Bundle.main.url(forResource: "toy_drummer_idle", withExtension: "usdz")
guard let usdzUrl else {return}
do {
usdzScene = try SCNScene(url: usdzUrl)
} catch let error as NSError {
print(error)
}
guard let usdzScene else {return}
scnView = SCNView(frame: .zero)
guard let scnView else {return}
scnView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(scnView)
self.view.addSubview(exportButton)
NSLayoutConstraint.activate([
scnView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
scnView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
scnView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scnView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30),
exportButton.widthAnchor.constraint(equalToConstant: 200),
exportButton.heightAnchor.constraint(equalToConstant: 40),
exportButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
exportButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {[weak self] in
guard let self else {return}
loadModel(scene: usdzScene)
}
}
func loadModel(scene: SCNScene){
guard let scnView else {return}
scnView.autoenablesDefaultLighting = true
scnView.scene = scene
scnView.allowsCameraControl = true
}
}
![]("https://developer.apple.com/forums/content/attachment/5ff0197b-7e0b-4efe-b295-cc205c73dc02" "title=Screenshot 2024-06-20 at 12.23.40.png;width=1712;height=1013")
Dear all,
I have several scenes, each with it’s own camera at different positions. The scenes will be loaded with transitions.
If I set the pointOfView in every Scene to the scene-camera, the transitions don’t work properly. The active scene View switches to the position of the camera of the scene, which is fading in.
If I comment the pointOfView out, the transitions works fine, but the following error message appears:
Error: camera node already has an authoring node - skip
Has someone an idea to fix this?
Many Thanks,
Ray
Hello!
I need to display a .scnz 3D model in an iOS app. I tried converting the file to a .scn file so I could use it with SCNScene but the file became corrupted.
I also tried to instantiate a SCNScene with the .scnz file but that didn't work either (crash when instantiating it).
After all this, what would be the best way to use this file knowing that converting it or exporting it to a .scn file with scntool hasn't worked?
Thank you!
Hello,
I am currently working on a project where I am creating a bookstore visualization with racks and shelves(Full immersive view). I have an array of names, each representing a USDZ object that is present in my working directory.
Here’s the enum I am trying to iterate over:
enum AssetName: String, Codable, Hashable, CaseIterable {
case book1 = "B1"
case book2 = "B2"
case book3 = "B3"
case book4 = "B4"
}
and the code for adding objects I wrote:
import SwiftUI
import RealityKit
struct LocalAssetRealityView: View {
let assetName: AssetName
var body: some View {
RealityView { content in
if let asset = try? await ModelEntity(named: assetName.rawValue) {
content.add(asset)
}
}
}
}
Now I get the error, when I try to add multiple objects on Button click:
Unable to present another Immersive Space when one is already requested or connected
please suggest any solutions. Also suggest if anything can be done to add positions for the objects as well programatically.
I'm working on a project in Xcode where I need to use a 3D model with multiple morph targets (shape keys in Blender) for animations. The model, specifically the Wolf3D_Head node, contains dozens of morph targets which are crucial for my project. Here's what I've done so far:
I verified the morph targets in Blender (I can see all the morph targets correctly when opening both the original .glb file and the converted .dae file in Blender).
Given that Xcode does not support .glb file format directly, I converted the model to .dae format, aiming to use it in my Xcode project. After importing the .dae file into Xcode, I noticed that Xcode does not show any morph targets for the Wolf3D_Head node or any other node in the model.
I've already attempted using tools like ColladaMorphAdjuster and another version by JakeHoldom to adjust the .dae file, hoping Xcode would recognize the morph targets, but it didn't resolve the issue.
My question is: How can I make Xcode recognize and display the morph targets present in the .dae file exported from Blender? Is there a specific process or tool that I need to use to ensure Xcode properly imports all the morph target information from a .dae file?
Tools tried: https://github.com/JonAllee/ColladaMorphAdjuster, https://github.com/JakeHoldom/ColladaMorphAdjuster
Thanks in advance!
Hi,
I'm an experienced developer on Apple platforms (having worked on iOS/tvOS projects for more than 10 years now). However, I've only worked on applications, or simple games which didn't require more than using UIKit or SwiftUI.
Now, I'd like to start a new project, recreating an old game on tvOS with 3D graphics. This is not a project I plan to release, only a simple personal challenge.
I'm torn with starting this project with either SceneKit, or Unity.
On one hand, I love Apple frameworks and tools, so I guess I could easily progress with SceneKit. Also, I don't know Unity very well, but even if it's a simple project, I've seen that there are several restrictions for free plans (no custom splash screen, etc).
On the other hand, I've read several threads (i.e. this one) making it look like that SceneKit isn't going anywhere, and clearly recommending Unity due to the fact that its documentation is way better, and the game more easily portable to other platforms.
Also, if I'm going to learn something new, maybe I could learn more with Unity (using a completely different platform, software and language) than I would with SceneKit and Swift stuff.
What's your opinion about this?
Thanks!
Hi,
My app has a volumetric window displaying some 3D content for the user. I would like the user to be able to control the color of the material using a color picker displayed below the model in the same window, but unfortunately neither ColorPicker nor Picker are functional in volumetric scenes.
Attempting to use them causes the app to crash with NSInternalInconsistencyException: Presentations are not permitted within volumetric window scenes.
This seems rather limiting. Is there a way either of these components can be utilized? I could build a different "control panel" window but it would not be attached to the model window and it would get confusing if user has multiple 3d windows open.
Thank you
I have a human-like rigged 3D model in a DAE file. I want to programmatically build a scene with several instances of this model in different poses.
I can extract the SCNSkinner and skeleton chain from the DAE file without problem.
I have discovered that to have different poses, I need to clone the skeleton chain, and clone the SCNSkinner as well, then modify the skeletons position. Works fine.
This is done this way:
// Read the skinner from the DAE file
let skinnerNode = daeScene.rootNode.childNode(withName: "toto-base", recursively: true)! // skinner
let skeletonNode1 = skinnerNode.skinner!.skeleton!
// Adding the skinner node as a child of the skeleton node makes it easier to
// 1) clone the whole thing
// 2) add the whole thing to a scene
skeletonNode1.addChildNode(skinnerNode)
// Clone first instance to have a second instance
var skeletonNode2 = skeletonNode1.clone()
// Position and move the first instance
skeletonNode1.position.x = -3
let skeletonNode1_rightLeg = skeletonNode1.childNode(withName: "RightLeg", recursively: true)!
skeletonNode1_rightLeg.eulerAngles.x = 0.6
scene.rootNode.addChildNode(skeletonNode1)
// Position and move the second instance
skeletonNode2.position.x = 3
let skeletonNode2_leftLeg = skeletonNode2.childNode(withName: "LeftLeg", recursively: true)!
skeletonNode2_leftLeg.eulerAngles.z = 1.3
scene.rootNode.addChildNode(skeletonNode2)
It seems the boneWeights and boneIndices sources are duplicated for each skinner, so if I have let's say 100 instances, I eat a huge amount of memory, for something that is constant.
Is there any way to avoid the duplication of the boneWeights and boneIndices ?
Hi
I would like to manipulate blend shapes of a usdz file in VisionOS, but I can not figure out how I could do that.
In SceneKit it was a piece of cake with SCNNode and morphs, but with Entity class I got stuck.
is it even possible? (I would be very disappointed if not)
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 ?
Hi everyone
I'm making a small private app for my one of my engineering projects, a part of this app shows a 3D model of what it looks like in real life based on a position value of a joint that needs to be updated in real time.
I was able import a USDZ of the exact model of the project, and make the proper nodes that can rotate, however I run into a problem where SceneKit takes forever to update the node, I'm not sure if my code just needs optimizing or SceneKit is just not the framework to use when needing things in a 3D model to be updated in real time
I've confirmed that the device receives the values in realtime, it is just SceneKit that doesn't update the model in time
I'm not very good at explaining things so I put as much detail as I possibly can and hope my problem is clear, I'm also pretty new to swift and iOS development.
Here is the code I'm using
import SwiftUI
import SceneKit
struct ModelView2: UIViewRepresentable {
@State private var eulerAngle: Float = 0.0
@StateObject var service = BluetoothService()
let sceneView = SCNView()
func makeUIView(context: Context) -> SCNView {
if let scene = SCNScene(named: "V4.usdz") {
sceneView.scene = scene
if let meshInstanceNode = scene.rootNode.childNode(withName: "MeshInstance", recursively: true),
let meshInstance1Node = scene.rootNode.childNode(withName: "MeshInstance_1", recursively: true),
let meshInstance562Node = scene.rootNode.childNode(withName: "MeshInstance_562", recursively: true) {
// Rotate mesh instance around its own axis
/*
meshInstance562Node.eulerAngles = SCNVector3(x: 0, y: -0.01745329 * service.posititonValue, z: 0)
*/
print(meshInstance562Node.eulerAngles)
}
}
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
return sceneView
}
func updateUIView(_ uiView: SCNView, context: Context) {
if let scene = SCNScene(named: "V4.usdz") {
sceneView.scene = scene
if let meshInstanceNode = scene.rootNode.childNode(withName: "MeshInstance", recursively: true),
let meshInstance1Node = scene.rootNode.childNode(withName: "MeshInstance_1", recursively: true),
let meshInstance562Node = scene.rootNode.childNode(withName: "MeshInstance_562", recursively: true) {
let boundingBox = meshInstance562Node.boundingBox
let pivot = SCNMatrix4MakeTranslation(
boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) / 2,
boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) / 2,
boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) / 2
)
meshInstance562Node.pivot = pivot
meshInstance562Node.addChildNode(meshInstanceNode)
meshInstance562Node.addChildNode(meshInstance1Node)
var original = SCNMatrix4Identity
original = SCNMatrix4Translate(original, 182.85785, 123.54999, 17.857864) // Translate along the Y-axis
meshInstance562Node.transform = original
print(service.posititonValue)
var buffer: Float = 0.0
if service.posititonValue != buffer {
meshInstance562Node.eulerAngles = SCNVector3(x: 0, y: -0.01745329 * service.posititonValue, z: 0)
buffer = service.posititonValue
}
}
}
}
func rotateNodeInPlace(node: SCNNode, duration: TimeInterval, angle: Float) {
// Create a rotation action
let rotationAction = SCNAction.rotateBy(x: 0, y: CGFloat(angle), z: 0, duration: duration)
// Repeat the rotation action indefinitely
// let repeatAction = SCNAction.repeatForever(rotationAction)
// Run the action on the node
node.runAction(rotationAction)
print(node.transform)
}
func rotate(node: SCNNode, angle: Float) {
node.eulerAngles = SCNVector3(x: 0, y: -0.01745329 * angle, z: 0)
}
}
#Preview {
ModelView2()
}
I found Scenekit crash on iOS 17 very frequently for all device on iOS 17
here is crash trace
Crashed: com.apple.scenekit.renderingQueue.SCNView0x15878c630
0 SceneKit 0x3eee4 C3DMatrix4x4GetAffineTransforms + 344
1 SceneKit 0x30208 C3DAdjustZRangeOfProjectionInfos + 140
2 SceneKit 0x2c0a90 C3DCullingContextSetupPointOfViewMatrices + 700
the attachment have the whole log
Crash Log
have anybody know how fo fix it
I am trying to use my animated model in XCode with SceneKit. I exported my model from Maya with Animation Data in .usd format, then converted it to .usdz with Reality Converter. When I open it in XCode viewer it is animated and everything is fine. However when I try to use it in my app it doesn't animate. On the other hand, when I try with the robot_walk_idle model from Apple's example models, it is animated. Maybe I am missing a option in export settings. Thanks for any help.
import SwiftUI
import SceneKit
struct ModelView: View {
var body: some View{
VStack{
SceneView(scene: SCNScene(named: "robot_walk_idle.usdz"))
}
}
}
Will visionOS support SceneKit?
Does anyone have a working example on how to play OGG files with swift?
I've been trying for over a year now. I was able to wrap the C Vorbis library in swift. I then used it to parse an OGG file successfully. Then I was required to use Obj-C\++ to fill the PCM because this method seems to only be available in C\++ and that part hangs my app for a good 40 seconds to several minutes depending on the audio file, it then plays for about 2 seconds and then crashes.
I can't get the examples on the Vorbis site to work in objective-c and i tried every example on github I could find (most of which are for iOS - I want to play the files on mac)
I also tried using Cricket Audio framework below.
https://github.com/sjmerel/ck
It has a swift example and it can play their proprietary soundbank format but it is also supposed to play OGG and it just doesn't do anything when trying to play OGG as you can see in the posted issue
https://github.com/sjmerel/ck/issues/3
Right now I believe every player that can play OGGs on mac is written in Objective-C or C++.
Anyway, any help/advice is appreciated. OGG format is very prevalent in the gaming community. I could use unity, which I believe plays oggs through the mono framework but I really really want to stay in swift.