@@ -18,15 +18,15 @@ import { EditorState } from "./state_manager";
1818
1919import { parse_obj } from "../utils/parser" ;
2020import * as primitives from "../rendering/utils/primitives" ;
21-
21+ import { Rotator , Oscillator , Orbit } from "./animation" ;
2222
2323export class SceneManager {
2424 public scene : Scene ;
2525 public nodes : Node [ ] = [ ] ;
2626
2727 private viewport : Viewport ;
2828 private inspector : Inspector ;
29- private editor_state :EditorState
29+ private editor_state : EditorState
3030
3131 private target_element : HTMLElement ;
3232 private wireframe_checkbox : HTMLInputElement ;
@@ -36,15 +36,20 @@ export class SceneManager {
3636 private animation_id : number | null = null ;
3737 public selected_node : Node | null = null ;
3838
39- private last_mx :number = 0 ;
40- private last_my :number = 0 ;
39+ private last_mx : number = 0 ;
40+ private last_my : number = 0 ;
4141
4242 private is_dragging = false ;
4343
4444 public current_triangles : number = 0 ;
4545 public current_capacity : number = 50000 ;
4646 public readonly MAX_TRIANGLES : number = 100000 ;
4747
48+ private is_playing = false ;
49+ private current_time = 0 ;
50+ private last_frame_time = 0 ;
51+ private initial_states : Map < Node , any > = new Map ( ) ;
52+
4853 constructor (
4954 svg_container_id : string ,
5055 toolbar_id : string ,
@@ -65,25 +70,49 @@ export class SceneManager {
6570
6671 this . viewport = new Viewport ( svg_container_id ) ;
6772 this . inspector = new Inspector ( inspector_id ) ;
68- this . editor_state = new EditorState ( this . inspector , this . scene ) ;
73+ this . editor_state = new EditorState ( this . inspector , this . scene ) ;
6974
7075 this . setup_lights ( ) ;
7176 this . update_stats_ui ( ) ;
7277 this . setup_raycasting ( ) ;
7378
74- initialize_toolbar ( toolbar_id , options_id , ( geo : Geometry ) => {
75- this . add_node ( geo ) ;
76- } , ( ) => this . add_point_light ( ) ) ;
79+ initialize_toolbar (
80+ toolbar_id ,
81+ options_id ,
82+ ( geo : Geometry , color : number [ ] ) => {
83+ this . add_node ( geo , color ) ;
84+ } ,
85+ ( intensity : number , radius : number , color : number [ ] ) => {
86+ this . add_point_light ( intensity , radius , color ) ;
87+ } ,
88+ ( action : "play" | "pause" | "stop" ) => {
89+ this . handle_playback ( action ) ;
90+ } ,
91+ ( type : string , params : number [ ] ) => {
92+ if ( ! this . selected_node ) {
93+ alert ( "Select a node first!" ) ;
94+ return ;
95+ }
96+ const any_node = this . selected_node as any ;
97+ if ( ! any_node . animations ) any_node . animations = [ ] ;
98+
99+ if ( type === "Rotator" ) any_node . animations . push ( new Rotator ( params [ 0 ] , params [ 1 ] ) ) ;
100+ else if ( type === "Oscillator" ) any_node . animations . push ( new Oscillator ( params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] ) ) ;
101+ else if ( type === "Orbit" ) any_node . animations . push ( new Orbit ( params [ 0 ] , params [ 1 ] , params [ 2 ] , params [ 3 ] ) ) ;
102+
103+ this . inspector . inspect ( this . selected_node ) ;
104+ }
105+ ) ;
77106 }
78107
79108 private setup_lights ( ) {
80109 const sun_light = new Light ( vec3 ( 10 , 10 , 10 ) , vec3 ( 1.0 , 0.95 , 0.9 ) , 3.0 , 200.0 ) ;
81- const leskow_light = new Light ( vec3 ( 5 , 0 , 0 ) , vec3 ( 0.8 , 0.2 , 0.0 ) , 2.0 , 100.0 ) ;
110+ const leskow_light = new Light ( vec3 ( 5 , 0 , 0 ) , vec3 ( 0.8 , 0.2 , 0.0 ) , 2.0 , 100.0 ) ;
82111 this . scene . add_light ( sun_light ) ;
83112 this . scene . add_light ( leskow_light ) ;
84113 }
85114
86- public add_node ( geo : Geometry ) {
115+ public add_node ( geo : Geometry , color_arr : number [ ] = [ 0.7 , 0.7 , 0.7 ] ) {
87116 const new_triangles = geo . indices . length / 3 ;
88117
89118 if ( this . current_triangles + new_triangles > this . MAX_TRIANGLES ) {
@@ -95,7 +124,7 @@ export class SceneManager {
95124 this . expand_capacity ( this . current_triangles + new_triangles ) ;
96125 }
97126
98- const mesh = this . scene . add_mesh ( geo , vec3 ( 0.7 , 0.7 , 0.7 ) , 0.5 ) ;
127+ const mesh = this . scene . add_mesh ( geo , vec3 ( color_arr [ 0 ] , color_arr [ 1 ] , color_arr [ 2 ] ) , 0.5 ) ;
99128
100129 const node = new Node ( mesh ) ;
101130 this . nodes . push ( node ) ;
@@ -106,13 +135,14 @@ export class SceneManager {
106135 this . select_node ( node ) ;
107136 }
108137
109- public add_point_light ( ) {
110- const new_light = new Light ( vec3 ( 0 , 0 , 0 ) , vec3 ( 1.0 , 1.0 , 1.0 ) , 2.0 , 50.0 ) ;
138+ public add_point_light ( intensity : number , radius : number , color_arr : number [ ] = [ 1.0 , 1.0 , 1.0 ] ) {
139+ const light_color = vec3 ( color_arr [ 0 ] , color_arr [ 1 ] , color_arr [ 2 ] ) ;
140+ const new_light = new Light ( vec3 ( 0 , 0 , 0 ) , light_color , intensity , radius ) ;
111141 this . scene . add_light ( new_light ) ;
112142
113143 const bulb_geo = primitives . create_sphere ( 0.2 , 8 , 8 ) ;
114144
115- const mesh = this . scene . add_mesh ( bulb_geo , vec3 ( 1.0 , 1.0 , 0.0 ) , 0.5 ) ;
145+ const mesh = this . scene . add_mesh ( bulb_geo , light_color , 0.5 ) ;
116146
117147 const light_node = new Node ( mesh , new_light ) ;
118148
@@ -125,7 +155,6 @@ export class SceneManager {
125155 if ( new_capacity < required_triangles ) new_capacity = required_triangles ;
126156 if ( new_capacity > this . MAX_TRIANGLES ) new_capacity = this . MAX_TRIANGLES ;
127157
128-
129158 this . scene . resize_buffers ( new_capacity ) ;
130159 this . string_buffer . resize ( new_capacity * 60 ) ;
131160 this . current_capacity = new_capacity ;
@@ -148,14 +177,74 @@ export class SceneManager {
148177 this . editor_state . select ( this . selected_node ) ;
149178 }
150179
180+ public handle_playback ( action : "play" | "pause" | "stop" ) {
181+ if ( action === "play" ) {
182+ if ( ! this . is_playing ) {
183+ this . last_frame_time = performance . now ( ) ;
184+ this . is_playing = true ;
185+ for ( const node of this . nodes ) {
186+ if ( ! this . initial_states . has ( node ) ) {
187+ this . initial_states . set ( node , {
188+ pos : [ ...node . position ] ,
189+ rot : [ ...node . rotation ]
190+ } ) ;
191+ }
192+ }
193+ }
194+ } else if ( action === "pause" ) {
195+ this . is_playing = false ;
196+ } else if ( action === "stop" ) {
197+ this . is_playing = false ;
198+ this . current_time = 0 ;
199+ for ( const node of this . nodes ) {
200+ const state = this . initial_states . get ( node ) ;
201+ if ( state ) {
202+ node . position [ 0 ] = state . pos [ 0 ] ;
203+ node . position [ 1 ] = state . pos [ 1 ] ;
204+ node . position [ 2 ] = state . pos [ 2 ] ;
205+ node . rotation [ 0 ] = state . rot [ 0 ] ;
206+ node . rotation [ 1 ] = state . rot [ 1 ] ;
207+ node . rotation [ 2 ] = state . rot [ 2 ] ;
208+ node . update_matrix ( ) ;
209+ }
210+ }
211+ this . initial_states . clear ( ) ;
212+ if ( this . selected_node ) {
213+ this . editor_state . update_gizmo_transforms ( ) ;
214+ this . inspector . inspect ( this . selected_node ) ;
215+ }
216+ }
217+ }
218+
151219 public start ( ) {
152220 if ( this . animation_id !== null ) {
153221 cancelAnimationFrame ( this . animation_id ) ;
154222 }
223+ this . last_frame_time = performance . now ( ) ;
155224 this . loop ( ) ;
156225 }
157226
158227 private loop = ( ) => {
228+ const now = performance . now ( ) ;
229+ const dt = ( now - this . last_frame_time ) / 1000.0 ;
230+ this . last_frame_time = now ;
231+
232+ if ( this . is_playing ) {
233+ this . current_time += dt ;
234+ for ( const node of this . nodes ) {
235+ const any_node = node as any ;
236+ if ( any_node . animations && any_node . animations . length > 0 ) {
237+ for ( const anim of any_node . animations ) {
238+ anim . apply ( node , this . current_time , dt ) ;
239+ }
240+ node . update_matrix ( ) ;
241+ }
242+ }
243+ if ( this . selected_node && ( this . selected_node as any ) . animations && ( this . selected_node as any ) . animations . length > 0 ) {
244+ this . inspector . inspect ( this . selected_node ) ;
245+ }
246+ }
247+
159248 const camera_pos = this . viewport . camera_pos ;
160249 const view = this . viewport . get_view_matrix ( ) ;
161250 const projection = this . viewport . get_projection ( ) ;
@@ -230,6 +319,7 @@ export class SceneManager {
230319 directional_vector : direction
231320 } ;
232321 }
322+
233323 private setup_raycasting ( ) {
234324 this . target_element . addEventListener ( "mousedown" , ( e : MouseEvent ) => {
235325 if ( e . button !== 0 ) return ;
@@ -239,24 +329,25 @@ export class SceneManager {
239329 this . last_my = e . clientY ;
240330
241331 const ray = this . get_mouse_ray ( e ) ;
242- if ( ! ray ) return ;
332+ if ( ! ray ) return ;
243333
244334 this . editor_state . handle_mouse_down ( ray , e . clientX , e . clientY , this . nodes ) ;
245335 this . selected_node = this . editor_state . selected_node ;
246336 } ) ;
337+
247338 window . addEventListener ( "mousemove" , ( e : MouseEvent ) => {
248- if ( ! this . is_dragging && this . editor_state . selected_node ) {
339+ if ( ! this . is_dragging && this . editor_state . selected_node ) {
249340 const ray = this . get_mouse_ray ( e ) ;
250- if ( ! ray ) return ;
341+ if ( ! ray ) return ;
251342 const is_hovering =
252- this . editor_state . gizmo_x . intersects_with ( ray ) ||
253- this . editor_state . gizmo_y . intersects_with ( ray ) ||
254- this . editor_state . gizmo_z . intersects_with ( ray ) ;
343+ this . editor_state . gizmo_x . intersects_with ( ray ) ||
344+ this . editor_state . gizmo_y . intersects_with ( ray ) ||
345+ this . editor_state . gizmo_z . intersects_with ( ray ) ;
255346 this . target_element . style . cursor = is_hovering ? "pointer" : "default" ;
256347 return ;
257348 }
258349
259- if ( ! this . is_dragging ) return ;
350+ if ( ! this . is_dragging ) return ;
260351
261352 const dx = e . clientX - this . last_mx ;
262353 const dy = e . clientY - this . last_my ;
@@ -267,14 +358,15 @@ export class SceneManager {
267358 if ( this . editor_state . mode !== "IDLE" ) {
268359 const view = this . viewport . get_view_matrix ( ) ;
269360 const projection = this . viewport . get_projection ( ) ;
270- const vp = mul_mat4 ( projection , view ) ;
361+ const vp = mul_mat4 ( projection , view ) ;
271362
272- const { width, height} = this . viewport . get_dimensions ( ) ;
273- this . editor_state . handle_mouse_move ( e . clientX , e . clientY , vp , width , height ) ;
363+ const { width, height } = this . viewport . get_dimensions ( ) ;
364+ this . editor_state . handle_mouse_move ( e . clientX , e . clientY , vp , width , height ) ;
274365 } else {
275366 this . viewport . orbit ( dx , dy ) ;
276367 }
277368 } ) ;
369+
278370 window . addEventListener ( "mouseup" , ( ) => {
279371 this . is_dragging = false ;
280372 this . editor_state . handle_mouse_up ( ) ;
0 commit comments