billboards, sprites, forest example

This commit is contained in:
2025-12-14 00:08:40 -06:00
parent d6c4e35201
commit a223d3d2b3
5 changed files with 1180 additions and 7 deletions

156
examples/billboard_test.ce Normal file
View File

@@ -0,0 +1,156 @@
// Billboard Test for retro3d
// Usage: cell run examples/billboard_test.ce <sprite_path>
// sprite_path should be without extension, e.g. "assets/goblin"
// Creates two rows of billboards going off into the distance like alongside a road
// Colors shift through hues as they get further away
var time_mod = use('time')
var retro3d = use('core')
var sprite_path = args[0]
if (!sprite_path) {
log.console("Usage: cell run examples/billboard_test.ce <sprite_path>")
log.console(" sprite_path: path without extension (e.g. 'assets/goblin')")
$_.stop()
}
var sprite = null
var last_time = 0
var cam_angle = 0
function hsv_to_rgb(h, s, v) {
var c = v * s
var x = c * (1 - Math.abs((h / 60) % 2 - 1))
var m = v - c
var r = 0, g = 0, b = 0
if (h < 60) { r = c; g = x; b = 0 }
else if (h < 120) { r = x; g = c; b = 0 }
else if (h < 180) { r = 0; g = c; b = x }
else if (h < 240) { r = 0; g = x; b = c }
else if (h < 300) { r = x; g = 0; b = c }
else { r = c; g = 0; b = x }
return [r + m, g + m, b + m, 1]
}
function _init() {
log.console("retro3d Billboard Test")
log.console("Loading sprite: " + sprite_path)
retro3d.set_style("ps1")
sprite = retro3d.load_sprite(sprite_path)
if (!sprite) {
log.console("Error: Could not load sprite: " + sprite_path)
$_.stop()
return
}
log.console("Sprite loaded: " + text(sprite.width) + "x" + text(sprite.height))
log.console("Controls:")
log.console(" A/D - Rotate camera")
log.console(" ESC - Exit")
retro3d.set_ambient(0.4, 0.4, 0.5)
retro3d.set_light_dir(0.5, 1.0, 0.3, 1.0, 0.95, 0.9, 1.0)
last_time = time_mod.number()
frame()
}
function _update(dt) {
if (retro3d._state.keys_held['escape']) {
$_.stop()
}
if (retro3d._state.keys_held['a']) {
cam_angle = cam_angle - 1.5 * dt
}
if (retro3d._state.keys_held['d']) {
cam_angle = cam_angle + 1.5 * dt
}
}
function _draw() {
retro3d.clear(0.2, 0.3, 0.4, 1.0)
if (!sprite) return
// Set up perspective camera
retro3d.camera_perspective(60, 0.1, 100)
// Camera orbits around origin
var cam_dist = 8
var cam_x = Math.sin(cam_angle) * cam_dist
var cam_z = Math.cos(cam_angle) * cam_dist
var cam_y = 2
retro3d.camera_look_at(cam_x, cam_y, cam_z, 0, 1, 0)
// Draw ground plane
retro3d.push_state()
var ground_mat = retro3d.make_material("lit", {
color: [0.3, 0.4, 0.3, 1]
})
retro3d.set_material(ground_mat)
var ground = retro3d.make_plane(20, 20)
var ground_transform = retro3d.make_transform()
retro3d.draw_model(ground, ground_transform)
retro3d.pop_state()
// Draw two rows of billboards going into the distance
// Left row at x = -2, right row at x = 2
var num_billboards = 10
var spacing = 2.0
var start_z = -2
for (var i = 0; i < num_billboards; i++) {
var z = start_z - i * spacing
var distance_norm = i / (num_billboards - 1) // 0 to 1
// Hue shifts from 0 (red) to 270 (purple) as distance increases
var hue = distance_norm * 270
var color = hsv_to_rgb(hue, 0.9, 1.0)
// Left row
retro3d.draw_billboard(sprite, -2, 0, z, {
color: color,
mode: "cutout",
face: "y"
})
// Right row (slightly different hue offset)
var hue_right = (hue + 30) % 360
var color_right = hsv_to_rgb(hue_right, 0.9, 1.0)
retro3d.draw_billboard(sprite, 2, 0, z, {
color: color_right,
mode: "cutout",
face: "y"
})
}
}
function frame() {
retro3d._begin_frame()
if (!retro3d._process_events()) {
log.console("Exiting...")
$_.stop()
return
}
var now = time_mod.number()
var dt = now - last_time
last_time = now
_update(dt)
_draw()
retro3d._end_frame()
$_.delay(frame, 1/60)
}
_init()

242
examples/forest.ce Normal file
View File

@@ -0,0 +1,242 @@
// Forest example (Diablo-style camera) for retro3d
// Fixed 3/4 camera, WASD movement, player faces the mouse using a ground screen-cast.
// Press ESC to exit.
var time_mod = use('time')
var retro3d = use('core')
var ground_model = null
var ground_transform = null
var ground_mat = null
var trunk_model = null
var trunk_mat = null
var canopy_model = null
var canopy_mat_a = null
var canopy_mat_b = null
var player_model = null
var player_mat = null
var marker_model = null
var marker_transform = null
var marker_mat = null
var player = {
transform: null,
speed: 6.0,
yaw: 0
}
var trees = []
var num_trees = 80
var cam_offset = { x: 10, y: 12, z: 10 }
var last_time = 0
var last_hit = null
function _set_camera() {
var px = player.transform.x
var pz = player.transform.z
retro3d.camera_perspective(55, 0.1, 300)
retro3d.camera_look_at(
px + cam_offset.x,
cam_offset.y,
pz + cam_offset.z,
px, 0, pz
)
}
function _set_yaw(t, yaw) {
player.yaw = yaw
var half = yaw / 2
retro3d.transform_set_rotation_quat(t, 0, Math.sin(half), 0, Math.cos(half))
}
function _init() {
log.console("retro3d Forest (Diablo camera) Example")
log.console("WASD move")
log.console("Face mouse (ground cast)")
log.console("ESC exit")
retro3d.set_style("ps1")
retro3d.seed(1337)
retro3d.set_ambient(0.35, 0.35, 0.40)
retro3d.set_light_dir(0.4, 1.0, 0.2, 1.0, 0.95, 0.9, 1.0)
retro3d.set_fog(40, 160, [0.55, 0.70, 0.90])
ground_model = retro3d.make_plane(220, 220)
ground_transform = retro3d.make_transform()
retro3d.transform_set_position(ground_transform, 0, 0, 0)
ground_mat = retro3d.make_material("lit", { color: [0.12, 0.22, 0.10, 1.0] })
trunk_model = retro3d.make_cube(1, 1, 1)
canopy_model = retro3d.make_cube(1, 1, 1)
trunk_mat = retro3d.make_material("lit", { color: [0.35, 0.23, 0.14, 1.0] })
canopy_mat_a = retro3d.make_material("lit", { color: [0.11, 0.40, 0.18, 1.0] })
canopy_mat_b = retro3d.make_material("lit", { color: [0.09, 0.33, 0.14, 1.0] })
player_model = retro3d.make_cube(1, 1, 1)
player_mat = retro3d.make_material("lit", { color: [0.85, 0.25, 0.20, 1.0] })
player.transform = retro3d.make_transform()
retro3d.transform_set_position(player.transform, 0, 0.5, 0)
retro3d.transform_set_scale(player.transform, 0.7, 1.0, 0.7)
_set_yaw(player.transform, 0)
marker_model = retro3d.make_cube(1, 1, 1)
marker_mat = retro3d.make_material("unlit", { color: [1.0, 0.95, 0.2, 1.0] })
marker_transform = retro3d.make_transform()
retro3d.transform_set_scale(marker_transform, 0.15, 0.15, 0.15)
for (var i = 0; i < num_trees; i++) {
var x, z
x = (retro3d.rand() - 0.5) * 90
z = (retro3d.rand() - 0.5) * 90
var trunk_h = retro3d.rand() * 3 + 2
var trunk_r = retro3d.rand() * 0.25 + 0.25
var canopy_s = retro3d.rand() * 1.0 + 1.5
var trunk = retro3d.make_transform()
retro3d.transform_set_position(trunk, x, trunk_h / 2, z)
retro3d.transform_set_scale(trunk, trunk_r, trunk_h, trunk_r)
var canopy = retro3d.make_transform()
retro3d.transform_set_position(canopy, x, trunk_h + canopy_s / 2, z)
retro3d.transform_set_scale(canopy, canopy_s, canopy_s, canopy_s)
trees.push({
trunk: trunk,
canopy: canopy,
canopy_mat: retro3d.rand() < 0.5 ? canopy_mat_a : canopy_mat_b
})
}
last_time = time_mod.number()
frame()
}
function _update(dt) {
if (retro3d._state.keys_held['escape']) {
$_.stop()
return
}
_set_camera()
var mx = retro3d._state.mouse_x || 0
var my = retro3d._state.mouse_y || 0
var hit = retro3d.screen_cast_ground(mx, my, 0)
if (hit) {
last_hit = hit
retro3d.transform_set_position(marker_transform, hit.x, 0.05, hit.z)
var px = player.transform.x
var pz = player.transform.z
var dx = hit.x - px
var dz = hit.z - pz
if (dx * dx + dz * dz > 0.00001) {
var yaw = Math.atan2(dx, dz)
_set_yaw(player.transform, yaw)
}
}
var forward = 0
var right = 0
if (retro3d._state.keys_held['w']) forward += 1
if (retro3d._state.keys_held['s']) forward -= 1
if (retro3d._state.keys_held['d']) right -= 1
if (retro3d._state.keys_held['a']) right += 1
if (forward != 0 || right != 0) {
var fx = Math.sin(player.yaw)
var fz = Math.cos(player.yaw)
var rx = Math.cos(player.yaw)
var rz = -Math.sin(player.yaw)
var vx = fx * forward + rx * right
var vz = fz * forward + rz * right
var len = Math.sqrt(vx * vx + vz * vz)
if (len > 0) {
vx /= len
vz /= len
}
var px = player.transform.x + vx * player.speed * dt
var pz = player.transform.z + vz * player.speed * dt
retro3d.transform_set_position(player.transform, px, 0.5, pz)
}
}
function _draw() {
retro3d.clear(0.55, 0.70, 0.90, 1.0)
_set_camera()
retro3d.push_state()
retro3d.set_material(ground_mat)
retro3d.draw_model(ground_model, ground_transform)
retro3d.pop_state()
retro3d.push_state()
retro3d.set_material(trunk_mat)
for (let i = 0; i < trees.length; i++) {
retro3d.draw_model(trunk_model, trees[i].trunk)
}
retro3d.pop_state()
retro3d.push_state()
for (let i = 0; i < trees.length; i++) {
retro3d.set_material(trees[i].canopy_mat)
retro3d.draw_model(canopy_model, trees[i].canopy)
}
retro3d.pop_state()
retro3d.push_state()
retro3d.set_material(player_mat)
retro3d.draw_model(player_model, player.transform)
retro3d.pop_state()
if (last_hit) {
retro3d.push_state()
retro3d.set_material(marker_mat)
retro3d.draw_model(marker_model, marker_transform)
retro3d.pop_state()
}
}
function frame() {
// Begin frame
retro3d._begin_frame()
// Process events
if (!retro3d._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 (submit GPU commands)
retro3d._end_frame()
// Schedule next frame
$_.delay(frame, 1/60)
}
// Start
_init()

117
examples/sprite_test.ce Normal file
View File

@@ -0,0 +1,117 @@
// Sprite Test for retro3d
// Usage: cell run examples/sprite_test.ce <sprite_path>
// sprite_path should be without extension, e.g. "assets/goblin"
// Fills screen with sprites, hue-shifted based on normalized screen position
var time_mod = use('time')
var retro3d = use('core')
var sprite_path = args[0]
if (!sprite_path) {
log.console("Usage: cell run examples/sprite_test.ce <sprite_path>")
log.console(" sprite_path: path without extension (e.g. 'assets/goblin')")
$_.stop()
}
var sprite = null
var last_time = 0
function hsv_to_rgb(h, s, v) {
var c = v * s
var x = c * (1 - Math.abs((h / 60) % 2 - 1))
var m = v - c
var r = 0, g = 0, b = 0
if (h < 60) { r = c; g = x; b = 0 }
else if (h < 120) { r = x; g = c; b = 0 }
else if (h < 180) { r = 0; g = c; b = x }
else if (h < 240) { r = 0; g = x; b = c }
else if (h < 300) { r = x; g = 0; b = c }
else { r = c; g = 0; b = x }
return [r + m, g + m, b + m, 1]
}
function _init() {
log.console("retro3d Sprite Test")
log.console("Loading sprite: " + sprite_path)
retro3d.set_style("ps1")
sprite = retro3d.load_sprite(sprite_path)
if (!sprite) {
log.console("Error: Could not load sprite: " + sprite_path)
$_.stop()
return
}
log.console("Sprite loaded: " + text(sprite.width) + "x" + text(sprite.height))
log.console("Press ESC to exit")
last_time = time_mod.number()
frame()
}
function _update(dt) {
if (retro3d._state.keys_held['escape']) {
$_.stop()
}
}
function _draw() {
retro3d.clear(0.1, 0.1, 0.15, 1.0)
if (!sprite) return
var w = sprite.width
var h = sprite.height
// Fill from bottom to top with sprites
// Hue shifts based on normalized screen position
var y = 0
while (y < 240) {
var x = 0
while (x < 320) {
// Normalized position (0-1)
var nx = x / 320
var ny = y / 240
// Hue based on position (0-360 degrees)
var hue = (nx + ny) * 180 // Combined X and Y influence
hue = hue % 360
var color = hsv_to_rgb(hue, 0.8, 1.0)
retro3d.draw_sprite(sprite, x, y, {
color: color,
mode: "cutout"
})
x = x + w
}
y = y + h
}
}
function frame() {
retro3d._begin_frame()
if (!retro3d._process_events()) {
log.console("Exiting...")
$_.stop()
return
}
var now = time_mod.number()
var dt = now - last_time
last_time = now
_update(dt)
_draw()
retro3d._end_frame()
$_.delay(frame, 1/60)
}
_init()