package framework.view3D; import java.util.ArrayList; import java3d.Transform3D; import java3d.TransformGroup; import java3d.Vector3d; import framework.model3D.GeometryUtility; import framework.model3D.Object3D; import framework.model3D.Placeable; import framework.model3D.Position3D; import framework.model3D.Universe; /** * 画角調整機能が付いたカメラ<BR> * 視点、注視対象、視線のうち2つを指定して使う。 * @author 新田直也 * */ public class Camera3D { private static final double NEAREST = 3.0; private static final Vector3d VIEW_FORWARD = new Vector3d(0.0, 0.0, -1.0); private static final Vector3d VIEW_DOWN = new Vector3d(0.0, -1.0, 0.0); private Universe universe = null; private double frontClipDistance = 0.5; // デプスバッファが小さいためあまり小さい値にできない private double backClipDistance = 1000.0; // デプスバッファが小さいためあまり大きい値にできない private double fieldOfView = Math.PI / 2.0; private TransformGroup viewPlatformTransform = null; protected ArrayList<Object3D> targetObjList = null; protected ArrayList<Position3D> targetList = null; protected Position3D viewPoint = null; protected Object3D viewPointObj = null; private Vector3d viewLine = null; private Vector3d cameraBack = null; private Vector3d viewUp = null; private boolean fParallel = false; // 以下の変数は省メモリ化のため導入 private Transform3D worldToView = new Transform3D(); private double matrix[] = new double[]{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0}; private Transform3D cameraTransform = new Transform3D(); public Camera3D(Universe universe) { this.universe = universe; } public Universe getUniverse() { return universe; } /** * カメラの注視点を追加する * * @param target * 注視点 */ public void addTarget(Position3D target) { if (targetList == null) targetList = new ArrayList<Position3D>(); targetList.add(target); } /** * カメラの注視対象を追加する * * @param target * 注視対象 */ public void addTarget(Object3D target) { if (targetObjList == null) targetObjList = new ArrayList<Object3D>(); targetObjList.add(target); } /** * カメラの注視対象を追加する * * @param target * 注視対象 */ public void addTarget(Placeable target) { if (targetObjList == null) targetObjList = new ArrayList<Object3D>(); if (target.getBody() instanceof Object3D) { targetObjList.add((Object3D)target.getBody()); } } /** * カメラの視点を設定する * * @param viewPoint * 視点 */ public void setViewPoint(Position3D viewPoint) { this.viewPoint = viewPoint; } /** * カメラの視点を設定する * * @param viewPoint * 視点となるオブジェクト */ public void setViewPoint(Object3D viewPointObj) { this.viewPointObj = viewPointObj; } /** * カメラの視点を設定する * * @param viewPoint * 視点となる登場物 */ public void setViewPoint(Placeable viewPointActor) { if (viewPointActor.getBody() instanceof Object3D) { viewPointObj = (Object3D)viewPointActor.getBody(); } } /** * 手前からの視線に設定する */ public void setSideView() { viewLine = VIEW_FORWARD; } /** * 上から見下ろした視線に設定する */ public void setTopView() { viewLine = VIEW_DOWN; } /** * 視線を設定する */ public void setViewLine(Vector3d viewLine) { this.viewLine = viewLine; } /** * 視野角を設定する * @param a 視野角 */ public void setFieldOfView(double a) { fieldOfView = a; } /** * 視野角を取得する * @return 視野角 */ public double getFieldOfView() { return fieldOfView; } /** * フロントクリップ距離を設定する * @param d */ public void setFrontClipDistance(double d) { frontClipDistance = d; } /** * フロントクリップ距離を取得する * @return フロントクリップ距離 */ public double getFrontClipDistance() { return frontClipDistance; } /** * バッククリップ距離を設定する * @param d */ public void setBackClipDistance(double d) { backClipDistance = d; } /** * バッククリップ距離を取得する * @return バッククリップ距離 */ public double getBackClipDistance() { return backClipDistance; } public void setViewUp(Vector3d viewUp) { this.viewUp = viewUp; } public Position3D getViewPoint() { if (viewPoint != null) return viewPoint; if (viewPointObj != null) return viewPointObj.getPosition3D(); // 視点が設定されていない場合 Vector3d center = getTargetCenter(); if (center != null) { Vector3d vz = new Vector3d(); if (viewLine != null) { // 注視対象と視線が設定されている場合 vz.negate(viewLine); } else { // 注視対象のみが設定されている場合 vz.negate(VIEW_FORWARD); } Vector3d vx = new Vector3d(); Vector3d vy = new Vector3d(); vx.cross(vz, VIEW_DOWN); if (vx.length() > GeometryUtility.TOLERANCE) { vx.normalize(); } else { vx = new Vector3d(1.0, 0.0, 0.0); } vy.cross(vz, vx); // 注視対象から下がる距離 if (cameraBack != null) { vx.scale(cameraBack.x); vy.scale(cameraBack.y); vz.scale(cameraBack.z); center.add(vz); center.add(vy); center.add(vz); } else { double z = getStandBackDistance(vx, vy); vz.scale(z); center.add(vz); } return new Position3D(center); } // 視点も注視点も設定されていない場合 return new Position3D(); } public Vector3d getViewLine() { if (viewLine != null) { // 視線が設定されている場合 return viewLine; } Vector3d center = getTargetCenter(); if (center != null) { if (viewPoint != null) { center.sub(viewPoint.getVector3d()); } else if (viewPointObj != null) { center.sub(viewPointObj.getPosition3D().getVector3d()); } else { center.set(VIEW_FORWARD); } } else { center = new Vector3d(VIEW_FORWARD); } center.normalize(); return center; } public Vector3d getViewUp() { if (viewUp != null) { return viewUp; } else { Vector3d vy = new Vector3d(0.0, 1.0, 0.0); Vector3d vv = (Vector3d) getViewLine().clone(); vv.cross(vy, vv); vv.cross(getViewLine(), vv); return vv; } } public Vector3d getTargetCenter() { Vector3d center = new Vector3d(); if (targetObjList != null && targetObjList.size() != 0) { for (int i = 0; i < targetObjList.size(); i++) { Position3D position = targetObjList.get(i).getPosition3D(); center.add(position.getVector3d()); } center.scale(1.0 / targetObjList.size()); } else if (targetList != null && targetList.size() != 0) { for (int i = 0; i < targetList.size(); i++) { Position3D position = targetList.get(i); center.add(position.getVector3d()); } center.scale(1.0 / targetList.size()); } else { return null; } return center; } /** * 注視対象や視点の移動、視線の変化に伴う画角調整 */ public void adjust(long interval) { // カメラ座標系(vx, vy, vz)を計算する Vector3d vx = new Vector3d(), vy = new Vector3d(), vz = new Vector3d(); if (viewLine == null) { // 視線が設定されていない場合 if ((viewPoint == null && viewPointObj == null) || ((targetObjList == null || targetObjList.size() == 0) && (targetList == null || targetList.size() == 0))) { // 視点または注視対象が設定されていない場合、手前からの視線にする vz.negate(VIEW_FORWARD); } else { // 注視対象の重心を注視点とする Vector3d center = getTargetCenter(); if (center == null) center = new Vector3d(); if (viewPoint != null) { center.sub(viewPoint.getVector3d()); } else { center.sub(viewPointObj.getPosition3D().getVector3d()); } center.normalize(); vz.negate(center); } } else { // 視線が設定されている場合 vz を視線方向 と逆向きに設定する viewLine.normalize(); vz.negate(viewLine); } vx.cross(vz, VIEW_DOWN); if (vx.length() > GeometryUtility.TOLERANCE) { vx.normalize(); } else { vx = new Vector3d(1.0, 0.0, 0.0); } vy.cross(vz, vx); // 世界座標からカメラ座標への変換を計算する if (viewPoint != null || viewPointObj != null) { // 視点が設定されている場合 Vector3d vp; if (viewPoint != null) { vp = viewPoint.getVector3d(); } else { vp = viewPointObj.getPosition3D().getVector3d(); } matrix[0] = vx.x; matrix[1] = vx.y; matrix[2] = vx.z; matrix[3] = 0.0; matrix[4] = vy.x; matrix[5] = vy.y; matrix[6] = vy.z; matrix[7] = 0.0; matrix[8] = vz.x; matrix[9] = vz.y; matrix[10] = vz.z; matrix[11] = 0.0; matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = 0.0; matrix[15] = 1.0; worldToView.set(matrix); worldToView.invert(); worldToView.setTranslation(vp); } else { // 視点が設定されていない場合、注視対象と視線から(カメラ座標系上での)視点を逆計算する if ((targetObjList == null || targetObjList.size() == 0) && (targetList == null || targetList.size() == 0)) return; // 視点も注視対象も設定されていない // 注視対象の中心 Vector3d center = getTargetCenter(); // カメラ座標上での注視点の座標 Vector3d eye = new Vector3d(center.dot(vx), center.dot(vy), center.dot(vz)); if (cameraBack != null) { // カメラの引き eye.add(cameraBack); } else { // 注視対象が入るようにカメラを引く double z = getStandBackDistance(vx, vy); eye.z += z; } matrix[0] = vx.x; matrix[1] = vx.y; matrix[2] = vx.z; matrix[3] = -eye.x; matrix[4] = vy.x; matrix[5] = vy.y; matrix[6] = vy.z; matrix[7] = -eye.y; matrix[8] = vz.x; matrix[9] = vz.y; matrix[10] = vz.z; matrix[11] = -eye.z; matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = 0.0; matrix[15] = 1.0; worldToView.set(matrix); worldToView.invert(); } if (viewPlatformTransform != null) viewPlatformTransform.setTransform(worldToView); } public double getStandBackDistance(Vector3d vx, Vector3d vy) { double xmax = 0; double xmin = 0; double ymax = 0; double ymin = 0; if (targetObjList != null && targetObjList.size() != 0) { for (int i = 0; i < targetObjList.size(); i++) { Position3D position = targetObjList.get(i).getPosition3D(); double px = position.getVector3d().dot(vx); double py = position.getVector3d().dot(vy); if (i == 0) { xmax = xmin = px; ymax = ymin = py; } else { if (xmax < px) xmax = px; if (xmin > px) xmin = px; if (ymax < py) ymax = py; if (ymin > py) ymin = py; } } } else if (targetList != null && targetList.size() != 0) { for (int i = 0; i < targetList.size(); i++) { Position3D position = targetList.get(i); double px = position.getVector3d().dot(vx); double py = position.getVector3d().dot(vy); if (i == 0) { xmax = xmin = px; ymax = ymin = py; } else { if (xmax < px) xmax = px; if (xmin > px) xmin = px; if (ymax < py) ymax = py; if (ymin > py) ymin = py; } } } double x = (xmax + xmin) / 2; double y = (ymax + ymin) / 2; double x_diff = Math.abs(xmax - x); double y_diff = Math.abs(ymax - y); if (x_diff < NEAREST) x_diff = NEAREST; if (y_diff < NEAREST) y_diff = NEAREST; double z; if (x_diff < y_diff) { z = y_diff / Math.tan(Math.PI / 18.0); } else { z = x_diff / Math.tan(Math.PI / 18.0); } return z; } public void setWorldToView(Position3D vp, Vector3d vl) { setViewPoint(vp); setViewLine(vl); //上方向のベクトル計算 Vector3d vy = new Vector3d(0, 1, 0); Vector3d vv = (Vector3d) vl.clone(); vv.cross(vy, vv); vv.cross(vv, vl); // activity.setCamera(vp, vl, vv); } public void setParallel(){ this.fParallel = true; } public boolean isParallel(){ return this.fParallel; } }