Compare commits
4 Commits
0262ed7388
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b88d22aefc | ||
|
|
9147db6fdc | ||
|
|
ce6b0ddb3a | ||
|
|
ef7e3e6449 |
@@ -234,7 +234,7 @@ action.on_input = function(action_id, evt)
|
||||
var matched_actions = []
|
||||
arrfor(array(this.action_map), mapped_action => {
|
||||
if (find(this.action_map[mapped_action], action_id) != null) {
|
||||
push(matched_actions, mapped_action)
|
||||
matched_actions[] = mapped_action
|
||||
|
||||
if (evt.pressed)
|
||||
this.down[mapped_action] = true
|
||||
|
||||
193
clay.cm
@@ -1,17 +1,21 @@
|
||||
// clay2.cm - Revised UI layout engine emitting flat drawables
|
||||
// clay.cm - UI layout engine emitting flat drawables with annotated tree
|
||||
//
|
||||
// Changes from clay.cm:
|
||||
// - No __proto__, uses meme/merge
|
||||
// - Uses meme/merge for config chains
|
||||
// - Emits flat list of drawables for film2d
|
||||
// - Supports scissor clipping
|
||||
//
|
||||
// Now returns [drawable, drawable, ...] instead of {type:'group', ...}
|
||||
// - Returns annotated tree root with .drawables for clay_input compatibility
|
||||
|
||||
var layout = use('layout')
|
||||
var graphics = use('graphics')
|
||||
|
||||
var clay = {}
|
||||
|
||||
// Unique key objects for tree traversal (used by clay_input)
|
||||
var CHILDREN = {}
|
||||
var PARENT = {}
|
||||
clay.CHILDREN = CHILDREN
|
||||
clay.PARENT = PARENT
|
||||
|
||||
// Layout context
|
||||
var lay_ctx = layout.make_context()
|
||||
|
||||
@@ -65,28 +69,50 @@ var config_stack = []
|
||||
|
||||
// Rewriting state management for cleaner recursion
|
||||
var tree_stack = []
|
||||
var _next_id = 0
|
||||
|
||||
function annotate_tree(node, root_height, parent_node) {
|
||||
var rect = lay_ctx.get_rect(node.id)
|
||||
node.boundingbox = {
|
||||
x: rect.x,
|
||||
y: root_height - (rect.y + rect.height),
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
}
|
||||
node[CHILDREN] = node.children
|
||||
node[PARENT] = parent_node
|
||||
arrfor(node.children, function(child) {
|
||||
annotate_tree(child, root_height, node)
|
||||
})
|
||||
}
|
||||
|
||||
clay.layout = function(fn, size) {
|
||||
var sz = is_array(size) ? {width: size[0], height: size[1]} : size
|
||||
lay_ctx.reset()
|
||||
_next_id = 0
|
||||
var root_id = lay_ctx.item()
|
||||
|
||||
lay_ctx.set_size(root_id, size)
|
||||
|
||||
lay_ctx.set_size(root_id, sz)
|
||||
lay_ctx.set_contain(root_id, layout.contain.row)
|
||||
|
||||
|
||||
var root_node = {
|
||||
id: root_id,
|
||||
config: meme(base_config, {size: size}),
|
||||
config: meme(base_config, {size: sz}),
|
||||
children: []
|
||||
}
|
||||
|
||||
|
||||
tree_stack = [root_node]
|
||||
|
||||
|
||||
fn() // User builds tree
|
||||
|
||||
|
||||
lay_ctx.run()
|
||||
|
||||
// Post-layout: build flat drawable list
|
||||
return build_drawables(root_node, size.height)
|
||||
|
||||
// Annotate tree for clay_input (boundingbox, CHILDREN, PARENT)
|
||||
annotate_tree(root_node, sz.height, null)
|
||||
|
||||
// Build flat drawable list and attach to tree root
|
||||
root_node.drawables = build_drawables(root_node, sz.height)
|
||||
return root_node
|
||||
}
|
||||
|
||||
function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
@@ -130,8 +156,10 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
|
||||
// Background
|
||||
if (node.config.background_image) {
|
||||
_next_id = _next_id + 1
|
||||
if (node.config.slice) {
|
||||
push(drawables, {
|
||||
drawables[] = {
|
||||
_id: _next_id,
|
||||
type: 'sprite',
|
||||
image: node.config.background_image,
|
||||
pos: {x: vis_x, y: vis_y},
|
||||
@@ -139,11 +167,12 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
height: rect.height,
|
||||
slice: node.config.slice,
|
||||
color: node.config.background_color || {r:1, g:1, b:1, a:1},
|
||||
layer: p_layer - 0.1, // slightly behind content
|
||||
layer: p_layer - 0.1,
|
||||
scissor: current_scissor
|
||||
})
|
||||
}
|
||||
} else {
|
||||
push(drawables, {
|
||||
drawables[] = {
|
||||
_id: _next_id,
|
||||
type: 'sprite',
|
||||
image: node.config.background_image,
|
||||
pos: {x: vis_x, y: vis_y},
|
||||
@@ -152,10 +181,12 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
color: node.config.background_color || {r:1, g:1, b:1, a:1},
|
||||
layer: p_layer - 0.1,
|
||||
scissor: current_scissor
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (node.config.background_color) {
|
||||
push(drawables, {
|
||||
_next_id = _next_id + 1
|
||||
drawables[] = {
|
||||
_id: _next_id,
|
||||
type: 'rect',
|
||||
pos: {x: vis_x, y: vis_y},
|
||||
width: rect.width,
|
||||
@@ -163,43 +194,39 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
color: node.config.background_color,
|
||||
layer: p_layer - 0.1,
|
||||
scissor: current_scissor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Content (Image/Text)
|
||||
if (node.config.image) {
|
||||
push(drawables, {
|
||||
_next_id = _next_id + 1
|
||||
drawables[] = {
|
||||
_id: _next_id,
|
||||
type: 'sprite',
|
||||
image: node.config.image,
|
||||
pos: {x: vis_x, y: vis_y},
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
height: rect.height,
|
||||
color: node.config.color,
|
||||
layer: p_layer,
|
||||
scissor: current_scissor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (node.config.text) {
|
||||
push(drawables, {
|
||||
_next_id = _next_id + 1
|
||||
drawables[] = {
|
||||
_id: _next_id,
|
||||
type: 'text',
|
||||
text: node.config.text,
|
||||
font: node.config.font_path,
|
||||
size: node.config.font_size,
|
||||
size: node.config.font_size,
|
||||
color: node.config.color,
|
||||
pos: {x: vis_x, y: vis_y + rect.height}, // Baseline adjustment
|
||||
anchor_y: 1.0, // Text usually draws from baseline up or top down?
|
||||
// film2d text uses top-left by default unless anchor set.
|
||||
// Original clay put it at `y + rect.height`.
|
||||
// Let's assume origin top-left, so we might need anchor adjustment or just position.
|
||||
// If frame is top-down (0 at top), `abs_y` is top.
|
||||
// `rect.y` in layout is bottom-up? "rect.y is from bottom" says original comment.
|
||||
// `abs_y = root_height - (rect.y + rect.height)` -> Top edge of element.
|
||||
// Text usually wants baseline.
|
||||
// If we put it at `vis_y + rect.height`, that's bottom of element.
|
||||
pos: {x: vis_x, y: vis_y + rect.height},
|
||||
anchor_y: 1.0,
|
||||
layer: p_layer,
|
||||
scissor: current_scissor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Children
|
||||
@@ -216,6 +243,20 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
||||
function process_configs(configs) {
|
||||
var cfg = meme(base_config, configs)
|
||||
|
||||
// Parse shorthand font string (e.g. 'blackcastle.64') into font_path and font_size
|
||||
var font_parts = null
|
||||
var parsed_size = null
|
||||
if (cfg.font && is_text(cfg.font)) {
|
||||
font_parts = array(cfg.font, '.')
|
||||
if (length(font_parts) >= 2) {
|
||||
parsed_size = number(font_parts[length(font_parts) - 1])
|
||||
if (parsed_size) {
|
||||
cfg.font_size = parsed_size
|
||||
cfg.font_path = font_parts[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg.color = normalize_color(cfg.color, base_config.color)
|
||||
if (cfg.background_color) cfg.background_color = normalize_color(cfg.background_color, {r:1,g:1,b:1,a:1})
|
||||
|
||||
@@ -250,15 +291,15 @@ function push_node(configs, contain_mode) {
|
||||
|
||||
// Add to parent
|
||||
var parent = tree_stack[length(tree_stack)-1]
|
||||
push(parent.children, node)
|
||||
parent.children[] = node
|
||||
lay_ctx.insert(parent.id, item)
|
||||
|
||||
push(tree_stack, node)
|
||||
tree_stack[] = node
|
||||
return node
|
||||
}
|
||||
|
||||
function pop_node() {
|
||||
pop(tree_stack)
|
||||
tree_stack[]
|
||||
}
|
||||
|
||||
// Generic container
|
||||
@@ -305,34 +346,34 @@ clay.zstack = function(configs, fn) {
|
||||
}
|
||||
|
||||
// Leaf nodes
|
||||
clay.image = function(path, configs) {
|
||||
clay.image = function(path, configs, extra) {
|
||||
var img = graphics.texture(path)
|
||||
var c = [{image: path}]
|
||||
var final_config = process_configs(configs)
|
||||
var _configs = configs ? (is_array(configs) ? configs : [configs]) : []
|
||||
if (extra) _configs = array(_configs, is_array(extra) ? extra : [extra])
|
||||
var final_config = process_configs(_configs)
|
||||
if (!final_config.size && !final_config.behave)
|
||||
c.size = {width: img.width, height: img.height}
|
||||
|
||||
var _configs = is_array(configs) ? configs : [configs]
|
||||
c[] = {size: {width: img.width, height: img.height}}
|
||||
|
||||
push_node(array(c, _configs), null)
|
||||
pop_node()
|
||||
}
|
||||
|
||||
clay.text = function(str, configs) {
|
||||
clay.text = function(str, configs, extra) {
|
||||
var c = [{text: str}]
|
||||
var final_config = process_configs(configs)
|
||||
var _configs = configs ? (is_array(configs) ? configs : [configs]) : []
|
||||
if (extra) _configs = array(_configs, is_array(extra) ? extra : [extra])
|
||||
var final_config = process_configs(_configs)
|
||||
if (!final_config.size && !final_config.behave) {
|
||||
c.size = {width: 100, height: 20}
|
||||
c[] = {size: {width: 100, height: 20}}
|
||||
}
|
||||
|
||||
var _configs = is_array(configs) ? configs : [configs]
|
||||
|
||||
push_node(array(c, _configs), null)
|
||||
pop_node()
|
||||
}
|
||||
|
||||
clay.rectangle = function(configs) {
|
||||
var _configs = is_array(configs) ? configs : [configs]
|
||||
var _configs = configs ? (is_array(configs) ? configs : [configs]) : [{}]
|
||||
push_node(_configs, null)
|
||||
pop_node()
|
||||
}
|
||||
@@ -344,12 +385,54 @@ clay.button = function(str, action, configs) {
|
||||
}]
|
||||
|
||||
var _configs = is_array(configs) ? configs : [configs]
|
||||
var merged = process_configs(_configs)
|
||||
|
||||
clay.zstack(array(btn_config, _configs), function() {
|
||||
clay.text(str, {color: {r:1,g:1,b:1,a:1}})
|
||||
clay.text(str, {color: {r:1,g:1,b:1,a:1}, font_path: merged.font_path})
|
||||
})
|
||||
}
|
||||
|
||||
// Spacer — fills available space
|
||||
clay.spacer = function(config) {
|
||||
var cfg = config || {}
|
||||
if (!cfg.behave) cfg.behave = layout.behave.hfill | layout.behave.vfill
|
||||
push_node([cfg], null)
|
||||
pop_node()
|
||||
}
|
||||
|
||||
// Convenience draw wrapper — auto-detects call patterns:
|
||||
// clay.draw(fn) — default 640x360
|
||||
// clay.draw(fn, size_obj) — fn first, size object second
|
||||
// clay.draw(size_array, fn) — size array first, fn second
|
||||
clay.draw = function(arg1, arg2) {
|
||||
var fn = null
|
||||
var size = {width: 640, height: 360}
|
||||
if (is_function(arg1)) {
|
||||
fn = arg1
|
||||
if (arg2) {
|
||||
if (is_array(arg2)) size = {width: arg2[0], height: arg2[1]}
|
||||
else if (is_object(arg2)) size = arg2
|
||||
}
|
||||
} else if (is_array(arg1)) {
|
||||
size = {width: arg1[0], height: arg1[1]}
|
||||
fn = arg2
|
||||
}
|
||||
return clay.layout(fn, size)
|
||||
}
|
||||
|
||||
// Offset all drawables in an array by a position
|
||||
clay.offset_drawables = function(drawables, offset) {
|
||||
var result = []
|
||||
var i = 0
|
||||
var d = null
|
||||
for (i = 0; i < length(drawables); i = i + 1) {
|
||||
d = meme(drawables[i])
|
||||
d.pos = {x: d.pos.x + offset.x, y: d.pos.y + offset.y}
|
||||
result[] = d
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Constants
|
||||
clay.behave = layout.behave
|
||||
clay.contain = layout.contain
|
||||
|
||||
@@ -34,7 +34,8 @@ function find_path(node, path, pos) {
|
||||
if (!pointer_enabled(node)) return null
|
||||
if (!rect_contains(node, pos)) return null
|
||||
|
||||
var next_path = array(path, node)
|
||||
var next_path = array(path)
|
||||
next_path[] = node
|
||||
var i = 0
|
||||
var child = null
|
||||
var child_path = null
|
||||
@@ -79,7 +80,7 @@ clay_input.click = function click(tree_root, mousepos, button) {
|
||||
clay_input.get_actionable = function get_actionable(tree_root) {
|
||||
var actionable = []
|
||||
function walk(node) {
|
||||
if (node.config.action) push(actionable, node)
|
||||
if (node.config.action) actionable[] = node
|
||||
if (node[clay.CHILDREN])
|
||||
arrfor(node[clay.CHILDREN], walk)
|
||||
}
|
||||
@@ -90,7 +91,7 @@ clay_input.get_actionable = function get_actionable(tree_root) {
|
||||
clay_input.filter = function filter(tree_root, predicate) {
|
||||
var results = []
|
||||
function rec(node) {
|
||||
if (predicate(node)) push(results, node)
|
||||
if (predicate(node)) results[] = node
|
||||
if (node[clay.CHILDREN])
|
||||
arrfor(node[clay.CHILDREN], rec)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ collision2d.overlap = function(body, pos, others) {
|
||||
for (i = 0; i < length(others); i++) {
|
||||
other = others[i]
|
||||
if (collision2d.test(body, pos, other.body, other.pos))
|
||||
push(results, other)
|
||||
results[] = other
|
||||
}
|
||||
return results
|
||||
}
|
||||
@@ -94,12 +94,12 @@ collision2d.overlap_point = function(point, bodies) {
|
||||
hw = b.body.width * 0.5
|
||||
hh = b.body.height * 0.5
|
||||
if (abs(point.x - c.x) < hw && abs(point.y - c.y) < hh)
|
||||
push(results, b)
|
||||
results[] = b
|
||||
} else if (b.body.type == 'circle') {
|
||||
dx = point.x - c.x
|
||||
dy = point.y - c.y
|
||||
if (dx * dx + dy * dy < b.body.radius * b.body.radius)
|
||||
push(results, b)
|
||||
results[] = b
|
||||
}
|
||||
}
|
||||
return results
|
||||
|
||||
@@ -23,7 +23,7 @@ compositor.compile = function(config) {
|
||||
|
||||
// Clear screen
|
||||
if (config.clear)
|
||||
push(ctx.passes, {type: 'clear', target: 'screen', color: config.clear})
|
||||
ctx.passes[] = {type: 'clear', target: 'screen', color: config.clear}
|
||||
|
||||
// Process each plane (supports both 'planes' and legacy 'layers' key)
|
||||
var planes = config.planes || config.layers || []
|
||||
@@ -44,11 +44,11 @@ compositor.compile = function(config) {
|
||||
}
|
||||
|
||||
function compile_imgui_layer(layer, ctx) {
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'imgui',
|
||||
target: 'screen',
|
||||
draw: layer.draw
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function compile_plane(plane_config, ctx, group_effects) {
|
||||
@@ -75,7 +75,7 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
var di = 0
|
||||
if (plane_config.drawables) {
|
||||
for (di = 0; di < length(plane_config.drawables); di++)
|
||||
push(all_sprites, plane_config.drawables[di])
|
||||
all_sprites[] = plane_config.drawables[di]
|
||||
}
|
||||
|
||||
// Find which sprites belong to groups with effects
|
||||
@@ -110,21 +110,21 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
if (group_effects[gname]) {
|
||||
if (!effect_groups[gname])
|
||||
effect_groups[gname] = {sprites: [], effects: group_effects[gname].effects}
|
||||
push(effect_groups[gname].sprites, s)
|
||||
effect_groups[gname].sprites[] = s
|
||||
assigned = true
|
||||
break // Only assign to first matching effect group
|
||||
}
|
||||
}
|
||||
|
||||
// Add to base sprites if not assigned to effect group and not mask-only
|
||||
if (!assigned && !is_mask_only) push(base_sprites, s)
|
||||
if (!assigned && !is_mask_only) base_sprites[] = s
|
||||
}
|
||||
|
||||
// Allocate plane target
|
||||
var plane_target = ctx.alloc(res.width, res.height, plane_config.name)
|
||||
|
||||
// Always clear plane target to prevent stale data between frames
|
||||
push(ctx.passes, {type: 'clear', target: plane_target, color: plane_config.clear || {r: 0, g: 0, b: 0, a: 0}})
|
||||
ctx.passes[] = {type: 'clear', target: plane_target, color: plane_config.clear || {r: 0, g: 0, b: 0, a: 0}}
|
||||
|
||||
// Render each effect group to temp target, apply effects, composite back
|
||||
arrfor(array(effect_groups), gname => {
|
||||
@@ -134,7 +134,7 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
var group_target = ctx.alloc(res.width, res.height, gname + '_content')
|
||||
|
||||
// Render group content
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'render',
|
||||
renderer: 'film2d',
|
||||
drawables: eg.sprites,
|
||||
@@ -143,7 +143,7 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
target_size: res,
|
||||
layer_sort: layer_sort,
|
||||
clear: {r: 0, g: 0, b: 0, a: 0}
|
||||
})
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
var current = group_target
|
||||
@@ -155,19 +155,19 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
}
|
||||
|
||||
// Composite result to plane
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'composite',
|
||||
source: current,
|
||||
dest: plane_target,
|
||||
source_size: res,
|
||||
dest_size: res,
|
||||
blend: 'over'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Render base sprites (no effects)
|
||||
if (length(base_sprites) > 0) {
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'render',
|
||||
renderer: 'film2d',
|
||||
drawables: base_sprites,
|
||||
@@ -176,17 +176,17 @@ function compile_plane(plane_config, ctx, group_effects) {
|
||||
target_size: res,
|
||||
layer_sort: layer_sort,
|
||||
clear: null // Don't clear, blend on top
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Composite plane to screen
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'blit_to_screen',
|
||||
source: plane_target,
|
||||
source_size: res,
|
||||
dest_size: ctx.screen_size,
|
||||
presentation: plane_config.presentation || 'stretch'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function apply_effect(ctx, effect, input, params) {
|
||||
@@ -212,36 +212,37 @@ function apply_effect(ctx, effect, input, params) {
|
||||
blur2 = ctx.alloc(size.width, size.height, hint + '_blur2')
|
||||
|
||||
// Threshold
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'shader_pass',
|
||||
shader: 'threshold',
|
||||
input: input,
|
||||
output: bright,
|
||||
uniforms: {threshold: effect.threshold || 0.8, intensity: effect.intensity || 1}
|
||||
})
|
||||
}
|
||||
|
||||
// Blur passes
|
||||
blur_passes = effect.blur_passes || 2
|
||||
blur_passes = effect.blur_passes != null ? effect.blur_passes : 2
|
||||
blur_in = bright
|
||||
for (p = 0; p < blur_passes; p++) {
|
||||
push(ctx.passes, {type: 'shader_pass', shader: 'blur', input: blur_in, output: blur1, uniforms: {direction: {x: 1, y: 0}, texel_size: {x: 1/size.width, y: 1/size.height}}})
|
||||
push(ctx.passes, {type: 'shader_pass', shader: 'blur', input: blur1, output: blur2, uniforms: {direction: {x: 0, y: 1}, texel_size: {x: 1/size.width, y: 1/size.height}}})
|
||||
ctx.passes[] = {type: 'shader_pass', shader: 'blur', input: blur_in, output: blur1, uniforms: {direction: {x: 1, y: 0}, texel_size: {x: 1/size.width, y: 1/size.height}}}
|
||||
ctx.passes[] = {type: 'shader_pass', shader: 'blur', input: blur1, output: blur2, uniforms: {direction: {x: 0, y: 1}, texel_size: {x: 1/size.width, y: 1/size.height}}}
|
||||
blur_in = blur2
|
||||
}
|
||||
|
||||
// Composite bloom
|
||||
push(ctx.passes, {type: 'composite_textures', base: input, overlay: blur2, output: output, mode: 'add'})
|
||||
ctx.passes[] = {type: 'composite_textures', base: input, overlay: blur_in, output: output, mode: 'add'}
|
||||
|
||||
} else if (effect.type == 'mask') {
|
||||
mask_group = effect.mask_group
|
||||
// Query masks within the same plane to avoid cross-plane mask issues
|
||||
mask_sprites = film2d.query({group: mask_group, plane: current_plane})
|
||||
log.compositor("mask effect: group=" + mask_group + " plane=" + current_plane + " sprites=" + text(length(mask_sprites)))
|
||||
|
||||
if (length(mask_sprites) > 0) {
|
||||
mask_target = ctx.alloc(size.width, size.height, hint + '_mask')
|
||||
|
||||
// Render mask
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'render',
|
||||
renderer: 'film2d',
|
||||
drawables: mask_sprites,
|
||||
@@ -249,24 +250,24 @@ function apply_effect(ctx, effect, input, params) {
|
||||
target: mask_target,
|
||||
target_size: size,
|
||||
clear: {r: 0, g: 0, b: 0, a: 0}
|
||||
})
|
||||
}
|
||||
|
||||
// Apply mask
|
||||
push(ctx.passes, {
|
||||
ctx.passes[] = {
|
||||
type: 'apply_mask',
|
||||
content: input,
|
||||
mask: mask_target,
|
||||
output: output,
|
||||
mode: effect.channel || 'alpha',
|
||||
invert: effect.invert || false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// No mask sprites, pass through
|
||||
push(ctx.passes, {type: 'blit', source: input, dest: output})
|
||||
ctx.passes[] = {type: 'blit', source: input, dest: output}
|
||||
}
|
||||
} else {
|
||||
// Unknown effect, pass through
|
||||
push(ctx.passes, {type: 'blit', source: input, dest: output})
|
||||
ctx.passes[] = {type: 'blit', source: input, dest: output}
|
||||
}
|
||||
|
||||
return output
|
||||
@@ -302,8 +303,8 @@ compositor.execute = function(plan) {
|
||||
|
||||
if (pass.type == 'clear') {
|
||||
target = resolve(pass.target)
|
||||
push(commands, {cmd: 'begin_render', target: target, clear: pass.color})
|
||||
push(commands, {cmd: 'end_render'})
|
||||
commands[] = {cmd: 'begin_render', target: target, clear: pass.color}
|
||||
commands[] = {cmd: 'end_render'}
|
||||
|
||||
} else if (pass.type == 'render') {
|
||||
result = film2d.render({
|
||||
@@ -315,68 +316,68 @@ compositor.execute = function(plan) {
|
||||
clear: pass.clear
|
||||
}, backend)
|
||||
for (c = 0; c < length(result.commands); c++)
|
||||
push(commands, result.commands[c])
|
||||
commands[] = result.commands[c]
|
||||
|
||||
} else if (pass.type == 'shader_pass') {
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'shader_pass',
|
||||
shader: pass.shader,
|
||||
input: resolve(pass.input),
|
||||
output: resolve(pass.output),
|
||||
uniforms: pass.uniforms
|
||||
})
|
||||
}
|
||||
|
||||
} else if (pass.type == 'composite_textures') {
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'composite_textures',
|
||||
base: resolve(pass.base),
|
||||
overlay: resolve(pass.overlay),
|
||||
output: resolve(pass.output),
|
||||
mode: pass.mode
|
||||
})
|
||||
}
|
||||
|
||||
} else if (pass.type == 'apply_mask') {
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'apply_mask',
|
||||
content_texture: resolve(pass.content),
|
||||
mask_texture: resolve(pass.mask),
|
||||
output: resolve(pass.output),
|
||||
mode: pass.mode,
|
||||
invert: pass.invert
|
||||
})
|
||||
}
|
||||
|
||||
} else if (pass.type == 'composite') {
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'blit',
|
||||
texture: resolve(pass.source),
|
||||
target: resolve(pass.dest),
|
||||
dst_rect: {x: 0, y: 0, width: pass.dest_size.width, height: pass.dest_size.height}
|
||||
})
|
||||
}
|
||||
|
||||
} else if (pass.type == 'blit_to_screen') {
|
||||
rect = _calc_presentation(pass.source_size, pass.dest_size, pass.presentation)
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'blit',
|
||||
texture: resolve(pass.source),
|
||||
target: 'screen',
|
||||
dst_rect: rect,
|
||||
filter: pass.presentation == 'integer_scale' ? 'nearest' : 'linear'
|
||||
})
|
||||
}
|
||||
} else if (pass.type == 'blit') {
|
||||
src = resolve(pass.source)
|
||||
dst = resolve(pass.dest)
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'blit',
|
||||
texture: src,
|
||||
target: dst,
|
||||
dst_rect: {x: 0, y: 0, width: dst.width, height: dst.height}
|
||||
})
|
||||
}
|
||||
} else if (pass.type == 'imgui') {
|
||||
push(commands, {
|
||||
commands[] = {
|
||||
cmd: 'imgui',
|
||||
target: resolve(pass.target),
|
||||
draw: pass.draw
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +426,7 @@ compositor.snapshot = function() {
|
||||
for (i = 0; i < length(_last_plan.passes); i++) {
|
||||
pass = _last_plan.passes[i]
|
||||
if (pass.type == 'render') {
|
||||
push(planes, {
|
||||
planes[] = {
|
||||
drawable_count: length(pass.drawables),
|
||||
camera: pass.camera ? {
|
||||
pos: pass.camera.pos,
|
||||
@@ -433,7 +434,7 @@ compositor.snapshot = function() {
|
||||
height: pass.camera.height
|
||||
} : null,
|
||||
target_size: pass.target_size
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
||||
9
core.cm
@@ -78,6 +78,9 @@ core.start = function(config) {
|
||||
// Start main loop
|
||||
_main_loop()
|
||||
|
||||
// Call start callback after the first frame and main loop are established
|
||||
if (config.start) config.start()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ function fps_add_sample(sample) {
|
||||
var n = length(_fps_samples)
|
||||
var old = null
|
||||
if (n < _fps_sample_count) {
|
||||
push(_fps_samples, sample)
|
||||
_fps_samples[] = sample
|
||||
_fps_sample_sum += sample
|
||||
} else {
|
||||
old = _fps_samples[_fps_sample_pos]
|
||||
@@ -225,7 +228,7 @@ function _main_loop() {
|
||||
// Handle both compositor result ({commands: [...]}) and fx_graph (graph object)
|
||||
if (render_result.commands) {
|
||||
if (_config.imgui || _config.editor) {
|
||||
push(render_result.commands, {
|
||||
render_result.commands[] = {
|
||||
cmd: 'imgui',
|
||||
draw: function(ui) {
|
||||
if (_config.imgui) _config.imgui(ui)
|
||||
@@ -235,7 +238,7 @@ function _main_loop() {
|
||||
}
|
||||
},
|
||||
target: 'screen'
|
||||
})
|
||||
}
|
||||
}
|
||||
// Compositor result - execute commands directly
|
||||
_backend.execute_commands(render_result.commands, win_size, dbg)
|
||||
|
||||
@@ -144,31 +144,31 @@ function _render_node_summary(imgui, node) {
|
||||
var info = []
|
||||
|
||||
if (node.pos) {
|
||||
push(info, "pos:(" + text(round(node.pos.x)) + "," + text(round(node.pos.y)) + ")")
|
||||
info[] = "pos:(" + text(round(node.pos.x)) + "," + text(round(node.pos.y)) + ")"
|
||||
}
|
||||
|
||||
if (node.width && node.height) {
|
||||
push(info, "size:" + text(node.width) + "x" + text(node.height))
|
||||
info[] = "size:" + text(node.width) + "x" + text(node.height)
|
||||
}
|
||||
|
||||
if (node.image) {
|
||||
push(info, "img:" + node.image)
|
||||
info[] = "img:" + node.image
|
||||
}
|
||||
|
||||
var t = null
|
||||
if (node.text) {
|
||||
t = node.text
|
||||
if (length(t) > 20) t = text(t, 0, 17) + "..."
|
||||
push(info, "\"" + t + "\"")
|
||||
info[] = "\"" + t + "\""
|
||||
}
|
||||
|
||||
var fx = []
|
||||
var j = 0
|
||||
if (node.effects && length(node.effects) > 0) {
|
||||
for (j = 0; j < length(node.effects); j++) {
|
||||
push(fx, node.effects[j].type)
|
||||
fx[] = node.effects[j].type
|
||||
}
|
||||
push(info, "fx:[" + text(fx, ",") + "]")
|
||||
info[] = "fx:[" + text(fx, ",") + "]"
|
||||
}
|
||||
|
||||
if (length(info) > 0) {
|
||||
|
||||
34
effects.cm
@@ -36,7 +36,7 @@ effects.register('bloom', {
|
||||
|
||||
// Threshold extraction
|
||||
var thresh_target = ctx.alloc_target(size.width, size.height, 'bloom_thresh')
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'threshold',
|
||||
input: input,
|
||||
@@ -45,7 +45,7 @@ effects.register('bloom', {
|
||||
threshold: params.threshold != null ? params.threshold : 0.8,
|
||||
intensity: params.intensity != null ? params.intensity : 1.0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Blur ping-pong
|
||||
var blur_a = ctx.alloc_target(size.width, size.height, 'bloom_blur_a')
|
||||
@@ -56,31 +56,31 @@ effects.register('bloom', {
|
||||
var i = 0
|
||||
|
||||
for (i = 0; i < blur_count; i++) {
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'blur',
|
||||
input: blur_src,
|
||||
output: blur_a,
|
||||
uniforms: {direction: {x: 2, y: 0}, texel_size: texel}
|
||||
})
|
||||
push(passes, {
|
||||
}
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'blur',
|
||||
input: blur_a,
|
||||
output: blur_b,
|
||||
uniforms: {direction: {x: 0, y: 2}, texel_size: texel}
|
||||
})
|
||||
}
|
||||
blur_src = blur_b
|
||||
}
|
||||
|
||||
// Additive composite
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'composite',
|
||||
base: input,
|
||||
overlay: blur_src,
|
||||
output: output,
|
||||
blend: 'add'
|
||||
})
|
||||
}
|
||||
|
||||
return passes
|
||||
}
|
||||
@@ -125,16 +125,16 @@ effects.register('mask', {
|
||||
|
||||
// Render mask source to target
|
||||
var mask_target = ctx.alloc_target(size.width, size.height, 'mask_src')
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'render_subtree',
|
||||
root: mask_source,
|
||||
output: mask_target,
|
||||
clear: {r: 0, g: 0, b: 0, a: 0},
|
||||
space: params.space || 'local'
|
||||
})
|
||||
}
|
||||
|
||||
// Apply mask shader
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'mask',
|
||||
inputs: [input, mask_target],
|
||||
@@ -143,7 +143,7 @@ effects.register('mask', {
|
||||
channel: params.channel == 'alpha' ? 0 : 1,
|
||||
invert: params.invert ? 1 : 0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return passes
|
||||
}
|
||||
@@ -192,25 +192,25 @@ effects.register('blur', {
|
||||
var i = 0
|
||||
|
||||
for (i = 0; i < blur_count; i++) {
|
||||
push(passes, {
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'blur',
|
||||
input: src,
|
||||
output: blur_a,
|
||||
uniforms: {direction: {x: 2, y: 0}, texel_size: texel}
|
||||
})
|
||||
push(passes, {
|
||||
}
|
||||
passes[] = {
|
||||
type: 'shader',
|
||||
shader: 'blur',
|
||||
input: blur_a,
|
||||
output: blur_b,
|
||||
uniforms: {direction: {x: 0, y: 2}, texel_size: texel}
|
||||
})
|
||||
}
|
||||
src = blur_b
|
||||
}
|
||||
|
||||
// Final blit to output
|
||||
push(passes, {type: 'blit', source: src, dest: output})
|
||||
passes[] = {type: 'blit', source: src, dest: output}
|
||||
return passes
|
||||
}
|
||||
})
|
||||
|
||||
45
examples/bisect.ce
Normal file
@@ -0,0 +1,45 @@
|
||||
log.console("bisect: start")
|
||||
var sprite = use('sprite')
|
||||
var clay = use('clay')
|
||||
var core = use('core')
|
||||
var fx_graph = use('fx_graph')
|
||||
var sdl_gpu = use('sdl_gpu')
|
||||
log.console("bisect: modules loaded")
|
||||
|
||||
core.start({
|
||||
width: 640,
|
||||
height: 480,
|
||||
title: "Paladin Simple",
|
||||
framerate: 60,
|
||||
update: function(dt) {},
|
||||
render: function() {
|
||||
var win_size = core.window_size()
|
||||
var graph = fx_graph.create()
|
||||
|
||||
var ui_scene = clay.layout(function() {
|
||||
clay.container({padding: 20, contain: clay.contain.content}, function() {
|
||||
clay.vstack({
|
||||
padding: 20,
|
||||
spacing: 10,
|
||||
width: 200
|
||||
}, function() {
|
||||
clay.text("PALADIN UI", {font_size: 24, color: {r:1, g:0.8, b:0.2, a:1}})
|
||||
clay.text("Item 1")
|
||||
clay.text("Item 2")
|
||||
})
|
||||
})
|
||||
}, [win_size.width, win_size.height])
|
||||
|
||||
graph.add_node('render_view', {
|
||||
root: ui_scene,
|
||||
camera: {pos: [0, 0], width: win_size.width, height: win_size.height, anchor: [0, 0], ortho: true},
|
||||
target: 'screen',
|
||||
clear_color: {r:0,g:0,b:0,a:0}
|
||||
})
|
||||
|
||||
return graph
|
||||
},
|
||||
input: function(ev) {
|
||||
if (ev.type == 'quit') $stop()
|
||||
}
|
||||
})
|
||||
BIN
examples/bunny.png
Normal file
|
After Width: | Height: | Size: 449 B |
@@ -47,8 +47,8 @@ function add_bunny(x, y) {
|
||||
anchor_x: 0.5, anchor_y: 0.5,
|
||||
plane: 'game'
|
||||
})
|
||||
push(bunnies, bunny)
|
||||
push(bunny_sprites, s)
|
||||
bunnies[] = bunny
|
||||
bunny_sprites[] = s
|
||||
}
|
||||
|
||||
// Initial bunnies
|
||||
|
||||
@@ -29,14 +29,14 @@ function init(grid) {
|
||||
row = []
|
||||
for (bx = 0; bx < 8; bx++) {
|
||||
col = ((bx + by) & 1) ? dark_color : light_color
|
||||
push(row, shape.rect({
|
||||
row[] = shape.rect({
|
||||
pos: {x: bx * S + S / 2, y: by * S + S / 2},
|
||||
width: S, height: S,
|
||||
fill: {r: col.r, g: col.g, b: col.b, a: col.a},
|
||||
plane: 'game', layer: 0
|
||||
}))
|
||||
})
|
||||
}
|
||||
push(board_shapes, row)
|
||||
board_shapes[] = row
|
||||
}
|
||||
|
||||
next_id = 0
|
||||
|
||||
@@ -49,7 +49,7 @@ function compute_valid_moves(from) {
|
||||
dest = grid.at(to)
|
||||
if (length(dest) && dest[0].colour == piece.colour) continue
|
||||
if (rules.canMove(piece, from, to, grid))
|
||||
push(validMoves, to)
|
||||
validMoves[] = to
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ function compute_valid_moves(from) {
|
||||
dest = grid.at(to)
|
||||
if (length(dest) && dest[0].colour == piece.colour) continue
|
||||
if (rules.canMove(piece, from, to, grid))
|
||||
push(validMoves, to)
|
||||
validMoves[] = to
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ var grid_prototype = {
|
||||
// add an entity into a cell
|
||||
add(entity, pos) {
|
||||
var cx = px(pos), cy = py(pos);
|
||||
push(this.cell(cx, cy), entity);
|
||||
this.cell(cx, cy)[] = entity;
|
||||
entity.coord = [cx, cy];
|
||||
},
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ function compute_valid_moves(from) {
|
||||
dest = grid.at(to)
|
||||
if (length(dest) && dest[0].colour == piece.colour) continue
|
||||
if (rules.canMove(piece, from, to, grid))
|
||||
push(validMoves, to)
|
||||
validMoves[] = to
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
examples/enemies/barnacle_attack_a.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
examples/enemies/barnacle_attack_b.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
examples/enemies/barnacle_attack_rest.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/enemies/bee_a.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/bee_b.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
examples/enemies/bee_rest.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/block_fall.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
examples/enemies/block_idle.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/block_rest.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/fish_blue_rest.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
examples/enemies/fish_blue_swim_a.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/fish_blue_swim_b.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/fish_purple_down.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/fish_purple_rest.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/fish_purple_up.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/fish_yellow_rest.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/fish_yellow_swim_a.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/fish_yellow_swim_b.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
examples/enemies/fly_a.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
examples/enemies/fly_b.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/fly_rest.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
examples/enemies/frog_idle.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
examples/enemies/frog_jump.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
examples/enemies/frog_rest.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
examples/enemies/ladybug_fly.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
examples/enemies/ladybug_rest.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/ladybug_walk_a.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/ladybug_walk_b.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/mouse_rest.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/enemies/mouse_walk_a.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/mouse_walk_b.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/saw_a.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
examples/enemies/saw_b.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
examples/enemies/saw_rest.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
examples/enemies/slime_block_jump.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
examples/enemies/slime_block_rest.png
Normal file
|
After Width: | Height: | Size: 685 B |
BIN
examples/enemies/slime_block_walk_a.png
Normal file
|
After Width: | Height: | Size: 767 B |
BIN
examples/enemies/slime_block_walk_b.png
Normal file
|
After Width: | Height: | Size: 760 B |
BIN
examples/enemies/slime_fire_flat.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/enemies/slime_fire_rest.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/slime_fire_walk_a.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/slime_fire_walk_b.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/slime_normal_flat.png
Normal file
|
After Width: | Height: | Size: 631 B |
BIN
examples/enemies/slime_normal_rest.png
Normal file
|
After Width: | Height: | Size: 905 B |
BIN
examples/enemies/slime_normal_walk_a.png
Normal file
|
After Width: | Height: | Size: 972 B |
BIN
examples/enemies/slime_normal_walk_b.png
Normal file
|
After Width: | Height: | Size: 920 B |
BIN
examples/enemies/slime_spike_flat.png
Normal file
|
After Width: | Height: | Size: 941 B |
BIN
examples/enemies/slime_spike_rest.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/slime_spike_walk_a.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/slime_spike_walk_b.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/enemies/snail_rest.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/snail_shell.png
Normal file
|
After Width: | Height: | Size: 939 B |
BIN
examples/enemies/snail_walk_a.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/snail_walk_b.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
examples/enemies/worm_normal_move_a.png
Normal file
|
After Width: | Height: | Size: 1007 B |
BIN
examples/enemies/worm_normal_move_b.png
Normal file
|
After Width: | Height: | Size: 989 B |
BIN
examples/enemies/worm_normal_rest.png
Normal file
|
After Width: | Height: | Size: 690 B |
BIN
examples/enemies/worm_ring_move_a.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
examples/enemies/worm_ring_move_b.png
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
examples/enemies/worm_ring_rest.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
examples/fonts/c64.ttf
Normal file
BIN
examples/fonts/dos.ttf
Normal file
BIN
examples/fonts/teenytinypixels.ttf
Normal file
303
examples/paladin.ce
Normal file
@@ -0,0 +1,303 @@
|
||||
log.console("paladin game starting")
|
||||
|
||||
var time = use('time')
|
||||
var random = use('random').random
|
||||
|
||||
var core = use('core')
|
||||
var sprite = use('sprite')
|
||||
var text2d = use('text2d')
|
||||
var particles2d = use('particles2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var tilemap2d = use('tilemap2d')
|
||||
var clay = use('clay')
|
||||
|
||||
var sprites = [] // Keep references for cleanup
|
||||
var emitters = [] // Particle emitters
|
||||
var bunnies = [] // Bouncing bunnies for Y-sort test
|
||||
|
||||
// Cameras
|
||||
var world_camera = {
|
||||
pos: {x: 2.5, y: 2.5},
|
||||
width: 5,
|
||||
height: 5,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var hud_camera = {
|
||||
pos: {x: 0, y: 0},
|
||||
width: 640,
|
||||
height: 360,
|
||||
anchor: {x: 0, y: 0}
|
||||
}
|
||||
|
||||
// Group effects definition - THIS IS THE KEY PART
|
||||
var group_effects = {
|
||||
'bloom': {
|
||||
effects: [{type: 'bloom', threshold: 0.1, intensity: 5, blur_passes: 1}]
|
||||
},
|
||||
'masked_fire': {
|
||||
effects: [
|
||||
{type: 'bloom', threshold: 0.1, intensity: 5, blur_passes: 0},
|
||||
{type: 'mask', channel: 'alpha', mask_group: 'mask_text'}
|
||||
]
|
||||
},
|
||||
'corner_fire': {
|
||||
effects: [{type: 'mask', channel: 'alpha', mask_group: 'corner_mask'}]
|
||||
}
|
||||
}
|
||||
|
||||
// Compositor config - pure data
|
||||
var compositor_config = {
|
||||
clear: {r: 0, g: 0, b: 0, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'world',
|
||||
plane: 'world',
|
||||
camera: world_camera,
|
||||
resolution: {width: 500, height: 500},
|
||||
presentation: 'letterbox',
|
||||
clear: {r: 0.1, g: 0.1, b: 0.9, a: 1}
|
||||
},
|
||||
{
|
||||
name: 'hud',
|
||||
plane: 'hud',
|
||||
camera: hud_camera,
|
||||
resolution: {width: 640, height: 360},
|
||||
presentation: 'integer_scale',
|
||||
clear: {r: 0, g: 0, b: 0, a: 0},
|
||||
layer_sort: {
|
||||
'100': 'y' // Bunny layer uses Y-sort
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'ui',
|
||||
plane: 'ui',
|
||||
camera: hud_camera,
|
||||
resolution: {width: 640, height: 360},
|
||||
presentation: 'integer_scale',
|
||||
clear: null
|
||||
}
|
||||
],
|
||||
group_effects
|
||||
}
|
||||
|
||||
function init_game() {
|
||||
// Cleanup old sprites
|
||||
var i = 0
|
||||
for (i = 0; i < length(sprites); i++)
|
||||
sprites[i].destroy()
|
||||
sprites = []
|
||||
emitters = []
|
||||
bunnies = []
|
||||
|
||||
// World sprites
|
||||
|
||||
// Background Tilemap
|
||||
var tiles = []
|
||||
var x = 0
|
||||
var y = 0
|
||||
for (x = 0; x < 5; x++) {
|
||||
tiles[x] = []
|
||||
for (y = 0; y < 5; y++) {
|
||||
tiles[x][y] = 'examples/tiles/terrain_dirt_cloud'
|
||||
}
|
||||
}
|
||||
|
||||
sprites[] = tilemap2d({
|
||||
plane: 'world',
|
||||
layer: 0,
|
||||
tiles: tiles,
|
||||
tile_width: 1,
|
||||
tile_height: 1,
|
||||
offset_x: 0,
|
||||
offset_y: 0
|
||||
})
|
||||
|
||||
sprites[] = sprite({
|
||||
plane: 'world',
|
||||
layer: 10,
|
||||
image: 'examples/enemies/saw_a',
|
||||
pos: {x: 1, y: 1},
|
||||
anchor_x: 0.5,
|
||||
})
|
||||
|
||||
// HUD sprites
|
||||
sprites[] = text2d({
|
||||
plane: 'hud',
|
||||
groups: ['mask_text'], // Effect routing only
|
||||
layer: 50,
|
||||
text: 'PALADIN',
|
||||
pos: {x: 640/2 - 270, y: 360/2},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 150
|
||||
})
|
||||
|
||||
var masked_fire = particles2d.create({
|
||||
plane: 'hud',
|
||||
groups: ['masked_fire'], // Effect routing only
|
||||
layer: 40,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 40,
|
||||
height: 40
|
||||
})
|
||||
sprites[] = masked_fire
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 320, y: 180},
|
||||
spawn_area: {width: 500, height: 200},
|
||||
velocity: {x: 0, y: 50},
|
||||
velocity_var: {x: 30, y: 30},
|
||||
life: 2,
|
||||
rate: 60,
|
||||
scale: 3,
|
||||
scale_var: 0.3,
|
||||
color: {r: 1, g: 0.6, b: 0.2},
|
||||
handle: masked_fire
|
||||
})
|
||||
|
||||
sprites[] = sprite({
|
||||
plane: 'hud',
|
||||
groups: ['corner_mask'], // Effect routing only
|
||||
layer: 60,
|
||||
image: 'examples/tiles/fireball',
|
||||
pos: {x: 600, y: 40},
|
||||
width: 50,
|
||||
height: 50,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5
|
||||
})
|
||||
|
||||
var corner_fire = particles2d.create({
|
||||
plane: 'hud',
|
||||
groups: ['corner_fire'], // Effect routing only
|
||||
layer: 70,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 25,
|
||||
height: 25
|
||||
})
|
||||
sprites[] = corner_fire
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 600, y: 40},
|
||||
spawn_area: {width: 30, height: 30},
|
||||
velocity: {x: 0, y: 30},
|
||||
velocity_var: {x: 20, y: 20},
|
||||
life: 1.5,
|
||||
rate: 10,
|
||||
scale: 1,
|
||||
scale_var: 0.2,
|
||||
color: {r: 1, g: 0.5, b: 0.1},
|
||||
handle: corner_fire
|
||||
})
|
||||
|
||||
var bloom_fire = particles2d.create({
|
||||
plane: 'hud',
|
||||
groups: ['bloom'], // Effect routing only
|
||||
layer: 80,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 48,
|
||||
height: 48
|
||||
})
|
||||
sprites[] = bloom_fire
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 100, y: 100},
|
||||
spawn_area: {width: 50, height: 50},
|
||||
velocity: {x: 100, y: 0},
|
||||
velocity_var: {x: 40, y: 40},
|
||||
life: 2.5,
|
||||
rate: 12,
|
||||
scale: 1.2,
|
||||
scale_var: 0.4,
|
||||
color: {r: 1, g: 0.8, b: 0.3},
|
||||
handle: bloom_fire
|
||||
})
|
||||
|
||||
// Bouncing bunnies - exactly two, big, bottom of screen, Y-sort layer 100
|
||||
var bunny = null
|
||||
for (i = 0; i < 2; i++) {
|
||||
bunny = sprite({
|
||||
plane: 'hud',
|
||||
layer: 100, // This layer has Y-sort enabled
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 320 + (i == 0 ? -20 : 20), y: 340},
|
||||
width: 96,
|
||||
height: 96,
|
||||
anchor_x: 0.5,
|
||||
})
|
||||
|
||||
bunny._base_y = 340
|
||||
bunny._t = i * 0.7 // phase offset so they don’t match
|
||||
bunny._amp = 240 // bounce height in pixels
|
||||
bunny._speed = 1 * random() // bounce speed
|
||||
sprites[] = bunny
|
||||
bunnies[] = bunny
|
||||
}
|
||||
|
||||
|
||||
log.console("World initialized - " + text(length(film2d.query({}))) + " drawables")
|
||||
}
|
||||
var json = use('json')
|
||||
var last_time = 0
|
||||
|
||||
function update(dt) {
|
||||
// Update all particle emitters
|
||||
var i = 0
|
||||
for (i = 0; i < length(emitters); i++)
|
||||
particles2d.emitters.update(emitters[i], dt)
|
||||
|
||||
// Update bouncing bunnies
|
||||
// Update bouncing bunnies (vertical bob only)
|
||||
var b = null
|
||||
var cycle = null
|
||||
var u = null
|
||||
for (i = 0; i < length(bunnies); i++) {
|
||||
b = bunnies[i]
|
||||
b._t += dt
|
||||
|
||||
// Bob between base_y and base_y - amp using triangle wave
|
||||
cycle = (b._t * b._speed) % 2
|
||||
u = cycle < 1 ? cycle : 2 - cycle
|
||||
b.pos.y = b._base_y - u * b._amp
|
||||
|
||||
// Optional: if your Y-sort uses an explicit key, keep it in sync
|
||||
// b.y_sort_key = b.pos.y
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function render() {
|
||||
// 1. Build Clay UI
|
||||
var clay_tree = clay.layout(function() {
|
||||
clay.vstack({gap: 10, padding: 20, align: 'center'}, function() {
|
||||
clay.text("Clay UI Sample", {font_size: 24, color: {r:1,g:1,b:0}})
|
||||
clay.image('examples/tiles/key_blue', {size:{width:32,height:32}, color: {r:1,g:0,b:0}})
|
||||
clay.container({size: [200, 100], background_color: {r:0.2, g:0.2, b:0.2, a:0.8}, padding: 10}, function() {
|
||||
clay.vstack(function() {
|
||||
clay.text("List Item 1")
|
||||
clay.text("List Item 2")
|
||||
clay.text("List Item 3")
|
||||
})
|
||||
})
|
||||
})
|
||||
}, {width: 640, height: 360})
|
||||
|
||||
// Assign clay drawables to UI plane
|
||||
compositor_config.planes[2].drawables = clay_tree.drawables
|
||||
compositor_config.planes[2].clear = {r: 0, g: 0, b: 0, a: 0}
|
||||
|
||||
var plan = compositor.compile(compositor_config)
|
||||
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init_game()
|
||||
|
||||
core.start({
|
||||
width: 1280,
|
||||
height: 720,
|
||||
title: "Paladin - Data Oriented",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render,
|
||||
editor: function(ui) {
|
||||
}
|
||||
})
|
||||
99
examples/paladin_simple.ce
Normal file
@@ -0,0 +1,99 @@
|
||||
// paladin_simple.ce - Simple test for prosperon rendering
|
||||
|
||||
log.console("paladin simple test starting")
|
||||
|
||||
var sprite = use('sprite')
|
||||
var clay = use('clay')
|
||||
var core = use('core')
|
||||
var fx_graph = use('fx_graph')
|
||||
var sdl_gpu = use('sdl_gpu')
|
||||
|
||||
var fcfg = {font_path: 'examples/fonts/dos'}
|
||||
|
||||
core.start({
|
||||
width: 640,
|
||||
height: 480,
|
||||
title: "Paladin Simple",
|
||||
framerate: 60,
|
||||
update: function(dt) {},
|
||||
render: build_render_graph,
|
||||
input: function(ev) {
|
||||
if (ev.type == 'quit') $stop()
|
||||
}
|
||||
})
|
||||
|
||||
function build_render_graph() {
|
||||
var win_size = core.window_size()
|
||||
var graph = fx_graph.create()
|
||||
|
||||
// Simple sprite scene
|
||||
var scene = {
|
||||
type: 'group',
|
||||
children: [{
|
||||
type: 'sprite',
|
||||
image: 'examples/tiles/fireball',
|
||||
pos: {x: win_size.width / 2, y: win_size.height / 2},
|
||||
width: win_size.height / 2, // Half screen height
|
||||
height: win_size.height / 2,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
}]
|
||||
}
|
||||
|
||||
var camera = {
|
||||
pos: [0, 0],
|
||||
width: win_size.width,
|
||||
height: win_size.height,
|
||||
anchor: [0, 0],
|
||||
ortho: true,
|
||||
background: {r: 0.2, g: 0.2, b: 0.3, a: 1}
|
||||
}
|
||||
|
||||
// Render to screen
|
||||
|
||||
var ui_scene = clay.layout(function() {
|
||||
clay.container({padding: 20, contain: clay.contain.content}, function() {
|
||||
// Top-left HUD panel
|
||||
clay.vstack({
|
||||
background_image: 'examples/tiles/brick_brown',
|
||||
slice: 0.33,
|
||||
padding: 20,
|
||||
spacing: 10,
|
||||
width: 200
|
||||
}, function() {
|
||||
clay.text("PALADIN UI", [{font_size: 24, color: {r:1, g:0.8, b:0.2, a:1}}, fcfg])
|
||||
|
||||
clay.hstack({spacing: 10}, function() {
|
||||
clay.image('examples/tiles/hud_heart', {width: 24, height: 24})
|
||||
clay.text("x 3", [{font_size: 20}, fcfg])
|
||||
})
|
||||
|
||||
clay.button("PAUSE", function() { log.console("Pause clicked") }, fcfg)
|
||||
|
||||
// Scissored scroll area test
|
||||
clay.container({
|
||||
width: 160, height: 60,
|
||||
background_color: {r:0, g:0, b:0, a:0.5},
|
||||
clipped: true
|
||||
}, function() {
|
||||
clay.vstack({offset: {x:0, y: -10}}, function() {
|
||||
clay.text("Item 1", fcfg)
|
||||
clay.text("Item 2", fcfg)
|
||||
clay.text("Item 3", fcfg)
|
||||
clay.text("Item 4", fcfg)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}, {width: win_size.width, height: win_size.height})
|
||||
|
||||
graph.add_node('render_view', {
|
||||
root: ui_scene,
|
||||
camera: {pos: [0, 0], width: win_size.width, height: win_size.height, anchor: [0, 0], ortho: true},
|
||||
target: 'screen',
|
||||
clear_color: {r:0,g:0,b:0,a:0} // Cleared transparent
|
||||
})
|
||||
|
||||
return graph
|
||||
}
|
||||
BIN
examples/rope.jpg
Normal file
|
After Width: | Height: | Size: 256 KiB |
@@ -91,12 +91,12 @@ function reset_game() {
|
||||
var cy = floor(gridH / 2)
|
||||
var wp = null
|
||||
for (i = 0; i < 3; i++) {
|
||||
push(snake_pos, {x: cx - i, y: cy})
|
||||
snake_pos[] = {x: cx - i, y: cy}
|
||||
wp = grid_to_world(cx - i, cy)
|
||||
push(snake_shapes, shape.rect({
|
||||
snake_shapes[] = shape.rect({
|
||||
pos: {x: wp.x, y: wp.y}, width: cellSize - 2, height: cellSize - 2,
|
||||
fill: {r: 0, g: 1, b: 0.3, a: 1}, plane: 'game'
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
dir = {x: 1, y: 0}
|
||||
@@ -166,7 +166,7 @@ core.start({
|
||||
var old_wp = grid_to_world(snake_pos[0].x, snake_pos[0].y)
|
||||
var new_wp = grid_to_world(hx, hy)
|
||||
var new_pos = [{x: hx, y: hy}]
|
||||
for (i = 0; i < length(snake_pos); i++) push(new_pos, snake_pos[i])
|
||||
for (i = 0; i < length(snake_pos); i++) new_pos[] = snake_pos[i]
|
||||
snake_pos = new_pos
|
||||
var head = shape.rect({
|
||||
pos: {x: old_wp.x, y: old_wp.y}, width: cellSize - 2, height: cellSize - 2,
|
||||
@@ -175,7 +175,7 @@ core.start({
|
||||
// Smooth tween from old position to new position
|
||||
tw.tween(head.pos).to({x: new_wp.x, y: new_wp.y}, move_interval).ease(ease.linear)
|
||||
var new_shapes = [head]
|
||||
for (i = 0; i < length(snake_shapes); i++) push(new_shapes, snake_shapes[i])
|
||||
for (i = 0; i < length(snake_shapes); i++) new_shapes[] = snake_shapes[i]
|
||||
snake_shapes = new_shapes
|
||||
|
||||
// Eat apple?
|
||||
@@ -185,9 +185,9 @@ core.start({
|
||||
spawn_apple()
|
||||
} else {
|
||||
// Remove tail
|
||||
tail = pop(snake_shapes)
|
||||
tail = snake_shapes[]
|
||||
tail.destroy()
|
||||
pop(snake_pos)
|
||||
snake_pos[]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
315
examples/test_line2d.ce
Normal file
@@ -0,0 +1,315 @@
|
||||
log.console("test_line2d starting")
|
||||
|
||||
var time = use('time')
|
||||
|
||||
var core = use('core')
|
||||
var line2d = use('line2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var lines = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 400, y: 300},
|
||||
width: 800,
|
||||
height: 600,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 800, height: 600},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Row 1: Basic lines with different widths
|
||||
|
||||
// Thin line
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 50, y: 550}, {x: 150, y: 520}, {x: 200, y: 560}],
|
||||
width: 4,
|
||||
tint: {r: 1, g: 0.3, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
// Medium line
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 250, y: 550}, {x: 350, y: 520}, {x: 400, y: 560}],
|
||||
width: 12,
|
||||
tint: {r: 0.3, g: 1, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
// Thick line
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 450, y: 550}, {x: 550, y: 520}, {x: 600, y: 560}],
|
||||
width: 24,
|
||||
tint: {r: 0.3, g: 0.3, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Variable width line
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 650, y: 560}, {x: 700, y: 520}, {x: 750, y: 560}],
|
||||
widths: [8, 24, 8],
|
||||
tint: {r: 1, g: 1, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
// Row 2: Textured ropes with different UV modes
|
||||
|
||||
// Rope with repeat UV (default for ropes)
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 50, y: 450}, {x: 120, y: 420}, {x: 200, y: 440}, {x: 280, y: 400}],
|
||||
width: 20,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'repeat',
|
||||
u_per_unit: 1 / 32
|
||||
}
|
||||
})
|
||||
|
||||
// Rope with stretch UV
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 320, y: 450}, {x: 400, y: 420}, {x: 480, y: 440}, {x: 560, y: 400}],
|
||||
width: 20,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'stretch'
|
||||
}
|
||||
})
|
||||
|
||||
// Rope with per-segment UV
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 600, y: 450}, {x: 660, y: 420}, {x: 720, y: 440}, {x: 780, y: 400}],
|
||||
width: 20,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'per_segment'
|
||||
}
|
||||
})
|
||||
|
||||
// Row 3: Cap styles
|
||||
|
||||
// Butt cap (default)
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 80, y: 320}, {x: 180, y: 320}],
|
||||
width: 20,
|
||||
cap: 'butt',
|
||||
tint: {r: 0.8, g: 0.5, b: 0.2, a: 1}
|
||||
})
|
||||
|
||||
// Square cap
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 250, y: 320}, {x: 350, y: 320}],
|
||||
width: 20,
|
||||
cap: 'square',
|
||||
tint: {r: 0.5, g: 0.8, b: 0.2, a: 1}
|
||||
})
|
||||
|
||||
// Round cap
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 420, y: 320}, {x: 520, y: 320}],
|
||||
width: 20,
|
||||
cap: 'round',
|
||||
tint: {r: 0.2, g: 0.5, b: 0.8, a: 1}
|
||||
})
|
||||
|
||||
// Round cap with texture
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 590, y: 320}, {x: 750, y: 320}],
|
||||
width: 24,
|
||||
cap: 'round',
|
||||
image: 'examples/rope'
|
||||
})
|
||||
|
||||
// Row 4: Complex paths
|
||||
|
||||
// Wavy line (will be animated)
|
||||
var wave_points = []
|
||||
var i = 0
|
||||
for (i = 0; i < 12; i++) {
|
||||
wave_points[] = {
|
||||
x: 50 + i * 30,
|
||||
y: 220 + math.sine(i * 0.8) * 30
|
||||
}
|
||||
}
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: wave_points,
|
||||
width: 14,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'repeat',
|
||||
u_per_unit: 1 / 24
|
||||
}
|
||||
})
|
||||
|
||||
// Spiral/coil
|
||||
var spiral_points = []
|
||||
var angle = null
|
||||
var radius = null
|
||||
for (i = 0; i < 20; i++) {
|
||||
angle = i * 0.5
|
||||
radius = 20 + i * 3
|
||||
spiral_points[] = {
|
||||
x: 550 + math.cosine(angle) * radius,
|
||||
y: 200 + math.sine(angle) * radius
|
||||
}
|
||||
}
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: spiral_points,
|
||||
width: 10,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'repeat',
|
||||
u_per_unit: 1 / 20
|
||||
}
|
||||
})
|
||||
|
||||
// Row 5: Closed shapes and special cases
|
||||
|
||||
// Closed triangle
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 80, y: 80}, {x: 140, y: 140}, {x: 20, y: 140}],
|
||||
width: 8,
|
||||
closed: true,
|
||||
tint: {r: 1, g: 0.5, b: 0.8, a: 1}
|
||||
})
|
||||
|
||||
// Closed pentagon
|
||||
var pent_points = []
|
||||
for (i = 0; i < 5; i++) {
|
||||
angle = i * (2 * 3.14159 / 5) - 3.14159 / 2
|
||||
pent_points[] = {
|
||||
x: 220 + math.cosine(angle) * 50,
|
||||
y: 110 + math.sine(angle) * 50
|
||||
}
|
||||
}
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: pent_points,
|
||||
width: 10,
|
||||
closed: true,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'repeat',
|
||||
u_per_unit: 1 / 30
|
||||
}
|
||||
})
|
||||
|
||||
// Animated rope (will swing)
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 1,
|
||||
points: [{x: 400, y: 50}, {x: 400, y: 100}, {x: 400, y: 150}],
|
||||
width: 16,
|
||||
image: 'examples/rope',
|
||||
uv: {
|
||||
mode: 'repeat',
|
||||
u_per_unit: 1 / 20
|
||||
}
|
||||
})
|
||||
|
||||
// Fuse/wire with opacity gradient (simulated via tint)
|
||||
lines[] = line2d.polyline({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
points: [{x: 500, y: 80}, {x: 580, y: 60}, {x: 660, y: 100}, {x: 740, y: 70}],
|
||||
width: 8,
|
||||
image: 'examples/rope',
|
||||
opacity: 0.7,
|
||||
tint: {r: 1, g: 0.8, b: 0.4, a: 1}
|
||||
})
|
||||
|
||||
log.console("test_line2d initialized with " + text(length(lines)) + " lines")
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Animate the wavy line (index 11)
|
||||
var pts = null
|
||||
var i = 0
|
||||
if (lines[11]) {
|
||||
pts = lines[11].points
|
||||
for (i = 0; i < length(pts); i++) {
|
||||
pts[i].y = 220 + math.sine(i * 0.8 + t * 2) * 30
|
||||
}
|
||||
lines[11].set_points(pts)
|
||||
}
|
||||
|
||||
// Animate the hanging rope (index 15) - swing like a pendulum
|
||||
var swing = null
|
||||
if (lines[15]) {
|
||||
swing = math.sine(t * 1.5) * 40
|
||||
pts = [
|
||||
{x: 400, y: 50},
|
||||
{x: 400 + swing * 0.5, y: 100},
|
||||
{x: 400 + swing, y: 150}
|
||||
]
|
||||
lines[15].set_points(pts)
|
||||
}
|
||||
|
||||
// Animate UV offset on the spiral (index 12) for scrolling texture effect
|
||||
if (lines[12]) {
|
||||
lines[12].uv.u_offset = t * 0.5
|
||||
lines[12]._rebuild()
|
||||
}
|
||||
|
||||
// Pulse opacity on the fuse (index 16)
|
||||
if (lines[16]) {
|
||||
lines[16].opacity = 0.5 + 0.5 * math.sine(t * 3)
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 800,
|
||||
height: 600,
|
||||
title: "Test Line2D - Ropes and Lines",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
301
examples/test_particles.ce
Normal file
@@ -0,0 +1,301 @@
|
||||
log.console("test_particles starting")
|
||||
|
||||
var time = use('time')
|
||||
var random = use('random').random
|
||||
|
||||
var core = use('core')
|
||||
var particles2d = use('particles2d')
|
||||
var text2d = use('text2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var particle_handles = []
|
||||
var emitters = []
|
||||
var labels = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 250, y: 250},
|
||||
width: 500,
|
||||
height: 500,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.05, g: 0.05, b: 0.1, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 500, height: 500},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.05, g: 0.05, b: 0.1, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Emitter 1: Normal particles (top-left)
|
||||
var handle1 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 20,
|
||||
height: 20
|
||||
})
|
||||
particle_handles[] = handle1
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 100, y: 400},
|
||||
spawn_area: {width: 30, height: 10},
|
||||
velocity: {x: 0, y: 50},
|
||||
velocity_var: {x: 20, y: 10},
|
||||
life: 2,
|
||||
rate: 15,
|
||||
scale: 1,
|
||||
scale_var: 0.2,
|
||||
color: {r: 1, g: 0.8, b: 0.3},
|
||||
handle: handle1
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Normal",
|
||||
pos: {x: 70, y: 480},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 2: Half opacity (top-center)
|
||||
var handle2 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 20,
|
||||
height: 20,
|
||||
opacity: 0.5
|
||||
})
|
||||
particle_handles[] = handle2
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 250, y: 400},
|
||||
spawn_area: {width: 30, height: 10},
|
||||
velocity: {x: 0, y: 50},
|
||||
velocity_var: {x: 20, y: 10},
|
||||
life: 2,
|
||||
rate: 15,
|
||||
scale: 1,
|
||||
scale_var: 0.2,
|
||||
color: {r: 1, g: 0.8, b: 0.3},
|
||||
handle: handle2
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Opacity 0.5",
|
||||
pos: {x: 210, y: 480},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 3: Red tint (top-right)
|
||||
var handle3 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 20,
|
||||
height: 20,
|
||||
tint: {r: 1, g: 0.2, b: 0.2, a: 1}
|
||||
})
|
||||
particle_handles[] = handle3
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 400, y: 400},
|
||||
spawn_area: {width: 30, height: 10},
|
||||
velocity: {x: 0, y: 50},
|
||||
velocity_var: {x: 20, y: 10},
|
||||
life: 2,
|
||||
rate: 15,
|
||||
scale: 1,
|
||||
scale_var: 0.2,
|
||||
color: {r: 1, g: 0.8, b: 0.3},
|
||||
handle: handle3
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Red Tint",
|
||||
pos: {x: 370, y: 480},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 4: Blue tint (bottom-left)
|
||||
var handle4 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 24,
|
||||
height: 24,
|
||||
tint: {r: 0.3, g: 0.5, b: 1, a: 1}
|
||||
})
|
||||
particle_handles[] = handle4
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 100, y: 200},
|
||||
spawn_area: {width: 40, height: 10},
|
||||
velocity: {x: 0, y: 60},
|
||||
velocity_var: {x: 30, y: 15},
|
||||
life: 2.5,
|
||||
rate: 20,
|
||||
scale: 1.2,
|
||||
scale_var: 0.3,
|
||||
color: {r: 1, g: 1, b: 1},
|
||||
handle: handle4
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Blue Tint",
|
||||
pos: {x: 70, y: 280},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 5: Green tint + opacity (bottom-center)
|
||||
var handle5 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 24,
|
||||
height: 24,
|
||||
tint: {r: 0.3, g: 1, b: 0.3, a: 1},
|
||||
opacity: 0.7
|
||||
})
|
||||
particle_handles[] = handle5
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 250, y: 200},
|
||||
spawn_area: {width: 40, height: 10},
|
||||
velocity: {x: 0, y: 60},
|
||||
velocity_var: {x: 30, y: 15},
|
||||
life: 2.5,
|
||||
rate: 20,
|
||||
scale: 1.2,
|
||||
scale_var: 0.3,
|
||||
color: {r: 1, g: 1, b: 1},
|
||||
handle: handle5
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Green + 70%",
|
||||
pos: {x: 210, y: 280},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 6: Animated tint (bottom-right)
|
||||
var handle6 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 24,
|
||||
height: 24,
|
||||
tint: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
particle_handles[] = handle6
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 400, y: 200},
|
||||
spawn_area: {width: 40, height: 10},
|
||||
velocity: {x: 0, y: 60},
|
||||
velocity_var: {x: 30, y: 15},
|
||||
life: 2.5,
|
||||
rate: 20,
|
||||
scale: 1.2,
|
||||
scale_var: 0.3,
|
||||
color: {r: 1, g: 1, b: 1},
|
||||
handle: handle6
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Rainbow",
|
||||
pos: {x: 370, y: 280},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Emitter 7: Animated opacity (center)
|
||||
var handle7 = particles2d.create({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/tiles/fireball',
|
||||
width: 32,
|
||||
height: 32,
|
||||
opacity: 1
|
||||
})
|
||||
particle_handles[] = handle7
|
||||
emitters[] = particles2d.emitters.create({
|
||||
pos: {x: 250, y: 80},
|
||||
spawn_area: {width: 60, height: 20},
|
||||
velocity: {x: 0, y: 40},
|
||||
velocity_var: {x: 40, y: 20},
|
||||
life: 3,
|
||||
rate: 25,
|
||||
scale: 1.5,
|
||||
scale_var: 0.4,
|
||||
color: {r: 1, g: 0.6, b: 0.2},
|
||||
handle: handle7
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Pulsing Opacity",
|
||||
pos: {x: 180, y: 20},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 0, a: 1}
|
||||
})
|
||||
|
||||
log.console("test_particles initialized with " + text(length(emitters)) + " emitters")
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Update all emitters
|
||||
var i = 0
|
||||
for (i = 0; i < length(emitters); i++) {
|
||||
particles2d.emitters.update(emitters[i], dt)
|
||||
}
|
||||
|
||||
// Animate rainbow tint on emitter 6
|
||||
var rainbow_handle = particle_handles[5]
|
||||
rainbow_handle.tint.r = 0.5 + 0.5 * math.sine(t * 2)
|
||||
rainbow_handle.tint.g = 0.5 + 0.5 * math.sine(t * 2 + 2.094)
|
||||
rainbow_handle.tint.b = 0.5 + 0.5 * math.sine(t * 2 + 4.188)
|
||||
|
||||
// Animate pulsing opacity on emitter 7
|
||||
var pulsing_handle = particle_handles[6]
|
||||
pulsing_handle.opacity = 0.3 + 0.7 * abs(math.sine(t * 1.5))
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 500,
|
||||
height: 500,
|
||||
title: "Test Particles Features",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
352
examples/test_shape2d.ce
Normal file
@@ -0,0 +1,352 @@
|
||||
log.console("test_shape2d starting")
|
||||
|
||||
var time = use('time')
|
||||
|
||||
var core = use('core')
|
||||
var shape2d = use('shape2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var shapes = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 400, y: 300},
|
||||
width: 800,
|
||||
height: 600,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 800, height: 600},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Row 1: Basic shapes - rect, circle, ellipse, pill
|
||||
|
||||
// Rectangle with rounded corners
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 100, y: 500},
|
||||
width: 120,
|
||||
height: 80,
|
||||
radius: 10,
|
||||
fill: {r: 0.2, g: 0.6, b: 0.9, a: 1}
|
||||
})
|
||||
|
||||
// Circle
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 250, y: 500},
|
||||
radius: 50,
|
||||
fill: {r: 0.9, g: 0.3, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
// Ellipse
|
||||
shapes[] = shape2d.ellipse({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 400, y: 500},
|
||||
width: 140,
|
||||
height: 80,
|
||||
fill: {r: 0.3, g: 0.9, b: 0.4, a: 1}
|
||||
})
|
||||
|
||||
// Pill (stadium)
|
||||
shapes[] = shape2d.pill({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 550, y: 500},
|
||||
width: 140,
|
||||
height: 60,
|
||||
fill: {r: 0.9, g: 0.7, b: 0.2, a: 1}
|
||||
})
|
||||
|
||||
// Row 2: Stroke variations
|
||||
|
||||
// Rect with stroke only
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 100, y: 380},
|
||||
width: 100,
|
||||
height: 80,
|
||||
radius: 5,
|
||||
fill: {r: 0, g: 0, b: 0, a: 0},
|
||||
stroke: {r: 1, g: 1, b: 1, a: 1},
|
||||
stroke_thickness: 3,
|
||||
stroke_align: 'center',
|
||||
dash_len: 14,
|
||||
gap_len: 6
|
||||
})
|
||||
|
||||
// Circle with thick stroke
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 250, y: 380},
|
||||
radius: 45,
|
||||
fill: {r: 0.2, g: 0.2, b: 0.3, a: 1},
|
||||
stroke: {r: 0.5, g: 0.8, b: 1, a: 1},
|
||||
stroke_thickness: 6,
|
||||
stroke_align: 'outside',
|
||||
dash_len: 14,
|
||||
gap_len: 6
|
||||
})
|
||||
|
||||
// Ellipse with inside stroke
|
||||
shapes[] = shape2d.ellipse({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 400, y: 380},
|
||||
width: 120,
|
||||
height: 70,
|
||||
fill: {r: 0.4, g: 0.2, b: 0.5, a: 1},
|
||||
stroke: {r: 1, g: 0.5, b: 0.8, a: 1},
|
||||
stroke_thickness: 4,
|
||||
stroke_align: 'inside',
|
||||
dash_len: 14,
|
||||
gap_len: 6
|
||||
})
|
||||
|
||||
// Pill with dashed stroke
|
||||
shapes[] = shape2d.pill({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 550, y: 380},
|
||||
width: 130,
|
||||
height: 50,
|
||||
fill: {r: 0.1, g: 0.3, b: 0.2, a: 1},
|
||||
stroke: {r: 0.3, g: 1, b: 0.5, a: 1},
|
||||
stroke_thickness: 3,
|
||||
dash_len: 15,
|
||||
gap_len: 8
|
||||
})
|
||||
|
||||
// Row 3: Feather / soft edges
|
||||
|
||||
// Soft rect
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 100, y: 260},
|
||||
width: 100,
|
||||
height: 80,
|
||||
radius: 20,
|
||||
fill: {r: 0.8, g: 0.4, b: 0.1, a: 1},
|
||||
feather: 10
|
||||
})
|
||||
|
||||
// Soft circle (glow effect)
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 250, y: 260},
|
||||
radius: 40,
|
||||
fill: {r: 1, g: 0.9, b: 0.3, a: 1},
|
||||
feather: 20
|
||||
})
|
||||
|
||||
// Soft ellipse
|
||||
shapes[] = shape2d.ellipse({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 400, y: 260},
|
||||
width: 120,
|
||||
height: 60,
|
||||
fill: {r: 0.3, g: 0.5, b: 1, a: 0.8},
|
||||
feather: 15
|
||||
})
|
||||
|
||||
// Sharp vs soft comparison
|
||||
shapes[] = shape2d.pill({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 550, y: 260},
|
||||
width: 120,
|
||||
height: 50,
|
||||
fill: {r: 1, g: 0.2, b: 0.6, a: 1},
|
||||
feather: 5
|
||||
})
|
||||
|
||||
// Row 4: Opacity and blending
|
||||
|
||||
// Semi-transparent overlapping shapes
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 80, y: 130},
|
||||
width: 100,
|
||||
height: 80,
|
||||
fill: {r: 1, g: 0, b: 0, a: 0.6},
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 1,
|
||||
pos: {x: 120, y: 130},
|
||||
width: 100,
|
||||
height: 80,
|
||||
fill: {r: 0, g: 1, b: 0, a: 0.6},
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 2,
|
||||
pos: {x: 100, y: 150},
|
||||
width: 100,
|
||||
height: 80,
|
||||
fill: {r: 0, g: 0, b: 1, a: 0.6},
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
// Animated opacity circle
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 280, y: 130},
|
||||
radius: 50,
|
||||
fill: {r: 1, g: 1, b: 1, a: 1},
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
// Different corner radii
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 420, y: 130},
|
||||
width: 100,
|
||||
height: 80,
|
||||
radius: 0,
|
||||
fill: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
})
|
||||
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 540, y: 130},
|
||||
width: 100,
|
||||
height: 80,
|
||||
radius: 20,
|
||||
fill: {r: 0.6, g: 0.6, b: 0.6, a: 1}
|
||||
})
|
||||
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 660, y: 130},
|
||||
width: 100,
|
||||
height: 80,
|
||||
radius: 40,
|
||||
fill: {r: 0.7, g: 0.7, b: 0.7, a: 1}
|
||||
})
|
||||
|
||||
// Row 5: Large decorative shapes
|
||||
|
||||
// Background panel
|
||||
shapes[] = shape2d.rect({
|
||||
plane: 'main',
|
||||
layer: -1,
|
||||
pos: {x: 700, y: 400},
|
||||
width: 160,
|
||||
height: 350,
|
||||
radius: 15,
|
||||
fill: {r: 0.15, g: 0.15, b: 0.2, a: 1},
|
||||
stroke: {r: 0.3, g: 0.3, b: 0.4, a: 1},
|
||||
stroke_thickness: 2
|
||||
})
|
||||
|
||||
// Decorative circles inside panel
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 700, y: 500},
|
||||
radius: 30,
|
||||
fill: {r: 0.8, g: 0.2, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 700, y: 420},
|
||||
radius: 25,
|
||||
fill: {r: 0.2, g: 0.8, b: 0.3, a: 1}
|
||||
})
|
||||
|
||||
shapes[] = shape2d.circle({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
pos: {x: 700, y: 350},
|
||||
radius: 20,
|
||||
fill: {r: 0.2, g: 0.3, b: 0.8, a: 1}
|
||||
})
|
||||
|
||||
log.console("test_shape2d initialized with " + text(length(shapes)) + " shapes")
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Animate opacity on the white circle (index 16)
|
||||
if (shapes[16]) {
|
||||
shapes[16].opacity = 0.3 + 0.7 * (0.5 + 0.5 * math.sine(t * 2))
|
||||
}
|
||||
|
||||
// Animate the glow circle size (index 10)
|
||||
var scale = null
|
||||
if (shapes[10]) {
|
||||
scale = 1 + 0.2 * math.sine(t * 3)
|
||||
shapes[10].width = 80 * scale
|
||||
shapes[10].height = 80 * scale
|
||||
}
|
||||
|
||||
// Rotate the dashed pill stroke (index 7)
|
||||
if (shapes[7]) {
|
||||
shapes[4].dash_offset = t * 30
|
||||
shapes[5].dash_offset = t * 30
|
||||
shapes[6].dash_offset = t * 30
|
||||
shapes[7].dash_offset = t * 30
|
||||
}
|
||||
|
||||
// Pulse the decorative circles
|
||||
if (shapes[21]) {
|
||||
shapes[21].fill.r = 0.5 + 0.5 * math.sine(t * 1.5)
|
||||
}
|
||||
if (shapes[22]) {
|
||||
shapes[22].fill.g = 0.5 + 0.5 * math.sine(t * 1.5 + 2.094)
|
||||
}
|
||||
if (shapes[23]) {
|
||||
shapes[23].fill.b = 0.5 + 0.5 * math.sine(t * 1.5 + 4.188)
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 800,
|
||||
height: 600,
|
||||
title: "Test Shape2D - SDF Shapes",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
206
examples/test_sprite.ce
Normal file
@@ -0,0 +1,206 @@
|
||||
log.console("test_sprite starting")
|
||||
|
||||
var time = use('time')
|
||||
|
||||
var core = use('core')
|
||||
var sprite = use('sprite')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var sprites = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 250, y: 250},
|
||||
width: 500,
|
||||
height: 500,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 500, height: 500},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Row 1: Opacity test - 0, 0.25, 0.5, 0.75, 1.0
|
||||
var i = 0
|
||||
for (i = 0; i < 5; i++) {
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 50 + i * 100, y: 450},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
opacity: i * 0.25
|
||||
})
|
||||
}
|
||||
|
||||
// Row 2: Tint test - red, green, blue, yellow, magenta
|
||||
var tints = [
|
||||
{r: 1, g: 0, b: 0, a: 1},
|
||||
{r: 0, g: 1, b: 0, a: 1},
|
||||
{r: 0, g: 0, b: 1, a: 1},
|
||||
{r: 1, g: 1, b: 0, a: 1},
|
||||
{r: 1, g: 0, b: 1, a: 1}
|
||||
]
|
||||
for (i = 0; i < 5; i++) {
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 50 + i * 100, y: 350},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
tint: tints[i]
|
||||
})
|
||||
}
|
||||
|
||||
// Row 3: Filter test - nearest (small scaled up) vs linear (small scaled up)
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 125, y: 250},
|
||||
width: 150,
|
||||
height: 150,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
filter: 'nearest'
|
||||
})
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 375, y: 250},
|
||||
width: 150,
|
||||
height: 150,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
filter: 'linear'
|
||||
})
|
||||
|
||||
// Row 4: Flip test - normal, flip_x, flip_y, flip_both
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 75, y: 125},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
flip: {x: false, y: false}
|
||||
})
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 175, y: 125},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
flip: {x: true, y: false}
|
||||
})
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 275, y: 125},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
flip: {x: false, y: true}
|
||||
})
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 375, y: 125},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
flip: {x: true, y: true}
|
||||
})
|
||||
|
||||
// Row 5: UV scroll test - animated texture offset
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 450, y: 125},
|
||||
width: 64,
|
||||
height: 64,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
uv: {offset: {x: 0, y: 0}, scale: {x: 1, y: 1}, rotate: 0}
|
||||
})
|
||||
|
||||
// Animated opacity sprite
|
||||
sprites[] = sprite({
|
||||
plane: 'main',
|
||||
layer: 1,
|
||||
image: 'examples/bunny',
|
||||
pos: {x: 250, y: 50},
|
||||
width: 80,
|
||||
height: 80,
|
||||
anchor_x: 0.5,
|
||||
anchor_y: 0.5,
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
log.console("test_sprite initialized with " + text(length(sprites)) + " sprites")
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Animate opacity on the last sprite (pulsing)
|
||||
var animated_opacity_sprite = sprites[length(sprites) - 1]
|
||||
animated_opacity_sprite.opacity = 0.5 + 0.5 * math.sine(t * 2)
|
||||
|
||||
// Animate UV scroll on the UV test sprite
|
||||
var uv_sprite = sprites[length(sprites) - 2]
|
||||
uv_sprite.uv.offset.x = (t * 0.5) % 1
|
||||
uv_sprite.uv.offset.y = (t * 0.3) % 1
|
||||
|
||||
// Animate tint on first tint sprite (cycling hue)
|
||||
var tint_sprite = sprites[5]
|
||||
tint_sprite.tint.r = 0.5 + 0.5 * math.sine(t * 1.5)
|
||||
tint_sprite.tint.g = 0.5 + 0.5 * math.sine(t * 1.5 + 2.094)
|
||||
tint_sprite.tint.b = 0.5 + 0.5 * math.sine(t * 1.5 + 4.188)
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 500,
|
||||
height: 500,
|
||||
title: "Test Sprite Features",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
256
examples/test_text.ce
Normal file
@@ -0,0 +1,256 @@
|
||||
log.console("test_text starting")
|
||||
|
||||
var time = use('time')
|
||||
|
||||
var core = use('core')
|
||||
var text2d = use('text2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var texts = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 250, y: 250},
|
||||
width: 500,
|
||||
height: 500,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 500, height: 500},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.1, g: 0.1, b: 0.15, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Row 1: Normal text
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Normal Text",
|
||||
pos: {x: 20, y: 470},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 24,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Row 2: Opacity variations
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Opacity 0.25",
|
||||
pos: {x: 20, y: 420},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
opacity: 0.25
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Opacity 0.5",
|
||||
pos: {x: 200, y: 420},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
opacity: 0.5
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Opacity 0.75",
|
||||
pos: {x: 360, y: 420},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
opacity: 0.75
|
||||
})
|
||||
|
||||
// Row 3: Tint colors
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Red Tint",
|
||||
pos: {x: 20, y: 360},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 24,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 1, g: 0.2, b: 0.2, a: 1}
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Green Tint",
|
||||
pos: {x: 180, y: 360},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 24,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 0.2, g: 1, b: 0.2, a: 1}
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Blue Tint",
|
||||
pos: {x: 360, y: 360},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 24,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 0.2, g: 0.2, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Row 4: Combined tint + opacity
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Yellow + 50% Opacity",
|
||||
pos: {x: 20, y: 300},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 1, g: 1, b: 0, a: 1},
|
||||
opacity: 0.5
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Cyan + 75% Opacity",
|
||||
pos: {x: 280, y: 300},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 0, g: 1, b: 1, a: 1},
|
||||
opacity: 0.75
|
||||
})
|
||||
|
||||
// Row 5: Different sizes
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Small",
|
||||
pos: {x: 20, y: 240},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 12,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Medium",
|
||||
pos: {x: 100, y: 240},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Large",
|
||||
pos: {x: 220, y: 240},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 32,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "XL",
|
||||
pos: {x: 360, y: 240},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 48,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Row 6: Animated text (pulsing opacity)
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Pulsing Opacity",
|
||||
pos: {x: 150, y: 150},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 28,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
opacity: 1
|
||||
})
|
||||
|
||||
// Row 7: Animated tint (rainbow cycle)
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "Rainbow Tint",
|
||||
pos: {x: 150, y: 100},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 28,
|
||||
color: {r: 1, g: 1, b: 1, a: 1},
|
||||
tint: {r: 1, g: 0, b: 0, a: 1}
|
||||
})
|
||||
|
||||
// Row 8: Typewriter effect simulation
|
||||
texts[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
text: "",
|
||||
pos: {x: 20, y: 50},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 20,
|
||||
color: {r: 0.8, g: 1, b: 0.8, a: 1}
|
||||
})
|
||||
|
||||
log.console("test_text initialized with " + text(length(texts)) + " text elements")
|
||||
}
|
||||
|
||||
var typewriter_text = "Hello, this is a typewriter effect demo..."
|
||||
var typewriter_index = 0
|
||||
var typewriter_timer = 0
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Animate pulsing opacity
|
||||
var pulsing_text = texts[length(texts) - 3]
|
||||
pulsing_text.opacity = 0.3 + 0.7 * abs(math.sine(t * 2))
|
||||
|
||||
// Animate rainbow tint
|
||||
var rainbow_text = texts[length(texts) - 2]
|
||||
rainbow_text.tint.r = 0.5 + 0.5 * math.sine(t * 2)
|
||||
rainbow_text.tint.g = 0.5 + 0.5 * math.sine(t * 2 + 2.094)
|
||||
rainbow_text.tint.b = 0.5 + 0.5 * math.sine(t * 2 + 4.188)
|
||||
|
||||
// Typewriter effect
|
||||
typewriter_timer += dt
|
||||
var typewriter = null
|
||||
if (typewriter_timer > 0.08) {
|
||||
typewriter_timer = 0
|
||||
typewriter_index++
|
||||
if (typewriter_index > length(typewriter_text)) {
|
||||
typewriter_index = 0
|
||||
}
|
||||
typewriter = texts[length(texts) - 1]
|
||||
typewriter.text = text(typewriter_text, 0, typewriter_index)
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 500,
|
||||
height: 500,
|
||||
title: "Test Text Features",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
187
examples/test_tilemap.ce
Normal file
@@ -0,0 +1,187 @@
|
||||
log.console("test_tilemap starting")
|
||||
|
||||
var time = use('time')
|
||||
|
||||
var core = use('core')
|
||||
var tilemap2d = use('tilemap2d')
|
||||
var text2d = use('text2d')
|
||||
var compositor = use('compositor')
|
||||
var film2d = use('film2d')
|
||||
var math = use('math/radians')
|
||||
|
||||
var tilemaps = []
|
||||
var labels = []
|
||||
var t = 0
|
||||
|
||||
var camera = {
|
||||
pos: {x: 250, y: 250},
|
||||
width: 500,
|
||||
height: 500,
|
||||
anchor: {x: 0.5, y: 0.5}
|
||||
}
|
||||
|
||||
var compositor_config = {
|
||||
clear: {r: 0.05, g: 0.05, b: 0.1, a: 1},
|
||||
planes: [
|
||||
{
|
||||
name: 'main',
|
||||
plane: 'main',
|
||||
camera: camera,
|
||||
resolution: {width: 500, height: 500},
|
||||
presentation: 'stretch',
|
||||
clear: {r: 0.05, g: 0.05, b: 0.1, a: 1}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function make_grid(w, h, img) {
|
||||
var tiles = []
|
||||
var x = 0
|
||||
var y = 0
|
||||
for (x = 0; x < w; x++) {
|
||||
tiles[x] = []
|
||||
for (y = 0; y < h; y++) {
|
||||
tiles[x][y] = img
|
||||
}
|
||||
}
|
||||
return tiles
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Tilemap 1: Normal (top-left)
|
||||
tilemaps[] = tilemap2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
tiles: make_grid(4, 4, 'examples/tiles/terrain_dirt_cloud'),
|
||||
tile_width: 25,
|
||||
tile_height: 25,
|
||||
offset_x: 0,
|
||||
offset_y: 14
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Normal",
|
||||
pos: {x: 50, y: 480},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Tilemap 2: Opacity 0.5 (top-right)
|
||||
tilemaps[] = tilemap2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
tiles: make_grid(4, 4, 'examples/tiles/terrain_dirt_cloud'),
|
||||
tile_width: 25,
|
||||
tile_height: 25,
|
||||
offset_x: 10,
|
||||
offset_y: 14,
|
||||
opacity: 0.5
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Opacity 0.5",
|
||||
pos: {x: 280, y: 480},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Tilemap 3: Red tint (bottom-left)
|
||||
tilemaps[] = tilemap2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
tiles: make_grid(4, 4, 'examples/tiles/terrain_dirt_cloud'),
|
||||
tile_width: 25,
|
||||
tile_height: 25,
|
||||
offset_x: 0,
|
||||
offset_y: 4,
|
||||
tint: {r: 1, g: 0.3, b: 0.3, a: 1}
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Red Tint",
|
||||
pos: {x: 50, y: 230},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Tilemap 4: Green tint (bottom-right)
|
||||
tilemaps[] = tilemap2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
tiles: make_grid(4, 4, 'examples/tiles/terrain_dirt_cloud'),
|
||||
tile_width: 25,
|
||||
tile_height: 25,
|
||||
offset_x: 10,
|
||||
offset_y: 4,
|
||||
tint: {r: 0.3, g: 1, b: 0.3, a: 1}
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Green Tint",
|
||||
pos: {x: 280, y: 230},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 1, a: 1}
|
||||
})
|
||||
|
||||
// Tilemap 5: Animated opacity (center bottom)
|
||||
tilemaps[] = tilemap2d({
|
||||
plane: 'main',
|
||||
layer: 0,
|
||||
tiles: make_grid(3, 3, 'examples/tiles/terrain_dirt_cloud'),
|
||||
tile_width: 30,
|
||||
tile_height: 30,
|
||||
offset_x: 6,
|
||||
offset_y: 0,
|
||||
opacity: 1
|
||||
})
|
||||
labels[] = text2d({
|
||||
plane: 'main',
|
||||
layer: 10,
|
||||
text: "Animated Opacity",
|
||||
pos: {x: 180, y: 20},
|
||||
font: 'examples/fonts/dos',
|
||||
size: 14,
|
||||
color: {r: 1, g: 1, b: 0, a: 1}
|
||||
})
|
||||
|
||||
log.console("test_tilemap initialized with " + text(length(tilemaps)) + " tilemaps")
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
t += dt
|
||||
|
||||
// Animate opacity on the center tilemap
|
||||
var animated_tilemap = tilemaps[4]
|
||||
animated_tilemap.opacity = 0.3 + 0.7 * abs(math.sine(t * 1.5))
|
||||
|
||||
// Animate tint on the red tilemap (pulse)
|
||||
var red_tilemap = tilemaps[2]
|
||||
var pulse = 0.5 + 0.5 * math.sine(t * 2)
|
||||
red_tilemap.tint.r = 0.5 + 0.5 * pulse
|
||||
red_tilemap.tint.g = 0.2 * pulse
|
||||
red_tilemap.tint.b = 0.2 * pulse
|
||||
}
|
||||
|
||||
function render() {
|
||||
var plan = compositor.compile(compositor_config)
|
||||
return compositor.execute(plan)
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
core.start({
|
||||
width: 500,
|
||||
height: 500,
|
||||
title: "Test Tilemap Features",
|
||||
framerate: 60,
|
||||
update: update,
|
||||
render: render
|
||||
})
|
||||
@@ -50,16 +50,16 @@ function init_board() {
|
||||
row = []
|
||||
srow = []
|
||||
for (c = 0; c < COLS; c++) {
|
||||
push(row, null)
|
||||
push(srow, shape.rect({
|
||||
row[] = null
|
||||
srow[] = shape.rect({
|
||||
pos: {x: c * TILE + TILE / 2, y: r * TILE + TILE / 2},
|
||||
width: TILE - 1, height: TILE - 1,
|
||||
fill: {r: 0.5, g: 0.5, b: 0.5, a: 1},
|
||||
plane: 'game', layer: 0, visible: false
|
||||
}))
|
||||
})
|
||||
}
|
||||
push(board, row)
|
||||
push(board_shapes, srow)
|
||||
board[] = row
|
||||
board_shapes[] = srow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,10 +104,10 @@ var next_label = text2d({
|
||||
var next_shapes = []
|
||||
var ns = 0
|
||||
for (ns = 0; ns < 4; ns++) {
|
||||
push(next_shapes, shape.rect({
|
||||
next_shapes[] = shape.rect({
|
||||
pos: {x: 0, y: 0}, width: TILE - 1, height: TILE - 1,
|
||||
fill: {r: 1, g: 1, b: 1, a: 1}, plane: 'game', layer: 2, visible: false
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
function random_shape() {
|
||||
@@ -244,10 +244,10 @@ function place_piece() {
|
||||
// Create 4 shapes for active piece (layer 1 = on top of board)
|
||||
var pi = 0
|
||||
for (pi = 0; pi < 4; pi++) {
|
||||
push(piece_shapes, shape.rect({
|
||||
piece_shapes[] = shape.rect({
|
||||
pos: {x: 0, y: 0}, width: TILE - 1, height: TILE - 1,
|
||||
fill: {r: 1, g: 1, b: 1, a: 1}, plane: 'game', layer: 1, visible: false
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
init_board()
|
||||
|
||||
BIN
examples/tiles/block_blue.png
Normal file
|
After Width: | Height: | Size: 468 B |
BIN
examples/tiles/block_coin.png
Normal file
|
After Width: | Height: | Size: 849 B |
BIN
examples/tiles/block_coin_active.png
Normal file
|
After Width: | Height: | Size: 851 B |
BIN
examples/tiles/block_empty.png
Normal file
|
After Width: | Height: | Size: 431 B |
BIN
examples/tiles/block_empty_warning.png
Normal file
|
After Width: | Height: | Size: 676 B |
BIN
examples/tiles/block_exclamation.png
Normal file
|
After Width: | Height: | Size: 808 B |
BIN
examples/tiles/block_exclamation_active.png
Normal file
|
After Width: | Height: | Size: 809 B |
BIN
examples/tiles/block_green.png
Normal file
|
After Width: | Height: | Size: 468 B |
BIN
examples/tiles/block_plank.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/tiles/block_planks.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |