// Model Viewer for lance3d // Usage: cell run examples/modelview.ce // Controls: WASD orbit, Q/E zoom, R/F move target, SPACE toggle animation, 1-9 switch clip // F1/F2/F3 - Switch style (PS1/N64/Saturn) var io = use('fd') var time_mod = use('time') var lance3d = use('core') var math = use('math/radians') log.console(lance3d.key) // Parse command line arguments var model_path = args[0] || "Duck.glb" // Camera orbit state var cam_distance = 5 var cam_yaw = 0 var cam_pitch = 0.3 var cam_target_y = 0 var orbit_speed = 2.0 var zoom_speed = 0.5 // Model var model = null // Animation state var animations = [] var current_anim = 0 var anim_time = 0 var anim_playing = true var anim_speed = 1.0 // Current style var style = "ps1" // Timing var last_time = 0 function _init() { log.console("lance3d Model Viewer") log.console("Loading: " + model_path) // Initialize lance3d with PS1 style lance3d.set_style(style) // Load the model model = lance3d.load_model(model_path) if (!model) { log.console("Error: Could not load model: " + model_path) $stop() return } log.console("Model loaded with " + text(length(model)) + " mesh(es)") // Get animation info var i = null animations = lance3d.anim_info(model) log.console(" Animations: " + text(length(animations))) for (i = 0; i < length(animations); i++) { log.console(" " + text(i) + ": " + animations[i].name + " (" + text(animations[i].duration) + "s)") } // Set up lighting lance3d.set_lighting({ sun_dir: [0.5, 1.0, 0.3], sun_color: [1.0, 0.95, 0.9], ambient: [0.3, 0.3, 0.35] }) last_time = time_mod.number() log.console("") log.console("Controls:") log.console(" WASD - Orbit camera") log.console(" Q/E - Zoom in/out") log.console(" R/F - Move target up/down") log.console(" SPACE - Toggle animation") log.console(" 1-9 - Switch animation clip") log.console(" ESC - Exit") log.console(" F1 - PS1 style") log.console(" F2 - N64 style") log.console(" F3 - Saturn style") // Start the main loop frame() } function _update(dt) { var i = null var clip_idx = null var duration = null // Handle input for camera orbit if (lance3d.key('a')) { cam_yaw -= orbit_speed * dt } if (lance3d.key('d')) { cam_yaw += orbit_speed * dt } if (lance3d.key('w')) { cam_pitch += orbit_speed * dt if (cam_pitch > 1.5) cam_pitch = 1.5 } if (lance3d.key('s')) { cam_pitch -= orbit_speed * dt if (cam_pitch < -1.5) cam_pitch = -1.5 } // Zoom if (lance3d.key('q')) { cam_distance -= zoom_speed * dt * cam_distance if (cam_distance < 0.5) cam_distance = 0.5 } if (lance3d.key('e')) { cam_distance += zoom_speed * dt * cam_distance if (cam_distance > 100) cam_distance = 100 } // Move target up/down if (lance3d.key('r')) { cam_target_y += zoom_speed * dt } if (lance3d.key('f')) { cam_target_y -= zoom_speed * dt } // Exit on escape if (lance3d.key('escape')) { $stop() } // Toggle animation with space if (lance3d.keyp('space') && length(animations) > 0) { anim_playing = !anim_playing log.console(anim_playing ? "Animation resumed" : "Animation paused") } // Switch animation clips with number keys if (length(animations) > 0) { for (i = 1; i <= 9; i++) { if (lance3d.keyp(text(i))) { clip_idx = i - 1 if (clip_idx < length(animations)) { current_anim = clip_idx anim_time = 0 log.console("Playing clip " + text(clip_idx) + ": " + animations[clip_idx].name) } } } } // Switch platform style with F1-F3 if (lance3d.keyp('f1')) { _switch_to_style("ps1") } if (lance3d.keyp('f2')) { _switch_to_style("n64") } if (lance3d.keyp('f3')) { _switch_to_style("saturn") } // Update animation time if (anim_playing && length(animations) > 0) { anim_time += dt * anim_speed duration = animations[current_anim].duration if (duration > 0) { while (anim_time > duration) { anim_time -= duration } } } } function _switch_to_style(new_style) { if (style == new_style) return style = new_style lance3d.switch_style(style) log.console("Switched to " + style + " style") } function _draw() { var pose = null // Clear with a nice color based on style if (style == "ps1") { lance3d.clear(0.1, 0.05, 0.15, 1.0) } else if (style == "n64") { lance3d.clear(0.0, 0.1, 0.2, 1.0) } else { lance3d.clear(0.05, 0.05, 0.1, 1.0) } // Set up camera lance3d.camera_perspective(60, 0.1, 100) // Calculate camera position from orbit var cam_x = math.sine(cam_yaw) * math.cosine(cam_pitch) * cam_distance var cam_y = math.sine(cam_pitch) * cam_distance + cam_target_y var cam_z = math.cosine(cam_yaw) * math.cosine(cam_pitch) * cam_distance lance3d.camera_look_at({x: cam_x, y: cam_y, z: cam_z}, {x: 0, y: cam_target_y, z: 0}) // Draw the model if (model) { pose = null if (length(animations) > 0) { pose = lance3d.sample_pose(model, current_anim, anim_time) } lance3d.draw_model(model, null, pose) } } function frame() { // Begin frame lance3d._begin_frame() // Process events if (!lance3d._process_events()) { log.console("Exiting...") $stop() return } // Calculate delta time var now = time_mod.number() var dt = now - last_time last_time = now // Update _update(dt) // Draw _draw() // End frame lance3d._end_frame() // Schedule next frame $delay(frame, 1/240) } // Start _init()