Skip to content

Instantly share code, notes, and snippets.

@McNull
Last active January 4, 2024 10:48
Show Gist options
  • Save McNull/0a043285f5c88c533b8b43b1dbcc8915 to your computer and use it in GitHub Desktop.
Save McNull/0a043285f5c88c533b8b43b1dbcc8915 to your computer and use it in GitHub Desktop.
three.js utils
export class ArrayPool<T> {
private _array: T[];
private _length: number;
private _capacity: number;
private _factory: () => T;
constructor(initialCapacity: number = 10, factory: () => T) {
this._array = new Array<T>(initialCapacity);
this._length = 0;
this._capacity = initialCapacity;
this._factory = factory;
this.fill(0, initialCapacity);
}
private fill(from: number, to: number, array: T[] = this._array) {
for (let i = from; i < to; i++) {
array[i] = this._factory();
}
}
set length(value: number) {
if (value > this._capacity) {
const newCapacity = Math.max(this._capacity * 2, value);
console.log(`Resizing array from ${this._capacity} to ${newCapacity}`);
const newArray = new Array<T>(newCapacity);
for (let i = 0; i < this._capacity; i++) {
newArray[i] = this._array[i];
}
this.fill(this._capacity, newCapacity, newArray);
this._array = newArray;
this._capacity = newCapacity;
}
this._length = value;
}
get length(): number {
return this._length;
}
get capacity(): number {
return this._capacity;
}
get(index: number): T {
if (index < 0 || index >= this._length) {
throw new Error(`Index out of bounds: ${index}`);
}
return this._array[index];
}
claim(): T {
this.length++;
return this._array[this._length - 1];
}
forEach(callback: (value: T, index: number) => boolean | void) {
for (let i = 0; i < this._length; i++) {
if(callback(this._array[i], i) === false) {
break;
}
}
}
}
import * as THREE from "three";
import { OrbitControls as _OrbitControls } from "three/examples/jsm/Addons.js";
const STORAGE_KEY = 'orbit-controls-state';
export class OrbitControls extends _OrbitControls {
constructor(object: THREE.Camera, domElement?: HTMLElement) {
super(object, domElement);
}
init() {
this.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.PAN,
RIGHT: THREE.MOUSE.DOLLY
};
this.zoomSpeed = -2.0;
this.loadState();
window.addEventListener("beforeunload", () => {
this.persistState();
});
}
loadState() {
if(localStorage.getItem(STORAGE_KEY)) {
const cs = JSON.parse(localStorage.getItem(STORAGE_KEY));
this.target0.set(cs.target0.x, cs.target0.y, cs.target0.z);
this.position0.set(cs.position0.x, cs.position0.y, cs.position0.z);
this.zoom0 = cs.zoom0;
this.reset();
};
}
persistState() {
this.saveState();
const { target0, position0, zoom0 } = this;
const cs = { target0, position0, zoom0 };
localStorage.setItem(STORAGE_KEY, JSON.stringify(cs));
}
clearState() {
localStorage.removeItem(STORAGE_KEY);
}
}
export class Random {
static X = 2**31-1;
static Y = 16807;
static Z = 1/(Random.X-1);
constructor(seed: number = 1234567890) {
this.seed = seed;
}
next(min = 0.0, max = 1.0) : number {
this._seed = this._seed * Random.Y % Random.X;
const r = (this._seed - 1) * Random.Z;
return min + r * (max - min);
}
nextInt(min = 0, max = 1) : number {
return Math.floor(this.next(min, max));
}
private _seed: number;
set seed(value: number) {
this._seed = value % Random.X;
if (this._seed <= 0) {
this._seed += Random.X - 1;
}
}
get seed() : number {
return this._seed;
}
}
export class Timer {
startTime: number = 0;
deltaTime: number = 0;
elapsedTime: number = 0;
fps: number = 0;
frames: number = 0;
private _lastTime: number = 0;
private _lastFpsTime: number = 0;
private _fpsFrameCount: number = 0;
private _pauseTime: number = 0;
private _pauseStartTime: number = 0;
private _paused: boolean = false;
get paused() : boolean {
return this._paused;
}
set paused(value: boolean) {
if(value !== this._paused) {
this._paused = value;
if(this._paused) {
// pause the timer
this._pauseStartTime = performance.now() * 0.001;
this.deltaTime = 0;
} else {
// resume the timer
const now = performance.now() * 0.001;
this._pauseTime += now - this._pauseStartTime;
}
}
}
start() {
this.reset();
const now = performance.now() * 0.001;
this.startTime = now;
this._lastTime = now;
this._lastFpsTime = now;
}
update() {
if(this._paused) {
return;
}
const now = performance.now() * 0.001 - this._pauseTime;
this.deltaTime = now - this._lastTime;
this.elapsedTime = now - this.startTime;
this._lastTime = now;
this.frames++;
this._fpsFrameCount++;
if (now - this._lastFpsTime >= 1.0) {
this.fps = this._fpsFrameCount / (now - this._lastFpsTime);
this._lastFpsTime = now;
this._fpsFrameCount = 0;
}
}
reset() {
this.startTime = 0;
this.deltaTime = 0;
this.elapsedTime = 0;
this.fps = 0;
this.frames = 0;
this._lastTime = 0;
this._lastFpsTime = 0;
this._fpsFrameCount = 0;
this._pauseTime = 0;
this._pauseStartTime = 0;
this._paused = false;
}
}
export function pauseOnInactiveTab(timer: Timer) {
document.addEventListener("visibilitychange", () => {
timer.paused = document.hidden;
});
}
export class Vec2 {
constructor(public x: number = 0, public y: number = 0) {}
clone(out: Vec2 = new Vec2()): Vec2 {
out.x = this.x;
out.y = this.y;
return out;
}
set(x: number, y: number): Vec2 {
this.x = x;
this.y = y;
return this;
}
add(v: Vec2, out: Vec2 = this): Vec2 {
out.x = this.x + v.x;
out.y = this.y + v.y;
return out;
}
subtract(v: Vec2, out: Vec2 = this): Vec2 {
out.x = this.x - v.x;
out.y = this.y - v.y;
return out;
}
scale(s: number, out: Vec2 = this): Vec2 {
out.x = this.x * s;
out.y = this.y * s;
return out;
}
dot(v: Vec2): number {
return this.x * v.x + this.y * v.y;
}
sqrLength(): number {
return this.dot(this);
}
length(): number {
return Math.sqrt(this.sqrLength());
}
normalize(out: Vec2 = this): Vec2 {
const len = this.length();
if (len > 0) {
out.scale(1 / len);
}
return out;
}
sqrDistance(v: Vec2): number {
const dx = this.x - v.x;
const dy = this.y - v.y;
return dx * dx + dy * dy;
}
distance(v: Vec2): number {
return Math.sqrt(this.sqrDistance(v));
}
negate(out: Vec2 = this): Vec2 {
out.x = -this.x;
out.y = -this.y;
return out;
}
lerp(v: Vec2, t: number, out: Vec2 = this): Vec2 {
const ax = this.x;
const ay = this.y;
out.x = ax + t * (v.x - ax);
out.y = ay + t * (v.y - ay);
return out;
}
toString(): string {
return `(${this.x}, ${this.y})`;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment