billboards, sprites, forest example
This commit is contained in:
156
examples/billboard_test.ce
Normal file
156
examples/billboard_test.ce
Normal 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
242
examples/forest.ce
Normal 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
117
examples/sprite_test.ce
Normal 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()
|
||||
Reference in New Issue
Block a user