Josh Engebretson 10 years ago
parent
commit
34fd2a3123
37 changed files with 646 additions and 389 deletions
  1. 104 193
      CMake/Toolchains/emscripten.toolchain.cmake
  2. 10 0
      Data/AtomicPlayer/Resources/CoreData/Shaders/GLSL/TerrainBlend.glsl
  3. 49 29
      Source/Atomic/Audio/Audio.cpp
  4. 7 1
      Source/Atomic/Audio/Audio.h
  5. 6 6
      Source/Atomic/Container/Vector.h
  6. 6 4
      Source/Atomic/Core/ProcessUtils.cpp
  7. 14 4
      Source/Atomic/Core/Thread.cpp
  8. 21 21
      Source/Atomic/Engine/Application.cpp
  9. 2 2
      Source/Atomic/Engine/Engine.cpp
  10. 2 2
      Source/Atomic/Graphics/AnimatedModel.cpp
  11. 4 2
      Source/Atomic/Graphics/Batch.cpp
  12. 7 2
      Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp
  13. 2 5
      Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.h
  14. 1 3
      Source/Atomic/Graphics/View.cpp
  15. 2 2
      Source/Atomic/Input/Controls.cpp
  16. 1 1
      Source/Atomic/Input/Controls.h
  17. 307 58
      Source/Atomic/Input/Input.cpp
  18. 21 2
      Source/Atomic/Input/Input.h
  19. 7 1
      Source/Atomic/Input/InputEvents.h
  20. 13 3
      Source/Atomic/Math/MathDefs.h
  21. 2 2
      Source/Atomic/Network/Connection.h
  22. 7 2
      Source/Atomic/Resource/JSONFile.cpp
  23. 4 2
      Source/Atomic/Resource/JSONFile.h
  24. 9 4
      Source/Atomic/Resource/XMLFile.cpp
  25. 6 4
      Source/Atomic/Resource/XMLFile.h
  26. 2 2
      Source/Atomic/Scene/LogicComponent.h
  27. 4 9
      Source/Atomic/Scene/Node.cpp
  28. 2 2
      Source/Atomic/Scene/Node.h
  29. 3 3
      Source/Atomic/Scene/Scene.cpp
  30. 2 2
      Source/Atomic/Scene/Scene.h
  31. 3 3
      Source/Atomic/UI/Font.cpp
  32. 2 2
      Source/Atomic/UI/Font.h
  33. 3 3
      Source/Atomic/UI/FontFaceBitmap.cpp
  34. 2 2
      Source/Atomic/UI/FontFaceBitmap.h
  35. 3 3
      Source/Atomic/UI/UIElement.cpp
  36. 2 2
      Source/Atomic/UI/UIElement.h
  37. 4 1
      Source/ThirdParty/rapidjson/include/rapidjson/prettywriter.h

+ 104 - 193
CMake/Toolchains/emscripten.toolchain.cmake

@@ -1,204 +1,115 @@
-#-------------------------------------------------------------------------------
-#	emscripten.toolchain.cmake
-#	Oryol cmake toolchain file for cross-compiling to emscripten.
-#-------------------------------------------------------------------------------
-
-message("Target Platform: emscripten")
-
 #
-# FIXME FIXME FIXME:
+# Copyright (c) 2008-2015 the Urho3D project.
 #
-#   emar currently has trouble using a non-standard .emscripten config
-#   file: https://github.com/kripken/emscripten/issues/2886
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-#   once this is fixed, set the CMAKE_AR_FLAGS variable to
-#   use the --em-config like the C/CXX compilers.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-
-# depending on whether the official EMSDK is used or the
-# 'raw' emscripten SDK, OSX and Linux have 2 potential
-# locations for the emscripten SDK and the .emscripten file
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# (NOTE: the 'raw SDK' is not supported on Windows)
-if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
-    set(EMSC_EMSDK_DIRNAME "sdks/windows/emsdk_portable/emscripten/incoming")
-    set(EMSC_RAWSDK_DIRNAME "")
-elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")
-    set(EMSC_EMSDK_DIRNAME "sdks/emsdk_portable/emscripten/incoming")
-    set(EMSC_RAWSDK_DIRNAME "sdks/osx/emscripten")
-elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux")
-    set(EMSC_EMSDK_DIRNAME "sdks/linux/emsdk_portable/emscripten/incoming")
-    set(EMSC_RAWSDK_DIRNAME "sdks/linux/emscripten")
-endif()
-
-set(ORYOL_PLATFORM EMSCRIPTEN)
-set(ORYOL_PLATFORM_NAME "emsc")
-set(ORYOL_EMSCRIPTEN 1)
-set(ORYOL_POSIX 1)
-set(ORYOL_OPENGL 1)
-set(ORYOL_OPENGLES2 1)
-set(ORYOL_PLATFORM_DEFINES " -DORYOL_EMSCRIPTEN=1 -DORYOL_POSIX=1")
-set(EMSCRIPTEN 1)
-
-# total memory is 128MB for main thread, and 16 MB for worker
-# NOTE: USE_MEMORY_INIT_FILE has/had problems that the script is already starting but the MEM file isn't loaded yet(?)
-# at least I'm having weird startup problems...
-set(EMSCRIPTEN_TOTAL_MEMORY 134217728)
-set(EMSCRIPTEN_TOTAL_MEMORY_WORKER 16777216)
-set(EMSCRIPTEN_USE_MEMORY_INIT_FILE 1)
-set(EMSCRIPTEN_LTO_LEVEL 1)
-set(EMSCRIPTEN_NO_FILESYSTEM 1)
-
-# disable closure for now, as long as ANGLE_instanced_array support is not fully supported in emscripten
-set(EMSCRIPTEN_USE_CLOSURE 0)
-set(EMSCRIPTEN_ASSERTIONS 0)
-set(EMSCRIPTEN_OUTLINING_LIMIT 20000)
-
-if (ORYOL_COMPILE_VERBOSE)
-    set(EMSCRIPTEN_BUILD_VERBOSE 1)
-else()
-    set(EMSCRIPTEN_BUILD_VERBOSE 0)
-endif()
-
-# exceptions on/off?
-if (ORYOL_EXCEPTIONS)
-    message("C++ exceptions are enabled")
-    set(ORYOL_EMSC_EXCEPTION_FLAGS "")
-    set(EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING 0)
-else()
-    message("C++ exceptions are disabled")
-    set(ORYOL_EMSC_EXCEPTION_FLAGS "-fno-exceptions")
-    set(EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING 1)
-endif()
-
-message("EMSCRIPTEN_TOTAL_MEMORY: ${EMSCRIPTEN_TOTAL_MEMORY}")
-message("EMSCRIPTEN_TOTAL_MEMORY_WORKER: ${EMSCRIPTEN_TOTAL_MEMORY_WORKER}")
-message("EMSCRIPTEN_USE_MEMORY_INIT_FILE: ${EMSCRIPTEN_USE_MEMORY_INIT_FILE}")
-message("EMSCRIPTEN_LTO_LEVEL: ${EMSCRIPTEN_LTO_LEVEL}")
-message("EMSCRIPTEN_USE_CLOSURE: ${EMSCRIPTEN_USE_CLOSURE}")
-message("EMSCRIPTEN_ASSERTIONS: ${EMSCRIPTEN_ASSERTIONS}")
-message("EMSCRIPTEN_OUTLINING_LIMIT: ${EMSCRIPTEN_OUTLINING_LIMIT}")
-message("EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING: ${EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING}")
-message("EMSCRIPTEN_NO_FILESYSTEM: ${EMSCRIPTEN_NO_FILESYSTEM}")
-
-set(CMAKE_SYSTEM_NAME Linux)
-set(CMAKE_SYSTEM_VERSION 1)
-set(COMPILING on)
-set(CMAKE_CROSSCOMPILING TRUE)
-
-# find the emscripten SDK and set the "EMSC_HAS_LOCAL_CONFIG" variable
-set(EMSC_HAS_LOCAL_CONFIG 0)
-macro(find_emscripten_sdk)
-    # first check for the official EMSDK, this does not allow to override
-    # the location of the .emscripten config file
-    get_filename_component(EMSCRIPTEN_ROOT_PATH "/Users/josh/Dev/${EMSC_EMSDK_DIRNAME}" ABSOLUTE)
-
-
-    message ("${EMSCRIPTEN_ROOT_PATH}")
-
-    if (EXISTS "${EMSCRIPTEN_ROOT_PATH}/emcc")
-        message("Emscripten SDK found (emsdk): ${EMSCRIPTEN_ROOT_PATH}")
-    else()
-        # check for the RAW SDK (not supported on Windows)
-        get_filename_component(EMSCRIPTEN_ROOT_PATH "${CMAKE_CURRENT_LIST_DIR}/../${EMSC_RAWSDK_DIRNAME}" ABSOLUTE)
-        if (EXISTS "${EMSCRIPTEN_ROOT_PATH}/emcc")
-            message("Emscripten SDK found (raw sdk): ${EMSCRIPTEN_ROOT_PATH}")
-            set(EMSC_HAS_LOCAL_CONFIG 1)
-        else()
-            message(FATAL_ERROR "Could not find emscripten SDK! See BUILD.md for instructions to setup Oryol for emscripten development!")
-        endif()
-    endif()
-endmacro()
 
-# find the emscripten SDK
-find_emscripten_sdk()
+# Based on cmake/Modules/Platform/Emscripten.cmake from https://github.com/kripken/emscripten
 
-# Normalize, convert Windows backslashes to forward slashes or CMake will crash.
-get_filename_component(EMSCRIPTEN_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}" ABSOLUTE)
+cmake_minimum_required (VERSION 2.6.3)
 
-# Set the path to .emscripten file
-get_filename_component(EMSCRIPTEN_DOT_FILE "${EMSCRIPTEN_ROOT_PATH}/../.emscripten" ABSOLUTE)
+if (CMAKE_TOOLCHAIN_FILE)
+    # Reference toolchain variable to suppress "unused variable" warning
+    mark_as_advanced (CMAKE_TOOLCHAIN_FILE)
+endif ()
 
-# Set up options that we always want to pass to emscripten
-if (EMSC_HAS_LOCAL_CONFIG)
-    set(EMSCRIPTEN_CONFIG_OPTIONS "--em-config ${EMSCRIPTEN_DOT_FILE}")
-else()
-    set(EMSCRIPTEN_CONFIG_OPTIONS "")    
-endif()
+# this one is important
+set (CMAKE_SYSTEM_NAME Linux)
+# this one not so much
+set (CMAKE_SYSTEM_VERSION 1)
 
-# tool suffic (.bat on windows)
+# specify the cross compiler
+if (NOT EMSCRIPTEN_ROOT_PATH AND DEFINED ENV{EMSCRIPTEN_ROOT_PATH})
+    file (TO_CMAKE_PATH $ENV{EMSCRIPTEN_ROOT_PATH} EMSCRIPTEN_ROOT_PATH)
+endif ()
+if (NOT EXISTS ${EMSCRIPTEN_ROOT_PATH}/emcc)
+    message (FATAL_ERROR "Could not find Emscripten cross compilation tool. "
+        "Use EMSCRIPTEN_ROOT_PATH environment variable or build option to specify the location of the toolchain.")
+endif ()
 if (CMAKE_HOST_WIN32)
-    set(EMCC_SUFFIX ".bat")
-else()
-    set(EMCC_SUFFIX "")
-endif()
-
-include(CMakeForceCompiler)
-CMAKE_FORCE_C_COMPILER("${CMAKE_C_COMPILER}" Clang)
-CMAKE_FORCE_CXX_COMPILER("${CMAKE_CXX_COMPILER}" Clang)
-
-# define configurations
-set(CMAKE_CONFIGURATION_TYPES Debug Release)
-
-# specify cross-compilers
-set(CMAKE_C_COMPILER "${EMSCRIPTEN_ROOT_PATH}/emcc${EMCC_SUFFIX}" CACHE PATH "gcc" FORCE)
-set(CMAKE_CXX_COMPILER "${EMSCRIPTEN_ROOT_PATH}/em++${EMCC_SUFFIX}" CACHE PATH "g++" FORCE)
-set(CMAKE_AR "${EMSCRIPTEN_ROOT_PATH}/emar${EMCC_SUFFIX}" CACHE PATH "archive" FORCE)
-set(CMAKE_LINKER "${EMSCRIPTEN_ROOT_PATH}/emcc${EMCC_SUFFIX}" CACHE PATH "linker" FORCE)
-set(CMAKE_RANLIB "${EMSCRIPTEN_ROOT_PATH}/emranlib${EMCC_SUFFIX}" CACHE PATH "ranlib" FORCE)
-
-# only search for libraries and includes in the toolchain
-set(CMAKE_FIND_ROOT_PATH ${EMSCRIPTEN_ROOT_PATH})
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-
-set(CMAKE_SYSTEM_INCLUDE_PATH "${EMSCRIPTEN_ROOT_PATH}/system/include")
-
-# c++ compiler flags
-set(CMAKE_CXX_FLAGS "${ORYOL_PLATFORM_DEFINES} ${EMSCRIPTEN_CONFIG_OPTIONS} -std=c++11 -stdlib=libc++ ${ORYOL_EMSC_EXCEPTION_FLAGS} -fstrict-aliasing -Wall -Wno-warn-absolute-paths -Wno-multichar -Wextra -Wno-unused-parameter -Wno-unknown-pragmas -Wno-ignored-qualifiers -Wno-long-long -Wno-overloaded-virtual -Wno-deprecated-writable-strings -Wno-unused-volatile-lvalue")
-set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
-set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g -D_DEBUG_ -D_DEBUG -DORYOL_DEBUG=1")
-set(CMAKE_CXX_FLAGS_PROFILING "-O3 --profiling")
-
-# c compiler flags
-set(CMAKE_C_FLAGS "${ORYOL_PLATFORM_DEFINES} ${EMSCRIPTEN_CONFIG_OPTIONS} -fstrict-aliasing -Wall -Wno-warn-absolute-paths -Wextra -Wno-multichar -Wno-unused-parameter -Wno-unknown-pragmas -Wno-ignored-qualifiers -Wno-long-long -Wno-overloaded-virtual -Wno-deprecated-writable-strings -Wno-unused-volatile-lvalue")
-set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
-set(CMAKE_C_FLAGS_DEBUG "-O3 -g -D_DEBUG_ -D_DEBUG -DORYOL_DEBUG=1")
-set(CMAKE_C_FLAGS_PROFILING "-O3 --profiling")
-
-# linker flags
-set(CMAKE_EXE_LINKER_FLAGS "${EMSCRIPTEN_CONFIG_OPTIONS} --memory-init-file ${EMSCRIPTEN_USE_MEMORY_INIT_FILE} -s WARN_ON_UNDEFINED_SYMBOLS=1 -s TOTAL_MEMORY=${EMSCRIPTEN_TOTAL_MEMORY} -s DISABLE_EXCEPTION_CATCHING=${EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING} -s NO_FILESYSTEM=${EMSCRIPTEN_NO_FILESYSTEM}")
-set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O3 --llvm-lto ${EMSCRIPTEN_LTO_LEVEL} -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE} -s ASM_JS=1 -s ASSERTIONS=${EMSCRIPTEN_ASSERTIONS} -s OUTLINING_LIMIT=${EMSCRIPTEN_OUTLINING_LIMIT} --closure ${EMSCRIPTEN_USE_CLOSURE}")
-set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-O3 -g -s ASM_JS=1 -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE}")
-set(CMAKE_EXE_LINKER_FLAGS_PROFILING "--profiling -O3 --llvm-lto ${EMSCRIPTEN_LTO_LEVEL} -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE} -s ASM_JS=1 -s ASSERTIONS=${EMSCRIPTEN_ASSERTIONS} -s OUTLINING_LIMIT=${EMSCRIPTEN_OUTLINING_LIMIT}")
-
-# dynamic lib linker flags
-set(CMAKE_SHARED_LINKER_FLAGS "-shared ${EMSCRIPTEN_CONFIG_OPTIONS} --memory-init-file 0 -s WARN_ON_UNDEFINED_SYMBOLS=1 -s TOTAL_MEMORY=${EMSCRIPTEN_TOTAL_MEMORY_WORKER} -s BUILD_AS_WORKER=1 -s EXPORTED_FUNCTIONS=[\\\"_dowork\\\"] -s DISABLE_EXCEPTION_CATCHING=${EMSCRIPTEN_DISABLE_EXCEPTION_CATCHING} -s NO_FILESYSTEM=${EMSCRIPTEN_NO_FILESYSTEM}")
-set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "-O3 --llvm-lto ${EMSCRIPTEN_LTO_LEVEL} -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE} -s ASM_JS=1 -s ASSERTIONS=${EMSCRIPTEN_ASSERTIONS} -s OUTLINING_LIMIT=${EMSCRIPTEN_OUTLINING_LIMIT} --closure ${EMSCRIPTEN_USE_CLOSURE}")
-set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "-O3 -g -s ASM_JS=1 -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE} --closure 0")
-set(CMAKE_SHARED_LINKER_FLAGS_PROFILING "--profiling -O3 --llvm-lto ${EMSCRIPTEN_LTO_LEVEL} -s VERBOSE=${EMSCRIPTEN_BUILD_VERBOSE} -s ASM_JS=1 -s ASSERTIONS=${EMSCRIPTEN_ASSERTIONS} -s OUTLINING_LIMIT=${EMSCRIPTEN_OUTLINING_LIMIT}")
-
-# update cache variables for cmake gui
-set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "Config Type" FORCE)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "Generic C++ Compiler Flags" FORCE)
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "C++ Debug Compiler Flags" FORCE)
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "C++ Release Compiler Flags" FORCE)
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "Generic C Compiler Flags" FORCE)
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}" CACHE STRING "C Debug Compiler Flags" FORCE)
-set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "C Release Compiler Flags" FORCE)
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" CACHE STRING "Generic Linker Flags" FORCE)
-set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG}" CACHE STRING "Debug Linker Flags" FORCE)
-set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}" CACHE STRING "Release Linker Flags" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "Generic Shared Linker Flags" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE STRING "Debug Shared Linker Flags" FORCE)
-set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}" CACHE STRING "Release Shared Linker Flags" FORCE)
-
-# set the build type to use
-if (NOT CMAKE_BUILD_TYPE)
-    set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Compile Type" FORCE)
-endif()
-set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release)
-
-message("Emscripten toolchain configured")
+    set (TOOL_EXT .bat)
+endif ()
+set (CMAKE_C_COMPILER   ${EMSCRIPTEN_ROOT_PATH}/emcc${TOOL_EXT}     CACHE PATH "C compiler")
+set (CMAKE_CXX_COMPILER ${EMSCRIPTEN_ROOT_PATH}/em++${TOOL_EXT}     CACHE PATH "C++ compiler")
+set (CMAKE_AR           ${EMSCRIPTEN_ROOT_PATH}/emar${TOOL_EXT}     CACHE PATH "archive")
+set (CMAKE_RANLIB       ${EMSCRIPTEN_ROOT_PATH}/emranlib${TOOL_EXT} CACHE PATH "ranlib")
+
+# specify the system root
+if (NOT EMSCRIPTEN_SYSROOT)
+    if (DEFINED ENV{EMSCRIPTEN_SYSROOT})
+        file (TO_CMAKE_PATH $ENV{EMSCRIPTEN_SYSROOT} EMSCRIPTEN_SYSROOT)
+    else ()
+        set (EMSCRIPTEN_SYSROOT ${EMSCRIPTEN_ROOT_PATH}/system)
+    endif ()
+    if (NOT EXISTS ${EMSCRIPTEN_SYSROOT})
+        message (FATAL_ERROR "Could not find Emscripten system root. "
+            "Use EMSCRIPTEN_SYSROOT environment variable or build option to specify the location of system root.")
+    endif ()
+    set (EMSCRIPTEN_ROOT_PATH ${EMSCRIPTEN_ROOT_PATH} CACHE STRING "Root path to Emscripten cross-compiler tools (Emscripten cross-compiling build only)" FORCE)
+    set (EMSCRIPTEN_SYSROOT ${EMSCRIPTEN_SYSROOT} CACHE PATH "Path to Emscripten system root (Emscripten cross-compiling build only)" FORCE)
+endif ()
+set (CMAKE_FIND_ROOT_PATH ${EMSCRIPTEN_SYSROOT})
+
+# only search libraries, and headers in the target directories
+set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+# Don't do compiler autodetection, since we are cross-compiling.
+include (CMakeForceCompiler)
+CMAKE_FORCE_C_COMPILER ("${CMAKE_C_COMPILER}" Clang)
+CMAKE_FORCE_CXX_COMPILER ("${CMAKE_CXX_COMPILER}" Clang)
+
+# Hardwire support for cmake-2.8/Modules/CMakeBackwardsCompatibilityC.cmake without having CMake to try complex things
+# to autodetect these:
+set (CMAKE_SKIP_COMPATIBILITY_TESTS 1)
+set (CMAKE_SIZEOF_CHAR 1)
+set (CMAKE_SIZEOF_UNSIGNED_SHORT 2)
+set (CMAKE_SIZEOF_SHORT 2)
+set (CMAKE_SIZEOF_INT 4)
+set (CMAKE_SIZEOF_UNSIGNED_LONG 4)
+set (CMAKE_SIZEOF_UNSIGNED_INT 4)
+set (CMAKE_SIZEOF_LONG 4)
+set (CMAKE_SIZEOF_VOID_P 4)
+set (CMAKE_SIZEOF_FLOAT 4)
+set (CMAKE_SIZEOF_DOUBLE 8)
+set (CMAKE_C_SIZEOF_DATA_PTR 4)
+set (CMAKE_CXX_SIZEOF_DATA_PTR 4)
+set (CMAKE_HAVE_LIMITS_H 1)
+set (CMAKE_HAVE_UNISTD_H 1)
+set (CMAKE_HAVE_PTHREAD_H 1)
+set (CMAKE_HAVE_SYS_PRCTL_H 1)
+set (CMAKE_WORDS_BIGENDIAN 0)
+set (CMAKE_DL_LIBS)
+
+# In order for check_function_exists() detection to work, we must signal it to pass an additional flag, which causes the compilation
+# to abort if linking results in any undefined symbols. The CMake detection mechanism depends on the undefined symbol error to be raised.
+set (CMAKE_REQUIRED_FLAGS "-s ERROR_ON_UNDEFINED_SYMBOLS=1")
+
+# Use response files on Windows host
+if (CMAKE_HOST_WIN32)
+    foreach (lang C CXX)
+        foreach (cat LIBRARIES OBJECTS INCLUDES)
+            set (CMAKE_${lang}_USE_RESPONSE_FILE_FOR_${cat} 1)
+        endforeach ()
+        set (CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> cr <TARGET> <LINK_FLAGS> <OBJECTS>")
+    endforeach ()
+endif ()
+
+set (EMSCRIPTEN 1)

+ 10 - 0
Data/AtomicPlayer/Resources/CoreData/Shaders/GLSL/TerrainBlend.glsl

@@ -6,7 +6,13 @@
 #include "Fog.glsl"
 
 varying vec2 vTexCoord;
+
+#ifndef GL_ES
 varying vec2 vDetailTexCoord;
+#else
+varying lowp vec2 vDetailTexCoord;
+#endif
+
 varying vec3 vNormal;
 varying vec4 vWorldPos;
 #ifdef PERPIXEL
@@ -35,7 +41,11 @@ uniform sampler2D sDetailMap1;
 uniform sampler2D sDetailMap2;
 uniform sampler2D sDetailMap3;
 
+#ifndef GL_ES
 uniform vec2 cDetailTiling;
+#else
+uniform lowp vec2 cDetailTiling;
+#endif
 
 void VS()
 {

+ 49 - 29
Source/Atomic/Audio/Audio.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -56,10 +56,10 @@ Audio::Audio(Context* context) :
 {
     // Set the master to the default value
     masterGain_[SOUND_MASTER_HASH] = 1.0f;
-    
+
     // Register Audio library object factories
     RegisterAudioLibrary(context_);
-    
+
     SubscribeToEvent(E_RENDERUPDATE, HANDLER(Audio, HandleRenderUpdate));
 }
 
@@ -71,32 +71,47 @@ Audio::~Audio()
 bool Audio::SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpolation)
 {
     Release();
-    
+
     bufferLengthMSec = Max(bufferLengthMSec, MIN_BUFFERLENGTH);
     mixRate = Clamp(mixRate, MIN_MIXRATE, MAX_MIXRATE);
-    
+
     SDL_AudioSpec desired;
     SDL_AudioSpec obtained;
-    
+
     desired.freq = mixRate;
-    desired.format = AUDIO_S16SYS;
+
+// The concept behind the emspcripten audio port is to treat it as 16 bit until the final acumulation form the clip buffer
+#ifdef EMSCRIPTEN
+    desired.format = AUDIO_F32LSB;
+#else
+    desired.format = AUDIO_S16;
+#endif
     desired.channels = stereo ? 2 : 1;
     desired.callback = SDLAudioCallback;
     desired.userdata = this;
-    
+
     // SDL uses power of two audio fragments. Determine the closest match
     int bufferSamples = mixRate * bufferLengthMSec / 1000;
     desired.samples = NextPowerOfTwo(bufferSamples);
     if (Abs((int)desired.samples / 2 - bufferSamples) < Abs((int)desired.samples - bufferSamples))
         desired.samples /= 2;
-    
+
     deviceID_ = SDL_OpenAudioDevice(0, SDL_FALSE, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
     if (!deviceID_)
     {
         LOGERROR("Could not initialize audio output");
         return false;
     }
-    
+
+#ifdef EMSCRIPTEN
+    if (obtained.format != AUDIO_F32LSB && obtained.format != AUDIO_F32MSB && obtained.format != AUDIO_F32SYS)
+    {
+        LOGERROR("Could not initialize audio output, 32-bit float buffer format not supported");
+        SDL_CloseAudioDevice(deviceID_);
+        deviceID_ = 0;
+        return false;
+    }
+#else
     if (obtained.format != AUDIO_S16SYS && obtained.format != AUDIO_S16LSB && obtained.format != AUDIO_S16MSB)
     {
         LOGERROR("Could not initialize audio output, 16-bit buffer format not supported");
@@ -104,25 +119,26 @@ bool Audio::SetMode(int bufferLengthMSec, int mixRate, bool stereo, bool interpo
         deviceID_ = 0;
         return false;
     }
-    
+#endif
+
     stereo_ = obtained.channels == 2;
     sampleSize_ = stereo_ ? sizeof(int) : sizeof(short);
     // Guarantee a fragment size that is low enough so that Vorbis decoding buffers do not wrap
     fragmentSize_ = Min((int)NextPowerOfTwo(mixRate >> 6), (int)obtained.samples);
-    mixRate_ = mixRate;
+    mixRate_ = obtained.freq;
     interpolation_ = interpolation;
     clipBuffer_ = new int[stereo ? fragmentSize_ << 1 : fragmentSize_];
-    
+
     LOGINFO("Set audio mode " + String(mixRate_) + " Hz " + (stereo_ ? "stereo" : "mono") + " " +
         (interpolation_ ? "interpolated" : ""));
-    
+
     return Play();
 }
 
 void Audio::Update(float timeStep)
 {
     PROFILE(UpdateAudio);
-    
+
     // Update in reverse order, because sound sources might remove themselves
     for (unsigned i = soundSources_.Size() - 1; i < soundSources_.Size(); --i)
         soundSources_[i]->Update(timeStep);
@@ -132,15 +148,15 @@ bool Audio::Play()
 {
     if (playing_)
         return true;
-    
+
     if (!deviceID_)
     {
         LOGERROR("No audio mode set, can not start playback");
         return false;
     }
-    
+
     SDL_PauseAudioDevice(deviceID_, 0);
-    
+
     playing_ = true;
     return true;
 }
@@ -221,10 +237,9 @@ float Audio::GetSoundSourceMasterGain(StringHash typeHash) const
 void SDLAudioCallback(void *userdata, Uint8* stream, int len)
 {
     Audio* audio = static_cast<Audio*>(userdata);
-    
     {
         MutexLock Lock(audio->GetMutex());
-        audio->MixOutput(stream, len / audio->GetSampleSize());
+        audio->MixOutput(stream, len / audio->GetSampleSize() / Audio::SAMPLE_SIZE_MUL);
     }
 }
 
@@ -232,10 +247,10 @@ void Audio::MixOutput(void *dest, unsigned samples)
 {
     if (!playing_ || !clipBuffer_)
     {
-        memset(dest, 0, samples * sampleSize_);
+        memset(dest, 0, samples * sampleSize_ * SAMPLE_SIZE_MUL);
         return;
     }
-    
+
     while (samples)
     {
         // If sample count exceeds the fragment (clip buffer) size, split the work
@@ -243,36 +258,41 @@ void Audio::MixOutput(void *dest, unsigned samples)
         unsigned clipSamples = workSamples;
         if (stereo_)
             clipSamples <<= 1;
-        
+
         // Clear clip buffer
         int* clipPtr = clipBuffer_.Get();
         memset(clipPtr, 0, clipSamples * sizeof(int));
-        
+
         // Mix samples to clip buffer
         for (PODVector<SoundSource*>::Iterator i = soundSources_.Begin(); i != soundSources_.End(); ++i)
             (*i)->Mix(clipPtr, workSamples, mixRate_, stereo_, interpolation_);
-        
+
         // Copy output from clip buffer to destination
+#ifdef EMSCRIPTEN
+        float* destPtr = (float*)dest;
+        while (clipSamples--)
+            *destPtr++ = (float)Clamp(*clipPtr++, -32768, 32767) / 32768.0f;
+#else
         short* destPtr = (short*)dest;
         while (clipSamples--)
             *destPtr++ = Clamp(*clipPtr++, -32768, 32767);
-        
+#endif
         samples -= workSamples;
-        ((unsigned char*&)dest) += sampleSize_ * workSamples;
+        ((unsigned char*&)dest) += sampleSize_ * SAMPLE_SIZE_MUL * workSamples;
     }
 }
 
 void Audio::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
     using namespace RenderUpdate;
-    
+
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 
 void Audio::Release()
 {
     Stop();
-    
+
     if (deviceID_)
     {
         SDL_CloseAudioDevice(deviceID_);

+ 7 - 1
Source/Atomic/Audio/Audio.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -95,6 +95,12 @@ public:
     /// Mix sound sources into the buffer.
     void MixOutput(void *dest, unsigned samples);
 
+    /// Final multiplier for for audio byte conversion
+#ifdef EMSCRIPTEN
+    static const int SAMPLE_SIZE_MUL = 2;
+#else
+    static const int SAMPLE_SIZE_MUL = 1;
+#endif
 private:
     /// Handle render update event.
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);

+ 6 - 6
Source/Atomic/Container/Vector.h

@@ -214,7 +214,7 @@ public:
         unsigned pos = dest - Begin();
         if (pos > size_)
             pos = size_;
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Resize(size_ + length, 0);
         MoveRange(pos + length, pos, size_ - pos - length);
         
@@ -231,7 +231,7 @@ public:
         unsigned pos = dest - Begin();
         if (pos > size_)
             pos = size_;
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Resize(size_ + length, 0);
         MoveRange(pos + length, pos, size_ - pos - length);
         
@@ -270,7 +270,7 @@ public:
         unsigned pos = start - Begin();
         if (pos >= size_)
             return End();
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Erase(pos, length);
         
         return Begin() + pos;
@@ -651,7 +651,7 @@ public:
         unsigned pos = dest - Begin();
         if (pos > size_)
             pos = size_;
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Resize(size_ + length);
         MoveRange(pos + length, pos, size_ - pos - length);
         CopyElements(Buffer() + pos, &(*start), length);
@@ -665,7 +665,7 @@ public:
         unsigned pos = dest - Begin();
         if (pos > size_)
             pos = size_;
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Resize(size_ + length);
         MoveRange(pos + length, pos, size_ - pos - length);
         
@@ -704,7 +704,7 @@ public:
         unsigned pos = start - Begin();
         if (pos >= size_)
             return End();
-        unsigned length = end - start;
+        unsigned length = (unsigned)(end - start);
         Erase(pos, length);
         
         return Begin() + pos;

+ 6 - 4
Source/Atomic/Core/ProcessUtils.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -34,7 +34,7 @@
 
 #if defined(IOS)
 #include <mach/mach_host.h>
-#elif !defined(ANDROID) && !defined(RPI)
+#elif !defined(ANDROID) && !defined(RPI) && !defined(EMSCRIPTEN)
 #include <LibCpuId/src/libcpuid.h>
 #endif
 
@@ -349,6 +349,8 @@ String GetPlatform()
     return "Mac OS X";
     #elif defined(RPI)
     return "Raspberry Pi";
+    #elif defined(EMSCRIPTEN)
+    return "HTML5";
     #elif defined(__linux__)
     return "Linux";
     #else
@@ -393,7 +395,7 @@ unsigned GetNumPhysicalCPUs()
     #endif
     #elif defined(ANDROID)
     return GetAndroidCPUCount();
-    #elif !defined(ANDROID) && !defined(RPI) && !defined(EMSCRIPTEN)
+    #elif !defined(RPI) && !defined(EMSCRIPTEN)
     struct cpu_id_t data;
     GetCPUData(&data);
     return data.num_cores;
@@ -415,7 +417,7 @@ unsigned GetNumLogicalCPUs()
     #endif
     #elif defined(ANDROID)
     return GetAndroidCPUCount();
-    #elif !defined(ANDROID) && !defined(RPI) && !defined(EMSCRIPTEN)
+    #elif !defined(RPI) && !defined(EMSCRIPTEN)
     struct cpu_id_t data;
     GetCPUData(&data);
     return data.num_logical_cpus;

+ 14 - 4
Source/Atomic/Core/Thread.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -46,7 +46,11 @@ void* ThreadFunctionStatic(void* data)
 {
     Thread* thread = static_cast<Thread*>(data);
     thread->ThreadFunction();
+#ifdef EMSCRIPTEN
+// note: emscripten doesn't have this function but doesn't use threading anyway
+// so #ifdef it out to prevent linker warnings
     pthread_exit((void*)0);
+#endif
     return 0;
 }
 #endif
@@ -73,7 +77,10 @@ bool Thread::Run()
     shouldRun_ = true;
     #ifdef WIN32
     handle_ = CreateThread(0, 0, ThreadFunctionStatic, this, 0, 0);
-    #else
+//    #else
+    #elif !defined(EMSCRIPTEN)
+// note: emscripten doesn't have this function but doesn't use
+// threading anyway so #ifdef it out to prevent linker warnings
     handle_ = new pthread_t;
     pthread_attr_t type;
     pthread_attr_init(&type);
@@ -93,7 +100,10 @@ void Thread::Stop()
     #ifdef WIN32
     WaitForSingleObject((HANDLE)handle_, INFINITE);
     CloseHandle((HANDLE)handle_);
-    #else
+//    #else
+    #elif !defined(EMSCRIPTEN)
+ // note: emscripten doesn't have this function but doesn't use
+ // threading anyway so #ifdef it out to prevent linker warnings
     pthread_t* thread = (pthread_t*)handle_;
     if (thread)
         pthread_join(*thread, 0);
@@ -108,7 +118,7 @@ void Thread::SetPriority(int priority)
     if (handle_)
         SetThreadPriority((HANDLE)handle_, priority);
     #endif
-    #if defined(__linux__) && !defined(ANDROID)
+    #if defined(__linux__) && !defined(ANDROID) && !defined(EMSCRIPTEN)
     pthread_t* thread = (pthread_t*)handle_;
     if (thread)
         pthread_setschedprio(*thread, priority);

+ 21 - 21
Source/Atomic/Engine/Application.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -38,15 +38,18 @@
 namespace Atomic
 {
 
-#ifdef IOS
-// Code for supporting SDL_iPhoneSetAnimationCallback
+#if defined(IOS) || defined(EMSCRIPTEN)
+// Code for supporting SDL_iPhoneSetAnimationCallback() and emscripten_set_main_loop_arg()
+#if defined(EMSCRIPTEN)
+#include <emscripten.h>
+#endif
 void RunFrame(void* data)
 {
-    Application* instance = reinterpret_cast<Application*>(data);
-    instance->GetSubsystem<Engine>()->RunFrame();
+    Engine* engine = reinterpret_cast<Engine*>(data);
+    engine->RunFrame();
 }
 #endif
-    
+
 Application::Application(Context* context) :
     Object(context),
     exitCode_(EXIT_SUCCESS)
@@ -55,18 +58,18 @@ Application::Application(Context* context) :
 
     // Create the Engine, but do not initialize it yet. Subsystems except Graphics & Renderer are registered at this point
     engine_ = new Engine(context);
-    
+
     // Subscribe to log messages so that can show errors if ErrorExit() is called with empty message
     SubscribeToEvent(E_LOGMESSAGE, HANDLER(Application, HandleLogMessage));
 }
 
 int Application::Run()
 {
-
-#ifndef EMSCRIPTEN
+    // Emscripten-specific: C++ exceptions are turned off by default in -O1 (and above), unless '-s DISABLE_EXCEPTION_CATCHING=0' flag is set
+    // Urho3D build configuration uses -O3 (Release), -O2 (RelWithDebInfo), and -O0 (Debug)
+    // Thus, the try-catch block below should be optimised out except in Debug build configuration
     try
     {
-#endif
         Setup();
         if (exitCode_)
             return exitCode_;
@@ -81,7 +84,7 @@ int Application::Run()
         if (exitCode_)
             return exitCode_;
 
-        // Platforms other than iOS run a blocking main loop
+        // Platforms other than iOS and EMSCRIPTEN run a blocking main loop
         #if !defined(IOS) && !defined(EMSCRIPTEN)
         while (!engine_->IsExiting())
             engine_->RunFrame();
@@ -89,24 +92,21 @@ int Application::Run()
         Stop();
         // iOS will setup a timer for running animation frames so eg. Game Center can run. In this case we do not
         // support calling the Stop() function, as the application will never stop manually
-        #ifdef IOS
-            SDL_iPhoneSetAnimationCallback(GetSubsystem<Graphics>()->GetImpl()->GetWindow(), 1, &RunFrame, this);
         #else
-            // EMSCRIPTEN
+        #if defined(IOS)
+        SDL_iPhoneSetAnimationCallback(GetSubsystem<Graphics>()->GetImpl()->GetWindow(), 1, &RunFrame, engine_);
+        #elif defined(EMSCRIPTEN)
+        emscripten_set_main_loop_arg(RunFrame, engine_, 0, 1);
         #endif
         #endif
-        
-        return exitCode_;
 
-#ifndef EMSCRIPTEN
+        return exitCode_;
     }
-
     catch (std::bad_alloc&)
     {
         ErrorDialog(GetTypeName(), "An out-of-memory error occurred. The application will now exit.");
         return EXIT_FAILURE;
     }
-#endif
 }
 
 void Application::ErrorExit(const String& message)
@@ -129,7 +129,7 @@ void Application::ErrorExit(const String& message)
 void Application::HandleLogMessage(StringHash eventType, VariantMap& eventData)
 {
     using namespace LogMessage;
-    
+
     if (eventData[P_LEVEL].GetInt() == LOG_ERROR)
     {
         // Strip the timestamp if necessary
@@ -137,7 +137,7 @@ void Application::HandleLogMessage(StringHash eventType, VariantMap& eventData)
         unsigned bracketPos = error.Find(']');
         if (bracketPos != String::NPOS)
             error = error.Substring(bracketPos + 2);
-        
+
         startupErrors_ += error + "\n";
     }
 }

+ 2 - 2
Source/Atomic/Engine/Engine.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -89,7 +89,7 @@ Engine::Engine(Context* context) :
     timeStep_(0.0f),
     timeStepSmoothing_(2),
     minFps_(10),
-    #if defined(ANDROID) || defined(IOS) || defined(RPI)
+    #if defined(ANDROID) || defined(IOS) || defined(RPI) || defined(EMSCRIPTEN)
     maxFps_(60),
     maxInactiveFps_(10),
     pauseMinimized_(true),

+ 2 - 2
Source/Atomic/Graphics/AnimatedModel.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -136,7 +136,7 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
 {
     // If no bones or no bone-level testing, use the StaticModel test
     RayQueryLevel level = query.level_;
-    if (level < RAY_AABB || !skeleton_.GetNumBones())
+    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones())
     {
         StaticModel::ProcessRayQuery(query, results);
         return;

+ 4 - 2
Source/Atomic/Graphics/Batch.cpp

@@ -376,7 +376,8 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
             case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
-                    unsigned numSplits = lightQueue_->shadowSplits_.Size();
+                    unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
+
                     for (unsigned i = 0; i < numSplits; ++i)
                         CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, Vector3::ZERO);
                     
@@ -434,7 +435,8 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
             case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
-                    unsigned numSplits = lightQueue_->shadowSplits_.Size();
+                    unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
+
                     for (unsigned i = 0; i < numSplits; ++i)
                     {
                         CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, isLightVolume ? cameraEffectivePos :

+ 7 - 2
Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp

@@ -329,8 +329,12 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         fullscreen = false;
 
     multiSample = Clamp(multiSample, 1, 16);
+bool isInitialized = IsInitialized();
+LOGINFO(isInitialized ? "isInitialized == true" : "isInitialized == false");
+
     
-    if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
+    //if (IsInitialized() && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
+    if (isInitialized && width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
         vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
         return true;
     
@@ -360,6 +364,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
             height = 768;
         }
     }
+
     
     // Check fullscreen mode validity (desktop only). Use a closest match if not found
     #if !defined(ANDROID) && !defined(IOS) && !defined(RPI)
@@ -449,7 +454,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
                 impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), x, y, width, height, flags);
             else
             {
-#ifndef EMSCRIPTEN
+#if !defined(EMSCRIPTEN)
                 if (!impl_->window_)
                     impl_->window_ = SDL_CreateWindowFrom(externalWindow_, SDL_WINDOW_OPENGL);
                 fullscreen = false;

+ 2 - 5
Source/Atomic/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,15 +26,12 @@
 #include "../../Container/HashMap.h"
 #include "../../Core/Timer.h"
 
-#if defined(ANDROID) || defined (RPI)
+#if defined(ANDROID) || defined (RPI) || defined (EMSCRIPTEN)
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #elif defined(IOS)
 #include <OpenGLES/ES2/gl.h>
 #include <OpenGLES/ES2/glext.h>
-#elif defined(EMSCRIPTEN)
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
 #else
 #include <GLEW/glew.h>
 #endif

+ 1 - 3
Source/Atomic/Graphics/View.cpp

@@ -1932,8 +1932,6 @@ void View::AllocateScreenBuffers()
     unsigned format = renderTarget_ ? renderTarget_->GetParentTexture()->GetFormat() : Graphics::GetRGBFormat();
     
     // If HDR rendering is enabled use RGBA16f and reserve a buffer
-    bool hdrRendering = renderer_->GetHDRRendering();
-
     if (renderer_->GetHDRRendering())
     {
         format = Graphics::GetRGBAFloat16Format();
@@ -1941,7 +1939,7 @@ void View::AllocateScreenBuffers()
     }
     
     #ifdef ATOMIC_OPENGL
-    if (deferred_ && !hdrRendering)
+    if (deferred_ && !renderer_->GetHDRRendering())
         format = Graphics::GetRGBAFormat();
     #endif
     

+ 2 - 2
Source/Atomic/Network/Controls.cpp → Source/Atomic/Input/Controls.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
 //
 
 #include "Precompiled.h"
-#include "../Network/Controls.h"
+#include "../Input/Controls.h"
 
 #include "../DebugNew.h"
 

+ 1 - 1
Source/Atomic/Network/Controls.h → Source/Atomic/Input/Controls.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 307 - 58
Source/Atomic/Input/Input.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -41,6 +41,9 @@
 #include <cstring>
 
 #include <SDL/include/SDL.h>
+#ifdef EMSCRIPTEN
+#include <emscripten/html5.h>
+#endif
 
 #include "../DebugNew.h"
 
@@ -77,6 +80,115 @@ UIElement* TouchState::GetTouchedElement()
     return touchedElement_.Get();
 }
 
+#ifdef EMSCRIPTEN
+#define EM_TRUE 1
+
+/// Glue between Urho Input and Emscripten HTML5
+/** HTML5 (Emscripten) is limited in the way it handles input. The EmscriptenInput class attempts to provide the glue between Urho3D Input behavior and HTML5, where SDL currently fails to do so.
+ *
+ * Mouse Input:
+ * - The OS mouse cursor position can't be set.
+ * - The mouse can be trapped within play area via 'PointerLock API', which requires a request and interaction between the user and browser.
+ * - To request mouse lock, call SetMouseMode(MM_RELATIVE). The E_MOUSEMODECHANGED event will be sent if/when the user confirms the request.
+ * NOTE: The request must be initiated by the user (eg: on mouse button down/up, key down/up).
+ * - The user can press 'escape' key and browser will force user out of pointer lock. Urho will send the E_MOUSEMODECHANGED event.
+ * - SetMouseMode(MM_ABSOLUTE) will leave pointer lock.
+ * - MM_WRAP is unsupported.
+ *
+ * Touch Input:
+ */
+/// % Emscripten Input glue. Intended to be used by the Input subsystem only.
+class EmscriptenInput
+{
+public:
+    /// Constructor, expecting pointer to constructing Input instance.
+    EmscriptenInput(Input* inputInst);
+
+    /// Static callback method for Pointer Lock API. Handles change in Pointer Lock state and sends events for mouse mode change.
+    static EM_BOOL PointerLockCallback(int eventType, const EmscriptenPointerlockChangeEvent* keyEvent, void* userData);
+    /// Static callback method for tracking lose focus event.
+    static EM_BOOL LoseFocus(int eventType, const EmscriptenFocusEvent* keyEvent, void* userData);
+    /// Static callback method for tracking gain focus event.
+    static EM_BOOL GainFocus(int eventType, const EmscriptenFocusEvent* keyEvent, void* userData);
+
+    /// Send request to user to gain pointer lock. This requires a user-browser interaction on the first call.
+    void RequestPointerLock();
+    /// Send request to exit pointer lock. This has the benefit of not requiring the user-browser interaction on the next pointer lock request.
+    void ExitPointerLock();
+    /// Returns whether the page is visible.
+    bool IsVisible();
+
+private:
+    /// Instance of Input subsystem that constructed this instance.
+    Input* inputInst_;
+};
+
+EmscriptenInput::EmscriptenInput(Input* inputInst)
+{
+    inputInst_ = inputInst;
+    emscripten_set_pointerlockchange_callback(NULL, (void*)inputInst, false, EmscriptenInput::PointerLockCallback);
+
+    // Handle focus changes:
+    emscripten_set_blur_callback(NULL, (void*)inputInst, false, EmscriptenInput::LoseFocus);
+    emscripten_set_focus_callback(NULL, (void*)inputInst, false, EmscriptenInput::GainFocus);
+}
+
+void EmscriptenInput::RequestPointerLock()
+{
+    emscripten_request_pointerlock(NULL, true);
+}
+
+void EmscriptenInput::ExitPointerLock()
+{
+    inputInst_->emscriptenExitingPointerLock_ = true;
+    emscripten_exit_pointerlock();
+}
+
+bool EmscriptenInput::IsVisible()
+{
+    EmscriptenVisibilityChangeEvent visibilityStatus;
+    if (emscripten_get_visibility_status(&visibilityStatus) >= EMSCRIPTEN_RESULT_SUCCESS)
+        return visibilityStatus.hidden >= EM_TRUE ? false : true;
+
+    // Assume visible
+    LOGWARNING("Could not determine visibility status.");
+    return true;
+}
+
+EM_BOOL EmscriptenInput::PointerLockCallback(int eventType, const EmscriptenPointerlockChangeEvent* keyEvent, void* userData)
+{
+    Input* inputInst = (Input*)userData;
+    if (keyEvent->isActive >= EM_TRUE)
+    {
+        // Pointer Lock is now active
+        inputInst->emscriptenEnteredPointerLock_ = true;
+        inputInst->SetMouseModeEmscripten(MM_RELATIVE);
+    }
+    else
+    {
+        // Pointer Lock is now inactive
+        inputInst->SetMouseModeEmscripten(MM_ABSOLUTE);
+    }
+    return EM_TRUE;
+}
+
+EM_BOOL EmscriptenInput::LoseFocus(int eventType, const EmscriptenFocusEvent* keyEvent, void* userData)
+{
+    Input* inputInst = (Input*)userData;
+    if (eventType == EMSCRIPTEN_EVENT_BLUR)
+        inputInst->LoseFocus();
+    return EM_TRUE;
+}
+
+EM_BOOL EmscriptenInput::GainFocus(int eventType, const EmscriptenFocusEvent* keyEvent, void* userData)
+{
+    Input* inputInst = (Input*)userData;
+    if (eventType == EMSCRIPTEN_EVENT_FOCUS)
+        inputInst->GainFocus();
+    return EM_TRUE;
+}
+#endif
+
 void JoystickState::Initialize(unsigned numButtons, unsigned numAxes, unsigned numHats)
 {
     buttons_.Resize(numButtons);
@@ -111,6 +223,10 @@ Input::Input(Context* context) :
     lastMouseVisible_(false),
     mouseGrabbed_(false),
     mouseMode_(MM_ABSOLUTE),
+#ifdef EMSCRIPTEN
+    emscriptenExitingPointerLock_(false),
+    emscriptenEnteredPointerLock_(false),
+#endif
     lastVisibleMousePosition_(MOUSE_POSITION_OFFSCREEN),
     touchEmulation_(false),
     inputFocus_(false),
@@ -124,12 +240,20 @@ Input::Input(Context* context) :
 
     SubscribeToEvent(E_SCREENMODE, HANDLER(Input, HandleScreenMode));
 
+    #ifdef EMSCRIPTEN
+    emscriptenInput_ = new EmscriptenInput(this);
+    #endif
+
     // Try to initialize right now, but skip if screen mode is not yet set
     Initialize();
 }
 
 Input::~Input()
 {
+    #ifdef EMSCRIPTEN
+    delete emscriptenInput_;
+    emscriptenInput_ = 0;
+    #endif
 }
 
 void Input::Update()
@@ -159,16 +283,48 @@ void Input::Update()
     }
 
     // Check and handle SDL events
+
+    if (!inputFocus_)
+    {
+        // While there is no input focus, don't process key, mouse, touch or joystick events.
+        SDL_PumpEvents();
+        SDL_FlushEvents(SDL_KEYDOWN, SDL_MULTIGESTURE);
+    }
+
     SDL_Event evt;
     while (SDL_PollEvent(&evt))
         HandleSDLEvent(&evt);
 
+    // Check for focus change this frame
+    SDL_Window* window = graphics_->GetImpl()->GetWindow();
+    unsigned flags = window ? SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS) : 0;
+#ifndef EMSCRIPTEN
+    if (window)
+    {
+#if defined(REQUIRE_CLICK_TO_FOCUS)
+        if (!inputFocus_ && (graphics_->GetFullscreen() || mouseVisible_) && flags == (SDL_WINDOW_INPUT_FOCUS |
+            SDL_WINDOW_MOUSE_FOCUS))
+#else
+        if (!inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS))
+#endif
+            focusedThisFrame_ = true;
+
+        if (focusedThisFrame_)
+            GainFocus();
+
+        if (inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS) == 0)
+            LoseFocus();
+    }
+    else
+        return;
+
+    // Handle mouse mode MM_WRAP
     if (mouseVisible_ && mouseMode_ == MM_WRAP)
     {
         IntVector2 mpos;
         SDL_GetMouseState(&mpos.x_, &mpos.y_);
 
-        int buffer = 5;
+        const int buffer = 5;
         int width = graphics_->GetWidth() - buffer * 2;
         int height = graphics_->GetHeight() - buffer * 2;
 
@@ -203,36 +359,27 @@ void Input::Update()
             SDL_FlushEvent(SDL_MOUSEMOTION);
         }
     }
+    #else
+    if (!window)
+        return;
 
-    // Check for activation and inactivation from SDL window flags. Must nullcheck the window pointer because it may have
-    // been closed due to input events
-    SDL_Window* window = graphics_->GetImpl()->GetWindow();
-    unsigned flags = window ? SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS) : 0;
-    if (window)
+    if (emscriptenExitingPointerLock_)
     {
-#ifdef REQUIRE_CLICK_TO_FOCUS
-        if (!inputFocus_ && (graphics_->GetFullscreen() || mouseVisible_) && flags == (SDL_WINDOW_INPUT_FOCUS |
-            SDL_WINDOW_MOUSE_FOCUS))
-#else
-        if (!inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS))
-#endif
-            focusedThisFrame_ = true;
-
-        if (focusedThisFrame_)
-            GainFocus();
-
-        if (inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS) == 0)
-            LoseFocus();
-    }
-    else
+        // Suppress mouse jump when exiting Pointer Lock
+        IntVector2 mousePosition = GetMousePosition();
+        mouseMove_ = IntVector2::ZERO;
+        lastMousePosition_ = lastVisibleMousePosition_;
+        emscriptenExitingPointerLock_ = false;
         return;
+    }
+    #endif
 
     // Check for relative mode mouse move
-    if (!touchEmulation_ && (graphics_->GetExternalWindow() || (!mouseVisible_ && inputFocus_ && (flags & SDL_WINDOW_MOUSE_FOCUS))))
+    if (!touchEmulation_ && mouseMode_ != MM_RELATIVE && (graphics_->GetExternalWindow() || (!mouseVisible_ && inputFocus_ && (flags & SDL_WINDOW_MOUSE_FOCUS))))
     {
         IntVector2 mousePosition = GetMousePosition();
         mouseMove_ = mousePosition - lastMousePosition_;
-
+        #ifndef EMSCRIPTEN
         if (graphics_->GetExternalWindow())
             lastMousePosition_ = mousePosition;
         else
@@ -245,7 +392,12 @@ void Input::Update()
                 lastMousePosition_ = center;
             }
         }
-
+        #else
+        if (mouseMode_ == MM_ABSOLUTE)
+        {
+            lastMousePosition_ = mousePosition;
+        }
+        #endif
         // Send mouse move event if necessary
         if (mouseMove_ != IntVector2::ZERO)
         {
@@ -304,17 +456,25 @@ void Input::SetMouseVisible(bool enable, bool suppressEvent)
             if (!mouseVisible_ && inputFocus_)
             {
                 SDL_ShowCursor(SDL_FALSE);
+                #ifndef EMSCRIPTEN
                 // Recenter the mouse cursor manually when hiding it to avoid erratic mouse move for one frame
                 lastVisibleMousePosition_ = GetMousePosition();
                 IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
                 SetMousePosition(center);
                 lastMousePosition_ = center;
+                #else
+                lastVisibleMousePosition_ = GetMousePosition();
+                lastMousePosition_ = lastVisibleMousePosition_;
+                #endif
             }
             else
             {
                 SDL_ShowCursor(SDL_TRUE);
+                #ifndef EMSCRIPTEN
                 if (lastVisibleMousePosition_.x_ != MOUSE_POSITION_OFFSCREEN.x_ && lastVisibleMousePosition_.y_ != MOUSE_POSITION_OFFSCREEN.y_)
                     SetMousePosition(lastVisibleMousePosition_);
+                lastMousePosition_ = lastVisibleMousePosition_;
+                #endif
             }
         }
 
@@ -334,6 +494,50 @@ void Input::SetMouseVisible(bool enable, bool suppressEvent)
     #endif
 }
 
+void Input::ResetMouseVisible()
+{
+    #ifndef EMSCRIPTEN
+    SetMouseVisible(lastMouseVisible_, true);
+    #else
+    SetMouseVisibleEmscripten(lastMouseVisible_);
+    #endif
+}
+
+#ifdef EMSCRIPTEN
+void Input::SetMouseVisibleEmscripten(bool enable)
+{
+    if (enable != mouseVisible_)
+    {
+        mouseVisible_ = enable;
+        SDL_ShowCursor(enable ? SDL_TRUE : SDL_FALSE);
+
+        if (!mouseVisible_)
+            lastVisibleMousePosition_ = GetMousePosition();
+
+        lastMousePosition_ = lastVisibleMousePosition_;
+    }
+}
+
+void Input::SetMouseModeEmscripten(MouseMode mode)
+{
+    mouseMode_ = mode;
+
+    if (mode == MM_RELATIVE)
+    {
+        SetMouseVisibleEmscripten(false);
+    }
+    else
+    {
+        ResetMouseVisible();
+    }
+    suppressNextMouseMove_ = true;
+
+    VariantMap& eventData = GetEventDataMap();
+    eventData[MouseModeChanged::P_MODE] = mode;
+    SendEvent(E_MOUSEMODECHANGED, eventData);
+}
+#endif
+
 void Input::SetMouseGrabbed(bool grab)
 {
     mouseGrabbed_ = grab;
@@ -345,47 +549,70 @@ void Input::SetMouseMode(MouseMode mode)
     {
         MouseMode previousMode = mouseMode_;
         mouseMode_ = mode;
+        suppressNextMouseMove_ = true;
+        SDL_Window* window = graphics_->GetImpl()->GetWindow();
         // Handle changing away from previous mode
         if (previousMode == MM_RELATIVE)
         {
+            #ifndef EMSCRIPTEN
             /// \todo Use SDL_SetRelativeMouseMode() for MM_RELATIVE mode
             ResetMouseVisible();
+            #else
+            emscriptenInput_->ExitPointerLock();
+            #endif
 
-            // Send updated mouse position:
-            using namespace MouseMove;
-
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_X] = lastVisibleMousePosition_.x_;
-            eventData[P_Y] = lastVisibleMousePosition_.y_;
-            eventData[P_DX] = mouseMove_.x_;
-            eventData[P_DY] = mouseMove_.y_;
-            eventData[P_BUTTONS] = mouseButtonDown_;
-            eventData[P_QUALIFIERS] = GetQualifiers();
-            SendEvent(E_MOUSEMOVE, eventData);
+            SDL_SetWindowGrab(window, SDL_FALSE);
         }
+        #ifndef EMSCRIPTEN
         else if (previousMode == MM_WRAP)
         {
-            SDL_Window* window = graphics_->GetImpl()->GetWindow();
             SDL_SetWindowGrab(window, SDL_FALSE);
         }
+        #endif
 
         // Handle changing to new mode
         if (mode == MM_ABSOLUTE)
+        {
+            #ifndef EMSCRIPTEN
             SetMouseGrabbed(false);
+            VariantMap& eventData = GetEventDataMap();
+            eventData[MouseModeChanged::P_MODE] = mode;
+            SendEvent(E_MOUSEMODECHANGED, eventData);
+            #else
+            // Deferred mouse mode change to PointerLock callback
+            #endif
+        }
         else
         {
             SetMouseGrabbed(true);
 
             if (mode == MM_RELATIVE)
             {
+                SDL_SetWindowGrab(window, SDL_TRUE);
+                #ifndef EMSCRIPTEN
                 SetMouseVisible(false, true);
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[MouseModeChanged::P_MODE] = mode;
+                SendEvent(E_MOUSEMODECHANGED, eventData);
+                #else
+                // Defer mouse mode change to PointerLock callback
+                mouseMode_ = previousMode;
+                emscriptenInput_->RequestPointerLock();
+                #endif
+
             }
+            #ifndef EMSCRIPTEN
             else if (mode == MM_WRAP)
             {
                 /// \todo When SDL 2.0.4 is integrated, use SDL_CaptureMouse() and global mouse functions for MM_WRAP mode.
-                SDL_Window* window = graphics_->GetImpl()->GetWindow();
                 SDL_SetWindowGrab(window, SDL_TRUE);
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[MouseModeChanged::P_MODE] = mode;
+                SendEvent(E_MOUSEMODECHANGED, eventData);
             }
+            #endif
         }
     }
 }
@@ -722,18 +949,19 @@ unsigned Input::LoadGestures(Deserializer& source)
     return SDL_LoadDollarTemplates(-1, wrapper.GetRWOps());
 }
 
+
 bool Input::RemoveGesture(unsigned gestureID)
 {
-#ifndef EMSCRIPTEN
-    return SDL_RemoveDollarTemplate(gestureID) != 0;
-#else
+#if defined(EMSCRIPTEN)
     return false;
+#else
+    return SDL_RemoveDollarTemplate(gestureID) != 0;
 #endif
 }
 
 void Input::RemoveAllGestures()
 {
-#ifndef EMSCRIPTEN
+#if !defined(EMSCRIPTEN)
     SDL_RemoveAllDollarTemplates();
 #endif
 }
@@ -957,8 +1185,16 @@ void Input::Initialize()
         mouseVisible_ = true;
 
     // Set the initial activation
-    focusedThisFrame_ = true;
     initialized_ = true;
+#ifndef EMSCRIPTEN
+    focusedThisFrame_ = true;
+#else
+    // Note: Page visibility and focus are slightly different, however we can't query last focus with Emscripten (1.29.0)
+    if (emscriptenInput_->IsVisible())
+        GainFocus();
+    else
+        LoseFocus();
+#endif
 
     ResetJoysticks();
     ResetState();
@@ -1145,6 +1381,18 @@ void Input::SetMouseButton(int button, bool newState)
     }
 #endif
 
+#ifdef EMSCRIPTEN
+    if (emscriptenEnteredPointerLock_)
+    {
+        // Suppress mouse jump on initial Pointer Lock
+        IntVector2 mousePosition = GetMousePosition();
+        lastMousePosition_ = mousePosition;
+        mouseMove_ = IntVector2::ZERO;
+        suppressNextMouseMove_ = true;
+        emscriptenEnteredPointerLock_ = false;
+    }
+#endif
+
     // If we do not have focus yet, do not react to the mouse button down
     if (!graphics_->GetExternalWindow() && newState && !inputFocus_)
         return;
@@ -1254,21 +1502,22 @@ void Input::HandleSDLEvent(void* sdlEvent)
     switch (evt.type)
     {
     case SDL_KEYDOWN:
-         // Convert to uppercase to match Win32 virtual key codes
- #ifndef EMSCRIPTEN
-         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, true);
- #else
-         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, true);
- #endif
-         break;
-
-     case SDL_KEYUP:
- #ifndef EMSCRIPTEN
-         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, false);
- #else
-         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, false);
- #endif
-         break;
+        // Convert to uppercase to match Win32 virtual key codes
+#if defined (EMSCRIPTEN)
+        SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, true);
+#else
+        SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, true);
+#endif
+        break;
+
+    case SDL_KEYUP:
+#if defined(EMSCRIPTEN)
+        SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, false);
+#else
+        SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, false);
+#endif
+        break;
+
     case SDL_TEXTINPUT:
         {
             textInput_ = &evt.text.text[0];

+ 21 - 2
Source/Atomic/Input/Input.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -120,9 +120,16 @@ struct JoystickState
     PODVector<int> hats_;
 };
 
+#ifdef EMSCRIPTEN
+class EmscriptenInput;
+#endif
+
 /// %Input subsystem. Converts operating system window messages to input state and events.
 class ATOMIC_API Input : public Object
 {
+    #ifdef EMSCRIPTEN
+    friend class EmscriptenInput;
+    #endif
     OBJECT(Input);
 
 public:
@@ -138,7 +145,7 @@ public:
     /// Set whether the operating system mouse cursor is visible. When not visible (default), is kept centered to prevent leaving the window. Mouse visiblility event can be suppressed-- this also recalls any unsuppressed SetMouseVisible which can be returned by ResetMouseVisible().
     void SetMouseVisible(bool enable, bool suppressEvent = false);
     /// Reset last mouse visibilty that was not suppressed in SetMouseVisible.
-    void ResetMouseVisible() { SetMouseVisible(lastMouseVisible_, true); }
+    void ResetMouseVisible();
     /// Set whether the mouse is currently being grabbed by an operation.
     void SetMouseGrabbed(bool grab);
     /// Set the mouse mode.
@@ -288,6 +295,10 @@ private:
     void SetMouseButton(int button, bool newState);
     /// Handle a key change.
     void SetKey(int key, int scancode, unsigned raw, bool newState);
+    #ifdef EMSCRIPTEN
+    void SetMouseVisibleEmscripten(bool enable);
+    void SetMouseModeEmscripten(MouseMode mode);
+    #endif
     /// Handle mousewheel change.
     void SetMouseWheel(int delta);
     /// Internal function to set the mouse cursor position.
@@ -357,6 +368,14 @@ private:
     bool suppressNextMouseMove_;
     /// Initialized flag.
     bool initialized_;
+#ifdef EMSCRIPTEN
+    /// Emscripten Input glue instance.
+    EmscriptenInput* emscriptenInput_;
+    /// Flag used to detect mouse jump when exiting pointer lock.
+    bool emscriptenExitingPointerLock_;
+    /// Flag used to detect mouse jump on initial mouse click when entering pointer lock.
+    bool emscriptenEnteredPointerLock_;
+#endif
 };
 
 }

+ 7 - 1
Source/Atomic/Input/InputEvents.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -211,6 +211,12 @@ EVENT(E_MOUSEVISIBLECHANGED, MouseVisibleChanged)
     PARAM(P_VISIBLE, Visible);              // bool
 }
 
+/// Mouse mode changed.
+EVENT(E_MOUSEMODECHANGED, MouseModeChanged)
+{
+    PARAM(P_MODE, Mode);                    // MouseMode
+}
+
 /// Application exit requested.
 EVENT(E_EXITREQUESTED, ExitRequested)
 {

+ 13 - 3
Source/Atomic/Math/MathDefs.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -58,8 +58,6 @@ enum Intersection
 
 /// Check whether two floating point values are equal within accuracy.
 inline bool Equals(float lhs, float rhs) { return lhs + M_EPSILON >= rhs && lhs - M_EPSILON <= rhs; }
-/// Check whether a floating point value is NaN.
-inline bool IsNaN(float value) { return value != value; }
 /// Linear interpolation between two float values.
 inline float Lerp(float lhs, float rhs, float t) { return lhs * (1.0f - t) + rhs * t; }
 /// Return the smaller of two floats.
@@ -71,6 +69,18 @@ inline float Abs(float value) { return value >= 0.0f ? value : -value; }
 /// Return the sign of a float (-1, 0 or 1.)
 inline float Sign(float value) { return value > 0.0f ? 1.0f : (value < 0.0f ? -1.0f : 0.0f); }
 
+/// Check whether a floating point value is NaN.
+/// Use a workaround for GCC, see https://github.com/urho3d/Urho3D/issues/655
+#ifndef __GNUC__
+inline bool IsNaN(float value) { return value != value; }
+#else
+inline bool IsNaN(float value)
+{
+    unsigned u = *(unsigned*)(&value);
+    return (u & 0x7fffffff) > 0x7f800000;
+}
+#endif
+
 /// Clamp a float to a range.
 inline float Clamp(float value, float min, float max)
 {

+ 2 - 2
Source/Atomic/Network/Connection.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,7 @@
 
 #pragma once
 
-#include "../Network/Controls.h"
+#include "../Input/Controls.h"
 #include "../Container/HashSet.h"
 #include "../Core/Object.h"
 #include "../Scene/ReplicationState.h"

+ 7 - 2
Source/Atomic/Resource/JSONFile.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -83,10 +83,15 @@ bool JSONFile::BeginLoad(Deserializer& source)
 }
 
 bool JSONFile::Save(Serializer& dest) const
+{
+    return Save(dest, "\t");
+}
+
+bool JSONFile::Save(Serializer& dest, const String& indendation) const
 {
     StringBuffer buffer;
     PrettyWriter<StringBuffer> writer(buffer, &(document_->GetAllocator()));
-    writer.SetIndent('\t', 1);
+    writer.SetIndent(!indendation.Empty() ?  indendation.Front() : '\0', indendation.Length());
 
     document_->Accept(writer);
     dest.Write(buffer.GetString(), buffer.GetSize());

+ 4 - 2
Source/Atomic/Resource/JSONFile.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -49,8 +49,10 @@ public:
 
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);
-    /// Save resource. Return true if successful. Only supports saving to a File.
+    /// Save resource with default indentation (one tab). Return true if successful.
     virtual bool Save(Serializer& dest) const;
+    /// Save resource with user-defined indentation, only the first character (if any) of the string is used and the length of the string defines the character count. Return true if successful.
+    bool Save(Serializer& dest, const String& indendation) const;
 
     /// Clear the document and create a root value, default is object type.
     JSONValue CreateRoot(JSONValueType valueType = JSON_OBJECT);

+ 9 - 4
Source/Atomic/Resource/XMLFile.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -133,9 +133,14 @@ bool XMLFile::BeginLoad(Deserializer& source)
 }
 
 bool XMLFile::Save(Serializer& dest) const
+{
+    return Save(dest, "\t");
+}
+
+bool XMLFile::Save(Serializer& dest, const String& indendation) const
 {
     XMLWriter writer(dest);
-    document_->save(writer);
+    document_->save(writer, indendation.CString());
     return writer.success_;
 }
 
@@ -167,11 +172,11 @@ XMLElement XMLFile::GetRoot(const String& name)
         return XMLElement(this, root.internal_object());
 }
 
-String XMLFile::ToString() const
+String XMLFile::ToString(const String& indendation) const
 {
     VectorBuffer dest;
     XMLWriter writer(dest);
-    document_->save(writer);
+    document_->save(writer, indendation.CString());
     return String((const char*)dest.GetData(), dest.GetSize());
 }
 

+ 6 - 4
Source/Atomic/Resource/XMLFile.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -50,9 +50,11 @@ public:
     
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);
-    /// Save resource. Return true if successful. Only supports saving to a File.
+    /// Save resource with default indentation (one tab). Return true if successful.
     virtual bool Save(Serializer& dest) const;
-    
+    /// Save resource with user-defined indentation. Return true if successful.
+    bool Save(Serializer& dest, const String& indendation) const;
+
     /// Deserialize from a string. Return true if successful.
     bool FromString(const String& source);
     /// Clear the document and create a root element.
@@ -63,7 +65,7 @@ public:
     /// Return the pugixml document.
     pugi::xml_document* GetDocument() const { return document_; }
     /// Serialize the XML content to a string.
-    String ToString() const;
+    String ToString(const String& indendation = "\t") const;
 
     /// Patch the XMLFile with another XMLFile. Based on RFC 5261.
     void Patch(XMLFile* patchFile);

+ 2 - 2
Source/Atomic/Scene/LogicComponent.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,7 @@ class ATOMIC_API LogicComponent : public Component
     virtual void Start() {}
     /// Called before the first update. At this point all other components of the node should exist. Will also be called if update events are not wanted; in that case the event is immediately unsubscribed afterward.
     virtual void DelayedStart() {}
-    /// Called when the component is detached from a scene node, usually on destruction.
+    /// Called when the component is detached from a scene node, usually on destruction. Note that you will no longer have access to the node and scene at that point.
     virtual void Stop() {}
     /// Called on scene update, variable timestep.
     virtual void Update(float timeStep);

+ 4 - 9
Source/Atomic/Scene/Node.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -224,14 +224,14 @@ void Node::AddReplicationState(NodeReplicationState* state)
     networkState_->replicationStates_.Push(state);
 }
 
-bool Node::SaveXML(Serializer& dest) const
+bool Node::SaveXML(Serializer& dest, const String& indentation) const
 {
     SharedPtr<XMLFile> xml(new XMLFile(context_));
     XMLElement rootElem = xml->CreateRoot("node");
     if (!SaveXML(rootElem))
         return false;
 
-    return xml->Save(dest);
+    return xml->Save(dest, indentation);
 }
 
 void Node::SetName(const String& name)
@@ -1778,8 +1778,6 @@ Node* Node::CloneRecursive(Node* parent, SceneResolver& resolver, CreateMode mod
 
 void Node::RemoveComponent(Vector<SharedPtr<Component> >::Iterator i)
 {
-    WeakPtr<Component> componentWeak(*i);
-
     // Send node change event. Do not send when already being destroyed
     if (Refs() > 0 && scene_)
     {
@@ -1796,11 +1794,8 @@ void Node::RemoveComponent(Vector<SharedPtr<Component> >::Iterator i)
     RemoveListener(*i);
     if (scene_)
         scene_->ComponentRemoved(*i);
+    (*i)->SetNode(0);
     components_.Erase(i);
-
-    // If the component is still referenced elsewhere, reset its node pointer now
-    if (componentWeak)
-        componentWeak->SetNode(0);
 }
 
 void Node::HandleAttributeAnimationUpdate(StringHash eventType, VariantMap& eventData)

+ 2 - 2
Source/Atomic/Scene/Node.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -85,7 +85,7 @@ public:
     virtual void AddReplicationState(NodeReplicationState* state);
 
     /// Save to an XML file. Return true if successful.
-    bool SaveXML(Serializer& dest) const;
+    bool SaveXML(Serializer& dest, const String& indentation = "\t") const;
     /// Set name of the scene node. Names are not required to be unique.
     void SetName(const String& name);
     /// Set position in parent space. If the scene node is on the root level (is child of the scene itself), this is same as world space.

+ 3 - 3
Source/Atomic/Scene/Scene.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -217,7 +217,7 @@ bool Scene::LoadXML(Deserializer& source)
         return false;
 }
 
-bool Scene::SaveXML(Serializer& dest) const
+bool Scene::SaveXML(Serializer& dest, const String& indentation) const
 {
     PROFILE(SaveSceneXML);
 
@@ -230,7 +230,7 @@ bool Scene::SaveXML(Serializer& dest) const
     if (ptr)
         LOGINFO("Saving scene to " + ptr->GetName());
 
-    if (xml->Save(dest))
+    if (xml->Save(dest, indentation))
     {
         FinishSaving(&dest);
         return true;

+ 2 - 2
Source/Atomic/Scene/Scene.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -103,7 +103,7 @@ public:
     /// Load from an XML file. Return true if successful.
     bool LoadXML(Deserializer& source);
     /// Save to an XML file. Return true if successful.
-    bool SaveXML(Serializer& dest) const;
+    bool SaveXML(Serializer& dest, const String& indentation = "\t") const;
     /// Load from a binary file asynchronously. Return true if started successfully. The LOAD_RESOURCES_ONLY mode can also be used to preload resources from object prefab files.
     bool LoadAsync(File* file, LoadMode mode = LOAD_SCENE_AND_RESOURCES);
     /// Load from an XML file asynchronously. Return true if started successfully. The LOAD_RESOURCES_ONLY mode can also be used to preload resources from object prefab files.

+ 3 - 3
Source/Atomic/UI/Font.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -101,7 +101,7 @@ bool Font::BeginLoad(Deserializer& source)
     return true;
 }
 
-bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
+bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs, const String& indentation)
 {
     FontFace* fontFace = GetFace(pointSize);
     if (!fontFace)
@@ -113,7 +113,7 @@ bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs)
     if (!packedFontFace->Load(fontFace, usedGlyphs))
         return false;
 
-    return packedFontFace->Save(dest, pointSize);
+    return packedFontFace->Save(dest, pointSize, indentation);
 }
 
 void Font::SetAbsoluteGlyphOffset(const IntVector2& offset)

+ 2 - 2
Source/Atomic/UI/Font.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -58,7 +58,7 @@ public:
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);
     /// Save resource as a new bitmap font type in XML format. Return true if successful.
-    bool SaveXML(Serializer& dest, int pointSize, bool usedGlyphs = false);
+    bool SaveXML(Serializer& dest, int pointSize, bool usedGlyphs = false, const String& indentation = "\t");
     /// Set absolute (in pixels) position adjustment for glyphs.
     void SetAbsoluteGlyphOffset(const IntVector2& offset);
     /// Set point size scaled position adjustment for glyphs.

+ 3 - 3
Source/Atomic/UI/FontFaceBitmap.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -262,7 +262,7 @@ bool FontFaceBitmap::Load(FontFace* fontFace, bool usedGlyphs)
     return true;
 }
 
-bool FontFaceBitmap::Save(Serializer& dest, int pointSize)
+bool FontFaceBitmap::Save(Serializer& dest, int pointSize, const String& indentation)
 {
     Context* context = font_->GetContext();
 
@@ -338,7 +338,7 @@ bool FontFaceBitmap::Save(Serializer& dest, int pointSize)
         }
     }
 
-    return xml->Save(dest);
+    return xml->Save(dest, indentation);
 }
 
 unsigned FontFaceBitmap::ConvertFormatToNumComponents(unsigned format)

+ 2 - 2
Source/Atomic/UI/FontFaceBitmap.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -44,7 +44,7 @@ public:
     /// Load from existed font face, pack used glyphs into smallest texture size and smallest number of texture.
     bool Load(FontFace* fontFace, bool usedGlyphs);
     /// Save as a new bitmap font type in XML format. Return true if successful.
-    bool Save(Serializer& dest, int pontSize);
+    bool Save(Serializer& dest, int pointSize, const String& indentation = "\t");
 
 private:
     /// Convert graphics format to number of components.

+ 3 - 3
Source/Atomic/UI/UIElement.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -538,11 +538,11 @@ bool UIElement::LoadXML(Deserializer& source)
     return xml->Load(source) && LoadXML(xml->GetRoot());
 }
 
-bool UIElement::SaveXML(Serializer& dest) const
+bool UIElement::SaveXML(Serializer& dest, const String& indentation) const
 {
     SharedPtr<XMLFile> xml(new XMLFile(context_));
     XMLElement rootElem = xml->CreateRoot("element");
-    return SaveXML(rootElem) && xml->Save(dest);
+    return SaveXML(rootElem) && xml->Save(dest, indentation);
 }
 
 bool UIElement::FilterAttributes(XMLElement& dest) const

+ 2 - 2
Source/Atomic/UI/UIElement.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -183,7 +183,7 @@ public:
     /// Load from an XML file. Return true if successful.
     bool LoadXML(Deserializer& source);
     /// Save to an XML file. Return true if successful.
-    bool SaveXML(Serializer& dest) const;
+    bool SaveXML(Serializer& dest, const String& indentation = "\t") const;
     /// Filter attributes in serialization process.
     bool FilterAttributes(XMLElement& dest) const;
 

+ 4 - 1
Source/ThirdParty/rapidjson/include/rapidjson/prettywriter.h

@@ -1,6 +1,8 @@
 #ifndef RAPIDJSON_PRETTYWRITER_H_
 #define RAPIDJSON_PRETTYWRITER_H_
 
+// Modified by Ali Kamarainen for Urho3D
+
 #include "writer.h"
 
 namespace rapidjson {
@@ -31,7 +33,8 @@ public:
 		\note The default indentation is 4 spaces.
 	*/
 	PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
-		RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
+		// Urho3D: in order to be consistent witht the XMLFile API, allow any character.
+		// RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
 		indentChar_ = indentChar;
 		indentCharCount_ = indentCharCount;
 		return *this;