Customizing camera orientation in Processing? - java

This is what I want to achieve: I want to draw the global axes (only the positive semiaxes) in a 3D processing sketch as x: red, y: green, g: blue. Then I want to place the camera at xyz coordinates (20,20,30), and have it look at xyz (0,0,0), such that the camera's Up vector is (nearly) colinear with the global z axis. Thus, in the end I want to see red axis (x) to the left, green axis (y) to the right, and blue axis (z) pointing upward - and then I want to have a mouse interaction like PeasyCam, but in respect to this orientation.
Because of Rotating camera around object axis with Peasycam, I know PeasyCam cannot really do something like this, so I tried to use OCD: Obsessive Camera Direction. Below is an MWE which uses it, and emulates some of the PeasyCam mouse interaction.
The problem is this: regardless of how I set the Up vector in the ODC Camera constructor, I get pretty much the same behavior when dragging (the white circle on the gif indicates the mouse position):
Clearly, regardless of how the Up vector is set, the rendering shows the green vector for y pointing downwards.
Actually, if one looks at the updateUp() function in ocd/src/damkjer/ocd/Camera.java, one can see that the originally set Up components are overwritten based on the camera and target location, so I guess, no wonder then why the sketch doesn't react differently on them; the only thing able to change the Up vector is seemingly the roll paramerer, set via roll() method.
My question is: what can I do in the code below, to achieve what I want (camera dragging interaction, but where the 0,0,1 vector is rendered/remains upwards)?
Here is the MWE code, Sketch.pde:
// modification of example on http://mrfeinberg.com/peasycam/
// https://stackoverflow.com/questions/17683602/rotating-camera-around-object-axis-with-peasycam/26755516#26755516
// sdaau, 2014
import damkjer.ocd.*;
DCamera cam1; //Camera cam1; // (see subclass below)
int saveCount=500;
void setup() {
// Setup graphics
size(300, 200, P3D);
// only .roll() seems to be able to manipulate up vector?
cam1 = new DCamera(this, //Camera(this, // (parent,
40, 40, 60, // cameraX, cameraY, cameraZ,
0, 0, 0, // targetX, targetY, targetZ
// 0, 0, 1, // upX, upY, upZ // (seems ignored)
// 0, 1, 0, // upX, upY, upZ // (seems ignored)
1, 0, 0, // upX, upY, upZ // (seems ignored)
10, 500 // nearClip, farClip) //(doesn't clip as peasycam!)
);
//~ cam1.roll(radians(-90));
}
void draw() {
cam1.feed(); //"send what this camera sees to the view port"
// actual drawing:
background(0);
stroke(255,0,0); line(0,0,0, 1000,0,0); // x axis
stroke(0,255,0); line(0,0,0, 0,1000,0); // ... y
stroke(0,0,255); line(0,0,0, 0,0,1000); // ... z
fill(255,0,0);
box(30);
pushMatrix();
translate(0,0,20);
fill(0,0,255);
box(5);
popMatrix();
fill(-1);
// text("U 0,0,1", -20, 30, 6);
// text("U 0,1,0", -20, 30, 6);
text("U 1,0,0", -20, 30, 6);
if (mouseButton == LEFT) {
hint(DISABLE_DEPTH_TEST);
camera(); // must have after disable for 2D draw
ellipse(mouseX,mouseY,20,20);
saveFrame( "images/image_" + saveCount + ".png" );
saveCount++;
hint(ENABLE_DEPTH_TEST);
}
}
// this to emulate peasycam:
// these to replicate the peasycam interaction with OCD:
void mouseDragged() {
if (mouseButton == LEFT) {
// http://www.airtightinteractive.com/demos/processing/bezier_ribbon_p3d/BezierRibbons.pde
cam1.arc(radians(-(mouseY - pmouseY))/4);
cam1.circle(radians(-(mouseX - pmouseX))/4);
} else if (mouseButton == RIGHT) {
cam1.zoom(radians(mouseY - pmouseY) / 2.0);
} else if (mouseButton == CENTER) {
// peasycam calls this .pan(); damkjer.ocd calls it .track()
cam1.track(-(mouseX - pmouseX), -(mouseY - pmouseY));
}
}
void mouseWheel(MouseEvent event) {
float e = event.getCount();
cam1.zoom(e*4.0);
}
public class DCamera extends damkjer.ocd.Camera {
// private final PApplet p; // in peasycam/src/peasy/PeasyCam.java; in ocd/src/damkjer/ocd/Camera.java it is called theParent! both are private!
private PApplet theParent; // replicate as in ocd/.../Camera.java; it helps, even if super has same name (must re-assign in ctor)
// directly from libraries/ocd/src/damkjer/ocd/Camera.java
public DCamera(PApplet aParent,
float aCameraX, float aCameraY, float aCameraZ,
float aTargetX, float aTargetY, float aTargetZ,
float aNearClip, float aFarClip)
{
super(aParent, aCameraX, aCameraY, aCameraZ, aTargetX, aTargetY, aTargetZ, aNearClip, aFarClip);
theParent = aParent;
}
// another constructor, to handle up vector:
public DCamera(PApplet aParent,
float aCameraX, float aCameraY, float aCameraZ,
float aTargetX, float aTargetY, float aTargetZ,
float anUpX, float anUpY, float anUpZ,
float aNearClip, float aFarClip)
{
super(aParent, aCameraX, aCameraY, aCameraZ, aTargetX, aTargetY, aTargetZ, anUpX, anUpY, anUpZ, aNearClip, aFarClip);
theParent = aParent;
}
}
/*
# https://stackoverflow.com/questions/3323619/how-to-sort-files-numerically-from-linux-command-line
# https://stackoverflow.com/questions/246215/how-can-i-list-files-with-their-absolute-path-in-linux
convert -delay 5 -loop 0 $(ls ./images/ | sort --version-sort -f) animate.gif
gifsicle -O2 --colors 8 animate.gif -o animate-O2.gif
*/

Well, I decided to try ProScene (Javadocs: proscene API), and it turns out it does what I want:
... that is, it can start with the z (blue) vector up - and then the mouse can be used to "turn around" that axis (approximately).
ProScene's default interaction is otherwise nearly the same as PeasyCam (mousewheel is opposite, though), so for this purpose, I think I've found a solution (though it would be great to know if the same is achievable with OCD).
Here is the code, Sketch.pde:
// modification of example on http://mrfeinberg.com/peasycam/
// http://stackoverflow.com/questions/17683602/rotating-camera-around-object-axis-with-peasycam/26755516#26755516
// sdaau, 2014
import remixlab.proscene.*;
import remixlab.dandelion.geom.*; // Vec
Scene scene;
int saveCount=500;
//Choose one of P3D for a 3D scene, or P2D or JAVA2D for a 2D scene
String renderer = P3D;
void setup() {
// Setup graphics
size(300, 200, renderer);
//Scene instantiation
scene = new Scene(this);
scene.setGridVisualHint(false);
scene.setAxesVisualHint(false);
// specify starting camera position, orientation and target
scene.camera().setUpVector(new Vec(0, 0, -1), true); // boolean noMove
scene.camera().setPosition(new Vec(30, 30, 50));
scene.camera().lookAt(new Vec(0, 0, 0));
// when damping friction = 0 -> spin
scene.eye().frame().setDampingFriction(0);
}
void draw() {
// actual drawing:
background(0);
stroke(255,0,0); line(0,0,0, 1000,0,0); // x axis
stroke(0,255,0); line(0,0,0, 0,1000,0); // ... y
stroke(0,0,255); line(0,0,0, 0,0,1000); // ... z
fill(255,0,0);
box(30);
pushMatrix();
translate(0,0,20);
fill(0,0,255);
box(5);
popMatrix();
fill(-1);
text("U 0,0,-1", -20, 30, 6);
if (mouseButton == LEFT) {
hint(DISABLE_DEPTH_TEST);
camera(); // must have after disable for 2D draw
ellipse(mouseX,mouseY,20,20);
// saveFrame( "images/image_" + saveCount + ".png" );
saveCount++;
hint(ENABLE_DEPTH_TEST);
}
}
void keyPressed() {
if(scene.eye().frame().dampingFriction() == 0)
scene.eye().frame().setDampingFriction(0.5);
else
scene.eye().frame().setDampingFriction(0);
println("Camera damping friction now is " + scene.eye().frame().dampingFriction());
}
/*
# http://stackoverflow.com/questions/3323619/how-to-sort-files-numerically-from-linux-command-line
# http://stackoverflow.com/questions/246215/how-can-i-list-files-with-their-absolute-path-in-linux
convert -delay 5 -loop 0 $(ls ./images/ | sort --version-sort -f) animate.gif
gifsicle -O2 --colors 8 animate.gif -o animate-O2.gif
*/

Related

(Processing)How to move a 3D object according to the screen's XY axis, instead of the world's X,Y,Z(PeasyCam)

Im writing a 3D sketch in which the user rotates the camera with peasyCam while left clicking and moving the mouse. The thing is that I want to move the objects while right click is pressed so that the user can drag the object across the screen's X and Y axis. Of course I know how to use mouseX and mouseY inputs to modify the translation but only across the 3D space coordinates as it shows on the GIF below:
example code of whats happening in the image:
import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;
PeasyCam cam;
float x=15;float y=15; float z=15;
float e;
void setup(){
size (700,700,P3D);
cam = new PeasyCam(this, 200);
cam.setRightDragHandler(null);
}
void draw(){
background(0);
pushMatrix();
translate(5, 5, 0);
fill(255);
stroke(255);
sphere(5);
popMatrix();
pushMatrix();
fill(255,0,0);
stroke(255,0,0);
translate(x, y, z);
sphere(5);
popMatrix();
stroke(0,0,255);
line(5,5,0,x,y,z);
//obvoiusly not working method
if(mousePressed && (mouseButton == RIGHT)){
x= x+(mouseX-pmouseX);
y= y+(mouseY-pmouseY);
}
}
void mouseWheel(MouseEvent event) {
e = event.getCount();
z=z+e;
println(e);
}
void mousePressed(){
if (mouseButton==RIGHT){
cam.setActive(false);
}
}
void mouseReleased(){
cam.setActive(true);
}
What I would need is to be able to drag the sphere only on the screens X/Y axis, at a fixed Z just like image below shows(simple simulation I made of the behaviour im looking for).
PeasyCam is for exploring the 3D space. The question might be difficult to undesrtand. The problem is about moving the object on the 3D world, using the screen/canvas 2D coordinates to make the object follow the cursor's movement. If the mouse goes to the left (x axis decreases), the object should move to the left on the screen, and not just on the worlds X axis. This is how the second example image behaves, but achieved by just simulating the 3D space with no actual rotations to the x,y,z axis.
I've been scratching my head with this thing and I cant seem to figure it out. I wouldn't have asked here otherwise. Thanks in advance guys.
PeasyCam is a library that gives you a camera that by default is controlled by the mouse. This allows you to render 3D scenes and not worry about the camera, since the library handles it for you.
But it sounds like that's not what you want. You want to render a 3D scene and use the mouse to control the position of the shapes in that scene. Basically, your controls are fighting with the default controls provided by the PeasyCam library.
I see that you've already tried disabling the right-click controls here:
cam.setRightDragHandler(null);
So at the very least you probably want to do the same thing with the left drag handler.
But at that point, why are you using the PeasyCam library at all?
And even if you disable the default left controls, you'll notice that the dragging of shapes is "exaggerated" because the camera is closer to the red shape, so moving it a little bit looks like it's moving more. Just like an object right in front of your face looks like it's moving a lot more than an object that's far away.
It sounds like what you really want to do is get rid of the PeasyCam library, and then use standard Processing functions to calculate the position of the spheres based on the user input. Check out the modelX(), modelY(), and modelZ() function in the reference.
Edit: Here is a simple example that shows the model functions in action:
float x;
float y;
void setup() {
size (700, 700, P3D);
}
void draw() {
background(0);
pushMatrix();
translate(width/2, height/2, 0);
fill(255);
stroke(255);
sphere(5);
popMatrix();
fill(255, 0, 0);
stroke(255, 0, 0);
if (mousePressed) {
x= modelX(mouseX, mouseY, 0);
y= modelY(mouseX, mouseY, 0);
}
translate(x, y, 15);
sphere(5);
}
May be you should be looking in beginHUD() and endHUD();
Here is the example code:
(Code from https://forum.processing.org/one/topic/2-questions-on-camera-view.html)
import peasy.*;
PeasyCam cam;
void setup() {
size(300,200,P3D);
// either put it here like this:
// cam = new PeasyCam(this, 50, 0, 0, 100);
cam = new PeasyCam(this, 0, 0, 0, 100);
cam.setMinimumDistance(50);
cam.setMaximumDistance(500);
// or separate like this:
cam.lookAt(50,0,0);
}
void draw() {
background(0);
//3D object
pushMatrix();
fill(255,0,0);
translate(50,0,0);
rotateX(-.5);
rotateY(-.5);
box(30);
popMatrix();
//2D object that is not affected by the camera
cam.beginHUD();
fill(0,0,255);
rect(200, height/2 -25 , 50, 50);
cam.endHUD();
}

How can I clear the screen in openGL using Java

I don't understand how I can simply clear the screen in Java while using OpenGL. I have searched all over the internet, there is like no real good resource for OpenGL information. Basically I just want to clear the screen and re-draw a circle. Instead my code decides that it isn't going to clear the screen ever, and it most definitely isn't going to draw anything else.. I want it to clear the screen when I press "e", and then draw a new circle. I have two java files.. I will only post relevant code for the sake of any user's who can help me - but will post more code if needed.
In the beginning of my JOGLEventListener.java file I'm also declaring a global var
// Test
GLAutoDrawable test = null;
JOGLEventListener.java
#Override
public void display(GLAutoDrawable gLDrawable)
{
// Set a global variable to hold the gLDrawable
// May not need this?
test = gLDrawable;
GL2 gl = gLDrawable.getGL().getGL2();
gl.glClearColor(backrgb[0], 0, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
backrgb[0]+=0.0005;
if (backrgb[0]> 1) backrgb[0] = 0;
// =============================================
// Draw my circle here
//
// =============================================
// =============================================
System.out.println("Drawing Circle..");
drawCircle(5.0f, 5.0f, 10.0f);
}
// Draw Circle
void drawCircle(float x, float y, float radius)
{
System.out.println("IN DRAWCIRCLE");
int i;
GL2 gl = test.getGL().getGL2();
int lineAmount = 100; //# of triangles used to draw circle
final
//GLfloat radius = 0.8f; //radius
float twicePi = (float) (2.0f * Math.PI);
gl.glBegin(gl.GL_LINE_LOOP);
for(i = 0; i <= lineAmount;i++) {
gl.glVertex2f(
x + (radius * (float)Math.cos(i * twicePi / lineAmount)),
y + (radius* (float)Math.sin(i * twicePi / lineAmount))
);
}
gl.glEnd();
}
#Override
public void keyTyped(KeyEvent e)
{
char key= e.getKeyChar();
System.out.printf("Key typed: %c\n", key);
GL2 gl = test.getGL().getGL2();
if(key == 'e')
{
// WHY ISNT THIS WORKING
// CLEAR THE SCREEN AND DRAW ME A NEW CIRCLE
gl.glClear( gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT );
gl.glLoadIdentity();
//test
float x = 100.0f;
float y = 100.0f;
float twicePi = (float) (2.0f * Math.PI);
float radius = 100f;
System.out.println("Draw Another Circle...");
gl.glBegin(gl.GL_LINE_LOOP);
for(int i = 0; i <= 360;i++)
{
gl.glVertex2f(
x + (radius * (float)Math.cos(i * twicePi / 360)),
y + (radius* (float)Math.sin(i * twicePi / 360))
);
}
gl.glEnd();
}
1) That's deprecated OpenGL, don't use it
2) Don't save the gl object to one global value, always get it from the drawable or the GLContext
3) Use a shader program to render and a vertex buffer to hold the vertices position. But first, I'd suggest you to start a tutorial to learn the basic of OpenGL. Or if you want to get something working asap, clone this hello triangle of mine and start experiment on that
The problem is apparently that you don't swap the front and back buffers.
I'm not familiar with the OpenGL bindings for Java, but I guess that the library already does that for you after it calls the display() function. It doesn't do that after keyTyped().
The way you are supposed to do this is to always draw the scene from scratch inside the display() function based on some internal state. Then in keyTyped() you shall modify that internal state and invalidate the window, which will cause the display() to be called again and redraw the scene properly.
EDIT: Calling display() yourself won't be enough. I can't find how to invalidate the window in Java (in C this would be so much easier). As a dirty hack you can try calling temp.swapBuffers() manually in display, setting setAutoSwapBufferMode(false) and calling display from keyTyped().

How to draw smooth continuous line in java swing that also varies in Size?

I am writing a 2D traditional animation program in Java using swing and JPen. The application works well. However, i am dissatisfied with the results I am getting when I draw lines.
Using the JPen API, the swing panel listens for the stylus input and its code is:
/**
* This method is called whenever the stylus makes contact with the tablet while inside of the JPanel functioning
* as the Drawing Canvas.
* #param evt
*/
public void penLevelEvent(PLevelEvent evt) {
// Get kind of event: does it come from mouse (CURSOR), STYLUS or ERASER?
PKind kind = evt.pen.getKind();
// Discard events from mouse
if (kind == PKind.valueOf(PKind.Type.CURSOR)){
//System.out.println("returning since this is only a mouse cursor");
return;
}
// Get the current cursor location
// position value is in with respect to entire application window
// Get the tilt values (not with a Bamboo... so untested!)
float curX = evt.pen.getLevelValue(PLevel.Type.X);
float curY = evt.pen.getLevelValue(PLevel.Type.Y);
float pressure = evt.pen.getLevelValue(PLevel.Type.PRESSURE);// 0.0 - 1.0
float xTilt = evt.pen.getLevelValue(PLevel.Type.TILT_X);
float yTilt = evt.pen.getLevelValue(PLevel.Type.TILT_Y);
// Set the brush's size, and darkness relative to the pressure
float darkness = 255 * pressure;
// Transform them to azimuthX and altitude, two angles with the projection of the pen against the X-Y plane
// azimuthX is the angle (clockwise direction) between this projection and the X axis. Range: -pi/2 to 3*pi/2.
// altitude is the angle between this projection and the pen itself. Range: 0 to pi/2.
// Might be more pratical to use than raw x/y tilt values.
double[] aa = { 0.0, 0.0 };
PLevel.Type.evalAzimuthXAndAltitude(aa, xTilt, yTilt);
// or just PLevel.Type.evalAzimuthXAndAltitude(aa, evt.pen);
double azimuthX = aa[0];
double altitude = aa[1];
//-------------------------------------------------------------------------------------
// If the stylus is being pressed down, we want to draw a black
// line onto the screen. If it's the eraser, we want to create
// a white line, effectively "erasing" the black line
//-------------------------------------------------------------------------------------
if (kind == PKind.valueOf(PKind.Type.STYLUS)) {
//System.out.println("Darkness "+darkness);
int alpha = 255 - (int)darkness;
color = new Color(0,0,255, 255 - alpha);
}
else if (kind == PKind.valueOf(PKind.Type.ERASER)) {
System.out.println("Handle eraser");
}
else {
return; // IGNORE or CUSTOM...
}
//If movement of the stylus is occuring
if (evt.isMovement()) {
//and the buttonIsDown(boolean)
if(buttonIsDown) {
//drawingCanvas:JPanel -> instruct the jpanel to draw at the following coordinate using the specified pressure
drawingCanvas.stylusMovementInput( prevXPos,prevYPos, curX,curY, pressure);
}
prevXPos = curX;
prevYPos = curY;
}
prevXPos = curX;
prevYPos = curY;
}
So after the above method is invoked, the jpanel(drawingCanvas) starts to draw on a BufferedImage by obtaining the image's graphics2D. Here is the code stylusMovementInput->calls -> performDrawOnBufferImageGraphic2D :
/**
* Draw on the active frame that is selected. Then call channel refresh, to refresh the the composite image derived
* from call changes related to the current frame
* #param cX current
* #param cY current
* #param oX previous
* #param oY previous
* #param pressure pressure 0 - 1f
*/
private void performDrawOnBufferImageGraphic2D(float oX, float oY, float cX, float cY, float pressure){
//Obtain the current layer that user wants to draw one
//MyImageData is encapsulating a BufferedImage
MyImageData $activeData = getActiveLayer();
//Exit if one is not valid
if( $activeData == null) return;
//if valid layer, get the, get the bufferedImage.getGraphics
Graphics2D $activeDataGFX = $activeData.getImageGraphics();
// Customize the drawing brush (create a BasicStroke)
Stroke thickness = Sys.makeStroke(getPencilSize(pressure), null);
// Determine the tranparency with respect to the pressure
int alpha = (int)(255 * pressure * getPencilOpacityPercentage());
// Get the current color found in the color wheel
Color cwVal = Sys.getColorFromColorWheel();
Color drawingColor ;
if(cwVal != null){
// add alpha value to it
drawingColor = new Color(cwVal.getRed(), cwVal.getGreen(), cwVal.getBlue(), alpha);
}else throw new RuntimeException("ColorWheel is null drawing stylus draw");
//set the brush and drawingColor
$activeDataGFX.setStroke(thickness);
// Save reference to the current bufferedImage graphic component
Composite originalComposite =$activeDataGFX.getComposite();
if(getCurrentTool() == DrawingCanvasTool.ERASER){
//If eraser, set new composite information, to allow erasing to transparency
$activeDataGFX.setPaint( new Color(255,255,255, 0));
$activeDataGFX.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.0f));
}else {
//set the drawing color
$activeDataGFX.setPaint(drawingColor);
}
//---------------------------------------------------------------
// Rotate, Translate, Zoom the image according to the panning, zoom, and rotate
// set by the user
//---------------------------------------------------------------
//Figure out the canvas center, as it is used for rotating
float theta = (float)Math.toRadians(canvasRotation);
Dimension drawingAreaComponentSize = getSize();
float centerX = drawingAreaComponentSize.width/2;
float centerY = drawingAreaComponentSize.height/2;
AffineTransform transform = new AffineTransform();
transform.rotate(-theta, centerX, centerY);
transform.scale(1.0f / canvasZoom, 1.0f / canvasZoom);//erase
transform.translate(-canvasPan.x, -canvasPan.y);//erase
$activeDataGFX.setTransform(transform);
//Now Draw inside of the active data graphics object
Shape line = new Line2D.Float(cX,cY, oX, oY);
Path2D.Float t = new Path2D.Float(line);
$activeDataGFX.draw(t);
//drawing is complete
if(getCurrentTool() ==DrawingCanvasTool.ERASER){
//Restore the old composite object
$activeDataGFX.setComposite(originalComposite);
}
//Refresh basically merges frames along a frame column into a single preview buffered image
//which will later be used to view the tool animation when user clicks "play" button
channelPannel.refreshFrameOut( channelPannel.getCurrentFrame() );
}
I commented a lot of the code, and provided the critical points related to the question. Any help is much appreciated. And again, the problem is how do i draw a smooth line worthy of a descent drawing program.

java libgdx move perspective camera

I'm creating a 3D game but I don't know how to translate the camera according to my fingers. I'm creating a map(x = -30 to +30;y = -30 to 30;z = -1 to 1) where every coordinate is used for a model using .g3db files from my assets and put in place using model translation. This works so far, the map looks good and is viewed in a 67% angle. The map is so large it can't be viewed in the camera at once(no I don't want to zoom out). Whenever I'm touching the screen of my android phone it's just rotating around the center, but instead I want the gamemap to stay in place from my point of view and instead change the position of the perspective camera on the x and y axis. There's no game object that can be used for position tracking, everything should be depending on my finger actions. This way I want to move to visible screen to each x and y direction(think of it visualy like a 2D camera moving up, down and to the sides in front of a picture). Can you help me?
This is my code so far:
public class MyGdxGame extends ApplicationAdapter implements ApplicationListener{
//define variables
...
#Override
public void create() {
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
modelBatch = new ModelBatch();
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(10f, 10f, 10f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
assets = new AssetManager();
//load assets
...
loading = true;
camController = new CameraInputController(cam);
camController.forwardTarget = true;
Gdx.input.setInputProcessor(camController);
}
private void doneLoading() {
//load models and add them to instances
loading = false
}
#Override
public void render() {
if (loading && assets.update())
doneLoading();
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instances, environment);
modelBatch.end();
}
I'm using the spring tool suite 3.5.1 as IDE and libgdx 1.0.1
Dont use a CameraInputController, but instead implement InputProcessor and use that class in the call to Gdx.input.setInputProcessor.
For example:
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
And in the create method:
Gdx.input.setInputProcessor(this);
You'll have to implement all methods as shown in this link, except for the touchDown and touchDragged method which require additional code:
private int dragX, dragY;
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
dragX = x;
dragY = y;
return true;
}
#Override
public boolean touchDragged (int x, int y, int pointer) {
float dX = (float)(x-dragX)/(float)Gdx.graphics.getWidth();
float dY = (float)(dragY-y)/(float)Gdx.graphics.getHeight();
dragX = x;
dragY = y;
cam.position.add(dX * 10f, dY * 10f, 0f);
cam.update();
return true;
}
Here the dX and dY values is the amount that user has dragged on the screen within the range between -1f and +1f. E.g. for dX a value of 0.5f means that the user dragged half the screen size to the right. Note that these are delta values since the last time the method was called. The method is likely to be called many times per drag operation. Therefor, the dX and dY will be small values.
The call to cam.position.add(...) will actually move the camera and the call to cam.update(); will recalculate the values needed for rendering. I've used a multiplier of 10f for both the X and Y translation of the camera. This means that if the user fully drags from the left edge to the right edge of the screen, that the camera will move a total amount of 10 units to the right. You can adjust the value as needed.
Note that this moves the camera on XY plane. If you need to move on e.g. the XZ plane, you need to adjust the call the cam.position.add(...) accordingly. Also, this doesn't change the direction of the camera. If you want to move the camera, while looking at the same location, you'll need to add cam.lookAt(0,0,0); just before cam.update();.

Drawing filled polygon with libGDX

I want to draw some (filled) polygons with libGDX. It shoudn't be filled with a graphic/texture. I have only the vertices of the polygon (closed path) and tried to visualize with meshes but at some point this is not the best solution, I think.
My code for an rectangle is:
private Mesh mesh;
#Override
public void create() {
if (mesh == null) {
mesh = new Mesh(
true, 4, 0,
new VertexAttribute(Usage.Position, 3, "a_position")
);
mesh.setVertices(new float[] {
-0.5f, -0.5f, 0
0.5f, -0.5f, 0,
-0.5f, 0.5f, 0,
0.5f, 0.5f, 0
});
}
}
// ...
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
mesh.render(GL10.GL_TRIANGLE_STRIP, 0, 4);
}
is there a function or something to draw filled polygons in an easier way?
Since recent updates of LibGDX, #Rus answer is using deprecated functions. However, I give him/her credits for the new updated version below:
PolygonSprite poly;
PolygonSpriteBatch polyBatch = new PolygonSpriteBatch(); // To assign at the beginning
Texture textureSolid;
// Creating the color filling (but textures would work the same way)
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0xDEADBEFF); // DE is red, AD is green and BE is blue.
pix.fill();
textureSolid = new Texture(pix);
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] { // Four vertices
0, 0, // Vertex 0 3--2
100, 0, // Vertex 1 | /|
100, 100, // Vertex 2 |/ |
0, 100 // Vertex 3 0--1
}, new short[] {
0, 1, 2, // Two triangles using vertex indices.
0, 2, 3 // Take care of the counter-clockwise direction.
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
For good triangulating algorithms if your polygon is not convex, see the almost-linear earclipping algorithm from Toussaint (1991)
Efficient triangulation of simple polygons, Godfried Toussaint, 1991
Here is a libGDX example which draws a 2D concave polygon.
Define class members for PolygonSprite PolygonSpriteBatch
PolygonSprite poly;
PolygonSpriteBatch polyBatch;
Texture textureSolid;
Create instances, 1x1 size texture used with red pixel as workaround. An array of coordinates (x, y) is used for initialization of the polygon.
ctor() {
textureSolid = makeTextureBox(1, 0xFFFF0000, 0, 0);
float a = 100;
float b = 100;
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] {
a*0, b*0,
a*0, b*2,
a*3, b*2,
a*3, b*0,
a*2, b*0,
a*2, b*1,
a*1, b*1,
a*1, b*0,
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
}
Draw and rotate polygon
void draw() {
super.draw();
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
poly.rotate(1.1f);
}
I believe the ShapeRenderer class now has a polygon method for vertex defined polygons:
ShapeRenderer.polygon()
You can use the ShapeRenderer API to draw simple, solid-color shapes with Libgdx.
The code you've given is a reasonable way to draw solid color polygons too. Its much more flexible than ShapeRenderer, but is a good bit more complicated. You'll need to use glColor4f to set the color, or add a Usage.Color attribute to each vertex. See the SubMeshColorTest example for more details on the first approach and the MeshColorTexture example for details on the second approach.
Another option to think about is using sprite textures. If you're only interested in simple solid colors objects, you can use very simple 1x1 textures of a single color and let the system stretch that across the sprite. Much of Libgdx and the underlying hardware are really optimized for rendering textures, so you may find it easier to use even if you're not really taking advantage of the texture contents. (You can even use a 1x1 white texture, and then use a SpriteBatch with setColor and draw()
to draw different color rectangles easily.)
You can also mix and match the various approaches, too.
Use triangulation algorithm and then draw all triangles as GL_TRIANGLE_STRIP
http://www.personal.psu.edu/cxc11/AERSP560/DELAUNEY/13_Two_algorithms_Delauney.pdf
just wanted to share my related solution with you, namely for implementing and drawing a walkZone with scene2d. I basically had to put together the different suggestions of the others' posts:
1) The WalkZone:
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Polygon;
import com.mygdx.game.MyGame;
public class WalkZone extends Polygon {
private PolygonRegion polygonRegion = null;
public WalkZone(float[] vertices) {
super(vertices);
if (MyGame.DEBUG) {
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0x00FF00AA);
pix.fill();
polygonRegion = new PolygonRegion(new TextureRegion(new Texture(pix)),
vertices, new EarClippingTriangulator().computeTriangles(vertices).toArray());
}
}
public PolygonRegion getPolygonRegion() {
return polygonRegion;
}
}
2) The Screen:
you can then add a listener in the desired Stage:
myStage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if (walkZone.contains(x, y)) player.walkTo(x, y);
// or even directly: player.addAction(moveTo ...
return super.touchDown(event, x, y, pointer, button);
}
});
3) The implementation:
The array passed to te WZ constructor is a set of x,y,x,y... points. If you put them counter-clockwise, it works (I didn't check the other way, nor know how it exactly works); for example this generates a 100x100 square:
yourScreen.walkZone = new WalkZone(new int[]{0, 0, 100, 0, 100, 100, 0, 100});
In my project it works like a charm, even with very intricated polygons. Hope it helps!!
Most answers suggest triangulation, which is fine, but you can also do it using the stencil buffer. It handles both convex and concave polygons. This may be a better solution if your polygon changes a lot, since otherwise you'd have to do triangulation every frame. Also, this solution properly handles self intersecting polygons, which EarClippingTriangulator does not.
FloatArray vertices = ... // The polygon x,y pairs.
Color color = ... // The color to draw the polygon.
ShapeRenderer shapes = ...
ImmediateModeRenderer renderer = shapes.getRenderer();
Gdx.gl.glClearStencil(0);
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
Gdx.gl.glColorMask(false, false, false, false);
renderer.begin(shapes.getProjectionMatrix(), GL20.GL_TRIANGLE_FAN);
renderer.vertex(vertices.get(0), vertices.get(1), 0);
for (int i = 2, n = vertices.size; i < n; i += 2)
renderer.vertex(vertices.get(i), vertices.get(i + 1), 0);
renderer.end();
Gdx.gl.glColorMask(true, true, true, true);
Gdx.gl.glStencilOp(GL20.GL_ZERO, GL20.GL_ZERO, GL20.GL_ZERO);
Gdx.gl.glStencilFunc(GL20.GL_EQUAL, 1, 1);
Gdx.gl.glEnable(GL20.GL_BLEND);
shapes.setColor(color);
shapes.begin(ShapeType.Filled);
shapes.rect(-9999999, -9999999, 9999999 * 2, 9999999 * 2);
shapes.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
To use the stencil buffer, you must specify the number of bits for the stencil buffer when your app starts. For example, here is how to do that using the LWJGL2 backend:
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.stencil = 8;
new LwjglApplication(new YourApp(), config);
For more information on this technique, try one of these links:
http://commaexcess.com/articles/7/concave-polygon-triangulation-shortcut
http://glprogramming.com/red/chapter14.html#name13
http://what-when-how.com/opengl-programming-guide/drawing-filled-concave-polygons-using-the-stencil-buffer-opengl-programming/

Categories