1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-09 13:22:24 +03:00

Compare commits

...

3 Commits

15 changed files with 1975 additions and 193 deletions

View File

@@ -19,7 +19,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --bind")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SAFE_HEAP=1")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s TOTAL_MEMORY=16MB")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s 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")
@@ -29,7 +29,6 @@ endif()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s MODULARIZE=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='createOccViewerModule'")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --extern-post-js ${CMAKE_CURRENT_SOURCE_DIR}/occt-webgl-viewer.js")
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
file(GLOB SOURCES
@@ -38,10 +37,12 @@ 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
@@ -66,6 +67,9 @@ endif()
set(OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel)
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}
@@ -81,5 +85,10 @@ if (NOT "${SOURCE_MAP_BASE}" STREQUAL "")
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})

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -41,64 +41,125 @@ public:
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);
//! Remove named object from viewer.
//! @param theName [in] object name
//! @return FALSE if object was not found
static bool removeObject (const std::string& theName);
//! Return the list of displayed objects.
static std::string displayedObjects();
//! Temporarily hide named object.
//! @param theName [in] object name
//! @return FALSE if object was not found
static bool eraseObject (const std::string& theName);
//! Return the list of selected objects.
static std::string selectedObjects();
//! Return the list of erased objects.
static std::string erasedObjects();
//! Display temporarily hidden object.
//! @param theName [in] object name
//! @param theNames [in] object name list
//! @return FALSE if object was not found
static bool displayObject (const std::string& theName);
static bool displayObjectList (const std::string& theNames,
bool theToUpdate);
//! Remove named objects from viewer.
//! @param theNames [in] object name found
//! @return FALSE if object was not found
static bool removeObjectList (const std::string& theNames,
bool theToUpdate);
//! Temporarily hide named objects.
//! @param theNames [in] object name list
//! @return FALSE if object was not found
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.
@@ -209,9 +270,43 @@ private:
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.
void addActionHotKeys (Aspect_VKey theAction,
unsigned int theHotKey1 = 0,
unsigned int theHotKey2 = 0,
unsigned int theHotKey3 = 0,
unsigned int theHotKey4 = 0,
unsigned int theHotKey5 = 0)
{
if (theHotKey1 != 0) { myNavKeyMap.Bind (theHotKey1, theAction); }
if (theHotKey2 != 0) { myNavKeyMap.Bind (theHotKey2, theAction); }
if (theHotKey3 != 0) { myNavKeyMap.Bind (theHotKey3, theAction); }
if (theHotKey4 != 0) { myNavKeyMap.Bind (theHotKey4, theAction); }
if (theHotKey5 != 0) { myNavKeyMap.Bind (theHotKey5, theAction); }
}
//! Handle navigation keys.
bool navigationKeyModifierSwitch (unsigned int theModifOld,
unsigned int theModifNew,
double theTimeStamp);
//! Handle hot-key.
bool processKeyPress (Aspect_VKey theKey);
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
Handle(AIS_InteractiveContext) myContext; //!< interactive context
Handle(V3d_View) myView; //!< 3D view

View File

@@ -20,7 +20,7 @@ extern "C" void onMainLoop()
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);

View File

@@ -9,17 +9,29 @@
<h2>OCCT WebGL Viewer Sample</h2>
<div>
<canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
<img id=occlogo src="OCC_logo.png" style="position: absolute; left: 20px; top: 0px; z-index: 2;" />
<canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
</div>
<div>
<label for="fileInput">Choose BREP file to upload: </label><input type="file" id="fileInput" accept=".brep">
<input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects()">
<input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true)">
<input type="checkbox" id="inputCheckShadows"><label for="inputCheckShadows">Shadows</label>
<input type="checkbox" id="inputAntiAliasing"><label for="inputAntiAliasing">Antialiasing</label>
<input type="checkbox" id="inputCheckGround" ><label for="inputCheckGround">Ground</label>
<input type="checkbox" id="inputCheckSkyBox" ><label for="inputCheckSkyBox">SkyBox</label>
<input type="checkbox" id="inputCheckHighlight"><label for="inputCheckHighlight">Dynamic highlight</label>
<br>
<input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects(true)">
<input type="file" id="inputUploadModel" accept=".gltf, .glb">
<input type="button" value="Upload File" onclick="doInputUploadModel()">
<input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true, true)">
<input type="button" value="Hide selected" onclick="doEraseSelected()">
<input type="button" value="Show hidden" onclick="doShowErased()">
<br>
<input type="checkbox" id="inputCheckExpand" ><label for="inputCheckExpand">Expand model</label>
</div>
<h4>Console output:</h4>
<p id="output"></p>
<script type="text/javascript" src="occt-webgl-sample.js" charset="utf-8"></script>
<script>
//! Resize canvas to fit into window.
function updateCanvasSize()
@@ -36,8 +48,6 @@ function updateCanvasSize()
var aDevicePixelRatio = window.devicePixelRatio || 1;
occViewerCanvas.width = aSizeX * aDevicePixelRatio;
occViewerCanvas.height = aSizeY * aDevicePixelRatio;
occlogo.style.top = (aSizeY - 30) + "px";
}
window.onresize = updateCanvasSize;
updateCanvasSize();
@@ -63,25 +73,89 @@ if (!isWasmSupported())
anElement.innerHTML += "Browser is too old - WebAssembly support is missing!<br>Please check updates or install a modern browser.<br>";
}
//! Handle file uploading.
fileInput.onchange = function()
var OccViewerModule =
{
if (fileInput.files.length == 0) { return; }
// Warning! Entire file is pre-loaded into memory.
var aFile = fileInput.files[0];
var aReader = new FileReader();
aReader.onload = function()
{
print: (function() {
var anElement = document.getElementById('output');
return function(theText) { anElement.innerHTML += theText + "<br>"; };
})(),
printErr: function(theText) { console.warn(theText); },
canvas: (function() {
var aCanvas = document.getElementById('occViewerCanvas');
//var aGlCtx = aCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } );
//if (aGlCtx == null) { aGlCtx = aCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: false, preserveDrawingBuffer: true } ); }
return aCanvas;
})(),
onRuntimeInitialized: function() {
//console.log(" @@ onRuntimeInitialized()" + Object.getOwnPropertyNames(OccViewerModule));
},
onDisplayedObjectsChanged: function() {
console.log(" @@ onDisplayedObjectsChanged() " + OccViewerModule.displayedObjects());
},
onSelectedObjectsChanged: function() {
console.log(" @@ onSelectedObjectsChanged() " + OccViewerModule.selectedObjects());
}
};
const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);
OccViewerModuleInitialized.then(function(Module) {
//var aSkyBox = "textures/cubemap512.jpg"; // "textures/cubemap2048.jpg";
var aSkyBox = "textures/cubemap2048.jpg";
var aDefModel = "models/yellow_up.glb";
OccViewerModule.setBackgroundCubemap (aSkyBox);
OccViewerModule.openFromUrl (aDefModel, aDefModel, false);
inputCheckShadows.checked = OccViewerModule.toCastShadows();
inputCheckShadows.onchange = function() { OccViewerModule.setCastShadows (inputCheckShadows.checked, true); }
inputAntiAliasing.checked = OccViewerModule.isAntiAliasingOn();
inputAntiAliasing.onchange = function() { OccViewerModule.setAntiAliasingOn (inputAntiAliasing.checked, true); }
inputCheckGround .checked = OccViewerModule.toShowGround();
inputCheckGround .onchange = function() { OccViewerModule.setShowGround (inputCheckGround.checked, true); }
inputCheckHighlight.checked = OccViewerModule.toDynamicHighlight();
inputCheckHighlight.onchange = function() { OccViewerModule.setDynamicHighlight (inputCheckHighlight.checked); }
inputCheckSkyBox .checked = true;
inputCheckSkyBox .onchange = function() {
if (inputCheckSkyBox.checked) { OccViewerModule.setBackgroundCubemap (aSkyBox); }
else { OccViewerModule.setBackgroundColor (0.0, 0.0, 0.0, true); }
}
inputCheckExpand.checked = false;
inputCheckExpand.onchange = function() {
OccViewerModule.removeAllObjects (true);
OccViewerModule.openFromUrl (aDefModel, aDefModel, inputCheckExpand.checked);
}
});
//! Handle erasing.
function doEraseSelected()
{
var aSelected = OccViewerModule.selectedObjects();
OccViewerModule.eraseObjectList (aSelected, true);
}
//! Handle show all.
function doShowErased()
{
var anErased = OccViewerModule.erasedObjects();
OccViewerModule.displayObjectList (anErased, true);
}
//! Handle file uploading.
function doInputUploadModel()
{
if (inputUploadModel.files.length == 0) { return; }
var aFile = inputUploadModel.files[0];
var aReader = new FileReader(); // Warning! Entire file is pre-loaded into memory
aReader.onload = function() {
var aDataArray = new Uint8Array (aReader.result);
const aDataBuffer = OccViewerModule._malloc (aDataArray.length);
OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);
OccViewerModule.openFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);
//OccViewerModule._free (aDataBuffer); will be freed by called method
OccViewerModule.displayGround (true);
OccViewerModule.openFromMemory (aFile.name, inputCheckExpand.checked, aDataBuffer, aDataArray.length, true);
};
aReader.readAsArrayBuffer(aFile);
aReader.readAsArrayBuffer (aFile);
};
</script>
<script type="text/javascript" src="occt-webgl-sample.js" charset="utf-8"></script>
</body>
</html>

View File

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

View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang=en-us>
<head>
<meta charset=utf-8><meta content="text/html; charset=utf-8" http-equiv=Content-Type>
<link rel="shortcut icon" href="lamp.ico" type="image/x-icon" />
<title>Three.js Viewer Sample</title>
</head>
<body>
<h2>Three.js Viewer Sample</h2>
<div>
<canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
</div>
<div>
<input type="checkbox" id="inputCheckShadows"><label for="inputCheckShadows">Shadows</label>
<input type="checkbox" id="inputAntiAliasing"><label for="inputAntiAliasing">Antialiasing</label>
<input type="checkbox" id="inputCheckGround" ><label for="inputCheckGround">Ground</label>
<input type="checkbox" id="inputCheckSkyBox" ><label for="inputCheckSkyBox">SkyBox</label>
<input type="checkbox" id="inputCheckHighlight"><label for="inputCheckHighlight">Dynamic highlight</label>
<br>
<input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects(true)">
<input type="file" id="inputUploadModel" accept=".gltf, .glb">
<input type="button" value="Upload File" onclick="doInputUploadModel()">
<input type="button" value="Fit All" onclick="OccViewerModule.fitAllObjects(true, true)">
<input type="button" value="Hide selected" onclick="doEraseSelected()">
<input type="button" value="Show hidden" onclick="doShowErased()">
<br>
<input type="checkbox" id="inputCheckExpand" ><label for="inputCheckExpand">Expand model</label>
</div>
<h4>Console output:</h4>
<p id="output"></p>
<script src="threejs/three.min.js"></script>
<script src="threejs/OrbitControls.js"></script>
<script src="threejs/GLTFLoader.js"></script>
<script src="threejs/BufferGeometryUtils.js"></script>
<script src="threejs/stats.min.js"></script>
<script src="threejs-sample.js"></script>
<script>
//! Resize canvas to fit into window.
function updateCanvasSize()
{
// size of canvas in logical (density-independent) units
var aSizeX = Math.min (window.innerWidth, window.screen.availWidth);
var aSizeY = Math.min (window.innerHeight, window.screen.availHeight);
aSizeX = Math.max (300, aSizeX - 30);
aSizeY = Math.max (300, aSizeY / 2);
occViewerCanvas.style.width = aSizeX + "px";
occViewerCanvas.style.height = aSizeY + "px";
// drawing buffer size (aka backing store)
var aDevicePixelRatio = window.devicePixelRatio || 1;
occViewerCanvas.width = aSizeX * aDevicePixelRatio;
occViewerCanvas.height = aSizeY * aDevicePixelRatio;
}
window.onresize = updateCanvasSize;
updateCanvasSize();
var aSkyBox = "textures/landing_pad/";
var aDefModel = "models/yellow_up.glb";
var aDefName = "yellow";
var aDevicePixelRatio = window.devicePixelRatio || 1;
inputAntiAliasing.checked = aDevicePixelRatio <= 1.25;
var OccViewerModule = createOccThreejsViewer (document.getElementById ('occViewerCanvas'), aSkyBox, inputAntiAliasing.checked);
OccViewerModule.openFromUrl (aDefName, aDefModel, false);
//OccViewerModule.openFromUrl ("stork", "models/Stork.glb");
//OccViewerModule.openFromUrl ("spheres", "models/spheres.glb");
inputCheckShadows.checked = OccViewerModule.toCastShadows();
inputCheckShadows.onchange = function() { OccViewerModule.setCastShadows (inputCheckShadows.checked, true); }
inputAntiAliasing.onchange = function() {}
inputCheckGround .checked = OccViewerModule.toShowGround();
inputCheckGround .onchange = function() { OccViewerModule.setShowGround (inputCheckGround.checked, true); }
inputCheckHighlight.checked = OccViewerModule.toDynamicHighlight();
inputCheckHighlight.onchange = function() { OccViewerModule.setDynamicHighlight (inputCheckHighlight.checked); }
inputCheckSkyBox .checked = true;
inputCheckSkyBox .onchange = function() {
if (inputCheckSkyBox.checked) { OccViewerModule.setBackgroundCubemap (aSkyBox, true); }
else { OccViewerModule.setBackgroundColor (0.0, 0.0, 0.0, true); }
}
inputCheckExpand.checked = false;
inputCheckExpand.onchange = function() {
OccViewerModule.removeAllObjects (true);
OccViewerModule.openFromUrl (aDefName, aDefModel, inputCheckExpand.checked);
}
//! Handle file uploading.
inputUploadModel.onchange = function()
{
if (inputUploadModel.files.length == 0) { return; }
// Warning! Entire file is pre-loaded into memory.
var aFile = inputUploadModel.files[0];
/*var aReader = new FileReader();
aReader.onload = function()
{
var aDataArray = new Uint8Array (aReader.result);
const aDataBuffer = OccViewerModule._malloc (aDataArray.length);
OccViewerModule.HEAPU8.set (aDataArray, aDataBuffer);
//OccViewerModule.openBRepFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);
OccViewerModule.openGltfFromMemory (aFile.name, aDataBuffer, aDataArray.length, true);
//OccViewerModule._free (aDataBuffer); will be freed by called method
OccViewerModule.displayGround (true);
};
aReader.readAsArrayBuffer(aFile);*/
//OccViewerModule.openFromUrl ("object", aFile.name);
};
</script>
</body>
</html>

View File

@@ -0,0 +1,354 @@
class OccThreejsViewer
{
myScene = new THREE.Scene();
myRaycaster = new THREE.Raycaster();
myObjects = new Map();
myLastPicked = null;
myLastPickedName = "";
myToDynamicHighlight = true;
myDynHighlightColor = 0x00FFFF;
mySelectionColor = 0xFFFF00;
myGround = null;
myDirLight = new THREE.DirectionalLight (0xffffff, 1);
myFpsMeter = new Stats();
constructor (theCanvas, theCubemap, theAntiAlias)
{
var aViewer = this;
var aGlCtx = theCanvas.getContext ('webgl2', { alpha: false, depth: true, antialias: theAntiAlias, preserveDrawingBuffer: true } );
if (aGlCtx == null) { aGlCtx = theCanvas.getContext ('webgl', { alpha: false, depth: true, antialias: theAntiAlias, preserveDrawingBuffer: true } ); }
this.myCamera = new THREE.PerspectiveCamera (45, theCanvas.width / theCanvas.height, 0.1, 1000);
this.myCamera.position.set (1, 1, 1);
this.myRenderer = new THREE.WebGLRenderer ({antialias: false, canvas: theCanvas, context: aGlCtx});
this.myRenderer.autoClear = true;
this.myRenderer.autoClearColor = true;
this.myRenderer.autoClearDepth = true;
this.myRenderer.autoClearStencil = true;
this.myRenderer.setSize (theCanvas.width, theCanvas.height);
this.myRenderer.outputEncoding = THREE.sRGBEncoding;
this.myRenderer.shadowMap.enabled = true;
//this.myRenderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
//this.myScene.background = new THREE.Color('black');
this.setBackgroundCubemap (theCubemap, false);
this.myControls = new THREE.OrbitControls (this.myCamera, theCanvas);
this.myControls.target.set (0, 0, -0.2);
this.myControls.update();
this.myControls.addEventListener ('change', function() { aViewer.updateView(); });
this.myDirLight.position.set (-0.098, 0.98, -0.20);
this.myDirLight.position.normalize()
this.myDirLight.castShadow = true;
this.myScene.add (new THREE.HemisphereLight (0xffffff, 0x000000, 0.4));
this.myScene.add (this.myDirLight);
//const aShadowHelper = new THREE.CameraHelper (this.myDirLight.shadow.camera); this.myScene.add (aShadowHelper)
const aPlaneGeom = new THREE.PlaneBufferGeometry (15, 15, 32, 32);
const aPlaneMat = new THREE.MeshStandardMaterial ({ color: 0xAAAAAA });
this.myGround = new THREE.Mesh (aPlaneGeom, aPlaneMat);
this.myGround.receiveShadow = true;
this.myGround.position.y = -1.0;
this.myGround.rotation.x = -1.57;
this.myScene.add (this.myGround);
this.updateView();
theCanvas.addEventListener ('click', this.onClick, false);
theCanvas.addEventListener ('mousemove', this.onMouseMove, false);
// FPS meter
this.myFpsMeter.showPanel (0);
document.body.appendChild (this.myFpsMeter.dom);
}
toDynamicHighlight() { return this.myToDynamicHighlight; }
setDynamicHighlight(theToEnable) { this.myToDynamicHighlight = theToEnable; }
onMouseMove()
{
if (event.buttons !== 0) { return; }
var aViewer = OccViewerModule;
if (!aViewer.myToDynamicHighlight) { return; }
var aTime1 = performance.now();
event.preventDefault();
var aMouse = new THREE.Vector2();
aMouse.x = (event.clientX / aViewer.myRenderer.domElement.width) * 2 - 1;
aMouse.y = -(event.clientY / aViewer.myRenderer.domElement.height) * 2 + 1;
aViewer.myRaycaster.setFromCamera (aMouse, aViewer.myCamera);
var anRes = aViewer.myRaycaster.intersectObject (aViewer.myScene, true);
var aNewPicked = anRes.length > 0 ? anRes[0].object : null;
if (aNewPicked !== aViewer.myLastPicked)
{
aViewer.myLastPickedName = "";
if (aViewer.myLastPicked !== null)
{
aViewer.myLastPicked.material.color.set (aViewer.myLastPicked.material.userData.oldColor);
aViewer.myLastPicked = null;
}
if (aNewPicked !== null)
{
for (var aParentIter = aNewPicked; aParentIter != null; aParentIter = aParentIter.parent)
{
if (aParentIter.name !== "")
{
if (aViewer.myLastPickedName !== "") { aViewer.myLastPickedName = "/" + aViewer.myLastPickedName; }
aViewer.myLastPickedName = aParentIter.name + aViewer.myLastPickedName;
}
}
var aTime2 = performance.now();
console.log ( "New picked '" + aViewer.myLastPickedName + "' in " + ((aTime2 - aTime1) * 0.001) + " s") ///
aNewPicked.material.userData.oldColor = aNewPicked.material.color.getHex();
aNewPicked.material.color.set (aViewer.myDynHighlightColor);
aViewer.myLastPicked = aNewPicked;
}
aViewer.updateView();
}
}
onClick()
{
var aViewer = OccViewerModule;
event.preventDefault();
if (aViewer.myLastPicked !== null)
{
aViewer.myLastPicked.material.color.set (aViewer.mySelectionColor);
aViewer.myLastPicked = null
}
aViewer.updateView();
}
setBackgroundColor (theR, theG, theB, theToUpdate)
{
this.myScene.background = new THREE.Color (theR, theG, theB);
if (theToUpdate) { this.updateView(); }
}
setBackgroundCubemap (theFolderPath, theToUpdate)
{
this.myScene.background = new THREE.CubeTextureLoader()
.setPath (theFolderPath)
.load (['px.jpg','nx.jpg','py.jpg','ny.jpg','pz.jpg','nz.jpg']);
/// TODO disable sRGB for consistency with OCCT sample workaround
//this.myScene.background.encoding = THREE.sRGBEncoding;
if (theToUpdate) { this.updateView(); }
}
/**
* Fit all/selected objects into view.
* @param {boolean} theAuto [in] fit selected objects (TRUE) or all objects (FALSE)
*/
fitAllObjects(theAuto, theToUpdate)
{
const aFitOffset = 1.2
const aBox = new THREE.Box3();
//this.myScene.traverse (function (theChild) { aBox.expandByObject (theChild) });
for (let [aKey, anObjIter] of this.myObjects)
{
aBox.expandByObject (anObjIter);
}
const aSize = aBox.getSize (new THREE.Vector3());
const aCenter = aBox.getCenter(new THREE.Vector3());
console.log (" @@ aSize= " + aSize.x + "x" + aSize.y + "x" + aSize.z) ///
const aMaxSize = Math.max (aSize.x, aSize.y, aSize.z);
const aFitHeightDist = aMaxSize / (2 * Math.atan (Math.PI * this.myCamera.fov / 360));
const aFitWidthDist = aFitHeightDist / this.myCamera.aspect;
const aDist = aFitOffset * Math.max (aFitHeightDist, aFitWidthDist);
const aDir = this.myControls.target.clone()
.sub (this.myCamera.position).normalize().multiplyScalar (aDist);
this.myControls.maxDistance = aDist * 10;
this.myControls.target.copy (aCenter);
this.myCamera.near = aDist / 100;
this.myCamera.far = aDist * 100;
this.myCamera.updateProjectionMatrix();
this.myCamera.position.copy (this.myControls.target).sub (aDir);
this.myControls.update();
if (theToUpdate) { this.updateView(); }
}
/**
* Redraw the view.
*/
updateView()
{
this.myFpsMeter.begin();
this.myRenderer.render (this.myScene, this.myCamera);
this.myFpsMeter.end();
}
/**
* 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
*/
removeObject (theName, theToUpdate)
{
var anOldObj = this.myObjects.get (theName);
if (anOldObj !== undefined)
{
this.myScene.remove (anOldObj);
this.myObjects.delete (theName)
if (theToUpdate) { this.updateView(); }
return true;
}
return false;
}
/**
* Clear all named objects from viewer.
*/
removeAllObjects (theToUpdate)
{
for (let [aKey, anObjIter] of this.myObjects)
{
this.myScene.remove (anObjIter);
}
this.myObjects.clear();
if (theToUpdate) { this.updateView(); }
}
/** Return TRUE if ground is displayed */
toShowGround() { return this.myGround.parent === this.myScene; }
/**
* Show/hide ground.
* @param theToShow [in] show or hide flag
*/
setShowGround (theToShow, theToUpdate)
{
if (theToShow)
{
if (this.myGround.parent !== this.myScene)
{
this.myScene.add (this.myGround);
}
}
else
{
this.myScene.remove (this.myGround);
}
if (theToUpdate) { this.updateView(); }
}
/** Returns TRUE if shadows are turned ON */
toCastShadows() { return this.myDirLight.castShadow; }
/** Turn shadows on/off */
setCastShadows (theUseShadows, theToUpdate)
{
this.myDirLight.castShadow = theUseShadows;
if (theToUpdate) { this.updateView(); }
}
/**
* Open object from the given URL.
* File will be loaded asynchronously.
* @param {string} theName [in] object name
* @param {string} theModelPath [in] model path
* @param {boolean} theToExpand [in] expand (explore) model or represent it as single object
*/
openFromUrl (theName, theModelPath, theToExpand)
{
this.removeObject (theName, false);
var aViewer = this;
var aTime1 = performance.now();
const aLoader = new THREE.GLTFLoader();
//aLoader.setPath();
aLoader.load (theModelPath, function (theGltf) {
if (theToExpand)
{
theGltf.scene.traverse (function (theChild) {
if (theChild.isMesh)
{
theChild.material = theChild.material.clone(); // duplicate materials to dynamically highlight nodes
theChild.material.envMap = aViewer.myScene.background;
theChild.castShadow = true;
theChild.receiveShadow = true
}
});
var aModelRoot = theGltf.scene;
aModelRoot.name = theName;
aViewer.myScene.add (aModelRoot);
aViewer.myObjects.set (theName, aModelRoot);
aViewer.fitAllObjects();
return;
}
// merge meshes with common material
var isFirst = true
const aMatMap = new Map();
theGltf.scene.traverse (function (theChild) {
if (theChild.isMesh)
{
var aMatObjects = aMatMap.get (theChild.material.name)
if (aMatObjects === undefined)
{
var aMatObjects = [];
aMatMap.set (theChild.material.name, aMatObjects)
}
aMatObjects.push (theChild)
}
});
var aModelRoot = new THREE.Group();
aModelRoot.name = theName;
theGltf.scene.updateMatrixWorld();
for (let [aMatName, aMeshes] of aMatMap)
{
const aGeomList = [];
for (let i = 0; i < aMeshes.length; ++i)
{
const aMesh = aMeshes[i];
if (aMesh.geometry.applyMatrix4 != undefined)
{
aMesh.geometry.applyMatrix4 (aMesh.matrixWorld); // pre-apply transformation
}
else
{
aMesh.geometry.applyMatrix (aMesh.matrixWorld); // pre-apply transformation
}
aGeomList.push (aMesh.geometry);
}
const aMaterial = aMeshes[0].material;
aMaterial.envMap = aViewer.myScene.background;
const aGeom = THREE.BufferGeometryUtils.mergeBufferGeometries (aGeomList);
const aNode = new THREE.Mesh (aGeom, aMaterial);
aNode.name = "";
aNode.castShadow = true;
aNode.receiveShadow = true
aModelRoot.add (aNode);
}
aViewer.myScene.add (aModelRoot);
aViewer.myObjects.set (theName, aModelRoot);
aViewer.fitAllObjects();
var aTime2 = performance.now();
console.log ("glTF '" + theModelPath + "' loading time: " + ((aTime2 - aTime1) * 0.001) + " s");
});
}
}
function createOccThreejsViewer (theCanvas, theCubemap, theAntiAlias)
{
let aViewer = new OccThreejsViewer (theCanvas, theCubemap, theAntiAlias);
return aViewer;
}

View File

@@ -1557,8 +1557,10 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile)
hasTexRGBA8 = IsGlGreaterEqual (3, 0)
|| CheckExtension ("GL_OES_rgb8_rgba8");
hasTexSRGB = IsGlGreaterEqual (3, 0);
hasFboSRGB = IsGlGreaterEqual (3, 0);
hasTexSRGB = IsGlGreaterEqual (3, 0)
|| CheckExtension ("GL_EXT_sRGB");
hasFboSRGB = IsGlGreaterEqual (3, 0)
|| CheckExtension ("GL_EXT_sRGB");
hasFboRenderMipmap = IsGlGreaterEqual (3, 0)
|| CheckExtension ("GL_OES_fbo_render_mipmap");
hasSRGBControl = CheckExtension ("GL_EXT_sRGB_write_control");
@@ -3355,8 +3357,15 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile)
GL_BACK;
#endif
GLint aWinColorEncoding = 0; // GL_LINEAR
arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, aDefWinBuffer, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &aWinColorEncoding);
ResetErrors (true);
bool toSkipCheck = false;
#ifdef __EMSCRIPTEN__
toSkipCheck = !IsGlGreaterEqual (3, 0);
#endif
if (!toSkipCheck)
{
arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, aDefWinBuffer, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &aWinColorEncoding);
ResetErrors (true);
}
myIsSRgbWindow = aWinColorEncoding == GL_SRGB;
// On desktop OpenGL, pixel formats are almost always sRGB-ready, even when not requested;

View File

@@ -33,6 +33,10 @@ typedef double GLclampd;
// GL_EXT_texture_format_BGRA8888
#define GL_BGRA_EXT 0x80E1 // same as GL_BGRA on desktop
// GL_EXT_sRGB
#define GL_SRGB_EXT 0x8C40 // GL_SRGB_EXT
#define GL_SRGB_ALPHA_EXT 0x8C42 // GL_SRGB_ALPHA_EXT
#define GL_R16 0x822A
#define GL_RGB4 0x804F
#define GL_RGB5 0x8050

View File

@@ -1012,6 +1012,20 @@ bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)& theCtx,
return false;
}
#if defined(GL_ES_VERSION_2_0)
if (theToGenMipmap
&& !theCtx->IsGlGreaterEqual (3, 0)
&& (aFormat.PixelFormat() == GL_SRGB_EXT
|| aFormat.PixelFormat() == GL_SRGB_ALPHA_EXT))
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Warning, GL_EXT_sRGB disallows generation of mipmaps - fallback using non-sRGB format")
+ " [" + myResourceId +"]");
aFormat.SetPixelFormat (aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB : GL_RGBA);
aFormat.SetInternalFormat(aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB8 : GL_RGBA8);
}
#endif
myTarget = GL_TEXTURE_CUBE_MAP;
myNbSamples = 1;
mySizeX = (GLsizei )theSize;
@@ -1132,7 +1146,11 @@ bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)& theCtx,
if (anErr != GL_NO_ERROR)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
TCollection_AsciiString ("Unable to initialize side of cubemap. Error ") + OpenGl_Context::FormatGlError (anErr));
TCollection_AsciiString ("Error: cubemap side ") + (int )theSize + "x" + (int )theSize
+ " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
+ " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
+ " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
+ " can not be created with error " + OpenGl_Context::FormatGlError (anErr) + ".");
Unbind (theCtx);
Release (theCtx.get());
return false;

View File

@@ -46,6 +46,7 @@ TCollection_AsciiString OpenGl_TextureFormat::FormatFormat (GLint theInternalFor
case 0x8050: return "GL_RGB5";
case GL_RGB8: return "GL_RGB8";
case GL_SRGB8: return "GL_SRGB8";
case GL_SRGB_EXT: return "GL_SRGB_EXT";
case 0x8052: return "GL_RGB10";
case 0x8053: return "GL_RGB12";
case 0x8054: return "GL_RGB16";
@@ -55,7 +56,8 @@ TCollection_AsciiString OpenGl_TextureFormat::FormatFormat (GLint theInternalFor
// RGBA variations
case GL_RGBA: return "GL_RGBA";
case GL_RGBA8: return "GL_RGBA8";
case GL_SRGB8_ALPHA8: return "GL_SRGB8_ALPHA8";
case GL_SRGB8_ALPHA8: return "GL_SRGB8_ALPHA8";
case GL_SRGB_ALPHA_EXT: return "GL_SRGB_ALPHA_EXT";
case GL_RGB10_A2: return "GL_RGB10_A2";
case 0x805A: return "GL_RGBA12";
case 0x805B: return "GL_RGBA16";
@@ -254,6 +256,12 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindFormat (const Handle(OpenGl_Conte
if (theIsColorMap
&& theCtx->ToRenderSRGB())
{
#if defined(GL_ES_VERSION_2_0)
if (!theCtx->IsGlGreaterEqual (3, 0))
{
aFormat.SetPixelFormat (GL_SRGB_ALPHA_EXT);
}
#endif
aFormat.SetInternalFormat (GL_SRGB8_ALPHA8);
}
return aFormat;
@@ -308,6 +316,10 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindFormat (const Handle(OpenGl_Conte
if (theIsColorMap
&& theCtx->ToRenderSRGB())
{
if (!theCtx->IsGlGreaterEqual (3, 0))
{
aFormat.SetPixelFormat (GL_SRGB_ALPHA_EXT);
}
aFormat.SetInternalFormat (GL_SRGB8_ALPHA8);
}
#endif
@@ -355,6 +367,12 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindFormat (const Handle(OpenGl_Conte
if (theIsColorMap
&& theCtx->ToRenderSRGB())
{
#if defined(GL_ES_VERSION_2_0)
if (!theCtx->IsGlGreaterEqual (3, 0))
{
aFormat.SetPixelFormat (GL_SRGB_EXT);
}
#endif
aFormat.SetInternalFormat (GL_SRGB8);
}
return aFormat;
@@ -516,6 +534,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_
return aFormat;
}
case GL_SRGB8_ALPHA8:
case GL_SRGB_ALPHA_EXT:
case GL_RGBA8:
case GL_RGBA:
{
@@ -524,7 +543,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_
aFormat.SetPixelFormat (GL_RGBA);
aFormat.SetDataType (GL_UNSIGNED_BYTE);
aFormat.SetImageFormat (Image_Format_RGBA);
if (theSizedFormat == GL_SRGB8_ALPHA8
if ((theSizedFormat == GL_SRGB8_ALPHA8 || theSizedFormat == GL_SRGB_ALPHA_EXT)
&& !theCtx->ToRenderSRGB())
{
aFormat.SetInternalFormat (GL_RGBA8); // fallback format
@@ -532,6 +551,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_
return aFormat;
}
case GL_SRGB8:
case GL_SRGB_EXT:
case GL_RGB8:
case GL_RGB:
{
@@ -540,7 +560,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_
aFormat.SetPixelFormat (GL_RGB);
aFormat.SetDataType (GL_UNSIGNED_BYTE);
aFormat.SetImageFormat (Image_Format_RGB);
if (theSizedFormat == GL_SRGB8
if ((theSizedFormat == GL_SRGB8 || theSizedFormat == GL_SRGB_EXT)
&& !theCtx->ToRenderSRGB())
{
aFormat.SetInternalFormat (GL_RGB8); // fallback format