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 jsGlBindBuffer(GlEnum bufferType, GlId bufferId)",
|
||||||
"void jsGlBindTexture(GlEnum target, GlId textureId)",
|
"void jsGlBindTexture(GlEnum target, GlId textureId)",
|
||||||
"void jsGlBindVertexArray(GlId vaoId)",
|
"void jsGlBindVertexArray(GlId vaoId)",
|
||||||
|
"void jsGlBlendEquation(GlEnum equation)",
|
||||||
"void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor)",
|
"void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor)",
|
||||||
"void jsGlBlendFuncSeparate(GlEnum srcRGB, GlEnum dstRGB, GlEnum srcAlpha, GlEnum dstAlpha)",
|
"void jsGlBlendFuncSeparate(GlEnum srcRGB, GlEnum dstRGB, GlEnum srcAlpha, GlEnum dstAlpha)",
|
||||||
"void jsGlBufferData(GlEnum bufferType, u32 dataLength, const void* dataPntr, GlEnum usageHint)",
|
"void jsGlBufferData(GlEnum bufferType, u32 dataLength, const void* dataPntr, GlEnum usageHint)",
|
||||||
@@ -462,6 +463,7 @@
|
|||||||
"void jsGlFrontFace(GlEnum cullMode)",
|
"void jsGlFrontFace(GlEnum cullMode)",
|
||||||
"void jsGlGenerateMipmap(GlEnum target)",
|
"void jsGlGenerateMipmap(GlEnum target)",
|
||||||
"void jsGlLinkProgram(GlId programId)",
|
"void jsGlLinkProgram(GlId programId)",
|
||||||
|
"void jsGlPixelStorei(GlEnum parameter, int value)",
|
||||||
"void jsGlShaderSource(GlId shaderId, int sourceLength, const char* sourcePntr)",
|
"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 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)",
|
"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_js_imports.h"
|
||||||
#include "cwasm_webgl_constants.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)
|
void InitializeCWasm(u32 scratchArenasSize)
|
||||||
{
|
{
|
||||||
InitGlobalArenas(scratchArenasSize);
|
InitGlobalArenas(scratchArenasSize);
|
||||||
@@ -30,3 +42,18 @@ WASM_EXPORT(cAllocMem) void* cAllocMem(Arena* arenaPntr, int numBytes, int align
|
|||||||
Assert(alignment >= 0);
|
Assert(alignment >= 0);
|
||||||
return AllocMemAligned(arenaPntr, (u32)numBytes, (u32)alignment);
|
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 jsGlDisable(GlEnum capability);
|
||||||
MAYBE_EXTERN_C void jsGlBlendFunc(GlEnum srcFactor, GlEnum dstFactor);
|
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 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 jsGlDepthFunc(GlEnum depthFunc);
|
||||||
MAYBE_EXTERN_C void jsGlFrontFace(GlEnum cullMode);
|
MAYBE_EXTERN_C void jsGlFrontFace(GlEnum cullMode);
|
||||||
MAYBE_EXTERN_C void jsGlDeleteBuffer(GlId bufferId);
|
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 GlId jsGlCreateTexture();
|
||||||
MAYBE_EXTERN_C void jsGlActiveTexture(GlEnum textureIndex);
|
MAYBE_EXTERN_C void jsGlActiveTexture(GlEnum textureIndex);
|
||||||
MAYBE_EXTERN_C void jsGlBindTexture(GlEnum target, GlId textureId);
|
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 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 jsGlTexParameteri(GlEnum target, GlEnum parameter, int value);
|
||||||
MAYBE_EXTERN_C void jsGlGenerateMipmap(GlEnum target);
|
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);
|
appGlobals.glContext.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||||
}
|
}
|
||||||
|
export function jsGlBlendEquation(equation)
|
||||||
|
{
|
||||||
|
appGlobals.glContext.blendEquation(equation);
|
||||||
|
}
|
||||||
|
|
||||||
export function jsGlDepthFunc(depthFunc)
|
export function jsGlDepthFunc(depthFunc)
|
||||||
{
|
{
|
||||||
@@ -203,6 +207,11 @@ export function jsGlBindTexture(target, textureId)
|
|||||||
appGlobals.glContext.bindTexture(target, texture);
|
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)
|
export function jsGlTexImage2D(target, level, internalFormat, width, height, border, format, type, dataLength, dataPntr)
|
||||||
{
|
{
|
||||||
let dataBuffer = new Uint8Array(appGlobals.memDataView.buffer, dataPntr, dataLength);
|
let dataBuffer = new Uint8Array(appGlobals.memDataView.buffer, dataPntr, dataLength);
|
||||||
@@ -563,6 +572,7 @@ export let jsGlFunctions = {
|
|||||||
jsGlDisable: jsGlDisable,
|
jsGlDisable: jsGlDisable,
|
||||||
jsGlBlendFunc: jsGlBlendFunc,
|
jsGlBlendFunc: jsGlBlendFunc,
|
||||||
jsGlBlendFuncSeparate: jsGlBlendFuncSeparate,
|
jsGlBlendFuncSeparate: jsGlBlendFuncSeparate,
|
||||||
|
jsGlBlendEquation: jsGlBlendEquation,
|
||||||
jsGlDepthFunc: jsGlDepthFunc,
|
jsGlDepthFunc: jsGlDepthFunc,
|
||||||
jsGlFrontFace: jsGlFrontFace,
|
jsGlFrontFace: jsGlFrontFace,
|
||||||
jsGlDeleteBuffer: jsGlDeleteBuffer,
|
jsGlDeleteBuffer: jsGlDeleteBuffer,
|
||||||
@@ -573,6 +583,7 @@ export let jsGlFunctions = {
|
|||||||
jsGlCreateTexture: jsGlCreateTexture,
|
jsGlCreateTexture: jsGlCreateTexture,
|
||||||
jsGlActiveTexture: jsGlActiveTexture,
|
jsGlActiveTexture: jsGlActiveTexture,
|
||||||
jsGlBindTexture: jsGlBindTexture,
|
jsGlBindTexture: jsGlBindTexture,
|
||||||
|
jsGlPixelStorei: jsGlPixelStorei,
|
||||||
jsGlTexImage2D: jsGlTexImage2D,
|
jsGlTexImage2D: jsGlTexImage2D,
|
||||||
jsGlTexParameteri: jsGlTexParameteri,
|
jsGlTexParameteri: jsGlTexParameteri,
|
||||||
jsGlGenerateMipmap: jsGlGenerateMipmap,
|
jsGlGenerateMipmap: jsGlGenerateMipmap,
|
||||||
|
|||||||
49
data/main.js
49
data/main.js
@@ -51,10 +51,22 @@ async function MainLoop()
|
|||||||
|
|
||||||
console.log("Loading WASM Module...");
|
console.log("Loading WASM Module...");
|
||||||
await LoadWasmModule("app.wasm", 4);
|
await LoadWasmModule("app.wasm", 4);
|
||||||
|
|
||||||
let initSuccess = appGlobals.wasmModule.exports.App_Initialize();
|
let initSuccess = appGlobals.wasmModule.exports.App_Initialize();
|
||||||
|
|
||||||
if (initSuccess)
|
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!");
|
console.log("Running!");
|
||||||
function renderFrame(currentTime)
|
function renderFrame(currentTime)
|
||||||
{
|
{
|
||||||
@@ -63,6 +75,43 @@ async function MainLoop()
|
|||||||
else { appGlobals.wasmModule.exports.App_Close(); }
|
else { appGlobals.wasmModule.exports.App_Close(); }
|
||||||
}
|
}
|
||||||
window.requestAnimationFrame(renderFrame);
|
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
|
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;
|
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 |
|
// | Initialize |
|
||||||
// +--------------------------------------------------------------+
|
// +--------------------------------------------------------------+
|
||||||
WASM_EXPORT(App_Initialize) bool App_Initialize()
|
WASM_EXPORT(App_Initialize) bool App_Initialize()
|
||||||
{
|
{
|
||||||
InitializeCWasm(Kilobytes(128));
|
InitializeCWasm(Megabytes(2));
|
||||||
memset(&app, 0x00, sizeof(app));
|
memset(&app, 0x00, sizeof(app));
|
||||||
|
|
||||||
#if 0
|
#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_VERSION: \"%s\"", jsGlGetParameterString(scratch, GL_VERSION));
|
||||||
PrintLine_D("GL_VENDOR: \"%s\"", jsGlGetParameterString(scratch, GL_VENDOR));
|
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[] = {
|
r32 positions[] = {
|
||||||
0.0, 0.0,
|
0.0, 0.0,
|
||||||
1.0, 0.0,
|
1.0, 0.0,
|
||||||
|
|||||||
Reference in New Issue
Block a user