Login  Register

Re: how to transform objects not using Transform3D

Posted by philjord on Jul 21, 2022; 3:50am
URL: https://forum.jogamp.org/how-to-transform-objects-not-using-Transform3D-tp4041515p4041793.html

Mr B.

You've made a lot of progress towards using the by ref work, but it needed just a little more.

I've given a "working" alteration to your code with notes at the bottom of this post

It'll move some of the vertices when pressing the keys, but it's currently only grabbing half of cube one, I'll leave it to you to get the other coordinates changed.

I've pushed all the code into one class, because it's just an example and referability outweighs code encapsulation (in this case).

The only format change you should adopt is the GeometryUpdater should be sent in as an anonymous inner class, this is just the way thread synchronized code tends to be passed around in the modern thread conscious programing paradigms (for example look at any android example from google, just filled with interface implementations for thready work).

The only down side is that it's easy to have code in a heavy duty worker thread class and put some disk or network fetching code into it, only to later realize you've pushed that code across to the main render thread and everything goes choppy.

So to get your code working I just had to flatten out your float arrays and then hang on to the reference to them, then edit the floats directly into them at update time.

The reason for flattening out the float arrays is because ByRef wants to be "closer to the metal", so the actual 3D pipeline calls always use long flat arrays. In fact they tend to only use long flat byte arrays, and the API calls have to tell the driver how many points of data there are and what the data is. Normally the coord data,the color data, the texture map data etc etc are all interleaved together, so not just every 3 floats is a coord, but every 11 floats is 3coords, 3 colors, 2 texture map, 3 normals, and in fact it's every 44 bytes, but the driver knows how to read them as floats.

So we have to use flat float arrays.

Then we need to hold onto a ref to the array (or we could ask for the geom to give it to us in the call)

I also edited the translate call to use deltas because it seemed clearer to me.

Finally I edited your way the LineArray is created to make it clearer we don't have to recreate it.

I hope this helps, please ask questions about it once you've seen it working, there is certainly a whole lot in the change from Java3D Transfrom3D to direct mods to the float data.

Phil.


package _testcase;

import org.jogamp.java3d.Appearance;
import org.jogamp.java3d.BranchGroup;
import org.jogamp.java3d.Geometry;
import org.jogamp.java3d.GeometryArray;
import org.jogamp.java3d.GeometryUpdater;
import org.jogamp.java3d.LineArray;
import org.jogamp.java3d.Locale;
import org.jogamp.java3d.Shape3D;
import org.jogamp.java3d.utils.universe.SimpleUniverse;
 

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

 

public class notusingtransform3D {

        public static void main(String[] args) {
                System.setProperty("sun.awt.noerasebackground", "true");
                new notusingtransform3D();
        }

        static float xloc = 0;
        static float yloc = 0;
        static float zloc = 0;

        LineArray cube_one, cube_two, cube_three;
        // PJ: as we are using byref geometry we can hold onto simply the float array itself and modify inplace in an updater call
        float[] cube_oneData, cube_twoData, cube_threeData;
       
        float[][] cube1_pt = {{xloc - 2, yloc + 0, zloc - 10}, {xloc - 2, yloc + 0, zloc - 11},
                {xloc - 2, yloc + 1, zloc - 11}, {xloc - 2, yloc + 1, zloc - 10}, {xloc - 1, yloc + 1, zloc - 10},
                {xloc - 1, yloc + 0, zloc - 10}, {xloc - 1, yloc + 0, zloc - 11}, {xloc - 1, yloc + 1, zloc - 11}};

        float[][] cube2_pt = {{xloc - 3, yloc + 0, zloc - 12}, {xloc - 3, yloc + 0, zloc - 14},
                {xloc - 3, yloc + 2, zloc - 14}, {xloc - 3, yloc + 2, zloc - 12}, {xloc - 1, yloc + 2, zloc - 12},
                {xloc - 1, yloc + 0, zloc - 12}, {xloc - 1, yloc + 0, zloc - 14}, {xloc - 1, yloc + 2, zloc - 14}};

        float[][] cube3_pt = {{xloc - 2, yloc + 0, zloc - 15}, {xloc - 2, yloc + 0, zloc - 17},
                {xloc - 2, yloc + 3, zloc - 17}, {xloc - 2, yloc + 3, zloc - 15}, {xloc - 1, yloc + 3, zloc - 15},
                {xloc - 1, yloc + 0, zloc - 15}, {xloc - 1, yloc + 0, zloc - 17}, {xloc - 1, yloc + 3, zloc - 17}};

        public float[][] translateArray(float[][] cube, int dir) {
                float[][] cube1 = new float[8][3];
                //copy array
                for (int i = 0; i < 8; i++) {
                        for (int j = 0; j < 3; j++) {
                                cube1 [i] [j] = cube [i] [j];
                        }
                }

                //translate
                for (int i = 0; i < 8; i++) {
                        for (int j = 0; j < 3; j++) {
                                if (dir == 0)
                                        cube1 [i] [0] = cube [i] [0] + xloc;
                                if (dir == 1)
                                        cube1 [i] [1] = cube [i] [1] + yloc;
                                if (dir == 2)
                                        cube1 [i] [2] = cube [i] [2] + zloc;
                        }
                }

                /*System.out.println("translate");
                for(int i=0; i<cube1.length; i++) {
                        System.out.print(i + ")  ");
                        for(int j=0; j<3; j++) {
                                System.out.print(cube1[i][j] + "  ");
                        }
                        System.out.println();
                }*/

                return cube1;
        }
       
        // PJ: takes a flatten array of floats as input (each set of 3 floats is a vertex coordinate)
        // translates directly into the array as arrays are like object pointers and not passed by value like a primitive
        public void translateArray(float[] cube,float deltaX,float deltaY, float deltaZ) {
                //translate, if cases will be slower than just setting the values
                //deltas are easier to understand here
                for (int i = 0; i < 8; i++) {
                        cube [i*3+0] += deltaX;
                        cube [i*3+1] += deltaY;
                        cube [i*3+2] += deltaZ;
                }
        }

        public notusingtransform3D() {

                SimpleUniverse uni = new SimpleUniverse();
                BranchGroup group = new BranchGroup();
                Appearance app = new Appearance();
                Locale loc = new Locale(uni);

                cube_one = ccr1(cube1_pt);//creates LineArray (Geometry) from float points array
                // PJ: grab the geometry data ref for use in the keyevent, bbecause we can use it directly
                cube_oneData = cube_one.getCoordRefFloat();
                cube_two = ccr1(cube2_pt);
                cube_twoData = cube_two.getCoordRefFloat();
                cube_three = ccr1(cube3_pt);
                cube_threeData = cube_three.getCoordRefFloat();

                Shape3D cube1 = new Shape3D(cube_one, app); //creates Shape3D from Geometry and Appearance
                Shape3D cube2 = new Shape3D(cube_two, app);
                Shape3D cube3 = new Shape3D(cube_three, app);

                group.addChild(cube1); //adding Shape3D to BranchGroup
                group.addChild(cube2);
                group.addChild(cube3);

                loc.addBranchGraph(group);
                uni.getViewingPlatform().setNominalViewingTransform();

                uni.getCanvas().addKeyListener(new KeyListener() {

                        @Override
                        public void keyTyped(KeyEvent e) {
                        }

                        @Override
                        public void keyReleased(KeyEvent e) {
                        }

                        @Override
                        public void keyPressed(KeyEvent e) {
                                if (e.getKeyCode() == KeyEvent.VK_A) {
                                        System.out.println("a");
                                        notusingtransform3D.xloc++;
       
                                        GeometryUpdater updt = new GeometryUpdater() {
                                                @Override
                                                public void updateData(Geometry geom) {
                                                        //PJ: use the altered flat in place method, with the cleaner delta values
                                                        translateArray(cube_oneData,1,0,0);
                                        }};
                                        // PJ: we don't call this method, the rendering thread does later when it's ready to have the data changed (between frames)
                                        //updt.updateData(cube_one);
                                       
                                        // PJ: instead we call this to set the updater to be used once on the next rendering pass
                                        cube_one.updateData(updt);
                                       
                                        System.out.println(notusingtransform3D.xloc);
                                }
                                if (e.getKeyCode() == KeyEvent.VK_D) {
                                        notusingtransform3D.xloc--;
                                        // tighter layout, something something lamdas
                                        cube_one.updateData(new GeometryUpdater() {
                                                @Override
                                                public void updateData(Geometry geom) {
                                                        translateArray(cube_oneData,-1,0,0);
                                                }});
                                        System.out.println(notusingtransform3D.xloc);
                                }
                                if (e.getKeyCode() == KeyEvent.VK_W) {
                                        notusingtransform3D.yloc++;
                                        cube_one.updateData(new GeometryUpdater() {
                                                @Override
                                                public void updateData(Geometry geom) {
                                                        translateArray(cube_oneData,0,1,0);
                                                }});
                                }
                                if (e.getKeyCode() == KeyEvent.VK_S) {
                                        notusingtransform3D.yloc--;
                                        cube_one.updateData(new GeometryUpdater() {
                                                @Override
                                                public void updateData(Geometry geom) {
                                                        translateArray(cube_oneData,0,-1,0);
                                                }});
                                }
                               
                        }
                });
        }

 
        // PJ: so we dont want to recreate the geometry
        // we want to reset the coords in the geometry
        // so ccr methods need to be broken into 2 pieces, one that just translate the coords
        // and one that creates the geometry
        // basically pull new LineArray up out of it
        public static LineArray ccr1(float cube_pts[][]) {
                 
                LineArray cube = new LineArray(24,
                                GeometryArray.COORDINATES | GeometryArray.BY_REFERENCE );
               
                // PJ: the ALLOW flags are for capabilities not vertex formats, they are very similar looking on Geometry objects
                // java 3d demands lots of explicit allowing to get things done as it does a lot of optimization under the hood
                cube.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);
               
               
                ccr2(cube, cube_pts );
                return cube;
        }
       
        public static void ccr2(LineArray geom, float cube_pts[][]) {
                float[][] cube_edges = new float[24][3];
                int offset = 0;
                for (int i = 0; i < 7; i++) {
                        for (int j = i + 1; j < 8; j++) {
                                int counter = 0;
                                if (cube_pts [i] [0] == cube_pts [j] [0])
                                        counter++;
                                if (cube_pts [i] [1] == cube_pts [j] [1])
                                        counter++;
                                if (cube_pts [i] [2] == cube_pts [j] [2])
                                        counter++;
                                if (counter == 2) {
                                        for (int l = 0; l < 3; l++) {
                                                cube_edges [offset + 0] [l] = cube_pts [i] [l];
                                                cube_edges [offset + 1] [l] = cube_pts [j] [l];
                                        }
                                        offset += 2;
                                }
                        }
                }

                // PJ: make it a single flat array (3 times as long)
                float[] flat_pts = new float[cube_edges.length*3];
                for (int i = 0; i < cube_edges.length; i++) {
                        flat_pts[i*3+0] = cube_edges [i][0];
                        flat_pts[i*3+1] = cube_edges [i][1];
                        flat_pts[i*3+2] = cube_edges [i][2];
                }
                // PJ: notice the ref method and no start index, it uses the entire array
                geom.setCoordRefFloat(flat_pts);
               
                //for (int i = 0; i < cube_edges.length; i++) {
                        //Exception in thread "main" java.lang.IllegalStateException: GeometryArray: cannot directly access data in BY_REFERENCE mode
                // geom.setCoordinates(i, cube_edges [i]);
                        // so the essential issue here is that the coords should be
                        // set as one large float array of sets of 3 coords
                        // meaning we should hang onto the reference to the float[] and play wiht it directly
                //}

                /*System.out.println("cube pts length = " + cube_pts.length);
                for(int i=0; i<cube_pts.length; i++) {
                        System.out.print(i + ")  ");
                        for(int j=0; j<3; j++) {
                                System.out.print(cube_pts[i][j] + "  ");
                        }
                        System.out.println();
                }
               
                System.out.println("cube edges length = " + cube_edges.length);
                for(int i=0; i<cube_edges.length; i++) {
                        System.out.print(i + ")  ");
                        for(int j=0; j<3; j++) {
                                System.out.print(cube_edges[i][j] + "  ");
                        }
                        System.out.println();
                }
                */
                 
        }

}