5 |
6 | Dojo 3D - Story Book
7 |
8 |
9 |
10 |
11 |
12 |
13 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/lib/dojo3d/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "incremental": true,
4 | "target": "es2017",
5 | "outDir": "build/main",
6 | "rootDir": "src",
7 | "moduleResolution": "node",
8 | "module": "ES2015",
9 | "declaration": true,
10 | "inlineSourceMap": true,
11 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
12 | //"resolveJsonModule": true /* Include modules imported with .json extension. */,
13 |
14 | // "strict": true /* Enable all strict type-checking options. */,
15 |
16 | /* Strict Type-Checking Options */
17 | // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
18 | // "strictNullChecks": true /* Enable strict null checks. */,
19 | // "strictFunctionTypes": true /* Enable strict checking of function types. */,
20 | // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
21 | // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
22 | // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
23 |
24 | /* Additional Checks */
25 | "noUnusedLocals": true /* Report errors on unused locals. */,
26 | "noUnusedParameters": false /* Report errors on unused parameters. */,
27 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
28 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
29 |
30 | /* Debugging Options */
31 | "traceResolution": false /* Report module resolution log messages. */,
32 | "listEmittedFiles": false /* Print names of generated files part of the compilation. */,
33 | "listFiles": false /* Print names of files part of the compilation. */,
34 | "pretty": true /* Stylize errors and messages using color and context. */,
35 |
36 | /* Experimental Options */
37 | // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
38 | // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
39 |
40 | "lib": ["es2017", "dom"],
41 | "types": [],
42 | "typeRoots": ["node_modules/@types", "src/types"]
43 | },
44 | "include": ["src/**/*.ts"],
45 | "exclude": ["node_modules/**"],
46 | "compileOnSave": false
47 | }
48 |
--------------------------------------------------------------------------------
/lib/dojo3d/src/lib/UI.ts:
--------------------------------------------------------------------------------
1 | export class Answer {
2 |
3 | constructor(public answer, public optionNumber) {
4 | this.toString = (): string => {
5 | return this.answer;
6 | }
7 | }
8 |
9 | }
10 |
11 | export class UI {
12 |
13 | public static showMessage(msg: string, x: number = 10, y: number = 10) {
14 | let msgbox = document.getElementById("msgbox");
15 |
16 | if (!msgbox) {
17 | const msgDiv = document.createElement("div");
18 | msgDiv.id = "msgbox";
19 | document.children[0].appendChild(msgDiv);
20 | msgbox = document.getElementById("msgbox");
21 | }
22 |
23 | msgbox.style.top = y + "%";
24 | msgbox.style.left = x + "%";
25 | msgbox.style.display = "block";
26 |
27 | msgbox.innerHTML = msg;
28 |
29 | return msgbox;
30 | }
31 |
32 | public static hideMessage() {
33 | let msgbox = document.getElementById("msgbox");
34 | if (msgbox) {
35 | msgbox.style.display = "none";
36 | }
37 | }
38 |
39 | public static async ask(question: string, options: string[], x: number = 10, y: number = 10): Promise {
40 | let msgbox = this.showMessage(question, x, y);
41 |
42 | return await new Promise(resolve => {
43 | const ul = document.createElement("ul");
44 |
45 | for (const o of options) {
46 | let li = document.createElement("li");
47 |
48 | let a = document.createElement("a");
49 | a.href = "#";
50 | a.innerHTML = o;
51 |
52 | a.addEventListener('click', () => {
53 | let optionIndex = options.indexOf(o) + 1;
54 | this.log(`You clicked option ${optionIndex}: ${o}`)
55 | resolve(new Answer(o, optionIndex));
56 | });
57 |
58 | li.appendChild(a);
59 | ul.appendChild(li);
60 |
61 | }
62 |
63 | msgbox.appendChild(ul);
64 | });
65 | }
66 |
67 | public static async wait(seconds: number) {
68 | await new Promise(r => setTimeout(r, seconds * 1000));
69 | }
70 |
71 |
72 | public static log(msg: string) {
73 | console.log(msg);
74 | }
75 |
76 | public static speak(message: string, voice: number = 0) {
77 |
78 | this.log("Saying:" + message);
79 |
80 | const msg = new SpeechSynthesisUtterance(message);
81 | var voices = window.speechSynthesis.getVoices()
82 | msg.voice = voices[voice]
83 |
84 | window.speechSynthesis.speak(msg)
85 | }
86 | }
--------------------------------------------------------------------------------
/lib/dojo3d/src/examples/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "categories": [
3 | "characters",
4 | "vehicles",
5 | "scenes"
6 | ],
7 | "models": [
8 | {
9 | "id": "9d37a98a-d606-4fc0-97d1-0e99ea3b7830",
10 | "name": "Phoenix Bird",
11 | "attribution": "https://sketchfab.com/norberto3d; License: Creative Commons Attribution ",
12 | "path": "characters/phoenix_bird/scene.gltf",
13 | "category": "characters"
14 | },
15 | {
16 | "id": "9a37a98a-d606-4fc0-97d1-0ea9ea3b7830",
17 | "name": "Box Man",
18 | "attribution": "Jan Bláha https://github.com/swift502/Sketchbook CC-0 license",
19 | "path": "characters/box_man/box_man.glb",
20 | "category": "characters"
21 | },
22 | {
23 | "id": "6a37a98a-d606-4fc0-97d1-0ea9ea3b7830",
24 | "name": "Banana Man",
25 | "attribution": "https://sketchfab.com/chuchodorado416; License: Creative Commons Attribution",
26 | "path": "characters/banana_dancing_fortnite/scene.gltf",
27 | "category": "characters"
28 | },
29 | {
30 | "id": "5a37a98a-d606-4fc0-97d1-0ea9ea3b7830",
31 | "name": "Among",
32 | "attribution": "https://sketchfab.com/juang3d; License: Creative Commons Attribution",
33 | "path": "characters/among_us_character/scene.gltf",
34 | "category": "characters"
35 | },
36 | {
37 | "id": "9d37a98a-d606-4fc0-97d1-0e19ea3b7830",
38 | "name": "Hover Bike (The Rocket)",
39 | "attribution": "https://sketchfab.com/TuppsM; License: Creative Commons Attribution",
40 | "path": "vehicles/hover_bike_-_the_rocket/scene.gltf",
41 | "category": "vehicles"
42 | },
43 | {
44 | "id": "9d37a98a-d606-4fc0-97d1-0e39ea3b7830",
45 | "name": "Wipeout 4K",
46 | "attribution": "https://sketchfab.com/Swiss_Fox; License: Creative Commons Attribution-NonCommercial",
47 | "path": "vehicles/qi_rd_4k/scene.gltf",
48 | "category": "vehicles"
49 | },
50 | {
51 | "id": "sd37a98a-d606-4fc0-97d1-0e39ea3b7830",
52 | "name": "Medieval Fantasy Book",
53 | "attribution": "https://sketchfab.com/stefan.lengyel1; License: Creative Commons Attribution",
54 | "path": "scenes/medieval_fantasy_book/scene.gltf",
55 | "category": "scenes"
56 | },
57 | {
58 | "id": "sa37a98a-d606-4fc0-97d1-0e39ea3b7830",
59 | "name": "Happy Halloween",
60 | "attribution": "https://sketchfab.com/JessSwynn; License: Creative Commons Attribution",
61 | "path": "scenes/happy_halloween/scene.gltf",
62 | "category": "scenes"
63 | },
64 | {
65 | "id": "aa37a98a-d606-4fc0-97d1-0e39ea3b7830",
66 | "name": "Terrain 01 - Low Poly",
67 | "attribution": "https://sketchfab.com/Jordi_Martos77; License: Creative Commons Attribution",
68 | "path": "scenes/terrain_low_poly/scene.gltf",
69 | "category": "scenes"
70 | }
71 | ]
72 | }
--------------------------------------------------------------------------------
/lib/dojo3d/src/examples/example-halloween.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dojo 3D - Story Book
7 |
8 |
9 |
10 |
11 |
12 |
13 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Dojo3D
2 |
3 | An all-in-one library for 3D story telling, aimed at all ages of coder. No software installation required, useful for CoderDojo, Hour of Code activities etc.
4 |
5 | 
6 |
7 | # Example Library Usage
8 |
9 | See the Happy Halloween example https://repl.it/@webprofusionchr/dojo3d-halloween
10 |
11 | Jump straight to `Our main code for a simple story` to see the main story code.
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 | Dojo 3D - Example Story Book
19 |
20 |
24 |
25 |
26 |
27 |
86 |
87 |
88 | ```
89 |
90 | # Other Examples
91 |
92 | [Low Poly Terrain Scene](https://repl.it/@webprofusionchr/amongyou)
93 | 
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dojo3D
2 |
3 | An all-in-one library for 3D story telling, aimed at all ages of coder. No software installation required, useful for CoderDojo, Hour of Code activities etc.
4 |
5 | 
6 |
7 | # Example Library Usage
8 |
9 | See the Happy Halloween example https://repl.it/@webprofusionchr/dojo3d-halloween
10 |
11 | Jump straight to `Our main code for a simple story` to see the main story code.
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 | Dojo 3D - Example Story Book
19 |
20 |
24 |
25 |
26 |
27 |
86 |
87 |
88 | ```
89 |
90 | # Other Examples
91 |
92 | [Low Poly Terrain Scene](https://repl.it/@webprofusionchr/amongyou)
93 | 
94 |
95 | # Building/testing the library from source
96 |
97 | - delete build folder
98 |
99 | - `npx rollup -w -c rollup.config.js`
100 |
101 | - `npx http-server ` (localhost:8080/src/examples)
102 |
--------------------------------------------------------------------------------
/lib/dojo3d/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dojo3d",
3 | "version": "1.0.0",
4 | "description": "dojo3d",
5 | "main": "build/main/index.js",
6 | "typings": "build/main/index.d.ts",
7 | "module": "build/module/index.js",
8 | "repository": "https://github.com/webprofusion-chrisc/dojo3d",
9 | "license": "MIT",
10 | "keywords": [],
11 | "scripts": {
12 | "build": "run-p build:*",
13 | "build:main": "tsc -p tsconfig.json",
14 | "build:module": "tsc -p tsconfig.module.json",
15 | "build-lib": "rollup -c rollup.config.",
16 | "fix": "run-s fix:*",
17 | "fix:prettier": "prettier \"src/**/*.ts\" --write",
18 | "fix:lint": "eslint src --ext .ts --fix",
19 | "test": "run-s build test:*",
20 | "test:lint": "eslint src --ext .ts",
21 | "test:prettier": "prettier \"src/**/*.ts\" --list-different",
22 | "test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.ts}\"",
23 | "test:unit": "nyc --silent ava",
24 | "check-cli": "run-s test diff-integration-tests check-integration-tests",
25 | "check-integration-tests": "run-s check-integration-test:*",
26 | "diff-integration-tests": "mkdir -p diff && rm -rf diff/test && cp -r test diff/test && rm -rf diff/test/test-*/.git && cd diff && git init --quiet && git add -A && git commit --quiet --no-verify --allow-empty -m 'WIP' && echo '\\n\\nCommitted most recent integration test output in the \"diff\" directory. Review the changes with \"cd diff && git diff HEAD\" or your preferred git diff viewer.'",
27 | "watch:build": "tsc -p tsconfig.json -w",
28 | "watch:test": "nyc --silent ava --watch",
29 | "cov": "run-s build test:unit cov:html cov:lcov && open-cli coverage/index.html",
30 | "cov:html": "nyc report --reporter=html",
31 | "cov:lcov": "nyc report --reporter=lcov",
32 | "cov:send": "run-s cov:lcov && codecov",
33 | "cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100",
34 | "doc": "run-s doc:html && open-cli build/docs/index.html",
35 | "doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs",
36 | "doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json",
37 | "doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs",
38 | "version": "standard-version",
39 | "reset-hard": "git clean -dfx && git reset --hard && npm i",
40 | "prepare-release": "run-s reset-hard test cov:check doc:html version doc:publish"
41 | },
42 | "engines": {
43 | "node": ">=10"
44 | },
45 | "dependencies": {
46 | "@tweenjs/tween.js": "^18.6.4",
47 | "three": "^0.121.1"
48 | },
49 | "devDependencies": {
50 | "@ava/typescript": "^1.1.1",
51 | "@istanbuljs/nyc-config-typescript": "^1.0.1",
52 | "@rollup/plugin-buble": "^0.21.3",
53 | "@rollup/plugin-commonjs": "^15.1.0",
54 | "@rollup/plugin-node-resolve": "^9.0.0",
55 | "@rollup/plugin-typescript": "^6.0.0",
56 | "@typescript-eslint/eslint-plugin": "^4.0.1",
57 | "@typescript-eslint/parser": "^4.0.1",
58 | "ava": "^3.12.1",
59 | "codecov": "^3.5.0",
60 | "cspell": "^4.1.0",
61 | "cz-conventional-changelog": "^3.3.0",
62 | "eslint": "^7.8.0",
63 | "eslint-config-prettier": "^6.11.0",
64 | "eslint-plugin-eslint-comments": "^3.2.0",
65 | "eslint-plugin-functional": "^3.0.2",
66 | "eslint-plugin-import": "^2.22.0",
67 | "gh-pages": "^3.1.0",
68 | "npm-run-all": "^4.1.5",
69 | "nyc": "^15.1.0",
70 | "open-cli": "^6.0.1",
71 | "prettier": "^2.1.1",
72 | "rollup": "^2.32.1",
73 | "rollup-plugin-delete": "^2.0.0",
74 | "rollup-plugin-sourcemaps": "^0.6.3",
75 | "rollup-plugin-terser": "^7.0.2",
76 | "rollup-plugin-uglify": "^6.0.4",
77 | "standard-version": "^9.0.0",
78 | "ts-node": "^9.0.0",
79 | "typedoc": "^0.19.0",
80 | "typescript": "^4.0.3"
81 | },
82 | "files": [
83 | "build/main",
84 | "build/module",
85 | "!**/*.spec.*",
86 | "!**/*.json",
87 | "CHANGELOG.md",
88 | "LICENSE",
89 | "README.md"
90 | ],
91 | "ava": {
92 | "failFast": true,
93 | "timeout": "60s",
94 | "typescript": {
95 | "rewritePaths": {
96 | "src/": "build/main/"
97 | }
98 | },
99 | "files": [
100 | "!build/module/**"
101 | ]
102 | },
103 | "config": {
104 | "commitizen": {
105 | "path": "cz-conventional-changelog"
106 | }
107 | },
108 | "prettier": {
109 | "singleQuote": true
110 | },
111 | "nyc": {
112 | "extends": "@istanbuljs/nyc-config-typescript",
113 | "exclude": [
114 | "**/*.spec.js"
115 | ]
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/lib/dojo3d/src/lib/world.ts:
--------------------------------------------------------------------------------
1 | import { BoxGeometry, DirectionalLight, Mesh, MeshStandardMaterial, PerspectiveCamera, PlaneBufferGeometry, Scene, sRGBEncoding, Vector3, WebGLRenderer } from 'three';
2 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
3 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
4 | import { Model, ModelCatalog } from './Model';
5 | import { SceneObject } from './SceneObject';
6 | import TWEEN from '@tweenjs/tween.js';
7 |
8 | export interface Viewpoint {
9 | title: string
10 | position: Vector3;
11 | }
12 |
13 | export interface LoadedModel {
14 | obj: any;
15 | animationClips: any;
16 | }
17 |
18 | class World {
19 |
20 | scene: Scene;
21 | camera: PerspectiveCamera;
22 | renderer: WebGLRenderer;
23 | controls: OrbitControls;
24 |
25 | modelCatalog: ModelCatalog;
26 |
27 | sceneObjects: SceneObject[] = [];
28 |
29 | isWorldCreated = false;
30 |
31 | prevTime = 0;
32 |
33 | assetsBaseUrl = "https://dojo3d.s3.amazonaws.com/";
34 |
35 | viewpoints: Viewpoint[] = [];
36 | sceneCenter: Vector3 = new Vector3(0, 0, 0);
37 |
38 | lastLoggedCameraPos: Vector3 = null;
39 |
40 | log(msg: string) {
41 | console.log(msg);
42 | }
43 |
44 | constructor(createWorld = true) {
45 | this.sceneObjects = [];
46 |
47 | if (createWorld == true) {
48 | this.create();
49 | }
50 | }
51 |
52 | public setAssetsBaseUrl(url: string) {
53 | this.assetsBaseUrl = url;
54 | }
55 |
56 | public addCube(w = 1, h = 1, d = 1, x = 0, y = 0, z = 0) {
57 | this.log(`Creating and adding cube w:${w},h:${h},d:${d} at x:${x},y:${y},z:${z}`);
58 |
59 | var geometry = new BoxGeometry(w, h, d);
60 | var material = new MeshStandardMaterial({ color: 0x00ff00 });
61 | var cube = new Mesh(geometry, material);
62 | cube.receiveShadow = true;
63 | cube.position.set(x, y, z);
64 |
65 | this.scene.add(cube);
66 |
67 | return cube;
68 | }
69 |
70 | public addGround() {
71 | const mesh = new Mesh(new PlaneBufferGeometry(100, 100), new MeshStandardMaterial({ color: 0x999999 }));
72 | mesh.rotation.x = - Math.PI / 2;
73 | mesh.receiveShadow = true;
74 |
75 | this.scene.add(mesh);
76 |
77 | return mesh;
78 | }
79 |
80 | public addLights() {
81 | var dirLight = new DirectionalLight(0xffffff, 1);
82 | dirLight.position.set(5, 2, 8);
83 | this.scene.add(dirLight);
84 | }
85 |
86 | public create() {
87 | if (!this.isWorldCreated) {
88 | this.scene = new Scene();
89 | this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
90 |
91 | this.renderer = new WebGLRenderer();
92 | this.renderer.setSize(window.innerWidth, window.innerHeight);
93 | this.renderer.outputEncoding = sRGBEncoding;
94 | this.renderer.shadowMap.enabled = true;
95 |
96 | document.body.appendChild(this.renderer.domElement);
97 | window.addEventListener('resize', () => { this.resize(); }, false);
98 |
99 | this.camera.position.z = 5;
100 |
101 | this.controls = new OrbitControls(this.camera, this.renderer.domElement);
102 |
103 | this.render(0);
104 |
105 | // report camera pos
106 |
107 | // log camera pos
108 | setInterval(() => {
109 | const camerapos = this.camera.getWorldPosition(this.sceneCenter);
110 | if (this.lastLoggedCameraPos != camerapos) {
111 | const positionString = `{x:${camerapos.x.toFixed(3)},y:${camerapos.y.toFixed(3)},z:${camerapos.z.toFixed(3)}}`;
112 | this.log(positionString);
113 |
114 | const statsDiv = document.getElementById("stats");
115 | if (statsDiv) {
116 | statsDiv.innerHTML = positionString;
117 | }
118 | }
119 | }, 3000);
120 |
121 | }
122 | }
123 |
124 | resize() {
125 |
126 | const { clientHeight, clientWidth } = this.renderer.domElement.parentElement;
127 |
128 | this.camera.aspect = clientWidth / clientHeight;
129 | this.camera.updateProjectionMatrix();
130 |
131 | this.renderer.setSize(clientWidth, clientHeight);
132 |
133 | this.log(`Resized: ${clientWidth} ${clientHeight}`);
134 | }
135 |
136 | public async fetchPrefabModels() {
137 | this.modelCatalog =
138 | await fetch(this.assetsBaseUrl + 'models/index.json', {
139 | method: 'GET', mode: 'cors', headers: {
140 | 'Content-Type': 'application/json',
141 | 'Accept': 'application/octet-stream'
142 | }
143 | })
144 |
145 | .then(response => response.json());
146 | }
147 |
148 | public getPrefabModel(id: string): Model {
149 | return this.modelCatalog.models.find(m => m.id == id);
150 | }
151 |
152 | public getPrefabModelByName(name: string): Model {
153 | return this.modelCatalog.models.find(m => m.name == name);
154 | }
155 |
156 | public async addSceneObject(definition: Model, scale = 1) {
157 |
158 | let url = definition.path;
159 |
160 | if (!url.startsWith("http")) {
161 | url = this.assetsBaseUrl + "models/" + definition.path;
162 | }
163 |
164 | const loadedModel = await this.loadModel(url, true, scale);
165 |
166 | var sceneObject = new SceneObject(loadedModel, definition);
167 |
168 | this.sceneObjects.push(sceneObject);
169 |
170 | return sceneObject;
171 | }
172 |
173 | public async addSceneModelAsObject(m: LoadedModel, scale = 1) {
174 |
175 |
176 | m.obj.scale.set(scale, scale, scale);
177 | this.scene.add(m.obj);
178 |
179 | var sceneObject = new SceneObject(m, null);
180 |
181 | this.sceneObjects.push(sceneObject);
182 |
183 | return sceneObject;
184 | }
185 |
186 | public async loadModel(gltf: string, addModelToScene: boolean = true, scale = 1): Promise {
187 | var loader = new GLTFLoader();
188 |
189 | // var dracoLoader = new DRACOLoader();
190 | //dracoLoader.setDecoderPath('/examples/js/libs/draco/');
191 | //loader.setDRACOLoader(dracoLoader);
192 |
193 | return new Promise((resolve, reject) => {
194 |
195 |
196 | loader.load(
197 | // resource URL
198 | gltf,
199 | // called when the resource is loaded
200 | (gltf) => {
201 |
202 |
203 | const scene = gltf.scene || gltf.scenes[0];
204 | const clips = gltf.animations || [];
205 |
206 | //scale new scene
207 | scene.scale.set(scale, scale, scale);
208 |
209 | if (addModelToScene) {
210 |
211 | /*
212 | gltf.animations; // Array
213 | gltf.scene; // THREE.Group
214 | gltf.scenes; // Array
215 | gltf.cameras; // Array
216 | gltf.asset; // Object
217 | */
218 |
219 |
220 | gltf.scene.scale.set(scale, scale, scale);
221 |
222 | this.scene.add(gltf.scene);
223 |
224 | gltf.animations; // Array
225 | gltf.scene; // THREE.Group
226 | gltf.scenes; // Array
227 | gltf.cameras; // Array
228 | gltf.asset; // Object
229 | }
230 |
231 | resolve({ obj: scene, animationClips: clips });
232 | },
233 | // called while loading is progressing
234 | (xhr) => {
235 |
236 | this.log((xhr.loaded / xhr.total * 100) + '% loaded');
237 |
238 | },
239 | // called when loading has errors
240 | (error) => {
241 | reject('An error happened:' + error);
242 |
243 | }
244 | );
245 |
246 | });
247 | }
248 |
249 |
250 |
251 | public render(time) {
252 | requestAnimationFrame((t) => {
253 | this.render(t);
254 | TWEEN.update(t);
255 | });
256 |
257 | this.controls.update();
258 |
259 | this.renderer.render(this.scene, this.camera);
260 |
261 | const deltaTime = (time - this.prevTime) / 1000;
262 |
263 | for (let o of this.sceneObjects) {
264 | o.onUpdate(deltaTime);
265 | }
266 |
267 | this.renderer.render(this.scene, this.camera);
268 |
269 | this.prevTime = time;
270 | }
271 |
272 | getCameraPositionAndDirection() {
273 | return {
274 | direction: this.camera.getWorldDirection(this.controls.center.clone()),
275 | position: this.camera.getWorldPosition(this.controls.center.clone())
276 | }
277 | }
278 |
279 | setViewpoint(title: string) {
280 | this.log("jumping to viewpoint " + title);
281 |
282 | const vp = this.viewpoints.find(v => v.title.toLowerCase() == title.toLowerCase());
283 |
284 | if (!vp) {
285 | this.log(`Viewpoint named ${title} not found`);
286 | return;
287 | }
288 |
289 | const viewpoint = vp.position;
290 |
291 | this.camera.position.set(viewpoint.x, viewpoint.y, viewpoint.z);
292 | }
293 |
294 | animateToViewpoint(title: string, timeSeconds: number = 1) {
295 |
296 | this.log("animating to viewpoint " + title);
297 |
298 | const vp = this.viewpoints.find(v => v.title.toLowerCase() == title.toLowerCase());
299 |
300 | if (!vp) {
301 | this.log(`Viewpoint named ${title} not found`);
302 | return false;
303 | }
304 |
305 | const view = vp.position;
306 | const viewpoint = new Vector3(view.x, view.y, view.z);
307 |
308 | var cameraPos = this.camera.position.clone();
309 |
310 | if (viewpoint.equals(cameraPos)) {
311 | this.log("nothing to tween, skipping.");
312 | return false;
313 | }
314 |
315 |
316 | return new Promise((resolve, reject) => {
317 | new TWEEN.Tween(cameraPos)
318 | .to(viewpoint, timeSeconds * 1000)
319 | .easing(TWEEN.Easing.Cubic.InOut)
320 | .onUpdate((o, e) => {
321 |
322 | // update camera position
323 | this.camera.position.x = cameraPos.x;
324 | this.camera.position.y = cameraPos.y;
325 | this.camera.position.z = cameraPos.z;
326 |
327 | })
328 | .start()
329 | .onComplete(() => {
330 | resolve(true);
331 | });
332 | });
333 |
334 | }
335 |
336 | setViewpoints(viewpoints: Viewpoint[]) {
337 | this.viewpoints = viewpoints;
338 | }
339 |
340 | explainModels() {
341 |
342 | this.log("Models available:");
343 |
344 | for (let m of this.modelCatalog.models) {
345 | this.log(`${m.name} (${m.category})`);
346 | }
347 |
348 | }
349 | }
350 |
351 | export { World }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------