+1 (972) 883-2091
ECSS 4.220, 800 W Campbell Rd, Richardson, TX 75083–0688, US

The “vo” package implements the 3D visualized objects. It contains the following classes:

  1. VehicleAgentVO
  2. Skybox
  3. EnvObjectFactory
  4. EventFactory
  5. TrafficEventVO
  6. TrafficEnvObjectVO

1. Implementing the “VehicleAgentVO” class

This class describes the 3D visualization properties of a vehicle agent.

Implementation Steps:

  1. 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”.
  2. The “VehicleAgentVO” class provides concrete implementations for the following abstract methods:
    1. “getModelRatio”: This method is used to adjust the scale of the original 3D vehicle model to fit the traffic 3D visualizer.
    2. “setupAgentModel”: This method is used to initialize the vehicle 3D model visualization properties.
    3. “updateState”: This method is invoked at each simulation cycle to update the state of the 3D visualized object.
    4. “updateVisibleProperties”: This method is used to update the visualized object visible properties (e.g., posture, vision cone).
    5. “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:

  1. Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “Skybox”.
  2. 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:

  1. Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “EnvObjectFactory”.
  2. 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:

  1. Inside the package “edu.utdallas.mavs.traffic.visualization.vis3D.vo”, create a class named “EventFactory”.
  2. 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:

  1. 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”.
  2. 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:

  1. 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”.
  2. 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);
	}
}