diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..5f2b8dc --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFactory.java=UTF-8 diff --git a/src/main/java/cactusServer/entities/Instance.java b/src/main/java/cactusServer/entities/Instance.java index c7186b7..9aa5118 100644 --- a/src/main/java/cactusServer/entities/Instance.java +++ b/src/main/java/cactusServer/entities/Instance.java @@ -44,12 +44,6 @@ // JSONDecode���̌Ăяo���p } - /** - * (�����͌����_�ł̉��̂���) - * - * @param name - * �C���X�^���X�� - */ public Instance(String name, State state, String stageURI) { setName(name); setState(state); @@ -61,7 +55,7 @@ universe = new Universe(); // stageURI�����ɂ��ēǂݍ��ރX�e�[�W�̃t�@�C����(���΃p�X)�����\�� - String stageFileName = ""; + String stageFileName = stageURI; // �X�e�[�W��3D�f�[�^��ǂݍ��ݔz�u���� Object3D stageObj = ModelFactory.loadModel(stageFileName, false, true).createObject(); @@ -91,7 +85,6 @@ return areaMap.keySet(); } - @JSONHint(ignore = true) public Area getArea(String areaId) { return areaMap.get(areaId); } @@ -101,7 +94,6 @@ return objMap.keySet(); } - @JSONHint(ignore = true) public Object getObject(String objId) { return objMap.get(objId); } @@ -111,7 +103,6 @@ return new ArrayList(characterMap.values()); } - @JSONHint(ignore = true) public Character getCharacter(String characterId) { return characterMap.get(characterId); } diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/AStar.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/AStar.java new file mode 100644 index 0000000..e2abd48 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/AStar.java @@ -0,0 +1,94 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; + +/** + * A*クラス。 + */ +public class AStar { + private LinkedList locationOpenList = new LinkedList(); + private LinkedList locationClosedList = new LinkedList(); + + /** + * 最短経路探査。 最短経路のリストを返すか、見つからない場合nullを返す。 + * start = goalの場合もnullを返す。 + * 戻り値にはgoalからstartの1個前まで経路をさかのぼったものが入っている。startは入っていない。 + */ + public Plan getPath(Location startLocation, + Location goalLocation) { + + //出発地点と目的地点が同じならnullを返す + if (startLocation.equals(goalLocation)) { + return null; + } + + // 初期化 + locationOpenList = new LinkedList(); + locationClosedList = new LinkedList(); + + // 出発地点をLocationOpenListに追加する + locationOpenList.add(startLocation); + while (!locationOpenList.isEmpty()) { + // LocationOpenListの最小スコアの地点をcurLocationとして取り出す + Collections.sort(locationOpenList, new CostComparator()); + Location curLocation = locationOpenList.removeFirst(); + + // 現在の地点が目的地点ならば探査完了 + if (curLocation.equals(goalLocation)) { + LinkedList locationPath = new LinkedList(); + + // 出発地点まで通った地点を辿る + curLocation = goalLocation; + while (!curLocation.equals(startLocation)) { + locationPath.add(curLocation); + curLocation = (Location) curLocation.getParent(); + } + return new Plan(locationPath); + } + + // 現在の地点が目的地点でないなら、現在の地点をLocationClosedListに移す + locationClosedList.add(curLocation); + + // 隣接する地点を調べる + ArrayList neighbors = curLocation.getSuccessors(); + + // 隣接する地点の数だけ、ループ + for (int i = 0; i < neighbors.size(); i++) { + // 通過でき、各リストに入ってないならコストをもらい、 + if (!locationOpenList.contains(neighbors.get(i)) + && !locationClosedList.contains(neighbors.get(i))) { + //地点コストを求める + //agent.getLocationCost((Location)neighbors.get(i), null, null, null); + + //パスコストを求める + ((Location)neighbors.get(i)).calculatesCosts(curLocation, goalLocation); + + // LocationOpenListに移す + locationOpenList.add((Location) neighbors.get(i)); + } + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + + } + return null; + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + // コストの昇順ソートのためのクラスを作ろう + + /** + * ソート条件式クラス。 + */ + class CostComparator implements Comparator { + public int compare(Location n1, Location n2) { + return n1.getScore() - n2.getScore(); + } + } + + // ------------------------------------------------------------------------------------------------------------------------------------------- + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/GeometryGraph.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/GeometryGraph.java new file mode 100644 index 0000000..7a4f59c --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/GeometryGraph.java @@ -0,0 +1,168 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.Geometry; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Point3d; + +public class GeometryGraph extends StateMachine { + // コンストラクタ + public GeometryGraph(Geometry g) { + if (g instanceof IndexedTriangleArray) { + + IndexedTriangleArray ita = (IndexedTriangleArray) g; + + // itaからstatesにデータを書き込み + for (int i = 0; i < ita.getIndexCount() / 3; i++) {// 面の数だけループ + Location e = new Location(i, ita); + if (e.getNormal() != null) { + states.add(e); + } + } + + // 関連付け + for (int i = 0; i < states.size(); i++) { + setSuccessors(i, ita); + } + } + } + + + // 連結成分を関連付け + public void setSuccessors(int index, IndexedTriangleArray ita) { + Location e = (Location)states.get(index); + + // 探索 + int[] List = new int[ita.getIndexCount()]; + + for (int i = 0; i < ita.getIndexCount(); i++) { + List[i] = ita.getCoordinateIndex(i); + } + + // -1で初期化 + for (int i = 0; i < 3; i++) { + e.successorIndex[i] = -1; + e.IndexOfSharedEdge[i] = -1; + } + + int j = 0; + + for (int i = 0; i < states.size() * 3; i++) { + // System.out.println("indexList[0] とList["+i+"] の照合"); + // //////idでListを置き換えてメソッド呼び出しを少なく? + // int id = ita.getCoordinateIndex(i); + if (e.indexList[0] == List[i]) { + if (i % 3 == 0) { + if (e.indexList[1] == List[i + 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i + 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 1) { + if (e.indexList[1] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i + 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 2) { + if (e.indexList[1] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.addSuccessor(states.get(i / 3)); + j++; + } else if (e.indexList[2] == List[i - 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[0] = e.indexList[0]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } + + } + } + + for (int i = 0; i < states.size() * 3; i++) { + // System.out.println("indexList[1] とList["+i+"] の照合"); + if (e.indexList[1] == List[i]) { + if (i % 3 == 0) { + if (e.indexList[2] == List[i + 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 1) { + if (e.indexList[2] == List[i - 1]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } else if (i % 3 == 2) { + if (e.indexList[2] == List[i - 2]) { + e.successorIndex[j] = i / 3; + e.IndexOfSharedEdge[1] = e.indexList[1]; + e.IndexOfSharedEdge[2] = e.indexList[2]; + e.addSuccessor(states.get(i / 3)); + j++; + } + } + } + } + + // System.out.println("1つ目のsuccessor**"+successorIndex[0]+"***"); + // System.out.println("2つ目のsuccessor**"+successorIndex[1]+"***"); + // System.out.println("3つ目のsuccessor**"+successorIndex[2]+"***"); + + return; + } + + + // アクセッサ + public ArrayList getStates() { + return states; + } + + public Location getNearestLocation(Position3D pos) { + Location nearest = null; + double distance = 0.0; + for (int n = 0; n < states.size(); n++) { + Location loc = (Location)states.get(n); + if (nearest == null) { + nearest = loc; + distance = nearest.getCenter().distance( + new Point3d(pos.getVector3d())); + } else { + double d = loc.getCenter().distance( + new Point3d(pos.getVector3d())); + if (d < distance) { + nearest = loc; + distance = d; + } + } + } + return nearest; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/IState.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/IState.java new file mode 100644 index 0000000..4cacec1 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/IState.java @@ -0,0 +1,16 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import java.util.ArrayList; + +public interface IState { + + abstract ArrayList getSuccessors(); + + abstract void addSuccessor(IState s); + /** + * 探査元のノード取得。 + * @return + * 探査元のノードを返す。 + */ + abstract IState getParent(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Location.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Location.java new file mode 100644 index 0000000..a1d6d45 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Location.java @@ -0,0 +1,137 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import org.ntlab.radishforandroidstudio.framework.model3D.GeometryUtility; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Point3d; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +import java.util.ArrayList; + +public class Location implements IState { + public int planeIndex; + public int[] indexList = new int[3]; + public int[] successorIndex = new int[100]; + public int numberOfSharedEdge;// 共有辺の本数 + public int[] IndexOfSharedEdge = new int[3];// 共有辺の頂点インデックス + private Point3d center; + private Vector3d normal; + + private double cost = 0; + private double heuristicCost = 0; + private Location parentNode = null; + private ArrayList successors = new ArrayList(); + + // テスト用の空のコンストラクタ + public Location(Point3d center) { + this.center = center; + normal = new Vector3d(0.0, 1.0, 0.0); + } + + // コンストラクタ + public Location(int index, IndexedTriangleArray ita) { + planeIndex = index; + + indexList[0] = ita.getCoordinateIndex(index * 3); + indexList[1] = ita.getCoordinateIndex(index * 3 + 1); + indexList[2] = ita.getCoordinateIndex(index * 3 + 2); + + Point3d p1 = new Point3d(); + Point3d p2 = new Point3d(); + Point3d p3 = new Point3d(); + + ita.getCoordinate(indexList[0], p1); + ita.getCoordinate(indexList[1], p2); + ita.getCoordinate(indexList[2], p3); + + // 中心座標の計算 + center = new Point3d((p1.getX() + p2.getX() + p3.getX()) / 3.0, (p1 + .getY() + + p2.getY() + p3.getY()) / 3.0, (p1.getZ() + p2.getZ() + p3 + .getZ()) / 3.0); + + // 法線ベクトルの計算 + p2.sub(p1); + p3.sub(p1); + Vector3d v2 = new Vector3d(p2); + Vector3d v3 = new Vector3d(p3); + v2.cross(v2, v3); + if (v2.length() < GeometryUtility.TOLERANCE) { + v2 = null; + } else { + v2.normalize(); + } + normal = v2; + } + + public void addSuccessor(IState s) { + successors.add(s); + } + + @Override + public ArrayList getSuccessors() { + // TODO Auto-generated method stub + return (ArrayList) successors; + } + + @Override + public IState getParent() { + // TODO Auto-generated method stub + return parentNode; + } + + /** + * スコアの取得。 + * + * @return スコアを返す。 + */ + public int getScore() { + // 比較に小数点も反映させる為、1000を掛けている + return (int) ((cost + heuristicCost) * 1000); + } + + /** + * コスト計算と探査元ノードを設定。 + * + * @param parentNode + * 探査元のノード。 + * @param goalNode + * 目的地のノード。 + */ + public void calculatesCosts(Location parentNode, Location goalNode) { + // コストを加算 + cost = parentNode.cost + 1; // agent.getPathCost(parentNode, this, null, + // null, null, null, null, null); + + // //ヒューリスティックコストを計算 + // disX = point.x - goalNode.point.x; + // disY = point.y - goalNode.point.y; + // ヒューリスティックコストの信頼性が薄い為、3分の1にしている + heuristicCost = 0; + + // 探査元ノードを記録 + this.parentNode = parentNode; + } + + // ///////// + // アクセッサ// + // ///////// + public int getPlaneIndex() { + return planeIndex; + } + + public int getIndexList(int index) { + return indexList[index]; + } + + public int getSuccessorIndex(int index) { + return successorIndex[index]; + } + + public Point3d getCenter() { + return center; + } + + public Vector3d getNormal() { + return normal; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Plan.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Plan.java new file mode 100644 index 0000000..7e5b9ec --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/Plan.java @@ -0,0 +1,73 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import java.util.LinkedList; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +public class Plan { + private LinkedList path; // 計画内容を表すパス + private int currentLoc = 0; // パス上の現在の Location + + /** + * 複数の通過点を持つ計画 + * @param locationPath + */ + public Plan(LinkedList locationPath) { + path = locationPath; + currentLoc = locationPath.size() - 1; + } + + /** + * スタートとゴールをダイレクトに結ぶ計画 + * @param start + * @param goal + */ + public Plan(Location start, Location goal) { + path = new LinkedList(); + path.add(goal); + path.add(start); + currentLoc = 1; + } + + /** + * 現在の Location を取得する + * @return 現在の Location, すでにゴールに着いているときは null を返す + */ + public Location getCurrentLocation() { + if (currentLoc <= 0) return null; + return path.get(currentLoc); + } + + /** + * 次の Location を取得する + * @return 次の Location, すでにゴールに着いているときは null を返す + */ + public Location getNextLocation() { + if (currentLoc <= 0) return null; + return path.get(currentLoc - 1); + } + + /** + * 現在の座標値を元に、現在の Location を更新する + * @param position 現在の座標値 + * @return 更新した --- true, 以前のまま --- false + */ + public boolean updateCurrentLocation(Position3D position) { + Vector3d toCurrentPosition = position.getVector3d(); + Location curLocation = getCurrentLocation(); + if (curLocation == null) return true; + toCurrentPosition.sub(curLocation.getCenter()); + double distanceToCurrentPosition = toCurrentPosition.length(); + Vector3d toNextLocation = new Vector3d(getNextLocation().getCenter()); + toNextLocation.sub(curLocation.getCenter()); + double distanceToNextLocation = toNextLocation.length(); + if (distanceToCurrentPosition >= distanceToNextLocation) { + // 次の Location を通り過ぎた場合 + currentLoc--; + return true; + } + return false; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/StateMachine.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/StateMachine.java new file mode 100644 index 0000000..4bc9296 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/AI/StateMachine.java @@ -0,0 +1,9 @@ +package org.ntlab.radishforandroidstudio.framework.AI; + +import java.util.ArrayList; + +public abstract class StateMachine { + protected ArrayList states = new ArrayList(); + protected IState curState; + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/Animation3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/Animation3D.java new file mode 100644 index 0000000..da19d53 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/Animation3D.java @@ -0,0 +1,137 @@ +package org.ntlab.radishforandroidstudio.framework.animation; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Quaternion3D; + +/** + * 階層化されたオブジェクトに対するアニメーション情報を保持する(部品単位で位置、向き、テクスチャをアニメーション可能) + * @author 新田直也 + * + */ +public class Animation3D { + public long time = 0; + + private ArrayList partList = new ArrayList(); + private long maxKey = getMaxKey(); + + public Animation3D() { + time = 0; + } + + public Animation3D(Animation3D a) { + time = 0; + partList = a.partList; + maxKey = a.maxKey; + } + + public boolean equals(Animation3D a) { + return (partList == a.partList && maxKey == a.maxKey); + } + + public void reset() { + time = 0; + } + + public boolean progress(long interval) { + if (maxKey == 0) + return true; // 空のアニメーションの場合動かさない + time += interval; + // System.out.println(time + "/" + maxKey); + if (time > maxKey) { + time = time % maxKey; // timeが最後の要素のkeyの値(アニメーションの最後のkey)を超えている場合、tを最後のkeyの値(maxKey)で割った余りとして設定する + return false; + } else + return true; + } + + public Pose3D getPose() { + if (maxKey == 0 || partList.size() == 0) + return new DefaultPose3D(); + + KeyFrame[] aroundKey = new KeyFrame[2]; + Quaternion3D q; + Position3D p; + Quaternion3D tq; + Position3D tp; + Pose3D pose = new Pose3D(); + for (int i = 0; i < partList.size(); i++) { + aroundKey = partList.get(i).getKey(time); + + // コピーコンストラクタの作成 + q = null; + p = null; + tq = null; + tp = null; + if (aroundKey[0].getPosition() != null) { + p = new Position3D(aroundKey[0].getPosition()); // getPosition()が参照を返すのでコピーしてから変更 + } + if (aroundKey[0].getQuaternion() != null) { + q = new Quaternion3D(aroundKey[0].getQuaternion()); // getQuaternion()がアドレス(参照)を返すので、コピー(=q)を作成してそちらを変更している。 + } + if (aroundKey[0].getTexturePosition() != null) { + tp = new Position3D(aroundKey[0].getTexturePosition()); // getPosition()が参照を返すのでコピーしてから変更 + } + if (aroundKey[0].getTextureQuaternion() != null) { + tq = new Quaternion3D(aroundKey[0].getTextureQuaternion()); // getQuaternion()がアドレス(参照)を返すので、コピー(=q)を作成してそちらを変更している。 + } + + // timeがkeyそのものだった場合(補間の計算が不要な場合) + if (aroundKey[1] != null) { + // t1 はtの前のkey(aroundKey[0])のスカラー倍 + double t1 = aroundKey[1].key - time; + // t2 はtの後のkey(aroundKey[1])のスカラー倍 + double t2 = time - aroundKey[0].key; + double t3 = aroundKey[1].key - aroundKey[0].key; + double timealpha = t2 / t3; + + // timeに対するQuaternionとPositionの計算 + if (p != null) { + Position3D p2 = new Position3D(aroundKey[1].getPosition()); + p.mul(t1 / t3).add(p2.mul(t2 / t3)); + } + if (q != null) { + Quaternion3D q2 = new Quaternion3D(aroundKey[1].getQuaternion()); + q.getInterpolate(q2, timealpha); + } + if (tp != null) { + Position3D tp2 = new Position3D(aroundKey[1].getTexturePosition()); + tp.mul(t1 / t3).add(tp2.mul(t2 / t3)); + } + if (tq != null) { + Quaternion3D tq2 = new Quaternion3D(aroundKey[1].getTextureQuaternion()); + tq.getInterpolate(tq2, timealpha); + } + } + pose.addPose(partList.get(i).getName(), p, q, aroundKey[0].getTexture(), tp, tq, partList.get(i).getTextureUnit()); + } + + return pose; + } + + public Animation3D merge(Animation3D a) { + this.partList.addAll(a.partList); + maxKey = getMaxKey(); + return this; + } + + public void addPartAnimation(PartAnimation pa) { + partList.add(pa); + maxKey = getMaxKey(); + } + + // アニメーションが終了したかを判定するためにkeyの最大値を探索して返すメソッド + private long getMaxKey() { + long maxKey = 0; + int i; + + for (i = 0; i < partList.size(); i++) { + if (maxKey < partList.get(i).getLastKey()) { + maxKey = partList.get(i).getLastKey(); + } else + continue; + } + return maxKey; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/DefaultPose3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/DefaultPose3D.java new file mode 100644 index 0000000..132666f --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/DefaultPose3D.java @@ -0,0 +1,27 @@ +package org.ntlab.radishforandroidstudio.framework.animation; + +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; + +/** + * 初期状態の姿勢 + * @author 新田直也 + * + */ +public class DefaultPose3D extends Pose3D { + public void applyTo(Object3D obj) { + obj.scale.setTransform(new Transform3D()); + for (int n = 0; n < obj.children.length; n++) { + subApplyTo((Object3D)obj.children[n]); + } + } + + private void subApplyTo(Object3D obj) { + obj.pos.setTransform(new Transform3D()); + obj.rot.setTransform(new Transform3D()); + obj.scale.setTransform(new Transform3D()); + for (int n = 0; n < obj.children.length; n++) { + subApplyTo((Object3D)obj.children[n]); + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/KeyFrame.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/KeyFrame.java new file mode 100644 index 0000000..c8c49b6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/KeyFrame.java @@ -0,0 +1,41 @@ +package org.ntlab.radishforandroidstudio.framework.animation; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Quaternion3D; +import org.ntlab.radishforandroidstudio.java3d.Texture; + + +/** + * アニメーション中でキーフレーム単位に定義される情報(キー値) + * @author 新田直也 + * + */ +public class KeyFrame { + long key = 0; + Position3D keyValuePosition = null; + Quaternion3D keyValueQuaternion = null; + Texture keyValueTexture = null; + Position3D keyValueTexturePosition = null; + Quaternion3D keyValueTextureQuaternion = null; + + Position3D getPosition() { + return keyValuePosition; + } + + Quaternion3D getQuaternion() { + return keyValueQuaternion; + } + + Texture getTexture() { + return keyValueTexture; + } + + Position3D getTexturePosition() { + return keyValueTexturePosition; + } + + Quaternion3D getTextureQuaternion() { + return keyValueTextureQuaternion; + } +} + diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/PartAnimation.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/PartAnimation.java new file mode 100644 index 0000000..64e0025 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/animation/PartAnimation.java @@ -0,0 +1,176 @@ +package org.ntlab.radishforandroidstudio.framework.animation; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Quaternion3D; + +import java.util.ArrayList; + + +/** + * 部品単位のアニメーション情報を保持する + * @author 新田直也 + * + */ +public class PartAnimation { + private String name; + private int textureUnit; + private ArrayList keyList = new ArrayList(); + public static final int SINGLE_TEXTURE = -1; + + public PartAnimation(String name) { + this.name = name; + textureUnit = SINGLE_TEXTURE; + } + + public PartAnimation(String name, int textureUnit) { + this.name = name; + this.textureUnit = textureUnit; + } + + String getName() { + return name; + } + + int getTextureUnit() { + return textureUnit; + } + + //アニメーションの経過時間の前後のkeyをKeyFrame型の配列で取得するメソッド + KeyFrame[] getKey(long t){ + int i; + KeyFrame[] aroundKey = new KeyFrame[2]; + + for(i=1;i partPoseList = new ArrayList(); + + public Pose3D() { + } + + public Pose3D(Pose3D p) { + partPoseList = new ArrayList(p.partPoseList); + } + + public void applyTo(Object3D obj) { + // obj.rotate(part, vx, vy, vz, a); + int n = partPoseList.size(); + for (int i = 0; i < n; i++) { + final PartPose partpose = (PartPose) partPoseList.get(i); + Object3D partObj = obj.getPart(partpose.name); + if (partObj != null) { + // 位置 + if (partpose.position != null) + partObj.apply(partpose.position, false); + // 向き + if (partpose.quaternion != null) + partObj.apply(partpose.quaternion, false); + // テクスチャ + if (partpose.texture != null + || partpose.texturePosition != null + || partpose.textureQuaternion != null) { + ObjectVisitor v = new ObjectVisitor() { + @Override + public void postVisit(Object3D obj) { + Appearance appearance = null; + Node node = obj.getPrimitiveNode(); + if (node != null && node instanceof Shape3D) { + appearance = ((Shape3D)node).getAppearance(); + } else if (node != null && node instanceof Primitive) { + appearance = ((Primitive)node).getAppearance(); + } + if (appearance != null) { + TextureUnitState tu = null; + if (partpose.texture != null) { + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + appearance.setTexture(partpose.texture); + } else { + tu = appearance.getTextureUnitState(partpose.textureUnit); + tu.setTexture(partpose.texture); + appearance.setTextureUnitState(partpose.textureUnit, tu); + } + } + if ((partpose.texturePosition != null || partpose.textureQuaternion != null) + && obj.hasAppearancePrepared()) { // Appearance が初回のレンダリングの直前で更新される可能性があるため + TextureAttributes ta; + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + ta = appearance.getTextureAttributes(); + } else { + tu = appearance.getTextureUnitState(partpose.textureUnit); + ta = tu.getTextureAttributes(); + } + if (ta == null) { + ta = new TextureAttributes(); + } + Transform3D t = new Transform3D(); + if (partpose.texturePosition != null) { + t.set(new Vector3d(partpose.texturePosition.getVector3d())); + } + if (partpose.textureQuaternion != null) { + Transform3D t2 = new Transform3D(); + t2.set(partpose.textureQuaternion.getQuat()); + t.mul(t2); + } +// shape.getAppearance().getTextureAttributes().setTextureTransform(t); + ta.setTextureTransform(t); + if (partpose.textureUnit == PartAnimation.SINGLE_TEXTURE) { + appearance.setTextureAttributes(ta); + } else { + tu.setTextureAttributes(ta); + appearance.setTextureUnitState(partpose.textureUnit, tu); + } + } + } + } + @Override + public void preVisit(Object3D obj) { + } + }; + partObj.accept(v); + } + } + } + } + + public void addPose(String name, Position3D p, Quaternion3D q, Texture texture, Position3D tp, Quaternion3D tq, int textureUnit) { + PartPose partpose = new PartPose(name, p, q, texture, tp, tq, textureUnit); + partPoseList.add(partpose); + } + + @Override + public Property3D clone() { + // TODO Auto-generated method stub + return new Pose3D(this); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Actor.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Actor.java new file mode 100644 index 0000000..af0c4ab --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Actor.java @@ -0,0 +1,349 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.animation.Animation3D; +import org.ntlab.radishforandroidstudio.framework.model3D.CollisionResult; +import org.ntlab.radishforandroidstudio.framework.model3D.GeometryUtility; +import org.ntlab.radishforandroidstudio.framework.model3D.Movable; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Quaternion3D; +import org.ntlab.radishforandroidstudio.framework.physics.Force3D; +import org.ntlab.radishforandroidstudio.framework.physics.Ground; +import org.ntlab.radishforandroidstudio.framework.physics.PhysicsUtility; +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; +import org.ntlab.radishforandroidstudio.framework.physics.Velocity3D; +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +/** + * ゲーム内の登場物全般 + * @author 新田直也 + * + */ +public abstract class Actor extends Animatable implements Movable { + protected Vector3d direction = new Vector3d(1.0, 0.0, 0.0); + protected Mode mode; + // 以下省メモリ化のため予めインスタンスを生成 + protected Mode modeFreeFall = new ModeFreeFall(); + protected Mode modeOnGround = new ModeOnGround(); + + public Actor(Object3D body, Animation3D animation) { + super(new Solid3D(body), animation); + mode = modeOnGround; + } + + public Actor(Solid3D body, Animation3D animation) { + super(body, animation); + mode = modeOnGround; + } + +// public Actor(ActorModel model) { +// super(model.createBody(), model.getAnimation()); +// mode = modeOnGround; +// } + + /** + * 単位時間ごとの動作(衝突判定処理は行わない) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + */ + public void motion(long interval){ + // 1. 位置を動かす + ((Solid3D)body).move(interval, getGravity(), getGravityCenter()); + super.motion(interval); + } + + /** + * 単位時間ごとの動作(衝突判定処理も行う) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + * @param ground --- 地面(構造物) + */ + public void motion(long interval, Ground ground) { + + // 1. 位置を動かす + ((Solid3D)body).move(interval, getGravity(), getGravityCenter()); + + if (animation != null) { + // 2. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 3. 姿勢を変える + body.apply(animation.getPose(), false); + } + + // 4. 衝突判定 + CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground); + + // 5. 衝突応答 + if (cr != null) { + // 構造物にぶつかった、または接触している時 + if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) { + // 上向きの面(=地面)にぶつかった、または接触している時 + if (cr.length <= GeometryUtility.TOLERANCE) { + // 地面に乗っている + if (!(mode instanceof ModeOnGround)) { + // 落ちてきて丁度乗った + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } + } else { + // 地面にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + if (!(mode instanceof ModeOnGround)) { + // 落ちてきてめり込んだ + // 6. 地面モードにする + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } else { + // 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ + // または、歩いている途中で斜面に差し掛かった + ((ModeOnGround)mode).setNormal(cr.normal); + } + } + } else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) { + // 垂直壁にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + } else { + // 下からぶつかった、または接触した(頭をぶつけた) + if (cr.length > GeometryUtility.TOLERANCE) { + // 5.1. 押し戻す + onIntersect(cr, interval); + } + } + cr = null; + } else { + // 地面とぶつかっても接触してもいない時 + // 6. 落下モードにする + mode = modeFreeFall; + } + } + + public void motion(long interval, Ground ground,ArrayList forces, + ArrayList appPoints) { + + forces.add(getGravity()); + appPoints.add(getGravityCenter()); + + // 1. 位置を動かす + ((Solid3D)body).move(interval, forces, appPoints); + + if (animation != null) { + // 2. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 3. 姿勢を変える + body.apply(animation.getPose(), false); + } + + // 4. 衝突判定 + CollisionResult cr = PhysicsUtility.doesIntersect((Solid3D)body, ground); + + // 5. 衝突応答 + if (cr != null) { + // 構造物にぶつかった、または接触している時 + if (cr.normal.dot(PhysicsUtility.vertical) > GeometryUtility.TOLERANCE) { + // 上向きの面(=地面)にぶつかった、または接触している時 + if (cr.length <= GeometryUtility.TOLERANCE) { + // 地面に乗っている + if (!(mode instanceof ModeOnGround)) { + // 落ちてきて丁度乗った + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } + } else { + // 地面にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + if (!(mode instanceof ModeOnGround)) { + // 落ちてきてめり込んだ + // 6. 地面モードにする + mode = modeOnGround; + ((ModeOnGround)mode).setNormal(cr.normal); + onEndFall(); + } else { + // 歩いている途中で、アニメーションの関係で一瞬だけめり込んだ + // または、歩いている途中で斜面に差し掛かった + ((ModeOnGround)mode).setNormal(cr.normal); + } + } + } else if (cr.normal.dot(PhysicsUtility.vertical) >= -GeometryUtility.TOLERANCE) { + // 垂直壁にめり込んだ + // 5.1. 押し戻す + onIntersect(cr, interval); + } else { + // 下からぶつかった、または接触した(頭をぶつけた) + if (cr.length > GeometryUtility.TOLERANCE) { + // 5.1. 押し戻す + onIntersect(cr, interval); + } + } + cr = null; + } else { + // 地面とぶつかっても接触してもいない時 + // 6. 落下モードにする + mode = modeFreeFall; + } + } + + public void setInitialDirection(Vector3d dir) { + direction = dir; + } + + public Vector3d getInitialDirection() { + return direction; + } + + /** + * 指定した方向に向かせる + * @param vec 新しい向き + */ + public void setDirection(Vector3d vec) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(direction, GeometryUtility.Y_AXIS); + v2.cross(vec, GeometryUtility.Y_AXIS); + if (v2.length() < GeometryUtility.TOLERANCE) return; + v1.normalize(); + v2.normalize(); + double cos = v1.dot(v2); + v1.cross(v1, v2); + double sin = v1.dot(GeometryUtility.Y_AXIS); + double angle = Math.atan2(sin, cos); + AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle); + Quaternion3D quat = new Quaternion3D(axisAngle); + ((Solid3D)body).apply(quat, false); + } + + /** + * 指定した方向に向かせる + * @param vec 新しい向き + */ + public void setDirection3D(Vector3d vec) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(direction, GeometryUtility.Y_AXIS); + double angle = Math.PI / 2.0 - Math.acos(vec.dot(GeometryUtility.Y_AXIS)); + AxisAngle4d axisAngle2 = new AxisAngle4d(v1, angle); + v2.cross(vec, GeometryUtility.Y_AXIS); + if (v2.length() < GeometryUtility.TOLERANCE) return; + v1.normalize(); + v2.normalize(); + double cos = v1.dot(v2); + v1.cross(v1, v2); + double sin = v1.dot(GeometryUtility.Y_AXIS); + angle = Math.atan2(sin, cos); + AxisAngle4d axisAngle = new AxisAngle4d(GeometryUtility.Y_AXIS, angle); + Quaternion3D quat = new Quaternion3D(axisAngle); + Quaternion3D quat2 = new Quaternion3D(axisAngle2); + quat.mul(quat2); + ((Solid3D)body).apply(quat, false); + } + + /** + * 現在向いている方向を取得する + * @return 現在の向き + */ + public Vector3d getDirection() { + Vector3d dir = new Vector3d(direction); + Transform3D trans = new Transform3D(); + trans.set(((Solid3D)body).getQuaternion().getAxisAngle()); + trans.transform(dir); + return dir; + } + + /** + * 移動速度ベクトルを設定する + * @param vel 新しい移動速度ベクトル + */ + public void setVelocity(Velocity3D vel) { + ((Solid3D)body).apply(vel, false); + } + + /** + * 移動速度ベクトルを取得する + * @return 現在の移動速度ベクトル + */ + public Velocity3D getVelocity() { + return ((Solid3D)body).getVelocity(); + } + + /** + * X軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotX(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.X_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * Y軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotY(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.Y_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * Z軸を中心に回転する + * @param angle 回転角(反時計回り, 単位:ラジアン) + */ + public void rotZ(double angle) { + Quaternion3D curQuat = body.getQuaternion(); + curQuat.add(new AxisAngle4d(GeometryUtility.Z_AXIS, angle)); + body.apply(curQuat, false); + } + + /** + * 加わっている重力を取得する + * @return 重力 + */ + public Force3D getGravity() { + return mode.getForce((Solid3D)body); + } + + /** + * 重心を取得する + * @return 重心位置 + */ + public Position3D getGravityCenter() { + return ((Solid3D)body).getGravityCenter(); + } + + /** + * 地面に乗っている状態か否かを取得する + * @return true --- 地面に乗っている, false --- 地面に乗っていない(空中にいる) + */ + public boolean isOnGround() { + return (mode instanceof ModeOnGround); + } + + /** + * 地面(構造物)に落下した瞬間に呼び出される + */ + public abstract void onEndFall(); + + /** + * 地面(構造物)に衝突した瞬間に呼び出される + * @param normal --- 地面の法線方向ベクトル + * @param interval --- 前回の動作からの経過時間(ミリ秒単位) + */ + public abstract void onIntersect(CollisionResult normal, long interval); + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ActorModel.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ActorModel.java new file mode 100644 index 0000000..4986ccf --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ActorModel.java @@ -0,0 +1,15 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; + + +public abstract class ActorModel extends GameModel { + + public ActorModel(String fileName) { + super(fileName); + } + + public Solid3D createBody() { + return new Solid3D(getModel().createObject()); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Animatable.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Animatable.java new file mode 100644 index 0000000..0c6b736 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Animatable.java @@ -0,0 +1,57 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + + +import org.ntlab.radishforandroidstudio.framework.animation.Animation3D; +import org.ntlab.radishforandroidstudio.framework.model3D.BaseObject3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Placeable; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; + +public abstract class Animatable implements Placeable { + public Object3D body; + public Animation3D animation; + + public Animatable(Object3D body, Animation3D animation) { + this.body = body; + this.animation = animation; + } + + /** + * 単位時間ごとの動作(アニメーション処理) + * @param interval --- 前回呼び出されたときからの経過時間(ミリ秒単位) + */ + public void motion(long interval) { + if (animation != null) { + // 1. アニメーションを実行 + if (animation.progress(interval) == false) { + onEndAnimation(); + } + + // 2. 姿勢を変える + body.apply(animation.getPose(), false); + } + } + + public TransformGroup getTransformGroupToPlace() { + return getBody().getTransformGroupToPlace(); + } + + public BaseObject3D getBody() { + return body; + } + + /** + * アニメーションが終了するたびに呼ばれる + */ + public abstract void onEndAnimation(); + + public Position3D getPosition() { + return body.getPosition3D(); + } + + public void setPosition(Position3D p) { + body.apply(p, false); + } + +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/GameModel.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/GameModel.java new file mode 100644 index 0000000..41b7ee2 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/GameModel.java @@ -0,0 +1,39 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + +import org.ntlab.radishforandroidstudio.framework.animation.Animation3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Model3D; +import org.ntlab.radishforandroidstudio.framework.model3D.ModelFactory; +import org.ntlab.radishforandroidstudio.framework.model3D.ModelFileFormatException; + +import java.io.IOException; + +public class GameModel { + + private Model3D model = null; + private String fileName; + + public GameModel(String fileName) { + this.fileName = fileName; + } + + public Animation3D getAnimation() { + return new Animation3D(); + } + + public Model3D getModel() { + if(model == null && fileName != null) { + try { + model = ModelFactory.loadModel(fileName); + } catch (IOException e) { + e.printStackTrace(); + } catch (ModelFileFormatException e) { + e.printStackTrace(); + } + } + return model; + } + + public void clearModel(){ + model = null; + } +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Mode.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Mode.java new file mode 100644 index 0000000..04736df --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/Mode.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; +import org.ntlab.radishforandroidstudio.framework.physics.Force3D; +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; + + +public abstract class Mode { + + abstract Force3D getForce(Solid3D body); + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeFreeFall.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeFreeFall.java new file mode 100644 index 0000000..630695b --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeFreeFall.java @@ -0,0 +1,11 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; +import org.ntlab.radishforandroidstudio.framework.physics.Force3D; +import org.ntlab.radishforandroidstudio.framework.physics.PhysicsUtility; +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; + + +public class ModeFreeFall extends Mode { + Force3D getForce(Solid3D body) { + return PhysicsUtility.getGravity(body); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeOnGround.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeOnGround.java new file mode 100644 index 0000000..3501e7a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/ModeOnGround.java @@ -0,0 +1,24 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + + +import org.ntlab.radishforandroidstudio.framework.physics.Force3D; +import org.ntlab.radishforandroidstudio.framework.physics.PhysicsUtility; +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class ModeOnGround extends Mode { + private Vector3d normal = PhysicsUtility.vertical; + + public Force3D getForce(Solid3D body) { + return Force3D.ZERO; + } + + public void setNormal(Vector3d normal) { + this.normal = normal; + } + + public Vector3d getNormal() { + return normal; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/OvergroundActor.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/OvergroundActor.java new file mode 100644 index 0000000..8f01031 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/gameMain/OvergroundActor.java @@ -0,0 +1,51 @@ +package org.ntlab.radishforandroidstudio.framework.gameMain; + + +import org.ntlab.radishforandroidstudio.framework.animation.Animation3D; +import org.ntlab.radishforandroidstudio.framework.model3D.CollisionResult; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.physics.Solid3D; +import org.ntlab.radishforandroidstudio.framework.physics.Velocity3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +/** + * 地面の上を移動するもの(ジャンプや自由落下させることも可能) + * @author 新田直也 + * + */ +public class OvergroundActor extends Actor { + + public OvergroundActor(Object3D body, Animation3D animation) { + super(body, animation); + } + + public OvergroundActor(Solid3D body, Animation3D animation) { + super(body, animation); + } + +// public OvergroundActor(ActorModel model) { +// super(model); +// } + + public void onIntersect(CollisionResult cr, long interval) { + // めり込んだら(めり込んだ面の法線方向に)押し戻す + Vector3d back = (Vector3d) cr.normal.clone(); + back.scale(cr.length * 2.0); + body.apply(new Position3D(body.getPosition3D().add(back)), false); + + // 速度の面の法線方向の成分を 0 にする + Vector3d v = (Vector3d) ((Solid3D)body).getVelocity().getVector3d().clone(); + double d = v.dot(cr.normal); + v.scaleAdd(-d, cr.normal, v); + body.apply(new Velocity3D(v), false); + } + + @Override + public void onEndFall() { + } + + @Override + public void onEndAnimation() { + } +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BackgroundBox.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BackgroundBox.java new file mode 100644 index 0000000..82e7b01 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BackgroundBox.java @@ -0,0 +1,6 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Background; + +public class BackgroundBox extends Background { +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BaseObject3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BaseObject3D.java new file mode 100644 index 0000000..5c8e7fb --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BaseObject3D.java @@ -0,0 +1,331 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; +import java.util.Enumeration; + +import org.ntlab.radishforandroidstudio.java3d.Appearance; +import org.ntlab.radishforandroidstudio.java3d.BoundingPolytope; +import org.ntlab.radishforandroidstudio.java3d.Box; +import org.ntlab.radishforandroidstudio.java3d.Cone; +import org.ntlab.radishforandroidstudio.java3d.Cylinder; +import org.ntlab.radishforandroidstudio.java3d.Geometry; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleFanArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleStripArray; +import org.ntlab.radishforandroidstudio.java3d.Node; +import org.ntlab.radishforandroidstudio.java3d.Shape3D; +import org.ntlab.radishforandroidstudio.java3d.Sphere; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; +import org.ntlab.radishforandroidstudio.java3d.TriangleArray; +import org.ntlab.radishforandroidstudio.java3d.TriangleFanArray; +import org.ntlab.radishforandroidstudio.java3d.TriangleStripArray; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; +import org.ntlab.radishforandroidstudio.java3d.Vector4d; + +public class BaseObject3D implements Placeable { + public TransformGroup center; + + protected BoundingSurface[] boundingSurfaces = null; + + public BaseObject3D() { + center = new TransformGroup(); + } + + public BaseObject3D(Geometry g, Appearance a) { + center = new TransformGroup(); + Shape3D shape = new Shape3D(g, a); + center.addChild(shape); + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return getBody().center; + } + + public BaseObject3D getBody() { + return this; + } + + public Node getPrimitiveNode() { + return (Node)center.getChild(0); + } + + public Enumeration getPrimitiveNodes() { + return (Enumeration)center.getAllChildren(); + } + + /** + * 衝突判定用のボリューム(ポリゴンと粗い判定用の多角柱)を取得する + * @return 衝突判定用のボリューム列 + */ + public BoundingSurface[] getBoundingSurfaces() { + if (boundingSurfaces == null) { + Node node = getPrimitiveNode(); + if (node == null) return null; + + ArrayList vertex3DList = null; + if (node instanceof Box) { + // Boxの場合 + Box box = ((Box)node); + // 頂点列を取得する + vertex3DList = getVertexList(box.getShape(Box.BACK).getGeometry()); + vertex3DList.addAll(getVertexList(box.getShape(Box.BOTTOM).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.FRONT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.LEFT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.RIGHT).getGeometry())); + vertex3DList.addAll(getVertexList(box.getShape(Box.TOP).getGeometry())); + } else if (node instanceof Cylinder) { + // Cylinderの場合 + Cylinder cylinder = ((Cylinder)node); + // 頂点列を取得する + vertex3DList = getVertexList(cylinder.getShape(Cylinder.BODY).getGeometry()); + vertex3DList.addAll(getVertexList(cylinder.getShape(Cylinder.BOTTOM).getGeometry())); + vertex3DList.addAll(getVertexList(cylinder.getShape(Cylinder.TOP).getGeometry())); + } else if (node instanceof Cone) { + // Coneの場合 + Cone cone = ((Cone)node); + // 頂点列を取得する + vertex3DList = getVertexList(cone.getShape(Cone.BODY).getGeometry()); + vertex3DList.addAll(getVertexList(cone.getShape(Cone.CAP).getGeometry())); + } else if (node instanceof Sphere) { + // Sphereの場合 + Sphere sphere = ((Sphere)node); + // 頂点列を取得する + vertex3DList = getVertexList(sphere.getShape(Sphere.BODY).getGeometry()); + } else if (node instanceof Shape3D) { + // Shape3Dの場合 + Shape3D shape = (Shape3D)node; + // 頂点列を取得する + vertex3DList = getVertexList(shape.getGeometry()); + } + if (vertex3DList == null) return null; + + BoundingSurface[] surfaces = new BoundingSurface[vertex3DList.size() / 3]; + + for (int i = 0; i < vertex3DList.size(); i += 3) { + Vector3d v1 = vertex3DList.get(i); + Vector3d v2 = vertex3DList.get(i + 1); + Vector3d v3 = vertex3DList.get(i + 2); + BoundingSurface bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v1.clone()); + bSurface.addVertex((Vector3d)v2.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); + surfaces[i / 3] = bSurface; + } + boundingSurfaces = surfaces; + } + return boundingSurfaces; + } + + private ArrayList getVertexList(Geometry g) { + ArrayList vertex3DList = new ArrayList(); + double coordinate1[] = new double[3]; + if (g instanceof IndexedTriangleArray) { + // IndexedTriangleArray の場合 + IndexedTriangleArray triArray = (IndexedTriangleArray)g; + + // 全頂点を3D上の頂点をvertex3DListに入れていく。 + for (int i = 0; i < triArray.getIndexCount(); i++) { + triArray.getCoordinates(triArray.getCoordinateIndex(i), coordinate1); + vertex3DList.add(new Vector3d(coordinate1)); + } + } else if (g instanceof TriangleArray) { + // TriangleArray の場合 + TriangleArray triArray = (TriangleArray)g; + + // 全頂点を3D上の頂点をvertex3DListに入れていく。 + for (int i = 0; i < triArray.getVertexCount(); i++) { + triArray.getCoordinates(i, coordinate1); + vertex3DList.add(new Vector3d(coordinate1)); + } + } else if (g instanceof IndexedTriangleStripArray) { + // IndexedTriangleStripArray の場合 + IndexedTriangleStripArray triStripAttay = (IndexedTriangleStripArray)g; + int stripVertexCounts[] = new int[triStripAttay.getNumStrips()]; + triStripAttay.getStripIndexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + for (int i = 0; i < triStripAttay.getNumStrips(); i++) { + for (int j = 0; j < stripVertexCounts[i]; j += 2) { + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); + triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); + vertex3DList.add(new Vector3d(coordinate1)); //1つめの三角形 + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + vertex3DList.add(new Vector3d(coordinate2)); //2つめの三角形 + vertex3DList.add(new Vector3d(coordinate4)); + vertex3DList.add(new Vector3d(coordinate3)); + index += 2; + } + } + } else if (g instanceof TriangleStripArray) { + // TriangleStripArray の場合 + TriangleStripArray triStripAttay = (TriangleStripArray)g; + int stripVertexCounts[] = new int[triStripAttay.getNumStrips()]; + triStripAttay.getStripVertexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + for (int i = 0; i < triStripAttay.getNumStrips(); i++) { + for (int j = 0; j < stripVertexCounts[i]; j += 2) { + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index+1, coordinate2); + triStripAttay.getCoordinates(index+2, coordinate3); + triStripAttay.getCoordinates(index+3, coordinate4); + vertex3DList.add(new Vector3d(coordinate1)); //1つめの三角形 + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + vertex3DList.add(new Vector3d(coordinate2)); //2つめの三角形 + vertex3DList.add(new Vector3d(coordinate4)); + vertex3DList.add(new Vector3d(coordinate3)); + index += 2; + } + } + } else if (g instanceof IndexedTriangleFanArray) { + // IndexedTriangleFanArray の場合 + IndexedTriangleFanArray triFanAttay = (IndexedTriangleFanArray)g; + int stripVertexCounts[] = new int[triFanAttay.getNumStrips()]; + triFanAttay.getStripIndexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = null; + for (int i = 0; i < triFanAttay.getNumStrips(); i++) { + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index), coordinate1); // 中心点 + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index+1), coordinate2); + index += 2; + for (int j = 2; j < stripVertexCounts[i]; j++) { + triFanAttay.getCoordinates(triFanAttay.getCoordinateIndex(index), coordinate3); + vertex3DList.add(new Vector3d(coordinate1)); + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + coordinate4 = coordinate2; + coordinate2 = coordinate3; + coordinate3 = coordinate4; + index++; + } + } + } else if (g instanceof TriangleFanArray) { + // TriangleFanArray の場合 + TriangleFanArray triFanAttay = (TriangleFanArray)g; + int stripVertexCounts[] = new int[triFanAttay.getNumStrips()]; + triFanAttay.getStripVertexCounts(stripVertexCounts); + // 全頂点を3D上の頂点をvertex3DListに入れていく + int index = 0; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = null; + for (int i = 0; i < triFanAttay.getNumStrips(); i++) { + triFanAttay.getCoordinates(index, coordinate1); // 中心点 + triFanAttay.getCoordinates(index + 1, coordinate2); + index += 2; + for (int j = 2; j < stripVertexCounts[i]; j++) { + triFanAttay.getCoordinates(index, coordinate3); + vertex3DList.add(new Vector3d(coordinate1)); + vertex3DList.add(new Vector3d(coordinate2)); + vertex3DList.add(new Vector3d(coordinate3)); + coordinate4 = coordinate2; + coordinate2 = coordinate3; + coordinate3 = coordinate4; + index++; + } + } + } else { + return null; + } + return vertex3DList; + } + + protected BoundingPolytope createBoundingPolytope(Vector3d vertex1, + Vector3d vertex2, Vector3d vertex3) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + Vector3d v3 = new Vector3d(); + Vector3d v4 = new Vector3d(); + Vector3d v5 = new Vector3d(); + Vector3d v6 = new Vector3d(); + Vector3d cv1 = new Vector3d(); + Vector3d cv2 = new Vector3d(); + cv1.sub(vertex3, vertex1); + cv2.sub(vertex2, vertex1); + Vector3d cv = new Vector3d(); + cv.cross(cv1, cv2); + cv.normalize(); + cv.scale(0.01); + v1.set(vertex1); + v2.set(vertex2); + v3.set(vertex3); + v4.set(vertex1); + v4.add(cv); + v5.set(vertex2); + v5.add(cv); + v6.set(vertex3); + v6.add(cv); + + Vector3d pv1 = new Vector3d(); + Vector3d pv2 = new Vector3d(); + Vector3d pv3 = new Vector3d(); + Vector3d pn = new Vector3d(); + Vector4d[] plane = new Vector4d[5]; + + // 0 + pv1 = v1; + pv2.sub(v2, v1); + pv3.sub(v3, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[0] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 1 + pv1 = v1; + pv2.sub(v4, v1); + pv3.sub(v2, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[1] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 2 + pv1 = v1; + pv2.sub(v3, v1); + pv3.sub(v4, v1); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[2] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 3 + pv1 = v6; + pv2.sub(v3, v6); + pv3.sub(v5, v6); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[3] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + // 4 + pv1 = v6; + pv2.sub(v5, v6); + pv3.sub(v4, v6); + + pn.cross(pv2, pv3); + pn.normalize(); + plane[4] = new Vector4d(pn.x, pn.y, pn.z, -pn.dot(pv1)); + + return new BoundingPolytope(plane); + } + + public boolean hasAppearancePrepared() { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BoundingSurface.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BoundingSurface.java new file mode 100644 index 0000000..08d3670 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/BoundingSurface.java @@ -0,0 +1,128 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.java3d.BoundingPolytope; +import org.ntlab.radishforandroidstudio.java3d.BoundingSphere; +import org.ntlab.radishforandroidstudio.java3d.Bounds; +import org.ntlab.radishforandroidstudio.java3d.Matrix4d; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; +import org.ntlab.radishforandroidstudio.java3d.Vector4d; + + +public class BoundingSurface implements Cloneable { + private Bounds bounds = null; // 粗い衝突判定用(粗い地面用) + private ArrayList children = new ArrayList(); + private ArrayList vertexList = new ArrayList(); + private static Vector4d plane[] = { new Vector4d(), new Vector4d(), new Vector4d(), + new Vector4d(), new Vector4d() }; + + public Object clone() { + BoundingSurface s = new BoundingSurface(); + if (bounds != null) { + s.setBounds((Bounds)bounds.clone()); + } + for (int i = 0; i < children.size(); i++) { + s.children.add((BoundingSurface)children.get(i).clone()); + } + for (int i = 0; i < vertexList.size(); i++) { + s.vertexList.add((Vector3d)vertexList.get(i).clone()); + } + return s; + } + + public void setBounds(Bounds bounds) { + this.bounds = bounds; + } + + public Bounds getBounds() { + return bounds; + } + + public void addVertex(Vector3d v) { + vertexList.add(v); + } + + public void addChild(BoundingSurface bs, boolean bCombineBounds) { + children.add(bs); + if (bCombineBounds) { + if (bounds == null) { + bounds = (Bounds)bs.bounds.clone(); + } else { + bounds.combine(bs.bounds); + } + } + } + + public void transform(Transform3D transform3D) { + bounds.transform(transform3D); + for (int i = 0; i < vertexList.size(); i++) { + Matrix4d mat4d = new Matrix4d(); + transform3D.get(mat4d); + double x = mat4d.m00 * vertexList.get(i).x + mat4d.m01 + * vertexList.get(i).y + mat4d.m02 * vertexList.get(i).z + + mat4d.m03; + double y = mat4d.m10 * vertexList.get(i).x + mat4d.m11 + * vertexList.get(i).y + mat4d.m12 * vertexList.get(i).z + + mat4d.m13; + double z = mat4d.m20 * vertexList.get(i).x + mat4d.m21 + * vertexList.get(i).y + mat4d.m22 * vertexList.get(i).z + + mat4d.m23; + vertexList.get(i).x = x; + vertexList.get(i).y = y; + vertexList.get(i).z = z; + } + } + + /** + * BoundingSphereとの粗い衝突判定 + * @param bs 衝突判定の対象 + * @return 衝突しているBoundingSurfaceのリスト(nullを返すことはない) + */ + public ArrayList intersect(BoundingSphere bs) { + ArrayList results = new ArrayList(); + if (children == null || children.size() == 0) { + if (bounds.intersect(bs)) { + results.add(this); + } + } else { + if (bounds == null || bounds.intersect(bs)) { + for (int n = 0; n < children.size(); n++) { + results.addAll(children.get(n).intersect(bs)); + } + } + } + return results; + } + + /** + * OBBとの詳細な衝突判定 + * @param obb 衝突判定の対象 + * @return 衝突判定の結果 + */ + public CollisionResult intersect(OBB obb) { + if (children == null || children.size() == 0) { + // 葉の場合は、凸ポリゴンを立ち上げた薄い多角柱 + if (bounds instanceof BoundingPolytope) { + ((BoundingPolytope)bounds).getPlanes(plane); + CollisionResult cr = obb.intersect(plane[0]); // obbが底面を含む無限平面と交わっているか? + if (cr != null) { + // 無限平面と交わっている場合、無限平面との衝突点が凸ポリゴンの内部に位置するか? + if (GeometryUtility.inside(vertexList, cr.collisionPoint.getVector3d(), cr.normal)) { + return cr; + } + } + } + return null; + } else { + for (int n = 0; n < children.size(); n++) { + CollisionResult cr = children.get(n).intersect(obb); + if (cr != null) { + return cr; + } + } + return null; + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/CollisionResult.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/CollisionResult.java new file mode 100644 index 0000000..436d9d0 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/CollisionResult.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class CollisionResult { + public Position3D collisionPoint = new Position3D(); + public Vector3d normal = new Vector3d(); + public double length = 0.0; +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ContainerModel.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ContainerModel.java new file mode 100644 index 0000000..5916828 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ContainerModel.java @@ -0,0 +1,42 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Transform3D; + +/** + * 子供を持つモデル + * @author 新田直也 + * + */ +public class ContainerModel extends Model3D { + private Model3D[] children; + private Transform3D defaultTransform = null; + + public ContainerModel(String name, Model3D[] children) { + this.children = children; + this.name = name; + this.defaultTransform = null; + } + + public ContainerModel(String name, Model3D[] children, + Transform3D transform) { + this.children = children; + this.name = name; + this.defaultTransform = transform; + } + + public Object3D createObject() { + Object3D[] objChild = new Object3D[children.length]; + for(int i = 0;i < children.length;i++){ + objChild[i] = (Object3D) children[i].createObject(); + } + return new Object3D(name, objChild, defaultTransform); + } + + public Object3D createObjectSharingAppearance() { + Object3D[] objChild = new Object3D[children.length]; + for(int i = 0;i < children.length;i++){ + objChild[i] = (Object3D) children[i].createObjectSharingAppearance(); + } + return new Object3D(name, objChild, defaultTransform); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryCollector.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryCollector.java new file mode 100644 index 0000000..a328ae7 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryCollector.java @@ -0,0 +1,99 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.java3d.Geometry; +import org.ntlab.radishforandroidstudio.java3d.IndexedGeometryArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Shape3D; + +public class GeometryCollector extends ObjectVisitor{ + private int totalVertexCount = 0; + private int totalIndexCount = 0; + private ArrayList geometryList = new ArrayList(); // 葉のとき、Geometryを保存 + private IndexedGeometryArray geometry = null; + + @Override + public void preVisit(Object3D obj) { + if (!obj.hasChildren()) { + Geometry g = ((Shape3D)obj.getBody().getPrimitiveNode()).getGeometry(); + geometryList.add(g); + totalVertexCount += ((IndexedGeometryArray)g).getVertexCount(); + totalIndexCount += ((IndexedGeometryArray)g).getIndexCount(); + } + } + + @Override + public void postVisit(Object3D obj) { + } + + public Geometry getGeometry(){ + if (geometry == null) { + // リスト中の複数の Geometry を1つにまとめる + int coordinateOfs = 0; + int indexOfs = 0; + double coodinates[] = new double[totalVertexCount * 3]; + int indicies[] = new int[totalIndexCount]; + for (int n = 0; n < geometryList.size(); n++) { + IndexedGeometryArray geometry = (IndexedGeometryArray)geometryList.get(n); + double tmpCoordinates[] = new double[geometry.getVertexCount() * 3]; + geometry.getCoordinates(0, tmpCoordinates); + System.arraycopy(tmpCoordinates, 0, coodinates, coordinateOfs * 3, geometry.getVertexCount() * 3); + int tmpIndicies[] = new int[geometry.getIndexCount()]; + geometry.getCoordinateIndices(0, tmpIndicies); + for (int m = 0; m < geometry.getIndexCount(); m++) { + tmpIndicies[m] += coordinateOfs; + } + System.arraycopy(tmpIndicies, 0, indicies, indexOfs, geometry.getIndexCount()); + coordinateOfs += geometry.getVertexCount(); + indexOfs += geometry.getIndexCount(); + } + geometry = new IndexedTriangleArray(totalVertexCount, IndexedTriangleArray.COORDINATES, totalIndexCount); + geometry.setCoordinates(0, coodinates); + geometry.setCoordinateIndices(0, indicies); + + // 重複している頂点を1つにまとめる + GeometryUtility.compressGeometry(geometry); + +// // IndexedTriangleStripArray に変換(重複している頂点を1つにまとめるため) +// GeometryInfo gi = new GeometryInfo(geometry); +// Stripifier sf = new Stripifier(); +// sf.stripify(gi); +// geometry = gi.getIndexedGeometryArray(); // IndexedTriangleStripArray が返される +// +// // IndexedTriangleArray に変換(GeometryGraph が IndexedTriangleStripArray に未対応のため) +// if (geometry instanceof IndexedTriangleStripArray) { +// int numStrips = ((IndexedTriangleStripArray)geometry).getNumStrips(); +// int indexCounts[] = new int[numStrips]; +// ((IndexedTriangleStripArray)geometry).getStripIndexCounts(indexCounts); +// int dstIndicies[] = new int[(((IndexedTriangleStripArray)geometry).getIndexCount() - numStrips * 2) * 3]; +// int dstOfs = 0; +// int srcOfs = 0; +// for (int s = 0; s < numStrips; s++) { +// for (int i = 2; i < indexCounts[s]; i += 2) { +// dstIndicies[dstOfs] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 2); +// dstIndicies[dstOfs + 1] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 1); +// dstIndicies[dstOfs + 2] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i); +// dstOfs += 3; +// if (i + 1 < indexCounts[s]) { +// dstIndicies[dstOfs] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i); +// dstIndicies[dstOfs + 1] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i - 1); +// dstIndicies[dstOfs + 2] = ((IndexedTriangleStripArray)geometry).getCoordinateIndex(srcOfs + i + 1); +// dstOfs += 3; +// } +// } +// srcOfs += indexCounts[s]; +// } +// +// int vertexCount = ((IndexedTriangleStripArray)geometry).getVertexCount(); +// coodinates = new double[vertexCount * 3]; +// ((IndexedTriangleStripArray)geometry).getCoordinates(0, coodinates); +// +// geometry = new IndexedTriangleArray(vertexCount, IndexedTriangleArray.COORDINATES, dstIndicies.length); +// geometry.setCoordinates(0, coodinates); +// geometry.setCoordinateIndices(0, dstIndicies); +// } + } + return geometry; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryUtility.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryUtility.java new file mode 100644 index 0000000..b73f85d --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/GeometryUtility.java @@ -0,0 +1,266 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; +import java.util.Hashtable; + +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.IndexedGeometryArray; +import org.ntlab.radishforandroidstudio.java3d.Matrix3d; +import org.ntlab.radishforandroidstudio.java3d.Point3d; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; +import org.ntlab.radishforandroidstudio.java3d.Vector4d; + +public class GeometryUtility { + public static final double TOLERANCE = 0.0000002; + public static final Vector3d X_AXIS = new Vector3d(1.0, 0.0, 0.0); + public static final Vector3d Y_AXIS = new Vector3d(0.0, 1.0, 0.0); + public static final Vector3d Z_AXIS = new Vector3d(0.0, 0.0, 1.0); + + public static ProjectionResult projection3D( + ArrayList vertex3Dlist, Vector3d axis) { + double k = 0.0; + int i = 0; + + ProjectionResult pr = new ProjectionResult(); + + axis.normalize(); + + for (i = 0; i < vertex3Dlist.size(); i++) { + Vector3d p = vertex3Dlist.get(i); + Vector3d p1 = new Vector3d(); + + k = p.dot(axis); + p1.scaleAdd(-k, axis, p); + + if (i == 0 || k >= pr.max) { + pr.max = k; + } else if (i == 0 || k <= pr.min) { + pr.min = k; + } + pr.vertexList.add(p1); + } + return pr; + } + + public static ProjectionResult projection2D( + ArrayList vertex2Dlist, Vector3d axis) { + int i = 0; + double k = 0.0; + // System.out.println("point3:"+axis); + if (axis.x != 0 || axis.y != 0 || axis.z != 0) { + axis.normalize(); + } + // System.out.println("point3_1:"+axis); + ProjectionResult pr = new ProjectionResult(); + + for (i = 0; i < vertex2Dlist.size(); i++) { + Vector3d p = vertex2Dlist.get(i); + + k = p.dot(axis); + // System.out.println("k:"+k); + // System.out.println("point3:"+axis); + if (i == 0 || k >= pr.max) { + pr.max = k; + } else if (i == 0 || k <= pr.min) { + pr.min = k; + } + } + return pr; + } + + // 以下、頂点の表裏の判定メソッド + public static boolean inside(Vector3d v, Vector4d plane) { + // System.out.println("vertex:" + v.x + "," + v.y + "," + v.z); + // System.out.println("plane:" + plane.x + "," + plane.y + "," + plane.z + // + "," + plane.w); + Vector3d pv = new Vector3d(plane.x, plane.y, plane.z); + // Vector3d nv = (Vector3d)pv.clone(); + // nv.scaleAdd(plane.w / pv.lengthSquared(),v); + if (pv.dot(v) + plane.w <= TOLERANCE) { + // System.out.println("内部判定!!"+nv.dot(pv)); + pv = null; + return true; + } + // System.out.println("外部判定!!"+nv.dot(pv)); + pv = null; + return false; + } + + /** + * 3点を通る平面を作成する(ただし、v1, v2, v3の内容が書き換えられるので注意) + * + * @param v1 + * --- 1点目の座標 + * @param v2 + * --- 2点目の座標 + * @param v3 + * --- 3点目の座標 + * @return v1, v2, v3を通る平面 + */ + public static Vector4d createPlane(Vector3d v1, Vector3d v2, Vector3d v3) { + v2.sub(v1); + v3.sub(v1); + v2.cross(v2, v3); + v2.normalize(); + return new Vector4d(v2.x, v2.y, v2.z, -v2.dot(v1)); + } + + /** + * + * 平面と直線の交点を求める + * + * @param plane + * 平面 + * @param v1 + * 直線上の点1 + * @param v2 + * 直線状の点2 + * @return 交点 + */ + public static Vector3d intersect(Vector4d plane, Vector3d v1, Vector3d v2) { + Vector3d n = new Vector3d(plane.x, plane.y, plane.z); + Vector3d v21 = (Vector3d) v2.clone(); + v21.sub(v1); + v21.scale((-plane.w - n.dot(v1)) / n.dot(v21)); + v21.add(v1); + return v21; + } + + /** + * + * 指定された点が凸ポリゴンの内部に包含されているか? + * + * @param vertexList + * 凸ポリゴンの頂点列 + * @param point + * 指定点 + * @return true --- 包含されている, false --- 包含されていない + */ + public static boolean inside(ArrayList vertexList, Vector3d point, Vector3d normal) { + boolean inside = true; + for (int i = 0; i < vertexList.size(); i++) { + // ポリゴンの各辺に対して衝突点が右側か左側か? + Vector3d center = (Vector3d) point.clone(); + Vector3d v2 = (Vector3d) (vertexList.get((i + 1) + % vertexList.size()).clone()); + Vector3d v1 = (Vector3d) vertexList.get(i).clone(); + center.sub(v1); + v2.sub(v1); + v1.cross(v2, center); + if (normal.dot(v1) < -GeometryUtility.TOLERANCE) { + inside = false; + break; + } + } + // すべて右側、またはすべて左側だった場合、凸ポリゴンの内部に位置したと考える + return inside; + } + + // IndexedGeometryArrayのインデックスのうち、同じ座標をさすインデックスを置き換える + public static void compressGeometry(IndexedGeometryArray g) { + // 1. Hashtableを作りながら、同一座標頂点群の代表representaion[]を決める + Hashtable> h = new Hashtable>(); + Point3d p = new Point3d(); + Point3d p2 = new Point3d(); + double hashValue; + ArrayList vertexList; + int[] representation = new int[g.getVertexCount()]; + + for (int i = 0; i < g.getVertexCount(); i++) { + g.getCoordinate(i, p); + + hashValue = p.getX() + p.getY() + p.getZ(); + + vertexList = h.get(hashValue); + + if (vertexList == null) {// hashに対応する要素がない場合 + // Hashtableを作る + vertexList = new ArrayList(); + vertexList.add(i); + h.put(hashValue, vertexList); + // representation[]を作る + representation[i] = i; + } else { + boolean bFound = false; + for (int j = 0; j < vertexList.size(); j++) { + g.getCoordinate(vertexList.get(j), p2); + if (p.getX() == p2.getX() + && p.getY() == p2.getY() + && p.getZ() == p2.getZ()) { + representation[i] = vertexList.get(j); + bFound = true; + break; + } + } + if (!bFound) { + vertexList.add(i); + // representation[]を作る + representation[i] = i; + } + } + } + + // 2. indexの置き換え + for (int i = 0; i < g.getIndexCount(); i++) { + int newIndex = representation[g.getCoordinateIndex(i)]; + g.setCoordinateIndex(i, newIndex); + } + } + + public static Matrix3d calcRotationForView(Vector3d viewLine) { + Vector3d v1 = (Vector3d)viewLine.clone(); + v1.normalize(); + Vector3d v2 = new Vector3d(); + v2.cross(v1, Y_AXIS); + double angle2 = Math.atan2(-v2.z, v2.x); + double angle1 = Math.PI / 2.0 - Math.acos(v1.dot(Y_AXIS)); + Matrix3d rot1 = new Matrix3d(); + rot1.rotX(angle1); + Matrix3d rot2 = new Matrix3d(); + rot2.rotY(angle2); + rot2.mul(rot1); + return rot2; + } + + public static Quaternion3D calcQuaternionForView(Vector3d viewLine) { + Vector3d v1 = (Vector3d)viewLine.clone(); + v1.normalize(); + Vector3d v2 = new Vector3d(); + v2.cross(v1, Y_AXIS); + double angle2 = Math.atan2(-v2.z, v2.x); + double angle1 = Math.PI / 2.0 - Math.acos(v1.dot(Y_AXIS)); + Quaternion3D quat1 = new Quaternion3D(new AxisAngle4d(X_AXIS, angle1)); + Quaternion3D quat2 = new Quaternion3D(new AxisAngle4d(Y_AXIS, angle2)); + + return quat1.mul(quat2); + } + + public static Quaternion3D calcQuaternionFromSrcToDst(Vector3d src, Vector3d dst) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + v1.cross(src, Y_AXIS); + v2.cross(dst, Y_AXIS); + Quaternion3D quat1; + Quaternion3D quat2; + double angle2 = Math.acos(Y_AXIS.dot(src) / src.length()) - Math.acos(Y_AXIS.dot(dst) / dst.length()); + if (Math.abs(v2.length()) < TOLERANCE) { + v1.normalize(); + quat1 = new Quaternion3D(); + quat2 = new Quaternion3D(new AxisAngle4d(v1, angle2)); + } else { + if (Math.abs(v1.length()) < TOLERANCE) { + quat1 = new Quaternion3D(); + } else { + v1.normalize(); + v2.normalize(); + double cos1 = v1.dot(v2); + v1.cross(v1, v2); + double sin1 = v1.dot(Y_AXIS); + double angle1 = Math.atan2(sin1, cos1); + quat1 = new Quaternion3D(new AxisAngle4d(Y_AXIS, angle1)); + } + quat2 = new Quaternion3D(new AxisAngle4d(v2, angle2)); + } + return quat1.mul(quat2); + } +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/IViewer3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/IViewer3D.java new file mode 100644 index 0000000..d77cd00 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/IViewer3D.java @@ -0,0 +1,18 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.GraphicsContext3D; +import org.ntlab.radishforandroidstudio.java3d.Light; +import org.ntlab.radishforandroidstudio.java3d.Node; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; + +import java.util.ArrayList; + +public interface IViewer3D { + public abstract void setGraphicsContext3D(GraphicsContext3D gc3D); + public abstract void surfaceChanged(int width, int height); + public abstract void onDrawFrame(); + public abstract void update(ArrayList lights, BackgroundBox skyBox); + public abstract void pushTransform(Transform3D t); + public abstract void popTransform(); + public abstract void draw(Node obj); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/LeafModel.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/LeafModel.java new file mode 100644 index 0000000..46f46e6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/LeafModel.java @@ -0,0 +1,27 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Appearance; +import org.ntlab.radishforandroidstudio.java3d.Geometry; + +public class LeafModel extends Model3D { + + private Geometry g; + private Appearance a; + + public LeafModel(String name, Geometry g, Appearance a){ + this.name = name; + this.g = g; + this.a = a; + } + + public Object3D createObject(){ + Appearance a2 = (Appearance)a.cloneNodeComponent(); + Object3D obj = new Object3D(name, g, a2); + return obj; + } + + public Object3D createObjectSharingAppearance(){ + Object3D obj = new Object3D(name, g, a); + return obj; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Model3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Model3D.java new file mode 100644 index 0000000..e8610a5 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Model3D.java @@ -0,0 +1,9 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + + + +public abstract class Model3D { + String name; + public abstract Object3D createObject(); + public abstract Object3D createObjectSharingAppearance(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFactory.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFactory.java new file mode 100644 index 0000000..d1d815a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFactory.java @@ -0,0 +1,896 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; + +import org.ntlab.radishforandroidstudio.java3d.Appearance; +import org.ntlab.radishforandroidstudio.java3d.GeometryArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedGeometryArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedTriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Material; +import org.ntlab.radishforandroidstudio.java3d.TextureLoader; +import org.ntlab.radishforandroidstudio.java3d.TriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Vector2f; +import org.ntlab.radishforandroidstudio.java3d.Vector3f; + + +/** + * 3谺。蜈�繝「繝�繝ォ繧堤函謌舌☆繧九◆繧√�ョ繧ッ繝ゥ繧ケ + * @author 譁ー逕ー逶エ荵� + * + */ +public class ModelFactory { + public static final String STL_FILE_EXTENSION = ".stl"; + public static final String OBJ_FILE_EXTENSION = ".obj"; + + /** + * 3谺。蜈�繝「繝�繝ォ繧帝嚴螻、讒矩��縺ィ蜷�驛ィ縺ョ蜷咲ァー縺ィ縺ィ繧ゅ↓繝輔ぃ繧、繝ォ縺九i隱ュ縺ソ霎シ繧� + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @return 隱ュ縺ソ霎シ繧薙□3D繝「繝�繝ォ + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(String fileName) throws IOException, ModelFileFormatException{ + return loadModel(fileName, null, false, false); + } + + /** + * 3谺。蜈�繝「繝�繝ォ繧帝嚴螻、讒矩��縺ィ蜷�驛ィ縺ョ蜷咲ァー縺ィ縺ィ繧ゅ↓繝輔ぃ繧、繝ォ縺九i隱ュ縺ソ霎シ繧� + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @param ap 3D繝「繝�繝ォ縺ォ驕ゥ逕ィ縺吶k陦ィ髱「螻樊�ァ(STL繝輔ぃ繧、繝ォ縺ョ縺ソ) + * @return 隱ュ縺ソ霎シ繧薙□3D繝「繝�繝ォ + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(String fileName, Appearance ap) throws IOException, ModelFileFormatException{ + return loadModel(fileName, ap, false, false); + } + + /** + * 3谺。蜈�繝「繝�繝ォ繧帝嚴螻、讒矩��縺ィ蜷�驛ィ縺ョ蜷咲ァー縺ィ縺ィ繧ゅ↓繝輔ぃ繧、繝ォ縺九i隱ュ縺ソ霎シ繧��シ磯�乗�弱ユ繧ッ繧ケ繝√Ε縺ョ譛臥┌縺ョ謖�螳壻サ倥″�シ� + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @param ap 3D繝「繝�繝ォ縺ォ驕ゥ逕ィ縺吶k陦ィ髱「螻樊�ァ(STL繝輔ぃ繧、繝ォ縺ョ縺ソ) + * @param bTransparent 騾乗�弱ユ繧ッ繧ケ繝√Ε縺後≠繧九°蜷ヲ縺�(Android逕ィ縺ァ縺ッ譛ェ菴ソ逕ィ) + * @param bCalcNormal 豕慕キ壹�ョ蜀崎ィ育ョ励r陦後≧縺� + * @return 隱ュ縺ソ霎シ繧薙□3D繝「繝�繝ォ + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadModel(String fileName, Appearance ap, boolean bTransparent, boolean bCalcNormal) throws IOException, ModelFileFormatException { + if (fileName.toLowerCase().endsWith(STL_FILE_EXTENSION)) { + // STL繝輔ぃ繧、繝ォ隱ュ縺ソ霎シ縺ソ + return loadStlFile(fileName, ap, bCalcNormal); + } else if (fileName.toLowerCase().endsWith(OBJ_FILE_EXTENSION)) { + // OBJ繝輔ぃ繧、繝ォ隱ュ縺ソ霎シ縺ソ + return loadObjFile( fileName, bCalcNormal); + } + return null; + } + + /** + * STL繝輔ぃ繧、繝ォ(繧「繧ケ繧ュ繝シ縲√ヰ繧、繝翫Μ)縺ョ隱ュ縺ソ霎シ縺ソ + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @param ap 3D繝「繝�繝ォ縺ォ驕ゥ逕ィ縺吶k陦ィ髱「螻樊�ァ + * @param bCalcNormal 豕慕キ壹�ョ蜀崎ィ育ョ励r陦後≧縺� + * @return 隱ュ縺ソ霎シ繧薙□3D繝「繝�繝ォ + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadStlFile(String fileName, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // STL繝輔ぃ繧、繝ォ隱ュ縺ソ霎シ縺ソ + InputStream in = new FileInputStream(new File(fileName)); + BufferedInputStream bis = new BufferedInputStream(in); + byte[] headerBin = new byte[80]; + int n = bis.read(headerBin); + if (headerBin[0] == 's' && + headerBin[1] == 'o' && + headerBin[2] == 'l' && + headerBin[3] == 'i' && + headerBin[4] == 'd' && + headerBin[5] == ' ') { + bis.close(); + + // STL縺ッ繝�繧ュ繧ケ繝医ヵ繧。繧、繝ォ + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + Model3D model = loadStlAsciiFile(br, ap, bCalcNormal); + br.close(); + return model; + } else if (n == 80) { + // STL縺ッ繝舌う繝翫Μ繝輔ぃ繧、繝ォ + Model3D model = loadStlBinaryFile(bis, headerBin, ap, bCalcNormal); + bis.close(); + return model; + } + return null; + } + + private static Model3D loadStlAsciiFile(BufferedReader br, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // 繝倥ャ繝�隱ュ縺ソ霎シ縺ソ + String header = br.readLine(); + if (header == null) { + br.close(); + throw new ModelFileFormatException(); + } + header.trim(); + String headerContents[] = header.split(" "); + String name; + if (headerContents.length > 0 && headerContents[0].equals("solid")) { + if (headerContents.length > 1) name = headerContents[1]; + else name = ""; + } else { + System.out.println("STL file's header error!!"); + br.close(); + throw new ModelFileFormatException(); + } + + // Facet隱ュ縺ソ霎シ縺ソ + ArrayList verticies = new ArrayList(); + String line; + while ((line = br.readLine()) != null && !line.startsWith("endsolid ")) { + // facet normal ... + line.trim(); + String[] data = line.split(" "); + if (data.length < 1 || !data[0].equals("facet")) { + System.out.println("Expected facet normal ..."); + br.close(); + throw new ModelFileFormatException(); + } + + // outer loop + line = br.readLine(); + if (line == null) { + System.out.println("Expected outer loop"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("outer loop")) { + System.out.println("Expected outer loop"); + br.close(); + throw new ModelFileFormatException(); + } + + // vertex * 3 + for (int i = 0; i < 3; i++) { + line = br.readLine(); + if (line == null) { + System.out.println("Expected vertex x y z"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + data = line.split(" "); + if (data.length < 4 || !data[0].equals("vertex")) { + System.out.println("Expected vertex x y z"); + br.close(); + throw new ModelFileFormatException(); + } + double[] vertex = new double[]{ + Double.parseDouble(data[1]), // X蠎ァ讓� + Double.parseDouble(data[2]), // Y蠎ァ讓� + Double.parseDouble(data[3]) // Z蠎ァ讓� + }; + verticies.add(vertex); // Z蠎ァ讓� + } + + // endloop + line = br.readLine(); + if (line == null) { + System.out.println("Expected endloop"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("endloop")) { + System.out.println("Expected endloop"); + br.close(); + throw new ModelFileFormatException(); + } + + // endfacet + line = br.readLine(); + if (line == null) { + System.out.println("Expected endfacet"); + br.close(); + throw new ModelFileFormatException(); + } + line.trim(); + if (!line.equals("endfacet")) { + System.out.println("Expected endfacet"); + br.close(); + throw new ModelFileFormatException(); + } + } + + TriangleArray triArray = new TriangleArray(verticies.size(), TriangleArray.COORDINATES); + for (int n = 0; n < verticies.size(); n++) { + triArray.setCoordinate(n, verticies.get(n)); + } + if (ap == null) ap = new Appearance(); + return new LeafModel(name, triArray, ap); + } + + private static Model3D loadStlBinaryFile(BufferedInputStream bis, byte[] header, Appearance ap, boolean bCalcNormal) throws IOException, ModelFileFormatException { + // Facet謨ー隱ュ縺ソ霎シ縺ソ + Integer numTri = readInt(bis); + if (numTri == null) { + System.out.println("Expected vertex count"); + bis.close(); + throw new ModelFileFormatException(); + } + + // Facet隱ュ縺ソ霎シ縺ソ + ArrayList verticies = new ArrayList(); + ArrayList normals = new ArrayList(); + Vector3f[] vertex = new Vector3f[] { + new Vector3f(), + new Vector3f(), + new Vector3f() + }; + + for (int n = 0; n < numTri; n++) { + Float nx = readFloat(bis); // 豕慕キ唸謌仙� + if (nx == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + Float ny = readFloat(bis); // 豕慕キ唳謌仙� + if (ny == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + Float nz = readFloat(bis); // 豕慕キ啝謌仙� + if (nz == null) { + System.out.println("Expected normal"); + bis.close(); + throw new ModelFileFormatException(); + } + + for (int i = 0; i < 3; i++) { + Float x = readFloat(bis); // X蠎ァ讓� + if (x == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + Float y = readFloat(bis); // Y蠎ァ讓� + if (y == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + Float z = readFloat(bis); // Z蠎ァ讓� + if (z == null) { + System.out.println("Expected vertex" + (i + 1)); + bis.close(); + throw new ModelFileFormatException(); + } + + verticies.add(new float[]{x, y, z}); + vertex[i].x = x; + vertex[i].y = y; + vertex[i].z = z; + } + if (bCalcNormal) { + vertex[1].sub(vertex[0]); + vertex[2].sub(vertex[0]); + vertex[1].normalize(); + vertex[2].normalize(); + vertex[0].cross(vertex[1], vertex[2]); + nx = vertex[0].x; + ny = vertex[0].y; + nz = vertex[0].z; + } + normals.add(new float[]{nx, ny, nz}); + normals.add(new float[]{nx, ny, nz}); + normals.add(new float[]{nx, ny, nz}); + + byte[] optionBin = new byte[2]; + if (bis.read(optionBin) < 2) { + System.out.println("Expected option"); + bis.close(); + throw new ModelFileFormatException(); + } + } + + TriangleArray triArray = new TriangleArray(verticies.size(), TriangleArray.COORDINATES | TriangleArray.NORMALS); + for (int n = 0; n < verticies.size(); n++) { + triArray.setCoordinate(n, verticies.get(n)); + triArray.setNormal(n, normals.get(n)); + } + + if (ap == null) ap = new Appearance(); + return new LeafModel(new String(header), triArray, ap); + } + + /** + * OBJ繝輔ぃ繧、繝ォ縺ョ隱ュ縺ソ霎シ縺ソ + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @param bCalcNormal 豕慕キ壹�ョ蜀崎ィ育ョ励r陦後≧縺� + * @return 隱ュ縺ソ霎シ繧薙□3D繝「繝�繝ォ + * @throws IOException + * @throws ModelFileFormatException + */ + public static Model3D loadObjFile(String fileName, boolean bCalcNormal) throws IOException, ModelFileFormatException { + InputStream in = new FileInputStream(new File(fileName)); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + HashMap appearances = new HashMap(); + ArrayList objects = new ArrayList(); + ArrayList groups = new ArrayList(); + ArrayList subGroups = new ArrayList(); + ArrayList objCoordinates = new ArrayList(); + ArrayList objTexCoordinates = new ArrayList(); + ArrayList objNormals = new ArrayList(); + ArrayList> subGroupCoordinateIndicies = new ArrayList>(); + ArrayList> subGroupTexCoordIndicies = new ArrayList>(); + ArrayList> subGroupNormalIndicies = new ArrayList>(); + String objectName = ""; + String groupName = ""; + String subGroupName = ""; + Appearance subGroupAp = null; + + String line = null; + while ((line = br.readLine()) != null) { + line.trim(); + String data[] = line.split(" "); + if (data[0].equals("#")) { + // 繧ウ繝。繝ウ繝� + } else if (data[0].equals("mtllib")) { + // 譚占ウェ繝輔ぃ繧、繝ォ縺ョ謖�螳� + String dir = new File(fileName).getParent(); + String mtlFileName = line.substring(line.indexOf(" ") + 1); + String mtlFilePath = new File(dir, mtlFileName).getPath(); + appearances = loadMtlFile(mtlFilePath); + } else if (data[0].equals("o")) { + // 繧ェ繝悶ず繧ァ繧ッ繝医�ョ髢句ァ� + if (groups.size() > 0 || subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 蜑阪�ョ繧ェ繝悶ず繧ァ繧ッ繝医�ョ繧ー繝ォ繝シ繝励�ョ谿九j縲√し繝悶げ繝ォ繝シ繝励�ョ谿九j縲√せ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧ェ繝悶ず繧ァ繧ッ繝医→縺励※霑ス蜉� + Model3D newObject = createRemainingObject(objectName, groups, groupName, objCoordinates, + objTexCoordinates, objNormals, subGroups, subGroupName, subGroupAp, + subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + objects.add(newObject); + groups.clear(); + } + + if (data.length < 1) { + System.out.println("Expected object's name"); + throw new ModelFileFormatException(); + } + objectName = data[1]; + } else if (data[0].equals("g")) { + // 繧ー繝ォ繝シ繝励�ョ髢句ァ� + if (subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 蜑阪�ョ繧ー繝ォ繝シ繝励�ョ繧オ繝悶げ繝ォ繝シ繝励�ョ谿九j縲√せ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧ー繝ォ繝シ繝励→縺励※霑ス蜉� + Model3D newGroup = createRemainingGroup(groupName, objCoordinates, objTexCoordinates, objNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + groups.add(newGroup); + subGroups.clear(); + } + + if (data.length < 1) { + System.out.println("Expected group's name"); + throw new ModelFileFormatException(); + } + groupName = data[1]; + } else if (data[0].equals("usemtl")) { + // 譚占ウェ縺ョ菴ソ逕ィ(繧オ繝悶げ繝ォ繝シ繝励�ョ髢句ァ�) + if (subGroupCoordinateIndicies.size() > 0) { + // 蜑阪�ョ繧オ繝悶げ繝ォ繝シ繝励�ョ繧ケ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧オ繝悶げ繝ォ繝シ繝励→縺励※霑ス蜉� + Model3D newSubGroup = createRemainingSubGroup(objCoordinates, objTexCoordinates, objNormals, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + subGroups.add(newSubGroup); + subGroupCoordinateIndicies.clear(); + subGroupTexCoordIndicies.clear(); + subGroupNormalIndicies.clear(); + } + + if (data.length < 1) { + System.out.println("Expected mtl file's name"); + throw new ModelFileFormatException(); + } + subGroupAp = appearances.get(data[1]); + subGroupName = groupName + "_" + data[1] + subGroups.size(); // 窶サ繧オ繝悶げ繝ォ繝シ繝励�ョ蜷榊燕縺碁㍾隍�縺励↑縺�繧医≧縺ォ縺吶k蜃ヲ逅� + } else if (data[0].equals("v")) { + // 鬆らせ蠎ァ讓吶ョ繝シ繧ソ + if (data.length < 4) { + System.out.println("Expected vertex coordinate"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + float z = Float.parseFloat(data[3]); + Vector3f v = new Vector3f(x, y, z); + objCoordinates.add(v); + } else if (data[0].equals("vt")) { + // 繝�繧ッ繧ケ繝√Ε蠎ァ讓吶ョ繝シ繧ソ + if (data.length < 3) { + System.out.println("Expected texture coordinate"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + Vector2f v = new Vector2f(x, y); + objTexCoordinates.add(v); + } else if (data[0].equals("vn")) { + // 豕慕キ壹�吶け繝医Ν繝�繝シ繧ソ + if (data.length < 4) { + System.out.println("Expected normal vector"); + throw new ModelFileFormatException(); + } + float x = Float.parseFloat(data[1]); + float y = Float.parseFloat(data[2]); + float z = Float.parseFloat(data[3]); + Vector3f v = new Vector3f(x, y, z); + objNormals.add(v); + } else if (data[0].equals("f")) { + // 繝昴Μ繧エ繝ウ(繧ケ繝医Μ繝�繝�)繝�繝シ繧ソ + if (data.length < 4) { + System.out.println("At least 3 verticies are needed"); + throw new ModelFileFormatException(); + } + ArrayList coordinateIndicies = new ArrayList(); + ArrayList texCoordIndicies = new ArrayList(); + ArrayList normalIndicies = new ArrayList(); + for (int n = 1; n < data.length; n++) { + String elements[] = data[n].split("/"); + if (elements.length >= 1) { + // 鬆らせ蠎ァ讓吶う繝ウ繝�繝�繧ッ繧ケ + coordinateIndicies.add(Integer.parseInt(elements[0]) - 1); + if (elements.length >= 2) { + // 繝�繧ッ繧ケ繝√Ε蠎ァ讓吶う繝ウ繝�繝�繧ッ繧ケ + if (elements[0].length() > 0) { + // "v//vn"縺ョ蝣エ蜷医b縺ゅk縺溘a + texCoordIndicies.add(Integer.parseInt(elements[1]) - 1); + } + if (elements.length >= 3) { + // 豕慕キ壹�吶け繝医Ν繧、繝ウ繝�繝�繧ッ繧ケ + normalIndicies.add(Integer.parseInt(elements[2]) - 1); + } + } + } + } + subGroupCoordinateIndicies.add(coordinateIndicies); + subGroupTexCoordIndicies.add(texCoordIndicies); + subGroupNormalIndicies.add(normalIndicies); + } + } + + // 繝輔ぃ繧、繝ォ隱ュ縺ソ霎シ縺ソ蠕後�ョ蜃ヲ逅� + if (groups.size() > 0 || subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 繧ケ繝医Μ繝�繝励�ョ谿九j縲√し繝悶げ繝ォ繝シ繝励�ョ谿九j縲√げ繝ォ繝シ繝励�ョ谿九j繧呈怙蠕後�ョ繧ェ繝悶ず繧ァ繧ッ繝医→縺励※霑ス蜉� + Model3D newObject = createRemainingObject(objectName, groups, + groupName, objCoordinates, objTexCoordinates, objNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + objects.add(newObject); + } + br.close(); + + if (objects.size() == 0) { + System.out.println("No polygon is included"); + throw new ModelFileFormatException(); + } else if (objects.size() == 1) { + if (groups.size() == 0) { + System.out.println("No polygon is included"); + throw new ModelFileFormatException(); + } else if (groups.size() == 1) { + // 繧ー繝ォ繝シ繝励′縺溘□荳�縺、縺ョ蝣エ蜷� + return groups.get(0); + } else { + // 繧ェ繝悶ず繧ァ繧ッ繝医′縺溘□荳�縺、縺ョ蝣エ蜷� + return objects.get(0); + } + } else { + // 繧ェ繝悶ず繧ァ繧ッ繝医′隍�謨ー縺ョ蝣エ蜷� + return new ContainerModel(fileName, (Model3D [])objects.toArray(new Model3D []{})); + } + } + + /** + * 繧ー繝ォ繝シ繝励�ョ谿九j繧呈眠縺励>繧ェ繝悶ず繧ァ繧ッ繝医→縺励※霑ス蜉� + * @param objectName 譁ー縺励>繧ェ繝悶ず繧ァ繧ッ繝医�ョ蜷榊燕 + * @param groups縲�譁ー縺励>繧ェ繝悶ず繧ァ繧ッ繝医r讒区�舌☆繧九げ繝ォ繝シ繝� + * @param groupName + * @param groupCoordinates + * @param groupTexCoordinates + * @param groupNormals + * @param subGroups + * @param subGroupName + * @param subGroupAp + * @param subGroupCoordinateIndicies + * @param subGroupTexCoordIndicies + * @param subGroupNormalIndicies + * @return + */ + private static Model3D createRemainingObject(String objectName, ArrayList groups, + String groupName, ArrayList groupCoordinates, ArrayList groupTexCoordinates, + ArrayList groupNormals, ArrayList subGroups, + String subGroupName, Appearance subGroupAp, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + if (subGroups.size() > 0 || subGroupCoordinateIndicies.size() > 0) { + // 繧ケ繝医Μ繝�繝励�ョ谿九j縲√し繝悶げ繝ォ繝シ繝励�ョ谿九j繧呈眠縺励>繧ー繝ォ繝シ繝励→縺励※霑ス蜉� + Model3D newGroup = createRemainingGroup(groupName, groupCoordinates, groupTexCoordinates, groupNormals, subGroups, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + groups.add(newGroup); + subGroups.clear(); + } + + // 繧ー繝ォ繝シ繝励�ョ谿九j繧呈眠縺励>繧ェ繝悶ず繧ァ繧ッ繝医→縺励※菴懈�� + Model3D newObject = new ContainerModel(objectName, (Model3D [])groups.toArray(new Model3D []{})); + return newObject; + } + + /** + * 繧オ繝悶げ繝ォ繝シ繝励�ョ谿九j繧呈眠縺励>繧ー繝ォ繝シ繝励→縺励※菴懈�� + * @param groupName縲�譁ー縺励>繧ー繝ォ繝シ繝励�ョ蜷榊燕 + * @param groupCoordinates縲�鬆らせ蠎ァ讓吶�ョ蛟、縺ョ繝ェ繧ケ繝� + * @param groupTexCoordinates縲�繝�繧ッ繧ケ繝√Ε蠎ァ讓吶�ョ蛟、縺ョ繝ェ繧ケ繝� + * @param groupNormals縲�豕慕キ壹�吶け繝医Ν縺ョ蛟、縺ョ繝ェ繧ケ繝� + * @param subGroups 譁ー縺励>繧ー繝ォ繝シ繝励r讒区�舌☆繧九し繝悶げ繝ォ繝シ繝� + * @param subGroupName + * @param subGroupAp + * @param subGroupCoordinateIndicies + * @param subGroupTexCoordIndicies + * @param subGroupNormalIndicies + * @return + */ + private static Model3D createRemainingGroup(String groupName, + ArrayList groupCoordinates, ArrayList groupTexCoordinates, ArrayList groupNormals, + ArrayList subGroups, + String subGroupName, Appearance subGroupAp, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + if (subGroupCoordinateIndicies.size() > 0) { + // 繧ケ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧オ繝悶げ繝ォ繝シ繝励→縺励※霑ス蜉� + Model3D newSubGroup = createRemainingSubGroup(groupCoordinates, groupTexCoordinates, groupNormals, + subGroupName, subGroupAp, subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + subGroups.add(newSubGroup); + subGroupCoordinateIndicies.clear(); + subGroupTexCoordIndicies.clear(); + subGroupNormalIndicies.clear(); + } + + // 繧オ繝悶げ繝ォ繝シ繝励�ョ谿九j繧呈眠縺励>繧ー繝ォ繝シ繝励→縺励※菴懈�� + return new ContainerModel(groupName, (Model3D [])subGroups.toArray(new Model3D []{})); + } + + /** + * 繧ケ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧オ繝悶げ繝ォ繝シ繝励→縺励※菴懈�� + * @param groupCoordinates + * @param groupTexCoordinates + * @param groupNormals + * @param subGroupName 譁ー縺励>繧オ繝悶げ繝ォ繝シ繝励�ョ蜷榊燕 + * @param subGroupAp 譁ー縺励>繧オ繝悶げ繝ォ繝シ繝励�ョ陦ィ髱「螻樊�ァ + * @param subGroupCoordinateIndicies縲�鬆らせ蠎ァ讓吶∈縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @param subGroupTexCoordIndicies 繝�繧ッ繧ケ繝√Ε蠎ァ讓吶∈縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @param subGroupNormalIndicies縲�豕慕キ壹�吶け繝医Ν縺ク縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @return + */ + private static Model3D createRemainingSubGroup(ArrayList groupCoordinates, + ArrayList groupTexCoordinates, ArrayList groupNormals, + String subGroupName, Appearance subGroupAp, ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + // 繧ケ繝医Μ繝�繝励�ョ谿九j繧呈眠縺励>繧オ繝悶げ繝ォ繝シ繝励→縺励※菴懈�� + // Geometry 縺ョ菴懈�� + GeometryArray geometry = createGeometryArray(groupCoordinates, groupTexCoordinates, groupNormals, + subGroupCoordinateIndicies, subGroupTexCoordIndicies, subGroupNormalIndicies); + // Object3D 縺ョ菴懈�� + return new LeafModel(subGroupName, geometry, subGroupAp); + } + + /** + * 繧ケ繝医Μ繝�繝励�ョ繧、繝ウ繝�繝�繧ッ繧ケ諠�蝣ア縺九i繧ク繧ェ繝。繝医Μ繝シ繧剃ス懈�� + * @param groupCoordinates 鬆らせ蠎ァ讓吶�ョ蛟、縺ョ繝ェ繧ケ繝� + * @param groupTexCoordinates 繝�繧ッ繧ケ繝√Ε蠎ァ讓吶�ョ蛟、縺ョ繝ェ繧ケ繝� + * @param groupNormals 豕慕キ壹�吶け繝医Ν縺ョ蛟、縺ョ繝ェ繧ケ繝� + * @param subGroupCoordinateIndicies 鬆らせ蠎ァ讓吶∈縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @param subGroupTexCoordIndicies 繝�繧ッ繧ケ繝√Ε蠎ァ讓吶∈縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @param subGroupNormalIndicies 豕慕キ壹�吶け繝医Ν縺ク縺ョ繧、繝ウ繝�繝�繧ッ繧ケ + * @return 菴懈�舌@縺溘ず繧ェ繝。繝医Μ繝シ + */ + private static GeometryArray createGeometryArray( + ArrayList groupCoordinates, + ArrayList groupTexCoordinates, + ArrayList groupNormals, + ArrayList> subGroupCoordinateIndicies, + ArrayList> subGroupTexCoordIndicies, + ArrayList> subGroupNormalIndicies) { + int vertexCount = 0; + int indexCount = 0; + for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { + ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); + vertexCount += coordinateIndicies.size(); + indexCount += (coordinateIndicies.size() - 2) * 3; + } + int vertexFormat = IndexedGeometryArray.COORDINATES; + if (subGroupTexCoordIndicies.size() > 0) { + vertexFormat |= IndexedGeometryArray.TEXTURE_COORDINATE_2; + } + if (subGroupNormalIndicies.size() > 0) { + vertexFormat |= IndexedGeometryArray.NORMALS; + } + IndexedGeometryArray geometry = new IndexedTriangleArray(vertexCount, vertexFormat, indexCount); + int vertexNum = 0; + int index = 0; + for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { + ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); + ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); + ArrayList normalIndicies = subGroupNormalIndicies.get(n); + Vector3f c = groupCoordinates.get(coordinateIndicies.get(0)); + geometry.setCoordinate(vertexNum, new float[]{c.x, c.y, c.z}); + c = groupCoordinates.get(coordinateIndicies.get(1)); + geometry.setCoordinate(vertexNum + 1, new float[]{c.x, c.y, c.z}); + if (texCoordIndicies.size() > 0) { + Vector2f t = groupTexCoordinates.get(texCoordIndicies.get(0)); + geometry.setTextureCoordinate(vertexNum, new float[]{t.x, t.y}); + t = groupTexCoordinates.get(texCoordIndicies.get(1)); + geometry.setTextureCoordinate(vertexNum + 1, new float[]{t.x, t.y}); + } + if (normalIndicies.size() > 0) { + Vector3f nr = groupNormals.get(normalIndicies.get(0)); + geometry.setNormal(vertexNum, nr); + nr = groupNormals.get(normalIndicies.get(1)); + geometry.setNormal(vertexNum + 1, nr); + } + int firstCount = vertexNum; + vertexNum += 2; + for (int i = 2; i < coordinateIndicies.size(); i++) { + c = groupCoordinates.get(coordinateIndicies.get(i)); + geometry.setCoordinate(vertexNum, new float[]{c.x, c.y, c.z}); + geometry.setCoordinateIndex(index, firstCount); + geometry.setCoordinateIndex(index + 1, vertexNum - 1); + geometry.setCoordinateIndex(index + 2, vertexNum); + if (texCoordIndicies.size() > 0) { + Vector2f t = groupTexCoordinates.get(texCoordIndicies.get(i)); + geometry.setTextureCoordinate(vertexNum, new float[]{t.x, t.y}); + geometry.setTextureCoordinateIndex(index, firstCount); + geometry.setTextureCoordinateIndex(index + 1, vertexNum - 1); + geometry.setTextureCoordinateIndex(index + 2, vertexNum); + } + if (normalIndicies.size() > 0) { + Vector3f nr = groupNormals.get(normalIndicies.get(i)); + geometry.setNormal(vertexNum, nr); + geometry.setNormalIndex(index, firstCount); + geometry.setNormalIndex(index + 1, vertexNum - 1); + geometry.setNormalIndex(index + 2, vertexNum); + } + vertexNum += 1; + index += 3; + } + } + return geometry; +// ArrayList coordinateIndiciesMap = new ArrayList(); +// ArrayList texCoordIndiciesMap = new ArrayList(); +// ArrayList normalIndiciesMap = new ArrayList(); +// int indexCount = 0; +// int[] stripIndexCounts = new int[subGroupCoordinateIndicies.size()]; +// for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { +// ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); +// ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); +// ArrayList normalIndicies = subGroupNormalIndicies.get(n); +// indexCount += coordinateIndicies.size(); +// stripIndexCounts[n] = coordinateIndicies.size(); +// for (int i = 0; i < coordinateIndicies.size(); i++) { +// int c = coordinateIndicies.get(i); +// int coordinateIndex = coordinateIndiciesMap.indexOf(c); +// if (coordinateIndex == -1) { +// coordinateIndex = coordinateIndiciesMap.size(); +// coordinateIndiciesMap.add(c); +// } +// coordinateIndicies.set(i, coordinateIndex); +// if (texCoordIndicies.size() > 0) { +// int t = texCoordIndicies.get(i); +// int texCoordIndex = texCoordIndiciesMap.indexOf(t); +// if (texCoordIndex == -1) { +// texCoordIndex = texCoordIndiciesMap.size(); +// texCoordIndiciesMap.add(t); +// } +// texCoordIndicies.set(i, texCoordIndex); +// } +// if (normalIndicies.size() > 0) { +// int nr = normalIndicies.get(i); +// int normalIndex = normalIndiciesMap.indexOf(nr); +// if (normalIndex == -1) { +// normalIndex = normalIndiciesMap.size(); +// normalIndiciesMap.add(nr); +// } +// normalIndicies.set(i, normalIndex); +// } +// } +// } +// int vertexCount = coordinateIndiciesMap.size(); +// int vertexFormat = IndexedGeometryArray.COORDINATES; +// if (texCoordIndiciesMap.size() > 0) { +// if (vertexCount < texCoordIndiciesMap.size()) vertexCount = texCoordIndiciesMap.size(); +// vertexFormat |= IndexedGeometryArray.TEXTURE_COORDINATE_2; +// } +// if (normalIndiciesMap.size() > 0) { +// if (vertexCount < normalIndiciesMap.size()) vertexCount = normalIndiciesMap.size(); +// vertexFormat |= IndexedGeometryArray.NORMALS; +// } +// IndexedGeometryArray geometry = new IndexedTriangleFanArray(vertexCount, vertexFormat, indexCount, stripIndexCounts); +// for (int n = 0; n < coordinateIndiciesMap.size(); n++) { +// Vector3f c = groupCoordinates.get(coordinateIndiciesMap.get(n)); +// geometry.setCoordinate(n, new float[]{c.x, c.y, c.z}); +// } +// for (int n = 0; n < texCoordIndiciesMap.size(); n++) { +// Vector2f t = groupTexCoordinates.get(texCoordIndiciesMap.get(n)); +// geometry.setTextureCoordinate(n, new float[]{t.x, t.y}); +// } +// for (int n = 0; n < normalIndiciesMap.size(); n++) { +// Vector3f nr = groupNormals.get(normalIndiciesMap.get(n)); +// geometry.setNormal(n, nr); +// } +// int index = 0; +// for (int n = 0; n < subGroupCoordinateIndicies.size(); n++) { +// ArrayList coordinateIndicies = subGroupCoordinateIndicies.get(n); +// ArrayList texCoordIndicies = subGroupTexCoordIndicies.get(n); +// ArrayList normalIndicies = subGroupNormalIndicies.get(n); +// for (int i = 0; i < coordinateIndicies.size(); i++) { +// geometry.setCoordinateIndex(index, coordinateIndicies.get(i)); +// if (texCoordIndicies.size() > 0) { +// geometry.setTextureCoordinateIndex(index, texCoordIndicies.get(i)); +// } +// if (normalIndicies.size() > 0) { +// geometry.setNormalIndex(index, normalIndicies.get(i)); +// } +// index++; +// } +// } +// return geometry; + } + + /** + * MTL繝輔ぃ繧、繝ォ縺ョ隱ュ縺ソ霎シ縺ソ + * @param res 繝ェ繧ス繝シ繧ケ + * @param fileName 繝輔ぃ繧、繝ォ蜷� + * @return縲�譚占ウェ蜷阪°繧芽。ィ髱「螻樊�ァ繧ェ繝悶ず繧ァ繧ッ繝医∈縺ョ繝槭ャ繝斐Φ繧ー + * @throws IOException + * @throws ModelFileFormatException + */ + private static HashMap loadMtlFile(String fileName) throws IOException, ModelFileFormatException { + InputStream in = new FileInputStream(new File(fileName)); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + HashMap appearances = new HashMap(); + String materialName; + Appearance ap = null; + Material m = null; + + String line = null; + while ((line = br.readLine()) != null) { + line.trim(); + String data[] = line.split(" "); + if (data[0].equals("newmtl")) { + if (data.length < 1) { + System.out.println("Expected material's name"); + throw new ModelFileFormatException(); + } + materialName = data[1]; + m = new Material(); + ap = new Appearance(); + ap.setMaterial(m); + appearances.put(materialName, ap); + } else if (data[0].equals("Kd")) { + if (data.length < 4) { + System.out.println("Expected diffuse color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setDiffuseColor(r, g, b); + } else if (data[0].equals("Ka")) { + if (data.length < 4) { + System.out.println("Expected ambient color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setAmbientColor(r, g, b); + } else if (data[0].equals("Ks")) { + if (data.length < 4) { + System.out.println("Expected specular color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setSpecularColor(r, g, b); + } else if (data[0].equals("Tr")) { + if (data.length < 4) { + System.out.println("Expected emissive color"); + throw new ModelFileFormatException(); + } + float r = Float.parseFloat(data[1]); + float g = Float.parseFloat(data[2]); + float b = Float.parseFloat(data[3]); + m.setEmissiveColor(r, g, b); + } else if (data[0].equals("Ns")) { + if (data.length < 2) { + System.out.println("Expected shiness value"); + throw new ModelFileFormatException(); + } + float s = Float.parseFloat(data[1]); + m.setShininess(s); + } else if (data[0].equals("map_Kd")) { + if (data.length < 2) { + System.out.println("Expected texture file's name"); + throw new ModelFileFormatException(); + } + // 繝�繧ッ繧ケ繝√Ε繝輔ぃ繧、繝ォ隱ュ縺ソ霎シ縺ソ + try { + String dir = new File(fileName).getParent(); + String texFileName = line.substring(line.indexOf(" ") + 1); + String texFilePath = new File(dir, texFileName).getPath(); + InputStream inTex = new FileInputStream(new File(texFilePath)); + TextureLoader texLoader = new TextureLoader(inTex, TextureLoader.BY_REFERENCE | TextureLoader.Y_UP); + ap.setTexture(texLoader.getTexture()); + inTex.close(); + } catch (IOException e) { + System.out.println("Not exist texture file: " + line.substring(line.indexOf(" ") + 1)); + } + } + } + return appearances; + } + + private static Integer readInt(BufferedInputStream bis) throws IOException { + byte[] intBin = new byte[4]; + if (bis.read(intBin, 3, 1) < 1) { + return null; + } + if (bis.read(intBin, 2, 1) < 1) { + return null; + } + if (bis.read(intBin, 1, 1) < 1) { + return null; + } + if (bis.read(intBin, 0, 1) < 1) { + return null; + } + return ByteBuffer.wrap(intBin).getInt(); + } + + private static Float readFloat(BufferedInputStream bis) throws IOException { + byte[] floatBin = new byte[4]; + if (bis.read(floatBin, 3, 1) < 1) { + return null; + } + if (bis.read(floatBin, 2, 1) < 1) { + return null; + } + if (bis.read(floatBin, 1, 1) < 1) { + return null; + } + if (bis.read(floatBin, 0, 1) < 1) { + return null; + } + return ByteBuffer.wrap(floatBin).getFloat(); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFileFormatException.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFileFormatException.java new file mode 100644 index 0000000..c76d858 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ModelFileFormatException.java @@ -0,0 +1,5 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +public class ModelFileFormatException extends Exception { + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Movable.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Movable.java new file mode 100644 index 0000000..82331e3 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Movable.java @@ -0,0 +1,7 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.framework.physics.Ground; + +public interface Movable extends Placeable { + public void motion(long interval, Ground ground); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/OBB.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/OBB.java new file mode 100644 index 0000000..833a3f8 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/OBB.java @@ -0,0 +1,342 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.java3d.BoundingPolytope; +import org.ntlab.radishforandroidstudio.java3d.BoundingSphere; +import org.ntlab.radishforandroidstudio.java3d.Matrix4d; +import org.ntlab.radishforandroidstudio.java3d.Point3d; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; +import org.ntlab.radishforandroidstudio.java3d.Vector4d; + + +public class OBB implements Cloneable { + public ArrayList vertexList = new ArrayList(); + public Vector4d[] plane = new Vector4d[6]; + public BoundingPolytope bp = null; + public static int edges[][] = { { 0, 1 }, { 0, 2 }, { 1, 3 }, { 2, 3 }, + { 0, 4 }, { 1, 5 }, { 3, 7 }, { 2, 6 }, { 4, 5 }, { 4, 6 }, + { 5, 7 }, { 6, 7 } }; + static boolean[][] inside = new boolean[8][6]; + static boolean[] inside2 = new boolean[8]; + + public OBB() { + } + + public OBB(Vector3d v1, Vector3d v2, Vector3d v3, Vector3d v4, + Vector3d v5, Vector3d v6, Vector3d v7, Vector3d v8) { + vertexList.add(v1); + vertexList.add(v2); + vertexList.add(v3); + vertexList.add(v4); + vertexList.add(v5); + vertexList.add(v6); + vertexList.add(v7); + vertexList.add(v8); + createPlanes(); + } + + public void addVertex(Vector3d v) { + vertexList.add(v); + } + + public Vector3d getVertex(int i) { + return vertexList.get(i); + } + + public BoundingSphere getBoundingSphere() { + double radius = 0.0; + Point3d p = new Point3d(); + Vector3d cv = new Vector3d(); + for (int i = 0; i < vertexList.size() - 1; i++) { + for (int j = i + 1; j < vertexList.size(); j++) { + Vector3d v = new Vector3d(); + v.sub(vertexList.get(i), vertexList.get(j)); + if (radius < v.length()) { + radius = v.length(); + cv.add(vertexList.get(i), vertexList.get(j)); + cv.scale(0.5); + p.x = cv.x; + p.y = cv.y; + p.z = cv.z; + } + } + } + BoundingSphere s = new BoundingSphere(p, radius / 2); + return s; + } + + /** + * 頂点情報を元に面および境界多面体を生成する + */ + public void createPlanes() { + // 面の作成 + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + Vector3d v3 = new Vector3d(); + Vector4d[] plane = new Vector4d[6]; + + // 0231 + v1 = vertexList.get(0); + v2.sub(vertexList.get(2), vertexList.get(0)); + v3.sub(vertexList.get(1), vertexList.get(0)); + Vector3d n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[0] = new Vector4d(); + plane[0].set(n.x, n.y, n.z, -n.dot(v1)); + + + // 0154 + v1 = vertexList.get(0); + v2.sub(vertexList.get(1), vertexList.get(0)); + v3.sub(vertexList.get(4), vertexList.get(0)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[1] = new Vector4d(); + plane[1].set(n.x, n.y, n.z, -n.dot(v1)); + + // 1375 + v1 = vertexList.get(1); + v2.sub(vertexList.get(3), vertexList.get(1)); + v3.sub(vertexList.get(5), vertexList.get(1)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[2] = new Vector4d(); + plane[2].set(n.x, n.y, n.z, -n.dot(v1)); + + // 4576 + v1 = vertexList.get(6); + v2.sub(vertexList.get(4), vertexList.get(6)); + v3.sub(vertexList.get(7), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[3] = new Vector4d(); + plane[3].set(n.x, n.y, n.z, -n.dot(v1)); + + // 0462 + v1 = vertexList.get(6); + v2.sub(vertexList.get(2), vertexList.get(6)); + v3.sub(vertexList.get(4), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[4] = new Vector4d(); + plane[4].set(n.x, n.y, n.z, -n.dot(v1)); + + // 2673 + v1 = vertexList.get(6); + v2.sub(vertexList.get(7), vertexList.get(6)); + v3.sub(vertexList.get(2), vertexList.get(6)); + n = new Vector3d(); + n.cross(v2, v3); + n.normalize(); + plane[5] = new Vector4d(); + plane[5].set(n.x, n.y, n.z, -n.dot(v1)); + + this.plane = plane; + + bp = new BoundingPolytope(); + bp.setPlanes(plane); + } + + /** + * 平面との衝突判定 + * @param plane 衝突判定の対象となる平面 + * @return 衝突判定の結果(衝突していない場合はnull) + */ + public CollisionResult intersect(Vector4d plane) { + int i = 0; + boolean inside = false; + boolean outside = false; + int count = 0; + Vector3d center = new Vector3d(0, 0, 0); + double l = Math.sqrt(plane.x * plane.x + plane.y * plane.y + plane.z * plane.z); + double length; + double deepest = 0.0; + Vector3d v; + for (i = 0; i < vertexList.size(); i++) { + v = vertexList.get(i); + if (GeometryUtility.inside(v, plane)) { + inside = true; + length = -(v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w) / l; + if (length > deepest + GeometryUtility.TOLERANCE) { + center.set(v); + count = 1; + deepest = length; + } else if (length >= deepest - GeometryUtility.TOLERANCE) { + center.add(v); + count++; + } + } else { + outside = true; + } + } + + if (!inside || !outside) { + // 全頂点が外側か全頂点が内側の場合 + return null; + } + + center.scale(1.0 / count); + + CollisionResult cr = new CollisionResult(); + cr.length = deepest; + cr.collisionPoint.setX(center.x); + cr.collisionPoint.setY(center.y); + cr.collisionPoint.setZ(center.z); + cr.normal.setX(plane.x / l); + cr.normal.setY(plane.y / l); + cr.normal.setZ(plane.z / l); + + return cr; + } + + /** + * OBBとの衝突判定 + * @param o 衝突判定の対象となるOBB + * @return 衝突判定の結果(衝突していない場合はnull) + */ + public CollisionResult intersect(OBB o) { + // oの各頂点がthisの各面の内側にあるかを調べる + // i:頂点 j:面 + for (int i = 0; i < o.vertexList.size(); i++) { + for (int j = 0; j < plane.length; j++) { + inside[i][j] = GeometryUtility.inside(o.vertexList.get(i), plane[j]); + } + } + + boolean collision = false; + int count = 0; + Vector3d center = new Vector3d(0, 0, 0); + + // oの各頂点がthisに包含されているか? + for (int v = 0; v < 8; v++) { + boolean f1 = false; + for (int p = 0; p < 6; p++) { + f1 = inside[v][p]; + if (!f1) { + break; + } + } + inside2[v] = f1; + if (f1) { + collision = true; + center.add(o.vertexList.get(v)); + count++; + } + } + + if (!collision) { + // 辺が相手の面と交わっているか? + for (int p = 0; p < 6; p++) { + Vector3d intersection = null; + for (int e = 0; e < 12; e++) { + if (inside[edges[e][0]][p] == !inside[edges[e][1]][p]) { + intersection = GeometryUtility.intersect( + plane[p], + o.vertexList.get(edges[e][0]), + o.vertexList.get(edges[e][1])); + boolean bInside = true; + for (int p2 = 0; p2 < 6; p2++) { + if (p != p2 + && !GeometryUtility.inside(intersection, plane[p2])) { + bInside = false; + break; + } + } + if (bInside) { + collision = true; + center.add(intersection); + count++; + } + } + } + } + } + + if (!collision) + return null; + + center.scale(1.0 / count); + + CollisionResult cr = new CollisionResult(); + + double lMin = 0; + int cm = -1; + Vector3d normal = null; + for (int m = 0; m < plane.length; m++) { + Vector3d n = new Vector3d(plane[m].x, plane[m].y, plane[m].z); + double l = -(center.x * plane[m].x + center.y * plane[m].y + center.z + * plane[m].z + plane[m].w) / n.length(); + if (lMin > l || cm == -1) { + lMin = l; + cm = m; + normal = n; + } + } + cr.length = lMin; + cr.collisionPoint.setVector3d(center); + cr.normal.setX(plane[cm].x / normal.length()); + cr.normal.setY(plane[cm].y / normal.length()); + cr.normal.setZ(plane[cm].z / normal.length()); + + return cr; + + } + + public Object clone() { + OBB obb = new OBB(); + obb.plane = new Vector4d[6]; + for (int i = 0; i < plane.length; i++) { + obb.plane[i] = (Vector4d) plane[i].clone(); + } + for (int i = 0; i < vertexList.size(); i++) { + obb.vertexList.add((Vector3d) vertexList.get(i).clone()); + } + obb.bp = new BoundingPolytope(obb.plane); + + // System.out.println("veretexListSIZE:"+vertexList.size()); + return obb; + } + + public void transform(Transform3D t) { + // TODO Auto-generated method stub + bp.transform(t); + bp.getPlanes(plane); + // for(int i = 0; i" + plane[i].x + "," + plane[i].y + "," + + // plane[i].z + "," + plane[i].w); + // } + for (int i = 0; i < vertexList.size(); i++) { + // System.out.println("vertex"); + // System.out.print(vertexList.get(i).x + "," + vertexList.get(i).y + // + "," + vertexList.get(i).z); + Matrix4d mat4d = new Matrix4d(); + t.get(mat4d); + double x = mat4d.m00 * vertexList.get(i).x + mat4d.m01 + * vertexList.get(i).y + mat4d.m02 * vertexList.get(i).z + + mat4d.m03; + double y = mat4d.m10 * vertexList.get(i).x + mat4d.m11 + * vertexList.get(i).y + mat4d.m12 * vertexList.get(i).z + + mat4d.m13; + double z = mat4d.m20 * vertexList.get(i).x + mat4d.m21 + * vertexList.get(i).y + mat4d.m22 * vertexList.get(i).z + + mat4d.m23; + vertexList.get(i).x = x; + vertexList.get(i).y = y; + vertexList.get(i).z = z; + // t.transform(vertexList.get(i)); + // System.out.println("-->" + vertexList.get(i).x + "," + + // vertexList.get(i).y + "," + vertexList.get(i).z); + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Object3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Object3D.java new file mode 100644 index 0000000..8f07679 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Object3D.java @@ -0,0 +1,535 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; + +import org.ntlab.radishforandroidstudio.java3d.Appearance; +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.BoundingSphere; +import org.ntlab.radishforandroidstudio.java3d.Box; +import org.ntlab.radishforandroidstudio.java3d.Cone; +import org.ntlab.radishforandroidstudio.java3d.Cylinder; +import org.ntlab.radishforandroidstudio.java3d.Geometry; +import org.ntlab.radishforandroidstudio.java3d.GeometryStripArray; +import org.ntlab.radishforandroidstudio.java3d.IndexedGeometryArray; +import org.ntlab.radishforandroidstudio.java3d.Leaf; +import org.ntlab.radishforandroidstudio.java3d.Node; +import org.ntlab.radishforandroidstudio.java3d.Primitive; +import org.ntlab.radishforandroidstudio.java3d.Quat4d; +import org.ntlab.radishforandroidstudio.java3d.Shape3D; +import org.ntlab.radishforandroidstudio.java3d.Sphere; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; +import org.ntlab.radishforandroidstudio.java3d.TriangleArray; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class Object3D extends BaseObject3D { + public String name; + public TransformGroup pos; + public TransformGroup rot; + public TransformGroup scale; + public Object3D[] children = new Object3D[0]; + private Position3D position; + protected Quaternion3D quaternion; + + private OBB obb = new OBB(); +// private boolean bLOD = false; +// private LOD lodNode = null; + + public BoundingSphere bs = null; + private UndoBuffer undoBuffer = new UndoBuffer(); + + public Object3D(String name, Primitive prim) { + init(name); + center.addChild(prim); + } + + public Object3D(String name, Leaf node) { + init(name); + center.addChild(node); + } + + public Object3D(String name, Geometry g, Appearance a) { + this(name, new Shape3D(g, a)); + } + + public Object3D(String name, Object3D[] children) { + init(name); + this.children = children; + for (int i = 0; i < children.length; i++) { + center.addChild(children[i].getTransformGroupToPlace()); + } + } + + public Object3D(String name, Object3D[] children, Transform3D defaultTransform) { + this(name, children); + if (defaultTransform == null) return; + pos.setTransform(defaultTransform); + } + + // コピーコンストラクタ + public Object3D(Object3D obj) { + this.name = new String(obj.name); + if (obj.position != null) { + this.position = new Position3D(obj.position); + } else { + this.position = new Position3D(); + } + if (obj.getQuaternion() != null) { + this.quaternion = new Quaternion3D(obj.getQuaternion()); + } else { + this.quaternion = new Quaternion3D(); + } + Transform3D transPos = new Transform3D(); + obj.pos.getTransform(transPos); + this.pos = new TransformGroup(transPos); + Transform3D transRot = new Transform3D(); + obj.rot.getTransform(transRot); + this.rot = new TransformGroup(transRot); + Transform3D transScale = new Transform3D(); + obj.scale.getTransform(transScale); + this.scale = new TransformGroup(transScale); + Transform3D transCenter = new Transform3D(); + obj.center.getTransform(transCenter); + this.center = new TransformGroup(transCenter); + this.pos.addChild(this.rot); + this.rot.addChild(this.scale); + this.scale.addChild(this.center); +// this.pos.addChild(this.scale); +//// this.rot.addChild(this.scale); +// this.scale.addChild(this.center); + if (obj.hasChildren()) { + this.children = new Object3D[obj.children.length]; + for (int i = 0; i < obj.children.length; i++) { + this.children[i] = new Object3D(obj.children[i]); + this.center.addChild(this.children[i].pos); + } + } else { + this.children = new Object3D[0]; + Enumeration nodes = obj.getPrimitiveNodes(); + while (nodes.hasMoreElements()) { + Node node = nodes.nextElement(); + if (node != null && node instanceof Shape3D) { + Shape3D shape = (Shape3D)node; + Appearance a = (Appearance)shape.getAppearance().cloneNodeComponent(); + this.center.addChild(new Shape3D((Geometry) shape.getGeometry(), a)); + } else if (node != null && node instanceof Primitive) { + Primitive primitive = (Primitive)node; + primitive = (Primitive)primitive.cloneTree(); + this.center.addChild(primitive); + } + } + } + if (obj.obb != null) { + this.obb = obj.obb; + } + if (obj.bs != null) { + this.bs = obj.bs; + } + this.undoBuffer = new UndoBuffer(obj.undoBuffer); + } + + // 自分を複製する(クローンを作る) + public Object3D duplicate() { + Object3D obj = new Object3D(this); + return obj; + } + + protected void init(String name) { + this.name = new String(name); + pos = new TransformGroup(); + rot = new TransformGroup(); + scale = new TransformGroup(); + center = new TransformGroup(); + pos.addChild(rot); + rot.addChild(scale); + scale.addChild(center); + } + + public Node getPrimitiveNode() { + if (hasChildren()) return null; +// if (!bLOD) { + return (Node)center.getChild(0); + // TODO 未実装 +// } else { +// // LOD の場合 +// return lodNode.getSwitch(0).getChild(0); +// } + } + + public Enumeration getPrimitiveNodes() { + if (hasChildren()) return null; +// if (!bLOD) { + return (Enumeration)center.getAllChildren(); + // TODO 未実装 +// } else { +// // LOD の場合 +// return lodNode.getSwitch(0).getAllChildren(); +// } + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return pos; + } + + public void setPosition(Position3D p) { + position = (Position3D) p.clone(); + Vector3d v = new Vector3d(p.getX(), p.getY(), p.getZ()); + Transform3D trans = new Transform3D(); + trans.set(v); + pos.setTransform(trans); + } + + public Position3D getPosition3D() { + return (Position3D) position.clone(); + } + + public void scale(double s) { + Transform3D trans = new Transform3D(); + trans.setScale(s); + scale.setTransform(trans); + } + + public void scale(double x, double y, double z) { + Transform3D trans = new Transform3D(); + trans.setScale(new Vector3d(x, y, z)); + scale.setTransform(trans); + } + + // キャラクターを回転させる + public void rotate(double vx, double vy, double vz, double a) { + AxisAngle4d t = new AxisAngle4d(vx, vy, vz, a); + Transform3D trans = new Transform3D(); + trans.setRotation(t); + rot.setTransform(trans); + } + + public void apply(Property3D p, boolean enableUndo) { + if (enableUndo) { + undoBuffer.push(p); + } + p.applyTo(this); + } + +// undoするポイントを設定する。 + public void setUndoMark() { + undoBuffer.setUndoMark(); + } + +// undoする。 + public void undo() { + Iterator iterator = undoBuffer.undo().iterator(); + while (iterator.hasNext()) { + Property3D p = iterator.next(); + p.applyTo(this); + } + } + + // objectに子供がいるかどうかを調べる + public boolean hasChildren() { + if (this.children != null && this.children.length > 0) + return true; + else + return false; + } + + /** + * 部品オブジェクトを探す + * @param partName 部品名 + * @return partName を持つ部品オブジェクト + */ + public Object3D getPart(String partName) { + if (partName.equals(this.name)) return this; + + for (int i = 0; i < children.length; i++) { + if (children[i] != null) { + Object3D obj = children[i].getPart(partName); + if (obj != null) return obj; + } + } + return null; + } + + protected void rotate(Quat4d quat) { + Transform3D trans = new Transform3D(); + trans.setRotation(quat); + rot.setTransform(trans); + } + + public void accept(ObjectVisitor objectVisitor) { + int i; + objectVisitor.preVisit(this); + if (children != null) { + for (i = 0; i < children.length; i++) + children[i].accept(objectVisitor); + } + objectVisitor.postVisit(this); + } + + // Quaternion3D の applyTo() 以外からは呼ばないこと + void setQuaternion(Quaternion3D quaternion) { + this.quaternion = quaternion; + rotate(quaternion.getQuat()); + } + + public Quaternion3D getQuaternion() { + return quaternion; + } + + public OBB getOBB(int pattern) { + if (obb == null || obb.bp == null) { + Node node = getPrimitiveNode(); + if (node == null) return null; + if (node instanceof Box) { + // Boxの場合 + Box box = ((Box)node); + double xDim = box.getXdimension(); + double yDim = box.getYdimension(); + double zDim = box.getZdimension(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Cylinder) { + // Cylinderの場合 + Cylinder cylinder = ((Cylinder)node); + double xDim = cylinder.getRadius(); + double yDim = cylinder.getHeight() / 2; + double zDim = cylinder.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Cone) { + // Coneの場合 + Cone cone = ((Cone)node); + double xDim = cone.getRadius(); + double yDim = cone.getHeight() / 2; + double zDim = cone.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else if (node instanceof Sphere) { + // Sphereの場合 + Sphere sphere = ((Sphere)node); + double xDim = sphere.getRadius(); + double yDim = sphere.getRadius(); + double zDim = sphere.getRadius(); + obb = new OBB(new Vector3d(-xDim, -yDim, -zDim), + new Vector3d(-xDim, yDim, -zDim), + new Vector3d(-xDim, -yDim, zDim), + new Vector3d(-xDim, yDim, zDim), + new Vector3d(xDim, -yDim, -zDim), + new Vector3d(xDim, yDim, -zDim), + new Vector3d(xDim, -yDim, zDim), + new Vector3d(xDim, yDim, zDim)); + } else { + if (!(node instanceof Shape3D)) return null; + // Shape3Dの場合 + Shape3D shape = (Shape3D)node; + double coordinate[] = new double[3]; + + // OBBの作成に用いる頂点列の取得 + ArrayList vertex3DList = new ArrayList(); + if (shape.getGeometry() instanceof IndexedGeometryArray) { + // IndexedGeometryArrayの場合 + IndexedGeometryArray iga = (IndexedGeometryArray) shape.getGeometry(); + for (int i = 0; i < iga.getIndexCount(); i++) { + iga.getCoordinates(iga.getCoordinateIndex(i), coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } else if (shape.getGeometry() instanceof GeometryStripArray) { + // GeometryStripArrayの場合 + GeometryStripArray gsa = (GeometryStripArray) shape.getGeometry(); + for (int i = 0; i < gsa.getVertexCount(); i++) { + gsa.getCoordinates(i, coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } else if (shape.getGeometry() instanceof TriangleArray) { + // TriangleArrayの場合 + TriangleArray tra = (TriangleArray) shape.getGeometry(); + for (int i = 0; i < tra.getVertexCount(); i++) { + tra.getCoordinates(i, coordinate); + Vector3d p = new Vector3d(coordinate[0], coordinate[1], coordinate[2]); + vertex3DList.add(p); + } + } + + if (pattern == 0) { + // 最大面積法 + Vector3d cv1 = new Vector3d(); + Vector3d cv2 = new Vector3d(); + + double cvMax = 0.0; + Vector3d axis1 = new Vector3d(); // 3D頂点から求めた法線ベクトル + Vector3d axis2 = new Vector3d(); // 2D頂点から求めた法線ベクトル + Vector3d axis3 = new Vector3d(); // axis1,axis2の外積から求めた法線ベクトル + + // 面積を3D頂点リストから求め、法線を求める処理 + for (int i = 0; i < vertex3DList.size(); i += 3) { + cv1.sub(vertex3DList.get(i + 2), vertex3DList.get(i)); + cv2.sub(vertex3DList.get(i + 1), vertex3DList.get(i)); + Vector3d cv = new Vector3d(); + cv.cross(cv1, cv2); + if (i == 0 || cv.length() >= cvMax) { + cvMax = cv.length(); + axis1 = cv; + } + } + + ProjectionResult pr1 = GeometryUtility.projection3D(vertex3DList, axis1); + // 辺のを2D頂点リストから求め、法線を求める処理 + for (int i = 0; i < pr1.vertexList.size() - 1; i++) { + Vector3d cv = new Vector3d(); + cv.sub(vertex3DList.get(i + 1), vertex3DList.get(i)); + if (i == 0 || cv.length() >= cvMax) { + cvMax = cv.length(); + axis2 = cv; + } + } + + + ProjectionResult pr2 = GeometryUtility.projection2D(pr1.vertexList, axis2); + // axis1,axis2で外積でaxis3を求める。 + axis3.cross(axis1, axis2); + + ProjectionResult pr3 = GeometryUtility.projection2D(pr1.vertexList, axis3); + AxisResult ar = new AxisResult(axis1, axis2, axis3); + + // ここから最大面積法で求めた点を取得していく処理 + + // OBBの生成 + obb = new OBB(); + + // No.1 + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.2 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.3 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.4 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.min); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.5 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.6 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.min); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.7 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.min); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // No.8 + ar = new AxisResult(axis1, axis2, axis3); + ar.axis1.scale(pr1.max); + ar.axis2.scale(pr2.max); + ar.axis3.scale(pr3.max); + ar.axis1.add(ar.axis2); + ar.axis3.add(ar.axis1); + obb.addVertex(ar.axis3); + + // 面および境界多面体の生成 + obb.createPlanes(); + } else { + // AABB + double minX, maxX, minY, maxY, minZ, maxZ; + minX = maxX = vertex3DList.get(0).x; + minY = maxY = vertex3DList.get(0).y; + minZ = maxZ = vertex3DList.get(0).z; + for (int n = 1; n < vertex3DList.size(); n++) { + Vector3d v = vertex3DList.get(n); + if (minX > v.x) minX = v.x; + if (maxX < v.x) maxX = v.x; + if (minY > v.y) minY = v.y; + if (maxY < v.y) maxY = v.y; + if (minZ > v.z) minZ = v.z; + if (maxZ < v.z) maxZ = v.z; + } + obb = new OBB(new Vector3d(minX, minY, minZ), + new Vector3d(minX, maxY, minZ), + new Vector3d(minX, minY, maxZ), + new Vector3d(minX, maxY, maxZ), + new Vector3d(maxX, minY, minZ), + new Vector3d(maxX, maxY, minZ), + new Vector3d(maxX, minY, maxZ), + new Vector3d(maxX, maxY, maxZ)); + } + } + } + return obb; + } + + private class AxisResult { + Vector3d axis1; + Vector3d axis2; + Vector3d axis3; + + AxisResult(Vector3d a1, Vector3d a2, Vector3d a3) { + axis1 = (Vector3d) a1.clone(); + axis2 = (Vector3d) a2.clone(); + axis3 = (Vector3d) a3.clone(); + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ObjectVisitor.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ObjectVisitor.java new file mode 100644 index 0000000..b777c40 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ObjectVisitor.java @@ -0,0 +1,57 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Transform3D; + +import java.util.ArrayList; + +/** + * オブジェクトの階層構造をトラバースするビジター + * @author 新田直也 + * + */ +public abstract class ObjectVisitor { + /** + * 根から訪問節点までのパス上に存在する変換行列 + */ + protected ArrayList stackList = new ArrayList(); + + /* + * 節点(オブジェクト)を訪問する直前に呼ばれるメソッド + * @param obj 訪問節点 + */ + public abstract void preVisit(Object3D obj); + /** + * 節点(オブジェクトを訪問した直後に呼ばれるメソッド) + * @param obj 訪問節点 + */ + public abstract void postVisit(Object3D obj); + + /** + * 1階層潜ったときに変換行列を増やす + * @param obj 新しく訪問した節点 + */ + protected void pushTransform(Object3D obj) { + Transform3D transPos = new Transform3D(); + obj.pos.getTransform(transPos); + stackList.add(transPos); + Transform3D transRot = new Transform3D(); + obj.rot.getTransform(transRot); + stackList.add(transRot); + Transform3D transScale = new Transform3D(); + obj.scale.getTransform(transScale); + stackList.add(transScale); + Transform3D transCenter = new Transform3D(); + obj.center.getTransform(transCenter); + stackList.add(transCenter); + } + + /** + * 1階層復帰したときに変換行列を 減らす + */ + protected void popTransform() { + for (int i = 0; i < 4; i++) { + stackList.remove(stackList.size() - 1); + } + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Placeable.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Placeable.java new file mode 100644 index 0000000..c2418ad --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Placeable.java @@ -0,0 +1,13 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; + +/** + * 配置できるもの全て + * @author 新田直也 + * + */ +public interface Placeable { + abstract TransformGroup getTransformGroupToPlace(); + abstract BaseObject3D getBody(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Position3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Position3D.java new file mode 100644 index 0000000..bfe6946 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Position3D.java @@ -0,0 +1,151 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +/** + * 座標値を表す + * @author 新田直也 + * + */ +public class Position3D extends Property3D { + private double x; + private double y; + private double z; + + // コンストラクタ + public Position3D() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public Position3D(double px, double py, double pz) { + x = px; + y = py; + z = pz; + } + + public Position3D(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + } + + // property3Dの抽象クラスを埋める + public void applyTo(Object3D o) { + o.setPosition(this); + } + + // コピーコンストラクタ + public Position3D(Position3D p) { + this.x = p.x; + this.y = p.y; + this.z = p.z; + } + + public Position3D set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + // 加算 + public Position3D add(double x, double y, double z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + // 加算 + public Position3D add(Position3D p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + return this; + } + + // 加算 + public Position3D add(Vector3d v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + // 減算 + public Position3D sub(double x, double y, double z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + // 減算 + public Position3D sub(Position3D p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + return this; + } + + // 減算 + public Position3D sub(Vector3d v) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + return this; + } + + // スカラー倍 + public Position3D mul(double d) { + this.x *= d; + this.y *= d; + this.z *= d; + return this; + } + + public Position3D setX(double x) { + this.x = x; + return this; + } + + public double getX() { + return x; + } + + public Position3D setY(double y) { + this.y = y; + return this; + } + + public double getY() { + return y; + } + + public Position3D setZ(double z) { + this.z = z; + return this; + } + + public double getZ() { + return z; + } + + public Vector3d getVector3d() { + return new Vector3d(x, y, z); + } + + public Position3D setVector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + return this; + } + + public Position3D clone() { + return new Position3D(this); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ProjectionResult.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ProjectionResult.java new file mode 100644 index 0000000..9a3d177 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/ProjectionResult.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +import java.util.ArrayList; + +public class ProjectionResult { + double max = 0.0; + double min = 0.0; + ArrayList vertexList = new ArrayList(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Property3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Property3D.java new file mode 100644 index 0000000..4110532 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Property3D.java @@ -0,0 +1,7 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +public abstract class Property3D{ + public abstract void applyTo(Object3D o); + + public abstract Property3D clone(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Quaternion3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Quaternion3D.java new file mode 100644 index 0000000..346be55 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Quaternion3D.java @@ -0,0 +1,107 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.Quat4d; + + +public class Quaternion3D extends Property3D { + private Quat4d quaternion; + + // コンストラクタ============================= + public Quaternion3D() { + AxisAngle4d aa = new AxisAngle4d(0.0, 0.0, 1.0, 0.0); + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(AxisAngle4d aa) { + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(double x, double y, double z, double w) { + AxisAngle4d aa = new AxisAngle4d(x, y, z, w); + quaternion = new Quat4d(); + quaternion.set(aa); + } + + public Quaternion3D(Quaternion3D q) { + this.quaternion = (Quat4d) q.getQuat().clone(); + } + + public Quat4d getQuat() { + return quaternion; + } + + // ゲッタ================================= + public double getX() { + return quaternion.x; + } + + public double getY() { + return quaternion.y; + } + + public double getZ() { + return quaternion.z; + } + + public double getW() { + return quaternion.w; + } + + public Quaternion3D getInterpolate(Quaternion3D q, double alpha) { + quaternion.interpolate(q.getQuat(), alpha); + return this; + } + + public AxisAngle4d getAxisAngle() { + AxisAngle4d axisAngle = new AxisAngle4d(); + axisAngle.set(quaternion); + return axisAngle; + } + + // セッタ================================= + public Quaternion3D setQuaternion(double x, double y, double z, double w) { + quaternion = new Quat4d(x, y, z, w); + return this; + } + + public Quaternion3D setAxisAngle(double x, double y, double z, double w) { + AxisAngle4d aa = new AxisAngle4d(x, y, z, w); + quaternion = new Quat4d(); + quaternion.set(aa); + return this; + } + + public Quaternion3D add(AxisAngle4d aa) { + Quat4d q = new Quat4d(); + q.set(aa); + q.mul(quaternion); + quaternion = q; + return this; + } + + public Quaternion3D mul(Quaternion3D q) { + quaternion.mul(q.getQuat()); + return this; + } + + public void applyTo(Object3D o) { + o.setQuaternion(this); + } + + @Override + public Quaternion3D clone() { + return new Quaternion3D(this); + } + + public double norm() { + return quaternion.w * quaternion.w + quaternion.x * quaternion.x + quaternion.y * quaternion.y + quaternion.z * quaternion.z; + } + + public Quaternion3D normalize() { + quaternion.normalize(); + return this; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Terrain3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Terrain3D.java new file mode 100644 index 0000000..57295c6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Terrain3D.java @@ -0,0 +1,449 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.java3d.Appearance; +import org.ntlab.radishforandroidstudio.java3d.BoundingBox; +import org.ntlab.radishforandroidstudio.java3d.Point3d; +import org.ntlab.radishforandroidstudio.java3d.Shape3D; +import org.ntlab.radishforandroidstudio.java3d.TriangleStripArray; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; +import org.ntlab.radishforandroidstudio.java3d.Vector3f; + +public class Terrain3D extends BaseObject3D { + static final int MESH_SIZE = 4; + private Position3D origin; + private double sizeX = 0; + private double sizeZ = 0; + private double heightMap[][] = null; + private int meshX = 0; + private int meshZ = 0; + private TriangleStripArray triStripAttay = null; + + public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { + super(); + this.origin = origin; + this.sizeX = sizeX; + this.sizeZ = sizeZ; + this.heightMap = heightMap; + meshX = heightMap[0].length; + meshZ = heightMap.length; + + int stripVertexCounts[] = new int[meshZ - 1]; + for (int n = 0; n < meshZ - 1; n++) { + stripVertexCounts[n] = meshX * 2; + } + triStripAttay = new TriangleStripArray(meshX * 2 * (meshZ - 1), + TriangleStripArray.COORDINATES | TriangleStripArray.NORMALS, + stripVertexCounts); + int index = 0; + for (int z = 0; z < meshZ; z++) { + for (int x = 0; x < meshX; x++) { + // メッシュごとに三角形を2つづつ生成 + if (z < meshZ - 1) { + // 頂点座標の設定 + // 頂点の法線ベクトルの設定 + triStripAttay.setCoordinate(index, + new Point3d(origin.getX() + sizeX * (double)x, + origin.getY() + heightMap[z][x], + origin.getZ() + sizeZ * (double)z)); + Vector3f normal = calcNormal(z, x); + normal.normalize(); + triStripAttay.setNormal(index, normal); + + triStripAttay.setCoordinate(index + 1, + new Point3d(origin.getX() + sizeX * (double)x, + origin.getY() + heightMap[z + 1][x], + origin.getZ() + sizeZ * (double)(z + 1))); + normal = calcNormal(z + 1, x); + normal.normalize(); + triStripAttay.setNormal(index + 1, normal); + + index += 2; + } + } + } + Appearance a2 = (Appearance)a.cloneNodeComponent(); + Shape3D shape = new Shape3D(triStripAttay, a2); + center.addChild(shape); + } + + private Vector3f calcNormal(int z, int x) { + Vector3f normal = new Vector3f(); + if (x < meshX - 1) { + Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); + if (z < meshZ - 1) { + Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(zp, xp); + v1.normalize(); + normal.add(v1); + } + if (z > 0) { + Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(xp, zm); + v1.normalize(); + normal.add(v1); + } + } + if (x > 0) { + Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); + if (z < meshZ - 1) { + Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(xm, zp); + v1.normalize(); + normal.add(v1); + } + if (z > 0) { + Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); + Vector3f v1 = new Vector3f(); + v1.cross(zm, xm); + v1.normalize(); + normal.add(v1); + } + } + return normal; + } + + public BoundingSurface[] getBoundingSurfaces() { + if (boundingSurfaces == null) { + if (triStripAttay == null) return null; + double coordinate1[] = new double[3]; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + + // GeometryArrayの習得 + if (triStripAttay instanceof TriangleStripArray) { + // IndexedTriangleStripArray の場合 + // 8 x 8 メッシュ単位でまとめる + BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; + int n = 0; + for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { + for (int i = 0; i < meshX - 1; i += MESH_SIZE) { + BoundingSurface parent = new BoundingSurface(); + double lowY = 1.0; + double highY = -1.0; + for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { + for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { + int index = (j + j2) * meshX * 2 + (i + i2) * 2; + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index + 1, coordinate2); + triStripAttay.getCoordinates(index + 2, coordinate3); + triStripAttay.getCoordinates(index + 3, coordinate4); + if (lowY > highY) { + lowY = highY = coordinate1[1]; + } else { + if (lowY > coordinate1[1]) { + lowY = coordinate1[1]; + } else if (highY < coordinate1[1]) { + highY = coordinate1[1]; + } + if (lowY > coordinate2[1]) { + lowY = coordinate2[1]; + } else if (highY < coordinate2[1]) { + highY = coordinate2[1]; + } + if (lowY > coordinate3[1]) { + lowY = coordinate3[1]; + } else if (highY < coordinate3[1]) { + highY = coordinate3[1]; + } + if (lowY > coordinate4[1]) { + lowY = coordinate4[1]; + } else if (highY < coordinate4[1]) { + highY = coordinate4[1]; + } + } + + // 1 x 1 メッシュ内のBoundingSurface + Vector3d v1 = new Vector3d(coordinate1); + Vector3d v2 = new Vector3d(coordinate2); + Vector3d v3 = new Vector3d(coordinate3); + Vector3d v4 = new Vector3d(coordinate4); + BoundingSurface bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 + bSurface.addVertex((Vector3d)v2.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); + parent.addChild(bSurface, false); + + bSurface = new BoundingSurface(); + bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 + bSurface.addVertex((Vector3d)v4.clone()); + bSurface.addVertex((Vector3d)v3.clone()); + bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); + parent.addChild(bSurface, true); + } + } + // 8 x 8 メッシュ単位のBoundingSurface + triStripAttay.getCoordinates(j * meshX * 2 + i * 2, coordinate1); + parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), + new Point3d(coordinate4[0], highY, coordinate4[2]))); + surfaces[n] = parent; + n++; + } + } + boundingSurfaces = surfaces; + } else { + return null; + } + } + return boundingSurfaces; + } + + public double getHeight(double x, double z) { + int i = (int)((x - origin.getX()) / sizeX); + int j = (int)((z - origin.getZ()) / sizeZ); + if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; + + int index = j * meshX * 2 + i * 2; + double coordinate1[] = new double[3]; + double coordinate2[] = new double[3]; + double coordinate3[] = new double[3]; + double coordinate4[] = new double[3]; + triStripAttay.getCoordinates(index, coordinate1); + triStripAttay.getCoordinates(index + 1, coordinate2); + triStripAttay.getCoordinates(index + 2, coordinate3); + triStripAttay.getCoordinates(index + 3, coordinate4); + Vector3d v1 = new Vector3d(coordinate1); + Vector3d v2 = new Vector3d(coordinate2); + Vector3d v3 = new Vector3d(coordinate3); + Vector3d v4 = new Vector3d(coordinate4); + Vector3d p1 = new Vector3d(x, 0.0, z); + Vector3d p2 = new Vector3d(x, 1.0, z); + + double x2 = x - (double)i * sizeX - origin.getX(); + double z2 = z - (double)j * sizeZ - origin.getZ(); + if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { + // 1つめの三角形を通る場合 + Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); + return crossPoint.getY(); + } else { + // 2つめの三角形を通る場合 + Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); + return crossPoint.getY(); + } + } +} + +// +// ********** IndexedTriangleStripArray を使うバージョン ********** +// (IndexedTriangleStripArrayを用いるとメモリを節約できるが、シェーディングができないので注意) +// +//public class Terrain3D extends BaseObject3D { +// static final int MESH_SIZE = 8; +// private Position3D origin; +// private double sizeX = 0; +// private double sizeZ = 0; +// private double heightMap[][] = null; +// private int meshX = 0; +// private int meshZ = 0; +// private IndexedTriangleStripArray triStripAttay = null; +// +// public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { +// super(); +// this.origin = origin; +// this.sizeX = sizeX; +// this.sizeZ = sizeZ; +// this.heightMap = heightMap; +// meshX = heightMap[0].length; +// meshZ = heightMap.length; +// +// int stripVertexCounts[] = new int[meshZ - 1]; +// for (int n = 0; n < meshZ - 1; n++) { +// stripVertexCounts[n] = meshX * 2; +// } +// triStripAttay = new IndexedTriangleStripArray(meshX * meshZ, +// IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.NORMALS, +// meshX * 2 * (meshZ - 1), +// stripVertexCounts); +// int index = 0, index2 = 0, i1, i2; +// for (int z = 0; z < meshZ; z++) { +// for (int x = 0; x < meshX; x++) { +// // 頂点座標の設定 +// triStripAttay.setCoordinate(index, +// new Point3d(origin.getX() + sizeX * (double)x, +// origin.getY() + heightMap[z][x], +// origin.getZ() + sizeZ * (double)z)); +// // 頂点の法線ベクトルの設定 +// Vector3f normal = new Vector3f(); +// if (x < meshX - 1) { +// Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); +// if (z < meshZ - 1) { +// Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(zp, xp); +// v1.normalize(); +// normal.add(v1); +// } +// if (z > 0) { +// Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(xp, zm); +// v1.normalize(); +// normal.add(v1); +// } +// } +// if (x > 0) { +// Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); +// if (z < meshZ - 1) { +// Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(xm, zp); +// v1.normalize(); +// normal.add(v1); +// } +// if (z > 0) { +// Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); +// Vector3f v1 = new Vector3f(); +// v1.cross(zm, xm); +// v1.normalize(); +// normal.add(v1); +// } +// } +// normal.normalize(); +// triStripAttay.setNormal(index, normal); +// index++; +// // メッシュごとに三角形を2つづつ生成 +// if (z < meshZ - 1) { +// i1 = z * meshX + x; +// i2 = (z + 1) * meshX + x; +// triStripAttay.setCoordinateIndices(index2, new int[]{i1, i2}); +// index2 += 2; +// } +// } +// } +// Appearance a2 = (Appearance)a.cloneNodeComponent(true); +// a2.setCapability(Appearance.ALLOW_MATERIAL_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_WRITE); +// a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_READ); +// a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE); +// Shape3D shape = new Shape3D(triStripAttay, a2); +// shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ); +// shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); +// center.addChild(shape); +// } +// +// public BoundingSurface[] getBoundingSurfaces() { +// if (boundingSurfaces == null) { +// if (triStripAttay == null) return null; +// double coordinate1[] = new double[3]; +// double coordinate2[] = new double[3]; +// double coordinate3[] = new double[3]; +// double coordinate4[] = new double[3]; +// +// // GeometryArrayの習得 +// if (triStripAttay instanceof IndexedTriangleStripArray) { +// // IndexedTriangleStripArray の場合 +// // 8 x 8 メッシュ単位でまとめる +// BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; +// int n = 0; +// for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { +// for (int i = 0; i < meshX - 1; i += MESH_SIZE) { +// BoundingSurface parent = new BoundingSurface(); +// double lowY = 1.0; +// double highY = -1.0; +// for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { +// for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { +// int index = (j + j2) * meshX * 2 + (i + i2) * 2; +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); +// if (lowY > highY) { +// lowY = highY = coordinate1[1]; +// } else { +// if (lowY > coordinate1[1]) { +// lowY = coordinate1[1]; +// } else if (highY < coordinate1[1]) { +// highY = coordinate1[1]; +// } +// if (lowY > coordinate2[1]) { +// lowY = coordinate2[1]; +// } else if (highY < coordinate2[1]) { +// highY = coordinate2[1]; +// } +// if (lowY > coordinate3[1]) { +// lowY = coordinate3[1]; +// } else if (highY < coordinate3[1]) { +// highY = coordinate3[1]; +// } +// if (lowY > coordinate4[1]) { +// lowY = coordinate4[1]; +// } else if (highY < coordinate4[1]) { +// highY = coordinate4[1]; +// } +// } +// +// // 1 x 1 メッシュ内のBoundingSurface +// Vector3d v1 = new Vector3d(coordinate1); +// Vector3d v2 = new Vector3d(coordinate2); +// Vector3d v3 = new Vector3d(coordinate3); +// Vector3d v4 = new Vector3d(coordinate4); +// BoundingSurface bSurface = new BoundingSurface(); +// bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 +// bSurface.addVertex((Vector3d)v2.clone()); +// bSurface.addVertex((Vector3d)v3.clone()); +// bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); +// parent.addChild(bSurface, false); +// +// bSurface = new BoundingSurface(); +// bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 +// bSurface.addVertex((Vector3d)v4.clone()); +// bSurface.addVertex((Vector3d)v3.clone()); +// bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); +// parent.addChild(bSurface, true); +// } +// } +// // 8 x 8 メッシュ単位のBoundingSurface +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(j * meshX * 2 + i * 2), coordinate1); +// parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), +// new Point3d(coordinate4[0], highY, coordinate4[2]))); +// surfaces[n] = parent; +// n++; +// } +// } +// boundingSurfaces = surfaces; +// } else { +// return null; +// } +// } +// return boundingSurfaces; +// } +// +// public double getHeight(double x, double z) { +// int i = (int)((x - origin.getX()) / sizeX); +// int j = (int)((z - origin.getZ()) / sizeZ); +// if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; +// +// int index = j * meshX * 2 + i * 2; +// double coordinate1[] = new double[3]; +// double coordinate2[] = new double[3]; +// double coordinate3[] = new double[3]; +// double coordinate4[] = new double[3]; +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); +// triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); +// Vector3d v1 = new Vector3d(coordinate1); +// Vector3d v2 = new Vector3d(coordinate2); +// Vector3d v3 = new Vector3d(coordinate3); +// Vector3d v4 = new Vector3d(coordinate4); +// Vector3d p1 = new Vector3d(x, 0.0, z); +// Vector3d p2 = new Vector3d(x, 1.0, z); +// +// double x2 = x - (double)i * sizeX - origin.getX(); +// double z2 = z - (double)j * sizeZ - origin.getZ(); +// if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { +// // 1つめの三角形を通る場合 +// Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); +// return crossPoint.getY(); +// } else { +// // 2つめの三角形を通る場合 +// Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); +// return crossPoint.getY(); +// } +// } +//} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/UndoBuffer.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/UndoBuffer.java new file mode 100644 index 0000000..edde0c8 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/UndoBuffer.java @@ -0,0 +1,48 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; + +public class UndoBuffer { + @SuppressWarnings("unchecked") + private ArrayList> list = new ArrayList>(); + @SuppressWarnings("unchecked") + private Hashtable nowCondition = new Hashtable(); + private static final int max = 3; + + UndoBuffer() { + } + + UndoBuffer(UndoBuffer another) { + this.list = (ArrayList>)another.list.clone(); + this.nowCondition = (Hashtable)another.nowCondition.clone(); + } + + public void push(Property3D p) { + nowCondition.put(p.getClass(), p.clone()); + } + + @SuppressWarnings("unchecked") + public void setUndoMark() { + if(list.size() == max) { + list.remove(max - 1); + } + list.add(0, (Hashtable) nowCondition.clone()); + } + + @SuppressWarnings("unchecked") + public Collection undo() { + if(list.size() != 0) { + nowCondition = list.remove(0); + return nowCondition.values(); + } + else { + return new Hashtable().values(); + } + } + + public void clear() { + + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Universe.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Universe.java new file mode 100644 index 0000000..258e7dd --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/model3D/Universe.java @@ -0,0 +1,142 @@ +package org.ntlab.radishforandroidstudio.framework.model3D; + +import org.ntlab.radishforandroidstudio.framework.physics.Ground; +import org.ntlab.radishforandroidstudio.java3d.BranchGroup; +import org.ntlab.radishforandroidstudio.java3d.Group; +import org.ntlab.radishforandroidstudio.java3d.Leaf; +import org.ntlab.radishforandroidstudio.java3d.Light; +import org.ntlab.radishforandroidstudio.java3d.Node; +import org.ntlab.radishforandroidstudio.java3d.Primitive; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; + +import java.util.ArrayList; + +public class Universe { + private BranchGroup root = null; + private ArrayList lights = new ArrayList(); + private BackgroundBox skyBox = null; + private Ground ground = null; + private ArrayList movableList = new ArrayList(); + + public Universe() { + root = new BranchGroup(); + } + + public void render(IViewer3D viewer) { + viewer.update(lights, skyBox); + render(viewer, root); + } + + private void render(IViewer3D viewer, Node node) { + if (node instanceof Group) { + if (node instanceof Primitive) { + viewer.draw((Primitive) node); + } else { + if (node instanceof TransformGroup) { + Transform3D transform = new Transform3D(); + ((TransformGroup) node).getTransform(transform); + // +// Vector3d vec = new Vector3d(1.0, 1.1, 1.0); +// transform.setScale(vec); +// AxisAngle4d aa = new AxisAngle4d(0.0, 0.0, 1.0, 1.0); +// transform.setRotation(aa); + // + viewer.pushTransform(transform); + for (int i = 0; i < ((Group) node).numChildren(); i++) { + Node node2 = ((Group) node).getChild(i); + render(viewer, node2); + } + viewer.popTransform(); + } else { + for (int i = 0; i < ((Group) node).numChildren(); i++) { + Node node2 = ((Group) node).getChild(i); + render(viewer, node2); + } + } + } + } else if (node instanceof Leaf) { + viewer.draw(node); + } + } + + public void update(long interval){ + for(int i = 0; i < movableList.size(); i++){ + Movable movable = movableList.get(i); + movable.motion(interval,ground); + } + } + + /** + * オブジェクトを配置する + * + * @param obj + * 配置するオブジェクト + */ + public void place(Placeable obj) { + if(obj instanceof Ground){ + ground = (Ground)obj; + } + if(obj instanceof Movable){ + movableList.add((Movable)obj); + } + place(obj.getTransformGroupToPlace()); + } + + public void place(Node node) { + root.addChild(node); + } + + /** + * 後で取り除けるようにオブジェクトを配置する + * + * @param obj + * 配置するオブジェクト + */ + public void placeDisplacable(Placeable obj) { + placeDisplacable(obj.getTransformGroupToPlace()); + } + + private void placeDisplacable(Node node) { + root.addChild(node); + } + + /** + * 光源の追加 + * + * @param light + * 追加する光源 + */ + public void placeLight(Light light) { + root.addChild(light); + getLights().add(light); + } + + /** + * スカイボックスの追加 + * @param skyBox 追加するスカイボックス + */ + public void placeSkyBox(BackgroundBox skyBox) { + root.addChild(skyBox); + this.skyBox = skyBox; + } + + /** + * オブジェクトを可能ならば取り除く + * + * @param obj + * 取り除くオブジェクト + */ + public void displace(Placeable obj) { + displace(obj.getTransformGroupToPlace()); + } + + private void displace(Node node) { + root.removeChild(node); + } + + public ArrayList getLights() { + return lights; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/AngularVelocity3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/AngularVelocity3D.java new file mode 100644 index 0000000..2ae9421 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/AngularVelocity3D.java @@ -0,0 +1,85 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Property3D; +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class AngularVelocity3D extends Property3D{ + private double x; + private double y; + private double z; + + public AngularVelocity3D(AngularVelocity3D w) { + x = w.x; + y = w.y; + z = w.z; + } + + public AngularVelocity3D(double x,double y,double z){ + this.x = x; + this.y = y; + this.z = z; + } + + public AngularVelocity3D(){ + x = 0.0; + y = 0.0; + z = 0.0; + } + + public void applyTo(Object3D o){ + ((Solid3D)o).setAngularVelocity(this); + } + + public double getX() { + // TODO Auto-generated method stub + return this.x; + } + + public double getY() { + // TODO Auto-generated method stub + return this.y; + } + + public double getZ() { + // TODO Auto-generated method stub + return this.z; + } + + public AngularVelocity3D add(double x, double y, double z){ + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public AngularVelocity3D add(Vector3d v) { + // TODO Auto-generated method stub + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + public Vector3d getVector3d(){ + return new Vector3d(x,y,z); + } + + public AxisAngle4d getAxisAngle4d() { + double l = getVector3d().length(); + if (l <= 0.00001) { + return new AxisAngle4d(0, 0, 1.0, 0.0); + } + return new AxisAngle4d(x / l, y / l, z / l, l); + } + + @Override + public Property3D clone() { + // TODO Auto-generated method stub + return new AngularVelocity3D(this); + } +} + + diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingBoxVisitor.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingBoxVisitor.java new file mode 100644 index 0000000..b1e6038 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingBoxVisitor.java @@ -0,0 +1,93 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.OBB; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.ObjectVisitor; +import org.ntlab.radishforandroidstudio.java3d.BoundingSphere; + +import java.util.ArrayList; + + +public class BoundingBoxVisitor extends ObjectVisitor { + private ArrayList obbList = new ArrayList(); // 全構成要素のOBBのリスト + private ArrayList bsStack = new ArrayList(); // オブジェクトの階層毎のBoundingSphereのスタック + private String partName = null; // 部品を指定する場合に使う + private boolean inPart = false; + + public BoundingBoxVisitor() { + partName = null; + } + + public BoundingBoxVisitor(String partName) { + this.partName = partName; + } + + public void preVisit(Object3D obj) { + pushTransform(obj); + if (partName != null && obj.name.equals(partName)) { + inPart = true; + } + if (obj.hasChildren() && obj.bs == null) { + // 子供がいる場合、下の階層用にnullをpushする + bsStack.add(null); + } + } + + public void postVisit(Object3D obj) { + int pattern = 2; + if (!obj.hasChildren()) { + // 葉の場合 + OBB obb = obj.getOBB(pattern); + if (obb != null) { + if (obj.bs == null) { + obj.bs = obb.getBoundingSphere(); + } + + obb = (OBB)obb.clone(); + BoundingSphere bs = (BoundingSphere)obj.bs.clone(); + for (int i = stackList.size() - 1; i >= 0; i--) { + obb.transform(stackList.get(i)); + bs.transform(stackList.get(i)); + } + if (partName == null || partName.length() == 0 || inPart) { + obbList.add(obb); // Transform3Dを適応させたBoundsをboundsListに追加 + int stackTop = bsStack.size() - 1; + if (bs != null && stackTop >= 0) { + if (bsStack.get(stackTop) == null) { + // その階層の最初のオブジェクトの場合、nullを置き換え + bsStack.set(stackTop, bs); + } else { + // その階層の2番目以降のオブジェクトの場合、結合 + bsStack.get(stackTop).combine(bs); + } + } + } + } + } else { + // 子供がいる場合 + int stackTop = bsStack.size() - 1; + if (obj.bs == null) { + // 下の階層の結合結果をpopして利用する + obj.bs = bsStack.remove(stackTop); + stackTop--; + } + if (obj.bs != null && stackTop >= 0) { + if (bsStack.get(stackTop) == null) { + // その階層の最初のオブジェクトの場合、nullを置き換え + bsStack.set(stackTop, obj.bs); + } else { + // その階層の2番目以降のオブジェクトの場合、結合 + bsStack.get(stackTop).combine(obj.bs); + } + } + } + popTransform(); + if (partName != null && obj.name.equals(partName)) { + inPart = false; + } + } + + public ArrayList getObbList() { + return obbList; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingSurfaceVisitor.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingSurfaceVisitor.java new file mode 100644 index 0000000..b672ff9 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/BoundingSurfaceVisitor.java @@ -0,0 +1,59 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.BaseObject3D; +import org.ntlab.radishforandroidstudio.framework.model3D.BoundingSurface; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.ObjectVisitor; + +import java.util.ArrayList; + +/** + * 地面の衝突判定用のボリュームを生成するためのビジター + * @author 新田直也 + * + */ +public class BoundingSurfaceVisitor extends ObjectVisitor { + private ArrayList boundingSurfaceList = new ArrayList(); + public BoundingSurfaceVisitor() { + boundingSurfaceList.add(new BoundingSurface()); + } + + public void preVisit(Object3D obj) { + pushTransform(obj); + if (obj.hasChildren()) { + boundingSurfaceList.add(new BoundingSurface()); + } + } + + public void postVisit(Object3D obj) { + if (!obj.hasChildren()) { + // 葉の場合 + BoundingSurface[] s = (BoundingSurface[]) obj.getBoundingSurfaces().clone(); + for (int i = 0; i < s.length; i++) { + s[i] = (BoundingSurface) s[i].clone(); + for (int j = stackList.size() - 1; j >= 0; j--) { + s[i].transform(stackList.get(j)); + } + boundingSurfaceList.get(boundingSurfaceList.size() - 1).addChild(s[i], true); // Transform3Dを適応させたBoundsをsurfaceListに追加 + } + } else { + BoundingSurface child = boundingSurfaceList.remove(boundingSurfaceList.size() - 1); + BoundingSurface parent = boundingSurfaceList.get(boundingSurfaceList.size() - 1); + parent.addChild(child, true); + } + popTransform(); + } + + public void baseVisit(BaseObject3D obj) { + BoundingSurface parent = boundingSurfaceList.get(boundingSurfaceList.size() - 1); + BoundingSurface[] s = (BoundingSurface[]) obj.getBoundingSurfaces().clone(); + for (int i = 0; i < s.length; i++) { + s[i] = (BoundingSurface) s[i].clone(); + parent.addChild(s[i], true); + } + } + + public BoundingSurface getBoundingSurface() { + return boundingSurfaceList.get(0); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Force3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Force3D.java new file mode 100644 index 0000000..f9ced43 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Force3D.java @@ -0,0 +1,35 @@ +package org.ntlab.radishforandroidstudio.framework.physics; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class Force3D { + double x; + double y; + double z; + public static final Force3D ZERO = new Force3D( 0.0, 0.0, 0.0); + + public Force3D(double x,double y,double z){ + this.x = x; + this.y = y; + this.z = z; + } + + public Force3D(Vector3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + public Vector3d getVector3d(){ + return new Vector3d(x,y,z); + } + + public void add(Force3D f) { + x += f.x; + y += f.y; + z += f.z; + } + + public double getSeverity() { + return getVector3d().length(); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Ground.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Ground.java new file mode 100644 index 0000000..2f4d038 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Ground.java @@ -0,0 +1,56 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.BaseObject3D; +import org.ntlab.radishforandroidstudio.framework.model3D.BoundingSurface; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Placeable; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; + + +/** + * 地面などの(基本的に動かない)構造物を表すオブジェクト + * @author 新田直也 + * + */ +public class Ground implements Placeable { + private BaseObject3D groundObj = null; + private BoundingSurface boundingSurface = null; // 衝突判定用ボリュームのキャッシュ + + public Ground(BaseObject3D obj) { + groundObj = obj; + } + + public BaseObject3D getBody() { + return groundObj; + } + + public void updateBody(BaseObject3D obj) { + groundObj = obj; + boundingSurface = null; + } + + @Override + public TransformGroup getTransformGroupToPlace() { + return groundObj.getTransformGroupToPlace(); + } + + /** + * 衝突判定用のボリュームを取得する + * @return 衝突判定用ボリューム(階層化されている場合がある) + */ + BoundingSurface getBoundingSurface() { + if (boundingSurface == null) { + // キャッシュに何も積まれていない場合のみ計算する + BoundingSurfaceVisitor surfaceVisitor = new BoundingSurfaceVisitor(); + if (groundObj instanceof Object3D) { + // Object3Dの場合階層構造をたどる + ((Object3D)groundObj).accept(surfaceVisitor); + } else { + // BaseObject3dの場合階層構造がない + surfaceVisitor.baseVisit(groundObj); + } + boundingSurface = surfaceVisitor.getBoundingSurface(); + } + return boundingSurface; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Inertia3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Inertia3D.java new file mode 100644 index 0000000..44d653f --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Inertia3D.java @@ -0,0 +1,54 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.OBB; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class Inertia3D { + double ixx; + double iyy; + double izz; + static final Inertia3D ZERO = new Inertia3D( 0.0, 0.0, 0.0); + + public Inertia3D(double x,double y,double z){ + this.ixx = x; + this.iyy = y; + this.izz = z; + } + + public Inertia3D(Vector3d v) { + this.ixx = v.x; + this.iyy = v.y; + this.izz = v.z; + } + + public Inertia3D(Solid3D obj) { + BoundingBoxVisitor visitor = new BoundingBoxVisitor(); + obj.accept(visitor); + if (visitor.getObbList().size() == 1) { + OBB obb = visitor.getObbList().get(0); + Vector3d vx = new Vector3d(); + vx.sub(obb.vertexList.get(4), obb.vertexList.get(0)); + + Vector3d vy = new Vector3d(); + vy.sub(obb.vertexList.get(1), obb.vertexList.get(0)); + + Vector3d vz = new Vector3d(); + vz.sub(obb.vertexList.get(2), obb.vertexList.get(0)); + +// System.out.println("vx:" + vx + ",vy:" + vy + ",vz:" + vz); + this.ixx = obj.mass * (1.0/12.0 * (Math.pow(vy.length(), 2.0) + Math.pow(vz.length(), 2.0))); + this.iyy = obj.mass * (1.0/12.0 * (Math.pow(vx.length(), 2.0) + Math.pow(vz.length(), 2.0))); + this.izz = obj.mass * (1.0/12.0 * (Math.pow(vx.length(), 2.0) + Math.pow(vy.length(), 2.0))); + } else { + this.ixx = obj.mass; + this.iyy = obj.mass; + this.izz = obj.mass; + } + } + + public Vector3d getVector3d() { + return new Vector3d(ixx, iyy, izz); + } +} + diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicalSystem.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicalSystem.java new file mode 100644 index 0000000..4d3f5ba --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicalSystem.java @@ -0,0 +1,152 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.CollisionResult; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class PhysicalSystem { + + public ArrayList objects = new ArrayList(); + + // 物体の挿入 + public int add(Solid3D s) { + objects.add(s); + return objects.size() - 1; + } + + // 物体の運動 + public void motion(int id, long interval, Force3D f, Position3D appPoint, Ground ground) { + ArrayList forces[] = new ArrayList[objects.size()]; + ArrayList appPoints[] = new ArrayList[objects.size()]; + for (int i = 0; i < objects.size(); i++) { + forces[i] = new ArrayList(); + appPoints[i] = new ArrayList(); + } + + // id番目の外力の計算 + forces[id].add(f); + appPoints[id].add(appPoint); +// objects.get(id).move(interval, f, appPoint); + + double l; // ばねの伸び + + Force3D penalty = new Force3D(0.0, 0.0, 0.0); // ペナルティ法によるペナルティの作用の力 + Force3D inversepenalty; // + // //ペナルティ法によるペナルティの反作用の力 + CollisionResult cr; + Solid3D s; + for (int n = 0; n < objects.size(); n++) { + // 重力の計算 + s = objects.get(n); + forces[n].add(PhysicsUtility.getGravity(s)); + appPoints[n].add(s.getGravityCenter()); +// objects.get(n).move(interval, +// PhysicsFacade.getGravity(objects.get(n)), +// objects.get(n).getGravityCenter()); // 重力の計算 + // 地面との当たり判定 + cr = PhysicsUtility.doesIntersect(s, ground); + // 地面に物体がめり込んでいる場合 + if (cr != null) { + double gk = 5000.0; // 地面でのばね係数 + double e = 1.0; // 地面での跳ね返り時の抵抗係数 + double b = 300.0; + l = cr.length; + // <作用の力の計算> + // ペナルティの変数 +// Vector3d v = cr.normal; +// v.scale(gk * l); + // 作用点ベクトルの作成 + Vector3d r = cr.collisionPoint.getVector3d(); + // (作用点-重心)ベクトル + r.sub(s.getGravityCenter().getVector3d()); + // 角速度ベクトルの作成 + Vector3d angVel = s.getAngularVelocity().getVector3d(); + // 角速度ベクトルと(作用点-重心)ベクトルの外積計算 + angVel.cross(angVel, r); + // 速度ベクトル+角速度ベクトルと(作用点-重心)ベクトルの外積計算 + Vector3d relV = s.getVelocity().getVector3d(); + // 相対速度ベクトルの作成 + relV.add(angVel); + Vector3d v = cr.normal; +//System.out.println(r + "," + (gk * l) + "," + (- relV.dot(v) * b)); + // ペナルティの大きさ決定 + v.scale(gk * l - relV.dot(v) * b); + penalty = new Force3D(v); + + // 作用の力による運動 + forces[n].add(penalty); + appPoints[n].add(cr.collisionPoint); +// objects.get(n).move(interval, penalty, cr.collisionPoint); + } + // 地面に物体がめり込んでいない場合 + else { + } + for (int m = 0; m < n; m++) { + Solid3D s1 = objects.get(n); + Solid3D s2 = objects.get(m); + cr = PhysicsUtility.checkCollision(s1, null, s2, null); + // 物体がめり込んでいる場合 + if (cr != null) { + double sk = 5000; // 物体でのばね係数 + double e = 0.2; // 物体での跳ね返り時の抵抗係数 + double b = 300.0; + l = cr.length; + // <作用の力の計算> + // 作用点ベクトルの作成 + // s1に関する計算 + // s1の角速度ベクトルの作成 + Vector3d r = cr.collisionPoint.getVector3d(); + r.sub(s1.getGravityCenter().getVector3d()); + Vector3d s1AngVel = s1.getAngularVelocity().getVector3d(); + s1AngVel.cross(s1AngVel, r); + // s1の速度ベクトルの作成 + Vector3d s1RelV = s1.getVelocity().getVector3d(); + // s1の速度ベクトル+s1の角速度ベクトルと(作用点-s1の重心)ベクトルの外積計算 + s1RelV.add(s1AngVel); + // s2に関する計算 + // s2の角速度ベクトルの作成 + r = cr.collisionPoint.getVector3d(); + r.sub(s2.getGravityCenter().getVector3d()); + Vector3d s2AngVel = s2.getAngularVelocity().getVector3d(); + s2AngVel.cross(s2AngVel, r); + // s2の速度ベクトルの作成 + Vector3d s2RelV = s2.getVelocity().getVector3d(); + // s2の速度ベクトル+s2の角速度ベクトルと(作用点-s2の重心)ベクトルの外積計算 + s2RelV.add(s2AngVel); + // 相対速度ベクトルの作成 + s1RelV.sub(s2RelV); + // ペナルティの大きさ決定 + Vector3d v = (Vector3d)cr.normal.clone(); +//System.out.println(r + "," + (sk * l) + "," + (- relV.dot(v) * b)); + v.scale(sk * l - s1RelV.dot(v) * b); + penalty = new Force3D(v); + + // 反作用の力の計算 + v.scale(-1); + inversepenalty = new Force3D(v); + + // 作用の力による物体の移動 + forces[n].add(penalty); + appPoints[n].add(cr.collisionPoint); +// s1.move(interval, penalty, cr.collisionPoint); + + // 反作用の力による物体の移動 + forces[m].add(inversepenalty); + appPoints[m].add(cr.collisionPoint); +// s2.move(interval, inversepenalty, cr.collisionPoint); + } +// // 物体がめり込んでいない場合 +// else { +// s2.move(interval, f, s2.getGravityCenter()); +// s1.move(interval, f, s1.getGravityCenter()); +// } + } + } + for (int n2 = 0; n2 < objects.size(); n2++) { + objects.get(n2).move(interval, forces[n2], appPoints[n2]); + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicsUtility.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicsUtility.java new file mode 100644 index 0000000..f52627c --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/PhysicsUtility.java @@ -0,0 +1,222 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.BoundingSurface; +import org.ntlab.radishforandroidstudio.framework.model3D.CollisionResult; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.BoundingSphere; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +/** + * 物理演算のコア + * @author 新田直也 + * + */ +public class PhysicsUtility { + public static final double GRAVITY = 9.8; // 重力の加速度 + public static final Vector3d horizon = new Vector3d(1.0, 0.0, 0.0); + public static final Vector3d vertical = new Vector3d(0.0, 1.0, 0.0); + + public static Vector3d gravityDirection = new Vector3d(0.0, 1.0, 0.0); + + /** + * 物体に加わる重力を求める + * @param body 対象物体 + * @return bodyに加わる重力 + */ + public static Force3D getGravity(Solid3D body) { + return new Force3D(gravityDirection.x * -body.mass * GRAVITY, gravityDirection.y * -body.mass * GRAVITY, gravityDirection.z * -body.mass * GRAVITY); + } + + /** + * @param v + * :単位ベクトル、あるいはゼロベクトルを引数で渡すこと + */ + public static void setGravityDirection(Vector3d v) { + gravityDirection = new Vector3d(v.x, v.y, v.z); + } + + // モーメントの計算 + static Vector3d calcMoment(Force3D f, Position3D gravityCenter, + Position3D applicationPoint) { + Vector3d v1 = applicationPoint.getVector3d(); + Vector3d v2 = gravityCenter.getVector3d(); + v1.sub(v2); + + Vector3d cv = new Vector3d(); + Vector3d fv = f.getVector3d(); + cv.cross(v1, fv); + return cv; + + } + + /** + * 物体の反発係数から衝突時に加わる力を求める + * @param interval 衝突している時間 + * @param nor 物体がぶつかった面の宝仙ベクトル + * @param solid 物体 + * @return 衝突時に加わる力 + */ + public static Force3D calcForce(long interval, Vector3d nor, Solid3D solid) { + double f1 = 0.0; + Vector3d vf = new Vector3d(solid.getVelocity().getX(), solid + .getVelocity().getY(), solid.getVelocity().getZ()); + f1 = solid.mass * (vf.length() + solid.e * vf.length()) + / ((double) interval / 1000.0); + nor.scale(f1); + Force3D f = new Force3D(nor.x, nor.y, nor.z); + return f; + } + + /** + * 物体と地面との衝突判定 + * @param obj 物体 + * @param ground 地面 + * @return 衝突情報(nullのとき衝突なし) + */ + public static CollisionResult doesIntersect(Solid3D obj, Ground ground) { + if (ground == null) return null; + CollisionResult cr = null; + BoundingSurface boundingSurface = ground.getBoundingSurface(); + + // BoundingSphereを使って大雑把に衝突判定を行う + ArrayList boundingSurfaceList = null; + if (obj.bs != null) { + BoundingSphere bs = (BoundingSphere) (obj.bs.clone()); + Transform3D t3d = new Transform3D(); + obj.center.getTransform(t3d); + bs.transform(t3d); + obj.scale.getTransform(t3d); + bs.transform(t3d); + obj.rot.getTransform(t3d); + bs.transform(t3d); + obj.pos.getTransform(t3d); + bs.transform(t3d); + // 粗い衝突判定を行う(最上位のBoundingSurfaceとBoundingSphereの間で) + boundingSurfaceList = boundingSurface.intersect(bs); + bs = null; + t3d = null; + } + + if (obj.bs == null) { + // BoundingSphere がまだ作られていななかった場合、 + // 詳細な衝突判定のために、最上位の全 BoundingSurface を取得する + if (boundingSurfaceList == null) boundingSurfaceList = new ArrayList(); + boundingSurfaceList.add(boundingSurface); + } + + if (boundingSurfaceList.size() > 0) { + // 粗い衝突判定で衝突していた場合、OBBの集合を用いてより詳しい衝突判定を行う + // (BoundingSphere がまだ作られていない場合、OBB の作成と同時に BoundingSphere を作成される) + BoundingBoxVisitor obbVisitor = new BoundingBoxVisitor(); + obj.accept(obbVisitor); + for (int i = 0; i < obbVisitor.getObbList().size(); i++) { + // OBBと衝突判定をする場合は、地面を多角形のまま扱う + for (int j = 0; j < boundingSurfaceList.size(); j++) { + cr = boundingSurfaceList.get(j).intersect(obbVisitor.getObbList().get(i)); + if (cr != null) { + return cr; + } + } + } + obbVisitor = null; + } + return null; + } + + /** + * 物体同士の衝突判定 + * @param obj1 物体1 + * @param part1 判定する物体1の部分の名称 + * @param obj2 物体2 + * @param part2 判定する物体2の部分の名称 + * @return 衝突情報(nullのとき衝突なし) + */ + public static CollisionResult checkCollision(Object3D obj1, String part1, + Object3D obj2, String part2) { + CollisionResult cr = null; + + // BoundingSphereを使って大雑把に衝突判定を行う + boolean f = false; + if (obj1.bs != null && obj2.bs != null) { + // sol1 の BoundingSphere を計算 + BoundingSphere bs1 = (BoundingSphere) (obj1.bs.clone()); + Transform3D t3d = new Transform3D(); + obj1.center.getTransform(t3d); + bs1.transform(t3d); + obj1.scale.getTransform(t3d); + bs1.transform(t3d); + obj1.rot.getTransform(t3d); + bs1.transform(t3d); + obj1.pos.getTransform(t3d); + bs1.transform(t3d); + + // sol2 の BoundingSphere を計算 + BoundingSphere bs2 = (BoundingSphere) (obj2.bs.clone()); + obj2.center.getTransform(t3d); + bs2.transform(t3d); + obj2.scale.getTransform(t3d); + bs2.transform(t3d); + obj2.rot.getTransform(t3d); + bs2.transform(t3d); + obj2.pos.getTransform(t3d); + bs2.transform(t3d); + + // BoundingSphere 同士の衝突判定 + if (bs1.intersect(bs2)) { + f = true; + } + t3d = null; + bs1 = null; + bs2 = null; + } + if (f || obj1.bs == null || obj2.bs == null) { + BoundingBoxVisitor visitor1 = new BoundingBoxVisitor(part1); + BoundingBoxVisitor visitor2 = new BoundingBoxVisitor(part2); + obj1.accept(visitor1); + obj2.accept(visitor2); + // OBB o1 = obj1.getOBB(0); + // OBB o2 = obj2.getOBB(0); + // cr = o1.intersect(o2); + int i, j; + for (i = 0; i < visitor1.getObbList().size(); i++) { + for (j = 0; j < visitor2.getObbList().size(); j++) { + cr = visitor2.getObbList().get(j).intersect(visitor1.getObbList().get(i)); + // System.out.println("flg:"+flg); + if (cr != null) { + // Vector3d v = new Vector3d(0,0,0); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getX()); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getY()); + // System.out.println("checkColision1 + // Yes!!"+cr.collisionPoint.getZ()); + // return v; + return cr; + } else { + cr = visitor1.getObbList().get(i).intersect( + visitor2.getObbList().get(j)); + if (cr != null) { + cr.normal.scale(-1.0); + // Vector3d v = new Vector3d(0,0,0); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getX()); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getY()); + // System.out.println("checkColision2 + // Yes!!"+cr.collisionPoint.getZ()); + // return v; + return cr; + } + } + } + } + } + // System.out.println("Yes!!"); + return null; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/ProjectionResult.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/ProjectionResult.java new file mode 100644 index 0000000..81b8687 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/ProjectionResult.java @@ -0,0 +1,11 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +import java.util.ArrayList; + +public class ProjectionResult { + double max = 0.0; + double min = 0.0; + ArrayList vertexList = new ArrayList(); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Solid3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Solid3D.java new file mode 100644 index 0000000..f8e8ccb --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Solid3D.java @@ -0,0 +1,176 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Quaternion3D; +import org.ntlab.radishforandroidstudio.java3d.AxisAngle4d; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +/** + * 物理的な振る舞いをする物体(剛体)を表す + * @author 新田直也 + * + */ +public class Solid3D extends Object3D { + private Velocity3D velocity; + private AngularVelocity3D angularvelocity; + private Position3D gravityCenter = getPosition3D(); + public double e = 1.0; + public double mass = 10; + private Inertia3D inertia = null; + + // コピーコンストラクタ + public Solid3D(Object3D obj) { + super(obj); + velocity = new Velocity3D(); + angularvelocity = new AngularVelocity3D(); + inertia = new Inertia3D(this); + } + + public Solid3D(Object3D obj, double mass) { + super(obj); + velocity = new Velocity3D(); + angularvelocity = new AngularVelocity3D(); + this.mass = mass; + inertia = new Inertia3D(this); + } + + public Solid3D(Solid3D solid) { + super(solid); + if (solid.velocity != null) { + velocity = new Velocity3D(solid.velocity); + } else { + velocity = new Velocity3D(); + } + if (solid.angularvelocity != null) { + angularvelocity = new AngularVelocity3D(solid.angularvelocity); + } else { + angularvelocity = new AngularVelocity3D(); + } + mass = solid.mass; + inertia = new Inertia3D(this); + } + + /** + * 力学運動の計算(加わる力が1つの場合) + * @param interval 単位時間 + * @param f 力 + * @param applicationPoint 力の作用点 + */ + public void move(long interval, Force3D f, Position3D applicationPoint) { + // モーメントの計算 + Vector3d moment = PhysicsUtility.calcMoment(f, getGravityCenter(), + applicationPoint); + moveSub(interval, f, moment); + } + + /** + * 力学運動の計算(同時に複数の力が加わる場合) + * @param interval 単位時間 + * @param forces 力(複数) + * @param appPoints それぞれの力の作用点 + */ + public void move(long interval, ArrayList forces, + ArrayList appPoints) { + // 重心に加わる力の合計を求める + Force3D f = new Force3D(0.0, 0.0, 0.0); + for (int n = 0; n < forces.size(); n++) { + f.add(forces.get(n)); + } + + // モーメントの合計を計算する + Position3D gc = getGravityCenter(); + Vector3d moment = new Vector3d(0.0, 0.0, 0.0); + for (int n2 = 0; n2 < forces.size(); n2++) { + moment.add(PhysicsUtility.calcMoment(forces.get(n2), gc, appPoints.get(n2))); + } + moveSub(interval, f, moment); + } + + private void moveSub(long interval, Force3D f, Vector3d moment) { + // 1.重心の運動方程式(ニュートン方程式) + // 加速度、速度計算 + Vector3d deltaV = f.getVector3d(); // 力ベクトルの取得 + deltaV.scale(1.0 / mass * ((double) interval / 1000.0)); // 加速度から速度の差分を計算 + Velocity3D v = getVelocity().add(deltaV); // 速度に差分を加算 + apply(v, false); + + // 重心位置計算 + Vector3d deltaP = velocity.getVector3d(); // 速度ベクトルの取得 + deltaP.scale(((double) interval / 1000.0)); + Position3D p = getPosition3D().add(deltaP); // 位置に差分を加算 + apply(p, false); + + // 2.オイラーの角運動方程式 + + // 角加速度、角速度計算 + AngularVelocity3D w = getAngularVelocity(); + Vector3d deltaAngularV = new Vector3d( + (moment.x + (inertia.iyy - inertia.izz) * w.getY() * w.getZ()) / inertia.ixx, + (moment.y + (inertia.izz - inertia.ixx) * w.getZ() * w.getX()) / inertia.iyy, + (moment.z + (inertia.ixx - inertia.iyy) * w.getX() * w.getY()) / inertia.izz); + deltaAngularV.scale((double) interval / 1000.0); + w.add(deltaAngularV); + apply(w, false); + + // 角速度による回転計算 + AxisAngle4d axisAngle = w.getAxisAngle4d(); + axisAngle.angle *= ((double) interval / 1000.0); + Quaternion3D q = getQuaternion().add(axisAngle); + apply(q, false); + } + + // 複製を作る + public Object3D duplicate() { + Object3D copy = new Solid3D(this); + return copy; + } + + public void scale(double s) { + super.scale(s); + inertia = new Inertia3D(this); + } + + public void scale(double sx, double sy, double sz) { + super.scale(sx, sy, sz); + inertia = new Inertia3D(this); + } + + public Velocity3D getVelocity() { + return (Velocity3D) velocity.clone(); + } + + public AngularVelocity3D getAngularVelocity() { + return (AngularVelocity3D) angularvelocity.clone(); + } + + // Velocity3D の applyTo 以外からは呼ばないこと + void setVelocity(Velocity3D v) { + velocity = (Velocity3D) v.clone(); + } + + // AngularVelocity3D の applyTo 以外からは呼ばないこと + void setAngularVelocity(AngularVelocity3D w) { + angularvelocity = (AngularVelocity3D) w.clone(); + } + + public void setGravityCenter(Position3D gravityCenter) { + this.gravityCenter = gravityCenter; + } + + public Position3D getGravityCenter() { + return getPosition3D().add(gravityCenter); + } + + public void setMass(double mass) { + this.mass = mass; + inertia = new Inertia3D(this); + } + + public double getMass() { + return mass; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Velocity3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Velocity3D.java new file mode 100644 index 0000000..bb8c1b4 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/physics/Velocity3D.java @@ -0,0 +1,140 @@ +package org.ntlab.radishforandroidstudio.framework.physics; + +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Property3D; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + + +public class Velocity3D extends Property3D { + private double x; + private double y; + private double z; + + public Velocity3D(Velocity3D v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Velocity3D(Vector3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + public Velocity3D(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Velocity3D() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + public void applyTo(Object3D o) { + ((Solid3D)o).setVelocity(this); + } + + public double getX() { + return this.x; + } + + public double getY() { + return this.y; + } + + public double getZ() { + return this.z; + } + + public Velocity3D setX(double x) { + this.x = x; + return this; + } + + public Velocity3D setY(double y) { + this.y = y; + return this; + } + + public Velocity3D setZ(double z) { + this.z = z; + return this; + } + + public Velocity3D add(double x, double y, double z){ + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public Velocity3D add(Vector3d v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + public Velocity3D setVector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + return this; + } + + public Vector3d getVector3d() { + return new Vector3d(x,y,z); + } + + @Override + public Property3D clone() { + return new Velocity3D(this); + } + + public Velocity3D rotX(double a) { + Vector3d v = getVector3d(); + Transform3D rotX = new Transform3D(); + rotX.rotX(a); + rotX.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D rotY(double a) { + Vector3d v = getVector3d(); + Transform3D rotY = new Transform3D(); + rotY.rotY(a); + rotY.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D rotZ(double a) { + Vector3d v = getVector3d(); + Transform3D rotZ = new Transform3D(); + rotZ.rotZ(a); + rotZ.transform(v); + setVector3d(v); + return this; + } + + public Velocity3D setVelocity(double velocity) { + Vector3d v = getVector3d(); + double oldV = v.length(); + v.scale(velocity / oldV); + setVector3d(v); + return this; + } + + public Velocity3D mul(double d) { + this.x *= d; + this.y *= d; + this.z *= d; + return this; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/Event.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/Event.java new file mode 100644 index 0000000..f174981 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/Event.java @@ -0,0 +1,13 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +public class Event { + private String name; + + public Event(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/FSM.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/FSM.java new file mode 100644 index 0000000..f5d1a5a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/FSM.java @@ -0,0 +1,38 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; + +public class FSM { + private State initialState = null; + protected State currentState = null; + private Hashtable states = new Hashtable(); + + public FSM(State initialState, Hashtable states) { + this.initialState = initialState; + this.states = states; + currentState = initialState; + Collection allStates = states.values(); + Iterator it = allStates.iterator(); + while (it.hasNext()) { + State s = it.next(); + s.setOwner(this); + } + } + + public void addState(String stateName, State s) { + states.put(stateName, s); + s.setOwner(this); + } + + public boolean trans(Event e) { + currentState = currentState.getSuccessor(e); + if (currentState == null) return false; + return true; + } + + public State getCurrentState() { + return currentState; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/IWorld.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/IWorld.java new file mode 100644 index 0000000..03d4789 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/IWorld.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +public interface IWorld { + abstract public void dialogOpen(); + abstract public void dialogClose(); + abstract public void dialogMessage(String message); + abstract public void showOption(int n, String option); + abstract public boolean isDialogOpen(); + abstract public void action(String action, Event event, ScenarioState nextState); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioAction.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioAction.java new file mode 100644 index 0000000..f2a9ff9 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioAction.java @@ -0,0 +1,13 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +public class ScenarioAction { + private String name; + + public ScenarioAction(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioFSM.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioFSM.java new file mode 100644 index 0000000..2fec862 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioFSM.java @@ -0,0 +1,21 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +import java.util.Hashtable; + +public class ScenarioFSM extends FSM { + ScenarioManager manager; + + public ScenarioFSM(State initialState, Hashtable states, ScenarioManager manager) { + super(initialState, states); + this.manager = manager; + } + + public boolean trans(Event e) { + e = ((ScenarioState)currentState).canTrans(e); + if (e == null) return false; + ScenarioAction action = ((ScenarioState)currentState).getAction(e); + boolean result = super.trans(e); + manager.action(action, e, (ScenarioState)currentState); + return result; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioManager.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioManager.java new file mode 100644 index 0000000..d1de488 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioManager.java @@ -0,0 +1,183 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.helpers.DefaultHandler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +public class ScenarioManager { + private IWorld realWorld; + private Hashtable stateMachines = new Hashtable(); + private Hashtable allStates = new Hashtable(); + private Hashtable allEvents = new Hashtable(); + + public ScenarioManager(String xmlFileName, IWorld realWorld) { + this.realWorld = realWorld; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + factory.setValidating(true); + factory.setAttribute( + "http://java.sun.com/xml/jaxp/properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.setErrorHandler(new DefaultHandler()); + Document document = builder.parse(xmlFileName); + + // 有限状態マシンの作成、状態の登録、イベントの登録 + Hashtable allTrans = new Hashtable(); + NodeList scenario = document.getChildNodes(); + NodeList fsmNodes = scenario.item(0).getChildNodes(); + for (int n = 0; n < fsmNodes.getLength(); n++) { + Node fsmNode = fsmNodes.item(n); + if (fsmNode.getNodeName().equals("FSM")) { + Hashtable fsmStates = new Hashtable(); + NodeList stateNodes = fsmNode.getChildNodes(); + for (int m = 0; m < stateNodes.getLength(); m++) { + Node stateNode = stateNodes.item(m); + if (stateNode.getNodeName().equals("State")) { + ScenarioState state = new ScenarioState(); + String stateName = stateNode.getAttributes().getNamedItem("name").getNodeValue(); + fsmStates.put(stateName, state); + allStates.put(stateName, state); + Node stateMessageNode = stateNode.getAttributes().getNamedItem("message"); + if (stateMessageNode != null) state.setMessage(stateMessageNode.getNodeValue()); + NodeList eventNodes = stateNode.getChildNodes(); + allTrans.put(state, eventNodes); + for (int l = 0; l < eventNodes.getLength(); l++) { + Node eventNode = eventNodes.item(l); + if (eventNode.getNodeName().equals("Event")) { + String eventName = eventNode.getAttributes().getNamedItem("name").getNodeValue(); + Event e = allEvents.get(eventName); + if (e == null) { + e = new Event(eventName); + allEvents.put(eventName, e); + } + } + } + } + } + String initialStateName = fsmNode.getAttributes().getNamedItem("initial").getNodeValue(); + State initialState = fsmStates.get(initialStateName); + ScenarioFSM fsm = new ScenarioFSM(initialState, fsmStates, this); + String fsmName = fsmNode.getAttributes().getNamedItem("name").getNodeValue(); + stateMachines.put(fsmName, fsm); + } + } + + // 状態遷移を設定する + Set> allTransEntries = allTrans.entrySet(); + Iterator> it = allTransEntries.iterator(); + while (it.hasNext()) { + Entry transEntry = it.next(); + ScenarioState state = transEntry.getKey(); + NodeList eventNodes = transEntry.getValue(); + for (int l = 0; l < eventNodes.getLength(); l++) { + Node eventNode = eventNodes.item(l); + if (eventNode.getNodeName().equals("Event")) { + Node eventNameNode = eventNode.getAttributes().getNamedItem("name"); + Event e = null; + if (eventNameNode != null) { + String eventName = eventNameNode.getNodeValue(); + e = allEvents.get(eventName); + } + Node nextStateNameNode = eventNode.getAttributes().getNamedItem("trans"); + State nextState = null; + if (nextStateNameNode != null) { + String nextStateName = nextStateNameNode.getNodeValue(); + nextState = allStates.get(nextStateName); + } + Node syncEventNameNode = eventNode.getAttributes().getNamedItem("sync"); + Event syncEvent = null; + if (syncEventNameNode != null) { + String syncEventName = syncEventNameNode.getNodeValue(); + syncEvent = allEvents.get(syncEventName); + } + ArrayList guards = new ArrayList(); + Node guardStateNameNode = eventNode.getAttributes().getNamedItem("guard"); + if (guardStateNameNode != null) { + String guardStateName = guardStateNameNode.getNodeValue(); + State guardState = allStates.get(guardStateName); + if (guardState != null) guards.add(guardState); + } + Node guardStateNameNode2 = eventNode.getAttributes().getNamedItem("guard2"); + if (guardStateNameNode2 != null) { + String guardStateName2 = guardStateNameNode2.getNodeValue(); + State guardState2 = allStates.get(guardStateName2); + if (guardState2 != null) guards.add(guardState2); + } + Node guardStateNameNode3 = eventNode.getAttributes().getNamedItem("guard3"); + if (guardStateNameNode3 != null) { + String guardStateName3 = guardStateNameNode3.getNodeValue(); + State guardState3 = allStates.get(guardStateName3); + if (guardState3 != null) guards.add(guardState3); + } + Node actionNameNode = eventNode.getAttributes().getNamedItem("action"); + ScenarioAction action = null; + if (actionNameNode != null) { + String actionName = actionNameNode.getNodeValue(); + action = new ScenarioAction(actionName); + } + state.addTransition(e, nextState, syncEvent, guards, action); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void fire(Event e){ + Collection fsms = stateMachines.values(); + Iterator it = fsms.iterator(); + while (it.hasNext()) { + ScenarioFSM fsm = it.next(); + fsm.trans(e); + } + } + + public void fire(String eventName) { + Event e = allEvents.get(eventName); + if (e == null) return; + fire(e); + } + + public void action(ScenarioAction action, Event event, ScenarioState nextState) { + if (action != null) { + String sAction = action.getName(); + if (sAction.equals("openDialog")) { + realWorld.dialogOpen(); + } else if (sAction.equals("closeDialog")) { + realWorld.dialogClose(); + } else if (sAction.equals("print")) { + System.out.println(event.getName()); + } + realWorld.action(sAction, event, nextState); + } + if (realWorld.isDialogOpen()) { + String message = nextState.getMessage(); + if (message != null) { + realWorld.dialogMessage(message); + Enumeration events = nextState.getEvents(); + int n = 0; + while (events.hasMoreElements()) { + Event ev = events.nextElement(); + realWorld.showOption(n, ev.getName()); + n++; + } + } + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioState.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioState.java new file mode 100644 index 0000000..fb0e4dc --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/ScenarioState.java @@ -0,0 +1,67 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; + +public class ScenarioState extends State { + private Hashtable transitionSyncs = new Hashtable(); + private Hashtable> transitionGuards = new Hashtable>(); + private Hashtable transitionActions = new Hashtable(); + private String message = null; + + public ScenarioState() { + super(); + } + + public ScenarioState(Hashtable transitions, + Hashtable transitionSyncs, + Hashtable> transitionGuards, + Hashtable transitionActions, + String message) { + super(transitions); + this.transitionSyncs = transitionSyncs; + this.transitionGuards = transitionGuards; + this.transitionActions = transitionActions; + this.message = message; + } + + public void addTransition(Event event, State succ, Event syncEvent, ArrayList guards, ScenarioAction action) { + addTransition(event, succ); + if (syncEvent != null) transitionSyncs.put(event, syncEvent); + if (guards != null) transitionGuards.put(event, guards); + if (action != null) transitionActions.put(event, action); + } + + Event canTrans(Event event) { + Enumeration events = getEvents(); + while (events.hasMoreElements()) { + Event e = events.nextElement(); + if (event == e || event == transitionSyncs.get(e)) { + ArrayList guardStates = transitionGuards.get(e); + boolean bSatisfyGuard = true; + for (int n = 0; n < guardStates.size(); n++) { + State s = guardStates.get(n); + if (s.getOwner().getCurrentState() != s) { + bSatisfyGuard = false; + break; + } + } + if (bSatisfyGuard) return e; + } + } + return null; + } + + public ScenarioAction getAction(Event e) { + return transitionActions.get(e); + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/State.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/State.java new file mode 100644 index 0000000..756adbc --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/scenario/State.java @@ -0,0 +1,37 @@ +package org.ntlab.radishforandroidstudio.framework.scenario; + +import java.util.Enumeration; +import java.util.Hashtable; + +public class State { + private FSM owner = null; + private Hashtable transitions = new Hashtable(); + + public State() { + } + + public State(Hashtable transitions) { + this.transitions = transitions; + } + + public void addTransition(Event e, State s) { + transitions.put(e, s); + } + + public Enumeration getEvents() { + return transitions.keys(); + } + + public State getSuccessor(Event e) { + if (!transitions.containsKey(e)) return null; + return transitions.get(e); + } + + public void setOwner(FSM fsm) { + owner = fsm; + } + + public FSM getOwner() { + return owner; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Camera3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Camera3D.java new file mode 100644 index 0000000..ba415e9 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Camera3D.java @@ -0,0 +1,459 @@ +package org.ntlab.radishforandroidstudio.framework.view3D; + +import java.util.ArrayList; + +import org.ntlab.radishforandroidstudio.framework.model3D.GeometryUtility; +import org.ntlab.radishforandroidstudio.framework.model3D.Object3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Placeable; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Universe; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.TransformGroup; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +/** + * 画角調整機能が付いたカメラ
+ * 視点、注視対象、視線のうち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 targetObjList = null; + protected ArrayList 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(); + targetList.add(target); + } + + /** + * カメラの注視対象を追加する + * + * @param target + * 注視対象 + */ + public void addTarget(Object3D target) { + if (targetObjList == null) targetObjList = new ArrayList(); + targetObjList.add(target); + } + + /** + * カメラの注視対象を追加する + * + * @param target + * 注視対象 + */ + public void addTarget(Placeable target) { + if (targetObjList == null) targetObjList = new ArrayList(); + 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; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Viewer3D.java b/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Viewer3D.java new file mode 100644 index 0000000..c4c535f --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/framework/view3D/Viewer3D.java @@ -0,0 +1,97 @@ +package org.ntlab.radishforandroidstudio.framework.view3D; + +import org.ntlab.radishforandroidstudio.framework.model3D.BackgroundBox; +import org.ntlab.radishforandroidstudio.framework.model3D.IViewer3D; +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; +import org.ntlab.radishforandroidstudio.java3d.Box; +import org.ntlab.radishforandroidstudio.java3d.Cone; +import org.ntlab.radishforandroidstudio.java3d.Cylinder; +import org.ntlab.radishforandroidstudio.java3d.GraphicsContext3D; +import org.ntlab.radishforandroidstudio.java3d.Light; +import org.ntlab.radishforandroidstudio.java3d.Node; +import org.ntlab.radishforandroidstudio.java3d.Shape3D; +import org.ntlab.radishforandroidstudio.java3d.Sphere; +import org.ntlab.radishforandroidstudio.java3d.Transform3D; +import org.ntlab.radishforandroidstudio.java3d.Vector3d; + +import java.util.ArrayList; + +public class Viewer3D implements IViewer3D { + private GraphicsContext3D gc3D = null; + private ArrayList lights = null; + private BackgroundBox skyBox = null; + private Camera3D camera = null; + + public Viewer3D(Camera3D camera) { + this.camera = camera; + } + + @Override + public void setGraphicsContext3D(GraphicsContext3D gc3D) { + if (this.gc3D != gc3D) { + this.gc3D = gc3D; + } + } + + @Override + public void surfaceChanged(int width, int height) { + gc3D.update(width, height, (float)camera.getFieldOfView(), (float)camera.getFrontClipDistance(), (float)camera.getBackClipDistance(), camera.isParallel()); + } + + @Override + public void onDrawFrame() { + Position3D eye = camera.getViewPoint(); + Position3D center = eye.clone().add(camera.getViewLine()); + Vector3d up = camera.getViewUp(); + gc3D.update((float)camera.getFieldOfView(), (float)camera.getFrontClipDistance(), (float)camera.getBackClipDistance(), eye, center, up, camera.isParallel()); + } + + @Override + public void update(ArrayList lights, BackgroundBox skyBox) { + // 光源の更新 + if (this.lights != lights) { + this.lights = lights; + } + + // スカイボックスの更新 + if (this.skyBox != skyBox) { + this.skyBox = skyBox; + } + } + + @Override + public void draw(Node node) { + if (node instanceof Box) { + gc3D.draw(((Box)node).getShape(Box.FRONT)); + gc3D.draw(((Box)node).getShape(Box.BACK)); + gc3D.draw(((Box)node).getShape(Box.LEFT)); + gc3D.draw(((Box)node).getShape(Box.RIGHT)); + gc3D.draw(((Box)node).getShape(Box.TOP)); + gc3D.draw(((Box)node).getShape(Box.BOTTOM)); + } else if (node instanceof Cone) { + gc3D.draw(((Cone)node).getShape(Cone.BODY)); + gc3D.draw(((Cone)node).getShape(Cone.CAP)); + } else if (node instanceof Cylinder) { + gc3D.draw(((Cylinder)node).getShape(Cylinder.BODY)); + gc3D.draw(((Cylinder)node).getShape(Cylinder.TOP)); + gc3D.draw(((Cylinder)node).getShape(Cylinder.BOTTOM)); + } else if (node instanceof Sphere) { + gc3D.draw(((Sphere)node).getShape(Sphere.BODY)); + } else if (node instanceof Shape3D) { + gc3D.draw((Shape3D)node); + } else if (node instanceof Light){ + gc3D.updateLightState((Light)node); + } + } + + @Override + public void pushTransform(Transform3D transform) { + gc3D.pushMatrix(); + gc3D.multMatrix(transform); + } + + @Override + public void popTransform() { + gc3D.popMatrix(); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/AmbientLight.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/AmbientLight.java new file mode 100644 index 0000000..bf9b396 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/AmbientLight.java @@ -0,0 +1,13 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class AmbientLight extends Light { + + @Override + public Node cloneTree() { + return new AmbientLight(color); + } + + public AmbientLight(Color3f c){ + super(c); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Appearance.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Appearance.java new file mode 100644 index 0000000..52cfcf1 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Appearance.java @@ -0,0 +1,77 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Appearance extends NodeComponent { + private Material material = null; + private Texture texture = null; + private TextureAttributes textureAttribute = null; + private TexCoordGeneration texCoordGeneration = null; + private TextureUnitState[] stateArray; + + public void setMaterial(Material m) { + this.material = m; + } + + public Material getMaterial() { + return material; + } + + public void setTexture(Texture texture) { + this.texture = texture; + } + + public Texture getTexture() { + return texture; + } + + public int getTextureUnitCount() { + if (stateArray == null) return 0; + return stateArray.length; + } + + public TextureUnitState getTextureUnitState(int textureUnit) { + if (stateArray == null) return null; + return stateArray[textureUnit]; + } + + public void setTextureUnitState(TextureUnitState[] stateArray) { + this.stateArray = stateArray; + } + + public void setTextureUnitState(int textureUnit, TextureUnitState textureUnitState) { + stateArray[textureUnit] = textureUnitState; + } + + public TextureAttributes getTextureAttributes() { + return textureAttribute; + } + + public void setTextureAttributes(TextureAttributes textureAttribute) { + this.textureAttribute = textureAttribute; + } + + public TexCoordGeneration getTexCoordGeneration() { + return texCoordGeneration; + } + + public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) { + this.texCoordGeneration = texCoordGeneration; + } + + @Override + public NodeComponent cloneNodeComponent() { + Appearance ap = new Appearance(); + if (material != null) { + ap.material = (Material)material.cloneNodeComponent(); + } + if (texture != null) { + ap.texture = (Texture)texture.cloneNodeComponent(); + } + if (textureAttribute != null) { + ap.textureAttribute = (TextureAttributes)textureAttribute.cloneNodeComponent(); + } + if (texCoordGeneration != null) { + ap.texCoordGeneration = (TexCoordGeneration)texCoordGeneration.cloneNodeComponent(); + } + return ap; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/AxisAngle4d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/AxisAngle4d.java new file mode 100644 index 0000000..2a6fcd9 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/AxisAngle4d.java @@ -0,0 +1,62 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class AxisAngle4d { + public double angle; + public double x; + public double y; + public double z; + static final double EPS = 1.0e-12; + + // コンストラクタ + public AxisAngle4d() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public AxisAngle4d(double px, double py, double pz, double pa) { + x = px; + y = py; + z = pz; + angle = pa; + } + + public AxisAngle4d(AxisAngle4d a) { + x = a.x; + y = a.y; + z = a.z; + angle = a.angle; + } + + public AxisAngle4d(Vector3d axis, double angle) { + x = axis.x; + y = axis.y; + z = axis.z; + this.angle = angle; + } + + public final void set(Quat4d q1) { + double mag = q1.x*q1.x + q1.y*q1.y + q1.z*q1.z; + + if( mag > EPS ) { + mag = Math.sqrt(mag); + double invMag = 1.0/mag; + + x = q1.x*invMag; + y = q1.y*invMag; + z = q1.z*invMag; + angle = 2.0* Math.atan2(mag, q1.w); + } else { + x = 0.0d; + y = 1.0d; + z = 0.0d; + angle = 0.0d; + } + } + + public AxisAngle4d clone() { + return new AxisAngle4d(x, y, z, angle); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Background.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Background.java new file mode 100644 index 0000000..97dbac4 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Background.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Background extends Leaf { + + @Override + public Node cloneTree() { + return new Background(); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingBox.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingBox.java new file mode 100644 index 0000000..2d066b3 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingBox.java @@ -0,0 +1,37 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class BoundingBox extends Bounds { + Point3d lower; + Point3d upper; + + public BoundingBox(Point3d lower, Point3d upper) { + boundId = BOUNDING_BOX; + this.lower = lower; + this.upper = upper; + } + + @Override + public Object clone() { + // TODO Auto-generated method stub + return new BoundingBox(new Point3d(lower), new Point3d(upper)); + } + + @Override + public void combine(Bounds boundsObject) { + // TODO Auto-generated method stub + + } + + @Override + public void transform(Transform3D trans) { + // TODO Auto-generated method stub + + } + + @Override + public boolean intersect(Bounds boundsObject) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingPolytope.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingPolytope.java new file mode 100644 index 0000000..5438784 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingPolytope.java @@ -0,0 +1,543 @@ +package org.ntlab.radishforandroidstudio.java3d; + + +public class BoundingPolytope extends Bounds { + Vector4d[] planes; + double[] mag; // magnitude of plane vector + double[] pDotN; // point on plane dotted with normal + Point3d[] verts; // vertices of polytope + int nVerts; // number of verts in polytope + Point3d centroid = new Point3d(); // centroid of polytope + Point3d boxVerts[]; + boolean allocBoxVerts = false; + + /** + * Constructs a BoundingPolytope using the specified planes. + * + * @param planes + * a set of planes defining the polytope. + * @exception IllegalArgumentException + * if the length of the specified array of planes is less + * than 4. + */ + public BoundingPolytope(Vector4d[] planes) { + boundId = BOUNDING_POLYTOPE; + int i; + double invMag; + this.planes = new Vector4d[planes.length]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + computeAllVerts(); // XXXX: lazy evaluate + } + + /** + * Constructs a BoundingPolytope and initializes it to a set of 6 planes + * that defines a cube such that -1 <= x,y,z <= 1. The values of the planes + * are as follows: + *
    + * planes[0] : x <= 1 (1,0,0,-1)
    + * planes[1] : -x <= 1 (-1,0,0,-1)
    + * planes[2] : y <= 1 (0,1,0,-1)
    + * planes[3] : -y <= 1 (0,-1,0,-1)
    + * planes[4] : z <= 1 (0,0,1,-1)
    + * planes[5] : -z <= 1 (0,0,-1,-1)
    + *
+ */ + public BoundingPolytope() { + boundId = BOUNDING_POLYTOPE; + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + planes[0] = new Vector4d(1.0, 0.0, 0.0, -1.0); + planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0); + planes[2] = new Vector4d(0.0, 1.0, 0.0, -1.0); + planes[3] = new Vector4d(0.0, -1.0, 0.0, -1.0); + planes[4] = new Vector4d(0.0, 0.0, 1.0, -1.0); + planes[5] = new Vector4d(0.0, 0.0, -1.0, -1.0); + mag[0] = 1.0; + mag[1] = 1.0; + mag[2] = 1.0; + mag[3] = 1.0; + mag[4] = 1.0; + mag[5] = 1.0; + computeAllVerts(); // XXXX: lazy evaluate + } + + public void setPlanes(Vector4d[] planes) { + int i; + double invMag; + this.planes = new Vector4d[planes.length]; + pDotN = new double[planes.length]; + mag = new double[planes.length]; + if (planes.length <= 0) { + computeAllVerts(); // XXXX: lazy evaluate + return; + } + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + computeAllVerts(); // XXXX: lazy evaluate + } + + /** + * Returns the equations of the bounding planes for this bounding polytope. + * The equations are copied into the specified array. The array must be + * large enough to hold all of the vectors. The individual array elements + * must be allocated by the caller. + * + * @param planes + * an array Vector4d to receive the bounding planes + */ + public void getPlanes(Vector4d[] planes) { + int i; + for (i = 0; i < planes.length; i++) { + planes[i].x = this.planes[i].x * mag[i]; + planes[i].y = this.planes[i].y * mag[i]; + planes[i].z = this.planes[i].z * mag[i]; + planes[i].w = this.planes[i].w * mag[i]; + } + } + + public int getNumPlanes() { + return planes.length; + } + + /** + * Sets the planes for this BoundingPolytope by keeping its current + * number and position of planes and computing new planes positions + * to enclose the given bounds object. + * @param boundsObject another bounds object + */ + public void set(Bounds boundsObject) { + int i,k; + double dis; + + // no polytope exists yet so initialize one using the boundsObject + if( boundsObject == null ) { + boundsIsEmpty = true; + boundsIsInfinite = false; + computeAllVerts(); // XXXX: lazy evaluate + }else if( boundsObject.boundId == BOUNDING_SPHERE ) { + BoundingSphere sphere = (BoundingSphere)boundsObject; + if( boundsIsEmpty) { + initEmptyPolytope(); // no ptope exist so must initialize to default + computeAllVerts(); + } + for(i=0;i 0.0) planes[i].w = -newD; + if( (newD = ux + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = ux + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + uy + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + uy + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + ly + uz ) + planes[i].w > 0.0) planes[i].w = -newD; + if( (newD = lx + ly + lz ) + planes[i].w > 0.0) planes[i].w = -newD; + } + boundsIsEmpty = boundsObject.boundsIsEmpty; + boundsIsInfinite = boundsObject.boundsIsInfinite; + computeAllVerts(); // XXXX: lazy evaluate + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope polytope = (BoundingPolytope)boundsObject; + if( planes.length != polytope.planes.length) { + planes = new Vector4d[polytope.planes.length]; + for(k=0;k 0.0 ) { + planes[i].w += -dis; + } + } + } else if( boundsObject instanceof BoundingBox){ + BoundingBox b = (BoundingBox)boundsObject; + if( !allocBoxVerts){ + boxVerts = new Point3d[8]; + for(int j=0;j<8;j++)boxVerts[j] = new Point3d(); + allocBoxVerts = true; + } + boxVerts[0].set(b.lower.x, b.lower.y, b.lower.z ); + boxVerts[1].set(b.lower.x, b.upper.y, b.lower.z ); + boxVerts[2].set(b.upper.x, b.lower.y, b.lower.z ); + boxVerts[3].set(b.upper.x, b.upper.y, b.lower.z ); + boxVerts[4].set(b.lower.x, b.lower.y, b.upper.z ); + boxVerts[5].set(b.lower.x, b.upper.y, b.upper.z ); + boxVerts[6].set(b.upper.x, b.lower.y, b.upper.z ); + boxVerts[7].set(b.upper.x, b.upper.y, b.upper.z ); + this.combine(boxVerts); + + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + BoundingPolytope polytope = (BoundingPolytope)boundsObject; + this.combine(polytope.verts); + } else { + } + + computeAllVerts(); + } + + /** + * Combines this bounding polytope with a point. + * @param point a 3d point in space + */ + public void combine(Point3d point) { + int i; + double dis; + + if(boundsIsInfinite) { + return; + } + + if( boundsIsEmpty ){ + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + nVerts = 1; + verts = new Point3d[nVerts]; + verts[0] = new Point3d( point.x, point.y, point.z); + + for(i=0;i 0.0 ) { + planes[i].w += -dis; + } + } + computeAllVerts(); + } + } + + /** + * Combines this bounding polytope with an array of points. + * @param points an array of 3d points in space + */ + public void combine(Point3d[] points) { + int i,j; + double dis; + + if( boundsIsInfinite) { + return; + } + + if( boundsIsEmpty ){ + planes = new Vector4d[6]; + mag = new double[planes.length]; + pDotN = new double[planes.length]; + nVerts = points.length; + verts = new Point3d[nVerts]; + verts[0] = new Point3d( points[0].x, points[0].y, points[0].z); + + for(i=0;i 0.0 ) { + planes[i].w += -dis; + } + } + } + + computeAllVerts(); + } + + /** + * Transforms this bounding polytope by the given transformation matrix. + * + * @param matrix + * a transformation matrix + */ + @Override + public void transform(Transform3D matrix) { + int i; + double invMag; + Transform3D invTrans = new Transform3D(matrix); + invTrans.invert(); + invTrans.transpose(); + for (i = 0; i < planes.length; i++) { + planes[i].x = planes[i].x * mag[i]; + planes[i].y = planes[i].y * mag[i]; + planes[i].z = planes[i].z * mag[i]; + planes[i].w = planes[i].w * mag[i]; + invTrans.transform(planes[i]); + } + for (i = 0; i < planes.length; i++) { + // normalize the plane normals + mag[i] = Math.sqrt(planes[i].x * planes[i].x + planes[i].y + * planes[i].y + planes[i].z * planes[i].z); + invMag = 1.0 / mag[i]; + this.planes[i] = new Vector4d(planes[i].x * invMag, planes[i].y + * invMag, planes[i].z * invMag, planes[i].w * invMag); + } + for (i = 0; i < verts.length; i++) { + matrix.transform(verts[i]); + } + } + + /** + * Test for intersection with another bounds object. + * @param boundsObject another bounds object + * @return true or false indicating if an intersection occured + */ + public boolean intersect(Bounds boundsObject) { + + if( boundsObject == null ) { + return false; + } + + if( boundsIsEmpty || boundsObject.boundsIsEmpty ) { + return false; + } + + if( boundsIsInfinite || boundsObject.boundsIsInfinite ) { + return true; + } + + if( boundsObject.boundId == BOUNDING_SPHERE ) { + return intersect_ptope_sphere( this, (BoundingSphere)boundsObject); + } else if( boundsObject.boundId == BOUNDING_BOX){ + return intersect_ptope_abox( this, (BoundingBox)boundsObject); + } else if(boundsObject.boundId == BOUNDING_POLYTOPE) { + return intersect_ptope_ptope( this, (BoundingPolytope)boundsObject); + } else { + return false; + } + } + + private void computeVertex(int a, int b, int c) { + double det, x, y, z; + det = planes[a].x * planes[b].y * planes[c].z + planes[a].y + * planes[b].z * planes[c].x + planes[a].z * planes[b].x + * planes[c].y - planes[a].z * planes[b].y * planes[c].x + - planes[a].y * planes[b].x * planes[c].z - planes[a].x + * planes[b].z * planes[c].y; + // System.err.println("\n det="+det); + if (det * det < EPSILON) { + // System.err.println("parallel planes="+a+" "+b+" "+c); + return; // two planes are parallel + } + det = 1.0 / det; + x = (planes[b].y * planes[c].z - planes[b].z * planes[c].y) * pDotN[a]; + y = (planes[b].z * planes[c].x - planes[b].x * planes[c].z) * pDotN[a]; + z = (planes[b].x * planes[c].y - planes[b].y * planes[c].x) * pDotN[a]; + x += (planes[c].y * planes[a].z - planes[c].z * planes[a].y) * pDotN[b]; + y += (planes[c].z * planes[a].x - planes[c].x * planes[a].z) * pDotN[b]; + z += (planes[c].x * planes[a].y - planes[c].y * planes[a].x) * pDotN[b]; + x += (planes[a].y * planes[b].z - planes[a].z * planes[b].y) * pDotN[c]; + y += (planes[a].z * planes[b].x - planes[a].x * planes[b].z) * pDotN[c]; + z += (planes[a].x * planes[b].y - planes[a].y * planes[b].x) * pDotN[c]; + x = x * det; + y = y * det; + z = z * det; + if (pointInPolytope(x, y, z)) { + if (nVerts >= verts.length) { + Point3d newVerts[] = new Point3d[nVerts << 1]; + for (int i = 0; i < nVerts; i++) { + newVerts[i] = verts[i]; + } + verts = newVerts; + } + verts[nVerts++] = new Point3d(x, y, z); + } + } + + private void computeAllVerts() { + int i, a, b, c; + double x, y, z; + nVerts = 0; + verts = new Point3d[planes.length * planes.length]; + for (i = 0; i < planes.length; i++) { + pDotN[i] = -planes[i].x * planes[i].w * planes[i].x - planes[i].y + * planes[i].w * planes[i].y - planes[i].z * planes[i].w + * planes[i].z; + } + for (a = 0; a < planes.length - 2; a++) { + for (b = a + 1; b < planes.length - 1; b++) { + for (c = b + 1; c < planes.length; c++) { + computeVertex(a, b, c); + } + } + } + // XXXX: correctly compute centroid + x = y = z = 0.0; + Point3d newVerts[] = new Point3d[nVerts]; + for (i = 0; i < nVerts; i++) { + x += verts[i].x; + y += verts[i].y; + z += verts[i].z; + // copy the verts into an array of the correct size + newVerts[i] = verts[i]; + } + this.verts = newVerts; // copy the verts into an array of the correct + // size + centroid.x = x / nVerts; + centroid.y = y / nVerts; + centroid.z = z / nVerts; + } + + private boolean pointInPolytope(double x, double y, double z) { + for (int i = 0; i < planes.length; i++) { + if ((x * planes[i].x + y * planes[i].y + z * planes[i].z + planes[i].w) > EPSILON) { + return false; + } + } + return true; + } + + private void checkBoundsIsEmpty() { + boundsIsEmpty = (planes.length < 4); + } + + private void initEmptyPolytope() { + planes = new Vector4d[6]; + pDotN = new double[6]; + mag = new double[6]; + verts = new Point3d[planes.length*planes.length]; + nVerts = 0; + + planes[0] = new Vector4d( 1.0, 0.0, 0.0, -1.0 ); + planes[1] = new Vector4d(-1.0, 0.0, 0.0, -1.0 ); + planes[2] = new Vector4d( 0.0, 1.0, 0.0, -1.0 ); + planes[3] = new Vector4d( 0.0,-1.0, 0.0, -1.0 ); + planes[4] = new Vector4d( 0.0, 0.0, 1.0, -1.0 ); + planes[5] = new Vector4d( 0.0, 0.0,-1.0, -1.0 ); + mag[0] = 1.0; + mag[1] = 1.0; + mag[2] = 1.0; + mag[3] = 1.0; + mag[4] = 1.0; + mag[5] = 1.0; + checkBoundsIsEmpty(); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingSphere.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingSphere.java new file mode 100644 index 0000000..326547a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BoundingSphere.java @@ -0,0 +1,93 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class BoundingSphere extends Bounds { + public Point3d center = new Point3d(); + public double radius; + + public BoundingSphere(Point3d center, double r) { + boundId = BOUNDING_SPHERE; + this.center = center; + this.radius = r; + } + + public BoundingSphere() { + boundId = BOUNDING_SPHERE; + center.x = 0; + center.y = 0; + center.z = 0; + radius = 1; + } + + public void combine(BoundingSphere bs) { + double r1 = this.radius; + double r2 = bs.radius; + + Point3d c1 = new Point3d(center); + Point3d c2 = new Point3d(bs.center); + + double d = center.distance(bs.center); + + if (d + r2 <= r1) { + // this が bs を包含する場合、何もしない + } else if (d + r1 < r2) { + // bs が this を包含する場合、bsをコピー + this.radius = bs.radius; + this.center.set(bs.center); + } else { + // this と  bs のいずれも他方を包含しない場合 + this.radius = (r1 + r2 + d) / 2.0; + + double a1 = this.radius - r1; + double a2 = this.radius - r2; + + c1.scale(a2); + c2.scale(a1); + c1.add(c2); + c1.scale(1 / d); + center.set(c1); + } + } + + public void transform(Transform3D trans) { + Vector4d v = new Vector4d(center.x, center.y, center.z, 1.0); + trans.transform(v); + center.set(v.x, v.y, v.z); + + double max = 0.0; + for(int i = 0; i < trans.scales.length; i++) { + if (max < trans.scales[i]) { + max = trans.scales[i]; + } + } + radius *= max; + } + + @Override + public Object clone() { + return new BoundingSphere(new Point3d(this.center), this.radius); + } + + @Override + public void combine(Bounds boundsObject) { + if( boundsObject instanceof BoundingSphere) { + combine((BoundingSphere) boundsObject); + } + } + + @Override + public boolean intersect(Bounds boundsObject) { + if (boundsObject instanceof BoundingSphere) { + BoundingSphere bs = (BoundingSphere) boundsObject; + double r = this.radius + bs.radius; + double x = center.x - bs.center.x; + double y = center.y - bs.center.y; + double z = center.z - bs.center.z; + double d = x * x + y * y + z * z; + if (d <= r * r) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Bounds.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Bounds.java new file mode 100644 index 0000000..80fcdda --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Bounds.java @@ -0,0 +1,459 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Bounds extends Object implements Cloneable { + static final double EPSILON = .000001; + static final boolean debug = false; + static final int BOUNDING_BOX = 0x1; + static final int BOUNDING_SPHERE = 0x2; + static final int BOUNDING_POLYTOPE = 0x4; + boolean boundsIsEmpty = false; + boolean boundsIsInfinite = false; + int boundId = 0; + + public Bounds(){ + + } + + // public Bounds clone() { + // return new Bounds(); + // } + + @Override + public abstract Object clone(); + + public abstract void combine(Bounds boundsObject); + + public abstract void transform(Transform3D trans); + + public abstract boolean intersect(Bounds boundsObject); + + + + // //浸食確認メソッド。radius(半径)を利用した円の接触であると仮定してみるといいかも + // public boolean intersect(BoundingSphere bs) { + // // TODO Auto-generated method stub + // return false; + // } + + private void test_point(Vector4d[] planes, Point3d new_point) { + for (int i = 0; i < planes.length; i++){ + double dist = (new_point.x*planes[i].x + new_point.y*planes[i].y + + new_point.z*planes[i].z + planes[i].w ) ; + if (dist > EPSILON ){ + System.err.println("new point is outside of" + + " plane["+i+"] dist = " + dist); + } + } + } + + /** + * computes the closest point from the given point to a set of planes + * (polytope) + * @param g the point + * @param planes array of bounding planes + * @param new_point point on planes closest g + */ + boolean closest_point(Point3d g, Vector4d[] planes, Point3d new_point) { + + double t, s, dist, w; + boolean converged, inside, firstPoint, firstInside; + int i, count; + double ab, ac, bc, ad, bd, cd, aa, bb, cc; + double b1, b2, b3, d1, d2, d3, y1, y2, y3; + double h11, h12, h13, h22, h23, h33; + double l12, l13, l23; + Point3d n = new Point3d(); + Point3d p = new Point3d(); + Vector3d delta = null; + + // These are temporary until the solve code is working + + /* + * The algorithm: We want to find the point "n", closest to "g", while + * still within the the polytope defined by "planes". We find the + * solution by minimizing the value for a "penalty function"; + * + * f = distance(n,g)^2 + sum for each i: w(distance(n, planes[i])) + * + * Where "w" is a weighting which indicates how much more important it + * is to be close to the planes than it is to be close to "g". + * + * We minimize this function by taking it's derivitive, and then solving + * for the value of n when the derivitive equals 0. + * + * For the 1D case with a single plane (a,b,c,d), x = n.x and g = g.x, + * this looks like: + * + * f(x) = (x - g) ^ 2 + w(ax + d)^2 f'(x) = 2x -2g + 2waax + 2wad + * + * (note aa = a^2) setting f'(x) = 0 gives: + * + * (1 + waa)x = g - wad + * + * Note that the solution is just outside the plane [a, d]. With the + * correct choice of w, this should be inside of the EPSILON tolerance + * outside the planes. + * + * Extending to 3D gives the matrix solution: + * + * | (1 + waa) wab wac | H = | wab (1 + wbb) wbc | | wac wbc (1 + wcc) | + * + * b = [g.x - wad, g.y - wbd, g.z - wcd] + * + * H * n = b + * + * n = b * H.inverse() + * + * The implementation speeds this process up by recognizing that H is + * symmetric, so that it can be decomposed into three matrices: + * + * H = L * D * L.transpose() + * + * 1.0 0.0 0.0 d1 0.0 0.0 L = l12 1.0 0.0 D = 0.0 d2 0.0 l13 l23 1.0 0.0 + * 0.0 d3 + * + * n can then be derived by back-substitution, where the original + * problem is decomposed as: + * + * H * n = b L * D * L.transpose() * n = b L * D * y = b; L.transpose() + * * n = y + * + * We can then multiply out the terms of L * D and solve for y, and then + * use y to solve for n. + */ + + w = 100.0 / EPSILON; // must be large enough to ensure that solution + // is within EPSILON of planes + + count = 0; + p.set(g); + + if (debug) { + System.err.println("closest_point():\nincoming g=" + " " + g.x + + " " + g.y + " " + g.z); + } + + converged = false; + firstPoint = true; + firstInside = false; + + Vector4d pln; + + while (!converged) { + if (debug) { + System.err.println("start: p=" + " " + p.x + " " + p.y + " " + + p.z); + } + + // test the current point against the planes, for each + // plane that is violated, add it's contribution to the + // penalty function + inside = true; + aa = 0.0; + bb = 0.0; + cc = 0.0; + ab = 0.0; + ac = 0.0; + bc = 0.0; + ad = 0.0; + bd = 0.0; + cd = 0.0; + for (i = 0; i < planes.length; i++) { + pln = planes[i]; + dist = (p.x * pln.x + p.y * pln.y + p.z * pln.z + pln.w); + // if point is outside or within EPSILON of the boundary, add + // the plane to the penalty matrix. We do this even if the + // point is already inside the polytope to prevent numerical + // instablity in cases where the point is just outside the + // boundary of several planes of the polytope + if (dist > -EPSILON) { + aa = aa + pln.x * pln.x; + bb = bb + pln.y * pln.y; + cc = cc + pln.z * pln.z; + ab = ab + pln.x * pln.y; + ac = ac + pln.x * pln.z; + bc = bc + pln.y * pln.z; + ad = ad + pln.x * pln.w; + bd = bd + pln.y * pln.w; + cd = cd + pln.z * pln.w; + } + // If the point is inside if dist is <= EPSILON + if (dist > EPSILON) { + inside = false; + if (debug) { + System.err.println("point outside plane[" + i + "]=(" + + pln.x + "," + pln.y + ",\n\t" + pln.z + "," + + pln.w + ")\ndist = " + dist); + } + } + } + // see if we are done + if (inside) { + if (debug) { + System.err.println("p is inside"); + } + if (firstPoint) { + firstInside = true; + } + new_point.set(p); + converged = true; + } else { // solve for a closer point + firstPoint = false; + + // this is the upper right corner of H, which is all we + // need to do the decomposition since the matrix is symetric + h11 = 1.0 + aa * w; + h12 = ab * w; + h13 = ac * w; + h22 = 1.0 + bb * w; + h23 = bc * w; + h33 = 1.0 + cc * w; + + if (debug) { + System.err.println(" hessin= "); + System.err.println(h11 + " " + h12 + " " + h13); + System.err.println(" " + h22 + " " + h23); + System.err.println(" " + h33); + } + + // these are the constant terms + b1 = g.x - w * ad; + b2 = g.y - w * bd; + b3 = g.z - w * cd; + + if (debug) { + System.err.println(" b1,b2,b3 = " + b1 + " " + b2 + " " + + b3); + } + + // solve, d1, d2, d3 actually 1/dx, which is more useful + d1 = 1 / h11; + l12 = d1 * h12; + l13 = d1 * h13; + s = h22 - l12 * h12; + d2 = 1 / s; + t = h23 - h12 * l13; + l23 = d2 * t; + d3 = 1 / (h33 - h13 * l13 - t * l23); + + if (debug) { + System.err.println(" l12,l13,l23 " + l12 + " " + l13 + " " + + l23); + System.err.println(" d1,d2,d3 " + d1 + " " + d2 + " " + d3); + } + + // we have L and D, now solve for y + y1 = d1 * b1; + y2 = d2 * (b2 - h12 * y1); + y3 = d3 * (b3 - h13 * y1 - t * y2); + + if (debug) { + System.err.println(" y1,y2,y3 = " + y1 + " " + y2 + " " + + y3); + } + + // we have y, solve for n + n.z = y3; + n.y = (y2 - l23 * n.z); + n.x = (y1 - l13 * n.z - l12 * n.y); + + if (debug) { + System.err.println("new point = " + n.x + " " + n.y + " " + + n.z); + test_point(planes, n); + + if (delta == null) + delta = new Vector3d(); + delta.sub(n, p); + delta.normalize(); + System.err.println("p->n direction: " + delta); + Matrix3d hMatrix = new Matrix3d(); + // check using the the javax.vecmath routine + hMatrix.m00 = h11; + hMatrix.m01 = h12; + hMatrix.m02 = h13; + hMatrix.m10 = h12; // h21 = h12 + hMatrix.m11 = h22; + hMatrix.m12 = h23; + hMatrix.m20 = h13; // h31 = h13 + hMatrix.m21 = h23; // h32 = h22 + hMatrix.m22 = h33; + hMatrix.invert(); + Point3d check = new Point3d(b1, b2, b3); + hMatrix.transform(check); + + System.err.println("check point = " + check.x + " " + + check.y + " " + check.z); + } + + // see if we have converged yet + dist = (p.x - n.x) * (p.x - n.x) + (p.y - n.y) * (p.y - n.y) + + (p.z - n.z) * (p.z - n.z); + + if (debug) { + System.err.println("p->n distance =" + dist); + } + + if (dist < EPSILON) { // close enough + converged = true; + new_point.set(n); + } else { + p.set(n); + count++; + if (count > 4) { // watch for cycling between two minimums + new_point.set(n); + converged = true; + } + } + } + } + if (debug) { + System.err.println("returning pnt (" + new_point.x + " " + + new_point.y + " " + new_point.z + ")"); + + if (firstInside) + System.err.println("input point inside polytope "); + } + return firstInside; + } + + boolean intersect_ptope_sphere( BoundingPolytope polyTope, + BoundingSphere sphere) { + Point3d p = new Point3d(); + boolean inside; + + + if (debug) { + System.err.println("ptope_sphere intersect sphere ="+sphere); + } + inside = closest_point( sphere.center, polyTope.planes, p ); + if (debug) { + System.err.println("ptope sphere intersect point ="+p); + } + if (!inside){ + // if distance between polytope and sphere center is greater than + // radius then no intersection + if (p.distanceSquared( sphere.center) > + sphere.radius*sphere.radius){ + if (debug) { + System.err.println("ptope_sphere returns false"); + } + return false; + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } else { + if (debug) { + System.err.println("ptope_sphere returns true"); + } + return true; + } + } + + boolean intersect_ptope_abox( BoundingPolytope polyTope, BoundingBox box) { + Vector4d planes[] = new Vector4d[6]; + + if (debug) { + System.err.println("ptope_abox, box = " + box); + } + planes[0] = new Vector4d( -1.0, 0.0, 0.0, box.lower.x); + planes[1] = new Vector4d( 1.0, 0.0, 0.0,-box.upper.x); + planes[2] = new Vector4d( 0.0,-1.0, 0.0, box.lower.y); + planes[3] = new Vector4d( 0.0, 1.0, 0.0,-box.upper.y); + planes[4] = new Vector4d( 0.0, 0.0,-1.0, box.lower.z); + planes[5] = new Vector4d( 0.0, 0.0, 1.0,-box.upper.z); + + + BoundingPolytope pbox = new BoundingPolytope( planes); + + boolean result = intersect_ptope_ptope( polyTope, pbox ); + if (debug) { + System.err.println("ptope_abox returns " + result); + } + return(result); + } + + + boolean intersect_ptope_ptope( BoundingPolytope poly1, + BoundingPolytope poly2) { + boolean intersect; + Point3d p = new Point3d(); + Point3d g = new Point3d(); + Point3d gnew = new Point3d(); + Point3d pnew = new Point3d(); + + intersect = false; + + p.x = 0.0; + p.y = 0.0; + p.z = 0.0; + + // start from an arbitrary point on poly1 + closest_point( p, poly1.planes, g); + + // get the closest points on each polytope + if (debug) { + System.err.println("ptope_ptope: first g = "+g); + } + intersect = closest_point( g, poly2.planes, p); + + if (intersect) { + return true; + } + + if (debug) { + System.err.println("first p = "+p+"\n"); + } + intersect = closest_point( p, poly1.planes, gnew); + if (debug) { + System.err.println("gnew = "+gnew+" intersect="+intersect); + } + + // loop until the closest points on the two polytopes are not changing + + double prevDist = p.distanceSquared(g); + double dist; + + while( !intersect ) { + + dist = p.distanceSquared(gnew); + + if (dist < prevDist) { + g.set(gnew); + intersect = closest_point( g, poly2.planes, pnew ); + if (debug) { + System.err.println("pnew = "+pnew+" intersect="+intersect); + } + } else { + g.set(gnew); + break; + } + prevDist = dist; + dist = pnew.distanceSquared(g); + + if (dist < prevDist) { + p.set(pnew); + if( !intersect ) { + intersect = closest_point( p, poly1.planes, gnew ); + if (debug) { + System.err.println("gnew = "+gnew+" intersect="+ + intersect); + } + } + } else { + p.set(pnew); + break; + } + prevDist = dist; + } + + if (debug) { + System.err.println("gnew="+" "+gnew.x+" "+gnew.y+" "+gnew.z); + System.err.println("pnew="+" "+pnew.x+" "+pnew.y+" "+pnew.z); + } + return intersect; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Box.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Box.java new file mode 100644 index 0000000..374cb01 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Box.java @@ -0,0 +1,238 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Box extends Primitive { + public static final int FRONT = 0; + public static final int BACK = 1; + public static final int RIGHT = 2; + public static final int LEFT = 3; + public static final int TOP = 4; + public static final int BOTTOM = 5; + + float xDim = 1.0f; + float yDim = 1.0f; + float zDim = 1.0f; + + private Shape3D frontShape = null; + private Shape3D backShape = null; + private Shape3D rightShape = null; + private Shape3D leftShape = null; + private Shape3D topShape = null; + private Shape3D bottomShape = null; + + public Box() { + this(1.0f, 1.0f, 1.0f, null); + } + + public Box(float x, float y, float z, Appearance ap) { + float coordinates[][] = { + {-x, -y, z}, + { x, -y, z}, + { x, y, z}, + {-x, y, z}, + {-x, -y, -z}, + { x, -y, -z}, + { x, y, -z}, + {-x, y, -z} + }; + + float uv[] = { + 0, 0, 1, 0, 1, 1 , 0, 1, + }; + + // 各面のジオメトリを作成 + TriangleFanArray frontGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray backGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray rightGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray leftGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray topGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + TriangleFanArray bottomGeom = new TriangleFanArray(4, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[]{4}); + + // 頂点座標の設定 + frontGeom.setCoordinate(0, coordinates[0]); + frontGeom.setCoordinate(1, coordinates[1]); + frontGeom.setCoordinate(2, coordinates[2]); + frontGeom.setCoordinate(3, coordinates[3]); + + backGeom.setCoordinate(0, coordinates[5]); + backGeom.setCoordinate(1, coordinates[4]); + backGeom.setCoordinate(2, coordinates[7]); + backGeom.setCoordinate(3, coordinates[6]); + + rightGeom.setCoordinate(0, coordinates[1]); + rightGeom.setCoordinate(1, coordinates[5]); + rightGeom.setCoordinate(2, coordinates[6]); + rightGeom.setCoordinate(3, coordinates[2]); + + leftGeom.setCoordinate(0, coordinates[4]); + leftGeom.setCoordinate(1, coordinates[0]); + leftGeom.setCoordinate(2, coordinates[3]); + leftGeom.setCoordinate(3, coordinates[7]); + + topGeom.setCoordinate(0, coordinates[3]); + topGeom.setCoordinate(1, coordinates[2]); + topGeom.setCoordinate(2, coordinates[6]); + topGeom.setCoordinate(3, coordinates[7]); + + bottomGeom.setCoordinate(0, coordinates[4]); + bottomGeom.setCoordinate(1, coordinates[5]); + bottomGeom.setCoordinate(2, coordinates[1]); + bottomGeom.setCoordinate(3, coordinates[0]); + + // テクスチャ座標の設定 + frontGeom.setTextureCoordinates(0, uv); + backGeom.setTextureCoordinates(0, uv); + rightGeom.setTextureCoordinates(0, uv); + leftGeom.setTextureCoordinates(0, uv); + topGeom.setTextureCoordinates(0, uv); + bottomGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + float[] frontNorm = new float[]{0.0f, 0.0f, 1.0f}; + frontGeom.setNormal(0, frontNorm); + frontGeom.setNormal(1, frontNorm); + frontGeom.setNormal(2, frontNorm); + frontGeom.setNormal(3, frontNorm); + + float[] backNorm = new float[]{0.0f, 0.0f, -1.0f}; + backGeom.setNormal(0, backNorm); + backGeom.setNormal(1, backNorm); + backGeom.setNormal(2, backNorm); + backGeom.setNormal(3, backNorm); + + float[] rightNorm = new float[]{1.0f, 0.0f, 0.0f}; + rightGeom.setNormal(0, rightNorm); + rightGeom.setNormal(1, rightNorm); + rightGeom.setNormal(2, rightNorm); + rightGeom.setNormal(3, rightNorm); + + float[] leftNorm = new float[]{-1.0f, 0.0f, 0.0f}; + leftGeom.setNormal(0, leftNorm); + leftGeom.setNormal(1, leftNorm); + leftGeom.setNormal(2, leftNorm); + leftGeom.setNormal(3, leftNorm); + + float[] topNorm = new float[]{0.0f, 1.0f, 0.0f}; + topGeom.setNormal(0, topNorm); + topGeom.setNormal(1, topNorm); + topGeom.setNormal(2, topNorm); + topGeom.setNormal(3, topNorm); + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + bottomGeom.setNormal(0, bottomNorm); + bottomGeom.setNormal(1, bottomNorm); + bottomGeom.setNormal(2, bottomNorm); + bottomGeom.setNormal(3, bottomNorm); + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); + Appearance ap1; + Appearance ap2; + Appearance ap3; + Appearance ap4; + Appearance ap5; + Appearance ap6; + Texture tex = ap.getTexture(); + if (tex != null && tex instanceof TextureCubeMap) { + // GL10 では GL_TEXTURE_CUBE_MAP が使えないので、TextureCubeMap の場合は Texture2D に分解する + ap1 = (Appearance)ap.cloneNodeComponent(); + ap2 = (Appearance)ap.cloneNodeComponent(); + ap3 = (Appearance)ap.cloneNodeComponent(); + ap4 = (Appearance)ap.cloneNodeComponent(); + ap5 = (Appearance)ap.cloneNodeComponent(); + ap6 = (Appearance)ap.cloneNodeComponent(); + ImageComponent ic1 = tex.getImage(TextureCubeMap.POSITIVE_Z); + Texture2D tex1 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic1.width, ic1.height); + tex1.setImage(0, ic1); + ap1.setTexture(tex1); + ImageComponent ic2 = tex.getImage(TextureCubeMap.NEGATIVE_Z); + Texture2D tex2 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic2.width, ic2.height); + tex2.setImage(0, ic2); + ap2.setTexture(tex2); + ImageComponent ic3 = tex.getImage(TextureCubeMap.POSITIVE_X); + Texture2D tex3 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic3.width, ic3.height); + tex3.setImage(0, ic3); + ap3.setTexture(tex3); + ImageComponent ic4 = tex.getImage(TextureCubeMap.NEGATIVE_X); + Texture2D tex4 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic4.width, ic4.height); + tex4.setImage(0, ic4); + ap4.setTexture(tex4); + ImageComponent ic5 = tex.getImage(TextureCubeMap.POSITIVE_Y); + Texture2D tex5 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic5.width, ic5.height); + tex5.setImage(0, ic5); + ap5.setTexture(tex5); + ImageComponent ic6 = tex.getImage(TextureCubeMap.NEGATIVE_Y); + Texture2D tex6 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, ic6.width, ic6.height); + tex6.setImage(0, ic6); + ap6.setTexture(tex6); + } else { + ap1 = ap2 = ap3 = ap4 = ap5 = ap6 = ap; + } + + // 各面の作成 + frontShape = new Shape3D(frontGeom, ap1); + backShape = new Shape3D(backGeom, ap2); + rightShape = new Shape3D(rightGeom, ap3); + leftShape = new Shape3D(leftGeom, ap4); + topShape = new Shape3D(topGeom, ap5); + bottomShape = new Shape3D(bottomGeom, ap6); + + xDim = x; + yDim = y; + zDim = z; + } + + public double getXdimension() { + return xDim; + } + + public double getYdimension() { + return yDim; + } + + public double getZdimension() { + return zDim; + } + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case FRONT: + return frontShape; + case BACK: + return backShape; + case RIGHT: + return rightShape; + case LEFT: + return leftShape; + case TOP: + return topShape; + case BOTTOM: + return bottomShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Box b = new Box(xDim, yDim, zDim, ap); + return b; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/BranchGroup.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BranchGroup.java new file mode 100644 index 0000000..9a14c54 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/BranchGroup.java @@ -0,0 +1,6 @@ +package org.ntlab.radishforandroidstudio.java3d; + + +public class BranchGroup extends Group { + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color3f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color3f.java new file mode 100644 index 0000000..9ba8f37 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color3f.java @@ -0,0 +1,26 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Color3f { + public float r; + public float g; + public float b; + + // コンストラクタ + public Color3f() { + r = 0.0f; + g = 0.0f; + b = 0.0f; + } + + // コンストラクタ + public Color3f(float pr, float pg, float pb) { + r = pr; + g = pg; + b = pb; + } + + public Color3f clone() { + return new Color3f(r, g, b); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color4f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color4f.java new file mode 100644 index 0000000..2569222 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Color4f.java @@ -0,0 +1,29 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Color4f { + public float r; + public float g; + public float b; + public float a; + + // コンストラクタ + public Color4f() { + r = 0.0f; + g = 0.0f; + b = 0.0f; + a = 0.0f; + } + + // コンストラクタ + public Color4f(float pr, float pg, float pb, float pa) { + r = pr; + g = pg; + b = pb; + a = pa; + } + + public Color4f clone() { + return new Color4f(r, g, b, a); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cone.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cone.java new file mode 100644 index 0000000..e9cb95a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cone.java @@ -0,0 +1,129 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Cone extends Primitive { + public static final int BODY = 0; + public static final int CAP = 1; + + static final int MID_REZ_DIV_X = 16; + static final int MID_REZ_DIV_Y = 1; + + float radius; + float height; + int xdivisions; + int ydivisions; + + + private Shape3D bodyShape = null; + private Shape3D capShape = null; + + public Cone() { + this(0.5f, 1.0f, null); + } + + public Cone(float r, float h, Appearance ap) { + xdivisions = MID_REZ_DIV_X; + ydivisions = MID_REZ_DIV_Y; + + float coordinates[][] = new float[xdivisions][3]; + + for (int i = 0; i < xdivisions; i++) { // BOTTOM(底面)の点の(x,y,z)座標 + coordinates[i][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i][1] = -h / 2; + coordinates[i][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + + float coordinates_center[][] = new float[2][3];//頂点と,底面の中心,の座標 + coordinates_center[0][0] = 0; + coordinates_center[0][1] = h / 2; + coordinates_center[0][2] = 0; + coordinates_center[1][0] = 0; + coordinates_center[1][1] = -h / 2; + coordinates_center[1][2] = 0; + + float uv[] = { 0, 0, 1, 0, 1, 1, 0, 1, }; + + // 各面のジオメトリを作成 + TriangleFanArray bodyGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + TriangleFanArray capGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + + // 頂点座標の設定 + //側面 + bodyGeom.setCoordinate(0,coordinates_center[0]); + for (int i = 0; i < xdivisions; i++){ + bodyGeom.setCoordinate(i + 1, coordinates[i]); //上から見て反時計回りの順 + } + bodyGeom.setCoordinate(xdivisions + 1, coordinates[0]); + //底面 + capGeom.setCoordinate(0,coordinates_center[1]); + capGeom.setCoordinate(1, coordinates[0]); + for (int i = 0; i < xdivisions; i++){ + capGeom.setCoordinate(i + 2, coordinates[xdivisions - i - 1]); //上から見て時計回りの順 + } + + // テクスチャ座標の設定 + bodyGeom.setTextureCoordinates(0, uv); // + capGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + double theta; + theta = Math.atan(h / r); + for (int i = 0; i < xdivisions; i++) { + float[] bodyNorm = new float[]{ + (float)(r * Math.sin(theta) * Math.cos(90 - theta) * Math.cos(2 * Math.PI * ((double)i / xdivisions))), + (float)(r * Math.sin(theta) * Math.sin(90 - theta)), + (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)) + }; + bodyGeom.setNormal(i, bodyNorm); + } + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + capGeom.setNormal(i, bottomNorm); + } + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); + + // 各面の作成 //全て同じテクスチャー(ap)を貼り付ける + bodyShape = new Shape3D(bodyGeom, ap); + capShape = new Shape3D(capGeom, ap); + + radius = r; + height = h; + } + + public double getRadius() { + return radius; + } + public double getHeight() { + return height; + } + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case BODY: + return bodyShape; + case CAP: + return capShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Cone c = new Cone(radius, height, ap); + return c; + } +} + diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cylinder.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cylinder.java new file mode 100644 index 0000000..d193242 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Cylinder.java @@ -0,0 +1,203 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Cylinder extends Primitive { + public static final int BODY = 0; + public static final int TOP = 1; + public static final int BOTTOM = 2; + + static final int MID_REZ_DIV_X = 16; + static final int MID_REZ_DIV_Y = 1; + + float radius; + float height; + int xdivisions; + int ydivisions; + + private Shape3D bodyShape = null; + private Shape3D topShape = null; + private Shape3D bottomShape = null; + + public Cylinder() { + this(0.5f, 1.0f, null); + } + + public Cylinder(float r, float h) { + this(r, h, null); + } + + public Cylinder(float r, float h, Appearance ap) { + xdivisions = MID_REZ_DIV_X; + ydivisions = MID_REZ_DIV_Y; + + float coordinates[][] = new float[xdivisions * 2][3]; + + float coordinates_center[][] = new float[2][3];//上面と底面の中心の座標 + coordinates_center[0][0] = 0; + coordinates_center[0][1] = h / 2; + coordinates_center[0][2] = 0; + coordinates_center[1][0] = 0; + coordinates_center[1][1] = -h / 2; + coordinates_center[1][2] = 0; + + for (int i = 0; i < xdivisions; i++) { // TOP(上面)側の点の(x,y,z)座標 + coordinates[i][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i][1] = h / 2; + coordinates[i][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + for (int i = 0; i < xdivisions; i++) { // BOTTOM(底面)の点の(x,y,z)座標 + coordinates[i + xdivisions][0] = r * (float) Math.cos(2 * Math.PI * ((double)i / xdivisions)); + coordinates[i + xdivisions][1] = -h / 2; + coordinates[i + xdivisions][2] = r * (float) Math.sin(2 * Math.PI * ((double)i / xdivisions)); + } + + float uv[] = { 0, 0, 1, 0, 1, 1, 0, 1, }; + + // 各面のジオメトリを作成 + IndexedTriangleStripArray bodyGeom = new IndexedTriangleStripArray(xdivisions * 2, + IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.NORMALS | IndexedTriangleStripArray.TEXTURE_COORDINATE_2, + xdivisions * 2 + 2, + new int[] { xdivisions * 2 + 2 }); + TriangleFanArray topGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + TriangleFanArray bottomGeom = new TriangleFanArray(xdivisions + 2, + TriangleFanArray.COORDINATES | TriangleFanArray.NORMALS | TriangleFanArray.TEXTURE_COORDINATE_2, + new int[] { xdivisions + 2 }); + + // 頂点座標の設定 + //側面 + for (int i = 0; i < xdivisions; i++) { //上面から底面へ垂直に降りて、底面から上面へ斜めに上る順に座標を設定 + bodyGeom.setCoordinate(2 * i , coordinates[i]); + bodyGeom.setCoordinate(2 * i + 1, coordinates[i + xdivisions]); + } + + int coordinateIndices[] = new int[xdivisions * 2 + 2]; + for (int i = 0; i < xdivisions * 2; i++) { + coordinateIndices[i] = i; + } + coordinateIndices[xdivisions * 2] = 0; + coordinateIndices[xdivisions * 2 + 1] = 1; + + bodyGeom.setCoordinateIndices(0, coordinateIndices); + + //上面 + topGeom.setCoordinate(0,coordinates_center[0]); + for(int i = 0; i < xdivisions; i++){ + topGeom.setCoordinate(i + 1, coordinates[i]); //上から見て反時計回りの順 + } + topGeom.setCoordinate(xdivisions + 1, coordinates[0]); + + //底面 + bottomGeom.setCoordinate(0,coordinates_center[1]); + bottomGeom.setCoordinate(1, coordinates[xdivisions]); + for(int i = 0; i < xdivisions; i++){ + bottomGeom.setCoordinate(i + 2, coordinates[2 * xdivisions - i - 1]); //上から見て時計回りの順 + } + + // テクスチャ座標の設定 + bodyGeom.setTextureCoordinates(0, uv); // + topGeom.setTextureCoordinates(0, uv); + bottomGeom.setTextureCoordinates(0, uv); + + // 法線の設定 + + for (int i = 0; i < xdivisions * 2; i++) { + float[] bodyNorm = new float[]{(float) Math.cos(Math.PI * ((double)i / xdivisions)),0.0f,(float) Math.sin(Math.PI * ((double)i / xdivisions))}; + bodyGeom.setNormal(i, bodyNorm); + } + + float[] topNorm = new float[]{0.0f, 1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + topGeom.setNormal(i, topNorm); + } + + float[] bottomNorm = new float[]{0.0f, -1.0f, 0.0f}; + for (int i = 0; i < xdivisions + 2; i++) { + bottomGeom.setNormal(i, bottomNorm); + } + + // 表面属性の作成 + if (ap == null) { + ap = new Appearance(); + } + setAppearance(ap); +// Appearance ap1 = new Appearance(); +// Appearance ap2 = new Appearance(); +// Appearance ap3 = new Appearance(); + // Appearance ap4 = new Appearance(); + // Appearance ap5 = new Appearance(); + // Appearance ap6 = new Appearance(); +// Texture tex = ap.getTexture(); +// if (tex != null && tex instanceof TextureCubeMap) { + // GL10 では GL_TEXTURE_CUBE_MAP が使えないので、TextureCubeMap の場合は Texture2D + // に分解する +// ImageComponent ic1 = tex.getImage(TextureCubeMap.POSITIVE_Z); +// Texture2D tex1 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic1.width, ic1.height); +// tex1.setImage(0, ic1); +// ap1.setTexture(tex1); +// ImageComponent ic2 = tex.getImage(TextureCubeMap.NEGATIVE_Z); +// Texture2D tex2 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic2.width, ic2.height); +// tex2.setImage(0, ic2); +// ap2.setTexture(tex2); +// ImageComponent ic3 = tex.getImage(TextureCubeMap.POSITIVE_X); +// Texture2D tex3 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, +// ic3.width, ic3.height); +// tex3.setImage(0, ic3); + // ap3.setTexture(tex3); + // ImageComponent ic4 = tex.getImage(TextureCubeMap.NEGATIVE_X); + // Texture2D tex4 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic4.width, ic4.height); + // tex4.setImage(0, ic4); + // ap4.setTexture(tex4); + // ImageComponent ic5 = tex.getImage(TextureCubeMap.POSITIVE_Y); + // Texture2D tex5 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic5.width, ic5.height); + // tex5.setImage(0, ic5); + // ap5.setTexture(tex5); + // ImageComponent ic6 = tex.getImage(TextureCubeMap.NEGATIVE_Y); + // Texture2D tex6 = new Texture2D(Texture2D.BASE_LEVEL, Texture2D.RGB, + // ic6.width, ic6.height); + // tex6.setImage(0, ic6); + // ap6.setTexture(tex6); +// } + // 各面の作成 //全て同じテクスチャー(ap)を貼り付ける + bodyShape = new Shape3D(bodyGeom, ap); + topShape = new Shape3D(topGeom, ap); + bottomShape = new Shape3D(bottomGeom, ap); + // + radius = r; + height = h; + } + + public double getRadius() { + return radius; + } + public double getHeight() { + return height; + } + + + @Override + public Shape3D getShape(int partid) { + switch (partid) { + case BODY: + return bodyShape; + case TOP: + return topShape; + case BOTTOM: + return bottomShape; + } + return null; + } + + public Node cloneTree() { + Appearance ap = getAppearance(); + if (ap != null) { + ap = (Appearance)ap.cloneNodeComponent(); + } + Cylinder c = new Cylinder(radius, height, ap); + return c; + } +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/DirectionalLight.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/DirectionalLight.java new file mode 100644 index 0000000..2c44315 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/DirectionalLight.java @@ -0,0 +1,26 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class DirectionalLight extends Light { + private Vector3f direction; + + @Override + public Node cloneTree() { + return new DirectionalLight(color, direction); + } + + public DirectionalLight(Color3f c, Vector3f d){ + super(c); + d.normalize(); + this.direction = d; + } + + public void setDirection(Vector3f d){ + d.normalize(); + this.direction = d; + } + + public Vector3f getDirection(){ + return direction; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Geometry.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Geometry.java new file mode 100644 index 0000000..e5b5fe0 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Geometry.java @@ -0,0 +1,10 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Geometry extends NodeComponent { + public static final int ALLOW_INTERSECT = 18; + + // コンストラクタ + public Geometry() { + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryArray.java new file mode 100644 index 0000000..83bb97c --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryArray.java @@ -0,0 +1,234 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +public abstract class GeometryArray extends Geometry { +// public static final int ALLOW_COLOR_READ = 2; +// public static final int ALLOW_COLOR_WRITE = 3; +// public static final int ALLOW_COORDINATE_READ = 0; +// public static final int ALLOW_COORDINATE_WRITE = 1; +// public static final int ALLOW_COUNT_READ = 8; +// public static final int ALLOW_COUNT_WRITE = 20; +// public static final int ALLOW_FORMAT_READ = 17; +// public static final int ALLOW_NORMAL_READ = 4; +// public static final int ALLOW_NORMAL_WRITE = 5; +// public static final int ALLOW_REF_DATA_READ = 21; +// public static final int ALLOW_REF_DATA_WRITE = 19; +// public static final int ALLOW_TEXCOORD_READ = 6; +// public static final int ALLOW_TEXCOORD_WRITE = 7; + + public static final int BY_REFERENCE = 128; + public static final int COLOR_3 = 4; + public static final int COLOR_4 = 12; + public static final int COORDINATES = 1; + public static final int INTERLEAVED = 256; + public static final int NORMALS = 2; + public static final int TEXTURE_COORDINATE_2 = 32; + public static final int TEXTURE_COORDINATE_3 = 64; + public static final int TEXTURE_COORDINATE_4 = 1024; + public static final int USE_COORD_INDEX_ONLY = 512; + public static final int USE_NIO_BUFFER = 2048; + + protected int vertexCount; + protected int vertexFormat; + protected FloatBuffer vertexBuffer = null; + protected FloatBuffer normalBuffer = null; + protected FloatBuffer uvBuffer = null; + + // コンストラクタ + public GeometryArray(int vertexCount, int vertexFormat) { + this.vertexCount = vertexCount; + this.vertexFormat = vertexFormat; + ByteBuffer vbb = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb.order(ByteOrder.nativeOrder()); + vertexBuffer = vbb.asFloatBuffer(); + if ((vertexFormat & NORMALS) != 0) { + ByteBuffer vbb2 = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb2.order(ByteOrder.nativeOrder()); + normalBuffer = vbb2.asFloatBuffer(); + } + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 2); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 3); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(vertexCount * 4 * 4); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + } +// mVertexBuffer.put(vertices); +// mVertexBuffer.position(0); + } + +// public GeometryArray(int vertexCount, int vertexFormat, int texCoordSetCount, int[] texCoordSetMap) { +// this.vertexCount = vertexCount; +// this.vertexFormat = vertexFormat; +// this.texCoordSetCount = texCoordSetCount; +// this.texCoordSetMap = texCoordSetMap; +// } + + /** オブジェクトの頂点数 */ + public int getVertexCount() { + return vertexCount; + } + + public int getVertexFormat() { + return vertexFormat; + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.get(p); + vertexBuffer.position(0); + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, double[] p) { + p[0] = vertexBuffer.get(index * 3); + p[1] = vertexBuffer.get(index * 3 + 1); + p[2] = vertexBuffer.get(index * 3 + 2); + } + + /** indexの頂点の座標をpに新しく格納する */ + public void getCoordinate(int index, Point3d p) { + p.x = vertexBuffer.get(index * 3); + p.y = vertexBuffer.get(index * 3 + 1); + p.z = vertexBuffer.get(index * 3 + 2); + } + + public void getCoordinates(int index, double[] coordinates) { + for (int n = 0; n < coordinates.length / 3 && n + index < vertexBuffer.capacity() / 3; n++) { + coordinates[n * 3] = vertexBuffer.get((index + n) * 3); + coordinates[n * 3 + 1] = vertexBuffer.get((index + n) * 3 + 1); + coordinates[n * 3 + 2] = vertexBuffer.get((index + n) * 3 + 2); + } + } + + /** indexの頂点の法線をpに新しく格納する */ + public void getNormal(int index, float[] p) { + if (normalBuffer == null) return; + normalBuffer .position(index * 3); + normalBuffer.get(p); + normalBuffer .position(0); + } + + /** indexの頂点の法線をpに新しく格納する */ + public void getNormal(int index, Vector3f p) { + if (normalBuffer == null) return; + p.x = normalBuffer.get(index * 3); + p.y = normalBuffer.get(index * 3 + 1); + p.z = normalBuffer.get(index * 3 + 2); + } + + public void setCoordinate(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put(p); + vertexBuffer.position(0); + } + + public void setCoordinate(int index, double[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put((float)p[0]); + vertexBuffer.put((float)p[1]); + vertexBuffer.put((float)p[2]); + vertexBuffer.position(0); + } + + public void setCoordinate(int index, Point3d p) { + vertexBuffer.position(index * 3); + vertexBuffer.put((float)p.x); + vertexBuffer.put((float)p.y); + vertexBuffer.put((float)p.z); + vertexBuffer.position(0); + } + + public void setCoordinates(int index, float[] p) { + vertexBuffer.position(index * 3); + vertexBuffer.put(p); + vertexBuffer.position(0); + } + + public void setCoordinates(int index, double[] p) { + vertexBuffer.position(index * 3); + for (int i = 0; i < p.length; i++) { + vertexBuffer.put((float)p[i]); + } + vertexBuffer.position(0); + } + + public void setNormal(int index, float[] n) { + if (normalBuffer == null) return; + normalBuffer .position(index * 3); + normalBuffer.put(n); + normalBuffer.position(0); + } + + public void setNormal(int index, Vector3f n) { + if (normalBuffer == null) return; + normalBuffer.position(index * 3); + normalBuffer.put(n.x); + normalBuffer.put(n.y); + normalBuffer.put(n.z); + normalBuffer.position(0); + } + + public void setTextureCoordinate(int index, float[] texCoord) { + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + uvBuffer.position(index * 2); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + uvBuffer.position(index * 3); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + uvBuffer.put(texCoord[2]); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + uvBuffer.position(index * 4); + uvBuffer.put(texCoord[0]); + uvBuffer.put(texCoord[1]); + uvBuffer.put(texCoord[2]); + uvBuffer.put(texCoord[3]); + } + uvBuffer.position(0); + } + + public void setTextureCoordinates(int index, float[] texCoords) { + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 2); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 2); + } else if ((vertexFormat & TEXTURE_COORDINATE_3) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 3); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 3); + } else if ((vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect((texCoords.length + index) * 4 * 4); + vbb3.order(ByteOrder.nativeOrder()); + uvBuffer = vbb3.asFloatBuffer(); + uvBuffer.position(index * 4); + } + uvBuffer.put(texCoords); + uvBuffer.position(0); + } + + public FloatBuffer getVertexBuffer() { + return vertexBuffer; + } + + public FloatBuffer getNormalBuffer() { + return normalBuffer; + } + + public FloatBuffer getUVBuffer() { + return uvBuffer; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryStripArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryStripArray.java new file mode 100644 index 0000000..29f2e53 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GeometryStripArray.java @@ -0,0 +1,22 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class GeometryStripArray extends GeometryArray { + protected int[] stripIndexCounts; + + public GeometryStripArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat); + this.stripIndexCounts = stripIndexCounts; + } + + public int getNumStrips() { + return stripIndexCounts.length; + } + + public void setStripIndexCounts(int[] stripIndexCounts) { + this.stripIndexCounts = stripIndexCounts; + } + + public void getStripVertexCounts(int[] stripIndexCounts) { + System.arraycopy(this.stripIndexCounts, 0, stripIndexCounts, 0, stripIndexCounts.length); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/GraphicsContext3D.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GraphicsContext3D.java new file mode 100644 index 0000000..cb0fe2e --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/GraphicsContext3D.java @@ -0,0 +1,378 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import android.opengl.GLU; +import android.opengl.GLUtils; + +import org.ntlab.radishforandroidstudio.framework.model3D.Position3D; + +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.HashMap; + +import javax.microedition.khronos.opengles.GL10; + +public class GraphicsContext3D { + private GL10 gl; + private Appearance appearance = null; + private boolean bFixAspect = false; + private float aspect; + private HashMap textureRegistry = new HashMap(); + private HashMap lightRegistry = new HashMap(); + + public GraphicsContext3D(GL10 gl) { + init(gl); + } + + public GraphicsContext3D setGL10(GL10 gl) { + if (this.gl != gl) { + init(gl); + } + return this; + } + + public void fixAspect(float aspect) { + this.bFixAspect = true; + this.aspect = aspect; + } + + public void init(GL10 gl) { + this.gl = gl; + // デプスバッファのテスト機能を有効にする + gl.glEnable(GL10.GL_DEPTH_TEST); + // 陰面消去の動作を設定 + gl.glDepthFunc(GL10.GL_LEQUAL); + gl.glDepthMask(true); + + // ライトを有効にする + gl.glEnable(GL10.GL_LIGHTING); + // どの光源を使用するか指定 + gl.glEnable(GL10.GL_LIGHT0); + + gl.glClearColor(0.0f,0.0f,0.0f,0.0f); + gl.glClearDepthf(1.0f); + } + + public void update(int width, int height, float fovx, float zNear, float zFar,boolean fParallel) { + setGL10(gl); + if (!bFixAspect) { + aspect = (float)width / (float)height; + } + // ビューポートの設定 + gl.glViewport(0, 0, width, height); + + // カメラの設定 + gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換 + gl.glLoadIdentity(); // 座標の初期化 + // 画角の設定 + float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f); + if(!fParallel){ + GLU.gluPerspective(gl, + fovy, //Y方向の画角 + aspect, //アスペクト比 + zNear, //ニアクリップ + zFar);//ファークリップ + }else{ + float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + gl.glOrthof(left, right, bottom, top, zNear, zFar); + } + } + + public void update(float fovx, float zNear, float zFar, Position3D eye, Position3D center, Vector3d up, boolean fParallel) { + // 表示画面とデプスバッファのクリア + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + + // カメラの設定 + gl.glMatrixMode(GL10.GL_PROJECTION); // 射影変換 + gl.glLoadIdentity(); // 座標の初期化 + // 画角の設定 + float fovy = (float)(Math.atan(Math.tan(fovx / 2.0) / aspect) / Math.PI * 360.0f); + if (!fParallel) { + GLU.gluPerspective(gl, + fovy, //Y方向の画角 + aspect, //アスペクト比 + zNear, //ニアクリップ + zFar);//ファークリップ + } else { + float top = zNear * (float) Math.tan(fovy * (Math.PI / 360.0)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + gl.glOrthof(left, right, bottom, top, zNear, zFar); + } + // モデルビュー行列の指定 + gl.glMatrixMode(GL10.GL_MODELVIEW); + // 座標の初期化 + gl.glLoadIdentity(); + + // カメラ外部パラメータの設定 + GLU.gluLookAt(gl, + (float)eye.getX(), (float)eye.getY(), (float)eye.getZ(), + (float)center.getX(), (float)center.getY(), (float)center.getZ(), + (float)up.getX(), (float)up.getY(), (float)up.getZ()); + } + + public void setLight(Light l, int i) { + lightRegistry.put(l, i); + Color3f c = l.getColor(); + float color[] = {c.r, c.g, c.b, 1.0f}; + gl.glEnable(GL10.GL_LIGHT0 + i); + if (l instanceof AmbientLight) { + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, color, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + } else { + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_AMBIENT, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_DIFFUSE, color, 0); + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_SPECULAR, new float[]{1.0f, 1.0f, 1.0f, 1.0f}, 0); + } + } + + public void updateLightState(Light l) { + Integer i = lightRegistry.get(l); + if (i == null) { + // Object3D内部に配置されている光源に対しては、初期化時にsetLight()が呼ばれないため + i = lightRegistry.size(); + setLight(l, i); + } + if (l instanceof DirectionalLight) { + Vector3f v = ((DirectionalLight)l).getDirection(); + float direction[] = {-v.x, -v.y, -v.z, 0.0f}; + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, direction, 0); + } else if (l instanceof PointLight) { + Point3f p = ((PointLight)l).getPosition(); + float position[] = {p.x, p.y, p.z, 1.0f}; + gl.glLightfv(GL10.GL_LIGHT0 + i, GL10.GL_POSITION, position, 0); + } + } + + public void pushMatrix() { + gl.glPushMatrix(); + } + + public void popMatrix() { + gl.glPopMatrix(); + } + + public void multMatrix(Transform3D transform) { + float m[] = transform.getMatrix(); + gl.glMultMatrixf(m, 0); + } + + public void setAppearance(Appearance appearance) { + this.appearance = appearance; + Material material = appearance.getMaterial(); + if (material != null) { + // 表面属性の設定 + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_DIFFUSE, material.diffuse, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_AMBIENT, material.ambient, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_SPECULAR, material.specular, 0); + gl.glMaterialfv(GL10.GL_FRONT, GL10.GL_EMISSION, material.emissive, 0); + gl.glMaterialf(GL10.GL_FRONT, GL10.GL_SHININESS, material.shininess); + } + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + Texture tex = appearance.getTexture(); + if (tex != null) registerTexture(tex); + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + TextureUnitState tus = appearance.getTextureUnitState(n); + Texture tex = tus.getTexture(); + if (tex != null) { + gl.glActiveTexture(GL10.GL_TEXTURE1 + n); + registerTexture(tex); + } + } + } + } + + private void registerTexture(Texture tex) { + if (textureRegistry.get(tex) == null) { + // テクスチャの登録 + int[] textureId = new int[1]; + gl.glGenTextures(1, textureId, 0); + if (tex instanceof TextureCubeMap) { + // 立方体マップの場合(GL10では対応していない) + } else { + // 通常の場合 + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId[0]); + ImageComponent[] imageComponents = tex.getImages(); + for (int level = 0; level < imageComponents.length; level++) { + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, ((ImageComponent2D)imageComponents[level]).getBitmap(), 0); +// ((ImageComponent2D)imageComponents[i]).getBitmap().recycle(); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); + gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); + } + } + textureRegistry.put(tex, textureId[0]); + } + } + + public void draw(Shape3D node) { + if (node == null) return; + setAppearance(node.getAppearance()); + draw(node.getGeometry()); + } + + public void draw(Geometry g) { + if (g == null) return; + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + Texture tex = appearance.getTexture(); + if (tex != null) { + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提 + TextureAttributes ta = appearance.getTextureAttributes(); + if (ta != null) setTextureAttributes(ta); + } + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + TextureUnitState tus = appearance.getTextureUnitState(n); + Texture tex = tus.getTexture(); + if (tex != null) { + gl.glActiveTexture(GL10.GL_TEXTURE0 + n); + gl.glEnable(GL10.GL_TEXTURE_2D); + gl.glBindTexture(GL10.GL_TEXTURE_2D, textureRegistry.get(tex)); // テクスチャが登録されていることを前提 + TextureAttributes ta = tus.getTextureAttributes(); + if (ta != null) setTextureAttributes(ta); + } + } + } + if (g instanceof GeometryArray) { + // バッファの設定 + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + FloatBuffer vertexBuffer = ((GeometryArray)g).getVertexBuffer(); + gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) { + gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); + FloatBuffer normalBuffer = ((GeometryArray)g).getNormalBuffer(); + gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer); + } + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, uvBuffer); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, uvBuffer); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + FloatBuffer uvBuffer = ((GeometryArray)g).getUVBuffer(); + gl.glTexCoordPointer(4, GL10.GL_FLOAT, 0, uvBuffer); + } + + // ジオメトリの描画 + if (g instanceof TriangleArray) { + TriangleArray ta = (TriangleArray)g; + int vertexCount = ta.getVertexCount(); + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexCount); + } else if (g instanceof TriangleFanArray) { + TriangleFanArray ta = (TriangleFanArray)g; + int start = 0; + int[] stripVertexCounts = new int[ta.getNumStrips()]; + ta.getStripVertexCounts(stripVertexCounts); + for (int n = 0; n < ta.getNumStrips(); n++) { + int vertexCount = stripVertexCounts[n]; + gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, start, vertexCount); + start += vertexCount; + } + } else if (g instanceof TriangleStripArray) { + TriangleStripArray ta = (TriangleStripArray)g; + int start = 0; + int[] stripVertexCounts = new int[ta.getNumStrips()]; + ta.getStripVertexCounts(stripVertexCounts); + for (int n = 0; n < ta.getNumStrips(); n++) { + int vertexCount = stripVertexCounts[n]; + gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, start, vertexCount); + start += vertexCount; + } + } else if (g instanceof IndexedTriangleArray) { + IndexedTriangleArray ita = (IndexedTriangleArray)g; + int vertexCount = ((IndexedTriangleArray)g).getIndexCount(); + gl.glDrawElements(GL10.GL_TRIANGLES, vertexCount, GL10.GL_UNSIGNED_SHORT, ita.getCoordinateIndexBuffer()); + } else if (g instanceof IndexedTriangleFanArray) { + IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g; + ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer(); + int start = 0; + int[] stripIndexCounts = new int[igsa.getNumStrips()]; + igsa.getStripIndexCounts(stripIndexCounts); + for (int n = 0; n < igsa.getNumStrips(); n++) { + int vertexCount = stripIndexCounts[n]; + indexBuffer.position(start); + gl.glDrawElements(GL10.GL_TRIANGLE_FAN, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer); + start += vertexCount; + } + } else if (g instanceof IndexedTriangleStripArray) { + IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)g; + ShortBuffer indexBuffer = igsa.getCoordinateIndexBuffer(); + int start = 0; + int[] stripIndexCounts = new int[igsa.getNumStrips()]; + igsa.getStripIndexCounts(stripIndexCounts); + for (int n = 0; n < igsa.getNumStrips(); n++) { + int vertexCount = stripIndexCounts[n]; + indexBuffer.position(start); + gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, vertexCount, GL10.GL_UNSIGNED_SHORT, indexBuffer); + start += vertexCount; + } + } + + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_2) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_3) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } else if ((((GeometryArray) g).getVertexFormat() & GeometryArray.TEXTURE_COORDINATE_4) != 0) { + gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + } + if ((((GeometryArray) g).getVertexFormat() & GeometryArray.NORMALS) != 0) { + gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); + } + gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); + } + if (appearance.getTextureUnitCount() == 0) { + // テクスチャユニットを使っていない場合(通常のテクスチャの場合) + if (appearance.getTexture() != null) { + gl.glDisable(GL10.GL_TEXTURE_2D); + } + } else { + // テクスチャユニットを使っている場合 + for (int n = 0; n < appearance.getTextureUnitCount(); n++) { + gl.glActiveTexture(GL10.GL_TEXTURE0 + n); + gl.glDisable(GL10.GL_TEXTURE_2D); + } + gl.glActiveTexture(GL10.GL_TEXTURE0); + } + } + + private void setTextureAttributes(TextureAttributes ta) { + int textureMode = ta.getTextureMode(); + switch (textureMode) { + case TextureAttributes.REPLACE: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); + break; + case TextureAttributes.BLEND: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_BLEND); + break; +// case TextureAttributes.COMBINE: +// gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_COMBINE); +// break; + case TextureAttributes.MODULATE: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); + break; + case TextureAttributes.DECAL: + gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL); + break; + } +// int perspCorrectionMode = ta.getPerspectiveCorrectionMode(); +// switch (perspCorrectionMode) { +// case TextureAttributes.NICEST: +// break; +// case TextureAttributes.FASTEST: +// break; +// } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Group.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Group.java new file mode 100644 index 0000000..1e1f2a6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Group.java @@ -0,0 +1,40 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +public class Group extends Node { + + private List children = new ArrayList(); + + public Node getChild(int num) { + return children.get(num); + } + + public int numChildren() { + return children.size(); + } + + public void addChild(Node node) { + children.add(node); + } + + public void removeChild(Node node) { + children.remove(node); + } + + public Enumeration getAllChildren() { + return Collections.enumeration(children); + } + + public Node cloneTree() { + Group newInstance = new Group(); + newInstance.children = new ArrayList(); + for (int n = 0; n < children.size(); n++) { + newInstance.children.add(children.get(n).cloneTree()); + } + return newInstance; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent.java new file mode 100644 index 0000000..77f7c5f --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent.java @@ -0,0 +1,16 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class ImageComponent extends NodeComponent { + public static final int FORMAT_RGB = 1; + public static final int FORMAT_RGBA = 2; + + protected int format = 0; + protected int width = 0; + protected int height = 0; + + public ImageComponent(int format, int width, int height) { + this.format = format; + this.width = width; + this.height = height; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent2D.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent2D.java new file mode 100644 index 0000000..f760c4c --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/ImageComponent2D.java @@ -0,0 +1,23 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import android.graphics.Bitmap; + +public class ImageComponent2D extends ImageComponent { + private Bitmap image = null; + + public ImageComponent2D(int format, Bitmap image) { + super(format, image.getWidth(), image.getHeight()); + this.image = image; + } + + public Bitmap getBitmap() { + return image; + } + + @Override + public NodeComponent cloneNodeComponent() { + ImageComponent2D newOne = new ImageComponent2D(format, image); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryArray.java new file mode 100644 index 0000000..9864985 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryArray.java @@ -0,0 +1,89 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +public abstract class IndexedGeometryArray extends GeometryArray { + + protected int indexCount; + protected ShortBuffer coordinateIndexBuffer = null; + protected ShortBuffer texCoordinateIndexBuffer = null; + protected ShortBuffer normalIndexBuffer = null; + + // コンストラクタ + public IndexedGeometryArray(int vertexCount, int vertexFormat, int indexCount) { + super(vertexCount, vertexFormat); + this.indexCount = indexCount; + ByteBuffer vbb = ByteBuffer.allocateDirect(indexCount * 2); + vbb.order(ByteOrder.nativeOrder()); + coordinateIndexBuffer = vbb.asShortBuffer(); + if ((vertexFormat & TEXTURE_COORDINATE_2) != 0 + || (vertexFormat & TEXTURE_COORDINATE_3) != 0 + || (vertexFormat & TEXTURE_COORDINATE_4) != 0) { + ByteBuffer vbb2 = ByteBuffer.allocateDirect(indexCount * 2); + vbb2.order(ByteOrder.nativeOrder()); + texCoordinateIndexBuffer = vbb2.asShortBuffer(); + } + if ((vertexFormat & NORMALS) != 0) { + ByteBuffer vbb3 = ByteBuffer.allocateDirect(indexCount * 2); + vbb3.order(ByteOrder.nativeOrder()); + normalIndexBuffer = vbb3.asShortBuffer(); + } + } + + /** オブジェクトのインデックス数を取得する */ + public int getIndexCount() { + return indexCount; + } + + public int getCoordinateIndex(int index) { + return coordinateIndexBuffer.get(index); + } + + public void getCoordinateIndices(int index, int[] coordinateIndicies) { + for (int i = 0; i < coordinateIndicies.length && index + i < coordinateIndexBuffer.limit(); i++) { + coordinateIndicies[i] = coordinateIndexBuffer.get(index + i); + } + } + + public void setCoordinateIndex(int index, int coordinateIndex) { + coordinateIndexBuffer.put(index, (short)coordinateIndex); + } + + public void setCoordinateIndices(int index, int[] coordinateIndicies) { + coordinateIndexBuffer.position(index); + for (int i = 0; i < coordinateIndicies.length; i++) { + coordinateIndexBuffer.put((short)coordinateIndicies[i]); + } + coordinateIndexBuffer.position(0); + } + + public int getTextureCoordinateIndex(int index) { + return texCoordinateIndexBuffer.get(index); + } + + public void setTextureCoordinateIndex(int index, int texCoordIndex) { + coordinateIndexBuffer.put(index, (short)texCoordIndex); + } + + public int getNormalIndex(int index) { + return normalIndexBuffer.get(index); + } + + public void setNormalIndex(int index, int normalIndex) { + normalIndexBuffer.put(index, (short)normalIndex); + } + + public ShortBuffer getCoordinateIndexBuffer() { + return coordinateIndexBuffer; + } + + public ShortBuffer getTextureCoordinateIndexBuffer() { + return texCoordinateIndexBuffer; + } + + public ShortBuffer getNormalIndexBuffer() { + return normalIndexBuffer; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryStripArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryStripArray.java new file mode 100644 index 0000000..a7f2ef0 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedGeometryStripArray.java @@ -0,0 +1,22 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class IndexedGeometryStripArray extends IndexedGeometryArray { + protected int[] stripIndexCounts; + + public IndexedGeometryStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount); + this.stripIndexCounts = stripIndexCounts; + } + + public int getNumStrips() { + return stripIndexCounts.length; + } + + public void setStripIndexCounts(int[] stripIndexCounts) { + this.stripIndexCounts = stripIndexCounts; + } + + public void getStripIndexCounts(int[] stripIndexCounts) { + System.arraycopy(this.stripIndexCounts, 0, stripIndexCounts, 0, stripIndexCounts.length); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleArray.java new file mode 100644 index 0000000..108546b --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleArray.java @@ -0,0 +1,21 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class IndexedTriangleArray extends IndexedGeometryArray { + public IndexedTriangleArray(int vertexCount, int vertexFormat, int indexCount) { + super(vertexCount, vertexFormat, indexCount); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleArray newOne = new IndexedTriangleArray(vertexCount, vertexFormat, indexCount); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleFanArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleFanArray.java new file mode 100644 index 0000000..0c09b4e --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleFanArray.java @@ -0,0 +1,21 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class IndexedTriangleFanArray extends IndexedGeometryStripArray { + public IndexedTriangleFanArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedGeometryArray newOne = new IndexedTriangleFanArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleStripArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleStripArray.java new file mode 100644 index 0000000..f8e76a6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/IndexedTriangleStripArray.java @@ -0,0 +1,20 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class IndexedTriangleStripArray extends IndexedGeometryStripArray { + public IndexedTriangleStripArray(int vertexCount, int vertexFormat, int indexCount, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, indexCount, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + IndexedTriangleStripArray newOne = new IndexedTriangleStripArray(vertexCount, vertexFormat, indexCount, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + newOne.indexCount = indexCount; + newOne.coordinateIndexBuffer = coordinateIndexBuffer.duplicate(); + if (texCoordinateIndexBuffer != null) newOne.texCoordinateIndexBuffer = texCoordinateIndexBuffer.duplicate(); + if (normalIndexBuffer != null) newOne.normalIndexBuffer = normalIndexBuffer.duplicate(); + return newOne; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Leaf.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Leaf.java new file mode 100644 index 0000000..1a3e5fe --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Leaf.java @@ -0,0 +1,5 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Leaf extends Node { + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Light.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Light.java new file mode 100644 index 0000000..b2a7f69 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Light.java @@ -0,0 +1,17 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Light extends Leaf { + protected Color3f color; + + public Light(Color3f c) { + this.color = c; + } + + public void setColor(Color3f c) { + color = c; + } + + public Color3f getColor() { + return color; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Material.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Material.java new file mode 100644 index 0000000..f8e9247 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Material.java @@ -0,0 +1,73 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Material extends NodeComponent { + float diffuseR = 1.0f; + float diffuseG = 1.0f; + float diffuseB = 1.0f; + float ambientR = 0.2f; + float ambientG = 0.2f; + float ambientB = 0.2f; + float specularR = 1.0f; + float specularG = 1.0f; + float specularB = 1.0f; + float emissiveR = 0.0f; + float emissiveG = 0.0f; + float emissiveB = 0.0f; + float shininess = 64.0f; + float[] diffuse = null; + float[] ambient = null; + float[] specular = null; + float[] emissive = null; + + public Material() { + setDiffuseColor(1.0f, 1.0f, 1.0f); + setAmbientColor(0.2f, 0.2f, 0.2f); + setSpecularColor(1.0f, 1.0f, 1.0f); + setEmissiveColor(0.0f, 0.0f, 0.0f); + setShininess(64.0f); + } + + public void setDiffuseColor(float r, float g, float b) { + this.diffuseR = r; + this.diffuseG = g; + this.diffuseB = b; + diffuse = new float[]{r, g ,b, 1.0f}; + } + + public void setAmbientColor(float r, float g, float b) { + this.ambientR = r; + this.ambientG = g; + this.ambientB = b; + ambient = new float[]{r, g ,b, 1.0f}; + } + + public void setSpecularColor(float r, float g, float b) { + this.specularR = r; + this.specularG = g; + this.specularB = b; + specular = new float[]{r, g ,b, 1.0f}; + } + + public void setEmissiveColor(float r, float g, float b) { + this.emissiveR = r; + this.emissiveG = g; + this.emissiveB = b; + emissive = new float[]{r, g ,b, 1.0f}; + } + + public void setShininess(float shininess) { + this.shininess = shininess; + } + + @Override + public NodeComponent cloneNodeComponent() { + Material m = new Material(); + m.setDiffuseColor(diffuseR, diffuseG, diffuseB); + m.setAmbientColor(ambientR, ambientG, ambientB); + m.setSpecularColor(specularR, specularG, specularB); + m.setEmissiveColor(emissiveR, emissiveG, emissiveB); + m.setShininess(shininess); + return m; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix3d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix3d.java new file mode 100644 index 0000000..e736dd1 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix3d.java @@ -0,0 +1,484 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Matrix3d { + public double m00; + public double m01; + public double m02; + public double m10; + public double m11; + public double m12; + public double m20; + public double m21; + public double m22; + + // コンストラクタ + public Matrix3d() { + m00 = 0.0; + m01 = 0.0; + m02 = 0.0; + m10 = 0.0; + m11 = 0.0; + m12 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 0.0; + } + + public Matrix3d(double[] v) { + m00 = v[0]; + m01 = v[1]; + m02 = v[2]; + m10 = v[3]; + m11 = v[4]; + m12 = v[5]; + m20 = v[6]; + m21 = v[7]; + m22 = v[8]; + } + + public Matrix3d(double m00, double m01, double m02, double m10, double m11, + double m12, double m20, double m21, double m22) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + } + + public Matrix3d(Matrix3d m1) { + this.m00 = m1.m00; + this.m01 = m1.m01; + this.m02 = m1.m02; + this.m10 = m1.m10; + this.m11 = m1.m11; + this.m12 = m1.m12; + this.m20 = m1.m20; + this.m21 = m1.m21; + this.m22 = m1.m22; + } + + public Matrix3d clone() { + return new Matrix3d(m00, m01, m02, m10, m11, m12, m20, m21, m22); + } + + /** 各要素に足す */ + public void add(Matrix3d m1) { + this.m00 += m1.m00; + this.m01 += m1.m01; + this.m02 += m1.m02; + this.m10 += m1.m10; + this.m11 += m1.m11; + this.m12 += m1.m12; + this.m20 += m1.m20; + this.m21 += m1.m21; + this.m22 += m1.m22; + } + + /** 二つの行列の和を格納する */ + public void add(Matrix3d m1, Matrix3d m2) { + this.m00 = m1.m00 + m2.m00; + this.m01 = m1.m01 + m2.m01; + this.m02 = m1.m02 + m2.m02; + this.m10 = m1.m10 + m2.m10; + this.m11 = m1.m11 + m2.m11; + this.m12 = m1.m12 + m2.m12; + this.m20 = m1.m20 + m2.m20; + this.m21 = m1.m21 + m2.m21; + this.m22 = m1.m22 + m2.m22; + } + + /** x軸周りにangle回転 */ + public void rotX(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = 1.0D; + m01 = 0.0D; + m02 = 0.0D; + m10 = 0.0D; + m11 = d2; + m12 = -d1; + m20 = 0.0D; + m21 = d1; + m22 = d2; + } + + /** y軸周りにangle回転 */ + public void rotY(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = d2; + m01 = 0.0D; + m02 = d1; + m10 = 0.0D; + m11 = 1.0D; + m12 = 0.0D; + m20 = -d1; + m21 = 0.0D; + m22 = d2; + } + + /** z軸周りにangle1回転 */ + public void rotZ(double angle) { + double d1 = Math.sin(angle); + double d2 = Math.cos(angle); + m00 = d2; + m01 = -d1; + m02 = 0.0D; + m10 = d1; + m11 = d2; + m12 = 0.0D; + m20 = 0.0D; + m21 = 0.0D; + m22 = 1.0D; + } + + /** 自分に引数の行列を掛けたものを格納する */ + public void mul(Matrix3d mat) { + double t00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20; + double t01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21; + double t02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22; + double t10 = m10 * mat.m10 + m11 * mat.m10 + m12 * mat.m20; + double t11 = m10 * mat.m11 + m11 * mat.m11 + m12 * mat.m21; + double t12 = m10 * mat.m12 + m11 * mat.m12 + m12 * mat.m22; + double t20 = m20 * mat.m20 + m21 * mat.m10 + m22 * mat.m20; + double t21 = m20 * mat.m21 + m21 * mat.m11 + m22 * mat.m21; + double t22 = m20 * mat.m22 + m21 * mat.m12 + m22 * mat.m22; + + set(t00, t01, t02, t10, t11, t12, t20, t21, t22); + } + + public void mul(Matrix3d mat1, Matrix3d mat2) { + this.set(mat1.m00, mat1.m01, mat1.m02, mat1.m10, mat1.m11, mat1.m12, mat1.m20, mat1.m21, mat1.m22); + mul(mat2); + } + + public void set(double t00, double t01, double t02, double t10, double t11, + double t12, double t20, double t21, double t22) { + m00 = t00; + m01 = t01; + m02 = t02; + m10 = t10; + m11 = t11; + m12 = t12; + m20 = t20; + m21 = t21; + m22 = t22; + } + + /** + * Sets the value of this matrix to the matrix inverse of the passed matrix + * m1. + * + * @param m1 + * the matrix to be inverted + */ + public final void invert(Matrix3d m1) { + invertGeneral(m1); + } + + /** + * Inverts this matrix in place. + */ + public final void invert() { + invertGeneral(this); + } + + /** + * General invert routine. Inverts m1 and places the result in "this". Note + * that this routine handles both the "this" version and the non-"this" + * version. + * + * Also note that since this routine is slow anyway, we won't worry about + * allocating a little bit of garbage. + */ + private final void invertGeneral(Matrix3d m1) { + double result[] = new double[9]; + int row_perm[] = new int[3]; + int i, r, c; + double[] tmp = new double[9]; // scratch matrix + + // Use LU decomposition and backsubstitution code specifically + // for floating-point 3x3 matrices. + + // Copy source matrix to t1tmp + tmp[0] = m1.m00; + tmp[1] = m1.m01; + tmp[2] = m1.m02; + + tmp[3] = m1.m10; + tmp[4] = m1.m11; + tmp[5] = m1.m12; + + tmp[6] = m1.m20; + tmp[7] = m1.m21; + tmp[8] = m1.m22; + + // Calculate LU decomposition: Is the matrix singular? + if (!luDecomposition(tmp, row_perm)) { + // Matrix has no inverse + // throw new + // SingularMatrixException(VecMathI18N.getString("Matrix3d12")); + } + + // Perform back substitution on the identity matrix + for (i = 0; i < 9; i++) + result[i] = 0.0; + result[0] = 1.0; + result[4] = 1.0; + result[8] = 1.0; + luBacksubstitution(tmp, row_perm, result); + + this.m00 = result[0]; + this.m01 = result[1]; + this.m02 = result[2]; + + this.m10 = result[3]; + this.m11 = result[4]; + this.m12 = result[5]; + + this.m20 = result[6]; + this.m21 = result[7]; + this.m22 = result[8]; + + } + + /** + * Given a 3x3 array "matrix0", this function replaces it with the LU + * decomposition of a row-wise permutation of itself. The input parameters + * are "matrix0" and "dimen". The array "matrix0" is also an output + * parameter. The vector "row_perm[3]" is an output parameter that contains + * the row permutations resulting from partial pivoting. The output + * parameter "even_row_xchg" is 1 when the number of row exchanges is even, + * or -1 otherwise. Assumes data type is always double. + * + * This function is similar to luDecomposition, except that it is tuned + * specifically for 3x3 matrices. + * + * @return true if the matrix is nonsingular, or false otherwise. + */ + // + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 40-45. + // + static boolean luDecomposition(double[] matrix0, int[] row_perm) { + + double row_scale[] = new double[3]; + + // Determine implicit scaling information by looping over rows + { + int i, j; + int ptr, rs; + double big, temp; + + ptr = 0; + rs = 0; + + // For each row ... + i = 3; + while (i-- != 0) { + big = 0.0; + + // For each column, find the largest element in the row + j = 3; + while (j-- != 0) { + temp = matrix0[ptr++]; + temp = Math.abs(temp); + if (temp > big) { + big = temp; + } + } + + // Is the matrix singular? + if (big == 0.0) { + return false; + } + row_scale[rs++] = 1.0 / big; + } + } + + { + int j; + int mtx; + + mtx = 0; + + // For all columns, execute Crout's method + for (j = 0; j < 3; j++) { + int i, imax, k; + int target, p1, p2; + double sum, big, temp; + + // Determine elements of upper diagonal matrix U + for (i = 0; i < j; i++) { + target = mtx + (3 * i) + j; + sum = matrix0[target]; + k = i; + p1 = mtx + (3 * i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 3; + } + matrix0[target] = sum; + } + + // Search for largest pivot element and calculate + // intermediate elements of lower diagonal matrix L. + big = 0.0; + imax = -1; + for (i = j; i < 3; i++) { + target = mtx + (3 * i) + j; + sum = matrix0[target]; + k = j; + p1 = mtx + (3 * i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 3; + } + matrix0[target] = sum; + + // Is this the best pivot so far? + if ((temp = row_scale[i] * Math.abs(sum)) >= big) { + big = temp; + imax = i; + } + } + + if (imax < 0) { + // throw new + // RuntimeException(VecMathI18N.getString("Matrix3d13")); + } + + // Is a row exchange necessary? + if (j != imax) { + // Yes: exchange rows + k = 3; + p1 = mtx + (3 * imax); + p2 = mtx + (3 * j); + while (k-- != 0) { + temp = matrix0[p1]; + matrix0[p1++] = matrix0[p2]; + matrix0[p2++] = temp; + } + + // Record change in scale factor + row_scale[imax] = row_scale[j]; + } + + // Record row permutation + row_perm[j] = imax; + + // Is the matrix singular + if (matrix0[(mtx + (3 * j) + j)] == 0.0) { + return false; + } + + // Divide elements of lower diagonal matrix L by pivot + if (j != (3 - 1)) { + temp = 1.0 / (matrix0[(mtx + (3 * j) + j)]); + target = mtx + (3 * (j + 1)) + j; + i = 2 - j; + while (i-- != 0) { + matrix0[target] *= temp; + target += 3; + } + } + } + } + + return true; + } + + /** + * Solves a set of linear equations. The input parameters "matrix1", and + * "row_perm" come from luDecompostionD3x3 and do not change here. The + * parameter "matrix2" is a set of column vectors assembled into a 3x3 + * matrix of floating-point values. The procedure takes each column of + * "matrix2" in turn and treats it as the right-hand side of the matrix + * equation Ax = LUx = b. The solution vector replaces the original column + * of the matrix. + * + * If "matrix2" is the identity matrix, the procedure replaces its contents + * with the inverse of the matrix from which "matrix1" was originally + * derived. + */ + // + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 44-45. + // + static void luBacksubstitution(double[] matrix1, int[] row_perm, + double[] matrix2) { + + int i, ii, ip, j, k; + int rp; + int cv, rv; + + // rp = row_perm; + rp = 0; + + // For each column vector of matrix2 ... + for (k = 0; k < 3; k++) { + // cv = &(matrix2[0][k]); + cv = k; + ii = -1; + + // Forward substitution + for (i = 0; i < 3; i++) { + double sum; + + ip = row_perm[rp + i]; + sum = matrix2[cv + 3 * ip]; + matrix2[cv + 3 * ip] = matrix2[cv + 3 * i]; + if (ii >= 0) { + // rv = &(matrix1[i][0]); + rv = i * 3; + for (j = ii; j <= i - 1; j++) { + sum -= matrix1[rv + j] * matrix2[cv + 3 * j]; + } + } else if (sum != 0.0) { + ii = i; + } + matrix2[cv + 3 * i] = sum; + } + + // Backsubstitution + // rv = &(matrix1[3][0]); + rv = 2 * 3; + matrix2[cv + 3 * 2] /= matrix1[rv + 2]; + + rv -= 3; + matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2] + * matrix2[cv + 3 * 2]) + / matrix1[rv + 1]; + + rv -= 3; + matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1] + * matrix2[cv + 3 * 1] - matrix1[rv + 2] + * matrix2[cv + 3 * 2]) + / matrix1[rv + 0]; + + } + } + + /** + * Multiply this matrix by the tuple t and place the result + * back into the tuple (t = this*t). + * @param t the tuple to be multiplied by this matrix and then replaced + */ + public final void transform(Tuple3d t) { + double x,y,z; + x = m00* t.x + m01*t.y + m02*t.z; + y = m10* t.x + m11*t.y + m12*t.z; + z = m20* t.x + m21*t.y + m22*t.z; + t.set(x,y,z); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix4d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix4d.java new file mode 100644 index 0000000..d16ceeb --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Matrix4d.java @@ -0,0 +1,216 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Matrix4d { + public double m00; + public double m01; + public double m02; + public double m03; + public double m10; + public double m11; + public double m12; + public double m13; + public double m20; + public double m21; + public double m22; + public double m23; + public double m30; + public double m31; + public double m32; + public double m33; + + // コンストラクタ + public Matrix4d() { + m00 = 1.0; + m01 = 0.0; + m02 = 0.0; + m03 = 0.0; + m10 = 0.0; + m11 = 1.0; + m12 = 0.0; + m13 = 0.0; + m20 = 0.0; + m21 = 0.0; + m22 = 1.0; + m23 = 0.0; + m30 = 0.0; + m31 = 0.0; + m32 = 0.0; + m33 = 1.0; + } + + public Matrix4d(double[] v) { + m00 = v[0]; + m01 = v[1]; + m02 = v[2]; + m03 = v[3]; + m10 = v[4]; + m11 = v[5]; + m12 = v[6]; + m13 = v[7]; + m20 = v[8]; + m21 = v[9]; + m22 = v[10]; + m23 = v[11]; + m30 = v[12]; + m31 = v[13]; + m32 = v[14]; + m33 = v[15]; + } + + public Matrix4d(double m00, double m01, double m02, double m03, double m10, + double m11, double m12, double m13, double m20, double m21, + double m22, double m23, double m30, double m31, double m32, + double m33) { + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m03 = m03; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + } + + public Matrix4d clone() { + return new Matrix4d(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33); + } + + public Matrix4d(Matrix4d m1) { + this.m00 = m1.m00; + this.m01 = m1.m01; + this.m02 = m1.m02; + this.m03 = m1.m03; + this.m10 = m1.m10; + this.m11 = m1.m11; + this.m12 = m1.m12; + this.m13 = m1.m13; + this.m20 = m1.m20; + this.m21 = m1.m21; + this.m22 = m1.m22; + this.m23 = m1.m23; + this.m30 = m1.m30; + this.m31 = m1.m31; + this.m32 = m1.m32; + this.m33 = m1.m33; + } + + /** 各要素に足す */ + public void add(Matrix4d m1) { + this.m00 += m1.m00; + this.m01 += m1.m01; + this.m02 += m1.m02; + this.m03 += m1.m03; + this.m10 += m1.m10; + this.m11 += m1.m11; + this.m12 += m1.m12; + this.m13 += m1.m13; + this.m20 += m1.m20; + this.m21 += m1.m21; + this.m22 += m1.m22; + this.m23 += m1.m23; + this.m30 += m1.m30; + this.m31 += m1.m31; + this.m32 += m1.m32; + this.m33 += m1.m33; + } + + /** 二つの行列の和を格納する */ + public void add(Matrix4d m1, Matrix4d m2) { + this.m00 = m1.m00 + m2.m00; + this.m01 = m1.m01 + m2.m01; + this.m02 = m1.m02 + m2.m02; + this.m03 = m1.m03 + m2.m03; + this.m10 = m1.m10 + m2.m10; + this.m11 = m1.m11 + m2.m11; + this.m12 = m1.m12 + m2.m12; + this.m13 = m1.m13 + m2.m13; + this.m20 = m1.m20 + m2.m20; + this.m21 = m1.m21 + m2.m21; + this.m22 = m1.m22 + m2.m22; + this.m23 = m1.m23 + m2.m23; + this.m30 = m1.m30 + m2.m30; + this.m31 = m1.m31 + m2.m31; + this.m32 = m1.m32 + m2.m32; + this.m33 = m1.m33 + m2.m33; + } + + /** 自分に引数の行列を右から掛けたものを格納する */ + public void mul(Matrix4d right) { + double _m00 = m00 * right.m00 + m01 * right.m10 + m02 * right.m20 + m03 * right.m30; + double _m01 = m00 * right.m01 + m01 * right.m11 + m02 * right.m21 + m03 * right.m31; + double _m02 = m00 * right.m02 + m01 * right.m12 + m02 * right.m22 + m03 * right.m32; + double _m03 = m00 * right.m03 + m01 * right.m13 + m02 * right.m23 + m03 * right.m33; + + double _m10 = m10 * right.m00 + m11 * right.m10 + m12 * right.m20 + m13 * right.m30; + double _m11 = m10 * right.m01 + m11 * right.m11 + m12 * right.m21 + m13 * right.m31; + double _m12 = m10 * right.m02 + m11 * right.m12 + m12 * right.m22 + m13 * right.m32; + double _m13 = m10 * right.m03 + m11 * right.m13 + m12 * right.m23 + m13 * right.m33; + + double _m20 = m20 * right.m00 + m21 * right.m10 + m22 * right.m20 + m23 * right.m30; + double _m21 = m20 * right.m01 + m21 * right.m11 + m22 * right.m21 + m23 * right.m31; + double _m22 = m20 * right.m02 + m21 * right.m12 + m22 * right.m22 + m23 * right.m32; + double _m23 = m20 * right.m03 + m21 * right.m13 + m22 * right.m23 + m23 * right.m33; + + double _m30 = m30 * right.m00 + m31 * right.m10 + m32 * right.m20 + m33 * right.m30; + double _m31 = m30 * right.m01 + m31 * right.m11 + m32 * right.m21 + m33 * right.m31; + double _m32 = m30 * right.m02 + m31 * right.m12 + m32 * right.m22 + m33 * right.m32; + double _m33 = m30 * right.m03 + m31 * right.m13 + m32 * right.m23 + m33 * right.m33; + + m00 = _m00; m01 = _m01; m02 = _m02; m03 = _m03; + m10 = _m10; m11 = _m11; m12 = _m12; m13 = _m13; + m20 = _m20; m21 = _m21; m22 = _m22; m23 = _m23; + m30 = _m30; m31 = _m31; m32 = _m32; m33 = _m33; + } + + public void invert() { + double det = determinant(); + if (det == 0) { + return; + } + double b00 = determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33) / det; + double b01 = -determinant3x3(m01,m02,m03,m21,m22,m23,m31,m32,m33) / det; + double b02 = determinant3x3(m01,m02,m03,m11,m12,m13,m31,m32,m33) / det; + double b03 = -determinant3x3(m01,m02,m03,m11,m12,m13,m21,m22,m23) / det; + + double b10 = -determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33) / det; + double b11 = determinant3x3(m00,m02,m03,m20,m22,m23,m30,m32,m33) / det; + double b12 = -determinant3x3(m00,m02,m03,m10,m12,m13,m30,m32,m33) / det; + double b13 = determinant3x3(m00,m02,m03,m10,m12,m13,m20,m22,m23) / det; + + double b20 = determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33) / det; + double b21 = -determinant3x3(m00,m01,m03,m20,m21,m23,m30,m31,m33) / det; + double b22 = determinant3x3(m00,m01,m03,m10,m11,m13,m30,m31,m33) / det; + double b23 = -determinant3x3(m00,m01,m03,m10,m11,m13,m20,m21,m23) / det; + + double b30 = -determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32) / det; + double b31 = determinant3x3(m00,m01,m02,m20,m21,m22,m30,m31,m32) / det; + double b32 = -determinant3x3(m00,m01,m02,m10,m11,m12,m30,m31,m32) / det; + double b33 = determinant3x3(m00,m01,m02,m10,m11,m12,m20,m21,m22) / det; + + m00 = b00; m01 = b01; m02 = b02; m03 = b03; + m10 = b10; m11 = b11; m12 = b12; m13 = b13; + m20 = b20; m21 = b21; m22 = b22; m23 = b23; + m30 = b30; m31 = b31; m32 = b32; m33 = b33; + } + + private static double determinant3x3(double m11, double m12, double m13,double m21,double m22, double m23,double m31,double m32,double m33) { + return m11*m22*m33+m12*m23*m31+m13*m21*m32-m11*m23*m32-m12*m21*m33-m13*m22*m31; + } + + /** 行列式を求める */ + public double determinant() { + return m00 * determinant3x3(m11,m12,m13,m21,m22,m23,m31,m32,m33)- + m01 * determinant3x3(m10,m12,m13,m20,m22,m23,m30,m32,m33)+ + m02 * determinant3x3(m10,m11,m13,m20,m21,m23,m30,m31,m33)- + m03 * determinant3x3(m10,m11,m12,m20,m21,m22,m30,m31,m32); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Node.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Node.java new file mode 100644 index 0000000..8eac9e6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Node.java @@ -0,0 +1,11 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Node { + + public Node() { + super(); + } + + public abstract Node cloneTree(); + +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/NodeComponent.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/NodeComponent.java new file mode 100644 index 0000000..f0bb6f5 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/NodeComponent.java @@ -0,0 +1,6 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class NodeComponent { + public abstract NodeComponent cloneNodeComponent(); + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3d.java new file mode 100644 index 0000000..07dc7ab --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3d.java @@ -0,0 +1,68 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Point3d extends Tuple3d { + + // コンストラクタ + public Point3d() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public Point3d(double[] p) { + this.x = p[0]; + this.y = p[1]; + this.z = p[2]; + } + + public Point3d(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3d(Point3d p1) { + this.x = p1.x; + this.y = p1.y; + this.z = p1.z; + } + + public Point3d(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + public void scale(double s) { + this.x = x * s; + this.y = y * s; + this.z = z * s; + } + + public void add(Tuple3d p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + } + + public void sub(Tuple3d p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + } + + public void set(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 引数の点との距離を返す */ + public double distance(Point3d p) { + return Math.sqrt(distanceSquared(p)); + } + + public double distanceSquared(Point3d p) { + return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3f.java new file mode 100644 index 0000000..a74c7fb --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Point3f.java @@ -0,0 +1,65 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Point3f { + public float x; + public float y; + public float z; + + // コンストラクタ + public Point3f() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public Point3f(float[] p) { + this.x = p[0]; + this.y = p[1]; + this.z = p[2]; + } + + public Point3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Point3f(Point3f p1) { + this.x = p1.x; + this.y = p1.y; + this.z = p1.z; + } + + public void scale(float s) { + this.x = x * s; + this.y = y * s; + this.z = z * s; + } + + public void add(Point3f p) { + this.x += p.x; + this.y += p.y; + this.z += p.z; + } + + public void sub(Point3f p) { + this.x -= p.x; + this.y -= p.y; + this.z -= p.z; + } + + public void set(Point3f v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 引数の点との距離を返す */ + public double distance(Point3f p) { + return Math.sqrt(distanceSquared(p)); + } + + public double distanceSquared(Point3f p) { + return (this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y) + (this.z - p.z) * (this.z - p.z); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/PointLight.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/PointLight.java new file mode 100644 index 0000000..ed9e19b --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/PointLight.java @@ -0,0 +1,31 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class PointLight extends Light { + private Point3f position; + private Point3f attenuation; + + public PointLight() { + super(new Color3f(1.0f, 1.0f, 1.0f)); + position = new Point3f(0.0f, 0.0f, 0.0f); + } + + public PointLight(Color3f color, Point3f position, Point3f attenuation){ + super(color); + this.position = position; + this.attenuation = attenuation; + } + + @Override + public Node cloneTree() { + return new PointLight(color, position, attenuation); + } + + public void setPosition(Point3f p){ + this.position = p; + } + + public Point3f getPosition(){ + return position; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Primitive.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Primitive.java new file mode 100644 index 0000000..959ec49 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Primitive.java @@ -0,0 +1,15 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Primitive extends Group { + private Appearance ap = null; + + public Appearance getAppearance() { + return ap; + } + + public void setAppearance(Appearance ap) { + this.ap = ap; + } + + public abstract Shape3D getShape(int partid); +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Quat4d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Quat4d.java new file mode 100644 index 0000000..56690ea --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Quat4d.java @@ -0,0 +1,130 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Quat4d implements Cloneable { + private static final double EPS = 1.0e-12; + public double x; + public double y; + public double z; + public double w; + + public Quat4d clone() { + Quat4d q; + try { + q = (Quat4d) super.clone(); + } catch (CloneNotSupportedException ce) { + throw new RuntimeException(); + } + q.set(x, y, z, w); + return q; + } + + // コンストラクタ + public Quat4d() { + x = 0.0; + y = 0.0; + z = 0.0; + w = 0.0; + } + + // コンストラクタ + public Quat4d(double px, double py, double pz, double pw) { + double mag = 1.0/ Math.sqrt(px*px+py*py+pz*pz+pw*pw); + x = px*mag; + y = py*mag; + z = pz*mag; + w = pw*mag; + } + + public Quat4d(Quat4d q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } + + public void set(AxisAngle4d a) { + double mag,amag; + amag = Math.sqrt( a.x*a.x + a.y*a.y + a.z*a.z); + if( amag < EPS ) { + w = 0.0; + x = 0.0; + y = 0.0; + z = 0.0; + } else { + amag = 1.0/amag; + mag = Math.sin(a.angle/2.0); + w = Math.cos(a.angle/2.0); + x = a.x*amag*mag; + y = a.y*amag*mag; + z = a.z*amag*mag; + } + } + + private void set(double x2, double y2, double z2, double w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + public void mul(Quat4d q1) { + double x, y, w; + w = this.w * q1.w - this.x * q1.x - this.y * q1.y - this.z * q1.z; + x = this.w * q1.x + q1.w * this.x + this.y * q1.z - this.z * q1.y; + y = this.w * q1.y + q1.w * this.y - this.x * q1.z + this.z * q1.x; + this.z = this.w * q1.z + q1.w * this.z + this.x * q1.y - this.y * q1.x; + this.w = w; + this.x = x; + this.y = y; + } + + public void interpolate(Quat4d q1, double alpha) { + double dot, s1, s2, om, sinom; + + dot = x * q1.x + y * q1.y + z * q1.z + w * q1.w; + + if (dot < 0) { + // negate quaternion + q1.x = -q1.x; + q1.y = -q1.y; + q1.z = -q1.z; + q1.w = -q1.w; + dot = -dot; + } + + if ((1.0 - dot) > EPS) { + om = Math.acos(dot); + sinom = Math.sin(om); + s1 = Math.sin((1.0 - alpha) * om) / sinom; + s2 = Math.sin(alpha * om) / sinom; + } else { + s1 = 1.0 - alpha; + s2 = alpha; + } + + w = s1 * w + s2 * q1.w; + x = s1 * x + s2 * q1.x; + y = s1 * y + s2 * q1.y; + z = s1 * z + s2 * q1.z; + } + + public void normalize() { + double norm; + + norm = (this.x * this.x + this.y * this.y + this.z * this.z + this.w + * this.w); + + if (norm > 0.0) { + norm = 1.0 / Math.sqrt(norm); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } else { + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + this.w = 0.0; + } + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Shape3D.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Shape3D.java new file mode 100644 index 0000000..a21b9de --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Shape3D.java @@ -0,0 +1,33 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Shape3D extends Leaf { + private Geometry geometry; + private Appearance ap; + + public Shape3D(Geometry g, Appearance a) { + this.geometry = g; + this.ap = a; + } + + public Appearance getAppearance() { + return ap; + } + + public void setAppearance(Appearance ap) { + this.ap = ap; + } + + public Geometry getGeometry() { + return geometry; + } + + public void setGeometry(Geometry geometry) { + this.geometry = geometry; + } + + @Override + public Node cloneTree() { + Shape3D shape = new Shape3D(geometry, (Appearance)ap.cloneNodeComponent()); + return shape; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Sphere.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Sphere.java new file mode 100644 index 0000000..e60953f --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Sphere.java @@ -0,0 +1,22 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Sphere extends Primitive { + public static final int BODY = 0; + static final int MID_REZ_DIV = 16; + + private float radius; + int divisions; + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + } + + @Override + public Shape3D getShape(int partid) { + return null; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TexCoordGeneration.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TexCoordGeneration.java new file mode 100644 index 0000000..a6b0fbc --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TexCoordGeneration.java @@ -0,0 +1,42 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TexCoordGeneration extends NodeComponent { + public static final int OBJECT_LINEAR = 0; + public static final int EYE_LINEAR = 1; + public static final int SPHERE_MAP = 2; + public static final int NORMAL_MAP = 3; + public static final int REFLECTION_MAP = 4; + public static final int TEXTURE_COORDINATE_2 = 0; + public static final int TEXTURE_COORDINATE_3 = 1; + public static final int TEXTURE_COORDINATE_4 = 2; + + private int genMode; + private int format; + + public TexCoordGeneration(int genMode, int format) { + this.genMode = genMode; + this.format = format; + } + + public int getGenMode() { + return genMode; + } + + public void setGenMode(int genMode) { + this.genMode = genMode; + } + + public int getFormat() { + return format; + } + + public void setFormat(int format) { + this.format = format; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TexCoordGeneration(genMode, format); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture.java new file mode 100644 index 0000000..c9a337a --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture.java @@ -0,0 +1,36 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public abstract class Texture extends NodeComponent { + public static final int BASE_LEVEL = 1; + public static final int RGB = 5; + public static final int RGBA = 6; + + protected ImageComponent[] imageComponents = null; + protected int mipmapMode; + protected int format; + protected int width; + protected int hight; + + public Texture(int mipmapMode, int format, int width, int hight) { + this.mipmapMode = mipmapMode; + this.format = format; + this.width = width; + this.hight = hight; + } + + public void setImage(int n, ImageComponent image) { + imageComponents[n] = image; + } + + public ImageComponent getImage(int n) { + return imageComponents[n]; + } + + public void setImages(ImageComponent[] images) { + imageComponents = images; + } + + public ImageComponent[] getImages() { + return imageComponents; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture2D.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture2D.java new file mode 100644 index 0000000..d9b9afe --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Texture2D.java @@ -0,0 +1,16 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Texture2D extends Texture { + + public Texture2D(int mipmapMode, int format, int width, int hight) { + super(mipmapMode, format, width, hight); + imageComponents = new ImageComponent[1]; + } + + @Override + public NodeComponent cloneNodeComponent() { + Texture2D tex2D = new Texture2D(mipmapMode, format, width, hight); + tex2D.imageComponents = imageComponents.clone(); + return tex2D; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureAttributes.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureAttributes.java new file mode 100644 index 0000000..5523633 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureAttributes.java @@ -0,0 +1,70 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TextureAttributes extends NodeComponent { + public static final int FASTEST = 0; + public static final int NICEST = 1; + public static final int MODULATE = 2; + public static final int DECAL = 3; + public static final int BLEND = 4; + public static final int REPLACE = 5; +// public static final int COMBINE = 6; +// public static final int COMBINE_REPLACE = 0; +// public static final int COMBINE_MODULATE = 1; +// public static final int COMBINE_ADD = 2; +// public static final int COMBINE_ADD_SIGNED = 3; +// public static final int COMBINE_SUBTRACT = 4; +// public static final int COMBINE_INTERPOLATE = 5; +// public static final int COMBINE_DOT3 = 6; +// public static final int COMBINE_OBJECT_COLOR = 0; +// public static final int COMBINE_TEXTURE_COLOR = 1; +// public static final int COMBINE_CONSTANT_COLOR = 2; +// public static final int COMBINE_PREVIOUS_TEXTURE_UNIT_STATE = 3; +// public static final int COMBINE_SRC_COLOR = 0; +// public static final int COMBINE_ONE_MINUS_SRC_COLOR = 1; +// public static final int COMBINE_SRC_ALPHA = 2; +// public static final int COMBINE_ONE_MINUS_SRC_ALPHA = 3; + + private int textureMode; + private Transform3D transform; + private Color4f textureBlendColor; + private int perspCorrectionMode; + + public TextureAttributes() { + textureMode = TextureAttributes.REPLACE; + textureBlendColor = new Color4f(); + perspCorrectionMode = TextureAttributes.NICEST; + } + + public TextureAttributes(int textureMode, Transform3D transform, Color4f textureBlendColor, int perspCorrectionMode) { + this.textureMode = textureMode; + this.transform = transform; + this.textureBlendColor = textureBlendColor; + this.perspCorrectionMode = perspCorrectionMode; + } + + public int getTextureMode() { + return textureMode; + } + + public void setTextureMode(int textureMode) { + this.textureMode = textureMode; + } + + public void setTextureTransform(Transform3D transform) { + this.transform = transform; + } + + public int getPerspectiveCorrectionMode() { + return perspCorrectionMode; + } + + public void setPerspectiveCorrectionMode(int perspCorrectionMode) { + this.perspCorrectionMode = perspCorrectionMode; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TextureAttributes(textureMode, new Transform3D(transform), textureBlendColor.clone(), perspCorrectionMode); + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureCubeMap.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureCubeMap.java new file mode 100644 index 0000000..20d96c4 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureCubeMap.java @@ -0,0 +1,29 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TextureCubeMap extends Texture { + public static final int POSITIVE_X = 0; + public static final int NEGATIVE_X = 1; + public static final int POSITIVE_Y = 2; + public static final int NEGATIVE_Y = 3; + public static final int POSITIVE_Z = 4; + public static final int NEGATIVE_Z = 5; + + public TextureCubeMap(int mipmapMode, int format, int width) { + super(mipmapMode, format, width, 0); + imageComponents = new ImageComponent[6]; + } + + + public void setImage(int level, int face, ImageComponent2D image) { + imageComponents[face] = image; + width = image.getBitmap().getWidth(); + } + + @Override + public NodeComponent cloneNodeComponent() { + TextureCubeMap texCubeMap = new TextureCubeMap(mipmapMode, format, width); + texCubeMap.imageComponents = imageComponents.clone(); + return texCubeMap; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureLoader.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureLoader.java new file mode 100644 index 0000000..dbd250b --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureLoader.java @@ -0,0 +1,43 @@ +package org.ntlab.radishforandroidstudio.java3d; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.InputStream; + +public class TextureLoader { + + public static final int BY_REFERENCE = 2; + public static final int Y_UP = 4; + private InputStream in = null; + private Resources res = null; + private int id = 0; + private Bitmap bitmap; + + public TextureLoader(InputStream in, int flags) { + this.in = in; + } + + public TextureLoader(Resources res, int id, int flags) { + this.res = res; + this.id = id; + } + + public ImageComponent2D getImage() { + if (in != null) { + bitmap = BitmapFactory.decodeStream(in); + } else if (res != null) { + bitmap = BitmapFactory.decodeResource(res, id); + } + return new ImageComponent2D(ImageComponent2D.FORMAT_RGB, bitmap); + } + + public Texture getTexture() { + ImageComponent2D ic2 = getImage(); + Texture tex = new Texture2D(Texture.BASE_LEVEL, Texture.RGB, bitmap.getWidth(), bitmap.getHeight()); + tex.setImage(0, ic2); + return tex; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureUnitState.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureUnitState.java new file mode 100644 index 0000000..9b39b54 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TextureUnitState.java @@ -0,0 +1,48 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TextureUnitState extends NodeComponent { + private Texture texture; + private TextureAttributes textureAttribute; + private TexCoordGeneration texCoordGeneration; + + public TextureUnitState() { + this(null, null ,null); + } + + public TextureUnitState(Texture texture, TextureAttributes textureAttribute, TexCoordGeneration texCoordGeneration) { + this.texture = texture; + this.textureAttribute = textureAttribute; + this.texCoordGeneration= texCoordGeneration; + } + + public Texture getTexture() { + return texture; + } + + public void setTexture(Texture texture) { + this.texture = texture; + } + + public TextureAttributes getTextureAttributes() { + return textureAttribute; + } + + public void setTextureAttributes(TextureAttributes textureAttribute) { + this.textureAttribute = textureAttribute; + } + + public TexCoordGeneration getTexCoordGeneration() { + return texCoordGeneration; + } + + public void setTexCoordGeneration(TexCoordGeneration texCoordGeneration) { + this.texCoordGeneration = texCoordGeneration; + } + + @Override + public NodeComponent cloneNodeComponent() { + return new TextureUnitState((Texture)texture.cloneNodeComponent(), + (TextureAttributes)textureAttribute.cloneNodeComponent(), + (TexCoordGeneration)texCoordGeneration.cloneNodeComponent()); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Transform3D.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Transform3D.java new file mode 100644 index 0000000..a944eb3 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Transform3D.java @@ -0,0 +1,425 @@ +package org.ntlab.radishforandroidstudio.java3d; + + +public class Transform3D { + double[][] mat; + double[] rot; + double[] scales = new double[] {1.0,1.0,1.0}; + + static final double EPSILON_ABSOLUTE = 1.0e-5; + + public Transform3D() { + mat = 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}}; + } + + public Transform3D(Matrix4d m1) { + mat = new double[][]{{m1.m00,m1.m01,m1.m02,m1.m03},{m1.m10,m1.m11,m1.m12,m1.m13},{m1.m20,m1.m21,m1.m22,m1.m23},{m1.m30,m1.m31,m1.m32,m1.m33}}; + } + + public Transform3D(Transform3D transform) { + mat = new double[4][4]; + set(transform); + } + + public void set(Transform3D transform) { + for (int i = 0;i < 4; i++) { + for (int j = 0;j < 4; j++) { + mat[i][j] = transform.mat[i][j]; + } + } + scales[0] = transform.scales[0]; + scales[1] = transform.scales[1]; + scales[2] = transform.scales[2]; + } + + /** + * this = t1 * t2 + * + * @param t1 + * @param t2 + */ + public void mul(Transform3D t1, Transform3D t2) { + for (int i = 0; i < 4; i++) { + double[] row = row(i, t1.mat); + for (int j = 0; j < 4; j++) { + mat[i][j] = 0.0; + double[] col = col(j, t2.mat); + for (int k = 0; k < 4; k++) { + mat[i][j] += row[k] * col[k]; + } + } + } + } + + /** + * this = this * t1 + * + * @param t1 + */ + public void mul(Transform3D t1) { + this.mul(new Transform3D(this), t1); + } + + /** 4*4行列の行を返す */ + private double[] row(int row, double[][] mat) { + return new double[] {mat[row][0], mat[row][1], mat[row][2], mat[row][3]}; + } + + /** 4*4行列の列を返す */ + private double[] col(int col, double[][] mat) { + return new double[] {mat[0][col], mat[1][col], mat[2][col], mat[3][col]}; + } + + public void set(Vector3d v) { + mat[0][3] = v.x; + mat[1][3] = v.y; + mat[2][3] = v.z; + } + + /** 4*4の行列を16の1次元配列で返す */ + public float[] getMatrix() { + float[] result = new float[16]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + result[j + 4 * i] = (float)(mat[j][i]); + } + } + return result; + } + + /** 対角行列をスカラ倍する */ + public void setScale(double s) { + for (int i = 0; i < 3; i++) { + mat[i][i] = s; + scales[i] = s; + } + } + + /** 対角行列をvectorの成分倍する */ + public void setScale(Vector3d vector3d) { + scales[0] = mat[0][0] = vector3d.x; + scales[1] = mat[1][1] = vector3d.y; + scales[2] = mat[2][2] = vector3d.z; + } + + /** 任意の単位ベクトルまわりに回転する */ + public void setRotation(AxisAngle4d t) { + double vx = t.x; + double vy = t.y; + double vz = t.z; + double sin = Math.sin(t.angle); + double cos = Math.cos(t.angle); + + mat[0][0] = (Math.pow(vx, 2.0)) * (1 - cos) + cos; + mat[0][1] = ((vx * vy) * (1 - cos)) - (vz * sin); + mat[0][2] = ((vz * vx) * (1 - cos)) + (vy * sin); + mat[1][0] = ((vx * vy) * (1 - cos)) + (vz * sin); + mat[1][1] = (Math.pow(vy, 2.0)) * (1 - cos) + cos; + mat[1][2] = ((vy * vz) * (1 - cos)) - (vx * sin); + mat[2][0] = ((vz * vx) * (1 - cos)) - (vy * sin); + mat[2][1] = ((vy * vz) * (1 - cos)) + (vx * sin); + mat[2][2] = (Math.pow(vz, 2.0)) * (1 - cos) + cos; + } + + /** 任意の単位ベクトルまわりに回転する */ + public void setRotation(Quat4d q) { +// double vx = q.x; +// double vy = q.y; +// double vz = q.z; +// double sin = Math.sin(q.w); +// double cos = Math.cos(q.w); +// double cosm = 1 - cos; +// +// mat[0][0] = (vx * vx) * cosm + cos; +// mat[0][1] = ((vx * vy) * cosm) - (vz * sin); +// mat[0][2] = ((vz * vx) * cosm) + (vy * sin); +// mat[1][0] = ((vx * vy) * cosm) + (vz * sin); +// mat[1][1] = (vy * vy) * cosm + cos; +// mat[1][2] = ((vy * vz) * cosm) - (vx * sin); +// mat[2][0] = ((vz * vx) * cosm) - (vy * sin); +// mat[2][1] = ((vy * vz) * cosm) + (vx * sin); +// mat[2][2] = (vz * vz) * cosm + cos; + + mat[0][0] = (1.0 - 2.0 * q.y * q.y - 2.0 * q.z * q.z) * scales[0]; + mat[1][0] = (2.0 * (q.x * q.y + q.w * q.z)) * scales[0]; + mat[2][0] = (2.0 * (q.x * q.z - q.w * q.y)) * scales[0]; + mat[0][1] = (2.0 * (q.x * q.y - q.w * q.z)) * scales[1]; + mat[1][1] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.z * q.z) * scales[1]; + mat[2][1] = (2.0 * (q.y * q.z + q.w * q.x)) * scales[1]; + mat[0][2] = (2.0 * (q.x * q.z + q.w * q.y)) * scales[2]; + mat[1][2] = (2.0 * (q.y * q.z - q.w * q.x)) * scales[2]; + mat[2][2] = (1.0 - 2.0 * q.x * q.x - 2.0 * q.y * q.y) * scales[2]; + } + + public void rotX(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = 1.0; + mat[0][1] = 0.0; + mat[0][2] = 0.0; + mat[0][3] = 0.0; + + mat[1][0] = 0.0; + mat[1][1] = cosAngle; + mat[1][2] = -sinAngle; + mat[1][3] = 0.0; + + mat[2][0] = 0.0; + mat[2][1] = sinAngle; + mat[2][2] = cosAngle; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + public void rotY(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = cosAngle; + mat[0][1] = 0.0; + mat[0][2] = sinAngle; + mat[0][3] = 0.0; + + mat[1][0] = 0.0; + mat[1][1] = 1.0; + mat[1][2] = 0.0; + mat[1][3] = 0.0; + + mat[2][0] = -sinAngle; + mat[2][1] = 0.0; + mat[2][2] = cosAngle; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + public void rotZ(double angle) { + double sinAngle = Math.sin(angle); + double cosAngle = Math.cos(angle); + + mat[0][0] = cosAngle; + mat[0][1] = -sinAngle; + mat[0][2] = 0.0; + mat[0][3] = 0.0; + + mat[1][0] = sinAngle; + mat[1][1] = cosAngle; + mat[1][2] = 0.0; + mat[1][3] = 0.0; + + mat[2][0] = 0.0; + mat[2][1] = 0.0; + mat[2][2] = 1.0; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + + /** 引数の成分を格納する */ + public void get(Matrix4d mat4d) { + mat4d.m00 = mat[0][0]; + mat4d.m01 = mat[0][1]; + mat4d.m02 = mat[0][2]; + mat4d.m03 = mat[0][3]; + mat4d.m10 = mat[1][0]; + mat4d.m11 = mat[1][1]; + mat4d.m12 = mat[1][2]; + mat4d.m13 = mat[1][3]; + mat4d.m20 = mat[2][0]; + mat4d.m21 = mat[2][1]; + mat4d.m22 = mat[2][2]; + mat4d.m23 = mat[2][3]; + mat4d.m30 = mat[3][0]; + mat4d.m31 = mat[3][1]; + mat4d.m32 = mat[3][2]; + mat4d.m33 = mat[3][3]; + } + + + public void set(double[] matrix) { + mat[0][0] = matrix[0]; + mat[0][1] = matrix[1]; + mat[0][2] = matrix[2]; + mat[0][3] = matrix[3]; + mat[1][0] = matrix[4]; + mat[1][1] = matrix[5]; + mat[1][2] = matrix[6]; + mat[1][3] = matrix[7]; + mat[2][0] = matrix[8]; + mat[2][1] = matrix[9]; + mat[2][2] = matrix[10]; + mat[2][3] = matrix[11]; + mat[3][0] = matrix[12]; + mat[3][1] = matrix[13]; + mat[3][2] = matrix[14]; + mat[3][3] = matrix[15]; + scales[0] = 1.0; + scales[1] = 1.0; + scales[2] = 1.0; + } + + public void set(AxisAngle4d a1) { + double mag = Math.sqrt( a1.x*a1.x + a1.y*a1.y + a1.z*a1.z); + if (almostZero(mag)) { + setIdentity(); + } else { + mag = 1.0/mag; + double ax = a1.x*mag; + double ay = a1.y*mag; + double az = a1.z*mag; + double sinTheta = Math.sin((double)a1.angle); + double cosTheta = Math.cos((double)a1.angle); + double t = 1.0 - cosTheta; + double xz = ax * az; + double xy = ax * ay; + double yz = ay * az; + mat[0][0] = t * ax * ax + cosTheta; + mat[0][1] = t * xy - sinTheta * az; + mat[0][2] = t * xz + sinTheta * ay; + mat[0][3] = 0.0; + mat[1][0] = t * xy + sinTheta * az; + mat[1][1] = t * ay * ay + cosTheta; + mat[1][2] = t * yz - sinTheta * ax; + mat[1][3] = 0.0; + mat[2][0] = t * xz - sinTheta * ay; + mat[2][1] = t * yz + sinTheta * ax; + mat[2][2] = t * az * az + cosTheta; + mat[2][3] = 0.0; + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + } + } + + public void set(Quat4d q1) { + mat[0][0] = (1.0f - 2.0f*q1.y*q1.y - 2.0f*q1.z*q1.z); + mat[1][0] = (2.0f*(q1.x*q1.y + q1.w*q1.z)); + mat[2][0] = (2.0f*(q1.x*q1.z - q1.w*q1.y)); + + mat[0][1] = (2.0f*(q1.x*q1.y - q1.w*q1.z)); + mat[1][1] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.z*q1.z); + mat[2][1] = (2.0f*(q1.y*q1.z + q1.w*q1.x)); + + mat[0][2] = (2.0f*(q1.x*q1.z + q1.w*q1.y)); + mat[1][2] = (2.0f*(q1.y*q1.z - q1.w*q1.x)); + mat[2][2] = (1.0f - 2.0f*q1.x*q1.x - 2.0f*q1.y*q1.y); + + mat[0][3] = 0.0; + mat[1][3] = 0.0; + mat[2][3] = 0.0; + + mat[3][0] = 0.0; + mat[3][1] = 0.0; + mat[3][2] = 0.0; + mat[3][3] = 1.0; + + // Issue 253: set all dirty bits if input is infinity or NaN +// if (isInfOrNaN(q1)) { +// dirtyBits = ALL_DIRTY; +// return; +// } + +// dirtyBits = CLASSIFY_BIT | SCALE_BIT | ROTATION_BIT; +// type = RIGID | CONGRUENT | AFFINE | ORTHO; + } + + /** Vector3dをTransform3dで変換する */ + public void transform(Vector3d v) { + double vx = mat[0][0]*v.x + mat[0][1]*v.y + mat[0][2]*v.z;// + mat[0][3] * 1.0; + double vy = mat[1][0]*v.x + mat[1][1]*v.y + mat[1][2]*v.z;// + mat[1][3] * 1.0; + v.z = mat[2][0]*v.x + mat[2][1]*v.y + mat[2][2]*v.z;// + mat[2][3] * 1.0; + v.x = vx; + v.y = vy; + } + + private static final boolean almostZero(double a) { + return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); + } + + public final void setIdentity() { + mat[0][0] = 1.0; mat[0][1] = 0.0; mat[0][2] = 0.0; mat[0][3] = 0.0; + mat[1][0] = 0.0; mat[1][1] = 1.0; mat[1][2] = 0.0; mat[1][3] = 0.0; + mat[2][0] = 0.0; mat[2][1] = 0.0; mat[2][2] = 1.0; mat[2][3] = 0.0; + mat[3][0] = 0.0; mat[3][1] = 0.0; mat[3][2] = 0.0; mat[3][3] = 1.0; + } + + public void invert() { + Matrix4d m = new Matrix4d(); + m.m00 = mat[0][0]; m.m01 = mat[0][1]; m.m02 = mat[0][2]; m.m03 = mat[0][3]; + m.m10 = mat[1][0]; m.m11 = mat[1][1]; m.m12 = mat[1][2]; m.m13 = mat[1][3]; + m.m20 = mat[2][0]; m.m21 = mat[2][1]; m.m22 = mat[2][2]; m.m23 = mat[2][3]; + m.m30 = mat[3][0]; m.m31 = mat[3][1]; m.m32 = mat[3][2]; m.m33 = mat[3][3]; + m.invert(); + mat = new double[][]{{m.m00,m.m01,m.m02,m.m03},{m.m10,m.m11,m.m12,m.m13},{m.m20,m.m21,m.m22,m.m23},{m.m30,m.m31,m.m32,m.m33}}; + } + + public void transpose() { + for (int i = 0; i < 3; i++) { + for (int j = 1 + i; j < 4; j++) { + double m = mat[j][i]; + mat[j][i] = mat[i][j]; + mat[i][j] = m; + } + } + } + + public void transform(Point3d point) { + Point3d p = new Point3d(); + p.x = mat[0][0] * point.x + mat[0][1] * point.y + mat[0][2] * point.z; + p.y = mat[1][0] * point.x + mat[1][1] * point.y + mat[1][2] * point.z; + p.z = mat[2][0] * point.x + mat[2][1] * point.y + mat[2][2] * point.z; + point.x = p.x; + point.y = p.y; + point.z = p.z; + } + + /** + * 平面に対してしか使ってはいけない + * @param plane + */ + public void transform(Vector4d plane) { +// double x = mat[0][0] * plane.x + mat[0][1] * plane.y + mat[0][2] * plane.z; +// double y = mat[1][0] * plane.x + mat[1][1] * plane.y + mat[1][2] * plane.z; +// double z = mat[2][0] * plane.x + mat[2][1] * plane.y + mat[2][2] * plane.z; +// +// double vx = x * plane.w - mat[0][3]; +// double vy = y * plane.w - mat[1][3]; +// double vz = z * plane.w - mat[2][3]; +// +// plane.x = x; +// plane.y = y; +// plane.z = z; +// plane.w = x * vx + y * vy + z * vz; +// + double x = (mat[0][0]*plane.x + mat[0][1]*plane.y + + mat[0][2]*plane.z + mat[0][3]*plane.w); + double y = (mat[1][0]*plane.x + mat[1][1]*plane.y + + mat[1][2]*plane.z + mat[1][3]*plane.w); + double z = (mat[2][0]*plane.x + mat[2][1]*plane.y + + mat[2][2]*plane.z + mat[2][3]*plane.w); + plane.w = (mat[3][0]*plane.x + mat[3][1]*plane.y + + mat[3][2]*plane.z + mat[3][3]*plane.w); + plane.x = x; + plane.y = y; + plane.z = z; + } + + public void setTranslation(Vector3d v) { + mat[3][0] = v.x; + mat[3][1] = v.y; + mat[3][2] = v.z; + } + +} \ No newline at end of file diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TransformGroup.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TransformGroup.java new file mode 100644 index 0000000..7efb965 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TransformGroup.java @@ -0,0 +1,27 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TransformGroup extends Group { + + private Transform3D transform; + + public TransformGroup() { + this(new Transform3D()); + } + + public TransformGroup(Transform3D transform) { + this.transform = transform; + } + + public void setTransform(Transform3D transform) { + this.transform = transform; + } + + public void getTransform(Transform3D transform) { + transform.set(this.transform); + } + + public Node cloneTree() { + TransformGroup newInstance = new TransformGroup(new Transform3D(transform)); + return newInstance; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleArray.java new file mode 100644 index 0000000..fff5d64 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleArray.java @@ -0,0 +1,18 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TriangleArray extends GeometryArray { + + public TriangleArray(int vertexCount, int vertexFormat) { + super(vertexCount, vertexFormat); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleArray newOne = new TriangleArray(vertexCount, vertexFormat); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleFanArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleFanArray.java new file mode 100644 index 0000000..dec8048 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleFanArray.java @@ -0,0 +1,18 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TriangleFanArray extends GeometryStripArray { + + public TriangleFanArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleFanArray newOne = new TriangleFanArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleStripArray.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleStripArray.java new file mode 100644 index 0000000..98454a0 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/TriangleStripArray.java @@ -0,0 +1,18 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class TriangleStripArray extends GeometryStripArray { + + public TriangleStripArray(int vertexCount, int vertexFormat, int[] stripIndexCounts) { + super(vertexCount, vertexFormat, stripIndexCounts); + } + + @Override + public NodeComponent cloneNodeComponent() { + TriangleStripArray newOne = new TriangleStripArray(vertexCount, vertexFormat, (int [])stripIndexCounts.clone()); + newOne.vertexBuffer = vertexBuffer.duplicate(); + if (normalBuffer != null) newOne.normalBuffer = normalBuffer.duplicate(); + if (uvBuffer != null) newOne.uvBuffer = uvBuffer.duplicate(); + return newOne; + } + +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Tuple3d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Tuple3d.java new file mode 100644 index 0000000..1344eb6 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Tuple3d.java @@ -0,0 +1,38 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Tuple3d { + + public double x; + public double y; + public double z; + + public void set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector2f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector2f.java new file mode 100644 index 0000000..f2305f7 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector2f.java @@ -0,0 +1,104 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Vector2f { + public float x; + public float y; + + // コンストラクタ + public Vector2f() { + x = 0.0f; + y = 0.0f; + } + + // コンストラクタ + public Vector2f(float px, float py) { + x = px; + y = py; + } + + public Vector2f(Vector2f v) { + x = v.x; + y = v.y; + } + + public Vector2f clone() { + return new Vector2f(this.x, this.y); + } + + public double dot(Vector2f a) { + return x*a.x + y*a.y; + } + + public float length() { + return (float) Math.sqrt(x*x + y*y); + } + + public void scale(float s) {; + x = x*s; + y = y*s; + } + + public void normalize() { + float l = length(); + x = x / l; + y = y / l; + } + + /** v1とv2の和を格納する */ + public void add(Vector2f v1, Vector2f v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + } + + /** v1との和を格納する */ + public void add(Vector2f v1) { + this.x += v1.x; + this.y += v1.y; + } + + /** v1とv2の差を格納する */ + public void sub(Vector2f v1, Vector2f v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + } + + /** v1との差を格納する */ + public void sub(Vector2f v1) { + this.x -= v1.x; + this.y -= v1.y; + } + + /** Vector3dをセットする */ + public void set(Vector2f v) { + this.x = v.x; + this.y = v.y; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + } + + /** this = d * axis + p */ + public void scaleAdd(float d, Vector2f axis, Vector2f p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3d.java new file mode 100644 index 0000000..0ae7a79 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3d.java @@ -0,0 +1,134 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Vector3d extends Tuple3d { + + // コンストラクタ + public Vector3d() { + x = 0.0; + y = 0.0; + z = 0.0; + } + + // コンストラクタ + public Vector3d(double px, double py, double pz) { + x = px; + y = py; + z = pz; + } + + public Vector3d(Vector3d v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3d(Tuple3d p) { + x = p.x; + y = p.y; + z = p.z; + } + + public Vector3d(double[] coordinate) { + x = coordinate[0]; + y = coordinate[1]; + z = coordinate[2]; + } + + public Vector3d clone() { + return new Vector3d(this.x, this.y, this.z); + } + + public double dot(Vector3d a) { + return x*a.x + y*a.y + z*a.z; + } + + public void cross(Vector3d a, Vector3d b) { + double x = a.y*b.z - a.z*b.y; + double y = a.z*b.x - a.x*b.z; + double z = a.x*b.y - a.y*b.x; + this.x = x; + this.y = y; + this.z = z; + } + + public double length() { + return Math.sqrt(x*x+y*y+z*z); + } + + public void scale(double s) { + x = x*s; + y = y*s; + z = z*s; + } + + public void normalize() { + double l = length(); + x = x / l; + y = y / l; + z = z / l; + } + + /** v1とv2の和を格納する */ + public void add(Tuple3d v1, Tuple3d v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + } + + /** v1との和を格納する */ + public void add(Tuple3d v1) { + this.x += v1.x; + this.y += v1.y; + this.z += v1.z; + } + + /** v1とv2の差を格納する */ + public void sub(Tuple3d v1, Tuple3d v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + } + + /** v1との差を格納する */ + public void sub(Tuple3d v1) { + this.x -= v1.x; + this.y -= v1.y; + this.z -= v1.z; + } + + /** Vector3dをセットする */ + public void set(Tuple3d v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + this.z = -z; + } + + /** this = d * axis + p */ + public void scaleAdd(double d, Vector3d axis, Vector3d p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + this.z = d * axis.z + p.z; + } + + public void negate(Vector3d v) { + this.x = -v.x; + this.y = -v.y; + this.z = -v.z; + } + + public double angle(Vector3d v) { + double d = dot(v) / (length() * v.length()); + if (d < -1D) + d = -1D; + if (d > 1.0D) + d = 1.0D; + return Math.acos(d); + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3f.java new file mode 100644 index 0000000..3660319 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector3f.java @@ -0,0 +1,134 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Vector3f { + public float x; + public float y; + public float z; + + // コンストラクタ + public Vector3f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + // コンストラクタ + public Vector3f(float px, float py, float pz) { + x = px; + y = py; + z = pz; + } + + public Vector3f(Vector3f v) { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3f clone() { + return new Vector3f(this.x, this.y, this.z); + } + + public double dot(Vector3f a) { + return x*a.x + y*a.y + z*a.z; + } + + public void cross(Vector3f a, Vector3f b) { + float x = a.y*b.z - a.z*b.y; + float y = a.z*b.x - a.x*b.z; + float z = a.x*b.y - a.y*b.x; + this.x = x; + this.y = y; + this.z = z; + } + + public float length() { + return (float) Math.sqrt(x*x + y*y + z*z); + } + + public void scale(float s) {; + x = x*s; + y = y*s; + z = z*s; + } + + public void normalize() { + float l = length(); + x = x / l; + y = y / l; + z = z / l; + } + + /** v1とv2の和を格納する */ + public void add(Vector3f v1, Vector3f v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + } + + /** v1との和を格納する */ + public void add(Vector3f v1) { + this.x += v1.x; + this.y += v1.y; + this.z += v1.z; + } + + /** v1とv2の差を格納する */ + public void sub(Vector3f v1, Vector3f v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + } + + /** v1との差を格納する */ + public void sub(Vector3f v1) { + this.x -= v1.x; + this.y -= v1.y; + this.z -= v1.z; + } + + /** Vector3dをセットする */ + public void set(Vector3f v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + + /** 符号を反転する */ + public void negate() { + this.x = -x; + this.y = -y; + this.z = -z; + } + + /** this = d * axis + p */ + public void scaleAdd(float d, Vector3f axis, Vector3f p) { + this.x = d * axis.x + p.x; + this.y = d * axis.y + p.y; + this.z = d * axis.z + p.z; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public void setZ(float z) { + this.z = z; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4d.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4d.java new file mode 100644 index 0000000..49e23c0 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4d.java @@ -0,0 +1,184 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Vector4d { + public double x; + public double y; + public double z; + public double w; + + // コンストラクタ + public Vector4d() { + x = 0.0; + y = 0.0; + z = 0.0; + w = 0.0; + } + + // コンストラクタ + public Vector4d(double px, double py, double pz, double pw) { + x = px; + y = py; + z = pz; + w = pw; + } + + public Vector4d(Vector4d v) { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + } + + public void set(double x2, double y2, double z2, double w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + private void set(double[] t) { + x = t[0]; + y = t[1]; + z = t[2]; + w = t[3]; + } + + public Vector4d clone() { + return new Vector4d(x, y, z, w); + } + + /** 絶対値を返す */ + final void absolute() { + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + w = Math.abs(w); + } + + final double angle(Vector4d v1) { + double vDot = this.dot(v1) / (this.length() * v1.length()); + if (vDot < -1.0) + vDot = -1.0; + if (vDot > 1.0) + vDot = 1.0; + return ((double) (Math.acos(vDot))); + } + + public final double dot(Vector4d v1) { + return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w); + } + + public final double length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + + this.w * this.w); + } + + final void clamp(double min, double max) { + if (x > max) { + x = max; + } else if (x < min) { + x = min; + } + + if (y > max) { + y = max; + } else if (y < min) { + y = min; + } + + if (z > max) { + z = max; + } else if (z < min) { + z = min; + } + + if (w > max) { + w = max; + } else if (w < min) { + w = min; + } + } + + final void clampMax(double max) { + if (x > max) + x = max; + if (y > max) + y = max; + if (z > max) + z = max; + if (w > max) + w = max; + } + + final void clampMin(double min) { + if (x < min) + x = min; + if (y < min) + y = min; + if (z < min) + z = min; + if (w < min) + w = min; + } + + final void normalize() { + double norm; + + norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z + * this.z + this.w * this.w); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } + + final void normalize(Vector4d v1) { + double norm; + + norm = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w + * v1.w); + this.x = v1.x * norm; + this.y = v1.y * norm; + this.z = v1.z * norm; + this.w = v1.w * norm; + } + + final void get(double[] t) { + t[0] = this.x; + t[1] = this.y; + t[2] = this.z; + t[3] = this.w; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public double getW() { + return w; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setW(double w) { + this.w = w; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4f.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4f.java new file mode 100644 index 0000000..272bfed --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/Vector4f.java @@ -0,0 +1,183 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class Vector4f { + public float x; + public float y; + public float z; + public float w; + + // コンストラクタ + public Vector4f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 0.0f; + } + + // コンストラクタ + public Vector4f(float px, float py, float pz, float pw) { + x = px; + y = py; + z = pz; + w = pw; + } + + public Vector4f(Vector4f v) { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + } + + public void set(float x2, float y2, float z2, float w2) { + x = x2; + y = y2; + z = z2; + w = w2; + } + + private void set(float[] t) { + x = t[0]; + y = t[1]; + z = t[2]; + w = t[3]; + } + + public Vector4f clone() { + return new Vector4f(x, y, z, w); + } + + /** 絶対値を返す */ + final void absolute() { + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + w = Math.abs(w); + } + + final double angle(Vector4f v1) { + double vDot = this.dot(v1) / (this.length() * v1.length()); + if (vDot < -1.0) + vDot = -1.0; + if (vDot > 1.0) + vDot = 1.0; + return ((double) (Math.acos(vDot))); + } + + public final double dot(Vector4f v1) { + return (this.x * v1.x + this.y * v1.y + this.z * v1.z + this.w * v1.w); + } + + public final double length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + + this.w * this.w); + } + + final void clamp(float min, float max) { + if (x > max) { + x = max; + } else if (x < min) { + x = min; + } + + if (y > max) { + y = max; + } else if (y < min) { + y = min; + } + + if (z > max) { + z = max; + } else if (z < min) { + z = min; + } + + if (w > max) { + w = max; + } else if (w < min) { + w = min; + } + } + + final void clampMax(float max) { + if (x > max) + x = max; + if (y > max) + y = max; + if (z > max) + z = max; + if (w > max) + w = max; + } + + final void clampMin(float min) { + if (x < min) + x = min; + if (y < min) + y = min; + if (z < min) + z = min; + if (w < min) + w = min; + } + + final void normalize() { + double norm; + + norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z + * this.z + this.w * this.w); + this.x *= norm; + this.y *= norm; + this.z *= norm; + this.w *= norm; + } + + final void normalize(Vector4f v1) { + float norm; + + norm = 1.0f / (float) Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z + v1.w * v1.w); + this.x = v1.x * norm; + this.y = v1.y * norm; + this.z = v1.z * norm; + this.w = v1.w * norm; + } + + final void get(float[] t) { + t[0] = this.x; + t[1] = this.y; + t[2] = this.z; + t[3] = this.w; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } + + public float getW() { + return w; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + public void setZ(float z) { + this.z = z; + } + + public void setW(float w) { + this.w = w; + } +} diff --git a/src/main/java/org/ntlab/radishforandroidstudio/java3d/View.java b/src/main/java/org/ntlab/radishforandroidstudio/java3d/View.java new file mode 100644 index 0000000..3480a02 --- /dev/null +++ b/src/main/java/org/ntlab/radishforandroidstudio/java3d/View.java @@ -0,0 +1,8 @@ +package org.ntlab.radishforandroidstudio.java3d; + +public class View { + + public View() { + + } +}