1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-03 17:56:21 +03:00

0032065: Samples - use MODULARIZE within WebGL sample

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().
This commit is contained in:
kgv 2021-01-18 20:50:52 +03:00 committed by bugmaster
parent 96049f2e3d
commit 5de4b704fe
14 changed files with 675 additions and 105 deletions

View File

@ -110,7 +110,7 @@ if [[ $toCMake == 1 ]]; then
-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" \

View File

@ -20,7 +20,9 @@ set "toClean=0"
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"
@ -50,6 +52,13 @@ for /f tokens^=2^ delims^=^" %%i in ('findstr /b /c:"#define OCC_VERSION_DEVELOP
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"] (
@ -72,7 +81,7 @@ set DAY00=%DAY00:~-2%
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
@ -95,10 +104,10 @@ if not ["%1"] == ["-nopause"] (
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%"
@ -109,9 +118,9 @@ if not exist "%aWorkDir%" ( mkdir "%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"] (
@ -139,7 +148,7 @@ if ["%toCMake%"] == ["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" ^
@ -226,8 +235,9 @@ pushd "%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="%aDestDir%/lib/cmake/opencascade" ^
-D freetype_DIR:PATH="%aFreeType%/lib/cmake/freetype" ^
"%aSrcRootSmpl%"

View File

@ -7,11 +7,15 @@ rem set "aCmakeBin=%ProgramW6432%\CMake\bin"
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"

View File

@ -18,6 +18,8 @@ set "toCMake=1"
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"
@ -26,6 +28,13 @@ call "%EMSDK_ROOT%\emsdk_env.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
@ -34,12 +43,12 @@ if not ["%1"] == ["-nopause"] (
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%"
@ -54,8 +63,9 @@ pushd "%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%"

View File

@ -7,16 +7,29 @@ set(APP_VERSION_MAJOR 1)
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
@ -24,9 +37,11 @@ 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
@ -57,10 +72,14 @@ target_link_libraries(
${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})

View File

@ -0,0 +1,75 @@
// 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;
}

View File

@ -0,0 +1,47 @@
// 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

View File

@ -22,6 +22,7 @@
#include "WasmOcctView.h"
#include "WasmVKeys.h"
#include "WasmOcctPixMap.h"
#include <AIS_Shape.hxx>
#include <AIS_ViewCube.hxx>
@ -30,8 +31,18 @@
#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>
@ -40,11 +51,11 @@
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, (), {
@ -57,6 +68,65 @@ namespace
{
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;
}
// ================================================================
@ -239,6 +309,14 @@ bool WasmOcctView::initViewer()
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();
@ -260,7 +338,9 @@ bool WasmOcctView::initViewer()
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();
@ -301,6 +381,18 @@ void WasmOcctView::initDemoScene()
// 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
@ -536,7 +628,7 @@ EM_BOOL WasmOcctView::onTouchEvent (int theEventType, const EmscriptenTouchEvent
}
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:
@ -677,3 +769,278 @@ EM_BOOL WasmOcctView::onKeyUpEvent (int theEventType, const EmscriptenKeyboardEv
}
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());
}

View File

@ -35,6 +35,72 @@ class AIS_ViewCube;
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();
@ -53,6 +119,9 @@ public:
//! Return device pixel ratio for handling high DPI displays.
float DevicePixelRatio() const { return myDevicePixelRatio; }
//! Request view redrawing.
void UpdateView();
private:
//! Create window.
@ -142,6 +211,8 @@ private:
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

View File

@ -8,17 +8,9 @@
#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()
{
@ -26,42 +18,7 @@ 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);
@ -71,10 +28,8 @@ int main()
// 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;
}

View File

@ -9,11 +9,15 @@
<h2>OCCT WebGL Viewer Sample</h2>
<div>
<canvas id=canvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
<canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
<img id=occlogo src="OCC_logo.png" style="position: absolute; left: 20px; top: 0px; z-index: 2;" />
</div>
<div><label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep"></div>
<div>
<label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep">
<input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects()">
<input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true)">
</div>
<h4>Console output:</h4>
<p id="output"></p>
<script>
@ -25,13 +29,13 @@ function updateCanvasSize()
var aSizeY = Math.min (window.innerHeight, window.screen.availHeight);
aSizeX = Math.max (300, aSizeX - 30);
aSizeY = Math.max (300, aSizeY / 2);
canvas.style.width = aSizeX + "px";
canvas.style.height = aSizeY + "px";
occViewerCanvas.style.width = aSizeX + "px";
occViewerCanvas.style.height = aSizeY + "px";
// drawing buffer size (aka backing store)
var aDevicePixelRatio = window.devicePixelRatio || 1;
canvas.width = aSizeX * aDevicePixelRatio;
canvas.height = aSizeY * aDevicePixelRatio;
occViewerCanvas.width = aSizeX * aDevicePixelRatio;
occViewerCanvas.height = aSizeY * aDevicePixelRatio;
occlogo.style.top = (aSizeY - 30) + "px";
}
@ -59,24 +63,6 @@ if (!isWasmSupported())
anElement.innerHTML += "Browser is too old - WebAssembly support is missing!<br>Please check updates or install a modern browser.<br>";
}
//! Define OCCT WebGL Viewer module.
var Module =
{
print: (function() {
var anElement = document.getElementById('output');
return function(theText) { anElement.innerHTML += theText + "<br>"; };
})(),
printErr: function(theText) {
//var anElement = document.getElementById('output');
//anElement.innerHTML += theText + "<br>";
console.warn(theText);
},
canvas: (function() {
var aCanvas = document.getElementById('canvas');
return aCanvas;
})()
};
//! Handle file uploading.
fileInput.onchange = function()
{
@ -86,17 +72,12 @@ fileInput.onchange = function()
var aReader = new FileReader();
aReader.onload = function()
{
var aNameLenBytes = lengthBytesUTF8(aFile.name) + 1;
const aNameBuffer = Module._malloc(aNameLenBytes);
stringToUTF8(aFile.name, aNameBuffer, aNameLenBytes);
var aDataArray = new Uint8Array (aReader.result);
const aDataBuffer = Module._malloc(aDataArray.length);
Module.HEAPU8.set(aDataArray, aDataBuffer);
Module.ccall('onFileDataRead', null, ['number', 'number', 'number'], [aNameBuffer, aDataBuffer, aDataArray.length]);
Module._free(aDataBuffer);
Module._free(aNameBuffer);
fileInput.value = '';
const aDataBuffer = OccViewerModule._malloc (aDataArray.length);
OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);
OccViewerModule.openFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);
//OccViewerModule._free (aDataBuffer); will be freed by called method
OccViewerModule.displayGround (true);
};
aReader.readAsArrayBuffer(aFile);
};

View File

@ -0,0 +1,28 @@
var OccViewerModule =
{
print: (function() {
var anElement = document.getElementById('output');
return function(theText) { anElement.innerHTML += theText + "<br>"; };
})(),
printErr: function(theText) {
//var anElement = document.getElementById('output');
//anElement.innerHTML += theText + "<br>";
console.warn(theText);
},
canvas: (function() {
var aCanvas = document.getElementById('occViewerCanvas');
var aGlCtx = aCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } );
if (aGlCtx == null) { aGlCtx = aCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } ); }
return aCanvas;
})(),
onRuntimeInitialized: function() {
//console.log(" @@ onRuntimeInitialized()" + Object.getOwnPropertyNames(OccViewerModule));
}
};
const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);
OccViewerModuleInitialized.then(function(Module) {
//OccViewerModule.setCubemapBackground ("cubemap.jpg");
OccViewerModule.openFromUrl ("ball", "samples/Ball.brep");
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -80,6 +80,9 @@ inline void Standard_ASSERT_DO_NOTHING() {}
#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>