rearview mirror

Started by athanazio, December 02, 2006, 03:45:08 AM

Previous topic - Next topic

athanazio

I´m implementing a basic car control to understand more about jpct features, but´m facing a challenge now: how to implement a rearview mirror ?
something like this for example ...


should I use more than one camera ?

thanks for the help

Melssj5

:shock:  Well, I guess that it cant be done with jpct, maybe if Egon implements a way to render a camera into a defined rectagle, you could render one camera and then the another anoe on the rectangle desired, Only Egon can resolve this.
Nada por ahora

athanazio

ouch !  :shock:

I tried last night to do like this:

1. created a new world
2. cloned all the objects to the new world (is itreally necessary, as Im looking for the same objects ? ...
3. created another FrameBuffer
4. rendered the new FrameBuffer in a small rectangle

result = black rectangle ...

is this a good aproach Egon ? IMO a good to do it is have the possibility of use the way I'm doing with some other details:
- dont need to copy the objects from one world to other
- define a view port or something like this to the rendering process

all the source code is here

and this is the game class, with what I tried

package com.foo.game;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.util.Enumeration;

import com.foo.game.test.Pathbuilder;
import com.foo.game.util.CameraUtil;
import com.foo.game.util.LightUtil;
import com.foo.game.util.PlaneUtil;
import com.foo.game.util.SimpleGame;
import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

public class RearViewGame extends SimpleGame {

public RearViewGame(Component owner, Pathbuilder builder) throws Exception {
super(owner, builder);
init();
}

private static final long serialVersionUID = 1L;

private static final float ROTATE_ANGLE = (float) Math.toRadians(20.0);

private static final float SPEED = 25.0f;

private Object3D current = null;

public void init() throws Exception {
addTexture("rocks", "textures/rocks.jpg");
addTexture("floor", "textures/floor.jpg");

Object3D[] list = new Object3D[3];

list[0] = Primitives.getCube(20);
setCurrent(list[0]);

// remove from the floor, decrease
list[0].translate(0, -20, 0);
list[2] = Primitives.getCylinder(50);
list[2].translate(0, -50, -100);

list[1] = PlaneUtil.getPlane("floor", 20, 300);
addObjects(list);

CameraUtil.setCameraLookingFromTop(getCamera(), list[0]);
LightUtil.addDefaultLight(getWorld());
createRearViewWorld();
}

World rearView = new World();

FrameBuffer rearViewBuffer;

private void createRearViewWorld() {
World rearView = new World();
Enumeration objects = getWorld().getObjects();
while (objects.hasMoreElements()) {
Object3D element = (Object3D) objects.nextElement();
rearView.addObject(element.cloneObject());
}
rearView.buildAllObjects();

LightUtil.addDefaultLight(rearView);
Camera c = rearView.getCamera();
c.setPosition(50, -100, 50);
c.lookAt(SimpleVector.ORIGIN);

rearViewBuffer = new FrameBuffer(300, 100,
FrameBuffer.SAMPLINGMODE_NORMAL);
rearViewBuffer.enableRenderer(IRenderer.RENDERER_SOFTWARE);

Enumeration o= rearView.getObjects();
while (o.hasMoreElements()) {
Object3D element = (Object3D) o.nextElement();
System.out.println(element.getVisibility());
System.out.println(element.getCenter());
}

}

private void renderRearView() {
rearViewBuffer.clear();
rearView.renderScene(rearViewBuffer);
rearView.draw(rearViewBuffer);
rearViewBuffer.update();
}

public void afterPaintComponent(Graphics g) {
rearViewBuffer.display(g, 30, 30);
g.setColor(Color.WHITE);
g.drawRect(30, 30, 300, 100);
}

// TODO move the camera with the object
public void loop() {
renderRearView();

if (getCurrent() != null) {

if (isUp()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isDown()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(-SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isLeft()) {
getCurrent().rotateY(ROTATE_ANGLE);
}

if (isRight()) {
getCurrent().rotateY(-ROTATE_ANGLE);
}
} else {
System.out.println("current is null");
}
}

public Object3D getCurrent() {
return current;
}

public void setCurrent(Object3D current) {
this.current = current;
}

}


this is the visual result :

athanazio

I found in a presentation of ogre, the way that is done over there ...
It can be done with the creation of ViewPort, or RenderTexture, that is something like render a camera to an texture ... this would be very cool :)
imagine to render mirrors and reflexive images over water ... uhuuuu !

I can see the player gatting close to the water and seeing its on image on it ... this is a good coding challenge hehehe

athanazio

well, I'm trying to solve this ... with no success ...
I tried to save the image fro mthe second renderer but still black screen ...
no good ... help me Egon !! :)

look what I tried ...

public void afterPaintComponent(Graphics g) {
BufferedImage buffer = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_RGB);

Graphics2D g2 = buffer.createGraphics();

rearViewBuffer.display(g2);

try {
generateJPG(buffer, "d:/foo.jpg");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

g.setColor(Color.WHITE);
g.drawRect(30, 30, 300, 100);
}

private static void generateJPG(BufferedImage buffer, String fileName)
throws FileNotFoundException, IOException {

FileOutputStream file = new FileOutputStream(fileName);

JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(file);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buffer);
param.setQuality(1.0F, true);
encoder.setJPEGEncodeParam(param);

encoder.encode(buffer);
file.close();
}

athanazio

getting better !! :)
after some small changes in the code, some stupid mistakes... shhh... =) like the second world instance wanst used in the code that copies the objects.

now I got and fixed image ... in the "mirror" now I will create something like a movement listener to update the clone with the position information ...

Hey would be great if we dont need to clone() the objets, just share the same objets, one sugestion is create a subclass of world called Viewport that can share the same objects, and have for example different light sources, differnt camera...

look how it looks

cyberkilla

Well done man:) I knew that would be possible.


I didnt read everything, so i dont know exactly what you did, but this is what id do...


2 Cameras. One for front, one for rear.

render view from REAR to bufferedimage.


render front view, and blit buffered image into place.
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

athanazio

aha !!!
now is working !!

http://www.athanazio.pro.br/wp-content/uploads/2006/11/testAppletRearView.html

What I made :
1. create a new World with a new FrameBuffer
2. add to my AbstractEntity (copied from the Car sample) some methods to deal with Object3D synchronization, if you change the master one the others are changed

3. and as cyberkilla said, another camera in other direction, hehehe worked fine !!

now I will try to do using an Texture, to offer object reflection :) lets see what happens ...

athanazio

source code for the implementation of an rearview mirror =) (this is to help google ...)
http://www.athanazio.pro.br/wp-content/uploads/2006/11/rearwindow_src_20061202.zip

some interesting pieces ... :)

the game class

package com.foo.game.rearview;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.util.Enumeration;

import com.foo.game.test.Pathbuilder;
import com.foo.game.util.AbstractEntity;
import com.foo.game.util.CameraUtil;
import com.foo.game.util.FPSListener;
import com.foo.game.util.LightUtil;
import com.foo.game.util.PlaneUtil;
import com.foo.game.util.SimpleGame;
import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

public class RearViewGame extends SimpleGame implements FPSListener{

public RearViewGame(Component owner, Pathbuilder builder) throws Exception {
super(owner, builder);
init();
}

private static final long serialVersionUID = 1L;

private static final float ROTATE_ANGLE = (float) Math.toRadians(20.0);

private static final float SPEED = 25.0f;

private Object3D current = null;
private int lastFPS = 0;

public void init() throws Exception {
addTexture("rocks", "textures/rocks.jpg");
addTexture("floor", "textures/floor.jpg");

AbstractEntity[] list = new AbstractEntity[3];

list[0] = new AbstractEntity(Primitives.getCube(20));
setCurrent(list[0]);

// remove from the floor, decrease
list[0].translate(0, -20, 0);
list[2] = new AbstractEntity(Primitives.getCylinder(50));
list[2].translate(0, -50, -100);

list[1] = new AbstractEntity(PlaneUtil.getPlane("floor", 20, 300));
addObjects(list);

CameraUtil.setCameraLookingFromTop(getCamera(), list[0]);
LightUtil.addDefaultLight(getWorld());
createRearViewWorld();
addFPSListener(this);
}

World rearViewWorld = new World();

FrameBuffer rearViewBuffer;

private void createRearViewWorld() {
rearViewWorld = new World();
Enumeration objects = getWorld().getObjects();
while (objects.hasMoreElements()) {
AbstractEntity element = (AbstractEntity) objects.nextElement();
AbstractEntity clone = new AbstractEntity( element.cloneObject());
[b] element.addSyncObject(clone);
[/b] rearViewWorld.addObject(clone);
}
rearViewWorld.buildAllObjects();

LightUtil.addDefaultLight(rearViewWorld);
Camera c = rearViewWorld.getCamera();
c.setPosition(50, -100, 50);
c.lookAt(SimpleVector.ORIGIN);

[b]rearViewBuffer = new FrameBuffer(300, 100,
FrameBuffer.SAMPLINGMODE_NORMAL);[/b]
rearViewBuffer.enableRenderer(IRenderer.RENDERER_SOFTWARE);

Enumeration o = rearViewWorld.getObjects();
while (o.hasMoreElements()) {
Object3D element = (Object3D) o.nextElement();
System.out.println(element.getCenter());
}

}

private void renderRearView() {
rearViewBuffer.clear();
rearViewWorld.renderScene(rearViewBuffer);
rearViewWorld.draw(rearViewBuffer);
rearViewBuffer.update();
}

[b] public void afterPaintComponent(Graphics g) {
[/b]
rearViewBuffer.display(g, 30, 30);

g.setColor(Color.WHITE);
g.drawRect(30, 30, 300, 100);

g.drawString(Integer.toString(lastFPS) + "FPS", 15, 15);
}

// TODO move the camera with the object
public void loop() {
renderRearView();

if (getCurrent() != null) {

if (isUp()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isDown()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(-SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isLeft()) {
getCurrent().rotateY(ROTATE_ANGLE);
}

if (isRight()) {
getCurrent().rotateY(-ROTATE_ANGLE);
}
} else {
System.out.println("current is null");
}
}

public Object3D getCurrent() {
return current;
}

public void setCurrent(Object3D current) {
this.current = current;
}

public void notifyFPS(int fps) {
lastFPS = fps;
}


}

raft

nice work athanazio ;)

isnt it possible two use two cameras instead of cloning objects and using two worlds ?

r a f t

athanazio

Oh well, I really like this challenge ! :)
soh I implemented the class ViewPort to be like a second view of an world, and add the possbility to create an texture with the current framebuffer of it

the result was pretty nice (I think ...)



This is the ViewPort class

package com.foo.game.util;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Enumeration;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Texture;
import com.threed.jpct.World;

public class ViewPort {

private World w;

private FrameBuffer fb;

private int width;

private int height;

public ViewPort(World master, int width, int height) {

this.width = width;
this.height = height;

w = new World();
Enumeration objects = master.getObjects();
while (objects.hasMoreElements()) {
AbstractEntity element = (AbstractEntity) objects.nextElement();
AbstractEntity clone = new AbstractEntity(element.cloneObject());
element.addSyncObject(clone);
w.addObject(clone);
}
w.buildAllObjects();

fb = new FrameBuffer(width, height, FrameBuffer.SAMPLINGMODE_NORMAL);
fb.enableRenderer(IRenderer.RENDERER_SOFTWARE);
}

public void render() {
fb.clear();
w.renderScene(fb);
w.draw(fb);
fb.update();
}

public World getWorld() {
return w;
}

/**
* draws the world using the framebuffer in the informed Graphics at the
* position (x,y)
*
* @param g
* @param x
* @param y
*/
public void display(Graphics g, int x, int y) {
fb.display(g, x, y);
}

/**
* returns the draw result of the view port in a texture
*
* @return
*/
public Texture getTexture() {
BufferedImage buffer = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);

Graphics2D g2 = buffer.createGraphics();
display(g2, 0, 0);

return new Texture(buffer);
}
}


This is the class that is using the ViewPort class

package com.foo.game.rearview;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;

import com.foo.game.test.Pathbuilder;
import com.foo.game.util.AbstractEntity;
import com.foo.game.util.CameraUtil;
import com.foo.game.util.FPSListener;
import com.foo.game.util.LightUtil;
import com.foo.game.util.PlaneUtil;
import com.foo.game.util.SimpleGame;
import com.foo.game.util.ViewPort;
import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;

public class RearViewGame extends SimpleGame implements FPSListener {

public RearViewGame(Component owner, Pathbuilder builder) throws Exception {
super(owner, builder);
init();
}

private static final long serialVersionUID = 1L;

private static final float ROTATE_ANGLE = (float) Math.toRadians(20.0);

private static final float SPEED = 25.0f;

private Object3D current = null;

private int lastFPS = 0;

private ViewPort rearview;

private ViewPort mirror;

public void init() throws Exception {
System.out.println("Config.maxPolysVisible=" + Config.maxPolysVisible);
Config.maxPolysVisible = 12000;

addTexture("rocks", "textures/rocks.jpg");
addTexture("floor", "textures/floor.jpg");

AbstractEntity[] list = new AbstractEntity[4];

list[0] = new AbstractEntity(Primitives.getCube(20));
setCurrent(list[0]);

// remove from the floor, decrease
list[0].translate(0, -20, 0);
list[2] = new AbstractEntity(Primitives.getCylinder(50));
list[2].translate(0, -50, -100);

// the mirror
list[3] = new AbstractEntity(Primitives.getPlane(50, 3));
list[3].translate(100, -50, 100);
getTextureManager().addTexture("mirror",
getTextureManager().getDummyTexture());
list[3].setTexture("mirror");

list[1] = new AbstractEntity(PlaneUtil.getPlane("floor", 20, 300));
addObjects(list);

CameraUtil.setCameraLookingFromTop(getCamera(), list[0]);

mirror = new ViewPort(getWorld(), 256, 256);
Camera c = mirror.getWorld().getCamera();
c.setPosition(100, -50, 100);
c.lookAt(SimpleVector.ORIGIN);

rearview = new ViewPort(getWorld(), 300, 100);
c = rearview.getWorld().getCamera();
c.setPosition(50, -100, 50);
c.lookAt(SimpleVector.ORIGIN);

LightUtil.addDefaultLight(getWorld());
LightUtil.addDefaultLight(rearview.getWorld());
LightUtil.addDefaultLight(mirror.getWorld());
addFPSListener(this);
}

public void afterPaintComponent(Graphics g) {

rearview.display(g, 30, 30);

g.setColor(Color.WHITE);
g.drawRect(30, 30, 300, 100);

g.drawString(Integer.toString(lastFPS) + "FPS", 15, 15);
}

public void loop() {
rearview.render();
mirror.render();
getTextureManager().replaceTexture("mirror", mirror.getTexture());

if (getCurrent() != null) {

if (isUp()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isDown()) {
SimpleVector a = getCurrent().getZAxis();
a.scalarMul(-SPEED);
getCurrent().translate(a);
CameraUtil.moveCamera(getWorld().getCamera(), current);
}

if (isLeft()) {
getCurrent().rotateY(ROTATE_ANGLE);
}

if (isRight()) {
getCurrent().rotateY(-ROTATE_ANGLE);
}
} else {
System.out.println("current is null");
}
}

public Object3D getCurrent() {
return current;
}

public void setCurrent(Object3D current) {
this.current = current;
}

public void notifyFPS(int fps) {
lastFPS = fps;
}

}

EgonOlsen

Well done. I haven't tried this myself yet. It will be difficult to do this using the hardware renderer, because currently there's no support for rendering into a texture. Anyway, nice work.

athanazio

Egon,

is there possible to set something at the Object3D to allow the use in multiple worlds ? doing like this I would be able to set more than one camera with less objects in memory.

EgonOlsen

No. An Object3D holds a reference to its World. That's bad, i know...but it's a requirement from some methods if you don't want to make the World a parameter that you would have to pass all time.
But you can work with different cameras on one World and render that world into different framebuffers. So i'm not sure if you really need multiple Worlds for implementing the mirror effects. It should be possible with a single one.

athanazio

done !! only one world and multiple cameras, and the mirror I'm rendering in a BufferedImage and changing the Texture in the TextureManager ... hehehe dont know if this is the best aproach ... but its working, the performance its not fantastic, but I'm trying to tune it ...


this is funny screenshot mirror seeing it self ... :)