view interpreters/src/th06/std.rs @ 792:11bc22bad1bf default tip

python: Replace the image crate with png We weren’t using any of its features anyway, so the png crate is exactly what we need, without the many heavy dependencies of image. https://github.com/image-rs/image-png/pull/670 will eventually make it even faster to build.
author Link Mauve <linkmauve@linkmauve.fr>
date Sat, 17 Jan 2026 22:22:25 +0100
parents 7f9b3f5001c2
children
line wrap: on
line source

//! Interpreter of STD files.

use touhou_formats::th06::std::{Stage, Call, Instruction};
use crate::th06::interpolator::{Interpolator, Formula};
use touhou_utils::math::{Mat4, setup_camera};
use std::cell::RefCell;
use std::rc::Rc;

/// Interpreter for Stage.
pub struct StageRunner {
    /// XXX: no pub.
    pub stage: Rc<RefCell<Stage>>,
    frame: u32,

    position: Interpolator<f32, 3>,
    direction: Interpolator<f32, 3>,

    /// XXX: no pub.
    pub fog_color: [f32; 4],
    /// XXX: no pub.
    pub fog_near: f32,
    /// XXX: no pub.
    pub fog_far: f32,
}

impl StageRunner {
    /// Create a new StageRunner attached to a Stage.
    pub fn new(stage: Rc<RefCell<Stage>>) -> StageRunner {
        StageRunner {
            stage,
            frame: 0,
            position: Interpolator::new([0., 0., 0.], 0, [0., 0., 0.], 0, Formula::Linear),
            direction: Interpolator::new([0., 0., 0.], 0, [0., 0., 0.], 0, Formula::Linear),
            fog_color: [1.; 4],
            fog_near: 0.,
            fog_far: 1000.,
        }
    }

    /// Advance the simulation one frame.
    pub fn run_frame(&mut self) {
        let stage = self.stage.borrow();

        for Call { time, instr } in stage.script.iter() {
            let time = *time;
            if time != self.frame {
                continue;
            }

            println!("{} {:?}", time, instr);

            match *instr {
                Instruction::SetViewpos(x, y, z) => {
                    self.position.set_start(time, [x, y, z]);
                    for Call { time, instr } in stage.script.iter().cloned() {
                        if time <= self.frame {
                            continue;
                        }
                        if let Instruction::SetViewpos(x, y, z) = instr {
                            self.position.set_end(time, [x, y, z]);
                            break;
                        }
                    }
                }
                Instruction::SetFog(b, g, r, a, near, far) => {
                    self.fog_color = [r as f32 / 255., g as f32 / 255., b as f32 / 255., a as f32 / 255.];
                    self.fog_near = near;
                    self.fog_far = far;
                }
                Instruction::SetViewpos2(dx, dy, dz) => {
                    let direction = [dx, dy, dz];
                    self.direction.set_start(time, if time == 0 { direction } else { self.direction.values(time) });
                    self.direction.set_end_values(direction);
                }
                Instruction::StartInterpolatingViewpos2(frame, _, _) => {
                    self.direction.set_end_frame(time + frame);
                }
                Instruction::StartInterpolatingFog(frame, _, _) => {
                }
                Instruction::Unknown(_, _, _) => {
                }
            }
        }

        self.frame += 1;
    }

    /// Generate the model-view matrix for the current frame.
    pub fn get_model_view(&self) -> Mat4 {
        let [x, y, z] = self.position.values(self.frame);

        let [dx, dy, dz] = self.direction.values(self.frame);

        let view = setup_camera(dx, dy, dz);

        let model = Mat4::new([[1., 0., 0., 0.],
                               [0., 1., 0., 0.],
                               [0., 0., 1., 0.],
                               [-x, -y, -z, 1.]]);
        model * view
    }
}