Switch to explicit resourceFree, add typescript example, add ArrayBuffer support

This commit is contained in:
Alexander Klingenbeck 2023-05-20 21:34:27 +02:00
parent 17ef4e6fd9
commit aaf8fb16d8
27 changed files with 3854 additions and 321 deletions

View File

@ -1,4 +1,4 @@
import { RayLibApi, RayLibFunction, RayLibStruct } from "./interfaces"
import { RayLibApi, RayLibFunction, RayLibStruct, RayLibType } from "./interfaces"
export class ApiFunction{
constructor(private api: RayLibFunction){
@ -9,6 +9,7 @@ export class ApiFunction{
get argc() { return this.api.params?.length || 0 }
get params() { return this.api.params || [] }
get returnType() { return this.api.returnType }
set returnType(v) { this.api.returnType = v }
get description() { return this.api.description }
}

View File

@ -33,6 +33,12 @@ function main(){
writeFileSync("bindings/raylib_math_api.json", JSON.stringify(mathApi))
const api = <RayLibApi>JSON.parse(readFileSync("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'))
api.functions.push({
name: "SetModelMaterial",
description: "Replace material in slot materialIndex",
returnType: "void",
params: [{type: "Model *",name:"model"},{type:"int",name:"materialIndex"},{type:"Material",name:"material"}]
})
mathApi.forEach(x => api.functions.push(x))
const apiDesc = new ApiDescription(api)
@ -122,6 +128,17 @@ function main(){
properties: {},
createConstructor: false
})
core.addApiStructByName("NPatchInfo",{
properties: {
source: { get: true, set: true },
left: { get: true, set: true },
top: { get: true, set: true },
right: { get: true, set: true },
bottom: { get: true, set: true },
layout: { get: true, set: true },
},
createConstructor: true
})
core.addApiStructByName("Image", {
properties: {
width: { get: true },
@ -129,7 +146,7 @@ function main(){
mipmaps: { get: true },
format: { get: true }
},
destructor: "UnloadImage"
//destructor: "UnloadImage"
})
core.addApiStructByName("Wave", {
properties: {
@ -138,45 +155,79 @@ function main(){
sampleSize: { get: true },
channels: { get: true }
},
destructor: "UnloadWave"
//destructor: "UnloadWave"
})
core.addApiStructByName("Sound", {
properties: {
frameCount: { get: true }
},
destructor: "UnloadSound"
//destructor: "UnloadSound"
})
core.addApiStructByName("Music", {
properties: {
frameCount: { get: true },
looping: { get: true, set: true }
},
destructor: "UnloadMusicStream"
//destructor: "UnloadMusicStream"
})
core.addApiStructByName("Model", {
properties: {},
destructor: "UnloadModel"
//destructor: "UnloadModel"
})
core.addApiStructByName("Mesh", {
properties: {},
destructor: "UnloadMesh"
properties: {
vertexCount: { get: true, set: true },
triangleCount: { get: true, set: true },
// TODO: Free previous pointers before overwriting
vertices: { set: true },
texcoords: { set: true },
texcoords2: { set: true },
normals: { set: true },
tangents: { set: true },
colors: { set: true },
indices: { set: true },
animVertices: { set: true },
animNormals: { set: true },
boneIds: { set: true },
boneWeights: { set: true },
},
createEmptyConstructor: true
//destructor: "UnloadMesh"
})
core.addApiStructByName("Shader", {
properties: {},
destructor: "UnloadShader"
//destructor: "UnloadShader"
})
core.addApiStructByName("Texture", {
properties: {
width: { get: true },
height: { get: true }
},
destructor: "UnloadTexture"
//destructor: "UnloadTexture"
})
core.addApiStructByName("Font", {
properties: {
baseSize: { get: true }
},
destructor: "UnloadFont"
//destructor: "UnloadFont"
})
core.addApiStructByName("RenderTexture", {
properties: { },
//destructor: "UnloadRenderTexture"
})
core.addApiStructByName("MaterialMap", {
properties: {
texture: { set: true },
color: { set: true, get: true },
value: { get: true, set: true }
},
//destructor: "UnloadMaterialMap"
})
core.addApiStructByName("Material", {
properties: {
shader: { set: true }
},
//destructor: "UnloadMaterial"
})
// Window-related functions
@ -245,8 +296,8 @@ function main(){
core.addApiFunctionByName("EndMode2D")
core.addApiFunctionByName("BeginMode3D")
core.addApiFunctionByName("EndMode3D")
//core.addApiFunctionByName("BeginTextureMode")
//core.addApiFunctionByName("EndTextureMode")
core.addApiFunctionByName("BeginTextureMode")
core.addApiFunctionByName("EndTextureMode")
core.addApiFunctionByName("BeginShaderMode")
core.addApiFunctionByName("EndShaderMode")
core.addApiFunctionByName("BeginBlendMode")
@ -297,7 +348,7 @@ function main(){
// core.addApiFunctionByName("SetShaderValueV")
core.addApiFunctionByName("SetShaderValueMatrix")
core.addApiFunctionByName("SetShaderValueTexture")
// "UnloadShader" called by finalizer
core.addApiFunctionByName("UnloadShader")
// ScreenSpaceRelatedFunctions
core.addApiFunctionByName("GetMouseRay")
@ -488,7 +539,7 @@ function main(){
core.addApiFunctionByName("LoadImageFromTexture")
core.addApiFunctionByName("LoadImageFromScreen")
core.addApiFunctionByName("IsImageReady")
// UnloadImage called by destructor
core.addApiFunctionByName("UnloadImage")
core.addApiFunctionByName("ExportImage")
// needed?
// core.addApiFunctionByName("ExportImageAsCode")
@ -532,7 +583,15 @@ function main(){
core.addApiFunctionByName("ImageColorContrast")
core.addApiFunctionByName("ImageColorBrightness")
core.addApiFunctionByName("ImageColorReplace")
//core.addApiFunctionByName("LoadImageColors")
const lic = <ApiFunction>apiDesc.getFunction("LoadImageColors")
lic.returnType = "unsigned char *"
core.addApiFunction(lic, null, { body: (gen) => {
gen.jsToC("Image","image","argv[0]", core.structLookup)
gen.call("LoadImageColors", ["image"], {name:"colors",type:"Color *"})
gen.statement("JSValue retVal = JS_NewArrayBufferCopy(ctx, (const uint8_t*)colors, image.width*image.height*sizeof(Color))")
gen.call("UnloadImageColors", ["colors"])
gen.returnExp("retVal")
}})
//core.addApiFunctionByName("LoadImagePalette")
//core.addApiFunctionByName("UnloadImageColors")
//core.addApiFunctionByName("UnloadImagePalette")
@ -561,11 +620,11 @@ function main(){
core.addApiFunctionByName("LoadTexture")
core.addApiFunctionByName("LoadTextureFromImage")
core.addApiFunctionByName("LoadTextureCubemap")
// core.addApiFunctionByName("LoadRenderTexture")
core.addApiFunctionByName("LoadRenderTexture")
core.addApiFunctionByName("IsTextureReady")
// "UnloadTexture" called by finalizer
// core.addApiFunctionByName("IsRenderTextureReady")
// core.addApiFunctionByName("UnloadRenderTexture")
core.addApiFunctionByName("UnloadTexture")
core.addApiFunctionByName("IsRenderTextureReady")
core.addApiFunctionByName("UnloadRenderTexture")
// core.addApiFunctionByName("UpdateTexture")
// core.addApiFunctionByName("UpdateTextureRec")
@ -580,7 +639,7 @@ function main(){
core.addApiFunctionByName("DrawTextureEx")
core.addApiFunctionByName("DrawTextureRec")
core.addApiFunctionByName("DrawTexturePro")
// core.addApiFunctionByName("DrawTextureNPatch")
core.addApiFunctionByName("DrawTextureNPatch")
// Color/pixel related functions
core.addApiFunctionByName("Fade")
@ -611,7 +670,7 @@ function main(){
// core.addApiFunctionByName("LoadFontData")
// core.addApiFunctionByName("GenImageFontAtlas")
// core.addApiFunctionByName("UnloadFontData")
// "UnloadFont" called by finalizer
core.addApiFunctionByName("UnloadFont")
// core.addApiFunctionByName("ExportFontAsCode")
// Text drawing functions
@ -668,12 +727,12 @@ function main(){
core.addApiFunctionByName("DrawPlane")
core.addApiFunctionByName("DrawRay")
core.addApiFunctionByName("DrawGrid")
// model management functions
core.addApiFunctionByName("LoadModel")
core.addApiFunctionByName("LoadModelFromMesh")
core.addApiFunctionByName("IsModelReady")
// "UnloadModel" called by finalizer
core.addApiFunctionByName("UnloadModel")
core.addApiFunctionByName("GetModelBoundingBox")
// model drawing functions
@ -689,10 +748,10 @@ function main(){
// Mesh management functions
// TODO: Refcounting needed?
core.addApiFunctionByName("UploadMesh")
// core.addApiFunctionByName("UpdateMeshBuffer")
// "UnloadMesh" called by finalizer
//core.addApiFunctionByName("DrawMesh")
// core.addApiFunctionByName("DrawMeshInstanced")
core.addApiFunctionByName("UpdateMeshBuffer")
core.addApiFunctionByName("UnloadMesh")
core.addApiFunctionByName("DrawMesh")
core.addApiFunctionByName("DrawMeshInstanced")
core.addApiFunctionByName("ExportMesh")
core.addApiFunctionByName("GetMeshBoundingBox")
core.addApiFunctionByName("GenMeshTangents")
@ -712,11 +771,12 @@ function main(){
// Material loading/unloading functions
// core.addApiFunctionByName("LoadMaterials")
// core.addApiFunctionByName("LoadMaterialDefault")
// core.addApiFunctionByName("IsMaterialReady")
// core.addApiFunctionByName("UnloadMaterial")
// core.addApiFunctionByName("SetMaterialTexture")
// core.addApiFunctionByName("SetModelMeshMaterial")
core.addApiFunctionByName("LoadMaterialDefault")
core.addApiFunctionByName("IsMaterialReady")
core.addApiFunctionByName("UnloadMaterial")
core.addApiFunctionByName("SetMaterialTexture")
core.addApiFunctionByName("SetModelMaterial")
core.addApiFunctionByName("SetModelMeshMaterial")
// Model animations loading/unloading functions
// core.addApiFunctionByName("LoadModelAnimations")
@ -751,8 +811,8 @@ function main(){
core.addApiFunctionByName("LoadSoundFromWave")
core.addApiFunctionByName("IsSoundReady")
// core.addApiFunctionByName("UpdateSound")
// "UnloadWave" called by finalizer
// "UnloadSound" called by finalizer
core.addApiFunctionByName("UnloadWave")
core.addApiFunctionByName("UnloadSound")
core.addApiFunctionByName("ExportWave")
// core.addApiFunctionByName("ExportWaveAsCode")
@ -775,7 +835,7 @@ function main(){
core.addApiFunctionByName("LoadMusicStream")
// core.addApiFunctionByName("LoadMusicStreamFromMemory")
core.addApiFunctionByName("IsMusicReady")
// "UnloadMusicStream" called by finalizer
core.addApiFunctionByName("UnloadMusicStream")
core.addApiFunctionByName("PlayMusicStream")
core.addApiFunctionByName("IsMusicStreamPlaying")
core.addApiFunctionByName("UpdateMusicStream")
@ -941,6 +1001,9 @@ function main(){
api.enums.find(x => x.name === "CameraMode")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description))
api.enums.find(x => x.name === "ShaderLocationIndex")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description))
api.enums.find(x => x.name === "ShaderUniformDataType")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description))
api.enums.find(x => x.name === "MaterialMapIndex")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description))
core.exportGlobalConstant("MATERIAL_MAP_DIFFUSE", "Albedo material (same as: MATERIAL_MAP_DIFFUSE")
core.exportGlobalConstant("MATERIAL_MAP_SPECULAR", "Metalness material (same as: MATERIAL_MAP_SPECULAR)")
core.writeTo("src/bindings/js_raylib_core.h")
core.typings.writeTo("examples/lib.raylib.d.ts")
}

View File

@ -78,6 +78,19 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
jsToC(type: string, name: string, src: string, classIds: StructLookup = {}, supressDeclaration = false){
switch (type) {
// Array Buffer
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
this.declare(name+"_size", "size_t")
this.declare(name+"_js", "void *", false, `(void *)JS_GetArrayBuffer(ctx, &${name}_size, ${src})`)
this.if(name+"_js == NULL").returnExp("JS_EXCEPTION")
this.declare(name, type, false, "malloc("+name+"_size)")
this.call("memcpy", ["(void *)"+name, "(const void *)"+name+"_js", name+"_size"])
break;
// String
case "const char *":
case "char *":
if(!supressDeclaration) this.statement(`${type} ${name} = (${type})JS_ToCString(ctx, ${src})`)
@ -111,6 +124,7 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
if(!supressDeclaration) this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`)
else this.statement(`${name} = JS_ToBool(ctx, ${src})`)
break;
// Structs / Struct *
default:
const isConst = type.startsWith('const')
const isPointer = type.endsWith(' *')
@ -163,9 +177,17 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
jsCleanUpParameter(type: string, name: string) {
switch (type) {
case "char *":
case "const char *":
this.statement(`JS_FreeCString(ctx, ${name})`)
break;
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
this.statement(`free((void *)${name})`)
break;
default:
break;
}
@ -206,7 +228,7 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
const body = this.function(`js_${structName}_finalizer`, "void", args, true)
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`)
body.if("ptr", cond => {
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName}"`])
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName} %p"`,"ptr"])
if(onFinalize) onFinalize(<T>cond, "ptr")
cond.call("js_free_rt", ["rt","ptr"])
})
@ -230,9 +252,6 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
const args = [{type: "JSContext*", name: "ctx" }, {type: "JSValueConst", name: "this_val"}]
const fun = this.function(`js_${structName}_get_${field}`,"JSValue",args,true)
fun.declare("ptr", structName+"*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`)
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION")
})
fun.declare(field, type, false, "ptr->"+field)
fun.jsToJs(type, "ret", field, classIds)
fun.returnExp("ret")
@ -243,9 +262,6 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
const args = [{type: "JSContext*", name: "ctx" }, {type: "JSValueConst", name: "this_val"},{type: "JSValueConst", name: "v"}]
const fun = this.function(`js_${structName}_set_${field}`,"JSValue",args,true)
fun.declare("ptr", structName+"*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`)
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION")
})
fun.jsToC(type, "value", "v", classIds);
fun.statement("ptr->"+field+" = value")
fun.returnExp("JS_UNDEFINED")

View File

@ -8,6 +8,7 @@ export interface StructBindingOptions {
destructor?: string,
construct?: string,
createConstructor?: boolean
createEmptyConstructor?: boolean
}
export interface FuncBindingOptions {
@ -95,8 +96,8 @@ export class RayLibHeader extends QuickJsHeader {
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"])
if(options?.createConstructor){
const body = this.functions.jsStructConstructor(struct.name, struct.fields, classId, this.structLookup)
if(options?.createConstructor || options?.createEmptyConstructor){
const body = this.functions.jsStructConstructor(struct.name, options?.createEmptyConstructor ? [] : struct.fields, classId, this.structLookup)
this.moduleInit.statement(`JSValue ${struct.name}_constr = JS_NewCFunction2(ctx, ${body.getTag("_name")},"${struct.name})", ${struct.fields.length}, JS_CFUNC_constructor_or_func, 0)`)
this.moduleInit.call("JS_SetModuleExport", ["ctx","m", `"${struct.name}"`, struct.name+"_constr"])

View File

@ -26,7 +26,7 @@ export class TypeScriptDeclaration {
addStruct(api: ApiStruct, options: StructBindingOptions){
var fields = api.fields.filter(x => !!(options.properties || {})[x.name]).map(x => ({name: x.name, description: x.description, type: this.toJsType(x.type)}))
this.structs.tsDeclareInterface(api.name, fields)
this.structs.tsDeclareType(api.name, !!options.createConstructor, fields)
this.structs.tsDeclareType(api.name, !!(options.createConstructor || options.createEmptyConstructor), options.createEmptyConstructor ? [] : fields)
}
private toJsType(type: string){
@ -38,6 +38,10 @@ export class TypeScriptDeclaration {
case "float":
case "double":
return "number"
case "unsigned char *":
case "unsigned short *":
case "float *":
return "ArrayBuffer"
case "bool":
return "boolean"
case "const char *":
@ -47,14 +51,19 @@ export class TypeScriptDeclaration {
case "const void *":
return "any"
case "Camera":
case "Camera *":
return "Camera3D"
case "Texture2D":
case "Texture2D *":
case "TextureCubemap":
return "Texture"
case "RenderTexture2D":
case "RenderTexture2D *":
return "RenderTexture"
case "Quaternion":
return "Vector4"
default:
return type.replace(" *", "")
return type.replace(" *", "").replace("const ", "")
}
}

BIN
doc/structs.drawio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

118
examples/1st_person_maze.js Normal file
View File

@ -0,0 +1,118 @@
/*******************************************************************************************
*
* raylib [models] example - first person maze
*
* Example originally created with raylib 2.5, last time updated with raylib 3.5
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2019-2023 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// Initialization
//--------------------------------------------------------------------------------------
const screenWidth = 800;
const screenHeight = 450;
initWindow(screenWidth, screenHeight, "raylib [models] example - first person maze");
// Define the camera to look into our 3d world
const 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)
const cubicmap = loadTextureFromImage(imMap); // Convert image to texture to display (VRAM)
const mesh = genMeshCubicmap(imMap, new Vector3(1.0, 1.0, 1.0));
const model = loadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
const 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, texture)
setModelMaterial(model,0,mat)
// Get map image data to be used for collision detection
const mapPixels = new Uint8Array(loadImageColors(imMap));
unloadImage(imMap); // Unload image from RAM
let mapPosition = new Vector3( -16.0, 0.0, -8.0); // Set model position
disableCursor(); // Limit cursor to relative movement inside the window
setTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
let oldCamPos = camera.position; // Store old camera position
updateCamera(camera, CAMERA_FIRST_PERSON);
// Check player collision (we simplify to 2D collision detection)
const playerPos = new Vector2(camera.position.x, camera.position.z);
const playerRadius = 0.1; // Collision radius (player is modelled as a cilinder for collision)
let playerCellX = Math.floor(playerPos.x - mapPosition.x + 0.5);
let playerCellY = Math.floor(playerPos.y - mapPosition.z + 0.5);
// Out-of-limits security check
if (playerCellX < 0) playerCellX = 0;
else if (playerCellX >= cubicmap.width) playerCellX = cubicmap.width - 1;
if (playerCellY < 0) playerCellY = 0;
else if (playerCellY >= cubicmap.height) playerCellY = 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 < cubicmap.height; y++)
{
for (let x = 0; x < cubicmap.width; x++)
{
const pixelValR = mapPixels[((y*cubicmap.width + x)*4)]
if ((pixelValR == 255) && // Collision: white pixel, only check R channel
(checkCollisionCircleRec(playerPos, playerRadius, new Rectangle(
mapPosition.x - 0.5 + x*1.0,
mapPosition.z - 0.5 + y*1.0, 1.0, 1.0 ))))
{
// Collision detected, reset camera position
camera.position = oldCamPos;
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
beginDrawing();
clearBackground(RAYWHITE);
beginMode3D(camera);
drawModel(model, mapPosition, 1.0, WHITE); // Draw maze map
endMode3D();
drawTextureEx(cubicmap, new Vector2(getScreenWidth() - cubicmap.width*4.0 - 20, 20.0), 0.0, 4.0, WHITE);
drawRectangleLines(getScreenWidth() - cubicmap.width*4 - 20, 20, cubicmap.width*4, cubicmap.height*4, GREEN);
// Draw player position radar
drawRectangle(getScreenWidth() - cubicmap.width*4 - 20 + playerCellX*4, 20 + playerCellY*4, 4, 4, RED);
drawFPS(10, 10);
endDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(cubicmap); // Unload cubicmap texture
unloadTexture(texture); // Unload map texture
unloadModel(model); // Unload map model
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -28,4 +28,8 @@ while (!windowShouldClose()) // Detect window close button or ESC key
endDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -83,5 +83,6 @@ while (!windowShouldClose()) // Detect window close button or ESC key
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(texBunny);
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -0,0 +1,50 @@
// Initialization
//--------------------------------------------------------------------------------------
const screenWidth = 800;
const screenHeight = 450;
initWindow(screenWidth, screenHeight, "raylib [core] example - javascript mesh generation");
const mesh = new Mesh()
mesh.vertexCount = 3
mesh.triangleCount = 1
const v1 = new Vector3(screenWidth/2, 0, 0)
const v2 = new Vector3(0, screenHeight, 0 )
const v3 = new Vector3(screenWidth, screenHeight, 0)
mesh.indices = new Uint16Array([0,1,2]).buffer
mesh.vertices = new Float32Array([
v1.x, v1.y, v1.z,
v2.x, v2.y, v2.z,
v3.x, v3.y, v3.z
]).buffer
uploadMesh(mesh, false) // If your forget to upload to GPU drawMesh will segfault
const material = loadMaterialDefault()
const matrix = matrixIdentity()
setTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
beginDrawing();
clearBackground(RAYWHITE);
drawMesh(mesh, material, matrix)
drawText("Mesh created from Javascript ArrayBuffers", 190, 200, 20, LIGHTGRAY);
endDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadMaterial(material)
unloadMesh(mesh)
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -122,6 +122,24 @@ interface Matrix {
declare var Matrix: {
prototype: Matrix;
}
interface NPatchInfo {
/** Texture source rectangle */
source: Rectangle,
/** Left border offset */
left: number,
/** Top border offset */
top: number,
/** Right border offset */
right: number,
/** Bottom border offset */
bottom: number,
/** Layout of the n-patch: 3x3, 1x3 or 3x1 */
layout: number,
}
declare var NPatchInfo: {
prototype: NPatchInfo;
new(source: Rectangle, left: number, top: number, right: number, bottom: number, layout: number): NPatchInfo;
}
interface Image {
/** Image base width */
width: number,
@ -170,9 +188,36 @@ declare var Model: {
prototype: Model;
}
interface Mesh {
/** Number of vertices stored in arrays */
vertexCount: number,
/** Number of triangles stored (indexed or not) */
triangleCount: number,
/** Vertex position (XYZ - 3 components per vertex) (shader-location = 0) */
vertices: ArrayBuffer,
/** Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) */
texcoords: ArrayBuffer,
/** Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) */
texcoords2: ArrayBuffer,
/** Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) */
normals: ArrayBuffer,
/** Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) */
tangents: ArrayBuffer,
/** Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) */
colors: ArrayBuffer,
/** Vertex indices (in case vertex data comes indexed) */
indices: ArrayBuffer,
/** Animated vertex positions (after bones transformations) */
animVertices: ArrayBuffer,
/** Animated normals (after bones transformations) */
animNormals: ArrayBuffer,
/** Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) */
boneIds: ArrayBuffer,
/** Vertex bone weight, up to 4 bones influence by vertex (skinning) */
boneWeights: ArrayBuffer,
}
declare var Mesh: {
prototype: Mesh;
new(): Mesh;
}
interface Shader {
}
@ -195,6 +240,29 @@ interface Font {
declare var Font: {
prototype: Font;
}
interface RenderTexture {
}
declare var RenderTexture: {
prototype: RenderTexture;
}
interface MaterialMap {
/** Material map texture */
texture: Texture,
/** Material map color */
color: Color,
/** Material map value */
value: number,
}
declare var MaterialMap: {
prototype: MaterialMap;
}
interface Material {
/** Material shader */
shader: Shader,
}
declare var Material: {
prototype: Material;
}
/** Initialize window and OpenGL context */
declare function initWindow(width: number, height: number, title: string): void;
/** Check if KEY_ESCAPE pressed or Close icon pressed */
@ -307,6 +375,10 @@ declare function endMode2D(): void;
declare function beginMode3D(camera: Camera3D): void;
/** Ends 3D mode and returns to default 2D orthographic mode */
declare function endMode3D(): void;
/** Begin drawing to render texture */
declare function beginTextureMode(target: RenderTexture): void;
/** Ends drawing to render texture */
declare function endTextureMode(): void;
/** Begin custom shader drawing */
declare function beginShaderMode(shader: Shader): void;
/** End custom shader drawing (use default shader) */
@ -333,6 +405,8 @@ declare function setShaderValue(shader: Shader, locIndex: number, value: any, un
declare function setShaderValueMatrix(shader: Shader, locIndex: number, mat: Matrix): void;
/** Set shader uniform value for texture (sampler2d) */
declare function setShaderValueTexture(shader: Shader, locIndex: number, texture: Texture): void;
/** Unload shader from GPU memory (VRAM) */
declare function unloadShader(shader: Shader): void;
/** Get a ray trace from mouse position */
declare function getMouseRay(mousePosition: Vector2, camera: Camera3D): Ray;
/** Get camera transform matrix (view matrix) */
@ -492,9 +566,9 @@ declare function getGesturePinchVector(): Vector2;
/** Get gesture pinch angle */
declare function getGesturePinchAngle(): number;
/** Update camera position for selected mode */
declare function updateCamera(camera: Camera, mode: number): void;
declare function updateCamera(camera: Camera3D, mode: number): void;
/** Update camera movement/rotation */
declare function updateCameraPro(camera: Camera, movement: Vector3, rotation: Vector3, zoom: number): void;
declare function updateCameraPro(camera: Camera3D, movement: Vector3, rotation: Vector3, zoom: number): void;
/** Draw a pixel */
declare function drawPixel(posX: number, posY: number, color: Color): void;
/** Draw a pixel (Vector version) */
@ -589,6 +663,8 @@ declare function loadImageFromTexture(texture: Texture): Image;
declare function loadImageFromScreen(): Image;
/** Check if an image is ready */
declare function isImageReady(image: Image): boolean;
/** Unload image from CPU memory (RAM) */
declare function unloadImage(image: Image): void;
/** Export image data to file, returns true on success */
declare function exportImage(image: Image, fileName: string): boolean;
/** Generate image: plain color */
@ -663,6 +739,8 @@ declare function imageColorContrast(image: Image, contrast: number): void;
declare function imageColorBrightness(image: Image, brightness: number): void;
/** Modify image color: replace color */
declare function imageColorReplace(image: Image, color: Color, replace: Color): void;
/** Load color data from image as a Color array (RGBA - 32bit) */
declare function loadImageColors(image: Image): ArrayBuffer;
/** Get image alpha border rectangle */
declare function getImageAlphaBorder(image: Image, threshold: number): Rectangle;
/** Get image pixel color at (x, y) position */
@ -705,10 +783,18 @@ declare function loadTexture(fileName: string): Texture;
declare function loadTextureFromImage(image: Image): Texture;
/** Load cubemap from image, multiple image cubemap layouts supported */
declare function loadTextureCubemap(image: Image, layout: number): Texture;
/** Load texture for rendering (framebuffer) */
declare function loadRenderTexture(width: number, height: number): RenderTexture;
/** Check if a texture is ready */
declare function isTextureReady(texture: Texture): boolean;
/** Unload texture from GPU memory (VRAM) */
declare function unloadTexture(texture: Texture): void;
/** Check if a render texture is ready */
declare function isRenderTextureReady(target: RenderTexture): boolean;
/** Unload render texture from GPU memory (VRAM) */
declare function unloadRenderTexture(target: RenderTexture): void;
/** Generate GPU mipmaps for a texture */
declare function genTextureMipmaps(texture: Texture2D): void;
declare function genTextureMipmaps(texture: Texture): void;
/** Set texture scaling filter mode */
declare function setTextureFilter(texture: Texture, filter: number): void;
/** Set texture wrapping mode */
@ -723,6 +809,8 @@ declare function drawTextureEx(texture: Texture, position: Vector2, rotation: nu
declare function drawTextureRec(texture: Texture, source: Rectangle, position: Vector2, tint: Color): void;
/** Draw a part of a texture defined by a rectangle with 'pro' parameters */
declare function drawTexturePro(texture: Texture, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: number, tint: Color): void;
/** Draws a texture (or part of it) that stretches or shrinks nicely */
declare function drawTextureNPatch(texture: Texture, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: number, tint: Color): void;
/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */
declare function fade(color: Color, alpha: number): Color;
/** Get hexadecimal value for a Color */
@ -757,6 +845,8 @@ declare function loadFont(fileName: string): Font;
declare function loadFontFromImage(image: Image, key: Color, firstChar: number): Font;
/** Check if a font is ready */
declare function isFontReady(font: Font): boolean;
/** Unload font from GPU memory (VRAM) */
declare function unloadFont(font: Font): void;
/** Draw current FPS */
declare function drawFPS(posX: number, posY: number): void;
/** Draw text (using default font) */
@ -821,6 +911,8 @@ declare function loadModel(fileName: string): Model;
declare function loadModelFromMesh(mesh: Mesh): Model;
/** Check if a model is ready */
declare function isModelReady(model: Model): boolean;
/** Unload model (including meshes) from memory (RAM and/or VRAM) */
declare function unloadModel(model: Model): void;
/** Compute model bounding box limits (considers all meshes) */
declare function getModelBoundingBox(model: Model): BoundingBox;
/** Draw a model (with texture if set) */
@ -841,6 +933,14 @@ declare function drawBillboardRec(camera: Camera3D, texture: Texture, source: Re
declare function drawBillboardPro(camera: Camera3D, texture: Texture, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: number, tint: Color): void;
/** Upload mesh vertex data in GPU and provide VAO/VBO ids */
declare function uploadMesh(mesh: Mesh, dynamic: boolean): void;
/** Update mesh vertex data in GPU for a specific buffer index */
declare function updateMeshBuffer(mesh: Mesh, index: number, data: any, dataSize: number, offset: number): void;
/** Unload mesh data from CPU and GPU */
declare function unloadMesh(mesh: Mesh): void;
/** Draw a 3d mesh with material and transform */
declare function drawMesh(mesh: Mesh, material: Material, transform: Matrix): void;
/** Draw multiple mesh instances with material and different transforms */
declare function drawMeshInstanced(mesh: Mesh, material: Material, transforms: Matrix, instances: number): void;
/** Export mesh data to file, returns true on success */
declare function exportMesh(mesh: Mesh, fileName: string): boolean;
/** Compute mesh bounding box limits */
@ -869,6 +969,18 @@ declare function genMeshKnot(radius: number, size: number, radSeg: number, sides
declare function genMeshHeightmap(heightmap: Image, size: Vector3): Mesh;
/** Generate cubes-based map mesh from image data */
declare function genMeshCubicmap(cubicmap: Image, cubeSize: Vector3): Mesh;
/** Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) */
declare function loadMaterialDefault(): Material;
/** Check if a material is ready */
declare function isMaterialReady(material: Material): boolean;
/** Unload material from GPU memory (VRAM) */
declare function unloadMaterial(material: Material): void;
/** Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) */
declare function setMaterialTexture(material: Material, mapType: number, texture: Texture): void;
/** Replace material in slot materialIndex */
declare function setModelMaterial(model: Model, materialIndex: number, material: Material): void;
/** Set material for a mesh */
declare function setModelMeshMaterial(model: Model, meshId: number, materialId: number): void;
/** Check collision between two spheres */
declare function checkCollisionSpheres(center1: Vector3, radius1: number, center2: Vector3, radius2: number): boolean;
/** Check collision between two bounding boxes */
@ -903,6 +1015,10 @@ declare function loadSound(fileName: string): Sound;
declare function loadSoundFromWave(wave: Wave): Sound;
/** Checks if a sound is ready */
declare function isSoundReady(sound: Sound): boolean;
/** Unload wave data */
declare function unloadWave(wave: Wave): void;
/** Unload sound */
declare function unloadSound(sound: Sound): void;
/** Export wave data to file, returns true on success */
declare function exportWave(wave: Wave, fileName: string): boolean;
/** Play a sound */
@ -931,6 +1047,8 @@ declare function waveFormat(wave: Wave, sampleRate: number, sampleSize: number,
declare function loadMusicStream(fileName: string): Music;
/** Checks if a music stream is ready */
declare function isMusicReady(music: Music): boolean;
/** Unload music stream */
declare function unloadMusicStream(music: Music): void;
/** Start music playing */
declare function playMusicStream(music: Music): void;
/** Check if music is playing */
@ -1042,9 +1160,9 @@ declare function vector3CrossProduct(v1: Vector3, v2: Vector3): Vector3;
/** */
declare function vector3Perpendicular(v: Vector3): Vector3;
/** */
declare function vector3Length(v: const Vector3): number;
declare function vector3Length(v: Vector3): number;
/** */
declare function vector3LengthSqr(v: const Vector3): number;
declare function vector3LengthSqr(v: Vector3): number;
/** */
declare function vector3DotProduct(v1: Vector3, v2: Vector3): number;
/** */
@ -1667,3 +1785,29 @@ declare var SHADER_UNIFORM_IVEC3: number;
declare var SHADER_UNIFORM_IVEC4: number;
/** Shader uniform type: sampler2d */
declare var SHADER_UNIFORM_SAMPLER2D: number;
/** Albedo material (same as: MATERIAL_MAP_DIFFUSE) */
declare var MATERIAL_MAP_ALBEDO: number;
/** Metalness material (same as: MATERIAL_MAP_SPECULAR) */
declare var MATERIAL_MAP_METALNESS: number;
/** Normal material */
declare var MATERIAL_MAP_NORMAL: number;
/** Roughness material */
declare var MATERIAL_MAP_ROUGHNESS: number;
/** Ambient occlusion material */
declare var MATERIAL_MAP_OCCLUSION: number;
/** Emission material */
declare var MATERIAL_MAP_EMISSION: number;
/** Heightmap material */
declare var MATERIAL_MAP_HEIGHT: number;
/** Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) */
declare var MATERIAL_MAP_CUBEMAP: number;
/** Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) */
declare var MATERIAL_MAP_IRRADIANCE: number;
/** Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) */
declare var MATERIAL_MAP_PREFILTER: number;
/** Brdf material */
declare var MATERIAL_MAP_BRDF: number;
/** Albedo material (same as: MATERIAL_MAP_DIFFUSE */
declare var MATERIAL_MAP_DIFFUSE: number;
/** Metalness material (same as: MATERIAL_MAP_SPECULAR) */
declare var MATERIAL_MAP_SPECULAR: number;

View File

@ -10,7 +10,6 @@
* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// Initialization
//--------------------------------------------------------------------------------------
const screenWidth = 800;
@ -26,19 +25,23 @@ const fovy = 45.0; // Camera field-of-view Y
const projection = CAMERA_PERSPECTIVE; // Camera projection type
const camera = new Camera3D(position, target, up, fovy, projection)
const image = loadImage("assets/cubicmap.png"); // Load cubicmap image (RAM)
const cubicmap = loadTextureFromImage(image); // Convert image to texture to display (VRAM)
let image = loadImage("assets/cubicmap.png"); // Load cubicmap image (RAM)
let cubicmap = loadTextureFromImage(image); // Convert image to texture to display (VRAM)
const mesh = genMeshCubicmap(image, new Vector3(1.0, 1.0, 1.0));
const model = loadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
const texture = loadTexture("assets/cubicmap_atlas.png"); // Load map texture
let 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, texture)
setModelMaterial(model,0,mat)
const mapPosition = new Vector3(-16.0, 0.0, -8.0); // Set model position
image = null; // Unload cubesmap image from RAM, already uploaded to VRAM
unloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM
setTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@ -50,7 +53,7 @@ while (!windowShouldClose()) // Detect window close button or ESC key
//----------------------------------------------------------------------------------
updateCamera(camera, CAMERA_ORBITAL);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
beginDrawing();
@ -77,9 +80,9 @@ while (!windowShouldClose()) // Detect window close button or ESC key
// De-Initialization
//--------------------------------------------------------------------------------------
cubicmap = null // Unload cubicmap texture
texture = null // Unload map texture
model = null // Unload map model
unloadTexture(cubicmap);
unloadTexture(texture);
unloadModel(model);
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -23,7 +23,7 @@ const viewCenterLoc = getShaderLocation(shader, "viewCenter");
const runTimeLoc = getShaderLocation(shader, "runTime");
const resolutionLoc = getShaderLocation(shader, "resolution");
let resolution = new Vector2(getRenderWidth(), getRenderHeight());
let resolution = new Vector2(getRenderWidth()*2, getRenderHeight()*2);
setShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
let runTime = 0.0;
@ -75,5 +75,6 @@ while (!windowShouldClose()) // Detect window close button or ESC key
// De-Initialization
//--------------------------------------------------------------------------------------
unloadShader(shader)
closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

367
examples/ts_game.js Normal file
View File

@ -0,0 +1,367 @@
/******/ (() => { // webpackBootstrap
/******/ "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__) => {
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);
while (!(this.quit = windowShouldClose())) {
this.systemHost.loadSystems();
this.systemHost.updateSystems();
beginDrawing();
clearBackground(this.clearColor);
this.systemHost.drawSystems();
this.systemHost.unloadSystems();
endDrawing();
}
this.systemHost.requestShutdown();
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;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
var exports = __webpack_exports__;
/*!**********************!*\
!*** ./src/index.ts ***!
\**********************/
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());
}
draw() {
drawMesh(this.mesh, this.material, this.matrix);
}
unload() {
super.unload();
unloadMaterial(this.material);
unloadMesh(this.mesh);
}
}
const game = new game_1.Game(800, 450, "Typescript Game");
game.addSystem(new examples_1.GameController());
game.addSystem(new MySys());
game.run();
})();
/******/ })()
;

1
examples/ts_game/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

1689
examples/ts_game/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
{
"name": "bindings",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
"watch": "webpack --watch --config webpack.config.js --mode development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
"webpack": "^5.82.0",
"webpack-cli": "^5.0.2"
}
}

View File

@ -0,0 +1,135 @@
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()
}
}

View File

@ -0,0 +1,38 @@
import { System, SystemHost } from "./systems"
export class Game {
public clearColor = RAYWHITE
private systemHost = new SystemHost()
private quit = false
constructor(public readonly width: number,
public readonly height: number,
public readonly title: string){
}
public run(){
initWindow(this.width,this.height,this.title)
setTargetFPS(60)
while(!(this.quit = windowShouldClose())){
this.systemHost.loadSystems()
this.systemHost.updateSystems()
beginDrawing()
clearBackground(this.clearColor)
this.systemHost.drawSystems()
this.systemHost.unloadSystems()
endDrawing()
}
this.systemHost.requestShutdown()
closeWindow()
}
addSystem(system: System){
return this.systemHost.addSystem(system)
}
removeSystem(id: number){
return this.systemHost.removeSystem(id)
}
}

View File

@ -0,0 +1,46 @@
import { GameController } from "./examples";
import { Game } from "./game";
import { SystemBase, SystemContainer } from "./systems";
class MySys extends SystemBase {
mesh!: Mesh;
material!: Material
matrix!: Matrix
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)
}
unload(): void {
super.unload()
unloadMaterial(this.material)
unloadMesh(this.mesh)
}
}
const game = new Game(800,450,"Typescript Game")
game.addSystem(new GameController())
game.addSystem(new MySys())
game.run()

View File

@ -0,0 +1,122 @@
export interface System {
isFinished: boolean;
load(): void;
unload(): void;
draw(): void;
update(dt: number): void;
}
export abstract class SystemBase implements System {
public promise!: Promise<void>
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<number,System>()
private unloadQueue = new Set<number>()
private loadQueue = new Set<number>()
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()
}
}
}

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"esModuleInterop": true,
"resolveJsonModule": true,
"strict": true,
"types": [
"../lib.raylib"
]
}
}

View File

@ -0,0 +1,24 @@
const path = require('path');
module.exports = {
entry: './src/index.ts',
devtool: false,
target: "node",
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'ts_game.js',
path: path.resolve(__dirname, '..'),
},
};

View File

@ -20,6 +20,7 @@ class ApiFunction {
get argc() { return this.api.params?.length || 0; }
get params() { return this.api.params || []; }
get returnType() { return this.api.returnType; }
set returnType(v) { this.api.returnType = v; }
get description() { return this.api.description; }
}
exports.ApiFunction = ApiFunction;
@ -378,6 +379,19 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
}
jsToC(type, name, src, classIds = {}, supressDeclaration = false) {
switch (type) {
// Array Buffer
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
this.declare(name + "_size", "size_t");
this.declare(name + "_js", "void *", false, `(void *)JS_GetArrayBuffer(ctx, &${name}_size, ${src})`);
this.if(name + "_js == NULL").returnExp("JS_EXCEPTION");
this.declare(name, type, false, "malloc(" + name + "_size)");
this.call("memcpy", ["(void *)" + name, "(const void *)" + name + "_js", name + "_size"]);
break;
// String
case "const char *":
case "char *":
if (!supressDeclaration)
@ -422,6 +436,7 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
else
this.statement(`${name} = JS_ToBool(ctx, ${src})`);
break;
// Structs / Struct *
default:
const isConst = type.startsWith('const');
const isPointer = type.endsWith(' *');
@ -474,9 +489,17 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
}
jsCleanUpParameter(type, name) {
switch (type) {
case "char *":
case "const char *":
this.statement(`JS_FreeCString(ctx, ${name})`);
break;
case "const void *":
case "void *":
case "float *":
case "unsigned short *":
case "unsigned char *":
this.statement(`free((void *)${name})`);
break;
default:
break;
}
@ -511,7 +534,7 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
const body = this.function(`js_${structName}_finalizer`, "void", args, true);
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`);
body.if("ptr", cond => {
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName}"`])
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName} %p"`,"ptr"])
if (onFinalize)
onFinalize(cond, "ptr");
cond.call("js_free_rt", ["rt", "ptr"]);
@ -534,9 +557,6 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }];
const fun = this.function(`js_${structName}_get_${field}`, "JSValue", args, true);
fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION");
});
fun.declare(field, type, false, "ptr->" + field);
fun.jsToJs(type, "ret", field, classIds);
fun.returnExp("ret");
@ -546,9 +566,6 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }, { type: "JSValueConst", name: "v" }];
const fun = this.function(`js_${structName}_set_${field}`, "JSValue", args, true);
fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
fun.if("!ptr", cond => {
cond.returnExp("JS_EXCEPTION");
});
fun.jsToC(type, "value", "v", classIds);
fun.statement("ptr->" + field + " = value");
fun.returnExp("JS_UNDEFINED");
@ -667,8 +684,8 @@ class RayLibHeader extends quickjs_1.QuickJsHeader {
classFuncList.jsPropStringDef("[Symbol.toStringTag]", struct.name);
const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"));
this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]);
if (options?.createConstructor) {
const body = this.functions.jsStructConstructor(struct.name, struct.fields, classId, this.structLookup);
if (options?.createConstructor || options?.createEmptyConstructor) {
const body = this.functions.jsStructConstructor(struct.name, options?.createEmptyConstructor ? [] : struct.fields, classId, this.structLookup);
this.moduleInit.statement(`JSValue ${struct.name}_constr = JS_NewCFunction2(ctx, ${body.getTag("_name")},"${struct.name})", ${struct.fields.length}, JS_CFUNC_constructor_or_func, 0)`);
this.moduleInit.call("JS_SetModuleExport", ["ctx", "m", `"${struct.name}"`, struct.name + "_constr"]);
this.moduleEntry.call("JS_AddModuleExport", ["ctx", "m", '"' + struct.name + '"']);
@ -734,7 +751,7 @@ class TypeScriptDeclaration {
addStruct(api, options) {
var fields = api.fields.filter(x => !!(options.properties || {})[x.name]).map(x => ({ name: x.name, description: x.description, type: this.toJsType(x.type) }));
this.structs.tsDeclareInterface(api.name, fields);
this.structs.tsDeclareType(api.name, !!options.createConstructor, fields);
this.structs.tsDeclareType(api.name, !!(options.createConstructor || options.createEmptyConstructor), options.createEmptyConstructor ? [] : fields);
}
toJsType(type) {
switch (type) {
@ -745,6 +762,10 @@ class TypeScriptDeclaration {
case "float":
case "double":
return "number";
case "unsigned char *":
case "unsigned short *":
case "float *":
return "ArrayBuffer";
case "bool":
return "boolean";
case "const char *":
@ -754,14 +775,19 @@ class TypeScriptDeclaration {
case "const void *":
return "any";
case "Camera":
case "Camera *":
return "Camera3D";
case "Texture2D":
case "Texture2D *":
case "TextureCubemap":
return "Texture";
case "RenderTexture2D":
case "RenderTexture2D *":
return "RenderTexture";
case "Quaternion":
return "Vector4";
default:
return type.replace(" *", "");
return type.replace(" *", "").replace("const ", "");
}
}
writeTo(filename) {
@ -891,6 +917,12 @@ function main() {
const mathApi = parseMathHeader();
(0, fs_1.writeFileSync)("bindings/raylib_math_api.json", JSON.stringify(mathApi));
const api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'));
api.functions.push({
name: "SetModelMaterial",
description: "Replace material in slot materialIndex",
returnType: "void",
params: [{ type: "Model *", name: "model" }, { type: "int", name: "materialIndex" }, { type: "Material", name: "material" }]
});
mathApi.forEach(x => api.functions.push(x));
const apiDesc = new api_1.ApiDescription(api);
const core = new raylib_header_1.RayLibHeader("raylib_core", apiDesc);
@ -979,6 +1011,17 @@ function main() {
properties: {},
createConstructor: false
});
core.addApiStructByName("NPatchInfo", {
properties: {
source: { get: true, set: true },
left: { get: true, set: true },
top: { get: true, set: true },
right: { get: true, set: true },
bottom: { get: true, set: true },
layout: { get: true, set: true },
},
createConstructor: true
});
core.addApiStructByName("Image", {
properties: {
width: { get: true },
@ -986,7 +1029,7 @@ function main() {
mipmaps: { get: true },
format: { get: true }
},
destructor: "UnloadImage"
//destructor: "UnloadImage"
});
core.addApiStructByName("Wave", {
properties: {
@ -995,45 +1038,79 @@ function main() {
sampleSize: { get: true },
channels: { get: true }
},
destructor: "UnloadWave"
//destructor: "UnloadWave"
});
core.addApiStructByName("Sound", {
properties: {
frameCount: { get: true }
},
destructor: "UnloadSound"
//destructor: "UnloadSound"
});
core.addApiStructByName("Music", {
properties: {
frameCount: { get: true },
looping: { get: true, set: true }
},
destructor: "UnloadMusicStream"
//destructor: "UnloadMusicStream"
});
core.addApiStructByName("Model", {
properties: {},
destructor: "UnloadModel"
//destructor: "UnloadModel"
});
core.addApiStructByName("Mesh", {
properties: {},
destructor: "UnloadMesh"
properties: {
vertexCount: { get: true, set: true },
triangleCount: { get: true, set: true },
// TODO: Free previous pointers before overwriting
vertices: { set: true },
texcoords: { set: true },
texcoords2: { set: true },
normals: { set: true },
tangents: { set: true },
colors: { set: true },
indices: { set: true },
animVertices: { set: true },
animNormals: { set: true },
boneIds: { set: true },
boneWeights: { set: true },
},
createEmptyConstructor: true
//destructor: "UnloadMesh"
});
core.addApiStructByName("Shader", {
properties: {},
destructor: "UnloadShader"
//destructor: "UnloadShader"
});
core.addApiStructByName("Texture", {
properties: {
width: { get: true },
height: { get: true }
},
destructor: "UnloadTexture"
//destructor: "UnloadTexture"
});
core.addApiStructByName("Font", {
properties: {
baseSize: { get: true }
},
destructor: "UnloadFont"
//destructor: "UnloadFont"
});
core.addApiStructByName("RenderTexture", {
properties: {},
//destructor: "UnloadRenderTexture"
});
core.addApiStructByName("MaterialMap", {
properties: {
texture: { set: true },
color: { set: true, get: true },
value: { get: true, set: true }
},
//destructor: "UnloadMaterialMap"
});
core.addApiStructByName("Material", {
properties: {
shader: { set: true }
},
//destructor: "UnloadMaterial"
});
// Window-related functions
core.addApiFunctionByName("InitWindow");
@ -1098,8 +1175,8 @@ function main() {
core.addApiFunctionByName("EndMode2D");
core.addApiFunctionByName("BeginMode3D");
core.addApiFunctionByName("EndMode3D");
//core.addApiFunctionByName("BeginTextureMode")
//core.addApiFunctionByName("EndTextureMode")
core.addApiFunctionByName("BeginTextureMode");
core.addApiFunctionByName("EndTextureMode");
core.addApiFunctionByName("BeginShaderMode");
core.addApiFunctionByName("EndShaderMode");
core.addApiFunctionByName("BeginBlendMode");
@ -1148,7 +1225,7 @@ function main() {
// core.addApiFunctionByName("SetShaderValueV")
core.addApiFunctionByName("SetShaderValueMatrix");
core.addApiFunctionByName("SetShaderValueTexture");
// "UnloadShader" called by finalizer
core.addApiFunctionByName("UnloadShader");
// ScreenSpaceRelatedFunctions
core.addApiFunctionByName("GetMouseRay");
core.addApiFunctionByName("GetCameraMatrix");
@ -1322,7 +1399,7 @@ function main() {
core.addApiFunctionByName("LoadImageFromTexture");
core.addApiFunctionByName("LoadImageFromScreen");
core.addApiFunctionByName("IsImageReady");
// UnloadImage called by destructor
core.addApiFunctionByName("UnloadImage");
core.addApiFunctionByName("ExportImage");
// needed?
// core.addApiFunctionByName("ExportImageAsCode")
@ -1364,7 +1441,15 @@ function main() {
core.addApiFunctionByName("ImageColorContrast");
core.addApiFunctionByName("ImageColorBrightness");
core.addApiFunctionByName("ImageColorReplace");
//core.addApiFunctionByName("LoadImageColors")
const lic = apiDesc.getFunction("LoadImageColors");
lic.returnType = "unsigned char *";
core.addApiFunction(lic, null, { body: (gen) => {
gen.jsToC("Image", "image", "argv[0]", core.structLookup);
gen.call("LoadImageColors", ["image"], { name: "colors", type: "Color *" });
gen.statement("JSValue retVal = JS_NewArrayBufferCopy(ctx, (const uint8_t*)colors, image.width*image.height*sizeof(Color))");
gen.call("UnloadImageColors", ["colors"]);
gen.returnExp("retVal");
} });
//core.addApiFunctionByName("LoadImagePalette")
//core.addApiFunctionByName("UnloadImageColors")
//core.addApiFunctionByName("UnloadImagePalette")
@ -1391,11 +1476,11 @@ function main() {
core.addApiFunctionByName("LoadTexture");
core.addApiFunctionByName("LoadTextureFromImage");
core.addApiFunctionByName("LoadTextureCubemap");
// core.addApiFunctionByName("LoadRenderTexture")
core.addApiFunctionByName("LoadRenderTexture");
core.addApiFunctionByName("IsTextureReady");
// "UnloadTexture" called by finalizer
// core.addApiFunctionByName("IsRenderTextureReady")
// core.addApiFunctionByName("UnloadRenderTexture")
core.addApiFunctionByName("UnloadTexture");
core.addApiFunctionByName("IsRenderTextureReady");
core.addApiFunctionByName("UnloadRenderTexture");
// core.addApiFunctionByName("UpdateTexture")
// core.addApiFunctionByName("UpdateTextureRec")
// Texture configuration functions
@ -1408,7 +1493,7 @@ function main() {
core.addApiFunctionByName("DrawTextureEx");
core.addApiFunctionByName("DrawTextureRec");
core.addApiFunctionByName("DrawTexturePro");
// core.addApiFunctionByName("DrawTextureNPatch")
core.addApiFunctionByName("DrawTextureNPatch");
// Color/pixel related functions
core.addApiFunctionByName("Fade");
core.addApiFunctionByName("ColorToInt");
@ -1436,7 +1521,7 @@ function main() {
// core.addApiFunctionByName("LoadFontData")
// core.addApiFunctionByName("GenImageFontAtlas")
// core.addApiFunctionByName("UnloadFontData")
// "UnloadFont" called by finalizer
core.addApiFunctionByName("UnloadFont");
// core.addApiFunctionByName("ExportFontAsCode")
// Text drawing functions
core.addApiFunctionByName("DrawFPS");
@ -1491,7 +1576,7 @@ function main() {
core.addApiFunctionByName("LoadModel");
core.addApiFunctionByName("LoadModelFromMesh");
core.addApiFunctionByName("IsModelReady");
// "UnloadModel" called by finalizer
core.addApiFunctionByName("UnloadModel");
core.addApiFunctionByName("GetModelBoundingBox");
// model drawing functions
core.addApiFunctionByName("DrawModel");
@ -1505,10 +1590,10 @@ function main() {
// Mesh management functions
// TODO: Refcounting needed?
core.addApiFunctionByName("UploadMesh");
// core.addApiFunctionByName("UpdateMeshBuffer")
// "UnloadMesh" called by finalizer
//core.addApiFunctionByName("DrawMesh")
// core.addApiFunctionByName("DrawMeshInstanced")
core.addApiFunctionByName("UpdateMeshBuffer");
core.addApiFunctionByName("UnloadMesh");
core.addApiFunctionByName("DrawMesh");
core.addApiFunctionByName("DrawMeshInstanced");
core.addApiFunctionByName("ExportMesh");
core.addApiFunctionByName("GetMeshBoundingBox");
core.addApiFunctionByName("GenMeshTangents");
@ -1526,11 +1611,12 @@ function main() {
core.addApiFunctionByName("GenMeshCubicmap");
// Material loading/unloading functions
// core.addApiFunctionByName("LoadMaterials")
// core.addApiFunctionByName("LoadMaterialDefault")
// core.addApiFunctionByName("IsMaterialReady")
// core.addApiFunctionByName("UnloadMaterial")
// core.addApiFunctionByName("SetMaterialTexture")
// core.addApiFunctionByName("SetModelMeshMaterial")
core.addApiFunctionByName("LoadMaterialDefault");
core.addApiFunctionByName("IsMaterialReady");
core.addApiFunctionByName("UnloadMaterial");
core.addApiFunctionByName("SetMaterialTexture");
core.addApiFunctionByName("SetModelMaterial");
core.addApiFunctionByName("SetModelMeshMaterial");
// Model animations loading/unloading functions
// core.addApiFunctionByName("LoadModelAnimations")
// core.addApiFunctionByName("UpdateModelAnimation")
@ -1560,8 +1646,8 @@ function main() {
core.addApiFunctionByName("LoadSoundFromWave");
core.addApiFunctionByName("IsSoundReady");
// core.addApiFunctionByName("UpdateSound")
// "UnloadWave" called by finalizer
// "UnloadSound" called by finalizer
core.addApiFunctionByName("UnloadWave");
core.addApiFunctionByName("UnloadSound");
core.addApiFunctionByName("ExportWave");
// core.addApiFunctionByName("ExportWaveAsCode")
// Wave/Sound management functions
@ -1582,7 +1668,7 @@ function main() {
core.addApiFunctionByName("LoadMusicStream");
// core.addApiFunctionByName("LoadMusicStreamFromMemory")
core.addApiFunctionByName("IsMusicReady");
// "UnloadMusicStream" called by finalizer
core.addApiFunctionByName("UnloadMusicStream");
core.addApiFunctionByName("PlayMusicStream");
core.addApiFunctionByName("IsMusicStreamPlaying");
core.addApiFunctionByName("UpdateMusicStream");
@ -1742,6 +1828,9 @@ function main() {
api.enums.find(x => x.name === "CameraMode")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description));
api.enums.find(x => x.name === "ShaderLocationIndex")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description));
api.enums.find(x => x.name === "ShaderUniformDataType")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description));
api.enums.find(x => x.name === "MaterialMapIndex")?.values.forEach(x => core.exportGlobalConstant(x.name, x.description));
core.exportGlobalConstant("MATERIAL_MAP_DIFFUSE", "Albedo material (same as: MATERIAL_MAP_DIFFUSE");
core.exportGlobalConstant("MATERIAL_MAP_SPECULAR", "Metalness material (same as: MATERIAL_MAP_SPECULAR)");
core.writeTo("src/bindings/js_raylib_core.h");
core.typings.writeTo("examples/lib.raylib.d.ts");
}

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,13 @@ int app_update_quickjs(){
return 0;
}
void SetModelMaterial(Model *model, int materialIndex, Material material)
{
if(model->materialCount <= materialIndex) return;
UnloadMaterial(model->materials[materialIndex]);
model->materials[materialIndex] = material;
}
#include "bindings/js_raylib_core.h"
int app_init_quickjs(int argc, char** argv){

2
thirdparty/raylib vendored

@ -1 +1 @@
Subproject commit 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62
Subproject commit a48bb6e1ed7b33190e486ba65b7875f0dff73701