Added an asynchronous resource loading mechanism through App_GetResourcePath and App_ResourceLoaded. Added stb_image.h and used it to parse a .png file loaded from the server and upload it to app.testTexture. There is some ugliness around the edges images with transparency, probably caused by something pre-multiplied alpha related.
This commit is contained in:
@@ -443,6 +443,7 @@
|
||||
"void jsGlBindBuffer(GlEnum bufferType, GlId bufferId)",
|
||||
"void jsGlBindTexture(GlEnum target, GlId textureId)",
|
||||
"void jsGlBindVertexArray(GlId vaoId)",
|
||||
"void jsGlBlendEquation(GlEnum equation)",
|
||||
"void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor)",
|
||||
"void jsGlBlendFuncSeparate(GlEnum srcRGB, GlEnum dstRGB, GlEnum srcAlpha, GlEnum dstAlpha)",
|
||||
"void jsGlBufferData(GlEnum bufferType, u32 dataLength, const void* dataPntr, GlEnum usageHint)",
|
||||
@@ -462,6 +463,7 @@
|
||||
"void jsGlFrontFace(GlEnum cullMode)",
|
||||
"void jsGlGenerateMipmap(GlEnum target)",
|
||||
"void jsGlLinkProgram(GlId programId)",
|
||||
"void jsGlPixelStorei(GlEnum parameter, int value)",
|
||||
"void jsGlShaderSource(GlId shaderId, int sourceLength, const char* sourcePntr)",
|
||||
"void jsGlTexImage2D(GlEnum target, GlEnum level, GlEnum internalFormat, int width, int height, int border, GlEnum format, GlEnum type, int dataLength, const void* dataPntr)",
|
||||
"void jsGlTexParameteri(GlEnum target, GlEnum parameter, int value)",
|
||||
|
||||
27
cwasm.c
27
cwasm.c
@@ -18,6 +18,18 @@ Description:
|
||||
#include "cwasm_webgl_js_imports.h"
|
||||
#include "cwasm_webgl_constants.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBIDEF static
|
||||
#define STBI_NO_STDIO
|
||||
#define STBI_ASSERT(expression) Assert(expression)
|
||||
#define STBI_MALLOC(numBytes) AllocMem(&ScratchArenas[0], numBytes)
|
||||
#define STBI_REALLOC_SIZED(allocPntr, oldSize, newSize) ReallocMem(&ScratchArenas[0], (allocPntr), (oldSize), (newSize))
|
||||
#define STBI_FREE(allocPntr) //nothing TODO: Once we have a general purpose arena we can implement this!
|
||||
#define STBI_NO_SIMD
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_THREAD_LOCALS
|
||||
#include "stb_image.h"
|
||||
|
||||
void InitializeCWasm(u32 scratchArenasSize)
|
||||
{
|
||||
InitGlobalArenas(scratchArenasSize);
|
||||
@@ -30,3 +42,18 @@ WASM_EXPORT(cAllocMem) void* cAllocMem(Arena* arenaPntr, int numBytes, int align
|
||||
Assert(alignment >= 0);
|
||||
return AllocMemAligned(arenaPntr, (u32)numBytes, (u32)alignment);
|
||||
}
|
||||
|
||||
WASM_EXPORT(cGetScratchArenaPntr) Arena* cGetScratchArenaPntr(Arena* conflictArenaPntr)
|
||||
{
|
||||
ArenaMark scratchMark = GetScratch1(conflictArenaPntr);
|
||||
return scratchMark.arena;
|
||||
}
|
||||
WASM_EXPORT(cGetScratchArenaMark) u32 cGetScratchArenaMark(Arena* conflictArenaPntr)
|
||||
{
|
||||
ArenaMark scratchMark = GetScratch1(conflictArenaPntr);
|
||||
return scratchMark.mark;
|
||||
}
|
||||
WASM_EXPORT(cEndScratchArena) void cEndScratchArena(Arena* arenaPntr, u32 mark)
|
||||
{
|
||||
ResetToMark(arenaPntr, mark);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ MAYBE_EXTERN_C void jsGlEnable(GlEnum capability);
|
||||
MAYBE_EXTERN_C void jsGlDisable(GlEnum capability);
|
||||
MAYBE_EXTERN_C void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor);
|
||||
MAYBE_EXTERN_C void jsGlBlendFuncSeparate(GlEnum srcRGB, GlEnum dstRGB, GlEnum srcAlpha, GlEnum dstAlpha);
|
||||
MAYBE_EXTERN_C void jsGlBlendEquation(GlEnum equation);
|
||||
MAYBE_EXTERN_C void jsGlDepthFunc(GlEnum depthFunc);
|
||||
MAYBE_EXTERN_C void jsGlFrontFace(GlEnum cullMode);
|
||||
MAYBE_EXTERN_C void jsGlDeleteBuffer(GlId bufferId);
|
||||
@@ -30,6 +31,7 @@ MAYBE_EXTERN_C void jsGlDeleteTexture(GlId textureId);
|
||||
MAYBE_EXTERN_C GlId jsGlCreateTexture();
|
||||
MAYBE_EXTERN_C void jsGlActiveTexture(GlEnum textureIndex);
|
||||
MAYBE_EXTERN_C void jsGlBindTexture(GlEnum target, GlId textureId);
|
||||
MAYBE_EXTERN_C void jsGlPixelStorei(GlEnum parameter, int value);
|
||||
MAYBE_EXTERN_C void jsGlTexImage2D(GlEnum target, GlEnum level, GlEnum internalFormat, int width, int height, int border, GlEnum format, GlEnum type, int dataLength, const void* dataPntr);
|
||||
MAYBE_EXTERN_C void jsGlTexParameteri(GlEnum target, GlEnum parameter, int value);
|
||||
MAYBE_EXTERN_C void jsGlGenerateMipmap(GlEnum target);
|
||||
|
||||
@@ -133,6 +133,10 @@ export function jsGlBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
|
||||
{
|
||||
appGlobals.glContext.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
export function jsGlBlendEquation(equation)
|
||||
{
|
||||
appGlobals.glContext.blendEquation(equation);
|
||||
}
|
||||
|
||||
export function jsGlDepthFunc(depthFunc)
|
||||
{
|
||||
@@ -203,6 +207,11 @@ export function jsGlBindTexture(target, textureId)
|
||||
appGlobals.glContext.bindTexture(target, texture);
|
||||
}
|
||||
|
||||
export function jsGlPixelStorei(parameter, value)
|
||||
{
|
||||
appGlobals.glContext.pixelStorei(parameter, value);
|
||||
}
|
||||
|
||||
export function jsGlTexImage2D(target, level, internalFormat, width, height, border, format, type, dataLength, dataPntr)
|
||||
{
|
||||
let dataBuffer = new Uint8Array(appGlobals.memDataView.buffer, dataPntr, dataLength);
|
||||
@@ -563,6 +572,7 @@ export let jsGlFunctions = {
|
||||
jsGlDisable: jsGlDisable,
|
||||
jsGlBlendFunc: jsGlBlendFunc,
|
||||
jsGlBlendFuncSeparate: jsGlBlendFuncSeparate,
|
||||
jsGlBlendEquation: jsGlBlendEquation,
|
||||
jsGlDepthFunc: jsGlDepthFunc,
|
||||
jsGlFrontFace: jsGlFrontFace,
|
||||
jsGlDeleteBuffer: jsGlDeleteBuffer,
|
||||
@@ -573,6 +583,7 @@ export let jsGlFunctions = {
|
||||
jsGlCreateTexture: jsGlCreateTexture,
|
||||
jsGlActiveTexture: jsGlActiveTexture,
|
||||
jsGlBindTexture: jsGlBindTexture,
|
||||
jsGlPixelStorei: jsGlPixelStorei,
|
||||
jsGlTexImage2D: jsGlTexImage2D,
|
||||
jsGlTexParameteri: jsGlTexParameteri,
|
||||
jsGlGenerateMipmap: jsGlGenerateMipmap,
|
||||
|
||||
49
data/main.js
49
data/main.js
@@ -51,10 +51,22 @@ async function MainLoop()
|
||||
|
||||
console.log("Loading WASM Module...");
|
||||
await LoadWasmModule("app.wasm", 4);
|
||||
|
||||
let initSuccess = appGlobals.wasmModule.exports.App_Initialize();
|
||||
|
||||
if (initSuccess)
|
||||
{
|
||||
var resourcePaths = [];
|
||||
var resourceIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
let resourcePathPntr = appGlobals.wasmModule.exports.App_GetResourcePath(resourceIndex);
|
||||
if (resourcePathPntr == 0) { break; }
|
||||
let resourcePathStr = wasmPntrToJsString(resourcePathPntr);
|
||||
resourcePaths.push(resourcePathStr);
|
||||
resourceIndex++;
|
||||
}
|
||||
|
||||
console.log("Running!");
|
||||
function renderFrame(currentTime)
|
||||
{
|
||||
@@ -63,6 +75,43 @@ async function MainLoop()
|
||||
else { appGlobals.wasmModule.exports.App_Close(); }
|
||||
}
|
||||
window.requestAnimationFrame(renderFrame);
|
||||
|
||||
if (resourcePaths.length > 0)
|
||||
{
|
||||
console.log("Loading " + resourcePaths.length + " Resource" + (resourcePaths.length == 1 ? "" : "s") + "...");
|
||||
// console.dir(resourcePaths);
|
||||
for (var rIndex = 0; rIndex < resourcePaths.length; rIndex++)
|
||||
{
|
||||
const file = await fetch(resourcePaths[rIndex]);
|
||||
if (file.ok)
|
||||
{
|
||||
const fileBytes = await file.arrayBuffer();
|
||||
// console.log(fileBytes);
|
||||
var fileBytesPntr = 0;
|
||||
var scratchArenaPntr = 0;
|
||||
var scratchArenaMark = 0;
|
||||
if (fileBytes.byteLength > 0)
|
||||
{
|
||||
scratchArenaPntr = appGlobals.wasmModule.exports.cGetScratchArenaPntr(0);
|
||||
scratchArenaMark = appGlobals.wasmModule.exports.cGetScratchArenaMark(0);
|
||||
fileBytesPntr = appGlobals.wasmModule.exports.cAllocMem(scratchArenaPntr, fileBytes.byteLength);
|
||||
if (fileBytesPntr != 0)
|
||||
{
|
||||
const writeArray = new Uint8Array(appGlobals.wasmModule.exports.memory.buffer);
|
||||
writeArray.set(new Uint8Array(fileBytes), fileBytesPntr);
|
||||
}
|
||||
else { console.error("Failed to allocate " + fileBytes.byteLength + " byte resource[" + rIndex + "] from \"" + resourcePaths[rIndex] + "\""); }
|
||||
}
|
||||
appGlobals.wasmModule.exports.App_ResourceLoaded(rIndex, fileBytes.byteLength, fileBytesPntr);
|
||||
if (fileBytes.byteLength > 0 && fileBytesPntr != 0) { appGlobals.wasmModule.exports.cEndScratchArena(scratchArenaPntr, scratchArenaMark); }
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error("Failed to fetch resource[" + rIndex + "] from \"" + resourcePaths[rIndex] + "\"");
|
||||
appGlobals.wasmModule.exports.App_ResourceLoaded(rIndex, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
7988
stb_image.h
Normal file
7988
stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
88
test_app.c
88
test_app.c
@@ -38,12 +38,94 @@ r32 OscillateBy(u64 timeSource, r32 min, r32 max, u64 periodMs, u64 offset)
|
||||
return min + (max - min) * lerpValue;
|
||||
}
|
||||
|
||||
// +--------------------------------------------------------------+
|
||||
// | Load Resources |
|
||||
// +--------------------------------------------------------------+
|
||||
WASM_EXPORT(App_GetResourcePath) const char* App_GetResourcePath(int resourceIndex)
|
||||
{
|
||||
switch (resourceIndex)
|
||||
{
|
||||
case 0: return "icon_64.png";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT(App_ResourceLoaded) void App_ResourceLoaded(int resourceIndex, int fileSize, u8* fileBytes)
|
||||
{
|
||||
switch (resourceIndex)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
PrintLine_D("Got resource %d bytes: %p %02X %02X %02X %02X", fileSize, fileBytes, fileBytes[0], fileBytes[1], fileBytes[2], fileBytes[3]);
|
||||
ScratchBegin(scratch); //NOTE: stbi_load_from_memory implicitly allocates from the first scratch arena
|
||||
int imageWidth = 0;
|
||||
int imageHeight = 0;
|
||||
int imageChannelCount = 0;
|
||||
stbi_uc* textureBytes = stbi_load_from_memory(fileBytes, fileSize, &imageWidth, &imageHeight, &imageChannelCount, 4);
|
||||
if (textureBytes != nullptr)
|
||||
{
|
||||
PrintLine_D("Parsed image: %p %dx%d %d channel(s)", textureBytes, imageWidth, imageHeight, imageChannelCount);
|
||||
|
||||
//We need to reverse the rows of pixels so the image is not upside-down
|
||||
//TODO: Replace with GL_UNPACK_FLIP_Y_WEBGL
|
||||
u32 rowWidth = imageWidth * sizeof(u32);
|
||||
u8* tempRowBuffer = AllocArray(u8, scratch, rowWidth);
|
||||
for (int yIndex = 0; yIndex < imageHeight/2; yIndex++)
|
||||
{
|
||||
memcpy(tempRowBuffer, &textureBytes[yIndex * rowWidth], rowWidth);
|
||||
memcpy(&textureBytes[yIndex * rowWidth], &textureBytes[(imageHeight-1 - yIndex) * rowWidth], rowWidth);
|
||||
memcpy(&textureBytes[(imageHeight-1 - yIndex) * rowWidth], tempRowBuffer, rowWidth);
|
||||
}
|
||||
#if 0
|
||||
for (int yIndex = 0; yIndex < imageHeight; yIndex++)
|
||||
{
|
||||
for (int xIndex = 0; xIndex < imageWidth; xIndex++)
|
||||
{
|
||||
u8* pixelPntr = &textureBytes[((yIndex * imageWidth) + xIndex) * sizeof(u32)];
|
||||
r32 alpha = (pixelPntr[3]/255.0f);
|
||||
pixelPntr[0] = (u8)((pixelPntr[0]/255.0f) * alpha * 255.0f);
|
||||
pixelPntr[1] = (u8)((pixelPntr[1]/255.0f) * alpha * 255.0f);
|
||||
pixelPntr[2] = (u8)((pixelPntr[2]/255.0f) * alpha * 255.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (app.testTexture != 0) { jsGlDeleteTexture(app.testTexture); }
|
||||
app.testTexture = jsGlCreateTexture();
|
||||
jsGlActiveTexture(GL_TEXTURE0);
|
||||
jsGlBindTexture(GL_TEXTURE_2D, app.testTexture);
|
||||
// jsGlPixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
||||
// jsGlPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, true);
|
||||
jsGlTexImage2D(
|
||||
GL_TEXTURE_2D, //bound texture type
|
||||
0, //image level
|
||||
GL_RGBA, //internal format
|
||||
imageWidth, //image width
|
||||
imageHeight, //image height
|
||||
0, //border
|
||||
GL_RGBA, //format
|
||||
GL_UNSIGNED_BYTE, //type
|
||||
imageWidth*imageHeight*sizeof(u32), //dataLength
|
||||
textureBytes //dataPntr
|
||||
);
|
||||
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
jsGlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
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);
|
||||
}
|
||||
else { PrintLine_E("Failed to parse %d byte image using stb_image.h!", fileSize); }
|
||||
ScratchEnd(scratch);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// +--------------------------------------------------------------+
|
||||
// | Initialize |
|
||||
// +--------------------------------------------------------------+
|
||||
WASM_EXPORT(App_Initialize) bool App_Initialize()
|
||||
{
|
||||
InitializeCWasm(Kilobytes(128));
|
||||
InitializeCWasm(Megabytes(2));
|
||||
memset(&app, 0x00, sizeof(app));
|
||||
|
||||
#if 0
|
||||
@@ -60,6 +142,10 @@ WASM_EXPORT(App_Initialize) bool App_Initialize()
|
||||
PrintLine_D("GL_VERSION: \"%s\"", jsGlGetParameterString(scratch, GL_VERSION));
|
||||
PrintLine_D("GL_VENDOR: \"%s\"", jsGlGetParameterString(scratch, GL_VENDOR));
|
||||
|
||||
jsGlEnable(GL_BLEND);
|
||||
jsGlBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
jsGlBlendEquation(GL_FUNC_ADD);
|
||||
|
||||
r32 positions[] = {
|
||||
0.0, 0.0,
|
||||
1.0, 0.0,
|
||||
|
||||
Reference in New Issue
Block a user