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 TOTAL_MEMORY=256MB")
#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")
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
)
source_group ("Headers" FILES
WasmOcctView.h
- WasmOcctPixMap.h)
+ WasmOcctPixMap.h
+ WasmOcctObject.h)
source_group ("Sources" FILES
WasmOcctView.cpp
WasmOcctPixMap.cpp
+ WasmOcctObject.cpp
main.cpp)
# FreeType
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)
+INCLUDE_DIRECTORIES("c:/workssd/Develop/3rdparty/android/rapidjson-1.1.0/include")
+add_definitions (-DHAVE_RAPIDJSON)
+
add_executable(${APP_TARGET} ${SOURCES})
target_link_libraries(
${APP_TARGET}
endif()
endif()
install(FILES occt-webgl-sample.html DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(FILES threejs-sample.html DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(FILES threejs-sample.js DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(DIRECTORY threejs DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(DIRECTORY textures DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(DIRECTORY models DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${OpenCASCADE_RESOURCE_DIR}/DrawResources/OCC_logo.png DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${OpenCASCADE_RESOURCE_DIR}/DrawResources/lamp.ico DESTINATION ${CMAKE_INSTALL_PREFIX})
--- /dev/null
+/**
+ * OCC Viewer global object.
+ */
+interface OccViewerModule {
+
+ /**
+ * Set cubemap background.
+ * File will be loaded asynchronously.
+ * @param {string} theImagePath [in] image path to load
+ */
+ void setCubemapBackground (DOMString theImagePath);
+
+ /**
+ * Clear all named objects from viewer.
+ */
+ void removeAllObjects();
+
+ /**
+ * Fit all/selected objects into view.
+ * @param {boolean} theAuto [in] fit selected objects (TRUE) or all objects (FALSE)
+ */
+ void fitAllObjects (boolean theAuto);
+
+ /**
+ * Remove named object from viewer.
+ * @param {string} theName [in] object name
+ * @param {boolean} theToUpdate [in] immediatly request viewer update
+ * @return {boolean} FALSE if object was not found
+ */
+ boolean removeObject (DOMString theName,
+ boolean theToUpdate);
+
+ /**
+ * Temporarily hide named object.
+ * @param {string} theName [in] object name
+ * @return {boolean} FALSE if object was not found
+ */
+ boolean eraseObject (DOMString theName);
+
+ /**
+ * Display temporarily hidden object.
+ * @param {string} theName [in] object name
+ * @return {boolean} FALSE if object was not found
+ */
+ boolean displayObject (DOMString theName);
+
+ /**
+ * Show/hide ground.
+ * @param {boolean} theToShow [in] show or hide flag
+ */
+ void displayGround (boolean theToShow);
+
+ /**
+ * Open object from the given URL.
+ * File will be loaded asynchronously.
+ * @param {string} theName [in] object name
+ * @param {string} theModelPath [in] model path
+ */
+ void openFromUrl (DOMString theName,
+ DOMString 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 {boolean} FALSE on reading error
+ */
+ //boolean openFromMemory (DOMString theName,
+ // long theBuffer, int theDataLen,
+ // boolean 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 boolean openBRepFromMemory (DOMString theName,
+ // long theBuffer, int theDataLen,
+ // boolean theToFree);
+
+ /**
+ * Open glTF 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 boolean openGltfFromMemory (DOMString theName,
+ // long theBuffer, int theDataLen,
+ // boolean theToFree);
+
+};
--- /dev/null
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of the examples of the Open CASCADE Technology software library.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
+
+#include "WasmOcctObject.h"
+
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+#include <Graphic3d_ArrayOfTriangles.hxx>
+#include <Prs3d_ShadingAspect.hxx>
+#include <Select3D_SensitiveTriangulation.hxx>
+#include <SelectMgr_EntityOwner.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Face.hxx>
+#include <XCAFDoc_VisMaterial.hxx>
+
+//! Gets triangulation of every face of shape and fills output array of triangles
+static Handle(Graphic3d_ArrayOfTriangles) fillTriangles (const TopoDS_Shape& theShape,
+ const bool theHasTexels,
+ const gp_Pnt2d& theUVOrigin,
+ const gp_Pnt2d& theUVRepeat,
+ const gp_Pnt2d& theUVScale)
+{
+ TopLoc_Location aLoc;
+ Standard_Integer aNbTriangles = 0, aNbVertices = 0;
+ bool hasNormals = true;
+ for (TopExp_Explorer aFaceIt (theShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
+ {
+ const TopoDS_Face& aFace = TopoDS::Face (aFaceIt.Current());
+ if (const Handle(Poly_Triangulation)& aT = BRep_Tool::Triangulation (aFace, aLoc))
+ {
+ aNbTriangles += aT->NbTriangles();
+ aNbVertices += aT->NbNodes();
+ hasNormals = hasNormals && aT->HasNormals();
+ }
+ }
+ if (aNbVertices < 3 || aNbTriangles < 1)
+ {
+ return Handle(Graphic3d_ArrayOfTriangles)();
+ }
+
+ const Graphic3d_ArrayFlags aFlags = (hasNormals ? Graphic3d_ArrayFlags_VertexNormal : Graphic3d_ArrayFlags_None)
+ | (theHasTexels ? Graphic3d_ArrayFlags_VertexTexel : Graphic3d_ArrayFlags_None);
+ Handle(Graphic3d_ArrayOfTriangles) anArray = new Graphic3d_ArrayOfTriangles (aNbVertices, 3 * aNbTriangles, aFlags);
+ Standard_Real aUmin (0.0), aUmax (1.0), aVmin (0.0), aVmax (1.0), dUmax (1.0), dVmax (1.0);
+ for (TopExp_Explorer aFaceIt(theShape, TopAbs_FACE); aFaceIt.More(); aFaceIt.Next())
+ {
+ const TopoDS_Face& aFace = TopoDS::Face (aFaceIt.Current());
+ const Handle(Poly_Triangulation)& aT = BRep_Tool::Triangulation (aFace, aLoc);
+ if (aT.IsNull())
+ {
+ continue;
+ }
+
+ // Determinant of transform matrix less then 0 means that mirror transform applied.
+ const gp_Trsf& aTrsf = aLoc.Transformation();
+ const bool isMirrored = aTrsf.VectorialPart().Determinant() < 0;
+
+ // Extracts vertices & normals from nodes
+ const TColgp_Array1OfPnt& aNodes = aT->Nodes();
+ const TColgp_Array1OfPnt2d* aUVNodes = theHasTexels && aT->HasUVNodes() && aT->UVNodes().Upper() == aNodes.Upper()
+ ? &aT->UVNodes()
+ : NULL;
+
+ const TShort_Array1OfShortReal* aNormals = aT->HasNormals() ? &aT->Normals() : NULL;
+ const Standard_ShortReal* aNormArr = aNormals != NULL ? &aNormals->First() : NULL;
+
+ const Standard_Integer aVertFrom = anArray->VertexNumber();
+ for (Standard_Integer aNodeIter = aNodes.Lower(); aNodeIter <= aNodes.Upper(); ++aNodeIter)
+ {
+ gp_Pnt aPoint = aNodes (aNodeIter);
+ const Standard_Integer anId = 3 * (aNodeIter - aNodes.Lower());
+ gp_Dir aNorm = aNormArr != NULL ? gp_Dir (aNormArr[anId + 0], aNormArr[anId + 1], aNormArr[anId + 2]) : gp::DZ();
+ if ((aFace.Orientation() == TopAbs_REVERSED) ^ isMirrored)
+ {
+ aNorm.Reverse();
+ }
+ if (!aLoc.IsIdentity())
+ {
+ aPoint.Transform (aTrsf);
+ aNorm .Transform (aTrsf);
+ }
+
+ if (aUVNodes != NULL)
+ {
+ const gp_Pnt2d aTexel = (dUmax == 0.0 || dVmax == 0.0)
+ ? aUVNodes->Value (aNodeIter)
+ : gp_Pnt2d ((-theUVOrigin.X() + (theUVRepeat.X() * (aUVNodes->Value (aNodeIter).X() - aUmin)) / dUmax) / theUVScale.X(),
+ (-theUVOrigin.Y() + (theUVRepeat.Y() * (aUVNodes->Value (aNodeIter).Y() - aVmin)) / dVmax) / theUVScale.Y());
+ anArray->AddVertex (aPoint, aNorm, aTexel);
+ }
+ else
+ {
+ anArray->AddVertex (aPoint, aNorm);
+ }
+ }
+
+ // Fill array with vertex and edge visibility info
+ const Poly_Array1OfTriangle& aTriangles = aT->Triangles();
+ Standard_Integer anIndex[3] = {};
+ for (Standard_Integer aTriIter = 1; aTriIter <= aT->NbTriangles(); ++aTriIter)
+ {
+ aTriangles (aTriIter).Get (anIndex[0], anIndex[1], anIndex[2]);
+ if (aFace.Orientation() == TopAbs_REVERSED) { std::swap (anIndex[1], anIndex[2]); }
+ anArray->AddEdges (anIndex[0] + aVertFrom, anIndex[1] + aVertFrom, anIndex[2] + aVertFrom);
+ }
+ }
+ return anArray;
+}
+
+// ================================================================
+// Function : WasmOcctObject
+// Purpose :
+// ================================================================
+WasmOcctObject::WasmOcctObject()
+{
+}
+
+// ================================================================
+// Function : ~WasmOcctObject
+// Purpose :
+// ================================================================
+WasmOcctObject::~WasmOcctObject()
+{
+}
+
+// ================================================================
+// Function : Compute
+// Purpose :
+// ================================================================
+void WasmOcctObject::Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+ const Handle(Prs3d_Presentation)& thePrs,
+ const Standard_Integer theMode)
+{
+ if (theMode != 0)
+ {
+ return;
+ }
+
+ gp_Pnt2d anUVOrigin, anUVRepeat, anUVScale;
+ bool hasTexCoords = false;
+
+ NCollection_DataMap<Handle(XCAFDoc_VisMaterial), TopoDS_Compound> aMatMap;
+
+ RWMesh_NodeAttributes aDefAttribs;
+ {
+ Handle(XCAFDoc_VisMaterial) aDefMat = new XCAFDoc_VisMaterial();
+ XCAFDoc_VisMaterialPBR aPbrMar;
+ aPbrMar.IsDefined = true;
+ aPbrMar.BaseColor.SetValues (0.243137f, 0.243137f, 0.243137f, 1.0f);
+ aDefMat->SetPbrMaterial (aPbrMar);
+ aDefAttribs.Style.SetMaterial (aDefMat);
+ }
+ for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
+ {
+ const TopoDS_Shape& aShape = aRootIter.Value();
+ /*if (Handle(Graphic3d_ArrayOfTriangles) aPArray = fillTriangles (aShape, hasTexCoords, anUVOrigin, anUVRepeat, anUVScale))
+ {
+ Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
+ aGroup->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
+ aGroup->AddPrimitiveArray (aPArray);
+ }*/
+
+ for (TopExp_Explorer aFaceIter (aShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
+ {
+ const TopoDS_Face& aFace = TopoDS::Face (aFaceIter.Value());
+ const RWMesh_NodeAttributes* anAttribs = myAttribMap.Seek (aFace);
+ if (anAttribs == NULL)
+ {
+ anAttribs = myAttribMap.Seek (aFace.Located (TopLoc_Location()));
+ if (anAttribs == NULL)
+ {
+ anAttribs = &aDefAttribs;
+ }
+ }
+ if (Handle(XCAFDoc_VisMaterial) aVisMat = anAttribs->Style.Material())
+ {
+ TopoDS_Compound* aComp = aMatMap.ChangeSeek (aVisMat);
+ if (aComp == NULL)
+ {
+ aComp = aMatMap.Bound (aVisMat, TopoDS_Compound());
+ BRep_Builder().MakeCompound (*aComp);
+ }
+ BRep_Builder().Add (*aComp, aFace);
+ }
+ }
+ }
+
+ for (NCollection_DataMap<Handle(XCAFDoc_VisMaterial), TopoDS_Compound>::Iterator aMatIter (aMatMap); aMatIter.More(); aMatIter.Next())
+ {
+ const Handle(XCAFDoc_VisMaterial)& aVisMat = aMatIter.Key();
+ const TopoDS_Compound& aShape = aMatIter.Value();
+ if (Handle(Graphic3d_ArrayOfTriangles) aPArray = fillTriangles (aShape, hasTexCoords, anUVOrigin, anUVRepeat, anUVScale))
+ {
+ Handle(Graphic3d_AspectFillArea3d) anAspects = new Graphic3d_AspectFillArea3d();
+ *anAspects = *myDrawer->ShadingAspect()->Aspect();
+ aVisMat->FillAspect (anAspects);
+
+ Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
+ aGroup->SetGroupPrimitivesAspect (anAspects);
+ aGroup->AddPrimitiveArray (aPArray);
+ }
+ }
+}
+
+// ================================================================
+// Function : ComputeSelection
+// Purpose :
+// ================================================================
+void WasmOcctObject::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
+ const Standard_Integer theMode)
+{
+ if (theMode != 0)
+ {
+ return;
+ }
+
+ Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this, 5);
+ for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
+ {
+ const TopoDS_Shape& aShape = aRootIter.Value();
+ for (TopExp_Explorer aFaceIter (aShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
+ {
+ const TopoDS_Face& aFace = TopoDS::Face (aFaceIter.Value());
+ TopLoc_Location aLoc;
+ if (Handle(Poly_Triangulation) aPolyTri = BRep_Tool::Triangulation (aFace, aLoc))
+ {
+ Handle(Select3D_SensitiveTriangulation) aSensTris = new Select3D_SensitiveTriangulation (anOwner, aPolyTri, aLoc);
+ theSel->Add (aSensTris);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (c) 2021 OPEN CASCADE SAS
+//
+// This file is part of the examples of the Open CASCADE Technology software library.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
+
+#ifndef _WasmOcctObject_HeaderFile
+#define _WasmOcctObject_HeaderFile
+
+#include <AIS_InteractiveObject.hxx>
+#include <RWMesh_NodeAttributes.hxx>
+#include <TopTools_SequenceOfShape.hxx>
+
+//! Sample presentation.
+class WasmOcctObject : public AIS_InteractiveObject
+{
+public:
+ //! Default constructor.
+ Standard_EXPORT WasmOcctObject();
+
+ //! Destructor.
+ Standard_EXPORT virtual ~WasmOcctObject();
+
+ //! Return sequence of root shapes.
+ TopTools_SequenceOfShape& ChangeShapes() { return myRootShapes; }
+
+ //! Return shape attributes.
+ RWMesh_NodeAttributeMap& ChangeAttributes() { return myAttribMap; }
+
+ //! Set a single shape.
+ void SetShape (const TopoDS_Shape& theShape)
+ {
+ myRootShapes.Clear();
+ myRootShapes.Append (theShape);
+ }
+
+protected:
+
+ //! Return TRUE for supported display mode.
+ virtual Standard_Boolean AcceptDisplayMode (const Standard_Integer theMode) const Standard_OVERRIDE { return theMode == 0; }
+
+ //! Compute 3D part of View Cube.
+ //! @param thePrsMgr [in] presentation manager.
+ //! @param thePrs [in] input presentation that is to be filled with flat presentation primitives.
+ //! @param theMode [in] display mode.
+ //! @warning this object accept only 0 display mode.
+ Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+ const Handle(Prs3d_Presentation)& thePrs,
+ const Standard_Integer theMode) Standard_OVERRIDE;
+
+ //! Redefine computing of sensitive entities for View Cube.
+ //! @param theSelection [in] input selection object that is to be filled with sensitive entities.
+ //! @param theMode [in] selection mode.
+ //! @warning object accepts only 0 selection mode.
+ Standard_EXPORT virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
+ const Standard_Integer theMode) Standard_OVERRIDE;
+
+private:
+
+ TopTools_SequenceOfShape myRootShapes; //!< sequence of result root shapes
+ RWMesh_NodeAttributeMap myAttribMap; //!< shape attributes
+
+};
+
+#endif // _WasmOcctObject_HeaderFile
#include <BRepBndLib.hxx>
#include <BRepTools.hxx>
#include <Standard_ArrayStreamBuffer.hxx>
+#include <TopExp_Explorer.hxx>
#include <emscripten/bind.h>
{
std::string Name;
std::string Path;
+ bool ToExpand;
+ OSD_Timer Timer;
- ModelAsyncLoader (const char* theName, const char* thePath)
- : Name (theName), Path (thePath) {}
+ ModelAsyncLoader (const char* theName, const char* thePath, const bool theToExpand)
+ : Name (theName), Path (thePath), ToExpand (theToExpand)
+ {
+ Timer.Start();
+ }
//! 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);
+ ModelAsyncLoader* aTask = (ModelAsyncLoader* )theOpaque;
+ WasmOcctView::openFromMemory (aTask->Name, aTask->ToExpand, reinterpret_cast<uintptr_t>(theBuffer), theDataLen, false);
+ aTask->Timer.Stop();
+ Message::SendInfo() << aTask->Path << " loaded in " << aTask->Timer.ElapsedTime() << " s";
delete aTask;
}
if (anImage->Init (theFilePath))
{
aCubemap = new Graphic3d_CubeMapPacked (anImage);
+ /// TODO WebGL 2.0 ensures sRGB conformance within glGenerateMipmap()
+ /// which leads to extremely SLOW generation (5 seconds instead of 0.035)
+ aCubemap->SetColorMap (false);
}
WasmOcctView::Instance().View()->SetBackgroundCubeMap (aCubemap, true, false);
WasmOcctView::Instance().UpdateView();
Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: unable to load image ") + theFilePath, Message_Fail);
}
};
+
+ //! Object list separator.
+ static const char THE_LIST_SEPARATOR = '\n';
}
// ================================================================
: myDevicePixelRatio (1.0f),
myUpdateRequests (0)
{
+ SetLockOrbitZUp (true);
+
addActionHotKeys (Aspect_VKey_NavForward, Aspect_VKey_W, Aspect_VKey_W | Aspect_VKeyFlags_SHIFT);
addActionHotKeys (Aspect_VKey_NavBackward , Aspect_VKey_S, Aspect_VKey_S | Aspect_VKeyFlags_SHIFT);
addActionHotKeys (Aspect_VKey_NavSlideLeft, Aspect_VKey_A, Aspect_VKey_A | Aspect_VKeyFlags_SHIFT);
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: font '") + aFontPath + "' is not found", Message_Fail);
}*/
+ const bool toAntialias = myDevicePixelRatio <= 1.25f;
+
Handle(Aspect_DisplayConnection) aDisp;
Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (aDisp, false);
aDriver->ChangeOptions().buffersNoSwap = true; // swap has no effect in WebGL
aDriver->ChangeOptions().buffersOpaqueAlpha = true; // avoid unexpected blending of canvas with page background
+ aDriver->ChangeOptions().useSystemBuffer = false; ///
+ //aDriver->ChangeOptions().useSystemBuffer = true; ///
if (!aDriver->InitContext())
{
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: EGL initialization failed"), Message_Fail);
if (aLight->Type() == Graphic3d_TOLS_DIRECTIONAL)
{
aLight->SetCastShadows (true);
+ aLight->SetHeadlight (false);
+ aLight->SetDirection (gp_Dir (0.098f, -0.20f, -0.98f));
}
}
myView = new V3d_View (aViewer);
myView->Camera()->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
+ myView->Camera()->SetFOV2d (360.0f);
+ myView->Camera()->SetFOVy (45.0f);
myView->SetImmediateUpdate (false);
- myView->ChangeRenderingParams().IsShadowEnabled = false;
+ //myView->ChangeRenderingParams().NbMsaaSamples = toAntialias ? 4 : 0;
+ myView->ChangeRenderingParams().RenderResolutionScale = toAntialias ? 2.0f : 1.0f;
myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
myView->ChangeRenderingParams().ToShowStats = true;
myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect();
myView->SetWindow (aWindow);
dumpGlInfo (false);
+ myView->SetShadingModel (aDriver->InquireLimit (Graphic3d_TypeOfLimit_HasPBR) == 1 ? Graphic3d_TOSM_PBR : Graphic3d_TOSM_FRAGMENT);
+ ///myView->ChangeRenderingParams().IsShadowEnabled = aDriver->InquireLimit (Graphic3d_TypeOfLimit_HasPBR) == 1; /// TODO
+ myView->ChangeRenderingParams().IsShadowEnabled = true; ///
+
myContext = new AIS_InteractiveContext (aViewer);
+ {
+ {
+ const Handle(Prs3d_Drawer)& aHiStyle = myContext->HighlightStyle();
+ aHiStyle->SetTransparency (0.8f);
+
+ Handle(Graphic3d_AspectFillArea3d) anAspect = new Graphic3d_AspectFillArea3d();
+ *anAspect = *myContext->DefaultDrawer()->ShadingAspect()->Aspect();
+ Graphic3d_MaterialAspect aMat (Graphic3d_NOM_STONE);
+ aMat.SetColor (aHiStyle->Color());
+ aMat.SetTransparency (aHiStyle->Transparency());
+ anAspect->SetFrontMaterial (aMat);
+ anAspect->SetInteriorColor (aHiStyle->Color());
+ aHiStyle->SetBasicFillAreaAspect (anAspect);
+ }
+ }
initPixelScaleRatio();
return true;
}
}
// ================================================================
-// Function : setCubemapBackground
+// Function : toDynamicHighlight
+// Purpose :
+// ================================================================
+bool WasmOcctView::toDynamicHighlight()
+{
+ return WasmOcctView::Instance().ToAllowHighlight();
+}
+
+// ================================================================
+// Function : setDynamicHighlight
+// Purpose :
+// ================================================================
+void WasmOcctView::setDynamicHighlight (bool theToEnable)
+{
+ WasmOcctView::Instance().SetAllowHighlight (theToEnable);
+}
+
+// ================================================================
+// Function : toCastShadows
+// Purpose :
+// ================================================================
+bool WasmOcctView::toCastShadows()
+{
+ return WasmOcctView::Instance().View()->RenderingParams().IsShadowEnabled;
+}
+
+// ================================================================
+// Function : setCastShadows
// Purpose :
// ================================================================
-void WasmOcctView::setCubemapBackground (const std::string& theImagePath)
+void WasmOcctView::setCastShadows (bool theUseShadows,
+ bool theToUpdate)
+{
+ if (WasmOcctView::Instance().View()->RenderingParams().IsShadowEnabled != theUseShadows)
+ {
+ WasmOcctView::Instance().View()->ChangeRenderingParams().IsShadowEnabled = theUseShadows;
+ if (theToUpdate)
+ {
+ WasmOcctView::Instance().UpdateView();
+ }
+ }
+}
+
+// ================================================================
+// Function : isAntiAliasingOn
+// Purpose :
+// ================================================================
+bool WasmOcctView::isAntiAliasingOn()
+{
+ return WasmOcctView::Instance().View()->RenderingParams().RenderResolutionScale > 1.1f;
+}
+
+// ================================================================
+// Function : setAntiAliasingOn
+// Purpose :
+// ================================================================
+void WasmOcctView::setAntiAliasingOn (bool theToEnable, bool theToUpdate)
+{
+ if (isAntiAliasingOn() != theToEnable)
+ {
+ WasmOcctView::Instance().View()->ChangeRenderingParams().RenderResolutionScale = theToEnable ? 2.0f : 1.0f;
+ if (theToUpdate)
+ {
+ WasmOcctView::Instance().UpdateView();
+ }
+ }
+}
+
+// ================================================================
+// Function : setBackgroundColor
+// Purpose :
+// ================================================================
+void WasmOcctView::setBackgroundColor (float theR, float theG, float theB,
+ bool theToUpdate)
+{
+ Quantity_Color aColor (theR, theG, theB, Quantity_TOC_RGB);
+ WasmOcctView::Instance().View()->SetBackgroundColor (aColor);
+ WasmOcctView::Instance().View()->SetBackgroundCubeMap (Handle(Graphic3d_CubeMap)(), true);
+ if (theToUpdate)
+ {
+ WasmOcctView::Instance().UpdateView();
+ }
+}
+
+// ================================================================
+// Function : setBackgroundCubemap
+// Purpose :
+// ================================================================
+void WasmOcctView::setBackgroundCubemap (const std::string& theImagePath)
{
if (!theImagePath.empty())
{
// Function : fitAllObjects
// Purpose :
// ================================================================
-void WasmOcctView::fitAllObjects (bool theAuto)
+void WasmOcctView::fitAllObjects (bool theAuto,
+ bool theToUpdate)
{
WasmOcctView& aViewer = Instance();
if (theAuto)
{
aViewer.View()->FitAll (0.01, false);
}
- aViewer.UpdateView();
+ if (theToUpdate)
+ {
+ aViewer.UpdateView();
+ }
}
+EM_JS(void, jsOnDisplayedObjectsChanged, (), {
+ Module.onDisplayedObjectsChanged();
+});
+EM_JS(void, jsOnSelectedObjectsChanged, (), {
+ Module.onSelectedObjectsChanged();
+});
+
// ================================================================
-// Function : removeAllObjects
+// Function : onDisplayedObjectsChanged
+// Purpose :
+// ================================================================
+void WasmOcctView::onDisplayedObjectsChanged()
+{
+ jsOnDisplayedObjectsChanged();
+}
+
+// ================================================================
+// Function : OnSelectionChanged
+// Purpose :
+// ================================================================
+void WasmOcctView::OnSelectionChanged (const Handle(AIS_InteractiveContext)& ,
+ const Handle(V3d_View)& )
+{
+ jsOnSelectedObjectsChanged();
+}
+
+// ================================================================
+// Function : displayedObjects
// Purpose :
// ================================================================
-void WasmOcctView::removeAllObjects()
+std::string WasmOcctView::displayedObjects()
{
WasmOcctView& aViewer = Instance();
+ std::string aList;
for (NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)>::Iterator anObjIter (aViewer.myObjects);
anObjIter.More(); anObjIter.Next())
{
- aViewer.Context()->Remove (anObjIter.Value(), false);
+ if (!aList.empty()) { aList += THE_LIST_SEPARATOR; }
+ aList += anObjIter.Key().ToCString();
}
- aViewer.myObjects.Clear();
- aViewer.UpdateView();
+ return aList;
}
// ================================================================
-// Function : removeObject
+// Function : selectedObjects
// Purpose :
// ================================================================
-bool WasmOcctView::removeObject (const std::string& theName)
+std::string WasmOcctView::selectedObjects()
{
WasmOcctView& aViewer = Instance();
- Handle(AIS_InteractiveObject) anObj;
- if (!theName.empty()
- && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+ std::string aList;
+ for (NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)>::Iterator anObjIter (aViewer.myObjects);
+ anObjIter.More(); anObjIter.Next())
{
- return false;
+ if (aViewer.Context()->IsSelected (anObjIter.Value()))
+ {
+ if (!aList.empty()) { aList += THE_LIST_SEPARATOR; }
+ aList += anObjIter.Key().ToCString();
+ }
}
+ return aList;
+}
- aViewer.Context()->Remove (anObj, false);
- aViewer.myObjects.RemoveKey (theName.c_str());
- aViewer.UpdateView();
+// ================================================================
+// Function : erasedObjects
+// Purpose :
+// ================================================================
+std::string WasmOcctView::erasedObjects()
+{
+ WasmOcctView& aViewer = Instance();
+ std::string aList;
+ for (NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)>::Iterator anObjIter (aViewer.myObjects);
+ anObjIter.More(); anObjIter.Next())
+ {
+ if (!aViewer.Context()->IsDisplayed (anObjIter.Value()))
+ {
+ if (!aList.empty()) { aList += THE_LIST_SEPARATOR; }
+ aList += anObjIter.Key().ToCString();
+ }
+ }
+ return aList;
+}
+
+// ================================================================
+// Function : displayObjectList
+// Purpose :
+// ================================================================
+bool WasmOcctView::displayObjectList (const std::string& theNames,
+ bool theToUpdate)
+{
+ WasmOcctView& aViewer = Instance();
+
+ std::stringstream aListStream (theNames);
+ std::string aName;
+ bool hasChanged = false;
+ while (std::getline (aListStream, aName, THE_LIST_SEPARATOR))
+ {
+ Handle(AIS_InteractiveObject) anObj;
+ if (!aViewer.myObjects.FindFromKey (aName.c_str(), anObj))
+ {
+ Message::SendFail() << "Error: unknown object '" << aName << "'";
+ return false;
+ }
+
+ hasChanged = true;
+ aViewer.Context()->Display (anObj, false);
+ }
+ if (theToUpdate
+ && hasChanged)
+ {
+ aViewer.UpdateView();
+ }
return true;
}
// ================================================================
-// Function : eraseObject
+// Function : removeObjectList
// Purpose :
// ================================================================
-bool WasmOcctView::eraseObject (const std::string& theName)
+bool WasmOcctView::removeObjectList (const std::string& theNames,
+ bool theToUpdate)
{
WasmOcctView& aViewer = Instance();
- Handle(AIS_InteractiveObject) anObj;
- if (!theName.empty()
- && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+
+ std::stringstream aListStream (theNames);
+ std::string aName;
+ bool hasChanged = false;
+ while (std::getline (aListStream, aName, THE_LIST_SEPARATOR))
{
- return false;
+ /// TODO filtering should be done for all keys starting with aName
+ Handle(AIS_InteractiveObject) anObj;
+ if (!aViewer.myObjects.FindFromKey (aName.c_str(), anObj))
+ {
+ //Message::SendFail() << "Error: unknown object '" << aName << "'";
+ return false;
+ }
+
+ hasChanged = true;
+ aViewer.Context()->Remove (anObj, false);
+ aViewer.myObjects.RemoveKey (aName.c_str());
}
- aViewer.Context()->Erase (anObj, false);
- aViewer.UpdateView();
+ if (hasChanged)
+ {
+ if (theToUpdate) { aViewer.UpdateView(); }
+ aViewer.onDisplayedObjectsChanged();
+ }
return true;
}
// ================================================================
-// Function : displayObject
+// Function : eraseObjectList
// Purpose :
// ================================================================
-bool WasmOcctView::displayObject (const std::string& theName)
+bool WasmOcctView::eraseObjectList (const std::string& theNames,
+ bool theToUpdate)
{
WasmOcctView& aViewer = Instance();
- Handle(AIS_InteractiveObject) anObj;
- if (!theName.empty()
- && !aViewer.myObjects.FindFromKey (theName.c_str(), anObj))
+
+ std::stringstream aListStream (theNames);
+ std::string aName;
+ bool hasChanged = false;
+ while (std::getline (aListStream, aName, THE_LIST_SEPARATOR))
{
- return false;
+ /// TODO filtering should be done for all keys starting with aName
+ Handle(AIS_InteractiveObject) anObj;
+ if (!aViewer.myObjects.FindFromKey (aName.c_str(), anObj))
+ {
+ Message::SendFail() << "Error: unknown object '" << aName << "'";
+ return false;
+ }
+
+ hasChanged = true;
+ aViewer.Context()->Erase (anObj, false);
}
- aViewer.Context()->Display (anObj, false);
- aViewer.UpdateView();
+ if (hasChanged
+ && theToUpdate)
+ {
+ aViewer.UpdateView();
+ }
return true;
}
+// ================================================================
+// Function : removeAllObjects
+// Purpose :
+// ================================================================
+void WasmOcctView::removeAllObjects (bool theToUpdate)
+{
+ WasmOcctView& aViewer = Instance();
+ if (aViewer.myObjects.IsEmpty())
+ {
+ return;
+ }
+
+ 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();
+ if (theToUpdate)
+ {
+ aViewer.UpdateView();
+ }
+ aViewer.onDisplayedObjectsChanged();
+}
+
// ================================================================
// Function : openFromUrl
// Purpose :
// ================================================================
void WasmOcctView::openFromUrl (const std::string& theName,
- const std::string& theModelPath)
+ const std::string& theModelPath,
+ const bool theToExpand)
{
- ModelAsyncLoader* aTask = new ModelAsyncLoader (theName.c_str(), theModelPath.c_str());
+ ModelAsyncLoader* aTask = new ModelAsyncLoader (theName.c_str(), theModelPath.c_str(), theToExpand);
emscripten_async_wget_data (theModelPath.c_str(), (void* )aTask, ModelAsyncLoader::onDataRead, ModelAsyncLoader::onReadFailed);
}
// Purpose :
// ================================================================
bool WasmOcctView::openFromMemory (const std::string& theName,
+ const bool theToExpand,
uintptr_t theBuffer, int theDataLen,
bool theToFree)
{
- removeObject (theName);
+ removeObjectList (theName, false);
char* aBytes = reinterpret_cast<char*>(theBuffer);
if (aBytes == nullptr
|| theDataLen <= 0)
if (dataStartsWithHeader(aBytes, "DBRep_DrawableShape"))
{
- return openBRepFromMemory (theName, theBuffer, theDataLen, theToFree);
+ return openBRepFromMemory (theName, theToExpand, theBuffer, theDataLen, theToFree);
}
else if (dataStartsWithHeader(aBytes, "glTF"))
{
- //return openGltfFromMemory (theName, theBuffer, theDataLen, theToFree);
+ return openGltfFromMemory (theName, theToExpand, theBuffer, theDataLen, theToFree);
}
if (theToFree)
{
// Purpose :
// ================================================================
bool WasmOcctView::openBRepFromMemory (const std::string& theName,
+ const bool theToExpand,
uintptr_t theBuffer, int theDataLen,
bool theToFree)
{
- removeObject (theName);
+ removeObjectList (theName, false);
- WasmOcctView& aViewer = Instance();
+ /*WasmOcctView& aViewer = Instance();
TopoDS_Shape aShape;
BRep_Builder aBuilder;
bool isLoaded = false;
Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + theName.c_str(), Message_Info);
Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
+ return true;*/
+ return false;
+}
+
+#include <OSD_OpenFile.hxx>
+#include <RWGltf_GltfJsonParser.hxx>
+#include <RWGltf_TriangulationReader.hxx>
+#include "WasmOcctObject.h"
+
+class WasmTriangulationReader : public RWGltf_TriangulationReader
+{
+public:
+
+ WasmTriangulationReader (std::istream& theStream,
+ const TCollection_AsciiString& theFile)
+ : myRootStream (&theStream), myRootPath (theFile)
+ {
+ //
+ }
+
+ virtual bool load (const Handle(RWGltf_GltfLatePrimitiveArray)& theMesh) override
+ {
+ reset();
+ if (theMesh.IsNull()
+ || theMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN)
+ {
+ return false;
+ }
+
+ for (NCollection_Sequence<RWGltf_GltfPrimArrayData>::Iterator aDataIter (theMesh->Data()); aDataIter.More(); aDataIter.Next())
+ {
+ const RWGltf_GltfPrimArrayData& aData = aDataIter.Value();
+ if (!aData.StreamData.IsNull())
+ {
+ Standard_ArrayStreamBuffer aStreamBuffer ((const char* )aData.StreamData->Data(), aData.StreamData->Size());
+ std::istream aStream (&aStreamBuffer);
+ aStream.seekg ((std::streamoff )aData.StreamOffset, std::ios_base::beg);
+ if (!readBuffer (aStream, theMesh->Id(), aData.Accessor, aData.Type, theMesh->PrimitiveMode()))
+ {
+ return false;
+ }
+ continue;
+ }
+ else if (aData.StreamUri.IsEmpty())
+ {
+ reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "' does not define uri.");
+ return false;
+ }
+
+ std::istream* aStream = &mySharedStream.Stream;
+ if (aData.StreamUri == myRootPath
+ && myRootStream != NULL)
+ {
+ aStream = myRootStream;
+ }
+ else if (mySharedStream.Path != aData.StreamUri)
+ {
+ mySharedStream.Stream.close();
+ mySharedStream.Path = aData.StreamUri;
+ }
+ if (aStream == &mySharedStream.Stream
+ && !mySharedStream.Stream.is_open())
+ {
+ OSD_OpenStream (mySharedStream.Stream, aData.StreamUri.ToCString(), std::ios::in | std::ios::binary);
+ if (!mySharedStream.Stream.is_open())
+ {
+ mySharedStream.Stream.close();
+ reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "refers to non-existing file '" + aData.StreamUri + "'.");
+ return false;
+ }
+ }
+
+ aStream->seekg ((std::streamoff )aData.StreamOffset, std::ios_base::beg);
+ if (!aStream->good())
+ {
+ mySharedStream.Stream.close();
+ reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "refers to invalid location.");
+ return false;
+ }
+
+ if (!readBuffer (*aStream, theMesh->Id(), aData.Accessor, aData.Type, theMesh->PrimitiveMode()))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+private:
+
+ std::istream* myRootStream;
+ TCollection_AsciiString myRootPath;
+
+};
+
+//! Parse glTF data.
+static bool parseGltfFromMemory (RWGltf_GltfJsonParser& theParser,
+ std::istream& theStream,
+ const TCollection_AsciiString& theFile)
+{
+ bool isBinaryFile = false;
+ char aGlbHeader[12] = {};
+ theStream.read (aGlbHeader, sizeof(aGlbHeader));
+ int64_t aBinBodyOffset = 0, aBinBodyLen = 0, aJsonBodyOffset = 0, aJsonBodyLen = 0;
+ if (::strncmp (aGlbHeader, "glTF", 4) == 0)
+ {
+ isBinaryFile = true;
+ const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
+ const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
+ if (*aVer != 2)
+ {
+ Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer));
+ return false;
+ }
+
+ for (int aChunkIter = 0; !theStream.eof() && aChunkIter < 2; ++aChunkIter)
+ {
+ char aChunkHeader2[8] = {};
+ if (int64_t(theStream.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(*aLen))
+ {
+ break;
+ }
+
+ theStream.read (aChunkHeader2, sizeof(aChunkHeader2));
+ if (!theStream.good())
+ {
+ Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format");
+ return false;
+ }
+
+ const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
+ const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
+ if (*aChunkType == 0x4E4F534A)
+ {
+ aJsonBodyOffset = int64_t(theStream.tellg());
+ aJsonBodyLen = int64_t(*aChunkLen);
+ }
+ else if (*aChunkType == 0x004E4942)
+ {
+ aBinBodyOffset = int64_t(theStream.tellg());
+ aBinBodyLen = int64_t(*aChunkLen);
+ }
+ if (*aChunkLen != 0)
+ {
+ theStream.seekg (*aChunkLen, std::ios_base::cur);
+ }
+ }
+
+ theStream.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
+ }
+ else
+ {
+ theStream.seekg (0, std::ios_base::beg);
+ }
+ if (isBinaryFile)
+ {
+ theParser.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
+ }
+
+ rapidjson::ParseResult aRes;
+ rapidjson::IStreamWrapper aFileStream (theStream);
+ if (isBinaryFile)
+ {
+ aRes = theParser.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
+ }
+ else
+ {
+ aRes = theParser.ParseStream (aFileStream);
+ }
+ if (aRes.IsError())
+ {
+ if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
+ {
+ Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is empty");
+ return false;
+ }
+ TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
+ Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
+ + anErrDesc + " [at offset " + (int )aRes.Offset() + "].");
+ return false;
+ }
+
+ if (!theParser.Parse (Message_ProgressRange()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// ================================================================
+// Function : openGltfFromMemory
+// Purpose :
+// ================================================================
+bool WasmOcctView::openGltfFromMemory (const std::string& theName,
+ const bool theToExpand,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree)
+{
+ removeObjectList (theName, false);
+
+ WasmOcctView& aViewer = Instance();
+
+ char* aRawData = reinterpret_cast<char*>(theBuffer);
+ Standard_ArrayStreamBuffer aStreamBuffer (aRawData, theDataLen);
+ std::istream aStream (&aStreamBuffer);
+
+ Handle(WasmOcctObject) aShapePrs = new WasmOcctObject();
+
+ RWMesh_CoordinateSystemConverter aTrsf;
+ aTrsf.SetInputLengthUnit (1.0); // meters
+ aTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
+ aTrsf.SetOutputLengthUnit (1.0); // meters
+ aTrsf.SetOutputCoordinateSystem(RWMesh_CoordinateSystem_Zup);
+
+ RWGltf_GltfJsonParser aParser (aShapePrs->ChangeShapes());
+ aParser.SetFilePath (theName.c_str());
+ aParser.SetErrorPrefix (TCollection_AsciiString ("File '") + theName.c_str() + "' defines invalid glTF!\n");
+ aParser.SetAttributeMap (aShapePrs->ChangeAttributes());
+ aParser.SetCoordinateSystemConverter (aTrsf);
+ //aParser.SetSkipEmptyNodes (myToSkipEmptyNodes);
+ //aParser.SetMeshNameAsFallback (myUseMeshNameAsFallback);
+ bool isParsed = parseGltfFromMemory (aParser, aStream, theName.c_str());
+ if (isParsed)
+ {
+ Handle(RWGltf_PrimitiveArrayReader) aReader = new WasmTriangulationReader (aStream, theName.c_str());
+ aReader->SetCoordinateSystemConverter (aTrsf);
+ for (NCollection_Vector<TopoDS_Face>::Iterator aFaceIter (aParser.FaceList()); aFaceIter.More(); aFaceIter.Next())
+ {
+ TopoDS_Face& aFace = aFaceIter.ChangeValue();
+ TopLoc_Location aDummyLoc;
+ Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (aFace, aDummyLoc));
+ Handle(Poly_Triangulation) aPolyData = aReader->Load (aLateData);
+ BRep_Builder aBuilder;
+ aBuilder.UpdateFace (aFace, aPolyData);
+ }
+ }
+ if (theToFree)
+ {
+ free (aRawData);
+ }
+
+ if (!isParsed)
+ {
+ return false;
+ }
+
+ if (theToExpand)
+ {
+ /// TODO this is just a dummy logic for testing - expanding should be done by assembly tree, not faces!
+ Standard_Integer aSubIndex = 0;
+ for (TopTools_SequenceOfShape::Iterator aShapeIter (aShapePrs->ChangeShapes()); aShapeIter.More(); aShapeIter.Next())
+ {
+ for (TopExp_Explorer aFaceIter (aShapeIter.Value(), TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
+ {
+ const TopoDS_Shape& aSubShape = aFaceIter.Current();
+ Handle(WasmOcctObject) aSubShapePrs = new WasmOcctObject();
+ aSubShapePrs->SetShape (aSubShape);
+ if (const RWMesh_NodeAttributes* anAttribs = aShapePrs->ChangeAttributes().Seek (aSubShape.Located (TopLoc_Location())))
+ {
+ aSubShapePrs->ChangeAttributes().Bind (aSubShape.Located (TopLoc_Location()), *anAttribs);
+ }
+ if (!theName.empty())
+ {
+ ++aSubIndex;
+ TCollection_AsciiString aName = TCollection_AsciiString (theName.c_str()) + "/" + aSubIndex;
+ aViewer.myObjects.Add (aName, aSubShapePrs);
+ }
+ aViewer.Context()->Display (aSubShapePrs, 0, 0, false);
+ }
+ }
+ }
+ else
+ {
+ if (!theName.empty())
+ {
+ aViewer.myObjects.Add (theName.c_str(), aShapePrs);
+ }
+ aViewer.Context()->Display (aShapePrs, 0, 0, false);
+ }
+
+ aViewer.View()->FitAll (0.01, false);
+ aViewer.View()->Invalidate();
+ aViewer.updateView();
+
+ setShowGround (aViewer.myToShowGround, false);
+
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + theName.c_str(), Message_Info);
+ Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
+ if (!theName.empty())
+ {
+ aViewer.onDisplayedObjectsChanged();
+ }
return true;
}
// ================================================================
-// Function : displayGround
+// Function : toShowGround
+// Purpose :
+// ================================================================
+bool WasmOcctView::toShowGround()
+{
+ return Instance().myToShowGround;
+}
+
+// ================================================================
+// Function : setShowGround
// Purpose :
// ================================================================
-void WasmOcctView::displayGround (bool theToShow)
+void WasmOcctView::setShowGround (bool theToShow,
+ bool theToUpdate)
{
- static Handle(AIS_Shape) aGroundPrs = new AIS_Shape (TopoDS_Shape());
+ static Handle(WasmOcctObject) aGroundPrs = new WasmOcctObject();
WasmOcctView& aViewer = Instance();
+ aViewer.myToShowGround = theToShow;
Bnd_Box aBox;
if (theToShow)
{
&& aGroundPrs->HasInteractiveContext())
{
aViewer.Context()->Remove (aGroundPrs, false);
- aViewer.UpdateView();
+ if (theToUpdate)
+ {
+ aViewer.UpdateView();
+ }
}
return;
}
aGroundPrs->SetToUpdate();
aGroundPrs->SetMaterial (Graphic3d_NameOfMaterial_Stone);
aGroundPrs->SetInfiniteState (false);
- aViewer.Context()->Display (aGroundPrs, AIS_Shaded, -1, false);
+ aViewer.Context()->Display (aGroundPrs, 0, -1, false);
aGroundPrs->SetInfiniteState (true);
- aViewer.UpdateView();
+ if (theToUpdate)
+ {
+ 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());
+ emscripten::function("toDynamicHighlight", &WasmOcctView::toDynamicHighlight);
+ emscripten::function("setDynamicHighlight", &WasmOcctView::setDynamicHighlight);
+ emscripten::function("toCastShadows", &WasmOcctView::toCastShadows);
+ emscripten::function("setCastShadows", &WasmOcctView::setCastShadows);
+ //emscripten::property("toCastShadows", &WasmOcctView::toCastShadows, &WasmOcctView::setCastShadows); // this could be only in class
+ emscripten::function("isAntiAliasingOn", &WasmOcctView::isAntiAliasingOn);
+ emscripten::function("setAntiAliasingOn", &WasmOcctView::setAntiAliasingOn);
+ emscripten::function("setBackgroundColor", &WasmOcctView::setBackgroundColor);
+ emscripten::function("setBackgroundCubemap", &WasmOcctView::setBackgroundCubemap);
+ emscripten::function("fitAllObjects", &WasmOcctView::fitAllObjects);
+ emscripten::function("removeAllObjects", &WasmOcctView::removeAllObjects);
+ emscripten::function("removeObjectList", &WasmOcctView::removeObjectList);
+ emscripten::function("eraseObjectList", &WasmOcctView::eraseObjectList);
+ emscripten::function("displayedObjects", &WasmOcctView::displayedObjects);
+ emscripten::function("selectedObjects", &WasmOcctView::selectedObjects);
+ emscripten::function("erasedObjects", &WasmOcctView::erasedObjects);
+ emscripten::function("displayObjectList", &WasmOcctView::displayObjectList);
+ emscripten::function("toShowGround", &WasmOcctView::toShowGround);
+ emscripten::function("setShowGround", &WasmOcctView::setShowGround);
+ emscripten::function("openFromUrl", &WasmOcctView::openFromUrl);
+ emscripten::function("openFromMemory", &WasmOcctView::openFromMemory, emscripten::allow_raw_pointers());
+ emscripten::function("openBRepFromMemory", &WasmOcctView::openBRepFromMemory, emscripten::allow_raw_pointers());
+ emscripten::function("openGltfFromMemory", &WasmOcctView::openGltfFromMemory, emscripten::allow_raw_pointers());
}
public: //! @name methods exported by Module
+ //! Returns TRUE if dynamic highlighting is turned ON.
+ static bool toDynamicHighlight();
+
+ //! Set if dynamic highlighting should be enabled or not.
+ static void setDynamicHighlight (bool theToEnable);
+
+ //! Returns TRUE if shadows are turned ON.
+ static bool toCastShadows();
+
+ //! Turn shadows on/off.
+ static void setCastShadows (bool theUseShadows,
+ bool theToUpdate);
+
+ //! Returns TRUE if anti-aliasing is turned ON.
+ static bool isAntiAliasingOn();
+
+ //! Turn antialiasing on/off.
+ static void setAntiAliasingOn (bool theToEnable, bool theToUpdate);
+
+ //! Set solid color background.
+ static void setBackgroundColor (float theR, float theG, float theB,
+ bool theToUpdate);
+
//! Set cubemap background.
//! File will be loaded asynchronously.
//! @param theImagePath [in] image path to load
- static void setCubemapBackground (const std::string& theImagePath);
+ static void setBackgroundCubemap (const std::string& theImagePath);
//! Clear all named objects from viewer.
- static void removeAllObjects();
+ static void removeAllObjects (bool theToUpdate);
//! Fit all/selected objects into view.
//! @param theAuto [in] fit selected objects (TRUE) or all objects (FALSE)
- static void fitAllObjects (bool theAuto);
+ static void fitAllObjects (bool theAuto,
+ bool theToUpdate);
+
+ //! Return the list of displayed objects.
+ static std::string displayedObjects();
+
+ //! Return the list of selected objects.
+ static std::string selectedObjects();
+
+ //! Return the list of erased objects.
+ static std::string erasedObjects();
- //! Remove named object from viewer.
- //! @param theName [in] object name
+ //! Display temporarily hidden object.
+ //! @param theNames [in] object name list
//! @return FALSE if object was not found
- static bool removeObject (const std::string& theName);
+ static bool displayObjectList (const std::string& theNames,
+ bool theToUpdate);
- //! Temporarily hide named object.
- //! @param theName [in] object name
+ //! Remove named objects from viewer.
+ //! @param theNames [in] object name found
//! @return FALSE if object was not found
- static bool eraseObject (const std::string& theName);
+ static bool removeObjectList (const std::string& theNames,
+ bool theToUpdate);
- //! Display temporarily hidden object.
- //! @param theName [in] object name
+ //! Temporarily hide named objects.
+ //! @param theNames [in] object name list
//! @return FALSE if object was not found
- static bool displayObject (const std::string& theName);
+ static bool eraseObjectList (const std::string& theNames,
+ bool theToUpdate);
+
+ //! Return TRUE if ground is displayed.
+ static bool toShowGround();
//! Show/hide ground.
//! @param theToShow [in] show or hide flag
- static void displayGround (bool theToShow);
+ static void setShowGround (bool theToShow,
+ bool theToUpdate);
//! Open object from the given URL.
//! File will be loaded asynchronously.
//! @param theName [in] object name
//! @param theModelPath [in] model path
+ //! @param theToExpand [in] expand model or display as a single part
static void openFromUrl (const std::string& theName,
- const std::string& theModelPath);
+ const std::string& theModelPath,
+ const bool theToExpand);
//! 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
+ //! @param theName [in] object name
+ //! @param theToExpand [in] expand model or display as a single part
+ //! @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,
+ const bool theToExpand,
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
+ //! @param theName [in] object name
+ //! @param theToExpand [in] expand model or display as a single part
+ //! @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,
+ const bool theToExpand,
+ uintptr_t theBuffer, int theDataLen,
+ bool theToFree);
+
+ //! Open glTF object from memory.
+ //! @param theName [in] object name
+ //! @param theToExpand [in] expand model or display as a single part
+ //! @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 openGltfFromMemory (const std::string& theName,
+ const bool theToExpand,
uintptr_t theBuffer, int theDataLen,
bool theToFree);
+ //! Displayed map changed notification.
+ void onDisplayedObjectsChanged();
+
public:
//! Default constructor.
static EM_BOOL onKeyUpCallback (int theEventType, const EmscriptenKeyboardEvent* theEvent, void* theView)
{ return ((WasmOcctView* )theView)->onKeyUpEvent (theEventType, theEvent); }
+ //! Callback called by handleMoveTo() on Selection in 3D Viewer.
+ //! This method is expected to be called from rendering thread.
+ virtual void OnSelectionChanged (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView) override;
+
private:
//! Register hot-keys for specified Action.
private:
NCollection_IndexedDataMap<TCollection_AsciiString, Handle(AIS_InteractiveObject)> myObjects; //!< map of named objects
+ bool myToShowGround = true;
+
NCollection_DataMap<unsigned int, Aspect_VKey> myNavKeyMap; //!< map of Hot-Key (key+modifiers) to Action
EMSCRIPTEN_KEEPALIVE int main()
{
- Message::DefaultMessenger()->Printers().First()->SetTraceLevel (Message_Trace);
+ Message::DefaultMessenger()->Printers().First()->SetTraceLevel (Message_Info);
Handle(Message_PrinterSystemLog) aJSConsolePrinter = new Message_PrinterSystemLog ("webgl-sample", Message_Trace);
Message::DefaultMessenger()->AddPrinter (aJSConsolePrinter); // open JavaScript console within the Browser to see this output
Message::DefaultMessenger()->Send (TCollection_AsciiString("NbLogicalProcessors: ") + OSD_Parallel::NbLogicalProcessors(), Message_Trace);
\r
<h2>OCCT WebGL Viewer Sample</h2>\r
<div>\r
- <canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>\r
- <img id=occlogo src="OCC_logo.png" style="position: absolute; left: 20px; top: 0px; z-index: 2;" />\r
+ <canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>\r
</div>\r
\r
<div>\r
- <label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep">\r
- <input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects()">\r
- <input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true)">\r
+ <input type="checkbox" id="inputCheckShadows"><label for="inputCheckShadows">Shadows</label>\r
+ <input type="checkbox" id="inputAntiAliasing"><label for="inputAntiAliasing">Antialiasing</label>\r
+ <input type="checkbox" id="inputCheckGround" ><label for="inputCheckGround">Ground</label>\r
+ <input type="checkbox" id="inputCheckSkyBox" ><label for="inputCheckSkyBox">SkyBox</label>\r
+ <input type="checkbox" id="inputCheckHighlight"><label for="inputCheckHighlight">Dynamic highlight</label>\r
+ <br>\r
+ <input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects(true)">\r
+ <input type="file" id="inputUploadModel" accept=".gltf, .glb">\r
+ <input type="button" value="Upload File" onclick="doInputUploadModel()">\r
+ <input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true, true)">\r
+ <input type="button" value="Hide selected" onclick="doEraseSelected()">\r
+ <input type="button" value="Show hidden" onclick="doShowErased()">\r
+ <br>\r
+ <input type="checkbox" id="inputCheckExpand" ><label for="inputCheckExpand">Expand model</label>\r
</div>\r
<h4>Console output:</h4>\r
<p id="output"></p>\r
+\r
+<script type="text/javascript" src="occt-webgl-sample.js" charset="utf-8"></script>\r
<script>\r
//! Resize canvas to fit into window.\r
function updateCanvasSize()\r
var aDevicePixelRatio = window.devicePixelRatio || 1;\r
occViewerCanvas.width = aSizeX * aDevicePixelRatio;\r
occViewerCanvas.height = aSizeY * aDevicePixelRatio;\r
-\r
- occlogo.style.top = (aSizeY - 30) + "px";\r
}\r
window.onresize = updateCanvasSize;\r
updateCanvasSize();\r
anElement.innerHTML += "Browser is too old - WebAssembly support is missing!<br>Please check updates or install a modern browser.<br>";\r
}\r
\r
+var OccViewerModule =\r
+{\r
+ print: (function() {\r
+ var anElement = document.getElementById('output');\r
+ return function(theText) { anElement.innerHTML += theText + "<br>"; };\r
+ })(),\r
+ printErr: function(theText) { console.warn(theText); },\r
+ canvas: (function() {\r
+ var aCanvas = document.getElementById('occViewerCanvas');\r
+ //var aGlCtx = aCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } );\r
+ //if (aGlCtx == null) { aGlCtx = aCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } ); }\r
+ return aCanvas;\r
+ })(),\r
+\r
+ onRuntimeInitialized: function() {\r
+ //console.log(" @@ onRuntimeInitialized()" + Object.getOwnPropertyNames(OccViewerModule));\r
+ },\r
+\r
+ onDisplayedObjectsChanged: function() {\r
+ console.log(" @@ onDisplayedObjectsChanged() " + OccViewerModule.displayedObjects());\r
+ },\r
+ onSelectedObjectsChanged: function() {\r
+ console.log(" @@ onSelectedObjectsChanged() " + OccViewerModule.selectedObjects());\r
+ }\r
+};\r
+\r
+const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);\r
+OccViewerModuleInitialized.then(function(Module) {\r
+ //var aSkyBox = "textures/cubemap512.jpg"; // "textures/cubemap2048.jpg";\r
+ var aSkyBox = "textures/cubemap2048.jpg";\r
+ var aDefModel = "models/yellow_up.glb";\r
+ OccViewerModule.setBackgroundCubemap (aSkyBox);\r
+ OccViewerModule.openFromUrl (aDefModel, aDefModel, false);\r
+\r
+ inputCheckShadows.checked = OccViewerModule.toCastShadows();\r
+ inputCheckShadows.onchange = function() { OccViewerModule.setCastShadows (inputCheckShadows.checked, true); }\r
+ inputAntiAliasing.checked = OccViewerModule.isAntiAliasingOn();\r
+ inputAntiAliasing.onchange = function() { OccViewerModule.setAntiAliasingOn (inputAntiAliasing.checked, true); }\r
+ inputCheckGround .checked = OccViewerModule.toShowGround();\r
+ inputCheckGround .onchange = function() { OccViewerModule.setShowGround (inputCheckGround.checked, true); }\r
+ inputCheckHighlight.checked = OccViewerModule.toDynamicHighlight();\r
+ inputCheckHighlight.onchange = function() { OccViewerModule.setDynamicHighlight (inputCheckHighlight.checked); }\r
+ inputCheckSkyBox .checked = true;\r
+ inputCheckSkyBox .onchange = function() {\r
+ if (inputCheckSkyBox.checked) { OccViewerModule.setBackgroundCubemap (aSkyBox); }\r
+ else { OccViewerModule.setBackgroundColor (0.0, 0.0, 0.0, true); }\r
+ }\r
+ inputCheckExpand.checked = false;\r
+ inputCheckExpand.onchange = function() {\r
+ OccViewerModule.removeAllObjects (true);\r
+ OccViewerModule.openFromUrl (aDefModel, aDefModel, inputCheckExpand.checked);\r
+ }\r
+});\r
+\r
+//! Handle erasing.\r
+function doEraseSelected()\r
+{\r
+ var aSelected = OccViewerModule.selectedObjects();\r
+ OccViewerModule.eraseObjectList (aSelected, true);\r
+}\r
+\r
+//! Handle show all.\r
+function doShowErased()\r
+{\r
+ var anErased = OccViewerModule.erasedObjects();\r
+ OccViewerModule.displayObjectList (anErased, true);\r
+}\r
+\r
//! Handle file uploading.\r
-fileInput.onchange = function()\r
+function doInputUploadModel()\r
{\r
- if (fileInput.files.length == 0) { return; }\r
- // Warning! Entire file is pre-loaded into memory.\r
- var aFile = fileInput.files[0];\r
- var aReader = new FileReader();\r
- aReader.onload = function()\r
- {\r
+ if (inputUploadModel.files.length == 0) { return; }\r
+ var aFile = inputUploadModel.files[0];\r
+ var aReader = new FileReader(); // Warning! Entire file is pre-loaded into memory\r
+ aReader.onload = function() {\r
var aDataArray = new Uint8Array (aReader.result);\r
const aDataBuffer = OccViewerModule._malloc (aDataArray.length);\r
OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);\r
- OccViewerModule.openFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);\r
- //OccViewerModule._free (aDataBuffer); will be freed by called method\r
- OccViewerModule.displayGround (true);\r
+ OccViewerModule.openFromMemory (aFile.name, inputCheckExpand.checked, aDataBuffer, aDataArray.length, true);\r
};\r
- aReader.readAsArrayBuffer(aFile);\r
+ aReader.readAsArrayBuffer (aFile);\r
};\r
+\r
</script>\r
-<script type="text/javascript" src="occt-webgl-sample.js" charset="utf-8"></script>\r
</body>\r
</html>\r
+++ /dev/null
-var OccViewerModule =\r
-{\r
- print: (function() {\r
- var anElement = document.getElementById('output');\r
- return function(theText) { anElement.innerHTML += theText + "<br>"; };\r
- })(),\r
- printErr: function(theText) {\r
- //var anElement = document.getElementById('output');\r
- //anElement.innerHTML += theText + "<br>";\r
- console.warn(theText);\r
- },\r
- canvas: (function() {\r
- var aCanvas = document.getElementById('occViewerCanvas');\r
- var aGlCtx = aCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } );\r
- if (aGlCtx == null) { aGlCtx = aCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } ); }\r
- return aCanvas;\r
- })(),\r
-\r
- onRuntimeInitialized: function() {\r
- //console.log(" @@ onRuntimeInitialized()" + Object.getOwnPropertyNames(OccViewerModule));\r
- }\r
-};\r
-\r
-const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);\r
-OccViewerModuleInitialized.then(function(Module) {\r
- //OccViewerModule.setCubemapBackground ("cubemap.jpg");\r
- OccViewerModule.openFromUrl ("ball", "samples/Ball.brep");\r
-});\r
--- /dev/null
+<!DOCTYPE html>\r
+<html lang=en-us>\r
+<head>\r
+<meta charset=utf-8><meta content="text/html; charset=utf-8" http-equiv=Content-Type>\r
+<link rel="shortcut icon" href="lamp.ico" type="image/x-icon" />\r
+<title>Three.js Viewer Sample</title>\r
+</head>\r
+<body>\r
+\r
+<h2>Three.js Viewer Sample</h2>\r
+<div>\r
+ <canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>\r
+</div>\r
+\r
+<div>\r
+ <input type="checkbox" id="inputCheckShadows"><label for="inputCheckShadows">Shadows</label>\r
+ <input type="checkbox" id="inputAntiAliasing"><label for="inputAntiAliasing">Antialiasing</label>\r
+ <input type="checkbox" id="inputCheckGround" ><label for="inputCheckGround">Ground</label>\r
+ <input type="checkbox" id="inputCheckSkyBox" ><label for="inputCheckSkyBox">SkyBox</label>\r
+ <input type="checkbox" id="inputCheckHighlight"><label for="inputCheckHighlight">Dynamic highlight</label>\r
+ <br>\r
+ <input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects(true)">\r
+ <input type="file" id="inputUploadModel" accept=".gltf, .glb">\r
+ <input type="button" value="Upload File" onclick="doInputUploadModel()">\r
+ <input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true, true)">\r
+ <input type="button" value="Hide selected" onclick="doEraseSelected()">\r
+ <input type="button" value="Show hidden" onclick="doShowErased()">\r
+ <br>\r
+ <input type="checkbox" id="inputCheckExpand" ><label for="inputCheckExpand">Expand model</label>\r
+</div>\r
+<h4>Console output:</h4>\r
+<p id="output"></p>\r
+\r
+<script src="threejs/three.min.js"></script>\r
+<script src="threejs/OrbitControls.js"></script>\r
+<script src="threejs/GLTFLoader.js"></script>\r
+<script src="threejs/BufferGeometryUtils.js"></script>\r
+<script src="threejs/stats.min.js"></script>\r
+<script src="threejs-sample.js"></script>\r
+<script>\r
+//! Resize canvas to fit into window.\r
+function updateCanvasSize()\r
+{\r
+ // size of canvas in logical (density-independent) units\r
+ var aSizeX = Math.min (window.innerWidth, window.screen.availWidth);\r
+ var aSizeY = Math.min (window.innerHeight, window.screen.availHeight);\r
+ aSizeX = Math.max (300, aSizeX - 30);\r
+ aSizeY = Math.max (300, aSizeY / 2);\r
+ occViewerCanvas.style.width = aSizeX + "px";\r
+ occViewerCanvas.style.height = aSizeY + "px";\r
+\r
+ // drawing buffer size (aka backing store)\r
+ var aDevicePixelRatio = window.devicePixelRatio || 1;\r
+ occViewerCanvas.width = aSizeX * aDevicePixelRatio;\r
+ occViewerCanvas.height = aSizeY * aDevicePixelRatio;\r
+}\r
+window.onresize = updateCanvasSize;\r
+updateCanvasSize();\r
+\r
+var aSkyBox = "textures/landing_pad/";\r
+var aDefModel = "models/yellow_up.glb";\r
+var aDefName = "yellow";\r
+var aDevicePixelRatio = window.devicePixelRatio || 1;\r
+inputAntiAliasing.checked = aDevicePixelRatio <= 1.25;\r
+var OccViewerModule = createOccThreejsViewer (document.getElementById ('occViewerCanvas'), aSkyBox, inputAntiAliasing.checked);\r
+OccViewerModule.openFromUrl (aDefName, aDefModel, false);\r
+//OccViewerModule.openFromUrl ("stork", "models/Stork.glb");\r
+//OccViewerModule.openFromUrl ("spheres", "models/spheres.glb");\r
+\r
+inputCheckShadows.checked = OccViewerModule.toCastShadows();\r
+inputCheckShadows.onchange = function() { OccViewerModule.setCastShadows (inputCheckShadows.checked, true); }\r
+inputAntiAliasing.onchange = function() {}\r
+inputCheckGround .checked = OccViewerModule.toShowGround();\r
+inputCheckGround .onchange = function() { OccViewerModule.setShowGround (inputCheckGround.checked, true); }\r
+inputCheckHighlight.checked = OccViewerModule.toDynamicHighlight();\r
+inputCheckHighlight.onchange = function() { OccViewerModule.setDynamicHighlight (inputCheckHighlight.checked); }\r
+inputCheckSkyBox .checked = true;\r
+inputCheckSkyBox .onchange = function() {\r
+ if (inputCheckSkyBox.checked) { OccViewerModule.setBackgroundCubemap (aSkyBox, true); }\r
+ else { OccViewerModule.setBackgroundColor (0.0, 0.0, 0.0, true); }\r
+}\r
+inputCheckExpand.checked = false;\r
+inputCheckExpand.onchange = function() {\r
+ OccViewerModule.removeAllObjects (true);\r
+ OccViewerModule.openFromUrl (aDefName, aDefModel, inputCheckExpand.checked);\r
+}\r
+\r
+//! Handle file uploading.\r
+inputUploadModel.onchange = function()\r
+{\r
+ if (inputUploadModel.files.length == 0) { return; }\r
+ // Warning! Entire file is pre-loaded into memory.\r
+ var aFile = inputUploadModel.files[0];\r
+ /*var aReader = new FileReader();\r
+ aReader.onload = function()\r
+ {\r
+ var aDataArray = new Uint8Array (aReader.result);\r
+ const aDataBuffer = OccViewerModule._malloc (aDataArray.length);\r
+ OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);\r
+ //OccViewerModule.openBRepFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);\r
+ OccViewerModule.openGltfFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);\r
+ //OccViewerModule._free (aDataBuffer); will be freed by called method\r
+ OccViewerModule.displayGround (true);\r
+ };\r
+ aReader.readAsArrayBuffer(aFile);*/\r
+ //OccViewerModule.openFromUrl ("object", aFile.name);\r
+};\r
+</script>\r
+</body>\r
+</html>\r
--- /dev/null
+class OccThreejsViewer\r
+{\r
+ myScene = new THREE.Scene();\r
+ myRaycaster = new THREE.Raycaster();\r
+ myObjects = new Map();\r
+ myLastPicked = null;\r
+ myLastPickedName = "";\r
+ myToDynamicHighlight = true;\r
+ myDynHighlightColor = 0x00FFFF;\r
+ mySelectionColor = 0xFFFF00;\r
+ myGround = null;\r
+ myDirLight = new THREE.DirectionalLight (0xffffff, 1);\r
+\r
+ myFpsMeter = new Stats();\r
+\r
+ constructor (theCanvas, theCubemap, theAntiAlias)\r
+ {\r
+ var aViewer = this;\r
+\r
+ var aGlCtx = theCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: theAntiAlias, preserveDrawingBuffer: true } );\r
+ if (aGlCtx == null) { aGlCtx = theCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: theAntiAlias, preserveDrawingBuffer: true } ); }\r
+ this.myCamera = new THREE.PerspectiveCamera (45, theCanvas.width / theCanvas.height, 0.1, 1000);\r
+ this.myCamera.position.set (1, 1, 1);\r
+\r
+ this.myRenderer = new THREE.WebGLRenderer ({antialias: false, canvas: theCanvas, context: aGlCtx});\r
+ this.myRenderer.autoClear = true;\r
+ this.myRenderer.autoClearColor = true;\r
+ this.myRenderer.autoClearDepth = true;\r
+ this.myRenderer.autoClearStencil = true;\r
+ this.myRenderer.setSize (theCanvas.width, theCanvas.height);\r
+ this.myRenderer.outputEncoding = THREE.sRGBEncoding;\r
+ this.myRenderer.shadowMap.enabled = true;\r
+ //this.myRenderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap\r
+\r
+ //this.myScene.background = new THREE.Color('black');\r
+ this.setBackgroundCubemap (theCubemap, false);\r
+\r
+ this.myControls = new THREE.OrbitControls (this.myCamera, theCanvas);\r
+ this.myControls.target.set (0, 0, -0.2);\r
+ this.myControls.update();\r
+ this.myControls.addEventListener ('change', function() { aViewer.updateView(); });\r
+\r
+ this.myDirLight.position.set (-0.098, 0.98, -0.20);\r
+ this.myDirLight.position.normalize()\r
+ this.myDirLight.castShadow = true;\r
+ this.myScene.add (new THREE.HemisphereLight (0xffffff, 0x000000, 0.4));\r
+ this.myScene.add (this.myDirLight);\r
+ //const aShadowHelper = new THREE.CameraHelper (this.myDirLight.shadow.camera); this.myScene.add (aShadowHelper)\r
+\r
+ const aPlaneGeom = new THREE.PlaneBufferGeometry (15, 15, 32, 32);\r
+ const aPlaneMat = new THREE.MeshStandardMaterial ({ color: 0xAAAAAA });\r
+ this.myGround = new THREE.Mesh (aPlaneGeom, aPlaneMat);\r
+ this.myGround.receiveShadow = true;\r
+ this.myGround.position.y = -1.0;\r
+ this.myGround.rotation.x = -1.57;\r
+ this.myScene.add (this.myGround);\r
+ this.updateView();\r
+\r
+ theCanvas.addEventListener ('click', this.onClick, false);\r
+ theCanvas.addEventListener ('mousemove', this.onMouseMove, false);\r
+\r
+ // FPS meter\r
+ this.myFpsMeter.showPanel (0);\r
+ document.body.appendChild (this.myFpsMeter.dom);\r
+ }\r
+\r
+ toDynamicHighlight() { return this.myToDynamicHighlight; }\r
+ setDynamicHighlight(theToEnable) { this.myToDynamicHighlight = theToEnable; }\r
+\r
+ onMouseMove()\r
+ {\r
+ if (event.buttons !== 0) { return; }\r
+\r
+ var aViewer = OccViewerModule;\r
+ if (!aViewer.myToDynamicHighlight) { return; }\r
+\r
+ var aTime1 = performance.now();\r
+ event.preventDefault();\r
+ var aMouse = new THREE.Vector2();\r
+ aMouse.x = (event.clientX / aViewer.myRenderer.domElement.width) * 2 - 1;\r
+ aMouse.y = -(event.clientY / aViewer.myRenderer.domElement.height) * 2 + 1;\r
+ aViewer.myRaycaster.setFromCamera (aMouse, aViewer.myCamera);\r
+ var anRes = aViewer.myRaycaster.intersectObject (aViewer.myScene, true);\r
+ var aNewPicked = anRes.length > 0 ? anRes[0].object : null;\r
+ if (aNewPicked !== aViewer.myLastPicked)\r
+ {\r
+ aViewer.myLastPickedName = "";\r
+ if (aViewer.myLastPicked !== null)\r
+ {\r
+ aViewer.myLastPicked.material.color.set (aViewer.myLastPicked.material.userData.oldColor);\r
+ aViewer.myLastPicked = null;\r
+ }\r
+ if (aNewPicked !== null)\r
+ {\r
+ for (var aParentIter = aNewPicked; aParentIter != null; aParentIter = aParentIter.parent)\r
+ {\r
+ if (aParentIter.name !== "")\r
+ {\r
+ if (aViewer.myLastPickedName !== "") { aViewer.myLastPickedName = "/" + aViewer.myLastPickedName; }\r
+ aViewer.myLastPickedName = aParentIter.name + aViewer.myLastPickedName;\r
+ }\r
+ }\r
+var aTime2 = performance.now();\r
+console.log ( "New picked '" + aViewer.myLastPickedName + "' in " + ((aTime2 - aTime1) * 0.001) + " s") ///\r
+\r
+ aNewPicked.material.userData.oldColor = aNewPicked.material.color.getHex();\r
+ aNewPicked.material.color.set (aViewer.myDynHighlightColor);\r
+ aViewer.myLastPicked = aNewPicked;\r
+ }\r
+ aViewer.updateView();\r
+ }\r
+ }\r
+\r
+ onClick()\r
+ {\r
+ var aViewer = OccViewerModule;\r
+ event.preventDefault();\r
+ if (aViewer.myLastPicked !== null)\r
+ {\r
+ aViewer.myLastPicked.material.color.set (aViewer.mySelectionColor);\r
+ aViewer.myLastPicked = null\r
+ }\r
+ aViewer.updateView();\r
+ }\r
+\r
+ setBackgroundColor (theR, theG, theB, theToUpdate)\r
+ {\r
+ this.myScene.background = new THREE.Color (theR, theG, theB);\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ setBackgroundCubemap (theFolderPath, theToUpdate)\r
+ {\r
+ this.myScene.background = new THREE.CubeTextureLoader()\r
+ .setPath (theFolderPath)\r
+ .load (['px.jpg','nx.jpg','py.jpg','ny.jpg','pz.jpg','nz.jpg']);\r
+\r
+ /// TODO disable sRGB for consistency with OCCT sample workaround\r
+ //this.myScene.background.encoding = THREE.sRGBEncoding;\r
+\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ /**\r
+ * Fit all/selected objects into view.\r
+ * @param {boolean} theAuto [in] fit selected objects (TRUE) or all objects (FALSE)\r
+ */\r
+ fitAllObjects(theAuto, theToUpdate)\r
+ {\r
+ const aFitOffset = 1.2\r
+ const aBox = new THREE.Box3();\r
+ //this.myScene.traverse (function (theChild) { aBox.expandByObject (theChild) });\r
+ for (let [aKey, anObjIter] of this.myObjects)\r
+ {\r
+ aBox.expandByObject (anObjIter);\r
+ }\r
+\r
+ const aSize = aBox.getSize (new THREE.Vector3());\r
+ const aCenter = aBox.getCenter(new THREE.Vector3());\r
+console.log (" @@ aSize= " + aSize.x + "x" + aSize.y + "x" + aSize.z) ///\r
+\r
+ const aMaxSize = Math.max (aSize.x, aSize.y, aSize.z);\r
+ const aFitHeightDist = aMaxSize / (2 * Math.atan (Math.PI * this.myCamera.fov / 360));\r
+ const aFitWidthDist = aFitHeightDist / this.myCamera.aspect;\r
+ const aDist = aFitOffset * Math.max (aFitHeightDist, aFitWidthDist);\r
+\r
+ const aDir = this.myControls.target.clone()\r
+ .sub (this.myCamera.position).normalize().multiplyScalar (aDist);\r
+ this.myControls.maxDistance = aDist * 10;\r
+ this.myControls.target.copy (aCenter);\r
+\r
+ this.myCamera.near = aDist / 100;\r
+ this.myCamera.far = aDist * 100;\r
+ this.myCamera.updateProjectionMatrix();\r
+\r
+ this.myCamera.position.copy (this.myControls.target).sub (aDir);\r
+ this.myControls.update();\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ /**\r
+ * Redraw the view.\r
+ */\r
+ updateView()\r
+ {\r
+ this.myFpsMeter.begin();\r
+\r
+ this.myRenderer.render (this.myScene, this.myCamera);\r
+\r
+ this.myFpsMeter.end();\r
+ }\r
+\r
+ /**\r
+ * Remove named object from viewer.\r
+ * @param {string} theName [in] object name\r
+ * @param {boolean} theToUpdate [in] immediatly request viewer update\r
+ * @return {boolean} FALSE if object was not found\r
+ */\r
+ removeObject (theName, theToUpdate)\r
+ {\r
+ var anOldObj = this.myObjects.get (theName);\r
+ if (anOldObj !== undefined)\r
+ {\r
+ this.myScene.remove (anOldObj);\r
+ this.myObjects.delete (theName)\r
+ if (theToUpdate) { this.updateView(); }\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Clear all named objects from viewer.\r
+ */\r
+ removeAllObjects (theToUpdate)\r
+ {\r
+ for (let [aKey, anObjIter] of this.myObjects)\r
+ {\r
+ this.myScene.remove (anObjIter);\r
+ }\r
+ this.myObjects.clear();\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ /** Return TRUE if ground is displayed */\r
+ toShowGround() { return this.myGround.parent === this.myScene; }\r
+\r
+ /**\r
+ * Show/hide ground.\r
+ * @param theToShow [in] show or hide flag\r
+ */\r
+ setShowGround (theToShow, theToUpdate)\r
+ {\r
+ if (theToShow)\r
+ {\r
+ if (this.myGround.parent !== this.myScene)\r
+ {\r
+ this.myScene.add (this.myGround);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ this.myScene.remove (this.myGround);\r
+ }\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ /** Returns TRUE if shadows are turned ON */\r
+ toCastShadows() { return this.myDirLight.castShadow; }\r
+\r
+ /** Turn shadows on/off */\r
+ setCastShadows (theUseShadows, theToUpdate)\r
+ {\r
+ this.myDirLight.castShadow = theUseShadows;\r
+ if (theToUpdate) { this.updateView(); }\r
+ }\r
+\r
+ /**\r
+ * Open object from the given URL.\r
+ * File will be loaded asynchronously.\r
+ * @param {string} theName [in] object name\r
+ * @param {string} theModelPath [in] model path\r
+ * @param {boolean} theToExpand [in] expand (explore) model or represent it as single object\r
+ */\r
+ openFromUrl (theName, theModelPath, theToExpand)\r
+ {\r
+ this.removeObject (theName, false);\r
+ var aViewer = this;\r
+\r
+ var aTime1 = performance.now();\r
+ const aLoader = new THREE.GLTFLoader();\r
+ //aLoader.setPath();\r
+ aLoader.load (theModelPath, function (theGltf) {\r
+ if (theToExpand)\r
+ {\r
+ theGltf.scene.traverse (function (theChild) {\r
+ if (theChild.isMesh)\r
+ {\r
+ theChild.material = theChild.material.clone(); // duplicate materials to dynamically highlight nodes\r
+ theChild.material.envMap = aViewer.myScene.background;\r
+ theChild.castShadow = true;\r
+ theChild.receiveShadow = true\r
+ }\r
+ });\r
+\r
+ var aModelRoot = theGltf.scene;\r
+ aModelRoot.name = theName;\r
+ aViewer.myScene.add (aModelRoot);\r
+ aViewer.myObjects.set (theName, aModelRoot);\r
+ aViewer.fitAllObjects();\r
+ return;\r
+ }\r
+\r
+ // merge meshes with common material\r
+ var isFirst = true\r
+ const aMatMap = new Map();\r
+ theGltf.scene.traverse (function (theChild) {\r
+ if (theChild.isMesh)\r
+ {\r
+ var aMatObjects = aMatMap.get (theChild.material.name)\r
+ if (aMatObjects === undefined)\r
+ {\r
+ var aMatObjects = [];\r
+ aMatMap.set (theChild.material.name, aMatObjects)\r
+ }\r
+ aMatObjects.push (theChild)\r
+ }\r
+ });\r
+\r
+ var aModelRoot = new THREE.Group();\r
+ aModelRoot.name = theName;\r
+ theGltf.scene.updateMatrixWorld();\r
+ for (let [aMatName, aMeshes] of aMatMap)\r
+ {\r
+ const aGeomList = [];\r
+ for (let i = 0; i < aMeshes.length; ++i)\r
+ {\r
+ const aMesh = aMeshes[i];\r
+ if (aMesh.geometry.applyMatrix4 != undefined)\r
+ {\r
+ aMesh.geometry.applyMatrix4 (aMesh.matrixWorld); // pre-apply transformation\r
+ }\r
+ else\r
+ {\r
+ aMesh.geometry.applyMatrix (aMesh.matrixWorld); // pre-apply transformation\r
+ }\r
+ aGeomList.push (aMesh.geometry);\r
+ }\r
+\r
+ const aMaterial = aMeshes[0].material;\r
+ aMaterial.envMap = aViewer.myScene.background;\r
+ const aGeom = THREE.BufferGeometryUtils.mergeBufferGeometries (aGeomList);\r
+ const aNode = new THREE.Mesh (aGeom, aMaterial);\r
+ aNode.name = "";\r
+ aNode.castShadow = true;\r
+ aNode.receiveShadow = true\r
+ aModelRoot.add (aNode);\r
+ }\r
+\r
+ aViewer.myScene.add (aModelRoot);\r
+ aViewer.myObjects.set (theName, aModelRoot);\r
+ aViewer.fitAllObjects();\r
+\r
+ var aTime2 = performance.now();\r
+ console.log ("glTF '" + theModelPath + "' loading time: " + ((aTime2 - aTime1) * 0.001) + " s");\r
+ });\r
+ }\r
+}\r
+\r
+function createOccThreejsViewer (theCanvas, theCubemap, theAntiAlias)\r
+{\r
+ let aViewer = new OccThreejsViewer (theCanvas, theCubemap, theAntiAlias);\r
+ return aViewer;\r
+}\r