I have a scene built up in RealityComposerPro, in which I've added a ParticleEmitter with isEmitting set to False and 'Loop' set to True.
In my app, when I toggle isEmitting to True there can be a delay of a few seconds before the ParticleEmitter starts.
However, if I programatically add the emitter in code at that point, it starts immediately.
To be clear, I'm seeing this on the VisionOS simulator - I don't have access to a device at this time.
Am I misunderstanding how to control the ParticleEmitter when I need precise control on when it starts.
Post
Replies
Boosts
Views
Activity
Hello,
I'm writing an EntityAction that animates a material base tint between two different colours. However, the colour that is being actually set differs in RGB values from that requested.
For example, trying to set an end target of R0.5, G0.5, B0.5, results in a value of R0.735357, G0.735357, B0.735357. I can also see during the animation cycle that intermediate actual tint values are also incorrect, versus those being set.
My understanding is the the values of material base colour are passed as a SIMD4. Therefore I have a couple of helper extensions to convert a UIColor into this format and mix between two colours. Note however, I don't think the issue is with this functions - even if their outputs are wrong, the final value of the base tint doesn't match the value being set.
I wondered if this was a colour space issue?
import simd
import RealityKit
import UIKit
typealias Float4 = SIMD4<Float>
extension Float4 {
func mixedWith(_ value: Float4, by mix: Float) -> Float4 {
Float4(
simd_mix(x, value.x, mix),
simd_mix(y, value.y, mix),
simd_mix(z, value.z, mix),
simd_mix(w, value.w, mix)
)
}
}
extension UIColor {
var float4: Float4 {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
getRed(&r, green: &g, blue: &b, alpha: &a)
return Float4(Float(r), Float(g), Float(b), Float(a))
}
}
struct ColourAction: EntityAction {
let startColour: SIMD4<Float>
let targetColour: SIMD4<Float>
var animatedValueType: (any AnimatableData.Type)? { SIMD4<Float>.self }
init(startColour: UIColor, targetColour: UIColor) {
self.startColour = startColour.float4
self.targetColour = targetColour.float4
}
static func registerEntityAction() {
ColourAction.subscribe(to: .updated) { event in
guard let animationState = event.animationState else { return }
let interpolatedColour = event.action.startColour.mixedWith(event.action.targetColour, by: Float(animationState.normalizedTime))
animationState.storeAnimatedValue(interpolatedColour)
}
}
}
extension Entity {
func updateColour(from currentColour: UIColor, to targetColour: UIColor, duration: Double, endAction: @escaping (Entity) -> Void = { _ in }) {
let colourAction = ColourAction(startColour: currentColour, targetColour: targetColour, endedAction: endAction)
if let colourAnimation = try? AnimationResource.makeActionAnimation(for: colourAction, duration: duration, bindTarget: .material(0).baseColorTint) {
playAnimation(colourAnimation)
}
}
}
The EntityAction can only be applied to an entity with a ModelComponent (because of the material), so it can be called like so:
guard
let modelComponent = entity.components[ModelComponent.self],
let material = modelComponent.materials.first as? PhysicallyBasedMaterial else
{
return
}
let currentColour = material.baseColor.tint
let targetColour = UIColor(_colorLiteralRed: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
entity.updateColour(from:currentColour, to: targetColour, duration: 2)
Hello,
I am trying to use the subdivision mesh rendering option.
I can see it working in RealityComposerPro:
But not when loading asset and displaying in Simulator:
Using this code:
import SwiftUI
import RealityKit
import RealityKitContent
struct AirspaceView: View {
// MARK: - VIEW BODY
var body: some View {
RealityView { content in
if let a = try? await Entity(named: "Models/Test/Test.usdc", in: realityKitContentBundle) {
content.add(a)
}
}
}
}
Any ideas why?
I'm trying to build a Shader in "Reality Composer Pro" that updates from a start time. Initially I tried the following:
The idea was that when the startTime was 0, the output would be 0, but then I would set startTime from within code and this would be compared with the current GPU time, and difference used to drive another part of the shader graph:
if
let testEntity = root.findEntity(named: "Test"),
var shaderGraphMaterial = testEntity.components[ModelComponent.self]?.materials.first as? ShaderGraphMaterial
{
let time = CFAbsoluteTimeGetCurrent()
try! shaderGraphMaterial.setParameter(name: "StartTime", value: .float(Float(time)))
testEntity.components[ModelComponent.self]?.materials[0] = shaderGraphMaterial
}
However, I haven't found a reference to the time the shader would be using.
So now I am trying to write an EntityAction to achieve the same effect. Instead of comparing a start time to the GPU's time I'm trying to animate one of the shader's uniform input. However, I'm not sure how to specify the bind target. Here's my attempt so far:
import RealityKit
struct ShaderAction: EntityAction {
let startValue: Float
let targetValue: Float
var animatedValueType: (any AnimatableData.Type)? { Float.self }
static func registerEntityAction() {
ShaderAction.subscribe(to: .updated) { event in
guard let animationState = event.animationState else { return }
let value = simd_mix(event.action.startValue, event.action.targetValue, Float(animationState.normalizedTime))
animationState.storeAnimatedValue(value)
}
}
}
extension Entity {
func updateShader(from startValue: Float, to targetValue: Float, duration: Double) {
let fadeAction = ShaderAction(startValue: startValue, targetValue: targetValue)
if let shaderAnimation = try? AnimationResource.makeActionAnimation(for: fadeAction, duration: duration, bindTarget: .material(0).customValue) {
playAnimation(shaderAnimation)
}
}
}
'''
Currently when I run this I get an assertion failure: 'Index out of range (operator[]:line 797) index = 260, max = 8'
Furthermore, even if it didn't crash I don't understand how to pass a binding to the custom shader value "startValue".
Any clues of how to achieve this effect - even if it's a completely different way.
Hello,
I am looking to create a shader to update an entity's rendering. As a basic example say I want to recolour an entity, but leave its original textures showing through:
I understand with VisionOS I need to use Reality Composer Pro to create the shader, but I'm lost as how to reference the original colour that I'm trying to update in the node graph. All my attempts appear to completely override the textures in the entity (and its sub-entities) that I want to impact. Also the tutorials / examples I've looked at appear to create materials, not add an effect on top of existing materials.
Any hints or pointers?
Assuming this is possible, I've been trying to load the material in code, and apply to an entity. But do I need to do this to all child entities, or just the topmost?
do {
let entity = MyAssets.createModelEntity(.plane) // Loads from bundle and performs config
let material = try await ShaderGraphMaterial(named: "/Root/TestMaterial", from: "Test", in: realityKitContentBundle)
entity.applyToChildren {
$0.components[ModelComponent.self]?.materials = [material]
}
root.addChild(entity)
} catch {
fatalError(error.localizedDescription)
}
Does anyone have experience of creating their own EntityActions?
Say for example I wanted one that faded up the opacity of an entity, then once it had completed set another property on one of the entity's components.
I understand that I could use the FromToByAction to control the opacity (and have this working), but I am interested to learn how to create my own dedicated EntityAction, and finding the documentation hard to fathom.
I got as far as creating a struct conforming to EntityAction protocol:
var animatedValueType: (any AnimatableData.Type)?
}
Subscribing to update events on this:
FadeUpAction.subscribe(to: .updated) { event in
guard let animationState = event.animationState else {
return
}
// My animation state is always nil, so I never get here!
let newValue = \\\Some Calc...
animationState.storeAnimatedValue(newValue)
}
And setting it up as an animation on an entity:
let action = FadeUpAction()
if let animation = try? AnimationResource.makeActionAnimation(
for:action,
duration: 2.0,
bindTarget: .opacity
) {
entity.playAnimation(animation)
}
...but haven't been able to understand how to extract the current timeDelta or set the value in the event handler.
Any pointers?
I am unable to get VisionOS 2.0 (simulator) to receive the GCControllerDidConnect notification and thus am unable to setup support for a gamepad. However, it works in VisionOS 1.2.
For VisionOS 2.0 I've tried adding:
.handlesGameControllerEvents(matching: .gamepad) attribute to the view
Supports Controller User Interaction to Info.plist
Supported game controller types -> Extended Gamepad to Info.plist
...but the notification still doesn't fire. It does when the code is run from VisionOS 1.2 simulator, both of which have the Send Game Controller To Device option enabled.
Here is the example code. It's based on the Xcode project template. The only files updated were ImmersiveView.swift and Info.plist, as detailed above:
import SwiftUI
import GameController
import RealityKit
import RealityKitContent
struct ImmersiveView: View {
var body: some View {
RealityView { content in
// Add the initial RealityKit content
if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
content.add(immersiveContentEntity)
}
NotificationCenter.default.addObserver(
forName: NSNotification.Name.GCControllerDidConnect,
object: nil, queue: nil) { _ in
print("Handling GCControllerDidConnect notification")
}
}
.modify {
if #available(visionOS 2.0, *) {
$0.handlesGameControllerEvents(matching: .gamepad)
} else {
$0
}
}
}
}
extension View {
func modify<T: View>(@ViewBuilder _ modifier: (Self) -> T) -> some View {
return modifier(self)
}
}
Hi,
I'm looking to update the metadata properties of a DNG image stored on disc, saving to a new file.
Using ImageIO's CGImageSource and CGImageDestination classes, I run into a problem where by the destination doesn't support the type of the source. For example:
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
if
let cgImageSource = CGImageSourceCreateWithURL(sourceURL as CFURL, imageSourceOptions),
let type = CGImageSourceGetType(cgImageSource) {
guard let imageDestination = CGImageDestinationCreateWithURL(destinationURL as CFURL, type, 1, nil) else {
fatalError("Unable to create image destination")
}
// Code to update properties and write out to destination url
}
}
When this code is executed I get the following errors on the command line when trying to create the destination:
2024-06-30 11:52:25.531530+0100 ABC[7564:273101] [ABC] findWriterForTypeAndAlternateType:119: *** ERROR: unsupported output file format 'com.adobe.raw-image'
2024-06-30 11:52:25.531661+0100 ABC[7564:273101] [ABC] CGImageDestinationCreateWithURL:4429: *** ERROR: CGImageDestinationCreateWithURL: failed to create 'CGImageDestinationRef'
I don't see a way to create a destination directly from a source? Yes, the code works for say a JPEG file but I want it to work for any image format that CGImageSource can work with?
Does anyone have any guidance / experience using TriggerVolumes to detect collision rather than the Physics engine in Reality Kit.
Aside from not participating the physics engine are there any other downside or upsides to using them?
Hello,
In the documentation for an ARView we see a diagram that shows that all Entity's are connected via AnchorEntity's to the Scene:
https://developer.apple.com/documentation/realitykit/arview
What happens when we are using a RealityView? Here the documentation suggests we direclty add Entity's:
https://developer.apple.com/documentation/realitykit/realityview/
Three questions:
Do we need to add Entitys to an AnchorEntity first and add this via content.add(...)?
Is an Entity ignored by physics engine if attached via an Anchor?
If both the AnchorEntity and an attached Entity is added via content.add(...), does is its anchor's position ignored?
I'm trying to detect when two entities collide. The following code shows a very basic set-up. How do I get the upperSphere to move? If I set its physicsBody.mode to .dynamic it moves with gravity (and the collision is reported), but when in kinematic mode it doesn't respond to the impulse:
struct CollisionView: View {
@State var subscriptions: [EventSubscription] = []
var body: some View {
RealityView { content in
let upperSphere = ModelEntity(mesh: .generateSphere(radius: 0.04))
let lowerSphere = ModelEntity(mesh: .generateSphere(radius: 0.04))
upperSphere.position = [0, 2, -2]
upperSphere.physicsBody = .init()
upperSphere.physicsBody?.mode = .kinematic
upperSphere.physicsMotion = PhysicsMotionComponent()
upperSphere.generateCollisionShapes(recursive: false)
lowerSphere.position = [0, 1, -2]
lowerSphere.physicsBody = .init()
lowerSphere.physicsBody?.mode = .static
lowerSphere.generateCollisionShapes(recursive: false)
let sub = content.subscribe(to: CollisionEvents.Began.self, on: nil) { _ in print("Collision!") }
subscriptions.append(sub)
content.add(upperSphere)
content.add(lowerSphere)
Task {
try? await Task.sleep(for: .seconds(2))
print("Impulse applied")
upperSphere.applyLinearImpulse([0, -1, 0], relativeTo: nil)
}
}
}
}
Xcode is not not picking up when I make changes to my unit test files when running tests. The only way is to clean the whole project and rebuild again. Any ideas why?
I recently tried Xcode cloud and this behaviour started after removing that option
I am building a tokeniser, and would like to hold this as a struct, passing in a locale during initiation.
Here's the gist of what I would want to write:
struct Tokeniser {
private let locale: Locale
private let integerRegex: Regex
init(locale: Locale) {
self.locale: Locale
self.integerRegex = Regex {
Capture {
.localizedInteger(locale: locale)
} transform: {
Token.number($0)
}
}
}
func parse(text: String) -> Token {
if let match = try integerRegex.firstMatch(in: text) {
//... other code here
}
}
\\...other code here
}
As Regex is generic the compiler suggests to set the integerRegex's type to Regex<Any>, but this triggers another set of compiler issues that I have not been able to figure out what the type should be. So then I tried to write something like this (inspired by SwiftUI):
var integerRegex: some Regex {
Capture {
.localizedInteger(locale: locale)
} transform: {
Token.number($0)
}
}
But again, the compiler prompts me to enter Regex.
The only way I have to be able to get the struct to compiler is to create lazy variables, which then have the side effect that I have to mark my functions as mutable, which have downstream issues when they are called from with SwiftUI structs.
lazy var integerRegex = Regex {
Capture {
.localizedInteger(locale: locale)
}
}
mutating func parse(text: String) -> Token {
if let match = try integerRegex.firstMatch(in: text) {
}
}
How can I code this?
I'm developing across two Macs: one Apple Silicon and an Intel laptop (used for performance and compatibility testing).
I've been testing my StoreKit2 implementation, and have come across a couple of issues.
From time to time something appears to become corrupted, and StoreKit2 says it has a 'connection' issue. Deleting the folder com.apple.storekitagent, which a sub folder Octane, restarting the machine appears to fix this.
The second issue I cannot appear to resolve. I have needed to change the App Bundle ID. I did this whilst developing on the M2 Mac without problem. But now when I try and run back on the Intel Mac I am getting a set of unfinished transactions which I can either process, nor delete (they are not appearing in Xcode's StoreKit debug window. I've tried deleting the folder com.apple.storekitagent, and restarting the machine, but to no avail.
Note if I reset back to my original App Bundle ID then all works as before.
Here is a sample of errors I am receiving:
Error finishing transaction 110: Error Domain=ASDErrorDomain Code=500 "Unhandled exception" UserInfo={NSUnderlyingError=0x600001863cf0 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, AMSURL=http://localhost:49242/WebObjects/MZFinance.woa/wa/inAppTransactionDone?REDACTED, AMSStatusCode=404, AMSServerPayload={
error = "Transaction not found";
}, NSLocalizedFailureReason=The response has an invalid status code}}, storefront-country-code=USA, client-environment-type=XcodeTest(file:///Users/peggers/Library/Caches/com.apple.storekitagent/Octane/com.bristolbaycodefactory.photo-organista/), AMSServerErrorCode=0, NSLocalizedFailureReason=An unknown error occurred, NSLocalizedDescription=Unhandled exception}
Any further ideas on how to reset or solve this issue?
I'm writing a photo management app where the document stores the location of folders (in which image files reside). These are added by the user, and saved as security scoped bookmarks. All this appeared to be working.
However, I tested a use-case where the user starts working on one machine where they add a folder, which resides on an external drive. This works, as long as they remain the on the same machine.
However, if they move to another machine, plug in the external drive and then load up the document it fails to resolve the bookmark, throws the following error:
Error Domain=NSCocoaErrorDomain Code=259 "The file couldn’t be opened because it isn’t in the correct format."
For completeness here is the code to resolve the bookmark:
do {
let url = try URL(
resolvingBookmarkData: data,
options: Self.bookmarkResolutionOptions,
relativeTo: nil,
bookmarkDataIsStale: &isStale
)
} catch {
print(error)
/// ... error handling code here
}
Do I therefore assume that I cannot achieve this, and will have to ask the user to re-authorise this folder on the second machine?