1
1
import { NgIf } from '@angular/common' ;
2
- import { Component , CUSTOM_ELEMENTS_SCHEMA , ElementRef , inject , ViewChild } from '@angular/core' ;
2
+ import { ChangeDetectorRef , Component , CUSTOM_ELEMENTS_SCHEMA , ElementRef , inject , ViewChild } from '@angular/core' ;
3
3
import {
4
+ applyProps ,
4
5
extend ,
6
+ injectBeforeRender ,
5
7
injectNgtLoader ,
6
8
NgtArgs ,
7
- NgtBeforeRenderEvent ,
8
9
NgtCanvas ,
9
10
NgtPush ,
10
11
NgtState ,
11
12
NgtStore ,
12
13
} from 'angular-three' ;
13
14
import { map } from 'rxjs' ;
14
15
import * as THREE from 'three' ;
15
- import { CCDIKHelper , DRACOLoader , GLTFLoader , TransformControls } from 'three-stdlib' ;
16
+ import { CCDIKHelper , CCDIKSolver , DRACOLoader , GLTFLoader , IKS , OrbitControls , TransformControls } from 'three-stdlib' ;
16
17
import { DemoOrbitControls } from '../ui-orbit-controls/orbit-controls.component' ;
17
- import { AnimationSkinningIKStore } from './animation-skinning-ik.store' ;
18
18
19
19
extend ( { TransformControls, CCDIKHelper } ) ;
20
20
21
21
@Component ( {
22
- selector : 'demo-ik-helper' ,
23
22
standalone : true ,
24
23
template : `
25
- <ng-container *ngIf="(ooi$ | ngtPush).kira as kira">
26
- <ngt-cCDIK-helper *args="[kira, iks, 0.01]" (beforeRender)="onBeforeRender()" />
27
- </ng-container>
28
- ` ,
29
- imports : [ NgtArgs , NgIf , NgtPush ] ,
30
- schemas : [ CUSTOM_ELEMENTS_SCHEMA ] ,
31
- } )
32
- export class IKHelper {
33
- private readonly ikStore = inject ( AnimationSkinningIKStore ) ;
34
- readonly iks = this . ikStore . iks ;
35
- readonly ooi$ = this . ikStore . select ( 'OOI' ) ;
24
+ <ngt-color *args="['#dddddd']" attach="background" />
25
+ <ngt-fog-exp2 *args="['#ffffff', 0.17]" attach="fog" />
36
26
37
- onBeforeRender ( ) {
38
- this . ikStore . solver . update ( ) ;
39
- }
40
- }
27
+ <ngt-ambient-light [intensity]="8" color="#ffffff" />
41
28
42
- @Component ( {
43
- selector : 'demo-sphere-camera' ,
44
- standalone : true ,
45
- template : `
46
- <ngt-cube-camera *args="[0.05, 50, cubeRenderTarget]" (beforeRender)="onBeforeRender($any($event))" />
47
- ` ,
48
- imports : [ NgtArgs ] ,
49
- schemas : [ CUSTOM_ELEMENTS_SCHEMA ] ,
50
- } )
51
- export class SphereCamera {
52
- private readonly ikStore = inject ( AnimationSkinningIKStore ) ;
53
- readonly cubeRenderTarget = this . ikStore . get ( 'cubeRenderTarget' ) ;
54
-
55
- onBeforeRender ( { object, state : { gl, scene } } : NgtBeforeRenderEvent < THREE . CubeCamera > ) {
56
- const sphere = this . ikStore . OOI . sphere ;
57
- if ( sphere ) {
58
- sphere . visible = false ;
59
- sphere . getWorldPosition ( object . position ) ;
60
- object . update ( gl , scene ) ;
61
- sphere . visible = true ;
62
- }
63
- }
64
- }
29
+ <ngt-primitive *args="[model$ | ngtPush : null]" (afterAttach)="onAfterModelAttach()" />
65
30
66
- @Component ( {
67
- selector : 'demo-kira' ,
68
- standalone : true ,
69
- template : `
70
- <ngt-primitive
71
- *args="[model$ | ngtPush : null]"
72
- (afterAttach)="onAfterAttach()"
73
- (beforeRender)="onBeforeRender()"
31
+ <ngt-cube-camera #cubeCamera *args="[0.05, 50, cubeRenderTarget]" />
32
+
33
+ <ng-container *ngIf="ooi['kira']">
34
+ <ngt-cCDIK-helper *args="[ooi['kira'], iks, 0.01]" />
35
+ </ng-container>
36
+
37
+ <demo-orbit-controls [minDistance]="0.2" [maxDistance]="1.5" (ready)="orbitControls = $any($event)" />
38
+
39
+ <ngt-transform-controls
40
+ #transformControls
41
+ *args="[camera, glDom]"
42
+ [size]="0.75"
43
+ [showX]="false"
44
+ space="world"
74
45
/>
75
46
` ,
76
- imports : [ NgtArgs , NgtPush ] ,
47
+ imports : [ NgtArgs , NgIf , DemoOrbitControls , NgtPush ] ,
77
48
schemas : [ CUSTOM_ELEMENTS_SCHEMA ] ,
78
49
} )
79
- export class Kira {
80
- private readonly ikStore = inject ( AnimationSkinningIKStore ) ;
81
- readonly v0 = new THREE . Vector3 ( ) ;
50
+ export class Scene {
51
+ readonly iks = [
52
+ {
53
+ target : 22 , // "target_hand_l"
54
+ effector : 6 , // "hand_l"
55
+ links : [
56
+ {
57
+ index : 5 , // "lowerarm_l"
58
+ enabled : true ,
59
+ rotationMin : new THREE . Vector3 ( 1.2 , - 1.8 , - 0.4 ) ,
60
+ rotationMax : new THREE . Vector3 ( 1.7 , - 1.1 , 0.3 ) ,
61
+ } ,
62
+ {
63
+ index : 4 , // "Upperarm_l"
64
+ enabled : true ,
65
+ rotationMin : new THREE . Vector3 ( 0.1 , - 0.7 , - 1.8 ) ,
66
+ rotationMax : new THREE . Vector3 ( 1.1 , 0 , - 1.4 ) ,
67
+ } ,
68
+ ] ,
69
+ } ,
70
+ ] ;
71
+
72
+ readonly cubeRenderTarget = new THREE . WebGLCubeRenderTarget ( 1024 ) ;
73
+ readonly material = new THREE . MeshBasicMaterial ( { envMap : this . cubeRenderTarget . texture } ) ;
74
+
75
+ private readonly store = inject ( NgtStore ) ;
76
+ readonly camera = this . store . get ( 'camera' ) ;
77
+ readonly glDom = this . store . get ( 'gl' , 'domElement' ) ;
78
+
79
+ private readonly cdr = inject ( ChangeDetectorRef ) ;
80
+
81
+ @ViewChild ( 'transformControls' ) transformControls ?: ElementRef < TransformControls > ;
82
+ @ViewChild ( 'cubeCamera' ) cubeCamera ?: ElementRef < THREE . CubeCamera > ;
83
+ orbitControls ?: OrbitControls ;
84
+ solver ?: CCDIKSolver ;
85
+
86
+ private readonly v0 = new THREE . Vector3 ( ) ;
87
+ readonly ooi : Record < string , THREE . Object3D > = { } ;
82
88
83
89
readonly model$ = injectNgtLoader (
84
90
( ) => GLTFLoader ,
@@ -90,79 +96,66 @@ export class Kira {
90
96
}
91
97
) . pipe (
92
98
map ( ( gltf ) => {
93
- const ooi : Record < string , THREE . Object3D > = { } ;
94
99
gltf . scene . traverse ( ( n ) => {
95
- if ( n . name === 'head' ) ooi [ 'head' ] = n ;
96
- if ( n . name === 'lowerarm_l' ) ooi [ 'lowerarm_l' ] = n ;
97
- if ( n . name === 'Upperarm_l' ) ooi [ 'Upperarm_l' ] = n ;
98
- if ( n . name === 'hand_l' ) ooi [ 'hand_l' ] = n ;
99
- if ( n . name === 'target_hand_l' ) ooi [ 'target_hand_l' ] = n ;
100
- if ( n . name === 'boule' ) ooi [ 'sphere' ] = n ;
101
- if ( n . name === 'Kira_Shirt_left' ) ooi [ 'kira' ] = n ;
100
+ if ( n . name === 'head' ) this . ooi [ 'head' ] = n ;
101
+ if ( n . name === 'lowerarm_l' ) this . ooi [ 'lowerarm_l' ] = n ;
102
+ if ( n . name === 'Upperarm_l' ) this . ooi [ 'Upperarm_l' ] = n ;
103
+ if ( n . name === 'hand_l' ) this . ooi [ 'hand_l' ] = n ;
104
+ if ( n . name === 'target_hand_l' ) this . ooi [ 'target_hand_l' ] = n ;
105
+ if ( n . name === 'boule' ) this . ooi [ 'sphere' ] = n ;
106
+ if ( n . name === 'Kira_Shirt_left' ) this . ooi [ 'kira' ] = n ;
102
107
if ( ( n as THREE . Mesh ) . isMesh ) n . frustumCulled = false ;
103
108
} ) ;
104
- this . ikStore . set ( { OOI : ooi } ) ;
109
+ this . cdr . detectChanges ( ) ;
105
110
return gltf . scene ;
106
111
} )
107
112
) ;
108
113
109
- onAfterAttach ( ) {
110
- this . ikStore . kiraReady ( ) ;
114
+ constructor ( ) {
115
+ injectBeforeRender ( ( { gl, scene } ) => {
116
+ if ( this . ooi [ 'sphere' ] && this . cubeCamera ) {
117
+ this . ooi [ 'sphere' ] . visible = false ;
118
+ this . ooi [ 'sphere' ] . getWorldPosition ( this . cubeCamera . nativeElement . position ) ;
119
+ this . cubeCamera . nativeElement . update ( gl , scene ) ;
120
+ this . ooi [ 'sphere' ] . visible = true ;
121
+ }
122
+
123
+ if ( this . solver ) {
124
+ this . solver . update ( ) ;
125
+ }
126
+
127
+ const head = this . ooi [ 'head' ] ;
128
+ const sphere = this . ooi [ 'sphere' ] ;
129
+ if ( head && sphere ) {
130
+ sphere . getWorldPosition ( this . v0 ) ;
131
+ head . lookAt ( this . v0 ) ;
132
+ head . rotation . set ( head . rotation . x , head . rotation . y + Math . PI , head . rotation . z ) ;
133
+ }
134
+ } ) ;
111
135
}
112
136
113
- onBeforeRender ( ) {
114
- const head = this . ikStore . OOI . head ;
115
- const sphere = this . ikStore . OOI . sphere ;
116
- if ( head && sphere ) {
117
- sphere . getWorldPosition ( this . v0 ) ;
118
- head . lookAt ( this . v0 ) ;
119
- head . rotation . set ( head . rotation . x , head . rotation . y + Math . PI , head . rotation . z ) ;
137
+ onAfterModelAttach ( ) {
138
+ this . orbitControls ?. target . copy ( this . ooi [ 'sphere' ] . position ) ;
139
+ this . ooi [ 'hand_l' ] . attach ( this . ooi [ 'sphere' ] ) ;
140
+ applyProps ( this . ooi [ 'sphere' ] , { material : this . material } ) ;
141
+
142
+ this . transformControls ?. nativeElement . attach ( this . ooi [ 'target_hand_l' ] ) ;
143
+ this . ooi [ 'kira' ] . add ( ( this . ooi [ 'kira' ] as THREE . SkinnedMesh ) . skeleton . bones [ 0 ] ) ;
144
+ this . solver = new CCDIKSolver ( this . ooi [ 'kira' ] as THREE . SkinnedMesh , this . iks as unknown as IKS [ ] ) ;
145
+
146
+ if ( this . transformControls && this . orbitControls ) {
147
+ this . transformControls . nativeElement . addEventListener (
148
+ 'mouseDown' ,
149
+ ( ) => ( this . orbitControls ! . enabled = false )
150
+ ) ;
151
+ this . transformControls . nativeElement . addEventListener (
152
+ 'mouseUp' ,
153
+ ( ) => ( this . orbitControls ! . enabled = true )
154
+ ) ;
120
155
}
121
156
}
122
157
}
123
158
124
- @Component ( {
125
- standalone : true ,
126
- template : `
127
- <ngt-color *args="['#dddddd']" attach="background" />
128
- <ngt-fog-exp2 *args="['#ffffff', 0.17]" attach="fog" />
129
-
130
- <ngt-ambient-light [intensity]="8" color="#ffffff" />
131
-
132
- <demo-kira />
133
- <demo-sphere-camera />
134
- <demo-ik-helper />
135
-
136
- <demo-orbit-controls
137
- [minDistance]="0.2"
138
- [maxDistance]="1.5"
139
- (ready)="ikStore.set({ orbitControls: $any($event) })"
140
- />
141
-
142
- <ngt-transform-controls
143
- #transformControls
144
- *args="[camera, glDom]"
145
- [size]="0.75"
146
- [showX]="false"
147
- space="world"
148
- />
149
- ` ,
150
- imports : [ NgtArgs , DemoOrbitControls , Kira , SphereCamera , IKHelper ] ,
151
- schemas : [ CUSTOM_ELEMENTS_SCHEMA ] ,
152
- } )
153
- export class Scene {
154
- readonly ikStore = inject ( AnimationSkinningIKStore ) ;
155
- private readonly store = inject ( NgtStore ) ;
156
- readonly camera = this . store . get ( 'camera' ) ;
157
- readonly glDom = this . store . get ( 'gl' , 'domElement' ) ;
158
-
159
- @ViewChild ( 'transformControls' ) set transformControls ( { nativeElement } : ElementRef < TransformControls > ) {
160
- this . ikStore . set ( { transformControls : nativeElement } ) ;
161
- nativeElement . addEventListener ( 'mouseDown' , ( ) => ( this . ikStore . orbitControls . enabled = false ) ) ;
162
- nativeElement . addEventListener ( 'mouseUp' , ( ) => ( this . ikStore . orbitControls . enabled = true ) ) ;
163
- }
164
- }
165
-
166
159
@Component ( {
167
160
standalone : true ,
168
161
template : `
@@ -178,7 +171,6 @@ export class Scene {
178
171
(created)="onCreated($event)"
179
172
/>
180
173
` ,
181
- providers : [ AnimationSkinningIKStore ] ,
182
174
imports : [ NgtCanvas ] ,
183
175
} )
184
176
export default class DemoAnimationSkinningIK {
0 commit comments