Mercurial > touhou
comparison interpreters/src/th06/anm0.rs @ 757:21b186be2590
Split the Rust version into multiple crates.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 05 Jan 2021 02:16:32 +0100 |
parents | src/th06/anm0_vm.rs@3687205fe620 |
children |
comparison
equal
deleted
inserted
replaced
756:4d91790cf8ab | 757:21b186be2590 |
---|---|
1 //! Animation runner. | |
2 | |
3 use touhou_formats::th06::anm0::{ | |
4 Script, | |
5 Anm0, | |
6 Call, | |
7 Instruction, | |
8 }; | |
9 use crate::th06::interpolator::{Interpolator1, Interpolator2, Interpolator3, Formula}; | |
10 use touhou_utils::math::Mat4; | |
11 use touhou_utils::prng::Prng; | |
12 use std::cell::RefCell; | |
13 use std::rc::{Rc, Weak}; | |
14 | |
15 /// TODO | |
16 #[repr(C)] | |
17 #[derive(Debug)] | |
18 pub struct Vertex { | |
19 /// XXX | |
20 pub pos: [i16; 3], | |
21 /// XXX | |
22 pub layer: u16, | |
23 /// XXX | |
24 pub uv: [f32; 2], | |
25 /// XXX | |
26 pub color: [u8; 4], | |
27 } | |
28 | |
29 /// Base visual element. | |
30 #[derive(Debug, Clone, Default)] | |
31 pub struct Sprite { | |
32 blendfunc: u32, | |
33 frame: u32, | |
34 | |
35 width_override: f32, | |
36 height_override: f32, | |
37 angle: f32, | |
38 | |
39 removed: bool, | |
40 changed: bool, | |
41 visible: bool, | |
42 force_rotation: bool, | |
43 automatic_orientation: bool, | |
44 allow_dest_offset: bool, | |
45 mirrored: bool, | |
46 corner_relative_placement: bool, | |
47 | |
48 scale_interpolator: Option<Interpolator2<f32>>, | |
49 fade_interpolator: Option<Interpolator1<f32>>, // XXX: should be u8! | |
50 offset_interpolator: Option<Interpolator3<f32>>, | |
51 rotation_interpolator: Option<Interpolator3<f32>>, | |
52 color_interpolator: Option<Interpolator3<f32>>, // XXX: should be u8! | |
53 | |
54 anm: Option<Anm0>, | |
55 | |
56 dest_offset: [f32; 3], | |
57 texcoords: [f32; 4], | |
58 texoffsets: [f32; 2], | |
59 rescale: [f32; 2], | |
60 scale_speed: [f32; 2], | |
61 rotations_3d: [f32; 3], | |
62 rotations_speed_3d: [f32; 3], | |
63 color: [u8; 4], | |
64 layer: u16, | |
65 } | |
66 | |
67 impl Sprite { | |
68 /// Create a new sprite. | |
69 pub fn new() -> Sprite { | |
70 Sprite { | |
71 changed: true, | |
72 visible: true, | |
73 rescale: [1., 1.], | |
74 color: [255, 255, 255, 255], | |
75 ..Default::default() | |
76 } | |
77 } | |
78 | |
79 /// Create a new sprite overriding its size. | |
80 pub fn with_size(width_override: f32, height_override: f32) -> Sprite { | |
81 Sprite { | |
82 width_override, | |
83 height_override, | |
84 changed: true, | |
85 visible: true, | |
86 rescale: [1., 1.], | |
87 color: [255, 255, 255, 255], | |
88 ..Default::default() | |
89 } | |
90 } | |
91 | |
92 /// TODO | |
93 pub fn fill_vertices(&self, vertices: &mut [Vertex; 4], x: f32, y: f32, z: f32) { | |
94 let mut mat = Mat4::new([[-0.5, 0.5, 0.5, -0.5], | |
95 [-0.5, -0.5, 0.5, 0.5], | |
96 [0., 0., 0., 0.], | |
97 [1., 1., 1., 1.]]); | |
98 | |
99 let [tx, ty, tw, th] = self.texcoords; | |
100 let [sx, sy] = self.rescale; | |
101 let width = if self.width_override > 0. { self.width_override } else { tw * sx }; | |
102 let height = if self.height_override > 0. { self.height_override } else { th * sy }; | |
103 | |
104 mat.scale2d(width, height); | |
105 if self.mirrored { | |
106 mat.flip(); | |
107 } | |
108 | |
109 let [rx, ry, mut rz] = self.rotations_3d; | |
110 if self.automatic_orientation { | |
111 rz += std::f32::consts::PI / 2. - self.angle; | |
112 } else if self.force_rotation { | |
113 rz += self.angle; | |
114 } | |
115 | |
116 if rx != 0. { | |
117 mat.rotate_x(-rx); | |
118 } | |
119 if ry != 0. { | |
120 mat.rotate_y(ry); | |
121 } | |
122 if rz != 0. { | |
123 mat.rotate_z(-rz); | |
124 } | |
125 | |
126 if self.allow_dest_offset { | |
127 mat.translate(self.dest_offset); | |
128 } | |
129 if self.corner_relative_placement { | |
130 mat.translate_2d(width / 2., height / 2.); | |
131 } | |
132 | |
133 mat.translate([x, y, z]); | |
134 | |
135 let mat = mat.borrow_inner(); | |
136 vertices[0].pos[0] = mat[0][0] as i16; | |
137 vertices[0].pos[1] = mat[1][0] as i16; | |
138 vertices[0].pos[2] = mat[2][0] as i16; | |
139 vertices[1].pos[0] = mat[0][1] as i16; | |
140 vertices[1].pos[1] = mat[1][1] as i16; | |
141 vertices[1].pos[2] = mat[2][1] as i16; | |
142 vertices[2].pos[0] = mat[0][2] as i16; | |
143 vertices[2].pos[1] = mat[1][2] as i16; | |
144 vertices[2].pos[2] = mat[2][2] as i16; | |
145 vertices[3].pos[0] = mat[0][3] as i16; | |
146 vertices[3].pos[1] = mat[1][3] as i16; | |
147 vertices[3].pos[2] = mat[2][3] as i16; | |
148 | |
149 // XXX: don’t clone here. | |
150 let (x_1, y_1) = self.anm.clone().unwrap().inv_size(); | |
151 let [tox, toy] = self.texoffsets; | |
152 let left = tx * x_1 + tox; | |
153 let right = (tx + tw) * x_1 + tox; | |
154 let bottom = ty * y_1 + toy; | |
155 let top = (ty + th) * y_1 + toy; | |
156 | |
157 vertices[0].uv[0] = left; | |
158 vertices[0].uv[1] = bottom; | |
159 vertices[1].uv[0] = right; | |
160 vertices[1].uv[1] = bottom; | |
161 vertices[2].uv[0] = right; | |
162 vertices[2].uv[1] = top; | |
163 vertices[3].uv[0] = left; | |
164 vertices[3].uv[1] = top; | |
165 | |
166 vertices[0].color = self.color; | |
167 vertices[1].color = self.color; | |
168 vertices[2].color = self.color; | |
169 vertices[3].color = self.color; | |
170 | |
171 vertices[0].layer = self.layer; | |
172 vertices[1].layer = self.layer; | |
173 vertices[2].layer = self.layer; | |
174 vertices[3].layer = self.layer; | |
175 } | |
176 | |
177 /// Update sprite values from the interpolators. | |
178 pub fn update(&mut self) { | |
179 self.frame += 1; | |
180 self.corner_relative_placement = true; | |
181 | |
182 let [sax, say, saz] = self.rotations_speed_3d; | |
183 if sax != 0. || say != 0. || saz != 0. { | |
184 let [ax, ay, az] = self.rotations_3d; | |
185 self.rotations_3d = [ax + sax, ay + say, az + saz]; | |
186 self.changed = true; | |
187 } else if let Some(ref interpolator) = self.rotation_interpolator { | |
188 self.rotations_3d = interpolator.values(self.frame); | |
189 self.changed = true; | |
190 } | |
191 | |
192 let [rsx, rsy] = self.scale_speed; | |
193 if rsx != 0. || rsy != 0. { | |
194 let [rx, ry] = self.rescale; | |
195 self.rescale = [rx + rsx, ry + rsy]; | |
196 self.changed = true; | |
197 } | |
198 | |
199 if let Some(ref interpolator) = self.fade_interpolator { | |
200 self.color[3] = interpolator.values(self.frame)[0] as u8; | |
201 self.changed = true; | |
202 } | |
203 | |
204 if let Some(ref interpolator) = self.scale_interpolator { | |
205 self.rescale = interpolator.values(self.frame); | |
206 self.changed = true; | |
207 } | |
208 | |
209 if let Some(ref interpolator) = self.offset_interpolator { | |
210 self.dest_offset = interpolator.values(self.frame); | |
211 self.changed = true; | |
212 } | |
213 | |
214 if let Some(ref interpolator) = self.color_interpolator { | |
215 let color = interpolator.values(self.frame); | |
216 // TODO: this can probably be made to look nicer. | |
217 self.color[0] = color[0] as u8; | |
218 self.color[1] = color[1] as u8; | |
219 self.color[2] = color[2] as u8; | |
220 self.changed = true; | |
221 } | |
222 } | |
223 } | |
224 | |
225 struct Anms { | |
226 inner: Rc<RefCell<[Anm0]>>, | |
227 } | |
228 | |
229 impl Anms { | |
230 fn new(anms: Rc<RefCell<[Anm0]>>) -> Anms { | |
231 Anms { | |
232 inner: anms, | |
233 } | |
234 } | |
235 | |
236 fn load_sprite(&self, sprite: &mut Sprite, id: u8) { | |
237 let anms = self.inner.borrow(); | |
238 let mut anm = None; | |
239 let mut texcoords = None; | |
240 let mut layer = 0; | |
241 'anm: for anm0 in anms.iter() { | |
242 for sp in anm0.sprites.iter() { | |
243 if sp.index == id as u32 { | |
244 texcoords = Some(sp); | |
245 anm = Some(anm0.clone()); | |
246 break 'anm; | |
247 } | |
248 } | |
249 layer += 1; | |
250 } | |
251 sprite.anm = anm; | |
252 sprite.layer = layer; | |
253 if let Some(texcoords) = texcoords { | |
254 sprite.texcoords = [texcoords.x, texcoords.y, texcoords.width, texcoords.height]; | |
255 } | |
256 } | |
257 | |
258 fn get_script(&self, id: u8) -> Script { | |
259 let anms = self.inner.borrow(); | |
260 for anm0 in anms.iter() { | |
261 if anm0.scripts.contains_key(&id) { | |
262 return anm0.scripts[&id].clone(); | |
263 } | |
264 } | |
265 unreachable!(); | |
266 } | |
267 } | |
268 | |
269 /// Interpreter for `Anm0` instructions to update a `Sprite`. | |
270 pub struct AnmRunner { | |
271 anms: Anms, | |
272 sprite: Rc<RefCell<Sprite>>, | |
273 prng: Weak<RefCell<Prng>>, | |
274 running: bool, | |
275 sprite_index_offset: u32, | |
276 script: Script, | |
277 instruction_pointer: usize, | |
278 frame: u16, | |
279 waiting: bool, | |
280 variables: ([i32; 4], [f32; 4], [i32; 4]), | |
281 timeout: Option<u32>, | |
282 } | |
283 | |
284 impl AnmRunner { | |
285 /// Create a new `AnmRunner`. | |
286 pub fn new(anms: Rc<RefCell<[Anm0]>>, script_id: u8, sprite: Rc<RefCell<Sprite>>, prng: Weak<RefCell<Prng>>, sprite_index_offset: u32) -> AnmRunner { | |
287 let anms = Anms::new(anms); | |
288 let script = anms.get_script(script_id); | |
289 let mut runner = AnmRunner { | |
290 anms, | |
291 sprite: sprite, | |
292 prng, | |
293 running: true, | |
294 waiting: false, | |
295 | |
296 script, | |
297 frame: 0, | |
298 timeout: None, | |
299 instruction_pointer: 0, | |
300 variables: ([0, 0, 0, 0 ], | |
301 [0., 0., 0., 0.], | |
302 [0, 0, 0, 0 ]), | |
303 | |
304 sprite_index_offset: sprite_index_offset, | |
305 }; | |
306 runner.run_frame(); | |
307 runner.sprite_index_offset = 0; | |
308 runner | |
309 } | |
310 | |
311 /// Get a Rc from the inner Sprite. | |
312 pub fn get_sprite(&self) -> Rc<RefCell<Sprite>> { | |
313 self.sprite.clone() | |
314 } | |
315 | |
316 /// Trigger an interrupt. | |
317 pub fn interrupt(&mut self, interrupt: i32) -> bool { | |
318 let mut new_ip = self.script.interrupts.get(&interrupt); | |
319 if new_ip.is_none() { | |
320 new_ip = self.script.interrupts.get(&-1); | |
321 } | |
322 let new_ip = if let Some(new_ip) = new_ip { | |
323 *new_ip as usize | |
324 } else { | |
325 return false; | |
326 }; | |
327 self.instruction_pointer = new_ip; | |
328 let Call { time: frame, instr: _ } = &self.script.instructions[self.instruction_pointer]; | |
329 self.frame = *frame; | |
330 self.waiting = false; | |
331 self.sprite.borrow_mut().visible = true; | |
332 true | |
333 } | |
334 | |
335 /// Advance the Anm of a single frame. | |
336 pub fn run_frame(&mut self) -> bool { | |
337 if !self.running { | |
338 return false; | |
339 } | |
340 | |
341 while self.running && !self.waiting { | |
342 let Call { time: frame, instr } = self.script.instructions[self.instruction_pointer]; | |
343 let frame = frame.clone(); | |
344 | |
345 if frame > self.frame { | |
346 break; | |
347 } else { | |
348 self.instruction_pointer += 1; | |
349 } | |
350 | |
351 if frame == self.frame { | |
352 self.run_instruction(instr); | |
353 self.sprite.borrow_mut().changed = true; | |
354 } | |
355 } | |
356 | |
357 if !self.waiting { | |
358 self.frame += 1; | |
359 } else if let Some(timeout) = self.timeout { | |
360 if timeout == self.sprite.borrow().frame { // TODO: check if it’s happening at the correct frame. | |
361 self.waiting = false; | |
362 } | |
363 } | |
364 | |
365 self.sprite.borrow_mut().update(); | |
366 | |
367 self.running | |
368 } | |
369 | |
370 fn run_instruction(&mut self, instruction: Instruction) { | |
371 let mut sprite = self.sprite.borrow_mut(); | |
372 match instruction { | |
373 Instruction::Delete() => { | |
374 sprite.removed = true; | |
375 self.running = false; | |
376 } | |
377 Instruction::LoadSprite(sprite_index) => { | |
378 self.anms.load_sprite(&mut sprite, (sprite_index + self.sprite_index_offset) as u8); | |
379 } | |
380 Instruction::SetScale(sx, sy) => { | |
381 sprite.rescale = [sx, sy]; | |
382 } | |
383 Instruction::SetAlpha(alpha) => { | |
384 // TODO: check this modulo. | |
385 sprite.color[3] = (alpha % 256) as u8; | |
386 } | |
387 Instruction::SetColor(b, g, r) => { | |
388 if sprite.fade_interpolator.is_none() { | |
389 sprite.color[0] = r; | |
390 sprite.color[1] = g; | |
391 sprite.color[2] = b; | |
392 } | |
393 } | |
394 Instruction::Jump(pointer) => { | |
395 // TODO: is that really how it works? | |
396 self.instruction_pointer = pointer as usize; | |
397 self.frame = self.script.instructions[pointer as usize].time; | |
398 } | |
399 Instruction::ToggleMirrored() => { | |
400 sprite.mirrored = !sprite.mirrored; | |
401 } | |
402 Instruction::SetRotations3d(rx, ry, rz) => { | |
403 sprite.rotations_3d = [rx, ry, rz]; | |
404 } | |
405 Instruction::SetRotationsSpeed3d(srx, sry, srz) => { | |
406 sprite.rotations_speed_3d = [srx, sry, srz]; | |
407 } | |
408 Instruction::SetScaleSpeed(ssx, ssy) => { | |
409 sprite.scale_speed = [ssx, ssy]; | |
410 } | |
411 Instruction::Fade(new_alpha, duration) => { | |
412 sprite.fade_interpolator = Some(Interpolator1::new([sprite.color[3] as f32], sprite.frame, [new_alpha as f32], sprite.frame + duration, Formula::Linear)); | |
413 } | |
414 Instruction::SetBlendmodeAlphablend() => { | |
415 sprite.blendfunc = 1; | |
416 } | |
417 Instruction::SetBlendmodeAdd() => { | |
418 sprite.blendfunc = 0; | |
419 } | |
420 Instruction::KeepStill() => { | |
421 self.running = false; | |
422 } | |
423 Instruction::LoadRandomSprite(min_index, mut amplitude) => { | |
424 if amplitude > 0 { | |
425 let prng = self.prng.upgrade().unwrap(); | |
426 let rand = prng.borrow_mut().get_u16(); | |
427 amplitude = (rand as u32) % amplitude; | |
428 } | |
429 let sprite_index = min_index + amplitude; | |
430 self.anms.load_sprite(&mut sprite, (sprite_index + self.sprite_index_offset) as u8); | |
431 } | |
432 Instruction::Move(x, y, z) => { | |
433 sprite.dest_offset = [x, y, z]; | |
434 } | |
435 Instruction::MoveToLinear(x, y, z, duration) => { | |
436 sprite.offset_interpolator = Some(Interpolator3::new(sprite.dest_offset, sprite.frame, [x, y, z], sprite.frame + duration, Formula::Linear)); | |
437 } | |
438 Instruction::MoveToDecel(x, y, z, duration) => { | |
439 sprite.offset_interpolator = Some(Interpolator3::new(sprite.dest_offset, sprite.frame, [x, y, z], sprite.frame + duration, Formula::InvertPower2)); | |
440 } | |
441 Instruction::MoveToAccel(x, y, z, duration) => { | |
442 sprite.offset_interpolator = Some(Interpolator3::new(sprite.dest_offset, sprite.frame, [x, y, z], sprite.frame + duration, Formula::Power2)); | |
443 } | |
444 Instruction::Wait() => { | |
445 self.waiting = true; | |
446 } | |
447 // There is nothing to do here. | |
448 Instruction::InterruptLabel(_label) => (), | |
449 Instruction::SetCornerRelativePlacement() => { | |
450 sprite.corner_relative_placement = true; | |
451 } | |
452 Instruction::WaitEx() => { | |
453 sprite.visible = false; | |
454 self.waiting = true; | |
455 } | |
456 Instruction::SetAllowOffset(value) => { | |
457 sprite.allow_dest_offset = value == 1 | |
458 } | |
459 Instruction::SetAutomaticOrientation(value) => { | |
460 sprite.automatic_orientation = value == 1 | |
461 } | |
462 Instruction::ShiftTextureX(dx) => { | |
463 let [tox, toy] = sprite.texoffsets; | |
464 sprite.texoffsets = [tox + dx, toy]; | |
465 } | |
466 Instruction::ShiftTextureY(dy) => { | |
467 let [tox, toy] = sprite.texoffsets; | |
468 sprite.texoffsets = [tox, toy + dy]; | |
469 } | |
470 Instruction::SetVisible(visible) => { | |
471 sprite.visible = (visible & 1) != 0; | |
472 } | |
473 Instruction::ScaleIn(sx, sy, duration) => { | |
474 sprite.scale_interpolator = Some(Interpolator2::new(sprite.rescale, sprite.frame, [sx, sy], sprite.frame + duration, Formula::Linear)); | |
475 } | |
476 Instruction::Todo(_todo) => { | |
477 // TODO. | |
478 } | |
479 } | |
480 } | |
481 } | |
482 | |
483 #[cfg(test)] | |
484 mod tests { | |
485 use super::*; | |
486 use std::io::{self, Read}; | |
487 use std::fs::File; | |
488 | |
489 #[test] | |
490 fn anm_runner() { | |
491 let file = File::open("EoSD/CM/player01.anm").unwrap(); | |
492 let mut file = io::BufReader::new(file); | |
493 let mut buf = vec![]; | |
494 file.read_to_end(&mut buf).unwrap(); | |
495 let (_, mut anms) = Anm0::from_slice(&buf).unwrap(); | |
496 let anm0 = anms.pop().unwrap(); | |
497 assert_eq!(anm0.size, (256, 256)); | |
498 assert_eq!(anm0.format, 5); | |
499 let sprite = Rc::new(RefCell::new(Sprite::new())); | |
500 let prng = Rc::new(RefCell::new(Prng::new(0))); | |
501 let mut anm_runner = AnmRunner::new(&anm0, 1, sprite.clone(), Rc::downgrade(&prng), 0); | |
502 for _ in 0..50 { | |
503 anm_runner.run_frame(); | |
504 } | |
505 } | |
506 } |