1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-09-08 14:17:06 +03:00

0031070: Configuration - fix building issues when using Emscripten toolchain

Handled __EMSCRIPTEN__ macros to:
- Workaround atomics (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 is undefined, but GCC atomics are provided).
- Suppress non-standard header <sys/signal.h> warning.
- Return OSD_LinuxREDHAT.
- Avoid inclusion of XLib headers.
- Skip fontconfig library.
- Enable EGL+GLES path (translated by Emscripten into WebGL).
- Skip eglCreatePbufferSurface() not implemented by Emscripten EGL.

Fixed Graphic3d_Vec4.hxx usage within Quantity_ColorRGBA.hxx.

OpenGl_ShaderManager::defaultGlslVersion() now prefers GLSL 300 es when WebGL 2.0 is available,
as there no any OpenGL ES greater than 3.0 emulation so far.

Shaders_Declarations.glsl - added workaround for GLSL compilation on WebGL 1.0
by defining Light properties accessors as macros instead of functions
('[]' : Index expression must be constant).

OpenGl_FrameBuffer::Init() - added workaround for initialization of GL_DEPTH24_STENCIL8
depth-stencil attachment on WebGL 1.0 + GL_WEBGL_depth_texture extension.

OpenGl_Context::Vec4FromQuantityColor() now considers myIsSRgbActive flag
to handle use case, when Immediate Layer is drawn directly into window buffer,
which is not sRGB-ready.

Added new sample - OCCT WebGL viewer.
This commit is contained in:
kgv
2019-10-16 02:42:33 +03:00
committed by apn
parent 36e28f96f6
commit 565baee64b
37 changed files with 1639 additions and 124 deletions

2
samples/webgl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/work

View File

@@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.2)
project(occt-webgl-sample)
set(CMAKE_CXX_STANDARD 11)
set(APP_VERSION_MAJOR 1)
set(APP_VERSION_MINOR 0)
set(APP_TARGET occt-webgl-sample)
# 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} -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")
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
file(GLOB SOURCES
*.h
*.cpp
)
source_group ("Headers" FILES
WasmOcctView.h)
source_group ("Sources" FILES
WasmOcctView.cpp
main.cpp)
# FreeType
find_package(freetype REQUIRED NO_DEFAULT_PATH)
if(freetype_FOUND)
message (STATUS "Using FreeType from \"${freetype_DIR}\"" )
else()
message(WARNING "Could not find FreeType, please set freetype_DIR variable." )
endif()
# Open CASCADE Technology
find_package(OpenCASCADE REQUIRED NO_DEFAULT_PATH)
if(OpenCASCADE_FOUND)
message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_DIR}\"" )
INCLUDE_DIRECTORIES(${OpenCASCADE_INCLUDE_DIR})
LINK_DIRECTORIES(${OpenCASCADE_LIBRARY_DIR})
else()
message(WARNING "Could not find OpenCASCADE, please set OpenCASCADE_DIR variable." )
set(OCCT_LIBRARY_DIR)
set(OCCT_BIN_DIR)
endif()
set(OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel)
add_executable(${APP_TARGET} ${SOURCES})
target_link_libraries(
${APP_TARGET}
${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})
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})

28
samples/webgl/ReadMe.md Normal file
View File

@@ -0,0 +1,28 @@
OCCT WebGL Viewer sample {#occt_samples_webgl}
==================
This sample demonstrates simple way of using OCCT libraries in Web application written in C++ and translated into WebAssembly module using Emscripten SDK (emsdk):
https://emscripten.org/
Sample consists of the Open CASCADE 3D Viewer with a button for opening a model in BREP format.
The sample requires a WebGL 2.0 capable browser supporting WebAssembly 1.0 (Wasm).
Installation and configuration:
1. Install Emscripten SDK and activate minimal configuration (Python, Java and CLang) following *emsdk* documentation. Activate also MinGW when building sample on Windows host.
2. Build (using *emsdk*) or download FreeType static library.
3. Configure CMake for building Open CASCADE Technology (OCCT) static libraries (BUILD_LIBRARY_TYPE="Static").
For this, activate *emsdk* command prompt, configure CMake for building OCCT using cross-compilation toolchain, disable *BUILD_MODULE_Draw*.
4. Perform building and installation steps.
~~~~~
> ${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake
~~~~~
5. Configure CMake for building this WebGL sample using *emsdk* with paths to OCCT and FreeType. Perform building and installation steps.
6. Copy data/occ/Ball.brep from OCCT into "samples" folder within WebGL sample installation path.
7. Navigate to installation folder and start web server from it; Python coming with *emsdk* can be used for this purpose:
~~~~~
> python -m SimpleHTTPServer 8080
~~~~~
8. Open compatible browser and enter path taking into account your web server settings:
~~~~~
> http://localhost:8080/occt-webgl-sample.html
~~~~~

View File

@@ -0,0 +1,678 @@
// Copyright (c) 2019 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 "WasmOcctView.h"
#include "WasmVKeys.h"
#include <AIS_Shape.hxx>
#include <AIS_ViewCube.hxx>
#include <Aspect_Handle.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <Aspect_NeutralWindow.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <Prs3d_DatumAspect.hxx>
#include <iostream>
#define THE_CANVAS_ID "canvas"
namespace
{
EM_JS(int, jsCanvasGetWidth, (), {
return canvas.width;
});
EM_JS(int, jsCanvasGetHeight, (), {
return canvas.height;
});
EM_JS(float, jsDevicePixelRatio, (), {
var aDevicePixelRatio = window.devicePixelRatio || 1;
return aDevicePixelRatio;
});
//! Return cavas size in pixels.
static Graphic3d_Vec2i jsCanvasSize()
{
return Graphic3d_Vec2i (jsCanvasGetWidth(), jsCanvasGetHeight());
}
}
// ================================================================
// Function : WasmOcctView
// Purpose :
// ================================================================
WasmOcctView::WasmOcctView()
: myDevicePixelRatio (1.0f),
myUpdateRequests (0)
{
}
// ================================================================
// Function : ~WasmOcctView
// Purpose :
// ================================================================
WasmOcctView::~WasmOcctView()
{
}
// ================================================================
// Function : run
// Purpose :
// ================================================================
void WasmOcctView::run()
{
initWindow();
initViewer();
initDemoScene();
if (myView.IsNull())
{
return;
}
myView->MustBeResized();
myView->Redraw();
// There is no inifinite message loop, main() will return from here immediately.
// Tell that our Module should be left loaded and handle events through callbacks.
//emscripten_set_main_loop (redrawView, 60, 1);
//emscripten_set_main_loop (redrawView, -1, 1);
EM_ASM(Module['noExitRuntime'] = true);
}
// ================================================================
// Function : initWindow
// Purpose :
// ================================================================
void WasmOcctView::initWindow()
{
myDevicePixelRatio = jsDevicePixelRatio();
myCanvasId = THE_CANVAS_ID;
const char* aTargetId = !myCanvasId.IsEmpty() ? myCanvasId.ToCString() : NULL;
const EM_BOOL toUseCapture = EM_TRUE;
emscripten_set_resize_callback (NULL, this, toUseCapture, onResizeCallback);
emscripten_set_mousedown_callback (NULL, this, toUseCapture, onMouseCallback);
emscripten_set_mouseup_callback (NULL, this, toUseCapture, onMouseCallback);
emscripten_set_mousemove_callback (NULL, this, toUseCapture, onMouseCallback);
emscripten_set_dblclick_callback (aTargetId, this, toUseCapture, onMouseCallback);
emscripten_set_click_callback (aTargetId, this, toUseCapture, onMouseCallback);
emscripten_set_mouseenter_callback (aTargetId, this, toUseCapture, onMouseCallback);
emscripten_set_mouseleave_callback (aTargetId, this, toUseCapture, onMouseCallback);
emscripten_set_wheel_callback (aTargetId, this, toUseCapture, onWheelCallback);
emscripten_set_touchstart_callback (aTargetId, this, toUseCapture, onTouchCallback);
emscripten_set_touchend_callback (aTargetId, this, toUseCapture, onTouchCallback);
emscripten_set_touchmove_callback (aTargetId, this, toUseCapture, onTouchCallback);
emscripten_set_touchcancel_callback(aTargetId, this, toUseCapture, onTouchCallback);
//emscripten_set_keypress_callback (NULL, this, toUseCapture, onKeyCallback);
emscripten_set_keydown_callback (NULL, this, toUseCapture, onKeyDownCallback);
emscripten_set_keyup_callback (NULL, this, toUseCapture, onKeyUpCallback);
}
// ================================================================
// Function : dumpGlInfo
// Purpose :
// ================================================================
void WasmOcctView::dumpGlInfo (bool theIsBasic)
{
TColStd_IndexedDataMapOfStringString aGlCapsDict;
myView->DiagnosticInformation (aGlCapsDict, theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
if (theIsBasic)
{
TCollection_AsciiString aViewport;
aGlCapsDict.FindFromKey ("Viewport", aViewport);
aGlCapsDict.Clear();
aGlCapsDict.Add ("Viewport", aViewport);
}
aGlCapsDict.Add ("Display scale", TCollection_AsciiString(myDevicePixelRatio));
// beautify output
{
TCollection_AsciiString* aGlVer = aGlCapsDict.ChangeSeek ("GLversion");
TCollection_AsciiString* aGlslVer = aGlCapsDict.ChangeSeek ("GLSLversion");
if (aGlVer != NULL
&& aGlslVer != NULL)
{
*aGlVer = *aGlVer + " [GLSL: " + *aGlslVer + "]";
aGlslVer->Clear();
}
}
TCollection_AsciiString anInfo;
for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter (aGlCapsDict); aValueIter.More(); aValueIter.Next())
{
if (!aValueIter.Value().IsEmpty())
{
if (!anInfo.IsEmpty())
{
anInfo += "\n";
}
anInfo += aValueIter.Key() + ": " + aValueIter.Value();
}
}
::Message::DefaultMessenger()->Send (anInfo, Message_Warning);
}
// ================================================================
// Function : initPixelScaleRatio
// Purpose :
// ================================================================
void WasmOcctView::initPixelScaleRatio()
{
SetTouchToleranceScale (myDevicePixelRatio);
if (!myView.IsNull())
{
myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
}
if (!myContext.IsNull())
{
myContext->SetPixelTolerance (int(myDevicePixelRatio * 6.0));
if (!myViewCube.IsNull())
{
static const double THE_CUBE_SIZE = 60.0;
myViewCube->SetSize (myDevicePixelRatio * THE_CUBE_SIZE, false);
myViewCube->SetBoxFacetExtension (myViewCube->Size() * 0.15);
myViewCube->SetAxesPadding (myViewCube->Size() * 0.10);
myViewCube->SetFontHeight (THE_CUBE_SIZE * 0.16);
if (myViewCube->HasInteractiveContext())
{
myContext->Redisplay (myViewCube, false);
}
}
}
}
// ================================================================
// Function : initViewer
// Purpose :
// ================================================================
bool WasmOcctView::initViewer()
{
// Build with "--preload-file MyFontFile.ttf" option
// and register font in Font Manager to use custom font(s).
/*const char* aFontPath = "MyFontFile.ttf";
if (Handle(Font_SystemFont) aFont = Font_FontMgr::GetInstance()->CheckFont (aFontPath))
{
Font_FontMgr::GetInstance()->RegisterFont (aFont, true);
}
else
{
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: font '") + aFontPath + "' is not found", Message_Fail);
}*/
Handle(Aspect_DisplayConnection) aDisp;
Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (aDisp, false);
aDriver->ChangeOptions().buffersNoSwap = true; // swap has no effect in WebGL
if (!aDriver->InitContext())
{
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: EGL initialization failed"), Message_Fail);
return false;
}
Handle(V3d_Viewer) aViewer = new V3d_Viewer (aDriver);
aViewer->SetComputedMode (false);
aViewer->SetDefaultShadingModel (Graphic3d_TOSM_FRAGMENT);
aViewer->SetDefaultLights();
aViewer->SetLightOn();
Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow();
Graphic3d_Vec2i aWinSize = jsCanvasSize();
if (aWinSize.x() < 10 || aWinSize.y() < 10)
{
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning);
}
aWindow->SetSize (aWinSize.x(), aWinSize.y());
myTextStyle = new Prs3d_TextAspect();
myTextStyle->SetFont (Font_NOF_ASCII_MONO);
myTextStyle->SetHeight (12);
myTextStyle->Aspect()->SetColor (Quantity_NOC_GRAY95);
myTextStyle->Aspect()->SetColorSubTitle (Quantity_NOC_BLACK);
myTextStyle->Aspect()->SetDisplayType (Aspect_TODT_SHADOW);
myTextStyle->Aspect()->SetTextFontAspect (Font_FA_Bold);
myTextStyle->Aspect()->SetTextZoomable (false);
myTextStyle->SetHorizontalJustification (Graphic3d_HTA_LEFT);
myTextStyle->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
myView = new V3d_View (aViewer);
myView->SetImmediateUpdate (false);
myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
myView->ChangeRenderingParams().ToShowStats = true;
myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect();
myView->ChangeRenderingParams().StatsTextHeight = (int )myTextStyle->Height();
myView->SetWindow (aWindow);
dumpGlInfo (false);
myContext = new AIS_InteractiveContext (aViewer);
initPixelScaleRatio();
return true;
}
// ================================================================
// Function : initDemoScene
// Purpose :
// ================================================================
void WasmOcctView::initDemoScene()
{
if (myContext.IsNull())
{
return;
}
//myView->TriedronDisplay (Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_WIREFRAME);
myViewCube = new AIS_ViewCube();
// presentation parameters
initPixelScaleRatio();
myViewCube->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_RIGHT_LOWER, Graphic3d_Vec2i (100, 100)));
myViewCube->Attributes()->SetDatumAspect (new Prs3d_DatumAspect());
myViewCube->Attributes()->DatumAspect()->SetTextAspect (myTextStyle);
// animation parameters
myViewCube->SetViewAnimation (myViewAnimation);
myViewCube->SetFixedAnimationLoop (false);
myViewCube->SetAutoStartAnimation (true);
myContext->Display (myViewCube, false);
// Build with "--preload-file MySampleFile.brep" option to load some shapes here.
}
// ================================================================
// Function : updateView
// Purpose :
// ================================================================
void WasmOcctView::updateView()
{
if (!myView.IsNull())
{
if (++myUpdateRequests == 1)
{
emscripten_async_call (onRedrawView, this, 0);
}
}
}
// ================================================================
// Function : redrawView
// Purpose :
// ================================================================
void WasmOcctView::redrawView()
{
if (!myView.IsNull())
{
FlushViewEvents (myContext, myView, true);
}
}
// ================================================================
// Function : handleViewRedraw
// Purpose :
// ================================================================
void WasmOcctView::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
const Handle(V3d_View)& theView)
{
myUpdateRequests = 0;
AIS_ViewController::handleViewRedraw (theCtx, theView);
if (myToAskNextFrame)
{
// ask more frames
++myUpdateRequests;
emscripten_async_call (onRedrawView, this, 0);
}
}
// ================================================================
// Function : onResizeEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onResizeEvent (int theEventType, const EmscriptenUiEvent* theEvent)
{
(void )theEventType; // EMSCRIPTEN_EVENT_RESIZE or EMSCRIPTEN_EVENT_CANVASRESIZED
(void )theEvent;
if (myView.IsNull())
{
return EM_FALSE;
}
Handle(Aspect_NeutralWindow) aWindow = Handle(Aspect_NeutralWindow)::DownCast (myView->Window());
Graphic3d_Vec2i aWinSizeOld, aWinSizeNew (jsCanvasSize());
if (aWinSizeNew.x() < 10 || aWinSizeNew.y() < 10)
{
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning);
}
aWindow->Size (aWinSizeOld.x(), aWinSizeOld.y());
const float aPixelRatio = jsDevicePixelRatio();
if (aWinSizeNew != aWinSizeOld
|| aPixelRatio != myDevicePixelRatio)
{
if (myDevicePixelRatio != aPixelRatio)
{
myDevicePixelRatio = aPixelRatio;
initPixelScaleRatio();
}
aWindow->SetSize (aWinSizeNew.x(), aWinSizeNew.y());
myView->MustBeResized();
myView->Invalidate();
myView->Redraw();
dumpGlInfo (true);
}
return EM_TRUE;
}
// ================================================================
// Function : onMouseEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onMouseEvent (int theEventType, const EmscriptenMouseEvent* theEvent)
{
if (myView.IsNull())
{
return EM_FALSE;
}
Graphic3d_Vec2i aWinSize;
myView->Window()->Size (aWinSize.x(), aWinSize.y());
const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->canvasX, theEvent->canvasY));
Aspect_VKeyFlags aFlags = 0;
if (theEvent->ctrlKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_CTRL; }
if (theEvent->shiftKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_SHIFT; }
if (theEvent->altKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_ALT; }
if (theEvent->metaKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_META; }
const bool isEmulated = false;
const Aspect_VKeyMouse aButtons = WasmVKeys_MouseButtonsFromNative (theEvent->buttons);
switch (theEventType)
{
case EMSCRIPTEN_EVENT_MOUSEMOVE:
{
if ((aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
|| aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
&& PressedMouseButtons() == Aspect_VKeyMouse_NONE)
{
return EM_FALSE;
}
if (UpdateMousePosition (aNewPos, aButtons, aFlags, isEmulated))
{
updateView();
}
break;
}
case EMSCRIPTEN_EVENT_MOUSEDOWN:
case EMSCRIPTEN_EVENT_MOUSEUP:
{
if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
|| aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
{
return EM_FALSE;
}
if (UpdateMouseButtons (aNewPos, aButtons, aFlags, isEmulated))
{
updateView();
}
break;
}
case EMSCRIPTEN_EVENT_CLICK:
case EMSCRIPTEN_EVENT_DBLCLICK:
{
if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
|| aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
{
return EM_FALSE;
}
break;
}
case EMSCRIPTEN_EVENT_MOUSEENTER:
{
break;
}
case EMSCRIPTEN_EVENT_MOUSELEAVE:
{
// there is no SetCapture() support, so that mouse unclick events outside canvas will not arrive,
// so we have to forget current state...
if (UpdateMouseButtons (aNewPos, Aspect_VKeyMouse_NONE, aFlags, isEmulated))
{
updateView();
}
break;
}
}
return EM_TRUE;
}
// ================================================================
// Function : onWheelEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onWheelEvent (int theEventType, const EmscriptenWheelEvent* theEvent)
{
if (myView.IsNull()
|| theEventType != EMSCRIPTEN_EVENT_WHEEL)
{
return EM_FALSE;
}
Graphic3d_Vec2i aWinSize;
myView->Window()->Size (aWinSize.x(), aWinSize.y());
const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->mouse.canvasX, theEvent->mouse.canvasY));
if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
|| aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
{
return EM_FALSE;
}
double aDelta = 0.0;
switch (theEvent->deltaMode)
{
case DOM_DELTA_PIXEL:
{
aDelta = theEvent->deltaY / (5.0 * myDevicePixelRatio);
break;
}
case DOM_DELTA_LINE:
{
aDelta = theEvent->deltaY * 8.0;
break;
}
case DOM_DELTA_PAGE:
{
aDelta = theEvent->deltaY >= 0.0 ? 24.0 : -24.0;
break;
}
}
if (UpdateZoom (Aspect_ScrollDelta (aNewPos, -aDelta)))
{
updateView();
}
return EM_TRUE;
}
// ================================================================
// Function : onTouchEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onTouchEvent (int theEventType, const EmscriptenTouchEvent* theEvent)
{
const double aClickTolerance = 5.0;
if (myView.IsNull())
{
return EM_FALSE;
}
Graphic3d_Vec2i aWinSize;
myView->Window()->Size (aWinSize.x(), aWinSize.y());
bool hasUpdates = false;
for (int aTouchIter = 0; aTouchIter < theEvent->numTouches; ++aTouchIter)
{
const EmscriptenTouchPoint& aTouch = theEvent->touches[aTouchIter];
if (!aTouch.isChanged)
{
continue;
}
const Standard_Size aTouchId = (Standard_Size )aTouch.identifier;
const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (aTouch.canvasX, aTouch.canvasY));
switch (theEventType)
{
case EMSCRIPTEN_EVENT_TOUCHSTART:
{
if (aNewPos.x() >= 0 && aNewPos.x() < aWinSize.x()
&& aNewPos.y() >= 0 && aNewPos.y() < aWinSize.y())
{
hasUpdates = true;
AddTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos));
myClickTouch.From.SetValues (-1.0, -1.0);
if (myTouchPoints.Extent() == 1)
{
myClickTouch.From = Graphic3d_Vec2d (aNewPos);
}
}
break;
}
case EMSCRIPTEN_EVENT_TOUCHMOVE:
{
const int anOldIndex = myTouchPoints.FindIndex (aTouchId);
if (anOldIndex != 0)
{
hasUpdates = true;
UpdateTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos));
if (myTouchPoints.Extent() == 1
&& (myClickTouch.From - Graphic3d_Vec2d (aNewPos)).cwiseAbs().maxComp() > aClickTolerance)
{
myClickTouch.From.SetValues (-1.0, -1.0);
}
}
break;
}
case EMSCRIPTEN_EVENT_TOUCHEND:
case EMSCRIPTEN_EVENT_TOUCHCANCEL:
{
if (RemoveTouchPoint (aTouchId))
{
if (myTouchPoints.IsEmpty()
&& myClickTouch.From.minComp() >= 0.0)
{
if (myDoubleTapTimer.IsStarted()
&& myDoubleTapTimer.ElapsedTime() <= myMouseDoubleClickInt)
{
myView->FitAll (0.01, false);
myView->Invalidate();
}
else
{
myDoubleTapTimer.Stop();
myDoubleTapTimer.Reset();
myDoubleTapTimer.Start();
SelectInViewer (Graphic3d_Vec2i (myClickTouch.From), false);
}
}
hasUpdates = true;
}
break;
}
}
}
if (hasUpdates)
{
updateView();
}
return hasUpdates || !myTouchPoints.IsEmpty() ? EM_TRUE : EM_FALSE;
}
// ================================================================
// Function : onKeyDownEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onKeyDownEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent)
{
if (myView.IsNull()
|| theEventType != EMSCRIPTEN_EVENT_KEYDOWN) // EMSCRIPTEN_EVENT_KEYPRESS
{
return EM_FALSE;
}
const double aTimeStamp = EventTime();
const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode);
if (aVKey == Aspect_VKey_UNKNOWN)
{
return EM_FALSE;
}
if (theEvent->repeat == EM_FALSE)
{
myKeys.KeyDown (aVKey, aTimeStamp);
}
if (Aspect_VKey2Modifier (aVKey) == 0)
{
// normal key
}
return EM_FALSE;
}
// ================================================================
// Function : onKeyUpEvent
// Purpose :
// ================================================================
EM_BOOL WasmOcctView::onKeyUpEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent)
{
if (myView.IsNull()
|| theEventType != EMSCRIPTEN_EVENT_KEYUP)
{
return EM_FALSE;
}
const double aTimeStamp = EventTime();
const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode);
if (aVKey == Aspect_VKey_UNKNOWN)
{
return EM_FALSE;
}
if (theEvent->repeat == EM_TRUE)
{
return EM_FALSE;
}
const unsigned int aModif = myKeys.Modifiers();
myKeys.KeyUp (aVKey, aTimeStamp);
if (Aspect_VKey2Modifier (aVKey) == 0)
{
// normal key released
switch (aVKey | aModif)
{
case Aspect_VKey_F:
{
myView->FitAll (0.01, false);
myView->Invalidate();
updateView();
return EM_TRUE;
}
}
}
return EM_FALSE;
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) 2019 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 _WasmOcctView_HeaderFile
#define _WasmOcctView_HeaderFile
#include <AIS_InteractiveContext.hxx>
#include <AIS_ViewController.hxx>
#include <V3d_View.hxx>
#include <emscripten.h>
#include <emscripten/html5.h>
class AIS_ViewCube;
//! Sample class creating 3D Viewer within Emscripten canvas.
class WasmOcctView : protected AIS_ViewController
{
public:
//! Default constructor.
WasmOcctView();
//! Destructor.
virtual ~WasmOcctView();
//! Main application entry point.
void run();
//! Return interactive context.
const Handle(AIS_InteractiveContext)& Context() const { return myContext; }
//! Return view.
const Handle(V3d_View)& View() const { return myView; }
//! Return device pixel ratio for handling high DPI displays.
float DevicePixelRatio() const { return myDevicePixelRatio; }
private:
//! Create window.
void initWindow();
//! Create 3D Viewer.
bool initViewer();
//! Fill 3D Viewer with a DEMO items.
void initDemoScene();
//! Application event loop.
void mainloop();
//! Request view redrawing.
void updateView();
//! Flush events and redraw view.
void redrawView();
//! Handle view redraw.
virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
const Handle(V3d_View)& theView) override;
//! Dump WebGL context information.
void dumpGlInfo (bool theIsBasic);
//! Initialize pixel scale ratio.
void initPixelScaleRatio();
//! Return point from logical units to backing store.
Graphic3d_Vec2d convertPointToBacking (const Graphic3d_Vec2d& thePnt) const
{
return thePnt * myDevicePixelRatio;
}
//! Return point from logical units to backing store.
Graphic3d_Vec2i convertPointToBacking (const Graphic3d_Vec2i& thePnt) const
{
Graphic3d_Vec2d aPnt = Graphic3d_Vec2d (thePnt) * myDevicePixelRatio + Graphic3d_Vec2d (0.5);
return Graphic3d_Vec2i (aPnt);
}
//! @name Emscripten callbacks
private:
//! Window resize event.
EM_BOOL onResizeEvent (int theEventType, const EmscriptenUiEvent* theEvent);
//! Mouse event.
EM_BOOL onMouseEvent (int theEventType, const EmscriptenMouseEvent* theEvent);
//! Scroll event.
EM_BOOL onWheelEvent (int theEventType, const EmscriptenWheelEvent* theEvent);
//! Touch event.
EM_BOOL onTouchEvent (int theEventType, const EmscriptenTouchEvent* theEvent);
//! Key down event.
EM_BOOL onKeyDownEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent);
//! Key up event.
EM_BOOL onKeyUpEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent);
//! @name Emscripten callbacks (static functions)
private:
static EM_BOOL onResizeCallback (int theEventType, const EmscriptenUiEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onResizeEvent (theEventType, theEvent); }
static void onRedrawView (void* theView)
{ return ((WasmOcctView* )theView)->redrawView(); }
static EM_BOOL onMouseCallback (int theEventType, const EmscriptenMouseEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onMouseEvent (theEventType, theEvent); }
static EM_BOOL onWheelCallback (int theEventType, const EmscriptenWheelEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onWheelEvent (theEventType, theEvent); }
static EM_BOOL onTouchCallback (int theEventType, const EmscriptenTouchEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onTouchEvent (theEventType, theEvent); }
static EM_BOOL onKeyDownCallback (int theEventType, const EmscriptenKeyboardEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onKeyDownEvent (theEventType, theEvent); }
static EM_BOOL onKeyUpCallback (int theEventType, const EmscriptenKeyboardEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onKeyUpEvent (theEventType, theEvent); }
private:
Handle(AIS_InteractiveContext) myContext; //!< interactive context
Handle(V3d_View) myView; //!< 3D view
Handle(Prs3d_TextAspect) myTextStyle; //!< text style for OSD elements
Handle(AIS_ViewCube) myViewCube; //!< view cube object
TCollection_AsciiString myCanvasId; //!< canvas element id on HTML page
Aspect_Touch myClickTouch; //!< single touch position for handling clicks
OSD_Timer myDoubleTapTimer; //!< timer for handling double tap
float myDevicePixelRatio; //!< device pixel ratio for handling high DPI displays
unsigned int myUpdateRequests; //!< counter for unhandled update requests
};
#endif // _WasmOcctView_HeaderFile

264
samples/webgl/WasmVKeys.h Normal file
View File

@@ -0,0 +1,264 @@
// Copyright (c) 2019 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 _WasmVKeys_HeaderFile
#define _WasmVKeys_HeaderFile
#include <Aspect_VKey.hxx>
#include <emscripten/key_codes.h>
//! Convert Emscripten mouse buttons into Aspect_VKeyMouse.
inline Aspect_VKeyMouse WasmVKeys_MouseButtonsFromNative (unsigned short theButtons)
{
Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
if ((theButtons & 0x1) != 0)
{
aButtons |= Aspect_VKeyMouse_LeftButton;
}
if ((theButtons & 0x2) != 0)
{
aButtons |= Aspect_VKeyMouse_RightButton;
}
if ((theButtons & 0x4) != 0)
{
aButtons |= Aspect_VKeyMouse_MiddleButton;
}
return aButtons;
}
//! Convert DOM virtual key into Aspect_VKey.
inline Aspect_VKey WasmVKeys_VirtualKeyFromNative (Standard_Integer theKey)
{
if (theKey >= DOM_VK_0
&& theKey <= DOM_VK_9)
{
// numpad keys
return Aspect_VKey((theKey - DOM_VK_0) + Aspect_VKey_0);
}
if (theKey >= DOM_VK_A
&& theKey <= DOM_VK_Z)
{
// main latin alphabet keys
return Aspect_VKey((theKey - DOM_VK_A) + Aspect_VKey_A);
}
if (theKey >= DOM_VK_F1
&& theKey <= DOM_VK_F24)
{
// special keys
if (theKey <= DOM_VK_F12)
{
return Aspect_VKey((theKey - DOM_VK_F1) + Aspect_VKey_F1);
}
return Aspect_VKey_UNKNOWN;
}
if (theKey >= DOM_VK_NUMPAD0
&& theKey <= DOM_VK_NUMPAD9)
{
// numpad keys
return Aspect_VKey((theKey - DOM_VK_NUMPAD0) + Aspect_VKey_Numpad0);
}
switch (theKey)
{
case DOM_VK_CANCEL:
case DOM_VK_HELP:
return Aspect_VKey_UNKNOWN;
case DOM_VK_BACK_SPACE:
return Aspect_VKey_Backspace;
case DOM_VK_TAB:
return Aspect_VKey_Tab;
case DOM_VK_CLEAR:
return Aspect_VKey_UNKNOWN;
case DOM_VK_RETURN:
case DOM_VK_ENTER:
return Aspect_VKey_Enter;
case DOM_VK_SHIFT:
return Aspect_VKey_Shift;
case DOM_VK_CONTROL:
return Aspect_VKey_Control;
case DOM_VK_ALT:
return Aspect_VKey_Alt;
case DOM_VK_PAUSE:
case DOM_VK_CAPS_LOCK:
case DOM_VK_KANA:
//case DOM_VK_HANGUL:
case DOM_VK_EISU:
case DOM_VK_JUNJA:
case DOM_VK_FINAL:
case DOM_VK_HANJA:
//case DOM_VK_KANJI:
return Aspect_VKey_UNKNOWN;
case DOM_VK_ESCAPE:
return Aspect_VKey_Escape;
case DOM_VK_CONVERT:
case DOM_VK_NONCONVERT:
case DOM_VK_ACCEPT:
case DOM_VK_MODECHANGE:
return Aspect_VKey_UNKNOWN;
case DOM_VK_SPACE:
return Aspect_VKey_Space;
case DOM_VK_PAGE_UP:
return Aspect_VKey_PageUp;
case DOM_VK_PAGE_DOWN:
return Aspect_VKey_PageDown;
case DOM_VK_END:
return Aspect_VKey_End;
case DOM_VK_HOME:
return Aspect_VKey_Home;
case DOM_VK_LEFT:
return Aspect_VKey_Left;
case DOM_VK_UP:
return Aspect_VKey_Up;
case DOM_VK_RIGHT:
return Aspect_VKey_Right;
case DOM_VK_DOWN:
return Aspect_VKey_Down;
case DOM_VK_SELECT:
case DOM_VK_PRINT:
case DOM_VK_EXECUTE:
case DOM_VK_PRINTSCREEN:
case DOM_VK_INSERT:
return Aspect_VKey_UNKNOWN;
case DOM_VK_DELETE:
return Aspect_VKey_Delete;
case DOM_VK_COLON:
return Aspect_VKey_Comma;
case DOM_VK_SEMICOLON:
return Aspect_VKey_Semicolon;
case DOM_VK_LESS_THAN:
return Aspect_VKey_UNKNOWN;
case DOM_VK_EQUALS:
return Aspect_VKey_Equal;
case DOM_VK_GREATER_THAN:
return Aspect_VKey_UNKNOWN;
case DOM_VK_QUESTION_MARK:
return Aspect_VKey_Slash;
case DOM_VK_AT: // @ key
return Aspect_VKey_UNKNOWN;
case DOM_VK_WIN:
return Aspect_VKey_Meta;
case DOM_VK_CONTEXT_MENU:
case DOM_VK_SLEEP:
return Aspect_VKey_UNKNOWN;
case DOM_VK_MULTIPLY:
return Aspect_VKey_NumpadMultiply;
case DOM_VK_ADD:
return Aspect_VKey_NumpadAdd;
case DOM_VK_SEPARATOR:
return Aspect_VKey_UNKNOWN;
case DOM_VK_SUBTRACT:
return Aspect_VKey_NumpadSubtract;
case DOM_VK_DECIMAL:
return Aspect_VKey_UNKNOWN;
case DOM_VK_DIVIDE:
return Aspect_VKey_NumpadDivide;
case DOM_VK_NUM_LOCK:
return Aspect_VKey_Numlock;
case DOM_VK_SCROLL_LOCK:
return Aspect_VKey_Scroll;
case DOM_VK_WIN_OEM_FJ_JISHO:
case DOM_VK_WIN_OEM_FJ_MASSHOU:
case DOM_VK_WIN_OEM_FJ_TOUROKU:
case DOM_VK_WIN_OEM_FJ_LOYA:
case DOM_VK_WIN_OEM_FJ_ROYA:
case DOM_VK_CIRCUMFLEX:
return Aspect_VKey_UNKNOWN;
case DOM_VK_EXCLAMATION:
case DOM_VK_DOUBLE_QUOTE:
//case DOM_VK_HASH:
case DOM_VK_DOLLAR:
case DOM_VK_PERCENT:
case DOM_VK_AMPERSAND:
case DOM_VK_UNDERSCORE:
case DOM_VK_OPEN_PAREN:
case DOM_VK_CLOSE_PAREN:
case DOM_VK_ASTERISK:
return Aspect_VKey_UNKNOWN;
case DOM_VK_PLUS:
return Aspect_VKey_Plus;
case DOM_VK_PIPE:
case DOM_VK_HYPHEN_MINUS:
return Aspect_VKey_UNKNOWN;
case DOM_VK_OPEN_CURLY_BRACKET:
return Aspect_VKey_BracketLeft;
case DOM_VK_CLOSE_CURLY_BRACKET:
return Aspect_VKey_BracketRight;
case DOM_VK_TILDE:
return Aspect_VKey_Tilde;
case DOM_VK_VOLUME_MUTE:
return Aspect_VKey_VolumeMute;
case DOM_VK_VOLUME_DOWN:
return Aspect_VKey_VolumeDown;
case DOM_VK_VOLUME_UP:
return Aspect_VKey_VolumeUp;
case DOM_VK_COMMA:
return Aspect_VKey_Comma;
case DOM_VK_PERIOD:
return Aspect_VKey_Period;
case DOM_VK_SLASH:
return Aspect_VKey_Slash;
case DOM_VK_BACK_QUOTE:
return Aspect_VKey_UNKNOWN;
case DOM_VK_OPEN_BRACKET:
return Aspect_VKey_BracketLeft;
case DOM_VK_BACK_SLASH:
return Aspect_VKey_Backslash;
case DOM_VK_CLOSE_BRACKET:
return Aspect_VKey_BracketRight;
case DOM_VK_QUOTE:
return Aspect_VKey_UNKNOWN;
case DOM_VK_META:
return Aspect_VKey_Meta;
case DOM_VK_ALTGR:
return Aspect_VKey_Alt;
case DOM_VK_WIN_ICO_HELP:
case DOM_VK_WIN_ICO_00:
case DOM_VK_WIN_ICO_CLEAR:
case DOM_VK_WIN_OEM_RESET:
case DOM_VK_WIN_OEM_JUMP:
case DOM_VK_WIN_OEM_PA1:
case DOM_VK_WIN_OEM_PA2:
case DOM_VK_WIN_OEM_PA3:
case DOM_VK_WIN_OEM_WSCTRL:
case DOM_VK_WIN_OEM_CUSEL:
case DOM_VK_WIN_OEM_ATTN:
case DOM_VK_WIN_OEM_FINISH:
case DOM_VK_WIN_OEM_COPY:
case DOM_VK_WIN_OEM_AUTO:
case DOM_VK_WIN_OEM_ENLW:
case DOM_VK_WIN_OEM_BACKTAB:
case DOM_VK_ATTN:
case DOM_VK_CRSEL:
case DOM_VK_EXSEL:
case DOM_VK_EREOF:
return Aspect_VKey_UNKNOWN;
case DOM_VK_PLAY:
return Aspect_VKey_MediaPlayPause;
case DOM_VK_ZOOM:
case DOM_VK_PA1:
case DOM_VK_WIN_OEM_CLEAR:
return Aspect_VKey_UNKNOWN;
}
return Aspect_VKey_UNKNOWN;
}
#endif // _WasmVKeys_HeaderFile

66
samples/webgl/main.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include <iostream>
#include "WasmOcctView.h"
#include <Message.hxx>
#include <Message_Messenger.hxx>
#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;
//! 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_NOM_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()
{
Message::DefaultMessenger()->Printers().First()->SetTraceLevel (Message_Trace);
Message::DefaultMessenger()->Send (TCollection_AsciiString("NbLogicalProcessors: ") + OSD_Parallel::NbLogicalProcessors(), Message_Trace);
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

@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang=en-us>
<head>
<meta charset=utf-8><meta content="text/html; charset=utf-8" http-equiv=Content-Type>
<link rel="shortcut icon" href="lamp.ico" type="image/x-icon" />
<title>OCCT WebGL Viewer Sample</title>
</head>
<body>
<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>
<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>
<h4>Console output:</h4>
<p id="output"></p>
<script>
//! Resize canvas to fit into window.
function updateCanvasSize()
{
// size of canvas in logical (density-independent) units
var aSizeX = Math.min (window.innerWidth, window.screen.availWidth);
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";
// drawing buffer size (aka backing store)
var aDevicePixelRatio = window.devicePixelRatio || 1;
canvas.width = aSizeX * aDevicePixelRatio;
canvas.height = aSizeY * aDevicePixelRatio;
occlogo.style.top = (aSizeY - 30) + "px";
}
window.onresize = updateCanvasSize;
updateCanvasSize();
//! Check browser support.
function isWasmSupported()
{
try {
if (typeof WebAssembly === "object"
&& typeof WebAssembly.instantiate === "function") {
const aDummyModule = new WebAssembly.Module (Uint8Array.of (0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
if (aDummyModule instanceof WebAssembly.Module)
{
return new WebAssembly.Instance(aDummyModule) instanceof WebAssembly.Instance;
}
}
} catch (e) {}
return false;
}
if (!isWasmSupported())
{
var anElement = document.getElementById('output');
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>";
},
canvas: (function() {
var aCanvas = document.getElementById('canvas');
return aCanvas;
})()
};
//! Handle file uploading.
fileInput.onchange = function()
{
if (fileInput.files.length == 0) { return; }
// Warning! Entire file is pre-loaded into memory.
var aFile = fileInput.files[0];
var aReader = new FileReader();
aReader.onload = function()
{
var aDataArray = new Uint8Array (aReader.result);
var aNameArray = new Uint8Array (toUtf8Array (aFile.name));
const aDataBuffer = Module._malloc(aDataArray.length);
const aNameBuffer = Module._malloc(aNameArray.length);
Module.HEAPU8.set(aNameArray, aNameBuffer);
Module.HEAPU8.set(aDataArray, aDataBuffer);
Module.ccall('onFileDataRead', null, ['number', 'number', 'number'], [aNameBuffer, aDataBuffer, aDataArray.length]);
Module._free(aDataBuffer);
Module._free(aNameBuffer);
fileInput.value = '';
};
aReader.readAsArrayBuffer(aFile);
};
//! Convert string into UTF-8 array.
function toUtf8Array (theText)
{
var aRes = [];
for (var aCharIter = 0; aCharIter < theText.length; ++aCharIter)
{
var aCharCode = theText.charCodeAt (aCharIter);
if (aCharCode < 0x80)
{
aRes.push (aCharCode);
}
else if (aCharCode < 0x800)
{
aRes.push (0xc0 | (aCharCode >> 6), 0x80 | (aCharCode & 0x3f));
}
else if (aCharCode < 0xd800 || aCharCode >= 0xe000)
{
aRes.push (0xe0 | (aCharCode >> 12), 0x80 | ((aCharCode>>6) & 0x3f), 0x80 | (aCharCode & 0x3f));
}
else
{
++aCharIter;
aCharCode = 0x10000 + (((aCharCode & 0x3ff)<<10) | (theText.charCodeAt (aCharIter) & 0x3ff));
aRes.push(0xf0 | (aCharCode >>18), 0x80 | ((aCharCode>>12) & 0x3f), 0x80 | ((aCharCode>>6) & 0x3f), 0x80 | (aCharCode & 0x3f));
}
}
return aRes;
}
</script>
<script type="text/javascript" src="occt-webgl-sample.js" charset="utf-8"></script>
</body>
</html>