]> OCCT Git - occt-copy.git/commitdiff
gltf reader
authorkgv <kgv@opencascade.com>
Wed, 20 Jan 2021 11:30:04 +0000 (14:30 +0300)
committerkgv <kgv@opencascade.com>
Sat, 23 Jan 2021 18:35:04 +0000 (21:35 +0300)
samples/webgl/CMakeLists.txt
samples/webgl/WasmOcctObject.cpp [new file with mode: 0644]
samples/webgl/WasmOcctObject.h [new file with mode: 0644]
samples/webgl/WasmOcctView.cpp
samples/webgl/WasmOcctView.h
samples/webgl/occt-webgl-sample.html
samples/webgl/occt-webgl-viewer.js

index bdcb29aa7bc8f2b5354cc3d7a66fd384eb2b4a0b..3a32e698dacc028a725314a5284918d24fff660e 100644 (file)
@@ -38,10 +38,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 +68,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}
diff --git a/samples/webgl/WasmOcctObject.cpp b/samples/webgl/WasmOcctObject.cpp
new file mode 100644 (file)
index 0000000..d6459c7
--- /dev/null
@@ -0,0 +1,243 @@
+// 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;
+  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);
+      }
+    }
+  }
+}
diff --git a/samples/webgl/WasmOcctObject.h b/samples/webgl/WasmOcctObject.h
new file mode 100644 (file)
index 0000000..a6b8ba6
--- /dev/null
@@ -0,0 +1,73 @@
+// 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; }
+
+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
index ec333033005dfbe04dd3c4a1eb5e7b0fe8bccb7c..92a484ce42e55e6f47e703749fa30b7b7a59188f 100644 (file)
@@ -137,6 +137,7 @@ WasmOcctView::WasmOcctView()
 : myDevicePixelRatio (1.0f),
   myUpdateRequests (0)
 {
+  SetLockOrbitZUp (true);
 }
 
 // ================================================================
@@ -294,10 +295,14 @@ bool WasmOcctView::initViewer()
     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);
@@ -315,6 +320,8 @@ bool WasmOcctView::initViewer()
     if (aLight->Type() == Graphic3d_TOLS_DIRECTIONAL)
     {
       aLight->SetCastShadows (true);
+      aLight->SetHeadlight (false);
+      aLight->SetDirection (gp_Dir (0.098f, -0.20f, -0.98f));
     }
   }
 
@@ -340,7 +347,8 @@ bool WasmOcctView::initViewer()
   myView = new V3d_View (aViewer);
   myView->Camera()->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
   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();
@@ -348,6 +356,10 @@ bool WasmOcctView::initViewer()
   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);
   initPixelScaleRatio();
   return true;
@@ -915,7 +927,7 @@ bool WasmOcctView::openFromMemory (const std::string& theName,
   }
   else if (dataStartsWithHeader(aBytes, "glTF"))
   {
-    //return openGltfFromMemory (theName, theBuffer, theDataLen, theToFree);
+    return openGltfFromMemory (theName, theBuffer, theDataLen, theToFree);
   }
   if (theToFree)
   {
@@ -971,6 +983,265 @@ bool WasmOcctView::openBRepFromMemory (const std::string& theName,
   return true;
 }
 
+#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,
+                                       uintptr_t theBuffer, int theDataLen,
+                                       bool theToFree)
+{
+  removeObject (theName);
+
+  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 (!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();
+
+displayGround (true); ///
+
+  Message::DefaultMessenger()->Send (TCollection_AsciiString("Loaded file ") + theName.c_str(), Message_Info);
+  Message::DefaultMessenger()->Send (OSD_MemInfo::PrintInfo(), Message_Trace);
+  return true;
+}
+
 // ================================================================
 // Function : displayGround
 // Purpose  :
@@ -1043,4 +1314,5 @@ EMSCRIPTEN_BINDINGS(OccViewerModule) {
   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());
 }
index c05ce179a34e08b77e8322d032acedf4ae65ce7a..8a0c42775f8fbad1396c8842c0a7c83368da8e3c 100644 (file)
@@ -99,6 +99,16 @@ public: //! @name methods exported by Module
                                   uintptr_t theBuffer, int theDataLen,
                                   bool 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 bool openGltfFromMemory (const std::string& theName,
+                                  uintptr_t theBuffer, int theDataLen,
+                                  bool theToFree);
+
 public:
 
   //! Default constructor.
index c04add8b47a38b855f5b7dd2d06ace29364eac5a..3e1c63361362ba40549df06323194aa018674dbd 100644 (file)
@@ -9,12 +9,11 @@
 \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
+  <label for="fileInput">Choose file to upload: </label><input type="file" id="fileInput" accept=".brep, .gltf, .glb">\r
   <input type="button" value="Clear All" onclick="OccViewerModule.removeAllObjects()">\r
   <input type="button" value="Fit All"   onclick="OccViewerModule.fitAllObjects(true)">\r
 </div>\r
@@ -36,8 +35,6 @@ function updateCanvasSize()
   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
@@ -75,7 +72,8 @@ fileInput.onchange = function()
     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.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
index 484b5192310f4b980a0c9e94d4614d6c04d062b8..0c3f01c54626327feee64a67bd9cca4585dcdc6f 100644 (file)
@@ -23,6 +23,9 @@ var OccViewerModule =
 \r
 const OccViewerModuleInitialized = createOccViewerModule(OccViewerModule);\r
 OccViewerModuleInitialized.then(function(Module) {\r
-  //OccViewerModule.setCubemapBackground ("cubemap.jpg");\r
-  OccViewerModule.openFromUrl ("ball", "samples/Ball.brep");\r
+  //OccViewerModule.setCubemapBackground ("textures/cubemap.jpg");\r
+  //OccViewerModule.setCubemapBackground ("textures/cubemap2.jpg");\r
+  OccViewerModule.setCubemapBackground ("textures/cubemap512.jpg");\r
+  //OccViewerModule.openFromUrl ("ball", "models/Ball.brep");\r
+  OccViewerModule.openFromUrl ("yellow", "models/yellow_up.glb");\r
 });\r