mirror of
https://github.com/xemu-project/xemu-website.git
synced 2024-11-23 03:19:44 +00:00
Add 3d xbox
This commit is contained in:
parent
6ea4a9d700
commit
c32a3f777b
6
resources/three.min.js
vendored
6
resources/three.min.js
vendored
File diff suppressed because one or more lines are too long
3224
resources/three/controls/ArcballControls.js
Normal file
3224
resources/three/controls/ArcballControls.js
Normal file
File diff suppressed because it is too large
Load Diff
613
resources/three/loaders/DRACOLoader.js
Normal file
613
resources/three/loaders/DRACOLoader.js
Normal file
@ -0,0 +1,613 @@
|
||||
import {
|
||||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
Color,
|
||||
FileLoader,
|
||||
Loader,
|
||||
LinearSRGBColorSpace,
|
||||
SRGBColorSpace
|
||||
} from 'three';
|
||||
|
||||
const _taskCache = new WeakMap();
|
||||
|
||||
class DRACOLoader extends Loader {
|
||||
|
||||
constructor( manager ) {
|
||||
|
||||
super( manager );
|
||||
|
||||
this.decoderPath = '';
|
||||
this.decoderConfig = {};
|
||||
this.decoderBinary = null;
|
||||
this.decoderPending = null;
|
||||
|
||||
this.workerLimit = 4;
|
||||
this.workerPool = [];
|
||||
this.workerNextTaskID = 1;
|
||||
this.workerSourceURL = '';
|
||||
|
||||
this.defaultAttributeIDs = {
|
||||
position: 'POSITION',
|
||||
normal: 'NORMAL',
|
||||
color: 'COLOR',
|
||||
uv: 'TEX_COORD'
|
||||
};
|
||||
this.defaultAttributeTypes = {
|
||||
position: 'Float32Array',
|
||||
normal: 'Float32Array',
|
||||
color: 'Float32Array',
|
||||
uv: 'Float32Array'
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
setDecoderPath( path ) {
|
||||
|
||||
this.decoderPath = path;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
setDecoderConfig( config ) {
|
||||
|
||||
this.decoderConfig = config;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
setWorkerLimit( workerLimit ) {
|
||||
|
||||
this.workerLimit = workerLimit;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
load( url, onLoad, onProgress, onError ) {
|
||||
|
||||
const loader = new FileLoader( this.manager );
|
||||
|
||||
loader.setPath( this.path );
|
||||
loader.setResponseType( 'arraybuffer' );
|
||||
loader.setRequestHeader( this.requestHeader );
|
||||
loader.setWithCredentials( this.withCredentials );
|
||||
|
||||
loader.load( url, ( buffer ) => {
|
||||
|
||||
this.parse( buffer, onLoad, onError );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
}
|
||||
|
||||
|
||||
parse( buffer, onLoad, onError = ()=>{} ) {
|
||||
|
||||
this.decodeDracoFile( buffer, onLoad, null, null, SRGBColorSpace, onError ).catch( onError );
|
||||
|
||||
}
|
||||
|
||||
decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = LinearSRGBColorSpace, onError = () => {} ) {
|
||||
|
||||
const taskConfig = {
|
||||
attributeIDs: attributeIDs || this.defaultAttributeIDs,
|
||||
attributeTypes: attributeTypes || this.defaultAttributeTypes,
|
||||
useUniqueIDs: !! attributeIDs,
|
||||
vertexColorSpace: vertexColorSpace,
|
||||
};
|
||||
|
||||
return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError );
|
||||
|
||||
}
|
||||
|
||||
decodeGeometry( buffer, taskConfig ) {
|
||||
|
||||
const taskKey = JSON.stringify( taskConfig );
|
||||
|
||||
// Check for an existing task using this buffer. A transferred buffer cannot be transferred
|
||||
// again from this thread.
|
||||
if ( _taskCache.has( buffer ) ) {
|
||||
|
||||
const cachedTask = _taskCache.get( buffer );
|
||||
|
||||
if ( cachedTask.key === taskKey ) {
|
||||
|
||||
return cachedTask.promise;
|
||||
|
||||
} else if ( buffer.byteLength === 0 ) {
|
||||
|
||||
// Technically, it would be possible to wait for the previous task to complete,
|
||||
// transfer the buffer back, and decode again with the second configuration. That
|
||||
// is complex, and I don't know of any reason to decode a Draco buffer twice in
|
||||
// different ways, so this is left unimplemented.
|
||||
throw new Error(
|
||||
|
||||
'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
|
||||
'settings. Buffer has already been transferred.'
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
let worker;
|
||||
const taskID = this.workerNextTaskID ++;
|
||||
const taskCost = buffer.byteLength;
|
||||
|
||||
// Obtain a worker and assign a task, and construct a geometry instance
|
||||
// when the task completes.
|
||||
const geometryPending = this._getWorker( taskID, taskCost )
|
||||
.then( ( _worker ) => {
|
||||
|
||||
worker = _worker;
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
worker._callbacks[ taskID ] = { resolve, reject };
|
||||
|
||||
worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
|
||||
|
||||
// this.debug();
|
||||
|
||||
} );
|
||||
|
||||
} )
|
||||
.then( ( message ) => this._createGeometry( message.geometry ) );
|
||||
|
||||
// Remove task from the task list.
|
||||
// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
|
||||
geometryPending
|
||||
.catch( () => true )
|
||||
.then( () => {
|
||||
|
||||
if ( worker && taskID ) {
|
||||
|
||||
this._releaseTask( worker, taskID );
|
||||
|
||||
// this.debug();
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
// Cache the task result.
|
||||
_taskCache.set( buffer, {
|
||||
|
||||
key: taskKey,
|
||||
promise: geometryPending
|
||||
|
||||
} );
|
||||
|
||||
return geometryPending;
|
||||
|
||||
}
|
||||
|
||||
_createGeometry( geometryData ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
|
||||
if ( geometryData.index ) {
|
||||
|
||||
geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
|
||||
|
||||
const result = geometryData.attributes[ i ];
|
||||
const name = result.name;
|
||||
const array = result.array;
|
||||
const itemSize = result.itemSize;
|
||||
|
||||
const attribute = new BufferAttribute( array, itemSize );
|
||||
|
||||
if ( name === 'color' ) {
|
||||
|
||||
this._assignVertexColorSpace( attribute, result.vertexColorSpace );
|
||||
|
||||
attribute.normalized = ( array instanceof Float32Array ) === false;
|
||||
|
||||
}
|
||||
|
||||
geometry.setAttribute( name, attribute );
|
||||
|
||||
}
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
_assignVertexColorSpace( attribute, inputColorSpace ) {
|
||||
|
||||
// While .drc files do not specify colorspace, the only 'official' tooling
|
||||
// is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc
|
||||
// file is passed into .load() or .parse(). GLTFLoader uses internal APIs
|
||||
// to decode geometry, and vertex colors are already Linear-sRGB in there.
|
||||
|
||||
if ( inputColorSpace !== SRGBColorSpace ) return;
|
||||
|
||||
const _color = new Color();
|
||||
|
||||
for ( let i = 0, il = attribute.count; i < il; i ++ ) {
|
||||
|
||||
_color.fromBufferAttribute( attribute, i ).convertSRGBToLinear();
|
||||
attribute.setXYZ( i, _color.r, _color.g, _color.b );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_loadLibrary( url, responseType ) {
|
||||
|
||||
const loader = new FileLoader( this.manager );
|
||||
loader.setPath( this.decoderPath );
|
||||
loader.setResponseType( responseType );
|
||||
loader.setWithCredentials( this.withCredentials );
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
loader.load( url, resolve, undefined, reject );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
||||
this._initDecoder();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
_initDecoder() {
|
||||
|
||||
if ( this.decoderPending ) return this.decoderPending;
|
||||
|
||||
const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
|
||||
const librariesPending = [];
|
||||
|
||||
if ( useJS ) {
|
||||
|
||||
librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
|
||||
|
||||
} else {
|
||||
|
||||
librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
|
||||
librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
|
||||
|
||||
}
|
||||
|
||||
this.decoderPending = Promise.all( librariesPending )
|
||||
.then( ( libraries ) => {
|
||||
|
||||
const jsContent = libraries[ 0 ];
|
||||
|
||||
if ( ! useJS ) {
|
||||
|
||||
this.decoderConfig.wasmBinary = libraries[ 1 ];
|
||||
|
||||
}
|
||||
|
||||
const fn = DRACOWorker.toString();
|
||||
|
||||
const body = [
|
||||
'/* draco decoder */',
|
||||
jsContent,
|
||||
'',
|
||||
'/* worker */',
|
||||
fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
|
||||
].join( '\n' );
|
||||
|
||||
this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
|
||||
|
||||
} );
|
||||
|
||||
return this.decoderPending;
|
||||
|
||||
}
|
||||
|
||||
_getWorker( taskID, taskCost ) {
|
||||
|
||||
return this._initDecoder().then( () => {
|
||||
|
||||
if ( this.workerPool.length < this.workerLimit ) {
|
||||
|
||||
const worker = new Worker( this.workerSourceURL );
|
||||
|
||||
worker._callbacks = {};
|
||||
worker._taskCosts = {};
|
||||
worker._taskLoad = 0;
|
||||
|
||||
worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
|
||||
|
||||
worker.onmessage = function ( e ) {
|
||||
|
||||
const message = e.data;
|
||||
|
||||
switch ( message.type ) {
|
||||
|
||||
case 'decode':
|
||||
worker._callbacks[ message.id ].resolve( message );
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
worker._callbacks[ message.id ].reject( message );
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.workerPool.push( worker );
|
||||
|
||||
} else {
|
||||
|
||||
this.workerPool.sort( function ( a, b ) {
|
||||
|
||||
return a._taskLoad > b._taskLoad ? - 1 : 1;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
const worker = this.workerPool[ this.workerPool.length - 1 ];
|
||||
worker._taskCosts[ taskID ] = taskCost;
|
||||
worker._taskLoad += taskCost;
|
||||
return worker;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
_releaseTask( worker, taskID ) {
|
||||
|
||||
worker._taskLoad -= worker._taskCosts[ taskID ];
|
||||
delete worker._callbacks[ taskID ];
|
||||
delete worker._taskCosts[ taskID ];
|
||||
|
||||
}
|
||||
|
||||
debug() {
|
||||
|
||||
console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
for ( let i = 0; i < this.workerPool.length; ++ i ) {
|
||||
|
||||
this.workerPool[ i ].terminate();
|
||||
|
||||
}
|
||||
|
||||
this.workerPool.length = 0;
|
||||
|
||||
if ( this.workerSourceURL !== '' ) {
|
||||
|
||||
URL.revokeObjectURL( this.workerSourceURL );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* WEB WORKER */
|
||||
|
||||
function DRACOWorker() {
|
||||
|
||||
let decoderConfig;
|
||||
let decoderPending;
|
||||
|
||||
onmessage = function ( e ) {
|
||||
|
||||
const message = e.data;
|
||||
|
||||
switch ( message.type ) {
|
||||
|
||||
case 'init':
|
||||
decoderConfig = message.decoderConfig;
|
||||
decoderPending = new Promise( function ( resolve/*, reject*/ ) {
|
||||
|
||||
decoderConfig.onModuleLoaded = function ( draco ) {
|
||||
|
||||
// Module is Promise-like. Wrap before resolving to avoid loop.
|
||||
resolve( { draco: draco } );
|
||||
|
||||
};
|
||||
|
||||
DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
|
||||
|
||||
} );
|
||||
break;
|
||||
|
||||
case 'decode':
|
||||
const buffer = message.buffer;
|
||||
const taskConfig = message.taskConfig;
|
||||
decoderPending.then( ( module ) => {
|
||||
|
||||
const draco = module.draco;
|
||||
const decoder = new draco.Decoder();
|
||||
|
||||
try {
|
||||
|
||||
const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig );
|
||||
|
||||
const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
|
||||
|
||||
if ( geometry.index ) buffers.push( geometry.index.array.buffer );
|
||||
|
||||
self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
console.error( error );
|
||||
|
||||
self.postMessage( { type: 'error', id: message.id, error: error.message } );
|
||||
|
||||
} finally {
|
||||
|
||||
draco.destroy( decoder );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function decodeGeometry( draco, decoder, array, taskConfig ) {
|
||||
|
||||
const attributeIDs = taskConfig.attributeIDs;
|
||||
const attributeTypes = taskConfig.attributeTypes;
|
||||
|
||||
let dracoGeometry;
|
||||
let decodingStatus;
|
||||
|
||||
const geometryType = decoder.GetEncodedGeometryType( array );
|
||||
|
||||
if ( geometryType === draco.TRIANGULAR_MESH ) {
|
||||
|
||||
dracoGeometry = new draco.Mesh();
|
||||
decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry );
|
||||
|
||||
} else if ( geometryType === draco.POINT_CLOUD ) {
|
||||
|
||||
dracoGeometry = new draco.PointCloud();
|
||||
decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
|
||||
|
||||
}
|
||||
|
||||
if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
|
||||
|
||||
throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
|
||||
|
||||
}
|
||||
|
||||
const geometry = { index: null, attributes: [] };
|
||||
|
||||
// Gather all vertex attributes.
|
||||
for ( const attributeName in attributeIDs ) {
|
||||
|
||||
const attributeType = self[ attributeTypes[ attributeName ] ];
|
||||
|
||||
let attribute;
|
||||
let attributeID;
|
||||
|
||||
// A Draco file may be created with default vertex attributes, whose attribute IDs
|
||||
// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
|
||||
// a Draco file may contain a custom set of attributes, identified by known unique
|
||||
// IDs. glTF files always do the latter, and `.drc` files typically do the former.
|
||||
if ( taskConfig.useUniqueIDs ) {
|
||||
|
||||
attributeID = attributeIDs[ attributeName ];
|
||||
attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
|
||||
|
||||
} else {
|
||||
|
||||
attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
|
||||
|
||||
if ( attributeID === - 1 ) continue;
|
||||
|
||||
attribute = decoder.GetAttribute( dracoGeometry, attributeID );
|
||||
|
||||
}
|
||||
|
||||
const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute );
|
||||
|
||||
if ( attributeName === 'color' ) {
|
||||
|
||||
attributeResult.vertexColorSpace = taskConfig.vertexColorSpace;
|
||||
|
||||
}
|
||||
|
||||
geometry.attributes.push( attributeResult );
|
||||
|
||||
}
|
||||
|
||||
// Add index.
|
||||
if ( geometryType === draco.TRIANGULAR_MESH ) {
|
||||
|
||||
geometry.index = decodeIndex( draco, decoder, dracoGeometry );
|
||||
|
||||
}
|
||||
|
||||
draco.destroy( dracoGeometry );
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
function decodeIndex( draco, decoder, dracoGeometry ) {
|
||||
|
||||
const numFaces = dracoGeometry.num_faces();
|
||||
const numIndices = numFaces * 3;
|
||||
const byteLength = numIndices * 4;
|
||||
|
||||
const ptr = draco._malloc( byteLength );
|
||||
decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
|
||||
const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
|
||||
draco._free( ptr );
|
||||
|
||||
return { array: index, itemSize: 1 };
|
||||
|
||||
}
|
||||
|
||||
function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
|
||||
|
||||
const numComponents = attribute.num_components();
|
||||
const numPoints = dracoGeometry.num_points();
|
||||
const numValues = numPoints * numComponents;
|
||||
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
|
||||
const dataType = getDracoDataType( draco, attributeType );
|
||||
|
||||
const ptr = draco._malloc( byteLength );
|
||||
decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
|
||||
const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
|
||||
draco._free( ptr );
|
||||
|
||||
return {
|
||||
name: attributeName,
|
||||
array: array,
|
||||
itemSize: numComponents
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function getDracoDataType( draco, attributeType ) {
|
||||
|
||||
switch ( attributeType ) {
|
||||
|
||||
case Float32Array: return draco.DT_FLOAT32;
|
||||
case Int8Array: return draco.DT_INT8;
|
||||
case Int16Array: return draco.DT_INT16;
|
||||
case Int32Array: return draco.DT_INT32;
|
||||
case Uint8Array: return draco.DT_UINT8;
|
||||
case Uint16Array: return draco.DT_UINT16;
|
||||
case Uint32Array: return draco.DT_UINT32;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { DRACOLoader };
|
4724
resources/three/loaders/GLTFLoader.js
Normal file
4724
resources/three/loaders/GLTFLoader.js
Normal file
File diff suppressed because it is too large
Load Diff
34
resources/three/loaders/draco_decoder.js
Normal file
34
resources/three/loaders/draco_decoder.js
Normal file
File diff suppressed because one or more lines are too long
53479
resources/three/three.module.js
Normal file
53479
resources/three/three.module.js
Normal file
File diff suppressed because one or more lines are too long
1373
resources/three/utils/BufferGeometryUtils.js
Normal file
1373
resources/three/utils/BufferGeometryUtils.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/xbox.glb
Normal file
BIN
resources/xbox.glb
Normal file
Binary file not shown.
@ -13,6 +13,17 @@
|
||||
margin: -5rem 0 0; /* negative fixed header height */
|
||||
}
|
||||
|
||||
#xbox-scene {
|
||||
position: relative;
|
||||
padding-bottom: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#xbox-scene canvas {
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#logo-canvas {
|
||||
display: block;
|
||||
@ -146,9 +157,9 @@
|
||||
<div class="container">
|
||||
<div class="row mt-5">
|
||||
<div class="col-md-6 mt-4 pb-5 pb-md-0 text-center">
|
||||
<img src="xbox_logo.png" id="xbox-logo" class="img-fluid" width=450 />
|
||||
<div id="xbox-scene"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-6 my-auto">
|
||||
<h1 class="display-3">
|
||||
<canvas id="logo-canvas" class="gl-logo"></canvas>
|
||||
<img id="logo-fallback" alt="xemu logo" src="/logo-green-jumbotron.svg" style="height: 1.03em;">
|
||||
@ -321,20 +332,41 @@ if (platform != undefined) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="three.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "./three/three.module.js",
|
||||
"three/addons/": "./three/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
||||
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
|
||||
|
||||
const gltf_loader = new GLTFLoader();
|
||||
const draco_loader = new DRACOLoader();
|
||||
draco_loader.setDecoderPath( 'three/loaders/' );
|
||||
draco_loader.setDecoderConfig( { type: 'js' } );
|
||||
gltf_loader.setDRACOLoader( draco_loader );
|
||||
|
||||
var canvas = document.querySelector('#bg-scene');
|
||||
var scene = new THREE.Scene();
|
||||
var camera = new THREE.PerspectiveCamera( 74.5, canvas.offsetWidth/canvas.offsetHeight, 0.1, 1000 );
|
||||
scene.fog = new THREE.Fog('black', 0.2, 2.45);
|
||||
scene.background = new THREE.Color('black');
|
||||
|
||||
var renderer = new THREE.WebGLRenderer();
|
||||
var renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize( canvas.offsetWidth, canvas.offsetHeight );
|
||||
renderer.setPixelRatio( window.devicePixelRatio );
|
||||
canvas.appendChild( renderer.domElement );
|
||||
|
||||
const texture = new THREE.TextureLoader().load("mesh_pattern.svg");
|
||||
texture.colorSpace = THREE.SRGBColorSpace;
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.repeat.set( 50, -25 );
|
||||
@ -343,6 +375,7 @@ const geometry = new THREE.SphereGeometry( 1, 50, 25 );
|
||||
var material = new THREE.MeshBasicMaterial( { map: texture, side: THREE.BackSide } );
|
||||
var sphere = new THREE.Mesh( geometry, material );
|
||||
scene.add( sphere );
|
||||
|
||||
camera.position.z = 0.65;
|
||||
camera.position.x = 0.12;
|
||||
|
||||
@ -350,7 +383,8 @@ var clock = new THREE.Clock();
|
||||
var render = function () {
|
||||
requestAnimationFrame( render );
|
||||
var t = clock.getElapsedTime();
|
||||
sphere.rotation.y = (Math.PI/10) * Math.sin(t * Math.PI / 50);
|
||||
var s = Math.sin(t * Math.PI / 50);
|
||||
sphere.rotation.y = (Math.PI/10) * s;
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
render();
|
||||
@ -359,12 +393,123 @@ window.addEventListener('resize', function(){
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
|
||||
}, false);
|
||||
|
||||
var xbox_canvas = document.querySelector('#xbox-scene');
|
||||
var xbox_scene = new THREE.Scene();
|
||||
// xbox_scene.background = new THREE.Color('black');
|
||||
|
||||
var xbox_camera = new THREE.PerspectiveCamera( 42.5, xbox_canvas.offsetWidth/xbox_canvas.offsetHeight, 0.1, 1000 );
|
||||
|
||||
var xbox_renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
xbox_renderer.setSize( xbox_canvas.offsetWidth, xbox_canvas.offsetHeight );
|
||||
xbox_renderer.setPixelRatio( window.devicePixelRatio );
|
||||
xbox_canvas.appendChild( xbox_renderer.domElement );
|
||||
|
||||
var xbox = null;
|
||||
var xbox_scale = 1;
|
||||
var xbox_sphere_radius = 0.23 * xbox_scale;
|
||||
var xbox_base_color = new THREE.Color(0xA0B020);
|
||||
var xbox_bright_color = new THREE.Color(0xD4E830);
|
||||
var xbox_material = new THREE.MeshStandardMaterial( {
|
||||
color: xbox_base_color,
|
||||
transparent: true,
|
||||
opacity: 0.25,
|
||||
depthTest: false,
|
||||
roughness: 0,
|
||||
});
|
||||
|
||||
gltf_loader.load('xbox.glb', function ( gltf ) {
|
||||
xbox = gltf.scene;
|
||||
xbox.renderOrder = 10;
|
||||
xbox.scale.set(xbox_scale, xbox_scale, xbox_scale);
|
||||
xbox.position.set(0,0.05,0);
|
||||
xbox.rotation.x = 0.14 * Math.PI;
|
||||
xbox.rotation.z = -0.02 * Math.PI;
|
||||
var order = 0;
|
||||
xbox.traverse((o) => {
|
||||
if (o.isMesh) {
|
||||
o.material = xbox_material;
|
||||
o.renderOrder = order;
|
||||
order += 1;
|
||||
}
|
||||
});
|
||||
xbox_scene.add(xbox);
|
||||
});
|
||||
|
||||
const xbox_orb_geometry = new THREE.SphereGeometry( xbox_sphere_radius, 50, 25 );
|
||||
var xbox_orb_material = new THREE.ShaderMaterial( {
|
||||
// https://jsfiddle.net/8n36c47p/4/
|
||||
vertexShader: `
|
||||
varying vec3 vPositionW;
|
||||
varying vec3 vNormalW;
|
||||
void main() {
|
||||
vPositionW = vec3( vec4( position, 1.0 ) * modelMatrix);
|
||||
vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
fragmentShader: `
|
||||
varying vec3 vPositionW;
|
||||
varying vec3 vNormalW;
|
||||
void main() {
|
||||
vec3 color = vec3(0.5, 1.0, 0.3);
|
||||
vec3 base = vec3(0.1, 0.6, 0.05);
|
||||
vec3 viewDirectionW = normalize(cameraPosition - vPositionW);
|
||||
float f = clamp(pow(1.0 - dot(viewDirectionW, vNormalW), 1.3), 0.0, 1.);
|
||||
gl_FragColor = vec4(mix(base, color, f), f * 0.5);
|
||||
}`,
|
||||
transparent: true,
|
||||
});
|
||||
|
||||
var xbox_orb = new THREE.Mesh( xbox_orb_geometry, xbox_orb_material );
|
||||
xbox_orb.renderOrder = 0;
|
||||
xbox_scene.add( xbox_orb );
|
||||
|
||||
var light_color = new THREE.Color(1,1,1);
|
||||
|
||||
const ambient_light = new THREE.AmbientLight(light_color, 2);
|
||||
xbox_scene.add(ambient_light);
|
||||
|
||||
var point_light = new THREE.PointLight(light_color, 5);
|
||||
point_light.position.set(-1, -0.5, 0.25);
|
||||
xbox_scene.add(point_light);
|
||||
|
||||
xbox_camera.position.z = 0.65;
|
||||
|
||||
var clock = new THREE.Clock();
|
||||
var xbox_render = function () {
|
||||
requestAnimationFrame( xbox_render );
|
||||
var t = clock.getElapsedTime();
|
||||
var s = Math.sin(t * Math.PI / 50);
|
||||
if (xbox != null) {
|
||||
xbox.rotation.y = Math.PI * (0.1 + s / 10);
|
||||
var new_color = new THREE.Color(xbox_base_color);
|
||||
new_color.lerp(xbox_bright_color, Math.abs(Math.sin(t * Math.PI / 4 )));
|
||||
xbox_material.color.set(new_color);
|
||||
}
|
||||
xbox_renderer.render(xbox_scene, xbox_camera);
|
||||
};
|
||||
xbox_render();
|
||||
|
||||
const controls = new ArcballControls( xbox_camera, xbox_renderer.domElement, xbox_scene );
|
||||
|
||||
controls.addEventListener( 'change', function () {
|
||||
xbox_renderer.render( xbox_scene, xbox_camera );
|
||||
} );
|
||||
|
||||
controls.update();
|
||||
|
||||
window.addEventListener('resize', function(){
|
||||
xbox_camera.aspect = xbox_canvas.offsetWidth/xbox_canvas.offsetHeight;
|
||||
xbox_camera.updateProjectionMatrix();
|
||||
xbox_renderer.setSize(xbox_canvas.offsetWidth, xbox_canvas.offsetHeight);
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="gl_logo.js"></script>
|
||||
<script type="text/javascript">
|
||||
function updateNavbarTransparency() {
|
||||
var nav = $(".navbar");
|
||||
var t = $(document).scrollTop() < $("#xbox-logo").offset().top - nav.height();
|
||||
var t = $(document).scrollTop() < $("#xbox-scene").offset().top - nav.height();
|
||||
nav.toggleClass('navbar-transparent', t);
|
||||
}
|
||||
$(document).scroll(updateNavbarTransparency);
|
||||
|
Loading…
Reference in New Issue
Block a user