379 lines
14 KiB
C
379 lines
14 KiB
C
/*
|
|
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 texCoordBuffer;
|
|
GlId vao;
|
|
GlId dotTexture;
|
|
GlId testTexture;
|
|
|
|
GlId vertexShader;
|
|
GlId fragmentShader;
|
|
GlId testShader;
|
|
GlId texture1Location;
|
|
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) bool App_Initialize()
|
|
{
|
|
InitializeCWasm(Kilobytes(128));
|
|
memset(&app, 0x00, sizeof(app));
|
|
|
|
#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
|
|
|
|
ScratchBegin(scratch);
|
|
|
|
PrintLine_D("GL_VERSION: \"%s\"", jsGlGetParameterString(scratch, GL_VERSION));
|
|
PrintLine_D("GL_VENDOR: \"%s\"", jsGlGetParameterString(scratch, GL_VENDOR));
|
|
|
|
r32 positions[] = {
|
|
0.0, 0.0,
|
|
1.0, 0.0,
|
|
0.0, 1.0,
|
|
|
|
1.0, 1.0,
|
|
0.0, 1.0,
|
|
1.0, 0.0
|
|
};
|
|
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, 255, 255,
|
|
255, 255, 255,
|
|
255, 255, 255,
|
|
|
|
255, 255, 255,
|
|
255, 255, 255,
|
|
255, 255, 255,
|
|
};
|
|
app.colorBuffer = jsGlCreateBuffer();
|
|
jsGlBindBuffer(GL_ARRAY_BUFFER, app.colorBuffer);
|
|
jsGlBufferData(GL_ARRAY_BUFFER, sizeof(colors), &colors[0], GL_STATIC_DRAW);
|
|
|
|
r32 texCoords[] = {
|
|
// U, V
|
|
0.0f, 0.0f,
|
|
1.0f, 0.0f,
|
|
0.0f, 1.0f,
|
|
|
|
1.0f, 1.0f,
|
|
0.0f, 1.0f,
|
|
1.0f, 0.0f,
|
|
};
|
|
app.texCoordBuffer = jsGlCreateBuffer();
|
|
jsGlBindBuffer(GL_ARRAY_BUFFER, app.texCoordBuffer);
|
|
jsGlBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), &texCoords[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,
|
|
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, //stride
|
|
0 //offset
|
|
);
|
|
// texCoord attribute data
|
|
jsGlBindBuffer(GL_ARRAY_BUFFER, app.texCoordBuffer);
|
|
jsGlEnableVertexAttribArray(2);
|
|
jsGlVertexAttribPointer(
|
|
2, // attrib location
|
|
2, // components per element:
|
|
GL_FLOAT,
|
|
false, // not normalized
|
|
0, //stride
|
|
0 //offset
|
|
);
|
|
jsGlBindVertexArray(0); // end "recording"
|
|
|
|
app.dotTexture = jsGlCreateTexture();
|
|
jsGlActiveTexture(GL_TEXTURE0);
|
|
jsGlBindTexture(GL_TEXTURE_2D, app.dotTexture);
|
|
u32 dotPixel = 0xFFFFFFFF;
|
|
jsGlTexImage2D(
|
|
GL_TEXTURE_2D, //bound texture type
|
|
0, //image level
|
|
GL_RGBA, //internal format
|
|
1, //image width
|
|
1, //image height
|
|
0, //border
|
|
GL_RGBA, //format
|
|
GL_UNSIGNED_BYTE, //type
|
|
sizeof(dotPixel), //dataLength
|
|
&dotPixel //dataPntr
|
|
);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
jsGlGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
app.testTexture = jsGlCreateTexture();
|
|
jsGlActiveTexture(GL_TEXTURE0);
|
|
jsGlBindTexture(GL_TEXTURE_2D, app.testTexture);
|
|
u32 testPixels[] = {
|
|
0xFFEEEEEE, 0xFFDDDDEE, 0xFFCCCCEE, 0xFFBBBBEE, 0xFFAAAAEE, 0xFF9999EE, 0xFF8888EE, 0xFF7777EE, 0xFF6666EE, 0xFF5555EE,
|
|
0xFFEEEEDD, 0xFFDDDDDD, 0xFFCCCCDD, 0xFFBBBBDD, 0xFFAAAADD, 0xFF9999DD, 0xFF8888DD, 0xFF7777DD, 0xFF6666DD, 0xFF5555DD,
|
|
0xFFEEEECC, 0xFFDDDDCC, 0xFFCCCCCC, 0xFFBBBBCC, 0xFFAAAACC, 0xFF9999CC, 0xFF8888CC, 0xFF7777CC, 0xFF6666CC, 0xFF5555CC,
|
|
0xFFEEEEBB, 0xFFDDDDBB, 0xFFCCCCBB, 0xFFBBBBBB, 0xFFFF0000, 0xFF9999BB, 0xFF8888BB, 0xFF7777BB, 0xFF6666BB, 0xFF5555BB,
|
|
0xFFEEEEAA, 0xFFDDDDAA, 0xFFCCCCAA, 0xFFBBBBAA, 0xFF00FF00, 0xFF9999AA, 0xFF8888AA, 0xFF7777AA, 0xFF6666AA, 0xFF5555AA,
|
|
0xFFEEEE99, 0xFFDDDD99, 0xFFCCCC99, 0xFFBBBB99, 0xFF0000FF, 0xFF999999, 0xFF888899, 0xFF777799, 0xFF666699, 0xFF555599,
|
|
0xFFEEEE88, 0xFFDDDD88, 0xFFCCCC88, 0xFFBBBB88, 0xFFAAAA88, 0xFF999988, 0xFF888888, 0xFF777788, 0xFF666688, 0xFF555588,
|
|
0xFFEEEE77, 0xFFDDDD77, 0xFFCCCC77, 0xFFBBBB77, 0xFFAAAA77, 0xFF999977, 0xFF888877, 0xFF777777, 0xFF666677, 0xFF555577,
|
|
0xFFEEEE66, 0xFFDDDD66, 0xFFCCCC66, 0xFFBBBB66, 0xFFAAAA66, 0xFF999966, 0xFF888866, 0xFF777766, 0xFF666666, 0xFF555566,
|
|
0xFFEEEE55, 0xFFDDDD55, 0xFFCCCC55, 0xFFBBBB55, 0xFFAAAA55, 0xFF999955, 0xFF888855, 0xFF777755, 0xFF666655, 0xFF555555,
|
|
};
|
|
jsGlTexImage2D(
|
|
GL_TEXTURE_2D, //bound texture type
|
|
0, //image level
|
|
GL_RGBA, //internal format
|
|
10, //image width
|
|
10, //image height
|
|
0, //border
|
|
GL_RGBA, //format
|
|
GL_UNSIGNED_BYTE, //type
|
|
sizeof(testPixels), //dataLength
|
|
&testPixels //dataPntr
|
|
);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
jsGlGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
app.vertexShader = jsGlCreateShader(GL_VERTEX_SHADER);
|
|
jsGlShaderSource(app.vertexShader, (int)strlen(VertexShaderCodeStr), VertexShaderCodeStr);
|
|
jsGlCompileShader(app.vertexShader);
|
|
if (!jsGlGetShaderParameterBool(app.vertexShader, GL_COMPILE_STATUS)) { PrintLine_E("Failed to compile vertex shader: \"%s\"", jsGlGetShaderInfoLog(scratch, app.vertexShader)); return false; }
|
|
app.fragmentShader = jsGlCreateShader(GL_FRAGMENT_SHADER);
|
|
jsGlShaderSource(app.fragmentShader, (int)strlen(FragmentShaderCodeStr), FragmentShaderCodeStr);
|
|
jsGlCompileShader(app.fragmentShader);
|
|
if (!jsGlGetShaderParameterBool(app.fragmentShader, GL_COMPILE_STATUS)) { PrintLine_E("Failed to compile fragment shader: \"%s\"", jsGlGetShaderInfoLog(scratch, app.fragmentShader)); return false; }
|
|
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)) { PrintLine_E("Failed to link shader program: \"%s\"", jsGlGetProgramInfoLog(scratch, app.testShader)); return false; }
|
|
|
|
// const char* uniformName = "TestUniform";
|
|
// app.testUniformLocation = jsGlGetUniformLocation(app.testShader, (int)strlen(uniformName), uniformName);
|
|
const char* texture1Name = "Texture1";
|
|
app.texture1Location = jsGlGetUniformLocation(app.testShader, (int)strlen(texture1Name), texture1Name);
|
|
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);
|
|
|
|
ScratchEnd(scratch);
|
|
return true;
|
|
}
|
|
|
|
// +--------------------------------------------------------------+
|
|
// | Close |
|
|
// +--------------------------------------------------------------+
|
|
WASM_EXPORT(App_Close) void App_Close()
|
|
{
|
|
jsGlDeleteProgram(app.testShader);
|
|
jsGlDeleteShader(app.vertexShader);
|
|
jsGlDeleteShader(app.fragmentShader);
|
|
jsGlDeleteVertexArray(app.vao);
|
|
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
|
|
jsGlActiveTexture(GL_TEXTURE0);
|
|
jsGlBindTexture(GL_TEXTURE_2D, app.testTexture);
|
|
|
|
mat4 identityMatrix = Mat4_Identity;
|
|
jsGlUniformMatrix4fv(app.viewMatrixLocation, &identityMatrix);
|
|
jsGlUniformMatrix4fv(app.projMatrixLocation, &identityMatrix);
|
|
|
|
#if 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, 6);
|
|
}
|
|
#endif
|
|
|
|
{
|
|
r32 offsetX = -0.4f;
|
|
r32 offsetY = -0.4f;
|
|
r32 scaleX = 0.8f;
|
|
r32 scaleY = 0.8f;
|
|
#if 1
|
|
mat4 worldMatrix = Mat4_Identity_Const;
|
|
worldMatrix = MulMat4(MakeScaleMat4(scaleX, scaleY, 1.0f), worldMatrix);
|
|
worldMatrix = MulMat4(MakeTranslateMat4(offsetX, offsetY, 0.0f), worldMatrix);
|
|
#else
|
|
mat4 worldMatrix = NewMat4_Const(
|
|
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
|
|
);
|
|
#endif
|
|
jsGlUniformMatrix4fv(app.worldMatrixLocation, &worldMatrix);
|
|
jsGlDrawArrays(GL_TRIANGLES, 0, 6);
|
|
}
|
|
|
|
// if (programTime > 5000) { shouldContinue = false; }
|
|
|
|
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 texCoord attrib at index: 2\n"
|
|
"layout(location=2) in vec2 aTexCoord;\n"
|
|
"\n"
|
|
"// this is the interpolate color which is\n"
|
|
"// passed to the fragment shader\n"
|
|
"out vec3 vCol;\n"
|
|
"\n"
|
|
"out vec2 vTexCoord;\n"
|
|
"\n"
|
|
"void main(){\n"
|
|
" vCol = aCol; // just pass through the value \n"
|
|
" vTexCoord = aTexCoord; // 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 sampler2D Texture1;\n"
|
|
"\n"
|
|
"in vec3 vCol; // the data from vertex shader\n"
|
|
"in vec2 vTexCoord; // 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"
|
|
" vec4 sampleColor = texture(Texture1, vTexCoord);\n"
|
|
" outCol = vec4(vCol, 1.0) * sampleColor;\n"
|
|
"}\n";
|