1use std::cell::RefCell;
2use std::path::PathBuf;
3use std::rc::Rc;
4
5use deno_core::OpState;
6
7use crate::renderer::SpriteCommand;
8use crate::renderer::TilemapStore;
9use crate::renderer::PointLight;
10use crate::renderer::camera::CameraBounds;
11use crate::renderer::msdf::MsdfFontStore;
12
13#[derive(Clone, Debug)]
15pub enum BridgeAudioCommand {
16 LoadSound { id: u32, path: String },
17 PlaySound { id: u32, volume: f32, looping: bool },
18 StopSound { id: u32 },
19 StopAll,
20 SetMasterVolume { volume: f32 },
21
22 PlaySoundEx {
24 sound_id: u32,
25 instance_id: u64,
26 volume: f32,
27 looping: bool,
28 bus: u32,
29 pan: f32,
30 pitch: f32,
31 low_pass_freq: u32,
32 reverb_mix: f32,
33 reverb_delay_ms: u32,
34 },
35 PlaySoundSpatial {
36 sound_id: u32,
37 instance_id: u64,
38 volume: f32,
39 looping: bool,
40 bus: u32,
41 pitch: f32,
42 source_x: f32,
43 source_y: f32,
44 listener_x: f32,
45 listener_y: f32,
46 },
47 StopInstance { instance_id: u64 },
48 SetInstanceVolume { instance_id: u64, volume: f32 },
49 SetInstancePitch { instance_id: u64, pitch: f32 },
50 UpdateSpatialPositions {
51 updates: Vec<(u64, f32, f32)>, listener_x: f32,
53 listener_y: f32,
54 },
55 SetBusVolume { bus: u32, volume: f32 },
56}
57
58#[derive(Clone)]
61pub struct RenderBridgeState {
62 pub sprite_commands: Vec<SpriteCommand>,
63 pub camera_x: f32,
64 pub camera_y: f32,
65 pub camera_zoom: f32,
66 pub camera_dirty: bool,
68 pub delta_time: f64,
69 pub keys_down: std::collections::HashSet<String>,
71 pub keys_pressed: std::collections::HashSet<String>,
72 pub mouse_x: f32,
73 pub mouse_y: f32,
74 pub mouse_buttons_down: std::collections::HashSet<u8>,
75 pub mouse_buttons_pressed: std::collections::HashSet<u8>,
76 pub gamepad_buttons_down: std::collections::HashSet<String>,
78 pub gamepad_buttons_pressed: std::collections::HashSet<String>,
80 pub gamepad_axes: std::collections::HashMap<String, f32>,
82 pub gamepad_count: u32,
84 pub gamepad_name: String,
86 pub touch_points: Vec<(u64, f32, f32)>,
88 pub touch_count: u32,
90 pub texture_load_queue: Vec<(String, u32)>,
92 pub base_dir: PathBuf,
94 pub next_texture_id: u32,
96 pub texture_path_to_id: std::collections::HashMap<String, u32>,
98 pub tilemaps: TilemapStore,
100 pub ambient_light: [f32; 3],
102 pub point_lights: Vec<PointLight>,
104 pub audio_commands: Vec<BridgeAudioCommand>,
106 pub next_sound_id: u32,
108 pub sound_path_to_id: std::collections::HashMap<String, u32>,
110 pub font_texture_queue: Vec<u32>,
112 pub viewport_width: f32,
114 pub viewport_height: f32,
115 pub scale_factor: f32,
117 pub clear_color: [f32; 4],
119 pub save_dir: PathBuf,
121 pub shader_create_queue: Vec<(u32, String, String)>,
123 pub shader_param_queue: Vec<(u32, u32, [f32; 4])>,
125 pub next_shader_id: u32,
127 pub effect_create_queue: Vec<(u32, String)>,
129 pub effect_param_queue: Vec<(u32, u32, [f32; 4])>,
131 pub effect_remove_queue: Vec<u32>,
133 pub effect_clear: bool,
135 pub next_effect_id: u32,
137 pub camera_bounds: Option<CameraBounds>,
139 pub gi_enabled: bool,
141 pub gi_intensity: f32,
143 pub gi_probe_spacing: Option<f32>,
145 pub gi_interval: Option<f32>,
147 pub gi_cascade_count: Option<u32>,
149 pub emissives: Vec<[f32; 8]>,
151 pub occluders: Vec<[f32; 4]>,
153 pub directional_lights: Vec<[f32; 5]>,
155 pub spot_lights: Vec<[f32; 9]>,
157 pub msdf_fonts: MsdfFontStore,
159 pub msdf_builtin_queue: Vec<(u32, u32)>,
161 pub msdf_shader_queue: Vec<(u32, String)>,
163 pub msdf_shader_pool: Vec<u32>,
165 pub msdf_texture_load_queue: Vec<(String, u32)>,
167 pub raw_texture_upload_queue: Vec<(u32, u32, u32, Vec<u8>)>,
169 pub frame_time_ms: f64,
171 pub draw_call_count: usize,
173}
174
175impl RenderBridgeState {
176 pub fn new(base_dir: PathBuf) -> Self {
177 let save_dir = base_dir.join(".arcane").join("saves");
178 Self {
179 sprite_commands: Vec::new(),
180 camera_x: 0.0,
181 camera_y: 0.0,
182 camera_zoom: 1.0,
183 camera_dirty: false,
184 delta_time: 0.0,
185 keys_down: std::collections::HashSet::new(),
186 keys_pressed: std::collections::HashSet::new(),
187 mouse_x: 0.0,
188 mouse_y: 0.0,
189 mouse_buttons_down: std::collections::HashSet::new(),
190 mouse_buttons_pressed: std::collections::HashSet::new(),
191 gamepad_buttons_down: std::collections::HashSet::new(),
192 gamepad_buttons_pressed: std::collections::HashSet::new(),
193 gamepad_axes: std::collections::HashMap::new(),
194 gamepad_count: 0,
195 gamepad_name: String::new(),
196 touch_points: Vec::new(),
197 touch_count: 0,
198 texture_load_queue: Vec::new(),
199 base_dir,
200 next_texture_id: 1,
201 texture_path_to_id: std::collections::HashMap::new(),
202 tilemaps: TilemapStore::new(),
203 ambient_light: [1.0, 1.0, 1.0],
204 point_lights: Vec::new(),
205 audio_commands: Vec::new(),
206 next_sound_id: 1,
207 sound_path_to_id: std::collections::HashMap::new(),
208 font_texture_queue: Vec::new(),
209 viewport_width: 800.0,
210 viewport_height: 600.0,
211 scale_factor: 1.0,
212 clear_color: [0.1, 0.1, 0.15, 1.0],
213 save_dir,
214 shader_create_queue: Vec::new(),
215 shader_param_queue: Vec::new(),
216 next_shader_id: 1,
217 effect_create_queue: Vec::new(),
218 effect_param_queue: Vec::new(),
219 effect_remove_queue: Vec::new(),
220 effect_clear: false,
221 next_effect_id: 1,
222 camera_bounds: None,
223 gi_enabled: false,
224 gi_intensity: 1.0,
225 gi_probe_spacing: None,
226 gi_interval: None,
227 gi_cascade_count: None,
228 emissives: Vec::new(),
229 occluders: Vec::new(),
230 directional_lights: Vec::new(),
231 spot_lights: Vec::new(),
232 msdf_fonts: MsdfFontStore::new(),
233 msdf_builtin_queue: Vec::new(),
234 msdf_shader_queue: Vec::new(),
235 msdf_shader_pool: Vec::new(),
236 msdf_texture_load_queue: Vec::new(),
237 raw_texture_upload_queue: Vec::new(),
238 frame_time_ms: 0.0,
239 draw_call_count: 0,
240 }
241 }
242}
243
244#[deno_core::op2(fast)]
247pub fn op_draw_sprite(
248 state: &mut OpState,
249 texture_id: u32,
250 x: f64,
251 y: f64,
252 w: f64,
253 h: f64,
254 layer: i32,
255 uv_x: f64,
256 uv_y: f64,
257 uv_w: f64,
258 uv_h: f64,
259 tint_r: f64,
260 tint_g: f64,
261 tint_b: f64,
262 tint_a: f64,
263 rotation: f64,
264 origin_x: f64,
265 origin_y: f64,
266 flip_x: f64,
267 flip_y: f64,
268 opacity: f64,
269 blend_mode: f64,
270 shader_id: f64,
271) {
272 let cmd = SpriteCommand {
273 texture_id,
274 x: x as f32,
275 y: y as f32,
276 w: w as f32,
277 h: h as f32,
278 layer,
279 uv_x: uv_x as f32,
280 uv_y: uv_y as f32,
281 uv_w: uv_w as f32,
282 uv_h: uv_h as f32,
283 tint_r: tint_r as f32,
284 tint_g: tint_g as f32,
285 tint_b: tint_b as f32,
286 tint_a: tint_a as f32,
287 rotation: rotation as f32,
288 origin_x: origin_x as f32,
289 origin_y: origin_y as f32,
290 flip_x: flip_x != 0.0,
291 flip_y: flip_y != 0.0,
292 opacity: opacity as f32,
293 blend_mode: (blend_mode as u8).min(3),
294 shader_id: shader_id as u32,
295 };
296 let active_target = {
298 use super::target_ops::TargetState;
299 let ts = state.borrow::<Rc<RefCell<TargetState>>>();
300 ts.borrow().active_target
301 };
302 if let Some(target_id) = active_target {
303 use super::target_ops::TargetState;
304 let ts = state.borrow::<Rc<RefCell<TargetState>>>();
305 ts.borrow_mut()
306 .target_sprite_queues
307 .entry(target_id)
308 .or_default()
309 .push(cmd);
310 } else {
311 let bridge = state.borrow::<Rc<RefCell<RenderBridgeState>>>();
312 bridge.borrow_mut().sprite_commands.push(cmd);
313 }
314}
315
316#[deno_core::op2(fast)]
318pub fn op_clear_sprites(state: &mut OpState) {
319 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
320 bridge.borrow_mut().sprite_commands.clear();
321}
322
323pub const SPRITE_STRIDE: usize = 22;
328
329#[deno_core::op2(fast)]
333pub fn op_submit_sprite_batch(state: &mut OpState, #[buffer] data: &[u8]) {
334 let floats: &[f32] = bytemuck::cast_slice(data);
335 let sprite_count = floats.len() / SPRITE_STRIDE;
336
337 let active_target = {
339 use super::target_ops::TargetState;
340 let ts = state.borrow::<Rc<RefCell<TargetState>>>();
341 ts.borrow().active_target
342 };
343
344 let parse_cmd = |s: &[f32]| SpriteCommand {
345 texture_id: s[0].to_bits(),
346 x: s[1],
347 y: s[2],
348 w: s[3],
349 h: s[4],
350 layer: s[5].to_bits() as i32,
351 uv_x: s[6],
352 uv_y: s[7],
353 uv_w: s[8],
354 uv_h: s[9],
355 tint_r: s[10],
356 tint_g: s[11],
357 tint_b: s[12],
358 tint_a: s[13],
359 rotation: s[14],
360 origin_x: s[15],
361 origin_y: s[16],
362 flip_x: s[17] != 0.0,
363 flip_y: s[18] != 0.0,
364 opacity: s[19],
365 blend_mode: (s[20] as u8).min(3),
366 shader_id: s[21].to_bits(),
367 };
368
369 if let Some(target_id) = active_target {
370 use super::target_ops::TargetState;
371 let ts = state.borrow::<Rc<RefCell<TargetState>>>();
372 let mut ts = ts.borrow_mut();
373 let queue = ts.target_sprite_queues.entry(target_id).or_default();
374 queue.reserve(sprite_count);
375 for i in 0..sprite_count {
376 let base = i * SPRITE_STRIDE;
377 queue.push(parse_cmd(&floats[base..base + SPRITE_STRIDE]));
378 }
379 } else {
380 let bridge = state.borrow::<Rc<RefCell<RenderBridgeState>>>();
381 let mut b = bridge.borrow_mut();
382 b.sprite_commands.reserve(sprite_count);
383 for i in 0..sprite_count {
384 let base = i * SPRITE_STRIDE;
385 b.sprite_commands.push(parse_cmd(&floats[base..base + SPRITE_STRIDE]));
386 }
387 }
388}
389
390#[deno_core::op2(fast)]
393pub fn op_set_camera(state: &mut OpState, x: f64, y: f64, zoom: f64) {
394 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
395 let mut b = bridge.borrow_mut();
396 b.camera_x = x as f32;
397 b.camera_y = y as f32;
398 b.camera_zoom = zoom as f32;
399 b.camera_dirty = true;
400}
401
402#[deno_core::op2]
404#[serde]
405pub fn op_get_camera(state: &mut OpState) -> Vec<f64> {
406 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
407 let b = bridge.borrow();
408 vec![b.camera_x as f64, b.camera_y as f64, b.camera_zoom as f64]
409}
410
411#[deno_core::op2(fast)]
414pub fn op_load_texture(state: &mut OpState, #[string] path: &str) -> u32 {
415 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
416 let mut b = bridge.borrow_mut();
417
418 let resolved = if std::path::Path::new(path).is_absolute() {
420 path.to_string()
421 } else {
422 b.base_dir.join(path).to_string_lossy().to_string()
423 };
424
425 if let Some(&id) = b.texture_path_to_id.get(&resolved) {
427 return id;
428 }
429
430 let id = b.next_texture_id;
431 b.next_texture_id += 1;
432 b.texture_path_to_id.insert(resolved.clone(), id);
433 b.texture_load_queue.push((resolved, id));
434 id
435}
436
437#[deno_core::op2(fast)]
439pub fn op_is_key_down(state: &mut OpState, #[string] key: &str) -> bool {
440 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
441 bridge.borrow().keys_down.contains(key)
442}
443
444#[deno_core::op2(fast)]
446pub fn op_is_key_pressed(state: &mut OpState, #[string] key: &str) -> bool {
447 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
448 bridge.borrow().keys_pressed.contains(key)
449}
450
451#[deno_core::op2]
453#[serde]
454pub fn op_get_mouse_position(state: &mut OpState) -> Vec<f64> {
455 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
456 let b = bridge.borrow();
457 vec![b.mouse_x as f64, b.mouse_y as f64]
458}
459
460#[deno_core::op2(fast)]
463pub fn op_is_mouse_button_down(state: &mut OpState, button: u8) -> bool {
464 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
465 bridge.borrow().mouse_buttons_down.contains(&button)
466}
467
468#[deno_core::op2(fast)]
471pub fn op_is_mouse_button_pressed(state: &mut OpState, button: u8) -> bool {
472 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
473 bridge.borrow().mouse_buttons_pressed.contains(&button)
474}
475
476#[deno_core::op2(fast)]
478pub fn op_get_delta_time(state: &mut OpState) -> f64 {
479 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
480 bridge.borrow().delta_time
481}
482
483#[deno_core::op2(fast)]
486pub fn op_create_solid_texture(
487 state: &mut OpState,
488 #[string] name: &str,
489 r: u32,
490 g: u32,
491 b: u32,
492 a: u32,
493) -> u32 {
494 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
495 let mut br = bridge.borrow_mut();
496
497 let key = format!("__solid__{name}");
498 if let Some(&id) = br.texture_path_to_id.get(&key) {
499 return id;
500 }
501
502 let id = br.next_texture_id;
503 br.next_texture_id += 1;
504 br.texture_path_to_id.insert(key.clone(), id);
505 br.texture_load_queue
507 .push((format!("__solid__:{name}:{r}:{g}:{b}:{a}"), id));
508 id
509}
510
511#[deno_core::op2(fast)]
514pub fn op_upload_rgba_texture(
515 state: &mut OpState,
516 #[string] name: &str,
517 width: f64,
518 height: f64,
519 #[buffer] pixels: &[u8],
520) -> u32 {
521 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
522 let mut b = bridge.borrow_mut();
523
524 let key = format!("__raw__:{name}");
525 if let Some(&id) = b.texture_path_to_id.get(&key) {
526 return id;
527 }
528
529 let id = b.next_texture_id;
530 b.next_texture_id += 1;
531 b.texture_path_to_id.insert(key, id);
532 b.raw_texture_upload_queue.push((
533 id,
534 width as u32,
535 height as u32,
536 pixels.to_vec(),
537 ));
538 id
539}
540
541#[deno_core::op2(fast)]
543pub fn op_create_tilemap(
544 state: &mut OpState,
545 texture_id: u32,
546 width: u32,
547 height: u32,
548 tile_size: f64,
549 atlas_columns: u32,
550 atlas_rows: u32,
551) -> u32 {
552 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
553 bridge
554 .borrow_mut()
555 .tilemaps
556 .create(texture_id, width, height, tile_size as f32, atlas_columns, atlas_rows)
557}
558
559#[deno_core::op2(fast)]
561pub fn op_set_tile(state: &mut OpState, tilemap_id: u32, gx: u32, gy: u32, tile_id: u32) {
562 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
563 if let Some(tm) = bridge.borrow_mut().tilemaps.get_mut(tilemap_id) {
564 tm.set_tile(gx, gy, tile_id as u16);
565 }
566}
567
568#[deno_core::op2(fast)]
570pub fn op_get_tile(state: &mut OpState, tilemap_id: u32, gx: u32, gy: u32) -> u32 {
571 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
572 bridge
573 .borrow()
574 .tilemaps
575 .get(tilemap_id)
576 .map(|tm| tm.get_tile(gx, gy) as u32)
577 .unwrap_or(0)
578}
579
580#[deno_core::op2(fast)]
583pub fn op_draw_tilemap(state: &mut OpState, tilemap_id: u32, world_x: f64, world_y: f64, layer: i32) {
584 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
585 let mut b = bridge.borrow_mut();
586 let cam_x = b.camera_x;
587 let cam_y = b.camera_y;
588 let cam_zoom = b.camera_zoom;
589 let vp_w = 800.0;
591 let vp_h = 600.0;
592
593 if let Some(tm) = b.tilemaps.get(tilemap_id) {
594 let cmds = tm.bake_visible(world_x as f32, world_y as f32, layer, cam_x, cam_y, cam_zoom, vp_w, vp_h);
595 b.sprite_commands.extend(cmds);
596 }
597}
598
599#[deno_core::op2(fast)]
604pub fn op_set_ambient_light(state: &mut OpState, r: f64, g: f64, b: f64) {
605 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
606 bridge.borrow_mut().ambient_light = [r as f32, g as f32, b as f32];
607}
608
609#[deno_core::op2(fast)]
612pub fn op_add_point_light(
613 state: &mut OpState,
614 x: f64,
615 y: f64,
616 radius: f64,
617 r: f64,
618 g: f64,
619 b: f64,
620 intensity: f64,
621) {
622 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
623 bridge.borrow_mut().point_lights.push(PointLight {
624 x: x as f32,
625 y: y as f32,
626 radius: radius as f32,
627 r: r as f32,
628 g: g as f32,
629 b: b as f32,
630 intensity: intensity as f32,
631 });
632}
633
634#[deno_core::op2(fast)]
636pub fn op_clear_lights(state: &mut OpState) {
637 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
638 bridge.borrow_mut().point_lights.clear();
639}
640
641#[deno_core::op2(fast)]
645pub fn op_load_sound(state: &mut OpState, #[string] path: &str) -> u32 {
646 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
647 let mut b = bridge.borrow_mut();
648
649 let resolved = if std::path::Path::new(path).is_absolute() {
650 path.to_string()
651 } else {
652 b.base_dir.join(path).to_string_lossy().to_string()
653 };
654
655 if let Some(&id) = b.sound_path_to_id.get(&resolved) {
656 return id;
657 }
658
659 let id = b.next_sound_id;
660 b.next_sound_id += 1;
661 b.sound_path_to_id.insert(resolved.clone(), id);
662 b.audio_commands.push(BridgeAudioCommand::LoadSound { id, path: resolved });
663 id
664}
665
666#[deno_core::op2(fast)]
669pub fn op_play_sound(state: &mut OpState, id: u32, volume: f64, looping: bool) {
670 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
671 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::PlaySound { id, volume: volume as f32, looping });
672}
673
674#[deno_core::op2(fast)]
676pub fn op_stop_sound(state: &mut OpState, id: u32) {
677 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
678 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::StopSound { id });
679}
680
681#[deno_core::op2(fast)]
683pub fn op_stop_all_sounds(state: &mut OpState) {
684 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
685 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::StopAll);
686}
687
688#[deno_core::op2(fast)]
691pub fn op_set_master_volume(state: &mut OpState, volume: f64) {
692 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
693 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::SetMasterVolume { volume: volume as f32 });
694}
695
696#[deno_core::op2(fast)]
700pub fn op_create_font_texture(state: &mut OpState) -> u32 {
701 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
702 let mut b = bridge.borrow_mut();
703
704 let key = "__builtin_font__".to_string();
705 if let Some(&id) = b.texture_path_to_id.get(&key) {
706 return id;
707 }
708
709 let id = b.next_texture_id;
710 b.next_texture_id += 1;
711 b.texture_path_to_id.insert(key, id);
712 b.font_texture_queue.push(id);
713 id
714}
715
716#[deno_core::op2]
720#[serde]
721pub fn op_get_viewport_size(state: &mut OpState) -> Vec<f64> {
722 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
723 let b = bridge.borrow();
724 vec![b.viewport_width as f64, b.viewport_height as f64]
725}
726
727#[deno_core::op2(fast)]
729pub fn op_get_scale_factor(state: &mut OpState) -> f64 {
730 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
731 bridge.borrow().scale_factor as f64
732}
733
734#[deno_core::op2(fast)]
736pub fn op_set_background_color(state: &mut OpState, r: f64, g: f64, b: f64) {
737 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
738 let mut br = bridge.borrow_mut();
739 br.clear_color = [r as f32, g as f32, b as f32, 1.0];
740}
741
742#[deno_core::op2(fast)]
746pub fn op_save_file(state: &mut OpState, #[string] key: &str, #[string] value: &str) -> bool {
747 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
748 let save_dir = bridge.borrow().save_dir.clone();
749
750 if !key.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
752 return false;
753 }
754
755 if std::fs::create_dir_all(&save_dir).is_err() {
757 return false;
758 }
759
760 let path = save_dir.join(format!("{key}.json"));
761 std::fs::write(path, value).is_ok()
762}
763
764#[deno_core::op2]
766#[string]
767pub fn op_load_file(state: &mut OpState, #[string] key: &str) -> String {
768 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
769 let save_dir = bridge.borrow().save_dir.clone();
770
771 let path = save_dir.join(format!("{key}.json"));
772 std::fs::read_to_string(path).unwrap_or_default()
773}
774
775#[deno_core::op2(fast)]
777pub fn op_delete_file(state: &mut OpState, #[string] key: &str) -> bool {
778 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
779 let save_dir = bridge.borrow().save_dir.clone();
780
781 let path = save_dir.join(format!("{key}.json"));
782 std::fs::remove_file(path).is_ok()
783}
784
785#[deno_core::op2]
787#[serde]
788pub fn op_list_save_files(state: &mut OpState) -> Vec<String> {
789 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
790 let save_dir = bridge.borrow().save_dir.clone();
791
792 let mut keys = Vec::new();
793 if let Ok(entries) = std::fs::read_dir(&save_dir) {
794 for entry in entries.flatten() {
795 let path = entry.path();
796 if path.extension().map_or(false, |ext| ext == "json") {
797 if let Some(stem) = path.file_stem() {
798 keys.push(stem.to_string_lossy().to_string());
799 }
800 }
801 }
802 }
803 keys.sort();
804 keys
805}
806
807#[deno_core::op2(fast)]
811pub fn op_create_shader(state: &mut OpState, #[string] name: &str, #[string] source: &str) -> u32 {
812 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
813 let mut b = bridge.borrow_mut();
814 let id = b.next_shader_id;
815 b.next_shader_id += 1;
816 b.shader_create_queue
817 .push((id, name.to_string(), source.to_string()));
818 id
819}
820
821#[deno_core::op2(fast)]
823pub fn op_set_shader_param(
824 state: &mut OpState,
825 shader_id: u32,
826 index: u32,
827 x: f64,
828 y: f64,
829 z: f64,
830 w: f64,
831) {
832 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
833 bridge.borrow_mut().shader_param_queue.push((
834 shader_id,
835 index,
836 [x as f32, y as f32, z as f32, w as f32],
837 ));
838}
839
840#[deno_core::op2(fast)]
844pub fn op_add_effect(state: &mut OpState, #[string] effect_type: &str) -> u32 {
845 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
846 let mut b = bridge.borrow_mut();
847 let id = b.next_effect_id;
848 b.next_effect_id += 1;
849 b.effect_create_queue
850 .push((id, effect_type.to_string()));
851 id
852}
853
854#[deno_core::op2(fast)]
856pub fn op_set_effect_param(
857 state: &mut OpState,
858 effect_id: u32,
859 index: u32,
860 x: f64,
861 y: f64,
862 z: f64,
863 w: f64,
864) {
865 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
866 bridge.borrow_mut().effect_param_queue.push((
867 effect_id,
868 index,
869 [x as f32, y as f32, z as f32, w as f32],
870 ));
871}
872
873#[deno_core::op2(fast)]
875pub fn op_remove_effect(state: &mut OpState, effect_id: u32) {
876 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
877 bridge.borrow_mut().effect_remove_queue.push(effect_id);
878}
879
880#[deno_core::op2(fast)]
882pub fn op_clear_effects(state: &mut OpState) {
883 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
884 bridge.borrow_mut().effect_clear = true;
885}
886
887#[deno_core::op2(fast)]
891pub fn op_set_camera_bounds(state: &mut OpState, min_x: f64, min_y: f64, max_x: f64, max_y: f64) {
892 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
893 bridge.borrow_mut().camera_bounds = Some(CameraBounds {
894 min_x: min_x as f32,
895 min_y: min_y as f32,
896 max_x: max_x as f32,
897 max_y: max_y as f32,
898 });
899}
900
901#[deno_core::op2(fast)]
903pub fn op_clear_camera_bounds(state: &mut OpState) {
904 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
905 bridge.borrow_mut().camera_bounds = None;
906}
907
908#[deno_core::op2]
910#[serde]
911pub fn op_get_camera_bounds(state: &mut OpState) -> Vec<f64> {
912 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
913 let b = bridge.borrow();
914 match b.camera_bounds {
915 Some(bounds) => vec![
916 bounds.min_x as f64,
917 bounds.min_y as f64,
918 bounds.max_x as f64,
919 bounds.max_y as f64,
920 ],
921 None => vec![],
922 }
923}
924
925#[deno_core::op2(fast)]
929pub fn op_enable_gi(state: &mut OpState) {
930 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
931 bridge.borrow_mut().gi_enabled = true;
932}
933
934#[deno_core::op2(fast)]
936pub fn op_disable_gi(state: &mut OpState) {
937 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
938 bridge.borrow_mut().gi_enabled = false;
939}
940
941#[deno_core::op2(fast)]
943pub fn op_set_gi_intensity(state: &mut OpState, intensity: f64) {
944 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
945 bridge.borrow_mut().gi_intensity = intensity as f32;
946}
947
948#[deno_core::op2(fast)]
951pub fn op_set_gi_quality(state: &mut OpState, probe_spacing: f64, interval: f64, cascade_count: f64) {
952 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
953 let mut b = bridge.borrow_mut();
954 if probe_spacing > 0.0 {
955 b.gi_probe_spacing = Some(probe_spacing as f32);
956 }
957 if interval > 0.0 {
958 b.gi_interval = Some(interval as f32);
959 }
960 if cascade_count > 0.0 {
961 b.gi_cascade_count = Some(cascade_count as u32);
962 }
963}
964
965#[deno_core::op2(fast)]
967pub fn op_add_emissive(
968 state: &mut OpState,
969 x: f64,
970 y: f64,
971 w: f64,
972 h: f64,
973 r: f64,
974 g: f64,
975 b: f64,
976 intensity: f64,
977) {
978 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
979 bridge.borrow_mut().emissives.push([
980 x as f32,
981 y as f32,
982 w as f32,
983 h as f32,
984 r as f32,
985 g as f32,
986 b as f32,
987 intensity as f32,
988 ]);
989}
990
991#[deno_core::op2(fast)]
993pub fn op_clear_emissives(state: &mut OpState) {
994 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
995 bridge.borrow_mut().emissives.clear();
996}
997
998#[deno_core::op2(fast)]
1000pub fn op_add_occluder(state: &mut OpState, x: f64, y: f64, w: f64, h: f64) {
1001 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1002 bridge.borrow_mut().occluders.push([x as f32, y as f32, w as f32, h as f32]);
1003}
1004
1005#[deno_core::op2(fast)]
1007pub fn op_clear_occluders(state: &mut OpState) {
1008 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1009 bridge.borrow_mut().occluders.clear();
1010}
1011
1012#[deno_core::op2(fast)]
1014pub fn op_add_directional_light(
1015 state: &mut OpState,
1016 angle: f64,
1017 r: f64,
1018 g: f64,
1019 b: f64,
1020 intensity: f64,
1021) {
1022 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1023 bridge.borrow_mut().directional_lights.push([
1024 angle as f32,
1025 r as f32,
1026 g as f32,
1027 b as f32,
1028 intensity as f32,
1029 ]);
1030}
1031
1032#[deno_core::op2(fast)]
1034pub fn op_add_spot_light(
1035 state: &mut OpState,
1036 x: f64,
1037 y: f64,
1038 angle: f64,
1039 spread: f64,
1040 range: f64,
1041 r: f64,
1042 g: f64,
1043 b: f64,
1044 intensity: f64,
1045) {
1046 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1047 bridge.borrow_mut().spot_lights.push([
1048 x as f32,
1049 y as f32,
1050 angle as f32,
1051 spread as f32,
1052 range as f32,
1053 r as f32,
1054 g as f32,
1055 b as f32,
1056 intensity as f32,
1057 ]);
1058}
1059
1060#[deno_core::op2(fast)]
1065pub fn op_play_sound_ex(
1066 state: &mut OpState,
1067 sound_id: u32,
1068 instance_id: f64,
1069 volume: f64,
1070 looping: bool,
1071 bus: u32,
1072 pan: f64,
1073 pitch: f64,
1074 low_pass_freq: u32,
1075 reverb_mix: f64,
1076 reverb_delay_ms: u32,
1077) {
1078 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1079 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::PlaySoundEx {
1080 sound_id,
1081 instance_id: instance_id as u64,
1082 volume: volume as f32,
1083 looping,
1084 bus,
1085 pan: pan as f32,
1086 pitch: pitch as f32,
1087 low_pass_freq,
1088 reverb_mix: reverb_mix as f32,
1089 reverb_delay_ms,
1090 });
1091}
1092
1093#[deno_core::op2(fast)]
1096pub fn op_play_sound_spatial(
1097 state: &mut OpState,
1098 sound_id: u32,
1099 instance_id: f64,
1100 volume: f64,
1101 looping: bool,
1102 bus: u32,
1103 pitch: f64,
1104 source_x: f64,
1105 source_y: f64,
1106 listener_x: f64,
1107 listener_y: f64,
1108) {
1109 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1110 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::PlaySoundSpatial {
1111 sound_id,
1112 instance_id: instance_id as u64,
1113 volume: volume as f32,
1114 looping,
1115 bus,
1116 pitch: pitch as f32,
1117 source_x: source_x as f32,
1118 source_y: source_y as f32,
1119 listener_x: listener_x as f32,
1120 listener_y: listener_y as f32,
1121 });
1122}
1123
1124#[deno_core::op2(fast)]
1127pub fn op_stop_instance(state: &mut OpState, instance_id: f64) {
1128 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1129 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::StopInstance {
1130 instance_id: instance_id as u64,
1131 });
1132}
1133
1134#[deno_core::op2(fast)]
1137pub fn op_set_instance_volume(state: &mut OpState, instance_id: f64, volume: f64) {
1138 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1139 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::SetInstanceVolume {
1140 instance_id: instance_id as u64,
1141 volume: volume as f32,
1142 });
1143}
1144
1145#[deno_core::op2(fast)]
1148pub fn op_set_instance_pitch(state: &mut OpState, instance_id: f64, pitch: f64) {
1149 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1150 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::SetInstancePitch {
1151 instance_id: instance_id as u64,
1152 pitch: pitch as f32,
1153 });
1154}
1155
1156#[deno_core::op2(fast)]
1160pub fn op_update_spatial_positions(state: &mut OpState, #[string] data_json: &str, listener_x: f64, listener_y: f64) {
1161 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1162
1163 let mut updates = Vec::new();
1166
1167 if let Some(ids_start) = data_json.find("\"instanceIds\":[") {
1169 if let Some(xs_start) = data_json.find("\"sourceXs\":[") {
1170 if let Some(ys_start) = data_json.find("\"sourceYs\":[") {
1171 let ids_str = &data_json[ids_start + 15..];
1172 let xs_str = &data_json[xs_start + 12..];
1173 let ys_str = &data_json[ys_start + 12..];
1174
1175 let ids_end = ids_str.find(']').unwrap_or(0);
1176 let xs_end = xs_str.find(']').unwrap_or(0);
1177 let ys_end = ys_str.find(']').unwrap_or(0);
1178
1179 let ids: Vec<u64> = ids_str[..ids_end]
1180 .split(',')
1181 .filter_map(|s| s.trim().parse().ok())
1182 .collect();
1183 let xs: Vec<f32> = xs_str[..xs_end]
1184 .split(',')
1185 .filter_map(|s| s.trim().parse().ok())
1186 .collect();
1187 let ys: Vec<f32> = ys_str[..ys_end]
1188 .split(',')
1189 .filter_map(|s| s.trim().parse().ok())
1190 .collect();
1191
1192 for i in 0..ids.len().min(xs.len()).min(ys.len()) {
1193 updates.push((ids[i], xs[i], ys[i]));
1194 }
1195 }
1196 }
1197 }
1198
1199 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::UpdateSpatialPositions {
1200 updates,
1201 listener_x: listener_x as f32,
1202 listener_y: listener_y as f32,
1203 });
1204}
1205
1206#[deno_core::op2(fast)]
1209pub fn op_set_bus_volume(state: &mut OpState, bus: u32, volume: f64) {
1210 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1211 bridge.borrow_mut().audio_commands.push(BridgeAudioCommand::SetBusVolume {
1212 bus,
1213 volume: volume as f32,
1214 });
1215}
1216
1217#[deno_core::op2]
1222#[string]
1223pub fn op_create_msdf_builtin_font(state: &mut OpState) -> String {
1224 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1225 let mut b = bridge.borrow_mut();
1226
1227 let key = "__msdf_builtin__".to_string();
1229 if let Some(&tex_id) = b.texture_path_to_id.get(&key) {
1230 let font_id_key = format!("__msdf_font_{tex_id}__");
1233 if let Some(&font_id) = b.texture_path_to_id.get(&font_id_key) {
1234 let pool = &b.msdf_shader_pool;
1235 let shader_id = pool.first().copied().unwrap_or(0);
1236 let pool_json: Vec<String> = pool.iter().map(|id| id.to_string()).collect();
1237 return format!(
1238 "{{\"fontId\":{},\"textureId\":{},\"shaderId\":{},\"shaderPool\":[{}]}}",
1239 font_id, tex_id, shader_id, pool_json.join(",")
1240 );
1241 }
1242 }
1243
1244 let tex_id = b.next_texture_id;
1246 b.next_texture_id += 1;
1247 b.texture_path_to_id.insert(key, tex_id);
1248
1249 let (_pixels, _width, _height, mut font) =
1251 crate::renderer::msdf::generate_builtin_msdf_font();
1252 font.texture_id = tex_id;
1253
1254 let font_id = b.msdf_fonts.register(font);
1256 b.texture_path_to_id
1257 .insert(format!("__msdf_font_{tex_id}__"), font_id);
1258
1259 b.msdf_builtin_queue.push((font_id, tex_id));
1262
1263 let pool = ensure_msdf_shader_pool(&mut b);
1265 let shader_id = pool.first().copied().unwrap_or(0);
1266 let pool_json: Vec<String> = pool.iter().map(|id| id.to_string()).collect();
1267
1268 format!(
1269 "{{\"fontId\":{},\"textureId\":{},\"shaderId\":{},\"shaderPool\":[{}]}}",
1270 font_id, tex_id, shader_id, pool_json.join(",")
1271 )
1272}
1273
1274#[deno_core::op2]
1277#[string]
1278pub fn op_get_msdf_glyphs(
1279 state: &mut OpState,
1280 font_id: u32,
1281 #[string] text: &str,
1282) -> String {
1283 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1284 let b = bridge.borrow();
1285
1286 let font = match b.msdf_fonts.get(font_id) {
1287 Some(f) => f,
1288 None => return "[]".to_string(),
1289 };
1290
1291 let mut entries = Vec::new();
1292 for ch in text.chars() {
1293 if let Some(glyph) = font.get_glyph(ch) {
1294 entries.push(format!(
1295 "{{\"char\":{},\"uv\":[{},{},{},{}],\"advance\":{},\"width\":{},\"height\":{},\"offsetX\":{},\"offsetY\":{}}}",
1296 ch as u32,
1297 glyph.uv_x, glyph.uv_y, glyph.uv_w, glyph.uv_h,
1298 glyph.advance, glyph.width, glyph.height,
1299 glyph.offset_x, glyph.offset_y,
1300 ));
1301 }
1302 }
1303
1304 format!("[{}]", entries.join(","))
1305}
1306
1307#[deno_core::op2]
1309#[string]
1310pub fn op_get_msdf_font_info(state: &mut OpState, font_id: u32) -> String {
1311 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1312 let b = bridge.borrow();
1313
1314 match b.msdf_fonts.get(font_id) {
1315 Some(font) => format!(
1316 "{{\"fontSize\":{},\"lineHeight\":{},\"distanceRange\":{},\"textureId\":{}}}",
1317 font.font_size, font.line_height, font.distance_range, font.texture_id,
1318 ),
1319 None => "null".to_string(),
1320 }
1321}
1322
1323#[deno_core::op2]
1326#[string]
1327pub fn op_load_msdf_font(
1328 state: &mut OpState,
1329 #[string] atlas_path: &str,
1330 #[string] metrics_json_or_path: &str,
1331) -> String {
1332 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1333 let mut b = bridge.borrow_mut();
1334
1335 let resolved = if std::path::Path::new(atlas_path).is_absolute() {
1337 atlas_path.to_string()
1338 } else {
1339 b.base_dir.join(atlas_path).to_string_lossy().to_string()
1340 };
1341
1342 let tex_id = if let Some(&id) = b.texture_path_to_id.get(&resolved) {
1344 id
1345 } else {
1346 let id = b.next_texture_id;
1347 b.next_texture_id += 1;
1348 b.texture_path_to_id.insert(resolved.clone(), id);
1349 b.msdf_texture_load_queue.push((resolved, id));
1350 id
1351 };
1352
1353 let metrics_json: String = if metrics_json_or_path.trim_start().starts_with('{') {
1356 metrics_json_or_path.to_string()
1357 } else {
1358 let json_path = if std::path::Path::new(metrics_json_or_path).is_absolute() {
1360 metrics_json_or_path.to_string()
1361 } else {
1362 b.base_dir
1363 .join(metrics_json_or_path)
1364 .to_string_lossy()
1365 .to_string()
1366 };
1367 match std::fs::read_to_string(&json_path) {
1368 Ok(content) => content,
1369 Err(e) => {
1370 return format!("{{\"error\":\"Failed to read metrics file {}: {}\"}}", json_path, e);
1371 }
1372 }
1373 };
1374
1375 let font = match crate::renderer::msdf::parse_msdf_metrics(&metrics_json, tex_id) {
1377 Ok(f) => f,
1378 Err(e) => {
1379 return format!("{{\"error\":\"{}\"}}", e);
1380 }
1381 };
1382
1383 let font_id = b.msdf_fonts.register(font);
1384 let pool = ensure_msdf_shader_pool(&mut b);
1385 let shader_id = pool.first().copied().unwrap_or(0);
1386 let pool_json: Vec<String> = pool.iter().map(|id| id.to_string()).collect();
1387
1388 format!(
1389 "{{\"fontId\":{},\"textureId\":{},\"shaderId\":{},\"shaderPool\":[{}]}}",
1390 font_id, tex_id, shader_id, pool_json.join(",")
1391 )
1392}
1393
1394const MSDF_SHADER_POOL_SIZE: usize = 8;
1396
1397fn ensure_msdf_shader_pool(b: &mut RenderBridgeState) -> Vec<u32> {
1399 if !b.msdf_shader_pool.is_empty() {
1400 return b.msdf_shader_pool.clone();
1401 }
1402
1403 let source = crate::renderer::msdf::MSDF_FRAGMENT_SOURCE.to_string();
1404 let mut pool = Vec::with_capacity(MSDF_SHADER_POOL_SIZE);
1405
1406 for _ in 0..MSDF_SHADER_POOL_SIZE {
1407 let id = b.next_shader_id;
1408 b.next_shader_id += 1;
1409 b.msdf_shader_queue.push((id, source.clone()));
1410 pool.push(id);
1411 }
1412
1413 b.msdf_shader_pool = pool.clone();
1414 pool
1415}
1416
1417#[deno_core::op2(fast)]
1421pub fn op_get_gamepad_count(state: &mut OpState) -> u32 {
1422 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1423 bridge.borrow().gamepad_count
1424}
1425
1426#[deno_core::op2]
1428#[string]
1429pub fn op_get_gamepad_name(state: &mut OpState) -> String {
1430 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1431 bridge.borrow().gamepad_name.clone()
1432}
1433
1434#[deno_core::op2(fast)]
1437pub fn op_is_gamepad_button_down(state: &mut OpState, #[string] button: &str) -> bool {
1438 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1439 bridge.borrow().gamepad_buttons_down.contains(button)
1440}
1441
1442#[deno_core::op2(fast)]
1444pub fn op_is_gamepad_button_pressed(state: &mut OpState, #[string] button: &str) -> bool {
1445 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1446 bridge.borrow().gamepad_buttons_pressed.contains(button)
1447}
1448
1449#[deno_core::op2(fast)]
1452pub fn op_get_gamepad_axis(state: &mut OpState, #[string] axis: &str) -> f64 {
1453 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1454 bridge.borrow().gamepad_axes.get(axis).copied().unwrap_or(0.0) as f64
1455}
1456
1457#[deno_core::op2(fast)]
1461pub fn op_get_touch_count(state: &mut OpState) -> u32 {
1462 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1463 bridge.borrow().touch_count
1464}
1465
1466#[deno_core::op2]
1468#[serde]
1469pub fn op_get_touch_position(state: &mut OpState, index: u32) -> Vec<f64> {
1470 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1471 let b = bridge.borrow();
1472 if let Some(&(_, x, y)) = b.touch_points.get(index as usize) {
1473 vec![x as f64, y as f64]
1474 } else {
1475 vec![]
1476 }
1477}
1478
1479#[deno_core::op2(fast)]
1481pub fn op_is_touch_active(state: &mut OpState) -> bool {
1482 let bridge = state.borrow_mut::<Rc<RefCell<RenderBridgeState>>>();
1483 bridge.borrow().touch_count > 0
1484}
1485
1486deno_core::extension!(
1487 render_ext,
1488 ops = [
1489 op_draw_sprite,
1490 op_clear_sprites,
1491 op_submit_sprite_batch,
1492 op_set_camera,
1493 op_get_camera,
1494 op_load_texture,
1495 op_upload_rgba_texture,
1496 op_is_key_down,
1497 op_is_key_pressed,
1498 op_get_mouse_position,
1499 op_is_mouse_button_down,
1500 op_is_mouse_button_pressed,
1501 op_get_delta_time,
1502 op_create_solid_texture,
1503 op_create_tilemap,
1504 op_set_tile,
1505 op_get_tile,
1506 op_draw_tilemap,
1507 op_set_ambient_light,
1508 op_add_point_light,
1509 op_clear_lights,
1510 op_load_sound,
1511 op_play_sound,
1512 op_stop_sound,
1513 op_stop_all_sounds,
1514 op_set_master_volume,
1515 op_play_sound_ex,
1516 op_play_sound_spatial,
1517 op_stop_instance,
1518 op_set_instance_volume,
1519 op_set_instance_pitch,
1520 op_update_spatial_positions,
1521 op_set_bus_volume,
1522 op_create_font_texture,
1523 op_get_viewport_size,
1524 op_get_scale_factor,
1525 op_set_background_color,
1526 op_save_file,
1527 op_load_file,
1528 op_delete_file,
1529 op_list_save_files,
1530 op_create_shader,
1531 op_set_shader_param,
1532 op_add_effect,
1533 op_set_effect_param,
1534 op_remove_effect,
1535 op_clear_effects,
1536 op_set_camera_bounds,
1537 op_clear_camera_bounds,
1538 op_get_camera_bounds,
1539 op_enable_gi,
1540 op_disable_gi,
1541 op_set_gi_intensity,
1542 op_set_gi_quality,
1543 op_add_emissive,
1544 op_clear_emissives,
1545 op_add_occluder,
1546 op_clear_occluders,
1547 op_add_directional_light,
1548 op_add_spot_light,
1549 op_create_msdf_builtin_font,
1550 op_get_msdf_glyphs,
1551 op_get_msdf_font_info,
1552 op_load_msdf_font,
1553 op_get_gamepad_count,
1554 op_get_gamepad_name,
1555 op_is_gamepad_button_down,
1556 op_is_gamepad_button_pressed,
1557 op_get_gamepad_axis,
1558 op_get_touch_count,
1559 op_get_touch_position,
1560 op_is_touch_active,
1561 ],
1562);