How to edit vertex shader in JOGL - java

So im working on a Java/Jogl application that basically just translates the vertices of a triangle. So far I am able to get the triangle to move left to right, but I cant figure out how to edit the vertex shader so that when I click a button, the triangle begins to move up and down instead of left and right.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.*;
import javax.swing.*;
import static com.jogamp.opengl.GL4.*;
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;
import graphicslib3D.GLSLUtils;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.Vector;
public class a1 extends JFrame implements GLEventListener
{
private GLCanvas myCanvas;
private int rendering_program;
private int vao[] = new int[1];
private GLSLUtils util = new GLSLUtils();
private Button button1, button2;
private float x = 0.0f;
private float y = 0.0f;
private float inc = 0.01f;
private float incy = 0.01f;
private int click = 1;
public a1()
{ setTitle("Chapter2 - program2");
setSize(600, 400);
myCanvas = new GLCanvas();
myCanvas.addGLEventListener(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(myCanvas, BorderLayout.CENTER);
JPanel sidePanel = new JPanel();
sidePanel.setLayout(new BoxLayout(sidePanel, BoxLayout.Y_AXIS));
button1 = new Button("button");
button2 = new Button("button2");
sidePanel.add(button1);
sidePanel.add(button2);
getContentPane().add(sidePanel, BorderLayout.WEST);
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
click = 1;
}
});
setVisible(true);
FPSAnimator animator = new FPSAnimator(myCanvas, 30);
animator.start();
}
public void display(GLAutoDrawable drawable)
{ GL4 gl = (GL4) GLContext.getCurrentGL();
gl.glUseProgram(rendering_program);
gl.glPointSize(50.0f);
float bkg[] = {0.0f, 0.0f, 0.0f, 1.0f};
FloatBuffer bkgBuffer = Buffers.newDirectFloatBuffer(bkg);
gl.glClearBufferfv(GL_COLOR, 0, bkgBuffer);
x+=inc;
y+=incy;
if(x > 1.0f) inc = -0.01f;
if(x < -1.0f) inc = 0.01f;
if(y > 1.0f) incy = -0.01f;
if(y< -1.0f) incy = 0.01f;
int offset_loc = gl.glGetUniformLocation(rendering_program, "inc");
int offset_y = gl.glGetUniformLocation(rendering_program, "incy");
int flag = gl.glGetUniformLocation(rendering_program, "flag");
gl.glProgramUniform1f(rendering_program, offset_loc, x);
gl.glProgramUniform1f(rendering_program, offset_y, y);
gl.glProgramUniform1f(rendering_program, flag, click);
gl.glDrawArrays(GL_TRIANGLES,0,3);
}
public void init(GLAutoDrawable drawable)
{ GL4 gl = (GL4) GLContext.getCurrentGL();
rendering_program = createShaderProgram();
gl.glGenVertexArrays(vao.length, vao, 0);
gl.glBindVertexArray(vao[0]);
}
private int createShaderProgram()
{
GL4 gl = (GL4) GLContext.getCurrentGL();
int[] vertCompiled = new int[1];
int[] fragCompiled = new int[1];
int[] linked = new int[1];
String vshaderSource[] = readShaderSource("src/vert.shader");
String fshaderSource[] = readShaderSource("src/frag.shader");
int vShader = gl.glCreateShader(GL_VERTEX_SHADER);
gl.glShaderSource(vShader, vshaderSource.length, vshaderSource, null, 0);
gl.glCompileShader(vShader);
util.checkOpenGLError();
gl.glGetShaderiv(vShader, GL_COMPILE_STATUS, vertCompiled, 0);
if(vertCompiled[0] == 1)
{
System.out.println("vertex compilation success");
}else{
System.out.println("vertex compilation failed");
util.printShaderLog(vShader);
}
int fShader = gl.glCreateShader(GL_FRAGMENT_SHADER);
gl.glShaderSource(fShader, fshaderSource.length, fshaderSource, null, 0);
gl.glCompileShader(fShader);
util.checkOpenGLError();
gl.glGetShaderiv(fShader, GL_COMPILE_STATUS, fragCompiled, 0);
if(fragCompiled[0] == 1)
{
System.out.println("fragment compilation success");
}else{
System.out.println("fragment compilation failed");
util.printShaderLog(fShader);
}
int vfprogram = gl.glCreateProgram();
gl.glAttachShader(vfprogram, vShader);
gl.glAttachShader(vfprogram, fShader);
gl.glLinkProgram(vfprogram);
util.checkOpenGLError();
gl.glGetProgramiv(vfprogram, GL_LINK_STATUS, linked, 0);
if(linked[0] == 1)
{
System.out.println("linking succeeded");
}else{
System.out.println("linking failed");
util.printProgramLog(vfprogram);
}
//gl.glDeleteShader(vShader);
//gl.glDeleteShader(fShader);
return vfprogram;
}
public static void main(String[] args) { new a1(); }
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
public void dispose(GLAutoDrawable drawable) {}
private String[] readShaderSource(String filename)
{
Vector<String> lines = new Vector<String>();
Scanner sc;
try
{
sc = new Scanner(new File(filename));
}catch (IOException e)
{
System.err.println("IOException reading file: " + e);
return null;
}
while(sc.hasNext())
{
lines.addElement(sc.nextLine());
}
String[] program = new String[lines.size()];
for(int i=0; i<lines.size(); i++)
{
program[i] = (String) lines.elementAt(i)+ "\n";
}
return program;
}
}
#version 430
uniform float inc;
void main(void)
{
if(gl_VertexID == 0) gl_Position = vec4(0.25, -0.25+inc, 0.0, 1.0);
else if(gl_VertexID == 1) gl_Position = vec4(-0.25, -0.25+inc, 0.0, 1.0);
else gl_Position = vec4(0.25, 0.25+inc, 0.0, 1.0);
}
How do I edit my vertex shader so that when I click on a button the location of the vertices changes and the triangle begins to move up and down instead of left to right?

1) Don't do the update like that
2) Don't use an int array for the OpenGL resources (vao), prefer a direct integer buffer, you can use GLBuffers.newDirectIntBuffer(1); for the vao for example..
3) Don't use an FPSAnimator, use Animator instead
4) Don't inialize a new direct buffer everytime in the display() method (bkgBuffer), do it just once in the variable declaration or in the init(). You should also dispose any direct buffer, since it is not guaranteed that they are gonna be removed by the garbage collector. I have a small class for that here. A better one however is the implementation of Gouessej here.
4) Use a vertex buffer object to declare your vertices attributes (like position)
5) Declare a boolean flag variable update to trigger the update.
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update = 1;
}
});
public void display(GLAutoDrawable drawable) {
GL4 gl = (GL4) GLContext.getCurrentGL();
if(update) {
update = false;
...
}
}
now you have 2 possibilities:
you update the vertices directly
gl3.glBindBuffer(GL_ARRAY_BUFFER, bufferName.get(Buffer.VERTEX));
gl3.glBufferSubData(GL_ARRAY_BUFFER, 0, vertexBuffer.capacity(), vertexBuffer);
gl3.glBindBuffer(GL_ARRAY_BUFFER, 0);
and your vertex shader may be something like
#version 430
layout (location = POSITION) in vec2 position;
void main(void)
{
gl_Position = vec4(position, 0, 1);
}
you update only the matrix that is going to multiply the vertices
gl3.glBindBuffer(GL_UNIFORM_BUFFER, bufferName.get(Buffer.TRANSFORM));
gl3.glBufferSubData(GL_UNIFORM_BUFFER, 0, Mat4.SIZE, matBuffer);
gl3.glBindBuffer(GL_UNIFORM_BUFFER, 0);
and in this case the vertex may be
#version 430
layout (location = POSITION) in vec2 position;
layout (binding = TRANSFORM) uniform Transform
{
mat4 mat;
};
void main(void)
{
gl_Position = mat * vec4(position, 0, 1);
}
In case you need further assistance, don't hesitate to ask for help.
For inspiration, you can refer to this hello triangle of mine

Related

How to prevent GLFW window from showing up right in creating?

I am creating a 3D game using LWJGL3, and I want the window loaded in the background and hidden, wait for my game setup and only than showing up.
My problem is even if I call GLFW.glfwHideWindow(window) immidetly after GLFW.glfwCreateWindow(width, height, title, isFullscreen ? GLFW.glfwGetPrimaryMonitor() : 0, 0);
the window flickering, than load the game and than showing up (when I want).
How to prevent the window from flickering? Maybe I just change one of the arguments in GLFW.glfwCreateWindow?
My code:
Window class:
package renderEngine.io;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowSizeCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjglx.util.vector.Vector3f;
public class Window {
private int width, height;
private String title;
private long window;
private int frames;
private static long time;
private Input input;
private Vector3f background = new Vector3f(0, 0, 0);
private GLFWWindowSizeCallback sizeCallback;
private boolean isResized;
private boolean isFullscreen;
private int[] windowPosX = new int[1], windowPosY = new int[1];
public Window(int width, int height, String title) {
this.width = width;
this.height = height;
this.title = title;
}
public static void initLWJGL() {
if (!GLFW.glfwInit()) {
System.err.println("ERROR: GLFW wasn't initializied");
return;
}
}
public void create() {
initLWJGL();
input = new Input();
window = GLFW.glfwCreateWindow(width, height, title, isFullscreen ? GLFW.glfwGetPrimaryMonitor() : 0, 0);
if (window == 0) {
System.err.println("ERROR: Window wasn't created");
return;
}
GLFW.glfwHideWindow(window);
GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
windowPosX[0] = (videoMode.width() - width) / 2;
windowPosY[0] = (videoMode.height() - height) / 2;
GLFW.glfwSetWindowPos(window, windowPosX[0], windowPosY[0]);
GLFW.glfwMakeContextCurrent(window);
GL.createCapabilities();
GL11.glEnable(GL11.GL_DEPTH_TEST);
createCallbacks();
GLFW.glfwSwapInterval(1);
time = System.currentTimeMillis();
}
public void setVisible(boolean visible) {
if (visible) {
GLFW.glfwShowWindow(window);
}else {
GLFW.glfwHideWindow(window);
}
}
private void createCallbacks() {
sizeCallback = new GLFWWindowSizeCallback() {
public void invoke(long window, int w, int h) {
width = w;
height = h;
isResized = true;
}
};
GLFW.glfwSetKeyCallback(window, input.getKeyboardCallback());
GLFW.glfwSetCursorPosCallback(window, input.getMouseMoveCallback());
GLFW.glfwSetMouseButtonCallback(window, input.getMouseButtonsCallback());
GLFW.glfwSetScrollCallback(window, input.getMouseScrollCallback());
GLFW.glfwSetWindowSizeCallback(window, sizeCallback);
}
public void update() {
if (isResized) {
GL11.glViewport(0, 0, width, height);
isResized = false;
}
GL11.glClearColor(background.getX(), background.getY(), background.getZ(), 1.0f);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GLFW.glfwPollEvents();
frames++;
if (System.currentTimeMillis() > time + 1000) {
GLFW.glfwSetWindowTitle(window, title + " | FPS: " + frames);
time = System.currentTimeMillis();
frames = 0;
}
}
public void swapBuffers() {
GLFW.glfwSwapBuffers(window);
}
public boolean shouldClose() {
return GLFW.glfwWindowShouldClose(window);
}
public void destroy() {
input.destroy();
sizeCallback.free();
GLFW.glfwWindowShouldClose(window);
GLFW.glfwDestroyWindow(window);
GLFW.glfwTerminate();
}
public void setBackgroundColor(float r, float g, float b) {
background.set(r, g, b);
}
public boolean isFullscreen() {
return isFullscreen;
}
public void setFullscreen(boolean isFullscreen) {
this.isFullscreen = isFullscreen;
isResized = true;
if (isFullscreen) {
GLFW.glfwGetWindowPos(window, windowPosX, windowPosY);
GLFW.glfwSetWindowMonitor(window, GLFW.glfwGetPrimaryMonitor(), 0, 0, width, height, 0);
} else {
GLFW.glfwSetWindowMonitor(window, 0, windowPosX[0], windowPosY[0], width, height, 0);
}
}
public void mouseState(boolean lock) {
GLFW.glfwSetInputMode(window, GLFW.GLFW_CURSOR, lock ? GLFW.GLFW_CURSOR_DISABLED : GLFW.GLFW_CURSOR_NORMAL);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public String getTitle() {
return title;
}
public long getWindow() {
return window;
}
}
Main class:
package main;
import java.util.Calendar;
import org.lwjglx.util.vector.Vector3f;
import entities.Camera;
import entities.Light;
import gameplay.Car;
import models.RawModel;
import models.TexturedModel;
import renderEngine.Loader;
import renderEngine.MasterRenderer;
import renderEngine.OBJLoader;
import renderEngine.io.Window;
import shaders.StaticShader;
import terrains.Terrain;
import textures.ModelTexture;
import textures.TerrainTexture;
import textures.TerrainTexturesPack;
public class Main{
public static Window window = new Window(1000, 600, "Driving Game");
public static void main(String[] args) {
Light light = new Light(new Vector3f(3, -2, -20), new Vector3f(1, 1, 1));
Loader loader = new Loader();
Camera camera = new Camera(null);
camera.setAngleAround(180);
camera.setPitch(20);
camera.setDistance(10);
Car myCar = new Car(null, new Vector3f(0,0,0),0,0,0,1f);
myCar.setBonus_height(0.5f);
myCar.setBonus_rotation(new Vector3f(0, -90, 0));
camera.setPlayer(myCar);
window.create();
TexturedModel carTexturedModel = loadTexturedModel(loader, "/models/cars/Car1.obj", "/models/cars/texture.png");
myCar.setModel(carTexturedModel);
Terrain terrain = new Terrain(0, -1, loader,
new TerrainTexturesPack(new TerrainTexture(loader.loadTexture("/res/1/grassy")),
new TerrainTexture(loader.loadTexture("/res/1/dirt")),
new TerrainTexture(loader.loadTexture("/res/1/pinkFlowers")),
new TerrainTexture(loader.loadTexture("/res/1/path"))),
new TerrainTexture(loader.loadTexture("/res/1/blendMap")), "/res/heightmap");
StaticShader shader = new StaticShader();
MasterRenderer masterRenderer = new MasterRenderer(loader);
Calendar c = Calendar.getInstance();
int frames = 0;
window.setVisible(true);
while(!window.shouldClose()){
window.update();
frames++;
camera.move();
myCar.move(terrain);
shader.start();
masterRenderer.proccessTerrain(terrain);
masterRenderer.proccessEntity(myCar);
masterRenderer.render(light, camera);
shader.stop();
window.swapBuffers();
}
System.out.println("The game run at "
+ ((double)frames/(Calendar.getInstance().getTimeInMillis() - c.getTimeInMillis())*1000) + " FPS");
shader.cleanUp();
loader.cleanUp();
masterRenderer.cleanUp();
window.destroy();
}
public static TexturedModel loadTexturedModel(Loader loader, String objFile, String textureFile) {
RawModel rawModel = OBJLoader.loadObjModel(objFile, loader);
ModelTexture modelTexture = new ModelTexture(loader.loadTexture(textureFile));
TexturedModel texturedModel = new TexturedModel(rawModel, modelTexture);
return texturedModel;
}
}
Notice:
I have to create the window before loading the models into the VAO, so I can't move the window.create() method lower than where it is.
Thank for any answer!
Create a hidden window. See GLFW - Window visibility. Set the GLFW_VISIBLE property before creating the window
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
window = GLFW.glfwCreateWindow(width, height, title,
isFullscreen ? GLFW.glfwGetPrimaryMonitor() : 0, 0);

rendering 3d lwjgl flat terrain doesn't work

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.GL15.*;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;
public class man {
private static final int VERTEX_COUNT = 128;
private static final float SIZE = 1;
public static int count;
public static int vid;
public static int iid;
public static int program;
public static float fov = 70f;
public static float near = 0.1f;
public static float far = 1000f;
public static Matrix4f modelMatrix = new Matrix4f() ;
public static Matrix4f view = new Matrix4f();
public static Matrix4f projectionmatrix = new Matrix4f();
public static GLFWCursorPosCallback mouseCallback;
public static boolean t = true;
public static void main(String[] argv) throws IOException {
glfwInit();
int prog = 0 , id=0;
long window = glfwCreateWindow(1920,
1080, "HI", 0 , 0);
glfwShowWindow(window);
glfwMakeContextCurrent(window);
GL.createCapabilities();
// glfwSetCursorPosCallback(window, mouseCallback = new mouse());
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
new man().createShader();
new man().bind();
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_COLOR_BUFFER_BIT);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glLoadIdentity();
glEnable(GL_PROJECTION_MATRIX);
glEnable(GL_PROJECTION);
new man().createprojection();
new man().createview();
glUseProgram(program);
new man().loadtoshader();
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vid);
glVertexPointer(3, GL_FLOAT , 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iid);
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisable(GL_VERTEX_ARRAY);
}
}
public void createShader() throws IOException
{
StringBuilder vertex = new StringBuilder();
StringBuilder frag = new StringBuilder();
BufferedReader vert ,fragment;
vert = new BufferedReader(new FileReader("D:/work/opengl2/src/opengl2/vertexShader.txt"));
fragment = new BufferedReader(new FileReader("D:/work/opengl2/src/opengl2/frageShader.txt"));
//vertex Shader
String line, line2;
while ( (line = vert.readLine()) != null)
{
vertex.append(line).append('\n');
}
while ( (line2 = fragment.readLine()) != null)
{
frag.append(line2).append('\n');
}
//create and comile shaders
int vertexShader = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(vertexShader, vertex);
GL20.glCompileShader(vertexShader);
int fragmentShader = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(fragmentShader, frag);
GL20.glCompileShader(fragmentShader);
program = GL20.glCreateProgram();
GL20.glAttachShader(program, vertexShader);
GL20.glAttachShader(program, fragmentShader);
GL20.glBindAttribLocation(program, 0 , "postion");
GL20.glLinkProgram(program);
if (GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) != 1)
{
System.err.println(GL20.glGetProgramInfoLog(program));
System.exit(1);
}
GL20.glValidateProgram(program);
}
public void bind()
{
float[] vertices = new float[128 * 128 * 3];
int[] indices = new int[6*(VERTEX_COUNT-1)*(VERTEX_COUNT-1)];
//generaete terrain
int vertexPointer = 0;
for(int i=0;i<VERTEX_COUNT;i++){
for(int j=0;j<VERTEX_COUNT;j++){
vertices[vertexPointer*3] = (float)j/((float)VERTEX_COUNT - 1) * SIZE;
vertices[vertexPointer*3+1] = 0;
vertices[vertexPointer*3+2] = (float)i/((float)VERTEX_COUNT - 1) * SIZE;
vertexPointer++;
}
}
int pointer = 0;
for(int gz=0;gz<VERTEX_COUNT-1;gz++){
for(int gx=0;gx<VERTEX_COUNT-1;gx++){
int topLeft = (gz*VERTEX_COUNT)+gx;
int topRight = topLeft + 1;
int bottomLeft = ((gz+1)*VERTEX_COUNT)+gx;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
//end generate terrain
FloatBuffer buffer = BufferUtils.createFloatBuffer(vertices.length);
buffer.put(vertices);
buffer.flip();
count = indices.length ;
vid = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vid);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
iid = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, iid);
IntBuffer indbuf = BufferUtils.createIntBuffer(indices.length);
indbuf.put(indices);
indbuf.flip();
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indbuf, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
public void createprojection()
{
float aspect = (float) 1920/1080;
float y_scale = (float) (1/Math.tan(Math.toRadians(fov/2f)));
float x_scale = y_scale / aspect;
float frustum_length = far - near;
projectionmatrix.m00 = x_scale;
projectionmatrix.m11 = y_scale;
projectionmatrix.m22 = -((far + near) / frustum_length);
projectionmatrix.m23 = -1;
projectionmatrix.m32 = -((2 * near * far) / frustum_length);
projectionmatrix.m33 = 0;
}
public void createview()
{
Vector3f modelPos = null;
Vector3f modelAngle = null;
Vector3f modelScale = null;
Vector3f camera = new Vector3f(0,1f,-2f);
modelPos = new Vector3f(0,0,0);
modelAngle = new Vector3f(0,0,0);
modelScale = new Vector3f(1, 1, 1);
modelMatrix.setIdentity();
Matrix4f.scale(modelScale, modelMatrix, modelMatrix);
Matrix4f.translate(modelPos, modelMatrix, modelMatrix);
Matrix4f.rotate((float) Math.toRadians(modelAngle.z), new Vector3f(0, 0, 1),
modelMatrix, modelMatrix);
Matrix4f.rotate((float) Math.toRadians(modelAngle.y), new Vector3f(0, 1, 0),
modelMatrix, modelMatrix);
Matrix4f.rotate((float) Math.toRadians(modelAngle.x), new Vector3f(1, 0, 0),
modelMatrix, modelMatrix);
Matrix4f.translate(camera, view, view);
}
public void loadtoshader()
{
int loc1 = glGetUniformLocation(program, "projection");
FloatBuffer matrixx = BufferUtils.createFloatBuffer(16);
projectionmatrix.store(matrixx);
matrixx.flip();
glUniformMatrix4fv(loc1, false,matrixx);
int loc2 = glGetUniformLocation(program, "view");
FloatBuffer matrixx2 = BufferUtils.createFloatBuffer(16);
view.store(matrixx2);
matrixx2.flip();
glUniformMatrix4fv(loc2, false,matrixx2);
int loc3 = glGetUniformLocation(program, "model");
FloatBuffer matrixx3 = BufferUtils.createFloatBuffer(16);
modelMatrix.store(matrixx3);
matrixx3.flip();
glUniformMatrix4fv(loc3, false,matrixx3);
}
}
class mouse extends GLFWCursorPosCallback
{
#Override
public void invoke(long arg0, double x, double y) {
// TODO Auto-generated method stub
System.out.println(x+ " "+ y );
}
}
Hi guys !
I want to generate a simple flat terrain in lwjgl but this code doesn't produce me anything. I am very new in this chapter so if you can explain why this code does nothing please do it! I am generating the terrain with the code shared by ThinMatrix , then i upload the vertices to the buffers and then render them in the main game loop . When i compile it , it shows me a black screen. I have searched for a lot of tutorials but I didn't find anything that can help me .
I see several issues with your code:
Issue Number 1: You are generating a new instance of the man class on every iteration of the loop. Create a single instance of the object outside of the loop and use it in the loop.
Issue Number 2: You are using the old, static pipeline OpenGL. Following ThinMatrix's tutorials, you should be using OpenGL 3 or newer. Everything should be going through shaders, rather than using the GL_PROJECTION_MATRIX and the likes of that.
Your program should look something like the following:
man instance = new man()
while (!glfwWindowShouldClose(window))
{
RENDER THE TERRAIN HERE
glfwPoll
glfwSwapBuffers
glClear
}
If you want some example code, I have a repository here that should help, as I used to watch ThinMatrix tutorials.
EDIT: SOLUTION
The vertex shader code should read:
gl_Position = projection * view * model * position;
My shaders: fragment
#version 330
in vec4 pos;
void main()
{
gl_FragColor = vec4 (pos);
}
vertex:
#version 330
attribute vec3 postion;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
out vec4 pos;
void main()
{
gl_Position = vec4(postion, 1.0) * projection * view * model;
pos = vec4(postion, 1.0) ;
}
I have updated my java code so it renders different colors base on the vertex position.

OpenGL ES 1.0 strange texture behavior

I am learning how to work with OpenGL ES 1.0 (I was told that it is better to start with "1.0"/"1.1").
I am trying to create transperent 3D globe on android device using transparent texture and vertex indexing but having problem which I don't understand and can't find any solution on internet.
The problem is that depending on angle of camera rotation my globe stops rendering "back side" of sphere or sphere back sides texture. On image below you can see what happens if I rotate camera in same direction.
Link to image: CLICK HERE (I have no reputation to add image directly, sorry)
So can you help me? What is my mistake, why "back" is disappearing?
"Sphere" class:
package com.example.OpenGL_Testing.openGl.geometry;
import com.example.OpenGL_Testing.openGl.Texture;
import javax.microedition.khronos.opengles.GL10;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
public class Sphere {
float step = 5f; // density of sphere; step in degrees
float[] verArray;
float[] texIndex;
short[] verIndex;
private FloatBuffer vBuff;
private FloatBuffer tBuff;
private ShortBuffer iBuff;
public final float mCenterX, mCenterY, mCenterZ;
public final float mRadius;
private final GL10 mGL;
private int stepsPai, stepsTheta;
private Texture texture;
public Sphere(float mRadius, float mCenterX, float mCenterY, float mCenterZ, GL10 mGL) {
this.mGL = mGL;
// sphere parameters
this.mRadius = mRadius;
this.mCenterX = mCenterX;
this.mCenterY = mCenterY;
this.mCenterZ = mCenterZ;
stepsPai = (int) (180f / step) ; //sphere vertical 'lines'
stepsTheta = (int) (360f / step) + 1; //sphere horizontal 'lines'
// create sphere 'dots'
createVerticesBuff();
createIndexingBuffer();
}
private float[] createVerticesArray() {
float[] vertices = new float[stepsPai * stepsTheta * 3];
int n = 0;
float cRadius, cHeight, co, si;
for (float pai = 180f - step; pai > 0f; pai -= step) {
cRadius = (float) Math.sin((pai + step) * Math.PI / 180f);
cHeight = (float) Math.cos((pai + step) * Math.PI / 180f);
for (float theta = 0.0f; theta <= 360f; theta += step) {
co = (float) Math.cos(theta * Math.PI / 180f);
si = -(float) Math.sin(theta * Math.PI / 180f);
vertices[n * 3] = mRadius * (cRadius * co) + mCenterX;
vertices[n * 3 + 1] = mRadius * (cHeight) + mCenterY;
vertices[n * 3 + 2] = mRadius * (cRadius * si) + mCenterZ;
n ++;
}
}
return vertices;
}
public FloatBuffer createVerticesBuff() {
// create array
verArray = createVerticesArray();
// create buffer
if (vBuff == null) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(verArray.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vBuff = byteBuffer.asFloatBuffer();
vBuff.put(verArray);
vBuff.flip();
}
return vBuff;
}
public short[] createIndexingArray() {
short[] indexies = new short[verArray.length*2];
int n=0;
for (int i = 0; i < stepsPai-1; i++) {
for (int j = 0; j < stepsTheta; j++) {
indexies[n] = (short) (i*stepsTheta+j);
indexies[n+1] = (short) ((i+1)*stepsTheta+j);
indexies[n+2] = (short) ((i+1)*stepsTheta+j+1);
indexies[n+3] = indexies[n+2];
indexies[n+4] = (short) (i*stepsTheta+j+1);
indexies[n+5] = indexies[n];
n+=6;
}
}
return indexies;
}
public void createIndexingBuffer(){
// create array
verIndex = createIndexingArray();
//create buffer
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(verIndex.length * 2);
byteBuffer.order(ByteOrder.nativeOrder());
iBuff = byteBuffer.asShortBuffer();
iBuff.put(verIndex);
iBuff.flip();
}
public void draw() {
// setup vertices buffer
mGL.glEnableClientState(GL10.GL_VERTEX_ARRAY);
vBuff.position(0);
mGL.glVertexPointer(3, GL10.GL_FLOAT, 0, vBuff);
// setup texture
if (texture != null) {
mGL.glEnable(GL10.GL_TEXTURE_2D);
mGL.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
tBuff.position(0);
mGL.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tBuff);
}
// display poligons on screen
mGL.glDrawElements(GL10.GL_TRIANGLES, verIndex.length, GL10.GL_UNSIGNED_SHORT, iBuff);
// reset settings
if (texture != null) {
mGL.glDisable(GL10.GL_TEXTURE_2D);
mGL.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
} else {
mGL.glColor4f(1, 1, 1, 1);
}
mGL.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
public void setTexture(Texture texture) {
if (this.texture != null) {
this.texture.dispose();
}
this.texture = texture;
// create array
texIndex = new float[stepsPai * stepsTheta * 2 * 2];
int n = 0;
for (float pai = 0f; pai < 180f; pai += step) {
for (float theta = 0.0f; theta <= 360f; theta += step) {
texIndex[n] = theta / 360f;
texIndex[n + 1] = pai / 180f;
n += 2;
}
}
// create buffer
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texIndex.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
tBuff = byteBuffer.asFloatBuffer();
tBuff.put(texIndex);
tBuff.flip();
// bind texture
texture.bind();
}
}
"World" class
package com.example.OpenGL_Testing.openGl;
import android.opengl.GLSurfaceView;
import android.support.v4.view.GestureDetectorCompat;
import android.view.MotionEvent;
import android.view.View;
import com.example.OpenGL_Testing.MyApp;
import com.example.OpenGL_Testing.MyGestureListener;
import com.example.OpenGL_Testing.Screen;
import com.example.OpenGL_Testing.openGl.geometry.Sphere;
import com.example.OpenGL_Testing.openGl.geometry.Sphere2;
import javax.microedition.khronos.opengles.GL10;
/**
* Created by Aleksandr.Tsatski on 11.11.2014.
*/
public class WorldMap implements Screen {
GLSurfaceView glView;
static final int VERTEX_SIZE = (3) * 4;
Sphere sphere;
GL10 gl;
private GestureDetectorCompat mDetector;
final Object stateChanged = new Object();
public WorldMap(final GLSurfaceView glView, final GL10 gl) {
this.glView = glView;
this.gl = gl;
sphere = new Sphere(400, 0, 0, 0, gl);
}
private void setupGestureListener(final GLSurfaceView glView, final GL10 gl) {
final MyGestureListener listener = new MyGestureListener();
listener.setGestureFeedBackListener(new MyGestureListener.Feedback() {
#Override
public void onFeedback(final int event, final float parameter1, final float parameter2) {
glView.queueEvent(new Runnable() {
#Override
public void run() {
switch (event) {
case MyGestureListener.SCROLL:
gl.glRotatef(parameter1/10, 0f, 1f, 0f);
gl.glRotatef(-parameter2/10, 0f, 0f, 1f);
break;
default:
break;
}
}
});
}
});
glView.post(new Runnable() {
#Override
public void run() {
mDetector = new GestureDetectorCompat(MyApp.getAppContext(), listener);
}
});
glView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return mDetector.onTouchEvent(motionEvent);
}
});
}
#Override
public void present(float deltaTime) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
sphere.draw();
}
#Override
public void resume() {
gl.glClearColor(1, 0, 0, 1);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
setupGestureListener(glView, gl);
sphere.setTexture(new Texture(gl, "world_map.png"));
}
#Override
public void pause() {
}
#Override
public void dispose() {
}
}

Java: Smooth Color Transition

I am trying to make a health bar, and as what might be original, it will start out green, and after losing health you find that it will turn yellow, then orange, then red.. or something relative to that.
I tried using the method provided in this link: https://stackoverflow.com/questions/19841477/java-smooth-color-transition
The result from that link was this code, just a test from value 100 to 0, but it ended in an IllegalArgumentException at normally Red and Green, and my guess for reason is it being over the value of 255.
Color to = Color.red;
Color base = Color.green;
int red = (int)Math.abs((100 * to.getRed()) + ((1 - 100) * base.getRed()));
int green = (int)Math.abs((100 * to.getGreen()) + ((1 - 100) * base.getGreen()));
int blue = (int)Math.abs((100 * to.getBlue()) + ((1 - 100) * base.getBlue()));
setForeground(new Color(red, green, blue));
It didn't really work, and I have absolutely no idea how I can get it to transition the way I wish it to.
So within my HealthBar class, I have an update() method
public void update() {
if (getValue() < 10) setForeground(Color.red);
else if (getValue() < 25) setForeground(Color.orange);
else if (getValue() < 60) setForeground(Color.yellow);
else setForeground(Color.green);
}
This code does a basic transition at certain points.
I need to create fields to use certain colors at certain values of the health bar, so now I have this..
if (getValue() < 10) {
Color to = Color.black;
// Color current = getForeground() ?
Color from = Color.red;
// ?
}
I am just going to use an example for the last bit.
So I know that I am going to have a color that I am going to, and a color that is a base. I am not so sure if I need a color for current. The problem that I see now is for the steps of the transition because each transition has a different amount of steps.
Summary and Question
I don't know how to achieve what I am attempting, all I know for sure is I need a to and base color, and I provided a link to an answer that I saw, but I couldn't really figure it out. With the information given, how could I get it to transition colors
?
I spent a lot of time trying to find/create a blending algorithm that worked for me, this is basically what I was able to hobble together.
I've used this approach to generate a gradient transition mixing multiple colors, as demonstrated here
Basically, this approach allows you to set up a series of colors and percentage marks so that you gain much greater control over which points the colors transition between.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.text.NumberFormat;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ColorFading {
public static void main(String[] args) {
new ColorFading();
}
public ColorFading() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new FadePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class FadePane extends JPanel {
private final float[] fractions = new float[]{0f, 0.5f, 1f};
private final Color[] colors = new Color[]{Color.RED, Color.YELLOW, Color.GREEN};
private float progress = 1f;
private JSlider slider;
public FadePane() {
slider = new JSlider(0, 100);
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
progress = ((float)slider.getValue() / 100f);
repaint();
}
});
slider.setValue(100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
Color startColor = blendColors(fractions, colors, progress);
g2d.setColor(startColor);
g2d.fillRect(0, 0, width, height);
g2d.dispose();
}
}
public static Color blendColors(float[] fractions, Color[] colors, float progress) {
Color color = null;
if (fractions != null) {
if (colors != null) {
if (fractions.length == colors.length) {
int[] indicies = getFractionIndicies(fractions, progress);
float[] range = new float[]{fractions[indicies[0]], fractions[indicies[1]]};
Color[] colorRange = new Color[]{colors[indicies[0]], colors[indicies[1]]};
float max = range[1] - range[0];
float value = progress - range[0];
float weight = value / max;
color = blend(colorRange[0], colorRange[1], 1f - weight);
} else {
throw new IllegalArgumentException("Fractions and colours must have equal number of elements");
}
} else {
throw new IllegalArgumentException("Colours can't be null");
}
} else {
throw new IllegalArgumentException("Fractions can't be null");
}
return color;
}
public static int[] getFractionIndicies(float[] fractions, float progress) {
int[] range = new int[2];
int startPoint = 0;
while (startPoint < fractions.length && fractions[startPoint] <= progress) {
startPoint++;
}
if (startPoint >= fractions.length) {
startPoint = fractions.length - 1;
}
range[0] = startPoint - 1;
range[1] = startPoint;
return range;
}
public static Color blend(Color color1, Color color2, double ratio) {
float r = (float) ratio;
float ir = (float) 1.0 - r;
float rgb1[] = new float[3];
float rgb2[] = new float[3];
color1.getColorComponents(rgb1);
color2.getColorComponents(rgb2);
float red = rgb1[0] * r + rgb2[0] * ir;
float green = rgb1[1] * r + rgb2[1] * ir;
float blue = rgb1[2] * r + rgb2[2] * ir;
if (red < 0) {
red = 0;
} else if (red > 255) {
red = 255;
}
if (green < 0) {
green = 0;
} else if (green > 255) {
green = 255;
}
if (blue < 0) {
blue = 0;
} else if (blue > 255) {
blue = 255;
}
Color color = null;
try {
color = new Color(red, green, blue);
} catch (IllegalArgumentException exp) {
NumberFormat nf = NumberFormat.getNumberInstance();
System.out.println(nf.format(red) + "; " + nf.format(green) + "; " + nf.format(blue));
exp.printStackTrace();
}
return color;
}
}
OK, before Mad posted his answer (and 1+ to it), I was working on this too, so I might as well post what I came up with....
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class ColorTransition extends JPanel {
private static final int TRANSITION_DELAY = 30;
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private RgbSliderPanel rgbSliderPanel1 = new RgbSliderPanel("Color 1");
private RgbSliderPanel rgbSliderPanel2 = new RgbSliderPanel("Color 2");
private Color background1;
private Color background2;
private JButton button = new JButton(new ButtonAction("Push Me"));
public ColorTransition() {
setBackground(Color.black);
add(rgbSliderPanel1.getMainPanel());
add(rgbSliderPanel2.getMainPanel());
add(button);
rgbSliderPanel1.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (RgbSliderPanel.COLOR.equals(evt.getPropertyName())) {
setBackground(rgbSliderPanel1.calculateColor());
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
button.setEnabled(enabled);
rgbSliderPanel1.setEnabled(enabled);
rgbSliderPanel2.setEnabled(enabled);
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
ColorTransition.this.setEnabled(false);
background1 = rgbSliderPanel1.calculateColor();
background2 = rgbSliderPanel2.calculateColor();
setBackground(background1);
Timer timer = new Timer(TRANSITION_DELAY, new TransitionListener());
timer.start();
}
private class TransitionListener implements ActionListener {
private int index = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (index > 100) {
((Timer) e.getSource()).stop();
ColorTransition.this.setEnabled(true);
} else {
int r = (int) (background2.getRed() * index / 100.0 + background1
.getRed() * (100 - index) / 100.0);
int g = (int) (background2.getGreen() * index / 100.0 + background1
.getGreen() * (100 - index) / 100.0);
int b = (int) (background2.getBlue() * index / 100.0 + background1
.getBlue() * (100 - index) / 100.0);
setBackground(new Color(r, g, b));
}
index++;
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("ColorTransition");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ColorTransition());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum Rgb {
RED("Red"), GREEN("Green"), BLUE("Blue");
private String name;
private Rgb(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class RgbSliderPanel {
public static final String COLOR = "color";
private JPanel mainPanel = new JPanel();
private SwingPropertyChangeSupport propertyChangeSupport = new SwingPropertyChangeSupport(
this);
private Map<Rgb, JSlider> colorSliderMap = new EnumMap<>(Rgb.class);
private String name;
protected Color color;
public RgbSliderPanel(String name) {
this.name = name;
mainPanel.setBorder(BorderFactory.createTitledBorder(name));
//mainPanel.setOpaque(false);
mainPanel.setLayout(new GridLayout(0, 1));
for (Rgb rgb : Rgb.values()) {
JSlider colorSlider = new JSlider(0, 255, 0);
colorSliderMap.put(rgb, colorSlider);
mainPanel.add(colorSlider);
colorSlider.setBorder(BorderFactory.createTitledBorder(rgb.getName()));
colorSlider.setPaintTicks(true);
colorSlider.setPaintTrack(true);
colorSlider.setMajorTickSpacing(50);
colorSlider.setMinorTickSpacing(10);
colorSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
Color oldValue = color;
Color newValue = calculateColor();
color = newValue;
propertyChangeSupport.firePropertyChange(COLOR, oldValue,
newValue);
}
});
}
}
public JComponent getMainPanel() {
return mainPanel;
}
public void setEnabled(boolean enabled) {
for (JSlider slider : colorSliderMap.values()) {
slider.setEnabled(enabled);
}
}
public Color calculateColor() {
int r = colorSliderMap.get(Rgb.RED).getValue();
int g = colorSliderMap.get(Rgb.GREEN).getValue();
int b = colorSliderMap.get(Rgb.BLUE).getValue();
return new Color(r, g, b);
}
public String getName() {
return name;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
}

How to make an image move while listening to a keypress in Java.

I'm starting to learn java programming and I think it's cool to learn java through game development. I know how to draw image and listen to a keypress then move that image. But is it possible to make the image move back and forth to the window while the window is listening to a keypress? Like for example, while the image or object(like spaceship) is moving left to right in the window, then if I press space key, a laser will fire at the bottom of the screen( cool huh :D ). But basically I just want to know how to make the image move left to right while the window is listening to a keypress.
I'm thinking that I will add a key listener to my window then fire an infinite loop to move the image. Or do I need to learn about threading so that another thread will move the object?
Please advise.
Many thanks.
Yep, a Swing Timer and Key Bindings would work well. Here's another example (mine) :)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class AnimationWithKeyBinding {
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel(); // the drawing JPanel
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK), name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}
Here is another example that uses this sprite sheet:
obtained from this site.
Again it's an example of drawing within a JPanel's paintComponent method and using Key Bindings to tell which direction to move.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mcve3 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 640;
private static final int TIMER_DELAY = 50;
private int spriteX = 400;
private int spriteY = 320;
private SpriteDirection spriteDirection = SpriteDirection.RIGHT;
private MySprite sprite = null;
private Timer timer = null;
public Mcve3() {
try {
sprite = new MySprite(spriteDirection, spriteX, spriteY);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
setBackground(Color.WHITE);
setKeyBindings(SpriteDirection.LEFT, KeyEvent.VK_LEFT);
setKeyBindings(SpriteDirection.RIGHT, KeyEvent.VK_RIGHT);
setKeyBindings(SpriteDirection.FORWARD, KeyEvent.VK_DOWN);
setKeyBindings(SpriteDirection.AWAY, KeyEvent.VK_UP);
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
private void setKeyBindings(SpriteDirection dir, int keyCode) {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
KeyStroke keyPressed = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke keyReleased = KeyStroke.getKeyStroke(keyCode, 0, true);
inputMap.put(keyPressed, keyPressed.toString());
inputMap.put(keyReleased, keyReleased.toString());
actionMap.put(keyPressed.toString(), new MoveAction(dir, false));
actionMap.put(keyReleased.toString(), new MoveAction(dir, true));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
sprite.draw(g);
}
private class MoveAction extends AbstractAction {
private SpriteDirection dir;
private boolean released;
public MoveAction(SpriteDirection dir, boolean released) {
this.dir = dir;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
sprite.setMoving(false);
} else {
sprite.setMoving(true);
sprite.setDirection(dir);
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (sprite.isMoving()) {
sprite.tick();
}
repaint();
}
}
private static void createAndShowGui() {
Mcve3 mainPanel = new Mcve3();
JFrame frame = new JFrame("MCVE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MySprite {
private static final String SPRITE_SHEET_PATH = "http://"
+ "orig12.deviantart.net/7db3/f/2010/338/3/3/"
+ "animated_sprite_sheet_32x32_by_digibody-d3479l2.gif";
private static final int MAX_MOVING_INDEX = 4;
private static final int DELTA = 4;
private SpriteDirection direction;
private Map<SpriteDirection, Image> standingImgMap = new EnumMap<>(SpriteDirection.class);
private Map<SpriteDirection, List<Image>> movingImgMap = new EnumMap<>(SpriteDirection.class);
private int x;
private int y;
private boolean moving = false;
private int movingIndex = 0;
public MySprite(SpriteDirection direction, int x, int y) throws IOException {
this.direction = direction;
this.x = x;
this.y = y;
createSprites();
}
public void draw(Graphics g) {
Image img = null;
if (!moving) {
img = standingImgMap.get(direction);
} else {
img = movingImgMap.get(direction).get(movingIndex);
}
g.drawImage(img, x, y, null);
}
private void createSprites() throws IOException {
URL spriteSheetUrl = new URL(SPRITE_SHEET_PATH);
BufferedImage img = ImageIO.read(spriteSheetUrl);
// get sub-images (sprites) from the sprite sheet
// magic numbers for getting sprites from sheet, all obtained by trial and error
int x0 = 0;
int y0 = 64;
int rW = 32;
int rH = 32;
for (int row = 0; row < 4; row++) {
SpriteDirection dir = SpriteDirection.values()[row];
List<Image> imgList = new ArrayList<>();
movingImgMap.put(dir, imgList);
int rY = y0 + row * rH;
for (int col = 0; col < 5; col++) {
int rX = x0 + col * rW;
BufferedImage subImg = img.getSubimage(rX, rY, rW, rH);
if (col == 0) {
// first image is standing
standingImgMap.put(dir, subImg);
} else {
// all others are moving
imgList.add(subImg);
}
}
}
}
public SpriteDirection getDirection() {
return direction;
}
public void setDirection(SpriteDirection direction) {
if (this.direction != direction) {
setMoving(false);
}
this.direction = direction;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isMoving() {
return moving;
}
public void setMoving(boolean moving) {
this.moving = moving;
if (!moving) {
movingIndex = 0;
}
}
public void tick() {
if (moving) {
switch (direction) {
case RIGHT:
x += DELTA;
break;
case LEFT:
x -= DELTA;
break;
case FORWARD:
y += DELTA;
break;
case AWAY:
y -= DELTA;
}
movingIndex++;
movingIndex %= MAX_MOVING_INDEX;
}
}
public int getMovingIndex() {
return movingIndex;
}
public void setMovingIndex(int movingIndex) {
this.movingIndex = movingIndex;
}
}
enum SpriteDirection {
FORWARD, LEFT, AWAY, RIGHT
}
As an alternative to KeyListener, consider using actions and key bindings, discussed here. Derived from this example, the program below moves a line left, down, up or right using either buttons or keys.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/questions/6991648
* #see https://stackoverflow.com/questions/6887296
* #see https://stackoverflow.com/questions/5797965
*/
public class LinePanel extends JPanel {
private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;
public LinePanel() {
this.setPreferredSize(new Dimension(640, 480));
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(8,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
drawing = true;
p1 = e.getPoint();
p2 = p1;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
p2 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
p2 = e.getPoint();
repaint();
}
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 10;
public ControlPanel() {
this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code, final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
});
ControlPanel.this.getInputMap(
WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() {
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.add(new ControlPanel(), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LinePanel().display();
}
});
}
}
But basically I just want to know how to make the image move left to right while the window is listening to a keypress
You can use a Swing Timer to animate an image:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerAnimation extends JLabel implements ActionListener
{
int deltaX = 2;
int deltaY = 3;
int directionX = 1;
int directionY = 1;
public TimerAnimation(
int startX, int startY,
int deltaX, int deltaY,
int directionX, int directionY,
int delay)
{
this.deltaX = deltaX;
this.deltaY = deltaY;
this.directionX = directionX;
this.directionY = directionY;
setIcon( new ImageIcon("dukewavered.gif") );
// setIcon( new ImageIcon("copy16.gif") );
setSize( getPreferredSize() );
setLocation(startX, startY);
new javax.swing.Timer(delay, this).start();
}
public void actionPerformed(ActionEvent e)
{
Container parent = getParent();
// Determine next X position
int nextX = getLocation().x + (deltaX * directionX);
if (nextX < 0)
{
nextX = 0;
directionX *= -1;
}
if ( nextX + getSize().width > parent.getSize().width)
{
nextX = parent.getSize().width - getSize().width;
directionX *= -1;
}
// Determine next Y position
int nextY = getLocation().y + (deltaY * directionY);
if (nextY < 0)
{
nextY = 0;
directionY *= -1;
}
if ( nextY + getSize().height > parent.getSize().height)
{
nextY = parent.getSize().height - getSize().height;
directionY *= -1;
}
// Move the label
setLocation(nextX, nextY);
}
public static void main(String[] args)
{
JPanel panel = new JPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().setLayout(null);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
// frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
// frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
}
}
You can add a KeyListener to the panel and it will operate independently of the image animation.

Categories

Resources