Add scan flow MVP and local Axiom skill workspace
This snapshot establishes the camera-to-result recognition flow and related tests while checking in the project skill/docs assets required for the configured local tooling.
This commit is contained in:
7
.claude/skills/axiom-scenekit-ref/.openskills.json
Normal file
7
.claude/skills/axiom-scenekit-ref/.openskills.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"source": "CharlesWiltgen/Axiom",
|
||||
"sourceType": "git",
|
||||
"repoUrl": "https://github.com/CharlesWiltgen/Axiom",
|
||||
"subpath": "axiom-codex/skills/axiom-scenekit-ref",
|
||||
"installedAt": "2026-04-12T08:06:36.400Z"
|
||||
}
|
||||
345
.claude/skills/axiom-scenekit-ref/SKILL.md
Normal file
345
.claude/skills/axiom-scenekit-ref/SKILL.md
Normal file
@@ -0,0 +1,345 @@
|
||||
---
|
||||
name: axiom-scenekit-ref
|
||||
description: SceneKit → RealityKit concept mapping, complete API cross-reference for migration, scene graph API, materials, lighting, camera, physics, animation, constraints
|
||||
license: MIT
|
||||
compatibility: [iOS 8+, macOS 10.8+, tvOS 9+]
|
||||
metadata:
|
||||
version: "1.0.0"
|
||||
---
|
||||
|
||||
# SceneKit API Reference & Migration Mapping
|
||||
|
||||
Complete API reference for SceneKit with RealityKit equivalents for every major concept.
|
||||
|
||||
## When to Use This Reference
|
||||
|
||||
Use this reference when:
|
||||
- Looking up SceneKit → RealityKit API equivalents during migration
|
||||
- Checking specific SceneKit class properties or methods
|
||||
- Planning which SceneKit features have direct RealityKit counterparts
|
||||
- Understanding architectural differences between scene graph and ECS
|
||||
|
||||
---
|
||||
|
||||
## Part 1: SceneKit → RealityKit Concept Mapping
|
||||
|
||||
### Core Architecture
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNScene` | `RealityViewContent` / `Entity` (root) | RealityKit scenes are entity hierarchies |
|
||||
| `SCNNode` | `Entity` | Lightweight container in both |
|
||||
| `SCNView` | `RealityView` (SwiftUI) | `ARView` for UIKit on iOS |
|
||||
| `SceneView` (SwiftUI) | `RealityView` | SceneView deprecated iOS 26 |
|
||||
| `SCNRenderer` | `RealityRenderer` | Low-level Metal rendering |
|
||||
| Node properties | Components | ECS separates data from hierarchy |
|
||||
| `SCNSceneRendererDelegate` | `System` / `SceneEvents.Update` | Frame-level updates |
|
||||
| `.scn` files | `.usdz` / `.usda` files | Convert with `xcrun scntool` |
|
||||
|
||||
### Geometry & Rendering
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNGeometry` | `MeshResource` | RealityKit generates from code or loads USD |
|
||||
| `SCNBox`, `SCNSphere`, etc. | `MeshResource.generateBox()`, `.generateSphere()` | Similar built-in shapes |
|
||||
| `SCNMaterial` | `SimpleMaterial`, `PhysicallyBasedMaterial` | PBR-first in RealityKit |
|
||||
| `SCNMaterial.lightingModel = .physicallyBased` | `PhysicallyBasedMaterial` | Default in RealityKit |
|
||||
| `SCNMaterial.diffuse` | `PhysicallyBasedMaterial.baseColor` | Different property name |
|
||||
| `SCNMaterial.metalness` | `PhysicallyBasedMaterial.metallic` | Different property name |
|
||||
| `SCNMaterial.roughness` | `PhysicallyBasedMaterial.roughness` | Same concept |
|
||||
| `SCNMaterial.normal` | `PhysicallyBasedMaterial.normal` | Same concept |
|
||||
| Shader modifiers | `ShaderGraphMaterial` / `CustomMaterial` | No direct port — must rewrite |
|
||||
| `SCNProgram` (custom shaders) | `CustomMaterial` with Metal functions | Different API surface |
|
||||
| `SCNGeometrySource` | `MeshResource.Contents` | Low-level mesh data |
|
||||
|
||||
### Transforms & Hierarchy
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `node.position` | `entity.position` | Both SCNVector3 / SIMD3<Float> |
|
||||
| `node.eulerAngles` | `entity.orientation` (quaternion) | RealityKit prefers quaternions |
|
||||
| `node.scale` | `entity.scale` | Both SIMD3<Float> |
|
||||
| `node.transform` | `entity.transform` | 4×4 matrix |
|
||||
| `node.worldTransform` | `entity.transform(relativeTo: nil)` | World-space transform |
|
||||
| `node.addChildNode(_:)` | `entity.addChild(_:)` | Same hierarchy concept |
|
||||
| `node.removeFromParentNode()` | `entity.removeFromParent()` | Same concept |
|
||||
| `node.childNodes` | `entity.children` | Children collection |
|
||||
| `node.parent` | `entity.parent` | Parent reference |
|
||||
| `node.childNode(withName:recursively:)` | `entity.findEntity(named:)` | Named lookup |
|
||||
|
||||
### Lighting
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNLight` (`.omni`) | `PointLightComponent` | Point light |
|
||||
| `SCNLight` (`.directional`) | `DirectionalLightComponent` | Sun/directional light |
|
||||
| `SCNLight` (`.spot`) | `SpotLightComponent` | Cone light |
|
||||
| `SCNLight` (`.area`) | No direct equivalent | Use multiple point lights |
|
||||
| `SCNLight` (`.ambient`) | `EnvironmentResource` (IBL) | Image-based lighting preferred |
|
||||
| `SCNLight` (`.probe`) | `EnvironmentResource` | Environment probes |
|
||||
| `SCNLight` (`.IES`) | No direct equivalent | Use light intensity profiles |
|
||||
|
||||
### Camera
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNCamera` | `PerspectiveCamera` entity | Entity with camera component |
|
||||
| `camera.fieldOfView` | `PerspectiveCameraComponent.fieldOfViewInDegrees` | Same concept |
|
||||
| `camera.zNear` / `camera.zFar` | `PerspectiveCameraComponent.near` / `.far` | Clipping planes |
|
||||
| `camera.wantsDepthOfField` | Post-processing effects | Different mechanism |
|
||||
| `camera.motionBlurIntensity` | Post-processing effects | Different mechanism |
|
||||
| `allowsCameraControl` | Custom gesture handling | No built-in orbit camera |
|
||||
|
||||
### Physics
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNPhysicsBody` | `PhysicsBodyComponent` | Component-based |
|
||||
| `.dynamic` | `.dynamic` | Same mode |
|
||||
| `.static` | `.static` | Same mode |
|
||||
| `.kinematic` | `.kinematic` | Same mode |
|
||||
| `SCNPhysicsShape` | `CollisionComponent` / `ShapeResource` | Separate from body in RealityKit |
|
||||
| `categoryBitMask` | `CollisionGroup` | Named groups vs raw bitmasks |
|
||||
| `collisionBitMask` | `CollisionFilter` | Filter-based |
|
||||
| `contactTestBitMask` | `CollisionEvents.Began` subscription | Event-based contacts |
|
||||
| `SCNPhysicsContactDelegate` | `scene.subscribe(to: CollisionEvents.Began.self)` | Combine-style events |
|
||||
| `SCNPhysicsField` | `PhysicsBodyComponent` forces | Apply forces directly |
|
||||
| `SCNPhysicsJoint` | `PhysicsJoint` | Similar joint types |
|
||||
|
||||
### Animation
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `SCNAction` | `entity.move(to:relativeTo:duration:)` | Transform animation |
|
||||
| `SCNAction.sequence` | Animation chaining | Less declarative in RealityKit |
|
||||
| `SCNAction.group` | Parallel animations | Apply to different entities |
|
||||
| `SCNAction.repeatForever` | `AnimationPlaybackController` repeat | Different API |
|
||||
| `SCNTransaction` (implicit) | No direct equivalent | Explicit animations only |
|
||||
| `CAAnimation` bridge | `entity.playAnimation()` | Load from USD |
|
||||
| `SCNAnimationPlayer` | `AnimationPlaybackController` | Playback control |
|
||||
| Morph targets | Blend shapes in USD | Load via USD files |
|
||||
|
||||
### Interaction
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `hitTest(_:options:)` | `RealityViewContent.entities(at:)` | Different API |
|
||||
| Gesture recognizers on SCNView | `ManipulationComponent` | Built-in drag/rotate/scale |
|
||||
| `allowsCameraControl` | Custom implementation | No built-in orbit |
|
||||
|
||||
### AR Integration
|
||||
|
||||
| SceneKit | RealityKit | Notes |
|
||||
|----------|-----------|-------|
|
||||
| `ARSCNView` | `RealityView` + `AnchorEntity` | Legacy → modern |
|
||||
| `ARSCNViewDelegate` | `AnchorEntity` auto-tracking | Event-driven |
|
||||
| `renderer(_:didAdd:for:)` | `AnchorEntity(.plane)` | Declarative anchoring |
|
||||
| `ARWorldTrackingConfiguration` | `SpatialTrackingSession` | iOS 18+ |
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Scene Graph API
|
||||
|
||||
### SCNScene
|
||||
|
||||
```swift
|
||||
// Loading
|
||||
let scene = SCNScene(named: "scene.usdz")!
|
||||
let scene = try SCNScene(url: url, options: [
|
||||
.checkConsistency: true,
|
||||
.convertToYUp: true
|
||||
])
|
||||
|
||||
// Properties
|
||||
scene.rootNode // Root of node hierarchy
|
||||
scene.background.contents // Skybox (UIImage, UIColor, MDLSkyCubeTexture)
|
||||
scene.lightingEnvironment.contents // IBL environment map
|
||||
scene.fogStartDistance // Fog near
|
||||
scene.fogEndDistance // Fog far
|
||||
scene.fogColor // Fog color
|
||||
scene.isPaused // Pause simulation
|
||||
```
|
||||
|
||||
### SCNNode
|
||||
|
||||
```swift
|
||||
// Creation
|
||||
let node = SCNNode()
|
||||
let node = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
|
||||
|
||||
// Transform
|
||||
node.position = SCNVector3(x, y, z)
|
||||
node.eulerAngles = SCNVector3(pitch, yaw, roll)
|
||||
node.scale = SCNVector3(1, 1, 1)
|
||||
node.simdPosition = SIMD3<Float>(x, y, z) // SIMD variants available
|
||||
node.pivot = SCNMatrix4MakeTranslation(0, -0.5, 0) // Offset pivot point
|
||||
|
||||
// Visibility
|
||||
node.isHidden = false
|
||||
node.opacity = 1.0
|
||||
node.castsShadow = true
|
||||
node.renderingOrder = 0 // Lower = rendered first
|
||||
|
||||
// Hierarchy
|
||||
node.addChildNode(child)
|
||||
node.removeFromParentNode()
|
||||
node.childNodes
|
||||
node.childNode(withName: "name", recursively: true)
|
||||
node.enumerateChildNodes { child, stop in }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Materials
|
||||
|
||||
### Lighting Models
|
||||
|
||||
| Model | Description | Use Case |
|
||||
|-------|-------------|----------|
|
||||
| `.physicallyBased` | PBR metallic-roughness | Realistic rendering (recommended) |
|
||||
| `.blinn` | Blinn-Phong specular | Simple shiny surfaces |
|
||||
| `.phong` | Phong specular | Classic specular highlight |
|
||||
| `.lambert` | Diffuse only, no specular | Matte surfaces |
|
||||
| `.constant` | Unlit, flat color | UI elements, debug visualization |
|
||||
| `.shadowOnly` | Invisible, receives shadows | AR ground plane |
|
||||
|
||||
### Material Properties
|
||||
|
||||
```swift
|
||||
let mat = SCNMaterial()
|
||||
mat.lightingModel = .physicallyBased
|
||||
|
||||
// Textures or scalar values
|
||||
mat.diffuse.contents = UIImage(named: "albedo") // Base color
|
||||
mat.metalness.contents = 0.0 // 0 = dielectric, 1 = metal
|
||||
mat.roughness.contents = 0.5 // 0 = mirror, 1 = rough
|
||||
mat.normal.contents = UIImage(named: "normal") // Normal map
|
||||
mat.ambientOcclusion.contents = UIImage(named: "ao") // AO map
|
||||
mat.emission.contents = UIColor.blue // Glow
|
||||
mat.displacement.contents = UIImage(named: "height") // Height map
|
||||
|
||||
// Options
|
||||
mat.isDoubleSided = false // Render both sides
|
||||
mat.writesToDepthBuffer = true
|
||||
mat.readsFromDepthBuffer = true
|
||||
mat.blendMode = .alpha // .add, .subtract, .multiply, .screen
|
||||
mat.transparencyMode = .aOne // .rgbZero for pre-multiplied alpha
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Physics
|
||||
|
||||
### Body Types and Properties
|
||||
|
||||
```swift
|
||||
// Dynamic body with custom shape
|
||||
let shape = SCNPhysicsShape(geometry: SCNSphere(radius: 0.5), options: nil)
|
||||
let body = SCNPhysicsBody(type: .dynamic, shape: shape)
|
||||
body.mass = 1.0
|
||||
body.friction = 0.5
|
||||
body.restitution = 0.3 // Bounciness
|
||||
body.damping = 0.1 // Linear damping
|
||||
body.angularDamping = 0.1 // Angular damping
|
||||
body.isAffectedByGravity = true
|
||||
body.allowsResting = true // Sleep optimization
|
||||
node.physicsBody = body
|
||||
|
||||
// Compound shapes
|
||||
let compound = SCNPhysicsShape(shapes: [shape1, shape2],
|
||||
transforms: [transform1, transform2])
|
||||
|
||||
// Concave (static only)
|
||||
let concave = SCNPhysicsShape(geometry: mesh, options: [
|
||||
.type: SCNPhysicsShape.ShapeType.concavePolyhedron
|
||||
])
|
||||
```
|
||||
|
||||
### Joint Types
|
||||
|
||||
| Joint | Description |
|
||||
|-------|-------------|
|
||||
| `SCNPhysicsHingeJoint` | Single-axis rotation (door) |
|
||||
| `SCNPhysicsBallSocketJoint` | Free rotation around point (pendulum) |
|
||||
| `SCNPhysicsSliderJoint` | Linear movement along axis (drawer) |
|
||||
| `SCNPhysicsConeTwistJoint` | Limited rotation (ragdoll limb) |
|
||||
|
||||
---
|
||||
|
||||
## Part 5: Animation API
|
||||
|
||||
### SCNAction Catalog
|
||||
|
||||
| Category | Actions |
|
||||
|----------|---------|
|
||||
| Movement | `move(by:duration:)`, `move(to:duration:)` |
|
||||
| Rotation | `rotate(by:around:duration:)`, `rotateTo(x:y:z:duration:)` |
|
||||
| Scale | `scale(by:duration:)`, `scale(to:duration:)` |
|
||||
| Fade | `fadeIn(duration:)`, `fadeOut(duration:)`, `fadeOpacity(to:duration:)` |
|
||||
| Visibility | `hide()`, `unhide()` |
|
||||
| Audio | `playAudio(source:waitForCompletion:)` |
|
||||
| Custom | `run { node in }`, `customAction(duration:action:)` |
|
||||
| Composition | `sequence([])`, `group([])`, `repeat(_:count:)`, `repeatForever(_:)` |
|
||||
| Control | `wait(duration:)`, `removeFromParentNode()` |
|
||||
|
||||
### Timing Functions
|
||||
|
||||
```swift
|
||||
action.timingMode = .linear // Default
|
||||
action.timingMode = .easeIn // Slow start
|
||||
action.timingMode = .easeOut // Slow end
|
||||
action.timingMode = .easeInEaseOut // Slow start and end
|
||||
action.timingFunction = { t in // Custom curve
|
||||
return t * t // Quadratic ease-in
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 6: Constraints
|
||||
|
||||
| Constraint | Purpose |
|
||||
|------------|---------|
|
||||
| `SCNLookAtConstraint` | Node always faces target |
|
||||
| `SCNBillboardConstraint` | Node always faces camera |
|
||||
| `SCNDistanceConstraint` | Maintains min/max distance |
|
||||
| `SCNReplicatorConstraint` | Copies transform of target |
|
||||
| `SCNAccelerationConstraint` | Smooths transform changes |
|
||||
| `SCNSliderConstraint` | Locks to axis |
|
||||
| `SCNIKConstraint` | Inverse kinematics chain |
|
||||
|
||||
```swift
|
||||
let lookAt = SCNLookAtConstraint(target: targetNode)
|
||||
lookAt.isGimbalLockEnabled = true // Prevent roll
|
||||
lookAt.influenceFactor = 0.8 // Partial constraint
|
||||
node.constraints = [lookAt]
|
||||
```
|
||||
|
||||
**In RealityKit**: No direct constraint system. Implement with `System` update logic or `entity.look(at:from:relativeTo:)`.
|
||||
|
||||
---
|
||||
|
||||
## Part 7: Scene Configuration
|
||||
|
||||
### SCNView Configuration
|
||||
|
||||
| Property | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `antialiasingMode` | `.multisampling4X` | MSAA level |
|
||||
| `preferredFramesPerSecond` | 60 | Target frame rate |
|
||||
| `allowsCameraControl` | `false` | Built-in orbit/pan/zoom |
|
||||
| `autoenablesDefaultLighting` | `false` | Add default light if none |
|
||||
| `showsStatistics` | `false` | FPS/node/draw count overlay |
|
||||
| `isTemporalAntialiasingEnabled` | `false` | TAA smoothing |
|
||||
| `isJitteringEnabled` | `false` | Temporal jitter for TAA |
|
||||
| `debugOptions` | `[]` | `.showPhysicsShapes`, `.showBoundingBoxes`, `.renderAsWireframe` |
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
**WWDC**: 2014-609, 2014-610, 2017-604, 2019-612
|
||||
|
||||
**Docs**: /scenekit, /scenekit/scnscene, /scenekit/scnnode, /scenekit/scnmaterial, /scenekit/scnphysicsbody, /scenekit/scnaction
|
||||
|
||||
**Skills**: axiom-scenekit, axiom-realitykit, axiom-realitykit-ref
|
||||
3
.claude/skills/axiom-scenekit-ref/agents/openai.yaml
Normal file
3
.claude/skills/axiom-scenekit-ref/agents/openai.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "SceneKit Reference"
|
||||
short_description: "SceneKit → RealityKit concept mapping, complete API cross-reference for migration, scene graph API, materials, lighti..."
|
||||
Reference in New Issue
Block a user