Fixed multitouch input.
Module is now exported with global functions hidden via MODULARIZE
as global object OccViewerModule created by createOccViewerModule().
Global Module setup has been moved to occt-webgl-viewer.js.
Use EMSCRIPTEN_KEEPALIVE attribute istead of listing C functions via EXTRA_EXPORTED_RUNTIME_METHODS.
WasmOcctView now exports static methods as Module functions using EMSCRIPTEN_BINDINGS.
Standard_ASSERT_DBGBREAK_() is now defined using emscripten_debugger().
-D ENABLE_VISIBILITY:BOOL="TRUE" \
-D CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS:BOOL="OFF" \
-D CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS:BOOL="OFF" \
- -D CMAKE_BUILD_TYPE:STRING="Release" \
+ -D CMAKE_BUILD_TYPE:STRING="$aBuildType" \
-D BUILD_LIBRARY_TYPE:STRING="$aLibType" \
-D INSTALL_DIR:PATH="$aDestDir" \
-D INSTALL_DIR_INCLUDE:STRING="inc" \
set "toMake=1"
set "toInstall=1"
set "toPack=0"
+set "toDebug=0"
set "toBuildSample=0"
+set "sourceMapBase="
rem OCCT Modules to build
set "BUILD_ModelingData=ON"
for /f tokens^=2^ delims^=^" %%i in ('findstr /b /c:"#define OCC_VERSION_COMPLETE" "%aCasSrc%\src\Standard\Standard_Version.hxx"') do ( set "anOcctVersion=%%i" )
for /f %%i in ('git symbolic-ref --short HEAD') do ( set "aGitBranch=%%i" )
+set "aBuildType=Release"
+set "aBuildTypePrefix="
+if ["%toDebug%"] == ["1"] (
+ set "aBuildType=Debug"
+ set "aBuildTypePrefix=-debug"
+)
+
call :cmakeGenerate
if errorlevel 1 (
if not ["%1"] == ["-nopause"] (
set MONTH00=%MONTH00:~-2%
set "aRevision=-%YEAR%-%MONTH00%-%DAY00%"
rem set "aRevision=-%aGitBranch%"
-set "anArchName=occt-%anOcctVersion%%anOcctVerSuffix%%aRevision%-wasm32"
+set "anArchName=occt-%anOcctVersion%%anOcctVerSuffix%%aRevision%-wasm32%aBuildTypePrefix%"
set "aTarget=%aBuildRoot%\%anArchName%"
if ["%toPack%"] == ["1"] (
echo Creating archive %anArchName%.7z
goto :eof
:cmakeGenerate
-set "aPlatformAndCompiler=wasm32"
-set "aWorkDir=%aBuildRoot%\%aPlatformAndCompiler%-make"
-set "aDestDir=%aBuildRoot%\%aPlatformAndCompiler%"
-set "aLogFile=%aBuildRoot%\build-%aPlatformAndCompiler%.log"
+set "aPlatformAndCompiler=wasm32%aBuildTypePrefix%"
+set "aWorkDir=%aBuildRoot%\occt-%aPlatformAndCompiler%-make"
+set "aDestDir=%aBuildRoot%\occt-%aPlatformAndCompiler%"
+set "aLogFile=%aBuildRoot%\occt-%aPlatformAndCompiler%-build.log"
if ["%toCMake%"] == ["1"] (
if ["%toClean%"] == ["1"] (
rmdir /S /Q %aWorkDir%"
if exist "%aLogFile%" ( del "%aLogFile%" )
set "aSrcRootSmpl=%aCasSrc%\samples\webgl"
-set "aWorkDirSmpl=%aBuildRoot%\%aPlatformAndCompiler%-sample-make"
-set "aDestDirSmpl=%aBuildRoot%\%aPlatformAndCompiler%-sample"
-set "aLogFileSmpl=%aBuildRoot%\build-%aPlatformAndCompiler%-sample.log"
+set "aWorkDirSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%-make"
+set "aDestDirSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%"
+set "aLogFileSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%-build.log"
if ["%toBuildSample%"] == ["1"] (
if ["%toCMake%"] == ["1"] (
if ["%toClean%"] == ["1"] (
echo Configuring OCCT for WASM...
cmake -G "MinGW Makefiles" ^
-D CMAKE_TOOLCHAIN_FILE:FILEPATH="%aToolchain%" ^
- -D CMAKE_BUILD_TYPE:STRING="Release" ^
+ -D CMAKE_BUILD_TYPE:STRING="%aBuildType%" ^
-D BUILD_LIBRARY_TYPE:STRING="Static" ^
-D INSTALL_DIR:PATH="%aDestDir%" ^
-D INSTALL_DIR_INCLUDE:STRING="inc" ^
if ["%toCMake%"] == ["1"] (
cmake -G "MinGW Makefiles" ^
-D CMAKE_TOOLCHAIN_FILE:FILEPATH="%aToolchain%" ^
- -D CMAKE_BUILD_TYPE:STRING="Release" ^
+ -D CMAKE_BUILD_TYPE:STRING="%aBuildType%" ^
-D CMAKE_INSTALL_PREFIX:PATH="%aDestDirSmpl%" ^
+ -D SOURCE_MAP_BASE:STRING="%sourceMapBase%" ^
-D OpenCASCADE_DIR:PATH="%aDestDir%/lib/cmake/opencascade" ^
-D freetype_DIR:PATH="%aFreeType%/lib/cmake/freetype" ^
"%aSrcRootSmpl%"
rem Uncomment to customize building steps
rem set "aBuildRoot=work"
rem set "toCMake=1"
-rem set "toClean=0"
+rem set "toClean=1"
rem set "toMake=1"
rem set "toInstall=1"
-rem set "toPack=0"
+rem set "toPack=1"
+rem set "toDebug=1"
rem set "toBuildSample=1"
+rem Source map base (should point to server where C++ sources will be copied)
+rem enables -g4 debug building option for WebGL sample and allows navigating C++ source code within JavaScript debugger.
+rem set "sourceMapBase=http://localhost:9090/"
rem set "BUILD_ModelingData=ON"
rem set "BUILD_ModelingAlgorithms=ON"
set "toClean=0"
set "toMake=1"
set "toInstall=1"
+set "toDebug=0"
+set "sourceMapBase="
rem Configuration file
if exist "%~dp0wasm_custom.bat" call "%~dp0wasm_custom.bat"
set "aToolchain=%EMSDK%/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
if not ["%aCmakeBin%"] == [""] ( set "PATH=%aCmakeBin%;%PATH%" )
+set "aBuildType=Release"
+set "aBuildTypePrefix="
+if ["%toDebug%"] == ["1"] (
+ set "aBuildType=Debug"
+ set "aBuildTypePrefix=-debug"
+)
+
call :cmakeGenerate
if not ["%1"] == ["-nopause"] (
pause
goto :eof
:cmakeGenerate
-set "aPlatformAndCompiler=wasm32"
-set "aDestDirOcct=%aBuildRoot%\%aPlatformAndCompiler%"
+set "aPlatformAndCompiler=wasm32%aBuildTypePrefix%"
+set "aDestDirOcct=%aBuildRoot%\occt-%aPlatformAndCompiler%"
set "aSrcRootSmpl=%aCasSrc%\samples\webgl"
-set "aWorkDirSmpl=%aBuildRoot%\%aPlatformAndCompiler%-sample-make"
-set "aDestDirSmpl=%aBuildRoot%\%aPlatformAndCompiler%-sample"
-set "aLogFileSmpl=%aBuildRoot%\build-%aPlatformAndCompiler%-sample.log"
+set "aWorkDirSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%-make"
+set "aDestDirSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%"
+set "aLogFileSmpl=%aBuildRoot%\sample-%aPlatformAndCompiler%-build.log"
if ["%toCMake%"] == ["1"] (
if ["%toClean%"] == ["1"] (
rmdir /S /Q %aWorkDirSmpl%"
if ["%toCMake%"] == ["1"] (
cmake -G "MinGW Makefiles" ^
-D CMAKE_TOOLCHAIN_FILE:FILEPATH="%aToolchain%" ^
- -D CMAKE_BUILD_TYPE:STRING="Release" ^
+ -D CMAKE_BUILD_TYPE:STRING="%aBuildType%" ^
-D CMAKE_INSTALL_PREFIX:PATH="%aDestDirSmpl%" ^
+ -D SOURCE_MAP_BASE:STRING="%sourceMapBase%" ^
-D OpenCASCADE_DIR:PATH="%aDestDirOcct%/lib/cmake/opencascade" ^
-D freetype_DIR:PATH="%aFreeType%/lib/cmake/freetype" ^
"%aSrcRootSmpl%"
set(APP_VERSION_MINOR 0)
set(APP_TARGET occt-webgl-sample)
+# option to enable or disable use of precompiled headers
+if (NOT DEFINED SOURCE_MAP_BASE)
+ set (SOURCE_MAP_BASE "" CACHE STRING "Path to source map server for debugging C++ code; e.g. http://localhost:9090/")
+endif()
+
# customize build
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_WEBGL2=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --bind")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SAFE_HEAP=1")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s TOTAL_MEMORY=16MB")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ABORTING_MALLOC=0")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FORCE_FILESYSTEM=1")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --preload-file myFile")
+if (NOT "${SOURCE_MAP_BASE}" STREQUAL "")
+ set(CMAKE_CXX_FLAGS_DEBUG "-g4 --source-map-base ${SOURCE_MAP_BASE}")
+endif()
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s MODULARIZE=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='createOccViewerModule'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --extern-post-js ${CMAKE_CURRENT_SOURCE_DIR}/occt-webgl-viewer.js")
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
file(GLOB SOURCES
*.cpp
)
source_group ("Headers" FILES
- WasmOcctView.h)
+ WasmOcctView.h
+ WasmOcctPixMap.h)
source_group ("Sources" FILES
WasmOcctView.cpp
+ WasmOcctPixMap.cpp
main.cpp)
# FreeType
${OpenCASCADE_LIBS}
freetype
)
-set_target_properties(${APP_TARGET} PROPERTIES LINK_FLAGS "-s EXPORTED_FUNCTIONS=['_main','_onFileDataRead'] -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wasm DESTINATION ${CMAKE_INSTALL_PREFIX})
+if (NOT "${SOURCE_MAP_BASE}" STREQUAL "")
+ if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wasm.map DESTINATION ${CMAKE_INSTALL_PREFIX})
+ endif()
+endif()
install(FILES occt-webgl-sample.html DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${OpenCASCADE_RESOURCE_DIR}/DrawResources/OCC_logo.png DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${OpenCASCADE_RESOURCE_DIR}/DrawResources/lamp.ico DESTINATION ${CMAKE_INSTALL_PREFIX})
--- /dev/null
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of the examples of the Open CASCADE Technology software library.
+//
+// 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:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// 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
+
+#include "WasmOcctPixMap.h"
+
+#include <Message.hxx>
+
+#include <emscripten.h>
+
+// ================================================================
+// Function : WasmOcctPixMap
+// Purpose :
+// ================================================================
+WasmOcctPixMap::WasmOcctPixMap()
+: myRawDataPtr (nullptr) {}
+
+// ================================================================
+// Function : ~WasmOcctPixMap
+// Purpose :
+// ================================================================
+WasmOcctPixMap::~WasmOcctPixMap()
+{
+ Clear();
+}
+
+// ================================================================
+// Function : Clear
+// Purpose :
+// ================================================================
+void WasmOcctPixMap::Clear()
+{
+ if (myRawDataPtr != nullptr) { free (myRawDataPtr); }
+ myRawDataPtr = nullptr;
+ Image_PixMap::Clear();
+}
+
+// ================================================================
+// Function : Init
+// Purpose :
+// ================================================================
+bool WasmOcctPixMap::Init (const char* theFilePath)
+{
+ Clear();
+ int aSizeX = 0, aSizeY = 0;
+ char* anImgData = emscripten_get_preloaded_image_data (theFilePath, &aSizeX, &aSizeY);
+ if (anImgData == nullptr)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: invalid image ") + theFilePath, Message_Fail);
+ return false;
+ }
+
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded image ") + theFilePath + "@" + aSizeX + "x" + aSizeY, Message_Info);
+ InitWrapper (Image_Format_RGBA, (Standard_Byte* )anImgData, aSizeX, aSizeY);
+ SetTopDown (true);
+ myRawDataPtr = anImgData;
+ return true;
+}
--- /dev/null
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of the examples of the Open CASCADE Technology software library.
+//
+// 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:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// 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
+
+#ifndef _WasmOcctPixMap_HeaderFile
+#define _WasmOcctPixMap_HeaderFile
+
+#include <Image_PixMap.hxx>
+
+//! Image pixmap loading image using Emscripten.
+class WasmOcctPixMap : public Image_PixMap
+{
+public:
+ //! Empty constructor.
+ WasmOcctPixMap();
+
+ //! Destructor.
+ virtual ~WasmOcctPixMap();
+
+ //! Load RGBA pixmap using emscripten_get_preloaded_image_data() from the given path.
+ bool Init (const char* theFilePath);
+
+ //! Release memory.
+ virtual void Clear() override;
+
+private:
+ char* myRawDataPtr;
+};
+
+#endif // _WasmOcctPixMap_HeaderFile
#include "WasmOcctView.h"
#include "WasmVKeys.h"
+#include "WasmOcctPixMap.h"
#include <AIS_Shape.hxx>
#include <AIS_ViewCube.hxx>
#include <Aspect_NeutralWindow.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
+#include <Graphic3d_CubeMapPacked.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <Prs3d_DatumAspect.hxx>
+#include <Prs3d_ToolCylinder.hxx>
+#include <Prs3d_ToolDisk.hxx>
+
+#include <BRep_Builder.hxx>
+#include <BRepBndLib.hxx>
+#include <BRepTools.hxx>
+#include <Standard_ArrayStreamBuffer.hxx>
+
+#include <emscripten/bind.h>
#include <iostream>
namespace
{
EM_JS(int, jsCanvasGetWidth, (), {
- return canvas.width;
+ return Module.canvas.width;
});
EM_JS(int, jsCanvasGetHeight, (), {
- return canvas.height;
+ return Module.canvas.height;
});
EM_JS(float, jsDevicePixelRatio, (), {
{
return Graphic3d_Vec2i (jsCanvasGetWidth(), jsCanvasGetHeight());
}
+
+ //! Auxiliary wrapper for loading model.
+ struct ModelAsyncLoader
+ {
+ std::string Name;
+ std::string Path;
+
+ ModelAsyncLoader (const char* theName, const char* thePath)
+ : Name (theName), Path (thePath) {}
+
+ //! File data read event.
+ static void onDataRead (void* theOpaque, void* theBuffer, int theDataLen)
+ {
+ const ModelAsyncLoader* aTask = (ModelAsyncLoader* )theOpaque;
+ WasmOcctView::openFromMemory (aTask->Name, reinterpret_cast<uintptr_t>(theBuffer), theDataLen, false);
+ delete aTask;
+ }
+
+ //! File read error event.
+ static void onReadFailed (void* theOpaque)
+ {
+ const ModelAsyncLoader* aTask = (ModelAsyncLoader* )theOpaque;
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load file ") + aTask->Path.c_str(), Message_Fail);
+ delete aTask;
+ }
+ };
+
+ //! Auxiliary wrapper for loading cubemap.
+ struct CubemapAsyncLoader
+ {
+ //! Image file read event.
+ static void onImageRead (const char* theFilePath)
+ {
+ Handle(Graphic3d_CubeMapPacked) aCubemap;
+ Handle(WasmOcctPixMap) anImage = new WasmOcctPixMap();
+ if (anImage->Init (theFilePath))
+ {
+ aCubemap = new Graphic3d_CubeMapPacked (anImage);
+ }
+ WasmOcctView::Instance().View()->SetBackgroundCubeMap (aCubemap, true, false);
+ WasmOcctView::Instance().UpdateView();
+ }
+
+ //! Image file failed read event.
+ static void onImageFailed (const char* theFilePath)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load image ") + theFilePath, Message_Fail);
+ }
+ };
+}
+
+// ================================================================
+// Function : Instance
+// Purpose :
+// ================================================================
+WasmOcctView& WasmOcctView::Instance()
+{
+ static WasmOcctView aViewer;
+ return aViewer;
}
// ================================================================
aViewer->SetDefaultShadingModel (Graphic3d_TOSM_FRAGMENT);
aViewer->SetDefaultLights();
aViewer->SetLightOn();
+ for (V3d_ListOfLight::Iterator aLightIter (aViewer->ActiveLights()); aLightIter.More(); aLightIter.Next())
+ {
+ const Handle(V3d_Light)& aLight = aLightIter.Value();
+ if (aLight->Type() == Graphic3d_TOLS_DIRECTIONAL)
+ {
+ aLight->SetCastShadows (true);
+ }
+ }
Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow();
Graphic3d_Vec2i aWinSize = jsCanvasSize();
myTextStyle->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
myView = new V3d_View (aViewer);
+ myView->Camera()->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
myView->SetImmediateUpdate (false);
+ myView->ChangeRenderingParams().IsShadowEnabled = false;
myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
myView->ChangeRenderingParams().ToShowStats = true;
myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect();
// Build with "--preload-file MySampleFile.brep" option to load some shapes here.
}
+// ================================================================
+// Function : UpdateView
+// Purpose :
+// ================================================================
+void WasmOcctView::UpdateView()
+{
+ if (!myView.IsNull())
+ {
+ myView->Invalidate();
+ updateView();
+ }
+}
// ================================================================
// Function : updateView
}
const Standard_Size aTouchId = (Standard_Size )aTouch.identifier;
- const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (aTouch.canvasX, aTouch.canvasY));
+ const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (aTouch.targetX, aTouch.targetY));
switch (theEventType)
{
case EMSCRIPTEN_EVENT_TOUCHSTART:
}
return EM_FALSE;
}
+
+// ================================================================
+// Function : setCubemapBackground
+// Purpose :
+// ================================================================
+void WasmOcctView::setCubemapBackground (const std::string& theImagePath)
+{
+ if (!theImagePath.empty())
+ {
+ emscripten_async_wget (theImagePath.c_str(), "/emulated/cubemap.jpg", CubemapAsyncLoader::onImageRead, CubemapAsyncLoader::onImageFailed);
+ }
+ else
+ {
+ WasmOcctView::Instance().View()->SetBackgroundCubeMap (Handle(Graphic3d_CubeMapPacked)(), true, false);
+ WasmOcctView::Instance().UpdateView();
+ }
+}
+
+// ================================================================
+// Function : fitAllObjects
+// Purpose :
+// ================================================================
+void WasmOcctView::fitAllObjects (bool theAuto)
+{
+ WasmOcctView& aViewer = Instance();
+ if (theAuto)
+ {
+ aViewer.FitAllAuto (aViewer.Context(), aViewer.View());
+ }
+ else
+ {
+ aViewer.View()->FitAll (0.01, false);
+ }
+ aViewer.UpdateView();
+}
+
+// ================================================================
+// Function : removeAllObjects
+// Purpose :
+// ================================================================
+void WasmOcctView::removeAllObjects()
+{
+ WasmOcctView& aViewer = Instance();
+ for (NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)>::Iterator anObjIter (aViewer.myObjects);
+ anObjIter.More(); anObjIter.Next())
+ {
+ aViewer.Context()->Remove (anObjIter.Value(), false);
+ }
+ aViewer.myObjects.Clear();
+ aViewer.UpdateView();
+}
+
+// ================================================================
+// Function : removeObject
+// Purpose :
+// ================================================================
+bool WasmOcctView::removeObject (const std::string& theName)
+{
+ WasmOcctView& aViewer = Instance();
+ Handle(AIS_InteractiveObject) anObj;
+ if (!theName.empty()
+ && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+ {
+ return false;
+ }
+
+ aViewer.Context()->Remove (anObj, false);
+ aViewer.myObjects.RemoveKey (theName.c_str());
+ aViewer.UpdateView();
+ return true;
+}
+
+// ================================================================
+// Function : eraseObject
+// Purpose :
+// ================================================================
+bool WasmOcctView::eraseObject (const std::string& theName)
+{
+ WasmOcctView& aViewer = Instance();
+ Handle(AIS_InteractiveObject) anObj;
+ if (!theName.empty()
+ && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+ {
+ return false;
+ }
+
+ aViewer.Context()->Erase (anObj, false);
+ aViewer.UpdateView();
+ return true;
+}
+
+// ================================================================
+// Function : displayObject
+// Purpose :
+// ================================================================
+bool WasmOcctView::displayObject (const std::string& theName)
+{
+ WasmOcctView& aViewer = Instance();
+ Handle(AIS_InteractiveObject) anObj;
+ if (!theName.empty()
+ && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+ {
+ return false;
+ }
+
+ aViewer.Context()->Display (anObj, false);
+ aViewer.UpdateView();
+ return true;
+}
+
+// ================================================================
+// Function : openFromUrl
+// Purpose :
+// ================================================================
+void WasmOcctView::openFromUrl (const std::string& theName,
+ const std::string& theModelPath)
+{
+ ModelAsyncLoader* aTask = new ModelAsyncLoader (theName.c_str(), theModelPath.c_str());
+ emscripten_async_wget_data (theModelPath.c_str(), (void* )aTask, ModelAsyncLoader::onDataRead, ModelAsyncLoader::onReadFailed);
+}
+
+// ================================================================
+// Function : openFromMemory
+// Purpose :
+// ================================================================
+bool WasmOcctView::openFromMemory (const std::string& theName,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree)
+{
+ removeObject (theName);
+ char* aBytes = reinterpret_cast<char*>(theBuffer);
+ if (aBytes == nullptr
+ || theDataLen <= 0)
+ {
+ return false;
+ }
+
+ // Function to check if specified data stream starts with specified header.
+ #define dataStartsWithHeader(theData, theHeader) (::strncmp(theData, theHeader, sizeof(theHeader) - 1) == 0)
+
+ if (dataStartsWithHeader(aBytes, "DBRep_DrawableShape"))
+ {
+ return openBRepFromMemory (theName, theBuffer, theDataLen, theToFree);
+ }
+ else if (dataStartsWithHeader(aBytes, "glTF"))
+ {
+ //return openGltfFromMemory (theName, theBuffer, theDataLen, theToFree);
+ }
+ if (theToFree)
+ {
+ free (aBytes);
+ }
+
+ Message::SendFail() << "Error: file '" << theName.c_str() << "' has unsupported format";
+ return false;
+}
+
+// ================================================================
+// Function : openBRepFromMemory
+// Purpose :
+// ================================================================
+bool WasmOcctView::openBRepFromMemory (const std::string& theName,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree)
+{
+ removeObject (theName);
+
+ WasmOcctView& aViewer = Instance();
+ TopoDS_Shape aShape;
+ BRep_Builder aBuilder;
+ bool isLoaded = false;
+ {
+ char* aRawData = reinterpret_cast<char*>(theBuffer);
+ Standard_ArrayStreamBuffer aStreamBuffer (aRawData, theDataLen);
+ std::istream aStream (&aStreamBuffer);
+ BRepTools::Read (aShape, aStream, aBuilder);
+ if (theToFree)
+ {
+ free (aRawData);
+ }
+ isLoaded = true;
+ }
+ if (!isLoaded)
+ {
+ return false;
+ }
+
+ Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
+ if (!theName.empty())
+ {
+ aViewer.myObjects.Add (theName.c_str(), aShapePrs);
+ }
+ aShapePrs->SetMaterial (Graphic3d_NameOfMaterial_Silver);
+ aViewer.Context()->Display (aShapePrs, AIS_Shaded, 0, false);
+ aViewer.View()->FitAll (0.01, false);
+ aViewer.UpdateView();
+
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + theName.c_str(), Message_Info);
+ Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
+ return true;
+}
+
+// ================================================================
+// Function : displayGround
+// Purpose :
+// ================================================================
+void WasmOcctView::displayGround (bool theToShow)
+{
+ static Handle(AIS_Shape) aGroundPrs = new AIS_Shape (TopoDS_Shape());
+
+ WasmOcctView& aViewer = Instance();
+ Bnd_Box aBox;
+ if (theToShow)
+ {
+ aViewer.Context()->Remove (aGroundPrs, false);
+ aBox = aViewer.View()->View()->MinMaxValues();
+ }
+ if (aBox.IsVoid()
+ || aBox.IsZThin (Precision::Confusion()))
+ {
+ if (!aGroundPrs.IsNull()
+ && aGroundPrs->HasInteractiveContext())
+ {
+ aViewer.Context()->Remove (aGroundPrs, false);
+ aViewer.UpdateView();
+ }
+ return;
+ }
+
+ const gp_XYZ aSize = aBox.CornerMax().XYZ() - aBox.CornerMin().XYZ();
+ const double aRadius = Max (aSize.X(), aSize.Y());
+ const double aZValue = aBox.CornerMin().Z() - Min (10.0, aSize.Z() * 0.01);
+ const double aZSize = aRadius * 0.01;
+ gp_XYZ aGroundCenter ((aBox.CornerMin().X() + aBox.CornerMax().X()) * 0.5,
+ (aBox.CornerMin().Y() + aBox.CornerMax().Y()) * 0.5,
+ aZValue);
+
+ TopoDS_Compound aGround;
+ gp_Trsf aTrsf1, aTrsf2;
+ aTrsf1.SetTranslation (gp_Vec (aGroundCenter - gp_XYZ(0.0, 0.0, aZSize)));
+ aTrsf2.SetTranslation (gp_Vec (aGroundCenter));
+ Prs3d_ToolCylinder aCylTool (aRadius, aRadius, aZSize, 50, 1);
+ Prs3d_ToolDisk aDiskTool (0.0, aRadius, 50, 1);
+ TopoDS_Face aCylFace, aDiskFace1, aDiskFace2;
+ BRep_Builder().MakeFace (aCylFace, aCylTool .CreatePolyTriangulation (aTrsf1));
+ BRep_Builder().MakeFace (aDiskFace1, aDiskTool.CreatePolyTriangulation (aTrsf1));
+ BRep_Builder().MakeFace (aDiskFace2, aDiskTool.CreatePolyTriangulation (aTrsf2));
+
+ BRep_Builder().MakeCompound (aGround);
+ BRep_Builder().Add (aGround, aCylFace);
+ BRep_Builder().Add (aGround, aDiskFace1);
+ BRep_Builder().Add (aGround, aDiskFace2);
+
+ aGroundPrs->SetShape (aGround);
+ aGroundPrs->SetToUpdate();
+ aGroundPrs->SetMaterial (Graphic3d_NameOfMaterial_Stone);
+ aGroundPrs->SetInfiniteState (false);
+ aViewer.Context()->Display (aGroundPrs, AIS_Shaded, -1, false);
+ aGroundPrs->SetInfiniteState (true);
+ aViewer.UpdateView();
+}
+
+// Module exports
+EMSCRIPTEN_BINDINGS(OccViewerModule) {
+ emscripten::function("setCubemapBackground", &WasmOcctView::setCubemapBackground);
+ emscripten::function("fitAllObjects", &WasmOcctView::fitAllObjects);
+ emscripten::function("removeAllObjects", &WasmOcctView::removeAllObjects);
+ emscripten::function("removeObject", &WasmOcctView::removeObject);
+ emscripten::function("eraseObject", &WasmOcctView::eraseObject);
+ emscripten::function("displayObject", &WasmOcctView::displayObject);
+ emscripten::function("displayGround", &WasmOcctView::displayGround);
+ emscripten::function("openFromUrl", &WasmOcctView::openFromUrl);
+ emscripten::function("openFromMemory", &WasmOcctView::openFromMemory, emscripten::allow_raw_pointers());
+ emscripten::function("openBRepFromMemory", &WasmOcctView::openBRepFromMemory, emscripten::allow_raw_pointers());
+}
class WasmOcctView : protected AIS_ViewController
{
public:
+
+ //! Return global viewer instance.
+ static WasmOcctView& Instance();
+
+public: //! @name methods exported by Module
+
+ //! Set cubemap background.
+ //! File will be loaded asynchronously.
+ //! @param theImagePath [in] image path to load
+ static void setCubemapBackground (const std::string& theImagePath);
+
+ //! Clear all named objects from viewer.
+ static void removeAllObjects();
+
+ //! Fit all/selected objects into view.
+ //! @param theAuto [in] fit selected objects (TRUE) or all objects (FALSE)
+ static void fitAllObjects (bool theAuto);
+
+ //! Remove named object from viewer.
+ //! @param theName [in] object name
+ //! @return FALSE if object was not found
+ static bool removeObject (const std::string& theName);
+
+ //! Temporarily hide named object.
+ //! @param theName [in] object name
+ //! @return FALSE if object was not found
+ static bool eraseObject (const std::string& theName);
+
+ //! Display temporarily hidden object.
+ //! @param theName [in] object name
+ //! @return FALSE if object was not found
+ static bool displayObject (const std::string& theName);
+
+ //! Show/hide ground.
+ //! @param theToShow [in] show or hide flag
+ static void displayGround (bool theToShow);
+
+ //! Open object from the given URL.
+ //! File will be loaded asynchronously.
+ //! @param theName [in] object name
+ //! @param theModelPath [in] model path
+ static void openFromUrl (const std::string& theName,
+ const std::string& theModelPath);
+
+ //! Open object from memory.
+ //! @param theName [in] object name
+ //! @param theBuffer [in] pointer to data
+ //! @param theDataLen [in] data length
+ //! @param theToFree [in] free theBuffer if set to TRUE
+ //! @return FALSE on reading error
+ static bool openFromMemory (const std::string& theName,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree);
+
+ //! Open BRep object from memory.
+ //! @param theName [in] object name
+ //! @param theBuffer [in] pointer to data
+ //! @param theDataLen [in] data length
+ //! @param theToFree [in] free theBuffer if set to TRUE
+ //! @return FALSE on reading error
+ static bool openBRepFromMemory (const std::string& theName,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree);
+
+public:
+
//! Default constructor.
WasmOcctView();
//! Return device pixel ratio for handling high DPI displays.
float DevicePixelRatio() const { return myDevicePixelRatio; }
+ //! Request view redrawing.
+ void UpdateView();
+
private:
//! Create window.
private:
+ NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)> myObjects; //!< map of named objects
+
Handle(AIS_InteractiveContext) myContext; //!< interactive context
Handle(V3d_View) myView; //!< 3D view
Handle(Prs3d_TextAspect) myTextStyle; //!< text style for OSD elements
#include <OSD_MemInfo.hxx>
#include <OSD_Parallel.hxx>
-#include <AIS_Shape.hxx>
-#include <BRepTools.hxx>
-#include <BRep_Builder.hxx>
-#include <Standard_ArrayStreamBuffer.hxx>
-
#include <emscripten.h>
#include <emscripten/html5.h>
-//! Global viewer instance.
-static WasmOcctView aViewer;
-
//! Dummy main loop callback for a single shot.
extern "C" void onMainLoop()
{
emscripten_cancel_main_loop();
}
-//! File data read event.
-extern "C" void onFileDataRead (void* theOpaque, void* theBuffer, int theDataLen)
-{
- const char* aName = theOpaque != NULL ? (const char* )theOpaque : "";
- {
- AIS_ListOfInteractive aShapes;
- aViewer.Context()->DisplayedObjects (AIS_KOI_Shape, -1, aShapes);
- for (AIS_ListOfInteractive::Iterator aShapeIter (aShapes); aShapeIter.More(); aShapeIter.Next())
- {
- aViewer.Context()->Remove (aShapeIter.Value(), false);
- }
- }
-
- Standard_ArrayStreamBuffer aStreamBuffer ((const char* )theBuffer, theDataLen);
- std::istream aStream (&aStreamBuffer);
- TopoDS_Shape aShape;
- BRep_Builder aBuilder;
- BRepTools::Read (aShape, aStream, aBuilder);
-
- Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
- aShapePrs->SetMaterial (Graphic3d_NameOfMaterial_Silver);
- aViewer.Context()->Display (aShapePrs, AIS_Shaded, 0, false);
- aViewer.View()->FitAll (0.01, false);
- aViewer.View()->Redraw();
- Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + aName, Message_Info);
- Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
-}
-
-//! File read error event.
-static void onFileReadFailed (void* theOpaque)
-{
- const char* aName = (const char* )theOpaque;
- Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load file ") + aName, Message_Fail);
-}
-
-int main()
+EMSCRIPTEN_KEEPALIVE int main()
{
Message::DefaultMessenger()->Printers().First()->SetTraceLevel (Message_Trace);
Handle(Message_PrinterSystemLog) aJSConsolePrinter = new Message_PrinterSystemLog ("webgl-sample", Message_Trace);
// setup a dummy single-shot main loop callback just to shut up a useless Emscripten error message on calling eglSwapInterval()
emscripten_set_main_loop (onMainLoop, -1, 0);
+ WasmOcctView& aViewer = WasmOcctView::Instance();
aViewer.run();
Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
-
- // load some file
- emscripten_async_wget_data ("samples/Ball.brep", (void* )"samples/Ball.brep", onFileDataRead, onFileReadFailed);
return 0;
}
\r
<h2>OCCT WebGL Viewer Sample</h2>\r
<div>\r
- <canvas id=canvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>\r
+ <canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>\r
<img id=occlogo src="OCC_logo.png" style="position: absolute; left: 20px; top: 0px; z-index: 2;" />\r
</div>\r
\r
-<div><label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep"></div>\r
+<div>\r
+ <label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep">\r
+ <input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects()">\r
+ <input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true)">\r
+</div>\r
<h4>Console output:</h4>\r
<p id="output"></p>\r
<script>\r
var aSizeY = Math.min (window.innerHeight, window.screen.availHeight);\r
aSizeX = Math.max (300, aSizeX - 30);\r
aSizeY = Math.max (300, aSizeY / 2);\r
- canvas.style.width = aSizeX + "px";\r
- canvas.style.height = aSizeY + "px";\r
+ occViewerCanvas.style.width = aSizeX + "px";\r
+ occViewerCanvas.style.height = aSizeY + "px";\r
\r
// drawing buffer size (aka backing store)\r
var aDevicePixelRatio = window.devicePixelRatio || 1;\r
- canvas.width = aSizeX * aDevicePixelRatio;\r
- canvas.height = aSizeY * aDevicePixelRatio;\r
+ occViewerCanvas.width = aSizeX * aDevicePixelRatio;\r
+ occViewerCanvas.height = aSizeY * aDevicePixelRatio;\r
\r
occlogo.style.top = (aSizeY - 30) + "px";\r
}\r
anElement.innerHTML += "Browser is too old - WebAssembly support is missing!<br>Please check updates or install a modern browser.<br>";\r
}\r
\r
-//! Define OCCT WebGL Viewer module.\r
-var Module =\r
-{\r
- print: (function() {\r
- var anElement = document.getElementById('output');\r
- return function(theText) { anElement.innerHTML += theText + "<br>"; };\r
- })(),\r
- printErr: function(theText) {\r
- //var anElement = document.getElementById('output');\r
- //anElement.innerHTML += theText + "<br>";\r
- console.warn(theText);\r
- },\r
- canvas: (function() {\r
- var aCanvas = document.getElementById('canvas');\r
- return aCanvas;\r
- })()\r
-};\r
-\r
//! Handle file uploading.\r
fileInput.onchange = function()\r
{\r
var aReader = new FileReader();\r
aReader.onload = function()\r
{\r
- var aNameLenBytes = lengthBytesUTF8(aFile.name) + 1;\r
- const aNameBuffer = Module._malloc(aNameLenBytes);\r
- stringToUTF8(aFile.name, aNameBuffer, aNameLenBytes);\r
-\r
var aDataArray = new Uint8Array (aReader.result);\r
- const aDataBuffer = Module._malloc(aDataArray.length);\r
- Module.HEAPU8.set(aDataArray, aDataBuffer);\r
- Module.ccall('onFileDataRead', null, ['number', 'number', 'number'], [aNameBuffer, aDataBuffer, aDataArray.length]);\r
- Module._free(aDataBuffer);\r
- Module._free(aNameBuffer);\r
- fileInput.value = '';\r
+ const aDataBuffer = OccViewerModule._malloc (aDataArray.length);\r
+ OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);\r
+ OccViewerModule.openFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);\r
+ //OccViewerModule._free (aDataBuffer); will be freed by called method\r
+ OccViewerModule.displayGround (true);\r
};\r
aReader.readAsArrayBuffer(aFile);\r
};\r
--- /dev/null
+var OccViewerModule =\r
+{\r
+ print: (function() {\r
+ var anElement = document.getElementById('output');\r
+ return function(theText) { anElement.innerHTML += theText + "<br>"; };\r
+ })(),\r
+ printErr: function(theText) {\r
+ //var anElement = document.getElementById('output');\r
+ //anElement.innerHTML += theText + "<br>";\r
+ console.warn(theText);\r
+ },\r
+ canvas: (function() {\r
+ var aCanvas = document.getElementById('occViewerCanvas');\r
+ var aGlCtx = aCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } );\r
+ if (aGlCtx == null) { aGlCtx = aCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } ); }\r
+ return aCanvas;\r
+ })(),\r
+\r
+ onRuntimeInitialized: function() {\r
+ //console.log(" @@ onRuntimeInitialized()" + Object.getOwnPropertyNames(OccViewerModule));\r
+ }\r
+};\r
+\r
+const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);\r
+OccViewerModuleInitialized.then(function(Module) {\r
+ //OccViewerModule.setCubemapBackground ("cubemap.jpg");\r
+ OccViewerModule.openFromUrl ("ball", "samples/Ball.brep");\r
+});\r
#include <windows.h>
#define Standard_ASSERT_DBGBREAK_() DebugBreak()
#endif
+ #elif defined(__EMSCRIPTEN__)
+ #include <emscripten.h>
+ #define Standard_ASSERT_DBGBREAK_() emscripten_debugger()
#else
// POSIX systems
#include <signal.h>