From f63cb2bfb25625e25a6fdb6a721b91d07a0dd8a4 Mon Sep 17 00:00:00 2001 From: frank <420@shampoo.ooo> Date: Tue, 6 Sep 2022 20:52:34 -0400 Subject: [PATCH] example program for using a c++ program to stream a webcam in browser --- README.md | 5 +- demo/browser_webcam_test/Makefile | 103 +++++++++++ demo/browser_webcam_test/Model.cpp | 166 ++++++++++++++++++ demo/browser_webcam_test/Model.hpp | 81 +++++++++ demo/browser_webcam_test/README.md | 35 ++++ .../browser_webcam_test.cpp | 154 ++++++++++++++++ demo/browser_webcam_test/config.json | 6 + demo/browser_webcam_test/index.html | 98 +++++++++++ demo/browser_webcam_test/shaders/flat.frag | 21 +++ demo/browser_webcam_test/shaders/flat.vert | 25 +++ 10 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 demo/browser_webcam_test/Makefile create mode 100644 demo/browser_webcam_test/Model.cpp create mode 100644 demo/browser_webcam_test/Model.hpp create mode 100644 demo/browser_webcam_test/README.md create mode 100644 demo/browser_webcam_test/browser_webcam_test.cpp create mode 100644 demo/browser_webcam_test/config.json create mode 100644 demo/browser_webcam_test/index.html create mode 100644 demo/browser_webcam_test/shaders/flat.frag create mode 100644 demo/browser_webcam_test/shaders/flat.vert diff --git a/README.md b/README.md index c0ce6f8..6ef9ba2 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ Test collision detection between a 2D sprite and other 2D sprites and boxes. Per Map an image from a rectangle to a circle or from a circle to a rectangle using a shader program. Based on the elliptical grid mapping equations at http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html +### browser webcam + +An example for using a C++ program to display a webcam stream in the browser using Emscripten to translate the code from C++ to WebAssembly. Get the frame pixel data from a canvas element, read it into a SPACEBOX object, write the pixel data to an OpenGL texture, and use Emscripten to display the video. Other libraries --------------- @@ -126,7 +129,7 @@ To build a WASM library that can be used to build an Emscripten version of a SPA $ find . -iname *.a ./zbar/.libs/libzbar.a -There is a detailed tutorial on using Zbar with Web Assembly at https://barkeywolf.consulting/posts/barcode-scanner-webassembly/ +There is a detailed tutorial on using Zbar with WebAssembly at https://barkeywolf.consulting/posts/barcode-scanner-webassembly/ Font ---- diff --git a/demo/browser_webcam_test/Makefile b/demo/browser_webcam_test/Makefile new file mode 100644 index 0000000..0fd1bf8 --- /dev/null +++ b/demo/browser_webcam_test/Makefile @@ -0,0 +1,103 @@ +# Browser Webcam Test + +####################### +# Location parameters # +####################### + +# Location of project specific source files +SRC_DIR := ./ + +# Locations of [SPACEBOX] source and dependencies required to be compiled from source. These locations are configured to match the +# structure of the [SPACEBOX] repository but can be modified as necessary. +SB_DIR := ../../ +SB_SRC_DIR := $(SB_DIR)src/ +SB_LIB_DIR := $(SB_DIR)lib/ +SDLGFX2_DIR := $(SB_LIB_DIR)sdl2-gfx/ +GLEW_DIR := $(SB_LIB_DIR)glew/ + +# C and C++ compiler commands +CC := clang +CXX := clang++ + +# Location of SDL config program +SDLCONFIG := $(HOME)/local/sdl/bin/sdl2-config + +# Edit to point to the location of BPmono.ttf +CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" . + +############################# +# Based on above parameters # +############################# + +SDL_CFLAGS = $(shell $(SDLCONFIG) --cflags) +SDL_LFLAGS := $(shell $(SDLCONFIG) --libs) +SB_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp)) +SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SB_H_FILES:.hpp=.o)) +SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) +SRC_O_FILES := browser_webcam_test.o $(SRC_H_FILES:.hpp=.o) + +##################################################################### +# Targets for building [SPACE BOX], dependencies and project source # +##################################################################### + +$(SDLGFX2_DIR)%.o: $(SDLGFX2_DIR)%.c $(SDLGFX2_DIR)%.h +$(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h + $(CC) $< $(CFLAGS) -c -o $@ + +$(SB_SRC_DIR)extension.o : $(addprefix $(SB_SRC_DIR),Box.hpp Segment.hpp Color.hpp filesystem.hpp Pixels.hpp Log.hpp) +$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp Log.hpp) +$(SB_SRC_DIR)Sprite.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Animation.hpp Color.hpp extension.hpp Pixels.hpp Log.hpp) +$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Sprite.hpp Recorder.hpp Input.hpp Configuration.hpp \ + Delegate.hpp Audio.hpp Log.hpp) +$(SB_SRC_DIR)Animation.o : $(addprefix $(SB_SRC_DIR),Node.hpp Timer.hpp) +$(SB_SRC_DIR)Recorder.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Configuration.hpp Delegate.hpp Animation.hpp extension.hpp) +$(SB_SRC_DIR)Input.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Configuration.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Log.hpp) +$(SB_SRC_DIR)Delegate.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Input.hpp) +$(SB_SRC_DIR)Display.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Configuration.hpp Delegate.hpp Log.hpp) +$(SB_SRC_DIR)Box.o : $(addprefix $(SB_SRC_DIR),extension.hpp Segment.hpp) +$(SB_SRC_DIR)Segment.o : $(addprefix $(SB_SRC_DIR),extension.hpp Box.hpp) +$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp Log.hpp utility.hpp) +$(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) +$(SB_SRC_DIR)GLObject.o : $(addprefix $(SB_SRC_DIR),Log.hpp) +$(SB_SRC_DIR)Texture.o : $(addprefix $(SB_SRC_DIR),GLObject.hpp filesystem.hpp Log.hpp) +$(SB_SRC_DIR)VBO.o : $(addprefix $(SB_SRC_DIR),Log.hpp GLObject.hpp Attributes.hpp extension.hpp) +$(SB_SRC_DIR)Attributes.o : $(addprefix $(SB_SRC_DIR),Log.hpp extension.hpp) +$(SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp utility.hpp) +$(SRC_DIR)*.o : $(SRC_H_FILES) $(SB_H_FILES) +%.o : %.cpp %.hpp + $(CXX) $(CXXFLAGS) $< -c -o $@ + +############# +# Web build # +############# + +# Use Emscripten to output JavaScript and an HTML index page for running in the browser + +EMSCRIPTENHOME = $(HOME)/ext/software/emsdk/upstream/emscripten +EMSCRIPTEN_CFLAGS = -O1 -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png', 'jpg']" -s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 \ + -I $(SB_LIB_DIR) -I $(SB_SRC_DIR) +EMSCRIPTEN_LFLAGS = -s MIN_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS="['_main']" -s ALLOW_MEMORY_GROWTH=1 -s FULL_ES3=1 \ + -s LLD_REPORT_UNDEFINED $(wildcard $(addprefix $(HOME)/ext/software/opencv-4.6.0/build_wasm/lib/,*.a)) $(HOME)/ext/software/ZBar/zbar/.libs/libzbar.a \ + --bind +EMSCRIPTEN_PRELOADS = --preload-file "BPmono.ttf"@/ --preload-file "shaders/"@/"shaders/" --preload-file "config.json"@/ + +emscripten : CC = $(EMSCRIPTENHOME)/emcc +emscripten : CXX = $(EMSCRIPTENHOME)/em++ +emscripten : CFLAGS = $(EMSCRIPTEN_CFLAGS) +emscripten : CXXFLAGS = $(CFLAGS) --std=c++17 +emscripten : $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SB_O_FILES) $(SRC_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CXX) $^ $(CXXFLAGS) $(EMSCRIPTEN_LFLAGS) $(EMSCRIPTEN_PRELOADS) -o browser_webcam_test.js + +######################### +# Clean up object files # +######################### + +clean : + -find $(SRC_DIR) -iname "*.o" -delete + rm -f BPmono.ttf browser_webcam_test.data browser_webcam_test.js browser_webcam_test.wasm + +clean-all : clean + -find $(SB_SRC_DIR) -iname "*.o" -delete + -find $(SB_LIB_DIR) -iname "*.o" -delete diff --git a/demo/browser_webcam_test/Model.cpp b/demo/browser_webcam_test/Model.cpp new file mode 100644 index 0000000..29c37b3 --- /dev/null +++ b/demo/browser_webcam_test/Model.cpp @@ -0,0 +1,166 @@ +/* Browser Webcam Test - "Model.cpp" + * + * This is a class for creating an OpenGL model (basically a collection of vertices, optional texture, and optional transformation). + * It contains convenience functions for loading models into OpenGL. + * + * It is copied from the in-development game Gunkiss. It will eventually be merged into [SPACE BOX]. + */ + +#include "Model.hpp" + +/* Default constructor for Model */ +Model::Model() {}; + +/* Construct a Model, adding Attributes each already wrapped in a shared pointer. The attributes should + * be passed as a map with each key being a name and each value being a shared pointer to attributes. */ +Model::Model(const std::map>& attributes_pack) +{ + for (auto attributes : attributes_pack) + { + this->attributes(attributes.second, attributes.first); + } +} + +/* Construct a Model, adding Attributes, which will each be wrapped in a shared pointer and stored in the + * created object. The attributes should be passed as a map with each key being a name and each value being + * an attributes object. */ +Model::Model(const std::map& attributes_pack) +{ + for (auto attributes : attributes_pack) + { + this->attributes(attributes.second, attributes.first); + } +} + +/* Construct a new model object by passing a list of names which will be used to initialize + * empty attributes objects with the given names */ +Model::Model(const std::initializer_list& names) +{ + for (const std::string& name : names) + { + this->attributes(sb::Attributes(), name); + } +} + +/* Get the entire map of attributes, each wrapped in its shared pointer held by this object. + * Can be used to iterate through the attributes. */ +std::map>& Model::attributes() +{ + return model_attributes; +} + +/* Get the attributes under name, wrapped in the shared pointer held by this object. This + * function uses the at method of std::map, so name must refer to attributes already + * stored in this model. Use this function to share ownership of the attributes or to gain + * access to the public interface of the attributes. */ +std::shared_ptr& Model::attributes(const std::string& name) +{ + return attributes().at(name); +} + +/* Get the attributes under name, wrapped in the shared pointer held by this object. This + * function uses operator[] or std::map, so this can be used to add new attributes to the + * object if they are wrapped in a shared pointer. */ +std::shared_ptr& Model::operator[](const std::string& name) +{ + auto element = attributes().find(name); + /* add an empty Attributes at name if it doesn't exist yet */ + if (element == attributes().end()) + { + attributes(sb::Attributes{}, name); + } + return attributes()[name]; +} + +/* Assign name to attributes, copy and wrap in a shared pointer. The model can share + * ownership of the created attribute memory with callers that request it. */ +void Model::attributes(const sb::Attributes& attributes, const std::string& name) +{ + this->attributes(std::make_shared(attributes), name); +} + +/* Assign name to attributes and share ownership. */ +void Model::attributes(const std::shared_ptr& attributes, const std::string& name) +{ + this->attributes()[name] = attributes; +} + +/* Enable all attributes. */ +void Model::enable() +{ + for (const auto& attributes : this->attributes()) + { + attributes.second->enable(); + } +} + +/* Disable all attributes. */ +void Model::disable() +{ + for (const auto& attributes : this->attributes()) + { + attributes.second->disable(); + } +} + +/* Return a reference to the texture container. */ +std::map& Model::textures() +{ + return model_textures; +} + +/* Get the texture at name. This can be used to read the texture memory, share ownership of it, or + * anything else a Texture object can be used for with direct calls to GL functions. */ +sb::Texture& Model::texture(const std::string& name) +{ + return textures().at(name); +} + +/* Get the default texture. The default texture must have previously been set with the default key as + * the name, which can be done using Model::texture(sb::Texture). */ +sb::Texture& Model::texture() +{ + return texture(DEFAULT_TEXTURE_NAME); +} + +/* Assign name to texture and share ownership. */ +void Model::texture(const sb::Texture& texture, const std::string& name) +{ + textures()[name] = texture; +} + +/* If no name is specified, use the default texture. This can be used to conveniently setup a model + * with only one texture. */ +void Model::texture(const sb::Texture& texture) +{ + this->texture(texture, DEFAULT_TEXTURE_NAME); +} + +/* Get the model's transformation matrix. */ +const glm::mat4& Model::transformation() const +{ + return model_transformation; +} + +/* Set the model's transformation matrix. */ +void Model::transformation(const glm::mat4& transformation) +{ + model_transformation = transformation; +} + +/* Return the size in bytes of the sum of the attributes. */ +std::size_t Model::size() +{ + std::size_t sum = 0; + for (const auto& attributes : this->attributes()) + { + sum += attributes.second->size(); + } + return sum; +} + +/* Return the transformation matrix. */ +Model::operator glm::mat4() const +{ + return model_transformation; +} diff --git a/demo/browser_webcam_test/Model.hpp b/demo/browser_webcam_test/Model.hpp new file mode 100644 index 0000000..803c691 --- /dev/null +++ b/demo/browser_webcam_test/Model.hpp @@ -0,0 +1,81 @@ +/* Browser Webcam Test - "Model.hpp" + * + * This is a class for creating an OpenGL model (basically a collection of vertices, optional texture, and optional transformation). + * It contains convenience functions for loading models into OpenGL. + * + * It is copied from the in-development game Gunkiss. It will eventually be merged into [SPACE BOX]. + */ + +#ifndef MODEL_H_ +#define MODEL_H_ + +/* GL functions */ +#if defined(__EMSCRIPTEN__) +#include +#else +#include "glew/glew.h" +#endif + +#include +#include +#include +#include +#include +#include "glm/glm.hpp" +#include "Attributes.hpp" +#include "Texture.hpp" + +class Model +{ + +private: + + inline static const std::string DEFAULT_TEXTURE_NAME = "default"; + std::map model_textures; + std::map> model_attributes; + glm::mat4 model_transformation {1.0f}; + +public: + + Model(); + Model(const std::map>&); + Model(const std::map&); + Model(const std::initializer_list&); + std::map>& attributes(); + std::shared_ptr& attributes(const std::string&); + void attributes(const sb::Attributes&, const std::string&); + void attributes(const std::shared_ptr&, const std::string&); + std::shared_ptr& operator[](const std::string&); + void enable(); + void disable(); + std::map& textures(); + sb::Texture& texture(const std::string&); + sb::Texture& texture(); + void texture(const sb::Texture&, const std::string&); + void texture(const sb::Texture&); + const glm::mat4& transformation() const; + void transformation(const glm::mat4&); + std::size_t size(); + operator glm::mat4() const; + +}; + +class Plane : public Model +{ + +public: + + inline const static std::shared_ptr position = std::make_shared(sb::Attributes{ + {-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f}, + {1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f} + }); + inline const static std::shared_ptr uv = std::make_shared(sb::Attributes{ + {0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f}, + {1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f} + }); + + Plane() : Model(std::map>({{"position", position}, {"uv", uv}})) {} + +}; + +#endif diff --git a/demo/browser_webcam_test/README.md b/demo/browser_webcam_test/README.md new file mode 100644 index 0000000..df06e87 --- /dev/null +++ b/demo/browser_webcam_test/README.md @@ -0,0 +1,35 @@ +Emscripten webcam pixel data test +================================= + +This is a demo program for passing image data from an HTML5 canvas object in JavaScript to an OpenGL context in a C++ program. It can be useful, for example, for developing cross-platform applications that use the same codebase to export both desktop and web versions, or for using C++ libraries and code to edit and display images on a web page. + +It uses the [SPACEBOX][] engine to set up an SDL + GL environment and create a model conveniently, but it can be ported to just Emscripten. + +Setup +----- + +### Emscripten + +Install the latest Emscripten version to a directory and specify the directory in the Makefile + +### SPACE BOX + +The [SPACEBOX][] game and interactive application framework is required for setting up SDL + OpenGL. It is being used for convenience in setting up the rendering, but this technique can be used without it. Get it from https://git.nugget.fun/nugget/spacebox and specify the path to it in the Makefile. + +Compiling +--------- + +Run at the root of the directory after setting up Emscripten and SPACEBOX + + make emscripten + +Running +------- + +Run at the root of the directory to create an HTTP server + + python -m http.server + +Browse to http://localhost:8000 to view the demo + +[SPACEBOX]: https://git.nugget.fun/nugget/spacebox diff --git a/demo/browser_webcam_test/browser_webcam_test.cpp b/demo/browser_webcam_test/browser_webcam_test.cpp new file mode 100644 index 0000000..23f15e0 --- /dev/null +++ b/demo/browser_webcam_test/browser_webcam_test.cpp @@ -0,0 +1,154 @@ +/* + * Browser Webcam Test by frank at shampoo.ooo + * + * Program for testing passing image data from an HTML5 canvas object in JavaScript to an OpenGL context in this C++ program. + * This can be useful, for example, for developing cross-platform applications that use the same codebase to export both desktop + * and web versions, or for using C++ libraries and code to edit and display images on a web page. + * + * It uses the [SPACE BOX] engine (https://git.shampoo.ooo/nugget/spacebox) to set up an SDL + GL environment and create a model + * conveniently, but it can be ported to just Emscripten. + */ + +#include +#include +#include +#include +#include "Game.hpp" +#include "GLObject.hpp" +#include "VBO.hpp" +#include "Model.hpp" + +using namespace emscripten; + +/* These variables will be bound to JS. They are placed in the global scope, so they can be read and written by both + * C++ and JS. The following functions are bound to JS so they can be used to write values to the variables. + */ +unsigned int emscripten_heap_offset = 0; +bool new_frame_flag = false; + +void flag_frame() +{ + new_frame_flag = true; +} + +void set_heap_offset(int offset) +{ + emscripten_heap_offset = offset; +} + +class Browser_Webcam_Test : public Game +{ + +public: + + sb::VAO vao; + sb::VBO vbo; + GLuint flat_program, texture_uniform; + Plane camera_frame_model; + + Browser_Webcam_Test() + { + /* The parent constructor initializes SDL and SDL extensions, sets GL attributes and creates a window. */ + }; + + /* Load pixel data from the Emscripten heap into an OpenGL texture. This will be called whenever new frame data is added to the Emscripten heap */ + void refresh() + { + /* Address of frame RGBA pixel data on the Emscripten heap (received as an unsigned int and cast to an unsigned 8-bit pointer) */ + unsigned char* pos = reinterpret_cast(emscripten_heap_offset); + + /* Print the first four 8-bit values, which should be an RGBA color */ + std::cout << "heap address " << emscripten_heap_offset << " first 4 bytes: "; + for (std::size_t ii = 0; ii < 4; ii++) + { + std::cout << static_cast(pos[ii]) << " "; + } + std::cout << std::endl; + + // If OpenCV were being used, a cv::Mat could be created: + // frame = cv::Mat(FW, FH, CV_8UC4, pos);; + + /* Add a texture to the camera Plane for storing frame image data */ + camera_frame_model.texture().load(pos, configuration()["display"]["dimensions"]); + + /* Indicate pixel data has finished loading */ + new_frame_flag = false; + } + + /* Set up GL buffers for attributes. Set up shaders and uniforms. Create the texture that will hold the camera pixel data. */ + void load_gl_context() + { + /* [SPACE BOX] creates an SDL GL context and initializes GLEW. */ + Game::load_gl_context(); + + /* Generate a vertex array object ID, bind it as current (requirement of OpenGL) */ + vao.generate(); + vao.bind(); + + /* Generate ID for the vertex buffer object that will hold all vertex data. Using one buffer for all attributes, data + * will be copied in one after the other. */ + vbo.generate(); + vbo.bind(); + + /* Load, configure, and set GL to use the shader program */ + GLuint vertex_shader = load_shader("shaders/flat.vert", GL_VERTEX_SHADER); + GLuint fragment_shader = load_shader("shaders/flat.frag", GL_FRAGMENT_SHADER); + flat_program = glCreateProgram(); + glAttachShader(flat_program, vertex_shader); + glAttachShader(flat_program, fragment_shader); + Plane::position->bind(0, flat_program, "in_position"); + Plane::uv->bind(1, flat_program, "vertex_uv"); + link_shader(flat_program); + glUseProgram(flat_program); + + /* Fill VBO with attribute data */ + vbo.allocate(camera_frame_model.size(), GL_STATIC_DRAW); + vbo.add(*Plane::position); + vbo.add(*Plane::uv); + + /* Set the active texture unit to #0, Get the texture uniform from the shader and set it use texture #0. See + * https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Binding_textures_to_samplers */ + glActiveTexture(GL_TEXTURE0); + texture_uniform = glGetUniformLocation(flat_program, "base_texture"); + glUniform1i(texture_uniform, 0); + + /* Create a texture the size of the video resolution (defined as 320, 240 in config.json and index.html) */ + camera_frame_model.texture(sb::Texture()); + camera_frame_model.texture().generate(configuration()["display"]["dimensions"]); + camera_frame_model.texture().bind(); + camera_frame_model.enable(); + } + + /* This gets called every frame by the parent class. Refresh the texture pixel data if a new frame is available. + * Clear the screen, then draw the camera model, which will render the texture. */ + void update() + { + /* This flag is set in JS whenever a camera frame is read and stored */ + if (new_frame_flag) + { + refresh(); + } + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, camera_frame_model.attributes("position")->count()); + SDL_GL_SwapWindow(window()); + } + +}; + +/* Emscripten will call this function. Create a game object, load its GL context, and run the game. */ +int main() +{ + Browser_Webcam_Test browser_webcam_test = Browser_Webcam_Test(); + browser_webcam_test.load_gl_context(); + browser_webcam_test.run(); + browser_webcam_test.quit(); + return 0; +} + +/* This will bind the global functions at the beginning of the file to Emscripten so those values can be set and read by this program */ +EMSCRIPTEN_BINDINGS(my_module) +{ + function("flag_frame", &flag_frame); + function("set_heap_offset", &set_heap_offset); +} diff --git a/demo/browser_webcam_test/config.json b/demo/browser_webcam_test/config.json new file mode 100644 index 0000000..7f551d9 --- /dev/null +++ b/demo/browser_webcam_test/config.json @@ -0,0 +1,6 @@ +{ + "display": { + "render driver": "opengles3", + "dimensions": [320, 240] + } +} diff --git a/demo/browser_webcam_test/index.html b/demo/browser_webcam_test/index.html new file mode 100644 index 0000000..a77004d --- /dev/null +++ b/demo/browser_webcam_test/index.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + diff --git a/demo/browser_webcam_test/shaders/flat.frag b/demo/browser_webcam_test/shaders/flat.frag new file mode 100644 index 0000000..90b26ab --- /dev/null +++ b/demo/browser_webcam_test/shaders/flat.frag @@ -0,0 +1,21 @@ +#version 300 es + +/* Browser Webcam Test */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +/* Forwarded from the vertex shader */ +in vec2 uv; + +/* The texture is provided by the program when it sets the uniform value. */ +uniform sampler2D base_texture; + +/* Setting this to a color value will color the fragment in the output display */ +out vec4 myOutputColor; + +void main(void) +{ + /* Get the color from the texture at the UV coordinates */ + myOutputColor = texture(base_texture, uv); +} diff --git a/demo/browser_webcam_test/shaders/flat.vert b/demo/browser_webcam_test/shaders/flat.vert new file mode 100644 index 0000000..513290b --- /dev/null +++ b/demo/browser_webcam_test/shaders/flat.vert @@ -0,0 +1,25 @@ +#version 300 es + +/* Browser Webcam Test */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +/* Values which are bound the VBO. */ +in vec2 in_position; +in vec2 vertex_uv; + +/* Will be forwarded to the fragment shader */ +out vec2 uv; + +/* Value is provided by the program when it sets the uniform value. */ +uniform mat4 transformation; + +void main(void) +{ + /* Reflect 2D coordinates over the X-axis to flip the canvas coordinates into GL coordinates and return as a vec4 */ + gl_Position = vec4(vec2(1, -1) * in_position, 0, 1); + + /* Forward the UV coordinates to the fragment shader. */ + uv = vertex_uv; +}