diff --git a/examples/ts_game.js b/examples/ts_game.js index f0b9ebc..52b21d2 100644 --- a/examples/ts_game.js +++ b/examples/ts_game.js @@ -2,287 +2,41 @@ /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ "./src/examples.ts": -/*!*************************!*\ - !*** ./src/examples.ts ***! - \*************************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.FirstPersonMaze = exports.GameController = void 0; -const systems_1 = __webpack_require__(/*! ./systems */ "./src/systems.ts"); -class GameController extends systems_1.SystemContainer { - constructor() { - super(...arguments); - this.systems = [ - new BasicWindow(), - new FirstPersonMaze() - ]; - } - load() { - super.load(); - this.currentIndex = 0; - this.currentId = this.addSystem(this.systems[this.currentIndex]); - } - update(dt) { - if (isKeyPressed(KEY_RIGHT)) { - this.removeSystem(this.currentId); - this.currentIndex = (this.currentIndex + 1) % this.systems.length; - this.currentId = this.addSystem(this.systems[this.currentIndex]); - } - super.update(dt); - } -} -exports.GameController = GameController; -class BasicWindow extends systems_1.SystemBase { - draw() { - super.draw(); - drawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - } -} -class FirstPersonMaze extends systems_1.SystemBase { - load() { - super.load(); - // Define the camera to look into our 3d world - this.camera = new Camera3D(new Vector3(0.2, 0.4, 0.2), new Vector3(0.185, 0.4, 0.0), new Vector3(0, 1, 0), 45, CAMERA_PERSPECTIVE); - const position = new Vector3(0, 0, 0); // Set model position - const imMap = loadImage("assets/cubicmap.png"); // Load cubicmap image (RAM) - this.cubicmap = loadTextureFromImage(imMap); // Convert image to texture to display (VRAM) - const mesh = genMeshCubicmap(imMap, new Vector3(1.0, 1.0, 1.0)); - this.model = loadModelFromMesh(mesh); - // NOTE: By default each cube is mapped to one part of texture atlas - this.texture = loadTexture("assets/cubicmap_atlas.png"); // Load map texture - //model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture - const mat = loadMaterialDefault(); - setMaterialTexture(mat, MATERIAL_MAP_DIFFUSE, this.texture); - setModelMaterial(this.model, 0, mat); - // Get map image data to be used for collision detection - this.mapPixels = new Uint8Array(loadImageColors(imMap)); - unloadImage(imMap); // Unload image from RAM - this.mapPosition = new Vector3(-16.0, 0.0, -8.0); // Set model position - disableCursor(); - } - update(dt) { - super.update(dt); - let oldCamPos = this.camera.position; // Store old camera position - updateCamera(this.camera, CAMERA_FIRST_PERSON); - // Check player collision (we simplify to 2D collision detection) - const playerPos = new Vector2(this.camera.position.x, this.camera.position.z); - const playerRadius = 0.1; // Collision radius (player is modelled as a cilinder for collision) - this.playerCellX = Math.floor(playerPos.x - this.mapPosition.x + 0.5); - this.playerCellY = Math.floor(playerPos.y - this.mapPosition.z + 0.5); - // Out-of-limits security check - if (this.playerCellX < 0) - this.playerCellX = 0; - else if (this.playerCellX >= this.cubicmap.width) - this.playerCellX = this.cubicmap.width - 1; - if (this.playerCellY < 0) - this.playerCellY = 0; - else if (this.playerCellY >= this.cubicmap.height) - this.playerCellY = this.cubicmap.height - 1; - // Check map collisions using image data and player position - // TODO: Improvement: Just check player surrounding cells for collision - for (let y = 0; y < this.cubicmap.height; y++) { - for (let x = 0; x < this.cubicmap.width; x++) { - const pixelValR = this.mapPixels[((y * this.cubicmap.width + x) * 4)]; - if ((pixelValR == 255) && // Collision: white pixel, only check R channel - (checkCollisionCircleRec(playerPos, playerRadius, new Rectangle(this.mapPosition.x - 0.5 + x * 1.0, this.mapPosition.z - 0.5 + y * 1.0, 1.0, 1.0)))) { - // Collision detected, reset camera position - this.camera.position = oldCamPos; - } - } - } - } - draw() { - super.draw(); - beginMode3D(this.camera); - drawModel(this.model, this.mapPosition, 1.0, WHITE); // Draw maze map - endMode3D(); - drawTextureEx(this.cubicmap, new Vector2(getScreenWidth() - this.cubicmap.width * 4.0 - 20, 20.0), 0.0, 4.0, WHITE); - drawRectangleLines(getScreenWidth() - this.cubicmap.width * 4 - 20, 20, this.cubicmap.width * 4, this.cubicmap.height * 4, GREEN); - // Draw player position radar - drawRectangle(getScreenWidth() - this.cubicmap.width * 4 - 20 + this.playerCellX * 4, 20 + this.playerCellY * 4, 4, 4, RED); - drawFPS(10, 10); - } - unload() { - enableCursor(); - unloadTexture(this.cubicmap); // Unload cubicmap texture - unloadTexture(this.texture); // Unload map texture - unloadModel(this.model); // Unload map model - super.unload(); - } -} -exports.FirstPersonMaze = FirstPersonMaze; - - -/***/ }), - /***/ "./src/game.ts": /*!*********************!*\ !*** ./src/game.ts ***! \*********************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { +/***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Game = void 0; -const systems_1 = __webpack_require__(/*! ./systems */ "./src/systems.ts"); class Game { constructor(width, height, title) { this.width = width; this.height = height; this.title = title; this.clearColor = RAYWHITE; - this.systemHost = new systems_1.SystemHost(); this.quit = false; } run() { initWindow(this.width, this.height, this.title); setTargetFPS(60); + this.load(); while (!(this.quit = windowShouldClose())) { - this.systemHost.loadSystems(); - this.systemHost.updateSystems(); + this.update(); beginDrawing(); clearBackground(this.clearColor); - this.systemHost.drawSystems(); - this.systemHost.unloadSystems(); + this.draw(); endDrawing(); } - this.systemHost.requestShutdown(); + this.unload(); closeWindow(); } - addSystem(system) { - return this.systemHost.addSystem(system); - } - removeSystem(id) { - return this.systemHost.removeSystem(id); - } } exports.Game = Game; -/***/ }), - -/***/ "./src/systems.ts": -/*!************************!*\ - !*** ./src/systems.ts ***! - \************************/ -/***/ ((__unused_webpack_module, exports) => { - - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.SystemHost = exports.SystemContainer = exports.SystemBase = void 0; -class SystemBase { - constructor() { - this.isFinished = false; - } - load() { - this.promise = new Promise((res, rej) => this.complete = res); - } - unload() { - this.complete(); - } - draw() { } - update(dt) { } - stop() { - this.isFinished = true; - } -} -exports.SystemBase = SystemBase; -class SystemContainer extends SystemBase { - constructor() { - super(...arguments); - this.systemHost = new SystemHost(); - } - update(dt) { - this.systemHost.loadSystems(); - this.systemHost.updateSystems(); - } - draw() { - this.systemHost.drawSystems(); - this.systemHost.unloadSystems(); - } - unload() { - this.systemHost.requestShutdown(); - super.unload(); - } - addSystem(system) { - return this.systemHost.addSystem(system); - } - removeSystem(id) { - return this.systemHost.removeSystem(id); - } -} -exports.SystemContainer = SystemContainer; -class SystemHost { - constructor() { - this.systems = new Map(); - this.unloadQueue = new Set(); - this.loadQueue = new Set(); - this.updateOrder = []; - this.updateOrderRev = []; - this.systemPrio = 0; - } - addSystem(system) { - const id = this.systemPrio++; - this.systems.set(id, system); - this.loadQueue.add(id); - return id; - } - removeSystem(id) { - if (this.systems.has(id)) { - this.unloadQueue.add(id); - } - } - refreshUpdateOrder() { - this.updateOrder = Array.from(this.systems.keys()).sort((a, b) => a - b); - this.updateOrderRev = this.updateOrder.reverse(); - } - loadSystems() { - if (this.loadQueue.size === 0) - return; - this.refreshUpdateOrder(); - for (const id of this.updateOrder) { - if (this.loadQueue.has(id)) - this.systems.get(id)?.load(); - } - this.loadQueue.clear(); - } - unloadSystems() { - if (this.unloadQueue.size === 0) - return; - for (const id of this.updateOrderRev) { - if (this.unloadQueue.has(id)) { - this.systems.get(id)?.unload(); - this.systems.delete(id); - } - } - this.refreshUpdateOrder(); - this.unloadQueue.clear(); - } - updateSystems() { - for (const id of this.updateOrder) { - this.systems.get(id)?.update(getFrameTime()); - } - } - drawSystems() { - for (const id of this.updateOrder) { - const sys = this.systems.get(id); - sys?.draw(); - if (sys?.isFinished) - this.unloadQueue.add(id); - } - } - requestShutdown() { - for (const id of this.updateOrderRev) { - this.systems.get(id)?.unload(); - } - } -} -exports.SystemHost = SystemHost; - - /***/ }) /******/ }); @@ -321,44 +75,22 @@ var exports = __webpack_exports__; \**********************/ Object.defineProperty(exports, "__esModule", ({ value: true })); -const examples_1 = __webpack_require__(/*! ./examples */ "./src/examples.ts"); const game_1 = __webpack_require__(/*! ./game */ "./src/game.ts"); -const systems_1 = __webpack_require__(/*! ./systems */ "./src/systems.ts"); -class MySys extends systems_1.SystemBase { - load() { - super.load(); - this.mesh = new Mesh(); - this.mesh.vertexCount = 3; - this.mesh.triangleCount = 1; - const v1 = new Vector3(400, 0, 0); - const v2 = new Vector3(0, 450, 0); - const v3 = new Vector3(800, 450, 0); - this.mesh.indices = new Uint16Array([0, 1, 2]).buffer; - this.mesh.vertices = new Float32Array([ - v1.x, v1.y, v1.z, - v2.x, v2.y, v2.z, - v3.x, v3.y, v3.z - ]).buffer; - // If your forget to upload to GPU draw will segfault - uploadMesh(this.mesh, false); - this.material = loadMaterialDefault(); - this.matrix = matrixIdentity(); - } - update(dt) { - this.matrix = matrixRotateZ(getTime()); - } +class MyGame extends game_1.Game { draw() { - drawMesh(this.mesh, this.material, this.matrix); + throw new Error("Method not implemented."); + } + update() { + throw new Error("Method not implemented."); + } + load() { + throw new Error("Method not implemented."); } unload() { - super.unload(); - unloadMaterial(this.material); - unloadMesh(this.mesh); + throw new Error("Method not implemented."); } } -const game = new game_1.Game(800, 450, "Typescript Game"); -game.addSystem(new examples_1.GameController()); -game.addSystem(new MySys()); +const game = new MyGame(800, 450, "Typescript Game"); game.run(); })(); diff --git a/examples/ts_game/.gitignore b/examples/ts_game/.gitignore index 40b878d..7e9804b 100644 --- a/examples/ts_game/.gitignore +++ b/examples/ts_game/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +main.js \ No newline at end of file diff --git a/examples/ts_game/src/animation.ts b/examples/ts_game/src/animation.ts new file mode 100644 index 0000000..3e7b160 --- /dev/null +++ b/examples/ts_game/src/animation.ts @@ -0,0 +1,33 @@ +import { HasColor } from "./entity" +import { makeUpdateablePromise } from "./game" + +export type easeFunc = (t: number, a: number, b: number, d: number) => number + +export const interpolate = (a: number, b: number, d: number, setter: (v: number) => void, fn: easeFunc) => { + const start = getTime() + return makeUpdateablePromise(() => { + const cur = getTime()-start + if(cur < d){ + setter(fn(cur, a, b, d)) + return false + } else { + setter(b) + return true + } + }) +} + +export const wait = (time: number) => { + const start = getTime() + return makeUpdateablePromise(() => { + const cur = getTime()-start + if(cur < time){ + return false + } else { + return true + } + }) +} + +export const fadeIn = (c: HasColor, time: number, easeFunc = easeLinearNone) => interpolate(0, 1, time, (v) => c.color = fade(c.color, v), easeFunc) +export const fadeOut = (c: HasColor, time: number, easeFunc = easeLinearNone) => interpolate(0, 1, time, (v) => c.color = fade(c.color, 1-v), easeFunc) \ No newline at end of file diff --git a/examples/ts_game/src/entity.ts b/examples/ts_game/src/entity.ts new file mode 100644 index 0000000..fd2c961 --- /dev/null +++ b/examples/ts_game/src/entity.ts @@ -0,0 +1,44 @@ + +export interface HasIdentity { + id: number +} + +export interface Drawable { + draw: (entity: T) => void +} + +export interface Updatable { + update: (entity: T) => void +} + +export interface HasResources { + load: (entity: T) => void + unload: (entity: T) => void +} + +export interface HasPosition { + position: Vector2 +} + +export interface HasColor { + color: Color +} + +export type EntityOf = HasIdentity & Partial> & Partial> & Partial> & T + +let ID = 0 +export const makeEntity = () => ({ + id: ID++ +}) + +export const makePosition = (x = 0, y = 0) => ({ + position: new Vector2(x,y) +}) + +export const makeColorRgb = (r = 255, g = 255, b = 255, a = 255) => ({ + color: new Color(r,g,b,a) +}) + +export const makeColorClone = (c = WHITE) => ({ + color: new Color(c.r,c.g,c.b,c.a) +}) diff --git a/examples/ts_game/src/examples.ts b/examples/ts_game/src/examples.ts deleted file mode 100644 index 18aa033..0000000 --- a/examples/ts_game/src/examples.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { SystemBase, SystemContainer } from "./systems"; - -export class GameController extends SystemContainer { - private currentId!: number - private currentIndex!: number - private systems = [ - new BasicWindow(), - new FirstPersonMaze() - ] - - load(): void { - super.load() - this.currentIndex = 0 - this.currentId = this.addSystem(this.systems[this.currentIndex]) - } - - update(dt: number): void { - if(isKeyPressed(KEY_RIGHT)){ - this.removeSystem(this.currentId) - this.currentIndex = (this.currentIndex+1)%this.systems.length - this.currentId = this.addSystem(this.systems[this.currentIndex]) - } - super.update(dt) - } -} - -class BasicWindow extends SystemBase { - draw(): void { - super.draw() - drawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - } -} - -export class FirstPersonMaze extends SystemBase { - - private camera!: Camera3D; - private playerCellX!: number; - private playerCellY!: number; - private cubicmap!: Texture; - private texture!: Texture; - private mapPixels!: Uint8Array; - private model!: Model; - private mapPosition!: Vector3; - - load(): void { - super.load() - // Define the camera to look into our 3d world - this.camera = new Camera3D(new Vector3(0.2, 0.4, 0.2), new Vector3(0.185, 0.4, 0.0), new Vector3(0, 1, 0), 45, CAMERA_PERSPECTIVE); - const position = new Vector3(0, 0, 0); // Set model position - - const imMap = loadImage("assets/cubicmap.png"); // Load cubicmap image (RAM) - this.cubicmap = loadTextureFromImage(imMap); // Convert image to texture to display (VRAM) - const mesh = genMeshCubicmap(imMap, new Vector3(1.0, 1.0, 1.0)); - this.model = loadModelFromMesh(mesh); - - // NOTE: By default each cube is mapped to one part of texture atlas - this.texture = loadTexture("assets/cubicmap_atlas.png"); // Load map texture - - //model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture - const mat = loadMaterialDefault(); - setMaterialTexture(mat, MATERIAL_MAP_DIFFUSE, this.texture); - setModelMaterial(this.model, 0, mat); - - // Get map image data to be used for collision detection - this.mapPixels = new Uint8Array(loadImageColors(imMap)); - unloadImage(imMap); // Unload image from RAM - - this.mapPosition = new Vector3(-16.0, 0.0, -8.0); // Set model position - - disableCursor(); - } - - update(dt: number): void { - super.update(dt); - let oldCamPos = this.camera.position; // Store old camera position - - updateCamera(this.camera, CAMERA_FIRST_PERSON); - - // Check player collision (we simplify to 2D collision detection) - const playerPos = new Vector2(this.camera.position.x, this.camera.position.z); - const playerRadius = 0.1; // Collision radius (player is modelled as a cilinder for collision) - - this.playerCellX = Math.floor(playerPos.x - this.mapPosition.x + 0.5); - this.playerCellY = Math.floor(playerPos.y - this.mapPosition.z + 0.5); - - // Out-of-limits security check - if (this.playerCellX < 0) - this.playerCellX = 0; - else if (this.playerCellX >= this.cubicmap.width) - this.playerCellX = this.cubicmap.width - 1; - - if (this.playerCellY < 0) - this.playerCellY = 0; - else if (this.playerCellY >= this.cubicmap.height) - this.playerCellY = this.cubicmap.height - 1; - - // Check map collisions using image data and player position - // TODO: Improvement: Just check player surrounding cells for collision - for (let y = 0; y < this.cubicmap.height; y++) { - for (let x = 0; x < this.cubicmap.width; x++) { - const pixelValR = this.mapPixels[((y * this.cubicmap.width + x) * 4)]; - if ((pixelValR == 255) && // Collision: white pixel, only check R channel - (checkCollisionCircleRec(playerPos, playerRadius, new Rectangle( - this.mapPosition.x - 0.5 + x * 1.0, - this.mapPosition.z - 0.5 + y * 1.0, 1.0, 1.0)))) { - // Collision detected, reset camera position - this.camera.position = oldCamPos; - } - } - } - } - - draw(): void { - super.draw() - beginMode3D(this.camera); - drawModel(this.model, this.mapPosition, 1.0, WHITE); // Draw maze map - endMode3D(); - - drawTextureEx(this.cubicmap, new Vector2(getScreenWidth() - this.cubicmap.width * 4.0 - 20, 20.0), 0.0, 4.0, WHITE); - drawRectangleLines(getScreenWidth() - this.cubicmap.width * 4 - 20, 20, this.cubicmap.width * 4, this.cubicmap.height * 4, GREEN); - - // Draw player position radar - drawRectangle(getScreenWidth() - this.cubicmap.width * 4 - 20 + this.playerCellX * 4, 20 + this.playerCellY * 4, 4, 4, RED); - - drawFPS(10, 10); - } - - unload(): void { - enableCursor(); - unloadTexture(this.cubicmap); // Unload cubicmap texture - unloadTexture(this.texture); // Unload map texture - unloadModel(this.model); // Unload map model - super.unload() - } -} diff --git a/examples/ts_game/src/game.ts b/examples/ts_game/src/game.ts index d90c1ea..28a9d17 100644 --- a/examples/ts_game/src/game.ts +++ b/examples/ts_game/src/game.ts @@ -1,8 +1,31 @@ -import { System, SystemHost } from "./systems" +const promiseUpdateList: (()=>boolean)[] = [] -export class Game { - public clearColor = RAYWHITE - private systemHost = new SystemHost() +const dispatchPromises = () => { + for (var i = promiseUpdateList.length - 1; i >= 0; i--) { + const finished = promiseUpdateList[i]() + if (finished) { + promiseUpdateList.splice(i, 1); + } + } +} + +export const makeUpdateablePromise = (updateFn: () => boolean) => { + let resFn: () => void + const promise = new Promise((resolve, reject) => { + resFn = resolve + }); + const update = () => { + const res = updateFn() + if(res) resFn() + return res + } + promiseUpdateList.unshift(update) + return promise +} + + +export abstract class Game { + public clearColor = BLACK private quit = false constructor(public readonly width: number, @@ -13,26 +36,21 @@ export class Game { public run(){ initWindow(this.width,this.height,this.title) setTargetFPS(60) + this.load() while(!(this.quit = windowShouldClose())){ - this.systemHost.loadSystems() - this.systemHost.updateSystems() + dispatchPromises() + this.update() beginDrawing() clearBackground(this.clearColor) - this.systemHost.drawSystems() - this.systemHost.unloadSystems() + this.draw() endDrawing() } - this.systemHost.requestShutdown() + this.unload() closeWindow() } - addSystem(system: System){ - return this.systemHost.addSystem(system) - } - - removeSystem(id: number){ - return this.systemHost.removeSystem(id) - } - - + abstract draw(): void; + abstract update(): void; + abstract load(): void; + abstract unload(): void; } \ No newline at end of file diff --git a/examples/ts_game/src/index.ts b/examples/ts_game/src/index.ts index 1417c85..e756342 100644 --- a/examples/ts_game/src/index.ts +++ b/examples/ts_game/src/index.ts @@ -1,46 +1,47 @@ -import { GameController } from "./examples"; -import { Game } from "./game"; -import { SystemBase, SystemContainer } from "./systems"; +import { fadeIn, fadeOut, wait } from "./animation"; +import { EntityOf } from "./entity"; +import { Game, makeUpdateablePromise } from "./game"; +import { TextEntity, makeTextEntity } from "./text"; -class MySys extends SystemBase { - mesh!: Mesh; - material!: Material - matrix!: Matrix +class MyGame extends Game { + entities: EntityOf[] = [] - load(): void { - super.load() - this.mesh = new Mesh() - this.mesh.vertexCount = 3 - this.mesh.triangleCount = 1 - const v1 = new Vector3(400, 0, 0) - const v2 = new Vector3(0, 450, 0 ) - const v3 = new Vector3(800, 450, 0) - this.mesh.indices = new Uint16Array([0,1,2]).buffer - this.mesh.vertices = new Float32Array([ - v1.x, v1.y, v1.z, - v2.x, v2.y, v2.z, - v3.x, v3.y, v3.z - ]).buffer - // If your forget to upload to GPU draw will segfault - uploadMesh(this.mesh, false) - this.material = loadMaterialDefault() - this.matrix = matrixIdentity() - } - update(dt: number): void { - this.matrix = matrixRotateZ(getTime()) - } draw(): void { - drawMesh(this.mesh, this.material, this.matrix) + for (const entity of this.entities) { + if(entity.draw) entity.draw(entity) + } + } + update(): void { + for (const entity of this.entities) { + if(entity.update) entity.update(entity) + } + } + load(): void { } unload(): void { - super.unload() - unloadMaterial(this.material) - unloadMesh(this.mesh) + } + + addEntity(entity: EntityOf){ + this.entities.push(entity) } } +const game = new MyGame(800,450,"Typescript Game") -const game = new Game(800,450,"Typescript Game") -game.addSystem(new GameController()) -game.addSystem(new MySys()) +const main = async () => { + const text = { + ...makeTextEntity("Welcome to rayjs!"), + size: 80, + position: new Vector2(100, game.height/2 - 40) + } + game.addEntity(text) + await fadeIn(text, 2) + await wait(2) + await fadeOut(text, 2) + text.position.x += 75 + text.text = "This Summer!" + await fadeIn(text, 2) +} + +main() game.run() \ No newline at end of file diff --git a/examples/ts_game/src/systems.ts b/examples/ts_game/src/systems.ts deleted file mode 100644 index 0b5915c..0000000 --- a/examples/ts_game/src/systems.ts +++ /dev/null @@ -1,122 +0,0 @@ -export interface System { - isFinished: boolean; - load(): void; - unload(): void; - draw(): void; - update(dt: number): void; -} - -export abstract class SystemBase implements System { - - public promise!: Promise - private complete!: () => void - public isFinished = false - - load(): void { - this.promise = new Promise((res,rej) => this.complete = res) - } - - unload(): void { - this.complete() - } - draw(): void {} - update(dt: number): void {} - stop(){ - this.isFinished = true - } -} - -export class SystemContainer extends SystemBase { - private systemHost = new SystemHost() - - update(dt: number): void { - this.systemHost.loadSystems() - this.systemHost.updateSystems() - } - - draw(): void { - this.systemHost.drawSystems() - this.systemHost.unloadSystems() - } - - unload(): void { - this.systemHost.requestShutdown() - super.unload() - } - - addSystem(system: System){ - return this.systemHost.addSystem(system) - } - - removeSystem(id: number){ - return this.systemHost.removeSystem(id) - } -} - -export class SystemHost { - private systems = new Map() - private unloadQueue = new Set() - private loadQueue = new Set() - private updateOrder: number[] = [] - private updateOrderRev: number[] = [] - private systemPrio = 0 - - public addSystem(system: System){ - const id = this.systemPrio++ - this.systems.set(id, system) - this.loadQueue.add(id) - return id - } - - public removeSystem(id: number){ - if(this.systems.has(id)) { - this.unloadQueue.add(id) - } - } - - private refreshUpdateOrder(){ - this.updateOrder = Array.from(this.systems.keys()).sort((a, b) => a - b); - this.updateOrderRev = this.updateOrder.reverse() - } - - public loadSystems(){ - if(this.loadQueue.size === 0) return - this.refreshUpdateOrder() - for (const id of this.updateOrder) { - if(this.loadQueue.has(id)) this.systems.get(id)?.load() - } - this.loadQueue.clear() - } - - public unloadSystems(){ - if(this.unloadQueue.size === 0) return - for (const id of this.updateOrderRev) { - if(this.unloadQueue.has(id)) { - this.systems.get(id)?.unload() - this.systems.delete(id) - } - } - this.refreshUpdateOrder() - this.unloadQueue.clear() - } - - public updateSystems(){ - for (const id of this.updateOrder) { - this.systems.get(id)?.update(getFrameTime()) - } - } - - public drawSystems(){ - for (const id of this.updateOrder) { - const sys = this.systems.get(id) - sys?.draw() - if(sys?.isFinished) this.unloadQueue.add(id) - } - } - - public requestShutdown(){ - for (const id of this.updateOrderRev) { - this.systems.get(id)?.unload() - } - } -} \ No newline at end of file diff --git a/examples/ts_game/src/text.ts b/examples/ts_game/src/text.ts new file mode 100644 index 0000000..a74ea6c --- /dev/null +++ b/examples/ts_game/src/text.ts @@ -0,0 +1,34 @@ +import { EntityOf, HasColor, HasPosition, makeColorRgb, makeEntity, makePosition } from "./entity" + +export interface Text extends HasPosition, HasColor { + text: string, + font: Font, + size: number, + spacing: number +} + +export interface TextEntity extends EntityOf { + type: "text" +} + +export const makeText = (text = "", size = 20, font = null, spacing = 1) => ({ + ...makePosition(), + ...makeColorRgb(), + text: text, + font: font === null ? getFontDefault() : loadFont(font), + size: size, + spacing: spacing +}) + +export const textDraw = (t: Text) => { + return drawTextEx(t.font, t.text, t.position, t.size, t.spacing, t.color); +} + +export const makeTextEntity = (text: string = "") => ({ + ...makeEntity(), + ...makeText(text), + type: "text", + draw: textDraw, +}) + + diff --git a/examples/ts_game/webpack.config.js b/examples/ts_game/webpack.config.js index 516246c..45d8f69 100644 --- a/examples/ts_game/webpack.config.js +++ b/examples/ts_game/webpack.config.js @@ -18,7 +18,7 @@ module.exports = { extensions: ['.tsx', '.ts', '.js'], }, output: { - filename: 'ts_game.js', - path: path.resolve(__dirname, '..'), + filename: 'main.js', + path: path.resolve(__dirname, '.'), }, }; diff --git a/test.json b/test.json new file mode 100644 index 0000000..358f6d7 --- /dev/null +++ b/test.json @@ -0,0 +1 @@ +[{"returnType":"float","name":"Clamp","params":[{"name":"value","type":"float"},{"name":"min","type":"float"},{"name":"max","type":"float"}],"description":"Clamp float value"},{"returnType":"float","name":"Lerp","params":[{"name":"start","type":"float"},{"name":"end","type":"float"},{"name":"amount","type":"float"}],"description":"Calculate linear interpolation between two floats"},{"returnType":"float","name":"Normalize","params":[{"name":"value","type":"float"},{"name":"start","type":"float"},{"name":"end","type":"float"}],"description":"Normalize input value within input range"},{"returnType":"float","name":"Remap","params":[{"name":"value","type":"float"},{"name":"inputStart","type":"float"},{"name":"inputEnd","type":"float"},{"name":"outputStart","type":"float"},{"name":"outputEnd","type":"float"}],"description":"Remap input value within input range to output range"},{"returnType":"float","name":"Wrap","params":[{"name":"value","type":"float"},{"name":"min","type":"float"},{"name":"max","type":"float"}],"description":"Wrap input value from min to max"},{"returnType":"int","name":"FloatEquals","params":[{"name":"x","type":"float"},{"name":"y","type":"float"}],"description":"Check whether two given floats are almost equal"},{"returnType":"Vector2","name":"Vector2Zero","params":[],"description":"Vector with components value 0.0f"},{"returnType":"Vector2","name":"Vector2One","params":[],"description":"Vector with components value 1.0f"},{"returnType":"Vector2","name":"Vector2Add","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Add two vectors (v1 + v2)"},{"returnType":"Vector2","name":"Vector2AddValue","params":[{"name":"v","type":"Vector2"},{"name":"add","type":"float"}],"description":"Add vector and float value"},{"returnType":"Vector2","name":"Vector2Subtract","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Subtract two vectors (v1 - v2)"},{"returnType":"Vector2","name":"Vector2SubtractValue","params":[{"name":"v","type":"Vector2"},{"name":"sub","type":"float"}],"description":"Subtract vector by float value"},{"returnType":"float","name":"Vector2Length","params":[{"name":"v","type":"Vector2"}],"description":"Calculate vector length"},{"returnType":"float","name":"Vector2LengthSqr","params":[{"name":"v","type":"Vector2"}],"description":"Calculate vector square length"},{"returnType":"float","name":"Vector2DotProduct","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Calculate two vectors dot product"},{"returnType":"float","name":"Vector2Distance","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Calculate distance between two vectors"},{"returnType":"float","name":"Vector2DistanceSqr","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Calculate square distance between two vectors"},{"returnType":"float","name":"Vector2Angle","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Calculate angle between two vectors\nNOTE: Angle is calculated from origin point (0, 0)"},{"returnType":"float","name":"Vector2LineAngle","params":[{"name":"start","type":"Vector2"},{"name":"end","type":"Vector2"}],"description":"Calculate angle defined by a two vectors line\nNOTE: Parameters need to be normalized\nCurrent implementation should be aligned with glm::angle"},{"returnType":"Vector2","name":"Vector2Scale","params":[{"name":"v","type":"Vector2"},{"name":"scale","type":"float"}],"description":"Scale vector (multiply by value)"},{"returnType":"Vector2","name":"Vector2Multiply","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Multiply vector by vector"},{"returnType":"Vector2","name":"Vector2Negate","params":[{"name":"v","type":"Vector2"}],"description":"Negate vector"},{"returnType":"Vector2","name":"Vector2Divide","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"}],"description":"Divide vector by vector"},{"returnType":"Vector2","name":"Vector2Normalize","params":[{"name":"v","type":"Vector2"}],"description":"Normalize provided vector"},{"returnType":"Vector2","name":"Vector2Transform","params":[{"name":"v","type":"Vector2"},{"name":"mat","type":"Matrix"}],"description":"Transforms a Vector2 by a given Matrix"},{"returnType":"Vector2","name":"Vector2Lerp","params":[{"name":"v1","type":"Vector2"},{"name":"v2","type":"Vector2"},{"name":"amount","type":"float"}],"description":"Calculate linear interpolation between two vectors"},{"returnType":"Vector2","name":"Vector2Reflect","params":[{"name":"v","type":"Vector2"},{"name":"normal","type":"Vector2"}],"description":"Calculate reflected vector to normal"},{"returnType":"Vector2","name":"Vector2Rotate","params":[{"name":"v","type":"Vector2"},{"name":"angle","type":"float"}],"description":"Rotate vector by angle"},{"returnType":"Vector2","name":"Vector2MoveTowards","params":[{"name":"v","type":"Vector2"},{"name":"target","type":"Vector2"},{"name":"maxDistance","type":"float"}],"description":"Move Vector towards target"},{"returnType":"Vector2","name":"Vector2Invert","params":[{"name":"v","type":"Vector2"}],"description":"Invert the given vector"},{"returnType":"Vector2","name":"Vector2Clamp","params":[{"name":"v","type":"Vector2"},{"name":"min","type":"Vector2"},{"name":"max","type":"Vector2"}],"description":"Clamp the components of the vector between\nmin and max values specified by the given vectors"},{"returnType":"Vector2","name":"Vector2ClampValue","params":[{"name":"v","type":"Vector2"},{"name":"min","type":"float"},{"name":"max","type":"float"}],"description":"Clamp the magnitude of the vector between two min and max values"},{"returnType":"int","name":"Vector2Equals","params":[{"name":"p","type":"Vector2"},{"name":"q","type":"Vector2"}],"description":"Check whether two given vectors are almost equal"},{"returnType":"Vector3","name":"Vector3Zero","params":[],"description":"Vector with components value 0.0f"},{"returnType":"Vector3","name":"Vector3One","params":[],"description":"Vector with components value 1.0f"},{"returnType":"Vector3","name":"Vector3Add","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Add two vectors"},{"returnType":"Vector3","name":"Vector3AddValue","params":[{"name":"v","type":"Vector3"},{"name":"add","type":"float"}],"description":"Add vector and float value"},{"returnType":"Vector3","name":"Vector3Subtract","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Subtract two vectors"},{"returnType":"Vector3","name":"Vector3SubtractValue","params":[{"name":"v","type":"Vector3"},{"name":"sub","type":"float"}],"description":"Subtract vector by float value"},{"returnType":"Vector3","name":"Vector3Scale","params":[{"name":"v","type":"Vector3"},{"name":"scalar","type":"float"}],"description":"Multiply vector by scalar"},{"returnType":"Vector3","name":"Vector3Multiply","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Multiply vector by vector"},{"returnType":"Vector3","name":"Vector3CrossProduct","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Calculate two vectors cross product"},{"returnType":"Vector3","name":"Vector3Perpendicular","params":[{"name":"v","type":"Vector3"}],"description":"Calculate one vector perpendicular vector"},{"returnType":"float","name":"Vector3Length","params":[{"name":"v","type":"const Vector3"}],"description":"Calculate vector length"},{"returnType":"float","name":"Vector3LengthSqr","params":[{"name":"v","type":"const Vector3"}],"description":"Calculate vector square length"},{"returnType":"float","name":"Vector3DotProduct","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Calculate two vectors dot product"},{"returnType":"float","name":"Vector3Distance","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Calculate distance between two vectors"},{"returnType":"float","name":"Vector3DistanceSqr","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Calculate square distance between two vectors"},{"returnType":"float","name":"Vector3Angle","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Calculate angle between two vectors"},{"returnType":"Vector3","name":"Vector3Negate","params":[{"name":"v","type":"Vector3"}],"description":"Negate provided vector (invert direction)"},{"returnType":"Vector3","name":"Vector3Divide","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Divide vector by vector"},{"returnType":"Vector3","name":"Vector3Normalize","params":[{"name":"v","type":"Vector3"}],"description":"Normalize provided vector"},{"returnType":"void","name":"Vector3OrthoNormalize","params":[{"name":"v1","type":"Vector3 *"},{"name":"v2","type":"Vector3 *"}],"description":"Orthonormalize provided vectors\nMakes vectors normalized and orthogonal to each other\nGram-Schmidt function implementation"},{"returnType":"Vector3","name":"Vector3Transform","params":[{"name":"v","type":"Vector3"},{"name":"mat","type":"Matrix"}],"description":"Transforms a Vector3 by a given Matrix"},{"returnType":"Vector3","name":"Vector3RotateByQuaternion","params":[{"name":"v","type":"Vector3"},{"name":"q","type":"Quaternion"}],"description":"Transform a vector by quaternion rotation"},{"returnType":"Vector3","name":"Vector3RotateByAxisAngle","params":[{"name":"v","type":"Vector3"},{"name":"axis","type":"Vector3"},{"name":"angle","type":"float"}],"description":"Rotates a vector around an axis"},{"returnType":"Vector3","name":"Vector3Lerp","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"},{"name":"amount","type":"float"}],"description":"Calculate linear interpolation between two vectors"},{"returnType":"Vector3","name":"Vector3Reflect","params":[{"name":"v","type":"Vector3"},{"name":"normal","type":"Vector3"}],"description":"Calculate reflected vector to normal"},{"returnType":"Vector3","name":"Vector3Min","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Get min value for each pair of components"},{"returnType":"Vector3","name":"Vector3Max","params":[{"name":"v1","type":"Vector3"},{"name":"v2","type":"Vector3"}],"description":"Get max value for each pair of components"},{"returnType":"Vector3","name":"Vector3Barycenter","params":[{"name":"p","type":"Vector3"},{"name":"a","type":"Vector3"},{"name":"b","type":"Vector3"},{"name":"c","type":"Vector3"}],"description":"Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c)\nNOTE: Assumes P is on the plane of the triangle"},{"returnType":"Vector3","name":"Vector3Unproject","params":[{"name":"source","type":"Vector3"},{"name":"projection","type":"Matrix"},{"name":"view","type":"Matrix"}],"description":"Projects a Vector3 from screen space into object space\nNOTE: We are avoiding calling other raymath functions despite available"},{"returnType":"float3","name":"Vector3ToFloatV","params":[{"name":"v","type":"Vector3"}],"description":"Get Vector3 as float array"},{"returnType":"Vector3","name":"Vector3Invert","params":[{"name":"v","type":"Vector3"}],"description":"Invert the given vector"},{"returnType":"Vector3","name":"Vector3Clamp","params":[{"name":"v","type":"Vector3"},{"name":"min","type":"Vector3"},{"name":"max","type":"Vector3"}],"description":"Clamp the components of the vector between\nmin and max values specified by the given vectors"},{"returnType":"Vector3","name":"Vector3ClampValue","params":[{"name":"v","type":"Vector3"},{"name":"min","type":"float"},{"name":"max","type":"float"}],"description":"Clamp the magnitude of the vector between two values"},{"returnType":"int","name":"Vector3Equals","params":[{"name":"p","type":"Vector3"},{"name":"q","type":"Vector3"}],"description":"Check whether two given vectors are almost equal"},{"returnType":"Vector3","name":"Vector3Refract","params":[{"name":"v","type":"Vector3"},{"name":"n","type":"Vector3"},{"name":"r","type":"float"}],"description":"Compute the direction of a refracted ray where v specifies the\nnormalized direction of the incoming ray, n specifies the\nnormalized normal vector of the interface of two optical media,\nand r specifies the ratio of the refractive index of the medium\nfrom where the ray comes to the refractive index of the medium\non the other side of the surface"},{"returnType":"float","name":"MatrixDeterminant","params":[{"name":"mat","type":"Matrix"}],"description":"Compute matrix determinant"},{"returnType":"float","name":"MatrixTrace","params":[{"name":"mat","type":"Matrix"}],"description":"Get the trace of the matrix (sum of the values along the diagonal)"},{"returnType":"Matrix","name":"MatrixTranspose","params":[{"name":"mat","type":"Matrix"}],"description":"Transposes provided matrix"},{"returnType":"Matrix","name":"MatrixInvert","params":[{"name":"mat","type":"Matrix"}],"description":"Invert provided matrix"},{"returnType":"Matrix","name":"MatrixIdentity","params":[],"description":"Get identity matrix"},{"returnType":"Matrix","name":"MatrixAdd","params":[{"name":"left","type":"Matrix"},{"name":"right","type":"Matrix"}],"description":"Add two matrices"},{"returnType":"Matrix","name":"MatrixSubtract","params":[{"name":"left","type":"Matrix"},{"name":"right","type":"Matrix"}],"description":"Subtract two matrices (left - right)"},{"returnType":"Matrix","name":"MatrixMultiply","params":[{"name":"left","type":"Matrix"},{"name":"right","type":"Matrix"}],"description":"Get two matrix multiplication\nNOTE: When multiplying matrices... the order matters!"},{"returnType":"Matrix","name":"MatrixTranslate","params":[{"name":"x","type":"float"},{"name":"y","type":"float"},{"name":"z","type":"float"}],"description":"Get translation matrix"},{"returnType":"Matrix","name":"MatrixRotate","params":[{"name":"axis","type":"Vector3"},{"name":"angle","type":"float"}],"description":"Create rotation matrix from axis and angle\nNOTE: Angle should be provided in radians"},{"returnType":"Matrix","name":"MatrixRotateX","params":[{"name":"angle","type":"float"}],"description":"Get x-rotation matrix\nNOTE: Angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixRotateY","params":[{"name":"angle","type":"float"}],"description":"Get y-rotation matrix\nNOTE: Angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixRotateZ","params":[{"name":"angle","type":"float"}],"description":"Get z-rotation matrix\nNOTE: Angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixRotateXYZ","params":[{"name":"angle","type":"Vector3"}],"description":"Get xyz-rotation matrix\nNOTE: Angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixRotateZYX","params":[{"name":"angle","type":"Vector3"}],"description":"Get zyx-rotation matrix\nNOTE: Angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixScale","params":[{"name":"x","type":"float"},{"name":"y","type":"float"},{"name":"z","type":"float"}],"description":"Get scaling matrix"},{"returnType":"Matrix","name":"MatrixFrustum","params":[{"name":"left","type":"double"},{"name":"right","type":"double"},{"name":"bottom","type":"double"},{"name":"top","type":"double"},{"name":"near","type":"double"},{"name":"far","type":"double"}],"description":"Get perspective projection matrix"},{"returnType":"Matrix","name":"MatrixPerspective","params":[{"name":"fovy","type":"double"},{"name":"aspect","type":"double"},{"name":"near","type":"double"},{"name":"far","type":"double"}],"description":"Get perspective projection matrix\nNOTE: Fovy angle must be provided in radians"},{"returnType":"Matrix","name":"MatrixOrtho","params":[{"name":"left","type":"double"},{"name":"right","type":"double"},{"name":"bottom","type":"double"},{"name":"top","type":"double"},{"name":"near","type":"double"},{"name":"far","type":"double"}],"description":"Get orthographic projection matrix"},{"returnType":"Matrix","name":"MatrixLookAt","params":[{"name":"eye","type":"Vector3"},{"name":"target","type":"Vector3"},{"name":"up","type":"Vector3"}],"description":"Get camera look-at matrix (view matrix)"},{"returnType":"float16","name":"MatrixToFloatV","params":[{"name":"mat","type":"Matrix"}],"description":"Get float array of matrix data"},{"returnType":"Quaternion","name":"QuaternionAdd","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"}],"description":"Add two quaternions"},{"returnType":"Quaternion","name":"QuaternionAddValue","params":[{"name":"q","type":"Quaternion"},{"name":"add","type":"float"}],"description":"Add quaternion and float value"},{"returnType":"Quaternion","name":"QuaternionSubtract","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"}],"description":"Subtract two quaternions"},{"returnType":"Quaternion","name":"QuaternionSubtractValue","params":[{"name":"q","type":"Quaternion"},{"name":"sub","type":"float"}],"description":"Subtract quaternion and float value"},{"returnType":"Quaternion","name":"QuaternionIdentity","params":[],"description":"Get identity quaternion"},{"returnType":"float","name":"QuaternionLength","params":[{"name":"q","type":"Quaternion"}],"description":"Computes the length of a quaternion"},{"returnType":"Quaternion","name":"QuaternionNormalize","params":[{"name":"q","type":"Quaternion"}],"description":"Normalize provided quaternion"},{"returnType":"Quaternion","name":"QuaternionInvert","params":[{"name":"q","type":"Quaternion"}],"description":"Invert provided quaternion"},{"returnType":"Quaternion","name":"QuaternionMultiply","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"}],"description":"Calculate two quaternion multiplication"},{"returnType":"Quaternion","name":"QuaternionScale","params":[{"name":"q","type":"Quaternion"},{"name":"mul","type":"float"}],"description":"Scale quaternion by float value"},{"returnType":"Quaternion","name":"QuaternionDivide","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"}],"description":"Divide two quaternions"},{"returnType":"Quaternion","name":"QuaternionLerp","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"},{"name":"amount","type":"float"}],"description":"Calculate linear interpolation between two quaternions"},{"returnType":"Quaternion","name":"QuaternionNlerp","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"},{"name":"amount","type":"float"}],"description":"Calculate slerp-optimized interpolation between two quaternions"},{"returnType":"Quaternion","name":"QuaternionSlerp","params":[{"name":"q1","type":"Quaternion"},{"name":"q2","type":"Quaternion"},{"name":"amount","type":"float"}],"description":"Calculates spherical linear interpolation between two quaternions"},{"returnType":"Quaternion","name":"QuaternionFromVector3ToVector3","params":[{"name":"from","type":"Vector3"},{"name":"to","type":"Vector3"}],"description":"Calculate quaternion based on the rotation from one vector to another"},{"returnType":"Quaternion","name":"QuaternionFromMatrix","params":[{"name":"mat","type":"Matrix"}],"description":"Get a quaternion for a given rotation matrix"},{"returnType":"Matrix","name":"QuaternionToMatrix","params":[{"name":"q","type":"Quaternion"}],"description":"Get a matrix for a given quaternion"},{"returnType":"Quaternion","name":"QuaternionFromAxisAngle","params":[{"name":"axis","type":"Vector3"},{"name":"angle","type":"float"}],"description":"Get rotation quaternion for an angle and axis\nNOTE: Angle must be provided in radians"},{"returnType":"void","name":"QuaternionToAxisAngle","params":[{"name":"q","type":"Quaternion"},{"name":"outAxis","type":"Vector3 *"},{"name":"outAngle","type":"float *"}],"description":"Get the rotation angle and axis for a given quaternion"},{"returnType":"Quaternion","name":"QuaternionFromEuler","params":[{"name":"pitch","type":"float"},{"name":"yaw","type":"float"},{"name":"roll","type":"float"}],"description":"Get the quaternion equivalent to Euler angles\nNOTE: Rotation order is ZYX"},{"returnType":"Vector3","name":"QuaternionToEuler","params":[{"name":"q","type":"Quaternion"}],"description":"Get the Euler angles equivalent to quaternion (roll, pitch, yaw)\nNOTE: Angles are returned in a Vector3 struct in radians"},{"returnType":"Quaternion","name":"QuaternionTransform","params":[{"name":"q","type":"Quaternion"},{"name":"mat","type":"Matrix"}],"description":"Transform a quaternion given a transformation matrix"},{"returnType":"int","name":"QuaternionEquals","params":[{"name":"p","type":"Quaternion"},{"name":"q","type":"Quaternion"}],"description":"Check whether two given quaternions are almost equal"}] \ No newline at end of file