Rotating voxels in toxiclibs' VolumetricSpace using Quaternions?
in
Contributed Library Questions
•
1 year ago
Ok, I drew stuff in a VolumetricSpace using this blue-illuminated table tennis ball.
It looks very 2d at the beginning but just take a look at the end of the video, where I rotate it:
https://vimeo.com/40036315
Ok, I rotate it. (the following is not in the video)
The problem:
Sure enough, the coordinate system of the VolumetricSpace got rotated too.
So when I draw now, my tabletennis ball and the newly drawn line won't be congruent(wont overlap) any more.
I thought if I still want them to be congruent, the VolumetricSpace shouldn't be rotated. - only the voxels inside of it should be rotated.
So I experimented with quaternions
http://stackoverflow.com/questions/9843298/java-quaternion-3d-rotation-implementation
It didn't work out that well after a few hours.
I also saw there's a quaternion toxiclibs class, but I couldn't figure out how to use it for this task.
I also thought maybe I could use proscene to accomplish this task, but I don't exactly know how I would do that yet.
Could someone give me some advice?
Here is what I worked out so far for rotation of voxels in a VolumetricSpace:
You can press 'r' to invoke this function call:
rotation3D(points, Math.toRadians(10), 1, 0, 0);
It rotates the voxels 10 degrees around the x-axis.
I couldn't figure out how to rotate the cube in the example around it's own center. Furthermore, the voxel's positions get kind of messy.
- import java.util.ArrayList;
- import processing.core.*;
- import processing.opengl.*;
- import javax.media.opengl.GL;
- import toxi.geom.*;
- import toxi.geom.mesh.*;
- import toxi.volume.*;
- public class Test extends PApplet {
- public static void main(String[] args) {
- PApplet.main(new String[] { "--present", "Test" });
- }
- ArrayList<Integer> points = new ArrayList<Integer>();
- int DIMX = 48;
- int DIMY = 48;
- int DIMZ = 48;
- float ISO_THRESHOLD = 0.1f;
- float NS = 0.03f;
- Vec3D SCALE = new Vec3D(1, 1, 1).scaleSelf(300);
- float currScale = 1;
- VolumetricSpace volume = new VolumetricSpaceArray(SCALE, DIMX, DIMY, DIMZ);
- VolumetricSpace newVolume = new VolumetricSpaceArray(SCALE, DIMX, DIMY, DIMZ);
- TriangleMesh mesh;
- PGraphicsOpenGL pgl;
- GL gl;
- // used to store mesh on GPU
- VBO vbo;
- public void rotation3D(ArrayList<Integer> points, double angle, int x,
- int y, int z) {
- // ArrayList<Integer> newpoints = points;
- for (int i = 0; i < points.size(); i += 3) {
- int x_old = points.get(i).intValue();
- int y_old = points.get(i + 1).intValue();
- int z_old = points.get(i + 2).intValue();
- double[] initial = { 1, 0, 0, 0 };
- double[] total = new double[4];
- double[] local = new double[4];
- // components for local quaternion
- // w
- local[0] = Math.cos(0.5 * angle);
- // x
- local[1] = x * Math.sin(0.5 * angle);
- // y
- local[2] = y * Math.sin(0.5 * angle);
- // z
- local[3] = z * Math.sin(0.5 * angle);
- // local = magnitude(local);
- // components for final quaternion Q1*Q2
- // w = w1w2 - x1x2 - y1y2 - z1z2
- total[0] = local[0] * initial[0] - local[1] * initial[1] - local[2]
- * initial[2] - local[3] * initial[3];
- // x = w1x2 + x1w2 + y1z2 - z1y2
- total[1] = local[0] * initial[1] + local[1] * initial[0] + local[2]
- * initial[3] - local[3] * initial[2];
- // y = w1y2 - x1z2 + y1w2 + z1x2
- total[2] = local[0] * initial[2] - local[1] * initial[3] + local[2]
- * initial[0] + local[3] * initial[1];
- // z = w1z2 + x1y2 - y1x2 + z1w2
- total[3] = local[0] * initial[3] + local[1] * initial[2] - local[2]
- * initial[1] + local[3] * initial[0];
- // new x,y,z of the 3d point using rotation matrix made from the
- // final quaternion
- int x_new = (int) ((1 - 2 * total[2] * total[2] - 2 * total[3]
- * total[3])
- * x_old
- + (2 * total[1] * total[2] - 2 * total[0] * total[3])
- * y_old + (2 * total[1] * total[3] + 2 * total[0]
- * total[2])
- * z_old);
- int y_new = (int) ((2 * total[1] * total[2] + 2 * total[0]
- * total[3])
- * x_old
- + (1 - 2 * total[1] * total[1] - 2 * total[3] * total[3])
- * y_old + (2 * total[2] * total[3] + 2 * total[0]
- * total[1])
- * z_old);
- int z_new = (int) ((2 * total[1] * total[3] - 2 * total[0]
- * total[2])
- * x_old
- + (2 * total[2] * total[3] - 2 * total[0] * total[1])
- * y_old + (1 - 2 * total[1] * total[1] - 2 * total[2]
- * total[2])
- * z_old);
- points.set(i, x_new);
- points.set(i + 1, y_new);
- points.set(i + 2, z_new);
- }
- }
- public void setup() {
- size(1024, 768, OPENGL);
- // fill volume
- for (int z = 0; z < DIMZ; z++) {
- for (int y = 0; y < DIMY; y++) {
- for (int x = 0; x < DIMX; x++) {
- //fill volume with a box
- if(x<24+10 && y<24+10 && z<24+10 && x>24-10 && y>24-10 && z>24-10){
- volume.setVoxelAt(x, y, z, 1);
- }
- points.add(x);
- points.add(y);
- points.add(z);
- }
- }
- }
- prepareVolume(volume);
- }
- public void prepareVolume(VolumetricSpace vol){
- vol.closeSides();
- // store in IsoSurface and compute surface mesh for the given threshold
- // value
- mesh = new TriangleMesh("iso");
- IsoSurface surface = new HashIsoSurface(vol, 0.333333f);
- surface.computeSurfaceMesh(mesh, ISO_THRESHOLD);
- // get opengl related references
- pgl = (PGraphicsOpenGL) g;
- gl = pgl.gl;
- // update lighting information
- mesh.computeVertexNormals();
- // get flattened vertex array
- float[] verts = mesh.getMeshAsVertexArray();
- // in the array each vertex has 4 entries (XYZ + 1 spacing)
- int numV = verts.length / 4;
- // create a VBO instance and set buffers from mesh
- vbo = new VBO(gl, numV);
- vbo.updateVertices(verts);
- vbo.updateNormals(mesh.getVertexNormalsAsArray());
- int[] normals = new int[mesh.getVertexNormalsAsArray().length];
- for (int i = 0; i < normals.length; i++) {
- print(normals[i]);
- }
- }
- public void draw() {
- background(128);
- translate(width / 2, height / 2, 0);
- scale(currScale);
- // need to switch to pure OpenGL mode first
- pgl.beginGL();
- glLights();
- // render mesh as triangle soup
- vbo.render(GL.GL_TRIANGLES);
- // back to processing
- pgl.endGL();
- }
- // setup some lights/material for OpenGL
- void glLights() {
- gl.glEnable(GL.GL_LIGHTING);
- gl.glEnable(GL.GL_LIGHT0);
- gl.glEnable(GL.GL_COLOR_MATERIAL);
- gl.glEnable(GL.GL_BLEND);
- gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE);
- gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, new float[] {
- -1.0f, -1.0f, 1.0f, 0.5f }, 0);
- }
- public void rotate() {
- rotation3D(points, Math.toRadians(10), 1, 0, 0);
- newVolume.clear();
- int i = 0;
- for (int z = 0; z < DIMZ; z++) {
- for (int y = 0; y < DIMY; y++) {
- for (int x = 0; x < DIMX; x++) {
- // int xp = Math.abs(points.get(i).intValue());
- // int yp = Math.abs(points.get(i + 1).intValue());
- // int zp = Math.abs(points.get(i + 2).intValue());
- int xp = points.get(i).intValue();
- int yp = points.get(i + 1).intValue();
- int zp = points.get(i + 2).intValue();
- i+=3;
- /*println("x "+xp);
- println("y "+yp);
- println("z "+zp);*/
- if(xp > 0 && yp > 0 && zp > 0){
- int ix = volume.getIndexFor(x, y, z);
- float vox = volume.getVoxelAt(ix);
- int newIx = newVolume.getIndexFor(xp, yp, zp);
- newVolume.setVoxelAt(newIx,vox);
- }
- }
- }
- }
- prepareVolume(newVolume);
- }
- public void keyPressed() {
- if (key == '-')
- currScale = max(currScale - 0.1f, 0.5f);
- if (key == '=')
- currScale = min(currScale + 0.1f, 10);
- if (key == 's') {
- // save mesh as STL or OBJ file
- mesh.saveAsSTL(sketchPath("noise.stl"));
- }
- if (key == 'r') {
- rotate();
- }
- }
- }
- /**
- * A reusable, barebones wrapper for OpenGL Vertex Buffer Objects. Only
- * handles actual vertex, normal and color buffers and assumes they can be dynamically
- * modified/updated.
- *
- * @author Karsten Schmidt <info at postspectacular.com>
- */
- import javax.media.opengl.GL;
- import processing.opengl.*;
- import com.sun.opengl.util.BufferUtil;
- public class VBO {
- final int STRIDE = BufferUtil.SIZEOF_FLOAT * 4;
- int numVertices;
- int[] vertID = new int[] {-1};
- int[] normID = new int[] {-1};
- int[] colorID = new int[] {-1};
- private GL gl;
- public VBO(GL gl, int num) {
- this.gl = gl;
- numVertices = num;
- }
- public void cleanup() {
- gl.glDeleteBuffers(1, vertID, 0);
- gl.glDeleteBuffers(1, normID, 0);
- gl.glDeleteBuffers(1, colorID, 0);
- }
- void initBuffer(int[] bufferID) {
- gl.glGenBuffers(1, bufferID, 0);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufferID[0]);
- gl.glBufferData(GL.GL_ARRAY_BUFFER, numVertices * STRIDE, null,
- GL.GL_DYNAMIC_DRAW);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
- }
- public void render(int shapeID) {
- // gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
- // check if we need to use normals?
- if (normID[0] != -1) {
- gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normID[0]);
- gl.glNormalPointer(GL.GL_FLOAT, STRIDE, 0);
- }
- // check if we need to use colors?
- if (colorID[0] != -1) {
- gl.glEnableClientState(GL.GL_COLOR_ARRAY);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, colorID[0]);
- gl.glColorPointer(4, GL.GL_FLOAT, STRIDE, 0);
- }
- gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertID[0]);
- gl.glVertexPointer(3, GL.GL_FLOAT, STRIDE, 0);
- gl.glDrawArrays(shapeID, 0, numVertices);
- gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
- if (normID[0] != -1) {
- gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
- }
- if (colorID[0] != -1) {
- gl.glDisableClientState(GL.GL_COLOR_ARRAY);
- }
- }
- protected void updateBuffer(int id, float[] data) {
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, id);
- gl.glMapBuffer(GL.GL_ARRAY_BUFFER, GL.GL_WRITE_ONLY)
- .asFloatBuffer().put(data);
- gl.glUnmapBuffer(GL.GL_ARRAY_BUFFER);
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
- }
- public void updateColors(float[] colors) {
- if (colorID[0] == -1) {
- initBuffer(colorID);
- }
- updateBuffer(colorID[0], colors);
- }
- public void updateNormals(float[] normals) {
- if (normID[0] == -1) {
- initBuffer(normID);
- }
- updateBuffer(normID[0], normals);
- }
- public void updateVertices(float[] vertices) {
- if (vertID[0] == -1) {
- initBuffer(vertID);
- }
- updateBuffer(vertID[0], vertices);
- }
- }
1