/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
* Copyright (C) 2015 JaamSim Software 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.util.ArrayList;
import com.jaamsim.MeshFiles.MeshData;
import com.jaamsim.math.AABB;
import com.jaamsim.math.ConvexHull;
import com.jaamsim.math.Mat4d;
import com.jaamsim.math.MathUtils;
import com.jaamsim.math.Ray;
import com.jaamsim.math.Transform;
import com.jaamsim.math.Vec3d;
import com.jaamsim.math.Vec4d;
public class Mesh implements Renderable {
private final MeshProto _proto;
private final Transform _trans;
private final Vec3d _scale; // Allow for a non-uniform scale
private final long _pickingID;
private final VisibilityInfo _visInfo;
private final ConvexHull _hull;
private final AABB _bounds;
private final Mat4d _modelMat;
private final Mat4d _invModelMat;
//private final ArrayList<Action.Queue> _actions;
private final MeshData.Pose pose;
private HullProto debugHull = null;
public Mesh(MeshProto proto, Transform trans, Vec3d scale,
ArrayList<Action.Queue> actions, VisibilityInfo visInfo, long pickingID) {
_trans = new Transform(trans);
_proto = proto;
_scale = new Vec3d(scale);
_visInfo = visInfo;
_modelMat = RenderUtils.mergeTransAndScale(_trans, _scale);
_invModelMat = RenderUtils.getInverseWithScale(_trans, _scale);
pose = _proto.getPose(actions);
_hull = _proto.getHull(pose);
_bounds = _hull.getAABB(_modelMat);
_pickingID = pickingID;
}
@Override
public AABB getBoundsRef() {
return _bounds;
}
@Override
public void render(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
_proto.render(contextID, renderer, _modelMat, _invModelMat, cam, pose);
}
@Override
public long getPickingID() {
return _pickingID;
}
@Override
public double getCollisionDist(Ray r, boolean precise)
{
double boundsDist = _bounds.collisionDist(r);
if (boundsDist < 0) {
return boundsDist;
}
double roughCollision = _hull.collisionDistance(r, _trans, _scale);
if (!precise || roughCollision < 0) {
// This is either a rough collision, or we missed outright
return roughCollision;
}
double shortDistance = Double.POSITIVE_INFINITY;
MeshData data = _proto.getRawData();
// Check against all sub meshes
for (MeshData.StaticMeshInstance subInst : data.getStaticMeshInstances()) {
MeshData.SubMeshData subData = data.getSubMeshData().get(subInst.subMeshIndex);
Mat4d invModelMat = new Mat4d();
invModelMat.mult4(subInst.invTrans, _invModelMat);
Mat4d fullModelMat = new Mat4d();
fullModelMat.mult4(_modelMat, subInst.transform);
double subDist = collideSubMesh(subData, r, fullModelMat, invModelMat);
if (subDist < shortDistance) {
shortDistance = subDist;
}
}
for (MeshData.AnimMeshInstance animInst : data.getAnimMeshInstances()) {
MeshData.SubMeshData subData = data.getSubMeshData().get(animInst.meshIndex);
Mat4d invModelMat = new Mat4d();
invModelMat.mult4(pose.invTransforms[animInst.nodeIndex], _invModelMat);
Mat4d fullModelMat = new Mat4d();
fullModelMat.mult4(_modelMat, pose.transforms[animInst.nodeIndex]);
double subDist = collideSubMesh(subData, r, fullModelMat, invModelMat);
if (subDist < shortDistance) {
shortDistance = subDist;
}
}
// Now check against line components
for (MeshData.StaticLineInstance subInst : data.getStaticLineInstances()) {
MeshData.SubLineData subData = data.getSubLineData().get(subInst.lineIndex);
Mat4d invModelMat = new Mat4d();
invModelMat.mult4(subInst.invTrans, _invModelMat);
Mat4d fullModelMat = new Mat4d();
fullModelMat.mult4(_modelMat, subInst.transform);
double lineDist = collideSubLine(subData, r, fullModelMat, invModelMat);
if (lineDist > 0 && lineDist < shortDistance) {
shortDistance = lineDist;
}
}
for (MeshData.AnimLineInstance animInst : data.getAnimLineInstances()) {
MeshData.SubLineData subData = data.getSubLineData().get(animInst.lineIndex);
Mat4d invModelMat = new Mat4d();
invModelMat.mult4(pose.invTransforms[animInst.nodeIndex], _invModelMat);
Mat4d fullModelMat = new Mat4d();
fullModelMat.mult4(_modelMat, pose.transforms[animInst.nodeIndex]);
double lineDist = collideSubLine(subData, r, fullModelMat, invModelMat);
if (lineDist > 0 && lineDist < shortDistance) {
shortDistance = lineDist;
}
}
if (shortDistance == Double.POSITIVE_INFINITY) {
return -1; // We did not actually collide with anything
}
return shortDistance;
}
private double collideSubMesh(MeshData.SubMeshData subData, Ray r, Mat4d modelMat, Mat4d invModelMat) {
Ray localRay = r.transform(invModelMat);
// Rough collision to AABB
if (subData.localBounds.collisionDist(localRay) < 0) {
return Double.POSITIVE_INFINITY;
}
ConvexHull subDataHull = subData.staticHull;
double subDist = subDataHull.collisionDistanceByMatrix(r, modelMat, invModelMat);
if (subDist < 0) {
return Double.POSITIVE_INFINITY;
}
// We have hit both the AABB and the convex hull for this sub instance, now do individual triangle collision
Vec3d[] triVecs = new Vec3d[3];
double shortDist = Double.POSITIVE_INFINITY;
for (int triInd = 0; triInd < subData.indices.length / 3; ++triInd) {
triVecs[0] = subData.verts.get(subData.indices[triInd*3+0]);
triVecs[1] = subData.verts.get(subData.indices[triInd*3+1]);
triVecs[2] = subData.verts.get(subData.indices[triInd*3+2]);
if ( triVecs[0].equals3(triVecs[1]) ||
triVecs[1].equals3(triVecs[2]) ||
triVecs[2].equals3(triVecs[0])) {
continue;
}
double triDist = MathUtils.collisionDistPoly(localRay, triVecs);
if (triDist > 0) {
// We have collided, now we need to figure out the distance in original ray space, not the transformed ray space
Vec3d temp = localRay.getPointAtDist(triDist);
temp.multAndTrans3(modelMat, temp); // Temp is the collision point in world space
temp.sub3(temp, r.getStartRef());
double newDist = temp.mag3();
if (newDist < shortDist) {
shortDist = newDist;
}
}
}
return shortDist;
}
private double collideSubLine(MeshData.SubLineData subData, Ray r, Mat4d modelMat, Mat4d invModelMat) {
double subDist = subData.hull.collisionDistanceByMatrix(r, modelMat, invModelMat);
if (subDist < 0) {
return Double.POSITIVE_INFINITY;
}
Mat4d rayMat = MathUtils.RaySpace(r);
Vec4d[] lineVerts = new Vec4d[subData.verts.size()];
for (int i = 0; i < lineVerts.length; ++i) {
lineVerts[i] = new Vec4d();
lineVerts[i].multAndTrans3(modelMat, subData.verts.get(i));
}
return MathUtils.collisionDistLines(rayMat, lineVerts, 0.01309); // Angle is 0.75 deg in radians
}
@Override
public boolean hasTransparent() {
return _proto.hasTransparent() || Renderer.debugDrawHulls();
}
@Override
public void renderTransparent(int contextID, Renderer renderer, Camera cam, Ray pickRay) {
_proto.renderTransparent(contextID, renderer, _modelMat, _invModelMat, cam, pose);
// Debug render of the convex hull
if (Renderer.debugDrawHulls()) {
Mat4d modelViewMat = new Mat4d();
cam.getViewMat4d(modelViewMat);
modelViewMat.mult4(_modelMat);
if (debugHull == null) {
debugHull = new HullProto(_hull);
}
debugHull.render(contextID, renderer, modelViewMat, cam);
}
}
@Override
public boolean renderForView(int viewID, Camera cam) {
double dist = cam.distToBounds(getBoundsRef());
return _visInfo.isVisible(viewID, dist);
}
}