Skip to content

Instantly share code, notes, and snippets.

View Ctrlmonster's full-sized avatar

Brian Breiholz Ctrlmonster

View GitHub Profile
@Ctrlmonster
Ctrlmonster / app.ts
Created March 22, 2025 11:37
koota debug overlay setup
import { Canvas } from '@react-three/fiber';
import { useActions, useQuery, useTraitEffect, useWorld } from 'koota/react';
import { useLayoutEffect, useRef, useState } from 'react';
import { SceneInfo, Transforms } from '../ecs/base-traits';
import { worldActions } from '../ecs';
import { SceneContainer } from './scene-container';
import { GameUiContainer } from './game-ui/game-ui-container';
export default function App() {
const world = useWorld();
import {MeshoptSimplifier, MeshoptEncoder} from "meshoptimizer";
import {BufferAttribute, BufferGeometry, Mesh} from "three";
await MeshoptSimplifier.ready; // await wasm promise
await MeshoptEncoder.ready; // await wasm promise
MeshoptSimplifier.useExperimentalFeatures = true;
export function createLOD_Levels(originalMesh: Mesh, simplificationLevel: number[]) {
import {
Group,
AmbientLight,
CubeCamera,
HalfFloatType,
Mesh,
MeshStandardMaterial,
Texture,
Vector3,
WebGLCubeRenderTarget,
// the returned function will return true at regular intervals p% of the time it gets called.
// use instead of (i % N === 0) checks, whenever other frequencies other than (1 / someInteger) are needed.
// as accumulated values carry over, we can change the targetd frequency at runtime.
const createFrequencyCheck = () => {
let sum = 0
return (p: number) => {
sum += p
if (sum >= 1) {
sum = sum - Math.trunc(sum)
return true

Design Goals:

  • Minimizes risk of forgetting dependencies
  • Allows the user to make their mental model easy
  • Harmonizes with ecosystem

Intro

As a user I never have the full graph in my head. Usually I only picture "islands of order" in my head that relate to the current part/feature of the game I'm working on. With a gowing list of system the risk that

@Ctrlmonster
Ctrlmonster / getPointerPosition.ts
Last active May 21, 2024 11:36
Get the pointer position with the smallest amount of input lag using `getCoalescedEvents` and `getPredictedEvents`. This is useful when syncing elements to cursor position (i.e. in a drag event)
const getPointerPosition = (() => {
const testEvent = new PointerEvent("pointermove");
// check for the existence of getCoalescedEvents and getPredictedEvents (Safari currently has both in tech preview)
const _pos = {x: 0, y: 0};
if (testEvent.getCoalescedEvents && testEvent.getCoalescedEvents) {
return (evt: PointerEvent, pos: {x: number, y: number} = _pos) => {
evt.stopPropagation();
// using the first prediction (the latest seems too chaotic when the cursor comes to a halt)
const latestPrediction = evt.getPredictedEvents().shift();
@Ctrlmonster
Ctrlmonster / SparseSet.ts
Last active May 29, 2024 11:20
Data structure for (Key, Value) pairs that keeps keys and values available in contiguous arrays
/**
* A class to store key value pairs while keeping
* both available in contiguous arrays. Basically
* always prefer a Map over this, unless you regularly
* need to create arrays from the values or keys
* (i.e. inside an update loop). Is strict about
* duplicate or non-existing keys.
*
* - Does not keep insertion order!
* - Throws error when using .set() with existing keys (use .mutate() instead)
@Ctrlmonster
Ctrlmonster / WeakCollection.ts
Last active July 13, 2023 16:43
Collection of Weakrefs that can be iterated
class WeakCollection<T extends object> {
#registry = new FinalizationRegistry((heldValue: WeakRef<T>) => {
this.delete(heldValue);
});
#refs: WeakRef<T>[] = [];
#values: T[] = [];
#clean() {
let index = 0;
@Ctrlmonster
Ctrlmonster / extract-root-motion.ts
Last active January 17, 2024 08:55
extract the root motion from a three.js AnimationClip
// Adapted from https://github.com/donmccurdy/three.js/blob/feat/extract-root-motion/examples/jsm/utils/SkeletonUtils.js#L606
import {AnimationClip, Bone, BufferAttribute, Object3D, Quaternion, Vector3, Vector4} from "three";
function getRootBone(rootNode: Object3D) {
const rootBones: Bone[] = [];
rootNode.traverse(function (object) {
// @ts-ignore
@Ctrlmonster
Ctrlmonster / makeContinuable.ts
Created January 6, 2023 18:45
small helper function to promisify via callbacks
export type Work = (...args: any[]) => any;
// Pass a function that provides a callback as single argument and returns another function F that calls that
// callback at some point. The result of makeContinuable will be a function that returns a promise that will
// resolve to the return value of F, once the callback is called.
export function makeContinuable<F extends Work>(wrapperFn: (cb: () => void) => F) {
return async (...args: Parameters<F>) => {
const outerPromise = new Promise<ReturnType<F>>(async (resolve1) => {
let res = null as ReturnType<F>;
// create another promise in whose executioner we'll call the passed function