Refactor wasm plugin
This commit is contained in:
parent
2798e9adcc
commit
850fe0f071
30
.clangd
30
.clangd
|
|
@ -1,5 +1,31 @@
|
|||
CompileFlags:
|
||||
Add:
|
||||
- --target=wasm32-wasip1
|
||||
- -D_GNU_SOURCE
|
||||
- -fno-exceptions
|
||||
- -fno-stack-protector
|
||||
- -fvisibility=hidden
|
||||
- -mbulk-memory
|
||||
- -mextended-const
|
||||
- -mrelaxed-simd
|
||||
- -msimd128
|
||||
- -mtail-call
|
||||
- -msign-ext
|
||||
- -mnontrapping-fptoint
|
||||
- -mreference-types
|
||||
- -Wall
|
||||
- -Wextra
|
||||
- -Wpedantic
|
||||
- -Wshadow
|
||||
- -Wconversion
|
||||
- -Werror
|
||||
---
|
||||
If:
|
||||
PathMatch: .*\.[ch]
|
||||
|
||||
CompileFlags:
|
||||
Add: [-std=c23]
|
||||
Add: [-std=gnu23]
|
||||
---
|
||||
If:
|
||||
PathMatch: .*\.(cpp|cc|cxx|hpp|hxx)
|
||||
CompileFlags:
|
||||
Add: [-std=gnu++26, -fno-rtti]
|
||||
|
|
|
|||
|
|
@ -175,3 +175,7 @@ dist
|
|||
.DS_Store
|
||||
error.log
|
||||
package-lock.json
|
||||
|
||||
# Generated by wasmPlugin
|
||||
*.c.d.ts
|
||||
*.cpp.d.ts
|
||||
|
|
|
|||
|
|
@ -2,4 +2,8 @@
|
|||
|
||||
#define JS_IMPORT(name) __attribute__((import_module("env"), import_name(#name)))
|
||||
#define JS_EXPORT_AS(name) __attribute__((export_name(#name)))
|
||||
#ifdef __cplusplus
|
||||
#define JS_EXPORT extern "C" __attribute__((visibility("default")))
|
||||
#else
|
||||
#define JS_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { plugin, $, type BunPlugin } from "bun";
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { renderDts } from './wasmTypes';
|
||||
|
||||
interface WasmLoaderConfig {
|
||||
production?: boolean;
|
||||
|
|
@ -10,6 +11,8 @@ interface WasmLoaderConfig {
|
|||
interface CompilerWithFlags {
|
||||
cc: string;
|
||||
nm: string;
|
||||
wasmOpt: string | null;
|
||||
reactorCrt: string | null;
|
||||
flags: string[];
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +33,8 @@ const getCompiler = (): Promise<CompilerWithFlags> => {
|
|||
const cc: CompilerWithFlags = {
|
||||
cc: 'clang',
|
||||
nm: 'nm',
|
||||
wasmOpt: Bun.which('wasm-opt') ?? null,
|
||||
reactorCrt: null,
|
||||
flags: [
|
||||
'--target=wasm32',
|
||||
'--no-standard-libraries',
|
||||
|
|
@ -74,6 +79,12 @@ const getCompiler = (): Promise<CompilerWithFlags> => {
|
|||
`--sysroot=${path.resolve(wasiDir, 'share', 'wasi-sysroot')}`,
|
||||
];
|
||||
|
||||
const wasmOptInSdk = path.resolve(wasiDir, 'bin', 'wasm-opt');
|
||||
cc.wasmOpt = await fs.access(wasmOptInSdk).then(() => wasmOptInSdk).catch(() => Bun.which('wasm-opt') ?? null);
|
||||
|
||||
const reactorCrt = path.resolve(wasiDir, 'share', 'wasi-sysroot', 'lib', 'wasm32-wasip1', 'crt1-reactor.o');
|
||||
cc.reactorCrt = await fs.access(reactorCrt).then(() => reactorCrt).catch(() => null);
|
||||
|
||||
return cc;
|
||||
})();
|
||||
return compilerPromise;
|
||||
|
|
@ -103,16 +114,13 @@ async function compileLibGroup(
|
|||
flags: string[],
|
||||
sources: string[],
|
||||
std: string[],
|
||||
output: string,
|
||||
): Promise<void> {
|
||||
if (sources.length === 0) return;
|
||||
objDir: string,
|
||||
): Promise<string[]> {
|
||||
if (sources.length === 0) return [];
|
||||
|
||||
// Per-file object dir derived from output name (dist/lib.c.o -> dist/lib.c/)
|
||||
const objDir = output.replace(/\.o$/, '');
|
||||
await fs.mkdir(objDir, { recursive: true });
|
||||
const objPaths = sources.map(src => path.resolve(objDir, path.basename(src) + '.o'));
|
||||
|
||||
// Compile only changed files, in parallel
|
||||
const compilationFlags = [...flags, ...std];
|
||||
await Promise.all(sources.map(async (src, i) => {
|
||||
if (await needsRebuild(objPaths[i], [src], compilationFlags)) {
|
||||
|
|
@ -122,10 +130,7 @@ async function compileLibGroup(
|
|||
}
|
||||
}));
|
||||
|
||||
// Partial link if any object is newer than combined output
|
||||
if (!(await needsRebuild(output, objPaths))) return;
|
||||
const partialResult = await $`${cc.cc} -r -o ${output} ${objPaths}`;
|
||||
if (partialResult.exitCode !== 0) throw new Error(`Partial link failed for ${output}`);
|
||||
return objPaths;
|
||||
}
|
||||
|
||||
async function instantiate(url: string) {
|
||||
|
|
@ -134,7 +139,7 @@ async function instantiate(url: string) {
|
|||
i32: 0x7f,
|
||||
i64: 0x7e,
|
||||
f32: 0x7d,
|
||||
f64: 0x7c
|
||||
f64: 0x7c,
|
||||
};
|
||||
|
||||
type tc = keyof typeof typeCodes;
|
||||
|
|
@ -211,26 +216,34 @@ async function instantiate(url: string) {
|
|||
lines.forEach(l => console.error(`[wasm] ${l}`));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
clock_time_get: (_id: number, _precision: bigint, timePtr: number) => {
|
||||
const now = Date.now();
|
||||
const nanosecondNow = BigInt(now) * 1_000_000n;
|
||||
const data = new DataView(memory.buffer);
|
||||
data.setBigInt64(timePtr, nanosecondNow, true);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
}, {
|
||||
get(target, p) {
|
||||
if (p in target) {
|
||||
console.debug(`[${String(p)}] exists`);
|
||||
return target[p as keyof typeof target];
|
||||
return (...args: any[]) => {
|
||||
const fn = target[p as keyof typeof target] as Function;
|
||||
const result = fn(...args) ?? 0;
|
||||
console.debug(`${String(p)}(${args.join(', ')}) = ${result}`);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return (...args: any[]) => {
|
||||
console.warn(`${String(p)}(${args.join(', ')}) is undefined`);
|
||||
return 0;
|
||||
}
|
||||
return (...args: any[]) => { console.warn(`[${String(p)}]`, args); return 0; }
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
(instance.exports._initialize as (() => void) | undefined)?.();
|
||||
|
||||
return {
|
||||
...Object.fromEntries(
|
||||
Object.entries(instance.exports)
|
||||
|
|
@ -274,16 +287,14 @@ const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
|||
'sign-ext',
|
||||
'nontrapping-fptoint',
|
||||
'reference-types',
|
||||
'multivalue',
|
||||
].map(f => `-m${f}`);
|
||||
|
||||
const flags = [
|
||||
...cc.flags,
|
||||
production ? '-O3' : '-O0',
|
||||
production ? '-Os' : '-O0',
|
||||
'-flto',
|
||||
'-fno-exceptions',
|
||||
'-fno-stack-protector',
|
||||
'-ffast-math',
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
'-Wall',
|
||||
|
|
@ -297,14 +308,11 @@ const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
|||
'-I', include,
|
||||
];
|
||||
|
||||
// Lib flags without -flto so clang -r (partial link) works on regular wasm objects
|
||||
const libFlags = flags.filter(f => f !== '-flto');
|
||||
|
||||
const std = args.path.endsWith('.cpp') ? STD_CPP : STD_C;
|
||||
|
||||
const inputObjPath = path.resolve(distDir, inputBasename + '.o');
|
||||
const libCObjPath = path.resolve(distDir, 'lib.c.o');
|
||||
const libCppObjPath = path.resolve(distDir, 'lib.cpp.o');
|
||||
const libCObjDir = path.resolve(distDir, 'lib.c');
|
||||
const libCppObjDir = path.resolve(distDir, 'lib.cpp');
|
||||
|
||||
const libCGlob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
|
||||
const libCppGlob = new Bun.Glob(`${buildAssets}/lib/**/*.cpp`);
|
||||
|
|
@ -313,9 +321,7 @@ const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
|||
Array.fromAsync(libCppGlob.scan()),
|
||||
]);
|
||||
|
||||
// Compile all three units in parallel
|
||||
await Promise.all([
|
||||
// 1. Input file -> dist/{filename}.o
|
||||
const [, libCObjs, libCppObjs] = await Promise.all([
|
||||
(async () => {
|
||||
const compilationFlags = [...flags, ...std];
|
||||
if (!await needsRebuild(inputObjPath, [args.path], compilationFlags)) return;
|
||||
|
|
@ -323,19 +329,11 @@ const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
|||
if (result.exitCode !== 0) throw new Error('Compile failed, check output');
|
||||
await saveFlags(inputObjPath, compilationFlags);
|
||||
})(),
|
||||
// 2. lib/**/*.c -> dist/lib.c.o
|
||||
compileLibGroup(cc, libFlags, libCFiles, STD_C, libCObjPath),
|
||||
// 3. lib/**/*.cpp -> dist/lib.cpp.o
|
||||
compileLibGroup(cc, libFlags, libCppFiles, STD_CPP, libCppObjPath),
|
||||
compileLibGroup(cc, flags, libCFiles, STD_C, libCObjDir),
|
||||
compileLibGroup(cc, flags, libCppFiles, STD_CPP, libCppObjDir),
|
||||
]);
|
||||
|
||||
// 4. Link if any input object is newer than the wasm output
|
||||
const libObjs = [
|
||||
libCFiles.length > 0 ? libCObjPath : null,
|
||||
libCppFiles.length > 0 ? libCppObjPath : null,
|
||||
].filter((x): x is string => x !== null);
|
||||
|
||||
const allObjs = [inputObjPath, ...libObjs];
|
||||
const allObjs = [inputObjPath, ...libCObjs, ...libCppObjs];
|
||||
|
||||
if (await needsRebuild(wasmPath, allObjs)) {
|
||||
const symbolsResult = await $`${cc.nm} ${inputObjPath}`.quiet();
|
||||
|
|
@ -356,14 +354,24 @@ const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
|||
'--no-entry',
|
||||
'--import-memory',
|
||||
'--export-table',
|
||||
...exports.flatMap(e => ['--export', e]),
|
||||
'--growable-table',
|
||||
...exports.flatMap(e => ['--export-if-defined', e]),
|
||||
].map(f => `-Wl,${f}`);
|
||||
|
||||
const linkResult = await $`${cc.cc} ${flags} ${linkFlags} -o ${wasmPath} -lstdc++ -nostartfiles ${allObjs}`;
|
||||
const reactorInputs = cc.reactorCrt ? [cc.reactorCrt] : [];
|
||||
const linkResult = await $`${cc.cc} ${flags} ${linkFlags} -o ${wasmPath} -lstdc++ -nostartfiles ${reactorInputs} ${allObjs}`;
|
||||
|
||||
if (linkResult.exitCode !== 0) {
|
||||
throw new Error('Link failed, check output');
|
||||
}
|
||||
|
||||
if (production && cc.wasmOpt) {
|
||||
const optResult = await $`${cc.wasmOpt} -Os --converge -o ${wasmPath} ${wasmPath}`;
|
||||
if (optResult.exitCode !== 0) throw new Error('wasm-opt failed, check output');
|
||||
}
|
||||
|
||||
const wasmBytes = new Uint8Array(await Bun.file(wasmPath).arrayBuffer());
|
||||
await Bun.write(`${args.path}.d.ts`, renderDts(wasmBytes));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
// Parses a wasm binary's type / function / import / export sections to recover
|
||||
// each function export's signature, then renders TypeScript declarations.
|
||||
|
||||
type Sig = { params: number[]; results: number[] };
|
||||
|
||||
const VALUE_TYPE_TS: Record<number, string> = {
|
||||
0x7f: 'number', // i32
|
||||
0x7e: 'bigint', // i64
|
||||
0x7d: 'number', // f32
|
||||
0x7c: 'number', // f64
|
||||
};
|
||||
|
||||
function readULEB128(bytes: Uint8Array, offset: number): [number, number] {
|
||||
let result = 0;
|
||||
let shift = 0;
|
||||
let i = 0;
|
||||
while (true) {
|
||||
const b = bytes[offset + i];
|
||||
result |= (b & 0x7f) << shift;
|
||||
i++;
|
||||
if ((b & 0x80) === 0) break;
|
||||
shift += 7;
|
||||
}
|
||||
return [result, i];
|
||||
}
|
||||
|
||||
function skipLimits(bytes: Uint8Array, offset: number): number {
|
||||
const flags = bytes[offset];
|
||||
let o = offset + 1;
|
||||
const [, l1] = readULEB128(bytes, o); o += l1;
|
||||
if (flags & 1) {
|
||||
const [, l2] = readULEB128(bytes, o); o += l2;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
export function parseWasmExports(bytes: Uint8Array): Map<string, Sig> {
|
||||
if (bytes[0] !== 0x00 || bytes[1] !== 0x61 || bytes[2] !== 0x73 || bytes[3] !== 0x6d) {
|
||||
throw new Error('Invalid wasm magic');
|
||||
}
|
||||
let offset = 8;
|
||||
|
||||
const types: Sig[] = [];
|
||||
const funcTypes: number[] = []; // defined function index → type index
|
||||
const importedFuncTypes: number[] = []; // imported function index → type index
|
||||
const exportEntries: { name: string; kind: number; index: number }[] = [];
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (offset < bytes.length) {
|
||||
const sectionId = bytes[offset++];
|
||||
const [sectionSize, s1] = readULEB128(bytes, offset);
|
||||
offset += s1;
|
||||
const sectionEnd = offset + sectionSize;
|
||||
|
||||
if (sectionId === 1) {
|
||||
const [count, c1] = readULEB128(bytes, offset); offset += c1;
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (bytes[offset++] !== 0x60) throw new Error('Expected functype 0x60');
|
||||
const [pc, p1] = readULEB128(bytes, offset); offset += p1;
|
||||
const params: number[] = [];
|
||||
for (let j = 0; j < pc; j++) params.push(bytes[offset++]);
|
||||
const [rc, r1] = readULEB128(bytes, offset); offset += r1;
|
||||
const results: number[] = [];
|
||||
for (let j = 0; j < rc; j++) results.push(bytes[offset++]);
|
||||
types.push({ params, results });
|
||||
}
|
||||
} else if (sectionId === 2) {
|
||||
const [count, c1] = readULEB128(bytes, offset); offset += c1;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const [modLen, m1] = readULEB128(bytes, offset); offset += m1 + modLen;
|
||||
const [fldLen, f1] = readULEB128(bytes, offset); offset += f1 + fldLen;
|
||||
const kind = bytes[offset++];
|
||||
if (kind === 0x00) {
|
||||
const [typeIdx, t1] = readULEB128(bytes, offset); offset += t1;
|
||||
importedFuncTypes.push(typeIdx);
|
||||
} else if (kind === 0x01) {
|
||||
offset++; // elem type
|
||||
offset = skipLimits(bytes, offset);
|
||||
} else if (kind === 0x02) {
|
||||
offset = skipLimits(bytes, offset);
|
||||
} else if (kind === 0x03) {
|
||||
offset += 2; // valtype + mutability
|
||||
}
|
||||
}
|
||||
} else if (sectionId === 3) {
|
||||
const [count, c1] = readULEB128(bytes, offset); offset += c1;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const [typeIdx, t1] = readULEB128(bytes, offset); offset += t1;
|
||||
funcTypes.push(typeIdx);
|
||||
}
|
||||
} else if (sectionId === 7) {
|
||||
const [count, c1] = readULEB128(bytes, offset); offset += c1;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const [nameLen, n1] = readULEB128(bytes, offset); offset += n1;
|
||||
const name = decoder.decode(bytes.subarray(offset, offset + nameLen));
|
||||
offset += nameLen;
|
||||
const kind = bytes[offset++];
|
||||
const [index, i1] = readULEB128(bytes, offset); offset += i1;
|
||||
exportEntries.push({ name, kind, index });
|
||||
}
|
||||
}
|
||||
|
||||
offset = sectionEnd;
|
||||
}
|
||||
|
||||
const result = new Map<string, Sig>();
|
||||
for (const { name, kind, index } of exportEntries) {
|
||||
if (kind !== 0x00) continue;
|
||||
const typeIdx = index < importedFuncTypes.length
|
||||
? importedFuncTypes[index]
|
||||
: funcTypes[index - importedFuncTypes.length];
|
||||
result.set(name, types[typeIdx]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function tsType(valType: number): string {
|
||||
return VALUE_TYPE_TS[valType] ?? 'unknown';
|
||||
}
|
||||
|
||||
function tsSignature({ params, results }: Sig): string {
|
||||
const paramList = params
|
||||
.map((t, i) => `a${i}: ${tsType(t)}`)
|
||||
.join(', ');
|
||||
let ret: string;
|
||||
if (results.length === 0) ret = 'void';
|
||||
else if (results.length === 1) ret = tsType(results[0]);
|
||||
else ret = `[${results.map(tsType).join(', ')}]`;
|
||||
return `(${paramList}) => ${ret}`;
|
||||
}
|
||||
|
||||
export function renderDts(wasmBytes: Uint8Array): string {
|
||||
const sigs = parseWasmExports(wasmBytes);
|
||||
const fns = Array.from(sigs.entries())
|
||||
.filter(([name]) => !name.startsWith('_'))
|
||||
.sort(([a], [b]) => a.localeCompare(b));
|
||||
|
||||
return [
|
||||
'// Auto-generated by wasmPlugin. Do not edit.',
|
||||
'declare const _: {',
|
||||
...fns.map(([name, sig]) => ` ${name}: ${tsSignature(sig)};`),
|
||||
' memory: WebAssembly.Memory;',
|
||||
' table: WebAssembly.Table;',
|
||||
' readonly data: DataView;',
|
||||
'};',
|
||||
'export default _;',
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Fallback type for .c/.cpp imports until the wasmPlugin generates a per-file .d.ts.
|
||||
// After a build, a sibling `${file}.d.ts` with concrete export names takes precedence.
|
||||
|
||||
declare module '*.cpp' {
|
||||
const _: {
|
||||
memory: WebAssembly.Memory;
|
||||
table: WebAssembly.Table;
|
||||
readonly data: DataView;
|
||||
[fn: string]: unknown;
|
||||
};
|
||||
export default _;
|
||||
}
|
||||
|
||||
declare module '*.c' {
|
||||
const _: {
|
||||
memory: WebAssembly.Memory;
|
||||
table: WebAssembly.Table;
|
||||
readonly data: DataView;
|
||||
[fn: string]: unknown;
|
||||
};
|
||||
export default _;
|
||||
}
|
||||
|
||||
declare module '*.wasm' {
|
||||
const _: {
|
||||
memory: WebAssembly.Memory;
|
||||
table?: WebAssembly.Table;
|
||||
data: DataView;
|
||||
[fn: string]: unknown;
|
||||
};
|
||||
export default _;
|
||||
}
|
||||
|
|
@ -1,12 +1,2 @@
|
|||
--target=wasm32-wasip1
|
||||
--sysroot=dist/wasi/share/wasi-sysroot
|
||||
-std=gnu++26
|
||||
-fno-exceptions
|
||||
-I
|
||||
build/assets/include
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wshadow
|
||||
-Wconversion
|
||||
-Werror
|
||||
-Ibuild/assets/include
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#define TYPE_CIRCLE 1
|
||||
#define TYPE_PLANE 2
|
||||
|
||||
#define rb_get(idx) (rigid_bodies + (idx))
|
||||
|
||||
////////// Types
|
||||
|
||||
typedef struct rigid_body_t {
|
||||
|
|
@ -50,12 +52,12 @@ static rigid_body_collision_callback_t rigid_body_collision_callback = NULL;
|
|||
////////// Functions
|
||||
|
||||
JS_EXPORT rigid_body* rigid_body_get(rigid_body_index idx) {
|
||||
return (rigid_bodies + (idx));
|
||||
return rb_get(idx);
|
||||
}
|
||||
|
||||
size_t rigid_body_new(float x, float y, float vx, float vy, float mass) {
|
||||
rigid_body_index idx = rigid_body_find_empty();
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
rb->type = TYPE_EMPTY;
|
||||
|
||||
rb->pos.x = x;
|
||||
|
|
@ -74,7 +76,7 @@ size_t rigid_body_new(float x, float y, float vx, float vy, float mass) {
|
|||
|
||||
JS_EXPORT rigid_body_index rigid_body_new_circle(float x, float y, float vx, float vy, float mass, float radius) {
|
||||
rigid_body_index idx = rigid_body_new(x, y, vx, vy, mass);
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
|
||||
rb->type = TYPE_CIRCLE;
|
||||
rb->circle.radius = radius;
|
||||
|
|
@ -84,7 +86,7 @@ JS_EXPORT rigid_body_index rigid_body_new_circle(float x, float y, float vx, flo
|
|||
|
||||
JS_EXPORT rigid_body_index rigid_body_new_plane(float x, float y, float nx, float ny) {
|
||||
rigid_body_index idx = rigid_body_new(x, y, 0, 0, INFINITY);
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
|
||||
rb->type = TYPE_PLANE;
|
||||
rb->plane.normal = vec2_normalize((vec2){nx, ny});
|
||||
|
|
@ -93,12 +95,12 @@ JS_EXPORT rigid_body_index rigid_body_new_plane(float x, float y, float nx, floa
|
|||
}
|
||||
|
||||
JS_EXPORT void rigid_body_free(rigid_body_index idx) {
|
||||
memset(rigid_body_get(idx), 0, sizeof(rigid_body));
|
||||
memset(rb_get(idx), 0, sizeof(rigid_body));
|
||||
}
|
||||
|
||||
JS_EXPORT void rigid_body_update(rigid_body_index idx, float dt) {
|
||||
rigid_body_resolve_collision(idx);
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
|
||||
if (!isinf(rb->mass)) {
|
||||
rb->vel.x += rb->force.x * dt / rb->mass;
|
||||
|
|
@ -114,7 +116,7 @@ JS_EXPORT void rigid_body_update(rigid_body_index idx, float dt) {
|
|||
|
||||
JS_EXPORT void rigid_body_update_all(float dt) {
|
||||
for (rigid_body_index idx = 0; idx < rigid_bodies_cap; idx++) {
|
||||
rigid_body* current = rigid_body_get(idx);
|
||||
rigid_body* current = rb_get(idx);
|
||||
if (current->type == TYPE_EMPTY) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -123,14 +125,14 @@ JS_EXPORT void rigid_body_update_all(float dt) {
|
|||
}
|
||||
|
||||
JS_EXPORT void rigid_body_add_force(rigid_body_index idx, float fx, float fy) {
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
rb->force.x += fx;
|
||||
rb->force.y += fy;
|
||||
}
|
||||
|
||||
JS_EXPORT void rigid_body_add_global_force(float fx, float fy) {
|
||||
for (rigid_body_index idx = 0; idx < rigid_bodies_cap; idx++) {
|
||||
rigid_body* current = rigid_body_get(idx);
|
||||
rigid_body* current = rb_get(idx);
|
||||
if (current->type == TYPE_EMPTY) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -143,10 +145,10 @@ JS_EXPORT void rigid_body_set_collision_callback(rigid_body_collision_callback_t
|
|||
}
|
||||
|
||||
void rigid_body_resolve_collision(rigid_body_index idx) {
|
||||
rigid_body* rb = rigid_body_get(idx);
|
||||
rigid_body* rb = rb_get(idx);
|
||||
int is_static = isinf(rb->mass);
|
||||
for (rigid_body_index i = idx + 1; i < rigid_bodies_cap; i++) {
|
||||
rigid_body* current = rigid_body_get(i);
|
||||
rigid_body* current = rb_get(i);
|
||||
if (!is_static || !isinf(current->mass)) {
|
||||
rigid_body_handle_collision(idx, i);
|
||||
}
|
||||
|
|
@ -154,8 +156,8 @@ void rigid_body_resolve_collision(rigid_body_index idx) {
|
|||
}
|
||||
|
||||
void rigid_body_handle_collision(rigid_body_index idx1, rigid_body_index idx2) {
|
||||
rigid_body* rb1 = rigid_body_get(idx1);
|
||||
rigid_body* rb2 = rigid_body_get(idx2);
|
||||
rigid_body* rb1 = rb_get(idx1);
|
||||
rigid_body* rb2 = rb_get(idx2);
|
||||
if (rb1->type == TYPE_CIRCLE && rb2->type == TYPE_CIRCLE) {
|
||||
vec2 d = vec2_sub(rb2->pos, rb1->pos);
|
||||
float distance = vec2_mag(d);
|
||||
|
|
@ -236,7 +238,7 @@ float point_to_line_dist(vec2 p, vec2 line_point, vec2 normal) {
|
|||
rigid_body_index rigid_body_find_empty() {
|
||||
if (rigid_bodies) {
|
||||
for (rigid_body_index idx = 0; idx < rigid_bodies_cap; idx++) {
|
||||
rigid_body* current = rigid_body_get(idx);
|
||||
rigid_body* current = rb_get(idx);
|
||||
if (current->type == TYPE_EMPTY) {
|
||||
memset(current, 0, sizeof(rigid_body));
|
||||
return idx;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#include <js.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define WIDTH 512
|
||||
#define HEIGHT 512
|
||||
|
|
@ -63,7 +62,7 @@ static int count_neighbours(int x, int y) {
|
|||
}
|
||||
|
||||
GRAPHICS_INIT {
|
||||
srand((uint32_t)time(NULL));
|
||||
srand(arc4random());
|
||||
field = malloc(WIDTH * HEIGHT);
|
||||
next_field = malloc(WIDTH * HEIGHT);
|
||||
image_data = image_create(WIDTH, HEIGHT);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <js.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -75,7 +75,13 @@ constexpr bit bit::nor(const bit& a, const bit& b) {
|
|||
return result;
|
||||
}
|
||||
|
||||
JS_EXPORT uint64_t awoo() {
|
||||
static struct InitProbe {
|
||||
InitProbe() {
|
||||
std::cout << "[ctor] _initialize ran" << std::endl;
|
||||
}
|
||||
} init_probe;
|
||||
|
||||
JS_EXPORT void awoo() {
|
||||
bit a{1, "a"};
|
||||
bit b{1, "b"};
|
||||
|
||||
|
|
@ -83,5 +89,5 @@ JS_EXPORT uint64_t awoo() {
|
|||
|
||||
std::cout << "sum = " << sum << "\ncarry = " << carry << std::endl;
|
||||
|
||||
return sum;
|
||||
std::cout << "rand = " << rand() << "\narc4 = " << arc4random() << std::endl;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import awoo from './awoo.cpp';
|
||||
|
||||
export default function main() {
|
||||
console.log(awoo);
|
||||
console.log(awoo)
|
||||
awoo.awoo();
|
||||
}
|
||||
|
|
@ -38,35 +38,6 @@ declare module "*.ogg" {
|
|||
const audio: HTMLAudioElement;
|
||||
export default audio;
|
||||
}
|
||||
declare module "*.wasm" {
|
||||
const instance: {
|
||||
memory: WebAssembly.Memory;
|
||||
data: DataView;
|
||||
|
||||
[x: string]: (...args: any) => any;
|
||||
};
|
||||
export default instance;
|
||||
}
|
||||
declare module "*.c" {
|
||||
const instance: {
|
||||
memory: WebAssembly.Memory;
|
||||
table: WebAssembly.Table;
|
||||
data: DataView;
|
||||
|
||||
[x: string]: (...args: any) => any;
|
||||
};
|
||||
export default instance;
|
||||
}
|
||||
declare module "*.cpp" {
|
||||
const instance: {
|
||||
memory: WebAssembly.Memory;
|
||||
table: WebAssembly.Table;
|
||||
data: DataView;
|
||||
|
||||
[x: string]: (...args: any) => any;
|
||||
};
|
||||
export default instance;
|
||||
}
|
||||
declare module "*.glsl" {
|
||||
const content: string;
|
||||
export default content;
|
||||
|
|
|
|||
Loading…
Reference in New Issue