/* File: test_app.c Author: Taylor Robbins Date: 08\28\2025 Description: ** When no other application is present, this serves as a simple test case for the CWasm layer */ #include "cwasm.c" struct { GlId positionBuffer; GlId colorBuffer; GlId vao; GlId vertexShader; GlId fragmentShader; GlId testShader; GlId testUniformLocation; GlId worldMatrixLocation; GlId viewMatrixLocation; GlId projMatrixLocation; u32 frameIndex; r64 prevProgramTimeR64; } app; static const char* VertexShaderCodeStr; static const char* FragmentShaderCodeStr; r32 OscillateBy(u64 timeSource, r32 min, r32 max, u64 periodMs, u64 offset) { r32 lerpValue = (sinf((((timeSource + offset) % periodMs) / (r32)periodMs) * 2*Pi32) + 1.0f) / 2.0f; return min + (max - min) * lerpValue; } // +--------------------------------------------------------------+ // | Initialize | // +--------------------------------------------------------------+ WASM_EXPORT(App_Initialize) void App_Initialize() { InitializeCWasm(Kilobytes(128)); #if 0 Write_D("Hello\nWorld!"); PrintLine_I(" Fuzzy %u Bunnies!\n%s", 31415926, "What"); WriteLine_D(""); WriteLine_W("When"); Write_D("\n"); WriteLine_E("Where"); #endif memset(&app, 0x00, sizeof(app)); r32 positions[] = { -0.7, -0.7, // left bottom corner 0.7, -0.7, // right bottom corner 0.0, 0.7 // center top corner }; app.positionBuffer = jsGlCreateBuffer(); jsGlBindBuffer(GL_ARRAY_BUFFER, app.positionBuffer); jsGlBufferData(GL_ARRAY_BUFFER, sizeof(positions), &positions[0], GL_STATIC_DRAW); u8 colors[] = { // R , G , B 255, 0, 0, // red 0, 255, 0, // green 0, 0, 255 // blue }; app.colorBuffer = jsGlCreateBuffer(); jsGlBindBuffer(GL_ARRAY_BUFFER, app.colorBuffer); jsGlBufferData(GL_ARRAY_BUFFER, sizeof(colors), &colors[0], GL_STATIC_DRAW); app.vao = jsGlCreateVertexArray(); jsGlBindVertexArray(app.vao); // start "recording" // position attribute data jsGlBindBuffer(GL_ARRAY_BUFFER, app.positionBuffer); jsGlEnableVertexAttribArray(0); jsGlVertexAttribPointer( 0, // attrib location 2, // components per element: vec2 for our postition data GL_FLOAT, // buffer data type: we have Float32Array false, // whether the data is normalized to 0.0 1.0 range in shaders 0, // stride, not important atm 0 // offset, not important atm ); // color attribute data jsGlBindBuffer(GL_ARRAY_BUFFER, app.colorBuffer); jsGlEnableVertexAttribArray(1); jsGlVertexAttribPointer( 1, // attrib location 3, // components per element: GL_UNSIGNED_BYTE, // we have Uint8Array true, // the 0..255 is normalized into 0.0...1.0 in shaders 0, 0 ); jsGlBindVertexArray(0); // end "recording" app.vertexShader = jsGlCreateShader(GL_VERTEX_SHADER); jsGlShaderSource(app.vertexShader, (int)strlen(VertexShaderCodeStr), VertexShaderCodeStr); jsGlCompileShader(app.vertexShader); if (!jsGlGetShaderParameterBool(app.vertexShader, GL_COMPILE_STATUS)) { WriteLine_E("Failed to compile vertex shader!"); } //TODO: jsGlGetShaderInfoLog app.fragmentShader = jsGlCreateShader(GL_FRAGMENT_SHADER); jsGlShaderSource(app.fragmentShader, (int)strlen(FragmentShaderCodeStr), FragmentShaderCodeStr); jsGlCompileShader(app.fragmentShader); if (!jsGlGetShaderParameterBool(app.fragmentShader, GL_COMPILE_STATUS)) { WriteLine_E("Failed to compile fragment shader!"); } //TODO: jsGlGetShaderInfoLog app.testShader = jsGlCreateProgram(); jsGlAttachShader(app.testShader, app.vertexShader); jsGlAttachShader(app.testShader, app.fragmentShader); jsGlLinkProgram(app.testShader); // also debug the program status if (!jsGlGetProgramParameterBool(app.testShader, GL_LINK_STATUS)) { WriteLine_E("Failed to link shader program!"); } //TODO: jsGlGetProgramInfoLog const char* uniformName = "TestUniform"; app.testUniformLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(uniformName), uniformName); const char* worldMatrixName = "WorldMatrix"; app.worldMatrixLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(worldMatrixName), worldMatrixName); const char* viewMatrixName = "ViewMatrix"; app.viewMatrixLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(viewMatrixName), viewMatrixName); const char* projMatrixName = "ProjMatrix"; app.projMatrixLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(projMatrixName), projMatrixName); } // +--------------------------------------------------------------+ // | Close | // +--------------------------------------------------------------+ WASM_EXPORT(App_Close) void App_Close() { //TODO: Delete vao jsGlDeleteProgram(app.testShader); jsGlDeleteShader(app.vertexShader); jsGlDeleteShader(app.fragmentShader); jsGlDeleteBuffer(app.positionBuffer); jsGlDeleteBuffer(app.colorBuffer); } // +--------------------------------------------------------------+ // | Update and Render | // +--------------------------------------------------------------+ WASM_EXPORT(App_UpdateAndRender) bool App_UpdateAndRender(r64 programTimeR64) { bool shouldContinue = true; r64 timeScaleR64 = (programTimeR64 - app.prevProgramTimeR64) / (1000.0 / 60.0); if (fabs(timeScaleR64 - 1.0) < 0.001) { timeScaleR64 = 1.0; } if (timeScaleR64 > 4.0) { timeScaleR64 = 4.0; } if (timeScaleR64 < 0.0) { timeScaleR64 = 0.0; } r32 timeScale = (r32)timeScaleR64; r32 programTimef = (r32)programTimeR64; u64 programTime = (u64)programTimeR64; u64 elapsedMs = programTime - (u64)app.prevProgramTimeR64; // jsGlClearColor(OscillateBy(programTime, 0.0f, 1.0f, 3700, 1500), OscillateBy(programTime, 0.0f, 1.0f, 5300, 2000), OscillateBy(programTime, 0.0f, 1.0f, 2300, 500), 1.0f); jsGlClearColor(32/255.0f, 32/255.0f, 32/255.0f, 1.0f); jsGlClear(GL_COLOR_BUFFER_BIT); jsGlBindVertexArray(app.vao); // our vertex array object jsGlUseProgram(app.testShader); // our shader program r32 identityMatrix[] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; jsGlUniformMatrix4fv(app.viewMatrixLocation, &identityMatrix[0]); jsGlUniformMatrix4fv(app.projMatrixLocation, &identityMatrix[0]); const u64 numTris = 75; for (u64 tIndex = 0; tIndex < numTris; tIndex++) { r32 uniformValues[] = { OscillateBy(programTime, 0.0f, 1.0f, 2000, 0 + tIndex*(2000/numTris)), OscillateBy(programTime, 0.0f, 1.0f, 2000, 1200 + tIndex*(2000/numTris)), OscillateBy(programTime, 0.0f, 1.0f, 2000, 700 + tIndex*(2000/numTris)), }; jsGlUniform1fv(app.testUniformLocation, ArrayCount(uniformValues), &uniformValues[0]); r32 offsetX = OscillateBy(programTime, -0.1f, 0.1f, 3000, 0 + tIndex*(3000/numTris)); r32 offsetY = OscillateBy(programTime, -0.1f, 0.1f, 3000, 750 + tIndex*(3000/numTris)); r32 scaleX = OscillateBy(programTime, 0.9f - tIndex*(0.9f/numTris), 1.1f - tIndex*(0.9f/numTris), 3000, 0 + tIndex*(3000/numTris)); r32 scaleY = OscillateBy(programTime, 0.9f - tIndex*(0.9f/numTris), 1.1f - tIndex*(0.9f/numTris), 3000, 1500 + tIndex*(3000/numTris)); r32 worldMatrix[] = { scaleX, 0.0f, 0.0f, offsetX, 0.0f, scaleY, 0.0f, offsetY, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; jsGlUniformMatrix4fv(app.worldMatrixLocation, &worldMatrix[0]); jsGlDrawArrays(GL_TRIANGLES, 0, 3); } app.frameIndex++; app.prevProgramTimeR64 = programTimeR64; return shouldContinue; } // +--------------------------------------------------------------+ // | Shaders | // +--------------------------------------------------------------+ static const char* VertexShaderCodeStr = "#version 300 es\n" "// ^^^\n" "// the version definition has to be the first line in\n" "// the string.\n" "\n" "// sets the precision level for all float and vec\n" "// data types\n" "precision highp float;\n" "\n" "uniform mat4 WorldMatrix;\n" "uniform mat4 ViewMatrix;\n" "uniform mat4 ProjMatrix;\n" "\n" "// this is the vertex attribute at index 0\n" "// which we defined in the vertex array object.\n" "// we can use any name for this in the glsl code\n" "// the important bit is the location=0\n" "layout(location=0) in vec2 aPos;\n" "\n" "// this is the color attrib at index: 1\n" "layout(location=1) in vec3 aCol;\n" "\n" "// this is the interpolate color which is\n" "// passed to the fragment shader\n" "out vec3 vCol;\n" "\n" "void main(){\n" " vCol = aCol; // just pass through the value \n" " \n" " // vertex position for the shader program\n" " // always a vec4 value\n" " gl_Position = ((vec4(aPos, 0.0, 1.0) * WorldMatrix) * ViewMatrix) * ProjMatrix;\n" "}\n"; static const char* FragmentShaderCodeStr = "#version 300 es\n" "precision highp float;\n" "\n" "uniform float TestUniform[3];\n" "\n" "in vec3 vCol; // the data from vertex shader\n" "\n" "// fragment output value\n" "// essentially the color of the output pixel\n" "out vec4 outCol;\n" "\n" "void main(){\n" " outCol = vec4(vec3(vCol.r * TestUniform[0], vCol.g * TestUniform[1], vCol.b * TestUniform[2]), 1.0);\n" "}\n";