/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.render;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import javax.imageio.ImageIO;
import com.jaamsim.DisplayModels.ColladaModel;
import com.jaamsim.DisplayModels.DisplayModel;
import com.jaamsim.DisplayModels.ImageModel;
import com.jaamsim.DisplayModels.ShapeModel;
import com.jaamsim.Graphics.DisplayEntity;
import com.jaamsim.controllers.RenderManager;
import com.jaamsim.math.Quaternion;
import com.jaamsim.math.Transform;
import com.jaamsim.math.Vec3d;
import com.jaamsim.math.Vec4d;
import com.jaamsim.ui.GUIFrame;
import com.jaamsim.ui.View;
public class PreviewCache {
private HashMap<DisplayModel, Future<BufferedImage>> _imageCache;
private DisplayEntity dummyEntity;
public PreviewCache() {
_imageCache = new HashMap<>();
if (GUIFrame.getInstance().getSimState() != GUIFrame.SIM_STATE_RUNNING) {
dummyEntity = new DisplayEntity();
dummyEntity.kill();
}
}
public void clear() {
_imageCache.clear();
}
/**
* Get the preview image for this, optionally a Runnable can be passed in that will be run when this image is ready
* @param ot
* @param notifier
* @return
*/
public Future<BufferedImage> getPreview(DisplayModel dm, Runnable notifier) {
synchronized (_imageCache) {
Future<BufferedImage> cached = _imageCache.get(dm);
if (cached != null) {
return cached;
}
// Fast path out for ImageModels
if (dm instanceof ImageModel) {
ImageModel im = (ImageModel)dm;
URI file = im.getImageFile();
Future<BufferedImage> ret = new Future<>(null);
try {
URL imageURL = file.toURL();
BufferedImage image = ImageIO.read(imageURL);
// For some weird reason, the resizing that may happen to this image fails silently
// for other color types, so we'll just hard convert it here
BufferedImage colored = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = colored.createGraphics();
g2.drawImage(image, new AffineTransform(), null);
g2.dispose();
ret.setComplete(colored);
} catch (Exception ex) {
ret.setFailed("Bad image");
}
_imageCache.put(dm, ret);
return ret;
}
// Otherwise we need to load it....
ArrayList<RenderProxy> proxies = new ArrayList<>();
// Collect the render proxies for a dummy version of this DisplayModel,
// This will all need to be refactored soonish.
if (dummyEntity == null) {
if (GUIFrame.getInstance().getSimState() != GUIFrame.SIM_STATE_RUNNING) {
dummyEntity = new DisplayEntity();
dummyEntity.kill();
} else {
// The simulation is running so we can't make the dummy entity
Future<BufferedImage> ret = new Future<>(null);
ret.setFailed("Simulation running");
return ret;
}
}
if (dm == null || !dm.canDisplayEntity(dummyEntity)) {
Future<BufferedImage> ret = new Future<>(null);
ret.setFailed("Cannot render preview");
return ret;
}
// For ColladaModels maintain the proportions of the imported model
if (dm instanceof ColladaModel) {
ColladaModel cm = (ColladaModel)dm;
MeshProtoKey key = ColladaModel.getCachedMeshKey(cm.getColladaFile());
Vec3d meshSize = new Vec3d(RenderManager.inst().getMeshBounds(key, true).radius);
double maxDim = Math.max(Math.max(meshSize.x, meshSize.y), meshSize.z);
meshSize.scale3(1/maxDim);
dummyEntity.setSize(meshSize);
}
dm.getBinding(dummyEntity).collectProxies(0, proxies);
boolean isFlat = true;
if (dm instanceof ShapeModel) {
isFlat = true;
}
if (dm instanceof ColladaModel) {
isFlat = false;
}
Transform camTrans;
if (!isFlat) {
// If this model is 3D, switch to an isometric view
Quaternion cameraRot = new Quaternion();
Quaternion tmp = new Quaternion();
tmp.setRotXAxis(Math.PI / 2);
cameraRot.mult(tmp, cameraRot);
tmp.setRotZAxis(3*Math.PI / 4);
cameraRot.mult(tmp, cameraRot);
tmp.setAxisAngle(new Vec3d(1.0d, -1.0d, 0.0d), Math.PI / 5);
cameraRot.mult(tmp, cameraRot);
camTrans = new Transform(new Vec4d(1.2, 1.2, 1.2, 1.0d), cameraRot, 1);
} else {
camTrans = new Transform(new Vec4d(0, 0, 1.2, 1.0d));
}
CameraInfo camInfo = new CameraInfo(Math.PI/3, camTrans, null);
Future<BufferedImage> fi = RenderManager.inst().renderOffscreen(proxies, camInfo, View.OMNI_VIEW_ID, 180, 180, notifier);
_imageCache.put(dm, fi);
return fi;
}
}
}