Skip to content

hunterh37/DicyaninEntityManagement

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 

Repository files navigation

DicyaninEntityManagement

A Swift package for managing 3D entities and scenes in RealityKit applications.

Why use this package?
This package provides a streamlined way to manage multiple 3D entities in visionOS applications. It handles the complexity of loading, positioning, and managing multiple models in a scene, making it easy to create rich spatial experiences. Perfect for applications that need to display and interact with multiple 3D objects simultaneously.

Features

  • Scene management with entity configurations
  • Support for animations, physics, and interactions
  • Easy integration with RealityKit and SwiftUI
  • Thread-safe entity management
  • Memory-efficient resource handling

Quick Example

import SwiftUI
import RealityKit
import DicyaninEntityManagement

struct MySceneView: View {
    var body: some View {
        // Simple usage with default scene and default entity
        DicyaninEntityView()
        
        // Use default entity configuration with custom scene details
        DicyaninEntityView(
            sceneId: "my_scene",
            sceneName: "My First Scene",
            sceneDescription: "A simple scene with default entity"
        )
        
        // Use custom entity configuration
        DicyaninEntityView(
            sceneId: "custom_scene",
            sceneName: "Custom Scene",
            sceneDescription: "A scene with custom entities",
            entityConfigurations: [
                DicyaninEntityConfiguration(
                    name: "spinning_cube",
                    position: SIMD3<Float>(0, 0, -1),
                    scale: SIMD3<Float>(repeating: 0.3),
                    animation: ModelAnimation(type: .spin(speed: 2.0, axis: SIMD3<Float>(0, 1, 0)))
                )
            ]
        )
        
        // Use default entity configuration with custom handlers
        DicyaninEntityView(
            onLoadingStateChanged: { isLoading in
                print("Loading state: \(isLoading)")
            },
            onError: { error in
                print("Error occurred: \(error)")
            },
            onEntitiesLoaded: { entities in
                print("Loaded \(entities.count) entities")
            }
        )
    }
}

Installation

Add the package to your Xcode project using Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/hunterh37/DicyaninEntityManagement.git", from: "0.0.1"),
    .package(url: "https://github.com/hunterh37/DicyaninEntity.git", from: "0.0.1")  // Required dependency
]

Note: This package requires DicyaninEntity as a dependency. Make sure to include both packages in your project.

API Documentation

DicyaninEntityManager

The core class responsible for managing scenes and entities.

public class DicyaninEntityManager {
    /// The root entity that contains all scenes
    let rootEntity: Entity
    
    /// Currently loaded scene
    private(set) public var currentScene: DicyaninScene?
    
    /// Returns the total number of loaded scenes
    public var sceneCount: Int
    
    /// Loads a scene configuration
    /// - Parameter scene: The scene configuration to load
    /// - Returns: Array of created entities
    @MainActor
    public func loadScene(_ scene: DicyaninScene) async throws -> [DicyaninEntity]
    
    /// Unloads a scene and removes its entities
    /// - Parameter scene: The scene to unload
    @MainActor
    public func unloadScene(_ scene: DicyaninScene) async throws
    
    /// Returns all entities for a specific scene
    /// - Parameter sceneId: The ID of the scene
    /// - Returns: Array of DicyaninEntity objects for the scene
    public func getEntitiesForScene(_ sceneId: String) -> [DicyaninEntity]?
}

DicyaninScene

Represents a scene configuration containing multiple entities.

public struct DicyaninScene {
    /// Unique identifier for the scene
    public let id: String
    
    /// Name of the scene for display purposes
    public let name: String
    
    /// Description of the scene
    public let description: String
    
    /// Array of entity configurations that make up this scene
    public let entityConfigurations: [DicyaninEntityConfiguration]
}

DicyaninSceneBuilder

A builder for creating scene configurations with a fluent interface.

public struct DicyaninSceneBuilder {
    /// Creates a new scene builder
    /// - Parameters:
    ///   - id: Unique identifier for the scene
    ///   - name: Display name for the scene
    ///   - description: Description of the scene
    public init(id: String, name: String, description: String)
    
    /// Adds a single entity configuration to the scene
    /// - Parameter configuration: The entity configuration to add
    /// - Returns: The builder instance for method chaining
    public func addEntity(_ configuration: DicyaninEntityConfiguration) -> DicyaninSceneBuilder
    
    /// Adds multiple entity configurations to the scene
    /// - Parameter configurations: Array of entity configurations to add
    /// - Returns: The builder instance for method chaining
    public func addEntities(_ configurations: [DicyaninEntityConfiguration]) -> DicyaninSceneBuilder
    
    /// Builds the final scene configuration
    /// - Returns: A DicyaninScene instance
    public func build() -> DicyaninScene
}

DicyaninEntityViewProvider

Protocol defining the requirements for a custom entity view provider.

public protocol DicyaninEntityViewProvider {
    /// The scene configuration to be loaded
    var scene: DicyaninScene { get }
    
    /// Optional loading state handler
    /// - Parameter isLoading: Boolean indicating if the scene is currently loading
    var onLoadingStateChanged: ((Bool) -> Void)? { get }
    
    /// Optional error handler
    /// - Parameter error: The error that occurred during scene loading
    var onError: ((Error) -> Void)? { get }
    
    /// Optional entity loaded handler
    /// - Parameter entities: Array of loaded entities
    var onEntitiesLoaded: (([DicyaninEntity]) -> Void)? { get }
}

DefaultDicyaninEntityViewProvider

A default implementation of DicyaninEntityViewProvider.

public struct DefaultDicyaninEntityViewProvider: DicyaninEntityViewProvider {
    /// Creates a new default provider
    /// - Parameters:
    ///   - scene: The scene configuration to load
    ///   - onLoadingStateChanged: Optional loading state handler
    ///   - onError: Optional error handler
    ///   - onEntitiesLoaded: Optional entity loaded handler
    public init(
        scene: DicyaninScene,
        onLoadingStateChanged: ((Bool) -> Void)? = nil,
        onError: ((Error) -> Void)? = nil,
        onEntitiesLoaded: (([DicyaninEntity]) -> Void)? = nil
    )
}

DicyaninEntityView

A SwiftUI view for displaying and managing 3D entities.

public struct DicyaninEntityView: View {
    /// Creates a new entity view
    /// - Parameter provider: The provider to use for scene configuration
    public init(provider: DicyaninEntityViewProvider = DefaultDicyaninEntityViewProvider(...))
}

Usage

Basic Scene Management

import DicyaninEntityManagement
import RealityKit

// Create a scene configuration
let scene = DicyaninScene(
    id: "my_scene",
    name: "My Scene",
    description: "A scene with multiple entities",
    entityConfigurations: [
        // A bouncing cube
        DicyaninEntityConfiguration(
            name: "bouncing_cube",
            position: SIMD3<Float>(0, 0, -1),
            scale: SIMD3<Float>(repeating: 0.3),
            animation: ModelAnimation(type: .bounce(height: 0.5, duration: 1.0))
        ),
        // A physics-enabled sphere
        DicyaninEntityConfiguration(
            name: "physics_sphere",
            position: SIMD3<Float>(1, 0, -1),
            scale: SIMD3<Float>(repeating: 0.3),
            physics: ModelPhysics(mass: 1.0, isDynamic: true),
            collision: ModelCollision(shape: .sphere(radius: 0.3), isStatic: false)
        )
    ]
)

// Create and load the scene
let entityManager = DicyaninEntityManager()
let entities = try await entityManager.loadScene(scene)

// Unload the scene when done
try await entityManager.unloadScene(scene)

Using with SwiftUI

You can use the default implementation:

import SwiftUI
import RealityKit
import DicyaninEntityManagement

struct ContentView: View {
    var body: some View {
        // Use default scene and entity
        DicyaninEntityView()
        
        // Or customize just the scene details
        DicyaninEntityView(
            sceneId: "custom_default",
            sceneName: "Custom Default",
            sceneDescription: "A customized default scene"
        )
        
        // Or use custom entities
        DicyaninEntityView(
            sceneId: "custom_entities",
            sceneName: "Custom Entities",
            sceneDescription: "A scene with custom entities",
            entityConfigurations: [
                DicyaninEntityConfiguration(
                    name: "custom_entity",
                    position: SIMD3<Float>(0, 0, -1),
                    scale: SIMD3<Float>(repeating: 0.5)
                )
            ]
        )
    }
}

Or create your own custom implementation with the builder pattern:

import SwiftUI
import RealityKit
import DicyaninEntityManagement

// Create a custom scene provider
struct MySceneProvider: DicyaninEntityViewProvider {
    let scene: DicyaninScene
    
    // Optional handlers for scene lifecycle events
    var onLoadingStateChanged: ((Bool) -> Void)?
    var onError: ((Error) -> Void)?
    var onEntitiesLoaded: (([DicyaninEntity]) -> Void)?
    
    init(
        sceneId: String,
        sceneName: String,
        sceneDescription: String,
        entityConfigurations: [DicyaninEntityConfiguration],
        onLoadingStateChanged: ((Bool) -> Void)? = nil,
        onError: ((Error) -> Void)? = nil,
        onEntitiesLoaded: (([DicyaninEntity]) -> Void)? = nil
    ) {
        // Create scene using the builder pattern
        self.scene = DicyaninSceneBuilder(id: sceneId, name: sceneName, description: sceneDescription)
            .addEntities(entityConfigurations)
            .build()
        
        self.onLoadingStateChanged = onLoadingStateChanged
        self.onError = onError
        self.onEntitiesLoaded = onEntitiesLoaded
    }
}

// Example usage in a view
struct CustomContentView: View {
    private let entityConfigurations: [DicyaninEntityConfiguration] = [
        DicyaninEntityConfiguration(
            name: "custom_entity_1",
            position: SIMD3<Float>(0, 0, -1),
            scale: SIMD3<Float>(repeating: 0.5)
        ),
        DicyaninEntityConfiguration(
            name: "custom_entity_2",
            position: SIMD3<Float>(1, 0, -1),
            scale: SIMD3<Float>(repeating: 0.5)
        )
    ]
    
    var body: some View {
        DicyaninEntityView(
            provider: MySceneProvider(
                sceneId: "custom_scene",
                sceneName: "My Custom Scene",
                sceneDescription: "A custom scene with multiple entities",
                entityConfigurations: entityConfigurations,
                onLoadingStateChanged: { isLoading in
                    print("Loading state: \(isLoading)")
                },
                onError: { error in
                    print("Error occurred: \(error)")
                },
                onEntitiesLoaded: { entities in
                    print("Loaded \(entities.count) entities")
                }
            )
        )
    }
}

You can also use the builder pattern directly with the default provider:

struct BuilderExampleView: View {
    var body: some View {
        let scene = DicyaninSceneBuilder(
            id: "builder_scene",
            name: "Builder Scene",
            description: "Scene created using the builder pattern"
        )
        .addEntity(DicyaninEntityConfiguration(
            name: "entity_1",
            position: SIMD3<Float>(0, 0, -1),
            scale: SIMD3<Float>(repeating: 0.5)
        ))
        .addEntity(DicyaninEntityConfiguration(
            name: "entity_2",
            position: SIMD3<Float>(1, 0, -1),
            scale: SIMD3<Float>(repeating: 0.5)
        ))
        .build()
        
        return DicyaninEntityView(
            provider: DefaultDicyaninEntityViewProvider(
                scene: scene,
                onLoadingStateChanged: { isLoading in
                    print("Loading state: \(isLoading)")
                },
                onError: { error in
                    print("Error occurred: \(error)")
                },
                onEntitiesLoaded: { entities in
                    print("Loaded \(entities.count) entities")
                }
            )
        )
    }
}

Entity Configuration

Entities can be configured with various properties:

let config = DicyaninEntityConfiguration(
    name: "my_entity",
    position: SIMD3<Float>(0, 0, -1),
    rotation: simd_quatf(angle: .pi/4, axis: SIMD3<Float>(0, 1, 0)),
    scale: SIMD3<Float>(repeating: 0.5),
    playAnimation: true,
    animation: ModelAnimation(type: .spin(speed: 2.0, axis: SIMD3<Float>(0, 1, 0))),
    physics: ModelPhysics(mass: 1.0, isDynamic: true),
    collision: ModelCollision(shape: .box(size: SIMD3<Float>(repeating: 0.5)), isStatic: false)
)

Package Integration

You can easily integrate this package with other packages by using the onEntityLoaded handler. This is called for each entity as it's loaded, allowing you to customize or extend the entity with functionality from other packages:

import SwiftUI
import RealityKit
import DicyaninEntityManagement
import YourOtherPackage

struct IntegratedSceneView: View {
    var body: some View {
        DicyaninEntityView(
            onEntityLoaded: { entity in
                // Integrate with another package
                entity.enableCustomFeature()  // From your other package
                entity.setupCustomBehavior()  // From your other package
                
                // Or add custom components
                entity.components[YourCustomComponent.self] = YourCustomComponent()
            }
        )
    }
}

This approach allows you to:

  • Add custom functionality to each entity
  • Integrate with other packages seamlessly
  • Keep the integration code clean and maintainable
  • Handle each entity individually as it's loaded

Requirements

  • visionOS 1.0+
  • Xcode 15.0+
  • Swift 5.9+

Author

Hunter Harris. Copyright © 2025 Dicyanin Labs.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A Swift package for managing 3D entities and scenes in RealityKit applications.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages