/*
* Simbad - Robot Simulator
* Copyright (C) 2004 Louis Hugues
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
-----------------------------------------------------------------------------
* $Author: sioulseuguh $
* $Date: 2005/08/07 12:25:03 $
* $Revision: 1.16 $
* $Source: /cvsroot/simbad/src/simbad/sim/World.java,v $
*/
package org.myrobotlab.mapper.sim;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.util.Map;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.LineArray;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PointLight;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.VirtualUniverse;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Sphere;
/**
* Represents a 3d world - this class use intensively JAVA3D. It creates the
* scenegraph and the view platform. The building process uses a
* EnvironmentDescription object containing colors , objects and attributes
* given by the user. This class is thighly coupled with Simulator Class.
*
* Remember to call System.setProperty("j3d.implicitAntialiasing", "true") at
* the very beginning of your program in order to enable antialiasing.
*
*/
public class World {
/** J3D universe */
VirtualUniverse universe;
/** The content branch. */
private BranchGroup sceneBranch;
/** The base translation of the sceneBranch. */
private TransformGroup sceneTrans;
/** The base rotation of the sceneBranch. */
private TransformGroup sceneRot;
/**
* All the pickable/collidable objects should be attached under this
* sub-branch
*/
private BranchGroup pickableSceneBranch;
/** The view branch */
private BranchGroup viewBranch;
private TransformGroup viewTransformGroup;
private ViewPlatform viewPlatform;
private View view;
/** The main canvas for viewing the world. */
private Canvas3D canvas3d;
/** For managing the mouse mouvement in the Canvas3D */
MouseOrbiter mouseOrbiter;
// lights
private Light light1;
private Light light2;
// constants used to specify the viewpoint
public static final int VIEW_FROM_TOP = 1;
public static final int VIEW_FROM_EAST = 2;
public static final int VIEW_BEHIND_AGENT = 3;
public static final int VIEW_ABOVE_AGENT = 4;
public static final int VIEW_ABOVE_AGENT_NEAR = 5;
public static final int VIEW_AGENT_SIDE = 6;
/** The size of the square containing the world */
protected float worldSize;
/** Prepared colors. */
Color3f white = new Color3f(1, 1, 1);
Color3f black = new Color3f(0, 0, 0);
/** Construct a World from a given EnvironmentDescription. */
public World(EnvironmentDescription ed) {
create(ed);
}
/**
* Add a light to the 3d world. Used only in the creation phase.
*/
Light addLight(Vector3d pos, Color3f color) {
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), worldSize * 2);
TransformGroup tg = new TransformGroup();
Transform3D t3d = new Transform3D();
t3d.set(pos);
tg.setTransform(t3d);
PointLight light = new PointLight();
light.setAttenuation(0.5f, 0, 0);
// light.setAttenuation(0f,.08f,0);
// light.setAttenuation(1.2f,0,0);
// note : light pos not affected by transform (but bound is).
light.setPosition((float) pos.x, (float) pos.y, (float) pos.z);
light.setInfluencingBounds(bounds);
sceneTrans.addChild(light);
// light geometry
ColoringAttributes ca = new ColoringAttributes();
ca.setColor(color);
Appearance appL1 = new Appearance();
appL1.setColoringAttributes(ca);
Primitive s = new Sphere(0.4f, appL1);
s.setCollidable(true);
tg.addChild(s);
sceneTrans.addChild(tg);
return light;
}
/**
* Adds a 3D object to the world. Used only in the creation phase.
*/
public void addObjectToPickableSceneBranch(BaseObject obj3d) {
obj3d.compile();
pickableSceneBranch.addChild(obj3d.getNode());
}
/** attach a 3d object to the scenegraph. */
public void attach(BaseObject obj3d) {
pickableSceneBranch.addChild(obj3d.getNode());
}
/**
* Change the user view Point . Note that we modify the ViewBranch transform
* not the scene transform.
*
* @param type
* can be VIEW_FROM_TOP,VIEW_FROM_EAST,VIEW_BEHIND_AGENT
* @param agent
* : specify the agent if VIEW_BEHIND_AGENT
*
* The VIEW_BEHIND_AGENT case has to be called regularly because of
* the agent displacement.
*/
public void changeViewPoint(int type, SimpleAgent agent) {
Point3d p1 = new Point3d();
Point3d p2 = new Point3d();
Transform3D t1 = new Transform3D();
Transform3D t2 = new Transform3D();
t1.setIdentity();
t2.setIdentity();
mouseOrbiter.resetView();
switch (type) {
case VIEW_FROM_TOP:
t1.lookAt(new Point3d(0, worldSize * 1.2, 0), new Point3d(0, 0, 0), new Vector3d(0, 0, -1));
t1.invert();
viewTransformGroup.setTransform(t1);
break;
case VIEW_FROM_EAST:
t1.lookAt(new Point3d(worldSize, worldSize, 0), new Point3d(0, 0, 0), new Vector3d(-1, 0, 0));
t1.invert();
viewTransformGroup.setTransform(t1);
break;
case VIEW_BEHIND_AGENT:
t1.setTranslation(new Vector3d(-agent.getRadius() * 2, 0, 0));
agent.getGroup().getLocalToVworld(t2);
t1.mul(t2);
viewTransformGroup.setTransform(t1);
break;
case VIEW_ABOVE_AGENT:
agent.getRotationTransformGroup().getLocalToVworld(t1);
t1.transform(p1);
t1.transform(p2);
p2.y = worldSize * .8;
t1.lookAt(p2, p1, new Vector3d(0, 0, -1));
t1.invert();
viewTransformGroup.setTransform(t1);
break;
case VIEW_ABOVE_AGENT_NEAR:
agent.getRotationTransformGroup().getLocalToVworld(t1);
t1.transform(p1);
t1.transform(p2);
p2.y = agent.getHeight() * worldSize * 0.5;
// avoid front clipping
if (p2.y < 0.2)
p2.y = 0.2;
t1.lookAt(p2, p1, new Vector3d(0, 0, -1));
t1.invert();
viewTransformGroup.setTransform(t1);
break;
case VIEW_AGENT_SIDE:
agent.getRotationTransformGroup().getLocalToVworld(t1);
t1.transform(p1);
t1.transform(p2);
agent.rotation.transform(p2);
t2.setTranslation(new Vector3d(0, agent.getHeight() * 2, agent.getRadius() * 10));
t2.transform(p2);
t1.lookAt(p2, p1, new Vector3d(0, 1, 0));
t1.invert();
viewTransformGroup.setTransform(t1);
break;
}
}
boolean checkCollisionAgainstBlockWorldObjects(BoundingSphere bs) {
return false;
}
/**
* Creates the world from the given environement Description. Used only in the
* creation phase.
*
* @param ed
* the environment description.
*/
private void create(EnvironmentDescription ed) {
worldSize = ed.worldSize;
createUniverse(ed);
}
/**
* Creates a representation of the 3 axis of the 3d world. Used only in the
* creation phase.
*/
private void createAxis() {
Point3f[] axisCoords = {
// X axis arrow
new Point3f(0.0f, 0.001f, 0.0f), new Point3f(1, 0.001f, 0.0f), new Point3f(1, 0.001f, 0.0f), new Point3f(0.95f, 0.001f, 0.05f), new Point3f(1, 0.001f, 0.0f),
new Point3f(0.95f, 0.001f, -0.05f),
// a small X
new Point3f(1.0f, 0.001f, 0.1f), new Point3f(0.9f, 0.001f, 0.2f), new Point3f(1.0f, 0.001f, 0.2f), new Point3f(0.9f, 0.001f, 0.1f),
// Z axis arrow
new Point3f(0.0f, 0.001f, 0.0f), new Point3f(0, 0.001f, 1.0f), new Point3f(0, 0.001f, 1.0f), new Point3f(0.05f, 0.001f, 0.95f), new Point3f(0, 0.001f, 1.0f),
new Point3f(-0.05f, 0.001f, 0.95f),
// a small Z
new Point3f(0.1f, 0.001f, 1.0f), new Point3f(0.2f, 0.001f, 1.0f), new Point3f(0.1f, 0.001f, 0.9f), new Point3f(0.2f, 0.001f, 0.9f), new Point3f(0.1f, 0.001f, 1.0f),
new Point3f(0.2f, 0.001f, 0.9f),
// Y axis arrow
new Point3f(0.0f, 0.001f, 0.0f), new Point3f(0, 1.0f, 0.0f), new Point3f(0, 1.0f, 0.0f), new Point3f(0.05f, 0.95f, 0f), new Point3f(0, 1f, 0f),
new Point3f(0.00f, 0.95f, 0.05f),
// a small Y
new Point3f(0.2f, 1f, 0.0f), new Point3f(0.1f, 0.9f, 0f), new Point3f(0.1f, 1.0f, 0.0f), new Point3f(0.15f, 0.95f, 0.0f)
};
// scale axis drawing to 5% of word size
for (int i = 0; i < axisCoords.length; i++) {
axisCoords[i].scale(worldSize * 0.05f);
}
LineArray axisLines = new LineArray(axisCoords.length, GeometryArray.COORDINATES);
axisLines.setCoordinates(0, axisCoords);
Appearance axisAppear = new Appearance();
ColoringAttributes ca = new ColoringAttributes();
ca.setColor(white);
axisAppear.setColoringAttributes(ca);
Material mat = new Material();
mat.setDiffuseColor(white);
axisAppear.setMaterial(mat);
Shape3D axis = new Shape3D(axisLines, axisAppear);
axis.setCollidable(false);
axis.setPickable(false);
sceneTrans.addChild(axis);
}
/**
* Creates the Canvas3D to visualize the 3D World. Used only in the creation
* phase.
*/
private void createCanvas3D() {
GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
template.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED);
GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getBestConfiguration(template);
// create canvas
canvas3d = new Canvas3D(config);
canvas3d.setDoubleBufferEnable(true);
// display j3d info
Map map = canvas3d.queryProperties();
System.out.println("doubleBufferAvailable = " + map.get("doubleBufferAvailable"));
System.out.println("sceneAntialiasingNumPasses = " + map.get("sceneAntialiasingNumPasses"));
System.out.println("sceneAntialiasingAvailable = " + map.get("sceneAntialiasingAvailable"));
System.out.println("texture3DAvailable = " + map.get("texture3DAvailable"));
}
/**
* Creates the floor of the 3d world. Used only in the creation phase.
*
* @param ed
* the environment description.
*/
private void createFloor(EnvironmentDescription wd) {
float minx = -worldSize / 2, maxx = worldSize / 2;
float minz = -worldSize / 2, maxz = worldSize / 2;
Point3f[] floorCoords = { new Point3f(minx, 0.0f, minz), new Point3f(minx, 0.0f, maxz), new Point3f(maxx, 0.0f, maxz), new Point3f(maxx, 0.0f, minz) };
Vector3f[] floorNormals = { new Vector3f(-0.6f, 0.6f, -0.6f), new Vector3f(-0.6f, 0.6f, 0.6f), new Vector3f(0.6f, 0.6f, 0.6f), new Vector3f(0.6f, 0.6f, -0.6f) };
Vector3f[] floorNormalsSimple = { new Vector3f(0, 1, 0), new Vector3f(0, 1, 0), new Vector3f(0, 1, 0), new Vector3f(0, 1, 0) };
QuadArray floorQuads = null;
switch (wd.normalsStyle) {
case EnvironmentDescription.NORMALS_REALISTIC:
floorQuads = new QuadArray(floorCoords.length, GeometryArray.COORDINATES | GeometryArray.NORMALS);
floorQuads.setNormals(0, floorNormals);
break;
case EnvironmentDescription.NORMALS_SIMPLE:
floorQuads = new QuadArray(floorCoords.length, GeometryArray.COORDINATES | GeometryArray.NORMALS);
floorQuads.setNormals(0, floorNormalsSimple);
break;
}
floorQuads.setCoordinates(0, floorCoords);
Appearance floorAppear = new Appearance();
Material mat = new Material();
mat.setDiffuseColor(wd.floorColor);
Color3f specular = new Color3f(wd.floorColor);
specular.scale(1.1f);
specular.clampMax(0.8f);
mat.setSpecularColor(specular);
floorAppear.setMaterial(mat);
Shape3D floor = new Shape3D(floorQuads, floorAppear);
floor.setPickable(false);
floor.setCollidable(false);
sceneTrans.addChild(floor);
}
/**
* Creates the branch for the visible content of the scenegraph. Used only in
* the creation phase.
*/
private void createSceneBranch(EnvironmentDescription wd) {
sceneBranch = new BranchGroup();
sceneRot = new TransformGroup();
sceneRot.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
sceneRot.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
sceneRot.setCapability(Group.ALLOW_CHILDREN_EXTEND);
// add transform
sceneTrans = new TransformGroup();
// allow transform access
sceneTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
sceneTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
sceneTrans.setCapability(Group.ALLOW_CHILDREN_EXTEND);
sceneBranch.addChild(sceneRot);
sceneRot.addChild(sceneTrans);
// bounds (lights,background, behaviors)
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), worldSize * 3);
// background
Color3f bgColor = wd.backgroundColor;
Background bgNode = new Background(bgColor);
bgNode.setApplicationBounds(bounds);
sceneTrans.addChild(bgNode);
// ambient light
TransformGroup tga = new TransformGroup();
AmbientLight ambientLight = new AmbientLight(wd.ambientLightColor);
ambientLight.setInfluencingBounds(bounds);
tga.addChild(ambientLight);
sceneBranch.addChild(tga);
// directional lights
light1 = addLight(wd.light1Position, wd.light1Color);
light2 = addLight(wd.light2Position, wd.light2Color);
light1.setEnable(wd.light1IsOn);
light2.setEnable(wd.light2IsOn);
createFloor(wd);
if (wd.hasAxis)
createAxis();
pickableSceneBranch = new BranchGroup();
sceneTrans.addChild(pickableSceneBranch);
pickableSceneBranch.setCapability(Group.ALLOW_CHILDREN_EXTEND);
pickableSceneBranch.setCapability(BranchGroup.ALLOW_DETACH);
pickableSceneBranch.setCapability(Group.ALLOW_CHILDREN_WRITE);
}
/**
* Creates the universe to attach the scenegraph. Used only in the creation
* phase.
*
* @param ed
* the environment description.
*/
private void createUniverse(EnvironmentDescription ed) {
System.out.println("create Universe");
// show infos
Map map = VirtualUniverse.getProperties();
System.out.println("----------------------------------------");
System.out.println("j3d.version = " + map.get("j3d.version"));
System.out.println("j3d.vendor = " + map.get("j3d.vendor"));
System.out.println("j3d.specification.version = " + map.get("j3d.specification.version"));
System.out.println("j3d.specification.vendor = " + map.get("j3d.specification.vendor"));
System.out.println("j3d.renderer = " + map.get("j3d.renderer"));
System.out.println("J3DThreadPriority = " + VirtualUniverse.getJ3DThreadPriority());
System.out.println("----------------------------------------");
createCanvas3D();
createSceneBranch(ed);
universe = new VirtualUniverse();
Locale locale = new Locale(universe);
// Create and add VIEW branch
// locale->viewBranch->viewTransformGroup->viewPlatform
viewBranch = new BranchGroup();
viewTransformGroup = new TransformGroup();
viewTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
viewTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Transform3D t3d = new Transform3D();
t3d.setIdentity();
viewTransformGroup.setTransform(t3d);
viewBranch.addChild(viewTransformGroup);
// Creates View and viewplatform
viewPlatform = new ViewPlatform();
viewPlatform.setViewAttachPolicy(View.NOMINAL_HEAD);
viewPlatform.setActivationRadius(100);
view = new View();
view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);
view.setViewPolicy(View.SCREEN_VIEW);
view.setVisibilityPolicy(View.VISIBILITY_DRAW_ALL);
view.setFrontClipDistance(0.02);
GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
template.setSceneAntialiasing(GraphicsConfigTemplate.REQUIRED);
template.setDoubleBuffer(GraphicsConfigTemplate.PREFERRED);
/*
* GraphicsConfiguration config = GraphicsEnvironment
* .getLocalGraphicsEnvironment().getDefaultScreenDevice()
* .getBestConfiguration(template);
*/
// request antialiasing
view.setSceneAntialiasingEnable(true);
view.addCanvas3D(canvas3d);
PhysicalBody phyBody = new PhysicalBody();
PhysicalEnvironment phyEnv = new PhysicalEnvironment();
view.setPhysicalBody(phyBody);
view.setPhysicalEnvironment(phyEnv);
view.attachViewPlatform(viewPlatform);
viewTransformGroup.addChild(viewPlatform);
// Add both branch to the unique locale
locale.addBranchGraph(viewBranch);
locale.addBranchGraph(sceneBranch);
// Add mouse control in the canvas3d
mouseOrbiter = new MouseOrbiter(canvas3d, viewTransformGroup);
// sets initial viewpoint
changeViewPoint(ed.worldViewPoint, null);
}
/** Detach a previously attached object from the scenegraph. */
public void detach(BaseObject obj3d) {
pickableSceneBranch.removeChild(obj3d.getNode());
}
/** Destroy the java3d graph */
public void dispose() {
universe.removeAllLocales();
view.removeAllCanvas3Ds();
}
/**
* @return the canvas3D associated to the world view platform
*/
public Canvas3D getCanvas3D() {
return canvas3d;
}
/**
* @return the scene branchgroup containing the world's object which can be
* picked
*/
BranchGroup getPickableSceneBranch() {
return pickableSceneBranch;
}
/** Do one rendering on main canvas 3D . Used for background mode. */
public void renderOnce() {
canvas3d.startRenderer();
try {
Thread.sleep((100));
} catch (InterruptedException e) {
e.printStackTrace();
}
canvas3d.stopRenderer();
}
/** Restart rendering on main canvas 3D . Used for background mode. */
public void startRendering() {
canvas3d.startRenderer();
}
/** Stop rendering on main canvas 3D . Used for background mode. */
public void stopRendering() {
canvas3d.stopRenderer();
}
}