The “vo” package implements the 3D visualized objects. It contains the following classes:
- VehicleAgentVO
- Skybox
- EnvObjectFactory
- EventFactory
- TrafficEventVO
- TrafficEnvObjectVO
1. Implementing the “VehicleAgentVO” class
This class describes the 3D visualization properties of a vehicle agent.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “VehicleAgentVO” that extends the class “AgentVO” defined in the package “edu.utdallas.mavs.divas.visualization.vis3D.vo”.
- The “VehicleAgentVO” class provides concrete implementations for the following abstract methods:
- “getModelRatio”: This method is used to adjust the scale of the original 3D vehicle model to fit the traffic 3D visualizer.
- “setupAgentModel”: This method is used to initialize the vehicle 3D model visualization properties.
- “updateState”: This method is invoked at each simulation cycle to update the state of the 3D visualized object.
- “updateVisibleProperties”: This method is used to update the visualized object visible properties (e.g., posture, vision cone).
- “setupAnimation”: This method is used to create and set the animation channels for the animated vehicle’s 3D model.
The following code provides a concrete implementation for the “VehicleAgentVO” class.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import com.jme3.animation.AnimControl; import com.jme3.light.AmbientLight; import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import edu.utdallas.mavs.divas.visualization.vis3D.Visualizer3DApplication; import edu.utdallas.mavs.divas.visualization.vis3D.utils.VisToolbox; import edu.utdallas.mavs.divas.visualization.vis3D.vo.AgentVO; import edu.utdallas.mavs.traffic.simulation.sim.common.state.Posture; import edu.utdallas.mavs.traffic.simulation.sim.common.state.VehicleAgentState; /** * This class describes a visualized agent */ public class VehicleAgentVO extends AgentVO<VehicleAgentState> { /** * A ratio constant to correct the mismatch between the 3D models and the simulation entity */ private static final float MODEL_RATIO = 0.12f; /** * The directory where the vehicles 3D models are stored in the DIVAs framework */ private static final String VEHICLE_MODELS_DIR = "traffic/vehicle/"; ColorRGBA agentMarkColor; private Posture prevPosture = null; static Quaternion rot90 = new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y); /** * Creates a new agent VO * * @param state * the agent state to be associated with this VO * @param cycle * the simulation cycle number associated with the agent state */ public VehicleAgentVO(VehicleAgentState state, long cycle) { super(state, cycle); AmbientLight agentLight = VisToolbox.createAmbientLight(ColorRGBA.White); addLight(agentLight); } @Override protected float getModelRatio() { return MODEL_RATIO; } @Override protected void setupAgentModel() { String agentPath = new String(); agentPath = VEHICLE_MODELS_DIR + state.getModelName() + ".mesh.xml"; agentModel = getAssetManager().loadModel(agentPath); } @Override protected void updateState() { rotation = new Quaternion(); rotation.lookAt(state.getHeading(), Vector3f.UNIT_Y); updateState(state.getPosition(), rotation, state.getScale()); // updates VO's visible properties updateVisibleProperties(); } /** * Updates this VO's visible properties (e.g., posture, vision cone) */ @Override protected void updateVisibleProperties() { if(prevFov != state.getFOV() || prevVisibleDistance != state.getVisibleDistance()) { if(visionConeEnabled) { detachVisionCone(); attachVisionCone(); } } prevFov = state.getFOV(); prevVisibleDistance = state.getVisibleDistance(); if(prevPosture == null) { prevPosture = state.getPosture(); } if(!prevPosture.equals(state.getPosture())) { if(Visualizer3DApplication.getVisConfig().animatedModels) { channel.setAnim(state.getPosture().toString()); prevPosture = state.getPosture(); } } } @Override protected void setupAnimation() { if(Visualizer3DApplication.getVisConfig().animatedModels) { control = agentModel.getControl(AnimControl.class); channel = control.createChannel(); channel.setAnim(Posture.forward_fast.toString()); } } }
2. Implementing the “Skybox” class
A skybox is a method of creating backgrounds to make the 3D scene looks bigger than it really is. The class “SkyBox” implements the skybox visualization in the 3D visualizer.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “Skybox”.
- The implementation of this class describes the creation of sky boxes and the mechanism to attach/deattach a skybox to/from the 3D visualization scene. The following code provides the full implementation of the “Skybox” class given the available texture files in DIVAs.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import java.util.concurrent.Callable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jme3.app.Application; import com.jme3.asset.AssetManager; import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.texture.Texture; import com.jme3.util.SkyFactory; import edu.utdallas.mavs.divas.core.config.VisConfig; import edu.utdallas.mavs.divas.visualization.vis3D.BaseApplication; import edu.utdallas.mavs.divas.visualization.vis3D.Visualizer3DApplication; import edu.utdallas.mavs.traffic.simulation.config.TrafficVisConfig; import edu.utdallas.mavs.traffic.simulation.config.SkyboxEnum; /** * This class describes the skybox of the 3D visualization. */ public class Skybox { private final static Logger logger = LoggerFactory.getLogger(Skybox.class); private static Spatial sky; private Skybox() {} /** * Unloads the skybox in the visualization */ public static void unloadSky() { try { Visualizer3DApplication.getInstance().getApp().enqueue(new Callable<Object>() { @Override public Object call() throws Exception { dettachSky(); return null; } }); } catch(Exception e) { e.printStackTrace(); } } private static void dettachSky() { Visualizer3DApplication.getInstance().getApp().getRootNode().detachChild(sky); } /** * Loads the skybox in the visualization */ public static void loadSky() { try { Visualizer3DApplication.getInstance().getApp().enqueue(new Callable<Object>() { @Override public Object call() throws Exception { updateSky(); return null; } }); } catch(Exception e) { e.printStackTrace(); } } private static void updateSky() { Application app = Visualizer3DApplication.getInstance().getApp(); AssetManager assetManager = app.getAssetManager(); SkyboxEnum skyboxName = VisConfig.getInstance().getCustomProperty(TrafficVisConfig.SKYBOX); String skyboxPath = String.format("skybox/%s/", skyboxName.toString()); try { if(skyboxName.equals(SkyboxEnum.Sunny) || skyboxName.equals(SkyboxEnum.Sunny_Nature) || skyboxName.equals(SkyboxEnum.Cloudy) || skyboxName.equals(SkyboxEnum.Dusk) || skyboxName.equals(SkyboxEnum.Night_Red) || skyboxName.equals(SkyboxEnum.Night_Stars) || skyboxName.equals(SkyboxEnum.Rise) || skyboxName.equals(SkyboxEnum.Sunny_Sea) || skyboxName.equals(SkyboxEnum.Sunset)) { Texture west = assetManager.loadTexture(String.format("%sW.jpg", skyboxPath)); Texture east = assetManager.loadTexture(String.format("%sE.jpg", skyboxPath)); Texture north = assetManager.loadTexture(String.format("%sN.jpg", skyboxPath)); Texture south = assetManager.loadTexture(String.format("%sS.jpg", skyboxPath)); Texture up = assetManager.loadTexture(String.format("%sU.jpg", skyboxPath)); Texture down = assetManager.loadTexture(String.format("%sD.jpg", skyboxPath)); sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); } else { Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); } if(sky != null) { sky.setQueueBucket(Bucket.Sky); Node node = ((BaseApplication<?,?>) app).getRootNode(); node.attachChild(sky); } } catch(Exception e) { logger.error("Error occured when loading the skybox."); } } }
3. Implementing the “EnvObjectFactory” class
The “EnvObjectFactory” is a factory class that is responsible for creating 3D visualizations of simulated environment objects.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “EnvObjectFactory”.
- The class implementation describes the creation of visualized object “VO” for an environment object. The following code provides the full implementation of the class “EnvbjectFactory”.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import com.jme3.math.Vector3f; import com.jme3.scene.Spatial; import edu.utdallas.mavs.divas.core.config.VisConfig; import edu.utdallas.mavs.divas.core.sim.common.state.EnvObjectState; import edu.utdallas.mavs.divas.visualization.vis3D.Visualizer3DApplication; import edu.utdallas.mavs.traffic.simulation.config.TrafficVisConfig; import edu.utdallas.mavs.traffic.simulation.config.SkyboxEnum; /** * This class describes a factory for visualized environment objects. */ public class EnvObjectFactory { /** * This is a factory method for visualized environment objects. * * @param state * an environment object state to be factored into a visualized event * @return a spatial associated with the newly created visualized environment object */ public static Spatial createEnvObjectVO(EnvObjectState state) { Spatial envObjModel; SkyboxEnum skybox = VisConfig.getInstance().getCustomProperty(TrafficVisConfig.SKYBOX); if(skybox.equals(SkyboxEnum.Night_Stars) || VisConfig.getInstance().getCustomProperty(TrafficVisConfig.SKYBOX).equals(SkyboxEnum.Night_Red)) { envObjModel = Visualizer3DApplication.getInstance().getAssetManager().loadModel(state.getModelName().replace(".j3o", "_night.j3o")); } else { envObjModel = Visualizer3DApplication.getInstance().getAssetManager().loadModel(state.getModelName()); } return envObjModel; } /** * @param modelName * The model to get local scale for * @param x * Scale X * @param y * Scale Y * @param z * Scale Z * @return The required local scale for the model */ public static Vector3f getLocalScale(String modelName, float x, float y, float z) { if(modelName.equals("objects/Tree.mesh.j3o")) { return new Vector3f(x * 4, y * 4, z * 4); } return new Vector3f(x, y, z); } }
4. Implementing the “EventFactory” class
The “EventFactory” is a factory class that is responsible for creating 3D visualization of environment events.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “EventFactory”.
- The class implementation describes the creation of 3D visulazations for environment events. The following code provides the full implementation of this class.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import com.jme3.scene.Spatial; import edu.utdallas.mavs.divas.core.sim.common.event.EnvEvent; /** * This class describes a factory for visualized events. */ public class EventFactory { /** * This is a factory method for visualized events. * * @param event * an event to be factored into a visualized event * @return a spatial associated with the newly created visualized event */ public static Spatial createEventVO(EnvEvent event) { Spatial vo = null; return vo; } }
5. Implementing the “TrafficEventVO” class
This class represents the 3D visualization of an environment event.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “TrafficEventVO” that extends the class “EventVO” defined in the package “edu.utdallas.mavs.divas.visualization.vis3D.vo”.
- The class implementation describes the 3D visualized representation of an environment event. The following code provides the full implementation of this class.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import com.jme3.scene.Spatial; import edu.utdallas.mavs.divas.core.sim.common.event.EnvEvent; import edu.utdallas.mavs.divas.visualization.vis3D.vo.EventVO; /** * This class represents a visualized event */ public class TrafficEventVO extends EventVO { /** * Cosntructs a new event VO * * @param event the event state associated with this VO * @param cycle the simulation cycle number associated with this event */ public TrafficEventVO(EnvEvent event, long cycle) { super(event, cycle); } @Override protected Spatial createEventVO(EnvEvent event) { return EventFactory.createEventVO(event); } }
6. Implementing the “TrafficEnvObjectVO” class
This class represents the 3D visualization of an environment object.
Implementation Steps:
- Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “TrafficEnvObjectVO” that extends the class “EnvObjectVO” defined in the package “edu.utdallas.mavs.divas.visualization.vis3D.vo”.
- The class implementation describes the 3D visualized representation of an environment object. The following code provides the full implementation of this class.
package edu.utdallas.mavs.traffic.visualization.vis3D.vo; import java.util.ArrayList; import java.util.List; import com.jme3.bounding.BoundingBox; import com.jme3.light.AmbientLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; import edu.utdallas.mavs.divas.core.sim.common.state.EnvObjectState; import edu.utdallas.mavs.divas.visualization.vis3D.Visualizer3DApplication; import edu.utdallas.mavs.divas.visualization.vis3D.utils.VisToolbox; import edu.utdallas.mavs.divas.visualization.vis3D.vo.EnvObjectVO; /** * This class describes a visualized environment object */ public class TrafficEnvObjectVO extends EnvObjectVO { /** * A list conttains the list of environemnet object models names */ protected static List<String> modifiedBoundingBoxList = new ArrayList<>(); /** * @param state * the VO's environment object state * @param cycle * the cycle of the simulation associated with the environment * object state */ public TrafficEnvObjectVO(EnvObjectState state, long cycle) { super(state, cycle); } /** * Constructs an empty interpolated VO To be called only for cloning * purposes. Not to be called if the VO is to be actually created in the * visualization. * * @param state * the VO's environment object state */ public TrafficEnvObjectVO(EnvObjectState state) { super(state); } @Override public void updateState() { /* * update position and rotation (handled by * InterpolatedVO.updateState(vector, quaternion) to enable smooth * motion) */ updateState(state.getPosition(), state.getRotation(), state.getScale()); } /** * Attaches this model geometric form */ @Override protected void attachModel() { setLocalTranslation(state.getPosition()); if (state.getType().equals("floor")) { isLocked = true; } // Attach 3D Models if (state.getType().equals("3DModel")) { attachEnvObjectModel(); } else { attachGeometry(); } } /** * Attaches a 3D model representing this VO */ protected void attachEnvObjectModel() { envObjModel = EnvObjectFactory.createEnvObjectVO(state); float stateX = 1; float stateY = 1; float stateZ = 1; float modelX = ((BoundingBox) envObjModel.getWorldBound()).getXExtent(); float modelY = ((BoundingBox) envObjModel.getWorldBound()).getYExtent(); float modelZ = ((BoundingBox) envObjModel.getWorldBound()).getZExtent(); float x = stateX / modelX; float y = stateY / modelY; float z = stateZ / modelZ; Vector3f localScale = EnvObjectFactory.getLocalScale( state.getModelName(), x, y, z); // If the model is attached before there is no need to update the // bounding box because the update is cached if (!modifiedBoundingBoxList.contains(state.getModelName())) { BoundingBox bb = (BoundingBox) envObjModel.getWorldBound(); modifiedBoundingBoxList.add(state.getModelName()); bb.setYExtent(bb.getYExtent() * 2); envObjModel.setModelBound(bb); envObjModel.setLocalScale(localScale); } // If the model is cached then you should multiply the Y scale by 2, // otherwise the Y access will be the half else { envObjModel.setLocalScale(localScale.getX(), localScale.getY() * 2, localScale.getZ()); } if (Visualizer3DApplication.getInstance().getVisRootNode() != null) { attachChild(envObjModel); } } /** * Attaches a textured geometry representing this VO */ protected void attachGeometry() { Box box = new Box(new Vector3f(0, 0, 0), 1, 1, 1); Geometry geometry = new Geometry("Box", box); Material material = new Material(getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); try { Texture texture = getAssetManager().loadTexture( TEXTURES_DIR + state.getMaterial() + ".jpg"); if (state.getType().equals("wall")) { texture.setWrap(WrapMode.Repeat); if (state.getScale().getZ() > 5) { box.scaleTextureCoordinates(new Vector2f(state.getScale() .getZ() / 2, 2.5f)); } else { box.scaleTextureCoordinates(new Vector2f(state.getScale() .getX() / 2, 2.5f)); } } material.setTexture("DiffuseMap", texture); } catch (Exception e) { if (state.getType().equals("door")) { Texture tex1 = getAssetManager().loadTexture( TEXTURES_DIR + "plasterwall.jpg"); material.setTexture("DiffuseMap", tex1); AmbientLight doorLight = VisToolbox .createAmbientLight(ColorRGBA.White.mult(2f)); addLight(doorLight); } else { material = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); material.setColor("Color", ColorRGBA.randomColor()); } } geometry.setMaterial(material); attachChild(geometry); } @Override protected EnvObjectVO createClone(EnvObjectState clone) { return new TrafficEnvObjectVO(clone); } }