分享three.js和cannon.js构建Web 3D场景

使用 three.js,您不再需要花哨的游戏PC或控制台来显示逼真的3D图形。 您甚至不需要下载特殊的应用程序。现在每个人都可以使用智能手机和网络浏览器体验令人惊叹的3D应用程序。

这个惊人的库和充满活力的社区是您在浏览器、笔记本电脑、平板电脑或智能手机上创建游戏、 音乐视频、科学和数据可视化或几乎任何您能想象的任何东西所需要的一切!

可在任何操作系统和设备(从智能手机到笔记本电脑到智能手表) 上运行的令人惊叹的、专业品质的、高性能的3D Web应用程序所需的一切, 即使您对Web开发和计算机图形完全陌生。three.js是有史以来最易于访问的计算机图形框架, 我们将充分利用它来引导您很快的获得高水平的专业知识。了解基本的three.js应用程序所需的所有基本概念,有了这些知识, 您将可以立即创建自己的惊人项目。有了三维的力量,唯一的限制就是你的想象力!

引入three.js和cannon.js依赖

<script src="https://cdn.bootcdn.net/ajax/libs/three.js/109/three.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>

前置知识点

JavaScript的原型和原型链实现类似强类型语言的继承功能

function EventDispatcher() {}
function Object3D() {}
Object3D.prototype = Object.assign(
    Object.create( EventDispatcher.prototype ), 
    {
       constructor: Object3D
    }
);

three.js大量使用了上面这种继承方式,其主要原理是通过Object.create创建一个对象,对象的__proto__属性指向EventDispatcher的原型链。

Object.create(EventDispatcher.prototype )

然后通过 Object.assign方法把扩展属性进行合并;注意这里的constructor,这里把Object3D的构造函数也合并起来,放在Object3D.prototype的原型链上。不然在Object3D.prototype赋值的时候,构造函数就会找不到。

three.js中使用的是右手坐标系,原因是WebGL默认的就是这种坐标系。简单理解就是,x轴正方向向右,y轴正方向向上,z轴正方向由屏幕从里向外。在场景中所有的物体在容器的位置都是依靠这个坐标系设置的。

一般使用的坐标系是当你面朝计算机屏幕时, X轴 是水平的(正方向为右), Y轴 是垂直的(正方向为上), Z轴 垂直于屏幕(正方向为外),这个坐标系也被称为 右手坐标系 。之所被称为 右手坐标系 是因为它是通过如图所示的 右手手势 确定的,即当你伸出右手摆出如图所示手势时, 拇指 指向 X轴的正方向 , 食指 指向 Y轴的正方向 , 中指 指向 Z轴的正方向 ,这种确定坐标系方式也被称为 右手定则 。

场景scene ,它就相当于一个大容器,我们需要展示的所有物体都要放入场景。它又被称为场景图,因为它是一个树形结构数据。能放入场景中的对象都继承了Object3D对象,所以每一个子节点都有自己的局部空间。简单理解就是场景中有一个空间可以添加组、Object3D、网格等物体类型,子节点也是一个小容器,同样可以添加组、Object3D、网格等物体类型。区别就是,子节点设置的坐标位置是相对于父节点的局部空间坐标来改变的。

建立几何体时通过指定几何体的顶点和三角形的面确定了几何体的形状,另外还需要给几何体新增皮肤才能实现物体的效果,材质就像物体的皮肤,决定了物体的质感。

基础材质:以简单着色方式来绘制几何体的材质,不受光照影响。

深度材质:按深度绘制几何体的材质。深度基于相机远近端面,离近端面越近就越白,离远端面越近就越黑。

法向量材质:把法向量对映到RGB颜色的材质。

Lambert材质:是一种需要光源的材质,非光泽表面的材质,没有镜面高光,适用于石膏等表面粗糙的物体。

Phong材质:也是一种需要光源的材质,具有镜面高光的光泽表面的材质,适用于金属、漆面等反光的物体。

材质捕获:使用储存了光照和反射等信息的贴图,然后利用法线方向进行取样。优点是可以用很低的消耗来实现很多特殊风格的效果;缺点是仅对于固定相机视角的情况较好。

下图是使用不同贴图实现的效果:

透视相机近大远小,同样大小的物体离相机近的在画面上显得大,离相机远的物体在画面上显得小。透视相机的视锥体如上图左侧所示,从近端面到远端面构成的区域内的物体才能显示在影象上。

正交相机无论物体距离相机远或者近,在最终渲染的图片中物体的大小都保持不变。正交相机的视锥体如下图右侧所示,和透视相机一样,从近端面到远端面构成的区域内的物体才能显示在影象上。

立即执行函数(function(){})(),先了解些函数的基本概念(函数声明、函数表达式、匿名函数)。加运算符确实可将函数声明转化为函数表达式,而之所以使用括号,是因为括号相对其他运算符会更安全。

在three.js基础上增加控制器

( function () {

	'use strict';

	var GizmoMaterial = function ( parameters ) {

		THREE.MeshBasicMaterial.call( this );

		this.depthTest = false;
		this.depthWrite = false;
		this.side = THREE.FrontSide;
		this.transparent = true;

		this.setValues( parameters );

		this.oldColor = this.color.clone();
		this.oldOpacity = this.opacity;

		this.highlight = function( highlighted ) {

			if ( highlighted ) {

				this.color.setRGB( 1, 1, 0 );
				this.opacity = 1;

			} else {

				this.color.copy( this.oldColor );
				this.opacity = this.oldOpacity;

			}

		};

	};

	GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype );
	GizmoMaterial.prototype.constructor = GizmoMaterial;

	var GizmoLineMaterial = function ( parameters ) {

		THREE.LineBasicMaterial.call( this );

		this.depthTest = false;
		this.depthWrite = false;
		this.transparent = true;
		this.linewidth = 1;

		this.setValues( parameters );

		this.oldColor = this.color.clone();
		this.oldOpacity = this.opacity;

		this.highlight = function( highlighted ) {

			if ( highlighted ) {

				this.color.setRGB( 1, 1, 0 );
				this.opacity = 1;

			} else {

				this.color.copy( this.oldColor );
				this.opacity = this.oldOpacity;

			}

		};

	};

	GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype );
	GizmoLineMaterial.prototype.constructor = GizmoLineMaterial;

	var pickerMaterial = new GizmoMaterial( { visible: false, transparent: false } );

	THREE.TransformGizmo = function () {

		var scope = this;

		this.init = function () {

			THREE.Object3D.call( this );

			this.handles = new THREE.Object3D();
			this.pickers = new THREE.Object3D();
			this.planes = new THREE.Object3D();

			this.add( this.handles );
			this.add( this.pickers );
			this.add( this.planes );

			 PLANES

			var planeGeometry = new THREE.PlaneBufferGeometry( 50, 50, 2, 2 );
			var planeMaterial = new THREE.MeshBasicMaterial( { visible: false, side: THREE.DoubleSide } );

			var planes = {
				"XY":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"YZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"XZ":   new THREE.Mesh( planeGeometry, planeMaterial ),
				"XYZE": new THREE.Mesh( planeGeometry, planeMaterial )
			};

			this.activePlane = planes[ "XYZE" ];

			planes[ "YZ" ].rotation.set( 0, Math.PI / 2, 0 );
			planes[ "XZ" ].rotation.set( - Math.PI / 2, 0, 0 );

			for ( var i in planes ) {

				planes[ i ].name = i;
				this.planes.add( planes[ i ] );
				this.planes[ i ] = planes[ i ];

			}

			 HANDLES AND PICKERS

			var setupGizmos = function( gizmoMap, parent ) {

				for ( var name in gizmoMap ) {

					for ( i = gizmoMap[ name ].length; i --; ) {

						var object = gizmoMap[ name ][ i ][ 0 ];
						var position = gizmoMap[ name ][ i ][ 1 ];
						var rotation = gizmoMap[ name ][ i ][ 2 ];

						object.name = name;

						if ( position ) object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
						if ( rotation ) object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );

						parent.add( object );

					}

				}

			};

			setupGizmos( this.handleGizmos, this.handles );
			setupGizmos( this.pickerGizmos, this.pickers );

			// reset Transformations

			this.traverse( function ( child ) {

				if ( child instanceof THREE.Mesh ) {

					child.updateMatrix();

					var tempGeometry = child.geometry.clone();
					tempGeometry.applyMatrix( child.matrix );
					child.geometry = tempGeometry;

					child.position.set( 0, 0, 0 );
					child.rotation.set( 0, 0, 0 );
					child.scale.set( 1, 1, 1 );

				}

			} );

		};

		this.highlight = function ( axis ) {

			this.traverse( function( child ) {

				if ( child.material && child.material.highlight ) {

					if ( child.name === axis ) {

						child.material.highlight( true );

					} else {

						child.material.highlight( false );

					}

				}

			} );

		};

	};

	THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype );
	THREE.TransformGizmo.prototype.constructor = THREE.TransformGizmo;

	THREE.TransformGizmo.prototype.update = function ( rotation, eye ) {

		var vec1 = new THREE.Vector3( 0, 0, 0 );
		var vec2 = new THREE.Vector3( 0, 1, 0 );
		var lookAtMatrix = new THREE.Matrix4();

		this.traverse( function( child ) {

			if ( child.name.search( "E" ) !== - 1 ) {

				child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) );

			} else if ( child.name.search( "X" ) !== - 1 || child.name.search( "Y" ) !== - 1 || child.name.search( "Z" ) !== - 1 ) {

				child.quaternion.setFromEuler( rotation );

			}

		} );

	};

	THREE.TransformGizmoTranslate = function () {

		THREE.TransformGizmo.call( this );

		var arrowGeometry = new THREE.Geometry();
		var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) );
		mesh.position.y = 0.5;
		mesh.updateMatrix();

		arrowGeometry.merge( mesh.geometry, mesh.matrix );

		var lineXGeometry = new THREE.BufferGeometry();
		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  1, 0, 0 ], 3 ) );

		var lineYGeometry = new THREE.BufferGeometry();
		lineYGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 1, 0 ], 3 ) );

		var lineZGeometry = new THREE.BufferGeometry();
		lineZGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 0, 1 ], 3 ) );

		this.handleGizmos = {

			X: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ],
				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],

			Y: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
				[	new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],

			Z: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],

			XYZ: [
				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
			],

			XY: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ]
			],

			YZ: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ]
			],

			XZ: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ] ]
			]

		};

		this.pickerGizmos = {

			X: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
			],

			Y: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ]
			],

			Z: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
			],

			XYZ: [
				[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), pickerMaterial ) ]
			],

			XY: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0.2, 0 ] ]
			],

			YZ: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ]
			],

			XZ: [
				[ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), pickerMaterial ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ] ]
			]

		};

		this.setActivePlane = function ( axis, eye ) {

			var tempMatrix = new THREE.Matrix4();
			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );

			if ( axis === "X" ) {

				this.activePlane = this.planes[ "XY" ];

				if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ];

			}

			if ( axis === "Y" ) {

				this.activePlane = this.planes[ "XY" ];

				if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ];

			}

			if ( axis === "Z" ) {

				this.activePlane = this.planes[ "XZ" ];

				if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ];

			}

			if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ];

			if ( axis === "XY" ) this.activePlane = this.planes[ "XY" ];

			if ( axis === "YZ" ) this.activePlane = this.planes[ "YZ" ];

			if ( axis === "XZ" ) this.activePlane = this.planes[ "XZ" ];

		};

		this.init();

	};

	THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype );
	THREE.TransformGizmoTranslate.prototype.constructor = THREE.TransformGizmoTranslate;

	THREE.TransformGizmoRotate = function () {

		THREE.TransformGizmo.call( this );

		var CircleGeometry = function ( radius, facing, arc ) {

			var geometry = new THREE.BufferGeometry();
			var vertices = [];
			arc = arc ? arc : 1;

			for ( var i = 0; i <= 64 * arc; ++ i ) {

				if ( facing === 'x' ) vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
				if ( facing === 'y' ) vertices.push( Math.cos( i / 32 * Math.PI ) * radius, 0, Math.sin( i / 32 * Math.PI ) * radius );
				if ( facing === 'z' ) vertices.push( Math.sin( i / 32 * Math.PI ) * radius, Math.cos( i / 32 * Math.PI ) * radius, 0 );

			}

			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
			return geometry;

		};

		this.handleGizmos = {

			X: [
				[ new THREE.Line( new CircleGeometry( 1, 'x', 0.5 ), new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],

			Y: [
				[ new THREE.Line( new CircleGeometry( 1, 'y', 0.5 ), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],

			Z: [
				[ new THREE.Line( new CircleGeometry( 1, 'z', 0.5 ), new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],

			E: [
				[ new THREE.Line( new CircleGeometry( 1.25, 'z', 1 ), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ]
			],

			XYZE: [
				[ new THREE.Line( new CircleGeometry( 1, 'z', 1 ), new GizmoLineMaterial( { color: 0x787878 } ) ) ]
			]

		};

		this.pickerGizmos = {

			X: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ] ]
			],

			Y: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ]
			],

			Z: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), pickerMaterial ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
			],

			E: [
				[ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), pickerMaterial ) ]
			],

			XYZE: [
				[ new THREE.Mesh( new THREE.Geometry() ) ]// TODO
			]

		};

		this.setActivePlane = function ( axis ) {

			if ( axis === "E" ) this.activePlane = this.planes[ "XYZE" ];

			if ( axis === "X" ) this.activePlane = this.planes[ "YZ" ];

			if ( axis === "Y" ) this.activePlane = this.planes[ "XZ" ];

			if ( axis === "Z" ) this.activePlane = this.planes[ "XY" ];

		};

		this.update = function ( rotation, eye2 ) {

			THREE.TransformGizmo.prototype.update.apply( this, arguments );

			var group = {

				handles: this[ "handles" ],
				pickers: this[ "pickers" ],

			};

			var tempMatrix = new THREE.Matrix4();
			var worldRotation = new THREE.Euler( 0, 0, 1 );
			var tempQuaternion = new THREE.Quaternion();
			var unitX = new THREE.Vector3( 1, 0, 0 );
			var unitY = new THREE.Vector3( 0, 1, 0 );
			var unitZ = new THREE.Vector3( 0, 0, 1 );
			var quaternionX = new THREE.Quaternion();
			var quaternionY = new THREE.Quaternion();
			var quaternionZ = new THREE.Quaternion();
			var eye = eye2.clone();

			worldRotation.copy( this.planes[ "XY" ].rotation );
			tempQuaternion.setFromEuler( worldRotation );

			tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
			eye.applyMatrix4( tempMatrix );

			this.traverse( function( child ) {

				tempQuaternion.setFromEuler( worldRotation );

				if ( child.name === "X" ) {

					quaternionX.setFromAxisAngle( unitX, Math.atan2( - eye.y, eye.z ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					child.quaternion.copy( tempQuaternion );

				}

				if ( child.name === "Y" ) {

					quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
					child.quaternion.copy( tempQuaternion );

				}

				if ( child.name === "Z" ) {

					quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
					child.quaternion.copy( tempQuaternion );

				}

			} );

		};

		this.init();

	};

	THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype );
	THREE.TransformGizmoRotate.prototype.constructor = THREE.TransformGizmoRotate;

	THREE.TransformGizmoScale = function () {

		THREE.TransformGizmo.call( this );

		var arrowGeometry = new THREE.Geometry();
		var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) );
		mesh.position.y = 0.5;
		mesh.updateMatrix();

		arrowGeometry.merge( mesh.geometry, mesh.matrix );

		var lineXGeometry = new THREE.BufferGeometry();
		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  1, 0, 0 ], 3 ) );

		var lineYGeometry = new THREE.BufferGeometry();
		lineYGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 1, 0 ], 3 ) );

		var lineZGeometry = new THREE.BufferGeometry();
		lineZGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  0, 0, 1 ], 3 ) );

		this.handleGizmos = {

			X: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ],
				[ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
			],

			Y: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
				[ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
			],

			Z: [
				[ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ],
				[ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
			],

			XYZ: [
				[ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
			]

		};

		this.pickerGizmos = {

			X: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ] ]
			],

			Y: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0.6, 0 ] ]
			],

			Z: [
				[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), pickerMaterial ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ]
			],

			XYZ: [
				[ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), pickerMaterial ) ]
			]

		};

		this.setActivePlane = function ( axis, eye ) {

			var tempMatrix = new THREE.Matrix4();
			eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );

			if ( axis === "X" ) {

				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs( eye.y ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "XZ" ];

			}

			if ( axis === "Y" ) {

				this.activePlane = this.planes[ "XY" ];
				if ( Math.abs( eye.x ) > Math.abs( eye.z ) ) this.activePlane = this.planes[ "YZ" ];

			}

			if ( axis === "Z" ) {

				this.activePlane = this.planes[ "XZ" ];
				if ( Math.abs( eye.x ) > Math.abs( eye.y ) ) this.activePlane = this.planes[ "YZ" ];

			}

			if ( axis === "XYZ" ) this.activePlane = this.planes[ "XYZE" ];

		};

		this.init();

	};

	THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype );
	THREE.TransformGizmoScale.prototype.constructor = THREE.TransformGizmoScale;

	THREE.TransformControls = function ( camera, domElement ) {

		// TODO: Make non-uniform scale and rotate play nice in hierarchies
		// TODO: ADD RXYZ contol

		THREE.Object3D.call( this );

		domElement = ( domElement !== undefined ) ? domElement : document;

		this.object = undefined;
		this.visible = false;
		this.snap = null;
		this.space = "world";
		this.size = 1;
		this.axis = null;

		var scope = this;

		var _mode = "translate";
		var _dragging = false;
		var _plane = "XY";
		var _gizmo = {

			"translate": new THREE.TransformGizmoTranslate(),
			"rotate": new THREE.TransformGizmoRotate(),
			"scale": new THREE.TransformGizmoScale()
		};

		for ( var type in _gizmo ) {

			var gizmoObj = _gizmo[ type ];

			gizmoObj.visible = ( type === _mode );
			this.add( gizmoObj );

		}

		var changeEvent = { type: "change" };
		var mouseDownEvent = { type: "mouseDown" };
		var mouseUpEvent = { type: "mouseUp", mode: _mode };
		var objectChangeEvent = { type: "objectChange" };

		var ray = new THREE.Raycaster();
		var pointerVector = new THREE.Vector2();

		var point = new THREE.Vector3();
		var offset = new THREE.Vector3();

		var rotation = new THREE.Vector3();
		var offsetRotation = new THREE.Vector3();
		var scale = 1;

		var lookAtMatrix = new THREE.Matrix4();
		var eye = new THREE.Vector3();

		var tempMatrix = new THREE.Matrix4();
		var tempVector = new THREE.Vector3();
		var tempQuaternion = new THREE.Quaternion();
		var unitX = new THREE.Vector3( 1, 0, 0 );
		var unitY = new THREE.Vector3( 0, 1, 0 );
		var unitZ = new THREE.Vector3( 0, 0, 1 );

		var quaternionXYZ = new THREE.Quaternion();
		var quaternionX = new THREE.Quaternion();
		var quaternionY = new THREE.Quaternion();
		var quaternionZ = new THREE.Quaternion();
		var quaternionE = new THREE.Quaternion();

		var oldPosition = new THREE.Vector3();
		var oldScale = new THREE.Vector3();
		var oldRotationMatrix = new THREE.Matrix4();

		var parentRotationMatrix  = new THREE.Matrix4();
		var parentScale = new THREE.Vector3();

		var worldPosition = new THREE.Vector3();
		var worldRotation = new THREE.Euler();
		var worldRotationMatrix  = new THREE.Matrix4();
		var camPosition = new THREE.Vector3();
		var camRotation = new THREE.Euler();

		domElement.addEventListener( "mousedown", onPointerDown, false );
		domElement.addEventListener( "touchstart", onPointerDown, false );

		domElement.addEventListener( "mousemove", onPointerHover, false );
		domElement.addEventListener( "touchmove", onPointerHover, false );

		domElement.addEventListener( "mousemove", onPointerMove, false );
		domElement.addEventListener( "touchmove", onPointerMove, false );

		domElement.addEventListener( "mouseup", onPointerUp, false );
		domElement.addEventListener( "mouseout", onPointerUp, false );
		domElement.addEventListener( "touchend", onPointerUp, false );
		domElement.addEventListener( "touchcancel", onPointerUp, false );
		domElement.addEventListener( "touchleave", onPointerUp, false );

		this.dispose = function () {

			domElement.removeEventListener( "mousedown", onPointerDown );
			domElement.removeEventListener( "touchstart", onPointerDown );

			domElement.removeEventListener( "mousemove", onPointerHover );
			domElement.removeEventListener( "touchmove", onPointerHover );

			domElement.removeEventListener( "mousemove", onPointerMove );
			domElement.removeEventListener( "touchmove", onPointerMove );

			domElement.removeEventListener( "mouseup", onPointerUp );
			domElement.removeEventListener( "mouseout", onPointerUp );
			domElement.removeEventListener( "touchend", onPointerUp );
			domElement.removeEventListener( "touchcancel", onPointerUp );
			domElement.removeEventListener( "touchleave", onPointerUp );

		};

		this.attach = function ( object ) {

			this.object = object;
			this.visible = true;
			this.update();

		};

		this.detach = function () {

			this.object = undefined;
			this.visible = false;
			this.axis = null;

		};

		this.setMode = function ( mode ) {

			_mode = mode ? mode : _mode;

			if ( _mode === "scale" ) scope.space = "local";

			for ( var type in _gizmo ) _gizmo[ type ].visible = ( type === _mode );

			this.update();
			scope.dispatchEvent( changeEvent );

		};

		this.setSnap = function ( snap ) {

			scope.snap = snap;

		};

		this.setSize = function ( size ) {

			scope.size = size;
			this.update();
			scope.dispatchEvent( changeEvent );

		};

		this.setSpace = function ( space ) {

			scope.space = space;
			this.update();
			scope.dispatchEvent( changeEvent );

		};

		this.update = function () {

			if ( scope.object === undefined ) return;

			scope.object.updateMatrixWorld();
			worldPosition.setFromMatrixPosition( scope.object.matrixWorld );
			worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) );

			camera.updateMatrixWorld();
			camPosition.setFromMatrixPosition( camera.matrixWorld );
			camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );

			scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size;
			this.position.copy( worldPosition );
			// NOTE: we are commenting this line because this transformation looks
			// confusing when using an ortographic camera
			// this.scale.set( scale, scale, scale );

			eye.copy( camPosition ).sub( worldPosition ).normalize();

			if ( scope.space === "local" ) {

				_gizmo[ _mode ].update( worldRotation, eye );

			} else if ( scope.space === "world" ) {

				_gizmo[ _mode ].update( new THREE.Euler(), eye );

			}

			_gizmo[ _mode ].highlight( scope.axis );

		};

		function onPointerHover( event ) {

			if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;

			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;

			var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children );

			var axis = null;

			if ( intersect ) {

				axis = intersect.object.name;

				event.preventDefault();

			}

			if ( scope.axis !== axis ) {

				scope.axis = axis;
				scope.update();
				scope.dispatchEvent( changeEvent );

			}

		}

		function onPointerDown( event ) {

			if ( scope.object === undefined || _dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;

			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;

			if ( pointer.button === 0 || pointer.button === undefined ) {

				var intersect = intersectObjects( pointer, _gizmo[ _mode ].pickers.children );

				if ( intersect ) {

					event.preventDefault();
					event.stopPropagation();

					scope.dispatchEvent( mouseDownEvent );

					scope.axis = intersect.object.name;

					scope.update();

					eye.copy( camPosition ).sub( worldPosition ).normalize();

					_gizmo[ _mode ].setActivePlane( scope.axis, eye );

					var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] );

					if ( planeIntersect ) {

						oldPosition.copy( scope.object.position );
						oldScale.copy( scope.object.scale );

						oldRotationMatrix.extractRotation( scope.object.matrix );
						worldRotationMatrix.extractRotation( scope.object.matrixWorld );

						parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
						parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );

						offset.copy( planeIntersect.point );

					}

				}

			}

			_dragging = true;

		}

		function onPointerMove( event ) {

			if ( scope.object === undefined || scope.axis === null || _dragging === false || ( event.button !== undefined && event.button !== 0 ) ) return;

			var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;

			var planeIntersect = intersectObjects( pointer, [ _gizmo[ _mode ].activePlane ] );

			if ( planeIntersect === false ) return;

			event.preventDefault();
			event.stopPropagation();

			point.copy( planeIntersect.point );

			if ( _mode === "translate" ) {

				point.sub( offset );
				point.multiply( parentScale );

				if ( scope.space === "local" ) {

					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );

					if ( scope.axis.search( "X" ) === - 1 ) point.x = 0;
					if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0;
					if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0;

					point.applyMatrix4( oldRotationMatrix );

					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );

				}

				if ( scope.space === "world" || scope.axis.search( "XYZ" ) !== - 1 ) {

					if ( scope.axis.search( "X" ) === - 1 ) point.x = 0;
					if ( scope.axis.search( "Y" ) === - 1 ) point.y = 0;
					if ( scope.axis.search( "Z" ) === - 1 ) point.z = 0;

					point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );

					scope.object.position.copy( oldPosition );
					scope.object.position.add( point );

				}

				if ( scope.snap !== null ) {

					if ( scope.axis.search( "X" ) !== - 1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.snap ) * scope.snap;
					if ( scope.axis.search( "Y" ) !== - 1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.snap ) * scope.snap;
					if ( scope.axis.search( "Z" ) !== - 1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.snap ) * scope.snap;

				}

			} else if ( _mode === "scale" ) {

				point.sub( offset );
				point.multiply( parentScale );

				if ( scope.space === "local" ) {

					if ( scope.axis === "XYZ" ) {

						scale = 1 + ( ( point.y ) / 50 );

						scope.object.scale.x = oldScale.x * scale;
						scope.object.scale.y = oldScale.y * scale;
						scope.object.scale.z = oldScale.z * scale;

					} else {

						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );

						if ( scope.axis === "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
						if ( scope.axis === "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
						if ( scope.axis === "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );

					}

				}

			} else if ( _mode === "rotate" ) {

				point.sub( worldPosition );
				point.multiply( parentScale );
				tempVector.copy( offset ).sub( worldPosition );
				tempVector.multiply( parentScale );

				if ( scope.axis === "E" ) {

					point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
					tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );

					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );

					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );

					quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );

					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );

					scope.object.quaternion.copy( tempQuaternion );

				} else if ( scope.axis === "XYZE" ) {

					quaternionE.setFromEuler( point.clone().cross( tempVector ).normalize() ); // rotation axis

					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
					quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo( tempVector ) );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );

					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );

					scope.object.quaternion.copy( tempQuaternion );

				} else if ( scope.space === "local" ) {

					point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );

					tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );

					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );

					quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );

					if ( scope.axis === "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
					if ( scope.axis === "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
					if ( scope.axis === "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );

					scope.object.quaternion.copy( quaternionXYZ );

				} else if ( scope.space === "world" ) {

					rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
					offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );

					tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );

					quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
					quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
					quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
					quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );

					if ( scope.axis === "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
					if ( scope.axis === "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
					if ( scope.axis === "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );

					tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );

					scope.object.quaternion.copy( tempQuaternion );

				}

			}

			scope.update();
			scope.dispatchEvent( changeEvent );
			scope.dispatchEvent( objectChangeEvent );

		}

		function onPointerUp( event ) {

			if ( event.button !== undefined && event.button !== 0 ) return;

			if ( _dragging && ( scope.axis !== null ) ) {

				mouseUpEvent.mode = _mode;
				scope.dispatchEvent( mouseUpEvent )

			}

			_dragging = false;
			onPointerHover( event );

		}

		function intersectObjects( pointer, objects ) {

			var rect = domElement.getBoundingClientRect();
			var x = ( pointer.clientX - rect.left ) / rect.width;
			var y = ( pointer.clientY - rect.top ) / rect.height;

			pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 );
			ray.setFromCamera( pointerVector, camera );

			var intersections = ray.intersectObjects( objects, true );
			return intersections[ 0 ] ? intersections[ 0 ] : false;

		}

	};

	THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
	THREE.TransformControls.prototype.constructor = THREE.TransformControls;
	
	THREE.OrbitControls = function ( object, domElement ) {

		this.object = object;
		this.domElement = ( domElement !== undefined ) ? domElement : document;

		// API

		this.enabled = true;

		this.center = new THREE.Vector3();

		this.userZoom = true;
		this.userZoomSpeed = 1.0;

		this.userRotate = true;
		this.userRotateSpeed = 1.0;

		this.userPan = true;
		this.userPanSpeed = 2.0;

		this.autoRotate = false;
		this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60

		this.minPolarAngle = 0; // radians
		this.maxPolarAngle = Math.PI; // radians

		this.minDistance = 0;
		this.maxDistance = Infinity;

		// 65 /*A*/, 83 /*S*/, 68 /*D*/
		this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };

		// internals

		var scope = this;

		var EPS = 0.000001;
		var PIXELS_PER_ROUND = 1800;

		var rotateStart = new THREE.Vector2();
		var rotateEnd = new THREE.Vector2();
		var rotateDelta = new THREE.Vector2();

		var zoomStart = new THREE.Vector2();
		var zoomEnd = new THREE.Vector2();
		var zoomDelta = new THREE.Vector2();

		var phiDelta = 0;
		var thetaDelta = 0;
		var scale = 1;

		var lastPosition = new THREE.Vector3();

		var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
		var state = STATE.NONE;

		// events

		var changeEvent = { type: 'change' };


		this.rotateLeft = function ( angle ) {

			if ( angle === undefined ) {

				angle = getAutoRotationAngle();

			}

			thetaDelta -= angle;

		};

		this.rotateRight = function ( angle ) {

			if ( angle === undefined ) {

				angle = getAutoRotationAngle();

			}

			thetaDelta += angle;

		};

		this.rotateUp = function ( angle ) {

			if ( angle === undefined ) {

				angle = getAutoRotationAngle();

			}

			phiDelta -= angle;

		};

		this.rotateDown = function ( angle ) {

			if ( angle === undefined ) {

				angle = getAutoRotationAngle();

			}

			phiDelta += angle;

		};

		this.zoomIn = function ( zoomScale ) {

			if ( zoomScale === undefined ) {

				zoomScale = getZoomScale();

			}

			scale /= zoomScale;

		};

		this.zoomOut = function ( zoomScale ) {

			if ( zoomScale === undefined ) {

				zoomScale = getZoomScale();

			}

			scale *= zoomScale;

		};

		this.pan = function ( distance ) {

			distance.transformDirection( this.object.matrix );
			distance.multiplyScalar( scope.userPanSpeed );

			this.object.position.add( distance );
			this.center.add( distance );

		};

		this.update = function () {

			var position = this.object.position;
			var offset = position.clone().sub( this.center );

			// angle from z-axis around y-axis

			var theta = Math.atan2( offset.x, offset.z );

			// angle from y-axis

			var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );

			if ( this.autoRotate ) {

				this.rotateLeft( getAutoRotationAngle() );

			}

			theta += thetaDelta;
			phi += phiDelta;

			// restrict phi to be between desired limits
			phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );

			// restrict phi to be betwee EPS and PI-EPS
			phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );

			var radius = offset.length() * scale;

			// restrict radius to be between desired limits
			radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );

			offset.x = radius * Math.sin( phi ) * Math.sin( theta );
			offset.y = radius * Math.cos( phi );
			offset.z = radius * Math.sin( phi ) * Math.cos( theta );

			position.copy( this.center ).add( offset );

			this.object.lookAt( this.center );

			thetaDelta = 0;
			phiDelta = 0;
			scale = 1;

			if ( lastPosition.distanceTo( this.object.position ) > 0 ) {

				this.dispatchEvent( changeEvent );

				lastPosition.copy( this.object.position );

			}

		};


		function getAutoRotationAngle() {

			return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;

		}

		function getZoomScale() {

			return Math.pow( 0.95, scope.userZoomSpeed );

		}

		function onMouseDown( event ) {

			if ( scope.enabled === false ) return;
			if ( scope.userRotate === false ) return;

			event.preventDefault();

			if ( state === STATE.NONE )
			{
				if ( event.button === 0 )
					state = STATE.ROTATE;
				if ( event.button === 1 )
					state = STATE.ZOOM;
				if ( event.button === 2 )
					state = STATE.PAN;
			}
			
			
			if ( state === STATE.ROTATE ) {

				//state = STATE.ROTATE;

				rotateStart.set( event.clientX, event.clientY );

			} else if ( state === STATE.ZOOM ) {

				//state = STATE.ZOOM;

				zoomStart.set( event.clientX, event.clientY );

			} else if ( state === STATE.PAN ) {

				//state = STATE.PAN;

			}

			document.addEventListener( 'mousemove', onMouseMove, false );
			document.addEventListener( 'mouseup', onMouseUp, false );

		}

		function onMouseMove( event ) {

			if ( scope.enabled === false ) return;

			event.preventDefault();

			
			
			if ( state === STATE.ROTATE ) {

				rotateEnd.set( event.clientX, event.clientY );
				rotateDelta.subVectors( rotateEnd, rotateStart );

				scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
				scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );

				rotateStart.copy( rotateEnd );

			} else if ( state === STATE.ZOOM ) {

				zoomEnd.set( event.clientX, event.clientY );
				zoomDelta.subVectors( zoomEnd, zoomStart );

				if ( zoomDelta.y > 0 ) {

					scope.zoomIn();

				} else {

					scope.zoomOut();

				}

				zoomStart.copy( zoomEnd );

			} else if ( state === STATE.PAN ) {

				var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
				var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

				scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );

			}

		}

		function onMouseUp( event ) {

			if ( scope.enabled === false ) return;
			if ( scope.userRotate === false ) return;

			document.removeEventListener( 'mousemove', onMouseMove, false );
			document.removeEventListener( 'mouseup', onMouseUp, false );

			state = STATE.NONE;

		}

		function onMouseWheel( event ) {

			if ( scope.enabled === false ) return;
			if ( scope.userZoom === false ) return;

			var delta = 0;

			if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9

				delta = event.wheelDelta;

			} else if ( event.detail ) { // Firefox

				delta = - event.detail;

			}

			if ( delta > 0 ) {

				scope.zoomOut();

			} else {

				scope.zoomIn();

			}

		}

		function onKeyDown( event ) {

			if ( scope.enabled === false ) return;
			if ( scope.userPan === false ) return;

			switch ( event.keyCode ) {

				/*case scope.keys.UP:
					scope.pan( new THREE.Vector3( 0, 1, 0 ) );
					break;
				case scope.keys.BOTTOM:
					scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
					break;
				case scope.keys.LEFT:
					scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
					break;
				case scope.keys.RIGHT:
					scope.pan( new THREE.Vector3( 1, 0, 0 ) );
					break;
				*/
				case scope.keys.ROTATE:
					state = STATE.ROTATE;
					break;
				case scope.keys.ZOOM:
					state = STATE.ZOOM;
					break;
				case scope.keys.PAN:
					state = STATE.PAN;
					break;
					
			}

		}
		
		function onKeyUp( event ) {

			switch ( event.keyCode ) {

				case scope.keys.ROTATE:
				case scope.keys.ZOOM:
				case scope.keys.PAN:
					state = STATE.NONE;
					break;
			}

		}

		this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
		this.domElement.addEventListener( 'mousedown', onMouseDown, false );
		this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
		this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
		window.addEventListener( 'keydown', onKeyDown, false );
		window.addEventListener( 'keyup', onKeyUp, false );

	};

	THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );

}() );

初始化三维场景,并增加点云、三个模型和控制功能

//
// Utils 工具
//

var Utils =  {};

Utils.createShadow = function (mesh, material) {
    var params = mesh.geometry.parameters;
    mesh.geometry.computeBoundingSphere();
    var geo = mesh.geometry.type === 'BoxGeometry'
        ? new THREE.PlaneGeometry(params.width, params.depth)
        : new THREE.CircleGeometry(mesh.geometry.boundingSphere.radius, 24);

    var shadow = new THREE.Mesh(geo, material);
    shadow.rotation.x = -Math.PI / 2;
    shadow.position.x = mesh.position.x;
    shadow.position.z = mesh.position.z;

    return shadow;
};

Utils.updateShadow = function (shadow, target) {
    shadow.position.x = target.position.x;
    shadow.position.z = target.position.z;
    shadow.visible = target.position.y >= 0;

    shadow.scale.x = target.scale.x;
    shadow.scale.y = target.scale.z;
};

function init(){
	var WIDTH = 800;
    var HEIGHT = 600;

    this._previousElapsed = 0;

    // setup a WebGL renderer within an existing canvas
    var canvas = document.getElementById('demo');
	// Renderer渲染器设置
    this.renderer = new THREE.WebGLRenderer({
		//抗锯齿属性,WebGLRenderer常用的一个属性
        antialias:true,
        alpha:true,
		canvas: canvas
	});
	this.renderer.setClearColor(0xb9d3ff, 0.2); //设置背景颜色和透明度

    canvas.width = WIDTH;
    canvas.height = HEIGHT;
    this.renderer.setViewport(0, 0, WIDTH, HEIGHT);

    // create the scene Scene设置
    this.scene = new THREE.Scene();
	this.scene.background = new THREE.Color('#e6e6e6');

	const fov = 40 // 视野范围
    const aspect = 2 // 相机默认值 画布的宽高比
    const near = 0.1 // 近平面
    const far = 1000 // 远平面
    // 透视投影相机
    this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
	// create an isometric camera 正交相机
    //this.camera = new THREE.OrthographicCamera(-5, 5, 5, -5, -1, 100);
    this.camera.position.set(10, 10, 10)
	this.camera.lookAt(this.scene.position); // point at origin
    //this.camera.lookAt(0, 0, 0)

    // create ground and axis / grid helpers
    var ground = new THREE.Mesh(new THREE.PlaneGeometry(10, 10),new THREE.MeshBasicMaterial({color: 0xcccccc}));
    ground.rotation.x = -Math.PI / 2;
    ground.position.y = -0.01; // to avoid z-fighting with axis and shadows
    this.scene.add(ground);
	
    this.scene.add((new THREE.AxesHelper(8)));
	
	// 光源
    const lightColor = 0xffffff;
    const intensity = 1;
    const light = new THREE.DirectionalLight(lightColor, intensity);
    this.scene.add(light);
	
	// 材质
	this.materials = {
		shadow: new THREE.MeshBasicMaterial({
			color: 0x000000,
			transparent: true,
			opacity: 0.5
		}),
		solid: new THREE.MeshNormalMaterial({}),
		colliding: new THREE.MeshBasicMaterial({
			color: 0xff0000,
			transparent: true,
			opacity: 0.5
		}),
		dot: new THREE.MeshBasicMaterial({
			color: 0x0000ff
		})
	};
	
	this.knot = new THREE.Mesh(new THREE.TorusKnotGeometry(0.5, 0.1), this.materials.solid);
    this.knot.position.set(-3, 2, 1);
    this.knot.geometry.computeBoundingSphere();

    this.sphere = new THREE.Mesh(new THREE.SphereGeometry(1), this.materials.solid);
    this.sphere.position.set(2, 2, 0);
    this.sphereShadow = Utils.createShadow(this.sphere, this.materials.shadow);

    // the object the user can control to check for collisions
    this.cube = new THREE.Mesh(new THREE.BoxGeometry(0.75, 0.75, 0.75),this.materials.solid);
    this.cube.position.set(2, 2, 1.74);
    this.cubeShadow = Utils.createShadow(this.cube, this.materials.shadow);

    // add objects to the scene
    this.scene.add(this.cube);
    this.scene.add(this.knot);
    this.scene.add(this.sphere);

    // add fake shadows to the scene
    this.scene.add(Utils.createShadow(this.knot, this.materials.shadow));
    this.scene.add(this.sphereShadow);
    this.scene.add(this.cubeShadow);
	
	// 添加相机控制器
	this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
	// 开启控制器的阻尼效果
	this.controls.enableDamping = true
	this.scene.add(this.controls);

	this.transFormControls = new THREE.TransformControls(this.camera, this.renderer.domElement);
    this.transFormControls.space = 'world';
    this.transFormControls.attach(this.cube);
    this.scene.add(this.transFormControls);
	
	this.timestamp = 0;
	
	function render(elapsed){
		window.requestAnimationFrame(render);
		// compute delta time in seconds -- also cap it
		var delta = (elapsed - this._previousElapsed) / 1000.0;
		delta = Math.min(delta, 0.25); // maximum delta of 250 ms
		this._previousElapsed = elapsed;
		
		this.update(delta);
		this.renderer.render(this.scene, this.camera);
	}
	
	
	this.world = new CANNON.World();
	
	this.addPhysicalBody = function (mesh, bodyOptions) {
		var shape;
		// create a Sphere shape for spheres and thorus knots,
		// a Box shape otherwise
		if (mesh.geometry.type === 'SphereGeometry' || mesh.geometry.type === 'ThorusKnotGeometry') {
			mesh.geometry.computeBoundingSphere();
			shape = new CANNON.Sphere(mesh.geometry.boundingSphere.radius);
		}
		else {
			mesh.geometry.computeBoundingBox();
			var box = mesh.geometry.boundingBox;
			shape = new CANNON.Box(new CANNON.Vec3(
				(box.max.x - box.min.x) / 2,
				(box.max.y - box.min.y) / 2,
				(box.max.z - box.min.z) / 2
			));
		}

		var body = new CANNON.Body(bodyOptions);
		body.addShape(shape);
		body.position.copy(mesh.position);
		body.computeAABB();
		// disable collision response so objects don't move when they collide
		// against each other
		body.collisionResponse = false;
		// keep a reference to the mesh so we can update its properties later
		body.mesh = mesh;

		this.world.addBody(body);
		return body;
	};
	
	this.initPhysicalWorld = function() {

		// add physical bodies
		this.knotBody = this.addPhysicalBody(this.knot, {mass: 1});
		this.addPhysicalBody(this.sphere, {mass: 1});
		this.cubeBody = this.addPhysicalBody(this.cube, {mass: 1});

		// register for collide events
		this.cubeBody.addEventListener('collide', function (e) {
			console.log('Collision!');
		}.bind(this));

		// rotate the knot
		this.knotBody.angularVelocity.x = Math.PI / 4;
	};
	
	this.update = function (delta) {
		this.timestamp += delta;

		// move the cube body with mouse controls
		this.controls.update();
		this.transFormControls.update();
		this.cubeBody.position.copy(this.cube.position);
		// update the cube shadow
		Utils.updateShadow(this.cubeShadow, this.cube);

		// update knot mesh rotation
		this.knot.quaternion.copy(this.knotBody.quaternion);

		// reset materials
		this.sphere.material = this.materials.solid;
		this.knot.material = this.materials.solid;

		this.updatePhysics(delta);
	};
	
	this.updatePhysics = function (delta) {
		this.world.step(delta);

		this.world.contacts.forEach(function (contact) {
			contact.bi.mesh.material = this.materials.colliding;
			contact.bj.mesh.material = this.materials.colliding;
			this.cube.material = this.materials.solid;
		}.bind(this));
	};
	
	// setup physic world
    this.initPhysicalWorld();
	
	this.addPointCloud = function(){
		// 彩色点云
		var particles = 50 * 1000;

		var geometry = new THREE.BufferGeometry();
		// 生成 5万个点需要的存储空间
		var positions = new Float32Array(particles * 3);

		// 每个顶点一种颜色
		var colors = new Float32Array(particles * 3);

		var color = new THREE.Color();

		var n = 4, n2 = n / 2; // 限定点出现的范围是[-2,2]这么一个立方体中,n2表示直径的一半

		for (var i = 0; i < positions.length; i += 9) {
		  // 通过随机数生成点的位置

		  // 生成一个顶点,范围是[-2,2]
		  var x = Math.random() * n - n2;
		  var y = Math.random() * n - n2;
		  var z = Math.random() * n - n2;

		  // 随机生成点
		  positions[i] = x;
		  positions[i + 1] = y;
		  positions[i + 2] = z;
		  
			// 为每个顶点赋值颜色
			// x / n得到范围[-0.5,0.5],加0.5得到[0,1]范围的颜色
			var vx = (x / n) + 0.5
			var vy = (y / n) + 0.5
			var vz = (z / n) + 0.5

			color.setRGB(vx, vy, vz)

			colors[i] = color.r
			colors[i + 1] = color.g
			colors[i + 2] = color.b
		}

		geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3))
		geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3))
		// 计算几何体的包围盒
		geometry.computeBoundingSphere();

		// 创建点材质
		var material = new THREE.PointsMaterial({
			size: 0.1,
			vertexColors: THREE.VertexColors
		})

		// 创建点
		particleSystem = new THREE.Points(geometry, material);
		this.scene.add(particleSystem);
	}
	
	this.addPointCloud();
	
	
	window.requestAnimationFrame(render);
	
}


//
// main
//

window.onload = function () {
	try {
	  init();
	} catch (error) {
	  console.error(error);
	  // Expected output: ReferenceError: nonExistentFunction is not defined
	  // (Note: the exact output may be browser-dependent)
	}
};

实现Web 3D效果

移动盒子,通过物理引擎cannon.js实现碰撞检测。

    this.world = new CANNON.World();
	
	this.addPhysicalBody = function (mesh, bodyOptions) {
		var shape;
		// create a Sphere shape for spheres and thorus knots,
		// a Box shape otherwise
		if (mesh.geometry.type === 'SphereGeometry' || mesh.geometry.type === 'ThorusKnotGeometry') {
			mesh.geometry.computeBoundingSphere();
			shape = new CANNON.Sphere(mesh.geometry.boundingSphere.radius);
		}
		else {
			mesh.geometry.computeBoundingBox();
			var box = mesh.geometry.boundingBox;
			shape = new CANNON.Box(new CANNON.Vec3(
				(box.max.x - box.min.x) / 2,
				(box.max.y - box.min.y) / 2,
				(box.max.z - box.min.z) / 2
			));
		}

		var body = new CANNON.Body(bodyOptions);
		body.addShape(shape);
		body.position.copy(mesh.position);
		body.computeAABB();
		// disable collision response so objects don't move when they collide
		// against each other
		body.collisionResponse = false;
		// keep a reference to the mesh so we can update its properties later
		body.mesh = mesh;

		this.world.addBody(body);
		return body;
	};
	
	this.initPhysicalWorld = function() {

		// add physical bodies
		this.knotBody = this.addPhysicalBody(this.knot, {mass: 1});
		this.addPhysicalBody(this.sphere, {mass: 1});
		this.cubeBody = this.addPhysicalBody(this.cube, {mass: 1});

		// register for collide events
		this.cubeBody.addEventListener('collide', function (e) {
			console.log('Collision!');
		}.bind(this));

		// rotate the knot
		this.knotBody.angularVelocity.x = Math.PI / 4;
	};

HTML结构

<!DOCTYPE html>
<html>
<head>
    <title>three.js+cannon.js Web 3D</title>
    <meta charset="utf-8">
	<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
	<meta name="renderer" content="webkit">
	<meta name="force-rendering" content="webkit">
	<meta http-equiv="X-UA-Compatible" content="IE=10,chrome=1">
	
	<meta data-rh="true" name="keywords" content="three.js,JavaScript,canon.js">
	<meta data-rh="true" name="description" content="three.js+cannon.js Web 3D">
	
	<meta data-rh="true" property="og:title" content="THREE.JS and CANON.JS">
	<meta data-rh="true" property="og:url" content="">
	<meta data-rh="true" property="og:description" content="three.js+cannon.js Web 3D">
	<meta data-rh="true" property="og:image" content="">
	<meta data-rh="true" property="og:type" content="article">
	<meta data-rh="true" property="og:site_name" content="">
	
    <link rel="stylesheet" href="styles.css" type="text/css">
	
	<script src="https://cdn.bootcdn.net/ajax/libs/three.js/109/three.min.js"></script>
	<script src="https://cdn.bootcdn.net/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
	
    <script src="js/lib/TransformControls.js"></script>
	<script src="js/index.js"></script>
	
</head>

<body>

<header class="main-header">
    <h1><a href="index.html">Web 3D</a></h1>
    <p>Using cannon.js (physics engine)</p>
</header>

<!-- main wrapper -->
<div class="main-wrapper">
    <!-- canvas wrapper -->
    <div class="canvas-wrapper">
        <canvas id="demo"></canvas>
        <aside class="message">
            <strong>Drag</strong> the cube around
        </aside>
    </div>
	<!-- canvas wrapper -->

    <footer class="main-footer">
        <p>Source code</p>
    </footer>
</div>
<!-- main wrapper -->

</body>
</html>

CSS样式

body {
    font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
    margin: 0;
}

canvas {
    background: transparent;
    padding: 0;
    margin: 0;
	border: 5px solid #31ffd5;
}

a, a:hover, a:visited {
    color: #000;
    text-decoration: none;
}

a {
    border-bottom: 2px solid #31ffd5;
}

a:hover {
    background:#31ffd5;
}

.main-wrapper {
    margin: 0 auto;
    text-align: center;
}

.main-header, .main-footer {
    text-align: center;
}

.subtitle {
    font-size: 1.5em;
}

.main-header {
    margin-top: 0;
    margin-bottom: 2em;
	transform: perspective(750px) translate3d(0px, 0px, -250px) rotateX(27deg) scale(0.96, 0.96);
    border-radius: 20px;
    border: 5px solid #e6e6e6;
    box-shadow: 0 70px 40px -20px rgba(0, 0, 0, 0.2);
    transition: 0.4s ease-in-out transform;
}

.main-header h1 {
    margin: 0;
    background: #31ffd5;
    padding: 0.5em 1em;
}

.main-header a {
    color: #000;
    text-decoration: none;
}

.main-footer {
    margin-top: 2em;
}

.canvas-wrapper {
    position: relative;
    background: #12345611;
}

.message {
    position: absolute;
    color: #fff;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 5px;
    text-align: center;
}

.message strong {
    color: #31ffd5;
}

li, p {
    line-height: 1.25em;
}

li {
    margin: 0.5em 0;
}

cannon.js

cannon.js是一个轻量级且简单的网络三维物理引擎。受three.js和ammon.js的启发,加上网络缺乏物理引擎,cannon.js应运而生。刚体物理引擎包括简单的碰撞检测、各种体型、接触、摩擦和约束。文件大小比许多移植的物理引擎更小。100%开源JavaScript,使用迭代高斯-塞德尔Gauss-Seidel解算器来求解约束。使用SPOOK步进器。

https://schteppe.github.io/cannon.js/

Three.js 官方文档

Three.js 官方文档是学习和使用 Three.js 这一基于 WebGL 的 JavaScript 3D 图形库的重要资源。它提供了丰富的信息,包括各种功能方法属性的详细描述,以及许多示例和教程。

https://threejs.org/docs/index.html

Three.js 中文网

Three.js 中文网是一个专注于 Three.js 资源分享与交流的中文社区网站。该网站提供了大量与 Three.js 相关的教程案例插件工具,帮助开发者更好地学习和应用 Three.js。

http://webgl3d.cn/

Three.js简版案例

Three.js简版案例集合的目标是提供一组基本的、有指导意义的示例,介绍Three.js中的各种功能。每个页面的源代码都包含详细的注释。

https://stemkoski.github.io/Three.js/

探索three.js

探索three.js是对 web 作为 3D 图形平台的完整介绍,它使用 three.js WebGL 库,编写自一位核心 three.js 开发人员。可以作为一个引导读者深入了解 three.js 库的指南。

https://discoverthreejs.com/zh/

three.js editor

three.js editor是一个基于 Three.js 库的在线3D编辑器。这个编辑器允许用户直接在网页上创建和编辑 3D 场景。在 Three.js 编辑器中,你可以:

  • 创建和编辑 3D 对象:通过编辑器提供的工具,你可以创建各种几何体(如立方体、球体、圆柱体等),并调整它们的位置、旋转和缩放。

  • 添加材质和贴图:为 3D 对象添加不同的材质,如基本材质、物理材质等,并可以应用贴图来改变对象的外观。

  • 设置光源和环境:在场景中添加不同类型的光源(如点光源、平行光源、环境光等),并调整光照参数。

  • 实时预览:编辑器会实时渲染你的 3D 场景,让你可以立即看到所做的更改。

  • 导入和导出模型:支持导入和导出多种 3D 文件格式,如 glTF、OBJ 等。

https://threejs.org/editor/

Shadertoy 

Shadertoy是一个基于 WebGL 的在线实时渲染平台,主要用于编辑分享查看着色器shader 程序及其实现的效果。

在这个平台上,用户可以创作和分享自己的 3D 图形效果。它提供了一个简单方便的环境,让用户可以轻松编辑自己的片段着色器,并实时查看修改的效果。

同时,Shadertoy 上有许多大佬分享他们制作的酷炫效果的代码,这些代码是完全开源的,用户可以在这些代码的基础上进行修改和学习。

除此之外,Shadertoy 还允许用户选择声音频道,将当前帧的声音信息转变成纹理(Texture),传入 shader 当中,从而根据声音信息来控制图形。这使得 Shadertoy 在视觉和听觉的结合上有了更多的可能性。

https://www.shadertoy.com/

glsl.app 

glsl.app 是一个在线的 GLSL (OpenGL Shading Language) 编辑器。GLSL 是一种用于图形渲染的着色语言,特别是在 OpenGL 图形库中。这种语言允许开发者为图形硬件编写着色器程序,这些程序可以运行在 GPU 上,用于计算图像的各种视觉效果。在 glsl.app 上,你可以:

  • 编写和编辑着色器代码:直接在网页上编写顶点着色器、片元着色器等。

  • 实时预览:当你编写或修改着色器代码时,可以立即在右侧的预览窗口中看到效果。

  • 分享你的作品:完成你的着色器后,你可以获得一个链接,通过这个链接与其他人分享你的作品。

  • 学习:如果你是初学者,该网站还提供了很多示例和教程,帮助你了解如何编写各种着色器效果。

Online WebGL (GLSL) Shaders Editor and Playground

参见:

解释基本的 3D 原理 - 游戏开发 | MDN

Three.js - examples

Three.js教程

《探索three.js》

three.js docs

3D 碰撞检测 - 游戏开发 | MDN

Mozilla Developer Relations · GitHub

Cannon.js - JavaScripting

schteppe/cannon.js @ GitHub

Beautiful CSS 3D Transform Perspective Examples in 2024 | Polypane

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/414465.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink SQL 中的流式概念:状态算子

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

认识AJAX

一、什么是Ajax? 有跳转就是同步&#xff0c;无跳转就是异步 Asynchronous Javascript And XML&#xff08;异步JavaScript和XML&#xff09; Ajax 异步 JavaScript 和XML。Ajax是一种用于创建快速动态网页的技术通过在后台与服务器进行少量数据交换&#xff0c;Ajax可以使网…

Python手册(Machine Learning)--LightGBM

Overview LightGBM&#xff08;Light Gradient Boosting Machine&#xff09;是一种高效的 Gradient Boosting 算法&#xff0c; 主要用于解决GBDT在海量数据中遇到的问题&#xff0c;以便更好更快的用于工业实践中。 数据结构说明lightgbm.DatasetLightGBM数据集lightgbm.Bo…

Stable Diffusion WebUI 图库浏览器插件:浏览器以前生成的图片

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本文介绍的插件叫图库浏览器&#xff0c;是一个用于浏览器以前生成的图片信息的插件。本文将介绍该插件的安装和使用&#xff0c;希望能够对你有所帮助。 文章…

【JavaEE】_前端POST请求使用json向后端传参

目录 1. 关于json 2. 通过Maven仓库&#xff0c;将Jackson下载导入到项目中 3. 使用Jackson 3.1 关于readValue方法 3.2 关于Request.class类对象 3.3 关于request对象的属性类型 3.4 关于writeValueAsString 前端向后端传递参数通常有三种方法&#xff1a; 第一种&…

UI自动化测试:playwright工具(一):python环境下安装、UI录制使用(需要些代码能力)

一、python环境下安装playwright工具 1. 安装playwright库 pip install playwright -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com#至于镜像源,可以选,也可不选:#阿里云 http://mirrors.aliyun.com/pypi/simple/ #中国科技大学 https://py…

全面升级!Apache HugeGraph 1.2.0版本发布

图数据库以独特的数据管理和分析能力&#xff0c;在企业数智化转型的过程中正在成为数据治理的核心&#xff0c;根据IDC调研显示&#xff0c;95%的企业认为图数据库是重要的数据管理工具&#xff0c;超过65%的厂商认为在业务上图数据库优于其他选择&#xff0c;尤其是在金融风控…

C语言题目练习

目录 前言 1、网购 1.1题目 描述 输入描述&#xff1a; 输出描述&#xff1a; ​编辑 1.1 解题 2、带空格直角三角形图案 2.1题目 描述 输入描述&#xff1a; 输出描述&#xff1a; ​编辑 2.2 解题 3、小乐乐改数字 3.1 题目 描述 输入描述&#xff1a; 输…

基于非合作博弈的风-光-氢微电网容量优化配置(matlab代码)

目录 1 主要内容 模型架构图 目标函数 非合作博弈流程 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现《基于非合作博弈的风-光-氢微电网容量优化配置》&#xff0c;程序包含3种场景&#xff0c;场景1中包含风电、光伏和制氢-储氢-发电3种分布式电源&#xff…

nginx 模块 高级配置

目录 一、高级配置 1.1. 网页的状态页 1.2.Nginx 第三方模块 ehco 模块 打印 1.3.变量 1.3.1 内置变量 1.3.2自定义变量 1.4.Nginx压缩功能 1.5.https 功能 1.6.自定义图标 一、高级配置 1.1. 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&…

探索 SPA 与 MPA:前端架构的选择与权衡

查看本专栏目录 关于作者 还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#x…

面试必问但日常不愿意看的题

1&#xff0c;做道 this 相关的题&#xff0c;看你对 js 的 this 掌握的如何2&#xff0c;BFC 这样答才完美 1&#xff0c;什么是 BFC&#xff1f;其规则是什么&#xff1f;2&#xff0c;如何触发 BFC3&#xff0c;BFC 到底可以解决什么问题呢3&#xff0c;作用域4&#xff0c;…

便携式森林消防灭火泵:森林安全的守护者

在自然环境中&#xff0c;森林是地球生态系统的重要组成部分&#xff0c;它们为我们提供氧气、净化空气、防止土壤侵蚀等重要功能。然而&#xff0c;当森林发生火灾时&#xff0c;它们也会成为我们的噩梦。火势蔓延迅速&#xff0c;难以控制&#xff0c;对森林和生态环境造成严…

星际争霸之小霸王之小蜜蜂(一)--窗口界面设计

目录 前言 一、安装pygame库 1、pygame库简介 2、在windows系统安装pygame库 二 、搭建游戏框架 1、创建游戏窗口 2、改变窗口颜色 总结 前言 大家应该都看过或者都听说过python神书“大蟒蛇”&#xff0c;上面有一个案例是《外星人入侵》&#xff0c;游戏介绍让我想起了上…

深度学习 精选笔记(5)多层感知机

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

【漏洞复现】鸿运(通天星CMSV6车载)主动安全监控云平台存在敏感信息泄露漏

漏洞描述 鸿运(通天星CMSV6车载)主动安全监控云平台实现对计算资源、存储资源、网络资源、云应用服务进行7*24小时全时区、多地域、全方位、立体式、智能化的IT运维监控,保障IT系统安全、稳定、可靠运行。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法…

Flask入门一(介绍、Flask安装、Flask运行方式及使用、虚拟环境、调试模式、配置文件、路由系统)

文章目录 一、Flask介绍二、Flask创建和运行1.安装2.快速使用3.Flask小知识4.flask的运行方式 三、Werkzeug介绍四、Jinja2介绍五、Click CLI 介绍六、Flask安装介绍watchdog使用python--dotenv使用&#xff08;操作环境变量&#xff09; 七、虚拟环境介绍Mac/linux创建虚拟环境…

个人建站前端篇(七)vite + vue3企业级项目模板

一、vite命令行创建项目 npm create vitelatest根据提示选择模板&#xff0c;选择vite vue3 ts即可。 二、项目连接远程仓库 git init git remote add origin https://gitee.com/niech_project/vite-vue3-template.git git pull origin master git checkout -b dev三、项目…

《大模型时代-ChatGPT开启通用人工智能浪潮》精华摘抄

原书很长&#xff0c;有19.3w字&#xff0c;本文尝试浓缩一下其中的精华。 知识点 GPT相关 谷歌发布LaMDA、BERT和PaLM-E&#xff0c;PaLM 2 Facebook的母公司Meta推出LLaMA&#xff0c;并在博客上免费公开LLM&#xff1a;OPT-175B。 在GPT中&#xff0c;P代表经过预训练(…

pclpy Ransac平面分割算法输出的索引从点云中提取点云的子集

pclpy Ransac平面分割算法输出的索引从点云中提取点云的子集 一、算法原理二、代码三、结果1.sor统计滤波2.Ransac内点分割平面3.Ransac外点分割平面 四、相关数据 一、算法原理 1、Ransac介绍 RANSAC(RAndom SAmple Consensus,随机采样一致)算法是从一组含有“外点”(outlier…