diff mrjunejune/src/public/raylib.js @ 100:65e5a5b89a4e

[Seobeo] Migrated everything to this page.
author June Park <parkjune1995@gmail.com>
date Sat, 03 Jan 2026 07:48:07 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/src/public/raylib.js	Sat Jan 03 07:48:07 2026 -0800
@@ -0,0 +1,612 @@
+/**
+ * I Stole this from https://github.com/tsoding/c3-demo/blob/main/raylib.js and added few functions for functionality.
+ * It seems to be from raylib.com/examples
+ */
+function make_environment(env) {
+  return new Proxy(env, {
+    get(_target, prop, _receiver) {
+      if (env[prop] !== undefined) {
+        return env[prop].bind(env);
+      }
+      return (...args) => {
+        throw new Error(`NOT IMPLEMENTED: ${prop} ${args}`);
+      };
+    },
+  });
+}
+
+let iota = 0;
+const LOG_ALL = iota++; // Display all logs
+const LOG_TRACE = iota++; // Trace logging, intended for internal use only
+const LOG_DEBUG = iota++; // Debug logging, used for internal debugging, it should be disabled on release builds
+const LOG_INFO = iota++; // Info logging, used for program execution info
+const LOG_WARNING = iota++; // Warning logging, used on recoverable failures
+const LOG_ERROR = iota++; // Error logging, used on unrecoverable failures
+const LOG_FATAL = iota++; // Fatal logging, used to abort program: exit(EXIT_FAILURE)
+const LOG_NONE = iota++; // Disable logging
+
+class RaylibJs {
+  #FONT_SCALE_MAGIC = 0.65;
+
+  #reset() {
+    this.previous = undefined;
+    this.wasm = undefined;
+    this.ctx = undefined;
+    this.dt = undefined;
+    this.targetFPS = 60;
+    this.entryFunction = undefined;
+    this.prevPressedKeyState = new Set();
+    this.currentPressedKeyState = new Set();
+    this.currentMouseWheelMoveState = 0;
+    /* -1 since 0 is left click, 1 middle, 2 is right click */
+    this.currentIsMouseButtonPressed = -1;
+    this.currentMousePosition = { x: 0, y: 0 };
+    this.images = [];
+    this.quit = false;
+  }
+
+  constructor() {
+    this.#reset();
+  }
+
+  stop() {
+    this.quit = true;
+  }
+
+  async start({ wasmPath, canvasId }) {
+    if (this.wasm !== undefined) {
+      console.error("The game is already running. Please stop() it first.");
+      return;
+    }
+
+    const canvas = document.getElementById(canvasId);
+    this.ctx = canvas.getContext("2d");
+    if (this.ctx === null) {
+      throw new Error("Could not create 2d canvas context");
+    }
+
+    this.wasm = await WebAssembly.instantiateStreaming(fetch(wasmPath), {
+      env: make_environment(this),
+    });
+
+    const keyDown = (e) => {
+      this.currentPressedKeyState.add(glfwKeyMapping[e.code]);
+    };
+    const keyUp = (e) => {
+      this.currentPressedKeyState.delete(glfwKeyMapping[e.code]);
+    };
+    const wheelMove = (e) => {
+      this.currentMouseWheelMoveState = Math.sign(-e.deltaY);
+    };
+    const mouseMove = (e) => {
+      this.currentMousePosition = { x: e.clientX, y: e.clientY };
+    };
+    const mouseClick = (e) => {
+      this.currentIsMouseButtonPressed = e.button;
+    };
+    const touchStarted = () => {
+      this.currentIsMouseButtonPressed = 0;
+    };
+    const touchOrClickEnded = () => {
+      this.currentIsMouseButtonPressed = -1;
+    };
+    window.addEventListener("keydown", keyDown);
+    window.addEventListener("keyup", keyUp);
+    window.addEventListener("wheel", wheelMove);
+    window.addEventListener("mousemove", mouseMove);
+    window.addEventListener("mousedown", mouseClick);
+    window.addEventListener("mouseup", touchOrClickEnded);
+
+    /* For phones */
+    window.addEventListener("touchstart", touchStarted);
+    window.addEventListener("touchend", touchOrClickEnded);
+    window.addEventListener("touchcancel", touchOrClickEnded);
+
+    this.wasm.instance.exports.main();
+    const next = (timestamp) => {
+      if (this.quit) {
+        this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+        window.removeEventListener("keydown", keyDown);
+        this.#reset();
+        return;
+      }
+      this.dt = (timestamp - this.previous) / 1000.0;
+      this.previous = timestamp;
+      this.entryFunction();
+      window.requestAnimationFrame(next);
+    };
+    window.requestAnimationFrame((timestamp) => {
+      this.previous = timestamp;
+      window.requestAnimationFrame(next);
+    });
+  }
+
+  InitWindow(width, height, title_ptr) {
+    this.ctx.canvas.width = width;
+    this.ctx.canvas.height = height;
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    document.title = cstr_by_ptr(buffer, title_ptr);
+  }
+
+  WindowShouldClose() {
+    return false;
+  }
+
+  SetTargetFPS(fps) {
+    console.log(
+      `The game wants to run at ${fps} FPS, but in Web we gonna just ignore it.`,
+    );
+    this.targetFPS = fps;
+  }
+
+  GetScreenWidth() {
+    return this.ctx.canvas.width;
+  }
+
+  GetScreenHeight() {
+    return this.ctx.canvas.height;
+  }
+
+  GetFrameTime() {
+    // TODO: This is a stopgap solution to prevent sudden jumps in dt when the user switches to a differen tab.
+    // We need a proper handling of Target FPS here.
+    return Math.min(this.dt, 1.0 / this.targetFPS);
+  }
+
+  BeginDrawing() {}
+
+  EndDrawing() {
+    this.prevPressedKeyState.clear();
+    this.prevPressedKeyState = new Set(this.currentPressedKeyState);
+    this.currentMouseWheelMoveState = 0.0;
+  }
+
+  DrawCircleV(center_ptr, radius, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [x, y] = new Float32Array(buffer, center_ptr, 2);
+    const [r, g, b, a] = new Uint8Array(buffer, color_ptr, 4);
+    const color = color_hex_unpacked(r, g, b, a);
+    this.ctx.beginPath();
+    this.ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+    this.ctx.fillStyle = color;
+    this.ctx.fill();
+  }
+
+  ClearBackground(color_ptr) {
+    this.ctx.fillStyle = getColorFromMemory(
+      this.wasm.instance.exports.memory.buffer,
+      color_ptr,
+    );
+    this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+  }
+
+  // RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color);       // Draw text (using default font)
+  DrawText(text_ptr, posX, posY, fontSize, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    const color = getColorFromMemory(buffer, color_ptr);
+    fontSize *= this.#FONT_SCALE_MAGIC;
+    this.ctx.fillStyle = color;
+    // TODO: since the default font is part of Raylib the css that defines it should be located in raylib.js and not in index.html
+    this.ctx.font = `${fontSize}px grixel`;
+
+    const lines = text.split("\n");
+    for (var i = 0; i < lines.length; i++) {
+      this.ctx.fillText(lines[i], posX, posY + fontSize + i * fontSize);
+    }
+  }
+
+  // RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color);                        // Draw a color-filled rectangle
+  DrawRectangle(posX, posY, width, height, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const color = getColorFromMemory(buffer, color_ptr);
+    this.ctx.fillStyle = color;
+    this.ctx.fillRect(posX, posY, width, height);
+  }
+
+  DrawRectangleV(position_ptr, size_ptr, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const color = getColorFromMemory(buffer, color_ptr);
+    const position = new Float32Array(buffer, position_ptr, 2);
+    const size = new Float32Array(buffer, size_ptr, 2);
+    this.ctx.fillStyle = color;
+    this.ctx.fillRect(position[0], position[1], size[0], size[1]);
+  }
+
+  IsKeyPressed(key) {
+    return (
+      !this.prevPressedKeyState.has(key) && this.currentPressedKeyState.has(key)
+    );
+  }
+  IsKeyDown(key) {
+    return this.currentPressedKeyState.has(key);
+  }
+  GetMouseWheelMove() {
+    return this.currentMouseWheelMoveState;
+  }
+  IsGestureDetected() {
+    return false;
+  }
+  IsMouseButtonPressed(key) {
+    return this.currentIsMouseButtonPressed == key;
+  }
+
+  TextFormat(...args) {
+    // TODO: Implement printf style formatting for TextFormat
+    return args[0];
+  }
+
+  TraceLog(logLevel, text_ptr, ...args) {
+    // TODO: Implement printf style formatting for TraceLog
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    switch (logLevel) {
+      case LOG_ALL:
+        console.log(`ALL: ${text} ${args}`);
+        break;
+      case LOG_TRACE:
+        console.log(`TRACE: ${text} ${args}`);
+        break;
+      case LOG_DEBUG:
+        console.log(`DEBUG: ${text} ${args}`);
+        break;
+      case LOG_INFO:
+        console.log(`INFO: ${text} ${args}`);
+        break;
+      case LOG_WARNING:
+        console.log(`WARNING: ${text} ${args}`);
+        break;
+      case LOG_ERROR:
+        console.log(`ERROR: ${text} ${args}`);
+        break;
+      case LOG_FATAL:
+        throw new Error(`FATAL: ${text}`);
+      case LOG_NONE:
+        console.log(`NONE: ${text} ${args}`);
+        break;
+    }
+  }
+
+  GetMousePosition(result_ptr) {
+    const bcrect = this.ctx.canvas.getBoundingClientRect();
+    const x = this.currentMousePosition.x - bcrect.left;
+    const y = this.currentMousePosition.y - bcrect.top;
+
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    new Float32Array(buffer, result_ptr, 2).set([x, y]);
+  }
+
+  CheckCollisionPointRec(point_ptr, rec_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [x, y] = new Float32Array(buffer, point_ptr, 2);
+    const [rx, ry, rw, rh] = new Float32Array(buffer, rec_ptr, 4);
+    return x >= rx && x <= rx + rw && y >= ry && y <= ry + rh;
+  }
+
+  Fade(result_ptr, color_ptr, alpha) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [r, g, b, _] = new Uint8Array(buffer, color_ptr, 4);
+    const newA = Math.max(0, Math.min(255, 255.0 * alpha));
+    new Uint8Array(buffer, result_ptr, 4).set([r, g, b, newA]);
+  }
+
+  DrawRectangleRec(rec_ptr, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [x, y, w, h] = new Float32Array(buffer, rec_ptr, 4);
+    const color = getColorFromMemory(buffer, color_ptr);
+    this.ctx.fillStyle = color;
+    this.ctx.fillRect(x, y, w, h);
+  }
+
+  DrawRectangleLinesEx(rec_ptr, lineThick, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [x, y, w, h] = new Float32Array(buffer, rec_ptr, 4);
+    const color = getColorFromMemory(buffer, color_ptr);
+    this.ctx.strokeStyle = color;
+    this.ctx.lineWidth = lineThick;
+    this.ctx.strokeRect(
+      x + lineThick / 2,
+      y + lineThick / 2,
+      w - lineThick,
+      h - lineThick,
+    );
+  }
+
+  MeasureText(text_ptr, fontSize) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    fontSize *= this.#FONT_SCALE_MAGIC;
+    this.ctx.font = `${fontSize}px grixel`;
+    return this.ctx.measureText(text).width;
+  }
+
+  TextSubtext(text_ptr, position, length) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    const subtext = text.substring(position, length);
+
+    var bytes = new Uint8Array(buffer, 0, subtext.length + 1);
+    for (var i = 0; i < subtext.length; i++) {
+      bytes[i] = subtext.charCodeAt(i);
+    }
+    bytes[subtext.length] = 0;
+
+    return bytes;
+  }
+
+  // RLAPI Texture2D LoadTexture(const char *fileName);
+  LoadTexture(result_ptr, filename_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const filename = cstr_by_ptr(buffer, filename_ptr);
+
+    var result = new Uint32Array(buffer, result_ptr, 5);
+    var img = new Image();
+    const isLocalhost = window.location.hostname === "localhost";
+    const baseUrl = isLocalhost
+      ? "http://localhost:6969/"
+      : `https://${window.location.hostname}/`;
+    img.src = `${baseUrl}${filename}`;
+    this.images.push(img);
+
+    result[0] = this.images.indexOf(img);
+    // TODO: get the true width and height of the image
+    result[1] = 256; // width
+    result[2] = 256; // height
+    result[3] = 1; // mipmaps
+    result[4] = 7; // format PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
+
+    return result;
+  }
+
+  // RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint);
+  DrawTexture(texture_ptr, posX, posY, color_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const [id, width, height, mipmaps, format] = new Uint32Array(
+      buffer,
+      texture_ptr,
+      5,
+    );
+    // // TODO: implement tinting for DrawTexture
+    // const tint = getColorFromMemory(buffer, color_ptr);
+
+    this.ctx.drawImage(this.images[id], posX, posY);
+  }
+
+  // TODO: codepoints are not implemented
+  LoadFontEx(
+    result_ptr,
+    fileName_ptr /*, fontSize, codepoints, codepointCount*/,
+  ) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const fileName = cstr_by_ptr(buffer, fileName_ptr);
+    // TODO: dynamically generate the name for the font
+    // Support more than one custom font
+    const font = new FontFace("myfont", `url(${fileName})`);
+    document.fonts.add(font);
+    font.load();
+  }
+
+  GenTextureMipmaps() {}
+  SetTextureFilter() {}
+
+  MeasureTextEx(result_ptr, font, text_ptr, fontSize, spacing) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    const result = new Float32Array(buffer, result_ptr, 2);
+    this.ctx.font = fontSize + "px myfont";
+    const metrics = this.ctx.measureText(text);
+    result[0] = metrics.width;
+    result[1] = fontSize;
+  }
+
+  DrawTextEx(font, text_ptr, position_ptr, fontSize, spacing, tint_ptr) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const text = cstr_by_ptr(buffer, text_ptr);
+    const [posX, posY] = new Float32Array(buffer, position_ptr, 2);
+    const tint = getColorFromMemory(buffer, tint_ptr);
+    this.ctx.fillStyle = tint;
+    this.ctx.font = fontSize + "px myfont";
+    this.ctx.fillText(text, posX, posY + fontSize);
+  }
+
+  GetRandomValue(min, max) {
+    return min + Math.floor(Math.random() * (max - min + 1));
+  }
+
+  ColorFromHSV(result_ptr, hue, saturation, value) {
+    const buffer = this.wasm.instance.exports.memory.buffer;
+    const result = new Uint8Array(buffer, result_ptr, 4);
+
+    // Red channel
+    let k = (5.0 + hue / 60.0) % 6;
+    let t = 4.0 - k;
+    k = t < k ? t : k;
+    k = k < 1 ? k : 1;
+    k = k > 0 ? k : 0;
+    result[0] = Math.floor((value - value * saturation * k) * 255.0);
+
+    // Green channel
+    k = (3.0 + hue / 60.0) % 6;
+    t = 4.0 - k;
+    k = t < k ? t : k;
+    k = k < 1 ? k : 1;
+    k = k > 0 ? k : 0;
+    result[1] = Math.floor((value - value * saturation * k) * 255.0);
+
+    // Blue channel
+    k = (1.0 + hue / 60.0) % 6;
+    t = 4.0 - k;
+    k = t < k ? t : k;
+    k = k < 1 ? k : 1;
+    k = k > 0 ? k : 0;
+    result[2] = Math.floor((value - value * saturation * k) * 255.0);
+
+    result[3] = 255;
+  }
+
+  raylib_js_set_entry(entry) {
+    this.entryFunction =
+      this.wasm.instance.exports.__indirect_function_table.get(entry);
+  }
+}
+
+const glfwKeyMapping = {
+  Space: 32,
+  Quote: 39,
+  Comma: 44,
+  Minus: 45,
+  Period: 46,
+  Slash: 47,
+  Digit0: 48,
+  Digit1: 49,
+  Digit2: 50,
+  Digit3: 51,
+  Digit4: 52,
+  Digit5: 53,
+  Digit6: 54,
+  Digit7: 55,
+  Digit8: 56,
+  Digit9: 57,
+  Semicolon: 59,
+  Equal: 61,
+  KeyA: 65,
+  KeyB: 66,
+  KeyC: 67,
+  KeyD: 68,
+  KeyE: 69,
+  KeyF: 70,
+  KeyG: 71,
+  KeyH: 72,
+  KeyI: 73,
+  KeyJ: 74,
+  KeyK: 75,
+  KeyL: 76,
+  KeyM: 77,
+  KeyN: 78,
+  KeyO: 79,
+  KeyP: 80,
+  KeyQ: 81,
+  KeyR: 82,
+  KeyS: 83,
+  KeyT: 84,
+  KeyU: 85,
+  KeyV: 86,
+  KeyW: 87,
+  KeyX: 88,
+  KeyY: 89,
+  KeyZ: 90,
+  BracketLeft: 91,
+  Backslash: 92,
+  BracketRight: 93,
+  Backquote: 96,
+  //  GLFW_KEY_WORLD_1   161 /* non-US #1 */
+  //  GLFW_KEY_WORLD_2   162 /* non-US #2 */
+  Escape: 256,
+  Enter: 257,
+  Tab: 258,
+  Backspace: 259,
+  Insert: 260,
+  Delete: 261,
+  ArrowRight: 262,
+  ArrowLeft: 263,
+  ArrowDown: 264,
+  ArrowUp: 265,
+  PageUp: 266,
+  PageDown: 267,
+  Home: 268,
+  End: 269,
+  CapsLock: 280,
+  ScrollLock: 281,
+  NumLock: 282,
+  PrintScreen: 283,
+  Pause: 284,
+  F1: 290,
+  F2: 291,
+  F3: 292,
+  F4: 293,
+  F5: 294,
+  F6: 295,
+  F7: 296,
+  F8: 297,
+  F9: 298,
+  F10: 299,
+  F11: 300,
+  F12: 301,
+  F13: 302,
+  F14: 303,
+  F15: 304,
+  F16: 305,
+  F17: 306,
+  F18: 307,
+  F19: 308,
+  F20: 309,
+  F21: 310,
+  F22: 311,
+  F23: 312,
+  F24: 313,
+  F25: 314,
+  NumPad0: 320,
+  NumPad1: 321,
+  NumPad2: 322,
+  NumPad3: 323,
+  NumPad4: 324,
+  NumPad5: 325,
+  NumPad6: 326,
+  NumPad7: 327,
+  NumPad8: 328,
+  NumPad9: 329,
+  NumpadDecimal: 330,
+  NumpadDivide: 331,
+  NumpadMultiply: 332,
+  NumpadSubtract: 333,
+  NumpadAdd: 334,
+  NumpadEnter: 335,
+  NumpadEqual: 336,
+  ShiftLeft: 340,
+  ControlLeft: 341,
+  AltLeft: 342,
+  MetaLeft: 343,
+  ShiftRight: 344,
+  ControlRight: 345,
+  AltRight: 346,
+  MetaRight: 347,
+  ContextMenu: 348,
+  //  GLFW_KEY_LAST   GLFW_KEY_MENU
+};
+
+function cstrlen(mem, ptr) {
+  let len = 0;
+  while (mem[ptr] != 0) {
+    len++;
+    ptr++;
+  }
+  return len;
+}
+
+function cstr_by_ptr(mem_buffer, ptr) {
+  const mem = new Uint8Array(mem_buffer);
+  const len = cstrlen(mem, ptr);
+  const bytes = new Uint8Array(mem_buffer, ptr, len);
+  return new TextDecoder().decode(bytes);
+}
+
+function color_hex_unpacked(r, g, b, a) {
+  r = r.toString(16).padStart(2, "0");
+  g = g.toString(16).padStart(2, "0");
+  b = b.toString(16).padStart(2, "0");
+  a = a.toString(16).padStart(2, "0");
+  return "#" + r + g + b + a;
+}
+
+function color_hex(color) {
+  const r = ((color >> (0 * 8)) & 0xff).toString(16).padStart(2, "0");
+  const g = ((color >> (1 * 8)) & 0xff).toString(16).padStart(2, "0");
+  const b = ((color >> (2 * 8)) & 0xff).toString(16).padStart(2, "0");
+  const a = ((color >> (3 * 8)) & 0xff).toString(16).padStart(2, "0");
+  return "#" + r + g + b + a;
+}
+
+function getColorFromMemory(buffer, color_ptr) {
+  const [r, g, b, a] = new Uint8Array(buffer, color_ptr, 4);
+  return color_hex_unpacked(r, g, b, a);
+}