View Javadoc

1   /*
2    * Copyright (C) 2013 AMIS research group, Faculty of Mathematics and Physics, Charles University in Prague, Czech Republic
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh;
18  
19  import java.io.BufferedReader;
20  import java.io.File;
21  import java.io.FileNotFoundException;
22  import java.io.FileReader;
23  import java.io.IOException;
24  import java.io.ObjectInputStream;
25  import java.io.Serializable;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Random;
29  import java.util.logging.Logger;
30  
31  import javax.vecmath.Vector2d;
32  import javax.vecmath.Vector3d;
33  
34  import math.geom2d.Point2D;
35  import math.geom2d.line.StraightLine2D;
36  import math.geom3d.Point3D;
37  import math.geom3d.Vector3D;
38  import math.geom3d.line.StraightLine3D;
39  import math.geom3d.plane.Plane3D;
40  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
41  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
42  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.BSPRayInfoContainer;
43  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.AutoTraceRay;
44  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.AutoTraceRayMessage;
45  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
46  import cz.cuni.amis.utils.ExceptionToString;
47  
48  /**
49   * Class containing complete data with the geometry of the environment
50   * It is useful for steering.
51   * It is part of NavMesh class.
52   * 
53   * @author Jakub Tomek
54   */
55  public class LevelGeometry implements Serializable {
56  	
57  	/**
58  	 * Auto-generated.
59  	 */
60  	private static final long serialVersionUID = -8146957816661176041L;
61  
62  	public static class RaycastResult {
63  		public Location from;
64  		public Location to;
65  		public Location rayVector;
66  		public Location hitLocation;
67  		public Vector3d hitNormal;
68  		public boolean hit;
69  		public double hitDistance;
70  		public List<LevelGeometryBSPNode> travelledNodes = new ArrayList<LevelGeometryBSPNode>();
71  		public List<Location> rayPoints = new ArrayList<Location>();
72  		/**
73  		 * Index into {@link LevelGeometry#triangles}.
74  		 */
75  		public int hitTriangle;
76  		
77  		public RaycastResult(Location from, Location to) {
78  			super();
79  			this.from = from;
80  			this.to = to;
81  			this.hit = false;
82  			rayVector = to.sub(from);
83  		}		
84  	}
85      
86  	private transient Logger log; 
87  	
88  	private transient boolean loaded = false;
89  	
90  	public static Random random = new Random();
91  	
92      public ArrayList<double[]> verts = new ArrayList<double[]>();
93      public ArrayList<int[]>  triangles = new ArrayList<int[]>();
94      private LevelGeometryBSPNode bspTree;
95      private LevelGeometryBSPNode biggestLeafInTree;
96      public int leafCount = 0;
97      
98      // boundries of the space
99      public double minX = Double.POSITIVE_INFINITY;
100     public double maxX = Double.NEGATIVE_INFINITY;
101     public double minY = Double.POSITIVE_INFINITY;
102     public double maxY = Double.NEGATIVE_INFINITY;
103     public double minZ = Double.POSITIVE_INFINITY;
104     public double maxZ = Double.NEGATIVE_INFINITY;
105 
106     /**
107      * Constructor creates the object from a file defined by map name and path which is in constants
108      * @param mapName 
109      */
110     public LevelGeometry(Logger log) {
111         this.log = log;
112         if (this.log == null) {
113         	this.log = new LogCategory("LevelGeometry");
114         }
115     }
116     
117     public boolean isLoaded() {
118     	return loaded;
119     }
120     
121     public LevelGeometryBSPNode getBSPRoot() {
122     	return bspTree;
123     }
124     
125     public void clear() {
126     	loaded = false;
127     	
128     	verts = new ArrayList<double[]>();
129         triangles = new ArrayList<int[]>();
130         bspTree = null;
131         biggestLeafInTree = null;
132         leafCount = 0;
133         
134         minX = Double.POSITIVE_INFINITY;
135         maxX = Double.NEGATIVE_INFINITY;
136         minY = Double.POSITIVE_INFINITY;
137         maxY = Double.NEGATIVE_INFINITY;
138         minZ = Double.POSITIVE_INFINITY;
139         maxZ = Double.NEGATIVE_INFINITY; 
140     }
141     
142     public boolean load(String mapName) throws FileNotFoundException, IOException, Exception { 
143     	if (loaded) {
144     		clear();
145     	}
146     	
147         // vertices are indexed from 1. Put something to 0
148         verts.add(new double[0]);
149         
150         double scale;
151         double[] centre;
152         String fileName, line;
153         File file;
154         BufferedReader br;
155         
156         // read scale
157         fileName = NavMeshConstants.pureLevelGeometryReadDir + "\\" + mapName + ".scale";
158         file = new File(fileName);
159         if (!file.exists()) {
160         	log.warning("LevelGeometry .scale file does not exist at: " + file.getAbsolutePath());
161         	return false;
162         }
163         br = new BufferedReader(new FileReader(file));
164         line = br.readLine();
165         scale = Double.parseDouble(line);
166         
167         // read centre
168         fileName = NavMeshConstants.pureLevelGeometryReadDir + "\\" + mapName + ".centre";
169         file = new File(fileName);
170         if (!file.exists()) {
171         	log.warning("LevelGeometry .centre file does not exist at: " + file.getAbsolutePath());
172         	return false;
173         }
174         br = new BufferedReader(new FileReader(file));
175         line = br.readLine();
176         String[] sc = line.split("[ \\t]");
177         centre = new double[3];
178         for(int i=0; i<3; i++) {
179             centre[i] = Double.parseDouble(sc[i]);
180         }
181         
182         // read all vertices and triangles from file
183         fileName = NavMeshConstants.pureLevelGeometryReadDir + "\\" + mapName + ".obj";
184         file = new File(fileName);
185         if (!file.exists()) {
186         	log.warning("LevelGeometry .obj file does not exist at: " + file.getAbsolutePath());
187         	return false;
188         }
189         br = new BufferedReader(new FileReader(file));     
190         while((line = br.readLine()) != null) {
191             String[] words = line.split("[ \\t]");
192             if(words[0].equals("v")) {
193                 double[] v = new double[3];
194                 v[0] = Double.parseDouble(words[1])*scale + centre[0];
195                 v[1] = Double.parseDouble(words[3])*scale + centre[1];
196                 v[2] = Double.parseDouble(words[2])*scale + centre[2];
197                 verts.add(v);
198                 
199                 // check the boundries
200                 if(v[0] < minX) minX = v[0];
201                 if(v[0] > maxX) maxX = v[0];
202                 if(v[1] < minY) minY = v[1];
203                 if(v[1] > maxY) maxY = v[1];
204                 if(v[2] < minZ) minZ = v[2];
205                 if(v[2] > maxZ) maxZ = v[2];                
206                 
207             }
208             if(words[0].equals("f")) {
209                 int[] t = new int[3];
210                 t[0] = Integer.parseInt(words[1]);
211                 t[1] = Integer.parseInt(words[2]);
212                 t[2] = Integer.parseInt(words[3]);
213                 triangles.add(t);
214             }
215         }
216         
217         // create a BSP tree structure
218         resetBSPTree();
219         
220         loaded = true;
221         
222         return true;
223     }
224     
225 	private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
226     	input.defaultReadObject();
227     	log = new LogCategory("LevelGeometry");
228     	loaded = true;
229     }
230     
231 //    public void draw(UT2004Server server, Location color) {
232 //        for(int tId = 0; tId < triangles.size(); tId++) {
233 //            DrawStayingDebugLines d = new DrawStayingDebugLines();
234 //            String lines = this.triangleToDebugString(tId);
235 //            d.setVectors(lines);
236 //            d.setColor(new Location(255, 255, 255));
237 //            d.setClearAll(tId==0);                
238 //            server.getAct().act(d); 
239 //        }
240 //    }
241 //
242 //    public void drawOneTriangle(UT2004Server server, int tId, Location color) {
243 //        DrawStayingDebugLines d = new DrawStayingDebugLines();
244 //        String lines = this.triangleToDebugString(tId);
245 //        d.setVectors(lines);
246 //        d.setColor(color);
247 //        d.setClearAll(false);                
248 //        server.getAct().act(d);                           
249 //    }
250 //
251 //    private String triangleToDebugString(int tId) {
252 //        StringBuilder result = new StringBuilder("");      
253 //        // projdeme vsechny polygony a vykreslime caru vzdy z vrcholu n do n+1
254 //        int[] t = (int[]) triangles.get(tId);
255 //        for(int j = 0; j<t.length; j++) {
256 //            if(result.length()>0) result.append(";");
257 //            // ziskame vrcholy v1 a v2 mezi kterymi vykreslime caru
258 //            double[] v1,v2;
259 //            v1 = (double[]) verts.get(t[j]);
260 //            if(j==t.length-1) v2 = (double[]) verts.get(t[0]);
261 //            else v2 = (double[]) verts.get(t[j+1]);
262 //            // pridani cary
263 //            result.append(v1[0]+","+v1[1]+","+v1[2]+";"+v2[0]+","+v2[1]+","+v2[2]);
264 //        }     
265 //        return result.toString(); 
266 //    }
267 
268     /**
269      * Gets a new BSPTree for this geometry
270      */
271     private void resetBSPTree() throws Exception {
272         log.info("Creating BSP tree...");
273         bspTree = new LevelGeometryBSPNode(this, null, minX, maxX, minY, maxY, minZ, maxZ);
274         for(int i = 0; i<triangles.size(); i++) bspTree.triangles.add(i);
275         biggestLeafInTree = null;
276         bspTree.build();
277         log.info("BSP tree is done building.");
278         log.info("Biggest leaf has " + biggestLeafInTree.triangles.size() + " triangles.");
279         log.info("BSP tree has " +leafCount+ " leaves");
280         bspTree.cleanInnerNodes();      
281     }
282 
283     /**
284      * Sets the biggest leag in tree
285      * @param node 
286      */
287     void setBiggestLeafInTree(LevelGeometryBSPNode node) {
288         biggestLeafInTree = node;
289     }
290 
291     int getNumberOfTrianglesInBiggestLeaf() {
292         if(biggestLeafInTree==null) return 0;
293         else return biggestLeafInTree.triangles.size();
294     }
295 
296     /**
297      * Returns the node of BSP leaf which contains this location 
298      * Walk througt BSP tree compating the coordinates
299      * @param from
300      * @return 
301      */
302     private LevelGeometryBSPNode getBSPLeaf(Location location) {
303         double x = location.x;
304         double y = location.y;
305         double z = location.z;
306         
307         LevelGeometryBSPNode node = this.bspTree;
308         //check if we are in
309         if(x>node.maxX || x<node.minX) return null;
310         if(y>node.maxY || y<node.minY) return null;
311         if(z>node.maxZ || z<node.minZ) return null;
312         
313         // we want a leaf!
314         while(node.left!=null) {
315             // in which half are we?
316             switch(node.sepDim) {
317                 case 0:
318                     if(x<node.sepVal) node = node.left;
319                     else node = node.right;
320                     break;
321                 case 1:
322                     if(y<node.sepVal) node = node.left;
323                     else node = node.right;                    
324                     break;
325                 case 2:
326                     if(z<node.sepVal) node = node.left;
327                     else node = node.right;                    
328                     break;
329                 default: throw new UnsupportedOperationException("This should not happen!");
330             }
331         }
332         return node;
333     }
334     
335     /**
336      * The most important method on this class.
337      * It computes the information about collision of the given ray and this geometry
338      * @param self
339      * @param rayInfo
340      * @return 
341      */
342     public AutoTraceRay getAutoTraceRayMessage(Self self, BSPRayInfoContainer rayInfo) {
343 
344         // lets update the direction vector according to agent rotation  
345         Vector3d direction = rayInfo.direction;
346         direction.normalize();
347         
348         // angle up/down
349         double upDownAngle = Math.asin(direction.getZ()/1);
350         
351         // we transform the ray vector to polar coordinates 
352         // -32768 - 32767 total is 65536
353         double UTFullAngle = 65536;
354         double UTHalfAngle = UTFullAngle/2;
355         double UTQuarterAngle = UTHalfAngle/2;        
356         // yaw:  
357         double rayYaw;
358         double x = rayInfo.direction.x;
359         double y = rayInfo.direction.y;
360         
361         if(x==0 && y==0) {
362             direction = new Vector3d(0,0,1);
363         } else {
364             rayYaw = NavMeshConstants.transform2DVectorToRotation(new Vector2d(x,y));
365             // now we have the input yaw
366             // let's add adent's rotation to get the final rotation
367             double Yaw = rayYaw + self.getRotation().getYaw();
368             Vector2d vector2d = NavMeshConstants.transformRotationTo2DVector(Yaw);
369             direction.x = vector2d.x;
370             direction.y = vector2d.y;
371             // now we have the direction on x,y.
372             // we must add back the z direction
373             direction.normalize();
374             direction.z = Math.tan(upDownAngle);           
375         }
376         
377         
378         direction.normalize();
379         // last thing with direction: stretch it to the proper length
380         direction.x *= rayInfo.length;
381         direction.y *= rayInfo.length;
382         direction.z *= rayInfo.length;   
383         // The direction is now Done. The agent's rotation is added
384 
385 
386         
387         // now get the from and to locations
388         
389         Location from = self.getLocation(); 
390         Location to = new Location(from.x + direction.x, from.y + direction.y, from.z + direction.z); 
391 
392         // call the recursive function
393         return getAutoTraceRayMessage(from, to, rayInfo);
394     }
395     
396     /**
397      * Recursive function for getting ray collision
398      * @param from
399      * @param to
400      * @param rayInfo
401      * @return 
402      */
403     private AutoTraceRay getAutoTraceRayMessage(Location from, Location to, BSPRayInfoContainer rayInfo) {
404         // the actual 3D line
405         StraightLine3D ray =  new StraightLine3D(from.asPoint3D(), to.asPoint3D());              
406         
407         RaycastResult raycast = raycast(from, to);
408         
409         if (raycast.hit) {
410         	return 
411         			new AutoTraceRayMessage(
412         					rayInfo.unrealId,
413         					from,
414         					to,
415         					false,
416         					rayInfo.floorCorrection,
417         					raycast.hit,
418         					raycast.hitNormal,
419         					raycast.hitLocation,
420         					false,
421         					null
422         			); 
423           
424           } else {
425               // otherwise there is no intersection at all
426               return 
427             		  new AutoTraceRayMessage(
428             				  rayInfo.unrealId,
429             				  from,
430             				  to,
431             				  false,
432             				  rayInfo.floorCorrection,
433             				  false,
434             				  null,
435             				  null,
436             				  false,
437             				  null
438                       ); 
439         }
440     }
441     
442     /**
443      * Recursive function for getting ray collision
444      * @param from
445      * @param to
446      * @param rayInfo
447      * @return 
448      */
449     public RaycastResult raycast(Location from, Location to) {
450     	RaycastResult result = new RaycastResult(from, to);
451     	try {
452     		raycastInner(from, to, result);
453     	} catch (Exception e) {
454     		log.severe(ExceptionToString.process("Failed to raycast: " + from + " -> " + to, e));
455     	}
456     	if (result.hit) {
457     		result.hitDistance = result.hitLocation.getDistance(from.getLocation());
458     	}
459     	return result;
460     }
461     
462     private boolean isValidNumber(Double number) {
463     	if (number == null) return false;
464     	if (number == Double.POSITIVE_INFINITY) return false;
465     	if (number == Double.NEGATIVE_INFINITY) return false;
466     	if (number == Double.NaN) return false;
467     	return true;
468     }
469     
470     private boolean isValidPoint(Point3D point) {
471     	return isValidNumber(point.getX()) && isValidNumber(point.getY()) && isValidNumber(point.getZ());
472     }
473     
474     private void raycastInner(Location from, Location to, RaycastResult result) {
475         // the actual 3D line
476         StraightLine3D ray =  new StraightLine3D(from.asPoint3D(), to.asPoint3D());              
477                 
478         // information abou ray's collision with triangle
479         boolean collisionFound = false;
480         double collisionDistance = Double.POSITIVE_INFINITY;
481         Location hitLocation = null;
482         Vector3d normalVector = null;
483         
484         // now start examining hitting the walls
485         // let's get the BSP leaf
486         LevelGeometryBSPNode leaf = this.getBSPLeaf(from);
487         
488         if (leaf == null) {
489         	// we're pretty far out of the geometry
490         	// TODO: not ideal...
491         	return;
492         }
493         
494         result.travelledNodes.add(leaf);
495         
496         int triangleHit = -1;
497         
498         // now let's examine the ray's collisions with triangles
499         for(Integer tId : leaf.triangles) {
500             int[] triangle = this.triangles.get(tId);
501             double[] v1 = this.verts.get(triangle[0]);
502             double[] v2 = this.verts.get(triangle[1]);
503             double[] v3 = this.verts.get(triangle[2]);
504             Point3D p1 = new Point3D(v1[0],v1[1],v1[2]);
505             Vector3D vector1 = new Vector3D(v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2]);
506             Vector3D vector2 = new Vector3D(v3[0]-v1[0], v3[1]-v1[1], v3[2]-v1[2]);            
507             Plane3D plane = new Plane3D(p1,vector1,vector2);
508             Point3D intersection = plane.getLineIntersection(ray);
509             
510             // is intersection inside triangle?
511             boolean collision = isValidPoint(intersection) && this.isPointInTriangle(intersection, tId);
512             if(!collision) continue;
513             
514             // is the point on the HalfLine FROM->TO?
515             double zero = intersection.getDistance(from.asPoint3D()) + intersection.getDistance(to.asPoint3D()) - from.getDistance(to);
516             collision = Math.abs(zero) < 0.001;
517             if(!collision) continue;
518 
519             // we remember the closest collision
520             double distance = intersection.getDistance(from.asPoint3D());
521             if(distance < collisionDistance) {
522                 collisionFound = true;
523                 collisionDistance = distance;
524                 hitLocation = new Location(intersection.getX(),intersection.getY(),intersection.getZ());
525                 Vector3D normalVector3D = plane.getNormalVector();
526                 normalVector = new Vector3d(normalVector3D.getX(),normalVector3D.getY(),normalVector3D.getZ());
527                 triangleHit = tId;
528             }
529         }
530         
531         // if we found a collision, we collect and return the information about it
532         if(collisionFound) {
533         	result.hitLocation = hitLocation;
534         	result.hitNormal = normalVector;
535         	result.hit = true;
536         	result.hitTriangle = triangleHit;
537             return;
538         }
539         
540         // we did not find a collision. Does the ray continue to different node?
541         boolean out = false;
542         
543         if(to.x > leaf.maxX) {
544             out = true;
545         }
546         if(to.x < leaf.minX) {
547             out = true;
548         }         
549         if(to.y > leaf.maxY) {
550             out = true;
551         }
552         if(to.y < leaf.minY) {
553             out = true;
554         }
555         if(to.z > leaf.maxZ) {
556             out = true;
557         }
558         if(to.z < leaf.minZ) {
559             out = true;
560         }
561         
562         if (!out) {
563         	// NO COLLISION AT ALL
564         	return;
565         }
566         
567         // if we are out, we must continue to the next node
568         // we must find the ray's intersections with the nodee's borders. 
569         // we will take 3 intersection with planes bordering the node on x,y,z and take the clocest one
570         
571         collisionDistance = Double.MAX_VALUE;
572         Point3D collisionPoint = null; 
573         double distance;
574         
575         // 1. x axis
576         if(to.x > from.x) {
577             Point3D p1 = new Point3D(leaf.maxX,0,0);
578             Vector3D vector1 = new Vector3D(0, 1, 0);
579             Vector3D vector2 = new Vector3D(0, 0, 1);
580             Plane3D plane = new Plane3D(p1,vector1,vector2);
581             Point3D intersection = plane.getLineIntersection(ray);
582             if (isValidPoint(intersection)) {
583             	distance = intersection.getDistance(from.asPoint3D());
584 	            // is it the closest one sofar?
585 	            if(distance < collisionDistance) {
586 	                collisionPoint = intersection;
587 	                collisionDistance = distance;
588 	            }
589             }
590         } else {
591             Point3D p1 = new Point3D(leaf.minX,0,0);
592             Vector3D vector1 = new Vector3D(0, 1, 0);
593             Vector3D vector2 = new Vector3D(0, 0, 1);
594             Plane3D plane = new Plane3D(p1,vector1,vector2);
595             Point3D intersection = plane.getLineIntersection(ray);
596             if (isValidPoint(intersection)) {
597             	distance = intersection.getDistance(from.asPoint3D());
598 	            // is it the closest one sofar?
599 	            if(distance < collisionDistance) {
600 	                collisionPoint = intersection;
601 	                collisionDistance = distance;
602 	            }
603             }
604         }
605         
606         // 2. y axis
607         if(to.y > from.y) {
608             Point3D p1 = new Point3D(0, leaf.maxY,0);
609             Vector3D vector1 = new Vector3D(1, 0, 0);
610             Vector3D vector2 = new Vector3D(0, 0, 1);
611             Plane3D plane = new Plane3D(p1,vector1,vector2);
612             Point3D intersection = plane.getLineIntersection(ray);
613             if (isValidPoint(intersection)) {
614             	distance = intersection.getDistance(from.asPoint3D());
615 	            // is it the closest one sofar?
616 	            if(distance < collisionDistance) {
617 	                collisionPoint = intersection;
618 	                collisionDistance = distance;
619 	            }
620             }
621         } else {
622             Point3D p1 = new Point3D(0,leaf.minY,0);
623             Vector3D vector1 = new Vector3D(1, 0, 0);
624             Vector3D vector2 = new Vector3D(0, 0, 1);
625             Plane3D plane = new Plane3D(p1,vector1,vector2);
626             Point3D intersection = plane.getLineIntersection(ray);
627             distance = intersection.getDistance(from.asPoint3D());
628             // is it the closest one sofar?
629             if(distance < collisionDistance) {
630                 collisionPoint = intersection;
631                 collisionDistance = distance;
632             }
633         }
634         
635         // 3. z axis
636         if(to.z > from.z) {
637             Point3D p1 = new Point3D(0,0,leaf.maxZ);
638             Vector3D vector1 = new Vector3D(1, 0, 0);
639             Vector3D vector2 = new Vector3D(0, 1, 0);
640             Plane3D plane = new Plane3D(p1,vector1,vector2);
641             Point3D intersection = plane.getLineIntersection(ray);
642             if (isValidPoint(intersection)) {
643 	            distance = intersection.getDistance(from.asPoint3D());
644 	            // is it the closest one sofar?
645 	            if(distance < collisionDistance) {
646 	                collisionPoint = intersection;
647 	                collisionDistance = distance;
648 	            }
649             }
650         } else {
651             Point3D p1 = new Point3D(0,0,leaf.minZ);
652             Vector3D vector1 = new Vector3D(1, 0, 0);
653             Vector3D vector2 = new Vector3D(0, 1, 0);
654             Plane3D plane = new Plane3D(p1,vector1,vector2);
655             Point3D intersection = plane.getLineIntersection(ray);
656             if (isValidPoint(intersection)) {
657 	            distance = intersection.getDistance(from.asPoint3D());
658 	            // is it the closest one sofar?
659 	            if(distance < collisionDistance) {
660 	                collisionPoint = intersection;
661 	                collisionDistance = distance;
662 	            }
663             }
664         }
665         
666         if (collisionPoint == null) {
667         	// ???
668         	return;
669         }
670         
671         // now we have the collision point at the border of the node.
672         // we will move it a little further, to push it into the node
673         Vector3d direction = to.sub(from).getNormalized().asVector3d();
674         Location newPoint = new Location(collisionPoint.getX()+direction.x,collisionPoint.getY()+direction.y,collisionPoint.getZ()+direction.z);
675         
676         if (from.getDistance(to) < from.getDistance(newPoint)) {
677         	// NO COLLISION
678         	return;
679         }
680         
681         // and get the answer recursively...
682         result.rayPoints.add(newPoint);
683         raycastInner(newPoint, to, result);        
684     }
685 
686 //    /***
687 //     * returns true is point is  inside 3D triangle, assuming it is on the same 2D plane in 3D
688 //     * @param intersection
689 //     * @param tId
690 //     * @return 
691 //     */
692 //    private boolean isPointInsideTriangle(Point3D point3D, Integer tId) {
693 //       boolean result = true;
694 //       int[] triangle = this.triangles.get(tId);
695 //            
696 //       // let's see if the point is inside in 2D projection
697 //       Point2D point2D = new Point2D(point3D.getX(), point3D.getY());
698 //       double rightSide = 0.0;
699 //       double[] v1, v2;
700 //       for(int i = 0; i < triangle.length; i++) {
701 //           v1 = this.verts.get(triangle[i]);
702 //           if(i < triangle.length -1) v2 = this.verts.get(triangle[i+1]);
703 //           else  v2 = this.verts.get(triangle[0]);
704 //           Point2D p1 = new Point2D(v1[0], v1[1]);
705 //           Point2D p2 = new Point2D(v2[0], v2[1]);
706 //           StraightLine2D line = new StraightLine2D(p1, p2);
707 //           double dist = line.getSignedDistance(point2D);
708 //           
709 //           if(rightSide == 0.0) {
710 //               rightSide = Math.signum(dist);
711 //           }
712 //           else {
713 //               if(rightSide * dist < 0) {
714 //                   result = false;
715 //                   break;
716 //               }
717 //           }
718 //       }
719 //       return result;      
720 //    }
721     
722     public boolean isPointInTriangle(Point3D point3D, Integer tId) {
723     	int[] triangle = triangles.get(tId);
724     	
725     	Location p  = new Location(point3D); 
726     	Location a = new Location(verts.get(triangle[0]));
727     	Location b = new Location(verts.get(triangle[1]));
728     	Location c = new Location(verts.get(triangle[2]));
729 
730     	Location v0 = b.sub(a);
731     	Location v1 = c.sub(a);
732     	Location v2 = p.sub(a);
733     	
734     	double d00 = v0.dot(v0);
735     	double d01 = v0.dot(v1);
736     	double d11 = v1.dot(v1);
737     	double d20 = v2.dot(v0);
738     	double d21 = v2.dot(v1);
739     	double denom = d00 * d11 - d01 *d01;
740     	
741     	if (Math.abs(denom) < 0.0000001) return false; // stupid triangle, ignore...
742     	
743     	double v = (d11 * d20 - d01 * d21) / denom;
744         double w = (d00 * d21 - d01 * d20) / denom;
745         double u = 1 - v - w;
746         
747         boolean inside = 0 < u && u < 1 && 0 < v && v < 1 && 0 < w && w < 1;
748     	
749     	if (inside) {
750     		return true;
751     	} else {
752     		return false;
753     	}
754     }
755 
756 	public double[][] getTriangle(int tId) {
757 		int[] triangle = triangles.get(tId);
758 		return new double[][]{ verts.get(triangle[0]), verts.get(triangle[1]), verts.get(triangle[2]) };
759 	}
760     
761 }
762