
Reality Composer Pro의 새로운 Timeline이라는 기능을 소개할 것이다.
작년에 3D content을 쉽게 미리보고 준비할 수 있는 Reality Composer Pro를 발표했다.
이는 scene을 구성하고, asset을 정렬하고, interactivity, 물리를 추가하고 심지어 ShaderGraph editor를 통해 material을 미세 조정할 수 있는 visual editor로 구성되어 있다.
Reality Composer Pro에 대해 알고 싶다면 이 영상을 봐라.

올해 새로운 기능들이 추가되었다. 이 세션에서는 timeline editor에 주목할 것이다.

다른 세션에서 Botanist app을 보았을 것이다.

timeline은 Reality Composer Pro의 새로운 기능이다.
이는 특정한 순서 혹은 시간대로 순차적인 동작이 일어나게 해준다.

좌측 패널에서는 timeline의 목록을 확인할 수 있다.

중앙은 main timeline editor고

우측 패널에서는 가능한 액션들을 확인할 수 있다.

타임라인을 생성하면 trigger에 기반하여 timeline을 시작할 수 있다.
timeline 이름을 더블탭하여 이름을 변경해준다.

탭한 방향을 볼 수 있게 spin action을 추가해준다.

inspector panel에서 target을 선택한다.

hierarchy나 viewport에서 robot entity를 찾아 선택해준다.

inspector panel에서 revolution 값을 설정하여 살짝 회전하도록 한다.

1초 뒤에 움직이도록 transform action을 추가한다.

로봇의 목적지를 설정한다.

바 길이를 조정하거나 inspector panel에서 duration을 조정하여 소요 시간을 조절한다.

다른 트랙에 action을 두면 여러 액션이 동시에 실행될 수 있다.

로봇의 움직임, 소리를 추가하기 위한 새로운 타임라인 생성.

이를 위해 이 두가지가 필요하다.

AnimationLibraryComponent는 애니메이션을 저장하고 애니메이션을 재생할 entity와 연관시키는 데 사용된다.
rigging된 entity에 AnimationLibraryComponent를 추가한 뒤 animation resource를 AnimationLibraryComponent에 추가하면 된다.

plus 버튼을 눌러서 프로젝트에 있는 애니메이션을 선택한다. 이것은 animation resource를 해당 entity의 AnimationLibraryComponent에 추가한다. 이는 나중에 코드로 애니메이션을 재생하거나 Reality Composer Pro에서 타임라인을 사용할 때 사용할 수 있다.

AudioLibaryComponent는 AnimationLibraryComponent와 매우 유사하게 동작한다. AudioLibaryComponent는 오디오 리소스를 저장하고 이를 재생할 entity 연결에 사용한다. AudioLibaryComponent를 entity에 추가한 다음 오디오 리소스를 여기에 추가하면 된다. AudioLibaryComponent에 오디오 리소르를 추가하려면 + 버튼을 누른 뒤 프로젝트에서 오디오 파일을 선택한다. 이 리소스는 나중에 코드에서 오디오를 재생하거나 타임라인을 사용할 때 사용 할 수 있다.

Animation Library Component가 이미 추가되어 있다. 이 Entity에 이미 USD animation이 연결되어 있기 때문이다. Reality Composer Pro는 컴포넌트를 자동으로 추가하고 애니메이션 목록에 컴포넌트의 기본 애니메이션을 표시한다. Animation Library Component는 수동으로 추가할 수도 있다.

가위를 통해 더 짧은 애니메이션을 만들 수 있다.

잘라서 연속 재생 가능한 두개의 클립을 생성.

연속 재생을 위해 timeline에 animation action을 추가하고

타겟을 설정한다.

그리고 애니메이션을 startWalk로 설정한다.

두번째 액션 추가.

똑같은 대상을 타겟으로 하고 endWalk를 애니메이션으로 추가한다.

오디오도 동시에 재생하기 위해서는 AudioLibaryComponent와 프로젝트에 미리 추가해 둔 오디오 파일을 사용해야 한다.
타겟 entity에 AudioLibaryComponent를 추가한다.
두개의 오디오를 추가했다.

audio aciton을 타임라인으로 드래그한다.

로봇에서 소리가 나도록 로봇을 Emitter로 선택하고 방금 추가한 오디오 리소스를 추가한다.

걷기와 함께 진행되도록 타이밍을 수정한다.

MoveToPoppy timeline을 선택하고 RobotMove timeline을 우클릭한 다음 Insert into Timeline 옵션을 선택한다.

이렇게하면 RobotMove timeline이 MoveToPoppy timeline에 삽입된다.

액션 흐름 조정.

회전할 때 소리도 추가하겠다.
오디오 액션을 추가하고 emiiter, audio resource를 설정해주었다.

이렇게 만든 타임라인은 어떻게 실행할 수 있을까?
두 가지 방법 중 하나를 사용할 수 있다.
RealityKit API를 사용하여 코드로 작성할 수도 있고, 새로운 Behaviors Component를 통해 코드를 작성하지 않고도 실행할 수 있다.

trigger entity에 Behavior component를 추가해야한다.

다양한 유형의 trigger를 사용할 수 있다.

식물에 탭 트리거 추가

탭 하면 액션 실행되도록 추가

RealityView에서 탭 제스처에 반응하도록 만들어야 함.

로봇이 식물에 도착하면 팔을 뻣는 것을 추가하려고 한다. 이를 위해 notification aciton 추가가 필요하다.
Reality Composer Pro에서 알림을 추가한다.
timeline 끝에 notification action을 추가한다.

타겟과 알림 식별자를 설정해준다.

코드에서 수신할 알림을 추가한다.

알림에 대한 publisher를 추가한다.

알람을 수신한다.

로봇이 식물에 도착하면 팔을 내밀도록 하고 싶다.

이를 위해 RealityKit의 Full Body Inverse Kinematics 시스템을 사용한다.

Inverse kinematics는 운동 방정식을 사용해 rigging된 skeletal 구조에서 원하는 위치에 도달하기 위한 joint의 동작을 결정한다. 목표 위치와 영향을 받는 Joint에 대한 제약 조건을 지정한다.

중간 joint는 자동으로 계산된다.

Inverse kinematics API에 대한 설명.
일단 IKRig를 인스턴스화한다. IKRig를 통해 Inverse kinematics 의 작동방식을 정의한다.
constraints는 joint가 변경될 수 있는 방법을 제한하여 부자연스러운 움직임을 방지하는데 사용한다.

그런 다음 IKResource를 생성한다. 이는 IK solver가 처리용으로 사용하는 런타임 데이터다.

IKComponent를 생성하고 Entity에 추가한다.

Component를 instance화 할 때 이 솔버를 설정하고 런타임에 매 프레임마다 업데이트 할 수 있다.
RealitKit의 IK solver가 하위 joint를 포함한 전체 캐릭터의 스켈레톤을 동시에 solve한다는 것을 알고 있어야 한다.
코드를 보자.
Inverse kinematics 솔버 설정을 위해 빈 Rig을 초기화하고 모델 스켈레톤을 전달한다.

글로벌 릭 설정을 업데이트한다.

릭 스켈레톤에 있는 joint 이름을 참고한다. 여기선 언덩이 가슴 왼쪽 손을 활용한다.

릭의 제약 조건을 정의한다. 두 개의 Parent와 Point 제약을 설정했다.
Parent 제약 조건은 조인트의 위치와 방향을 제약한다. Point 제약 조건은 조인트의 위치를 제약한다.

그리고 릭을 표현하는 리소스를 만들고 리소스에 IKComponent를 추가한다.

매 프레임마다 IK target을 업데이트하려면 씬에서 entity를 찾아 위치를 지정해야한다.
일단 손이 닿을 위치를 구한다. 이 위치는 IKComponent가 있는 Entity에 상대적이여야 한다.

그리고 x, y, z 위치를 변경하여 프레임마다 약간 달라지게 한다.

각 프레임에서 왼손에 대한 constraint를 업데이트 하고 싶다.
IKComponent를 가져오고, 왼손 타깃 이동과 왼손의 위치를 설정한다.

값이 0이면 IK가 제약 조건에 영향을 미치지 않고 값이 1이면 IK가 제약 조건에 전적인 영향을 미친다.

IK를 시작하고 끝낼 때 팔 에니메이션을 부드럽게 하려면 시간이 지남에 따라 위치가 IK에 의해 구동되는 정도와 기본 포즈에 의해 구동되는 정도에 대한 가중치를 늘리거나 줄여야 한다.
그 후 업데이트 된 값을 컴포넌트에 커밋한다.

물 - 파티클 효과
로봇이 시작 위치로 돌아가게 만들기.
Animation action을 사용하면 동작이 발생하는 시기를 설정하고 일정한 기간 동안 동작을 함께 시퀀싱 할 수 있다. 이는 게임의 cut scene animation과 유사하지만 Animation action을 사용하면 실시간 수행이 가능하다. 이 동작은 캐릭터가 발걸음을 딛는 경우에 많이 사용한다. 캐릭터의 발이 땅에 닿을 때 마다 소리가 난다.

actions API는 built-in acion과 custom action이 있다.
built-in acion에는 여러가지 pre-built(내장형) 액션으로 구성되어 있다.

커스텀 엑션은 나만의 동작을 코드로 작성하고 타임라인에서 동기화 할 수 있다.
built-in acion API를 사용하려면 built-in acionn을 통해 entity animation 정의를 만든다. 그리고 에니메이션 리소스를 만들고 재생할 애니메이션 리소스를 그룹화한다. 그리고 재생할 애니메이션 리소스를 특정 순서대로 시퀀싱한다. 마지막으로 entity에서 애니메이션 재생을 호출한다.

로직이 포함된 몇 가지 메소드가 있다. 이들은 애니메이션 리소스를 반환하는 역할을 한다. 이들은 RealityKit API를 사용해 정의되었다.

이 애니메이션들을 시퀀싱한다.

그리고 재생한다.

custom action은 EntityAction의 자체 프로토콜을 따른다. 그리고 makeActionAnimation API를 통해 Animation Resource를 생성한다. 그리고 애니메이션 리소스를 다른 리소스와 그룹화하거나 특정 순서로 실행되도록 시퀀싱한다. 그리고 실행!

custom action을 만들어 이동이 완료되면 이를 알리는데 사용한다.

그리고 인스턴스를 생성한다.

makeActionAnimation API를 사용해 AnimationResource를 생성한다.

custom action을 통해 여러 애니메이션을 시퀀싱하거나 그룹화할 수 있다는 것을 염두해둬라. 여기선 안할 거임.
그리고 재생.

EntityAction이 시작될 때 이벤트를 구독하도록 함. 구독 종료는 동작이 시작될 때 호출됨.

시작 이벤트가 발생하면 로봇이 목적지에 도착함을 인지하고 애니메이션을 중지한다.

그리고 RobotMoveToHomeComplete EntityAction이 종료된 경우에 이벤트를 구독한다.

종료가 호출되면 로봇의 상태를 변화시킨다.

이제 식물 애니메이션을 보겠다.
Blend Shapes animation은 한 포즈에서 다른 포즈로 부드럽게 전환하여 실사같은 움직임을 만들 수 있다.

Blend Shapes animation API는 BlendShapeWeigthsMapping, BlendShapeWeightsComponet 및 애니메이션을 실행하는 두 가지 방법인 Procedurally 혹은 USD animation 사용으로 구성되어 있다.

BlendShapeWeigthsMapping로 블렌드 타깃에 연결된 가중치 설정이 가능하다. entity에 blend target을 설정하면 각 타깃에 연결된 가중치를 가질 수 있다. 이는 0-1로 설정할 수 있다. 시간이 지남에 따라 이 값을 업데이트 해보겠다.

Blend Shapes animation API를 사용하려면 BlendShapeWeigthsMapping을 정의한 다음 BlendShapeWeightsComponet를 생성하고 컴포넌트를 엔티티에 추가한다. 컴포넌트가 엔티티에 추가되면 BlendShapeWeightsComponet에 가중치를 query하고 언제든지 업데이트 할 수 있다.

이제 RealityKit playAnimation API를 통해 Procedural로 FromToBy 혹은 샘플링된 Blend Shapes animation을 만들 수 있다. 혹은 USD Blend Shapes animation을 재생할 수 있다.
우선 entity hierarchy에서 ModelComponent가 있는 model entity를 찾는다.

해당 메시 리소스를 가져와서 BlendShapeWeigthsMapping을 생성한다.

그리고 매핑에서 BlendShapeWeightsComponet를 생성한다.

블렌드 가중치 업데이트를 위해 BlendShapeWeightsComponet를 엔티티에서 가져온다.

BlendShapeWeightsSet의 복사본으로 가중치 값을 할당한다.

BlendShapeWeightsSet의 blendWidthIndex로 모든 가중치 값을 업데이트하고 건강한 식물을 나타내는 0으로 설정한다. 건강한 상태에서 시든 상태는 easing function을 통해 값을 점차적으로 늘리거나 줄인다.

그리고 새 가중치 값을 BlendShapeWeightsComponet에 할당해 적용시킨다.

두번째 로봇은 Inverse kinematics을 통해 손을 뻗고 있고 나비는 애니메이션을 통해 날아다니고 있다. 이는 Reality Composer Pro에서 타임라인을 사용해 생성하고 나비의 위치를 수정하는 custom component를 사용한 동작이다. 로봇의 머리를 자세히보면 머리가 나비의 비행 경로를 따라 움직이고 있다. 이 움직임을 위해 Skeletal poses API를 사용한다.

일반적으로 리깅된 3D 캐릭터는 서로 연결된 뼈대로 구성된 Skeleton 구조로 구성된다. 각 뼈대는 캐릭터 또는 오브젝트의 다른 부분에 해당한다.

캐릭터에 애니메이션을 적용하려면 joint를 회전시켜서 오브젝트가 여러 자세를 취하게 하고 애니메이션을 적용한다. 이는 일반적으로 캐릭터를 걷거나 뛰게 만드는데 사용된다.

Skeletal poses API는
새로운 SkeletalPosesComponent를 추가한다.
그리고 RealityKit에서 제작한 애니메이션을 사용할 수 있다.
혹은 런타임에 스켈레톤을 수정할 수 있는데 프로그래밍 방식으로 SkeletalPosesComponent를 쿼리하고 transform update interface로 수정이 가능하다.

Skeletal poses API를 사용해 로봇의 목뼈가 나비를 보는 것 처럼 만들어보겟다.
USD 파일에서 RealityKit으로 임포트 된 모든 skinned mesh는 이미 SkeletalPosesComponent가 첨부되어 있다. 따라서 초기화 필요 없이 그냥 가져오면 된다.

joint의 회전을 업데이트한다. 이때 조인트는 로컬 스페이스에 있다. 모든 프레임에서 목 회전을 업데이트 하기 위해서 RealityKit update 함수에서 이 코드를 호출한다.

RealityKit은 모든 시스템에서 frame마다 update 함수를 호출하고 업데이트 된 값을 컴포넌트에 커밋한다. 전체 joint transform을 업데이트하거나 translation, scale, rotation만 업데이트 가능하다.

