./opengl/test/java/de.oklischat.ogl.lwjgl.coil/src/de/oklischat/ogl/lwjgl/coil/CoilCanvas.java

download original
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package de.oklischat.ogl.lwjgl.coil;

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ComponentEvent;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Drawable;
import org.lwjgl.opengl.PixelFormat;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL14.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL21.*;

/**
 *
 * @author olaf
 */
public class CoilCanvas extends AWTGLCanvas {

    private static int sharedCoilDisplayList;
    private static boolean sharedCoilDisplayListInitialized = false;

    private static Drawable sharedGLDrawable;
    //private static GraphicsDevice sharedGraphicsDevice;
    private static PixelFormat sharedPixelFormat;

    public static CoilCanvas create() throws LWJGLException {
        if (null == sharedGLDrawable) {
            //sharedGraphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
            sharedPixelFormat = new PixelFormat();
            sharedGLDrawable = new CoilCanvas(null, sharedPixelFormat, null);
            return (CoilCanvas) sharedGLDrawable;
        } else {
            return new CoilCanvas(null, sharedPixelFormat, sharedGLDrawable);
        }
    }

    private CoilCanvas(GraphicsDevice gd, PixelFormat pf, Drawable sharedDrawable) throws LWJGLException {
        super(gd, pf, sharedDrawable);
        if (null == sharedGLDrawable) {
            sharedGLDrawable = this;
        }
    }

    // this is a least-effort port straight from my C "coil" test app
    // Beware: Very ugly. No typedefs in Java... must use classes eventually
    // TODO: at the very least, (re)use the FloatBuffers a bit more efficiently
    // the people in #lwjgl (on freenode) basically say: never use glPush/PopMatrix, glTranslate/Rotate/MultMatrix etc.,
    //    do all the matrix/vector algebra outside JOGL/LWJGL in Java, copy the result into a
    //    (preferably reused) direct FloatBuffer and glLoadMatrix() that

    // in isometric (glOrtho) projection: width of viewport in world coords
    final float vpWidthInWorldCoords = 100;

    // in perspective (glFrustum) projection: angular width of viewport in radiants
    /*
    05:31 < multi_io> what angular resolution (angle per pixel) do you usually choose for perspective (glFrustum) projections?
    05:32 < multi_io> or do you choose a specific angular width and height of the viewport?
    05:41 < SolraBizna> the latter
    05:50 < multi_io> ok
    05:51 < multi_io> what would be a common value for that? Are there any conventions/standards?
    05:51 < SolraBizna> at least one OpenGL reference recommends calculating the actual angular width of the window from the perspective of the person using
                        the computer
    05:52 < SolraBizna> in practice, values between 30 and 60 work pretty well

    (0.9 rad = 51.... degrees)
     */
    final float vpWidthInRadiants = 0.9F;

    public /*static*/ final float[] GLCOLOR_RED = {0.6F,0F,0F,1F};
    public /*static*/ final float[] GLCOLOR_GREEN = {0F,0.6F,0F,1F};
    public /*static*/ final float[] GLCOLOR_BLUE = {0F,0F,0.6F,1F};
    public /*static*/ final float[] GLCOLOR_WHITE = {0.6F,0.6F,0.6F,1F};

    public /*static*/ final float low_shininess[] = {5};
    public /*static*/ final float mid_shininess[] = {20,0,0,0};   // 0,0,0 for FloatBuffer "padding"?
    //20:42 < multi_io> do parameters like GL_SHININESS that only require a single float still need a FloatBuffer
    //          with 4 floats?
    //20:42 < MatthiasM2> multi_io: yes, this makes the checking code in LWJGL a lot easier
    //20:43 < MatthiasM2> and I always use the 16 FloatBuffer anyway
    //20:43 < multi_io> ok

    public /*static*/ final float high_shininess[] = {100};

//        public /*static*/ final FloatBuffer GLCOLOR_RED = FloatBuffer.wrap(new float[]{0.6F,0F,0F,1F});
//        public /*static*/ final FloatBuffer GLCOLOR_GREEN = FloatBuffer.wrap(new float[]{0F,0.6F,0F,1F});
//        public /*static*/ final FloatBuffer GLCOLOR_BLUE = FloatBuffer.wrap(new float[]{0F,0F,0.6F,1F});
//        public /*static*/ final FloatBuffer GLCOLOR_WHITE = FloatBuffer.wrap(new float[]{0.6F,0.6F,0.6F,1F});
//
//        public /*static*/ final FloatBuffer low_shininess = FloatBuffer.wrap(new float[]{5});
//        public /*static*/ final FloatBuffer mid_shininess = FloatBuffer.wrap(new float[]{20});
//        public /*static*/ final FloatBuffer high_shininess = FloatBuffer.wrap(new float[]{100});

    float[] identityTransform = new float[16];

    {
        LinAlg.fillIdentity(identityTransform);
    }

    class Coil {
        float[] locationInWorld = new float[3];
        float rotAngle;   // in degrees
        float rotAngularVelocity;   // in degrees / sec
        float[] color = new float[4];
    };

    Collection<Coil> coils = new ArrayList<Coil>();

    class Viewer {
        float[] worldToEyeCoordTransform = new float[16];
    };

    Viewer theViewer = new Viewer();

    public void init() {
        setVSyncEnabled(true);
        initCoilsAndViewer();
        setupEye2ViewportTransformation();
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_RESCALE_NORMAL);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_COLOR_MATERIAL);
        glClearColor(0,0,0,0);
        //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //glShadeModel(GL_FLAT);
        if (!sharedCoilDisplayListInitialized) {
            System.out.println("initializing coil display list...");
            sharedCoilDisplayList = glGenLists(1);  //TODO: error handling
            glNewList(sharedCoilDisplayList, GL_COMPILE);
            for (int mesh_h = 0; mesh_h < mesh_count_h; mesh_h++) {
                float ah = mesh_h * mesh_da_h;
                glBegin(GL_TRIANGLE_STRIP);
                for (int mesh_w = 0; mesh_w < mesh_count_w; mesh_w++) {
                    float aw = mesh_w * mesh_da_w;
                    float[] objv = new float[3], normv = new float[3];
                    mesh2normv(ah, aw, normv);
                    mesh2objCoord(ah, aw, objv);
                    glNormal3f(normv[0], normv[1], normv[2]);
                    glVertex3f(objv[0], objv[1], objv[2]);
                    mesh2normv(ah + mesh_da_h, aw, normv);
                    mesh2objCoord(ah + mesh_da_h, aw, objv);
                    glNormal3f(normv[0], normv[1], normv[2]);
                    glVertex3f(objv[0], objv[1], objv[2]);
                }
                glEnd();
            }
            glEndList();
            sharedCoilDisplayListInitialized = true;
        }
    }

    private void initCoilsAndViewer() {
        LinAlg.fillIdentity(theViewer.worldToEyeCoordTransform);

        Coil coil1 = new Coil();
        coil1.locationInWorld[0] = 15;
        coil1.locationInWorld[1] = 0;
        coil1.locationInWorld[2] = -70;
        LinAlg.copyArr(GLCOLOR_RED, coil1.color);
        coil1.color[0] = 0.4F;
        coil1.color[1] = 0.0F;
        coil1.color[2] = 0.0F;
        coil1.color[3] = 1.0F;
        coil1.rotAngle = 70;
        coil1.rotAngularVelocity = 0;

        Coil coil2 = new Coil();
        coil2.locationInWorld[0] = -20;
        coil2.locationInWorld[1] = 15;
        coil2.locationInWorld[2] = -110;
        LinAlg.copyArr(GLCOLOR_GREEN, coil2.color);
        coil2.rotAngle = 0;
        coil1.rotAngularVelocity = 40;

        coils.add(coil1);
        coils.add(coil2);
    }

    private void setupEye2ViewportTransformation() {
        System.out.println("projecting to viewport width=" + getWidth() + ", height=" + getHeight());
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        /*
        // isometric projection
        GLdouble vpHeightInObjCoords = vpWidthInWorldCoords * vpHeight / vpWidth;
        glOrtho(-vpWidthInWorldCoords/2,  //	GLdouble  	left,
                vpWidthInWorldCoords/2,   //    GLdouble  	right,
                -vpHeightInObjCoords/2, //    GLdouble  	bottom,
                vpHeightInObjCoords/2,  //    GLdouble  	top,
                -1000, //  GLdouble  	nearVal,
                1000   //  GLdouble  	farVal
                );
        */

        // perspective projection
        float nearVal = 3;
        float farVal = 300;
        float vpHeightInRadiants = vpWidthInRadiants * getHeight() / getWidth();
        float right = nearVal * (float) Math.tan(vpWidthInRadiants/2);
        float left = -right;
        float top = nearVal * (float) Math.tan(vpHeightInRadiants/2);
        float bottom = -top;

        glFrustum(left,
                  right,
                  bottom,
                  top,
                  nearVal,
                  farVal
                  );

        // eye coord -> viewport transformation
        glViewport(0, //GLint x,
                   0, //GLint y,
                   getWidth(), //GLsizei width,
                   getHeight() //GLsizei height
                   );
        glDepthRange(0,1);
    }

    // these should be per-coil eventually
    final float coil_radius = 15;
    final float wire_radius = 3;
    final float coil_height = 40;
    final float coil_winding_angle_range = 3 * 2 * (float) Math.PI;
    final float mesh_count_w = 20;
    final float mesh_count_h = 40 * (coil_winding_angle_range / 2 / (float) Math.PI);

    final float mesh_da_w = 2 * (float) Math.PI / (mesh_count_w - 1);
    final float mesh_da_h = coil_winding_angle_range / (mesh_count_h - 1);
    final float coil_bottom = -coil_height/2;
    final float coil_dh = coil_height / (mesh_count_h - 1);


    private void mesh2objCoord(float ah, float aw, float[] result) {
        result[0] = coil_radius * (float)Math.cos(ah) + wire_radius * (float)Math.cos(aw) * (float)Math.cos(ah);
        result[1] = coil_bottom + coil_dh * ah / mesh_da_h + wire_radius * (float)Math.sin(aw);
        result[2] = coil_radius * (float)Math.sin(ah) + wire_radius * (float)Math.cos(aw) * (float)Math.sin(ah);
    }


    private void mesh2normv(float ah, float aw, float[] result) {
        float[] tangv1 = new float[3];
        tangv1[0] = -coil_radius * (float)Math.sin(ah) - wire_radius * (float)Math.cos(aw) * (float)Math.sin(ah);
        tangv1[1] = coil_dh/mesh_da_h;
        tangv1[2] = coil_radius * (float)Math.cos(ah) + wire_radius * (float)Math.cos(ah) * (float)Math.cos(aw);

        float[] tangv2 = new float[3];
        tangv2[0] = - wire_radius * (float)Math.cos(ah) * (float)Math.sin(aw);
        tangv2[1] = wire_radius * (float)Math.cos(aw);
        tangv2[2] = - wire_radius * (float)Math.sin(ah) * (float)Math.sin(aw);

        //cross(tangv1, tangv2, unnormalized);
        float[] unnormalized = LinAlg.cross(tangv2, tangv1, null);

        LinAlg.norm(unnormalized, result);
    }


    private void drawCoil(Coil c) {
        // printf("Drawing coil at %lf, %lf, %lf\n", c.locationInWorld[0], c.locationInWorld[1], c.locationInWorld[2]);
        glPushAttrib(GL_COLOR_BUFFER_BIT|GL_CURRENT_BIT);
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        //glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
        glMaterial(GL_FRONT_AND_BACK, GL_SPECULAR, FloatBuffer.wrap(GLCOLOR_WHITE));
        glMaterial(GL_FRONT_AND_BACK, GL_SHININESS, FloatBuffer.wrap(mid_shininess));
        glColor4f(c.color[0], c.color[1], c.color[2], c.color[3]);
        glCallList(sharedCoilDisplayList);
        glPopAttrib();
    }

    private boolean contextInitialized = false, resizePending = false;

    @Override
    protected void paintGL() {
        if (!contextInitialized) {
            init();
            contextInitialized = true;
            resizePending = false;
        }
        if (resizePending) {
            setupEye2ViewportTransformation();
            resizePending = false;
        }
        animate();
        // printf("re-displaying...\n");
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glMultMatrix(FloatBuffer.wrap(theViewer.worldToEyeCoordTransform));
        // define light source
        float[] l0Pos = {200, 40, -10, 0};
        glLight(GL_LIGHT0, GL_POSITION, FloatBuffer.wrap(l0Pos));
        // global ambient light
        float ambientLight[] = {1,1,1, 0.1F};
        glLightModel(GL_LIGHT_MODEL_AMBIENT, FloatBuffer.wrap(ambientLight));
        // draw all the coils
        for (Coil c : coils) {
            glPushMatrix();
            glTranslatef(c.locationInWorld[0], c.locationInWorld[1], c.locationInWorld[2]);
            glRotatef(c.rotAngle, 0, 1, 0);
            drawCoil(c);
            glPopMatrix();
        }
        try {
            swapBuffers();
        } catch (LWJGLException ex) {
            throw new RuntimeException(ex);
        }
    }

    private long lastAnimStepTime = -1;

    private void animate() {
        final long now = System.currentTimeMillis();
        if (lastAnimStepTime > 0) {
            float dt = (float) (now - lastAnimStepTime) / 1000;
            for (Coil c : coils) {
                c.rotAngle += dt * c.rotAngularVelocity;
                c.rotAngle -= 360 * (int)(c.rotAngle / 360);
            }
        }
        lastAnimStepTime = now;
    }

    @Override
    public void componentResized(ComponentEvent e) {
        super.componentResized(e);
        if (contextInitialized) {
            //setupEye2ViewportTransformation();
            resizePending = true;
        }
    }

};

  
back to coil

(C) 1998-2017 Olaf Klischat <olaf.klischat@gmail.com>