1 // Copyright (c) 2019 OPEN CASCADE SAS
3 // This file is part of the examples of the Open CASCADE Technology software library.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
22 #include "WasmOcctView.h"
24 #include <AIS_Shape.hxx>
25 #include <AIS_ViewCube.hxx>
26 #include <Aspect_Handle.hxx>
27 #include <Aspect_DisplayConnection.hxx>
28 #include <Image_AlienPixMap.hxx>
29 #include <Message.hxx>
30 #include <Message_Messenger.hxx>
31 #include <Graphic3d_CubeMapPacked.hxx>
32 #include <OpenGl_GraphicDriver.hxx>
33 #include <Prs3d_DatumAspect.hxx>
34 #include <Prs3d_ToolCylinder.hxx>
35 #include <Prs3d_ToolDisk.hxx>
36 #include <Wasm_Window.hxx>
38 #include <BRep_Builder.hxx>
39 #include <BRepBndLib.hxx>
40 #include <BRepTools.hxx>
41 #include <Standard_ArrayStreamBuffer.hxx>
43 #include <emscripten/bind.h>
47 #define THE_CANVAS_ID "canvas"
51 //! Auxiliary wrapper for loading model.
52 struct ModelAsyncLoader
57 ModelAsyncLoader (const char* theName, const char* thePath)
58 : Name (theName), Path (thePath) {}
60 //! File data read event.
61 static void onDataRead (void* theOpaque, void* theBuffer, int theDataLen)
63 const ModelAsyncLoader* aTask = (ModelAsyncLoader* )theOpaque;
64 WasmOcctView::openFromMemory (aTask->Name, reinterpret_cast<uintptr_t>(theBuffer), theDataLen, false);
68 //! File read error event.
69 static void onReadFailed (void* theOpaque)
71 const ModelAsyncLoader* aTask = (ModelAsyncLoader* )theOpaque;
72 Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load file ") + aTask->Path.c_str(), Message_Fail);
77 //! Auxiliary wrapper for loading cubemap.
78 struct CubemapAsyncLoader
80 //! Image file read event.
81 static void onImageRead (const char* theFilePath)
83 Handle(Graphic3d_CubeMapPacked) aCubemap;
84 Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
85 if (anImage->Load (theFilePath))
87 aCubemap = new Graphic3d_CubeMapPacked (anImage);
89 WasmOcctView::Instance().View()->SetBackgroundCubeMap (aCubemap, true, false);
90 WasmOcctView::Instance().UpdateView();
93 //! Image file failed read event.
94 static void onImageFailed (const char* theFilePath)
96 Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load image ") + theFilePath, Message_Fail);
101 // ================================================================
102 // Function : Instance
104 // ================================================================
105 WasmOcctView& WasmOcctView::Instance()
107 static WasmOcctView aViewer;
111 // ================================================================
112 // Function : WasmOcctView
114 // ================================================================
115 WasmOcctView::WasmOcctView()
116 : myDevicePixelRatio (1.0f),
117 myNbUpdateRequests (0)
119 addActionHotKeys (Aspect_VKey_NavForward, Aspect_VKey_W, Aspect_VKey_W | Aspect_VKeyFlags_SHIFT);
120 addActionHotKeys (Aspect_VKey_NavBackward , Aspect_VKey_S, Aspect_VKey_S | Aspect_VKeyFlags_SHIFT);
121 addActionHotKeys (Aspect_VKey_NavSlideLeft, Aspect_VKey_A, Aspect_VKey_A | Aspect_VKeyFlags_SHIFT);
122 addActionHotKeys (Aspect_VKey_NavSlideRight, Aspect_VKey_D, Aspect_VKey_D | Aspect_VKeyFlags_SHIFT);
123 addActionHotKeys (Aspect_VKey_NavRollCCW, Aspect_VKey_Q, Aspect_VKey_Q | Aspect_VKeyFlags_SHIFT);
124 addActionHotKeys (Aspect_VKey_NavRollCW, Aspect_VKey_E, Aspect_VKey_E | Aspect_VKeyFlags_SHIFT);
126 addActionHotKeys (Aspect_VKey_NavSpeedIncrease, Aspect_VKey_Plus, Aspect_VKey_Plus | Aspect_VKeyFlags_SHIFT,
128 Aspect_VKey_NumpadAdd, Aspect_VKey_NumpadAdd | Aspect_VKeyFlags_SHIFT);
129 addActionHotKeys (Aspect_VKey_NavSpeedDecrease, Aspect_VKey_Minus, Aspect_VKey_Minus | Aspect_VKeyFlags_SHIFT,
130 Aspect_VKey_NumpadSubtract, Aspect_VKey_NumpadSubtract | Aspect_VKeyFlags_SHIFT);
132 // arrow keys conflict with browser page scrolling, so better be avoided in non-fullscreen mode
133 addActionHotKeys (Aspect_VKey_NavLookUp, Aspect_VKey_Numpad8); // Aspect_VKey_Up
134 addActionHotKeys (Aspect_VKey_NavLookDown, Aspect_VKey_Numpad2); // Aspect_VKey_Down
135 addActionHotKeys (Aspect_VKey_NavLookLeft, Aspect_VKey_Numpad4); // Aspect_VKey_Left
136 addActionHotKeys (Aspect_VKey_NavLookRight, Aspect_VKey_Numpad6); // Aspect_VKey_Right
137 addActionHotKeys (Aspect_VKey_NavSlideLeft, Aspect_VKey_Numpad1); // Aspect_VKey_Left |Aspect_VKeyFlags_SHIFT
138 addActionHotKeys (Aspect_VKey_NavSlideRight, Aspect_VKey_Numpad3); // Aspect_VKey_Right|Aspect_VKeyFlags_SHIFT
139 addActionHotKeys (Aspect_VKey_NavSlideUp, Aspect_VKey_Numpad9); // Aspect_VKey_Up |Aspect_VKeyFlags_SHIFT
140 addActionHotKeys (Aspect_VKey_NavSlideDown, Aspect_VKey_Numpad7); // Aspect_VKey_Down |Aspect_VKeyFlags_SHIFT
143 // ================================================================
144 // Function : ~WasmOcctView
146 // ================================================================
147 WasmOcctView::~WasmOcctView()
151 // ================================================================
154 // ================================================================
155 void WasmOcctView::run()
165 myView->MustBeResized();
168 // There is no infinite message loop, main() will return from here immediately.
169 // Tell that our Module should be left loaded and handle events through callbacks.
170 //emscripten_set_main_loop (redrawView, 60, 1);
171 //emscripten_set_main_loop (redrawView, -1, 1);
172 EM_ASM(Module['noExitRuntime'] = true);
175 // ================================================================
176 // Function : initWindow
178 // ================================================================
179 void WasmOcctView::initWindow()
181 myDevicePixelRatio = emscripten_get_device_pixel_ratio();
182 myCanvasId = THE_CANVAS_ID;
183 const char* aTargetId = !myCanvasId.IsEmpty() ? myCanvasId.ToCString() : EMSCRIPTEN_EVENT_TARGET_WINDOW;
184 const EM_BOOL toUseCapture = EM_TRUE;
185 emscripten_set_resize_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onResizeCallback);
187 emscripten_set_mousedown_callback (aTargetId, this, toUseCapture, onMouseCallback);
188 // bind these events to window to track mouse movements outside of canvas
189 //emscripten_set_mouseup_callback (aTargetId, this, toUseCapture, onMouseCallback);
190 //emscripten_set_mousemove_callback (aTargetId, this, toUseCapture, onMouseCallback);
191 //emscripten_set_mouseleave_callback (aTargetId, this, toUseCapture, onMouseCallback);
192 emscripten_set_mouseup_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onMouseCallback);
193 emscripten_set_mousemove_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onMouseCallback);
195 emscripten_set_dblclick_callback (aTargetId, this, toUseCapture, onMouseCallback);
196 emscripten_set_click_callback (aTargetId, this, toUseCapture, onMouseCallback);
197 emscripten_set_mouseenter_callback (aTargetId, this, toUseCapture, onMouseCallback);
198 emscripten_set_wheel_callback (aTargetId, this, toUseCapture, onWheelCallback);
200 emscripten_set_touchstart_callback (aTargetId, this, toUseCapture, onTouchCallback);
201 emscripten_set_touchend_callback (aTargetId, this, toUseCapture, onTouchCallback);
202 emscripten_set_touchmove_callback (aTargetId, this, toUseCapture, onTouchCallback);
203 emscripten_set_touchcancel_callback(aTargetId, this, toUseCapture, onTouchCallback);
205 //emscripten_set_keypress_callback (aTargetId, this, toUseCapture, onKeyCallback);
206 emscripten_set_keydown_callback (aTargetId, this, toUseCapture, onKeyDownCallback);
207 emscripten_set_keyup_callback (aTargetId, this, toUseCapture, onKeyUpCallback);
208 //emscripten_set_focus_callback (aTargetId, this, toUseCapture, onFocusCallback);
209 //emscripten_set_focusin_callback (aTargetId, this, toUseCapture, onFocusCallback);
210 emscripten_set_focusout_callback (aTargetId, this, toUseCapture, onFocusCallback);
213 // ================================================================
214 // Function : dumpGlInfo
216 // ================================================================
217 void WasmOcctView::dumpGlInfo (bool theIsBasic)
219 TColStd_IndexedDataMapOfStringString aGlCapsDict;
220 myView->DiagnosticInformation (aGlCapsDict, theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
223 TCollection_AsciiString aViewport;
224 aGlCapsDict.FindFromKey ("Viewport", aViewport);
226 aGlCapsDict.Add ("Viewport", aViewport);
228 aGlCapsDict.Add ("Display scale", TCollection_AsciiString(myDevicePixelRatio));
232 TCollection_AsciiString* aGlVer = aGlCapsDict.ChangeSeek ("GLversion");
233 TCollection_AsciiString* aGlslVer = aGlCapsDict.ChangeSeek ("GLSLversion");
237 *aGlVer = *aGlVer + " [GLSL: " + *aGlslVer + "]";
242 TCollection_AsciiString anInfo;
243 for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter (aGlCapsDict); aValueIter.More(); aValueIter.Next())
245 if (!aValueIter.Value().IsEmpty())
247 if (!anInfo.IsEmpty())
251 anInfo += aValueIter.Key() + ": " + aValueIter.Value();
255 ::Message::DefaultMessenger()->Send (anInfo, Message_Warning);
258 // ================================================================
259 // Function : initPixelScaleRatio
261 // ================================================================
262 void WasmOcctView::initPixelScaleRatio()
264 SetTouchToleranceScale (myDevicePixelRatio);
265 if (!myView.IsNull())
267 myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
269 if (!myContext.IsNull())
271 myContext->SetPixelTolerance (int(myDevicePixelRatio * 6.0));
272 if (!myViewCube.IsNull())
274 static const double THE_CUBE_SIZE = 60.0;
275 myViewCube->SetSize (myDevicePixelRatio * THE_CUBE_SIZE, false);
276 myViewCube->SetBoxFacetExtension (myViewCube->Size() * 0.15);
277 myViewCube->SetAxesPadding (myViewCube->Size() * 0.10);
278 myViewCube->SetFontHeight (THE_CUBE_SIZE * 0.16);
279 if (myViewCube->HasInteractiveContext())
281 myContext->Redisplay (myViewCube, false);
287 // ================================================================
288 // Function : initViewer
290 // ================================================================
291 bool WasmOcctView::initViewer()
293 // Build with "--preload-file MyFontFile.ttf" option
294 // and register font in Font Manager to use custom font(s).
295 /*const char* aFontPath = "MyFontFile.ttf";
296 if (Handle(Font_SystemFont) aFont = Font_FontMgr::GetInstance()->CheckFont (aFontPath))
298 Font_FontMgr::GetInstance()->RegisterFont (aFont, true);
302 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: font '") + aFontPath + "' is not found", Message_Fail);
305 Handle(Aspect_DisplayConnection) aDisp;
306 Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (aDisp, false);
307 aDriver->ChangeOptions().buffersNoSwap = true; // swap has no effect in WebGL
308 aDriver->ChangeOptions().buffersOpaqueAlpha = true; // avoid unexpected blending of canvas with page background
309 if (!aDriver->InitContext())
311 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: EGL initialization failed"), Message_Fail);
315 Handle(V3d_Viewer) aViewer = new V3d_Viewer (aDriver);
316 aViewer->SetComputedMode (false);
317 aViewer->SetDefaultShadingModel (Graphic3d_TypeOfShadingModel_Phong);
318 aViewer->SetDefaultLights();
319 aViewer->SetLightOn();
320 for (V3d_ListOfLight::Iterator aLightIter (aViewer->ActiveLights()); aLightIter.More(); aLightIter.Next())
322 const Handle(V3d_Light)& aLight = aLightIter.Value();
323 if (aLight->Type() == Graphic3d_TypeOfLightSource_Directional)
325 aLight->SetCastShadows (true);
329 Handle(Wasm_Window) aWindow = new Wasm_Window (THE_CANVAS_ID);
330 aWindow->Size (myWinSizeOld.x(), myWinSizeOld.y());
332 myTextStyle = new Prs3d_TextAspect();
333 myTextStyle->SetFont (Font_NOF_ASCII_MONO);
334 myTextStyle->SetHeight (12);
335 myTextStyle->Aspect()->SetColor (Quantity_NOC_GRAY95);
336 myTextStyle->Aspect()->SetColorSubTitle (Quantity_NOC_BLACK);
337 myTextStyle->Aspect()->SetDisplayType (Aspect_TODT_SHADOW);
338 myTextStyle->Aspect()->SetTextFontAspect (Font_FA_Bold);
339 myTextStyle->Aspect()->SetTextZoomable (false);
340 myTextStyle->SetHorizontalJustification (Graphic3d_HTA_LEFT);
341 myTextStyle->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
343 myView = new V3d_View (aViewer);
344 myView->Camera()->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
345 myView->SetImmediateUpdate (false);
346 myView->ChangeRenderingParams().IsShadowEnabled = false;
347 myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
348 myView->ChangeRenderingParams().ToShowStats = true;
349 myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect();
350 myView->ChangeRenderingParams().StatsTextHeight = (int )myTextStyle->Height();
351 myView->SetWindow (aWindow);
354 myContext = new AIS_InteractiveContext (aViewer);
355 initPixelScaleRatio();
359 // ================================================================
360 // Function : initDemoScene
362 // ================================================================
363 void WasmOcctView::initDemoScene()
365 if (myContext.IsNull())
370 //myView->TriedronDisplay (Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_WIREFRAME);
372 myViewCube = new AIS_ViewCube();
373 // presentation parameters
374 initPixelScaleRatio();
375 myViewCube->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_RIGHT_LOWER, Graphic3d_Vec2i (100, 100)));
376 myViewCube->Attributes()->SetDatumAspect (new Prs3d_DatumAspect());
377 myViewCube->Attributes()->DatumAspect()->SetTextAspect (myTextStyle);
378 // animation parameters
379 myViewCube->SetViewAnimation (myViewAnimation);
380 myViewCube->SetFixedAnimationLoop (false);
381 myViewCube->SetAutoStartAnimation (true);
382 myContext->Display (myViewCube, false);
384 // Build with "--preload-file MySampleFile.brep" option to load some shapes here.
387 // ================================================================
388 // Function : ProcessInput
390 // ================================================================
391 void WasmOcctView::ProcessInput()
393 if (!myView.IsNull())
395 // Queue onRedrawView()/redrawView callback to redraw canvas after all user input is flushed by browser.
396 // Redrawing viewer on every single message would be a pointless waste of resources,
397 // as user will see only the last drawn frame due to WebGL implementation details.
398 // -1 in emscripten_async_call() redirects to requestAnimationFrame();
399 // requestPostAnimationFrame() is a better under development alternative.
400 if (++myNbUpdateRequests == 1)
402 emscripten_async_call (onRedrawView, this, -1);
407 // ================================================================
408 // Function : UpdateView
410 // ================================================================
411 void WasmOcctView::UpdateView()
413 if (!myView.IsNull())
415 myView->Invalidate();
416 // queue next onRedrawView()/redrawView()
421 // ================================================================
422 // Function : redrawView
424 // ================================================================
425 void WasmOcctView::redrawView()
427 if (!myView.IsNull())
429 myNbUpdateRequests = 0;
430 FlushViewEvents (myContext, myView, true);
434 // ================================================================
435 // Function : handleViewRedraw
437 // ================================================================
438 void WasmOcctView::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
439 const Handle(V3d_View)& theView)
441 AIS_ViewController::handleViewRedraw (theCtx, theView);
442 if (myToAskNextFrame)
445 if (++myNbUpdateRequests == 1)
447 emscripten_async_call (onRedrawView, this, -1);
452 // ================================================================
453 // Function : onResizeEvent
455 // ================================================================
456 EM_BOOL WasmOcctView::onResizeEvent (int theEventType, const EmscriptenUiEvent* theEvent)
458 (void )theEventType; // EMSCRIPTEN_EVENT_RESIZE or EMSCRIPTEN_EVENT_CANVASRESIZED
465 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
466 Graphic3d_Vec2i aWinSizeNew;
468 aWindow->Size (aWinSizeNew.x(), aWinSizeNew.y());
469 const float aPixelRatio = emscripten_get_device_pixel_ratio();
470 if (aWinSizeNew != myWinSizeOld
471 || aPixelRatio != myDevicePixelRatio)
473 myWinSizeOld = aWinSizeNew;
474 if (myDevicePixelRatio != aPixelRatio)
476 myDevicePixelRatio = aPixelRatio;
477 initPixelScaleRatio();
480 myView->MustBeResized();
481 myView->Invalidate();
488 //! Update canvas bounding rectangle.
489 EM_JS(void, jsUpdateBoundingClientRect, (), {
490 Module._myCanvasRect = Module.canvas.getBoundingClientRect();
493 //! Get canvas bounding top.
494 EM_JS(int, jsGetBoundingClientTop, (), {
495 return Math.round(Module._myCanvasRect.top);
498 //! Get canvas bounding left.
499 EM_JS(int, jsGetBoundingClientLeft, (), {
500 return Math.round(Module._myCanvasRect.left);
503 // ================================================================
504 // Function : onMouseEvent
506 // ================================================================
507 EM_BOOL WasmOcctView::onMouseEvent (int theEventType, const EmscriptenMouseEvent* theEvent)
514 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
515 if (theEventType == EMSCRIPTEN_EVENT_MOUSEMOVE
516 || theEventType == EMSCRIPTEN_EVENT_MOUSEUP)
518 // these events are bound to EMSCRIPTEN_EVENT_TARGET_WINDOW, and coordinates should be converted
519 jsUpdateBoundingClientRect();
520 EmscriptenMouseEvent anEvent = *theEvent;
521 anEvent.targetX -= jsGetBoundingClientLeft();
522 anEvent.targetY -= jsGetBoundingClientTop();
523 aWindow->ProcessMouseEvent (*this, theEventType, &anEvent);
527 return aWindow->ProcessMouseEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
530 // ================================================================
531 // Function : onWheelEvent
533 // ================================================================
534 EM_BOOL WasmOcctView::onWheelEvent (int theEventType, const EmscriptenWheelEvent* theEvent)
537 || theEventType != EMSCRIPTEN_EVENT_WHEEL)
542 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
543 return aWindow->ProcessWheelEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
546 // ================================================================
547 // Function : onTouchEvent
549 // ================================================================
550 EM_BOOL WasmOcctView::onTouchEvent (int theEventType, const EmscriptenTouchEvent* theEvent)
557 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
558 return aWindow->ProcessTouchEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
561 // ================================================================
562 // Function : navigationKeyModifierSwitch
564 // ================================================================
565 bool WasmOcctView::navigationKeyModifierSwitch (unsigned int theModifOld,
566 unsigned int theModifNew,
569 bool hasActions = false;
570 for (unsigned int aKeyIter = 0; aKeyIter < Aspect_VKey_ModifiersLower; ++aKeyIter)
572 if (!myKeys.IsKeyDown (aKeyIter))
577 Aspect_VKey anActionOld = Aspect_VKey_UNKNOWN, anActionNew = Aspect_VKey_UNKNOWN;
578 myNavKeyMap.Find (aKeyIter | theModifOld, anActionOld);
579 myNavKeyMap.Find (aKeyIter | theModifNew, anActionNew);
580 if (anActionOld == anActionNew)
585 if (anActionOld != Aspect_VKey_UNKNOWN)
587 myKeys.KeyUp (anActionOld, theTimeStamp);
589 if (anActionNew != Aspect_VKey_UNKNOWN)
592 myKeys.KeyDown (anActionNew, theTimeStamp);
598 // ================================================================
599 // Function : onFocusEvent
601 // ================================================================
602 EM_BOOL WasmOcctView::onFocusEvent (int theEventType, const EmscriptenFocusEvent* theEvent)
605 || (theEventType != EMSCRIPTEN_EVENT_FOCUS
606 && theEventType != EMSCRIPTEN_EVENT_FOCUSIN // about to receive focus
607 && theEventType != EMSCRIPTEN_EVENT_FOCUSOUT))
612 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
613 return aWindow->ProcessFocusEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
616 // ================================================================
617 // Function : onKeyDownEvent
619 // ================================================================
620 EM_BOOL WasmOcctView::onKeyDownEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent)
623 || theEventType != EMSCRIPTEN_EVENT_KEYDOWN) // EMSCRIPTEN_EVENT_KEYPRESS
628 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
629 return aWindow->ProcessKeyEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
632 //=======================================================================
635 //=======================================================================
636 void WasmOcctView::KeyDown (Aspect_VKey theKey,
640 const unsigned int aModifOld = myKeys.Modifiers();
641 AIS_ViewController::KeyDown (theKey, theTime, thePressure);
643 const unsigned int aModifNew = myKeys.Modifiers();
644 if (aModifNew != aModifOld
645 && navigationKeyModifierSwitch (aModifOld, aModifNew, theTime))
647 // modifier key just pressed
650 Aspect_VKey anAction = Aspect_VKey_UNKNOWN;
651 if (myNavKeyMap.Find (theKey | myKeys.Modifiers(), anAction)
652 && anAction != Aspect_VKey_UNKNOWN)
654 AIS_ViewController::KeyDown (anAction, theTime, thePressure);
658 // ================================================================
659 // Function : onKeyUpEvent
661 // ================================================================
662 EM_BOOL WasmOcctView::onKeyUpEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent)
665 || theEventType != EMSCRIPTEN_EVENT_KEYUP)
670 Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
671 return aWindow->ProcessKeyEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
674 //=======================================================================
677 //=======================================================================
678 void WasmOcctView::KeyUp (Aspect_VKey theKey,
681 const unsigned int aModifOld = myKeys.Modifiers();
682 AIS_ViewController::KeyUp (theKey, theTime);
684 Aspect_VKey anAction = Aspect_VKey_UNKNOWN;
685 if (myNavKeyMap.Find (theKey | myKeys.Modifiers(), anAction)
686 && anAction != Aspect_VKey_UNKNOWN)
688 AIS_ViewController::KeyUp (anAction, theTime);
689 processKeyPress (anAction);
692 const unsigned int aModifNew = myKeys.Modifiers();
693 if (aModifNew != aModifOld
694 && navigationKeyModifierSwitch (aModifOld, aModifNew, theTime))
696 // modifier key released
699 processKeyPress (theKey | aModifNew);
702 //==============================================================================
703 //function : processKeyPress
705 //==============================================================================
706 bool WasmOcctView::processKeyPress (Aspect_VKey theKey)
712 myView->FitAll (0.01, false);
720 // ================================================================
721 // Function : setCubemapBackground
723 // ================================================================
724 void WasmOcctView::setCubemapBackground (const std::string& theImagePath)
726 if (!theImagePath.empty())
728 emscripten_async_wget (theImagePath.c_str(), "/emulated/cubemap.jpg", CubemapAsyncLoader::onImageRead, CubemapAsyncLoader::onImageFailed);
732 WasmOcctView::Instance().View()->SetBackgroundCubeMap (Handle(Graphic3d_CubeMapPacked)(), true, false);
733 WasmOcctView::Instance().UpdateView();
737 // ================================================================
738 // Function : fitAllObjects
740 // ================================================================
741 void WasmOcctView::fitAllObjects (bool theAuto)
743 WasmOcctView& aViewer = Instance();
746 aViewer.FitAllAuto (aViewer.Context(), aViewer.View());
750 aViewer.View()->FitAll (0.01, false);
752 aViewer.UpdateView();
755 // ================================================================
756 // Function : removeAllObjects
758 // ================================================================
759 void WasmOcctView::removeAllObjects()
761 WasmOcctView& aViewer = Instance();
762 for (NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)>::Iterator anObjIter (aViewer.myObjects);
763 anObjIter.More(); anObjIter.Next())
765 aViewer.Context()->Remove (anObjIter.Value(), false);
767 aViewer.myObjects.Clear();
768 aViewer.UpdateView();
771 // ================================================================
772 // Function : removeObject
774 // ================================================================
775 bool WasmOcctView::removeObject (const std::string& theName)
777 WasmOcctView& aViewer = Instance();
778 Handle(AIS_InteractiveObject) anObj;
780 && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
785 aViewer.Context()->Remove (anObj, false);
786 aViewer.myObjects.RemoveKey (theName.c_str());
787 aViewer.UpdateView();
791 // ================================================================
792 // Function : eraseObject
794 // ================================================================
795 bool WasmOcctView::eraseObject (const std::string& theName)
797 WasmOcctView& aViewer = Instance();
798 Handle(AIS_InteractiveObject) anObj;
800 && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
805 aViewer.Context()->Erase (anObj, false);
806 aViewer.UpdateView();
810 // ================================================================
811 // Function : displayObject
813 // ================================================================
814 bool WasmOcctView::displayObject (const std::string& theName)
816 WasmOcctView& aViewer = Instance();
817 Handle(AIS_InteractiveObject) anObj;
819 && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
824 aViewer.Context()->Display (anObj, false);
825 aViewer.UpdateView();
829 // ================================================================
830 // Function : openFromUrl
832 // ================================================================
833 void WasmOcctView::openFromUrl (const std::string& theName,
834 const std::string& theModelPath)
836 ModelAsyncLoader* aTask = new ModelAsyncLoader (theName.c_str(), theModelPath.c_str());
837 emscripten_async_wget_data (theModelPath.c_str(), (void* )aTask, ModelAsyncLoader::onDataRead, ModelAsyncLoader::onReadFailed);
840 // ================================================================
841 // Function : openFromMemory
843 // ================================================================
844 bool WasmOcctView::openFromMemory (const std::string& theName,
845 uintptr_t theBuffer, int theDataLen,
848 removeObject (theName);
849 char* aBytes = reinterpret_cast<char*>(theBuffer);
850 if (aBytes == nullptr
856 // Function to check if specified data stream starts with specified header.
857 #define dataStartsWithHeader(theData, theHeader) (::strncmp(theData, theHeader, sizeof(theHeader) - 1) == 0)
859 if (dataStartsWithHeader(aBytes, "DBRep_DrawableShape"))
861 return openBRepFromMemory (theName, theBuffer, theDataLen, theToFree);
863 else if (dataStartsWithHeader(aBytes, "glTF"))
865 //return openGltfFromMemory (theName, theBuffer, theDataLen, theToFree);
872 Message::SendFail() << "Error: file '" << theName.c_str() << "' has unsupported format";
876 // ================================================================
877 // Function : openBRepFromMemory
879 // ================================================================
880 bool WasmOcctView::openBRepFromMemory (const std::string& theName,
881 uintptr_t theBuffer, int theDataLen,
884 removeObject (theName);
886 WasmOcctView& aViewer = Instance();
888 BRep_Builder aBuilder;
889 bool isLoaded = false;
891 char* aRawData = reinterpret_cast<char*>(theBuffer);
892 Standard_ArrayStreamBuffer aStreamBuffer (aRawData, theDataLen);
893 std::istream aStream (&aStreamBuffer);
894 BRepTools::Read (aShape, aStream, aBuilder);
906 Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
907 if (!theName.empty())
909 aViewer.myObjects.Add (theName.c_str(), aShapePrs);
911 aShapePrs->SetMaterial (Graphic3d_NameOfMaterial_Silver);
912 aViewer.Context()->Display (aShapePrs, AIS_Shaded, 0, false);
913 aViewer.View()->FitAll (0.01, false);
914 aViewer.UpdateView();
916 Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + theName.c_str(), Message_Info);
917 Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
921 // ================================================================
922 // Function : displayGround
924 // ================================================================
925 void WasmOcctView::displayGround (bool theToShow)
927 static Handle(AIS_Shape) aGroundPrs = new AIS_Shape (TopoDS_Shape());
929 WasmOcctView& aViewer = Instance();
933 aViewer.Context()->Remove (aGroundPrs, false);
934 aBox = aViewer.View()->View()->MinMaxValues();
937 || aBox.IsZThin (Precision::Confusion()))
939 if (!aGroundPrs.IsNull()
940 && aGroundPrs->HasInteractiveContext())
942 aViewer.Context()->Remove (aGroundPrs, false);
943 aViewer.UpdateView();
948 const gp_XYZ aSize = aBox.CornerMax().XYZ() - aBox.CornerMin().XYZ();
949 const double aRadius = Max (aSize.X(), aSize.Y());
950 const double aZValue = aBox.CornerMin().Z() - Min (10.0, aSize.Z() * 0.01);
951 const double aZSize = aRadius * 0.01;
952 gp_XYZ aGroundCenter ((aBox.CornerMin().X() + aBox.CornerMax().X()) * 0.5,
953 (aBox.CornerMin().Y() + aBox.CornerMax().Y()) * 0.5,
956 TopoDS_Compound aGround;
957 gp_Trsf aTrsf1, aTrsf2;
958 aTrsf1.SetTranslation (gp_Vec (aGroundCenter - gp_XYZ(0.0, 0.0, aZSize)));
959 aTrsf2.SetTranslation (gp_Vec (aGroundCenter));
960 Prs3d_ToolCylinder aCylTool (aRadius, aRadius, aZSize, 50, 1);
961 Prs3d_ToolDisk aDiskTool (0.0, aRadius, 50, 1);
962 TopoDS_Face aCylFace, aDiskFace1, aDiskFace2;
963 BRep_Builder().MakeFace (aCylFace, aCylTool .CreatePolyTriangulation (aTrsf1));
964 BRep_Builder().MakeFace (aDiskFace1, aDiskTool.CreatePolyTriangulation (aTrsf1));
965 BRep_Builder().MakeFace (aDiskFace2, aDiskTool.CreatePolyTriangulation (aTrsf2));
967 BRep_Builder().MakeCompound (aGround);
968 BRep_Builder().Add (aGround, aCylFace);
969 BRep_Builder().Add (aGround, aDiskFace1);
970 BRep_Builder().Add (aGround, aDiskFace2);
972 aGroundPrs->SetShape (aGround);
973 aGroundPrs->SetToUpdate();
974 aGroundPrs->SetMaterial (Graphic3d_NameOfMaterial_Stone);
975 aGroundPrs->SetInfiniteState (false);
976 aViewer.Context()->Display (aGroundPrs, AIS_Shaded, -1, false);
977 aGroundPrs->SetInfiniteState (true);
978 aViewer.UpdateView();
982 EMSCRIPTEN_BINDINGS(OccViewerModule) {
983 emscripten::function("setCubemapBackground", &WasmOcctView::setCubemapBackground);
984 emscripten::function("fitAllObjects", &WasmOcctView::fitAllObjects);
985 emscripten::function("removeAllObjects", &WasmOcctView::removeAllObjects);
986 emscripten::function("removeObject", &WasmOcctView::removeObject);
987 emscripten::function("eraseObject", &WasmOcctView::eraseObject);
988 emscripten::function("displayObject", &WasmOcctView::displayObject);
989 emscripten::function("displayGround", &WasmOcctView::displayGround);
990 emscripten::function("openFromUrl", &WasmOcctView::openFromUrl);
991 emscripten::function("openFromMemory", &WasmOcctView::openFromMemory, emscripten::allow_raw_pointers());
992 emscripten::function("openBRepFromMemory", &WasmOcctView::openBRepFromMemory, emscripten::allow_raw_pointers());