package org.geogebra.common.geogebra3D.euclidian3D.openGL; import org.geogebra.common.awt.GBufferedImage; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GPoint; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianController3D; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.euclidian3D.Hitting; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawLabel3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3DListsForView; import org.geogebra.common.io.MyXMLio; import org.geogebra.common.kernel.Matrix.CoordMatrix4x4; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.AnimationExportSlider; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.main.App; /** * * Used for openGL display. * <p> * It provides: * <ul> * <li>methods for displaying {@link Drawable3D}, with painting parameters</li> * <li>methods for picking object</li> * </ul> * * @author ggb3D * */ public abstract class Renderer { /** * renderer type (shader or not) */ public enum RendererType { SHADER, GL2, NOT_SPECIFIED } private RendererType type; public static final int MOUSE_PICK_DEPTH = 10; // other public Drawable3DListsForView drawable3DLists; public EuclidianView3D view3D; // for drawing protected CoordMatrix4x4 m_drawingMatrix; // matrix for drawing // ///////////////// // primitives // private RendererPrimitives primitives; // ///////////////// // geometries protected Manager geometryManager; // ///////////////// // textures protected Textures textures; // ///////////////// // arrows /** no arrows */ static final public int ARROW_TYPE_NONE = 0; /** simple arrows */ static final public int ARROW_TYPE_SIMPLE = 1; /** * creates a renderer linked to an {@link EuclidianView3D} * * @param view * the {@link EuclidianView3D} linked to * @param type */ public Renderer(EuclidianView3D view, RendererType type) { // link to 3D view this.view3D = view; // type this.type = type; // textures textures = new Textures(this); } /** * dummy renderer (when no GL available) */ public Renderer() { } /** * * @return canvas (for desktop version at least) */ abstract public Object getCanvas(); /** * set the list of {@link Drawable3D} to be drawn * * @param dl * list of {@link Drawable3D} */ public void setDrawable3DLists(Drawable3DListsForView dl) { drawable3DLists = dl; } /** * re-calc the display immediately */ abstract public void display(); /** * */ protected void updateViewAndDrawables() { view3D.update(); view3D.updateOwnDrawablesNow(); // update 3D drawables view3D.updateDrawables(drawable3DLists); // say that 3D view changed has been performed view3D.resetViewChanged(); } /** * init rendering values (clear color buffer, etc.) */ protected void initRenderingValues() { // clear color buffer if (!view3D.getCompanion().isStereoBuffered()) { clearColorBuffer(); } // init lighting initLighting(); } /** * * @return true if is exporting equirectangular image */ public boolean isExportingImageEquirectangular() { return exportImageEquirectangular; } /** * draw the scene */ public void drawScene() { // if (!view3D.isShowing()) { // return; // } if (exportImageEquirectangular) { view3D.setProjectionEquirectangular(); } // update 3D controller ((EuclidianController3D) view3D.getEuclidianController()) .updateInput3D(); useShaderProgram(); // clip planes if (waitForUpdateClipPlanes) { // Application.debug(enableClipPlanes); if (enableClipPlanes) { enableClipPlanes(); } else { disableClipPlanes(); } waitForUpdateClipPlanes = false; } // update 3D controller ((EuclidianController3D) view3D.getEuclidianController()).update(); // long time = System.currentTimeMillis(); // update 3D view and drawables updateViewAndDrawables(); // Log.debug("======= UPDATE : "+(System.currentTimeMillis() - time)); if (needExportImage && !exportImageEquirectangular) { selectFBO(); } if (waitForSetStencilLines) { setStencilLines(); } if (waitForDisableStencilLines) { disableStencilLines(); } if (waitForUpdateClearColor) { updateClearColor(); waitForUpdateClearColor = false; } // init rendering values initRenderingValues(); // time = System.currentTimeMillis(); if (exportImageEquirectangular) { setExportImageDimension(EXPORT_IMAGE_EQUIRECTANGULAR_WIDTH_ELEMENT, EXPORT_IMAGE_EQUIRECTANGULAR_HEIGHT_ELEMENT); selectFBO(); initExportImageEquirectangularTiles(); for (int i = 0; i < EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_STEPS; i++) { // update view // view3D.setEquirectangularAngle(-EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_ELEMENT // * i); view3D.setEquirectangularAngle( -i * EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_DELTA); // left eye clearColorBuffer(); clearDepthBuffer(); setDrawLeft(); setView(); draw(); setExportImageEquirectangularTileLeft(i); // right eye clearColorBuffer(); clearDepthBuffer(); setDrawRight(); setView(); draw(); setExportImageEquirectangularTileRight(i); setColorMask(true, true, true, true); } setExportImageEquirectangularFromTiles(); exportImageEquirectangular(); unselectFBO(); setNeedExportImage(false); exportImageEquirectangular = false; return; } else if (view3D .getProjection() == EuclidianView3D.PROJECTION_GLASSES) { // left eye setDrawLeft(); setView(); draw(); // right eye setDrawRight(); setView(); draw(); } else { if (view3D.getCompanion().isStereoBuffered()) { // we draw the same image on both left/right buffers setBufferLeft(); clearColorBuffer(); clearDepthBuffer(); setView(); draw(); setBufferRight(); clearColorBuffer(); clearDepthBuffer(); setView(); draw(); } else { clearDepthBuffer(); setView(); draw(); } } // Log.debug("======= DRAW : "+(System.currentTimeMillis() - time)); // prepare correct color mask for next clear setColorMask(true, true, true, true); boolean nei = needExportImage; exportImage(); if (nei) { unselectFBO(); } } /** * export equirectangular image */ abstract protected void exportImageEquirectangular(); /** * init tiles array */ abstract protected void initExportImageEquirectangularTiles(); /** * create equirectangular image i-th tile, left eye */ abstract protected void setExportImageEquirectangularTileLeft(int i); /** * create equirectangular image i-th tile, right eye */ abstract protected void setExportImageEquirectangularTileRight(int i); /** * concatenates tiles to create equirectangular image */ abstract protected void setExportImageEquirectangularFromTiles(); public boolean needExportImage = false; private boolean exportImageForThumbnail = false; private boolean exportImageEquirectangular = false; // private double exportImageEquirectangularAngle = 0; /** max latitude viewed -- must be factor of 9 */ public static final int EXPORT_IMAGE_EQUIRECTANGULAR_LATITUTDE_MAX = 54; public static final double EXPORT_IMAGE_EQUIRECTANGULAR_LATITUTDE_MAX_TAN = Math .tan(EXPORT_IMAGE_EQUIRECTANGULAR_LATITUTDE_MAX * Math.PI / 180); public static final int EXPORT_IMAGE_EQUIRECTANGULAR_HEIGHT = 2000; public static final int EXPORT_IMAGE_EQUIRECTANGULAR_HEIGHT_ELEMENT = EXPORT_IMAGE_EQUIRECTANGULAR_HEIGHT * EXPORT_IMAGE_EQUIRECTANGULAR_LATITUTDE_MAX / 90; public static final int EXPORT_IMAGE_EQUIRECTANGULAR_WIDTH = 2 * EXPORT_IMAGE_EQUIRECTANGULAR_HEIGHT; public static final int EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_STEPS = 200; public static final int EXPORT_IMAGE_EQUIRECTANGULAR_WIDTH_ELEMENT = EXPORT_IMAGE_EQUIRECTANGULAR_WIDTH / EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_STEPS; private static double EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_DELTA = 360.0 / EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_STEPS; private static double EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_HALF_ELEMENT_TAN = Math .tan(EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_DELTA * Math.PI / 360.0); /** * says that an export image is needed, and call immediate display */ public void needExportImage() { setExportImageForThumbnail(true); double scale = Math.min(MyXMLio.THUMBNAIL_PIXELS_X / getWidth(), MyXMLio.THUMBNAIL_PIXELS_Y / getHeight()); needExportImage(scale, (int) (getWidth() * scale), (int) (getHeight() * scale)); } /** * * @param maxX * @param maxY * @return export image immediately */ public GBufferedImage getExportImage(double scale) { setExportImageForThumbnail(true); needExportImage(scale, (int) (getWidth() * scale), (int) (getHeight() * scale)); return getExportImage(); } private void setExportImageForThumbnail(boolean flag) { exportImageForThumbnail = flag; } /** * @return whether exported image is for thumbnail */ protected boolean getExportImageForThumbnail() { return exportImageForThumbnail; } /** * says that an export image is needed, and call immediate display * * @param scale * scale for export image */ public void needExportImage(double scale, boolean forThumbnail) { setExportImageForThumbnail(forThumbnail); needExportImage(scale, (int) (getWidth() * scale), (int) (getHeight() * scale)); } /** * @return an image containing last export image created */ public GBufferedImage getExportImage() { return null; } /** * start animation for gif export * * @param gifEncoder * gif encoder * @param num * slider to anime * @param n * number of images * @param val * start value * @param min * slider min value * @param max * slider max value * @param step * slider step */ public void startAnimatedGIFExport(Object gifEncoder, AnimationExportSlider num, int n, double val, double min, double max, double step) { exportType = ExportType.ANIMATEDGIF; num.setValue(val); num.updateRepaint(); export_i = 0; this.export_n = n; this.export_num = num; this.export_val = val; this.export_min = min; this.export_max = max; this.export_step = step; setGIFEncoder(gifEncoder); needExportImage(1, false); } /** * set gif encoder * * @param gifEncoder * gif encoder */ protected void setGIFEncoder(Object gifEncoder) { // TODO make it abstract } /** * says that we need an export image with scale, width and height * * @param scale * scale factor * @param w * width * @param h * height */ abstract protected void needExportImage(double scale, int w, int h); /** * set export image width and height * * @param w * width * @param h * height */ abstract protected void setExportImageDimension(int w, int h); protected void selectFBO() { // to be overridden } protected void unselectFBO() { // to be overridden } /** * set drawing for left eye */ final protected void setDrawLeft() { if (view3D.getCompanion().isPolarized()) { // draw where stencil's value is 0 setStencilFunc(0); } else if (view3D.getCompanion().isStereoBuffered()) { setBufferLeft(); clearColorBuffer(); } eye = EYE_LEFT; setColorMask(); clearDepthBuffer(); } /** * set drawing for right eye */ final protected void setDrawRight() { if (view3D.getCompanion().isPolarized()) { // draw where stencil's value is 1 setStencilFunc(1); } else if (view3D.getCompanion().isStereoBuffered()) { setBufferRight(); clearColorBuffer(); } if (view3D.getCompanion().isStereoBuffered() && !view3D.getCompanion().wantsStereo()) { // draw again left eye if no stereo glasses detected eye = EYE_LEFT; } else { eye = EYE_RIGHT; } setColorMask(); clearDepthBuffer(); // clear depth buffer } /** * set drawing to left buffer (when stereo buffered) */ abstract protected void setBufferLeft(); /** * set drawing to right buffer (when stereo buffered) */ abstract protected void setBufferRight(); /** * clear color buffer */ abstract protected void clearColorBuffer(); /** * clear depth buffer */ abstract protected void clearDepthBuffer(); /** * set value for the stencil function (equal to value) * * @param value * stencil value */ abstract protected void setStencilFunc(int value); /** * do export image if needed */ abstract protected void exportImage(); /** * enable culling */ abstract public void enableCulling(); /** * disable culling */ abstract public void disableCulling(); /** * cull front faces */ abstract public void setCullFaceFront(); /** * cull back faces */ abstract public void setCullFaceBack(); /** * disable blending */ abstract public void disableBlending(); /** * enable blending */ abstract public void enableBlending(); protected void drawTranspNotCurved() { disableCulling(); drawable3DLists.drawTransp(this); drawable3DLists.drawTranspClosedNotCurved(this); enableCulling(); } protected void drawTransp() { setLight(1); drawTranspNotCurved(); setCullFaceFront(); drawable3DLists.drawTranspClosedCurved(this);// draws inside parts if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawTranspClipped(this); // clipped surfaces // back-faces disableClipPlanesIfNeeded(); } setCullFaceBack(); drawable3DLists.drawTranspClosedCurved(this);// draws outside parts if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawTranspClipped(this); // clipped surfaces // back-faces disableClipPlanesIfNeeded(); } setLight(0); } /** * switch GL_LIGHT0 / GL_LIGHT1 * * @param light * GL_LIGHT0 or GL_LIGHT1 */ abstract protected void setLight(int light); protected void drawNotTransp() { setLight(1); enableBlending(); // TODO improve this ! enableCulling(); setCullFaceFront(); drawable3DLists.drawNotTransparentSurfaces(this); drawable3DLists.drawNotTransparentSurfacesClosed(this);// draws inside // parts if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawNotTransparentSurfacesClipped(this); // clipped // surfaces // back-faces disableClipPlanesIfNeeded(); } setCullFaceBack(); drawable3DLists.drawNotTransparentSurfaces(this); drawable3DLists.drawNotTransparentSurfacesClosed(this);// draws outside // parts if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawNotTransparentSurfacesClipped(this); // clipped // surfaces // back-faces disableClipPlanesIfNeeded(); } setLight(0); } /** * enable textures */ abstract public void enableTextures(); /** * disable multi samples (for antialiasing) */ abstract public void disableTextures(); /** * enable multi samples (for antialiasing) */ abstract public void enableMultisample(); /** * disable textures */ abstract public void disableMultisample(); /** * enable alpha test : avoid z-buffer writing for transparent parts */ abstract public void enableAlphaTest(); /** * disable alpha test */ abstract public void disableAlphaTest(); /** * disable lighting */ abstract public void disableLighting(); /** * enable lighting */ abstract public void enableLighting(); /** * disable shine (specular) */ public void disableShine() { // only implemented with shaders } /** * enable shine (specular) */ public void enableShine() { // only implemented with shaders } /** * init lighting */ abstract public void initLighting(); /** * set real-world origin for label * * @param origin * real-world coordinates */ abstract public void setLabelOrigin(float[] origin); /** * draw face-to screen parts (labels, ...) */ protected void drawFaceToScreen() { // drawing labels and texts enableAlphaTest(); disableLighting(); enableBlending(); enableTexturesForText(); drawable3DLists.drawLabel(this); drawable3DLists.drawNotAbsoluteText(this); disableTextures(); if (enableClipPlanes) { disableClipPlanes(); } view3D.drawMouseCursor(this); if (enableClipPlanes) { enableClipPlanes(); } } /** * draw face-to screen parts at end (absolute texts, ...) */ protected void drawFaceToScreenEnd() { // drawing texts enableAlphaTest(); disableLighting(); enableBlending(); enableTexturesForText(); drawable3DLists.drawAbsoluteText(this); disableTextures(); } public boolean enableClipPlanes; protected boolean waitForUpdateClipPlanes = false; /** * sets if clip planes have to be enabled * * @param flag * flag */ public void setEnableClipPlanes(boolean flag) { waitForUpdateClipPlanes = true; enableClipPlanes = flag; } /** * enables clip planes */ abstract protected void enableClipPlanes(); /** * disables clip planes */ abstract protected void disableClipPlanes(); /** * enable clipping if needed */ public void enableClipPlanesIfNeeded() { if (!enableClipPlanes) { enableClipPlanes(); } } /** * disable clipping if needed */ public void disableClipPlanesIfNeeded() { if (!enableClipPlanes) { disableClipPlanes(); } } /** * sets the clip planes * * @param minmax * min/max for x/y/z */ abstract public void setClipPlanes(double[][] minMax); /** * init drawing matrix to view3D toScreen matrix */ abstract protected void setMatrixView(); /** * reset to projection matrix only */ abstract protected void unsetMatrixView(); /** * enable depth mask (write in depth buffer) */ abstract public void enableDepthMask(); /** * disable depth mask (write in depth buffer) */ abstract public void disableDepthMask(); /** * enable depth test */ abstract public void enableDepthTest(); /** * disable depth test */ abstract public void disableDepthTest(); /** * set the color mask * * @param r * red * @param g * green * @param b * blue * @param a * alpha */ abstract public void setColorMask(boolean r, boolean g, boolean b, boolean a); protected void draw() { // labels if (enableClipPlanes) { enableClipPlanes(); } drawFaceToScreen(); // init drawing matrix to view3D toScreen matrix setMatrixView(); setLightPosition(); setLight(0); // drawing the cursor enableLighting(); disableAlphaTest(); enableCulling(); if (needExportImage) { // we don't want mouse cursor on export image setCullFaceBack(); // needed for further calculations } else { if (enableClipPlanes) { disableClipPlanes(); } setCullFaceBack(); view3D.drawCursor(this); if (enableClipPlanes) { enableClipPlanes(); } } // drawing hidden part enableAlphaTest(); disableTextures(); drawable3DLists.drawHiddenNotTextured(this); enableDash(); drawable3DLists.drawHiddenTextured(this); // //////////////////////////// // draw surfaces enableShine(); // draw hidden surfaces enableFading(); // from RendererShaders -- check when enable textures if // already done drawNotTransp(); disableTextures(); disableAlphaTest(); // drawing transparents parts disableDepthMask(); enableFading(); drawTransp(); enableDepthMask(); disableTextures(); enableCulling(); disableBlending(); // drawing hiding parts setColorMask(false, false, false, false); // no writing in color buffer setCullFaceFront(); // draws inside parts drawable3DLists.drawClosedSurfacesForHiding(this); // closed surfaces // back-faces if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawClippedSurfacesForHiding(this); // clipped // surfaces // back-faces disableClipPlanesIfNeeded(); } disableCulling(); drawable3DLists.drawSurfacesForHiding(this); // non closed surfaces // getGL().glColorMask(true,true,true,true); setColorMask(); // re-drawing transparents parts for better transparent effect // TODO improve it ! enableFading(); disableDepthMask(); enableBlending(); drawTransp(); enableDepthMask(); disableTextures(); // drawing hiding parts setColorMask(false, false, false, false); // no writing in color buffer disableBlending(); enableCulling(); setCullFaceBack(); // draws inside parts drawable3DLists.drawClosedSurfacesForHiding(this); // closed surfaces // front-faces if (drawable3DLists.containsClippedSurfacesInclLists()) { enableClipPlanesIfNeeded(); drawable3DLists.drawClippedSurfacesForHiding(this); // clipped // surfaces // back-faces disableClipPlanesIfNeeded(); } setColorMask(); // re-drawing transparents parts for better transparent effect // TODO improve it ! enableFading(); disableDepthMask(); enableBlending(); drawTransp(); enableDepthMask(); // //////////////////////// // end of surfaces disableShine(); // drawing not hidden parts enableDash(); enableCulling(); setCullFaceBack(); drawable3DLists.draw(this); // draw surface outlines // drawSurfacesOutline(); disableLighting(); disableDepthTest(); // drawWireFrame(); unsetMatrixView(); // drawFPS(); // absolute texts if (enableClipPlanes) { disableClipPlanes(); } enableTexturesForText(); drawFaceToScreenEnd(); enableDepthTest(); enableLighting(); } /** * draw outline for surfaces */ abstract protected void drawSurfacesOutline(); /* * private void drawWireFrame() { * * getGL().glPushAttrib(GLlocal.GL_ALL_ATTRIB_BITS); * * getGL().glDepthMask(false); getGL().glPolygonMode(GLlocal.GL_FRONT, * GLlocal.GL_LINE);getGL().glPolygonMode(GLlocal.GL_BACK, GLlocal.GL_LINE); * * getGL().glLineWidth(5f); * * getGL().glEnable(GLlocal.GL_LIGHTING); * getGL().glDisable(GLlocal.GL_LIGHT0); * getGL().glDisable(GLlocal.GL_CULL_FACE); * getGL().glDisable(GLlocal.GL_BLEND); * getGL().glEnable(GLlocal.GL_ALPHA_TEST); * * drawable3DLists.drawTransp(this); * drawable3DLists.drawTranspClosedNotCurved(this); * drawable3DLists.drawTranspClosedCurved(this); if * (drawable3DLists.containsClippedSurfaces()){ enableClipPlanesIfNeeded(); * drawable3DLists.drawTranspClipped(this); disableClipPlanesIfNeeded(); } * * getGL().glPopAttrib(); } */ /** * set line width * * @param width * line width */ abstract public void setLineWidth(double width); // ///////////////////////////////////////////////// // // pencil methods // // /////////////////////////////////////////////////// /** * sets the color * * @param color * (r,g,b,a) vector * */ final public void setColor(Coords color) { setColor((float) color.getX(), (float) color.getY(), (float) color.getZ(), (float) color.getW()); } /** * sets the color * * @param color * (r,g,b,a) */ final public void setColor(GColor color) { setColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); } /** * sets the color * * @param r * red * @param g * green * @param b * blue * @param a * alpha * */ abstract protected void setColor(float r, float g, float b, float a); // layer /** * sets the layer to l. Use gl.glPolygonOffset( ). * * @param l * the layer */ abstract public void setLayer(int l); // drawing matrix /** * sets the matrix in which coord sys the pencil draws. * * @param a_matrix * the matrix */ public void setMatrix(CoordMatrix4x4 a_matrix) { m_drawingMatrix = a_matrix; } /** * gets the matrix describing the coord sys used by the pencil. * * @return the matrix */ public CoordMatrix4x4 getMatrix() { return m_drawingMatrix; } /** * sets the drawing matrix to openGLlocal. same as * initMatrix(m_drawingMatrix) */ abstract public void initMatrix(); /** * set the matrix for face to screen part */ abstract public void initMatrixForFaceToScreen(); /** * turn off the last drawing matrix set in openGLlocal. */ abstract public void resetMatrix(); // ///////////////////////////////////////////////////////// // drawing geometries final public Manager getGeometryManager() { return geometryManager; } // ///////////////////////////////////////////////////////// // textures public Textures getTextures() { return textures; } /** * draws a 3D cross cursor * * @param cursorType */ final public void drawCursor(int cursorType) { if (!PlotterCursor.isTypeAlready(cursorType)) { disableLighting(); } initMatrix(); geometryManager.draw(geometryManager.cursor.getIndex(cursorType)); resetMatrix(); if (!PlotterCursor.isTypeAlready(cursorType)) { enableLighting(); } } final public void drawCompletingCursor(double value, boolean out) { initMatrix(); setLineWidth(PlotterCompletingCursor.WIDTH); enableBlending(); geometryManager.getCompletingCursor().drawCircle(out); geometryManager.getCompletingCursor().drawCompleting(value, out); disableBlending(); resetMatrix(); } /** * draws a view button */ public void drawViewInFrontOf() { // Application.debug("ici"); initMatrix(); disableBlending(); geometryManager.draw(geometryManager.getViewInFrontOf().getIndex()); enableBlending(); resetMatrix(); } /** * draws mouse cursor */ final public void drawMouseCursor() { initMatrixForFaceToScreen(); disableBlending(); disableCulling(); geometryManager.draw(geometryManager.getMouseCursor().getIndex()); enableCulling(); enableBlending(); resetMatrix(); } public int startPolygons(int old) { return geometryManager.startPolygons(old); } public void endPolygons() { geometryManager.endPolygons(); } /* * draws the text s * * @param x x-coord * * @param y y-coord * * @param s text * * @param colored says if the text has to be colored * * public void drawText(float x, float y, String s, boolean colored){ * * * //if (true) return; * * getGL().glMatrixMode(GLlocal.GL_TEXTURE); getGL().glLoadIdentity(); * * getGL().glMatrixMode(GLlocal.GL_MODELVIEW); * * * initMatrix(); initMatrix(view3D.getUndoRotationMatrix()); * * * textRenderer.begin3DRendering(); * * if (colored) textRenderer.setColor(textColor); * * * float textScaleFactor = DEFAULT_TEXT_SCALE_FACTOR/((float) * view3D.getScale()); * * * if (x<0) x=x-(s.length()-0.5f)*8; //TODO adapt to police size * * textRenderer.draw3D(s, x*textScaleFactor,//w / -2.0f * textScaleFactor, * y*textScaleFactor,//h / -2.0f * textScaleFactor, 0, textScaleFactor); * * textRenderer.end3DRendering(); * * * * resetMatrix(); //initMatrix(m_view3D.getUndoRotationMatrix()); * resetMatrix(); //initMatrix(); * * } */ // /////////////////////// // FPS /* * double displayTime = 0; int nbFrame = 0; double fps = 0; * * private void drawFPS(){ * * if (displayTime==0) displayTime = System.currentTimeMillis(); * * nbFrame++; * * double newDisplayTime = System.currentTimeMillis(); * * * //displayTime = System.currentTimeMillis(); if (newDisplayTime > * displayTime+1000){ * * * * fps = 1000*nbFrame/(newDisplayTime - displayTime); displayTime = * newDisplayTime; nbFrame = 0; } * * * * * getGL().glMatrixMode(GLlocal.GL_TEXTURE); getGL().glLoadIdentity(); * * getGL().glMatrixMode(GLlocal.GL_MODELVIEW); * * getGL().glPushMatrix(); getGL().glLoadIdentity(); * * * textRenderer.begin3DRendering(); * * * textRenderer.setColor(Color.BLACK); * * * textRenderer.draw3D("FPS="+ ((int) fps),left,bottom,0,1); * * textRenderer.end3DRendering(); * * getGL().glPopMatrix(); } */ /** * set hits for mouse location * * @param mouseLoc * mouse location * @param threshold * threshold */ abstract public void setHits(GPoint mouseLoc, int threshold); /** * set label hits for mouse location * * @param mouseLoc * mouse location * @return first label hitted geo */ abstract public GeoElement getLabelHit(GPoint mouseLoc); abstract protected void pushSceneMatrix(); public enum PickingType { POINT_OR_CURVE, SURFACE, LABEL } /** * process picking for intersection curves SHOULD NOT BE CALLED OUTSIDE THE * DISPLAY LOOP */ abstract public void pickIntersectionCurves(); // //////////////////////////////// // LIGHTS // //////////////////////////////// static final private float SQRT2_DIV2 = (float) Math.sqrt(2) / 2; public static final float[] LIGHT_POSITION_W = { SQRT2_DIV2, 0f, SQRT2_DIV2 }; static final public float[] LIGHT_POSITION_D = { SQRT2_DIV2, 0f, SQRT2_DIV2, 0f }; protected void setLightPosition() { setLightPosition(getLightPosition()); } /** * * @return light position */ abstract protected float[] getLightPosition(); /** * set light position * * @param values * attribute values */ abstract protected void setLightPosition(float[] values); /** * set light ambiant and diffuse values (white lights) * */ abstract protected void setLightAmbiantDiffuse(float ambiant0, float diffuse0, float ambiant1, float diffuse1); // //////////////////////////////// // clear color protected boolean waitForUpdateClearColor = false; public void setWaitForUpdateClearColor() { waitForUpdateClearColor = true; } final protected void updateClearColor() { GColor c = view3D.getApplyedBackground(); float r, g, b; if (view3D.getProjection() == EuclidianView3D.PROJECTION_GLASSES && !view3D.getCompanion().isPolarized() && !view3D.getCompanion().isStereoBuffered()) { // grayscale for // anaglyph // glasses r = (float) (c.getGrayScale() / 255); g = r; b = r; } else { r = (float) c.getRed() / 255; g = view3D.isShutDownGreen() ? 0 : (float) c.getGreen() / 255; b = (float) c.getBlue() / 255; } setClearColor(r, g, b, 1.0f); } /** * set clear color * * @param r * red * @param g * green * @param b * blue * @param a * alpha */ abstract public void setClearColor(float r, float g, float b, float a); // //////////////////////////////// // initializations /** * init shaders (when used) */ protected void initShaders() { // no shader here } /** * Use the shaderProgram that got linked during the init part. */ protected void useShaderProgram() { // no shader here } /** * * @return new geometry manager */ abstract protected Manager createManager(); abstract protected void setColorMaterial(); abstract protected void setLightModel(); abstract protected void setAlphaFunc(); /** * ensure that animation is on (needed when undocking/docking 3D view) */ abstract public void resumeAnimator(); // projection mode public int left = 0; public int right = 640; public int bottom = 0; public int top = 480; public int getLeft() { return left; } public int getRight() { return right; } public int getWidth() { return right - left; } /** * Used for dip density devices * * @return height in pixels */ public int getWidthInPixels() { return getWidth(); } public int getBottom() { return bottom; } public int getTop() { return top; } public int getHeight() { return top - bottom; } /** * Used for dip density devices * * @return height in pixels */ public int getHeightInPixels() { return getHeight(); } public int getVisibleDepth() { return getWidth() * 2; } // keep visible objects at twice center-to-right distance public int getNear() { return -getWidth(); } public int getFar() { return getWidth(); } /** * for a line described by (o,v), return the min and max parameters to draw * the line * * @param minmax * initial interval * @param o * origin of the line * @param v * direction of the line * @param extendedDepth * says if it looks to real depth bounds, or working depth bounds * @return interval to draw the line */ public double[] getIntervalInFrustum(double[] minmax, Coords o, Coords v, boolean extendedDepth) { double left1 = (getLeft() - o.get(1)) / v.get(1); double right1 = (getRight() - o.get(1)) / v.get(1); updateIntervalInFrustum(minmax, left1, right1); double top1 = (getTop() - o.get(2)) / v.get(2); double bottom1 = (getBottom() - o.get(2)) / v.get(2); updateIntervalInFrustum(minmax, top1, bottom1); double halfDepth = getVisibleDepth() / 2.0; double front = (-halfDepth - o.get(3)) / v.get(3); double back = (halfDepth - o.get(3)) / v.get(3); updateIntervalInFrustum(minmax, front, back); return minmax; } /** * return the intersection of intervals [minmax] and [v1,v2] * * @param minmax * initial interval * @param v1 * first value * @param v2 * second value * @return intersection interval */ private static double[] updateIntervalInFrustum(double[] minmax, double v1, double v2) { double vMin = v1; double vMax = v2; if (vMin > vMax) { vMin = v2; vMax = v1; } if (vMin > minmax[0]) { minmax[0] = vMin; } if (vMax < minmax[1]) { minmax[1] = vMax; } return minmax; } /** * set up the view */ abstract protected void setView(); public boolean waitForDisableStencilLines = false; public void setWaitForDisableStencilLines() { waitForDisableStencilLines = true; } abstract protected void disableStencilLines(); public boolean waitForSetStencilLines = false; public void setWaitForSetStencilLines() { waitForSetStencilLines = true; } abstract protected void setStencilLines(); protected void setProjectionMatrixForPicking() { switch (view3D.getProjection()) { default: case EuclidianView3D.PROJECTION_ORTHOGRAPHIC: viewOrtho(); break; case EuclidianView3D.PROJECTION_GLASSES: viewGlasses(); break; case EuclidianView3D.PROJECTION_PERSPECTIVE: viewPersp(); break; case EuclidianView3D.PROJECTION_OBLIQUE: viewOblique(); break; } } public final void setProjectionMatrix() { switch (view3D.getProjection()) { default: case EuclidianView3D.PROJECTION_ORTHOGRAPHIC: viewOrtho(); break; case EuclidianView3D.PROJECTION_PERSPECTIVE: viewPersp(); break; case EuclidianView3D.PROJECTION_GLASSES: case EuclidianView3D.PROJECTION_EQUIRECTANGULAR: viewGlasses(); break; case EuclidianView3D.PROJECTION_OBLIQUE: viewOblique(); break; } } /** * for shaders : update projection matrix */ abstract public void updateOrthoValues(); /** * Set Up An Ortho View regarding left, right, bottom, front values * */ abstract protected void viewOrtho(); protected double[] eyeToScreenDistance = new double[2]; final public void setNear(double left, double right) { eyeToScreenDistance[EYE_LEFT] = left; eyeToScreenDistance[EYE_RIGHT] = right; updatePerspValues(); updatePerspEye(); } /** distance camera-near plane */ private final static double PERSP_NEAR_MIN = 10; public double[] perspNear = { PERSP_NEAR_MIN, PERSP_NEAR_MIN }; public double[] perspLeft = new double[2]; public double[] perspRight = new double[2]; public double[] perspBottom = new double[2]; public double[] perspTop = new double[2]; public double[] perspFar = new double[2]; public double[] perspDistratio = new double[2]; public double[] perspFocus = new double[2]; public Coords perspEye; protected void updatePerspValues() { for (int i = 0; i < 2; i++) { perspNear[i] = eyeToScreenDistance[i] - getVisibleDepth() / 2.0; if (perspNear[i] < PERSP_NEAR_MIN) { perspNear[i] = PERSP_NEAR_MIN; } perspFocus[i] = -eyeToScreenDistance[i] + view3D.getScreenZOffset(); // App.error(""+ view3D.getScreenZOffset()); if (exportImageEquirectangular) { // frustum: top and bottom perspTop[i] = EXPORT_IMAGE_EQUIRECTANGULAR_LATITUTDE_MAX_TAN * perspNear[i]; perspBottom[i] = -perspTop[i]; // ratio to see vertical angle of 45 degrees perspDistratio[i] = perspTop[i] / getTop(); // frustum: right and left perspRight[i] = perspNear[i] * EXPORT_IMAGE_EQUIRECTANGULAR_LONGITUDE_HALF_ELEMENT_TAN; perspLeft[i] = -perspRight[i]; } else { // ratio so that distance on screen plane are not changed perspDistratio[i] = perspNear[i] / eyeToScreenDistance[i]; // frustum perspLeft[i] = getLeft() * perspDistratio[i]; perspRight[i] = getRight() * perspDistratio[i]; perspBottom[i] = getBottom() * perspDistratio[i]; perspTop[i] = getTop() * perspDistratio[i]; } // distance camera-far plane perspFar[i] = perspNear[i] + getVisibleDepth(); } } private void updatePerspEye() { perspEye = new Coords(glassesEyeX[1], glassesEyeY[1], -perspFocus[EYE_LEFT], 1); // perspFocus is negative } /** * * @return coords of the eye (in real coords) when perspective projection */ public Coords getPerspEye() { return perspEye; } /** * * @return eyes separation (half of, in real coords) */ public double getEyeSep() { return (glassesEyeX[0] - glassesEyeX[1]) / 2; } abstract protected void viewPersp(); public double[] glassesEyeX = new double[2]; public double[] glassesEyeX1 = new double[2]; public double[] glassesEyeY = new double[2]; public double[] glassesEyeY1 = new double[2]; public void updateGlassesValues() { for (int i = 0; i < 2; i++) { // eye values glassesEyeX[i] = view3D.getEyeX(i); glassesEyeY[i] = view3D.getEyeY(i); // eye values for frustum glassesEyeX1[i] = glassesEyeX[i] * perspDistratio[i]; glassesEyeY1[i] = glassesEyeY[i] * perspDistratio[i]; } } abstract protected void viewGlasses(); public static final int EYE_LEFT = 0; public static final int EYE_RIGHT = 1; public int eye = EYE_LEFT; protected void setColorMask() { if (view3D.getProjection() == EuclidianView3D.PROJECTION_GLASSES && !view3D.getCompanion().isPolarized() && !view3D.getCompanion().isStereoBuffered()) { if (eye == EYE_LEFT) { setColorMask(true, false, false, true); // cyan // getGL().glColorMask(false,true,false,true); //magenta // getGL().glColorMask(false,false,false,true); } else { setColorMask(false, !view3D.isGlassesShutDownGreen(), true, true); // red // getGL().glColorMask(true,false,false,true); //cyan -> green // getGL().glColorMask(false,false,false,true); } } else { setColorMask(true, true, true, true); } } public enum ExportType { NONE, ANIMATEDGIF, THUMBNAIL_IN_GGBFILE, PNG, CLIPBOARD, UPLOAD_TO_GEOGEBRATUBE } public double obliqueX; public double obliqueY; private Coords obliqueOrthoDirection; // direction "orthogonal" to the // screen (i.e. not visible) private ExportType exportType = ExportType.NONE; private int export_n; private double export_val; private double export_min; private double export_max; private double export_step; private int export_i; private AnimationExportSlider export_num; public void updateProjectionObliqueValues() { double angle = Math.toRadians(view3D.getProjectionObliqueAngle()); obliqueX = -view3D.getProjectionObliqueFactor() * Math.cos(angle); obliqueY = -view3D.getProjectionObliqueFactor() * Math.sin(angle); obliqueOrthoDirection = new Coords(obliqueX, obliqueY, -1, 0); } abstract protected void viewOblique(); /** * * @return x oblique factor */ public double getObliqueX() { return obliqueX; } /** * * @return y oblique factor */ public double getObliqueY() { return obliqueY; } public Coords getObliqueOrthoDirection() { return obliqueOrthoDirection; } /** * Set Up An Ortho View after setting left, right, bottom, front values * * @param x * left * @param y * bottom * @param w * width * @param h * height * */ public void setView(int x, int y, int w, int h) { left = x - w / 2; bottom = y - h / 2; right = left + w; top = bottom + h; if (needExportImage) { return; } switch (view3D.getProjection()) { default: case EuclidianView3D.PROJECTION_ORTHOGRAPHIC: updateOrthoValues(); break; case EuclidianView3D.PROJECTION_PERSPECTIVE: updatePerspValues(); updatePerspEye(); break; case EuclidianView3D.PROJECTION_GLASSES: updatePerspValues(); updateGlassesValues(); updatePerspEye(); if (view3D.getCompanion().isPolarized()) { setWaitForSetStencilLines(); } break; case EuclidianView3D.PROJECTION_OBLIQUE: updateProjectionObliqueValues(); break; } setView(); view3D.setViewChanged(); view3D.setWaitForUpdate(); } public void exportToClipboard() { exportType = ExportType.CLIPBOARD; needExportImage(App.getMaxScaleForClipBoard(view3D), true); } public void uploadToGeoGebraTube() { exportType = ExportType.UPLOAD_TO_GEOGEBRATUBE; needExportImage(); } /** * Double.POSITIVE_INFINITY for parallel projections * * @return eye to screen distance */ public double getEyeToScreenDistance() { if (view3D.getProjection() == EuclidianView3D.PROJECTION_PERSPECTIVE || view3D.getProjection() == EuclidianView3D.PROJECTION_GLASSES || view3D .getProjection() == EuclidianView3D.PROJECTION_EQUIRECTANGULAR) { return eyeToScreenDistance[EYE_LEFT]; } return Double.POSITIVE_INFINITY; } /** * enable GL textures 2D */ abstract public void enableTextures2D(); /** * disable GL textures 2D */ abstract public void disableTextures2D(); /** * generate textures * * @param number * texture length * @param index * indices */ abstract public void genTextures2D(int number, int[] index); /** * bind the texture * * @param index * texture index */ abstract public void bindTexture(int index); abstract public GBufferedImage createBufferedImage(DrawLabel3D label); /** * create alpha texture for label from image * * @param label * label * @param bimg * buffered image */ abstract public void createAlphaTexture(DrawLabel3D label, GBufferedImage bimg); /** * * @param val * @return first power of 2 greater than val */ public static final int firstPowerOfTwoGreaterThan(int val) { int ret = 1; while (ret < val) { ret *= 2; } return ret; } /** * @param sizeX * @param sizeY * @param buf */ abstract public void textureImage2D(int sizeX, int sizeY, byte[] buf); /** * set texture linear parameters */ abstract public void setTextureLinear(); /** * set texture nearest parameters */ abstract public void setTextureNearest(); /** * init the renderer */ public void init() { initShaders(); geometryManager = createManager(); // GL_LIGHT0 & GL_LIGHT1 float ambiant0 = 0.5f; float diffuse0 = 1f - ambiant0; float ambiant1 = 0.4f; float diffuse1 = 1f - ambiant1; setLightAmbiantDiffuse(ambiant0, diffuse0, ambiant1, diffuse1); // material and light setColorMaterial(); // setLight(GLlocal.GL_LIGHT0); setLightModel(); enableLightingOnInit(); // common enabling enableDepthTest(); setDepthFunc(); enablePolygonOffsetFill(); initCulling(); // blending setBlendFunc(); enableBlending(); updateClearColor(); setAlphaFunc(); // normal anti-scaling enableNormalNormalized(); // textures initTextures(); // reset euclidian view view3D.reset(); // ensure that animation is on (needed when undocking/docking 3D view) resumeAnimator(); } protected void initTextures() { textures.init(); } protected void enableLightingOnInit() { enableLighting(); } protected void initCulling() { enableCulling(); setCullFaceBack(); } /** * set the depth function */ abstract protected void setDepthFunc(); /** * enable polygon offset fill */ abstract protected void enablePolygonOffsetFill(); /** * set the blend function */ abstract protected void setBlendFunc(); /** * enables normalization for normals */ abstract protected void enableNormalNormalized(); /** * enable fading (e.g. for planes) */ abstract public void enableFading(); /** * enable text textures */ public void enableTexturesForText() { enableTextures(); } /** * enable fading (e.g. for planes) */ abstract public void enableDash(); abstract public void setDashTexture(int index); /** * * @return true if it uses shaders */ abstract public boolean useShaders(); /** * * @return hitting */ public Hitting getHitting() { return null; } /** * reset the point center */ public void resetCenter() { // only used with shaders } /** * * @return the 3D view attached */ final public EuclidianView3D getView() { return view3D; } public CoordMatrix4x4 getToScreenMatrix() { return view3D.getToScreenMatrixForGL(); } final public void setNeedExportImage(boolean flag) { // Log.printStacktrace("" + flag); needExportImage = flag; } protected ExportType getExportType() { return exportType; } protected AnimationExportSlider getExportNum() { return export_num; } protected double getExportVal() { return export_val; } protected double getExportMax() { return export_max; } protected double getExportMin() { return export_min; } protected int getExportI() { return export_i; } protected double getExportN() { return export_n; } protected double getExportStep() { return export_step; } protected RendererType getType() { return type; } protected void setType(RendererType t) { type = t; } protected void setExportVal(ExportType t) { exportType = t; } protected void setExportStep(double step) { export_step = step; } protected void setExportVal(double val) { export_val = val; } protected void setExportI(int i) { export_i = i; } protected void setExportType(ExportType type) { exportType = type; } }