├── .npmrc
├── CHANGELOG.md
├── dist
├── .DS_Store
└── threebox.css
├── docs
├── gallery.jpg
└── soldieranimation.jpg
├── examples
├── README.md
├── 21-terrain.html
├── images
│ ├── game.jpg
│ ├── line.jpg
│ ├── tube.jpg
│ ├── basic.jpg
│ ├── eiffel.jpg
│ ├── statue.jpg
│ ├── vuejs.jpg
│ ├── animation.jpg
│ ├── azuremaps.jpg
│ ├── fixedzoom.jpg
│ ├── logistics.jpg
│ ├── mercator.jpg
│ ├── object3D.jpg
│ ├── raycaster.jpg
│ ├── terrain.jpg
│ ├── 3dbuildings.jpg
│ ├── extrusions.jpg
│ ├── multilayer.jpg
│ ├── performance.jpg
│ ├── stylechange.jpg
│ ├── add-3d-model.jpg
│ ├── alignmentTest.jpg
│ ├── buildingshadow.jpg
│ ├── eiffel-tower.png
│ └── statue-of-liberty.png
├── models
│ ├── Soldier.glb
│ ├── plane
│ │ └── plane.glb
│ ├── radar
│ │ └── 34M_17.glb
│ ├── vehicles
│ │ ├── car.glb
│ │ └── truck.glb
│ ├── history
│ │ ├── elephant.glb
│ │ └── triceratops.fbx
│ ├── landmarks
│ │ ├── eiffel.glb
│ │ ├── spaceneedle.glb
│ │ └── libertystatue.glb
│ ├── windmill_a
│ │ ├── windmill_a.bin
│ │ └── textures
│ │ │ └── material_baseColor.png
│ └── Truck.mtl
├── css
│ ├── free-fa-solid-900.woff2
│ ├── threebox.css
│ ├── free-v4-font-face-min.css
│ └── fontawesome.js
├── webfonts
│ └── free-fa-solid-900.woff2
├── config_template.js
├── 01-basic.html
├── 06-object3d.html
├── 02-line.html
├── 03-tube.html
├── 04-mercator.html
├── 10-stylechange.html
├── 17-azuremaps.html
├── 21-multifloor.html
├── 12-add3dmodel.html
├── 18-extrusions.html
├── 05-logistics.html
├── 08-3dbuildings.html
├── 14-buildingshadow.html
├── 07-alignmentTest.html
├── 16-multilayer.html
├── 09-raycaster.html
├── 19-fixedzoom.html
├── 15-performance.html
├── 20-game.html
└── 11-animation.html
├── tests
├── unit
│ ├── camera.test.js
│ ├── validate.test.js
│ ├── object.test.js
│ ├── material.test.js
│ └── utilities.test.js
├── threebox-tests.js
└── threebox-tests.html
├── exports.js
├── src
├── objects
│ ├── loaders
│ │ └── GLTFLoader.js
│ ├── tooltip.js
│ ├── label.js
│ ├── sphere.js
│ ├── tube.js
│ ├── Object3D.js
│ ├── extrusion.js
│ ├── LabelRenderer.js
│ ├── loadObj.js
│ ├── CSS2DRenderer.js
│ └── effects
│ │ └── BuildingShadows.js
└── utils
│ ├── ValueGenerator.js
│ ├── constants.js
│ ├── material.js
│ └── validate.js
├── main.js
├── server.stop.js
├── .gitignore
├── .github
├── dependabot.yml
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ ├── bug_report.md
│ └── other-topics-and-questions.md
├── package.json
├── server.js
├── LICENSE.txt
└── README.md
/.npmrc:
--------------------------------------------------------------------------------
1 | scripts-prepend-node-path=true
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/CHANGELOG.md
--------------------------------------------------------------------------------
/dist/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/dist/.DS_Store
--------------------------------------------------------------------------------
/docs/gallery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/docs/gallery.jpg
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/README.md
--------------------------------------------------------------------------------
/tests/unit/camera.test.js:
--------------------------------------------------------------------------------
1 | function cameraTest(instance){
2 |
3 |
4 | }
5 |
6 |
7 |
--------------------------------------------------------------------------------
/examples/21-terrain.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/21-terrain.html
--------------------------------------------------------------------------------
/examples/images/game.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/game.jpg
--------------------------------------------------------------------------------
/examples/images/line.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/line.jpg
--------------------------------------------------------------------------------
/examples/images/tube.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/tube.jpg
--------------------------------------------------------------------------------
/docs/soldieranimation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/docs/soldieranimation.jpg
--------------------------------------------------------------------------------
/examples/images/basic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/basic.jpg
--------------------------------------------------------------------------------
/examples/images/eiffel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/eiffel.jpg
--------------------------------------------------------------------------------
/examples/images/statue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/statue.jpg
--------------------------------------------------------------------------------
/examples/images/vuejs.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/vuejs.jpg
--------------------------------------------------------------------------------
/exports.js:
--------------------------------------------------------------------------------
1 | window.Threebox = require('./src/Threebox.js'),
2 | window.THREE = require('./src/three.js')
3 |
--------------------------------------------------------------------------------
/examples/images/animation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/animation.jpg
--------------------------------------------------------------------------------
/examples/images/azuremaps.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/azuremaps.jpg
--------------------------------------------------------------------------------
/examples/images/fixedzoom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/fixedzoom.jpg
--------------------------------------------------------------------------------
/examples/images/logistics.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/logistics.jpg
--------------------------------------------------------------------------------
/examples/images/mercator.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/mercator.jpg
--------------------------------------------------------------------------------
/examples/images/object3D.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/object3D.jpg
--------------------------------------------------------------------------------
/examples/images/raycaster.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/raycaster.jpg
--------------------------------------------------------------------------------
/examples/images/terrain.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/terrain.jpg
--------------------------------------------------------------------------------
/examples/models/Soldier.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/Soldier.glb
--------------------------------------------------------------------------------
/examples/images/3dbuildings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/3dbuildings.jpg
--------------------------------------------------------------------------------
/examples/images/extrusions.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/extrusions.jpg
--------------------------------------------------------------------------------
/examples/images/multilayer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/multilayer.jpg
--------------------------------------------------------------------------------
/examples/images/performance.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/performance.jpg
--------------------------------------------------------------------------------
/examples/images/stylechange.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/stylechange.jpg
--------------------------------------------------------------------------------
/examples/models/plane/plane.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/plane/plane.glb
--------------------------------------------------------------------------------
/examples/images/add-3d-model.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/add-3d-model.jpg
--------------------------------------------------------------------------------
/examples/images/alignmentTest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/alignmentTest.jpg
--------------------------------------------------------------------------------
/examples/images/buildingshadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/buildingshadow.jpg
--------------------------------------------------------------------------------
/examples/images/eiffel-tower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/eiffel-tower.png
--------------------------------------------------------------------------------
/examples/models/radar/34M_17.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/radar/34M_17.glb
--------------------------------------------------------------------------------
/examples/models/vehicles/car.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/vehicles/car.glb
--------------------------------------------------------------------------------
/examples/models/vehicles/truck.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/vehicles/truck.glb
--------------------------------------------------------------------------------
/src/objects/loaders/GLTFLoader.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/src/objects/loaders/GLTFLoader.js
--------------------------------------------------------------------------------
/examples/css/free-fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/css/free-fa-solid-900.woff2
--------------------------------------------------------------------------------
/examples/models/history/elephant.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/history/elephant.glb
--------------------------------------------------------------------------------
/examples/models/landmarks/eiffel.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/landmarks/eiffel.glb
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | module.exports = exports = {
2 | Threebox: require('./src/Threebox'),
3 | THREE: require('./src/three.js')
4 | }
--------------------------------------------------------------------------------
/examples/images/statue-of-liberty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/images/statue-of-liberty.png
--------------------------------------------------------------------------------
/examples/models/history/triceratops.fbx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/history/triceratops.fbx
--------------------------------------------------------------------------------
/examples/models/landmarks/spaceneedle.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/landmarks/spaceneedle.glb
--------------------------------------------------------------------------------
/examples/models/windmill_a/windmill_a.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/windmill_a/windmill_a.bin
--------------------------------------------------------------------------------
/examples/webfonts/free-fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/webfonts/free-fa-solid-900.woff2
--------------------------------------------------------------------------------
/examples/models/landmarks/libertystatue.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/landmarks/libertystatue.glb
--------------------------------------------------------------------------------
/examples/config_template.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | accessToken: 'mapbox key goes here',
3 | subscriptionKey: 'azure maps subscription key goes here'
4 | };
5 |
--------------------------------------------------------------------------------
/examples/models/windmill_a/textures/material_baseColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jscastro76/threebox/HEAD/examples/models/windmill_a/textures/material_baseColor.png
--------------------------------------------------------------------------------
/server.stop.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var port = 8080 || process.env.PORT || 1337;
3 |
4 | const io = require('socket.io-client');
5 | const socketClient = io.connect('http://localhost' + port); // Specify port if your express server is not using default port 80
6 |
7 | socketClient.on('connect', () => {
8 | socketClient.emit('npmStop');
9 | setTimeout(() => {
10 | process.exit(0);
11 | }, 1000);
12 | });
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio.
3 | ################################################################################
4 |
5 | /node_modules
6 | /obj/Debug
7 | /obj/
8 | /bin/
9 | /.vs/
10 | /examples/config.js
11 | /.vs/ThreeboxSolution/v16
12 | /tests/node_modules
13 | /tests/obj/Debug
14 | /server.js
15 | /ThreeboxSolution.sln
16 | /ThreeboxSolution/
--------------------------------------------------------------------------------
/src/utils/ValueGenerator.js:
--------------------------------------------------------------------------------
1 | const ValueGenerator = function(input) {
2 | if(typeof input === 'object' && input.property !== undefined) // Value name comes from a property in each item
3 | return (f => f.properties[input.property]);
4 | else if(typeof input === 'object' && input.generator !== undefined) // Value name generated by a function run on each item
5 | return input.generator;
6 | else return (() => input);
7 |
8 | return undefined;
9 | }
10 |
11 | module.exports = exports = ValueGenerator;
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/src/objects/tooltip.js:
--------------------------------------------------------------------------------
1 | const utils = require("../utils/utils.js");
2 | const Objects = require('./objects.js');
3 | const CSS2D = require('./CSS2DRenderer.js');
4 | var THREE = require("../three.js");
5 |
6 | function Tooltip(obj) {
7 |
8 | obj = utils._validate(obj, Objects.prototype._defaults.tooltip);
9 |
10 | if (obj.text) {
11 |
12 | let divToolTip = Objects.prototype.drawTooltip(obj.text, obj.mapboxStyle);
13 |
14 | let tooltip = new CSS2D.CSS2DObject(divToolTip);
15 | tooltip.visible = false;
16 | tooltip.name = "tooltip";
17 | var userScaleGroup = Objects.prototype._makeGroup(tooltip, obj);
18 | Objects.prototype._addMethods(userScaleGroup);
19 | return userScaleGroup;
20 | }
21 |
22 | }
23 |
24 | module.exports = exports = Tooltip;
--------------------------------------------------------------------------------
/src/objects/label.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author jscastro / https://github.com/jscastro76
3 | */
4 | const utils = require("../utils/utils.js");
5 | const Objects = require('./objects.js');
6 | const CSS2D = require('./CSS2DRenderer.js');
7 |
8 | function Label(obj) {
9 |
10 | obj = utils._validate(obj, Objects.prototype._defaults.label);
11 |
12 | let div = Objects.prototype.drawLabelHTML(obj.htmlElement, obj.cssClass);
13 |
14 | let label = new CSS2D.CSS2DObject(div);
15 | label.name = "label";
16 | label.visible = obj.alwaysVisible;
17 | label.alwaysVisible = obj.alwaysVisible;
18 | var userScaleGroup = Objects.prototype._makeGroup(label, obj);
19 | Objects.prototype._addMethods(userScaleGroup);
20 | userScaleGroup.visibility = obj.alwaysVisible;
21 |
22 | return userScaleGroup;
23 | }
24 |
25 |
26 | module.exports = exports = Label;
--------------------------------------------------------------------------------
/src/objects/sphere.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author peterqliu / https://github.com/peterqliu
3 | * @author jscastro / https://github.com/jscastro76
4 | */
5 | const utils = require("../utils/utils.js");
6 | const material = require("../utils/material.js");
7 | const THREE = require('../three.js');
8 | const Objects = require('./objects.js');
9 | const Object3D = require('./Object3D.js');
10 |
11 | function Sphere(opt) {
12 |
13 | opt = utils._validate(opt, Objects.prototype._defaults.sphere);
14 | let geometry = new THREE.SphereBufferGeometry(opt.radius, opt.sides, opt.sides);
15 | let mat = material(opt)
16 | let output = new THREE.Mesh(geometry, mat);
17 | //[jscastro] we convert it in Object3D to add methods, bounding box, model, tooltip...
18 | return new Object3D({ obj: output, units: opt.units, anchor: opt.anchor, adjustment: opt.adjustment, bbox: opt.bbox, tooltip: opt.tooltip, raycasted: opt.raycasted });
19 |
20 | }
21 |
22 |
23 | module.exports = exports = Sphere;
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ":green_apple: feature"
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 |
16 | **Is your feature request related to a problem? Please describe.**
17 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
18 |
19 | **Describe the solution you'd like**
20 | A clear and concise description of what you want to happen.
21 |
22 | **Describe alternatives you've considered**
23 | A clear and concise description of any alternative solutions or features you've considered.
24 |
25 | **Additional context**
26 | Add any other context or screenshots about the feature request here.
27 |
--------------------------------------------------------------------------------
/src/objects/tube.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author peterqliu / https://github.com/peterqliu
3 | * @author jscastro / https://github.com/jscastro76
4 | */
5 | const utils = require("../utils/utils.js");
6 | const material = require("../utils/material.js");
7 | const Objects = require('./objects.js');
8 | const THREE = require("../three.js");
9 | const Object3D = require('./Object3D.js');
10 |
11 | function tube(opt, world){
12 |
13 | // validate and prep input geometry
14 | opt = utils._validate(opt, Objects.prototype._defaults.tube);
15 |
16 | let points = []
17 | opt.geometry.forEach(p => {
18 | points.push(new THREE.Vector3(p[0], p[1], p[2]));
19 | })
20 | const curve = new THREE.CatmullRomCurve3(points);
21 | let tube = new THREE.TubeGeometry(curve, points.length, opt.radius, opt.sides, false);
22 | let mat = material(opt);
23 | let obj = new THREE.Mesh(tube, mat);
24 | //[jscastro] we convert it in Object3D to add methods, bounding box, model, tooltip...
25 | return new Object3D({ obj: obj, units: opt.units, anchor: opt.anchor, adjustment: opt.adjustment, bbox: opt.bbox, tooltip: opt.tooltip, raycasted: opt.raycasted });
26 | }
27 |
28 | module.exports = exports = tube;
29 |
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ":beetle: bug"
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 |
16 | **Describe the bug**
17 | A clear and concise description of what the bug is.
18 |
19 | **To Reproduce**
20 | Steps to reproduce the behavior:
21 | 1. Go to '...'
22 | 2. Click on '....'
23 | 3. Scroll down to '....'
24 | 4. See error
25 |
26 | **Expected behavior**
27 | A clear and concise description of what you expected to happen.
28 |
29 | **Relevant Code or Code Sandbox**
30 | Share the relevant code producing the bug or provide a Minimal Reproducible Example on [fiddle](https://jsfiddle.net/), [codepen](https://codepen.io/) or any other code sandbox.
31 |
32 | **Console Results**
33 | Share the browser console results
34 |
35 | **Screenshots**
36 | If applicable, add screenshots to help explain your problem.
37 |
38 | - [Version [e.g. 2.1.7]
39 |
40 | **Additional context**
41 | Add any other context about the problem here.
42 |
--------------------------------------------------------------------------------
/src/objects/Object3D.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author peterqliu / https://github.com/peterqliu
3 | * @author jscastro / https://github.com/jscastro76
4 | */
5 | const Objects = require('./objects.js');
6 | const utils = require("../utils/utils.js");
7 |
8 | function Object3D(opt) {
9 | opt = utils._validate(opt, Objects.prototype._defaults.Object3D);
10 | // [jscastro] full refactor of Object3D to behave exactly like 3D Models loadObj
11 | let obj = opt.obj;
12 | // [jscastro] options.rotation was wrongly used
13 | const r = utils.types.rotation(opt.rotation, [0, 0, 0]);
14 | const s = utils.types.scale(opt.scale, [1, 1, 1]);
15 | obj.rotation.set(r[0], r[1], r[2]);
16 | obj.scale.set(s[0], s[1], s[2]);
17 | obj.name = "model";
18 | let userScaleGroup = Objects.prototype._makeGroup(obj, opt);
19 | opt.obj.name = "model";
20 | Objects.prototype._addMethods(userScaleGroup);
21 | //[jscastro] calculate automatically the pivotal center of the object
22 | userScaleGroup.setAnchor(opt.anchor);
23 | //[jscastro] override the center calculated if the object has adjustments
24 | userScaleGroup.setCenter(opt.adjustment);
25 | //[jscastro] if the object is excluded from raycasting
26 | userScaleGroup.raycasted = opt.raycasted;
27 | userScaleGroup.visibility = true;
28 |
29 | return userScaleGroup
30 | }
31 |
32 | module.exports = exports = Object3D;
--------------------------------------------------------------------------------
/src/utils/constants.js:
--------------------------------------------------------------------------------
1 | const WORLD_SIZE = 1024000; //TILE_SIZE * 2000
2 | const MERCATOR_A = 6378137.0; // 900913 projection property. (Deprecated) Replaced by EARTH_RADIUS
3 | const FOV_ORTHO = 0.1 / 180 * Math.PI; //Mapbox doesn't accept 0 as FOV
4 | const FOV = Math.atan(3 / 4); //from Mapbox https://github.com/mapbox/mapbox-gl-js/blob/main/src/geo/transform.js#L93
5 | const EARTH_RADIUS = 6371008.8; //from Mapbox https://github.com/mapbox/mapbox-gl-js/blob/0063cbd10a97218fb6a0f64c99bf18609b918f4c/src/geo/lng_lat.js#L11
6 | const EARTH_CIRCUMFERENCE_EQUATOR = 40075017 //from Mapbox https://github.com/mapbox/mapbox-gl-js/blob/0063cbd10a97218fb6a0f64c99bf18609b918f4c/src/geo/lng_lat.js#L117
7 |
8 | module.exports = exports = {
9 | WORLD_SIZE: WORLD_SIZE,
10 | PROJECTION_WORLD_SIZE: WORLD_SIZE / (EARTH_RADIUS * Math.PI * 2),
11 | MERCATOR_A: EARTH_RADIUS,
12 | DEG2RAD: Math.PI / 180,
13 | RAD2DEG: 180 / Math.PI,
14 | EARTH_RADIUS: EARTH_RADIUS,
15 | EARTH_CIRCUMFERENCE: 2 * Math.PI * EARTH_RADIUS, //40075000, // In meters
16 | EARTH_CIRCUMFERENCE_EQUATOR: EARTH_CIRCUMFERENCE_EQUATOR,
17 | FOV_ORTHO: FOV_ORTHO, // closest to 0
18 | FOV: FOV, // Math.atan(3/4) radians. If this value is changed, FOV_DEGREES must be calculated
19 | FOV_DEGREES: FOV * 180 / Math.PI, // Math.atan(3/4) in degrees
20 | TILE_SIZE: 512
21 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other-topics-and-questions.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Other topics and questions
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ":question: question"
6 | assignees: ''
7 |
8 | ---
9 |
10 |
22 |
23 | **Threebox version**:
24 |
25 | ### Question
26 |
--------------------------------------------------------------------------------
/dist/threebox.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | #map {
7 | position: absolute;
8 | top: 0;
9 | bottom: 0;
10 | width: 100%;
11 | }
12 |
13 | .helpDiv {
14 | width: auto;
15 | left: 50%;
16 | top: 0px;
17 | z-index: 2;
18 | position: absolute;
19 | }
20 |
21 | .help {
22 | background: rgba(0, 0, 0, 0.5);
23 | color: #fff;
24 | position: relative;
25 | text-align: center;
26 | top: 10px;
27 | left: -50%;
28 | padding: 5px 10px;
29 | margin: 0;
30 | font-size: 11px;
31 | line-height: 18px;
32 | border-radius: 3px;
33 | z-index: 1;
34 | display: block;
35 | }
36 |
37 | /*these 3 clases will provide mapbox-like style for labels*/
38 | .toolTip {
39 | border: 0.5px black solid;
40 | display: inline-block;
41 | background: white;
42 | padding: 1px 6px;
43 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
44 | font-size: 11px !important;
45 | }
46 |
47 | .marker {
48 | max-width: 240px;
49 | display: flex;
50 | margin-bottom: 5em;
51 | text-align: center;
52 | color: black;
53 | }
54 |
55 | .mapboxgl-popup-tip {
56 | margin-top: -1px;
57 | }
58 |
59 | .top-right-tools {
60 | display: flex;
61 | right: 40px;
62 | z-index: 10;
63 | }
64 |
65 | .tools-i.mapboxgl-ctrl {
66 | margin: 10px 20px 0 0;
67 | display: inline-flex;
68 | }
69 |
70 | #toolsControl {
71 | margin: 10px 20px 0 0;
72 | display: inline-flex;
73 | }
74 |
75 | #toolsControl.mapboxgl-ctrl-group button + button, #toolsControl.mapboxgl-ctrl-group div + div {
76 | border-left: 1px solid #ddd;
77 | border-top: 0px;
78 | }
--------------------------------------------------------------------------------
/examples/css/threebox.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | #map {
7 | position: absolute;
8 | top: 0;
9 | bottom: 0;
10 | width: 100%;
11 | }
12 |
13 | .helpDiv {
14 | width: auto;
15 | left: 50%;
16 | top: 0px;
17 | z-index: 2;
18 | position: absolute;
19 | }
20 |
21 | .help {
22 | background: rgba(0, 0, 0, 0.5);
23 | color: #fff;
24 | position: relative;
25 | text-align: center;
26 | top: 10px;
27 | left: -50%;
28 | padding: 5px 10px;
29 | margin: 0;
30 | font-size: 11px;
31 | line-height: 18px;
32 | border-radius: 3px;
33 | z-index: 1;
34 | display: block;
35 | }
36 |
37 | /*these 3 clases will provide mapbox-like style for labels*/
38 | .toolTip {
39 | border: 0.5px black solid;
40 | display: inline-block;
41 | background: white;
42 | padding: 1px 6px;
43 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
44 | font-size: 11px !important;
45 | }
46 |
47 | .marker {
48 | max-width: 240px;
49 | display: flex;
50 | margin-bottom: 5em;
51 | text-align: center;
52 | color: black;
53 | }
54 |
55 | .mapboxgl-popup-tip {
56 | margin-top: -1px;
57 | }
58 |
59 | .top-right-tools {
60 | display: flex;
61 | right: 40px;
62 | z-index: 10;
63 | }
64 |
65 | .tools-i.mapboxgl-ctrl {
66 | margin: 10px 20px 0 0;
67 | display: inline-flex;
68 | }
69 |
70 | #toolsControl {
71 | margin: 10px 20px 0 0;
72 | display: inline-flex;
73 | }
74 |
75 | #toolsControl.mapboxgl-ctrl-group button + button, #toolsControl.mapboxgl-ctrl-group div + div {
76 | border-left: 1px solid #ddd;
77 | border-top: 0px;
78 | }
--------------------------------------------------------------------------------
/tests/threebox-tests.js:
--------------------------------------------------------------------------------
1 | window.test = require('tape'),
2 | window.Threebox = require("../src/Threebox.js"),
3 | window.THREE = require("../src/three.js");
4 |
5 | //window.runTests = function () {
6 | // material(instance);
7 | //}
8 |
9 | function vector3Equals(t, input, expected, allowableError, epsilon) {
10 | // Check that two Vector3s are equal to each other, allowing for a certain percentage of error due to floating point math
11 | if (allowableError === undefined) allowableError = 0.0000001;
12 | if (epsilon === undefined) epsilon = 0.00000000000001;
13 | var dX, dY, dZ;
14 | dX = Math.abs(input.x - expected.x) / (expected.x === 0 ? 1 : expected.x);
15 | dY = Math.abs(input.y - expected.y) / (expected.y === 0 ? 1 : expected.y);
16 | dZ = Math.abs(input.z - expected.z) / (expected.z === 0 ? 1 : expected.z);
17 |
18 | if (dX < epsilon) dX = 0;
19 | if (dY < epsilon) dY = 0;
20 | if (dZ < epsilon) dZ = 0;
21 |
22 | if (dX > allowableError || dY > allowableError || dZ > allowableError) {
23 | t.fail("Vector3 Equivalence failed: (" + input.x + ", " + input.y + ", " + input.z + ") expected: (" + expected.x + ", " + expected.y + ", " + expected.z + ")");
24 | console.log(dY);
25 | }
26 | t.pass("ok Vector3 equivalence");
27 | }
28 |
29 |
30 | function precisionRound(number, precision) {
31 | var factor = Math.pow(10, precision);
32 | var tempNumber = number * factor;
33 | var roundedTempNumber = Math.round(tempNumber);
34 | return roundedTempNumber / factor;
35 | };
36 |
37 |
--------------------------------------------------------------------------------
/examples/models/Truck.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'Truck.blend'
2 | # Material Count: 7
3 |
4 | newmtl Black
5 | Ns 96.078431
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.013347 0.013347 0.013347
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
14 | newmtl Cream
15 | Ns 96.078431
16 | Ka 1.000000 1.000000 1.000000
17 | Kd 0.565467 0.640000 0.496259
18 | Ks 0.500000 0.500000 0.500000
19 | Ke 0.000000 0.000000 0.000000
20 | Ni 1.000000
21 | d 1.000000
22 | illum 2
23 |
24 | newmtl Silver
25 | Ns 96.078431
26 | Ka 1.000000 1.000000 1.000000
27 | Kd 0.385546 0.385546 0.385546
28 | Ks 0.500000 0.500000 0.500000
29 | Ke 0.000000 0.000000 0.000000
30 | Ni 1.000000
31 | d 1.000000
32 | illum 2
33 |
34 | newmtl Wheel_Rim
35 | Ns 96.078431
36 | Ka 1.000000 1.000000 1.000000
37 | Kd 0.640000 0.640000 0.640000
38 | Ks 0.500000 0.500000 0.500000
39 | Ke 0.000000 0.000000 0.000000
40 | Ni 1.000000
41 | d 1.000000
42 | illum 2
43 |
44 | newmtl Wheels
45 | Ns 96.078431
46 | Ka 1.000000 1.000000 1.000000
47 | Kd 0.047401 0.047401 0.047401
48 | Ks 0.500000 0.500000 0.500000
49 | Ke 0.000000 0.000000 0.000000
50 | Ni 1.000000
51 | d 1.000000
52 | illum 2
53 |
54 | newmtl White
55 | Ns 96.078431
56 | Ka 1.000000 1.000000 1.000000
57 | Kd 0.800000 0.800000 0.800000
58 | Ks 0.500000 0.500000 0.500000
59 | Ke 0.000000 0.000000 0.000000
60 | Ni 1.000000
61 | d 1.000000
62 | illum 2
63 |
64 | newmtl Window
65 | Ns 96.078431
66 | Ka 1.000000 1.000000 1.000000
67 | Kd 0.362586 0.585789 0.640000
68 | Ks 0.500000 0.500000 0.500000
69 | Ke 0.000000 0.000000 0.000000
70 | Ni 1.000000
71 | d 1.000000
72 | illum 2
73 |
--------------------------------------------------------------------------------
/src/utils/material.js:
--------------------------------------------------------------------------------
1 | // This module creates a THREE material from the options object provided into the Objects class.
2 | // Users can do this in one of three ways:
3 |
4 | // - provide a preset THREE.Material in the `material` parameter
5 | // - specify a `material` string, `color`, and/or `opacity` as modifications of the default material
6 | // - provide none of these parameters, to use the default material
7 |
8 | var utils = require("../utils/utils.js");
9 | var THREE = require("../three.js");
10 |
11 | var defaults = {
12 | material: 'MeshBasicMaterial',
13 | color: 'black',
14 | opacity: 1
15 | };
16 |
17 |
18 | function material (options) {
19 |
20 | var output;
21 |
22 | if (options) {
23 |
24 | options = utils._validate(options, defaults);
25 |
26 | // check if user provided material object
27 | if (options.material && options.material.isMaterial) output = options.material;
28 |
29 | // check if user provided any material parameters. create new material object based on that.
30 | else if (options.material || options.color || options.opacity){
31 | output = new THREE[options.material]({color: options.color, transparent: options.opacity<1});
32 | }
33 |
34 | // if neither, return default material
35 | else output = generateDefaultMaterial();
36 |
37 | output.opacity = options.opacity;
38 | if (options.side) output.side = options.side
39 |
40 | }
41 |
42 | // if no options, return default
43 | else output = generateDefaultMaterial();
44 |
45 | function generateDefaultMaterial(){
46 | return new THREE[defaults.material]({color: defaults.color});
47 | }
48 |
49 | return output
50 | }
51 |
52 | module.exports = exports = material;
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "threebox-plugin",
3 | "version": "2.2.7",
4 | "description": "A Three.js plugin for Mapbox GL JS, using the CustomLayerInterface feature. Provides convenient methods to manage objects in lnglat coordinates, and to synchronize the map and scene cameras.",
5 | "main": "main.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/jscastro76/threebox.git"
9 | },
10 | "author": " @jscastro76, @peterqliu, @kronick",
11 | "license": "MIT",
12 | "bugs": {
13 | "url": "https://github.com/jscastro76/threebox/issues"
14 | },
15 | "homepage": "https://github.com/jscastro76/threebox#readme",
16 | "scripts": {
17 | "build": "browserify -p tinyify exports.js > dist/threebox.min.js && ncp ./examples/css/threebox.css ./dist/threebox.css --stopOnErr",
18 | "dev": "watchify exports.js --verbose -o dist/threebox.js ",
19 | "all": "browserify -p tinyify exports.js > dist/threebox.min.js && watchify exports.js --verbose -o dist/threebox.js ",
20 | "test": "browserify tests/threebox-tests.js > tests/threebox-tests-bundle.js",
21 | "start": "node server.js",
22 | "stop": "node server.stop.js"
23 | },
24 | "directories": {
25 | "doc": "docs",
26 | "example": "examples",
27 | "test": "tests"
28 | },
29 | "devDependencies": {
30 | "browserify": "^17.0.0",
31 | "ncp": "^2.0.0",
32 | "tape": "^5.1.1",
33 | "tinyify": "^4.0.0",
34 | "uglifyify": "^5.0.2",
35 | "watchify": "^4.0.0"
36 | },
37 | "keywords": [
38 | "three.js",
39 | "mapbox",
40 | "mapbox-gl-js",
41 | "azure-maps",
42 | "3D"
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/validate.test.js:
--------------------------------------------------------------------------------
1 | function validateTest(instance){
2 |
3 | var v = instance.utils.Validator;
4 |
5 | // Coords validation
6 |
7 | test('VALIDATE invalid Coords', function(t){
8 |
9 | t.error(v.Coords(true), 'error');
10 | t.error(v.Coords(['a']), 'error');
11 | t.error(v.Coords([1]), 'error');
12 | t.error(v.Coords([3, false]), 'error');
13 |
14 | t.end();
15 |
16 | });
17 |
18 | test('VALIDATE valid Coords', function(t){
19 |
20 | t.deepEqual(v.Coords([22,33]), [22,33]);
21 | t.deepEqual(v.Coords([22,33,-10]), [22,33, -10]);
22 | t.end();
23 |
24 | });
25 |
26 | // Line validation
27 |
28 | test('VALIDATE invalid Line', function(t){
29 |
30 | t.error(v.Line([[1,2], [false, 4]]), 'error');
31 | t.end();
32 |
33 | });
34 |
35 | test('VALIDATE valid Line', function(t){
36 |
37 | t.deepEqual(v.Line([[-100, 20], [22,33]]), [[-100, 20], [22,33]]);
38 | t.end();
39 |
40 | });
41 |
42 | // Rotation validation
43 |
44 | test('VALIDATE invalid Rotation', function(t){
45 |
46 | t.error(v.Rotation('rotate'), 'error');
47 | t.end();
48 |
49 | });
50 |
51 | test('VALIDATE valid Rotation', function(t){
52 |
53 | t.deepEqual(v.Rotation(40), {z:40});
54 | t.deepEqual(v.Rotation({x:20, y:10, z:90}), {x:20, y:10, z:90});
55 |
56 | t.end();
57 |
58 | });
59 |
60 | // Scale validation
61 |
62 | test('VALIDATE invalid Scale', function(t){
63 |
64 | t.error(v.Scale('scale'), 'error');
65 | t.end();
66 |
67 | });
68 |
69 | test('VALIDATE valid Scale', function(t){
70 |
71 | t.deepEqual(v.Scale(22), {x:22, y:22, z:22});
72 | t.deepEqual(v.Scale({x:20, y:10, z:90}), {x:20, y:10, z:90});
73 |
74 | t.end();
75 |
76 | });
77 | }
78 |
79 |
80 |
--------------------------------------------------------------------------------
/tests/unit/object.test.js:
--------------------------------------------------------------------------------
1 | function objectTest(instance){
2 |
3 | var mesh = new THREE.Mesh();
4 | var group
5 |
6 | test('OBJECT _makeGroup from one object', function(t){
7 | group = instance.objects._makeGroup(mesh, {foo: true});
8 |
9 | t.equal(group.userData.foo, true);
10 | t.equal(group.type, 'Group');
11 | t.equal(group.children.length, 1);
12 | t.end();
13 |
14 | });
15 |
16 | test('OBJECT _makeGroup from multiple objects', function(t){
17 | var mesh2 = new THREE.Mesh();
18 | group = instance.objects._makeGroup([mesh, mesh2], {foo: false});
19 |
20 | t.equal(group.userData.foo, false);
21 | t.equal(group.type, 'Group');
22 | t.equal(group.children.length, 2);
23 | t.end();
24 |
25 | })
26 |
27 | test('OBJECT _addMethods static', function(t){
28 |
29 | group = instance.objects._makeGroup(mesh, {});
30 | var addedMethods = instance.objects._addMethods(group, true);
31 |
32 | t.equal(addedMethods.setCoords, undefined);
33 | t.equal(addedMethods.type, 'Group');
34 | t.end();
35 |
36 | })
37 |
38 | test('OBJECT _addMethods dynamic', function(t){
39 |
40 | var sphere = instance.sphere({units: 'meters'});
41 |
42 | t.equal(sphere.type, 'Group');
43 | t.equal(typeof sphere.followPath, 'function');
44 | t.deepEqual(sphere.coordinates, [0,0,0]);
45 | t.end();
46 |
47 | })
48 |
49 | test('OBJECT setCoords updates both position and scale', function(t){
50 |
51 | var sphere = instance.sphere({units: 'meters'});
52 | var newPosition = [0,-20];
53 |
54 | sphere.setCoords(newPosition)
55 | var scaleFactor = instance.projectedUnitsPerMeter(newPosition[1]);
56 |
57 | vector3Equals(t, sphere.position, instance.projectToWorld(newPosition));
58 | t.equal(sphere.scale.x, scaleFactor);
59 | t.deepEqual(sphere.coordinates, newPosition)
60 | t.end();
61 |
62 | })
63 |
64 | }
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/objects/extrusion.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author jscastro / https://github.com/jscastro76
3 | */
4 | const Objects = require('./objects.js');
5 | const utils = require("../utils/utils.js");
6 | const THREE = require("../three.js");
7 | const Object3D = require('./Object3D.js');
8 |
9 | /**
10 | *
11 | * @param {any} opt must fit the default defined in Objects.prototype._defaults.extrusion
12 | * @param {arr} opt.coordinates could receive a feature.geometry.coordinates
13 | */
14 | function extrusion(opt) {
15 |
16 | opt = utils._validate(opt, Objects.prototype._defaults.extrusion);
17 | let shape = extrusion.prototype.buildShape(opt.coordinates);
18 | let geometry = extrusion.prototype.buildGeometry(shape, opt.geometryOptions);
19 | let mesh = new THREE.Mesh(geometry, opt.materials);
20 | opt.obj = mesh;
21 | //[jscastro] we convert it in Object3D to add methods, bounding box, model, tooltip...
22 | return new Object3D(opt);
23 |
24 | }
25 |
26 | extrusion.prototype = {
27 |
28 | buildShape: function (coords) {
29 | if (coords[0] instanceof (THREE.Vector2 || THREE.Vector3)) return new THREE.Shape(coords);
30 | let shape = new THREE.Shape();
31 | for (let i = 0; i < coords.length; i++) {
32 | if (i === 0) {
33 | shape = new THREE.Shape(this.buildPoints(coords[0], coords[0]));
34 | } else {
35 | shape.holes.push(new THREE.Path(this.buildPoints(coords[i], coords[0])));
36 | }
37 | }
38 | return shape;
39 | },
40 |
41 | buildPoints: function (coords, initCoords) {
42 | const points = [];
43 | let init = utils.projectToWorld([initCoords[0][0], initCoords[0][1], 0]);
44 | for (let i = 0; i < coords.length; i++) {
45 | let pos = utils.projectToWorld([coords[i][0], coords[i][1], 0]);
46 | points.push(new THREE.Vector2(utils.toDecimal((pos.x - init.x), 9), utils.toDecimal((pos.y - init.y), 9)));
47 | }
48 | return points;
49 | },
50 |
51 | buildGeometry: function (shape, settings) {
52 | let geometry = new THREE.ExtrudeBufferGeometry(shape, settings);
53 | geometry.computeBoundingBox();
54 | return geometry;
55 | }
56 |
57 | }
58 |
59 | module.exports = exports = extrusion;
--------------------------------------------------------------------------------
/src/objects/LabelRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author jscastro / https://github.com/jscastro76
3 | */
4 |
5 | const THREE = require("./CSS2DRenderer.js");
6 |
7 | function LabelRenderer(map) {
8 |
9 | this.map = map;
10 |
11 | this.renderer = new THREE.CSS2DRenderer();
12 |
13 | this.renderer.setSize(this.map.getCanvas().clientWidth, this.map.getCanvas().clientHeight);
14 | this.renderer.domElement.style.position = 'absolute';
15 | this.renderer.domElement.id = 'labelCanvas'; //TODO: this value must come by parameter
16 | this.renderer.domElement.style.top = 0;
17 | this.renderer.domElement.style.zIndex = "0";
18 | this.map.getCanvasContainer().appendChild(this.renderer.domElement);
19 |
20 | this.scene, this.camera;
21 |
22 | this.dispose = function () {
23 | this.map.getCanvasContainer().removeChild(this.renderer.domElement)
24 | this.renderer.domElement.remove();
25 | this.renderer = {};
26 | }
27 |
28 | this.setSize = function (width, height) {
29 | this.renderer.setSize(width, height);
30 | }
31 |
32 | this.map.on('resize', function () {
33 | this.renderer.setSize(this.map.getCanvas().clientWidth, this.map.getCanvas().clientHeight);
34 | }.bind(this));
35 |
36 | this.state = {
37 | reset: function () {
38 | //TODO: Implement a good state reset, check out what is made in WebGlRenderer
39 | }
40 | }
41 |
42 | this.render = async function (scene, camera) {
43 | this.scene = scene;
44 | this.camera = camera;
45 | return new Promise((resolve) => { resolve(this.renderer.render(scene, camera)) });
46 | }
47 |
48 | //[jscastro] method to toggle Layer visibility
49 | this.toggleLabels = async function (layerId, visible) {
50 | return new Promise((resolve) => {
51 | resolve(this.setVisibility(layerId, visible, this.scene, this.camera, this.renderer));
52 | })
53 | };
54 |
55 | //[jscastro] method to set visibility
56 | this.setVisibility = function (layerId, visible, scene, camera, renderer) {
57 | var cache = this.renderer.cacheList;
58 | cache.forEach(function (l) {
59 | if (l.visible != visible && l.layer === layerId) {
60 | if ((visible && l.alwaysVisible) || !visible) {
61 | l.visible = visible;
62 | renderer.renderObject(l, scene, camera);
63 | }
64 | }
65 | });
66 | };
67 |
68 | }
69 |
70 | module.exports = exports = LabelRenderer;
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var http = require('http');
3 | var url = require('url');
4 | var fs = require('fs');
5 | var path = require('path');
6 | var baseDirectory = __dirname;
7 | var port = 8080 || process.env.PORT || 1337;
8 | var counter = 0;
9 |
10 | http.createServer(function (request, response) {
11 | try {
12 | var requestUrl = url.parse(request.url);
13 | // need to use path.normalize so people can't access directories underneath baseDirectory
14 | var fsPath = baseDirectory + path.normalize(requestUrl.pathname);
15 |
16 | var ext = path.extname(fsPath)
17 | var validExtensions = {
18 | ".html": "text/html",
19 | ".js": "application/javascript",
20 | ".json": "application/json",
21 | ".geojson": "application/json",
22 | ".bin": "application/octet-stream",
23 | ".css": "text/css",
24 | ".txt": "text/plain",
25 | ".bmp": "image/bmp",
26 | ".jpg": "image/jpeg",
27 | ".gif": "image/gif",
28 | ".png": "image/png",
29 | ".ico": "image/x-icon",
30 | ".dae": "application/vnd.oipf.dae.svg+xml",
31 | ".pbf": "application/octet-stream",
32 | ".mtl": "model/mtl",
33 | ".obj": "model/obj",
34 | ".glb": "model/gltf-binary",
35 | ".gltf": "model/gltf+json",
36 | ".fbx": "application/octet-stream",
37 | ".ttf": "application/octet-stream",
38 | ".woff": "font/woff",
39 | ".woff2": "font/woff2",
40 |
41 | };
42 |
43 | var isValidExt = validExtensions[ext];
44 |
45 | var fileStream = fs.createReadStream(fsPath);
46 | fileStream.pipe(response);
47 | fileStream.on('open', function () {
48 | response.setHeader("Content-Type", validExtensions[ext]);
49 | response.writeHead(200);
50 | });
51 | fileStream.on('error', function (e) {
52 | response.writeHead(404); // assume the file doesn't exist
53 | response.end();
54 | });
55 | } catch (e) {
56 | response.writeHead(500);
57 | response.end(); // end the response so browsers don't hang
58 | console.log(e.stack);
59 | }
60 |
61 | }).listen(port);
62 |
63 |
--------------------------------------------------------------------------------
/tests/unit/material.test.js:
--------------------------------------------------------------------------------
1 | function materialTest(instance){
2 |
3 | var material;
4 |
5 | test('MATERIAL default type, color, opacity', function(t) {
6 | material = instance.material();
7 | t.equal(material.type, 'MeshBasicMaterial');
8 | t.deepEqual(material.color, { r: 0, g: 0, b: 0 });
9 | t.equal(material.opacity, 1);
10 | t.end();
11 | });
12 |
13 | test('MATERIAL custom type', function(t) {
14 | material = instance.material({material:'MeshPhysicalMaterial'});
15 | t.equal(material.opacity, 1);
16 | t.equal(material.type, 'MeshPhysicalMaterial');
17 | t.deepEqual(material.color, { r: 0, g: 0, b: 0 });
18 | t.end();
19 | });
20 |
21 | test('MATERIAL custom color', function(t) {
22 | material = instance.material({color:'red'});
23 | t.equal(material.opacity, 1);
24 | t.equal(material.type, 'MeshBasicMaterial');
25 | t.deepEqual(material.color, { r: 1, g: 0, b: 0 });
26 | t.end();
27 | });
28 |
29 | test('MATERIAL custom opacity', function(t) {
30 | material = instance.material({opacity:0.5});
31 | t.equal(material.opacity, 0.5);
32 | t.equal(material.type, 'MeshBasicMaterial');
33 | t.deepEqual(material.color, { r: 0, g: 0, b: 0 });
34 | t.end();
35 | });
36 |
37 | test('MATERIAL custom color, opacity, type', function(t) {
38 | allCustom = instance.material({
39 | material: 'MeshBasicMaterial',
40 | opacity: 0.5,
41 | color: 'blue'
42 | });
43 | t.equal(allCustom.opacity, 0.5);
44 | t.equal(allCustom.type, 'MeshBasicMaterial');
45 | t.deepEqual(allCustom.color, { r: 0, g: 0, b: 1 });
46 | t.end();
47 | });
48 |
49 | test('MATERIAL when THREE.Material provided, other material params ignored except opacity', function(t) {
50 | threeMaterial = instance.material({
51 | material: new THREE.MeshBasicMaterial({color: 'cyan'}),
52 | opacity: 0.5,
53 | color: 'blue'
54 | });
55 |
56 | t.equal(threeMaterial.opacity, 0.5);
57 | t.equal(threeMaterial.type, 'MeshBasicMaterial');
58 | t.deepEqual(threeMaterial.color, { r: 0, g: 1, b: 1 });
59 | t.end();
60 | });
61 | }
62 |
63 |
64 |
--------------------------------------------------------------------------------
/examples/01-basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sphere Example
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
100 |
--------------------------------------------------------------------------------
/examples/css/free-v4-font-face-min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.12.1 by @fontawesome - https://fontawesome.com
3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | @font-face {
6 | font-family: "FontAwesome";
7 | src: url(../webfonts/free-fa-solid-900.eot),url(../webfonts/free-fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/free-fa-solid-900.woff2) format("woff2"),url(../webfonts/free-fa-solid-900.woff) format("woff"),url(../webfonts/free-fa-solid-900.ttf) format("truetype"),url(../webfonts/free-fa-solid-900.svg#fontawesome) format("svg")
8 | }
9 |
10 | @font-face {
11 | font-family: "FontAwesome";
12 | src: url(../webfonts/free-fa-brands-400.eot),url(../webfonts/free-fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/free-fa-brands-400.woff2) format("woff2"),url(../webfonts/free-fa-brands-400.woff) format("woff"),url(../webfonts/free-fa-brands-400.ttf) format("truetype"),url(../webfonts/free-fa-brands-400.svg#fontawesome) format("svg")
13 | }
14 |
15 | @font-face {
16 | font-family: "FontAwesome";
17 | src: url(../webfonts/free-fa-regular-400.eot),url(../webfonts/free-fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/free-fa-regular-400.woff2) format("woff2"),url(../webfonts/free-fa-regular-400.woff) format("woff"),url(../webfonts/free-fa-regular-400.ttf) format("truetype"),url(../webfonts/free-fa-regular-400.svg#fontawesome) format("svg");
18 | unicode-range: U+f004-f005,U+f007,U+f017,U+f022,U+f024,U+f02e,U+f03e,U+f044,U+f057-f059,U+f06e,U+f070,U+f075,U+f07b-f07c,U+f080,U+f086,U+f089,U+f094,U+f09d,U+f0a0,U+f0a4-f0a7,U+f0c5,U+f0c7-f0c8,U+f0e0,U+f0eb,U+f0f3,U+f0f8,U+f0fe,U+f111,U+f118-f11a,U+f11c,U+f133,U+f144,U+f146,U+f14a,U+f14d-f14e,U+f150-f152,U+f15b-f15c,U+f164-f165,U+f185-f186,U+f191-f192,U+f1ad,U+f1c1-f1c9,U+f1cd,U+f1d8,U+f1e3,U+f1ea,U+f1f6,U+f1f9,U+f20a,U+f247-f249,U+f24d,U+f254-f25b,U+f25d,U+f271-f274,U+f279,U+f28b,U+f28d,U+f2b5-f2b6,U+f2b9,U+f2bb,U+f2bd,U+f2c1-f2c2,U+f2d0,U+f2d2,U+f2dc,U+f2ed,U+f3a5,U+f3d1,U+f410
19 | }
20 |
21 | @font-face {
22 | font-family: "FontAwesome";
23 | src: url(../webfonts/free-fa-v4deprecations.eot),url(../webfonts/free-fa-v4deprecations.eot?#iefix) format("embedded-opentype"),url(../webfonts/free-fa-v4deprecations.woff2) format("woff2"),url(../webfonts/free-fa-v4deprecations.woff) format("woff"),url(../webfonts/free-fa-v4deprecations.ttf) format("truetype"),url(../webfonts/free-fa-v4deprecations.svg#fontawesome) format("svg");
24 | unicode-range: U+f003,U+f006,U+f014,U+f016,U+f01a-f01b,U+f01d,U+f040,U+f045-f047,U+f05c-f05d,U+f07d-f07e,U+f087-f088,U+f08a-f08b,U+f08e,U+f090,U+f096-f097,U+f0a2,U+f0e4-f0e6,U+f0ec-f0ee,U+f0f5-f0f7,U+f10c,U+f112,U+f114-f115,U+f11d,U+f123,U+f132,U+f145,U+f147-f149,U+f14c,U+f166,U+f16a,U+f172,U+f175-f178,U+f18e,U+f190,U+f196,U+f1b1,U+f1d9,U+f1db,U+f1f7,U+f20c,U+f219,U+f230,U+f24a,U+f250,U+f278,U+f27b,U+f283,U+f28c,U+f28e,U+f29b-f29c,U+f2b7,U+f2ba,U+f2bc,U+f2be,U+f2c0,U+f2c3,U+f2d3-f2d4
25 | }
26 |
--------------------------------------------------------------------------------
/examples/06-object3d.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox Object3D Example
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
100 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | // Type validator
2 |
3 | function Validate(){
4 |
5 | };
6 |
7 | Validate.prototype = {
8 |
9 | Coords: function(input) {
10 |
11 | if (input.constructor !== Array) {
12 | console.error("Coords must be an array")
13 | return
14 | }
15 |
16 | if (input.length < 2) {
17 | console.error("Coords length must be at least 2")
18 | return
19 | }
20 |
21 | for (const member of input) {
22 | if (member.constructor !== Number) {
23 | console.error("Coords values must be numbers")
24 | return
25 | }
26 | }
27 |
28 | if (Math.abs(input[1]) > 90) {
29 | console.error("Latitude must be between -90 and 90")
30 | return
31 | }
32 |
33 | return input
34 | },
35 |
36 | Line: function(input) {
37 |
38 | var scope = this;
39 |
40 | if (input.constructor !== Array) {
41 | console.error("Line must be an array")
42 | return
43 | }
44 |
45 | for (const coord of input){
46 | if (!scope.Coords(coord)) {
47 | console.error("Each coordinate in a line must be a valid Coords type")
48 | return
49 | }
50 |
51 | }
52 |
53 | return input
54 | },
55 |
56 | Rotation: function(input) {
57 |
58 | if (input.constructor === Number) input = {z: input}
59 |
60 | else if (input.constructor === Object) {
61 |
62 | for (const key of Object.keys(input)){
63 |
64 | if (!['x', 'y', 'z'].includes(key)) {
65 | console.error('Rotation parameters must be x, y, or z')
66 | return
67 | }
68 | if (input[key].constructor !== Number) {
69 | console.error('Individual rotation values must be numbers')
70 | return
71 | }
72 | }
73 | }
74 |
75 | else {
76 | console.error('Rotation must be an object or a number')
77 | return
78 | }
79 |
80 | return input
81 | },
82 |
83 | Scale: function(input) {
84 |
85 | if (input.constructor === Number) {
86 | input = {x:input, y:input, z: input}
87 | }
88 |
89 | else if (input.constructor === Object) {
90 |
91 | for (const key of Object.keys(input)){
92 |
93 | if (!['x', 'y', 'z'].includes(key)) {
94 | console.error('Scale parameters must be x, y, or z')
95 | return
96 | }
97 | if (input[key].constructor !== Number) {
98 | console.error('Individual scale values must be numbers')
99 | return
100 | }
101 | }
102 | }
103 |
104 | else {
105 | console.error('Scale must be an object or a number')
106 | return
107 | }
108 |
109 | return input
110 | }
111 |
112 | }
113 |
114 |
115 | module.exports = exports = Validate;
--------------------------------------------------------------------------------
/examples/02-line.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Line Example
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
123 |
--------------------------------------------------------------------------------
/examples/03-tube.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tube Example
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
121 |
--------------------------------------------------------------------------------
/tests/threebox-tests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox tests
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
29 | Open the console to see test results
30 |
31 |
32 |
99 |
--------------------------------------------------------------------------------
/examples/04-mercator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Mercator projection
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
125 |
--------------------------------------------------------------------------------
/examples/10-stylechange.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox change map style for Eiffel Tower
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
31 |
32 |
44 |
135 |
136 |
--------------------------------------------------------------------------------
/examples/17-azuremaps.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Azure Maps Sample
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
31 |
32 |
33 |
34 |
35 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/examples/21-multifloor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox alignment Test
5 |
6 |
7 |
8 |
9 |
10 |
23 |
24 |
25 |
26 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/objects/loadObj.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author peterqliu / https://github.com/peterqliu
3 | * @author jscastro / https://github.com/jscastro76
4 | */
5 | const utils = require("../utils/utils.js");
6 | const Objects = require('./objects.js');
7 | const OBJLoader = require("./loaders/OBJLoader.js");
8 | const MTLLoader = require("./loaders/MTLLoader.js");
9 | const FBXLoader = require("./loaders/FBXLoader.js");
10 | const GLTFLoader = require("./loaders/GLTFLoader.js");
11 | const ColladaLoader = require("./loaders/ColladaLoader.js");
12 | const objLoader = new OBJLoader();
13 | const materialLoader = new MTLLoader();
14 | const gltfLoader = new GLTFLoader();
15 | const fbxLoader = new FBXLoader();
16 | const daeLoader = new ColladaLoader();
17 |
18 | function loadObj(options, cb, promise) {
19 |
20 | if (options === undefined) return console.error("Invalid options provided to loadObj()");
21 | options = utils._validate(options, Objects.prototype._defaults.loadObj);
22 |
23 | let loader;
24 | if (!options.type) { options.type = 'mtl'; };
25 | //[jscastro] support other models
26 | switch (options.type) {
27 | case "mtl":
28 | // TODO: Support formats other than OBJ/MTL
29 | loader = objLoader;
30 | break;
31 | case "gltf":
32 | case "glb":
33 | // [jscastro] Support for GLTF/GLB
34 | loader = gltfLoader;
35 | break;
36 | case "fbx":
37 | loader = fbxLoader;
38 | break;
39 | case "dae":
40 | loader = daeLoader;
41 | break;
42 | }
43 |
44 | materialLoader.withCredentials = options.withCredentials;
45 | materialLoader.load(options.mtl, loadObject, () => (null), error => {
46 | console.warn("No material file found " + error.stack);
47 | });
48 |
49 | function loadObject(materials) {
50 |
51 | if (materials && options.type == "mtl") {
52 | materials.preload();
53 | loader.setMaterials(materials);
54 | }
55 |
56 | loader.withCredentials = options.withCredentials;
57 | loader.load(options.obj, obj => {
58 |
59 | //[jscastro] MTL/GLTF/FBX models have a different structure
60 | let animations = [];
61 | switch (options.type) {
62 | case "mtl":
63 | obj = obj.children[0];
64 | break;
65 | case "gltf":
66 | case "glb":
67 | case "dae":
68 | animations = obj.animations;
69 | obj = obj.scene;
70 | break;
71 | case "fbx":
72 | animations = obj.animations;
73 | break;
74 | }
75 | obj.animations = animations;
76 | // [jscastro] options.rotation was wrongly used
77 | const r = utils.types.rotation(options.rotation, [0, 0, 0]);
78 | const s = utils.types.scale(options.scale, [1, 1, 1]);
79 | obj.rotation.set(r[0], r[1], r[2]);
80 | obj.scale.set(s[0], s[1], s[2]);
81 | // [jscastro] normalize specular/metalness/shininess from meshes in FBX and GLB model as it would need 5 lights to illuminate them properly
82 | if (options.normalize) { normalizeSpecular(obj); }
83 | obj.name = "model";
84 | let userScaleGroup = Objects.prototype._makeGroup(obj, options);
85 | Objects.prototype._addMethods(userScaleGroup);
86 | //[jscastro] calculate automatically the pivotal center of the object
87 | userScaleGroup.setAnchor(options.anchor);
88 | //[jscastro] override the center calculated if the object has adjustments
89 | userScaleGroup.setCenter(options.adjustment);
90 | //[jscastro] if the object is excluded from raycasting
91 | userScaleGroup.raycasted = options.raycasted;
92 | //[jscastro] return to cache
93 | promise(userScaleGroup);
94 | //[jscastro] then return to the client-side callback
95 | cb(userScaleGroup);
96 | //[jscastro] apply the fixed zoom scale if needed
97 | userScaleGroup.setFixedZoom(options.mapScale);
98 | //[jscastro] initialize the default animation to avoid issues with skeleton position
99 | userScaleGroup.idle();
100 |
101 | }, () => (null), error => {
102 | console.error("Could not load model file: " + options.obj + " \n " + error.stack);
103 | promise("Error loading the model");
104 | });
105 |
106 | };
107 |
108 | //[jscastro] some FBX/GLTF models have too much specular effects for mapbox
109 | function normalizeSpecular(model) {
110 | model.traverse(function (c) {
111 |
112 | if (c.isMesh) {
113 | //c.castShadow = true;
114 | let specularColor;
115 | if (c.material.type == 'MeshStandardMaterial') {
116 |
117 | if (c.material.metalness) { c.material.metalness *= 0.1; }
118 | if (c.material.glossiness) { c.material.glossiness *= 0.25; }
119 | specularColor = new THREE.Color(12, 12, 12);
120 |
121 | } else if (c.material.type == 'MeshPhongMaterial') {
122 | c.material.shininess = 0.1;
123 | specularColor = new THREE.Color(20, 20, 20);
124 | }
125 | if (c.material.specular && c.material.specular.isColor) {
126 | c.material.specular = specularColor;
127 | }
128 | //c.material.needsUpdate = true;
129 |
130 | }
131 |
132 | });
133 | }
134 |
135 | }
136 |
137 | module.exports = exports = loadObj;
--------------------------------------------------------------------------------
/examples/12-add3dmodel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Add a 3D model with Threebox
5 |
6 |
7 |
8 |
9 |
10 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
158 |
159 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | v.2.0.1 - v.2.2.6
2 | MIT License
3 | Copyright (c) 2020 Jesus Serrano
4 |
5 | v.0.3.0
6 | MIT License
7 | Copyright (c) 2017 Peter Liu
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in all
17 | copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
26 |
27 | ---------------------------------------------------------------------------------------------
28 | SunCalc (c) 2011-2015, Vladimir Agafonkin
29 | Copyright (c) 2014, Vladimir Agafonkin
30 | All rights reserved.
31 |
32 | Redistribution and use in source and binary forms, with or without modification, are
33 | permitted provided that the following conditions are met:
34 |
35 | 1. Redistributions of source code must retain the above copyright notice, this list of
36 | conditions and the following disclaimer.
37 |
38 | 2. Redistributions in binary form must reproduce the above copyright notice, this list
39 | of conditions and the following disclaimer in the documentation and/or other materials
40 | provided with the distribution.
41 |
42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
43 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
44 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
45 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
46 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
49 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 |
52 | ---------------------------------------------------------------------------------------------
53 | 3D models attributions
54 |
55 | - Eiffel Tower
56 | - Creative Commons License attribution: Eiffel Tower model by https://www.cgtrader.com/lefabshop
57 | from https://www.cgtrader.com/items/108594/download-page
58 |
59 | - Elephant
60 | - Creative Commons NonCommercial License attribution: Asian Elephant low poly model by https://sketchfab.com/jeremielouvetz
61 | from https://sketchfab.com/3d-models/asian-elephant-2aeeb8958bc64240962b093705abffdf
62 |
63 | - Liberty Statue
64 | - Creative Commons License attribution: Liberty statue model by https://sketchfab.com/hellolucy2
65 | from https://sketchfab.com/3d-models/ellis-island-3cd765a23c5c4c7087acd00624d30590
66 |
67 | - Plane
68 | - Creative Commons License attribution: Plane model by https://sketchfab.com/ideehochzwei
69 | from https://sketchfab.com/3d-models/plane-aa001f5a88f64b16b98356c042f2d5f3
70 |
71 | - Radar
72 | - Attribution, no License specified: Model by https://github.com/nasa/
73 | from https://nasa3d.arc.nasa.gov/detail/jpl-vtad-dsn34
74 |
75 | - Soldier
76 | - Attribution: Soldier animated model by T. Choonyung at https://www.mixamo.com
77 | from https://www.mixamo.com/#/?page=1&query=vanguard&type=Character
78 |
79 | - Space Needle
80 | - Creative Commons License attribution: Space Needle model by https://sketchfab.com/microsoft
81 | from https://sketchfab.com/3d-models/space-needle-1d1325bc1ad745dd9eb34fc76e8f6e87
82 |
83 | - Triceratops
84 | - Creative Commons NonCommercial License attribution: Dino low poly model by https://sketchfab.com/Blender_Fox1234
85 | from https://sketchfab.com/3d-models/triceratops-lowpoly-76beb95d0b5b41a3aae0d07c3eae99fa
86 |
87 | - Truck & Car
88 | - Royalty Free License: Vehicles by https://www.cgtrader.com/antonmoek
89 | from https://www.cgtrader.com/free-3d-models/car/concept/cartoon-low-poly-city-cars-pack
90 |
91 | - Windmill
92 | - Creative Commons License attribution: Windmill animated model by https://sketchfab.com/data3anshow
93 | from https://sketchfab.com/3d-models/windmill-animated-6ce5667e8d5c47068ea13196036efd52
94 |
95 | - Glacier d'Argentiere
96 | - Attribution, no License specified: Glacier model by https://github.com/jbbarre
97 | from https://github.com/jbbarre/glacierargentiere/blob/main/data/glacier_wgs.gltf
--------------------------------------------------------------------------------
/examples/18-extrusions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threbox extrusions sample
4 |
5 |
6 |
7 |
8 |
9 |
10 |
22 |
23 |
24 |
25 |
142 |
--------------------------------------------------------------------------------
/examples/05-logistics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Animated truck
4 |
5 |
6 |
7 |
8 |
9 |
10 |
27 |
28 |
29 |
30 | Click on the map to drive the truck there
31 |
179 |
--------------------------------------------------------------------------------
/examples/08-3dbuildings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox display buildings in 3D with auto tooltips
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
178 |
179 |
--------------------------------------------------------------------------------
/examples/14-buildingshadow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox building Sun light and shadows
5 |
6 |
7 |
8 |
9 |
10 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
187 |
188 |
--------------------------------------------------------------------------------
/examples/07-alignmentTest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox alignment Test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
25 |
26 |
27 |
28 |
29 | This demo shows camera alignment between fill-extrusion layer and a 3DObject
30 |
31 |
32 |
33 |
180 |
181 |
--------------------------------------------------------------------------------
/examples/css/fontawesome.js:
--------------------------------------------------------------------------------
1 | window.FontAwesomeKitConfig = { "asyncLoading": { "enabled": false }, "autoA11y": { "enabled": true }, "baseUrl": "https://kit-free.fontawesome.com", "detectConflictsUntil": null, "license": "free", "method": "css", "minify": { "enabled": true }, "v4FontFaceShim": { "enabled": true }, "v4shim": { "enabled": true }, "version": "latest" };
2 | !function () { function r(e) { var t, n = [], i = document, o = i.documentElement.doScroll, r = "DOMContentLoaded", a = (o ? /^loaded|^c/ : /^loaded|^i|^c/).test(i.readyState); a || i.addEventListener(r, t = function () { for (i.removeEventListener(r, t), a = 1; t = n.shift();)t() }), a ? setTimeout(e, 0) : n.push(e) } !function () { if (!(void 0 === window.Element || "classList" in document.documentElement)) { var e, t, n, i = Array.prototype, o = i.push, r = i.splice, a = i.join; d.prototype = { add: function (e) { this.contains(e) || (o.call(this, e), this.el.className = this.toString()) }, contains: function (e) { return -1 != this.el.className.indexOf(e) }, item: function (e) { return this[e] || null }, remove: function (e) { if (this.contains(e)) { for (var t = 0; t < this.length && this[t] != e; t++); r.call(this, t, 1), this.el.className = this.toString() } }, toString: function () { return a.call(this, " ") }, toggle: function (e) { return this.contains(e) ? this.remove(e) : this.add(e), this.contains(e) } }, window.DOMTokenList = d, e = Element.prototype, t = "classList", n = function () { return new d(this) }, Object.defineProperty ? Object.defineProperty(e, t, { get: n }) : e.__defineGetter__(t, n) } function d(e) { for (var t = (this.el = e).className.replace(/^\s+|\s+$/g, "").split(/\s+/), n = 0; n < t.length; n++)o.call(this, t[n]) } }(); function a(e) { var t, n, i, o; prefixesArray = e || ["fa"], prefixesSelectorString = "." + Array.prototype.join.call(e, ",."), t = document.querySelectorAll(prefixesSelectorString), Array.prototype.forEach.call(t, function (e) { n = e.getAttribute("title"), e.setAttribute("aria-hidden", "true"), i = !e.nextElementSibling || !e.nextElementSibling.classList.contains("sr-only"), n && i && ((o = document.createElement("span")).innerHTML = n, o.classList.add("sr-only"), e.parentNode.insertBefore(o, e.nextSibling)) }) } var d = function (e, t) { var n = document.createElement("link"); n.href = e, n.media = "all", n.rel = "stylesheet", t && t.detectingConflicts && t.detectionIgnoreAttr && n.setAttributeNode(document.createAttribute(t.detectionIgnoreAttr)), document.getElementsByTagName("head")[0].appendChild(n) }, c = function (e, t) { !function (e, t) { var n, i = t && t.before || void 0, o = t && t.media || void 0, r = window.document, a = r.createElement("link"); if (t && t.detectingConflicts && t.detectionIgnoreAttr && a.setAttributeNode(document.createAttribute(t.detectionIgnoreAttr)), i) n = i; else { var d = (r.body || r.getElementsByTagName("head")[0]).childNodes; n = d[d.length - 1] } var c = r.styleSheets; a.rel = "stylesheet", a.href = e, a.media = "only x", function e(t) { if (r.body) return t(); setTimeout(function () { e(t) }) }(function () { n.parentNode.insertBefore(a, i ? n : n.nextSibling) }); var s = function (e) { for (var t = a.href, n = c.length; n--;)if (c[n].href === t) return e(); setTimeout(function () { s(e) }) }; function l() { a.addEventListener && a.removeEventListener("load", l), a.media = o || "all" } a.addEventListener && a.addEventListener("load", l), (a.onloadcssdefined = s)(l) }(e, t) }, e = function (e, t, n) { var i = t && void 0 !== t.autoFetchSvg ? t.autoFetchSvg : void 0, o = t && void 0 !== t.async ? t.async : void 0, r = t && void 0 !== t.autoA11y ? t.autoA11y : void 0, a = document.createElement("script"), d = document.scripts[0]; a.src = e, void 0 !== r && a.setAttribute("data-auto-a11y", r ? "true" : "false"), i && (a.setAttributeNode(document.createAttribute("data-auto-fetch-svg")), a.setAttribute("data-fetch-svg-from", t.fetchSvgFrom)), o && a.setAttributeNode(document.createAttribute("defer")), n && n.detectingConflicts && n.detectionIgnoreAttr && a.setAttributeNode(document.createAttribute(n.detectionIgnoreAttr)), d.parentNode.appendChild(a) }; function s(e, t) { var n = t && t.addOn || "", i = t && t.baseFilename || e.license + n, o = t && t.minify ? ".min" : "", r = t && t.fileSuffix || e.method, a = t && t.subdir || e.method; return e.baseUrl + "/releases/" + ("latest" === e.version ? "latest" : "v".concat(e.version)) + "/" + a + "/" + i + o + "." + r } var t, n, i, o, l; try { if (window.FontAwesomeKitConfig) { var u, f = window.FontAwesomeKitConfig, m = { detectingConflicts: f.detectConflictsUntil && new Date <= new Date(f.detectConflictsUntil), detectionIgnoreAttr: "data-fa-detection-ignore", detectionTimeoutAttr: "data-fa-detection-timeout", detectionTimeout: null }; "js" === f.method && (o = m, l = { async: (i = f).asyncLoading.enabled, autoA11y: i.autoA11y.enabled }, "pro" === i.license && (l.autoFetchSvg = !0, l.fetchSvgFrom = i.baseUrl + "/releases/" + ("latest" === i.version ? "latest" : "v".concat(i.version)) + "/svgs"), i.v4shim.enabled && e(s(i, { addOn: "-v4-shims", minify: i.minify.enabled })), e(s(i, { minify: i.minify.enabled }), l, o)), "css" === f.method && function (e, t) { var n, i = a.bind(a, ["fa", "fab", "fas", "far", "fal", "fad"]); e.autoA11y.enabled && (r(i), n = i, "undefined" != typeof MutationObserver && new MutationObserver(n).observe(document, { childList: !0, subtree: !0 })), e.v4shim.enabled && (e.license, e.asyncLoading.enabled ? c(s(e, { addOn: "-v4-shims", minify: e.minify.enabled }), t) : d(s(e, { addOn: "-v4-shims", minify: e.minify.enabled }), t)); e.v4FontFaceShim.enabled && (e.asyncLoading.enabled ? c(s(e, { addOn: "-v4-font-face", minify: e.minify.enabled }), t) : d(s(e, { addOn: "-v4-font-face", minify: e.minify.enabled }), t)); var o = s(e, { minify: e.minify.enabled }); e.asyncLoading.enabled ? c(o, t) : d(o, t) }(f, m), m.detectingConflicts && ((u = document.currentScript.getAttribute(m.detectionTimeoutAttr)) && (m.detectionTimeout = u), document.currentScript.setAttributeNode(document.createAttribute(m.detectionIgnoreAttr)), t = f, n = m, r(function () { var e = document.createElement("script"); n && n.detectionIgnoreAttr && e.setAttributeNode(document.createAttribute(n.detectionIgnoreAttr)), n && n.detectionTimeoutAttr && n.detectionTimeout && e.setAttribute(n.detectionTimeoutAttr, n.detectionTimeout), e.src = s(t, { baseFilename: "conflict-detection", fileSuffix: "js", subdir: "js", minify: t.minify.enabled }), e.async = !0, document.body.appendChild(e) })) } } catch (e) { } }();
--------------------------------------------------------------------------------
/src/objects/CSS2DRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | const THREE = require('../three.js');
6 |
7 | (function () {
8 |
9 | class CSS2DObject extends THREE.Object3D {
10 |
11 | constructor(element) {
12 |
13 | super();
14 | this.element = element || document.createElement('div');
15 | this.element.style.position = 'absolute';
16 | this.element.style.userSelect = 'none';
17 | this.element.setAttribute('draggable', false);
18 |
19 | //[jscastro] some labels must be always visible
20 | this.alwaysVisible = false;
21 |
22 | //[jscastro] layer is needed to be rendered/hidden based on layer visibility
23 | Object.defineProperty(this, 'layer', {
24 | get() { return (this.parent && this.parent.parent ? this.parent.parent.layer : null) }
25 | });
26 |
27 | //[jscastro] implement dispose
28 | this.dispose = function () {
29 | this.remove();
30 | this.element = null;
31 | }
32 | //[jscastro] implement explicit method
33 | this.remove = function () {
34 | if (this.element instanceof Element && this.element.parentNode !== null) {
35 | this.element.parentNode.removeChild(this.element);
36 | }
37 | }
38 |
39 | this.addEventListener('removed', function () {
40 |
41 | this.remove();
42 |
43 | });
44 |
45 | }
46 |
47 | copy(source, recursive) {
48 |
49 | super.copy(source, recursive);
50 | this.element = source.element.cloneNode(true);
51 | return this;
52 |
53 | }
54 |
55 | }
56 |
57 | CSS2DObject.prototype.isCSS2DObject = true; //
58 |
59 | const _vector = new THREE.Vector3();
60 |
61 | const _viewMatrix = new THREE.Matrix4();
62 |
63 | const _viewProjectionMatrix = new THREE.Matrix4();
64 |
65 | const _a = new THREE.Vector3();
66 |
67 | const _b = new THREE.Vector3();
68 |
69 | class CSS2DRenderer {
70 |
71 | constructor() {
72 |
73 | const _this = this;
74 |
75 | let _width, _height;
76 |
77 | let _widthHalf, _heightHalf;
78 |
79 | const cache = {
80 | objects: new WeakMap(),
81 | list: new Map()
82 | };
83 | this.cacheList = cache.list;
84 | const domElement = document.createElement('div');
85 | domElement.style.overflow = 'hidden';
86 | this.domElement = domElement;
87 |
88 | this.getSize = function () {
89 |
90 | return {
91 | width: _width,
92 | height: _height
93 | };
94 |
95 | };
96 |
97 | this.render = function (scene, camera) {
98 |
99 | if (scene.autoUpdate === true) scene.updateMatrixWorld();
100 | if (camera.parent === null) camera.updateMatrixWorld();
101 |
102 | _viewMatrix.copy(camera.matrixWorldInverse);
103 |
104 | _viewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, _viewMatrix);
105 |
106 | renderObject(scene, scene, camera);
107 | zOrder(scene);
108 |
109 | };
110 |
111 | this.setSize = function (width, height) {
112 |
113 | _width = width;
114 | _height = height;
115 | _widthHalf = _width / 2;
116 | _heightHalf = _height / 2;
117 | domElement.style.width = width + 'px';
118 | domElement.style.height = height + 'px';
119 |
120 | };
121 |
122 | function renderObject(object, scene, camera) {
123 |
124 | if (object.isCSS2DObject) {
125 |
126 | //[jscastro] optimize performance and don't update and remove the labels that are not visible
127 | if (!object.visible) {
128 | cache.objects.delete({ key: object.uuid });
129 | cache.list.delete(object.uuid);
130 | object.remove();
131 | }
132 | else {
133 |
134 | object.onBeforeRender(_this, scene, camera);
135 |
136 | _vector.setFromMatrixPosition(object.matrixWorld);
137 |
138 | _vector.applyMatrix4(_viewProjectionMatrix);
139 |
140 | const element = object.element;
141 | var style;
142 | if (/apple/i.test(navigator.vendor)) {
143 |
144 | // https://github.com/mrdoob/three.js/issues/21415
145 | style = 'translate(-50%,-50%) translate(' + Math.round(_vector.x * _widthHalf + _widthHalf) + 'px,' + Math.round(- _vector.y * _heightHalf + _heightHalf) + 'px)';
146 |
147 | } else {
148 |
149 | style = 'translate(-50%,-50%) translate(' + (_vector.x * _widthHalf + _widthHalf) + 'px,' + (- _vector.y * _heightHalf + _heightHalf) + 'px)';
150 |
151 | }
152 |
153 | element.style.WebkitTransform = style;
154 | element.style.MozTransform = style;
155 | element.style.oTransform = style;
156 | element.style.transform = style;
157 |
158 | element.style.display = object.visible && _vector.z >= - 1 && _vector.z <= 1 ? '' : 'none';
159 |
160 | const objectData = {
161 | distanceToCameraSquared: getDistanceToSquared(camera, object)
162 | };
163 |
164 | cache.objects.set({ key: object.uuid }, objectData);
165 | cache.list.set(object.uuid, object);
166 |
167 | if (element.parentNode !== domElement) {
168 |
169 | domElement.appendChild(element);
170 |
171 | }
172 |
173 | object.onAfterRender(_this, scene, camera);
174 |
175 | }
176 | }
177 |
178 | for (let i = 0, l = object.children.length; i < l; i++) {
179 |
180 | renderObject(object.children[i], scene, camera);
181 |
182 | }
183 |
184 |
185 | }
186 |
187 | function getDistanceToSquared(object1, object2) {
188 |
189 | _a.setFromMatrixPosition(object1.matrixWorld);
190 |
191 | _b.setFromMatrixPosition(object2.matrixWorld);
192 |
193 | return _a.distanceToSquared(_b);
194 |
195 | }
196 |
197 | function filterAndFlatten(scene) {
198 |
199 | const result = [];
200 | scene.traverse(function (object) {
201 |
202 | if (object.isCSS2DObject) result.push(object);
203 |
204 | });
205 | return result;
206 |
207 | }
208 |
209 | function zOrder(scene) {
210 |
211 | const sorted = filterAndFlatten(scene).sort(function (a, b) {
212 | //[jscastro] check the objects already exist in the cache
213 | let cacheA = cache.objects.get({ key: a.uuid });
214 | let cacheB = cache.objects.get({ key: b.uuid });
215 |
216 | if (cacheA && cacheB) {
217 | const distanceA = cacheA.distanceToCameraSquared;
218 | const distanceB = cacheB.distanceToCameraSquared;
219 | return distanceA - distanceB;
220 | }
221 |
222 | });
223 |
224 | const zMax = sorted.length;
225 |
226 | for (let i = 0, l = sorted.length; i < l; i++) {
227 |
228 | sorted[i].element.style.zIndex = zMax - i;
229 |
230 | }
231 |
232 | }
233 |
234 | }
235 |
236 | }
237 |
238 | THREE.CSS2DObject = CSS2DObject;
239 | THREE.CSS2DRenderer = CSS2DRenderer;
240 |
241 | })();
242 |
243 | module.exports = exports = { CSS2DRenderer: THREE.CSS2DRenderer, CSS2DObject: THREE.CSS2DObject };
244 |
245 |
--------------------------------------------------------------------------------
/src/objects/effects/BuildingShadows.js:
--------------------------------------------------------------------------------
1 | const SunCalc = require('../../utils/suncalc.js');
2 |
3 | class BuildingShadows {
4 | constructor(options, threebox) {
5 | this.id = options.layerId;
6 | this.type = 'custom';
7 | this.renderingMode = '3d';
8 | this.opacity = 0.5;
9 | this.buildingsLayerId = options.buildingsLayerId;
10 | this.minAltitude = options.minAltitude || 0.10;
11 | this.tb = threebox;
12 | }
13 | onAdd(map, gl) {
14 | this.map = map;
15 | // find layer source
16 | const sourceName = this.map.getLayer(this.buildingsLayerId).source;
17 | this.source = (this.map.style.sourceCaches || this.map.style._otherSourceCaches)[sourceName];
18 | if (!this.source) {
19 | console.warn(`Can't find layer ${this.buildingsLayerId}'s source.`);
20 | }
21 |
22 | // vertex shader of fill-extrusion layer is different in mapbox v1 and v2.
23 | // https://github.com/mapbox/mapbox-gl-js/commit/cef95aa0241e748b396236f1269fbb8270f31565
24 | const vertexSource = this._getVertexSource();
25 | const fragmentSource = `
26 | void main() {
27 | gl_FragColor = vec4(0.0, 0.0, 0.0, 0.7);
28 | }
29 | `;
30 | const vertexShader = gl.createShader(gl.VERTEX_SHADER);
31 | gl.shaderSource(vertexShader, vertexSource);
32 | gl.compileShader(vertexShader);
33 | const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
34 | gl.shaderSource(fragmentShader, fragmentSource);
35 | gl.compileShader(fragmentShader);
36 | this.program = gl.createProgram();
37 | gl.attachShader(this.program, vertexShader);
38 | gl.attachShader(this.program, fragmentShader);
39 | gl.linkProgram(this.program);
40 | gl.validateProgram(this.program);
41 | this.uMatrix = gl.getUniformLocation(this.program, "u_matrix");
42 | this.uHeightFactor = gl.getUniformLocation(this.program, "u_height_factor");
43 | this.uAltitude = gl.getUniformLocation(this.program, "u_altitude");
44 | this.uAzimuth = gl.getUniformLocation(this.program, "u_azimuth");
45 |
46 | if (this.tb.mapboxVersion >= 2.0) {
47 | this.aPosNormal = gl.getAttribLocation(this.program, "a_pos_normal_ed");
48 | } else {
49 | this.aPos = gl.getAttribLocation(this.program, "a_pos");
50 | this.aNormal = gl.getAttribLocation(this.program, "a_normal_ed");
51 | }
52 |
53 | this.aBase = gl.getAttribLocation(this.program, "a_base");
54 | this.aHeight = gl.getAttribLocation(this.program, "a_height");
55 | }
56 | render(gl, matrix) {
57 | if (!this.source) return;
58 |
59 | gl.useProgram(this.program);
60 | const coords = this.source.getVisibleCoordinates().reverse();
61 | const buildingsLayer = this.map.getLayer(this.buildingsLayerId);
62 | const context = this.map.painter.context;
63 | const { lng, lat } = this.map.getCenter();
64 | const pos = this.tb.getSunPosition(this.tb.lightDateTime, [lng, lat]);
65 | gl.uniform1f(this.uAltitude, (pos.altitude > this.minAltitude ? pos.altitude : 0));
66 | gl.uniform1f(this.uAzimuth, pos.azimuth + 3 * Math.PI / 2);
67 | //this.opacity = Math.sin(Math.max(pos.altitude, 0)) * 0.6;
68 | gl.enable(gl.BLEND);
69 | //gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.DST_ALPHA, gl.SRC_ALPHA);
70 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
71 | var ext = gl.getExtension('EXT_blend_minmax');
72 | //gl.blendEquationSeparate(gl.FUNC_SUBTRACT, ext.MIN_EXT);
73 | //gl.blendEquation(gl.FUNC_ADD);
74 | gl.disable(gl.DEPTH_TEST);
75 | for (const coord of coords) {
76 | const tile = this.source.getTile(coord);
77 | const bucket = tile.getBucket(buildingsLayer);
78 | if (!bucket) continue;
79 | const [heightBuffer, baseBuffer] = bucket.programConfigurations.programConfigurations[this.buildingsLayerId]._buffers;
80 | gl.uniformMatrix4fv(this.uMatrix, false, (coord.posMatrix || coord.projMatrix));
81 | gl.uniform1f(this.uHeightFactor, Math.pow(2, coord.overscaledZ) / tile.tileSize / 8);
82 | for (const segment of bucket.segments.get()) {
83 | const numPrevAttrib = context.currentNumAttributes || 0;
84 | const numNextAttrib = 2;
85 | for (let i = numNextAttrib; i < numPrevAttrib; i++) gl.disableVertexAttribArray(i);
86 | const vertexOffset = segment.vertexOffset || 0;
87 | gl.enableVertexAttribArray(this.aNormal);
88 | gl.enableVertexAttribArray(this.aHeight);
89 | gl.enableVertexAttribArray(this.aBase);
90 | bucket.layoutVertexBuffer.bind();
91 | if (this.tb.mapboxVersion >= 2.0) {
92 | gl.enableVertexAttribArray(this.aPosNormal);
93 | gl.vertexAttribPointer(this.aPosNormal, 4, gl.SHORT, false, 8, 8 * vertexOffset);
94 | } else {
95 | gl.enableVertexAttribArray(this.aPos);
96 | gl.vertexAttribPointer(this.aPos, 2, gl.SHORT, false, 12, 12 * vertexOffset);
97 | gl.vertexAttribPointer(this.aNormal, 4, gl.SHORT, false, 12, 4 + 12 * vertexOffset);
98 | }
99 |
100 | heightBuffer.bind();
101 | gl.vertexAttribPointer(this.aHeight, 1, gl.FLOAT, false, 4, 4 * vertexOffset);
102 | baseBuffer.bind();
103 | gl.vertexAttribPointer(this.aBase, 1, gl.FLOAT, false, 4, 4 * vertexOffset);
104 | bucket.indexBuffer.bind();
105 | context.currentNumAttributes = numNextAttrib;
106 | gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2);
107 | }
108 | }
109 | }
110 |
111 | _getVertexSource() {
112 | if (this.tb.mapboxVersion >= 2.0) {
113 | return `
114 | uniform mat4 u_matrix;
115 | uniform float u_height_factor;
116 | uniform float u_altitude;
117 | uniform float u_azimuth;
118 | attribute vec4 a_pos_normal_ed;
119 | attribute lowp vec2 a_base;
120 | attribute lowp vec2 a_height;
121 | void main() {
122 | float base = max(0.0, a_base.x);
123 | float height = max(0.0, a_height.x);
124 |
125 | vec3 pos_nx = floor(a_pos_normal_ed.xyz * 0.5);
126 | mediump vec3 top_up_ny = a_pos_normal_ed.xyz - 2.0 * pos_nx;
127 | float t = top_up_ny.x;
128 | vec4 pos = vec4(pos_nx.xy, t > 0.0 ? height : base, 1);
129 |
130 | float len = pos.z * u_height_factor / tan(u_altitude);
131 | pos.x += cos(u_azimuth) * len;
132 | pos.y += sin(u_azimuth) * len;
133 | pos.z = 0.0;
134 | gl_Position = u_matrix * pos;
135 | }
136 | `;
137 | } else {
138 | return `
139 | uniform mat4 u_matrix;
140 | uniform float u_height_factor;
141 | uniform float u_altitude;
142 | uniform float u_azimuth;
143 | attribute vec2 a_pos;
144 | attribute vec4 a_normal_ed;
145 | attribute lowp vec2 a_base;
146 | attribute lowp vec2 a_height;
147 | void main() {
148 | float base = max(0.0, a_base.x);
149 | float height = max(0.0, a_height.x);
150 | float t = mod(a_normal_ed.x, 2.0);
151 | vec4 pos = vec4(a_pos, t > 0.0 ? height : base, 1);
152 | float len = pos.z * u_height_factor / tan(u_altitude);
153 | pos.x += cos(u_azimuth) * len;
154 | pos.y += sin(u_azimuth) * len;
155 | pos.z = 0.0;
156 | gl_Position = u_matrix * pos;
157 | }
158 | `;
159 | }
160 | }
161 | }
162 |
163 |
164 | module.exports = exports = BuildingShadows;
--------------------------------------------------------------------------------
/examples/16-multilayer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threbox multilayer sample
4 |
5 |
6 |
7 |
8 |
9 |
63 |
64 |
65 |
66 |
67 |
208 |
--------------------------------------------------------------------------------
/tests/unit/utilities.test.js:
--------------------------------------------------------------------------------
1 | function utilitiesTest(instance){
2 |
3 | test('PROJECTION project / unproject', function(t) {
4 |
5 | var coord, projected, expected;
6 |
7 | coord = [0,0,0];
8 | var projected = instance.projectToWorld(coord);
9 | var unprojected = instance.unprojectFromWorld(projected);
10 | var expected = new THREE.Vector3(0,0,0);
11 | vector3Equals(t, projected, expected);
12 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
13 |
14 | coord = [30,30,0];
15 | var projected = instance.projectToWorld(coord);
16 | var unprojected = instance.unprojectFromWorld(projected);
17 | var expected = new THREE.Vector3(-85333.33333333333, -89522.98305691125, 0);
18 | vector3Equals(t, projected, expected);
19 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
20 |
21 | coord = [30,-30,0];
22 | var projected = instance.projectToWorld(coord);
23 | var unprojected = instance.unprojectFromWorld(projected);
24 | var expected = new THREE.Vector3(-85333.33333333333, -89522.9830569113, 0);
25 | vector3Equals(t, projected, expected);
26 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
27 |
28 | coord = [-30,30,0];
29 | var projected = instance.projectToWorld(coord);
30 | var unprojected = instance.unprojectFromWorld(projected);
31 | var expected = new THREE.Vector3(-85333.33333333333, -89522.9830569113, 0);
32 | vector3Equals(t, projected, expected);
33 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
34 |
35 | coord = [-30,-30,0];
36 | var projected = instance.projectToWorld(coord);
37 | var unprojected = instance.unprojectFromWorld(projected);
38 | var expected = new THREE.Vector3(-85333.33333333333, 89522.9830569113, 0);
39 | vector3Equals(t, projected, expected);
40 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
41 |
42 | t.end();
43 | });
44 |
45 | test('PROJECTION project / unproject extended lat/lng range', function(t) {
46 | var coord, projected, expected;
47 |
48 | coord = [180,0,0];
49 | var projected = instance.projectToWorld(coord);
50 | var unprojected = instance.unprojectFromWorld(projected);
51 | var expected = new THREE.Vector3(-511999.99999999994,1.8093822187881337e-11,0);
52 | vector3Equals(t, projected, expected);
53 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
54 |
55 | coord = [-180,0,0];
56 | var projected = instance.projectToWorld(coord);
57 | var unprojected = instance.unprojectFromWorld(projected);
58 | var expected = new THREE.Vector3(511999.99999999994,1.8093822187881337e-11,0);
59 | vector3Equals(t, projected, expected);
60 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
61 |
62 | coord = [0,90,0];
63 | var projected = instance.projectToWorld(coord);
64 | var unprojected = instance.unprojectFromWorld(projected);
65 | var expected = new THREE.Vector3(0,-3042.073317352722,0);
66 | vector3Equals(t, projected, expected);
67 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
68 |
69 |
70 | coord = [0, 85.051129,0];
71 | var projected = instance.projectToWorld(coord);
72 | var unprojected = instance.unprojectFromWorld(projected);
73 | var expected = new THREE.Vector3(0, -512000.00726036413, 0);
74 | vector3Equals(t, projected, expected);
75 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
76 |
77 | coord = [0, -85.051129,0];
78 | var projected = instance.projectToWorld(coord);
79 | var unprojected = instance.unprojectFromWorld(projected);
80 | var expected = new THREE.Vector3(0, -512000.00726036413, 0);
81 | vector3Equals(t, projected, expected);
82 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
83 |
84 |
85 | coord = [300,0,0];
86 | var projected = instance.projectToWorld(coord);
87 | var unprojected = instance.unprojectFromWorld(projected);
88 | var expected = new THREE.Vector3(-853333.3333333333,1.8093822187881337e-11,0);
89 | vector3Equals(t, projected, expected);
90 | vector3Equals(t, new THREE.Vector3(unprojected), new THREE.Vector3(expected));
91 |
92 | t.end();
93 | });
94 |
95 |
96 | test('PROJECTION with altitude', function(t) {
97 | var coord, projected, expected;
98 |
99 | coord = [0,0,10000];
100 | var projected = instance.projectToWorld(coord);
101 | var expected = new THREE.Vector3(0,0,255.52089831565812);
102 | vector3Equals(t, projected, expected);
103 |
104 |
105 | coord = [0,0,-10000];
106 | var projected = instance.projectToWorld(coord);
107 | var expected = new THREE.Vector3(0,0,-255.52089831565812);
108 | vector3Equals(t, projected, expected);
109 |
110 | t.end();
111 | });
112 |
113 | test('PROJECTION projectedUnitsPerMeter', function(t) {
114 |
115 | var pupm1 = instance.projectedUnitsPerMeter(10);
116 | var pupm2 = instance.projectedUnitsPerMeter(-35);
117 |
118 | t.equals(pupm1, 0.025946272004267072);
119 | t.equals(pupm2, 0.03119334195612554);
120 |
121 | t.end();
122 | });
123 |
124 |
125 | // test('PROJECTION project / unproject invalid input', function(t) {
126 | // // TODO: Check for null/undefined/NaN values
127 | // t.end();
128 | // });
129 |
130 |
131 | test('NORMALIZEVERTICES', function(t){
132 |
133 | var input = [
134 | new THREE.Vector3(100, 101, 102),
135 | new THREE.Vector3(103, 104, 105)
136 | ];
137 |
138 | var normalized = instance.utils.normalizeVertices(input);
139 |
140 | t.deepEqual(
141 | normalized.position,
142 | {
143 | x: 101.5,
144 | y: 102.5,
145 | z: 103.5
146 | }
147 | );
148 |
149 | t.deepEqual(
150 | normalized.vertices,
151 | [
152 | {x: -1.5, y: -1.5, z: -1.5},
153 | {x: 1.5, y: 1.5, z: 1.5}
154 | ]
155 | );
156 |
157 | t.end();
158 | })
159 |
160 | var defaults = {
161 | foo: 'bar',
162 | biz: false
163 | }
164 |
165 | test('VALIDATOR empty input', function(t) {
166 | var output = instance.utils._validate({}, defaults);
167 | t.deepEqual(output, defaults);
168 | t.end();
169 | });
170 |
171 | test('VALIDATOR does not overwrite unknown props', function(t) {
172 | var output = instance.utils._validate({a:true}, defaults);
173 |
174 | t.deepEqual(output, {
175 | foo: 'bar',
176 | biz: false,
177 | a: true
178 | });
179 |
180 | t.end();
181 | });
182 |
183 | test('VALIDATOR missing required params throw error', function(t) {
184 | var output = instance.utils._validate({}, {b: null});
185 | t.error(output, 'proper error');
186 | t.end();
187 | });
188 | }
189 |
--------------------------------------------------------------------------------
/examples/09-raycaster.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox raycaster of Objects3D, 3D models and Fill-extrusions
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
228 |
229 |
230 |
--------------------------------------------------------------------------------
/examples/19-fixedzoom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox fixed zoom
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
26 |
365 |
366 |
--------------------------------------------------------------------------------
/examples/15-performance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threebox performance test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
48 |
49 |
50 |
51 |
58 |
59 |
319 |
320 |
321 |
--------------------------------------------------------------------------------
/examples/20-game.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox WASD driving game
4 |
5 |
6 |
7 |
8 |
9 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | Press "W", "A", "S", "D" keys to drive the truck
52 |
53 |
54 |
55 |
56 |
339 |
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `Threebox`
2 |
3 | 
4 | 
5 | [](https://github.com/jscastro76/threebox/releases/)
6 | [](https://www.npmjs.org/package/threebox-plugin)
7 | [](https://www.npmjs.org/package/threebox-plugin)
8 |
9 | A **[*Three.js*](https://threejs.org/)** plugin for **[*Mapbox GL JS*](https://docs.mapbox.com/mapbox-gl-js/examples/)** and **[*Azure Maps*](https://azure.microsoft.com/en-us/services/azure-maps/)** using the [`CustomLayerInterface`](https://docs.mapbox.com/mapbox-gl-js/api/properties/#customlayerinterface) feature. Provides convenient methods to manage objects in lnglat coordinates, and to synchronize the map and scene cameras.
10 |
11 |
12 |
13 |
14 | - - -
15 | ## Latest release
16 |
17 | 
18 | Latest **code release** is [](https://github.com/jscastro76/threebox/releases/), please review the [**Change log**](https://github.com/jscastro76/threebox/blob/master/CHANGELOG.md) for more details.
19 |
20 | Threebox is also available as an **nmp package** [](https://www.npmjs.org/package/threebox-plugin)
21 |
22 | ```js
23 | npm i threebox-plugin
24 | ```
25 |
26 |
27 | - - -
28 |
29 | ## ONLY in this Threebox fork
30 |
31 | |Models built-in & custom animations |Mouse over/out, Selected, Drag&Drop, Drag&Rotate, Wireframe
32 | |---------|-----------------------
33 | | |
34 |
35 | |Tooltips using altitude|Optimization of camera perspective and depth
36 | |----------|-------
37 | | |
38 |
39 | |Runtime style change|Optimized performance through cache
40 | |----------|-------
41 | | |
42 |
43 | |Customizable FOV|Geojson and Points Extrusions
44 | |---------|-------
45 | | |
46 |
47 | |Sunlight illumination for a given datetime and lnglat|Models built-in shadows and sky layer synced with Sunlight
48 | |---------|-------
49 | | |
50 |
51 |
52 |
53 | Only in this fork, there is a list of new features implemented on top of the amazing work from [@peterqliu](https://github.com/peterqliu/threebox/):
54 | - Updated to [**Three.js r132**](https://github.com/mrdoob/three.js/releases/tag/r132).
55 | - Updated to **Mapbox-gl-js v2.2.0**.
56 | - Updated to **Azure Maps v2.0.31**.
57 | - [+20 examples](https://github.com/jscastro76/threebox/tree/master/examples) with all the new features.
58 | - Support for multiple 3D format objects (FBX, GLTF/GLB, Collada, OBJ/MTL).
59 | - Support for 3D extruded shapes from [GeoJson](https://geojson.org/) features or points array.
60 | - Support for CSS2D labels and rich HTML controls through a new LabelManager.
61 | - Support for CSS2D tooltips/title browser-like and mapbox-like.
62 | - Support for built-in Raycaster in Object3D and fill-extrusions together.
63 | - Support for built-in MouseOver/Mouseout, Selected, Drag&Drop, Drag&Rotate, Wireframe in loadedObjects including events.
64 | - Support for wireframing on any Object3D, removing them from the Raycaster.
65 | - Support for [GeoJson](https://geojson.org/) standard features format import and export in different layers.
66 | - Support for Object3D embedded animations, and custom animations on AnimationManager (i.e. embedded animation + translate + rotate).
67 | - Support for multi-layer and multi-floor design of spaces.
68 | - Support for built-in shadows and real Sun light positioning for a given datetime and lnglat coords.
69 | - Support for built-in Mapbox v2 Sky and Terrain layer synced with real Sun light.
70 | - Support for Non-AABB Non Axes Aligned Bounding Box and real model size, including floor projection.
71 | - Support for Object3D auto-centering and 9 default anchor positions customizable through adjustments.
72 | - Support for `setLayerZoomRange` and `setLayoutProperty` on Custom Layers (not available in Mapbox).
73 | - Support for `removeLayer` considering Object3D.
74 | - Support for style change through `setStyle` and keeping Object3D.
75 | - Support for partial and full dispose of Mapbox, Three and Threebox resources and memory.
76 | - Support for Orthographic view, customizable Perspective FOV and fixed-size Object3D.
77 | - Optimization of Camera perspective to have Raycast with pixel-precision level and depth sync between Mapbox and Threebox objects.
78 | - Optimization for loading thousands of objects through cache.
79 | - Available as [npm package](https://www.npmjs.com/package/threebox-plugin)
80 | - Check out [change log](https://github.com/jscastro76/threebox/blob/master/CHANGELOG.md) for more detail.
81 |
82 |
83 |
84 | - - -
85 |
86 |
87 | ## Documentation
88 |
89 |
90 | All the [**Threebox Documentation**](/docs/Threebox.md) has been completely updated, including all the methods, properties and events implemented in Threebox and objects, but still *'work in progress'* adding better documented examples and images to illustrate Threebox capabilities.
91 | - [**Using Threebox**](/docs/Threebox.md#using-threebox)
92 | - [**Loading a 3D Model**](/docs/Threebox.md#loading-a-3d-model)
93 | - [**Threebox methods**](/docs/Threebox.md#threebox-methods)
94 | - [**Object methods**](/docs/Threebox.md#object-methods)
95 | - [**Examples**](/examples/README.md)
96 |
97 |
98 |
99 | - - -
100 |
101 | ## Compatibility/Dependencies
102 |
103 | - [**Three.js 132**](https://github.com/mrdoob/three.js/releases/tag/r132). (already bundled into the Threebox build). If desired, other versions can be swapped in and rebuilt [here](https://github.com/jscastro76/threebox/blob/master/src/three.js), though compatibility is not guaranteed.
104 | - **Mapbox-gl-js v1.11.1. or v.2.0.1**. **Warning**: Despite v1.11.1 still supported, if used, some features from mapbox v.2.0.1 won't be obviously available such as sky layers.
105 | - **Azure Maps v2.0.31.**
106 |
107 |
108 |
109 | - - -
110 |
111 | ## Getting started
112 |
113 | You can use threebox in three different ways.
114 |
115 | #### NPM install
116 | Add threebox to your project via **npm package** [](https://www.npmjs.org/package/threebox-plugin) :
117 | ```js
118 | npm install threebox-plugin
119 | ```
120 |
121 | Then you will need to import Threebox object in your code. Depending your javascript framework this might be different.
122 | ```js
123 | import { Threebox } from 'threebox-plugin';
124 | ```
125 | Depending the framework, wrapper or bundler you ar using, try with this:
126 | ```js
127 | import { Threebox } from 'threebox-plugin/dist/threebox';
128 | ```
129 |
130 |
131 |
132 | #### Use the bundle locally
133 | Download the bundle from [`dist/threebox.js`](dist/threebox.js) or [`dist/threebox.min.js`](dist/threebox.min.js) and include it in a `
137 |
138 | ```
139 |
140 |
141 | #### Public CDNs
142 | Threebox can be also used from different public CDNs:
143 |
144 | ##### jsdelivr
145 | This CDN has the particularity that always requires the version of the package to download individual files.
146 | ```html
147 |
148 |
149 | ```
150 |
151 |
152 |
153 | ##### unpkg
154 | Despite this CDN admits version, if omitted, it will download always the last one published.
155 |
156 | ```html
157 |
158 |
159 | ```
160 |
161 | For an specific version (i.e. v2.2.1) use the followin:
162 | ```html
163 |
164 |
165 | ```
166 |
167 |
168 |
169 | #### Test the samples
170 | Several introductory examples are [here](https://github.com/jscastro76/threebox/tree/master/examples).
171 | To run them, create a `config.js` file with your Mapbox-gl-js access token, alongside and in the format of [the template](https://github.com/jscastro76/threebox/blob/master/examples/config_template.js).
172 |
173 |
174 |
175 | - - -
176 |
177 | ## Contributing
178 | - Clone the [Github repo](https://github.com/jscastro76/threebox/).
179 | - Build the library with `npm run build` to get the minimized version, or `npm run dev` to get the development version and rebuild continuously as you develop.
180 | - Both commands will output a bundle in [`dist/`](dist/) folder.
181 |
182 | #### Unit tests
183 | Tests live [here](/tests).
184 | - Build first the test bundle with `npm run test`, this will create [`tests\threebox-tests-bundle.js`](tests/threebox-tests-bundle.js)
185 | - Then in your preferred browser navigate to [`threebox-tests.html`](https://github.com/jscastro76/threebox/blob/master/tests/threebox-tests.html) and check the console for test results.
186 |
187 | #### How to build the project in Visual Studio
188 | Sample to get a full build from scratch for Visual Studio:
189 | - Install [Node.js](https://nodejs.org/en/)
190 | - Clone the repo and open a new Project using main.js
191 | - Install / Update the packages browserify, tape, ncp, uglyfy, watchify.
192 | - Right click on the project at the Solution Explorer > Open Node.js Interactive Window:
193 | - execute `.npm [ProjectName] init -y`
194 | - execute `.npm [ProjectName] install`
195 | - execute `.npm [ProjectName] i`
196 | - execute `.npm [ProjectName] run dev` or `.npm run build
197 | `
198 |
199 |
200 |
--------------------------------------------------------------------------------
/examples/11-animation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Threebox animated soldier
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
90 |
91 |
92 |
93 |
99 |
100 |
101 | Add or Select an object
102 |
103 |
104 |
105 | Select the soldier, drag, rotate, wireframe and play animation
106 |
107 |
363 |
--------------------------------------------------------------------------------