I'm working with RealityKit and trying to create an animation where a red triangle rotates while maintaining contact with a black triangle at their shared vertices. Here's my current code:
struct TestView: View {
var body: some View {
RealityView {content in
var material = PhysicallyBasedMaterial()
material.baseColor = SimpleMaterial.BaseColor(tint: .pink1)
material.faceCulling = .none
var firstDescription = MeshDescriptor(name: "firstDescription")
firstDescription.positions = MeshBuffers.Positions(
[[-1, -1, 0], [1, -1, 0], [0, 1, 0]]
)
firstDescription.primitives = .triangles([0, 1, 2])
let firstEntity = ModelEntity(
mesh: try! .generate(from: [firstDescription]),
materials: [SimpleMaterial(color: .black, isMetallic: false)]
)
firstEntity.scale = .init(x: 0.1, y: 0.1, z: 0.1)
content.add(firstEntity)
var secondDescription = MeshDescriptor(name: "secondDescription")
secondDescription.positions = MeshBuffers.Positions(
[[-1, -1, 0], [0, -3, 0], [1, -1, 0]]
)
secondDescription.primitives = .triangles([0, 1, 2])
let secondEntity = ModelEntity(
mesh: try! .generate(from: [secondDescription]),
materials: [material]
)
secondEntity.scale = .init(x: 0.1, y: 0.1, z: 0.1)
secondEntity.generateCollisionShapes(recursive: true)
secondEntity.components[InputTargetComponent.self] = InputTargetComponent()
content.add(secondEntity)
}
.gesture(TapGesture()
.targetedToAnyEntity()
.onEnded({ tap in
let tapentity = tap.entity
var transform = tapentity.transform
transform.translation = [0, -0.2, 0.001]
transform.rotation = simd_quatf(angle: -.pi, axis: [1,0,0])
let animationDefinition = FromToByAnimation(to: transform,
bindTarget: .transform)
let animationViewDefinition = AnimationView(source: animationDefinition,
delay: 0,
speed: 0.5)
let animationResource = try! AnimationResource.generate(with: animationViewDefinition)
tapentity.playAnimation(animationResource)
}))
}
}
Is there a way to define an animation that maintains the vertex connection between both triangles during the rotation? Maybe using a different animation approach or calculating intermediate positions?
If the imaginary axis around which the rotation occurs (in this case I mean the X
axis) will be located at a height of Y=0
, then the rotation will be expected, i.e. during the animation there will be no separation of the base of one triangle from another. To do this, I changed the Y-axis
location by +1
meter for each vertex.
firstDescription.positions = .init([
[-1, 0, .zero], [1, 0, .zero], [0, 2, .zero]
])
secondDescription.positions = .init([
[-1, 0, .zero], [0, -2, .zero], [1, 0, .zero]
])
Also, I placed both triangles in a parent
group so that they could be moved around in space.
import SwiftUI
import RealityKit
struct TestView: View {
@State var pbr = PhysicallyBasedMaterial()
let parent = Entity()
var body: some View {
RealityView { rvc in
var firstDescription = MeshDescriptor(name: "first")
firstDescription.positions = .init(
[[-1, 0, .zero], [1, 0, .zero], [0, 2, .zero]]
)
firstDescription.primitives = .triangles([0, 1, 2])
let firstModel = ModelEntity(
mesh: try! .generate(from: [firstDescription]),
materials: [UnlitMaterial(color: .black)]
)
firstModel.scale /= 5
parent.addChild(firstModel)
parent.position = [0, 1,-1]
rvc.add(parent)
var secondDescription = MeshDescriptor(name: "second")
secondDescription.positions = .init(
[[-1, 0, .zero], [0, -2, .zero], [1, 0, .zero]]
)
secondDescription.primitives = .triangles([0, 1, 2])
pbr.baseColor = .init(tint: .red)
pbr.faceCulling = .none
let secondModel = ModelEntity(
mesh: try! .generate(from: [secondDescription]),
materials: [pbr]
)
secondModel.scale /= 5
secondModel.generateCollisionShapes(recursive: false)
secondModel.components[InputTargetComponent.self] = .init()
parent.addChild(secondModel)
}
.gesture(
TapGesture()
.targetedToAnyEntity()
.onEnded {
let entity = $0.entity
var transform = entity.transform
transform.rotation = .init(angle: -.pi, axis: [1,0,0])
let definition = FromToByAnimation(
to: transform, bindTarget: .transform
)
let animationView = AnimationView(
source: definition, speed: 1.0
)
let resource = try! AnimationResource.generate(
with: animationView
)
entity.playAnimation(resource)
}
)
}
}