summaryrefslogtreecommitdiffstats
path: root/src/demos
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-03-22 17:03:53 +0100
committerSven Gothel <[email protected]>2023-03-22 17:03:53 +0100
commitab95624f85cf2ee5fa8d2753ed68597b221fc9c9 (patch)
tree12d138520cbe02793ab09ddfcfe85ef8f0bea5bd /src/demos
parent95ea4470e7a9dd79dd502db800333e350f311d3d (diff)
GraphUI Demos: (Text) Type Animation ...
Diffstat (limited to 'src/demos')
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java302
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java395
2 files changed, 697 insertions, 0 deletions
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java
new file mode 100644
index 000000000..fa4e5ca89
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java
@@ -0,0 +1,302 @@
+/**
+ * Copyright 2010-2023 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.demos.graph.ui;
+
+import java.io.IOException;
+
+import com.jogamp.common.os.Clock;
+import com.jogamp.common.util.IOUtil;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.ui.gl.Scene;
+import com.jogamp.graph.ui.gl.shapes.GLButton;
+import com.jogamp.graph.ui.gl.shapes.Label;
+import com.jogamp.newt.MonitorDevice;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.graph.FontSetDemos;
+import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Res independent Shape, Scene attached to GLWindow showing simple linear Shape movement.
+ * <p>
+ * This variation of {@link UISceneDemo00} shows a text animation assembling one line of text,
+ * each glyph coming from the right moving to its destination sequentially.
+ * </p>
+ * <p>
+ * Pass '-keep' to main-function to keep running.
+ * </p>
+ */
+public class UISceneDemo02 {
+ static float req_total_dur_s = 6f; // [s]
+
+ public static void main(final String[] args) throws IOException {
+ final int surface_width = 1280, surface_height = 720;
+ final int renderModes = Region.VBAA_RENDERING_BIT;
+ final GLProfile reqGLP = GLProfile.getGL2ES2();
+ boolean wait_to_start = false;
+ int autoSpeed = 0;
+
+ boolean keepRunning = false;
+ if( 0 != args.length ) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-keep")) {
+ keepRunning = true;
+ } else if(args[i].equals("-dur")) {
+ ++i;
+ req_total_dur_s = MiscUtils.atoi(args[i], (int)req_total_dur_s*1000) / 1000f;
+ } else if(args[i].equals("-aspeed")) {
+ autoSpeed = 1;
+ req_total_dur_s = 1f;
+ keepRunning = true;
+ } else if(args[i].equals("-wait")) {
+ wait_to_start = true;
+ }
+ }
+ }
+
+ //
+ // Resolution independent, no screen size
+ //
+ final Font font = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeSerif.ttf", FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ // final Font font = FontFactory.get(IOUtil.getResource("jogamp/graph/font/fonts/ubuntu/Ubuntu-R.ttf", FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ System.err.println("Font: "+font.getFullFamilyName());
+ final Font fontStatus = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMonoBold.ttf", FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+
+ final Label destText = new Label(renderModes, font, 1f, "");
+ destText.setColor(0.1f, 0.1f, 0.1f, 1);
+ final Label movingGlyph = new Label(renderModes, font, 1f, "");
+ movingGlyph.setColor(0.1f, 0.1f, 0.1f, 1);
+
+ final Scene scene = new Scene();
+ scene.setClearParams(new float[] { 1f, 1f, 1f, 1f}, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ scene.addShape(destText);
+ scene.addShape(movingGlyph);
+
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(1*60, null); // System.err);
+
+ final GLCapabilities caps = new GLCapabilities(reqGLP);
+ caps.setAlphaBits(4);
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setSize(surface_width, surface_height);
+ window.setTitle(UISceneDemo02.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ window.setVisible(true);
+ window.addGLEventListener(scene);
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowResized(final WindowEvent e) {
+ window.setTitle(UISceneDemo02.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ }
+ @Override
+ public void windowDestroyNotify(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ scene.attachInputListenerTo(window);
+
+ animator.add(window);
+ animator.start();
+
+ //
+ // After initial display we can use screen resolution post initial Scene.reshape(..)
+ // However, in this example we merely use the resolution to
+ // - Compute the animation values with DPI
+ scene.waitUntilDisplayed();
+
+ final GLProfile hasGLP = window.getChosenGLCapabilities().getGLProfile();
+ final AABBox sceneBox = scene.getBounds();
+ System.err.println("SceneBox "+sceneBox);
+
+ if( wait_to_start ) {
+ MiscUtils.waitForKey("Start");
+ }
+
+ final Label statusLabel;
+ {
+ final AABBox fbox = fontStatus.getGlyphBounds("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ // final float statusLabelScale = (sceneBox.getHeight() / 30f) * fbox.getHeight();
+ final float statusLabelScale = sceneBox.getWidth() / fbox.getWidth();
+ System.err.println("StatusLabelScale: " + statusLabelScale + " = " + sceneBox.getHeight() + " / " + fbox.getHeight() + ", " + fbox);
+
+ statusLabel = new Label(renderModes, fontStatus, statusLabelScale, "Nothing there yet");
+ statusLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f);
+ statusLabel.moveTo(sceneBox.getMinX(), sceneBox.getMinY() + statusLabelScale * (fontStatus.getMetrics().getLineGap() - fontStatus.getMetrics().getDescent()), 0f);
+ scene.addShape(statusLabel);
+ }
+
+ final String originalText = "GraphUI & JOGL, Resolution Agnostic Curve Rendering @ GPU via OpenGL® on Java™";
+ // String originalText = "JOGL, Java™ Binding for the OpenGL® API";
+
+ //
+ // Compute the metric animation values -> shape obj-velocity
+ //
+ float req_dur_s = 0f;
+ float req_velocity = 0f;
+ final float init_velocity = 2000/1e3f; // [m]/[s]
+
+ final float pixPerMM, dpiV;
+ {
+ final float[] tmp = window.getPixelsPerMM(new float[2]);
+ pixPerMM = tmp[0]; // [px]/[mm]
+ final float[] sDPI = MonitorDevice.perMMToPerInch( tmp );
+ dpiV = sDPI[1];
+ }
+
+ do {
+ final AABBox fbox = font.getGlyphBounds(originalText);
+ final float fontScale = sceneBox.getWidth() / fbox.getWidth();
+ System.err.println("FontScale: "+fontScale+" = "+sceneBox.getWidth()+" / "+fbox.getWidth());
+ destText.setFontScale(fontScale);
+ movingGlyph.setFontScale(fontScale);
+
+ destText.moveTo(sceneBox.getMinX(), 0f, 0f);
+
+ final long t0_us = Clock.currentNanos() / 1000; // [us]
+ float exp_total_dur_s = 0f;
+ float total_dist_m = 0f;
+ for(int idx = 0; idx < originalText.length() && window.isNativeValid(); ++idx ) {
+ boolean skipChar = false;
+ final String[] movingChar = { null };
+ do {
+ movingChar[0] = originalText.substring(idx, idx+1);
+ if( Character.isWhitespace(movingChar[0].charAt(0)) ) {
+ destText.setText(destText.getText() + movingChar[0]);
+ ++idx;
+ skipChar = true;
+ } else {
+ skipChar = false;
+ }
+ } while( skipChar && idx < originalText.length() );
+ if( movingChar[0].isEmpty() ) {
+ break; // bail
+ }
+ // sync point
+ destText.validate(hasGLP);
+ movingGlyph.setText(hasGLP, movingChar[0]);
+ final float start_pos = sceneBox.getMaxX() - movingGlyph.getScaledWidth();
+ final float end_pos = sceneBox.getMinX() + ( destText.getText().isEmpty() ? 0 : destText.getScaledWidth() );
+ movingGlyph.moveTo(start_pos, 0f, 0f);
+
+ final PMVMatrix pmv = new PMVMatrix();
+ final int[] destTextSizePx = destText.getSurfaceSize(scene, pmv, new int[2]); // [px]
+ final int[] movingGlyphSizePx = movingGlyph.getSurfaceSize(scene, pmv, new int[2]); // [px]
+ final float[] movingGlyphPixPerShapeUnit = movingGlyph.getPixelPerShapeUnit(movingGlyphSizePx, new float[2]); // [px]/[shapeUnit]
+
+ final float dist_px = scene.getWidth() - movingGlyphSizePx[0]; // [px]
+ final float dist_m = dist_px/pixPerMM/1e3f; // [m]
+ final float exp_dur_s = dist_m / init_velocity; // [s]
+ total_dist_m += dist_m;
+ if( 0 == idx ) {
+ exp_total_dur_s = ( exp_dur_s * originalText.length() ) / 2f; // Gauss'ian sum estimate
+ req_dur_s = ( req_total_dur_s * 2f ) / originalText.length();
+ req_velocity = dist_m / req_dur_s;
+ // req_dur_s = exp_dur_s;
+ // req_velocity = init_velocity;
+ }
+ final float velocity_px = req_velocity * 1e3f * pixPerMM; // [px]/[s]
+ final float velovity_obj = velocity_px / movingGlyphPixPerShapeUnit[0]; // [shapeUnit]/[s]
+
+ if( 0 == idx ) {
+ System.err.println();
+ System.err.printf("DestText: %d x %d [pixel], %s%n", destTextSizePx[0], destTextSizePx[1], destText.getText());
+ System.err.printf("MovingGl: %d x %d [pixel], %.4f px/su, %s%n", movingGlyphSizePx[0], movingGlyphSizePx[1], movingGlyphPixPerShapeUnit[0], movingGlyph.getText());
+ // System.err.printf("Shape: %s%n", movingGlyph);
+ System.err.println();
+ System.err.printf("Distance: %.0f pixel @ %.3f px/mm, %.3f mm%n", dist_px, pixPerMM, dist_m*1e3f);
+ System.err.printf("Velocity: init %.3f mm/s, req %.3f mm/s, %.3f px/s, %.6f obj/s, duration exp %.3f s, req %.3f s%n",
+ init_velocity*1e3f, req_velocity*1e3f, velocity_px, velovity_obj, exp_dur_s, req_dur_s);
+ // System.err.println();
+ // System.err.printf("Path: start %.4f, end %.4f, pos %.5f%n", start_pos, end_pos, movingGlyph.getPosition()[0]);
+ }
+
+ final long t1_us = Clock.currentNanos() / 1000; // [us]
+ final long[] t2_us = { t1_us };
+ while( movingGlyph.getPosition()[0] > end_pos && window.isNativeValid() ) {
+ // Move on GL thread to have vsync for free
+ // Otherwise we would need to employ a sleep(..) w/ manual vsync
+ final long[] t3_us = { 0 };
+ window.invoke(true, (drawable) -> {
+ t3_us[0] = Clock.currentNanos() / 1000;
+ final float dt_s = ( t3_us[0] - t2_us[0] ) / 1e6f;
+ final float dx = -1f * velovity_obj * dt_s; // [shapeUnit]
+ movingGlyph.move(dx, 0f, 0f);
+ final String text = String.format("%s, anim-duration %.1f s",
+ scene.getStatusText(drawable, renderModes, 0, dpiV), req_total_dur_s);
+ statusLabel.setText(text);
+ return true;
+ });
+ t2_us[0] = t3_us[0];
+ }
+ if( 0 == idx ) {
+ final float has_dur_s = ( ( Clock.currentNanos() / 1000 ) - t1_us ) / 1e6f; // [us]
+ System.err.printf("Actual char travel-duration %.3f s, %.3f mm/s, delay exp %.3f s, req %.3f%n",
+ has_dur_s, (dist_m/has_dur_s)*1e3f, has_dur_s-exp_dur_s, has_dur_s-req_dur_s);
+ }
+ destText.setText(destText.getText() + movingGlyph.getText());
+ movingGlyph.setText("");
+ }
+ final float has_dur_s = ( ( Clock.currentNanos() / 1000 ) - t0_us ) / 1e6f; // [us]
+ System.err.printf("Text travel-duration %.3f s, dist %.3f mm, %.3f mm/s, %d chars, %.3f s/char; Exp %.3f s, delay %.3f s, Req %.3f s, delay %.3f s%n",
+ has_dur_s, total_dist_m*1e3f, (total_dist_m/has_dur_s)*1e3f, originalText.length(), has_dur_s / originalText.length(),
+ exp_total_dur_s, has_dur_s - exp_total_dur_s,
+ req_total_dur_s, has_dur_s - req_total_dur_s);
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+ destText.setText("");
+ if( autoSpeed > 0 ) {
+ if( req_total_dur_s > 3f ) {
+ req_total_dur_s -= 3f;
+ } else {
+ req_total_dur_s += 3f;
+ autoSpeed = -1;
+ }
+ } else if( autoSpeed < 0 ) {
+ if( req_total_dur_s < 10f ) {
+ req_total_dur_s += 3f;
+ } else {
+ req_total_dur_s -= 3f;
+ autoSpeed = 1;
+ }
+ }
+ } while ( keepRunning && window.isNativeValid() );
+ if( !keepRunning ) {
+ window.destroy();
+ }
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java
new file mode 100644
index 000000000..97b6090b9
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java
@@ -0,0 +1,395 @@
+/**
+ * Copyright 2010-2023 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.demos.graph.ui;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import com.jogamp.common.os.Clock;
+import com.jogamp.common.util.IOUtil;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.ui.gl.Scene;
+import com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup;
+import com.jogamp.graph.ui.gl.shapes.Label;
+import com.jogamp.newt.MonitorDevice;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.graph.FontSetDemos;
+import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.Quaternion;
+import com.jogamp.opengl.math.Vec3f;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Res independent Shape, Scene attached to GLWindow showing simple linear Shape movement.
+ * <p>
+ * This variation of {@link UISceneDemo00} shows a text animation assembling one line of text,
+ * each glyph coming from from a random 3D point moving to its destination all at once.
+ * </p>
+ * <p>
+ * Pass '-keep' to main-function to keep running.
+ * </p>
+ */
+public class UISceneDemo03 {
+ // final String originalText = "JOGL, Java™ Binding for the OpenGL® API";
+ static final String[] originalTexts = {
+ "JOGL, Java™ Binding for the OpenGL® API",
+ "GraphUI, Resolution Independent Curves",
+ "JogAmp, Java™ libraries for 3D & Media"
+ };
+
+ static int renderModes = Region.VBAA_RENDERING_BIT;
+ static int sceneMSAASamples = 0;
+ static float velocity = 30 / 1e3f; // [m]/[s]
+ static float rot_step = velocity * 1;
+
+ static void setVelocity(final float v) {
+ velocity = v; // Math.max(1/1e3f, v);
+ rot_step = velocity * 1;
+ }
+
+ public static void main(final String[] args) throws IOException {
+ final int surface_width = 1280, surface_height = 720;
+ final GLProfile reqGLP = GLProfile.getGL2ES2();
+ int autoSpeed = 0;
+ boolean wait_to_start = false;
+
+ boolean keepRunning = false;
+ if (0 != args.length) {
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("-keep")) {
+ keepRunning = true;
+ } else if (args[i].equals("-v")) {
+ ++i;
+ setVelocity(MiscUtils.atoi(args[i], (int) velocity * 1000) / 1000f);
+ } else if(args[i].equals("-aspeed")) {
+ autoSpeed = -1;
+ setVelocity(80/1000f);
+ keepRunning = true;
+ } else if(args[i].equals("-wait")) {
+ wait_to_start = true;
+ } else if(args[i].equals("-gnone")) {
+ sceneMSAASamples = 0;
+ renderModes = 0;
+ } else if(args[i].equals("-smsaa")) {
+ i++;
+ sceneMSAASamples = MiscUtils.atoi(args[i], sceneMSAASamples);
+ renderModes = 0;
+ } else if(args[i].equals("-gmsaa")) {
+ sceneMSAASamples = 0;
+ renderModes = Region.MSAA_RENDERING_BIT;
+ } else if(args[i].equals("-gvbaa")) {
+ sceneMSAASamples = 0;
+ renderModes = Region.VBAA_RENDERING_BIT;
+ }
+ }
+ }
+
+ //
+ // Resolution independent, no screen size
+ //
+ final Font font = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeSerif.ttf",FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ // final Font font = FontFactory.get(IOUtil.getResource("jogamp/graph/font/fonts/ubuntu/Ubuntu-R.ttf",FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+ System.err.println("Font: " + font.getFullFamilyName());
+ final Font fontStatus = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMonoBold.ttf", FontSetDemos.class.getClassLoader(), FontSetDemos.class).getInputStream(), true);
+
+ final Scene scene = new Scene();
+ scene.setClearParams(new float[] { 1f, 1f, 1f, 1f }, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ scene.setPMVMatrixSetup(new MyPMVMatrixSetup());
+
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(1 * 60, null); // System.err);
+
+ final GLCapabilities caps = new GLCapabilities(reqGLP);
+ caps.setAlphaBits(4);
+ if( sceneMSAASamples > 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(sceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setSize(surface_width, surface_height);
+ window.setTitle(UISceneDemo03.class.getSimpleName() + ": " + window.getSurfaceWidth() + " x " + window.getSurfaceHeight());
+ window.setVisible(true);
+ window.addGLEventListener(scene);
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowResized(final WindowEvent e) {
+ window.setTitle(UISceneDemo03.class.getSimpleName() + ": " + window.getSurfaceWidth() + " x " + window.getSurfaceHeight());
+ }
+
+ @Override
+ public void windowDestroyNotify(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+ window.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyReleased(final KeyEvent e) {
+ if (e.getKeySymbol() == KeyEvent.VK_PLUS ||
+ e.getKeySymbol() == KeyEvent.VK_ADD)
+ {
+ if (e.isShiftDown()) {
+ setVelocity(velocity + 10 / 1000f);
+ } else {
+ setVelocity(velocity + 1 / 1000f);
+ }
+ } else if (e.getKeySymbol() == KeyEvent.VK_MINUS ||
+ e.getKeySymbol() == KeyEvent.VK_SUBTRACT)
+ {
+ if (e.isShiftDown()) {
+ setVelocity(velocity - 10 / 1000f);
+ } else {
+ setVelocity(velocity - 1 / 1000f);
+ }
+ }
+ }
+ });
+
+ scene.attachInputListenerTo(window);
+
+ animator.add(window);
+ animator.start();
+
+ //
+ // After initial display we can use screen resolution post initial
+ // Scene.reshape(..)
+ // However, in this example we merely use the resolution to
+ // - Compute the animation values with DPI
+ scene.waitUntilDisplayed();
+
+ scene.setFrustumCullingEnabled(true);
+ window.invoke(true, (drawable) -> {
+ drawable.getGL().glEnable(GL.GL_DEPTH_TEST);
+ return true;
+ });
+
+ final GLProfile hasGLP = window.getChosenGLCapabilities().getGLProfile();
+ final AABBox sceneBox = scene.getBounds();
+ System.err.println("SceneBox " + sceneBox);
+ System.err.println("Frustum " + scene.getMatrix().glGetFrustum());
+
+ if( wait_to_start ) {
+ MiscUtils.waitForKey("Start");
+ }
+
+ //
+ //
+ //
+ final float fontScale;
+ {
+ final AABBox fbox = font.getGlyphBounds(originalTexts[0]);
+ fontScale = sceneBox.getWidth() / fbox.getWidth();
+ // final float fontScale = sceneBox.getWidth() / 20;
+ System.err.println("FontScale: " + fontScale + " = " + sceneBox.getWidth() + " / " + fbox.getWidth());
+ }
+ final Label statusLabel;
+ {
+ final AABBox fbox = fontStatus.getGlyphBounds("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ // final float statusLabelScale = (sceneBox.getHeight() / 30f) * fbox.getHeight();
+ final float statusLabelScale = sceneBox.getWidth() / fbox.getWidth();
+ System.err.println("StatusLabelScale: " + statusLabelScale + " = " + sceneBox.getHeight() + " / " + fbox.getHeight() + ", " + fbox);
+
+ statusLabel = new Label(renderModes, fontStatus, statusLabelScale, "Nothing there yet");
+ statusLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f);
+ statusLabel.moveTo(sceneBox.getMinX(), sceneBox.getMinY() + statusLabelScale * (fontStatus.getMetrics().getLineGap() - fontStatus.getMetrics().getDescent()), 0f);
+ scene.addShape(statusLabel);
+ }
+
+ //
+ // Setup the moving glyphs
+ //
+
+ final List<Label> glyphs = new ArrayList<Label>();
+ final List<Label> addedGlyphs = new ArrayList<Label>();
+ final List<Vec3f> glyphsTarget = new ArrayList<Vec3f>();
+ final List<Vec3f> glyphsPos = new ArrayList<Vec3f>();
+
+ final float pixPerMM, dpiV;
+ {
+ final float[] tmp = window.getPixelsPerMM(new float[2]);
+ pixPerMM = tmp[0]; // [px]/[mm]
+ final float[] sDPI = MonitorDevice.perMMToPerInch( tmp );
+ dpiV = sDPI[1];
+ }
+
+ boolean z_only = true;
+ int txt_idx = 0;
+
+ do {
+ z_only = !z_only;
+ window.invoke(true, (drawable) -> {
+ scene.removeShapes(drawable.getGL().getGL2ES2(), addedGlyphs);
+ addedGlyphs.clear();
+ return true;
+ });
+
+ final float[] movingGlyphPixPerShapeUnit;
+ {
+ final Random random = new Random();
+
+ final Label destText = new Label(renderModes, font, fontScale, "");
+ destText.setColor(0.1f, 0.1f, 0.1f, 1);
+
+ destText.setText(hasGLP, "X");
+ final PMVMatrix pmv = new PMVMatrix();
+ final int[] movingGlyphSizePx = destText.getSurfaceSize(scene, pmv, new int[2]); // [px]
+ movingGlyphPixPerShapeUnit = destText.getPixelPerShapeUnit(movingGlyphSizePx, new float[2]); // [px]/[shapeUnit]
+ destText.setText("");
+
+ for (int idx = 0; idx < originalTexts[txt_idx].length(); ++idx) {
+ final String movingChar = originalTexts[txt_idx].substring(idx, idx + 1);
+ destText.validate(hasGLP);
+ final Label movingGlyph = new Label(renderModes, font, fontScale, movingChar);
+ movingGlyph.setColor(0.1f, 0.1f, 0.1f, 1);
+ movingGlyph.validate(hasGLP);
+ final float end_pos_x = sceneBox.getMinX() + (destText.getText().isEmpty() ? 0 : destText.getBounds().getWidth());
+ final float end_pos_y = 0f; // movingGlyph.getBounds().getCenter()[1];
+ final float end_pos_z = 0f;
+ final float start_pos_x = z_only ? end_pos_x :
+ sceneBox.getMinX() + random.nextFloat() * sceneBox.getWidth();
+ final float start_pos_y = z_only ? end_pos_y :
+ sceneBox.getMinY() + random.nextFloat() * sceneBox.getHeight();
+ final float start_pos_z = 0f + random.nextFloat() * sceneBox.getHeight() * 1f;
+ glyphsTarget.add( new Vec3f(end_pos_x, end_pos_y, end_pos_z) );
+ glyphsPos.add( new Vec3f(start_pos_x, start_pos_y, start_pos_z) );
+ movingGlyph.moveTo(start_pos_x, start_pos_y, start_pos_z);
+ glyphs.add(movingGlyph);
+
+ destText.setText( destText.getText() + movingGlyph.getText() );
+ }
+ // just add destText to scene to be cleaned up, invisible
+ destText.setEnabled(false);
+ scene.addShape(destText);
+ }
+ scene.addShapes(glyphs);
+ addedGlyphs.addAll(glyphs);
+
+ final long t0_us = Clock.currentNanos() / 1000; // [us]
+ final long[] t2_us = { t0_us };
+ while (!glyphs.isEmpty()) {
+ window.invoke(true, (drawable) -> {
+ final long t3_us = Clock.currentNanos() / 1000;
+ final float dt_s = (t3_us - t2_us[0]) / 1e6f;
+ t2_us[0] = t3_us;
+
+ final float velocity_px = velocity * 1e3f * pixPerMM; // [px]/[s]
+ final float velovity_obj = velocity_px / movingGlyphPixPerShapeUnit[0]; // [shapeUnit]/[s]
+ final float dxy = velovity_obj * dt_s; // [shapeUnit]
+
+ for (int idx = glyphs.size() - 1; 0 <= idx; --idx) {
+ final Label glyph = glyphs.get(idx);
+ final Vec3f pos = glyphsPos.get(idx);
+ final Vec3f target = glyphsTarget.get(idx);
+ final Vec3f p_t = target.minus(pos);
+ if ( p_t.length() <= glyph.getBounds().getSize() / 5f &&
+ Math.abs( glyph.getRotation().getY() ) <= 0.4f )
+ {
+ // arrived
+ glyph.moveTo(target.x(), target.y(), 0f);
+ glyph.getRotation().setIdentity();
+ glyphs.remove(idx);
+ glyphsPos.remove(idx);
+ glyphsTarget.remove(idx);
+ continue;
+ }
+ p_t.normalize();
+ pos.add(p_t.scale(dxy));
+ glyph.moveTo(pos.x(), pos.y(), pos.z());
+ final Quaternion rot = glyph.getRotation();
+ rot.rotateByAngleY(rot_step);
+ }
+ final String text = String.format("%s, v %.1f mm/s, r %.3f",
+ scene.getStatusText(drawable, renderModes, 0, dpiV), velocity * 1e3f, rot_step);
+ statusLabel.setText(text);
+ return true;
+ });
+ }
+ final float has_dur_s = ((Clock.currentNanos() / 1000) - t0_us) / 1e6f; // [us]
+ System.err.printf("Text travel-duration %.3f s, %d chars, %.3f s/char%n", has_dur_s, originalTexts[txt_idx].length(), has_dur_s / originalTexts[txt_idx].length());
+ try { Thread.sleep(2000); } catch (final InterruptedException e1) { }
+ if( autoSpeed > 0 ) {
+ if( velocity < 60/1000f ) {
+ setVelocity(velocity + 9/1000f);
+ } else {
+ setVelocity(velocity - 9/1000f);
+ autoSpeed = -1;
+ }
+ } else if( autoSpeed < 0 ) {
+ if( velocity > 11/1000f ) {
+ setVelocity(velocity - 9/1000f);
+ } else {
+ setVelocity(velocity + 9/1000f);
+ autoSpeed = 1;
+ }
+ }
+ txt_idx = ( txt_idx + 1 ) % originalTexts.length;
+ } while (keepRunning && window.isNativeValid());
+ if (!keepRunning) {
+ window.destroy();
+ }
+ }
+
+ /**
+ * Our PMVMatrixSetup:
+ * - gluPerspective like Scene's default
+ * - no normal scale to 1, keep distance to near plane for rotation effects.
+ */
+ public static class MyPMVMatrixSetup implements PMVMatrixSetup {
+ @Override
+ public void set(final PMVMatrix pmv, final int x, final int y, final int width, final int height) {
+ final float ratio = (float) width / (float) height;
+ pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
+ pmv.glLoadIdentity();
+ pmv.gluPerspective(Scene.DEFAULT_ANGLE, ratio, Scene.DEFAULT_ZNEAR, Scene.DEFAULT_ZFAR);
+ pmv.glTranslatef(0f, 0f, Scene.DEFAULT_SCENE_DIST);
+
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ }
+
+ @Override
+ public void setPlaneBox(final AABBox planeBox, final PMVMatrix pmv, final int x, final int y, final int width, final int height) {
+ Scene.getDefaultPMVMatrixSetup().setPlaneBox(planeBox, pmv, x, y, width, height);
+ }
+ };
+}