1 // Copyright (c) 2014 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <OcctJni_Viewer.hxx>
15 #include <OcctJni_MsgPrinter.hxx>
17 #include <AIS_Shape.hxx>
18 #include <Image_AlienPixMap.hxx>
19 #include <BRepTools.hxx>
20 #include <Message_Messenger.hxx>
21 #include <Message_MsgFile.hxx>
22 #include <OpenGl_GraphicDriver.hxx>
23 #include <OSD_Environment.hxx>
24 #include <OSD_Timer.hxx>
25 #include <Standard_Version.hxx>
27 #include <BRepPrimAPI_MakeBox.hxx>
29 #include <STEPControl_Reader.hxx>
30 #include <IGESControl_Reader.hxx>
31 #include <XSControl_WorkSession.hxx>
35 #include <sys/types.h>
40 //! @return true if file exists
41 static bool isFileExist (const TCollection_AsciiString& thePath)
43 struct stat64 aStatBuffer;
44 return stat64 (thePath.ToCString(), &aStatBuffer) == 0;
47 //! Cut-off the last split character from the path and everything after it.
48 static TCollection_AsciiString getParentDir (const TCollection_AsciiString& thePath)
50 TCollection_AsciiString aPath = thePath;
51 char* aSplitter = (char* )aPath.ToCString();
52 for (char* anIter = aSplitter; *anIter != '\0'; ++anIter)
60 *aSplitter = '\0'; // cut off file name or trailing folder
61 return TCollection_AsciiString (aPath.ToCString());
64 //! Set environment variable theVarName indicating location of resource
65 //! file theFile so as to correspond to actual location of this file.
67 //! The resource file is searched in directory where Test.Draw.dll is located,
68 //! and if not found - also in subdirectory ../res from there.
69 //! If file is found, environment variable is set for C subsystem.
70 //! Otherwise, environment is not changed.
72 //! If theToAddFileName is true, complete file name is set as value of the variable,
73 //! if theToAddFileName is false, only path is set.
74 Standard_Boolean setResourceEnv (const TCollection_AsciiString& theVarName,
75 const TCollection_AsciiString& theRoot,
76 const TCollection_AsciiString& theFile,
77 const Standard_Boolean theToAddFileName)
79 // use location of current assembly to figure out possible location of resource
80 TCollection_AsciiString aBaseDir = theRoot;
82 // check the same directory where binary is located
83 if (!isFileExist (aBaseDir + "/" + theFile))
85 // check subdirectory ../res
86 aBaseDir = getParentDir (aBaseDir) + "/res";
87 if (!isFileExist (aBaseDir + "/" + theFile))
89 return Standard_False;
93 // set C library environment
96 aBaseDir = aBaseDir + "/" + theFile;
99 OSD_Environment anEnv (theVarName, aBaseDir);
101 return Standard_True;
104 // =======================================================================
105 // function : OcctJni_Viewer
107 // =======================================================================
108 OcctJni_Viewer::OcctJni_Viewer()
110 // prepare necessary environment
111 TCollection_AsciiString aResRoot = "/data/data/com.opencascade.jnisample/files";
113 setResourceEnv ("CSF_ShadersDirectory", aResRoot + "/Shaders", "Declarations.glsl", Standard_False);
114 setResourceEnv ("CSF_XSMessage", aResRoot + "/XSMessage", "XSTEP.us", Standard_False);
115 setResourceEnv ("CSF_SHMessage", aResRoot + "/XSMessage", "SHAPE.us", Standard_False);
116 //setResourceEnv ("CSF_PluginDefaults", "Plugin", Standard_False);
118 // make sure OCCT loads the dictionary
119 //UnitsAPI::SetLocalSystem (UnitsAPI_SI);
122 // =======================================================================
125 // =======================================================================
126 bool OcctJni_Viewer::init()
129 int aWidth = 0, aHeight = 0;
130 EGLDisplay anEglDisplay = eglGetCurrentDisplay();
131 EGLContext anEglContext = eglGetCurrentContext();
132 EGLSurface anEglSurf = eglGetCurrentSurface (EGL_DRAW);
133 if (anEglDisplay == EGL_NO_DISPLAY
134 || anEglContext == EGL_NO_CONTEXT
135 || anEglSurf == EGL_NO_SURFACE)
137 Message::DefaultMessenger()->Send ("Error: No active EGL context!", Message_Fail);
142 eglQuerySurface (anEglDisplay, anEglSurf, EGL_WIDTH, &aWidth);
143 eglQuerySurface (anEglDisplay, anEglSurf, EGL_HEIGHT, &aHeight);
144 eglQuerySurface (anEglDisplay, anEglSurf, EGL_CONFIG_ID, &aCfgId);
145 const EGLint aConfigAttribs[] = { EGL_CONFIG_ID, aCfgId, EGL_NONE };
146 EGLint aNbConfigs = 0;
147 void* anEglConfig = NULL;
148 if (eglChooseConfig (anEglDisplay, aConfigAttribs, &anEglConfig, 1, &aNbConfigs) != EGL_TRUE)
150 Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail);
155 TCollection_AsciiString anEglInfo = TCollection_AsciiString()
156 + "\n EGLVersion: " + eglQueryString (anEglDisplay, EGL_VERSION)
157 + "\n EGLVendor: " + eglQueryString (anEglDisplay, EGL_VENDOR)
158 + "\n EGLClient APIs: " + eglQueryString (anEglDisplay, EGL_CLIENT_APIS)
159 + "\n GLvendor: " + (const char* )glGetString (GL_VENDOR)
160 + "\n GLdevice: " + (const char* )glGetString (GL_RENDERER)
161 + "\n GLversion: " + (const char* )glGetString (GL_VERSION) + " [GLSL: " + (const char* )glGetString (GL_SHADING_LANGUAGE_VERSION) + "]";
162 ::Message::DefaultMessenger()->Send (anEglInfo, Message_Info);
164 if (!myViewer.IsNull())
166 Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
167 Handle(OcctJni_Window) aWindow = Handle(OcctJni_Window)::DownCast (myView->Window());
168 if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
170 Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail);
175 aWindow->SetSize (aWidth, aHeight);
176 myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
180 Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (NULL, Standard_False);
181 aDriver->ChangeOptions().buffersNoSwap = Standard_True;
182 //aDriver->ChangeOptions().glslWarnings = Standard_True; /// for debug only!
183 if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
185 Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail);
191 myViewer = new V3d_Viewer (aDriver);
192 myViewer->SetDefaultBackgroundColor (Quantity_NOC_BLACK);
193 myViewer->SetDefaultLights();
194 myViewer->SetLightOn();
196 // create AIS context
197 myContext = new AIS_InteractiveContext (myViewer);
198 //myContext->SetDisplayMode (AIS_WireFrame, false);
199 myContext->SetDisplayMode (AIS_Shaded, false);
201 Handle(OcctJni_Window) aWindow = new OcctJni_Window (aWidth, aHeight);
202 myView = myViewer->CreateView();
204 myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
205 myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER);
211 // =======================================================================
212 // function : release
214 // =======================================================================
215 void OcctJni_Viewer::release()
222 // =======================================================================
225 // =======================================================================
226 void OcctJni_Viewer::resize (int theWidth,
229 if (myContext.IsNull())
231 Message::DefaultMessenger()->Send ("Resize failed - view is unavailable", Message_Fail);
235 Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
236 Handle(OcctJni_Window) aWindow = Handle(OcctJni_Window)::DownCast (myView->Window());
237 aWindow->SetSize (theWidth, theHeight);
238 //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed
240 EGLContext anEglContext = eglGetCurrentContext();
241 myView->SetImmediateUpdate (Standard_False);
242 myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
243 //saveSnapshot ("/sdcard/Download/tt.png", theWidth, theHeight);
246 // =======================================================================
247 // function : initContent
249 // =======================================================================
250 void OcctJni_Viewer::initContent()
252 myContext->RemoveAll (Standard_False);
256 if (!myShape.IsNull())
258 Handle(AIS_Shape) aShapePrs = new AIS_Shape (myShape);
259 myContext->Display (aShapePrs, Standard_False);
263 BRepPrimAPI_MakeBox aBuilder (1.0, 2.0, 3.0);
264 Handle(AIS_Shape) aShapePrs = new AIS_Shape (aBuilder.Shape());
265 myContext->Display (aShapePrs, Standard_False);
270 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info);
273 //! Load shape from IGES file
274 static TopoDS_Shape loadIGES (const TCollection_AsciiString& thePath)
277 IGESControl_Reader aReader;
278 IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail;
281 aReadStatus = aReader.ReadFile (thePath.ToCString());
283 catch (Standard_Failure)
285 Message::DefaultMessenger()->Send ("Error: IGES reader, computation error", Message_Fail);
289 if (aReadStatus != IFSelect_RetDone)
291 Message::DefaultMessenger()->Send ("Error: IGES reader, bad file format", Message_Fail);
295 // now perform the translation
296 aReader.TransferRoots();
297 if (aReader.NbShapes() <= 0)
299 Handle(XSControl_WorkSession) aWorkSession = new XSControl_WorkSession();
300 aWorkSession->SelectNorm ("IGES");
301 aReader.SetWS (aWorkSession, Standard_True);
302 aReader.SetReadVisible (Standard_False);
303 aReader.TransferRoots();
305 if (aReader.NbShapes() <= 0)
307 Message::DefaultMessenger()->Send ("Error: IGES reader, no shapes has been found", Message_Fail);
310 return aReader.OneShape();
311 /*TopoDS_Shape anImportedShape = aReader.OneShape();
313 // apply sewing on the imported shape
314 BRepBuilderAPI_Sewing aTool (0.0);
315 aTool.SetNonManifoldMode (Standard_False);
316 aTool.SetFloatingEdgesMode(Standard_True);
317 aTool.Load (anImportedShape);
319 TopoDS_Shape aSewedShape = aTool.SewedShape();
321 if (aSewedShape.IsNull())
323 Message::DefaultMessenger()->Send ("Error: Sewing result is empty", Message_Fail);
326 if (aSewedShape.IsSame(anImportedShape))
328 aShape = anImportedShape;
332 // apply shape healing
333 ShapeFix_Shape aShapeFixer(aSewedShape);
334 aShapeFixer.FixSolidMode() = 1;
335 aShapeFixer.FixFreeShellMode() = 1;
336 aShapeFixer.FixFreeFaceMode() = 1;
337 aShapeFixer.FixFreeWireMode() = 0;
338 aShapeFixer.FixSameParameterMode() = 0;
339 aShapeFixer.FixVertexPositionMode() = 0;
340 aShape = aShapeFixer.Perform() ? aShapeFixer.Shape() : aSewedShape;
345 //! Load shape from STEP file
346 static TopoDS_Shape loadSTEP (const TCollection_AsciiString& thePath)
348 STEPControl_Reader aReader;
349 IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail;
352 aReadStatus = aReader.ReadFile (thePath.ToCString());
354 catch (Standard_Failure)
356 Message::DefaultMessenger()->Send ("Error: STEP reader, computation error", Message_Fail);
357 return TopoDS_Shape();
360 if (aReadStatus != IFSelect_RetDone)
362 Message::DefaultMessenger()->Send ("Error: STEP reader, bad file format", Message_Fail);
363 return TopoDS_Shape();
365 else if (aReader.NbRootsForTransfer() <= 0)
367 Message::DefaultMessenger()->Send ("Error: STEP reader, shape is empty", Message_Fail);
368 return TopoDS_Shape();
371 // now perform the translation
372 aReader.TransferRoots();
373 return aReader.OneShape();
376 // =======================================================================
379 // =======================================================================
380 bool OcctJni_Viewer::open (const TCollection_AsciiString& thePath)
383 if (!myContext.IsNull())
385 myContext->RemoveAll (Standard_False);
387 if (thePath.IsEmpty())
394 TCollection_AsciiString aFormatStr;
395 const Standard_Integer aLen = thePath.Length();
397 && thePath.Value (aLen - 4) == '.')
399 aFormatStr = thePath.SubString (aLen - 3, aLen);
402 && thePath.Value (aLen - 3) == '.')
404 aFormatStr = thePath.SubString (aLen - 2, aLen);
407 && thePath.Value (aLen - 2) == '.')
409 aFormatStr = thePath.SubString (aLen - 1, aLen);
411 aFormatStr.LowerCase();
414 if (aFormatStr == "stp"
415 || aFormatStr == "step")
417 aShape = loadSTEP (thePath);
419 else if (aFormatStr == "igs"
420 || aFormatStr == "iges")
422 aShape = loadIGES (thePath);
425 // if (aFormatStr == "brep"
426 // || aFormatStr == "rle")
428 BRep_Builder aBuilder;
429 if (!BRepTools::Read (aShape, thePath.ToCString(), aBuilder))
431 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Error: file '" + thePath + "' can not be opened!", Message_Info);
440 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "File '" + thePath + "' loaded in " + aTimer.ElapsedTime() + " seconds", Message_Info);
443 if (myContext.IsNull())
451 Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
452 myContext->Display (aShapePrs, Standard_False);
456 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info);
460 // =======================================================================
461 // function : saveSnapshot
463 // =======================================================================
464 bool OcctJni_Viewer::saveSnapshot (const TCollection_AsciiString& thePath,
468 if (myContext.IsNull()
469 || thePath.IsEmpty())
471 Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail);
478 myView->Window()->Size (theWidth, theHeight);
483 Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail);
487 Image_AlienPixMap anAlienImage;
488 if (!anAlienImage.InitTrash (Image_PixMap::ImgBGRA, theWidth, theHeight))
490 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "RGBA image " + theWidth + "x" + theHeight + " allocation failed", Message_Fail);
494 // OpenGL ES does not support fetching data in BGRA format
495 // while FreeImage does not support RGBA format.
496 Image_PixMap anImage;
497 anImage.InitWrapper (Image_PixMap::ImgRGBA,
498 anAlienImage.ChangeData(),
499 anAlienImage.SizeX(),
500 anAlienImage.SizeY(),
501 anAlienImage.SizeRowBytes());
502 if (!myView->ToPixMap (anImage, theWidth, theHeight, Graphic3d_BT_RGBA))
504 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View dump to the image " + theWidth + "x" + theHeight + " failed", Message_Fail);
507 for (Standard_Size aRow = 0; aRow < anAlienImage.SizeY(); ++aRow)
509 for (Standard_Size aCol = 0; aCol < anAlienImage.SizeX(); ++aCol)
511 Image_ColorRGBA& aPixel = anAlienImage.ChangeValue<Image_ColorRGBA> (aRow, aCol);
512 std::swap (aPixel.r(), aPixel.b());
517 if (!anAlienImage.Save (thePath))
519 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Image saving to path '" + thePath + "' failed", Message_Fail);
522 Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View " + theWidth + "x" + theHeight + " dumped to image '" + thePath + "'", Message_Info);
526 // =======================================================================
529 // =======================================================================
530 void OcctJni_Viewer::redraw()
540 // =======================================================================
543 // =======================================================================
544 void OcctJni_Viewer::fitAll()
551 myView->FitAll (0.01, Standard_False);
552 myView->Invalidate();
555 // =======================================================================
556 // function : startRotation
558 // =======================================================================
559 void OcctJni_Viewer::startRotation (int theStartX,
567 myView->StartRotation (theStartX, theStartY, 0.45);
568 myView->Invalidate();
571 // =======================================================================
572 // function : onRotation
574 // =======================================================================
575 void OcctJni_Viewer::onRotation (int theX,
583 myView->Rotation (theX, theY);
584 myView->Invalidate();
587 // =======================================================================
588 // function : onPanning
590 // =======================================================================
591 void OcctJni_Viewer::onPanning (int theDX,
599 myView->Pan (theDX, theDY);
600 myView->Invalidate();
603 // =======================================================================
604 // function : onClick
606 // =======================================================================
607 void OcctJni_Viewer::onClick (int theX,
615 myContext->MoveTo (theX, theY, myView, Standard_False);
616 myContext->Select (Standard_False);
617 myView->Invalidate();
620 // =======================================================================
621 // function : stopAction
623 // =======================================================================
624 void OcctJni_Viewer::stopAction()
632 #define jexp extern "C" JNIEXPORT
634 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppCreate (JNIEnv* theEnv,
637 return jlong(new OcctJni_Viewer());
640 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppDestroy (JNIEnv* theEnv,
644 delete (OcctJni_Viewer* )theCppPtr;
646 Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger();
647 aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter));
650 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRelease (JNIEnv* theEnv,
654 ((OcctJni_Viewer* )theCppPtr)->release();
657 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppInit (JNIEnv* theEnv,
661 Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger();
662 aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter));
663 aMsgMgr->AddPrinter (new OcctJni_MsgPrinter (theEnv, theObj));
664 ((OcctJni_Viewer* )theCppPtr)->init();
667 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppResize (JNIEnv* theEnv,
673 ((OcctJni_Viewer* )theCppPtr)->resize (theWidth, theHeight);
676 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOpen (JNIEnv* theEnv,
681 const char* aPathPtr = theEnv->GetStringUTFChars (thePath, 0);
682 const TCollection_AsciiString aPath (aPathPtr);
683 theEnv->ReleaseStringUTFChars (thePath, aPathPtr);
684 ((OcctJni_Viewer* )theCppPtr)->open (aPath);
687 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRedraw (JNIEnv* theEnv,
691 ((OcctJni_Viewer* )theCppPtr)->redraw();
694 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetAxoProj (JNIEnv* theEnv,
698 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_XposYnegZpos);
701 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXposProj (JNIEnv* theEnv,
705 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xpos);
708 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYposProj (JNIEnv* theEnv,
712 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Ypos);
715 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZposProj (JNIEnv* theEnv,
719 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zpos);
722 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXnegProj (JNIEnv* theEnv,
726 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xneg);
729 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYnegProj (JNIEnv* theEnv,
733 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Yneg);
736 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZnegProj (JNIEnv* theEnv,
740 ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zneg);
743 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppFitAll (JNIEnv* theEnv,
747 ((OcctJni_Viewer* )theCppPtr)->fitAll();
750 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStartRotation (JNIEnv* theEnv,
756 ((OcctJni_Viewer* )theCppPtr)->startRotation (theStartX, theStartY);
759 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnRotation (JNIEnv* theEnv,
765 ((OcctJni_Viewer* )theCppPtr)->onRotation (theX, theY);
768 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnPanning (JNIEnv* theEnv,
774 ((OcctJni_Viewer* )theCppPtr)->onPanning (theDX, theDY);
777 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnClick (JNIEnv* theEnv,
783 ((OcctJni_Viewer* )theCppPtr)->onClick (theX, theY);
786 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStopAction (JNIEnv* theEnv,
790 ((OcctJni_Viewer* )theCppPtr)->stopAction();
793 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMajorVersion (JNIEnv* theEnv,
796 return OCC_VERSION_MAJOR;
799 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMinorVersion (JNIEnv* theEnv,
802 return OCC_VERSION_MINOR;
805 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMicroVersion (JNIEnv* theEnv,
808 return OCC_VERSION_MAINTENANCE;