Build SmartBody From Source
Install Emscripten
Build Supporting Libraries
Build SmartBody
Rendering using WebGL friendly subset of OpenGL
- Useful links
Install Emscripten
SmartBody JavaScript uses Emscripten to port SmartBody and its external supporting libraries from C/C++ into highly optimizable Javascript code in asm.js format. This section briefly introduce how to install Emscripten under Windows and Linux and notes about the installation. For more information please refer to Emscripten Installation.
Emscripten Setup in Linux
1. Check Environmentpython --version # Check for Python nodejs --version # Check for node.js on Linux git --version # Check for git java -version # Check for Java gcc --version # Check for gcc / g++ g++ cmake --version # Check for cmake#If anything above is missing apt-get it:sudo apt-get install build-essential cmake python2.7 nodejs default-jre git-core
2. Emcripten download and installation:
1) Download the portable version of Emscripeten emsdk-portable.tar.gz
2) Unzip it and into folder of emsdk_portable
3) Open a command prompt inside the SDK directory and run the following commands# Fetch the latest registry of available tools. ./emsdk update # Download and install the latest SDK tools. ./emsdk install latest # Make the "latest" SDK "active" ./emsdk activate latest # Set the current Emscripten path on Linux/Mac OS X source ./emsdk_env.sh
4) Type emcc -v to check the installation, if you encounter the following error message, edit the ~/.emscripten change the line NODE_JS = 'node' to NODE_JS = 'nodejs' save and close.
5) Check the emcc -v again, you should be able to see the version of emcc as follows:
- Emscripten Setup in Windows with Visual Studio 2010
1. Download the Windows version of Emscripten: Emscripten SDK Offline Installer
2. Run the installer and follow the instruction to install Emscripten
3. After the installation, check it with the command line: C:\Program Files\Emscripten>emcc -v you should be able to see the version of Emscripten.4. Visual Studio Setup
1) Open SmartBodu VS 2010 project
2) Open: Toolbar->Build->Configuration Manager
3) Active Solution Platform drop-down , you should be able to see Emscripten Selection and copy settings from Win32
Make sure the version is the same under Windows and Linux
If you need to build some libraries in Linux and others in Windows, please make sure the version of Emscripten is uniformed. This is very important, cause using different version of Emscripten may result in the failure of linking static libraries. If the versions are different, you need unify them. For example, if the version under Windows is 1.30.0 with Clang 3.5, but the version under Linux is 1.33.0 and Clang 3.7, you need to either upgrade Windows version or downgrade Linux version. From my experience, downgrade Linux version is easier.
Downgrade Linux version of Emscripten
1. Using ./emsdk list to see what has been installed of Emscripten:2. Install emscripten:
Suppose we need to install emscripten-1.30.0, then we using cmd: ./emsdk install emscripten-1.30.0. After a while it will be installed, use ./emsdk list to check the installation. Also remember to activate it using emsdk activate emscripten-1.30.0
Also please note as indicated in the reference page, you should use the same branch (incoming, or master) for building all three repositories.Therefore, we need also change LLVM and Clang version.
You can find the right version of LLVM and Clang version on the following repo, distinguished by tags:
https://github.com/kripken/emscripten-fastcomp
https://github.com/kripken/emscripten-fastcomp-clang
Please follow the instruction in the reference link to build Fastcomp, there is only one thing worth mentioning, when you checkout the git repo, make sure you checkout the right tags:
The following git commands helps you checkout the right version:git clone <repo-address> git tag -l git checkout <tag-name> git branch -D master git checkout -b master
3. Use emcc -v again to check the version of Emscripten and Clang.
Build Supporting Libraries
Smartbody/Javascript relies on many supporting libraries such as Boost, Xerces, VHCL and more. We first need to compile those libraries from C/C++ from LLVM bitcode.
- Xerces-c
Version: 3.1.2
Platform: Linux
1. Download package and unzip
2. Run command: $ emconfigure ./configure --disable-threads
3. Run command: $ emmake make
4. Location: ./src/.libs/libxerces-c.a
5. Don't delete .c files in xerces-c\include\xercesc\util - Boost
Version: 1.58.0
Platform: Linux
Credit to Ariel Malka's Github project
1. Clone the repository into your local directory from https://github.com/ZengruiWang/chronotext-boost
2. Run: bash setup.sh which downloads the boost_1_58_0 package and some patches
3. Edit 'build-emscripten.sh'
1) Specify EMSCRIPTEN_BIN = '/your/path/to/emsdk_portable/emscripten/1.30.0' (where your Emscripten SDK locates)
2) Specify the libraries to build: --without-python --without-coroutine --without-context
4. Run: bash build-emscripten.sh
5. All compiled libs is under ./chronotext-boost/lib/emscripten/ - ODE
Version: 0.11.1
Platform: Linux
1. Download package and unzip
2. Run command: $emconfigure ./configure --disable-demos --without-x --enable-double-precision
3. Run command: $emmake make
4. Complied libs location:
./ode/src/joints/.libs/libjoints.a
./ode/src/.libs/libode.a
./ode/src/.libs/libfast.a
./OPCODE/.libs/libOPCODE.a
./OPCODE/Ice/.libs/libIce.a Protobuf
Version: protobuf-2.5.0
Platform: Windows Visual Studio 2010
Instruction:
1. Modify protobuf-2.5.0\src\google\protobuf\stubs\hash.h line 42: append at the end " && !defined(EMSCRIPTEN) "
2. Open vsprojects/protobuf.sln in Microsoft Visual Studio.
3. For project "libprotobuf"
1) Change the extension of all source files from .cc to .cpp
2) Replace and add the following files in patch directory to the folder: ..\src\google\protobuf\stubs
atomicops.h
atomicops_internals_emscripten.h
platform_macros.h
common.cpp
3) Also delete the Additional Options in the project properties->Clang C/C++->Command Line
4) Compile the project, DONE
4. For project "libprotoc"
1) Change the extension of all source files from .cc to .cpp
2) Also in the header folder, there is also a .cc file java_doc_comment.cc change its extension to .cpp too
3) Also delete the Additional Options in the project properties->Clang C/C++->Command Line
4) Compile the project, DONE
5. Modify the extract_includes.bat file: add "copy ..\src\google\protobuf\stubs\atomicops_internals_emscripten.h include\google\protobuf\stubs\atomicops_internals_emscripten.h
6. Run the extract_includes.bat to generate the include directory- FreeALUT
Version: unknown(under emscripten/1.30.0/test/freealut)
Platform: Linux
1. Run command: $ emconfigure ./configure --disable-threads
2. Run command: $ emmake make
3. Location: emsdk_portable/emscripten/1.30.0/tests/freealut - LibSNDFile
Version: 1.0.25
Platform: Linux
1) Download package and unzip
2) Run command: $emconfigure ./configure
3) Copy and Paste config.h and sndfile.h to ./src
4) Eddit sndfile.c in the function SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) Comment out the assert(sizeof(sf_count_t) == 8);
5) Run command: $emmake make
6) Complied libs location:
./src/.libs/libcommon.a
./src/.libs/libsndfile.a OpenAL
Out of box support#if defined(EMSCRIPTEN_BUILD) #include <AL/al.h> #include <AL/alc.h> #include <AL/alut.h> #endif
- Zlib
Replace deflate.c and zconf.h with the following ones: deflate.c and zconf.h in core\smartbody\SmartBody\src\external\zlib-1.2.5 Eigen
This is a new library for Smartbody/Javascript to replace the Boost Numeric Bindings LAPACK/BLAS
Version: 3.2.6
Platform: Windows
1) Download package and unzip
2) No need to compile itVHCL
Platform: Visual Studio 2010
1. Configuration project properties
1) vhcl->Properties->Configureation Properties->General->Configureation Type->Static LLVM Bitcode Library(.bc)
2) vhcl->Properties->Clang C/C++->Command Line->Additional Options #Delete "/MP"
3) Additional Include Directories(-I)include ..\..\javascript\lib\vhcl\libsndfile\include ..\..\javascript\lib\vhcl\freealut\include
4) Delete WIN32 and NDEBUG in Preprocessor
5) Exclude nocturnal from poject
2. Source code modification
1) In vhcl_platform.h, add Emscripten platform//We first need to undef _WIN32, because it is explicitly and errantly defined at somewhere, we will look into it later. For now we just undefined it. #if defined(EMSCRIPTEN) #undef _WIN32 #endif // Platform identification. #if defined(_WIN32) .... #elif defined(EMSCRIPTEN) #define EMSCRIPTEN_BUILD 1 #else #define UNKNOWN_BUILD 1 #endif
Also modify the build type part(Line 116)
#else #if defined(LINUX_BUILD) || defined(MAC_BUILD) || defined(IPHONE_BUILD) || defined(ANDROID_BUILD) || defined(FLASH_BUILD) || defined(EMSCRIPTEN_BUILD) #define RELEASE_BUILD 1 #else #define UNKNOWN_BUILD 1 #endif #endif
2) In vhcl_audio.cpp include OpenAL and FreeAlut header files the same as LINUX_BUILD
#elif defined(LINUX_BUILD) || defined(EMSCRIPTEN_BUILD) #include <AL/al.h> #include <AL/alc.h> #include <AL/alut.h>
3) In vhcl_assert.h, modified the following part by adding EMSCRIPTEN_BUILD
#if defined(VHCL_ASSERTS_ENABLED) #define POW2_ASSERTS_ENABLED #elif defined(VHCL_ASSERTS_DISABLED) #else #if defined(DEBUG_BUILD) && !defined(EMSCRIPTEN_BUILD) #define POW2_ASSERTS_ENABLED #elif defined(RELEASE_BUILD) //#define POW2_ASSERTS_ENABLED #endif #endif
4) In vhcl_log.cpp
void LOG( const char * message, ... ) { #if defined(VHCL_LOG_FUNCTION_ENABLED) #if defined(EMSCRIPTEN) va_list argList; char buf[BUFSIZ]; va_start( argList, message ); vsprintf ( buf, message, argList ); printf ( "%s\n", buf ); va_end ( argList ); #else va_list argList; va_start( argList, message ); vhcl::Log::g_log.vLog( message, argList ); va_end( argList ); #endif #endif }
5) In vhcl_string.cpp
#if defined(MAC_BUILD) || defined(ANDROID_BUILD) || defined(EMSCRIPTEN_BUILD) #include <sys/select.h> #endif
SteerLib
1. Configuration project properties
1) steerlib->Properties->Configuration Properties->General->Configureation Type->Static LLVM Bitcode Library(.bc)
2) steerlib->Properties->Clang C/C++->Command Line->Additional Options #Delete "/MP"
3) steerlib->Properties->Configuration Properties->General->Output Directory: ..\..\..\build\Emscripten\$(Configuration)\
4) Delete all preprocessor
5) Additional Include Directories(-I)..\..\include ..\..\..\external ..\..\..\external\tinyxml ..\..\..\pprAI\include
2. Source code modification
1) In util/HighResCounter.h#define __UTIL_HIGH_RES_COUNTER_H__ #if defined(EMSCRIPTEN) #undef _WIN32 #endif
static inline unsigned long long getHighResCounterValue()#elif defined (BUILD_ANDROID) || defined (EMSCRIPTEN) timespec timeInfo; clock_gettime(CLOCK_REALTIME, &timeInfo); unsigned long long int nanosecs = ((unsigned long long)timeInfo.tv_sec)*1000000000 + ((unsigned long long)timeInfo.tv_nsec); return nanosecs;
2) In simulation/SimulationEngine.cpp add Emscripten platform support, since it uses static library
#if defined(SB_IPHONE) || defined(__ANDROID__) || defined(__FLASHPLAYER__) || defined(EMSCRIPTEN) #include "PPRAIModule.h" #endif ... //(around line 671) #if defined(SB_IPHONE) || defined(__ANDROID__) || defined(EMSCRIPTEN) newModule = new PPRAIModule; #else
- pprAI
1. Configuration project properties
1) pprAI->Properties->Configureation Properties->General->Configureation Type->Static LLVM Bitcode Library(.bc)
2) pprAI->Properties->Configuration Properties->General->Output Directory: ..\..\..\build\Emscripten\$(Configuration)\3) Delete all preprocessor
4) Additional Include Directories(-I)..\..\include ..\..\..\steerlib\include ..\..\..\external
Build SmartBody
Configuration project properties
1. SmartBody->Properties->Configuration Properties->General->Output Directory: $(Configuration)\
2. SmartBody->Properties->Configureation Properties->General->Configureation Type->Static LLVM Bitcode Library(.bc)
3. Additional Include Directories(-I).\src ..\..\..\javascript\lib\protobuf\include ..\..\..\javascript\lib\xerces-c\include ..\..\..\javascript\lib\boost ..\..\..\javascript\lib\ode\include ..\..\..\javascript\lib\eigen ..\..\..\lib\vhcl\include ..\..\..\core\smartbody\steersuite-1.3\steerlib\include ..\..\..\core\smartbody\steersuite-1.3\pprAI\include ..\..\..\core\smartbody\steersuite-1.3\external
4. Preprocessor
SB_NO_ASSIMP SB_NO_PYTHON SB_NO_BONEBUS SB_NO_VHMSG SB_EXPORTS dDOUBLE _WINDOWS _USRDLL _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS BOOST_ALL_NO_LIB BOOST_TYPEOF_SILENT
5. Clang C/C++->Command Line->Additional Options: delete /MP and add: --bind, which compiles the source code using the Embind bindings to connect C/C++ and JavaScript.
6. Open SmartBody.vcxproj, search /bigobj ,delete the following line:<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">/bigobj %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">/bigobj %(AdditionalOptions)</AdditionalOptions>
7. Project Property->Configuration Properties->Configure Type, switch to Browser Application(.html), and click apply, then go to Emcc Linker->Input, delete all of Additional Dependencies. Finally, switch back to Static LLVM Bitcode(.bc)
8. Clear all Build EventsSource code modification
1. In controllers/me_ct_motion_example.cpp, we don't need those debug information.
#if defined(_DEBUG) && !defined(EMSCRIPTEN) #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW #endif
In controllers/me_ct_motion_example.hpp, delete
#ifndef EMSCRIPTEN
#include "controllers/me_ct_ublas.hpp"
#endifand leave it as #include "controllers/me_ct_ublas.hpp"
2. In controllers/me_ct_physics_controller.cpp, we need to pass a pointer to handleEvent or it will cause pointer delete exception when bind to Javascript using Embind
bool MeCtPhysicsController::controller_evaluate(double t, MeFrameData& frame)if (colRecords.size() > 0 && !hasGaze) { ... //around line 97 std::string cmd = eventMsg; #if !defined(EMSCRIPTEN) ... #else SmartBody::SBMotionEvent* motionEvent = new SmartBody::SBMotionEvent(); std::string eventType = "collision"; motionEvent->setType(eventType); motionEvent->setParameters(cmd); SmartBody::SBEventManager* manager = SmartBody::SBScene::getScene()->getEventManager(); manager->handleEvent(motionEvent,t); #endif ... } else if (hasGaze && t > fadeOutTime) { ... //around line 124 std::string cmd = eventMsg; #if !defined(EMSCRIPTEN) ... #else SmartBody::SBMotionEvent *motionEvent = new SmartBody::SBMotionEvent(); std::string eventType = "collision"; motionEvent->setType(eventType); motionEvent->setParameters(cmd); SmartBody::SBEventManager* manager = SmartBody::SBScene::getScene()->getEventManager(); manager->handleEvent(motionEvent,t); #endif }
3. In controllers/me_ct_ublas.cpp
#if defined(EMSCRIPTEN) #include <Eigen/Dense> #include <Eigen/SVD> #endif using namespace Eigen;
void MeCtUBLAS::matrixMatMult(const dMatrix& mat1, const dMatrix& mat2, dMatrix& mat3)#if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) blas::gemm(mat1,mat2,mat3); #endif #if defined(EMSCRIPTEN) MatrixXd m1(mat1.size1(), mat1.size2()), m2(mat2.size1(),mat2.size2()), m3(mat3.size1(), mat3.size2()); for(unsigned int i = 0; i < mat1.size1(); ++i) for(unsigned int j = 0; j < mat1.size2(); ++j) m1(i, j) = mat1(i, j); for(unsigned int i = 0; i < mat2.size1(); ++i) for(unsigned int j = 0; j < mat2.size2(); ++j) m2(i, j) = mat2(i, j); m3 = m1 * m2; //copy result to mat3 for(unsigned int i = 0; i < mat3.size1(); ++i) for(unsigned int j = 0; j < mat3.size2(); ++j) mat3(i, j) = m3(i, j); #endif
void MeCtUBLAS::matrixVecMult(const dMatrix& mat1, const dVector& vin, dVector& vout)#if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) blas::gemv('N',1.0,mat1,vin,0.0,vout); #endif #if defined(EMSCRIPTEN) MatrixXd m1(mat1.size1(), mat1.size2()); for(size_t i = 0; i < mat1.size1(); ++i) for(size_t j = 0; j < mat1.size2(); ++j) m1(i, j) = mat1(i, j); VectorXd vIn(vin.size()), vOut; for(size_t i = 0; i < vin.size(); ++i) vIn(i) = vin(i); vOut = m1 * vIn; for(size_t i = 0; i < vout.size(); ++i) vout(i) = vOut(i); #endif
bool MeCtUBLAS::inverseMatrix( const dMatrix& mat, dMatrix& inv )#if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) lapack::gesv(A,inv); #endif #if defined(EMSCRIPTEN) MatrixXd m(mat.size1(), mat.size2()), mInv; for(size_t i = 0; i < mat.size1(); ++i) for(size_t j = 0; j < mat.size2(); ++j) m(i, j) = mat(i, j); mInv = m.inverse(); for(size_t i = 0; i < inv.size1(); ++i) for(size_t j = 0; j < inv.size2(); ++j) inv(i, j) = mInv(i, j); #endif
bool MeCtUBLAS::matrixSVD( const dMatrix& A, dVector& S, dMatrix& U, dMatrix& V )#if !defined(__ANDROID__) && !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) dMatrix M(A); lapack::gesvd(M,S,U,V); #endif #if defined(EMSCRIPTEN) MatrixXd mA; for(size_t i = 0; i < A.size1(); ++i) for(size_t j = 0; j < A.size2(); ++j) mA(i, j) = A(i, j); JacobiSVD<MatrixXd> svd(mA, ComputeThinU | ComputeThinV); VectorXd s = svd.singularValues(); MatrixXd mU = svd.matrixU(); MatrixXd mV = svd.matrixV(); for(int i = 0; i < s.size(); ++i) S(i) = s(i); for(int i = 0; i < mU.innerSize(); ++i) for(int j = 0; j < mU.outerSize(); ++j) U(i, j) = mU(i, j); for(int i = 0; i < mV.innerSize(); ++i) for(int j = 0; j < mV.outerSize(); ++j) V(i, j) = mV(i, j); #endif
4. In controllers/MeCtBodyReachState.cpp, we need to pass a pointer to handleEvent or it will cause pointer delete exception when bind to Javascript using Embind
void ReachHandAction::sendReachEvent(const std::string& etype, const std::string& cmd, float time /*= 0.0*/ ) { #if defined(EMSCRIPTEN) std::string eventType = etype; SmartBody::SBMotionEvent* motionEvent = new SmartBody::SBMotionEvent(); motionEvent->setType(eventType); motionEvent->setParameters(cmd); SmartBody::SBEventManager* manager = SmartBody::SBScene::getScene()->getEventManager(); manager->handleEvent(motionEvent,time); #else ... #endif }
5. In external/faceshift/fsbinarystream.cpp find all lines of #if _MSC_VER == 1500 and replace with #if _MSC_VER == 1500 || defined(EMSCRIPTEN)
6. In external/faceshift/fsbinarystream.h find #if _MSC_VER == 1500 and replace with #if _MSC_VER == 1500 || defined(EMSCRIPTEN)
7. In SOIL/SOIL.c, we use OpenGL ES 2.0#elif defined(__FLASHPLAYER__) #include <GL/gl.h> #elif defined(EMSCRIPTEN) #include <GLES2/gl2.h> #include <GLES2/gl2ext.h>
Modify the following part, since some openGL commands in desktop are not supported in OpenGL ES 2.0
//around line 99 #if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) #define GL_CLAMP GL_CLAMP_TO_EDGE; #endif #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) typedef void (APIENTRY* P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data); P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL; #endif
unsigned int SOIL_internal_create_OGL_texture//around line 1020 { /* variables */ unsigned char* img; unsigned int tex_id; #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) ... #endif return tex_id; }
unsigned int SOIL_direct_load_DDS_from_memory//around line 1774 #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) soilGlCompressedTexImage2D(cf_target, 0, S3TC_type, width, height, 0, DDS_main_size, DDS_data ); #endif ... //around line 1085 #if !defined(__ANDROID__) && !defined(EMSCRIPTEN) mip_size = ((w+3)/4)*((h+3)/4)*block_size; soilGlCompressedTexImage2D(cf_target, i,S3TC_type, w, h, 0,mip_size, &DDS_data[byte_offset] ); #endif ... //around 1850 #if defined(EMSCRIPTEN) unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; #else unsigned int clamp_mode = GL_CLAMP; #endif
int query_DXT_capability( void )//around line 2007 /* and find the address of the extension function */ #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL; #else void* ext_addr = NULL; #endif ... //around line 2044 #elif defined(__FLASHPLAYER__) || defined(__ANDROID__) || defined(SB_IPHONE) || defined (EMSCRIPTEN) #else ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) glXGetProcAddressARB((const GLubyte *)"glCompressedTexImage2DARB"); #endif ... //around line 2065 /* all's well! */ #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) soilGlCompressedTexImage2D = ext_addr; has_DXT_capability = SOIL_CAPABILITY_PRESENT; #endif
8. In GPU/TBOData.cpp, we don't use TBOData.
#if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) #include "external/glew/glew.h" #endif .... #define BUFFER_OFFSET(i) ((char *)NULL + (i)) #if !defined(EMSCRIPTEN) ... #endif
9. In GPU/TBOData.h, give every function an empty implementation
#if !defined(EMSCRIPTEN) class TBOData { ... } #else class TBOData { ... } #endif
The same is for VBOData.h and VBOData.cpp
10. In sb/PABlend.cpp
void PABlend::getWeight(SrVec& pt, SrVec& v1, SrVec& v2, SrVec& v3, SrVec& v4, double& w1, double& w2, double& w3, double& w4)dMatrix invMat(3, 3); //around line 1166 #if !defined(EMSCRIPTEN) MeCtUBLAS::inverseMatrix(mat,invMat); #else SrMat mat4x4, mat4x4_inv; for(int i = 0; i < 3; ++i) for(int j = 0; j < 3; ++j) mat4x4.setData(j, i, mat(i, j)); mat4x4_inv = mat4x4.inverse(); #endif dVector vecIn(3); ... vecIn(2) = pt.z - v4.z; #if !defined(EMSCRIPTEN) MeCtUBLAS::matrixVecMult(invMat, vecIn, vecOut); w1 = vecOut(0); w2 = vecOut(1); w3 = vecOut(2); w4 = 1 - vecOut(0) - vecOut(1) - vecOut(2); #else SrVec vec_in, vec_out; for(int i = 0; i < 3; ++i) vec_in.setData(i, vecIn(i)); //vec_out = mat4x4_inv * vec_in; vec_out = vec_in*mat4x4_inv; w1 = vec_out.getData(0); w2 = vec_out.getData(1); w3 = vec_out.getData(2); w4 = 1 - w1 - w2 - w3; #endif
11. In sb/SBEvent.cpp
SBEventManager::~SBEventManager()#if !defined(SB_NO_JAVASCRIPT) && defined(EMSCRIPTEN) delete handler; // deleting old handler causes crash when handler is created with Python - need to fix this #endif
void SBEventManager::removeEventHandler(const std::string& type)#if !defined(SB_NO_JAVASCRIPT) && defined(EMSCRIPTEN) delete oldHandler; // deleting old handler causes crash when handler is created with Python - need to fix this #endif
12. In sb/sbm_character.cpp
void SbmCharacter::createStandardControllers()//around line 382 #if !defined(EMSCRIPTEN) postprocess_ct = new MeCtPosePostProcessing(sbSkel); postprocess_ct->setName(getName() + "_postprocessController"); postprocess_ct->ref(); #endif ... //around line 492 #if !defined(EMSCRIPTEN) postprocess_ct->init(dynamic_cast<SmartBody::SBCharacter*>(this),"base"); #endif ... //around line 520 #if !defined(EMSCRIPTEN) ct_tree_p->add_controller( postprocess_ct ); #endif
13. In sb/SBNavigationMesh.cpp
SBAPI bool SBNavigationMesh::buildNavigationMeshFromModel( SrModel& inMesh )#if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) int* tris = new int[inMesh.F.size()*3]; for (int i=0;i<inMesh.F.size();i++) { tris[i*3+0] = inMesh.F.get(i)[0]; tris[i*3+1] = inMesh.F.get(i)[1]; tris[i*3+2] = inMesh.F.get(i)[2]; } #else const int* tris = &inMesh.F.get(0)[0]; #endif
14. In sb/SBScene.cpp
#if !defined(__FLASHPLAYER__) && !defined(ANDROID_BUILD) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include "external/glew/glew.h" #include "sbm/GPU/SbmDeformableMeshGPU.h" #endif #ifndef SB_NO_JAVASCRIPT #include <emscripten.h> #endif ... #if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) #include <external/zlib-1.2.5/zip.h> #endif
void SBScene::initialize()#if !defined (__ANDROID__) && !defined(SB_IPHONE) && !defined(__native_client__) && !defined(EMSCRIPTEN) SbmShaderManager::singleton().setViewer(NULL); #endif
//around line 2179 #if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) void writeFileToZip(zipFile& zf, std::string readFileName, std::string fileNameInZip) ... #endif void SBScene::saveAssets(std::stringstream& strstr, bool remoteSetup, std::string outMediaPath)
std::vector<std::string> SBScene::checkVisibility(const std::string& characterName) { std::vector<std::string> visible_pawns, nonOccludePawns; #if !defined(EMSCRIPTEN) ... #endif // Returns visible pawns return nonOccludePawns; } std::vector<std::string> SBScene::occlusionTest( const std::vector<std::string>& testPawns) { std::vector<std::string> visiblePawns; #if !defined(GLES_RENDER) && !defined(EMSCRIPTEN) ... #endif return visiblePawns; }
bool SBScene::run(const std::string& command)#ifndef SB_NO_JAVASCRIPT emscripten_run_script( command.c_str() ); //otehr options: //int emscripten_run_script_int(const char *script) //char *emscripten_run_script_string(const char *script) #endif
bool SBScene::runScript(const std::string& script)#ifndef SB_NO_JAVASCRIPT emscripten_run_script(script.c_str()); #endif
15. In sb/SBScene.h
#if !defined(__FLASHPLAYER__) && !defined(EMSCRIPTEN) SBAPI void exportScenePackage(std::string outDir, std::string outZipArchiveName = ""); SBAPI void exportCharacter(std::string charName, std::string outDir); #endif
16. In sb/SBSteerManager.cpp, we need to add this even though we don't use dynamic library, or std::string ai = dynamic_cast<SmartBody::StringAttribute*>( SmartBody::SBScene::getScene()->getSteerManager()->getAttribute("aimodule") )->getValue(); will crush.
#if defined(EMSCRIPTEN) createStringAttribute("aimodule", "pprAI", true, "Basic", 60, false, false, false, "Agent module library"); #endif
17. In sb/SBVersion.hpp
#ifdef __ANDROID__ strstr << " android "; #else #ifdef EMSCRIPTEN strstr << " emscripten "; #else strstr << " other "; #endif #endif
18. In sbm/Heightfiled.cpp
void Heightfield::render( int renderMode ){ #if defined (__ANDROID__) || defined (SB_IPHONE) || defined(__native_client__) || defined(EMSCRIPTEN) #else ... #endif }
19. In PPRAISteeringAgent.cpp, we need to pass a pointer to handleEvent or it will cause pointer delete exception when bind to Javascript using Embind
void PPRAISteeringAgent::sendLocomotionEvent(const std::string& status)void PPRAISteeringAgent::sendLocomotionEvent(const std::string& status) { #if defined(EMSCRIPTEN) std::string eventType = "locomotion"; SmartBody::SBMotionEvent* motionEvent = new SmartBody::SBMotionEvent(); motionEvent->setType(eventType); std::stringstream strstr; strstr << character->getName() << " " << status; motionEvent->setParameters(strstr.str()); SmartBody::SBEventManager* manager = SmartBody::SBScene::getScene()->getEventManager(); manager->handleEvent(motionEvent, SmartBody::SBScene::getScene()->getSimulationManager()->getTime()); //LOG("PPRAISteeringAgent::sendLocomotionEvent Over"); #else ... #endif }
20. In sbm/sbm_deformable_mesh.cpp
#elif defined(EMSCRIPTEN) #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #endif #if !defined(__FLASHPLAYER__) && !defined(ANDROID_BUILD) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include "external/glew/glew.h" #include "sbm/GPU/SbmDeformableMeshGPU.h" #endif
void DeformableMeshInstance::blendShapes()#if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); #else glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_w, tex_h, 0, GL_RGB, GL_FLOAT, NULL); #endif ... #if !defined(EMSCRIPTEN) SbmBlendTextures::BlendAllAppearancesPairwise( _tempFBOPairs, _tempTexPairs, weights, texIDs, texture_names, SbmBlendTextures::getShader("Blend_All_Textures_Pairwise"), tex_w, tex_h); #endif
In void DeformableMeshInstance::update(), add updateTransformBuffer();
#if defined(EMSCRIPTEN) updateTransformBuffer(); return; #endif //updateFast(); //return;
21. In sbm/sbm_deformable_mesh.h#elif defined(__FLASHPLAYER__) #include <GL/gl.h> #elif defined(EMSCRIPTEN) #include <GLES2/gl2.h> #include <GLES2/gl2ext.h>
class SbmSubMesh#if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) std::vector<SrModel::Face> triBuf; #else std::vector<SrVec3i> triBuf; #endif
22. In sbm/SbmBlendFace.cpp, we don't use SbmBlendFace for now, treat it the same as VBOData, TBOData and SbmBlendFace.h
#if !defined(__FLASHPLAYER__) && !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include "external/glew/glew.h" #include "external/jpge/jpge.h" #endif #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) SbmBlendFace::SbmBlendFace() : DeformableMesh(){ ... } .... #endif #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) SbmBlendTextures::SbmBlendTextures(){ } .... #endif
23. In SbmDeformableMeshGPU.h#if !defined(EMSCRIPTEN) //The original code #else //Empty implementation of each function #endif
For SbmDeformableMeshGPU.cpp #if !defined(EMSCRIPTEN) all implementations, also true for SbmShader.cpp and SbmShader.h
24. For SBAssetHandlerCOLLADA.cpp, SBAssetHandlerObj.cpp, SBAssetHandlerOgre.cpp, SBAssetHandlerPly.cpp, and SBAssetHandlerSBMeshBinary.cpp
#if !defined (__ANDROID__) && !defined(SB_IPHONE) && !defined(__FLASHPLAYER__) && !defined(__native_client__) && !defined(EMSCRIPTEN) SbmDeformableMeshGPU* mesh = new SbmDeformableMeshGPU(); #else DeformableMesh* mesh = new DeformableMesh(); #endif
25. In SbmTexture.cpp
void SbmTexture::buildTexture(bool buildMipMap)//around line 277 #if !defined(EMSCRIPTEN) //OpenGL ES 2.0 no EMUN for GL_TEXTURE_2D myGLEnable(GL_TEXTURE_2D); #endif glPixelStorei(GL_UNPACK_ALIGNMENT, 1); #if !defined(EMSCRIPTEN) myGLEnable(iType); #endif ... //around line #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) if (!glIsTexture(texID)) { SbmShaderProgram::printOglError("SbmTexture.cpp:100"); } #endif #if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) #define GL_CLAMP GL_CLAMP_TO_EDGE #define GL_RGB8 GL_RGB #define GL_RGBA8 GL_RGBA #endif ... #if !defined(EMSCRIPTEN) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #endif ... //glTexImage2D(iType,0,texture_format,width,height,0,texture_format,GL_UNSIGNED_BYTE,buffer); #if !defined (__FLASHPLAYER__) && !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) if (buildMipMap) gluBuild2DMipmaps(iType, channels, width, height, texture_format, GL_UNSIGNED_BYTE, &imgBuffer[0] ); else glTexImage2D(iType,0,texture_format,width,height,0,texture_format,GL_UNSIGNED_BYTE, &imgBuffer[0]); #elif defined(EMSCRIPTEN) glTexImage2D(iType,0,texture_format,width,height,0,texture_format,GL_UNSIGNED_BYTE, &imgBuffer[0]); glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); glGenerateMipmap(GL_TEXTURE_2D); #endif ... #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) GLclampf iPrority = 1.0; glPrioritizeTextures(1,&texID,&iPrority); #endif
26. In sr_gl.h
#elif defined(EMSCRIPTEN) #include <GLES/gl.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> # else # include <GL/gl.h> # include <GL/glu.h> ... #if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) #define GLES_RENDER 1 #define GLdouble GLfloat #endif
27. In sr_gl.cpp, we give every function an empty implementation cause we don't need those functions
#if !defined(EMSCRIPTEN) //======================================= geometry ================================ ....till the end #else SBAPI void glClearColor ( const SrColor& c ){}; SBAPI void glLight ( int id, const SrLight& l, bool bind_pos){}; //!< id = x E {0,...,7}, from GL_LIGHTx SBAPI void glLightPos( int id, const SrLight& l ){}; SBAPI void glMaterial ( const SrMaterial &m ){}; //!< Sets material for GL_FRONT_AND_BACK SBAPI void glMaterialFront ( const SrMaterial &m ){}; SBAPI void glMaterialBack ( const SrMaterial &m ){}; //==================================== matrices ============================== SBAPI void glMultMatrix ( const SrMat &m ){}; SBAPI void glLoadMatrix ( const SrMat &m ){}; SBAPI void glTranslate ( const SrVec &v ){}; SBAPI void glScale ( float s ){}; SBAPI void glRotate ( const SrQuat &q ){}; SBAPI void glLookAt ( const SrVec &eye, const SrVec ¢er, const SrVec &up ){}; SBAPI void glPerspective ( float fovy, float aspect, float znear, float zfar ){}; SBAPI void glGetViewMatrix ( SrMat &m ){}; SBAPI void glGetProjectionMatrix ( SrMat &m ){}; //==================================== info ============================== SBAPI void glPrintInfo ( SrOutput &o ){}; #endif
28. In sr_gl_render_funcs.cpp
#if !defined(__FLASHPLAYER__) && !defined(ANDROID_BUILD) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include "external/glew/glew.h" #include "external/SOIL/SOIL.h" #endif ... #if !defined(ANDROID_BUILD) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include <sbm/GPU/SbmShader.h> #include <sbm/GPU/SbmDeformableMeshGPU.h> #endif ... #if !defined(EMSCRIPTEN) //=============================== render_model ==================================== ... till the end #else static void renderBlendFace( DeformableMeshInstance* shape){}; static void renderDeformableMesh( DeformableMeshInstance* shape, bool showSkinWeight = false ){}; static void render_model ( SrSnShapeBase* shape ){}; static void render_lines ( SrSnShapeBase* shape ){}; static void render_points ( SrSnShapeBase* shape ){}; static void render_box ( SrSnShapeBase* shape ){}; static void render_sphere ( SrSnShapeBase* shape ){}; static void render_cylinder ( SrSnShapeBase* shape ){}; static void render_polygon ( SrSnShapeBase* shape ){}; static void render_polygons ( SrSnShapeBase* shape ){}; #endif
29. In sr_model.cpp
int SrModel::common_vertices_of_faces ( int i1, int i2 )#if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) unsigned short *f1 = &(F[i1].a); unsigned short *f2 = &(F[i2].a); #else int *f1 = &(F[i1].a); int *f2 = &(F[i2].a); #endif
30. Insr_model.h
struct Face#if defined(__ANDROID__) || defined(SB_IPHONE) || defined(EMSCRIPTEN) ... #else ... #endif
31. In sr_sn_colorsurf.cpp
# include <sr/sr_sn_colorsurf.h> #if !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) # include <sr/sr_gl.h> #endif
void SrSnColorSurf::gl_render_node(bool alphaBlend) const#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) ... #endif
32. In sr_camera.cpp
#elif defined(EMSCRIPTEN) #include<GLES2/gl2.h> #include<GLES2/gl2ext.h> ... void SrFrustum::extractFrustum() { #if !defined(__ANDROID__) && !defined(EMSCRIPTEN) ... #endif }
33. In sr_model_important_ply.cpp
#if !defined (__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) #include <sbm/GPU/SbmTexture.h> #endif ... static void load_texture(int type, const char* file, const SrStringArray& paths) { #if !defined (__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) ... #endif } .... bool SrModel::import_ply( const char* file ) { ... #if TEST_TEXTURE #if !defined (__ANDROID__) && !defined(SB_IPHONE) && !defined(EMSCRIPTEN) ... #endif #endif }
34. Change all .cc files extension in protocols to .cpp
35. In SBPython.cpp, move #ifndef SB_NO_PYTHON up to the top of the functionvoid initPython(std::string pythonLibPath) { #ifndef SB_NO_PYTHON ... #endif }
36. In SBHandSynthesis.cpp, SBMotionGraph.cpp add #include <boost/lexical_cast.hpp>37) In SBPythonMesh.cpp change NO_PYTHON to SB_NO_PYTHON38) In SBPythonClass.cpp, move #ifndef SB_NO_PYTHON right above SrViewer* getViewer()39) In SBPythonSystem.cpp, move #include <sb/SBVersion.hpp> inside #ifndef SB_NO_PYTHON
- File deleted
1. Exclude example.c, iowin32.h and iowin32.c in external/zlib from project File added
Unlike other versions of SmartBody, we use Embind instead of Boost:: python to bind C/C++ functions and classes to Javascript, so that the complied code could be used in Javascript. For more information about Embind, refer here
List of added files:
SBJavascript.cpp, SBJavascript.h, SBJavascriptAnimation.cpp, SBJavascriptAttribute.cpp, SBJavascriptCharacter.cpp, SBJavascriptClass.cpp, SBJavascriptClass.h, SBJavascriptInternal.h, SBJavascriptMath.cpp, SBJavascriptMesh.cpp, SBJavascriptMotion.cpp, SBJavascriptScene.cpp
SBJavascriptSimulation.cpp, SBJavascriptSkeleton.cpp, SBJavascriptSystem.cpp
Link SmartBody and Supporting Libraries
We wrote a small rendering engine for SmartBody/Javascript, you could use it either as a reference or standalone application. It contains code on initialize EGL, OpenGL ES 2.0 for the rendering, as well as a binding for Interface. Please refer to the project for more information.
To compile the project, first we copy all the static libraries into the project, there is a batch file called CopyLibs.bat can be used.
After that open Project property page and choose the configure type either be Browser Application (.html) or Console Application( .js). If you only need the SmartBody Javascript libraries and will do the HTML presentation by yourself, you should choose using Console Application( .js). Also you need to add the Additional Include Directories, Preprocessor and Command Line very similar to SmartBody. We will focus on Emcc Linker part.
In Emcc Linker->Input->Additional Dependencies add the names of static libraries you copied.
In the Command Line window
-s ALLOW_MEMORY_GROWTH=1 --bind -s DISABLE_EXCEPTION_CATCHING=0 -g2
Then we could build the SmartBodyJS project, which we generate SmartBodyJS.js and SmartBodyJS.mem file.
Rendering using WebGL friendly subset of OpenGL
If you write your own code for rendering, remember to use WebGL friendly subset of OpenGL, which maps OpenGL command directly to WebGL. It include almost all commands of OpenGL ES 2.0 except for client-side array. More information could be found here.
Useful Links
[1] OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables (very useful when converting code to OpenGL ES 2.0 or WebGL)
http://web.eecs.umich.edu/~sugih/courses/eecs487/common/notes/APITables.xml
[2] WebGL Browser Report (useful when you need to know your brower's support for WebGL)
https://www.browserleaks.com/webgl
[3] GCC and MSVC C++ Demangler (useful when debuging code)