Kaynağa Gözat

reset to "added examples/webgl_camera_minimap.html & examples/js/misc/Minimap.js" and commit the added files only

LeonYuanYao 5 yıl önce
ebeveyn
işleme
8d328997b8

+ 160 - 0
examples/js/misc/Minimap.js

@@ -0,0 +1,160 @@
+/**
+ * @author Yuan Yao https://github.com/LeonYuanYao
+ * 
+ * A Minimap viewing tool for three.js
+ *
+ * @param {THREE.WebGLRenderer} renderer
+ * @param {THREE.Scene} scene
+ * @param {THREE.Camera} mainCamera
+ * @param {Object} params
+ */
+
+THREE.Minimap = function(renderer, scene, mainCamera, params) {
+
+    this.renderer = renderer;
+    this.scene = scene;
+    this.object = mainCamera;
+    this.viewRange = params.viewRange !== undefined ? params.viewRange : 1000;
+    this.mapSize = params.mapSize !== undefined ? params.mapSize : 300;
+    this.heightOffset = params.heightOffset !== undefined ? params.heightOffset : 10;
+
+    var scope = this;
+    var clientWidth, clientHeight;
+    var updatePos, idcPos;
+
+    var viewport = new THREE.Vector4();
+    renderer.getCurrentViewport(viewport);
+
+    var up = mainCamera.up.clone(), lookat = new THREE.Vector3(), lastLookat = new THREE.Vector3();
+    getLookatVecProjected(mainCamera, lastLookat);
+
+    var minimapGroup = new THREE.Group();
+    var minimapCamera = new THREE.OrthographicCamera(
+        - scope.viewRange / 2,
+        scope.viewRange / 2,
+        scope.viewRange / 2,
+        - scope.viewRange / 2,
+        0.01,
+        10000
+    );
+    
+    // set orthographic camera always look downward
+    minimapCamera.lookAt(up.clone().negate());
+    minimapGroup.add(minimapCamera);
+
+    var mainCameraHelper = new THREE.CameraHelper(mainCamera);
+    minimapGroup.add(mainCameraHelper);
+
+    scene.add(minimapGroup);
+
+    initBackplane(0x000000);
+
+    initIndicator();
+
+
+    Minimap.prototype.getCamera = function () {
+        return minimapCamera;
+    };
+
+
+    Minimap.prototype.setMinimapVisibility = function (flag) {
+        minimapCamera.backplane.visible = flag;
+        minimapCamera.indicator.visible = flag;
+        mainCameraHelper.visible = flag;
+    };
+
+
+    Minimap.prototype.renderMinimap = function (){
+
+        setAutoClear(false);
+
+        // render minimap
+        scope.setMinimapVisibility(true);
+        renderMinimap();
+
+        // set the render state back to original
+        scope.renderer.setViewport(0, 0, clientWidth, clientHeight);
+		scope.renderer.setScissor(0, 0, clientWidth, clientHeight);
+        scope.renderer.setScissorTest(true);
+        setAutoClear(true);
+    };
+
+
+
+    /**
+     * Private functions
+     */
+    function setAutoClear(flag) {
+        scope.renderer.autoClear = flag;
+        scope.renderer.autoClearColor = flag;
+        scope.renderer.autoClearDepth = flag;
+    }
+
+
+    function renderMinimap() {
+        updateMinimapCamera();
+        scope.renderer.setViewport(0, 0, scope.mapSize, scope.mapSize);
+        scope.renderer.render(scene, minimapCamera);
+    }
+
+
+    function initBackplane(color) {
+        var width = Math.abs(minimapCamera.left - minimapCamera.right);
+        var height = Math.abs(minimapCamera.top - minimapCamera.bottom);
+        var plane = new THREE.Mesh(
+            new THREE.PlaneBufferGeometry(width, height, 1),
+            new THREE.MeshBasicMaterial({ color: color, side: THREE.FrontSide })
+        );
+        plane.quaternion.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2);
+        minimapGroup.add(plane);
+        minimapCamera.backplane = plane;
+    }
+
+
+    function initIndicator() {
+        var dot = new THREE.Mesh(
+            new THREE.CircleBufferGeometry(scope.mapSize / 45, 16, 0, 2 * Math.PI),
+            new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide })
+        );
+        dot.rotateX(Math.PI / 2);
+        dot.visible = false;
+        minimapGroup.add(dot);
+
+        minimapCamera.indicator = dot;
+    }
+
+
+    function getLookatVecProjected(object, result) {
+        object.getWorldDirection(result);
+        result.projectOnPlane(up); // get the lookat vector projected on the orthographic camera
+    }
+
+
+    function updateMinimapCamera() {
+        clientWidth = scope.renderer.domElement.clientWidth;
+        clientHeight = scope.renderer.domElement.clientHeight;
+
+        // update view frustum
+        minimapCamera.left = - scope.viewRange / 2;
+        minimapCamera.right = scope.viewRange / 2;
+        minimapCamera.top = scope.viewRange / 2;
+        minimapCamera.bottom = - scope.viewRange / 2;
+        minimapCamera.updateProjectionMatrix();
+
+        // update position
+        updatePos = mainCamera.position.clone().add(up.clone().multiplyScalar(scope.heightOffset));
+        minimapCamera.position.set(updatePos.x, updatePos.y, updatePos.z);
+        minimapCamera.updateMatrixWorld();
+
+        minimapCamera.backplane.position.set(minimapCamera.position.x, minimapCamera.position.y, minimapCamera.position.z - minimapCamera.far);
+        minimapCamera.backplane.updateMatrixWorld();
+
+        var idc = minimapCamera.indicator;
+        idcPos = minimapCamera.position.clone().add(up.clone().negate().multiplyScalar(0.02));
+        // idcPos = mainCamera.position.clone().add(up.clone().negate().multiplyScalar(0.02));
+        idc.position.set(idcPos.x, idcPos.y, idcPos.z);
+        idc.updateMatrixWorld();
+
+        getLookatVecProjected(mainCamera, lastLookat); // store the last lookat vector
+    }
+}

+ 12 - 0
examples/jsm/misc/Minimap.d.ts

@@ -0,0 +1,12 @@
+/**
+ * @author Yuan Yao https://github.com/LeonYuanYao
+ *
+ * A Minimap viewing tool for three.js
+ *
+ * @param {WebGLRenderer} renderer
+ * @param {Scene} scene
+ * @param {Camera} mainCamera
+ * @param {Object} params
+ */
+declare var Minimap: (renderer: any, scene: any, mainCamera: any, params: any) => void;
+export { Minimap };

+ 178 - 0
examples/jsm/misc/Minimap.js

@@ -0,0 +1,178 @@
+/**
+ * @author Yuan Yao https://github.com/LeonYuanYao
+ * 
+ * A Minimap viewing tool for three.js
+ *
+ * @param {WebGLRenderer} renderer
+ * @param {Scene} scene
+ * @param {Camera} mainCamera
+ * @param {Object} params
+ */
+
+import {
+	Camera,
+	CameraHelper,
+	CircleBufferGeometry,
+	DoubleSide,
+	FrontSide,
+	Group,
+	Mesh,
+	MeshBasicMaterial,
+	OrthographicCamera,
+	PlaneBufferGeometry,
+	Scene,
+	Vector3,
+	Vector4,
+	WebGLRenderer
+} from "../../../build/three.module.js";
+
+var Minimap = function(renderer, scene, mainCamera, params) {
+
+    this.renderer = renderer;
+    this.scene = scene;
+    this.object = mainCamera;
+    this.viewRange = params.viewRange !== undefined ? params.viewRange : 1000;
+    this.mapSize = params.mapSize !== undefined ? params.mapSize : 300;
+    this.heightOffset = params.heightOffset !== undefined ? params.heightOffset : 10;
+
+    var scope = this;
+    var clientWidth, clientHeight;
+    var updatePos, idcPos;
+
+    var viewport = new Vector4();
+    renderer.getCurrentViewport(viewport);
+
+    var up = mainCamera.up.clone(), lookat = new Vector3(), lastLookat = new Vector3();
+    getLookatVecProjected(mainCamera, lastLookat);
+
+    var minimapGroup = new Group();
+    var minimapCamera = new OrthographicCamera(
+        - scope.viewRange / 2,
+        scope.viewRange / 2,
+        scope.viewRange / 2,
+        - scope.viewRange / 2,
+        0.01,
+        10000
+    );
+    
+    // set orthographic camera always look downward
+    minimapCamera.lookAt(up.clone().negate());
+    minimapGroup.add(minimapCamera);
+
+    var mainCameraHelper = new CameraHelper(mainCamera);
+    minimapGroup.add(mainCameraHelper);
+
+    scene.add(minimapGroup);
+
+    initBackplane(0x000000);
+
+    initIndicator();
+
+
+    Minimap.prototype.getCamera = function () {
+        return minimapCamera;
+    };
+
+
+    Minimap.prototype.setMinimapVisibility = function (flag) {
+        minimapCamera.backplane.visible = flag;
+        minimapCamera.indicator.visible = flag;
+        mainCameraHelper.visible = flag;
+    };
+
+
+    Minimap.prototype.renderMinimap = function (){
+
+        setAutoClear(false);
+
+        // render minimap
+        scope.setMinimapVisibility(true);
+        renderMinimap();
+
+        // set the render state back to original
+        scope.renderer.setViewport(0, 0, clientWidth, clientHeight);
+		scope.renderer.setScissor(0, 0, clientWidth, clientHeight);
+        scope.renderer.setScissorTest(true);
+        setAutoClear(true);
+    };
+
+
+
+    /**
+     * Private functions
+     */
+    function setAutoClear(flag) {
+        scope.renderer.autoClear = flag;
+        scope.renderer.autoClearColor = flag;
+        scope.renderer.autoClearDepth = flag;
+    }
+
+
+    function renderMinimap() {
+        updateMinimapCamera();
+        scope.renderer.setViewport(0, 0, scope.mapSize, scope.mapSize);
+        scope.renderer.render(scene, minimapCamera);
+    }
+
+
+    function initBackplane(color) {
+        var width = Math.abs(minimapCamera.left - minimapCamera.right);
+        var height = Math.abs(minimapCamera.top - minimapCamera.bottom);
+        var plane = new Mesh(
+            new PlaneBufferGeometry(width, height, 1),
+            new MeshBasicMaterial({ color: color, side: FrontSide })
+        );
+        plane.quaternion.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2);
+        minimapGroup.add(plane);
+        minimapCamera.backplane = plane;
+    }
+
+
+    function initIndicator() {
+        var dot = new Mesh(
+            new CircleBufferGeometry(scope.mapSize / 45, 16, 0, 2 * Math.PI),
+            new MeshBasicMaterial({ color: 0xffffff, side: DoubleSide })
+        );
+        dot.rotateX(Math.PI / 2);
+        dot.visible = false;
+        minimapGroup.add(dot);
+
+        minimapCamera.indicator = dot;
+    }
+
+
+    function getLookatVecProjected(object, result) {
+        object.getWorldDirection(result);
+        result.projectOnPlane(up); // get the lookat vector projected on the orthographic camera
+    }
+
+
+    function updateMinimapCamera() {
+        clientWidth = scope.renderer.domElement.clientWidth;
+        clientHeight = scope.renderer.domElement.clientHeight;
+
+        // update view frustum
+        minimapCamera.left = - scope.viewRange / 2;
+        minimapCamera.right = scope.viewRange / 2;
+        minimapCamera.top = scope.viewRange / 2;
+        minimapCamera.bottom = - scope.viewRange / 2;
+        minimapCamera.updateProjectionMatrix();
+
+        // update position
+        updatePos = mainCamera.position.clone().add(up.clone().multiplyScalar(scope.heightOffset));
+        minimapCamera.position.set(updatePos.x, updatePos.y, updatePos.z);
+        minimapCamera.updateMatrixWorld();
+
+        minimapCamera.backplane.position.set(minimapCamera.position.x, minimapCamera.position.y, minimapCamera.position.z - minimapCamera.far);
+        minimapCamera.backplane.updateMatrixWorld();
+
+        var idc = minimapCamera.indicator;
+        idcPos = minimapCamera.position.clone().add(up.clone().negate().multiplyScalar(0.02));
+        // idcPos = mainCamera.position.clone().add(up.clone().negate().multiplyScalar(0.02));
+        idc.position.set(idcPos.x, idcPos.y, idcPos.z);
+        idc.updateMatrixWorld();
+
+        getLookatVecProjected(mainCamera, lastLookat); // store the last lookat vector
+    }
+}
+export { Minimap };

+ 265 - 0
examples/webgl_camera_minimap.html

@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+	<title>three.js webgl - minimap</title>
+	<meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+	<link type="text/css" rel="stylesheet" href="main.css">
+	<style>
+		a {
+			color: #08f;
+		}
+
+		b {
+			color: lightgreen;
+		}
+
+		#mask {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			background-color: rgba(0, 0, 0, 0.4);
+		}
+
+		#text {
+			position: absolute;
+			font-size: 30px;
+			width: 100%;
+			top: 50%;
+			text-align: center;
+			cursor: pointer;
+		}
+	</style>
+</head>
+
+<body>
+	<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a><a> - minimap</a><br />
+	</div>
+
+	<div id="mask">
+		<div id="text">Click to start</div>
+	</div>
+
+	<script type="module">
+
+		import * as THREE from '../build/three.module.js';
+		import { PointerLockControls } from './jsm/controls/PointerLockControls.js';
+		import { GUI } from './jsm/libs/dat.gui.module.js';
+		import { Minimap } from './jsm/misc/Minimap.js';
+
+		var camera, scene, renderer, controls;
+		var objects = [];
+		var raycaster;
+		var moveForward = false;
+		var moveBackward = false;
+		var moveLeft = false;
+		var moveRight = false;
+		var canJump = false;
+		var isPointerLocked = true;
+		var prevTime = performance.now();
+		var velocity = new THREE.Vector3();
+		var direction = new THREE.Vector3();
+		var vertex = new THREE.Vector3();
+		var color = new THREE.Color();
+		var params = {
+			viewRange: 300, 
+			mapSize: 300
+		};
+		var minimap;
+
+		init();
+
+		animate();
+
+		function init() {
+			//
+			renderer = new THREE.WebGLRenderer({ antialias: true });
+			renderer.setPixelRatio(window.devicePixelRatio);
+			renderer.setSize(window.innerWidth, window.innerHeight);
+			document.body.appendChild(renderer.domElement);
+
+			camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
+			camera.position.y = 10;
+			scene = new THREE.Scene();
+			scene.background = new THREE.Color(0xffffff);
+			scene.fog = new THREE.Fog(0xffffff, 0, 200);
+
+			var light = new THREE.HemisphereLight(0xeeeeff, 0x777788, 0.75);
+			light.position.set(0.5, 1, 0.75);
+			scene.add(light);
+
+			//camera controls
+			controls = new PointerLockControls(camera, renderer.domElement);
+			scene.add(controls.getObject());
+
+			var mask = document.getElementById('mask');
+			var text = document.getElementById('text');
+
+			mask.addEventListener('click', function () {
+				controls.lock();
+			}, false);
+
+			controls.addEventListener('lock', function () {
+				mask.style.display = 'none';
+			});
+			controls.addEventListener('unlock', function () {
+				mask.style.display = 'block';
+				text.innerText = 'Click to continue';
+			});
+
+			renderer.domElement.addEventListener('click', function () {
+				controls.unlock();
+				mask.style.display = 'block';
+			}, false);
+
+			var onKeyDown = function (event) {
+
+				if (!controls.isLocked) return; 
+
+				switch (event.keyCode) {
+					case 38: // up
+					case 87: // w
+						moveForward = true;
+						break;
+					case 37: // left
+					case 65: // a
+						moveLeft = true;
+						break;
+					case 40: // down
+					case 83: // s
+						moveBackward = true;
+						break;
+					case 39: // right
+					case 68: // d
+						moveRight = true;
+						break;
+					case 32: // space
+						if (canJump === true) velocity.y += 250;
+						canJump = false;
+						break;
+				}
+			};
+
+			var onKeyUp = function (event) {
+				switch (event.keyCode) {
+					case 38: // up
+					case 87: // w
+						moveForward = false;
+						break;
+					case 37: // left
+					case 65: // a
+						moveLeft = false;
+						break;
+					case 40: // down
+					case 83: // s
+						moveBackward = false;
+						break;
+					case 39: // right
+					case 68: // d
+						moveRight = false;
+						break;
+				}
+			};
+
+			document.addEventListener('keydown', onKeyDown, false);
+			document.addEventListener('keyup', onKeyUp, false);
+
+			raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, - 1, 0), 0, 10);
+
+			// ground mesh
+			var groundGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
+			groundGeometry.rotateX(- Math.PI / 2);
+			var groundMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(0.2, 0.75, 0.5) });
+			var ground = new THREE.Mesh(groundGeometry, groundMaterial);
+			scene.add(ground);
+
+			// obstacles
+			for (var i = 0; i < 600; i++) {
+				var boxGeometry = new THREE.BoxBufferGeometry(20, 80, 20);
+				var boxMaterial = new THREE.MeshPhongMaterial({ 
+					specular: new THREE.Color(Math.random(), Math.random(), Math.random()), 
+					flatShading: true 
+				});
+				boxMaterial.color.setHSL(Math.random() * 0.2 + 0.4, 0.6, Math.random() * 0.3 + 0.75); //Math.random() * 0.2 + 0.5
+				var box = new THREE.Mesh(boxGeometry, boxMaterial);
+				box.position.x = Math.floor(Math.random() * 40 - 20) * 20;
+				box.position.z = Math.floor(Math.random() * 40 - 20) * 20;
+				if (box.position.x == 0 && box.position.z == 0) continue;
+				scene.add(box);
+				objects.push(box);
+			}
+
+			// init minimap
+			minimap = new Minimap(renderer, scene, camera, {
+				viewRange: params.viewRange, 
+				mapSize: params.mapSize, 
+				heightOffset: 100
+			});
+
+			var gui = new GUI();
+			gui.add(params, 'viewRange', 100, 800);
+			gui.add(params, 'mapSize', 100, 500);
+			gui.open();
+
+			//
+			window.addEventListener('resize', function () {
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+				renderer.setSize(window.innerWidth, window.innerHeight);
+			}, false);
+		}
+
+
+		function animate() {
+
+			requestAnimationFrame(animate);
+
+			// first person control
+			raycaster.ray.origin.copy(controls.getObject().position);
+			raycaster.ray.origin.y -= 10;
+			var intersections = raycaster.intersectObjects(objects);
+			var onObject = intersections.length > 0;
+			var time = performance.now();
+			var delta = (time - prevTime) / 1000;
+			velocity.x -= velocity.x * 10.0 * delta;
+			velocity.z -= velocity.z * 10.0 * delta;
+			velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
+			direction.z = Number(moveForward) - Number(moveBackward);
+			direction.x = Number(moveRight) - Number(moveLeft);
+			direction.normalize(); // this ensures consistent movements in all directions
+			if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta;
+			if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta;
+			if (onObject === true) {
+				velocity.y = Math.max(0, velocity.y);
+				canJump = true;
+			}
+			controls.moveRight(- velocity.x * delta);
+			controls.moveForward(- velocity.z * delta);
+			controls.getObject().position.y += (velocity.y * delta); // new behavior
+			if (controls.getObject().position.y < 10) {
+				velocity.y = 0;
+				controls.getObject().position.y = 10;
+				canJump = true;
+			}
+			prevTime = time;
+
+			// render main scene
+			minimap.setMinimapVisibility(false);
+			scene.fog.far = 200;
+			renderer.render(scene, camera);
+
+			// render minimap
+			scene.fog.far = 100000;
+			minimap.viewRange = params.viewRange;
+			minimap.mapSize = params.mapSize;
+			minimap.renderMinimap();
+			
+		}
+
+
+	</script>
+
+</body>
+
+</html>