You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
8.2 KiB

2 years ago
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - arcball controls</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">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - arcball controls<br/>
<a href="http://www.polycount.com/forum/showthread.php?t=130641" target="_blank" rel="noopener">Cerberus(FFVII Gun) model</a> by Andrew Maximov.
</div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
const cameras = [ 'Orthographic', 'Perspective' ];
const cameraType = { type: 'Perspective' };
const perspectiveDistance = 2.5;
const orthographicDistance = 120;
let camera, controls, scene, renderer, gui;
let folderOptions, folderAnimations;
const arcballGui = {
gizmoVisible: true,
setArcballControls: function () {
controls = new ArcballControls( camera, renderer.domElement, scene );
controls.addEventListener( 'change', render );
this.gizmoVisible = true;
this.populateGui();
},
populateGui: function () {
folderOptions.add( controls, 'enabled' ).name( 'Enable controls' );
folderOptions.add( controls, 'enableGrid' ).name( 'Enable Grid' );
folderOptions.add( controls, 'enableRotate' ).name( 'Enable rotate' );
folderOptions.add( controls, 'enablePan' ).name( 'Enable pan' );
folderOptions.add( controls, 'enableZoom' ).name( 'Enable zoom' );
folderOptions.add( controls, 'cursorZoom' ).name( 'Cursor zoom' );
folderOptions.add( controls, 'adjustNearFar' ).name( 'adjust near/far' );
folderOptions.add( controls, 'scaleFactor', 1.1, 10, 0.1 ).name( 'Scale factor' );
folderOptions.add( controls, 'minDistance', 0, 50, 0.5 ).name( 'Min distance' );
folderOptions.add( controls, 'maxDistance', 0, 50, 0.5 ).name( 'Max distance' );
folderOptions.add( controls, 'minZoom', 0, 50, 0.5 ).name( 'Min zoom' );
folderOptions.add( controls, 'maxZoom', 0, 50, 0.5 ).name( 'Max zoom' );
folderOptions.add( arcballGui, 'gizmoVisible' ).name( 'Show gizmos' ).onChange( function () {
controls.setGizmosVisible( arcballGui.gizmoVisible );
} );
folderOptions.add( controls, 'copyState' ).name( 'Copy state(ctrl+c)' );
folderOptions.add( controls, 'pasteState' ).name( 'Paste state(ctrl+v)' );
folderOptions.add( controls, 'reset' ).name( 'Reset' );
folderAnimations.add( controls, 'enableAnimations' ).name( 'Enable anim.' );
folderAnimations.add( controls, 'dampingFactor', 0, 100, 1 ).name( 'Damping' );
folderAnimations.add( controls, 'wMax', 0, 100, 1 ).name( 'Angular spd' );
}
};
init();
function init() {
const container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 3;
renderer.domElement.style.background = 'linear-gradient( 180deg, rgba( 0,0,0,1 ) 0%, rgba( 128,128,255,1 ) 100% )';
container.appendChild( renderer.domElement );
//
scene = new THREE.Scene();
scene.add( new THREE.HemisphereLight( 0x443333, 0x222233, 4 ) );
camera = makePerspectiveCamera();
camera.position.set( 0, 0, perspectiveDistance );
const material = new THREE.MeshStandardMaterial();
new OBJLoader()
.setPath( 'models/obj/cerberus/' )
.load( 'Cerberus.obj', function ( group ) {
const textureLoader = new THREE.TextureLoader().setPath( 'models/obj/cerberus/' );
material.roughness = 1;
material.metalness = 1;
const diffuseMap = textureLoader.load( 'Cerberus_A.jpg', render );
diffuseMap.encoding = THREE.sRGBEncoding;
material.map = diffuseMap;
material.metalnessMap = material.roughnessMap = textureLoader.load( 'Cerberus_RM.jpg', render );
material.normalMap = textureLoader.load( 'Cerberus_N.jpg', render );
material.map.wrapS = THREE.RepeatWrapping;
material.roughnessMap.wrapS = THREE.RepeatWrapping;
material.metalnessMap.wrapS = THREE.RepeatWrapping;
material.normalMap.wrapS = THREE.RepeatWrapping;
group.traverse( function ( child ) {
if ( child.isMesh ) {
child.material = material;
}
} );
group.rotation.y = Math.PI / 2;
group.position.x += 0.25;
scene.add( group );
render();
new RGBELoader()
.setPath( 'textures/equirectangular/' )
.load( 'venice_sunset_1k.hdr', function ( hdrEquirect ) {
hdrEquirect.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = hdrEquirect;
render();
} );
window.addEventListener( 'keydown', onKeyDown );
window.addEventListener( 'resize', onWindowResize );
//
gui = new GUI();
gui.add( cameraType, 'type', cameras ).name( 'Choose Camera' ).onChange( function () {
setCamera( cameraType.type );
} );
folderOptions = gui.addFolder( 'Arcball parameters' );
folderAnimations = folderOptions.addFolder( 'Animations' );
arcballGui.setArcballControls();
render();
} );
}
function makeOrthographicCamera() {
const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5;
const halfFovH = Math.atan( ( window.innerWidth / window.innerHeight ) * Math.tan( halfFovV ) );
const halfW = perspectiveDistance * Math.tan( halfFovH );
const halfH = perspectiveDistance * Math.tan( halfFovV );
const near = 0.01;
const far = 2000;
const newCamera = new THREE.OrthographicCamera( - halfW, halfW, halfH, - halfH, near, far );
return newCamera;
}
function makePerspectiveCamera() {
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 0.01;
const far = 2000;
const newCamera = new THREE.PerspectiveCamera( fov, aspect, near, far );
return newCamera;
}
function onWindowResize() {
if ( camera.type == 'OrthographicCamera' ) {
const halfFovV = THREE.MathUtils.DEG2RAD * 45 * 0.5;
const halfFovH = Math.atan( ( window.innerWidth / window.innerHeight ) * Math.tan( halfFovV ) );
const halfW = perspectiveDistance * Math.tan( halfFovH );
const halfH = perspectiveDistance * Math.tan( halfFovV );
camera.left = - halfW;
camera.right = halfW;
camera.top = halfH;
camera.bottom = - halfH;
} else if ( camera.type == 'PerspectiveCamera' ) {
camera.aspect = window.innerWidth / window.innerHeight;
}
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function render() {
renderer.render( scene, camera );
}
function onKeyDown( event ) {
if ( event.key === 'c' ) {
if ( event.ctrlKey || event.metaKey ) {
controls.copyState();
}
} else if ( event.key === 'v' ) {
if ( event.ctrlKey || event.metaKey ) {
controls.pasteState();
}
}
}
function setCamera( type ) {
if ( type == 'Orthographic' ) {
camera = makeOrthographicCamera();
camera.position.set( 0, 0, orthographicDistance );
} else if ( type == 'Perspective' ) {
camera = makePerspectiveCamera();
camera.position.set( 0, 0, perspectiveDistance );
}
controls.setCamera( camera );
render();
}
</script>
</body>