package me.drton.jmavsim; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; import javax.media.j3d.*; import javax.swing.*; import javax.vecmath.*; import java.awt.*; import java.util.Enumeration; /** * 3D Visualizer, works in own thread, synchronized with "world" thread. */ public class Visualizer3D extends JFrame { private static Color3f white = new Color3f(1.0f, 1.0f, 1.0f); private final World world; private SimpleUniverse universe; private BoundingSphere sceneBounds = new BoundingSphere(new Point3d(0, 0, 0), 100000.0); private Vector3d viewerPosition = new Vector3d(0.0, 0.0, 0.0); private Vector3d viewerPositionOffset = new Vector3d(0.0, 0.0, 0.0); private Transform3D viewerTransform = new Transform3D(); private TransformGroup viewerTransformGroup; private KinematicObject viewerTargetObject; private KinematicObject viewerPositionObject; public Visualizer3D(World world) { this.world = world; setSize(640, 480); setDefaultCloseOperation(EXIT_ON_CLOSE); GraphicsConfiguration gc = SimpleUniverse.getPreferredConfiguration(); Canvas3D canvas = new Canvas3D(gc); getContentPane().add(canvas); universe = new SimpleUniverse(canvas); universe.getViewer().getView().setBackClipDistance(100000.0); viewerTransformGroup = universe.getViewingPlatform().getViewPlatformTransform(); createEnvironment(); for (WorldObject object : world.getObjects()) { if (object instanceof KinematicObject) { BranchGroup bg = ((KinematicObject) object).getBranchGroup(); if (bg != null) { universe.addBranchGraph(bg); } } } setVisible(true); Matrix3d mat = new Matrix3d(); Matrix3d mat1 = new Matrix3d(); mat.rotZ(Math.PI); mat1.rotY(Math.PI / 2); mat.mul(mat1); mat1.rotZ(-Math.PI / 2); mat.mul(mat1); viewerTransform.setRotation(mat); } /** * Target object to point camera, has effect only if viewerPositionObject is not set. * * @param object */ public void setViewerTargetObject(KinematicObject object) { this.viewerTargetObject = object; } /** * Object to place camera on, if nullptr then camera will be placed in fixed point set by setViewerPosition(). * * @param object */ public void setViewerPositionObject(KinematicObject object) { this.viewerPositionObject = object; } /** * Fixed camera position, has effect only if viewerPositionObject not set. * * @param position */ public void setViewerPosition(Vector3d position) { this.viewerPositionObject = null; this.viewerPosition = position; viewerTransform.setTranslation(viewerPosition); } /** * Camera position offset from object position when viewer placed on some object * * @param offset position offset */ public void setViewerPositionOffset(Vector3d offset) { this.viewerPositionOffset = offset; } private void createEnvironment() { BranchGroup group = new BranchGroup(); // Sky BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 1000.0); Background bg = new Background(); bg.setApplicationBounds(bounds); BranchGroup backGeoBranch = new BranchGroup(); Sphere skySphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS | Sphere.GENERATE_NORMALS_INWARD | Sphere.GENERATE_TEXTURE_COORDS, 32); // Sphere.GENERATE_NORMALS | Sphere.GENERATE_NORMALS_INWARD | Sphere.GENERATE_TEXTURE_COORDS, 32); Texture texSky = new TextureLoader("environment/sky.jpg", null).getTexture(); skySphere.getAppearance().setTexture(texSky); Transform3D transformSky = new Transform3D(); //transformSky.setTranslation(new Vector3d(0.0, 0.0, -0.5)); Matrix3d rot = new Matrix3d(); rot.rotX(Math.PI / 2); transformSky.setRotation(rot); TransformGroup tgSky = new TransformGroup(transformSky); tgSky.addChild(skySphere); backGeoBranch.addChild(tgSky); bg.setGeometry(backGeoBranch); group.addChild(bg); //group.addChild(tgSky); // Ground QuadArray polygon1 = new QuadArray(4, QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2); polygon1.setCoordinate(0, new Point3f(-1000f, 1000f, 0f)); polygon1.setCoordinate(1, new Point3f(1000f, 1000f, 0f)); polygon1.setCoordinate(2, new Point3f(1000f, -1000f, 0f)); polygon1.setCoordinate(3, new Point3f(-1000f, -1000f, 0f)); polygon1.setTextureCoordinate(0, 0, new TexCoord2f(0.0f, 0.0f)); polygon1.setTextureCoordinate(0, 1, new TexCoord2f(10.0f, 0.0f)); polygon1.setTextureCoordinate(0, 2, new TexCoord2f(10.0f, 10.0f)); polygon1.setTextureCoordinate(0, 3, new TexCoord2f(0.0f, 10.0f)); Texture texGround = new TextureLoader("environment/grass2.jpg", null).getTexture(); Appearance apGround = new Appearance(); apGround.setTexture(texGround); Shape3D ground = new Shape3D(polygon1, apGround); Transform3D transformGround = new Transform3D(); transformGround.setTranslation( new Vector3d(0.0, 0.0, 0.005 + world.getEnvironment().getGroundLevel(new Vector3d(0.0, 0.0, 0.0)))); TransformGroup tgGround = new TransformGroup(transformGround); tgGround.addChild(ground); group.addChild(tgGround); // Light DirectionalLight light1 = new DirectionalLight(white, new Vector3f(4.0f, 7.0f, 12.0f)); light1.setInfluencingBounds(sceneBounds); group.addChild(light1); AmbientLight light2 = new AmbientLight(new Color3f(0.5f, 0.5f, 0.5f)); light2.setInfluencingBounds(sceneBounds); group.addChild(light2); // Update behavior Behavior b = new UpdateBehavior(); b.setSchedulingBounds(bounds); group.addChild(b); universe.addBranchGraph(group); } private void updateVisualizer() { synchronized (world) { // Synchronize with "world" thread // Update branch groups of all kinematic objects for (WorldObject object : world.getObjects()) { if (object instanceof KinematicObject) { BranchGroup bg = ((KinematicObject) object).getBranchGroup(); if (bg != null) { ((KinematicObject) object).updateBranchGroup(); } } } // Update view platform if (viewerPositionObject != null) { // Camera on object viewerPosition.set(viewerPositionOffset); viewerPositionObject.getRotation().transform(viewerPosition); viewerPosition.add(viewerPositionObject.getPosition()); viewerTransform.setTranslation(viewerPosition); Matrix3d mat = new Matrix3d(); Matrix3d mat1 = new Matrix3d(); mat.set(viewerPositionObject.getRotation()); mat1.rotZ(Math.PI / 2); mat.mul(mat1); mat1.rotX(-Math.PI / 2); mat.mul(mat1); viewerTransform.setRotation(mat); } else { // Fixed camera if (viewerTargetObject != null) { // Point camera to target Vector3d pos = viewerTargetObject.getPosition(); Vector3d dist = new Vector3d(); dist.sub(pos, viewerPosition); Matrix3d mat = new Matrix3d(); Matrix3d mat1 = new Matrix3d(); mat.rotZ(Math.PI); mat1.rotY(Math.PI / 2); mat.mul(mat1); mat1.rotZ(-Math.PI / 2); mat.mul(mat1); mat1.rotY(-Math.atan2(pos.y - viewerPosition.y, pos.x - viewerPosition.x)); mat.mul(mat1); mat1.rotX(-Math.asin((pos.z - viewerPosition.z) / dist.length())); mat.mul(mat1); viewerTransform.setRotation(mat); } } viewerTransformGroup.setTransform(viewerTransform); } } class UpdateBehavior extends Behavior { private WakeupCondition condition = new WakeupOnElapsedFrames(0, false); @Override public void initialize() { wakeupOn(condition); } @Override public void processStimulus(Enumeration wakeup) { Object w; while (wakeup.hasMoreElements()) { w = wakeup.nextElement(); if (w instanceof WakeupOnElapsedFrames) { updateVisualizer(); } wakeupOn(condition); } } } }