From 3cd6805dc071af48c623f557509124057d28bd03 Mon Sep 17 00:00:00 2001 From: Joseph Chow Date: Thu, 13 Dec 2018 13:01:58 -0500 Subject: [PATCH 01/53] add tooling for building Cinder against Emscripten + sample generation, etc. --- emscripten/CMakeLists.txt | 323 +++++++++++++++++++++ emscripten/cibuild | 17 ++ emscripten/cmake/Cinder.cmake | 70 +++++ emscripten/dev-server/.gitignore | 3 + emscripten/dev-server/bin/cli.js | 4 + emscripten/dev-server/index.js | 25 ++ emscripten/dev-server/package-lock.json | 160 ++++++++++ emscripten/dev-server/package.json | 16 + emscripten/dev-server/readme.md | 11 + emscripten/sample_generation/gensamples.py | 149 ++++++++++ emscripten/shell.html | 293 +++++++++++++++++++ tools/emscripten/cibuilder | 176 +++++++++++ tools/emscripten/fonts/DroidSans.ttf | Bin 0 -> 41028 bytes tools/emscripten/scripts/gensamplebuilds | 175 +++++++++++ 14 files changed, 1422 insertions(+) create mode 100644 emscripten/CMakeLists.txt create mode 100644 emscripten/cibuild create mode 100644 emscripten/cmake/Cinder.cmake create mode 100644 emscripten/dev-server/.gitignore create mode 100644 emscripten/dev-server/bin/cli.js create mode 100644 emscripten/dev-server/index.js create mode 100644 emscripten/dev-server/package-lock.json create mode 100644 emscripten/dev-server/package.json create mode 100644 emscripten/dev-server/readme.md create mode 100644 emscripten/sample_generation/gensamples.py create mode 100644 emscripten/shell.html create mode 100644 tools/emscripten/cibuilder create mode 100644 tools/emscripten/fonts/DroidSans.ttf create mode 100644 tools/emscripten/scripts/gensamplebuilds diff --git a/emscripten/CMakeLists.txt b/emscripten/CMakeLists.txt new file mode 100644 index 0000000000..218cb4fae1 --- /dev/null +++ b/emscripten/CMakeLists.txt @@ -0,0 +1,323 @@ +cmake_minimum_required( VERSION 3.1 FATAL_ERROR ) +set( CMAKE_VERBOSE_MAKEFILE on ) + +# Suppress compiler checks. Why? CMake seems to be inconsistent +# with how it checks compilers on different platforms. +set( CMAKE_C_COMPILER_WORKS 1 ) +set( CMAKE_CXX_COMPILER_WORKS 1 ) + +# Assume Debug if a build type isn't specified +if( "" STREQUAL "${CMAKE_BUILD_TYPE}" ) + set( CMAKE_BUILD_TYPE "Debug" CACHE FILEPATH "" FORCE ) +endif() + +get_filename_component( CINDER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE ) +set( CINDER_INC_DIR ${CINDER_DIR}/include ) +set( CINDER_SRC_DIR ${CINDER_DIR}/src ) +set( CINDER_LIB_DIR ${CINDER_DIR}/lib/emscripten ) + +### Needs to come atfer compilers are set +project( cinder_emscripten ) + +### Force some Emscripten CMake variables. CMake seems to be really particular +### about when these vars show up. They need to come after project. +set( CMAKE_AR "emcc" CACHE FILEPATH "" FORCE) +set( CMAKE_STATIC_LIBRARY_SUFFIX ".bc" ) +set( CMAKE_C_CREATE_STATIC_LIBRARY " -o " ) +set( CMAKE_CXX_CREATE_STATIC_LIBRARY " -o " ) + +set( CINDER_GL_ES_PLATFORM "es3" ) +set( CINDER_GL_ES_FLAGS "-DCINDER_GL_ES_3 -s USE_WEBGL2=1 -s FULL_ES3=1" ) + +# FreeType Flags +set( CINDER_FREETYPE_FLAGS "-DFT2_BUILD_LIBRARY -DFT_DEBUG_LEVEL_TRACE" ) + +# C Flags +set( C_FLAGS "-fvisibility=default -D_UNIX ${CINDER_GL_ES_FLAGS} ${CINDER_FREETYPE_FLAGS} -s USE_GLFW=3 -s DISABLE_EXCEPTION_CATCHING=0" ) +set( CMAKE_C_FLAGS_DEBUG "${C_FLAGS} -g -O0" ) +set( CMAKE_C_FLAGS_RELEASE "${C_FLAGS} -Os" ) + +# CPP Flags +set( CXX_FLAGS "-stdlib=libc++ -std=c++11 -fvisibility=default -D_UNIX ${CINDER_GL_ES_FLAGS} ${CINDER_FREETYPE_FLAGS} -s USE_GLFW=3 -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0" ) +set( CMAKE_CXX_FLAGS_DEBUG "${CXX_FLAGS} -g -O0 -fexceptions -frtti" ) +set( CMAKE_CXX_FLAGS_RELEASE "${CXX_FLAGS} -Os -fexceptions -frtti" ) + +list( APPEND CINDER_C_SRC_FILES + ${CINDER_SRC_DIR}/freetype/bdf/bdf.c + ${CINDER_SRC_DIR}/freetype/cff/cff.c + ${CINDER_SRC_DIR}/freetype/pcf/pcf.c + ${CINDER_SRC_DIR}/freetype/pfr/pfr.c + ${CINDER_SRC_DIR}/freetype/sfnt/sfnt.c + ${CINDER_SRC_DIR}/freetype/truetype/truetype.c + ${CINDER_SRC_DIR}/freetype/type1/type1.c + ${CINDER_SRC_DIR}/freetype/type42/type42.c + ${CINDER_SRC_DIR}/freetype/winfonts/winfnt.c + ${CINDER_SRC_DIR}/freetype/base/ftbase.c + ${CINDER_SRC_DIR}/freetype/base/ftbbox.c + ${CINDER_SRC_DIR}/freetype/base/ftbdf.c + ${CINDER_SRC_DIR}/freetype/base/ftbitmap.c + ${CINDER_SRC_DIR}/freetype/base/ftcid.c + ${CINDER_SRC_DIR}/freetype/base/ftdebug.c + ${CINDER_SRC_DIR}/freetype/base/ftfstype.c + ${CINDER_SRC_DIR}/freetype/base/ftgasp.c + ${CINDER_SRC_DIR}/freetype/base/ftglyph.c + ${CINDER_SRC_DIR}/freetype/base/ftgxval.c + ${CINDER_SRC_DIR}/freetype/base/ftinit.c + + #including this causes a multiple symbol error :/ not sure what to do here if needed. + #${CINDER_SRC_DIR}/freetype/base/ftlcdfil.c + ${CINDER_SRC_DIR}/freetype/base/ftmm.c + ${CINDER_SRC_DIR}/freetype/base/ftotval.c + ${CINDER_SRC_DIR}/freetype/base/ftpatent.c + ${CINDER_SRC_DIR}/freetype/base/ftpfr.c + ${CINDER_SRC_DIR}/freetype/base/ftstroke.c + ${CINDER_SRC_DIR}/freetype/base/ftsynth.c + ${CINDER_SRC_DIR}/freetype/base/ftsystem.c + ${CINDER_SRC_DIR}/freetype/base/fttype1.c + ${CINDER_SRC_DIR}/freetype/base/ftwinfnt.c + ${CINDER_SRC_DIR}/freetype/raster/raster.c + ${CINDER_SRC_DIR}/freetype/smooth/smooth.c + ${CINDER_SRC_DIR}/freetype/autofit/autofit.c + ${CINDER_SRC_DIR}/freetype/bzip2/ftbzip2.c + ${CINDER_SRC_DIR}/freetype/cache/ftcache.c + ${CINDER_SRC_DIR}/freetype/gzip/ftgzip.c + ${CINDER_SRC_DIR}/freetype/lzw/ftlzw.c + ${CINDER_SRC_DIR}/freetype/gxvalid/gxvalid.c + ${CINDER_SRC_DIR}/freetype/otvalid/otvalid.c + ${CINDER_SRC_DIR}/freetype/psaux/psaux.c + ${CINDER_SRC_DIR}/freetype/pshinter/pshinter.c + ${CINDER_SRC_DIR}/freetype/psnames/psnames.c + ${CINDER_SRC_DIR}/freetype/cid/type1cid.c + ${CINDER_SRC_DIR}/freetype/bdf/bdflib.c + + ${CINDER_SRC_DIR}/linebreak/linebreak.c + ${CINDER_SRC_DIR}/linebreak/linebreakdata.c + ${CINDER_SRC_DIR}/linebreak/linebreakdef.c + + ${CINDER_SRC_DIR}/libtess2/bucketalloc.c + ${CINDER_SRC_DIR}/libtess2/dict.c + ${CINDER_SRC_DIR}/libtess2/geom.c + ${CINDER_SRC_DIR}/libtess2/mesh.c + ${CINDER_SRC_DIR}/libtess2/priorityq.c + ${CINDER_SRC_DIR}/libtess2/sweep.c + ${CINDER_SRC_DIR}/libtess2/tess.c + + ${CINDER_SRC_DIR}/oggvorbis/ogg/bitwise.c + ${CINDER_SRC_DIR}/oggvorbis/ogg/framing.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/analysis.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/bitrate.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/block.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/codebook.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/envelope.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/floor0.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/floor1.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/info.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/lookup.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/lpc.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/lsp.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/mapping0.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/mdct.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/psy.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/registry.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/res0.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/sharedbook.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/smallft.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/synthesis.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/vorbisenc.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/vorbisfile.c + ${CINDER_SRC_DIR}/oggvorbis/vorbis/window.c + + ${CINDER_SRC_DIR}/zlib/adler32.c + ${CINDER_SRC_DIR}/zlib/compress.c + ${CINDER_SRC_DIR}/zlib/crc32.c + ${CINDER_SRC_DIR}/zlib/crc32.h + ${CINDER_SRC_DIR}/zlib/deflate.c + ${CINDER_SRC_DIR}/zlib/gzclose.c + ${CINDER_SRC_DIR}/zlib/gzlib.c + ${CINDER_SRC_DIR}/zlib/gzread.c + ${CINDER_SRC_DIR}/zlib/gzwrite.c + ${CINDER_SRC_DIR}/zlib/infback.c + ${CINDER_SRC_DIR}/zlib/inffast.c + ${CINDER_SRC_DIR}/zlib/inflate.c + ${CINDER_SRC_DIR}/zlib/inftrees.c + ${CINDER_SRC_DIR}/zlib/trees.c + ${CINDER_SRC_DIR}/zlib/uncompr.c + ${CINDER_SRC_DIR}/zlib/zconf.h + ${CINDER_SRC_DIR}/zlib/zutil.c +) + +list( APPEND CINDER_CXX_SRC_FILES + ${CINDER_SRC_DIR}/cinder/linux/gl_es_load.cpp + + ${CINDER_SRC_DIR}/cinder/app/emscripten/AppEmscripten.cpp + ${CINDER_SRC_DIR}/cinder/app/emscripten/AppImplEmscripten.cpp + ${CINDER_SRC_DIR}/cinder/app/emscripten/PlatformEmscripten.cpp + ${CINDER_SRC_DIR}/cinder/app/emscripten/RendererImplGlEmscripten.cpp + ${CINDER_SRC_DIR}/cinder/app/emscripten/WindowImplEmscripten.cpp + ${CINDER_SRC_DIR}/cinder/emscripten/EmscriptenVideo.cpp + #${CINDER_SRC_DIR}/cinder/audio/emscripten/ContextWebAudio.cpp + ${CINDER_SRC_DIR}/cinder/emscripten/globalbindings.cpp + ${CINDER_SRC_DIR}/cinder/app/AppBase.cpp + ${CINDER_SRC_DIR}/cinder/app/KeyEvent.cpp + ${CINDER_SRC_DIR}/cinder/app/Platform.cpp + ${CINDER_SRC_DIR}/cinder/app/Renderer.cpp + ${CINDER_SRC_DIR}/cinder/app/RendererGl.cpp + ${CINDER_SRC_DIR}/cinder/app/Window.cpp + + ${CINDER_SRC_DIR}/cinder/gl/draw.cpp + ${CINDER_SRC_DIR}/cinder/gl/scoped.cpp + ${CINDER_SRC_DIR}/cinder/gl/wrapper.cpp + ${CINDER_SRC_DIR}/cinder/gl/Batch.cpp + ${CINDER_SRC_DIR}/cinder/gl/BufferObj.cpp + ${CINDER_SRC_DIR}/cinder/gl/BufferTexture.cpp + ${CINDER_SRC_DIR}/cinder/gl/ConstantConversions.cpp + ${CINDER_SRC_DIR}/cinder/gl/Context.cpp + ${CINDER_SRC_DIR}/cinder/gl/Environment.cpp + ${CINDER_SRC_DIR}/cinder/gl/EnvironmentEs.cpp + ${CINDER_SRC_DIR}/cinder/gl/Fbo.cpp + ${CINDER_SRC_DIR}/cinder/gl/GlslProg.cpp + ${CINDER_SRC_DIR}/cinder/gl/Pbo.cpp + ${CINDER_SRC_DIR}/cinder/gl/Query.cpp + ${CINDER_SRC_DIR}/cinder/gl/Shader.cpp + ${CINDER_SRC_DIR}/cinder/gl/ShaderPreprocessor.cpp + ${CINDER_SRC_DIR}/cinder/gl/Sync.cpp + ${CINDER_SRC_DIR}/cinder/gl/Texture.cpp + ${CINDER_SRC_DIR}/cinder/gl/TextureFont.cpp + ${CINDER_SRC_DIR}/cinder/gl/TextureFormatParsers.cpp + ${CINDER_SRC_DIR}/cinder/gl/TransformFeedbackObj.cpp + ${CINDER_SRC_DIR}/cinder/gl/Ubo.cpp + ${CINDER_SRC_DIR}/cinder/gl/Vao.cpp + ${CINDER_SRC_DIR}/cinder/gl/VaoImplCore.cpp + ${CINDER_SRC_DIR}/cinder/gl/VaoImplEs.cpp + ${CINDER_SRC_DIR}/cinder/gl/VaoImplSoftware.cpp + ${CINDER_SRC_DIR}/cinder/gl/Vbo.cpp + ${CINDER_SRC_DIR}/cinder/gl/VboMesh.cpp + + ${CINDER_SRC_DIR}/cinder/ip/Blend.cpp + ${CINDER_SRC_DIR}/cinder/ip/Blur.cpp + ${CINDER_SRC_DIR}/cinder/ip/Checkerboard.cpp + ${CINDER_SRC_DIR}/cinder/ip/EdgeDetect.cpp + ${CINDER_SRC_DIR}/cinder/ip/Fill.cpp + ${CINDER_SRC_DIR}/cinder/ip/Flip.cpp + ${CINDER_SRC_DIR}/cinder/ip/Grayscale.cpp + ${CINDER_SRC_DIR}/cinder/ip/Hdr.cpp + ${CINDER_SRC_DIR}/cinder/ip/Premultiply.cpp + ${CINDER_SRC_DIR}/cinder/ip/Resize.cpp + ${CINDER_SRC_DIR}/cinder/ip/Threshold.cpp + ${CINDER_SRC_DIR}/cinder/ip/Trim.cpp + + ${CINDER_SRC_DIR}/cinder/svg/Svg.cpp + + ${CINDER_SRC_DIR}/cinder/Area.cpp + ${CINDER_SRC_DIR}/cinder/Base64.cpp + ${CINDER_SRC_DIR}/cinder/BSpline.cpp + ${CINDER_SRC_DIR}/cinder/BSplineFit.cpp + ${CINDER_SRC_DIR}/cinder/Buffer.cpp + ${CINDER_SRC_DIR}/cinder/Camera.cpp + ${CINDER_SRC_DIR}/cinder/CameraUi.cpp + #${CINDER_SRC_DIR}/cinder/Capture.cpp + ${CINDER_SRC_DIR}/cinder/Channel.cpp + ${CINDER_SRC_DIR}/cinder/CinderAssert.cpp + ${CINDER_SRC_DIR}/cinder/CinderMath.cpp + ${CINDER_SRC_DIR}/cinder/Color.cpp + #${CINDER_SRC_DIR}/cinder/ConvexHull.cpp + ${CINDER_SRC_DIR}/cinder/DataSource.cpp + ${CINDER_SRC_DIR}/cinder/DataTarget.cpp + ${CINDER_SRC_DIR}/cinder/Display.cpp + ${CINDER_SRC_DIR}/cinder/Exception.cpp + ${CINDER_SRC_DIR}/cinder/Font.cpp + ${CINDER_SRC_DIR}/cinder/Frustum.cpp + ${CINDER_SRC_DIR}/cinder/GeomIo.cpp + ${CINDER_SRC_DIR}/cinder/ImageIo.cpp + ${CINDER_SRC_DIR}/cinder/ImageSourceFileRadiance.cpp + ${CINDER_SRC_DIR}/cinder/ImageSourceFileStbImage.cpp + ${CINDER_SRC_DIR}/cinder/ImageTargetFileStbImage.cpp + ${CINDER_SRC_DIR}/cinder/Json.cpp + ${CINDER_SRC_DIR}/cinder/Log.cpp + ${CINDER_SRC_DIR}/cinder/Matrix.cpp + ${CINDER_SRC_DIR}/cinder/ObjLoader.cpp + ${CINDER_SRC_DIR}/cinder/Path2d.cpp + ${CINDER_SRC_DIR}/cinder/Perlin.cpp + ${CINDER_SRC_DIR}/cinder/Plane.cpp + ${CINDER_SRC_DIR}/cinder/PolyLine.cpp + ${CINDER_SRC_DIR}/cinder/Rand.cpp + ${CINDER_SRC_DIR}/cinder/Ray.cpp + ${CINDER_SRC_DIR}/cinder/Rect.cpp + ${CINDER_SRC_DIR}/cinder/Shape2d.cpp + ${CINDER_SRC_DIR}/cinder/Signals.cpp + ${CINDER_SRC_DIR}/cinder/Sphere.cpp + ${CINDER_SRC_DIR}/cinder/Stream.cpp + ${CINDER_SRC_DIR}/cinder/Surface.cpp + ${CINDER_SRC_DIR}/cinder/System.cpp + ${CINDER_SRC_DIR}/cinder/Text.cpp + ${CINDER_SRC_DIR}/cinder/Timeline.cpp + ${CINDER_SRC_DIR}/cinder/TimelineItem.cpp + ${CINDER_SRC_DIR}/cinder/Timer.cpp + ${CINDER_SRC_DIR}/cinder/Triangulate.cpp + ${CINDER_SRC_DIR}/cinder/TriMesh.cpp + ${CINDER_SRC_DIR}/cinder/Tween.cpp + ${CINDER_SRC_DIR}/cinder/Unicode.cpp + ${CINDER_SRC_DIR}/cinder/Url.cpp + ${CINDER_SRC_DIR}/cinder/Utilities.cpp + ${CINDER_SRC_DIR}/cinder/Xml.cpp + + ${CINDER_SRC_DIR}/jsoncpp/jsoncpp.cpp + + ${CINDER_SRC_DIR}/cinder/audio/ChannelRouterNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/Context.cpp + ${CINDER_SRC_DIR}/cinder/audio/DelayNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/Device.cpp + ${CINDER_SRC_DIR}/cinder/audio/FileOggVorbis.cpp + ${CINDER_SRC_DIR}/cinder/audio/FilterNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/GenNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/InputNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/MonitorNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/Node.cpp + ${CINDER_SRC_DIR}/cinder/audio/NodeMath.cpp + ${CINDER_SRC_DIR}/cinder/audio/OutputNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/PanNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/Param.cpp + ${CINDER_SRC_DIR}/cinder/audio/SamplePlayerNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/SampleRecorderNode.cpp + ${CINDER_SRC_DIR}/cinder/audio/Source.cpp + ${CINDER_SRC_DIR}/cinder/audio/Target.cpp + ${CINDER_SRC_DIR}/cinder/audio/Utilities.cpp + ${CINDER_SRC_DIR}/cinder/audio/Voice.cpp + ${CINDER_SRC_DIR}/cinder/audio/WaveTable.cpp + + ${CINDER_SRC_DIR}/cinder/audio/dsp/Biquad.cpp + ${CINDER_SRC_DIR}/cinder/audio/dsp/Converter.cpp + ${CINDER_SRC_DIR}/cinder/audio/dsp/ConverterR8brain.cpp + ${CINDER_SRC_DIR}/cinder/audio/dsp/Dsp.cpp + ${CINDER_SRC_DIR}/cinder/audio/dsp/Fft.cpp + ${CINDER_SRC_DIR}/cinder/audio/dsp/ooura/fftsg.cpp + + ${CINDER_SRC_DIR}/r8brain/r8bbase.cpp +) + + +### Include directories +list(APPEND CINDER_INCLUDE_DIRS + ${CINDER_INC_DIR} + ${CINDER_INC_DIR}/freetype + ${CINDER_INC_DIR}/jsoncpp + ${CINDER_INC_DIR}/oggvorbis + ${CINDER_SRC_DIR}/linebreak + ${CINDER_SRC_DIR}/libtess2 + ${CINDER_SRC_DIR}/AntTweakBar + ${CINDER_SRC_DIR}/oggvorbis/vorbis + ${CINDER_SRC_DIR}/r8brain + ${CINDER_SRC_DIR}/zlib + ${BOOST_INC_DIR} + ${EMSCRIPTEN_ROOT_PATH}/system/lib/libcxxabi/include +) + +### Output path - should output to lib/emscripten +set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CINDER_LIB_DIR}" ) + +### Create our Cinder target. +add_library( cinder STATIC ${CINDER_C_SRC_FILES} ${CINDER_CXX_SRC_FILES} ) + +target_include_directories( cinder PUBLIC "$" ) + +set_target_properties( cinder PROPERTIES DEBUG_POSTFIX "_d" ) diff --git a/emscripten/cibuild b/emscripten/cibuild new file mode 100644 index 0000000000..69cd22c43d --- /dev/null +++ b/emscripten/cibuild @@ -0,0 +1,17 @@ +#!/bin/bash + +# Requires the bleeding edge Emscripten: +# cd ${YOUR_EMSCRIPTEN_DIR} +# ./emsdk update +# ./emsdk install --build=Release sdk-incoming-64bit +# ./emsdk activate --build=Release sdk-incoming-64bit +# +# Source the emsdk_env.sh before you do anything: +# source ${YOUR_EMSCRIPTEN_DIR}/emsdk_env.sh +# + +if [ "$#" -eq 0 ]; then + ../tools/emscripten/cibuilder -b Debug +else + ../tools/emscripten/cibuilder "$@" +fi diff --git a/emscripten/cmake/Cinder.cmake b/emscripten/cmake/Cinder.cmake new file mode 100644 index 0000000000..d9c5a61df5 --- /dev/null +++ b/emscripten/cmake/Cinder.cmake @@ -0,0 +1,70 @@ + +# Assume Debug if a build type isn't specified +if( "" STREQUAL "${CMAKE_BUILD_TYPE}" ) + set( CMAKE_BUILD_TYPE "Debug" CACHE FILEPATH "" FORCE ) +endif() + +### Force some Emscripten CMake variables. CMake seems to be really particular +### about when these vars show up. They need to come after project. +set( CMAKE_AR "emcc" CACHE FILEPATH "" FORCE) +set( CMAKE_STATIC_LIBRARY_SUFFIX ".bc" ) +set( CMAKE_C_CREATE_STATIC_LIBRARY " -o " ) +set( CMAKE_CXX_CREATE_STATIC_LIBRARY " -o " ) + +# set basic flags for Emscripten +set( CINDER_GL_ES_FLAGS "-DCINDER_GL_ES_3 -s USE_WEBGL2=1 -s FULL_ES3=1" ) + +# C Flags +set( C_FLAGS "-fvisibility=default -D_UNIX ${CINDER_GL_ES_FLAGS} -s USE_GLFW=3 -s NO_EXIT_RUNTIME=1 -s DISABLE_EXCEPTION_CATCHING=0" ) +set( CMAKE_C_FLAGS_DEBUG "${C_FLAGS} -g -O0" ) +set( CMAKE_C_FLAGS_RELEASE "${C_FLAGS} -Os" ) + +# CPP Flags +set( CXX_FLAGS "-stdlib=libc++ -std=c++11 -fvisibility=default -D_UNIX ${CINDER_GL_ES_FLAGS} -s USE_GLFW=3 -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0" ) +set( CMAKE_CXX_FLAGS_DEBUG "${CXX_FLAGS} -g -O0 -fexceptions -frtti" ) +set( CMAKE_CXX_FLAGS_RELEASE "${CXX_FLAGS} -Os -fexceptions -frtti") + +get_filename_component( CINDER_INC_DIR "${CINDER_DIR}/include" ABSOLUTE ) +get_filename_component( CINDER_LIB_DIR "${CINDER_DIR}/lib" ABSOLUTE ) + +# we need an externs file to get around closure compiler mangling names(or is it UglifyJS ? I see references to both!¯\_(ツ)_/¯ ) +list(APPEND EMCC_CLOSURE_ARGS "--externs ${CINDER_INC_DIR}/cinder/emscripten/externs.js") +################### HELPER VARIABLES ################################## + +set(ALLOW_MEMORY_GROWTH "-s ALLOW_MEMORY_GROWTH=1") + +# set variable for helper file when handling DOM related stuff. This should be a part of the --pre-js Emscripten flag +set( CINDER_JS_HELPERS "--pre-js ${CINDER_INC_DIR}/cinder/emscripten/helpers.js") + +set( CINDER_VIDEO "${CINDER_JS_HELPERS} ${CINDER_USE_EMBIND}") + +# add to your project flags to build your file as a WebWorker. +# note that you apparently need the --bind flag when building workers +set( BUILD_AS_WORKER "-s BUILD_AS_WORKER=1") + +# the flag to set when you want to include an assets folder and it's contents. +# this assumes that your assets directory is one directory behind. Note that +# this may not work for samples +set( INCLUDE_RESOURCES_FOLDER "--preload-file ../resources@") + +# simple function to allow you to re-set the resources path - just pass in a new path relative to your build directory +function(SET_RESOURCES_PATH path) +set( INCLUDE_RESOURCES_FOLDER "--preload-file ${path}@" PARENT_SCOPE) +endfunction() + +# this flag tells Emscripten to use the browser to decode media assets whenever possible, when used +# in conjunction with a related API function. +set( USE_BROWSER_FOR_DECODING "--use-preload-plugins") + +# adds optimizations to the final output +set( ADD_OPTIMIZATIONS "-s WASM=1 -Os -g0 -closure 1") + +# enables use of pthreads +set(USE_THREADS "-s USE_PTHREADS=1") + + +# note - release command would be +# emcmake cmake .. -DCMAKE_BUILD_TYPE=Release +if( "Debug" STREQUAL "${CMAKE_BUILD_TYPE}" ) + set( CINDER_LIB_SUFFIX "_d" CACHE FILEPATH "" FORCE ) +endif() diff --git a/emscripten/dev-server/.gitignore b/emscripten/dev-server/.gitignore new file mode 100644 index 0000000000..6d841856fb --- /dev/null +++ b/emscripten/dev-server/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.idea +node_modules \ No newline at end of file diff --git a/emscripten/dev-server/bin/cli.js b/emscripten/dev-server/bin/cli.js new file mode 100644 index 0000000000..c99daaf7e2 --- /dev/null +++ b/emscripten/dev-server/bin/cli.js @@ -0,0 +1,4 @@ +#! /usr/bin/env node +var argv = require('minimist')(process.argv.slice(2)) + +require('../index')(argv.port); \ No newline at end of file diff --git a/emscripten/dev-server/index.js b/emscripten/dev-server/index.js new file mode 100644 index 0000000000..555f50e6c9 --- /dev/null +++ b/emscripten/dev-server/index.js @@ -0,0 +1,25 @@ +var http = require('http') +var serveStatic = require('serve-static') +var finalhandler = require('finalhandler') + + +module.exports = function(port){ + + var PORT = port !== undefined ? port : 3000; + + var serve = serveStatic(process.cwd(), { + 'index':['index.html','index.htm'], + setHeaders:function(res,path,stat){ + if (path.search('.wasm') !== -1) { + res.setHeader('Content-type','application/wasm') + } + } + }); + + var server = http.createServer(function(req,res){ + serve(req,res,finalhandler(req,res)); + }) + + server.listen(PORT); + console.log("Starting server on port " + PORT + ". Press ctrl/cmd - c to stop") +} \ No newline at end of file diff --git a/emscripten/dev-server/package-lock.json b/emscripten/dev-server/package-lock.json new file mode 100644 index 0000000000..17a4382dd6 --- /dev/null +++ b/emscripten/dev-server/package-lock.json @@ -0,0 +1,160 @@ +{ + "name": "wasm-server", + "version": "0.0.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": "1.4.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.3", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + } + } +} diff --git a/emscripten/dev-server/package.json b/emscripten/dev-server/package.json new file mode 100644 index 0000000000..6f930a962c --- /dev/null +++ b/emscripten/dev-server/package.json @@ -0,0 +1,16 @@ +{ + "name": "wasm-server", + "version": "0.0.4", + "description": "Simple test server that can be run to serve wasm files", + "main": "index.js", + "bin": { + "wasm-server": "./bin/cli.js" + }, + "author": "Joseph Chow", + "license": "", + "dependencies": { + "finalhandler": "^1.1.1", + "minimist": "^1.2.0", + "serve-static": "^1.13.2" + } +} diff --git a/emscripten/dev-server/readme.md b/emscripten/dev-server/readme.md new file mode 100644 index 0000000000..565b74a5bd --- /dev/null +++ b/emscripten/dev-server/readme.md @@ -0,0 +1,11 @@ +wasm-server +==== + +This is just a simple static server that happens to be able to serve and process WebAssembly files correctly. +It's not intended to be run or used in anything, just storing files here as a backup. + +If you'd like to run it instead of the default python server (or whatever other server) +* make sure you have Node JS installed, at least version 8+ +* run `npm` or `yarn` : `npm -g install wasm-server` +* you should be able to run the command `wasm-server` By default it opens on port 3000 but you can change the port number by +adding the number to the end of the command \ No newline at end of file diff --git a/emscripten/sample_generation/gensamples.py b/emscripten/sample_generation/gensamples.py new file mode 100644 index 0000000000..38915f1304 --- /dev/null +++ b/emscripten/sample_generation/gensamples.py @@ -0,0 +1,149 @@ +''' + This is a script for generating working sample files fit for + building with Emscripten. It will generate a folder called "emscripten" at the root + of each sample and in it, it will generate a CMakeLists.txt file as well as a build directory + to run that cmake file. +''' + +import os +from shutil import copy + +BASE_PATH = "../../samples" +dirs = os.listdir(BASE_PATH) + +#ignored folders of places we don't need to generate something in. +ignore = [ + "_AllSamples", + "vc2015", + "xcode", + "xcode_ios", + "_audio", + "resources", + "src", + "assets", + "include", + "androidstudio", + "xcode_iOS", + "vc2013_winrt", + "vc2015_winrt", + ".jpg", + ".png", + "LICENSE", + "models", + "sound", + "winrt", + ".icns", + ".ico", + "environment_maps", + "QuickTimeAvfWriter", + "iosKeyboard", + "iosNativeControls", + "LocationManager", + "SerialCommunication", + "ClothSimulation", + "GeometryShaderBasic", + "NVidiaComputeParticles", + "ParticleSphereCS", + "BufferPlayer", + "common", + "DelayFeedback", + "InputAnalyser", + "MultichannelOutput", + "NodeAdvanced", + "NodeBasic", + "NodeSubclassing", + "VoiceBasic", + "VoiceBasicProcessing" +] + +# checks the filename you pass in against the ignore list to see if +# it's something that needs to be ignored. +def should_ignore(file): + shouldIgnore = False + for x in ignore : + if file.find(x) != -1: + shouldIgnore = True + return shouldIgnore + + +print("Generating Emscripten project folder for samples.") +for dir in dirs : + if should_ignore(dir) == False: + subdirs = os.listdir(BASE_PATH + "/" + dir) + template = "" + + for subdir in subdirs: + # check to make sure there aren't directories that should be ignored. + # if not the proceed. + if should_ignore(subdir) != True: + projectPath = BASE_PATH + "/" + dir + + if(subdir == "proj"): + template = open( BASE_PATH + "/" + dir + "/proj/cmake/CMakeLists.txt").read() + elif os.path.exists(BASE_PATH + "/" + dir + "/" + subdir + "/proj/cmake/CMakeLists.txt"): + template = open( BASE_PATH + "/" + dir + "/" + subdir + "/proj/cmake/CMakeLists.txt").read() + + # replace cinderMakeApp with cinderEmscriptenApp + template = template.replace("cinderMakeApp","cinderEmscriptenApp") + + # replace ci_make_app with ci_emscripten_app + template = template.replace("ci_make_app","ci_emscripten_app") + + # fix pathing for Cinder + template = template.replace( + 'CINDER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../.." ABSOLUTE', + 'CINDER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../" ABSOLUTE' + ) + + # fix pathing for src + template = template.replace( + 'APP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../" ABSOLUTE', + 'APP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../" ABSOLUTE' + ) + + + ######## make a build directory at proj / emscripten / build #################3 + if(subdir == "proj"): + + path = projectPath + "/emscripten" + if os.path.exists(path) is False: + os.makedirs(path) + if os.path.exists(path + "/build") is False: + os.makedirs(path + "/build") + + else: + path = projectPath + "/" + subdir + + if os.path.exists(path + "/emscripten") is False: + os.makedirs(path + "/emscripten") + if os.path.exists(path + "/emscripten/build") is False: + os.makedirs(path + "/emscripten/build") + + ######### copy cmake template ############### + templatePath = "" + if subdir == "proj": + templatePath = projectPath + "/emscripten" + else: + templatePath = projectPath + "/" + subdir + "/emscripten" + + with open(templatePath + "/CMakelists.txt","w+") as f: + f.write(template) + +print("Done generating - you'll find an emscripten folder at / emscripten") + + + + + + + + + + + + + + + + + diff --git a/emscripten/shell.html b/emscripten/shell.html new file mode 100644 index 0000000000..1812fc4016 --- /dev/null +++ b/emscripten/shell.html @@ -0,0 +1,293 @@ + + + + + + Emscripten-Generated Code + + + +
+ +
+
+ +
+
Downloading...
+
+
+ + + +
+ + Resize canvas + Lock/hide mouse pointer     + + + + +
+ +
+
+
+
+ +
+ + + + {{{ SCRIPT }}} + + \ No newline at end of file diff --git a/tools/emscripten/cibuilder b/tools/emscripten/cibuilder new file mode 100644 index 0000000000..d7918c5876 --- /dev/null +++ b/tools/emscripten/cibuilder @@ -0,0 +1,176 @@ +#!/bin/bash + +if (( $# == 0 )); then + echo -e "cibuilder.sh - Cinder for Empscripten wrapper script" + echo -e "usage examples:" + echo -e "\tcibuilder.sh -j 4 -b Debug" + echo -e "\tcibuilder.sh clean -b Debug" + echo -e "" + echo -e "options:" + echo -e "\tclean Cleans the build for specified build type" + echo -e "\t-b [str] Build type, ex: -b Debug,Release " + echo -e "\t-j [n] Number of compile processes, ex: -j 4" + echo -e "" + echo -e "flags:" + echo -e "\t-r Rebuild instead of full build" + echo -e "\t-v Turns on verbose mode" + exit 0 +fi + + +if [[ "$(uname -s)" == *"Linux"* ]]; then + SCRIPT_DIR=$(dirname "$(readlink -f "$0")") + NUMPROCS=$(nproc) +elif [[ "$(uname -s)" == *"Darwin"* ]]; then + SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + NUMPROCS=$(sysctl -n hw.ncpu) +else + SCRIPT_DIR=$(dirname "$(readlink -f "$0")") + NUMPROCS=$(nproc) +fi + +CLEAN=false +BUILD_TYPES=(Debug) +OPENGL_PLAT="es3" +FULLBUILD=true +VERBOSE= +APP_BUILD=false +SAMPLES= + + +# Process arguments +while (( $# >= 1 )) +do + arg=$1 + + case $arg in + # Clean + clean) + CLEAN=true + shift 1 + ;; + + # Build types + -b) + # Clear array + BUILD_TYPES=() + # Split string into tokens + tokens=(${2//,/ }) + # Parse build types + for tok in ${tokens[@]}; do + build=$(echo $tok | tr '[:upper:]' '[:lower:]') + case $build in + debug) + BUILD_TYPES+=(Debug) + ;; + + release) + BUILD_TYPES+=(Release) + ;; + + *) + echo "-b Unknown build type: $build" + exit 1 + ;; + esac + done + shift 2 + ;; + + # Number of build processes + -j) + # Make sure this is a number + if [[ $2 = *[[:digit:]]* ]]; then + NUMPROCS=$2 + else + echo "-j requires a number" + exit 1 + fi + shift 2 + ;; + + # Rebuild + -r) + FULLBUILD=false + shift 1 + ;; + + # Verbose + -v) + VERBOSE="VERBOSE=1" + shift 1 + ;; + + ?*) + echo "Unknown parameter: ${arg}" + exit 1 + ;; + esac +done + +for build in ${BUILD_TYPES[@]}; do + build_type=${build} + build_dir=$(pwd)/${build}/${OPENGL_PLAT} + + if [ ${APP_BUILD} == true ]; then + build_dir=$(pwd)/${build}/${OPENGL_PLAT} + else + build_dir=${SCRIPT_DIR}/../../emscripten/${build}/${OPENGL_PLAT} + fi + + # Clean + if [ ${CLEAN} == true ] || [ ${FULLBUILD} == true ]; then + echo "Cleaning ${build_type} ..." + if [ -d "${build_dir}" ]; then + rm -rf "${build_dir}" + echo "...deleted dir: ${build_dir}" + fi + + if [ ${CLEAN} == true ]; then + exit 0 + fi + fi + + # Check for CMakeLists.txt + cmake_file_path="${SCRIPT_DIR}/../../emscripten/CMakeLists.txt" + if [ ${APP_BUILD} == true ]; then + cmake_file_path="$(pwd)/CMakeLists.txt" + fi + + if [ ! -f "${cmake_file_path}" ]; then + echo "ERROR: Couldn't find CMakeLists.txt in $(cmake_file_path)" + exit + fi + + # Build + echo "---------------------------------------------------------------------------------" + echo "Building Cinder in ${build_type} mode with ${NUMPROCS} compile processes" + + if [ ! -d "${build_dir}" ]; then + mkdir -p ${build_dir} + echo "...created dir: ${build_dir}" + fi + + cd ${build_dir} + + if [ "$(uname -s)" == *"Linux"* ] || [ "$(uname -s)" == *"Darwin"* ]; then + if [ ${FULLBUILD} == true ] || [ ! -f "${build_dir}/CMakeCache.txt" ]; then + echo "CMAKE COMMAND: cmake .. -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=${build_type} ${GLES} ${SAMPLES}" + + emcmake cmake ../.. -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=${build_type} ${GLES} ${SAMPLES} + fi + make -j ${NUMPROCS} ${VERBOSE} + + echo -e "" + else + if [ ${FULLBUILD} == true ] || [ ! -f "${build_dir}/CMakeCache.txt" ]; then + echo "CMAKE COMMAND IS :emcmake cmake ../.. -DCMAKE_MAKE_PROGRAM=mingw32-make -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=${build_type} ${GLES} ${SAMPLES}" + # add -G "Unix Makefiles" to generate makefile instead of Visual studio stuff. + emcmake cmake ../.. -DCMAKE_MAKE_PROGRAM=mingw32-make -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=${build_type} ${GLES} ${SAMPLES} + fi + # TODO any other make commands to try? + mingw32-make -j ${NUMPROCS} ${VERBOSE} || make -j ${NUMPROCS} ${VERBOSE} || echo "An error occured - remember you need to have a \"make\" program installed. It's also possible one of the core files had an error while building." + echo -e "" + fi + +done diff --git a/tools/emscripten/fonts/DroidSans.ttf b/tools/emscripten/fonts/DroidSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..efd1f8bbd882a6a434159fa1da7238babbaa2fb3 GIT binary patch literal 41028 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 literal 0 HcmV?d00001 diff --git a/tools/emscripten/scripts/gensamplebuilds b/tools/emscripten/scripts/gensamplebuilds new file mode 100644 index 0000000000..3d215baca1 --- /dev/null +++ b/tools/emscripten/scripts/gensamplebuilds @@ -0,0 +1,175 @@ +#!/usr/bin/python + +import os +import time +import datetime +import stat + +print( "Samples Emscripten Build File Generate v0.001\n" ) + +Build = """#!/bin/sh +$/tools/emscripten/cibuilder -app "$@" +""" + +CMakeListsTxt= """# $ +cmake_minimum_required( VERSION 3.1 FATAL_ERROR ) +set( CMAKE_VERBOSE_MAKEFILE on ) + +get_filename_component( CINDER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/$" ABSOLUTE ) +include( ${CINDER_DIR}/emscripten/cmake/Cinder.cmake ) + +project( $ ) + +# For Emscripten we need to do this again since project() resets some vars . +include( ${CINDER_DIR}/emscripten/cmake/Cinder.cmake ) + +get_filename_component( SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../src" ABSOLUTE ) +get_filename_component( INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../include" ABSOLUTE ) +set( ASSETS_DIR "../../../assets@/" ) + +#if( NOT TARGET cinder${CINDER_LIB_SUFFIX} ) +# find_package( cinder REQUIRED +# PATHS ${PROJECT_SOURCE_DIR}/$/emscripten/${CMAKE_BUILD_TYPE}/${CINDER_OUT_DIR_PREFIX} +# $ENV{Cinder_DIR}/emscripten/${CMAKE_BUILD_TYPE}/${CINDER_OUT_DIR_PREFIX} +# ) +#endif() + +# Use PROJECT_NAME since CMAKE_PROJET_NAME returns the top-level project name. +set( EXE_NAME ${PROJECT_NAME} ) + +set( SRC_FILES + $ +) + +set( CMAKE_EXECUTABLE_SUFFIX ".html" ) +if( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../assets" ) + set( CMAKE_EXE_LINKER_FLAGS "--preload-file ${ASSETS_DIR}" ) +endif() + +add_executable( "${EXE_NAME}" ${SRC_FILES} ) + +target_include_directories( + "${EXE_NAME}" + PUBLIC ${INC_DIR} + ${CINDER_INC_DIR} +) + +target_link_libraries( + "${EXE_NAME}" + ${CINDER_LIB_DIR}/emscripten/${CINDER_GL_ES_PLATFORM}/libcinder${CINDER_LIB_SUFFIX}.bc + ${CINDER_LIB_DIR}/emscripten/libboost_filesystem.bc + ${CINDER_LIB_DIR}/emscripten/libboost_system.bc +) +""" + +gCinderDir = os.path.abspath( "../../.." ) +print( "Cinder Dir is at %s\n" % gCinderDir ) + +def get_sub_dirs( path ): + return [name for name in os.listdir(path) if os.path.isdir(os.path.join(path, name))] + pass + +def make_emscripten_dir( path ): + if not os.path.exists( path ): + absPath = os.path.abspath( path ) + os.makedirs( absPath ) + print( "\t\tCreated %s" % path ) + pass + +def write_build_file( path, cinderRelPath, dateTime ): + text = Build.replace( "$", cinderRelPath ) + text = text.replace( "$", dateTime ) + + outFile = open( path, "w" ) + outFile.write( text ) + outFile.close() + + st = os.stat( path ) + os.chmod( path, st.st_mode | stat.S_IEXEC ) + pass + +def get_c_files( path ): + result = [] + if os.path.exists(path): + files = [name for name in os.listdir(path) if os.path.isfile(os.path.join(path, name))] + for file in files: + lc = file.lower() + if lc.endswith( ".cpp" ) or lc.endswith( ".cc" ) or lc.endswith( ".cxx" ): + result.append( "${SRC_DIR}/%s" % file ) + pass + return result + pass + +def get_cpp_files( path ): + result = [] + if os.path.exists(path): + files = [name for name in os.listdir(path) if os.path.isfile(os.path.join(path, name))] + for file in files: + lc = file.lower() + if lc.endswith( ".cpp" ) or lc.endswith( ".cc" ) or lc.endswith( ".cxx" ): + result.append( "${SRC_DIR}/%s" % file ) + pass + return result + pass + +def write_cmake_file( path, emscriptenDir, cinderRelPath, appName, dateTime ): + text = CMakeListsTxt.replace( "$", dateTime ) + text = text.replace( "$", cinderRelPath ) + text = text.replace( "$", appName ) + + srcPath = os.path.join( emscriptenDir, "../src" ) + cFiles = get_c_files( srcPath ) + cppFiles = get_cpp_files( srcPath ) + + allFiles = [] + for f in cppFiles: + allFiles.append( f ) + + sourceFiles = "\n\t".join( allFiles ) + + text = text.replace( "$", sourceFiles ) + + outFile = open( path, "w" ) + outFile.write( text ) + outFile.close() + pass + + +def generate_build( path, excludes ): + print( "Generating builds for projects in %s" % (path) ) + + subDirs = get_sub_dirs( path ) + subDirs.sort() + for subDir in subDirs: + if subDir in excludes: + continue + pass + + appDir = subDir + appName = subDir + #if not appName.endswith( "App" ): + #appName = appName[:-3] + #appName = ("%sApp" % appName) + + emscriptenPath = os.path.join( path, appDir, "emscripten" ) + buildPath = os.path.join( emscriptenPath, "cibuild" ) + cmakePath = os.path.join( emscriptenPath, "CMakeLists.txt" ) + + cinderRelPath = os.path.relpath( gCinderDir, os.path.abspath( emscriptenPath ) ) + + dateTime = datetime.datetime.fromtimestamp( time.time() ).strftime('%Y-%m-%d %H:%M:%S') + + print( "\t%s" % appName ) + make_emscripten_dir( emscriptenPath ) + write_build_file( buildPath, cinderRelPath, dateTime ) + write_cmake_file( cmakePath, emscriptenPath, cinderRelPath, appName, dateTime ) + + print( "" ) + pass + +generate_build( "../../../samples", [ "data", "_AllSamples", "_audio", "_opengl", "_svg", "_timeline" ] ) +generate_build( "../../../samples/_audio", [ "common" ] ) +generate_build( "../../../samples/_opengl", [] ) +generate_build( "../../../samples/_svg", [] ) +generate_build( "../../../samples/_timeline", [] ) + From cf7505ba5d87be33e4e86fb8d4b57e58aa73a74e Mon Sep 17 00:00:00 2001 From: Joseph Chow Date: Thu, 13 Dec 2018 13:09:41 -0500 Subject: [PATCH 02/53] add ci_emscripten_app --- proj/cmake/modules/cinderEmscriptenApp.cmake | 280 +++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 proj/cmake/modules/cinderEmscriptenApp.cmake diff --git a/proj/cmake/modules/cinderEmscriptenApp.cmake b/proj/cmake/modules/cinderEmscriptenApp.cmake new file mode 100644 index 0000000000..1b7fb03de4 --- /dev/null +++ b/proj/cmake/modules/cinderEmscriptenApp.cmake @@ -0,0 +1,280 @@ +# TODO +# 1. Add CinderBlock support - not sure what the best way is at the moment. + +function(ci_emscripten_app) + set( oneValueArgs + + # the path to Cinder installation + CINDER_PATH + + # path to your + ASSETS + + MEMORY_DEBUG + + ) + set( multiValueArgs + + # a path to a custom HTML template + # See https://kripken.github.io/emscripten-site/docs/tools_reference/emcc.html#emcc-shell-file for details. + HTML_TEMPLATE + + # a list of source files to use in the build. Typically you'll only need to list your main app file. + SOURCES + + # a list of header files to include in the build + INCLUDES + + # a list of library paths that you would like to include in the build. + LIBRARIES + + # any additional non-standard flags you may want to include + FLAGS + + # tells emscripten to include use of threads. Note that this requires use of SharedArrayBuffers - check your + # browser for how to enable, if it's possible. + THREADS + + # Thread pool size - this indicates the number of threads you'd like to pre-allocate. + # note that it is apparently better to pre-allocate then dynamically generate. + THREAD_POOL_SIZE + + # Thread hint num cores + THREAD_NUM_CORES + + # tells emscripten to use browser decoding + BROWSER_DECODE + + # if your project is intended to be a web worker, this adds the necessary flags. + BUILD_AS_WORKER + + # tells Emscripten what functions you want to expose to your main application within your worker. + # the parameter should be a single quoted string with all functions separated by a comma ie + # '_onmessage,_postmessage' + EXPORT_FROM_WORKER + + # Tells Emscripten to not build with async libraries bundled. + # note that this will disable ci::app::loadAsset and you will have to rely on async functions. + NO_ASYNC + + # Sepecifies the type of build you want to do, either "Debug" or "Release" + BUILD_TYPE + + # argument to tell emscripten to bundle resources + RESOURCES + + # argument to tell what to name all the files it will output + OUTPUT_NAME + + # argument to tell Emscripten where to write all compiled files. + OUTPUT_DIRECTORY + ) + + cmake_parse_arguments( ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + # set C++ standard. Important cause otherwise build will fail. + set (CMAKE_CXX_STANDARD 11) + + if( ARG_UNPARSED_ARGUMENTS ) + message( WARNING "unhandled arguments: ${ARG_UNPARSED_ARGUMENTS}" ) + endif() + + # ========== SET OUTPUT DIRECTORY ============ + + # if build type isn't set - set things to "Debug" + if (NOT ARG_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") + endif () + + # otherwise set build type to whatever was passed in. Note that you can only pass in "Debug" or "Release" + # not the most elegant of methods but does seem to work. + if (ARG_BUILD_TYPE) + if(${ARG_BUILD_TYPE} MATCHES "Debug") + set(CMAKE_BUILD_TYPE "Debug") + elseif(${ARG_BUILD_TYPE} MATCHES "Release") + set(CMAKE_BUILD_TYPE "Release") + else() + message(STATUS "Build type can only be set to \"Debug\" or \"Release\" - settings things to \"Debug\"") + endif() + endif () + + # set runtime output directory when doing Debug builds + if("Debug" STREQUAL "${CMAKE_BUILD_TYPE}" ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY "Debug" ) + endif() + + # set runtime output directory when doing Release builds + if("Release" STREQUAL "${CMAKE_BUILD_TYPE}" ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY "Release" ) + endif() + + # ensure the runtime output directory exists, in case we need to copy other files to it + if( NOT EXISTS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" ) + endif() + + # Log current settings + message("\nCURRENT SETTINGS\n======") + message( "APP_NAME: ${ARG_OUTPUT_NAME}" ) + message( "SOURCES: ${ARG_SOURCES}" ) + message( "INCLUDES: ${ARG_INCLUDES}" ) + message( "LIBRARIES: ${ARG_LIBRARIES}" ) + message( "CINDER_PATH: ${ARG_CINDER_PATH}" ) + message( "CMAKE_RUNTIME_OUTPUT_DIRECTORY: ${CMAKE_BINARY_DIR}/${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" ) + message( "CINDER_BUILD_TYPE: ${CMAKE_BUILD_TYPE}" ) + + if (ARG_RESOURCES) + message( "Resources folder is set to ${ARG_RESOURCES}" ) + endif () + + if (ARG_ASSETS) + message("Assets folder is set to ${ARG_ASSETS}") + endif () + message("\n") + + # ========= SETUP BUILD ============== #$ + get_filename_component( CINDER_DIR "${ARG_CINDER_PATH}" ABSOLUTE PARENT_SCOPE) + include( ${CINDER_DIR}/emscripten/cmake/Cinder.cmake ) + + set(CINDER_INCLUDE_DIR ${CINDER_DIR}/include) + + # this is important, if not set you will only get JS files. + if(NOT ARG_BUILD_AS_WORKER) + set(CMAKE_EXECUTABLE_SUFFIX ".html" PARENT_SCOPE) + endif() + + # append core flags + set(CXX_FLAGS "${CXX_FLAGS} --bind") + + if(NOT ARG_BUILD_AS_WORKER) + set(CXX_FLAGS "${CXX_FLAGS} ${CINDER_JS_HELPERS}") + endif() + + # if user wants to build as a worker + if (ARG_BUILD_AS_WORKER) + set(CXX_FLAGS "${CXX_FLAGS} ${BUILD_AS_WORKER}") + endif() + + + set(CXX_FLAGS "${CXX_FLAGS} ${ALLOW_MEMORY_GROWTH}") + + if(ARG_BUILD_AS_WORKER) + if(ARG_EXPORT_FROM_WORKER) + set(CXX_FLAGS "${CXX_FLAGS} -s EXPORTED_FUNCTIONS=[${ARG_EXPORT_FROM_WORKER}]") + else() + message(ERROR " In order to use web workers, you need to expose functions from your worker to your main application" ) + endif() + endif() + + # Emscripten async libraries are included by default - need to test to see if user has opted out by passing in the NO_ASYNC param + if(NOT ARG_NO_ASYNC) + set(CXX_FLAGS "${CXX_FLAGS} -s EMTERPRETIFY=1 -s EMTERPRETIFY_FILE=${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/em.data.binary -s EMTERPRETIFY_ASYNC=1") + endif() + + # also turn off async libs if building web worker. + if(ARG_BUILD_AS_WORKER) + set(CXX_FLAGS "${CXX_FLAGS} -s EMTERPRETIFY=0 -s EMTERPRETIFY_ASYNC=0") + endif() + + # if custom html template is wanted, use that, otherwise, use default + if(ARG_HTML_TEMPLATE) + set(CXX_FLAGS "${CXX_FLAGS} --shell-file ${ARG_HTML_TEMPLATE}") + else() + set(CXX_FLAGS "${CXX_FLAGS} --shell-file ${CINDER_DIR}/emscripten/shell.html") + endif() + + # if we need assertations set flags + if(ARG_MEMORY_DEBUG) + set(CXX_FLAGS "${CXX_FLAGS} -s ASSERTIONS=1 -s SAFE_HEAP=1") + endif() + + # if FLAGS parameter is set, append additional flags. + if(ARG_FLAGS) + set(CXX_FLAGS "${CXX_FLAGS} ${ARG_FLAGS}") + endif() + + # if building release add some extra flags to optimize final bundle. + # see https://kripken.github.io/emscripten-site/docs/optimizing/Optimizing-Code.html for other tips on optimizing code. + if( "Release" STREQUAL "${CMAKE_BUILD_TYPE}" ) + + set(CXX_FLAGS "${CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") + set(CXX_FLAGS "${CXX_FLAGS} ${ADD_OPTIMIZATIONS}") + set(CXX_FLAGS "${CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") + list(APPEND EMCC_CLOSURE_ARGS "--externs ${CINDER_INC_DIR}/cinder/emscripten/externs.js") + + endif() + # =========== ADD OTHER OPTIONAL FLAGS ================ # + + # if we have resources to bundle, build and append flags for that + if (ARG_RESOURCES) + message(WARNING "Note that some resources(really large media files), while it is possible to bundle, they may not load as expected." ) + set(CXX_FLAGS "${CXX_FLAGS} --preload-file ${ARG_RESOURCES}@") + endif () + + + # if user wants to use browser for decoding media. + if(ARG_BROWSER_DECODE) + set(CXX_FLAGS "${CXX_FLAGS} ${USE_BROWSER_FOR_DECODING}") + endif() + + + # if user wants threading support. + if(ARG_THREADS) + message(STATUS "Note that threads require SharedArrayBuffer object which may/may not be disabled in your browser. \n See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html for more information.") + set(CXX_FLAGS "${CXX_FLAGS} ${USE_THREADS}") + + if( NOT ARG_THREAD_POOL_SIZE ) + set(CXX_FLAGS "${CXX_FLAGS} -s PTHREAD_POOL_SIZE=-1") + endif() + + if( NOT ARG_THREAD_NUM_CORES ) + set(CXX_FLAGS "${CXX_FLAGS} -s PTHREAD_HINT_NUM_CORES=-1") + endif() + endif() + + message("Current flags for build are : \n ${CXX_FLAGS} \n") + + + # ========== COMPILE ALL FLAGS TOGETHER ============== # + + # compile all necessary flags + set(CMAKE_EXE_LINKER_FLAGS "${CXX_FLAGS}" PARENT_SCOPE) + + + # ========= COMPILE ALL BUILD PARAMETERS TOGETHER ============= # + + if(ARG_OUTPUT_NAME) + set(OUTPUT_NAME "${ARG_OUTPUT_NAME}") + else() + set(OUTPUT_NAME "index") + endif() + + add_executable(${OUTPUT_NAME} ${ARG_SOURCES} ) + target_include_directories( + ${OUTPUT_NAME} + PUBLIC ${CINDER_DIR}/include + + # TODO check to if PUBLIC specifier works if multiple files are specified, otherwise we'll need to manually do this. + PUBLIC ${ARG_INCLUDES} + ) + + # if a specific output directory is specified make sure to tell cmake + if(ARG_OUTPUT_DIRECTORY) + set_target_properties(${OUTPUT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${ARG_OUTPUT_DIRECTORY}) + endif() + + target_link_libraries( + ${OUTPUT_NAME} + ${CINDER_DIR}/lib/emscripten/libboost_filesystem.bc + ${CINDER_DIR}/lib/emscripten/libboost_system.bc + ${CINDER_DIR}/lib/emscripten/libcinder_d.bc + ${ARG_LIBRARIES} + ) + + # copy assets to build folder. + if(ARG_ASSETS) + file(INSTALL ${ARG_ASSETS} DESTINATION "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") + endif() + + +endfunction() From 1bd6958007a99fb49fdd9b7d9199d10f4375b1fe Mon Sep 17 00:00:00 2001 From: Joseph Chow Date: Fri, 14 Dec 2018 12:29:40 -0500 Subject: [PATCH 03/53] add core files and core changes needed to get stuff to run --- .gitignore | 4 + emscripten/emsdk_set_env.sh | 10 + include/cinder/Cinder.h | 2 + include/cinder/Font.h | 8 +- include/cinder/Log.h | 2 +- include/cinder/Timer.h | 2 +- include/cinder/app/App.h | 8 +- include/cinder/app/Renderer.h | 4 +- include/cinder/app/RendererGl.h | 10 +- include/cinder/app/Window.h | 24 +- include/cinder/app/emscripten/AppEmscripten.h | 99 + .../cinder/app/emscripten/AppImplEmscripten.h | 99 + .../app/emscripten/PlatformEmscripten.h | 72 + .../app/emscripten/RendererImplGlEmscripten.h | 59 + .../app/emscripten/WindowImplEmscripten.h | 97 + include/cinder/emscripten/CinderEmscripten.h | 170 + include/cinder/emscripten/EmscriptenVideo.h | 117 + include/cinder/emscripten/HTML.h | 116 + include/cinder/emscripten/UrlImplEmscripten.h | 96 + include/cinder/emscripten/Worker.h | 60 + include/cinder/emscripten/externs.js | 4 + include/cinder/emscripten/globalbindings.h | 44 + include/cinder/emscripten/helpers.js | 504 +++ include/cinder/emscripten/offscreen.h | 65 + include/cinder/gl/Environment.h | 15 +- include/cinder/gl/platform.h | 14 +- include/cinder/linux/gl_es_load.h | 4 +- samples/BasicApp/CMakeLists.txt | 17 + samples/apptest/CMakeLists.txt | 15 + samples/apptest/app.cpp | 35 + src/cinder/Log.cpp | 6 +- src/cinder/Timer.cpp | 12 +- src/cinder/Url.cpp | 8 +- src/cinder/app/AppBase.cpp | 25 +- src/cinder/app/KeyEvent.cpp | 2 +- src/cinder/app/Platform.cpp | 12 +- src/cinder/app/RendererGl.cpp | 68 +- src/cinder/app/Window.cpp | 3 +- src/cinder/app/emscripten/AppEmscripten.cpp | 116 + .../app/emscripten/AppImplEmscripten.cpp | 456 ++ src/cinder/app/emscripten/DroidFont.h | 3734 +++++++++++++++++ .../app/emscripten/PlatformEmscripten.cpp | 293 ++ .../emscripten/RendererImplGlEmscripten.cpp | 89 + .../app/emscripten/WindowImplEmscripten.cpp | 174 + src/cinder/emscripten/DroidFont.h | 3734 +++++++++++++++++ src/cinder/emscripten/EmscriptenVideo.cpp | 125 + src/cinder/emscripten/globalbindings.cpp | 46 + src/cinder/gl/ConstantConversions.cpp | 4 +- src/cinder/gl/Context.cpp | 4 +- src/cinder/gl/Environment.cpp | 4 + src/cinder/gl/EnvironmentEs.cpp | 10 +- src/cinder/gl/GlslProg.cpp | 8 +- src/cinder/linux/gl_es_load.cpp | 4 +- src/zlib/gzlib.c | 4 + src/zlib/gzread.c | 5 +- src/zlib/gzwrite.c | 4 + 56 files changed, 10665 insertions(+), 61 deletions(-) create mode 100644 emscripten/emsdk_set_env.sh create mode 100644 include/cinder/app/emscripten/AppEmscripten.h create mode 100644 include/cinder/app/emscripten/AppImplEmscripten.h create mode 100644 include/cinder/app/emscripten/PlatformEmscripten.h create mode 100644 include/cinder/app/emscripten/RendererImplGlEmscripten.h create mode 100644 include/cinder/app/emscripten/WindowImplEmscripten.h create mode 100644 include/cinder/emscripten/CinderEmscripten.h create mode 100644 include/cinder/emscripten/EmscriptenVideo.h create mode 100644 include/cinder/emscripten/HTML.h create mode 100644 include/cinder/emscripten/UrlImplEmscripten.h create mode 100644 include/cinder/emscripten/Worker.h create mode 100644 include/cinder/emscripten/externs.js create mode 100644 include/cinder/emscripten/globalbindings.h create mode 100644 include/cinder/emscripten/helpers.js create mode 100644 include/cinder/emscripten/offscreen.h create mode 100644 samples/BasicApp/CMakeLists.txt create mode 100644 samples/apptest/CMakeLists.txt create mode 100644 samples/apptest/app.cpp create mode 100644 src/cinder/app/emscripten/AppEmscripten.cpp create mode 100644 src/cinder/app/emscripten/AppImplEmscripten.cpp create mode 100644 src/cinder/app/emscripten/DroidFont.h create mode 100644 src/cinder/app/emscripten/PlatformEmscripten.cpp create mode 100644 src/cinder/app/emscripten/RendererImplGlEmscripten.cpp create mode 100644 src/cinder/app/emscripten/WindowImplEmscripten.cpp create mode 100644 src/cinder/emscripten/DroidFont.h create mode 100644 src/cinder/emscripten/EmscriptenVideo.cpp create mode 100644 src/cinder/emscripten/globalbindings.cpp diff --git a/.gitignore b/.gitignore index 440c1a0ecc..bde1a1035e 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,7 @@ cinderConfig.cmake # CinderBlocks blocks/ !blocks/[__AppTemplates,Box2D,Cairo,FMOD,LocationManager,MotionManager,OSC,QuickTime,TUIO] +lib/emscripten/libcinder_d.bc +lib/emscripten/libboost_system.bc +lib/emscripten/libboost_filesystem.bc +.vscode/settings.json diff --git a/emscripten/emsdk_set_env.sh b/emscripten/emsdk_set_env.sh new file mode 100644 index 0000000000..147ce03e2b --- /dev/null +++ b/emscripten/emsdk_set_env.sh @@ -0,0 +1,10 @@ +export PATH="/c/Users/rvguest/Documents/emsdk:/c/Users/rvguest/Documents/emsdk/clang/e1.38.12_64bit:/c/Users/rvguest/Documents/emsdk/node/8.9.1_64bit/bin:/c/Users/rvguest/Documents/emsdk/python/2.7.13.1_64bit/python-2.7.13.amd64:/c/Users/rvguest/Documents/emsdk/java/8.152_64bit/bin:/c/Users/rvguest/Documents/emsdk/emscripten/1.38.12:/c/Users/rvguest/bin:/c/Program Files/Git/mingw64/bin:/c/Program Files/Git/usr/local/bin:/c/Program Files/Git/usr/bin:/c/Program Files/SVS-VISTEK GmbH/SVCam Kit/SDK/bin/Win32_i86:/c/Program Files/SVS-VISTEK GmbH/SVCam Kit/SDK/bin:/c/Python/Scripts:/c/Python:/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/bin:/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/libnvvp:/c/WINDOWS/system32:/c/WINDOWS:/c/WINDOWS/System32/Wbem:/c/WINDOWS/System32/WindowsPowerShell/v1.0:/c/Program Files (x86)/Windows Kits/10/Windows Performance Toolkit:/c/Program Files/Git/cmd:/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/c/Program Files/CMake/bin:/c/WINDOWS/System32/OpenSSH:/c/Program Files/PCL 1.8.1/bin:/c/Program Files/nodejs:/c/Program Files/doxygen/bin:/c/Users/rvguest/AppData/Local/Microsoft/WindowsApps:/c/MinGW/bin:/c/Users/rvguest/AppData/Local/atom/bin:/c/Users/rvguest/Documents/boost_1_68_0/tools/build:/c/Users/rvguest/Documents:/c/Users/rvguest/Desktop/kotlin-native-windows-0.8.2/kotlin-native-windows-0.8.2/bin:/c/Users/rvguest/.konan/dependencies/msys2-mingw-w64-x86_64-gcc-7.2.0-clang-llvm-5.0.0-windows-x86-64/bin:/c/Users/rvguest/Documents/doxygen-1.8.14.windows.x64.bin:/c/Users/rvguest/python:/c/Users/rvguest/emacs/bin:/c/Users/rvguest/Documents/Neovim/bin:/c/Users/rvguest/AppData/Local/Programs/Microsoft VS Code/bin:/c/Users/rvguest/AppData/Roaming/npm:/c/Program Files/Git/usr/bin/vendor_perl:/c/Program Files/Git/usr/bin/core_perl" +export EMSDK="C:/Users/rvguest/Documents/emsdk" +export EM_CONFIG="C:\Users\rvguest\.emscripten" +export LLVM_ROOT="C:/Users/rvguest/Documents/emsdk/clang/e1.38.12_64bit" +export EMSCRIPTEN_NATIVE_OPTIMIZER="C:/Users/rvguest/Documents/emsdk/clang/e1.38.12_64bit/optimizer.exe" +export BINARYEN_ROOT="C:/Users/rvguest/Documents/emsdk/clang/e1.38.12_64bit/binaryen" +export EMSDK_NODE="C:/Users/rvguest/Documents/emsdk/node/8.9.1_64bit/bin/node.exe" +export EMSDK_PYTHON="C:/Users/rvguest/Documents/emsdk/python/2.7.13.1_64bit/python-2.7.13.amd64/python.exe" +export JAVA_HOME="C:/Users/rvguest/Documents/emsdk/java/8.152_64bit" +export EMSCRIPTEN="C:/Users/rvguest/Documents/emsdk/emscripten/1.38.12" diff --git a/include/cinder/Cinder.h b/include/cinder/Cinder.h index 564a287397..e72dccf7fc 100644 --- a/include/cinder/Cinder.h +++ b/include/cinder/Cinder.h @@ -91,6 +91,8 @@ using std::uint64_t; #define CINDER_POSIX #define CINDER_ANDROID #include +#elif defined( __EMSCRIPTEN__ ) + #define CINDER_EMSCRIPTEN #else #error "cinder compile error: Unknown platform" #endif diff --git a/include/cinder/Font.h b/include/cinder/Font.h index 99ea9840cb..b5981ce42f 100644 --- a/include/cinder/Font.h +++ b/include/cinder/Font.h @@ -43,7 +43,7 @@ namespace Gdiplus { class Font; } -#elif defined( CINDER_UWP ) || defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) +#elif defined( CINDER_UWP ) || defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) typedef struct FT_FaceRec_* FT_Face; #endif @@ -54,7 +54,7 @@ class FontObj; //! Represents an instance of a font at a point size. \ImplShared class CI_API Font { public: -#if defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) +#if defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) typedef uint32_t Glyph; struct GlyphMetrics { ivec2 advance; @@ -77,7 +77,7 @@ class CI_API Font { std::string getFullName() const; float getSize() const; -#if defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) +#if defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) float getLinespace() const; #endif float getLeading() const; @@ -93,7 +93,7 @@ class CI_API Font { //! Returns the bounding box of a Glyph, relative to the baseline as the origin Rectf getGlyphBoundingBox( Glyph glyph ) const; -#if defined( CINDER_UWP ) || defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) +#if defined( CINDER_UWP ) || defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) FT_Face getFreetypeFace() const; #endif diff --git a/include/cinder/Log.h b/include/cinder/Log.h index 8df4699892..2f2035ec85 100644 --- a/include/cinder/Log.h +++ b/include/cinder/Log.h @@ -176,7 +176,7 @@ class CI_API LoggerSystem : public Logger { protected: Level mMinLevel; -#if defined( CINDER_COCOA ) || defined( CINDER_LINUX ) +#if defined( CINDER_COCOA ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) class ImplSysLog; std::unique_ptr mImpl; #elif defined( CINDER_MSW_DESKTOP ) diff --git a/include/cinder/Timer.h b/include/cinder/Timer.h index 53044d8ad4..152db97eb2 100644 --- a/include/cinder/Timer.h +++ b/include/cinder/Timer.h @@ -54,7 +54,7 @@ class CI_API Timer { double mStartTime, mEndTime; #elif defined( CINDER_MSW ) double mStartTime, mEndTime, mInvNativeFreq; -#elif defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) +#elif defined( CINDER_ANDROID ) || defined( CINDER_LINUX ) || defined( CINDER_EMSCRIPTEN ) double mStartTime, mEndTime; #endif }; diff --git a/include/cinder/app/App.h b/include/cinder/app/App.h index 5b4c4cf233..9ea4ca5b93 100644 --- a/include/cinder/app/App.h +++ b/include/cinder/app/App.h @@ -61,4 +61,10 @@ typedef AppLinux App; } } // namespace cinder::app #define CINDER_APP( APP, RENDERER, ... ) CINDER_APP_LINUX( APP, RENDERER, ##__VA_ARGS__ ) -#endif +#elif defined ( CINDER_EMSCRIPTEN ) + #include "cinder/app/emscripten/AppEmscripten.h" + namespace cinder { namespace app { + typedef AppEmscripten App; + } } // namespace cinder::app + #define CINDER_APP( APP, RENDERER, ... ) CINDER_APP_EMSCRIPTEN( APP, RENDERER, ##__VA_ARGS__ ) +#endif \ No newline at end of file diff --git a/include/cinder/app/Renderer.h b/include/cinder/app/Renderer.h index d119454060..08fa3e5dae 100644 --- a/include/cinder/app/Renderer.h +++ b/include/cinder/app/Renderer.h @@ -115,7 +115,9 @@ class CI_API Renderer { #else virtual void setup( void* nativeWindow, RendererRef sharedRenderer ) = 0; #endif -#endif +#elif defined( CINDER_EMSCRIPTEN ) + virtual void setup( void* nativeWindow, RendererRef sharedRenderer ) = 0; +#endif virtual Surface8u copyWindowSurface( const Area &area, int32_t windowHeightPixels ) = 0; diff --git a/include/cinder/app/RendererGl.h b/include/cinder/app/RendererGl.h index 8f26aee6a0..e2d495b8ff 100644 --- a/include/cinder/app/RendererGl.h +++ b/include/cinder/app/RendererGl.h @@ -191,7 +191,9 @@ class CI_API RendererGl : public Renderer { #else virtual void setup( void* nativeWindow, RendererRef sharedRenderer ) override; #endif -#endif +#elif defined( CINDER_EMSCRIPTEN ) + virtual void setup( void* nativeWindow, RendererRef sharedRenderer ) override; +#endif const Options& getOptions() const { return mOptions; } @@ -236,7 +238,11 @@ class CI_API RendererGl : public Renderer { class RendererGlLinux *mImpl; RendererGlLinux *getImpl() { return mImpl; } friend class WindowImplLinux; -#endif +#elif defined( CINDER_EMSCRIPTEN ) + class RendererImplGlEmscripten *mImpl; + RendererImplGlEmscripten *getImpl() { return mImpl; } + friend class WindowImplEmscripten; +#endif std::function mStartDrawFn; std::function mFinishDrawFn; diff --git a/include/cinder/app/Window.h b/include/cinder/app/Window.h index bd4b1503cf..83403f0d52 100644 --- a/include/cinder/app/Window.h +++ b/include/cinder/app/Window.h @@ -99,7 +99,11 @@ typedef std::shared_ptr WindowRef; namespace cinder { namespace app { class WindowImplLinux; } } // namespace cinder::app -#endif +#elif defined( CINDER_EMSCRIPTEN ) + namespace cinder { namespace app { + class WindowImplEmscripten; + } } // namespace cinder::app +#endif namespace cinder { namespace app { @@ -444,7 +448,9 @@ class CI_API Window : public std::enable_shared_from_this { static WindowRef privateCreate__( WindowImplAndroid *impl, AppBase *app ) #elif defined( CINDER_LINUX ) static WindowRef privateCreate__( WindowImplLinux *impl, AppBase *app ) -#else +#elif defined (CINDER_EMSCRIPTEN ) + static WindowRef privateCreate__( WindowImplEmscripten *impl, AppBase *app ) +#else static WindowRef privateCreate__( WindowImplCocoa *impl, AppBase *app ) #endif { @@ -482,7 +488,10 @@ class CI_API Window : public std::enable_shared_from_this { void setImpl( WindowImplAndroid *impl ) { mImpl = impl; } #elif defined( CINDER_LINUX ) void setImpl( WindowImplLinux *impl ) { mImpl = impl; } -#endif +#elif defined( CINDER_EMSCRIPTEN ) +void setImpl( WindowImplEmscripten *impl ) { mImpl = impl; } + +#endif AppBase *mApp; bool mValid; @@ -508,7 +517,9 @@ class CI_API Window : public std::enable_shared_from_this { WindowImplAndroid *mImpl; #elif defined( CINDER_LINUX ) WindowImplLinux *mImpl; -#endif +#elif defined ( CINDER_EMSCRIPTEN ) + WindowImplEmscripten * mImpl; +#endif private: #if defined( CINDER_ANDROID ) @@ -517,7 +528,10 @@ class CI_API Window : public std::enable_shared_from_this { #elif defined( CINDER_LINUX ) friend class AppImplLinux; WindowImplLinux *getImpl() { return mImpl; } -#endif +#elif defined ( CINDER_EMSCRIPTEN ) + friend class AppImplEmscripten; + WindowImplEmscripten *getImpl() { return mImpl; } +#endif }; } } // namespace cinder::app diff --git a/include/cinder/app/emscripten/AppEmscripten.h b/include/cinder/app/emscripten/AppEmscripten.h new file mode 100644 index 0000000000..a3088d403c --- /dev/null +++ b/include/cinder/app/emscripten/AppEmscripten.h @@ -0,0 +1,99 @@ +/* + Copyright (c) 2012, The Cinder Project, All rights reserved. + + This code is intended for use with the Cinder C++ library: http://libcinder.org + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "cinder/app/AppBase.h" + +namespace cinder { namespace app { + +class AppImplEmscripten; + +class AppEmscripten : public AppBase { + public: + typedef std::function SettingsFn; + + AppEmscripten(); + virtual ~AppEmscripten(); + + WindowRef createWindow( const Window::Format &format = Window::Format() ) override; + void quit() override; + + float getFrameRate() const override; + void setFrameRate( float frameRate ) override; + void disableFrameRate() override; + bool isFrameRateEnabled() const override; + + WindowRef getWindow() const override; + WindowRef getWindowIndex( size_t index ) const override; + size_t getNumWindows() const override; + + WindowRef getForegroundWindow() const override; + + void hideCursor() override; + void showCursor() override; + ivec2 getMousePos() const override; + + //! \cond + // Called during application instanciation via CINDER_APP_LINUX macro + template + static void main( const RendererRef &defaultRenderer, const char *title, int argc, char * const argv[], const SettingsFn &settingsFn = SettingsFn() ); + //! \endcond + + protected: + void launch() override; + + private: + AppImplEmscripten* mImpl = nullptr; +}; + +template +void AppEmscripten::main( const RendererRef &defaultRenderer, const char *title, int argc, char * const argv[], const SettingsFn &settingsFn ) +{ + AppBase::prepareLaunch(); + + Settings settings; + AppBase::initialize( &settings, defaultRenderer, title, argc, argv ); + + if( settingsFn ) + settingsFn( &settings ); + + if( settings.getShouldQuit() ) + return; + + AppEmscripten *app = static_cast( new AppT ); + app->executeLaunch(); + delete app; + + AppBase::cleanupLaunch(); +} + +#define CINDER_APP_EMSCRIPTEN( APP, RENDERER, ... ) \ +int main( int argc, char* argv[] ) \ +{ \ + cinder::app::RendererRef renderer( new RENDERER ); \ + cinder::app::AppEmscripten::main( renderer, #APP, argc, argv, ##__VA_ARGS__ ); \ + return 0; \ +} + +}} // namespace cinder::app diff --git a/include/cinder/app/emscripten/AppImplEmscripten.h b/include/cinder/app/emscripten/AppImplEmscripten.h new file mode 100644 index 0000000000..2e99c8bf65 --- /dev/null +++ b/include/cinder/app/emscripten/AppImplEmscripten.h @@ -0,0 +1,99 @@ +/* + Copyright (c) 2012, The Cinder Project, All rights reserved. + + This code is intended for use with the Cinder C++ library: http://libcinder.org + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "cinder/app/emscripten/AppEmscripten.h" + +// Use the system version, not the one that ships with Cinder. +#include + +namespace cinder { namespace app { + +class AppEmscripten; +class WindowImplEmscripten; + +class AppImplEmscripten { + public: + + AppImplEmscripten( AppEmscripten *aApp, const AppEmscripten::Settings &settings ); + virtual ~AppImplEmscripten(); + + AppEmscripten *getApp(); + + protected: + RendererRef findSharedRenderer( const RendererRef &searchRenderer ); + + WindowRef createWindow( Window::Format format ); + void quit(); + + float getFrameRate() const; + void setFrameRate( float aFrameRate ); + void disableFrameRate(); + bool isFrameRateEnabled() const; + + + WindowRef getWindow() const; + void setWindow( WindowRef window ); + size_t getNumWindows() const; + WindowRef getWindowIndex( size_t index ) const; + WindowRef getForegroundWindow() const; + void setForegroundWindow( WindowRef window ); + + void hideCursor(); + void showCursor(); + ivec2 getMousePos() const; + + +private: + AppEmscripten *mApp = nullptr; + WindowRef mMainWindow; + + std::list mWindows; + WindowRef mActiveWindow; + WindowRef mForegroundWindow; + + float mFrameRate; + bool mFrameRateEnabled; + bool mShouldQuit = false; + + bool mSetupHasBeenCalled = false; + + double mNextFrameTime; + + void sleepUntilNextFrame(); + void run(); + + void registerWindowEvents( WindowImplEmscripten* window ); + void unregisterWindowEvents( WindowImplEmscripten* window ); + + friend class AppEmscripten; + friend class WindowImplEmscripten; + friend class GlfwCallbacks; + +public: + // This has to be static so we can call it from the callback. + void updateAndDraw(); +}; + +}} // namespace cinder::app diff --git a/include/cinder/app/emscripten/PlatformEmscripten.h b/include/cinder/app/emscripten/PlatformEmscripten.h new file mode 100644 index 0000000000..a66b201678 --- /dev/null +++ b/include/cinder/app/emscripten/PlatformEmscripten.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2012, The Cinder Project, All rights reserved. + + This code is intended for use with the Cinder C++ library: http://libcinder.org + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "cinder/app/Platform.h" + +namespace cinder { namespace app { + +class PlatformEmscripten : public Platform +{ + public: + + PlatformEmscripten(); + virtual ~PlatformEmscripten(); + + static PlatformEmscripten* get(); + + virtual void cleanupLaunch() override; + + virtual DataSourceRef loadResource( const fs::path &resourcePath ) override; + virtual DataSourceRef loadAsset( const fs::path &resourcePath ) override; + + virtual fs::path getResourceDirectory() const override; + virtual fs::path getResourcePath( const fs::path &rsrcRelativePath ) const override; + + virtual fs::path getOpenFilePath( const fs::path &initialPath, const std::vector &extensions ) override; + virtual fs::path getFolderPath( const fs::path &initialPath ) override; + virtual fs::path getSaveFilePath( const fs::path &initialPath, const std::vector &extensions ) override; + + virtual std::map getEnvironmentVariables() override; + + virtual fs::path expandPath( const fs::path &path ) override; + virtual fs::path getHomeDirectory() const override; + virtual fs::path getDocumentsDirectory() const override; + virtual fs::path getDefaultExecutablePath() const override; + + virtual void sleep( float milliseconds ) override; + void setThreadName( const std::string &name ) override; + virtual void launchWebBrowser( const Url &url ) override; + + virtual std::vector stackTrace() override; + virtual const std::vector& getDisplays() override; + + private: + bool mDisplaysInitialized = false; + std::vector mDisplays; + mutable std::vector mResourceDirectories; + mutable bool mResourceDirsInitialized = false; +}; + +}} // namespace cinder::app diff --git a/include/cinder/app/emscripten/RendererImplGlEmscripten.h b/include/cinder/app/emscripten/RendererImplGlEmscripten.h new file mode 100644 index 0000000000..26ed19b1b0 --- /dev/null +++ b/include/cinder/app/emscripten/RendererImplGlEmscripten.h @@ -0,0 +1,59 @@ +/* + Copyright (c) 2015, The Cinder Project, All rights reserved. + + This code is intended for use with the Cinder C++ library: http://libcinder.org + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "cinder/app/AppBase.h" + +typedef struct GLFWwindow GLFWwindow; + +namespace cinder { namespace gl { + +class Context; +using ContextRef = std::shared_ptr; + +}} // namespace cinder::gl + +namespace cinder { namespace app { + +class RendererImplGlEmscripten { + public: + + + RendererImplGlEmscripten( class RendererGl *aRenderer ); + virtual ~RendererImplGlEmscripten(); + + virtual bool initialize( void *window, RendererRef sharedRenderer ); + virtual void kill(); + virtual void defaultResize() const; + virtual void swapBuffers() const; + virtual void makeCurrentContext( bool force = false ); + + private: + class RendererGl *mRenderer = nullptr; + gl::ContextRef mCinderContext; + + GLFWwindow *mContext = nullptr; +}; + +}} // namespace cinder::app diff --git a/include/cinder/app/emscripten/WindowImplEmscripten.h b/include/cinder/app/emscripten/WindowImplEmscripten.h new file mode 100644 index 0000000000..6d029232b3 --- /dev/null +++ b/include/cinder/app/emscripten/WindowImplEmscripten.h @@ -0,0 +1,97 @@ +/* + Copyright (c) 2012, The Cinder Project, All rights reserved. + + This code is intended for use with the Cinder C++ library: http://libcinder.org + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "cinder/app/emscripten/AppEmscripten.h" +#include "cinder/app/Window.h" +#include "cinder/Display.h" + +#include "glfw/glfw3.h" + +namespace cinder { namespace app { + +class AppImplEmscripten; + +class WindowImplEmscripten { +public: + + WindowImplEmscripten( const Window::Format &format, RendererRef sharedRenderer, AppImplEmscripten *appImpl ); + virtual ~WindowImplEmscripten(); + + virtual bool isFullScreen() { return true; } + virtual void setFullScreen( bool fullScreen, const app::FullScreenOptions &options ); + virtual ivec2 getSize() const; + virtual void setSize( const ivec2 &size ); + virtual ivec2 getPos() const; + virtual void setPos( const ivec2 &pos ); + virtual void close(); + virtual std::string getTitle() const { return mTitle; } + virtual void setTitle( const std::string &title ); + virtual void hide(); + virtual void show(); + virtual bool isHidden() const { return false; } + virtual DisplayRef getDisplay() const { return mDisplay; } + virtual RendererRef getRenderer() const { return mRenderer; } + virtual const std::vector& getActiveTouches() const; + + virtual GLFWwindow *getNative() { return mGlfwWindow; } + virtual GLFWwindow *getNative() const { return mGlfwWindow; } + + bool isBorderless() const { return mBorderless; } + void setBorderless( bool borderless ); + bool isAlwaysOnTop() const { return mAlwayOnTop; } + void setAlwaysOnTop( bool alwaysOnTop ); + + AppImplEmscripten* getAppImpl() { return mAppImpl; } + WindowRef getWindow() { return mWindowRef; } + + virtual void keyDown( const KeyEvent &event ); + virtual void draw(); + virtual void resize(); + + + void hideCursor(); + void showCursor(); + ivec2 getMousePos() const; + +protected: + AppImplEmscripten *mAppImpl = nullptr; + WindowRef mWindowRef; + + GLFWwindow *mGlfwWindow = nullptr; + + std::string mTitle; + bool mBorderless = false; + bool mAlwayOnTop = false; + + DisplayRef mDisplay; + RendererRef mRenderer; + + // Always empty for now + std::vector mActiveTouches; + + friend class AppImplEmscripten; +}; + +}} // namespace cinder::app diff --git a/include/cinder/emscripten/CinderEmscripten.h b/include/cinder/emscripten/CinderEmscripten.h new file mode 100644 index 0000000000..ae5a7b93a4 --- /dev/null +++ b/include/cinder/emscripten/CinderEmscripten.h @@ -0,0 +1,170 @@ +/* + Copyright (c) 2018, The Barbarian Group + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + +#pragma once + +#include +#include +#include +#include +#include +#include "cinder/app/App.h" +#include "cinder/Log.h" +#include "cinder/Filesystem.h" +#include "cinder/Utilities.h" +#include "cinder/DataSource.h" +#include "cinder/Surface.h" +#include "cinder/Url.h" +#include "cinder/Timer.h" + + + +namespace cinder { namespace em { namespace helpers { + + //! A helper function to add an event listener to an element in a more simplified form. + template + static void addEventListener( emscripten::val element,std::string eventName,std::function functor ) { + auto window = emscripten::val::global( "window" ); + + // this basically transforms your callback function so you don't have to call .onload + auto functor_adapter = emscripten::val( functor )["onload"].call( "bind", emscripten::val(functor) ); + + // add the listener to the element + //window.call("addElementListener",element,val(eventName),(functor_adapter)); + element.call( "addEventListener", emscripten::val(eventName), functor_adapter ); + } + + //! Retrieve helper JS functions + static emscripten::val getCinderHelpers(){ + return emscripten::val::global("window")["CINDER_HELPERS"]; + } + + //! retrieve audio JS helpers + static emscripten::val getAudioHelpers(){ + return emscripten::val::global("window")["CINDER_AUDIO"]; + } + + //! retrieve video JS helpers + static emscripten::val getVideoHelpers(){ + return emscripten::val::global("window")["CINDER_VIDEO"]; + } + + // TODO get rid of this since functions mostly un-needed - keeping to ensure we don't break anything. + static emscripten::val getFileHelpers(){ + return emscripten::val::global("window")["CINDER_FILEIO"]; + } + + /** + Copies a JS TypedArray to a C++ vector + https://github.com/kripken/emscripten/issues/5519 + */ + template + static void copyToVector( const emscripten::val &typedArray,std::vector &vec ) { + unsigned int length = typedArray["length"].as(); + emscripten::val memory = emscripten::val::module_property( "buffer" ); + vec.reserve( length ); + vec.resize( length ); + emscripten::val memoryView = typedArray["constructor"].new_( memory, reinterpret_cast(vec.data() ), length); + memoryView.call( "set", typedArray ); + } + + /** + * Same as copyToVector but works with regular arrays and floating point data. + */ + template + static void copyFloatArrayToVector(const emscripten::val &typedArray,std::vector &vec){ + unsigned int length = typedArray["length"].as(); + emscripten::val memory = emscripten::val::module_property( "buffer" ); + vec.reserve( length ); + vec.resize( length ); + emscripten::val memoryView = emscripten::val::global( "Float32Array" ).new_( memory, reinterpret_cast(vec.data() ), length); + memoryView.call( "set", typedArray ); + } + + //! Converts an std::function into a suitable value to use as a callback in the Javascript context. + //! Note that this only works with std::functions that have been declared in cinder/emscripten/globalbindings.cpp (or elsewhere) + //! that utilize the string "onload" as their function name. + static emscripten::val generateCallback( std::function functor ) + { + return emscripten::val( functor )["onload"].call( "bind", emscripten::val(functor) ); + } + + static emscripten::val getWorkletHelpers() + { + return emscripten::val::global("window")["CINDER_WORKLETS"]; + } + + //! Returns an image. Assumes you don't need specialized processing. + //! TODO return a Surface or something. + static char * getImage( DataSourceRef url ){ + + // determine the file type. + fs::path filename = fs::path( url->getUrl().str() ); + + // fetch the file - put it in the assets directory after it loads. + emscripten_wget( url->getUrl().c_str(),filename.c_str() ); + + int width, height; + + char *data = emscripten_get_preloaded_image_data( filename.c_str(), &width, &height ); + + return data; + } +}}} + +extern "C" { + extern void loadAsync(const char * url, void * data, int * size); +} + +namespace cinder { namespace app { + + //! Loads an asset asyncronously. + //! Pass in a string url as well as a lambda that accepts a DataSourceRef as a parameter. + static void loadAssetAsync( std::string url,std::function cb ) + { + auto helpers = emscripten::val::global("window")["CINDER_ASYNC"]; + + // callback that will construct the DataSourceRef + std::function sourceConstructor = [=]( emscripten::val byteArray )-> void + { + // turn emscripten::val into something more c++ friendly + std::vector data; + em::helpers::copyToVector(byteArray,data); + + // construct data source + ci::BufferRef rawBuff = ci::Buffer::create( static_cast( data.data() ),data.size() ); + ci::DataSourceBufferRef dbuff = ci::DataSourceBuffer::create( rawBuff ); + cb( dbuff ); + }; + + // generate javascript friendly callback + emscripten::val _cb = em::helpers::generateCallback( sourceConstructor ); + + // prefix with "assets" + auto finalPath = "assets/" + url; + + helpers.call( "loadAssetAsync",emscripten::val( finalPath ),_cb ); + } + } +} + diff --git a/include/cinder/emscripten/EmscriptenVideo.h b/include/cinder/emscripten/EmscriptenVideo.h new file mode 100644 index 0000000000..8b3f502962 --- /dev/null +++ b/include/cinder/emscripten/EmscriptenVideo.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2018, The Barbarian Group + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that + the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "cinder/Log.h" +#include "cinder/gl/Texture.h" +#include "cinder/gl/gl.h" + +using namespace ci; +using namespace emscripten; + +namespace cinder { namespace em { + + typedef std::shared_ptr< class EmscriptenVideo >EmscriptenVideoRef; + + /** + * Basic handling of