transform handling
This commit is contained in:
@@ -1,321 +1,5 @@
|
||||
var component = {};
|
||||
|
||||
class LooseQuadtree {
|
||||
constructor(boundary, capacity=4, looseness=1.25) {
|
||||
this.boundary = boundary // {x, y, width, height}
|
||||
this.capacity = capacity // max items before subdivision
|
||||
this.looseness = looseness // factor by which nodes expand
|
||||
this.sprites = [] // sprite objects stored here
|
||||
this.divided = false
|
||||
|
||||
// "loose boundary": expand our node’s bounding box
|
||||
// so items that cross the boundary lines can be placed deeper
|
||||
this.looseBounds = this.getLooseBounds(boundary)
|
||||
}
|
||||
|
||||
// Expand boundary by the looseness factor (looseness >= 1)
|
||||
getLooseBounds(boundary) {
|
||||
// For each dimension, we expand half on each side
|
||||
let marginW = (this.looseness - 1) * boundary.width / 2
|
||||
let marginH = (this.looseness - 1) * boundary.height / 2
|
||||
|
||||
return {
|
||||
x: boundary.x - marginW,
|
||||
y: boundary.y - marginH,
|
||||
width: boundary.width + marginW * 2,
|
||||
height: boundary.height + marginH * 2
|
||||
}
|
||||
}
|
||||
|
||||
subdivide() {
|
||||
let x = this.boundary.x
|
||||
let y = this.boundary.y
|
||||
let w = this.boundary.width / 2
|
||||
let h = this.boundary.height / 2
|
||||
|
||||
this.northwest = new LooseQuadtree({x, y, width: w, height: h}, this.capacity, this.looseness)
|
||||
this.northeast = new LooseQuadtree({x: x + w, y, width: w, height: h}, this.capacity, this.looseness)
|
||||
this.southwest = new LooseQuadtree({x, y: y + h, width: w, height: h}, this.capacity, this.looseness)
|
||||
this.southeast = new LooseQuadtree({x: x + w, y: y + h, width: w, height: h}, this.capacity, this.looseness)
|
||||
|
||||
this.divided = true
|
||||
}
|
||||
|
||||
insert(sprite) {
|
||||
let rect = sprite.rect;
|
||||
|
||||
// If outside *loose* bounds, ignore
|
||||
if (!geometry.rect_intersects(this.looseBounds, rect)) return false
|
||||
|
||||
// If we have room and no subdivision, store it here
|
||||
if (this.sprites.length < this.capacity && !this.divided) {
|
||||
this.sprites.push(sprite)
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise, subdivide if not already
|
||||
if (!this.divided) this.subdivide()
|
||||
|
||||
// Try placing into children
|
||||
if (this.northwest.insert(sprite)) return true
|
||||
if (this.northeast.insert(sprite)) return true
|
||||
if (this.southwest.insert(sprite)) return true
|
||||
if (this.southeast.insert(sprite)) return true
|
||||
|
||||
// If it doesn't cleanly fit (overlaps multiple child boundaries),
|
||||
// store at this level
|
||||
this.sprites.push(sprite)
|
||||
return true
|
||||
}
|
||||
|
||||
query(range, found=[]) {
|
||||
// If query doesn't intersect our *loose* boundary, no need to check further
|
||||
if (!geometry.rect_intersects(this.looseBounds, range)) return found
|
||||
|
||||
// Check sprites in this node
|
||||
for (let s of this.sprites)
|
||||
if (geometry.rect_intersects(s.rect, range)) found.push(s)
|
||||
|
||||
// If subdivided, recurse
|
||||
if (this.divided) {
|
||||
this.northwest.query(range, found)
|
||||
this.northeast.query(range, found)
|
||||
this.southwest.query(range, found)
|
||||
this.southeast.query(range, found)
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
||||
class Quadtree {
|
||||
constructor(boundary, capacity=4) {
|
||||
this.boundary = boundary // {x, y, width, height} for this node
|
||||
this.capacity = capacity // max sprites before subdividing
|
||||
this.sprites = [] // sprite objects stored in this node
|
||||
this.divided = false // has this node subdivided?
|
||||
}
|
||||
|
||||
subdivide() {
|
||||
let x = this.boundary.x
|
||||
let y = this.boundary.y
|
||||
let w = this.boundary.width / 2
|
||||
let h = this.boundary.height / 2
|
||||
|
||||
this.northwest = new Quadtree({x, y, width: w, height: h}, this.capacity)
|
||||
this.northeast = new Quadtree({x: x + w, y, width: w, height: h}, this.capacity)
|
||||
this.southwest = new Quadtree({x, y: y + h, width: w, height: h}, this.capacity)
|
||||
this.southeast = new Quadtree({x: x + w, y: y + h, width: w, height: h}, this.capacity)
|
||||
|
||||
this.divided = true
|
||||
}
|
||||
|
||||
insert(sprite) {
|
||||
// Get the sprite's bounding rect
|
||||
let rect = sprite.rect;
|
||||
|
||||
// If it doesn't intersect this quadtree's boundary, ignore
|
||||
if (!geometry.rect_intersects(this.boundary, rect)) return false
|
||||
|
||||
// If there's room here and no subdivision yet, just store it
|
||||
if (this.sprites.length < this.capacity && !this.divided) {
|
||||
this.sprites.push(sprite)
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise, subdivide if not done yet
|
||||
if (!this.divided) this.subdivide()
|
||||
|
||||
// Try inserting into children
|
||||
if (this.northwest.insert(sprite)) return true
|
||||
if (this.northeast.insert(sprite)) return true
|
||||
if (this.southwest.insert(sprite)) return true
|
||||
if (this.southeast.insert(sprite)) return true
|
||||
|
||||
// If it doesn't cleanly fit into a child (spans multiple quadrants), store here
|
||||
this.sprites.push(sprite)
|
||||
return true
|
||||
}
|
||||
|
||||
// Query all sprites that intersect the given range (e.g. your camera rect)
|
||||
query(range, found=[]) {
|
||||
// If there's no overlap, nothing to do
|
||||
if (!geometry.rect_intersects(range, this.boundary)) return found
|
||||
|
||||
// Check sprites in this node
|
||||
for (let s of this.sprites)
|
||||
if (geometry.rect_intersects(range, s.rect)) found.push(s)
|
||||
|
||||
// Recursively query children if subdivided
|
||||
if (this.divided) {
|
||||
this.northwest.query(range, found)
|
||||
this.northeast.query(range, found)
|
||||
this.southwest.query(range, found)
|
||||
this.southeast.query(range, found)
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
||||
class RNode {
|
||||
constructor() {
|
||||
this.children = []
|
||||
this.bbox = null
|
||||
this.leaf = true
|
||||
this.height = 1
|
||||
}
|
||||
}
|
||||
|
||||
class RTree {
|
||||
constructor(maxEntries = 4) {
|
||||
this.maxEntries = maxEntries
|
||||
this.minEntries = Math.max(2, Math.floor(maxEntries * 0.4))
|
||||
this.root = new RNode()
|
||||
}
|
||||
|
||||
_getBBox(child) {
|
||||
return child instanceof RNode ? child.bbox : child.rect
|
||||
}
|
||||
|
||||
_unionBBox(a, b) {
|
||||
if (!a) return {...b}
|
||||
if (!b) return {...a}
|
||||
const minX = Math.min(a.x, b.x)
|
||||
const minY = Math.min(a.y, b.y)
|
||||
const maxX = Math.max(a.x + a.width, b.x + b.width)
|
||||
const maxY = Math.max(a.y + a.height, b.y + b.height)
|
||||
return {x: minX, y: minY, width: maxX - minX, height: maxY - minY}
|
||||
}
|
||||
|
||||
insert(item) {
|
||||
if (!item) return
|
||||
|
||||
if (this.root.leaf && this.root.children.length === 0) {
|
||||
this.root.children.push(item)
|
||||
this.root.bbox = {...item.rect}
|
||||
return
|
||||
}
|
||||
|
||||
let node = this._chooseSubtree(this.root, item)
|
||||
node.children.push(item)
|
||||
this._extend(node, item)
|
||||
|
||||
while (node && node.children.length > this.maxEntries) {
|
||||
this._split(node)
|
||||
node = this._findParent(this.root, node)
|
||||
}
|
||||
}
|
||||
|
||||
_chooseSubtree(node, item) {
|
||||
while (!node.leaf) {
|
||||
let minEnlargement = Infinity
|
||||
let bestChild = null
|
||||
|
||||
for (const child of node.children) {
|
||||
const enlargement = this._enlargement(child.bbox, item)
|
||||
if (enlargement < minEnlargement) {
|
||||
minEnlargement = enlargement
|
||||
bestChild = child
|
||||
}
|
||||
}
|
||||
node = bestChild || node.children[0]
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
_enlargement(bbox, item) {
|
||||
if (!bbox) return Number.MAX_VALUE
|
||||
const itemBBox = this._getBBox(item)
|
||||
const union = this._unionBBox(bbox, itemBBox)
|
||||
return (union.width * union.height) - (bbox.width * bbox.height)
|
||||
}
|
||||
|
||||
_split(node) {
|
||||
const items = node.children
|
||||
|
||||
items.sort((a, b) => {
|
||||
const aBBox = this._getBBox(a)
|
||||
const bBBox = this._getBBox(b)
|
||||
return aBBox.x - bBBox.x
|
||||
})
|
||||
|
||||
const splitIndex = Math.ceil(items.length / 2)
|
||||
const group1 = items.slice(0, splitIndex)
|
||||
const group2 = items.slice(splitIndex)
|
||||
|
||||
if (node === this.root) {
|
||||
this.root = new RNode()
|
||||
this.root.leaf = false
|
||||
node.children = group1
|
||||
node.bbox = this._getBBoxes(group1)
|
||||
|
||||
const sibling = new RNode()
|
||||
sibling.leaf = node.leaf
|
||||
sibling.children = group2
|
||||
sibling.bbox = this._getBBoxes(group2)
|
||||
|
||||
this.root.children = [node, sibling]
|
||||
this.root.bbox = this._unionBBox(node.bbox, sibling.bbox)
|
||||
} else {
|
||||
node.children = group1
|
||||
node.bbox = this._getBBoxes(group1)
|
||||
|
||||
const sibling = new RNode()
|
||||
sibling.leaf = node.leaf
|
||||
sibling.children = group2
|
||||
sibling.bbox = this._getBBoxes(group2)
|
||||
|
||||
const parent = this._findParent(this.root, node)
|
||||
parent.children.push(sibling)
|
||||
parent.bbox = this._unionBBox(parent.bbox, sibling.bbox)
|
||||
}
|
||||
}
|
||||
|
||||
_getBBoxes(items) {
|
||||
let bbox = null
|
||||
for (const item of items) {
|
||||
bbox = this._unionBBox(bbox, this._getBBox(item))
|
||||
}
|
||||
return bbox
|
||||
}
|
||||
|
||||
_extend(node, item) {
|
||||
const itemBBox = this._getBBox(item)
|
||||
node.bbox = node.bbox ? this._unionBBox(node.bbox, itemBBox) : {...itemBBox}
|
||||
}
|
||||
|
||||
_findParent(node, target, parent = null) {
|
||||
if (!node || node === target) return parent
|
||||
if (!node.leaf) {
|
||||
for (const child of node.children) {
|
||||
if (child instanceof RNode) {
|
||||
const result = this._findParent(child, target, node)
|
||||
if (result) return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
query(range, results = []) {
|
||||
const stack = [this.root]
|
||||
while (stack.length > 0) {
|
||||
const node = stack.pop()
|
||||
if (!node.bbox || !geometry.rect_intersects(node.bbox, range)) continue
|
||||
|
||||
if (node.leaf) {
|
||||
for (const item of node.children) {
|
||||
if (geometry.rect_intersects(item.rect, range)) {
|
||||
results.push(item)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stack.push(...node.children)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
function make_point_obj(o, p) {
|
||||
return {
|
||||
pos: p,
|
||||
@@ -337,17 +21,8 @@ frog = {
|
||||
}
|
||||
*/
|
||||
|
||||
var world = {x:-1000,y:-1000, width:3000, height:3000};
|
||||
//globalThis.sprite_qt = new Quadtree({x:-1000,y:-1000, width:3000, height:3000}, 10);
|
||||
//globalThis.sprite_qt = new LooseQuadtree(world, 10, 1.2);
|
||||
//globalThis.sprite_qt = new RTree(10)
|
||||
//globalThis.sprite_qt = os.make_quadtree(world, 4);
|
||||
//globalThis.sprite_qt = os.make_qtree(world);
|
||||
globalThis.sprite_qt = os.make_rtree();
|
||||
|
||||
var spritetree = os.make_quadtree([0,0], [10000,10000]);
|
||||
globalThis.spritetree = spritetree;
|
||||
|
||||
var sprite = {
|
||||
image: undefined,
|
||||
get diffuse() { return this.image; },
|
||||
@@ -564,6 +239,11 @@ component.sprite = function (obj) {
|
||||
sp.transform.parent = obj.transform;
|
||||
sp.guid = prosperon.guid();
|
||||
allsprites.push(sp);
|
||||
sp.transform.change_hook = function() {
|
||||
sprite_qt.remove(sp);
|
||||
sp.rect = sp.transform.torect();
|
||||
sprite_qt.insert(sp);
|
||||
}
|
||||
return sp;
|
||||
};
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ emitter.step = function step(dt) {
|
||||
}
|
||||
}
|
||||
|
||||
for (var p of this.particles)
|
||||
p.transform.clean();
|
||||
// for (var p of this.particles)
|
||||
// p.transform.clean();
|
||||
};
|
||||
|
||||
emitter.burst = function (count, t) {
|
||||
|
||||
@@ -756,19 +756,8 @@ function insertion_sort(arr, cmp)
|
||||
return arr
|
||||
}
|
||||
|
||||
var culled;
|
||||
|
||||
var treed;
|
||||
function sprites_to_queue(sprites, ysort = false)
|
||||
{
|
||||
if (!treed) {
|
||||
for (var sp of allsprites) {
|
||||
sp.rect = sp.transform.torect();
|
||||
sprite_qt.insert(sp)
|
||||
}
|
||||
treed = true;
|
||||
}
|
||||
|
||||
var pos = prosperon.camera.transform.pos;
|
||||
var size = prosperon.camera.size;
|
||||
var camrect = {
|
||||
|
||||
@@ -371,8 +371,6 @@ struct lrtb {
|
||||
float b;
|
||||
};
|
||||
|
||||
static JSContext *global_js;
|
||||
|
||||
static SDL_GPUDevice *global_gpu;
|
||||
|
||||
SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v)
|
||||
@@ -3312,7 +3310,7 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
||||
size_t base = i * 4;
|
||||
|
||||
|
||||
HMM_Mat3 trmat = tr->gcache3;
|
||||
// HMM_Mat3 trmat = transform2mat3_global(tr);
|
||||
|
||||
HMM_Vec3 base_quad[4] = {
|
||||
{0.0,0.0,1.0},
|
||||
@@ -3321,8 +3319,8 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
||||
{1.0,1.0,1.0}
|
||||
};
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
// for (int j = 0; j < 4; j++)
|
||||
// posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
|
||||
// Define the UV coordinates based on the source rectangle
|
||||
uvdata[base + 0] = (HMM_Vec2){ src.x, src.y + src.h };
|
||||
@@ -3989,12 +3987,10 @@ JSC_CCALL(gpu_sort_sprite,
|
||||
JS_GETATOM(js,blayer,b,layer_atom,number)
|
||||
if (alayer != blayer) return number2js(js,alayer - blayer);
|
||||
|
||||
transform *atr, *btr;
|
||||
JS_GETATOM(js,atr,a,transform_atom,transform)
|
||||
JS_GETATOM(js,btr,b,transform_atom,transform);
|
||||
HMM_Mat3 am3 = transform2mat3_global(atr);
|
||||
HMM_Mat3 bm3 = transform2mat3_global(btr);
|
||||
if (am3.Columns[2].y != bm3.Columns[2].y) return number2js(js,bm3.Columns[2].y - am3.Columns[2].y);
|
||||
rect ar, br;
|
||||
JS_GETATOM(js,ar,a,rect_atom,rect)
|
||||
JS_GETATOM(js,br,b,rect_atom,rect)
|
||||
if (ar.y != br.y) return number2js(js,br.y-ar.y);
|
||||
|
||||
JSValue aimg,bimg;
|
||||
aimg = JS_GetProperty(js,a,image_atom);
|
||||
@@ -4148,8 +4144,7 @@ JSC_CCALL(gpu_make_sprite_mesh,
|
||||
JS_FreeValue(js,sub);
|
||||
|
||||
size_t base = i*4;
|
||||
HMM_Mat3 trmat = tr->gcache3;
|
||||
// HMM_Mat3 trmat = transform2mat3(tr);
|
||||
HMM_Mat3 trmat = transform2mat3(tr);
|
||||
for (int j = 0; j < 4; j++)
|
||||
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
|
||||
@@ -5769,7 +5764,7 @@ JSC_CCALL(transform_lookat,
|
||||
transform *go = js2transform(js,self);
|
||||
HMM_Mat4 m = HMM_LookAt_RH(go->pos, point, vUP);
|
||||
go->rotation = HMM_M4ToQ_RH(m);
|
||||
go->dirty = true;
|
||||
transform_apply(go);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_rotate,
|
||||
@@ -5811,7 +5806,6 @@ JSC_CCALL(transform_unit,
|
||||
t->rotation = QUAT1;
|
||||
t->scale = v3one;
|
||||
transform_apply(t);
|
||||
t->parent = NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_trs,
|
||||
@@ -5839,6 +5833,20 @@ JSC_CCALL(transform_array,
|
||||
JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i]));
|
||||
)
|
||||
|
||||
static JSValue js_transform_get_change_hook(JSContext *js, JSValueConst self)
|
||||
{
|
||||
transform *t = js2transform(js,self);
|
||||
return JS_DupValue(js,t->change_hook);
|
||||
}
|
||||
|
||||
static JSValue js_transform_set_change_hook(JSContext *js, JSValueConst self, JSValue v)
|
||||
{
|
||||
transform *t = js2transform(js,self);
|
||||
if (!JS_IsUndefined(v) && !JS_IsFunction(js,v)) return JS_ThrowReferenceError(js, "Hook must be a function.");
|
||||
JS_FreeValue(js,t->change_hook);
|
||||
t->change_hook = JS_DupValue(js,v);
|
||||
}
|
||||
|
||||
static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
|
||||
{
|
||||
transform *t = js2transform(js,self);
|
||||
@@ -5848,26 +5856,42 @@ static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
|
||||
|
||||
static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v)
|
||||
{
|
||||
transform *p = js2transform(js,v);
|
||||
if (!p)
|
||||
return JS_ThrowReferenceError(js,"Parent must be another transform.");
|
||||
|
||||
transform *t = js2transform(js,self);
|
||||
if (t->parent)
|
||||
if (t->parent) {
|
||||
JS_FreeValue(js,t->jsparent);
|
||||
|
||||
transform *p = js2transform(js,v);
|
||||
for (int i = 0; i < arrlen(p->children); i++) {
|
||||
if (p->children[i] == t) {
|
||||
arrdelswap(p->children,i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrlen(p->jschildren); i++) {
|
||||
if (JS_SameValue(js,p->jschildren[i],self)) {
|
||||
JS_FreeValue(js,p->jschildren[i]);
|
||||
arrdelswap(p->jschildren,i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t->parent = p;
|
||||
if (p)
|
||||
t->jsparent = JS_DupValue(js,v);
|
||||
t->jsparent = JS_DupValue(js,v);
|
||||
|
||||
arrput(p->children, t);
|
||||
JSValue child = JS_DupValue(js,self);
|
||||
arrput(p->jschildren,child);
|
||||
|
||||
transform_apply(t);
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSC_CCALL(transform_clean,
|
||||
transform2mat3_global(js2transform(js,self));
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_dirty,
|
||||
return JS_NewBool(js,transform_dirty_chain(js2transform(js,self)));
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_torect,
|
||||
transform *t = js2transform(js,self);
|
||||
return rect2js(js,transform2rect(t));
|
||||
@@ -5877,7 +5901,8 @@ static const JSCFunctionListEntry js_transform_funcs[] = {
|
||||
CGETSET_ADD(transform, pos),
|
||||
CGETSET_ADD(transform, scale),
|
||||
CGETSET_ADD(transform, rotation),
|
||||
CGETSET_ADD(transform, parent),
|
||||
CGETSET_ADD(transform, parent),
|
||||
CGETSET_ADD(transform, change_hook),
|
||||
MIST_FUNC_DEF(transform, trs, 3),
|
||||
MIST_FUNC_DEF(transform, phys2d, 3),
|
||||
MIST_FUNC_DEF(transform, move, 1),
|
||||
@@ -5888,8 +5913,6 @@ static const JSCFunctionListEntry js_transform_funcs[] = {
|
||||
MIST_FUNC_DEF(transform, unit, 0),
|
||||
MIST_FUNC_DEF(transform, rect, 1),
|
||||
MIST_FUNC_DEF(transform, array, 0),
|
||||
MIST_FUNC_DEF(transform, clean, 0),
|
||||
MIST_FUNC_DEF(transform, dirty, 0),
|
||||
MIST_FUNC_DEF(transform, torect, 0),
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
static JSContext *js = NULL;
|
||||
static JSRuntime *rt = NULL;
|
||||
|
||||
JSContext *global_js = NULL;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT
|
||||
#else
|
||||
|
||||
@@ -10,4 +10,6 @@ void script_evalf(const char *format, ...);
|
||||
JSValue script_eval(const char *file, const char *script);
|
||||
void script_call_sym(JSValue sym, int argc, JSValue *argv);
|
||||
|
||||
extern JSContext *global_js;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "transform.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "script.h"
|
||||
|
||||
#include "stb_ds.h"
|
||||
|
||||
transform *make_transform()
|
||||
{
|
||||
@@ -9,10 +12,50 @@ transform *make_transform()
|
||||
t->scale = (HMM_Vec3){1,1,1};
|
||||
t->rotation = (HMM_Quat){0,0,0,1};
|
||||
transform_apply(t);
|
||||
t->change_hook = JS_UNDEFINED;
|
||||
return t;
|
||||
}
|
||||
|
||||
void transform_free(JSRuntime *rt, transform *t) { free(t); }
|
||||
void transform_clean(transform *t)
|
||||
{
|
||||
if (!t->dirty) return;
|
||||
t->dirty = 0;
|
||||
t->cache = HMM_M4TRS(t->pos,t->rotation,t->scale);
|
||||
if (t->parent)
|
||||
t->gcache = HMM_MulM4(t->parent->gcache, t->cache);
|
||||
else
|
||||
t->gcache = t->cache;
|
||||
|
||||
HMM_Mat4 m = t->gcache;
|
||||
rect r = {0};
|
||||
r.x = m.Columns[3].x;
|
||||
r.y = m.Columns[3].y;
|
||||
r.w = m.Columns[0].x;
|
||||
r.h = m.Columns[1].y;
|
||||
t->rcache = r;
|
||||
|
||||
for (int i = 0; i < arrlen(t->children); i++) {
|
||||
t->children[i]->dirty = 1;
|
||||
transform_clean(t->children[i]);
|
||||
}
|
||||
|
||||
if (!JS_IsUndefined(t->change_hook)) {
|
||||
JSValue ret = JS_Call(global_js, t->change_hook, JS_UNDEFINED, 0, NULL);
|
||||
JS_FreeValue(global_js,ret);
|
||||
}
|
||||
}
|
||||
|
||||
void transform_free(JSRuntime *rt, transform *t) {
|
||||
JS_FreeValueRT(rt,t->change_hook);
|
||||
JS_FreeValueRT(rt,t->jsparent);
|
||||
for (int i = 0; i < arrlen(t->jschildren); i++)
|
||||
JS_FreeValueRT(rt,t->jschildren[i]);
|
||||
|
||||
arrfree(t->jschildren);
|
||||
arrfree(t->children);
|
||||
free(t);
|
||||
printf("FREED TRANSFORM!\n");
|
||||
}
|
||||
void transform_move(transform *t, HMM_Vec3 v)
|
||||
{
|
||||
t->pos = HMM_AddV3(t->pos, v);
|
||||
@@ -52,76 +95,30 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
|
||||
return mat3_t_pos(m, dir);
|
||||
}
|
||||
|
||||
static inline void transform_clean(transform *t)
|
||||
{
|
||||
if (!t->dirty) return;
|
||||
t->dirty = 0;
|
||||
t->cache = HMM_M4TRS(t->pos,t->rotation,t->scale);
|
||||
t->cache3 = HMM_M3TRS(t->pos.xy,t->rotation.x,t->scale.xy);
|
||||
}
|
||||
|
||||
HMM_Mat4 transform2mat(transform *t)
|
||||
{
|
||||
transform_clean(t);
|
||||
return t->cache;
|
||||
}
|
||||
|
||||
HMM_Mat3 transform2mat3(transform *t)
|
||||
{
|
||||
transform_clean(t);
|
||||
return t->cache3;
|
||||
return HMM_M3TRS(t->pos.xy, t->rotation.x, t->scale.xy);
|
||||
}
|
||||
|
||||
HMM_Mat4 transform2mat4_global(transform *t)
|
||||
{
|
||||
if (!transform_dirty_chain(t)) return t->gcache;
|
||||
|
||||
HMM_Mat4 tm = transform2mat(t);
|
||||
if (t->parent)
|
||||
t->gcache = HMM_MulM4(transform2mat(t->parent), tm);
|
||||
else
|
||||
t->gcache = tm;
|
||||
|
||||
return t->gcache;
|
||||
}
|
||||
|
||||
rect transform2rect(transform *t)
|
||||
{
|
||||
if (!transform_dirty_chain(t)) return t->rcache;
|
||||
HMM_Mat4 m3 = transform2mat4_global(t);
|
||||
rect r = {0};
|
||||
r.x = m3.Columns[3].x;
|
||||
r.y = m3.Columns[3].y;
|
||||
r.w = m3.Columns[0].x;
|
||||
r.h = m3.Columns[1].y;
|
||||
t->rcache = r;
|
||||
return t->rcache;
|
||||
}
|
||||
|
||||
HMM_Mat3 transform2mat3_global(transform *t)
|
||||
{
|
||||
if (!transform_dirty_chain(t)) return t->gcache3;
|
||||
|
||||
HMM_Mat3 tm = transform2mat3(t);
|
||||
if (t->parent)
|
||||
t->gcache3 = HMM_MulM3(transform2mat3(t->parent), tm);
|
||||
else
|
||||
t->gcache3 = tm;
|
||||
|
||||
return t->gcache3;
|
||||
}
|
||||
|
||||
int transform_dirty_chain(transform *t)
|
||||
{
|
||||
if (t->dirty) return 1;
|
||||
if (t->parent)
|
||||
return transform_dirty_chain(t->parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void transform_apply(transform *t)
|
||||
{
|
||||
t->dirty = 1;
|
||||
transform_clean(t);
|
||||
}
|
||||
|
||||
HMM_Quat angle2rotation(float angle)
|
||||
|
||||
@@ -10,15 +10,14 @@ typedef struct transform {
|
||||
HMM_Vec3 scale;
|
||||
HMM_Quat rotation;
|
||||
HMM_Mat4 cache;
|
||||
HMM_Mat3 cache3;
|
||||
HMM_Mat3 gcache3;
|
||||
HMM_Mat4 gcache;
|
||||
rect rcache;
|
||||
int dirty;
|
||||
struct transform *parent;
|
||||
JSValue jsparent;
|
||||
struct transform *children;
|
||||
struct transform **children;
|
||||
JSValue *jschildren;
|
||||
JSValue change_hook;
|
||||
} transform;
|
||||
|
||||
transform *make_transform();
|
||||
@@ -52,9 +51,7 @@ HMM_Mat4 transform2mat(transform *t);
|
||||
HMM_Mat3 transform2mat3(transform *t);
|
||||
|
||||
HMM_Mat4 transform2mat4_global(transform *t);
|
||||
HMM_Mat3 transform2mat3_global(transform *t);
|
||||
rect transform2rect(transform *t);
|
||||
int transform_dirty_chain(transform *t);
|
||||
|
||||
transform mat2transform(HMM_Mat4 m);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user