Computer graphics/2013-2014/Laboratory 10
Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, JOGL template.
World Box
editThe world box is nothing else than a simple cube (sphere) which contains all of the scene's objects as well as the camera. The following constraints must be met:
- the world box must be inside the view volume (or at least the sides that are visible)
- the objects outside the world box should not be draws (they will not be visible)
- the camera should not be allowed to exit the bounding box
World boxes are used to add realism to our environment by showing distant mountains, clouds, star fields. Generally they are used to in the scenes objects that are to far from us and thus cannot be reached (near the horizon).
Important: when drawing the world box we should note that it must be drawn, so that the face polygon is on the inside!
Using glDrawArrays and glDrawElements instead of glVertex*, glNormal* and glTexCoord* methods
editInstead of drawing every vertex, normal and texture coordinates we can directly use an array of elements and dereference them by indices. This approach has several advantages including: minimising both the code length and the memory space by not duplicating shared vertices.
The following example shows how we can draw a cube using glDrawElements:
// use the JOGL BufferUtil class for handling buffers
import com.sun.opengl.util.BufferUtil;
private void drawCube (GL gl)
{
float vertices[] = {1,1,0, 0,1,0, 0,0,0, 1,0,0
1,0,1, 1,1,1, 0,1,1, 0,0,1}; // 8 vertex coords
int indices[] = {0,1,2,3,
0,3,4,5,
0,5,6,1,
1,6,7,2,
7,4,3,2,
4,7,6,5}; // 24 indices
FloatBuffer bVertices = BufferUtil.newFloatBuffer(vertices.length);
for (int i = 0; i < vertices.length; i++)
bVertices.put(vertices[i]);
bVertices.rewind();
IntBuffer bIndices = BufferUtil.newIntBuffer(indices.length);
for (int i = 0; i < indices.length; i++)
bIndices.put(indices[i]);
bIndices.rewind();
// Activate and specify pointer to vertex array.
gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, bVertices);
// Draw the cube.
gl.glDrawElements(GL.GL_QUADS, 24, GL.GL_UNSIGNED_INT, bIndices);
//gl.glDrawArrays(GL.GL_QUADS, 0, vertices.length);
// Deactivate vertex arrays after drawing.
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
}
Normals and texture coordinates can be added in the same way by using arrays mixed with glNormalPointer and glTexCoordPointer:
[...]
gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
[...]
gl.glNormalPointer(GL.GL_FLOAT, 0, bNormals);
gl.glTexCoordPointer(3, GL.GL_FLOAT, 0, bTextures);
[...]
// Draw cube.
[...]
NOTE: For a cube, normals and texture coordinates cannot be shared! So the indices must go from 0 to 23 instead of 0 to 7.
Links:
Object Collisions
editCollision detection allows programmers to simulate solid objects through which nothing can pass. It is useful in simulating physical laws. For example we could simulate a billiard game or a projectile which explodes when hitting a surface.
One of the simplest techniques involves surrounding each body (including the camera) with either a sphere or a cube. For simplicity we will use a sphere. The following steps detail how we could create a rudimentary collision detection mechanism:
- draw a sphere (cube) around every object
- if any of the spheres (cubes) overlap then you have a collision:
- d = the distance between 2 objects
- a = radius of the circle around the first object P
- b = radius of the circle around the second object Q
- if ( a + b >= d ) then we have a collision
Important:The previous logic should be applied each time a frame is drawn and for small steps of object/camera movement otherwise we could end up passing through an object without detecting the collision. Additionally we could check in advance if two objects would collide by computing their future positions and apply the previous logic.
To avoid passing through an object as previously stated we first need the following (given two spheres P and Q):
- the initial position of the spheres - Pi and Qi (the current position : t=0)
- the final position of the spheres - Pf and Qf (the position after moving the sphere : t=1)
- the direction vector - VP=Pf-Pi and VQ=Qf-Qi (final position - initial position)
Given the above we can compute the position at moment t as:
- P(t) = Pi + t*VP
- Q(t) = Qi + t*VQ
The distance at moment t can be then computed as:
- d(t) = A^2 + 2*t*A*B + t^2*B^2
where A = Pi - Qi B = VP - VQ
The time of collision t can be computed as follows:
t = (-A*B - sqrt((A*B)^2 - B^2 * (A*A - (a + b)^2))) / B^2
If t is inside the interval [0,1) then a collision occurs during this time frame.
NOTE: A*B means the scalar product between A and B. The result is a scalar and is given by (Ax*Bx + Ay*By + Az*Bz)
Links:
Exercise
edit- Given the scene in our previous laboratories where there are several textured spheres circling a centered one, add a surrounding world box. It must be have a cube shape. The textures on it should represent a star field.
- Add a simple collision detection mechanism so that the user should not be allowed to either leave the world box nor to go through the spheres.
NOTE: Use glDrawArrays and then glDrawElements instead of glVertex* to render the world box cube.
NOTE: For collision detection in the case of the 3D simulator (Exercise 1) the first sphere is always the camera and the second sphere could be any object in the scene. Use moveCamera together with cameraCoordsPos* to compute the current position and the position at the next step of the camera. You may need additional the tmp vector inside the moveCamera method to get the direction vector.