diff --git a/examples/common/bootstrap.js b/examples/common/bootstrap.js index aec7a4f..0fb9917 100644 --- a/examples/common/bootstrap.js +++ b/examples/common/bootstrap.js @@ -75,6 +75,7 @@ export class Bootstrap { get renderer() { return this.#renderer; } get solver() { return this.#solver; } + get debug() {return this.#debugInstance;} /** @deprecated Use renderer instead */ get canvas() { return this.#renderer.canvas; } @@ -95,16 +96,13 @@ export class Bootstrap { /** * @param renderer * @param {{ - * debug?: boolean, slowMotion?: number, useDpr?: boolean, - * showBoundary?: boolean, showVectorLength?: boolean, showVector?: boolean, statistics?: boolean, - * solverSteps?: number, solverBias?: number, solverBeta?: number, allowedOverlap?: boolean, solverWarming?: boolean, - * solverTreeDivider?: number, solverTreeMaxCount?: number + * solver: SolverSettings, + * debug: DebugSettings, * }} options */ constructor(renderer, options = {}) { this.#renderer = renderer; - this.#debug = options.debug; this.#slowMotion = Math.max(0.01, Math.min(2, options.slowMotion ?? 1)); this.#particleSystem = new ParticleSystem(); @@ -112,26 +110,41 @@ export class Bootstrap { this.#particleSystem.onParticleDeleted.subscribe(this, this.#destroyParticle.bind(this)); this.#solver = new ImpulseBasedSolver(); - if (Number.isFinite(options.solverSteps)) this.#solver.steps = options.solverSteps; - if (Number.isFinite(options.solverBias)) this.#solver.velocityBiasFactor = options.solverBias; - if (Number.isFinite(options.solverBeta)) this.#solver.positionCorrectionBeta = options.solverBeta; - if (Number.isFinite(options.allowedOverlap)) this.#solver.allowedOverlap = options.allowedOverlap; - if (Number.isFinite(options.solverTreeDivider)) this.#solver.treeDivider = options.solverTreeDivider; - if (Number.isFinite(options.solverTreeMaxCount)) this.#solver.treeMaxCount = options.solverTreeMaxCount; - if (options.solverWarming !== undefined) this.#solver.warming = options.solverWarming; - - if (options.debug) { - this.#debugInstance = new Debug(options); - this.#solver.setDebugger(this.#debugInstance); - window.__app = {DebugInstance: this.#debugInstance}; - } + this.configure(options); + + this.#init(); + } + + configure(options) { + if (Number.isFinite(options.solver?.steps)) this.#solver.steps = options.solver.steps; + if (Number.isFinite(options.solver?.bias)) this.#solver.velocityBiasFactor = options.solver.bias; + if (Number.isFinite(options.solver?.beta)) this.#solver.positionCorrectionBeta = options.solver.beta; + if (Number.isFinite(options.solver?.overlap)) this.#solver.allowedOverlap = options.solver.overlap; + if (Number.isFinite(options.solver?.treeDivider)) this.#solver.treeDivider = options.solver.treeDivider; + if (Number.isFinite(options.solver?.treeMaxCount)) this.#solver.treeMaxCount = options.solver.treeMaxCount; + if (options.solver?.warming !== undefined) this.#solver.warming = options.solver.warming; - if (options.statistics) { - this.#statsElement = document.createElement("pre"); - document.body.appendChild(this.#statsElement); + this.#debug = options.debug?.debug ?? false; + if (this.#debug) { + if (!this.#debugInstance) { + this.#debugInstance = new Debug(options.debug); + this.#solver.setDebugger(this.#debugInstance); + window.__app = {DebugInstance: this.#debugInstance}; + } else { + this.#debugInstance.configure(options.debug); + } } - this.#init(); + if (options.debug?.statistics) { + if (!this.#statsElement) { + this.#statsElement = document.createElement("pre"); + document.body.appendChild(this.#statsElement); + } + + this.#statsElement.style.display = "block"; + } else if (this.#statsElement) { + this.#statsElement.style.display = "none"; + } } /** @@ -345,7 +358,7 @@ export class Bootstrap { this.#auxCanvas = document.createElement("canvas"); this.#auxCanvas.style.pointerEvents = "none"; this.#auxCanvas.style.touchAction = "none"; - this.#auxCanvas.style.zIndex = "9999999"; + this.#auxCanvas.style.zIndex = "1000"; this.#auxCanvas.style.position = "absolute"; this.#auxCanvas.style.left = rect.left + "px"; this.#auxCanvas.style.top = rect.top + "px"; @@ -422,6 +435,8 @@ export class Bootstrap { #render(delta) { if (!this.#debugInstance || this.#debugInstance.showBodies) { this.#renderer.render(this.state === State.play ? delta : 1e-12); + } else { + this.#renderer.clear(); } if (this.state === State.play) { @@ -430,7 +445,7 @@ export class Bootstrap { } if (this.#debugInstance || this.#shapeId > 0) { - this.#auxCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + this.#auxCtx.clearRect(0, 0, this.renderer.canvasWidth, this.renderer.canvasHeight); } if (this.#debug) { diff --git a/examples/common/common.css b/examples/common/common.css index a7be337..9305bfc 100644 --- a/examples/common/common.css +++ b/examples/common/common.css @@ -312,6 +312,7 @@ input[invalid=true] { height: 1em; width: 1em; transform: translateY(-15%); + user-select: none; } .align-corner-end { diff --git a/examples/common/settings/debug.js b/examples/common/settings/debug.js new file mode 100644 index 0000000..24ff657 --- /dev/null +++ b/examples/common/settings/debug.js @@ -0,0 +1,79 @@ +import {DependantProperties, Property, SettingsBase, SettingsGroup} from "./base.js"; +import {ComponentType} from "./enum.js"; + +export class DebugSettings extends SettingsBase { + static Properties = { + statistics: Property.bool("stats", true) + .setName("Statistics"), + + debug: Property.bool("debug", false) + .setName("Debug mode"), + + showBodies: Property.bool("debug_body", true) + .setName("Show bodies"), + showVectors: Property.bool("debug_vector", true) + .setName("Show vectors"), + showVectorLength: Property.bool("debug_vector_length", false) + .setName("Show vector length"), + showPoints: Property.bool("debug_vector", true) + .setName("Show points"), + + showBoundary: Property.bool("debug_boundary", true) + .setName("Show boundaries"), + showVelocityVector: Property.bool("debug_velocity", true) + .setName("Show velocity"), + + showNormalVector: Property.bool("debug_normal", false) + .setName("Show normal"), + showTangentVector: Property.bool("debug_tangent", false) + .setName("Show tangent"), + showContactVector: Property.bool("debug_contact", false) + .setName("Show contact"), + showWarmVector: Property.bool("debug_warming", false) + .setName("Show warming"), + + debugTree: Property.bool("debug_tree", false) + .setName("Debug tree"), + showTreeLeafs: Property.bool("debug_tree_leafs", true) + .setName("Show tree leafs"), + showTreeSegments: Property.bool("debug_tree_segments", false) + .setName("Show tree segments"), + showTreeBoundaryCollision: Property.bool("debug_tree_collision", true) + .setName("Show tree boundary collision"), + + vectorArrowSize: Property.float("vector_arrow_size", 2) + .setName("Vector arrow size"), + collisionSize: Property.float("collision_size", 4) + .setName("Collision point size"), + } + + static PropertiesDependencies = new Map([ + [this.Properties.debug, new DependantProperties([ + this.Properties.debugTree, + this.Properties.showBodies, + this.Properties.showVectors, + this.Properties.showVectorLength, + this.Properties.showPoints, + this.Properties.showBoundary, + this.Properties.showVelocityVector, + this.Properties.showNormalVector, + this.Properties.showTangentVector, + this.Properties.showContactVector, + this.Properties.showWarmVector, + ])], + + [this.Properties.debugTree, new DependantProperties([ + this.Properties.showTreeLeafs, + this.Properties.showTreeSegments, + this.Properties.showTreeBoundaryCollision, + ])] + ]) +} + +for (const prop of Object.values(DebugSettings.Properties)) { + prop.setAffects(ComponentType.debug); +} + +export const DebugConfigGroup = { + debug: SettingsGroup.of(DebugSettings).setName("Debug") +} \ No newline at end of file diff --git a/examples/common/settings/enum.js b/examples/common/settings/enum.js index 911b2b0..238c91f 100644 --- a/examples/common/settings/enum.js +++ b/examples/common/settings/enum.js @@ -3,5 +3,6 @@ */ export const ComponentType = { solver: "solver", + debug: "debug", renderer: "renderer" } diff --git a/examples/common/settings/renderer.js b/examples/common/settings/renderer.js new file mode 100644 index 0000000..ec727b0 --- /dev/null +++ b/examples/common/settings/renderer.js @@ -0,0 +1,17 @@ +import {Property, SettingsBase, SettingsGroup} from "./base.js"; +import {ComponentType} from "./enum.js"; + +export class RenderSettings extends SettingsBase { + static Properties = { + useDpr: Property.bool("dpr", true) + .setName("Use DPR"), + } +} + +for (const prop of Object.values(RenderSettings.Properties)) { + prop.setAffects(ComponentType.renderer); +} + +export const RenderConfigGroup = { + renderer: SettingsGroup.of(RenderSettings).setName("Renderer"), +} \ No newline at end of file diff --git a/examples/common/settings/solver.js b/examples/common/settings/solver.js new file mode 100644 index 0000000..f0b0176 --- /dev/null +++ b/examples/common/settings/solver.js @@ -0,0 +1,35 @@ +import {Property, SettingsBase, SettingsGroup} from "./base.js"; +import {ComponentType} from "./enum.js"; + +export class SolverSettings extends SettingsBase { + static Properties = { + steps: Property.int("steps", 6) + .setName("Steps") + .setConstraints(1, 20), + bias: Property.float("bias", 0.2) + .setName("Velocity bias") + .setConstraints(0, 1), + beta: Property.float("beta", 0.5) + .setName("Position correction beta") + .setConstraints(0, 1), + overlap: Property.float("overlap", 1) + .setName("Allowed overlap") + .setConstraints(0, 10), + warming: Property.bool("warming", true) + .setName("Warming"), + treeDivider: Property.int("tree_divider", 2) + .setName("Tree divider") + .setConstraints(2, 16), + treeMaxCount: Property.int("tree_cnt", 4) + .setName("Tree max segment count") + .setConstraints(2, 1000) + } +} + +for (const prop of Object.values(SolverSettings.Properties)) { + prop.setAffects(ComponentType.solver); +} + +export const SolverConfigGroup = { + solver: SettingsGroup.of(SolverSettings).setName("Solver") +} \ No newline at end of file diff --git a/examples/common/ui/controllers/settings.js b/examples/common/ui/controllers/settings.js index 19130aa..11eb7b4 100644 --- a/examples/common/ui/controllers/settings.js +++ b/examples/common/ui/controllers/settings.js @@ -48,13 +48,15 @@ export class SettingsController extends ControllerBase { } for (const prop of this.propData.keys()) { - const {key, groupKey} = this.propData.get(prop); + const {key, groupKey, control: propControl} = this.propData.get(prop); const deps = this.settings[groupKey].constructor.PropertiesDependencies.get(prop); - if (deps && deps.length > 0) { - for (const depProp of deps) { + + if (deps?.properties?.length > 0) { + const value = !!this.settings[groupKey][key] && propControl.enabled; + + for (const depProp of deps.properties) { if (!(depProp instanceof ReadOnlyProperty)) { - const value = this.settings[groupKey][key]; - this.propData.get(depProp).control.setEnabled(!!value); + this.propData.get(depProp).control.setEnabled(value); } } } @@ -77,12 +79,14 @@ export class SettingsController extends ControllerBase { const deps = config[groupKey].constructor.PropertiesDependencies.get(prop); if (deps && deps.properties.length > 0) { for (const depProp of deps.properties) { - this.onParameterChanged(depProp, true); - if (!(depProp instanceof ReadOnlyProperty)) { + const depValue = !!value && control.enabled; + const invert = deps.options.invert && (deps.options.invert === true || deps.options.invert[depProp.key] === true); - this.propData.get(depProp).control.setEnabled(invert ? !value : !!value); + this.propData.get(depProp).control.setEnabled(invert ? !depValue : depValue); } + + this.onParameterChanged(depProp, true); } } } diff --git a/examples/common/ui/controls/base.js b/examples/common/ui/controls/base.js index d4c6dd2..4839684 100644 --- a/examples/common/ui/controls/base.js +++ b/examples/common/ui/controls/base.js @@ -25,6 +25,8 @@ export class Control { return /** @type {T} */ new this(e, ...params); } + get enabled() {return !this.element.hasAttribute("disabled");} + setVisibility(show) { this.element.style.display = show ? null : "none"; } diff --git a/examples/gravity/index.js b/examples/gravity/index.js index 15c50b3..1c00e47 100644 --- a/examples/gravity/index.js +++ b/examples/gravity/index.js @@ -1,5 +1,4 @@ import {Bootstrap} from "../common/bootstrap.js"; -import * as Params from "../common/params.js"; import {ResistanceForce} from "../../lib/physics/force.js"; import {InsetConstraint} from "../../lib/physics/constraint.js"; import {GravityComponentType, GravityExampleSettings} from "./settings.js"; @@ -12,12 +11,10 @@ import {GravityWorld} from "./world.js"; import {GravityRender} from "./render.js"; import {updateUrl} from "../common/utils.js"; import * as Utils from "../common/utils.js"; +import {SolverSettings} from "../common/settings/solver.js"; -const options = Params.parse({ - resistance: 1, - restitution: 0.2, - friction: 0.5 -}); +SolverSettings.Properties.bias.defaultValue = 0.5; +SolverSettings.Properties.beta.defaultValue = 1; let Settings = GravityExampleSettings.fromQueryParams(); const settingsCtrl = new SettingsController(document.getElementById("settings-content"), this); @@ -51,20 +48,23 @@ document.addEventListener("visibilitychange", function () { lblPause.setVisibility(document.hidden); }); -const RenderInstance = new GravityRender(document.getElementById("canvas"), Settings, options) +const RenderInstance = new GravityRender(document.getElementById("canvas"), Settings) const BootstrapInstance = new Bootstrap( RenderInstance.renderer, - Object.assign({solverBias: 0.5, solverBeta: 1}, options) + Settings, + //Object.assign({solverBias: 0.5, solverBeta: 1}, options) ); RenderInstance.initialize(); const GravityInstance = new GravityPhysics(BootstrapInstance, Settings); -const WorldInstance = new GravityWorld(BootstrapInstance, Settings, options); +const WorldInstance = new GravityWorld(BootstrapInstance, Settings); await WorldInstance.initialize(); +const Resistance = new ResistanceForce(Settings.world.resistance) BootstrapInstance.addConstraint(new InsetConstraint(WorldInstance.worldRect)) -BootstrapInstance.addForce(new ResistanceForce(options.resistance)); +BootstrapInstance.addForce(Resistance); +BootstrapInstance.debug?.setViewMatrix(RenderInstance.canvasMatrix); BootstrapInstance.enableHotKeys(); BootstrapInstance.run(); @@ -96,6 +96,9 @@ async function reconfigure(newSettings) { } }); + BootstrapInstance.configure(Settings); + BootstrapInstance.debug?.setViewMatrix(RenderInstance.canvasMatrix); + Resistance.resistance = Settings.world.resistance; } // noinspection InfiniteLoopJS diff --git a/examples/gravity/render.js b/examples/gravity/render.js index 24e6122..d256ea5 100644 --- a/examples/gravity/render.js +++ b/examples/gravity/render.js @@ -2,17 +2,20 @@ import {WebglRenderer} from "../../lib/render/renderer/webgl/renderer.js"; import {m4} from "../../lib/render/renderer/webgl/utils/m4.js"; export class GravityRender { + matrix = null; + canvasMatrix = null; + renderer; settings; + /** * @param {HTMLCanvasElement} element * @param {GravityExampleSettings} settings - * @param {*} options */ - constructor(element, settings, options) { + constructor(element, settings) { this.settings = settings; - this.renderer = new WebglRenderer(element, options); + this.renderer = new WebglRenderer(element, settings.renderer); } initialize() { @@ -20,8 +23,16 @@ export class GravityRender { projMatrix = m4.translate(projMatrix, this.renderer.canvasWidth / 2, this.renderer.canvasHeight / 2, 0); projMatrix = m4.scale(projMatrix, 1 / this.settings.world.worldScale, 1 / this.settings.world.worldScale, 1); + this.matrix = projMatrix; this.renderer.setProjectionMatrix(projMatrix) + let canvasMatrix = m4.projection(this.renderer.canvasWidth, this.renderer.canvasHeight, 2); + canvasMatrix = m4.scale(canvasMatrix, this.renderer.canvasWidth, -this.renderer.canvasHeight, 1); + canvasMatrix = m4.translate(canvasMatrix, this.renderer.canvasWidth / 2, this.renderer.canvasHeight / 2, 0); + canvasMatrix = m4.scale(canvasMatrix, 1 / this.settings.world.worldScale, 1 / this.settings.world.worldScale, 1); + + this.canvasMatrix = canvasMatrix; + if (this.settings.render.particleBlending) { this.renderer.setBlending(WebGL2RenderingContext.SRC_COLOR, WebGL2RenderingContext.ONE); } else { diff --git a/examples/gravity/settings.js b/examples/gravity/settings.js index 60a458b..913ca92 100644 --- a/examples/gravity/settings.js +++ b/examples/gravity/settings.js @@ -1,5 +1,8 @@ import {AppSettingsBase, SettingsBase, SettingsGroup, Property} from "../common/settings/base.js"; import {ComponentType} from "../common/settings/enum.js"; +import {SolverConfigGroup, SolverSettings} from "../common/settings/solver.js"; +import {DebugConfigGroup} from "../common/settings/debug.js"; +import {RenderConfigGroup} from "../common/settings/renderer.js"; /** * @enum {string} @@ -27,6 +30,12 @@ class ParticleSettings extends SettingsBase { .setName("Particle max size") .setConstraints(0.1, 1000) .setAffects(GravityComponentType.particleSizing), + friction: Property.float("friction", 0.5) + .setName("Friction") + .setConstraints(0, 1), + restitution: Property.float("restitution", 0.2) + .setName("Restitution") + .setConstraints(0, 1), } } @@ -46,7 +55,7 @@ class RenderSettings extends SettingsBase { particleColoring: Property.bool("color", true) .setName("Particle Coloring") .setAffects(GravityComponentType.particleLook), - particleTextureUrl: Property.string("tex", new URL("./sprites/particle.png", import.meta.url)) + particleTextureUrl: Property.string("tex", new URL("./sprites/particle.png", import.meta.url).toString()) .setName("Particle texture url") .setBreaks(GravityComponentType.particleLook), } @@ -83,6 +92,9 @@ class WorldSettings extends SettingsBase { .setConstraints(1, 10000) .setAffects(GravityComponentType.renderer) .setBreaks(GravityComponentType.world), + resistance: Property.float("resistance", 1) + .setName("Resistance") + .setConstraints(0, 1) } } @@ -91,6 +103,10 @@ export class GravityExampleSettings extends AppSettingsBase { particle: SettingsGroup.of(ParticleSettings).setName("Particles"), simulation: SettingsGroup.of(SimulationSettings).setName("Simulation"), render: SettingsGroup.of(RenderSettings).setName("Render"), - world: SettingsGroup.of(WorldSettings).setName("Display"), + world: SettingsGroup.of(WorldSettings).setName("World"), + + ...RenderConfigGroup, + ...SolverConfigGroup, + ...DebugConfigGroup, } } \ No newline at end of file diff --git a/examples/gravity/world.js b/examples/gravity/world.js index 579248b..a5fedd7 100644 --- a/examples/gravity/world.js +++ b/examples/gravity/world.js @@ -16,12 +16,10 @@ export class GravityWorld { /** * @param {Bootstrap} bootstrap * @param {GravityExampleSettings} settings - * @param options */ - constructor(bootstrap, settings, options) { + constructor(bootstrap, settings) { this.bootstrap = bootstrap; this.settings = settings; - this.options = options; this.worldRect = new BoundaryBox( -settings.world.worldScale * bootstrap.renderer.canvasWidth / 2, @@ -92,8 +90,6 @@ export class GravityWorld { const body = new CircleBody(position.x, position.y, 1) .setVelocity(Vector2.fromAngle(Math.random() * Math.PI * 2).scale(this.settings.simulation.gravity)) - .setFriction(this.options.friction) - .setRestitution(this.options.restitution) .setTag("particle"); const renderer = new SpriteObject(body); @@ -110,6 +106,9 @@ export class GravityWorld { for (const particle of this.bootstrap.rigidBodies) { if (particle.tag !== "particle") continue; + particle.setFriction(this.settings.particle.friction) + .setRestitution(this.settings.particle.restitution); + if (updateSizing) { const size = this.settings.particle.minSize + Math.random() * maxSize; particle.radius = size; diff --git a/lib/misc/debug.js b/lib/misc/debug.js index c4e16de..c190817 100644 --- a/lib/misc/debug.js +++ b/lib/misc/debug.js @@ -1,5 +1,6 @@ -import {Collider} from "../physics/collider/base.js"; import {Vector2} from "../utils/vector.js"; +import {Collider} from "../physics/collider/base.js"; +import {m4} from "../render/renderer/webgl/utils/m4.js"; const COLORS = [ "red", "darkkhaki", "blue", "gold", "deepskyblue", "seagreen", "orange", "violet", "navy", "lightcoral", @@ -7,30 +8,44 @@ const COLORS = [ ] export class Debug { + #matrix; + /** @type {Array<{position: Vector2, size: Vector2, color: string, labeled: boolean}>}*/ vectors = []; /** @type {Array<{position: Vector2, color: string, size: number}>}*/ collisions = []; + /** + * @param {DebugSettings} options + */ constructor(options = null) { + this.configure(options); + } + + configure(options = null) { this.showBoundary = options?.showBoundary ?? true; this.showBodies = options?.showBodies ?? true; this.showVectorLength = options?.showVectorLength ?? false; - this.showVector = options?.showVector ?? true; + this.showVectors = options?.showVectors ?? true; this.showPoints = options?.showPoints ?? true; this.showVelocityVector = options?.showVelocityVector ?? true; this.showNormalVector = options?.showNormalVector ?? false; this.showTangentVector = options?.showTangentVector ?? false; this.showContactVector = options?.showContactVector ?? false; this.showWarmVector = options?.showWarmVector ?? false; - this.showTree = options?.showTree ?? false; + this.debugTree = options?.debugTree ?? false; this.showTreeLeafs = options?.showTreeLeafs ?? true; this.showTreeSegments = options?.showTreeSegments ?? false; this.showTreeBoundaryCollision = options?.showTreeBoundaryCollision ?? true; + this.vectorArrowSize = options?.vectorArrowSize ?? 2; this.collisionSize = options?.collisionSize ?? 4; } + setViewMatrix(matrix) { + this.#matrix = matrix ? m4.to2d(matrix) : null; + } + /** * @param {Vector2} position * @param {Vector2} size @@ -38,7 +53,7 @@ export class Debug { * @param {boolean|null} [labeled=null] */ addVector(position, size, color = null, labeled = true) { - if (!this.showVector) { + if (!this.showVectors) { return; } @@ -65,6 +80,16 @@ export class Debug { * @param {SpatialTree} tree */ render(ctx, bodies, tree) { + if (this.#matrix) { + ctx.save(); + ctx.setTransform( + this.#matrix[0], this.#matrix[1], + this.#matrix[3], this.#matrix[4], + this.#matrix[6], this.#matrix[7] + ); + ctx.lineWidth = 2 / Math.max(this.#matrix[0], this.#matrix[4]); + } + if (this.showBoundary) { for (const body of bodies) { ctx.strokeStyle = body.active ? "#007500" : "#394d39"; @@ -73,8 +98,8 @@ export class Debug { } } - ctx.font = "12px serif"; - if (this.showVector) { + ctx.font = "1rem serif"; + if (this.showVectors) { for (const vectorInfo of this.vectors) { ctx.strokeStyle = vectorInfo.color || "green"; ctx.fillStyle = ctx.strokeStyle; @@ -89,9 +114,13 @@ export class Debug { } } - if (this.showTree && tree) { + if (this.debugTree && tree) { this.#drawTreeDebug(ctx, tree); } + + if (this.#matrix) { + ctx.restore(); + } } /** diff --git a/lib/render/renderer/base.js b/lib/render/renderer/base.js index ce086c2..4739579 100644 --- a/lib/render/renderer/base.js +++ b/lib/render/renderer/base.js @@ -76,6 +76,8 @@ export class IRenderer { */ removeObject(obj) {} + clear() {} + /** * @abstract * @param {number} delta diff --git a/lib/render/renderer/canvas/renderer.js b/lib/render/renderer/canvas/renderer.js index 19ec597..07870ed 100644 --- a/lib/render/renderer/canvas/renderer.js +++ b/lib/render/renderer/canvas/renderer.js @@ -54,8 +54,12 @@ export class CanvasRenderer extends IRenderer { } } - render(delta) { + clear() { this.#ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + render(delta) { + this.clear(); this.#objects.sort((r1, r2) => r1.z - r2.z); for (const object of this.#objects) { diff --git a/lib/render/renderer/webgl/renderer.js b/lib/render/renderer/webgl/renderer.js index a731134..798ba90 100644 --- a/lib/render/renderer/webgl/renderer.js +++ b/lib/render/renderer/webgl/renderer.js @@ -40,11 +40,14 @@ export class WebglRenderer extends IRenderer { return this.#bodyToRenderObjectMapping; } + clear() { + this.#gl.clear(this.#gl.COLOR_BUFFER_BIT | this.#gl.DEPTH_BUFFER_BIT); + } + render(delta) { this.#prepare(); - this.#gl.clear(this.#gl.COLOR_BUFFER_BIT | this.#gl.DEPTH_BUFFER_BIT); - + this.clear(); this.#gl.depthMask(true); for (const data of this.#objectsByType.values()) { this.#render(data.key, data.opaque); diff --git a/lib/render/renderer/webgl/utils/m4.js b/lib/render/renderer/webgl/utils/m4.js index 6c401c6..372cbd3 100644 --- a/lib/render/renderer/webgl/utils/m4.js +++ b/lib/render/renderer/webgl/utils/m4.js @@ -234,6 +234,14 @@ export class m4 { ]; } + static to2d(matrix4x4) { + return [ + matrix4x4[0], matrix4x4[1], matrix4x4[3], + matrix4x4[4], matrix4x4[5], matrix4x4[7], + matrix4x4[12], matrix4x4[13], matrix4x4[15] + ]; + } + static transformPoint(lhs, mtx) { return [ (lhs.x * mtx[0 * 4 + 0]) + (lhs.y * mtx[1 * 4 + 0]) + (lhs.z * mtx[2 * 4 + 0]) + (1 * mtx[3 * 4 + 0]),