The biggest problem among beginners is not knowing how to start when working with just a basic opengl library. My favorite is LWJGL and that is what I use. More information and how to set it up is on: lwjgl.org - Home of the Lightweight Java Game Library
Basics-
A 3d Java program requires a few things:
- Camera
- Display/Gameloop
- Rendering Loop
- Optional (Models+Model Loader)
The camera is like your eyes. Its where you are in perspective to everything. With the camera you should be able to control where you are, where you look, your field of view(think if you were wearing blinders or if you had eyes on the side of your heads), and what is too far to see.
The display contains all the elements. The Gameloop keeps everything updated and current. Calls things like rendering and input managers.
The rendering loop constantly draws vertices (corners of the model or shape) so you always see them.
Models are optional in the beginning but if you plan to make a game you'll need some sort of way to load the models. More on that later!
First thing you should do is set up a display/Gameloop:
I'm a big fan of keeping methods clean and the main method very small even if it calls on a lot of other things. It helps to debug and beautify it all, but thats just my style.
Code:
public static void main(String[] args) {
startGameView();
}
public static void startGameView() {
//setUpDisplay();
//setUpCamera();
//while (!Display.isCloseRequested()) {
// renderObjects();
// checkInput();
// Display.update();
// Display.sync(60);
//}
Display.destroy();
System.exit(0);
}
Everything is commented out for a good reason. As you learn the methods it calls you can uncomment them.
setUpDisplay();
This creates the screen.
Code:
private static void setUpDisplay() {
try {
Display.setDisplayMode(new DisplayMode(950, 700));
Display.setVSyncEnabled(true);
Display.setTitle("Client");
Display.create();
} catch (LWJGLException e) {
System.err.println("The display wasn't initialized- " + e);
Display.destroy();
System.exit(1);
}
}
Should be easy to understand for anyone Familiar with Java.
GameLoop
Code:
//while (!Display.isCloseRequested()) {
// renderObjects();
// checkInput();
// Display.update();
// Display.sync(60);
//}
In english; when the program isnt trying to be closed , it will render objects, check if theres input (like arrow keys to move the camera), and update the display.
Now for the Camera.
This camera class is very very universal. ABSOLUTELY NO NEED TO EDIT. This can be used for any project, just use the parts you want from it
Code:
/**
*
* @author Joey Livengood
*/
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.glu.GLU;
import static org.lwjgl.opengl.GL11.*;
public final class Camera {
private float x = 0;
private float y = 0;
private float z = 0;
private float pitch = 0;
private float yaw = 0;
private float roll = 0;
private float fov = 90;
private float aspectRatio;
private float zNear = 0.3f;
private float zFar = 100f;
/**
* Creates a new camera with the given aspect ratio.
* It's located at [0 0 0] with the orientation [0 0 0]. It has a zNear of 0.3, a zFar of 100.0, and an fov of 90.
* @param aspectRatio the aspect ratio (width/height) of the camera
*/
public Camera(float aspectRatio) {
this.aspectRatio = aspectRatio;
}
/**
* Creates a new camera with the given aspect ratio and location.
* @param aspectRatio the aspect ratio (width/height) of the camera
* @param x the first location coordinate
* @param y the second location coordinate
* @param z the third location coordinate
*/
public Camera(float aspectRatio, double x, double y, double z) {
this.aspectRatio = aspectRatio;
this.x = (float) x;
this.y = (float) y;
this.z = (float) z;
}
/**
* Creates a new camera with the given aspect ratio and location.
* @param aspectRatio the aspect ratio (width/height) of the camera
* @param x the first location coordinate
* @param y the second location coordinate
* @param z the third location coordinate
*/
public Camera(float aspectRatio, float x, float y, float z) {
this.aspectRatio = aspectRatio;
this.x = x;
this.y = y;
this.z = z;
}
/**
* Creates a new camera with the given aspect ratio, location, and orientation.
* @param aspectRatio the aspect ratio (width/height) of the camera
* @param x the first location coordinate
* @param y the second location coordinate
* @param z the third location coordinate
* @param pitch the pitch (rotation on the x-axis)
* @param yaw the yaw (rotation on the y-axis)
* @param roll the roll (rotation on the z-axis)
*/
public Camera(float aspectRatio, double x, double y, double z, double pitch, double yaw, double roll) {
this.aspectRatio = aspectRatio;
this.x = (float) x;
this.y = (float) y;
this.z = (float) z;
this.pitch = (float) pitch;
this.yaw = (float) yaw;
this.roll = (float) roll;
}
/**
* Creates a new camera with the given aspect ratio, location, and orientation.
* @param aspectRatio the aspect ratio (width/height) of the camera
* @param x the first location coordinate
* @param y the second location coordinate
* @param z the third location coordinate
* @param pitch the pitch (rotation on the x-axis)
* @param yaw the yaw (rotation on the y-axis)
* @param roll the roll (rotation on the z-axis)
*/
public Camera(float aspectRatio, float x, float y, float z, float pitch, float yaw, float roll) {
this.aspectRatio = aspectRatio;
this.x = x;
this.y = y;
this.z = z;
this.pitch = pitch;
this.yaw = yaw;
this.roll = roll;
}
/**
* Processes mouse input and converts it in to camera movement using the mouseSpeed value.
* @param mouseSpeed the speed (sensitity) of the mouse
* @param maxLookUp the maximum angle at which you can look up
* @param maxLookDown the maximum angle at which you can look down
*/
public void processMouse(float mouseSpeed, float maxLookUp, float maxLookDown) {
if (!Mouse.isGrabbed()) return;
float mouseDX = Mouse.getDX() * mouseSpeed * 0.16f;
float mouseDY = Mouse.getDY() * mouseSpeed * 0.16f;
if (yaw + mouseDX >= 360) {
yaw = yaw + mouseDX - 360;
} else if (yaw + mouseDX < 0) {
yaw = 360 - yaw + mouseDX;
} else {
yaw += mouseDX;
}
if (pitch - mouseDY >= maxLookDown
&& pitch - mouseDY <= maxLookUp) {
pitch += -mouseDY;
} else if (pitch - mouseDY < maxLookDown) {
pitch = maxLookDown;
} else if (pitch - mouseDY > maxLookUp) {
pitch = maxLookUp;
}
}
/**
* @param delta the elapsed time since the last frame update in seconds
* @param speedX the speed of the movement on the x-axis (normal = 0.003)
* @param speedY the speed of the movement on the y-axis (normal = 0.003)
* @param speedZ the speed of the movement on the z-axis (normal = 0.003)
*/
public void processKeyboard(float delta, float speedX, float speedY, float speedZ) {
boolean keyUp = Keyboard.isKeyDown(Keyboard.KEY_UP) || Keyboard.isKeyDown(Keyboard.KEY_W);
boolean keyDown = Keyboard.isKeyDown(Keyboard.KEY_DOWN) || Keyboard.isKeyDown(Keyboard.KEY_S);
boolean keyLeft = Keyboard.isKeyDown(Keyboard.KEY_LEFT) || Keyboard.isKeyDown(Keyboard.KEY_A);
boolean keyRight = Keyboard.isKeyDown(Keyboard.KEY_RIGHT) || Keyboard.isKeyDown(Keyboard.KEY_D);
boolean flyUp = Keyboard.isKeyDown(Keyboard.KEY_SPACE);
boolean flyDown = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
if (keyUp && keyRight && !keyLeft && !keyDown) {
moveFromLook(speedX * delta, 0, -speedZ * delta);
}
if (keyUp && keyLeft && !keyRight && !keyDown) {
moveFromLook(-speedX * delta, 0, -speedZ * delta);
}
if (keyUp && !keyLeft && !keyRight && !keyDown) {
moveFromLook(0, 0, -speedZ * delta);
}
if (keyDown && keyLeft && !keyRight && !keyUp) {
moveFromLook(-speedX * delta, 0, speedZ * delta);
}
if (keyDown && keyRight && !keyLeft && !keyUp) {
moveFromLook(speedX * delta, 0, speedZ * delta);
}
if (keyDown && !keyUp && !keyLeft && !keyRight) {
moveFromLook(0, 0, speedZ * delta);
}
if (keyLeft && !keyRight && !keyUp && !keyDown) {
moveFromLook(-speedX * delta, 0, 0);
}
if (keyRight && !keyLeft && !keyUp && !keyDown) {
moveFromLook(speedX * delta, 0, 0);
}
if (flyUp && !flyDown) {
y += speedY * delta;
}
if (flyDown && !flyUp) {
y -= speedY * delta;
}
}
public void moveFromLook(float dx, float dy, float dz) {
float nX = this.x;
float nY = this.y;
float nZ = this.z;
float hypotenuseX = dx;
float adjacentX = hypotenuseX * (float) Math.cos(Math.toRadians(yaw - 90));
float oppositeX = (float) Math.sin(Math.toRadians(yaw - 90)) * hypotenuseX;
nZ += adjacentX;
nX -= oppositeX;
nY += dy;
float hypotenuseZ = dz;
float adjacentZ = hypotenuseZ * (float) Math.cos(Math.toRadians(yaw));
float oppositeZ = (float) Math.sin(Math.toRadians(yaw)) * hypotenuseZ;
nZ += adjacentZ;
nX -= oppositeZ;
this.x = nX;
this.y = nY;
this.z = nZ;
}
public void moveAlongAxis(float magnitude, float x, float y, float z) {
this.x += x * magnitude;
this.y += y * magnitude;
this.z += z * magnitude;
System.out.println(this.x + ", " + this.y + ", " + this.z);
}
public void setPosition(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public void applyOrthographicMatrix() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1, 1, -1, 1, 0, 10000);
glMatrixMode(GL_MODELVIEW);
}
public void applyPerspectiveMatrix() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLU.gluPerspective(fov, aspectRatio, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
}
public void applyModelviewMatrix(boolean resetMatrix) {
if (resetMatrix) glLoadIdentity();
glRotatef(pitch, 1, 0, 0);
glRotatef(yaw, 0, 1, 0);
glRotatef(roll, 0, 0, 1);
glTranslatef(-x, -y, -z);
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getZ() {
return z;
}
public void setZ(float z) {
this.z = z;
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public float getRoll() {
return roll;
}
public void setRoll(float roll) {
this.roll = roll;
}
public float getFov() {
return fov;
}
public void setFov(float fov) {
this.fov = fov;
}
public float getAspectRatio() {
return aspectRatio;
}
public void setAspectRatio(float aspectRatio) {
this.aspectRatio = aspectRatio;
}
public float getzNear() {
return zNear;
}
public void setzNear(float zNear) {
this.zNear = zNear;
}
public float getzFar() {
return zFar;
}
public void setzFar(float zFar) {
this.zFar = zFar;
}
@Override
public String toString() {
return "Camera [x=" + x + ", y=" + y + ", z=" + z + ", pitch=" + pitch
+ ", yaw=" + yaw + ", roll=" + roll + ", fov=" + fov
+ ", aspectRatio=" + aspectRatio + ", zNear=" + zNear
+ ", zFar=" + zFar + "]";
}
}
The camera needs to have placement (x,y,z),yaw,pitch,roll and fov.
This all takes practice to just get to what you like. Once in your completed program you can just move the camera how you'd like and print the settings and youll get better at knowing whats what.
Now let's go back to the main class and implement this. uncomment this void in the main method btw..
Code:
public static void setUpCamera() {
cam = new Camera((float) Display.getWidth()
/ (float) Display.getHeight(), -1.38f, 1.36f, 7.95f);
cam.setPitch(-1.12f);
cam.setYaw(0.16f);
cam.setFov(70);
cam.applyPerspectiveMatrix();
}
aspect ratio is the width/height (first argument) just so a cube will actually be a cube and stuff... the next 3 arguments are the xyz of the camera location
the cam.setSTUFFFFF sets the angel and direction and all that. It COULD be just added to the new Camera method right above it but I wanted to show you multiple ways it can be done.
Input method now:
Code:
public static void checkInput() {
cam.processMouse(1, 80, -80);
cam.processKeyboard(16, 0.003f, 0.003f, 0.003f);
if (Mouse.isButtonDown(0)) //left button
Mouse.setGrabbed(true); //no pointer
else if (Mouse.isButtonDown(1)) //right button
Mouse.setGrabbed(false); //show pointer
}
In the camera class youll see how our mouse and keyboard controls movement and stuff... so you can really adjust how you want stuff handled and what you want to do what. This is currently mouse controls looking and keyboard controls movement and height.
I AM NOT SPOON FEEDING JAVA. You need to add all your imports. My advice, get netbeans and alt+enter will show you what imports you should add to fix errors
however, the cam.whatever is fixed by making an instance of the camera class...
Code:
private static Camera cam;
(btw, you DONT have to do private static Camera cam = new Camera())...I see it all the time.
Heres where it will get harder.
You need to now render an object to your 'scene'.
This involves enabling GL, setting up the settings for rendering/lights/filling/shading/ect , and calling on the models/vertices to be loaded and drawn.
A nice basic setup for 3d
Code:
public static void renderObjects(int region) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
cam.applyModelviewMatrix(true);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //fill
glEnable(GL_DEPTH_TEST); //zbufferingbasicly
glEnable(GL_LIGHTING); // Turn on lighting.
glEnable(GL_LIGHT0); // Turn on light number 0.
glEnable(GL_DEPTH_TEST); // Turn on the depth test.
glShadeModel(GL_SMOOTH); // Use smooth shading.
//glCallList(co.objectDisplayList);
}
You'll just have to trust me or learn that yourself , lots of options
now, the glCallList(co.objectDisplayList); is where it calls for the info to render from. Theres many ways to store that information but I like Lists.
(REMEMBER: This is to get your feet wet, YOU should research this all and make your own how YOU like and learn!)
So lets construct our first list.
Lets not forget private static CameraObject co; in our main class.
Now as that says lets make a class CameraObject
first of all.. the 'list' is an int.
Code:
public static int objectDisplayList;
now the loading/setting up lists
Code:
public static void drawObjects() {
objectDisplayList = glGenLists(1);
glNewList(objectDisplayList, GL_COMPILE);
{
glBegin(GL_TRIANGLES);
// Top & Red
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 1.0f);
// Right & Green
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(1.0f, 1.0f);
// Left & Blue
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(1.0f, -1.0f);
glEnd();
}
glEndList();
}
Theres a simple triangle.
Now, obviously you want to actually be able to load an obj you make...
on of the easiest formats to learn is .obj,, exportable from blender and c4d and many other programs. Also has texture support, transparency support, ect. including nurbs
The format in a .obj file is like this:
Code:
# List of Vertices, with (x,y,z[,w]) coordinates, w is optional and defaults to 1.0.
v 0.123 0.234 0.345 1.0
v ...
...
# Texture coordinates, in (u[,v][,w]) coordinates, v and w are optional and default to 0.
vt 0.500 -1.352 [0.234]
vt ...
...
# Normals in (x,y,z) form
vn 0.707 0.000 0.707
vn ...
...
# Parameter space vertices in ( u [,v] [,w] ) form;
vp 0.310000 3.210000 2.100000
vp ...
...
# Face Definitions
f 1 2 3
f 3/1 4/2 5/3
f 6/4/1 3/5/3 7/6/5
You should lookup and understand what all those are... honestly. Itll help when we get to loading.
Under Construction As You Read
PM ME QUESTIONS , my animation section will be basic as its a lot but pm me for how i learned animations