Skip to content

Commit 00aed52

Browse files
committed
Cleaned up version of collada pickray intersection code.
1 parent 4b49140 commit 00aed52

File tree

8 files changed

+413
-64
lines changed

8 files changed

+413
-64
lines changed

examples/Collada.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
* PDF found in code directory.
2727
*/
2828
/**
29-
* Illustrates how to load and display a Collada 3D model onto the globe.
29+
* Illustrates how to load and display a Collada 3D model onto the globe. Also shows how to calculate
30+
* intersection points when you click on the model.
3031
*/
3132

3233
requirejs(['./WorldWindShim',
@@ -79,13 +80,18 @@ requirejs(['./WorldWindShim',
7980
duckScene = scene;
8081
});
8182

83+
// Add place marks to intersection points with the ray extending from the eye point when the
84+
// model is clicked.
8285
var placemarkAttributes = new WorldWind.PlacemarkAttributes(null);
8386
placemarkAttributes.imageScale = 1;
8487
placemarkAttributes.imageColor = WorldWind.Color.RED;
8588
placemarkAttributes.labelAttributes.color = WorldWind.Color.YELLOW;
8689
placemarkAttributes.drawLeaderLine = true;
8790
placemarkAttributes.leaderLineAttributes.outlineColor = WorldWind.Color.RED;
8891
placemarkAttributes.imageSource = WorldWind.configuration.baseUrl + "images/crosshair.png";
92+
var closestPlacemarkAttributes = new WorldWind.PlacemarkAttributes(placemarkAttributes);
93+
closestPlacemarkAttributes.imageColor = WorldWind.Color.GREEN;
94+
closestPlacemarkAttributes.leaderLineAttributes.outlineColor = WorldWind.Color.GREEN;
8995
var handleClick = function (o) {
9096
if (duckScene == null) {
9197
return;
@@ -98,7 +104,11 @@ requirejs(['./WorldWindShim',
98104
for (var i = 0, len = intersections.length; i < len; i++) {
99105
var placemark = new WorldWind.Placemark(intersections[i], true, null);
100106
placemark.altitudeMode = WorldWind.ABSOLUTE;
101-
placemark.attributes = placemarkAttributes;
107+
if (i == 0) {
108+
placemark.attributes = closestPlacemarkAttributes;
109+
} else {
110+
placemark.attributes = placemarkAttributes;
111+
}
102112
placemarkLayer.addRenderable(placemark);
103113
}
104114
}

karma.conf.js

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,74 @@
11
// Karma configuration
22
// Generated on Thu Feb 04 2016 14:07:41 GMT+0100 (CET)
33

4-
module.exports = function(config) {
5-
config.set({
4+
module.exports = function (config) {
5+
config.set({
66

7-
// base path that will be used to resolve all patterns (eg. files, exclude)
8-
basePath: '',
7+
// base path that will be used to resolve all patterns (eg. files, exclude)
8+
basePath: '',
99

1010

11-
// frameworks to use
12-
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13-
frameworks: ['jasmine', 'requirejs'],
11+
// frameworks to use
12+
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13+
frameworks: ['jasmine', 'requirejs'],
1414

1515

16-
// list of files / patterns to load in the browser
17-
files: [
18-
'test/test-main.js',
19-
{pattern: 'test/**/*.test.js', included: false},
20-
{pattern: 'src/**/*.js', included: false},
21-
{pattern: 'examples/data/KML_Samples.kml', included: false},
22-
{pattern: 'test/formats/geotiff/*.tif', included: false},
23-
{pattern: 'test/ogc/wcs/*.xml', included: false},
24-
{pattern: 'test/formats/aaigrid/*.asc', included: false}
25-
],
16+
// list of files / patterns to load in the browser
17+
files: [
18+
'test/test-main.js',
19+
{pattern: 'test/**/*.test.js', included: false},
20+
{pattern: 'src/**/*.js', included: false},
21+
{pattern: 'examples/data/KML_Samples.kml', included: false},
22+
{pattern: 'test/formats/geotiff/*.tif', included: false},
23+
{pattern: 'test/ogc/wcs/*.xml', included: false},
24+
{pattern: 'test/formats/aaigrid/*.asc', included: false},
25+
{pattern: 'test/formats/collada/*.dae', included: false}
26+
],
2627

2728

28-
// list of files to exclude
29-
exclude: [
30-
],
29+
// list of files to exclude
30+
exclude: [],
3131

3232

33-
// preprocess matching files before serving them to the browser
34-
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
35-
preprocessors: {
36-
},
33+
// preprocess matching files before serving them to the browser
34+
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
35+
preprocessors: {},
3736

3837

39-
// test results reporter to use
40-
// possible values: 'dots', 'progress'
41-
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
42-
reporters: ['progress'],
38+
// test results reporter to use
39+
// possible values: 'dots', 'progress'
40+
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
41+
reporters: ['progress'],
4342

4443

45-
// web server port
46-
port: 9876,
44+
// web server port
45+
port: 9876,
4746

4847

49-
// enable / disable colors in the output (reporters and logs)
50-
colors: true,
48+
// enable / disable colors in the output (reporters and logs)
49+
colors: true,
5150

5251

53-
// level of logging
54-
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
55-
logLevel: config.LOG_INFO,
52+
// level of logging
53+
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
54+
logLevel: config.LOG_INFO,
5655

5756

58-
// enable / disable watching file and executing tests whenever any file changes
59-
autoWatch: true,
57+
// enable / disable watching file and executing tests whenever any file changes
58+
autoWatch: true,
6059

6160

62-
// start these browsers
63-
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
64-
browsers: ['PhantomJS'],
61+
// start these browsers
62+
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
63+
browsers: ['PhantomJS'],
6564

6665

67-
// Continuous Integration mode
68-
// if true, Karma captures browsers, runs the tests and exits
69-
singleRun: false,
66+
// Continuous Integration mode
67+
// if true, Karma captures browsers, runs the tests and exits
68+
singleRun: false,
7069

71-
// Concurrency level
72-
// how many browser should be started simultaneous
73-
concurrency: Infinity
74-
})
70+
// Concurrency level
71+
// how many browser should be started simultaneous
72+
concurrency: Infinity
73+
})
7574
};

src/formats/collada/ColladaScene.js

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,7 @@ define([
7474
}
7575

7676
Renderable.call(this);
77-
78-
this._currentData = {
79-
expired: true,
80-
transformedPoints: []
81-
};
77+
this.resetCurrentData();
8278
// Documented in defineProperties below.
8379
this._position = position;
8480

@@ -516,6 +512,14 @@ define([
516512

517513
});
518514

515+
// Internal. Resets the cache of calculated data that doesn't need to be recomputed each time.
516+
ColladaScene.prototype.resetCurrentData = function () {
517+
this._currentData = {
518+
expired: true,
519+
transformedPoints: []
520+
};
521+
};
522+
519523
// Internal. Intentionally not documented.
520524
ColladaScene.prototype.setSceneData = function (sceneData) {
521525
if (sceneData) {
@@ -622,6 +626,7 @@ define([
622626
return this;
623627
};
624628

629+
// Internal. Calculates the transformed cartesian coordinates of a mesh.
625630
ColladaScene.prototype.computeTransformedPoints = function (mesh) {
626631
var vtxs = mesh.vertices;
627632
var points = [];
@@ -646,6 +651,15 @@ define([
646651
return points;
647652
};
648653

654+
/**
655+
* Calculates the intersection positions of a given ray with this scene.
656+
* @param {Globe} globe The globe to use for translating points to positions.
657+
* @param {Line} pointRay The ray to test for intersections.
658+
* @param {Position[]} results An array to hold the results if any. This list is sorted in ascending order by
659+
* distance from the eyepoint. The closest intersection will be the 0th element of the list.
660+
* @returns {Boolean} true if any intersections are found.
661+
* @throws {ArgumentError} if any of the arguments are not supplied.
662+
*/
649663
ColladaScene.prototype.computePointIntersections = function (globe, pointRay, results) {
650664
if (!globe) {
651665
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ColladaScene",
@@ -663,10 +677,12 @@ define([
663677
"computePointIntersections", "missingResults"));
664678
}
665679

680+
var eyePoint = pointRay.origin;
666681
var computeTransforms = this._currentData.transformedPoints.length <= this._entities.length;
667682
if (computeTransforms) {
668683
this._currentData.transformedPoints = [];
669684
}
685+
var eyeDists = [];
670686
for (var i = 0, len = this._entities.length; i < len; i++) {
671687
var mesh = this._entities[i].mesh;
672688
if (computeTransforms) {
@@ -679,7 +695,20 @@ define([
679695
var position = new Position(0, 0, 0);
680696
globe.computePositionFromPoint(intersectionPoints[j][0],
681697
intersectionPoints[j][1], intersectionPoints[j][2], position);
682-
results.push(position);
698+
// sorted insert
699+
var jEyeDist = intersectionPoints[j].distanceTo(eyePoint);
700+
var inserted = false;
701+
for (var k = 0, eLen = eyeDists.length; k < eLen && !inserted; k++) {
702+
if (jEyeDist < eyeDists[k]) {
703+
results.splice(k, 0, position);
704+
eyeDists.splice(k, 0, jEyeDist);
705+
inserted = true;
706+
}
707+
}
708+
if (!inserted) {
709+
results.push(position);
710+
eyeDists.push(jEyeDist);
711+
}
683712
}
684713
}
685714
}
@@ -708,7 +737,7 @@ define([
708737
// Internal. Intentionally not documented.
709738
ColladaScene.prototype.beginDrawing = function (dc) {
710739
if (this._currentData.expired) {
711-
this._currentData.transformedPoints = [];
740+
this.resetCurrentData();
712741
}
713742
var gl = dc.currentGlContext;
714743
var gpuResourceCache = dc.gpuResourceCache;

src/geom/Matrix.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,8 +1500,7 @@ define([
15001500
for (j = ii; j <= i - 1; j += 1) {
15011501
sum -= A[i][j] * b[j];
15021502
}
1503-
}
1504-
else if (sum != 0.0) {
1503+
} else if (sum != 0.0) {
15051504
ii = i;
15061505
}
15071506

@@ -1548,8 +1547,7 @@ define([
15481547

15491548
if (big == 0.0) {
15501549
return 0.0; // Matrix is singular if the entire row contains zero.
1551-
}
1552-
else {
1550+
} else {
15531551
vv[i] = 1.0 / big;
15541552
}
15551553
}
@@ -1928,6 +1926,17 @@ define([
19281926
return true;
19291927
};
19301928

1929+
/**
1930+
* Returns a string representation of this matrix.
1931+
* @returns {String} A string representation of this matrix.
1932+
*/
1933+
Matrix.prototype.toString = function () {
1934+
return "(" + this[0] + ", " + this[1] + ", " + this[2] + ", " + this[3] + ")\n" +
1935+
"(" + this[4] + ", " + this[5] + ", " + this[6] + ", " + this[7] + ")\n" +
1936+
"(" + this[8] + ", " + this[9] + ", " + this[10] + ", " + this[11] + ")\n" +
1937+
"(" + this[12] + ", " + this[13] + ", " + this[14] + ", " + this[15] + ")";
1938+
};
1939+
19311940
return Matrix;
19321941
});
19331942

src/shapes/PlacemarkAttributes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ define([
6161
this._imageScale = attributes ? attributes._imageScale : 1;
6262
this._imageSource = attributes ? attributes._imageSource : null;
6363
this._depthTest = attributes ? attributes._depthTest : true;
64-
this._labelAttributes = attributes ? attributes._labelAttributes : new TextAttributes(null);
64+
this._labelAttributes = attributes ? new TextAttributes(attributes._labelAttributes) : new TextAttributes(null);
6565
this._drawLeaderLine = attributes ? attributes._drawLeaderLine : false;
66-
this._leaderLineAttributes = attributes ? attributes._leaderLineAttributes : new ShapeAttributes(null);
66+
this._leaderLineAttributes = attributes ? new ShapeAttributes(attributes._leaderLineAttributes) : new ShapeAttributes(null);
6767

6868
/**
6969
* Indicates whether this object's state key is invalid. Subclasses must set this value to true when their

src/util/WWMath.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,16 @@ define([
258258
}
259259
},
260260

261+
/**
262+
* Computes the Cartesian intersection point(s) of a specified line with a non-indexed list of
263+
* triangle vertices.
264+
* @param {Line} line The line for which to compute the intersection(s).
265+
* @param {Vec3[]} points The list of triangle vertices arranged such that each
266+
* 3-tuple, (i,i+1,i+2), specifies a triangle.
267+
* @param {Vec3[]} results The Cartesian intersection point(s) if any.
268+
* @returns {boolean} true if the line intersects any triangle, otherwise false
269+
* @throws {ArgumentError} If any of the arguments is not supplied.
270+
*/
261271
computeTriangleListIntersection: function (line, points, results) {
262272
if (!line) {
263273
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "WWMath",

test/formats/collada/ColladaScene.test.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,20 @@
2626
* PDF found in code directory.
2727
*/
2828
define([
29+
'src/formats/collada/ColladaScene',
30+
'test/CustomMatchers.test',
31+
'src/globe/ElevationModel',
32+
'src/globe/Globe',
33+
'src/geom/Line',
34+
'src/geom/Matrix',
2935
'src/geom/Position',
30-
'src/formats/collada/ColladaScene'
31-
], function (Position, ColladaScene) {
36+
'src/projections/ProjectionWgs84',
37+
'src/geom/Vec3'
38+
], function (ColladaScene, CustomMatchers, ElevationModel, Globe, Line, Matrix, Position, ProjectionWgs84, Vec3) {
3239
"use strict";
3340

3441
describe("ColladaScene calculation and data manipulation testing", function () {
42+
3543
it("Should properly calculate new normals and create proper vertex order", function () {
3644
var indices = [0, 1, 2, 3, 2, 1, 4, 5, 6, 7, 6, 5, 8, 9, 10, 11, 10, 9, 12, 13, 14, 15, 14, 13, 16, 17, 18,
3745
19, 18, 17, 20, 21, 22, 23, 22, 21];
@@ -92,5 +100,44 @@ define([
92100
expect(mesh.uvs[i]).toBe(expectedUvs[i]);
93101
}
94102
});
103+
it("Should properly compute intersection points with a ray", function () {
104+
var colladaLoader = new WorldWind.ColladaLoader(new Position(44, -96, 10000));
105+
colladaLoader.init({dirPath: '../base/test/formats/collada/'});
106+
colladaLoader.load('bad_normals.dae', function (scene) {
107+
scene.scale = 5000;
108+
var transformation = new Matrix(-522.6423163382683, 3454.283622726456, -3576.9777274867624, -4577455.847120033,
109+
-5.898059818402838e-13, 3596.6807208022315, 3473.3107826121095, 4415038.196148923,
110+
4972.609476841367, 363.05983855741573, -375.9555086098537, -481109.9962739506,
111+
0, 0, 0, 1);
112+
scene._transformationMatrix = transformation;
113+
var origin = new Vec3(-12416258.178691395, 10375578.62866234, -2479066.981438789);
114+
var direction = new Vec3(0.8649427998779189, -0.4976765198118716, 0.06474592317119227);
115+
var pointRay = new Line(origin, direction);
116+
var intersections = [];
117+
var globe = new Globe(new WorldWind.ElevationModel(), new WorldWind.ProjectionWgs84());
118+
var intersectionsFound = scene.computePointIntersections(globe, pointRay, intersections);
119+
expect(intersectionsFound).toBe(false);
120+
expect(intersections.length).toBe(0);
121+
origin = new Vec3(-5117511.089956183, 4226332.17224274, -193896.81490928633);
122+
direction = new Vec3(0.862977304871313, 0.24683805077319543, -0.4408414090889536);
123+
pointRay = new Line(origin, direction);
124+
intersectionsFound = scene.computePointIntersections(globe, pointRay, intersections);
125+
expect(intersectionsFound).toBe(true);
126+
expect(intersections.length).toBe(2);
127+
var i1 = new Position(43.088776730634535, -95.18631436743668, 29685.22045946613);
128+
var i2 = new Position(43.30646919618773, -95.39472743244926, 15659.079434582456);
129+
expect(intersections[0]).toBeCloseToPosition(i1, 7, 7, 7);
130+
expect(intersections[1]).toBeCloseToPosition(i2, 7, 7, 7);
131+
expect(function () {
132+
scene.computePointIntersections(null, pointRay, intersections);
133+
}).toThrow();
134+
expect(function () {
135+
scene.computePointIntersections(globe, null, intersections);
136+
}).toThrow();
137+
expect(function () {
138+
scene.computePointIntersections(globe, pointRay, null);
139+
}).toThrow();
140+
});
141+
});
95142
});
96143
});

0 commit comments

Comments
 (0)