You may have an app which needs to change the cursor when you interact with elements in the canvas. In the app I'm working on, I needed the cursor to change when you hover over drawn bars that allow for sizing. To do this I wrote a little javascript code to do the cursor change and then had the c++ call it.
The javascript code:
function changeMouseCursor( type ) {
if ( type == 0 ) {
$('canvas').css( 'cursor', 'default' );
}
else if ( type == 1 ) {
$('canvas').css('cursor', 'n-resize' );
}
else if ( type == 2 ) {
$('canvas').css('cursor', 's-resize' );
}
else if ( type == 4 ) {
$('canvas').css('cursor', 'e-resize' );
}
else if ( type == 5 ) {
$('canvas').css('cursor', 'se-resize' );
}
else if ( type == 6 ) {
$('canvas').css('cursor', 'ne-resize' );
}
else if ( type == 8 ) {
$('canvas').css('cursor', 'w-resize' );
}
else if ( type == 9 ) {
$('canvas').css('cursor', 'sw-resize' );
}
else if ( type == 10 ) {
$('canvas').css('cursor', 'nw-resize' );
}
}
Code in emscripten:
int sizeType = IsOverDraggableBorder( m_mouseX, m_mouseY );
if ( sizeType != m_sizingType )
{
//change the cursor
char call[200];
sprintf(call, "changeMouseCursor(%d)", sizeType );
emscripten_run_script(call);
m_sizingType = sizeType;
}
Here is what it looks like in my app :)
Fantastic Coder
Friday, June 28, 2013
Friday, June 7, 2013
Emscripten speed optimizations
One more important thing I have left off from previous posts is that without these optimization options fed to the compiler it will not be fast! Make sure that you have "-O2 -s ASM_JS=1" as part of your build config. As far as I can tell the "-s ASM_JS=1" doesn't seem to do much, but the first part "-O2" makes a significant difference!
Your build command might look as follows:
~/emscripten/emscripten/emcc -O2 -s ASM_JS=1 main.cpp -o /var/www/html/test.html
Another thing to note is that you most likely should get the beta firefox version (currently 22) to see how your code performs with the latest asm.js optimizations.
Your build command might look as follows:
~/emscripten/emscripten/emcc -O2 -s ASM_JS=1 main.cpp -o /var/www/html/test.html
Another thing to note is that you most likely should get the beta firefox version (currently 22) to see how your code performs with the latest asm.js optimizations.
Wednesday, May 29, 2013
Loading an external image into Emscripten in ARGB
I have been working on an archery game and was investigating the ability to dynamically load images into the app. Most Emscripten apps are designed where the files are prepackaged into the game - a loading bar shows when starting the app and this loads all of the assets. I could have done it this way as well, but I wanted to explore if I could load an image externally and then bring it into emscripten.
The advantages are at least a couple:
So let's begin, if you haven't followed my other posts at this point I have a few handy things setup -
<img id="funnyturtle" style="display:none" src="turtle.jpg"/>
<canvas id="imageloader" style="display:none;width:250px;height:200px"></canvas>
<script type='text/javascript'>
setTimeout(loadImage,2000);
var context;
function loadImage()
{
var elem = document.getElementById('imageloader');
if (elem && elem.getContext)
{
elem.setAttribute('width', '250px'); //###IMPORTANT
elem.setAttribute('height', '200px');
context = elem.getContext('2d');
if (context)
{
var img=document.getElementById('funnyturtle');
var width = 250;
var height = 200;
context.drawImage(img,0,0, img.width, img.height, 0, 0, width, height);
var imgd = context.getImageData(0, 0, width, height);
var numBytes = width * height * 4;
var ptr= Module._malloc(numBytes);
var heapBytes= new Uint8Array(Module.HEAPU8.buffer, ptr, numBytes);
// copy data into heapBytes
heapBytes.set(new Uint8Array(imgd.data));
var imageloaded = Module.cwrap('imageloaded', 'number', ['number','number','number'])
var n = imageloaded(heapBytes.byteOffset, width, height );
Module._free(heapBytes.byteOffset);
}
}
}
</script>
What goes on here is 2 seconds after the page is loaded it will call loadImage(). 2 seconds is because for some reason the javascript for the Emscripten engine is not ready in the normal ready() from jquery. Once loadimage() gets called, we assume that the image is loaded, and basically draw the image to a hidden canvas element. From the hidden canvas we can get the ARGB32 values for the image and call imageLoaded() passing the image rgb values into our c++ code.
Now about the C++ code - one big gotcha is that when you are exporting functions via build options, remember that everything is lopped off unless you add it to your export list. I spent some time figuring out why Module._malloc did not exist and hopefully I can save you some time. So in your build script make sure you have something similar to this:
~/emscripten/emscripten/emcc main.cpp image.cpp game.cpp ShootingScreen.cpp drawing.cpp mainmenu.cpp -o /var/www/html/archery.html -s EXPORTED_FUNCTIONS="['_imageloaded','_main','_malloc','_free']"
From the above, you can see I exported _imageloaded and the other more standard functions. Here is the code in c++ that I have:
extern "C" int imageloaded( char * buffer, int width, int height )
{
g_game.SetImageLoaded( buffer, width, height );
return 0;
}
void CGame::SetImageLoaded( char * buffer, int width, int height )
{
for ( int n = 0; n < IMAGE_CACHE_COUNT; n++ )
{
if ( m_images[n] == NULL )
{
m_images[n] = new CImage("mike.jpg", buffer, width, height, 200 );
}
}
}
CImage::CImage( const char * name, char * imageData, int width, int height, int alpha /*255*/ )
{
m_imageData = (unsigned int*)malloc( width*height*4 );
memcpy( m_imageData, imageData, width * height * 4 );
m_name = strdup( name );
m_width = width;
m_height = height;
m_alpha = alpha;
}
You can see that imageloaded gets called with my rgb32 buffer. I then call into my global game object, to save off the image. To be stupid simple I have an array of CImage* (I haven't gotten std::vector to work yet!). Each image object allocates the size of the image and then makes a copy.
Just for added kicks, here is my routine to draw the image.
void CDrawing::DrawImage( SURFACE & surface, CImage * image, int x, int y )
{
if ( image->m_alpha == 255 )
{
int bufferOffset = x + y * surface.screenWidth;
for ( int n = 0; n < image->m_height; n++ )
{
memcpy( surface.buffer + bufferOffset, image->m_imageData + n * image->m_width, image->m_width * 4 );
bufferOffset += surface.screenWidth;
}
}
else
{
unsigned int * src = image->m_imageData;
unsigned int * dest = surface.buffer + x + y * surface.screenWidth;
double alphaPercent = image->m_alpha / 255.0;
double destAlpha = 1.0 - alphaPercent;
for ( int n = 0; n < image->m_height; n++ )
{
for ( int a = 0; a < image->m_width; a++ )
{
*dest = ARGB( 0,
(int)(GET_RED(*src) * alphaPercent + GET_RED(*dest) * destAlpha),
(int)(GET_GREEN(*src) * alphaPercent + GET_GREEN(*dest) * destAlpha),
(int)(GET_BLUE(*src) * alphaPercent + GET_BLUE(*dest) * destAlpha) );
src++;
dest++;
}
dest += surface.screenWidth - image->m_width;
}
}
}
This just draws the image without stretching, nothing fancy. The alpha blending is split out as it should be slower! That's pretty much all there is too it.
The advantages are at least a couple:
- I don't need any image loading code - jpeg decoder libraries etc and the image will be loaded in ARGB32 format (Note, I am not using opengl either)
- I can have dynamic content loaded from another website or service.
So let's begin, if you haven't followed my other posts at this point I have a few handy things setup -
- I am working on an amazon ami, which builds and publishes to a web folder
- I wrote a program to combine html, javascript, into emscripten output so that I can run a build script and have all of these pieces combined into the output
<img id="funnyturtle" style="display:none" src="turtle.jpg"/>
<canvas id="imageloader" style="display:none;width:250px;height:200px"></canvas>
<script type='text/javascript'>
setTimeout(loadImage,2000);
var context;
function loadImage()
{
var elem = document.getElementById('imageloader');
if (elem && elem.getContext)
{
elem.setAttribute('width', '250px'); //###IMPORTANT
elem.setAttribute('height', '200px');
context = elem.getContext('2d');
if (context)
{
var img=document.getElementById('funnyturtle');
var width = 250;
var height = 200;
context.drawImage(img,0,0, img.width, img.height, 0, 0, width, height);
var imgd = context.getImageData(0, 0, width, height);
var numBytes = width * height * 4;
var ptr= Module._malloc(numBytes);
var heapBytes= new Uint8Array(Module.HEAPU8.buffer, ptr, numBytes);
// copy data into heapBytes
heapBytes.set(new Uint8Array(imgd.data));
var imageloaded = Module.cwrap('imageloaded', 'number', ['number','number','number'])
var n = imageloaded(heapBytes.byteOffset, width, height );
Module._free(heapBytes.byteOffset);
}
}
}
</script>
What goes on here is 2 seconds after the page is loaded it will call loadImage(). 2 seconds is because for some reason the javascript for the Emscripten engine is not ready in the normal ready() from jquery. Once loadimage() gets called, we assume that the image is loaded, and basically draw the image to a hidden canvas element. From the hidden canvas we can get the ARGB32 values for the image and call imageLoaded() passing the image rgb values into our c++ code.
Now about the C++ code - one big gotcha is that when you are exporting functions via build options, remember that everything is lopped off unless you add it to your export list. I spent some time figuring out why Module._malloc did not exist and hopefully I can save you some time. So in your build script make sure you have something similar to this:
~/emscripten/emscripten/emcc main.cpp image.cpp game.cpp ShootingScreen.cpp drawing.cpp mainmenu.cpp -o /var/www/html/archery.html -s EXPORTED_FUNCTIONS="['_imageloaded','_main','_malloc','_free']"
From the above, you can see I exported _imageloaded and the other more standard functions. Here is the code in c++ that I have:
extern "C" int imageloaded( char * buffer, int width, int height )
{
g_game.SetImageLoaded( buffer, width, height );
return 0;
}
void CGame::SetImageLoaded( char * buffer, int width, int height )
{
for ( int n = 0; n < IMAGE_CACHE_COUNT; n++ )
{
if ( m_images[n] == NULL )
{
m_images[n] = new CImage("mike.jpg", buffer, width, height, 200 );
}
}
}
CImage::CImage( const char * name, char * imageData, int width, int height, int alpha /*255*/ )
{
m_imageData = (unsigned int*)malloc( width*height*4 );
memcpy( m_imageData, imageData, width * height * 4 );
m_name = strdup( name );
m_width = width;
m_height = height;
m_alpha = alpha;
}
You can see that imageloaded gets called with my rgb32 buffer. I then call into my global game object, to save off the image. To be stupid simple I have an array of CImage* (I haven't gotten std::vector to work yet!). Each image object allocates the size of the image and then makes a copy.
Just for added kicks, here is my routine to draw the image.
void CDrawing::DrawImage( SURFACE & surface, CImage * image, int x, int y )
{
if ( image->m_alpha == 255 )
{
int bufferOffset = x + y * surface.screenWidth;
for ( int n = 0; n < image->m_height; n++ )
{
memcpy( surface.buffer + bufferOffset, image->m_imageData + n * image->m_width, image->m_width * 4 );
bufferOffset += surface.screenWidth;
}
}
else
{
unsigned int * src = image->m_imageData;
unsigned int * dest = surface.buffer + x + y * surface.screenWidth;
double alphaPercent = image->m_alpha / 255.0;
double destAlpha = 1.0 - alphaPercent;
for ( int n = 0; n < image->m_height; n++ )
{
for ( int a = 0; a < image->m_width; a++ )
{
*dest = ARGB( 0,
(int)(GET_RED(*src) * alphaPercent + GET_RED(*dest) * destAlpha),
(int)(GET_GREEN(*src) * alphaPercent + GET_GREEN(*dest) * destAlpha),
(int)(GET_BLUE(*src) * alphaPercent + GET_BLUE(*dest) * destAlpha) );
src++;
dest++;
}
dest += surface.screenWidth - image->m_width;
}
}
}
This just draws the image without stretching, nothing fancy. The alpha blending is split out as it should be slower! That's pretty much all there is too it.
Friday, May 24, 2013
How to modify the output html page to interact with Javascript - Emscripten candy!
I described in the previous post how to call your compiled code from Javascript. Well, that's cool and all, but... every time I build emscripten generates a new html file and wipes any editing I may wish to put inside the html file! To solve this I created a little utility which will combine javascript with your output html so that you can test the interaction between both in the html page. I just added the utility to the end of my build script and am set after that!
~/utils/htmljoin /var/www/html/archery.html javascript /var/www/html/archery2.html
The text in red, takes my emscripten output, archery.html and javascript and combines it into one file archery2.html. The contents in javascript file are inserted before the <body> tag.
This script code gets injected into the html output. Using this code I can call "imageloaded" which exists in my c++/c code. I found that you need to delay until you can call a function for things to load. My guess is that you could use ready() from jquery to know when you can call a c function. Also, adding jquery library links to this file might be a great idea!
#include <cstdio>
#include <cstdlib>
#include <cstring>
int getFileSize( FILE * file )
{
fseek (file , 0 , SEEK_END);
int size = ftell (file);
rewind (file);
return size;
}
char * readContentsFromFile( char * filePath )
{
FILE * file = fopen( filePath, "r" );
if ( file == NULL )
{
printf( "ERROR: could not load file \"%s\"\n", filePath );
return NULL;
}
int size = getFileSize( file );
char * buffer = (char*) malloc (sizeof(char)*size + 1);
if (buffer == NULL)
{
printf("ERROR: error allocating memory for reading the file\n");
fclose( file );
return NULL;
}
printf( "reading %s file, %d bytes\n", filePath, size );
// copy the file into the buffer:
int result = fread( buffer, 1, size, file );
buffer[size] = '\0';
fclose( file );
if (result != size)
{
printf("ERROR: error reading the contents of the file\n");
free( buffer );
return NULL;
}
return buffer;
}
void CreateJoinedFile( char * emscriptenHtmlFileData, char * joinHtmlFileData, FILE * outputHtmlFilePath )
{
//find where body is in the string
char * bodyLocation = strstr( emscriptenHtmlFileData, "<body>" );
if ( bodyLocation == NULL )
{
printf( "ERROR: could not find the <body> tag\n" );
return;
}
printf( "writing first piece of emscripten file...\n" );
size_t toWrite = bodyLocation - emscriptenHtmlFileData;
fwrite( emscriptenHtmlFileData, 1, toWrite, outputHtmlFilePath );
printf( "joining files...\n" );
//insert the code
int lengthJoin = strlen( joinHtmlFileData );
fwrite( joinHtmlFileData, 1, lengthJoin, outputHtmlFilePath );
printf( "writing last piece of emscripten file...\n" );
int bodyLength = strlen( bodyLocation );
fwrite ( bodyLocation, 1, bodyLength, outputHtmlFilePath );
}
int main( int argc, char *argv[] )
{
char emscriptenHtmlFilePath[500], joinHtmlFilePath[500], outputHtmlFilePath[500];
if ( argc < 4 )
{
printf( "you can run this program via command line with three arguments:\n" );
printf( "arg 1 - emscripten html file\n");
printf( "arg 2 - html file to join\n");
printf( "arg 3 - output html file\n");
printf( "please enter these arguments manually\n" );
printf( "emscripten html file: " );
gets( emscriptenHtmlFilePath );
printf( "html file to join: " );
gets( joinHtmlFilePath );
printf( "output html file: " );
gets( outputHtmlFilePath );
}
else
{
strcpy( emscriptenHtmlFilePath, argv[1] );
strcpy( joinHtmlFilePath, argv[2] );
strcpy( outputHtmlFilePath, argv[3] );
}
///////////////////////////////////////
// merge the files here
char * emscriptenHtmlFileData = NULL, * joinHtmlFileData = NULL, * outputHtmlFileData = NULL;
FILE * outputHtmlFile = NULL;
emscriptenHtmlFileData = readContentsFromFile( emscriptenHtmlFilePath );
if ( emscriptenHtmlFileData == NULL )
goto cleanup;
joinHtmlFileData = readContentsFromFile( joinHtmlFilePath );
if ( joinHtmlFileData == NULL )
goto cleanup;
printf( "opening the file to write to...\n" );
//open the file for writing
outputHtmlFile = fopen( outputHtmlFilePath, "w+" );
if ( outputHtmlFile == NULL )
{
printf( "ERROR: could not load file \"%s\"\n", outputHtmlFilePath );
goto cleanup;
}
printf( "Creating joined file...\n" );
CreateJoinedFile( emscriptenHtmlFileData, joinHtmlFileData, outputHtmlFile );
cleanup:
if ( outputHtmlFile != NULL )
fclose( outputHtmlFile );
free( emscriptenHtmlFileData );
free( joinHtmlFileData );
free( outputHtmlFileData );
return 0;
}
This is my build script at this point:
~/emscripten/emscripten/emcc main.cpp game.cpp ShootingScreen.cpp drawing.cpp mainmenu.cpp -o /var/www/html/archery.html -s EXPORTED_FUNCTIONS="['_imageloaded','_main']"~/utils/htmljoin /var/www/html/archery.html javascript /var/www/html/archery2.html
The text in red, takes my emscripten output, archery.html and javascript and combines it into one file archery2.html. The contents in javascript file are inserted before the <body> tag.
My javascript file
This script code gets injected into the html output. Using this code I can call "imageloaded" which exists in my c++/c code. I found that you need to delay until you can call a function for things to load. My guess is that you could use ready() from jquery to know when you can call a c function. Also, adding jquery library links to this file might be a great idea!
Code to htmljoin utility
Save what is below to htmljoin.cpp and run "gcc htmljoin.cpp -o htmljoin" to compile this code. It's that simple!#include <cstdio>
#include <cstdlib>
#include <cstring>
int getFileSize( FILE * file )
{
fseek (file , 0 , SEEK_END);
int size = ftell (file);
rewind (file);
return size;
}
char * readContentsFromFile( char * filePath )
{
FILE * file = fopen( filePath, "r" );
if ( file == NULL )
{
printf( "ERROR: could not load file \"%s\"\n", filePath );
return NULL;
}
int size = getFileSize( file );
char * buffer = (char*) malloc (sizeof(char)*size + 1);
if (buffer == NULL)
{
printf("ERROR: error allocating memory for reading the file\n");
fclose( file );
return NULL;
}
printf( "reading %s file, %d bytes\n", filePath, size );
// copy the file into the buffer:
int result = fread( buffer, 1, size, file );
buffer[size] = '\0';
fclose( file );
if (result != size)
{
printf("ERROR: error reading the contents of the file\n");
free( buffer );
return NULL;
}
return buffer;
}
void CreateJoinedFile( char * emscriptenHtmlFileData, char * joinHtmlFileData, FILE * outputHtmlFilePath )
{
//find where body is in the string
char * bodyLocation = strstr( emscriptenHtmlFileData, "<body>" );
if ( bodyLocation == NULL )
{
printf( "ERROR: could not find the <body> tag\n" );
return;
}
printf( "writing first piece of emscripten file...\n" );
size_t toWrite = bodyLocation - emscriptenHtmlFileData;
fwrite( emscriptenHtmlFileData, 1, toWrite, outputHtmlFilePath );
printf( "joining files...\n" );
//insert the code
int lengthJoin = strlen( joinHtmlFileData );
fwrite( joinHtmlFileData, 1, lengthJoin, outputHtmlFilePath );
printf( "writing last piece of emscripten file...\n" );
int bodyLength = strlen( bodyLocation );
fwrite ( bodyLocation, 1, bodyLength, outputHtmlFilePath );
}
int main( int argc, char *argv[] )
{
char emscriptenHtmlFilePath[500], joinHtmlFilePath[500], outputHtmlFilePath[500];
if ( argc < 4 )
{
printf( "you can run this program via command line with three arguments:\n" );
printf( "arg 1 - emscripten html file\n");
printf( "arg 2 - html file to join\n");
printf( "arg 3 - output html file\n");
printf( "please enter these arguments manually\n" );
printf( "emscripten html file: " );
gets( emscriptenHtmlFilePath );
printf( "html file to join: " );
gets( joinHtmlFilePath );
printf( "output html file: " );
gets( outputHtmlFilePath );
}
else
{
strcpy( emscriptenHtmlFilePath, argv[1] );
strcpy( joinHtmlFilePath, argv[2] );
strcpy( outputHtmlFilePath, argv[3] );
}
///////////////////////////////////////
// merge the files here
char * emscriptenHtmlFileData = NULL, * joinHtmlFileData = NULL, * outputHtmlFileData = NULL;
FILE * outputHtmlFile = NULL;
emscriptenHtmlFileData = readContentsFromFile( emscriptenHtmlFilePath );
if ( emscriptenHtmlFileData == NULL )
goto cleanup;
joinHtmlFileData = readContentsFromFile( joinHtmlFilePath );
if ( joinHtmlFileData == NULL )
goto cleanup;
printf( "opening the file to write to...\n" );
//open the file for writing
outputHtmlFile = fopen( outputHtmlFilePath, "w+" );
if ( outputHtmlFile == NULL )
{
printf( "ERROR: could not load file \"%s\"\n", outputHtmlFilePath );
goto cleanup;
}
printf( "Creating joined file...\n" );
CreateJoinedFile( emscriptenHtmlFileData, joinHtmlFileData, outputHtmlFile );
cleanup:
if ( outputHtmlFile != NULL )
fclose( outputHtmlFile );
free( emscriptenHtmlFileData );
free( joinHtmlFileData );
free( outputHtmlFileData );
return 0;
}
Calling a C function from Javascript in your Emscripten program
One thing you may want to do is pass dynamic content into your Emscripten program. This is possible! I'll show in a little bit.
When emscripten was first released I could lazy load it. Lazy loading loads it byte per byte on demand from a location on the web. This is no longer supported though. For me lazy loading isn't really ideal either for a couple of reasons. First it would be syncronous which would stop other parts of my program from loading while it fetched a chunk of the image. Secondly I don't want to deal with writing code to decode an image! It's not 1995 all over again! I'd prefer the browser to decode the image and send me the data in rgb32. So my solution is to have javascript load the image, map it to a canvas object, then use the canvas to pass into my program the rgb32 array. Simple enough right!
Let's dig in and figure out how to call a C function from js, pass it some paramaters and have the program react to those arguements!
Here is a function I exported:
2) Second, you'll need to create some variable to interact that is global. For an archery game, I created a CGame object that is accessible everywhere. You may have a CApplication object. Whatever makes sense for you, but somehow you need to interact with the state of the app before you leave your C style function.
3) The build command must export the function. If you don't do this, the compiler will see it is not used and remove the function. When you export this function, the hidden gotcha is you need to export your main() function as well to continue to allow your program to work as it was before - that is assuming you have an application. Here is what I added to my build command to export main and imageloaded:
~/emscripten/emscripten/emcc main.cpp javascriptmapper.c game.cpp ShootingScreen.cpp drawing.cpp mainmenu.cpp -o /var/www/html/archery.html -s EXPORTED_FUNCTIONS="['_imageloaded','_main']"
Note! The magic emscripten is doing in the background is prefixing functions with an _ so they don't collide with other function names. That is why when you export them you add an _.
4) Call the function in javascript. The easy way to test this is load up your html program, then run the debugger in your browser using the f12 key. Go to the console, and run this javascript:
That's how you can call your function in javascript code. I changed imageloaded to return the integer 5 and you can see that result.
There is another way to call functions that is more performent when calling a function lots of times. I would check out the emscripten documentation for how to do this and also check out the documentations for how to send in parameters.
Here is a link:
https://github.com/kripken/emscripten/wiki/Interacting-with-code
A little background
I am in the process of writing a program in emscripten - this is a little different then what most people do, which is port an existing project into emscripten. When you port into emscripten, you can package all of your content (images, soundes, etc) into the build. This works out pretty well for most C programs as they tend not to interact with external pieces. Say you write a notepad app - it is just interacting with the file system or if you write an asteroid game, all of the content is static and doesn't rely on external dynamic content. For my program, I want to access an image that can change and isn't bundled with the app.When emscripten was first released I could lazy load it. Lazy loading loads it byte per byte on demand from a location on the web. This is no longer supported though. For me lazy loading isn't really ideal either for a couple of reasons. First it would be syncronous which would stop other parts of my program from loading while it fetched a chunk of the image. Secondly I don't want to deal with writing code to decode an image! It's not 1995 all over again! I'd prefer the browser to decode the image and send me the data in rgb32. So my solution is to have javascript load the image, map it to a canvas object, then use the canvas to pass into my program the rgb32 array. Simple enough right!
Let's dig in and figure out how to call a C function from js, pass it some paramaters and have the program react to those arguements!
Calling a C function from Javascript
1) First, create a function in a .c file or tag a function with extern "C". This is what you will be allowing to be called from javascript. Extern "C" will prevent name mangling c++ does. All exported functions need to retain their original names to be called from javascript.Here is a function I exported:
2) Second, you'll need to create some variable to interact that is global. For an archery game, I created a CGame object that is accessible everywhere. You may have a CApplication object. Whatever makes sense for you, but somehow you need to interact with the state of the app before you leave your C style function.
3) The build command must export the function. If you don't do this, the compiler will see it is not used and remove the function. When you export this function, the hidden gotcha is you need to export your main() function as well to continue to allow your program to work as it was before - that is assuming you have an application. Here is what I added to my build command to export main and imageloaded:
~/emscripten/emscripten/emcc main.cpp javascriptmapper.c game.cpp ShootingScreen.cpp drawing.cpp mainmenu.cpp -o /var/www/html/archery.html -s EXPORTED_FUNCTIONS="['_imageloaded','_main']"
Note! The magic emscripten is doing in the background is prefixing functions with an _ so they don't collide with other function names. That is why when you export them you add an _.
4) Call the function in javascript. The easy way to test this is load up your html program, then run the debugger in your browser using the f12 key. Go to the console, and run this javascript:
That's how you can call your function in javascript code. I changed imageloaded to return the integer 5 and you can see that result.
There is another way to call functions that is more performent when calling a function lots of times. I would check out the emscripten documentation for how to do this and also check out the documentations for how to send in parameters.
Here is a link:
https://github.com/kripken/emscripten/wiki/Interacting-with-code
Thursday, May 23, 2013
Dynamic loading of content in Emscripten (Packaging files and lazy loading)
It appears Emscripten allows two types of loading or at least did. The first type is where you package files with your build command. The compiled code then shows a nice download progress bar. Once it's loaded then your program runs. This is the majority of how I have seen the apps run. The other method lazy loading no longer works. Lazy loading was intended to work by synchronously requesting chunks of bytes. Say you need X bytes of a file, lazy loading would request these bytes via http and then wait on the call until those bytes were returned. Just like reading from a hard drive so everything seems to work the same right? Firefox and Chrome disabled this synchronous call or supporting technology used behind the hood of the engine. So lazy loading is unlikely to work for you. My thoughts to get around this are to load the file via javascript and then use a hook from the C code to get at that data. I will report back if I am able to do this.
Tuesday, May 21, 2013
Getting started guide with Emscripten on an Amazon EC2 instance
You may find yourself in my same circumstance - wanting to try out Emscripten but not having an available linux box to install and run everything you need. So the shortcut to setting up a linux box is to use a cloud linux box. It's also a great opportunity to get more familar with amazon services. So to do this there are a lot of steps, that I've boiled down into different blog posts. This is the high level blog post which if you follow all of these steps you'll be up and running emscripten in short order.
Getting started using Emscripten
http://faantasticcoder.blogspot.com/2013/05/creating-amazon-linux-ami-for-emscripten.html
Getting started using Emscripten
1) Launch a new Amazon Linux ami instance.
http://faantasticcoder.blogspot.com/2013/05/creating-amazon-linux-ami-for-emscripten.html
2) Setup an Amazon EC2 machine to compile Emscripten
http://faantasticcoder.blogspot.com/2013/05/setting-up-amazon-ec2-machine-to.html3) Get setup to write code for Emscripten on an Amazon EC2 machine
http://faantasticcoder.blogspot.com/2013/05/setting-up-to-write-code-for-emscripten.html
Subscribe to:
Posts (Atom)