434 lines
11 KiB
JavaScript
434 lines
11 KiB
JavaScript
import * as THREE from 'three';
|
|
import { VRButton } from 'three/examples/jsm/webxr/VRButton.js';
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
import { BoxLineGeometry } from 'three/examples/jsm/geometries/BoxLineGeometry.js';
|
|
|
|
import Stats from 'three/examples/jsm/libs/stats.module.js';
|
|
|
|
import ThreeMeshUI, { FontLibrary } from 'three-mesh-ui';
|
|
import MSDFNormalMaterial from 'three-mesh-ui/examples/materials/msdf/MSDFNormalMaterial';
|
|
import ROBOTO_ADJUSTMENT from 'three-mesh-ui/examples/assets/fonts/msdf/roboto/adjustment';
|
|
|
|
const WIDTH = window.innerWidth;
|
|
const HEIGHT = window.innerHeight;
|
|
|
|
let container, justifyInRow, justifyInColumn;
|
|
const DIM_HIGH = 1.6;
|
|
const MIN_HIGH = 1.1;
|
|
|
|
const DIM_LOW = 0.25;
|
|
|
|
const justificationLegend = [
|
|
{ id: 'start', color: 0xff9900 },
|
|
{ id: 'end', color: 0xff0099 },
|
|
{ id: 'center', color: 0x00ff99 },
|
|
{ id: "space-between", color: 0x99ff00 },
|
|
{ id: "space-around", color: 0x9900ff },
|
|
{ id: "space-evenly", color: 0x0099ff }
|
|
];
|
|
|
|
let scene, camera, renderer, controls, stats;
|
|
|
|
// Using `ThreeMeshUI.FontLibrary.prepare( fontFamily, [...fontFamily] )
|
|
// We can ensure any fontFamily passed in that function and theirs variants are properly loaded and setup
|
|
FontLibrary.prepare(
|
|
|
|
FontLibrary
|
|
// Registering a fontFamily called "Roboto", the name is up to us.
|
|
.addFontFamily("Roboto")
|
|
// On the fontFamily added, lets add a variant
|
|
// a font variant usually requires 4 parameters
|
|
.addVariant(
|
|
// The weight of the variant '100'|'200'|'300'|'400'|'600'|'700'|'800'|'900'
|
|
// LIGHTER NORMAL BOLD BOLDER
|
|
"400",
|
|
|
|
// The style of the variant 'normal'|'italic'|'oblique'|'oblique(x deg)'
|
|
"normal",
|
|
|
|
// The json definition of the msdf font 'urlToLoad'|loadedObject
|
|
"./assets/fonts/msdf/roboto/regular.json",
|
|
|
|
// The texture of the msdf font 'urlToLoad'|Texture
|
|
"./assets/fonts/msdf/roboto/regular.png"
|
|
)
|
|
|
|
// Registering additional variants
|
|
.addVariant("700", "italic", "./assets/fonts/msdf/roboto/bold-italic.json", "./assets/fonts/msdf/roboto/bold-italic.png" )
|
|
.addVariant("700", "normal", "./assets/fonts/msdf/roboto/bold.json", "./assets/fonts/msdf/roboto/bold.png" )
|
|
.addVariant("400", "italic", "./assets/fonts/msdf/roboto/italic.json", "./assets/fonts/msdf/roboto/italic.png" )
|
|
|
|
// FontLibrary.prepare() returns a Promise, we can therefore add a callback to be executed when all files are loaded
|
|
).then( () => {
|
|
|
|
// Once font are registered, we can get the font family
|
|
const RobotoFamily = FontLibrary.getFontFamily("Roboto");
|
|
|
|
// And then retrieve a fontVariant defined in this Family
|
|
const RobotoRegular = RobotoFamily.getVariant('400','normal');
|
|
|
|
// Having font variant allows us to perform some modifications
|
|
// 1. Adjustments
|
|
// If you look closely the `Getting started - Basic Setup` you may have noticed that :
|
|
// - the `h` character is slightly below the baseline
|
|
// This can be adjusted per fontVariant
|
|
RobotoRegular.adjustTypographicGlyphs( {
|
|
// 'h' character must change some of its properties defined in the json
|
|
h: {
|
|
// the yoffset property should be 2 (instead of 4 in the json)
|
|
yoffset: 2
|
|
}
|
|
} );
|
|
// Once adjusted, any three-mesh-ui Text using this font variant will use the adjusted properties
|
|
|
|
// 1. Material
|
|
// Instead of assigning custom materials to Text one by one
|
|
// We can assign a Material(class) to a font variant (Here the bold one)
|
|
RobotoFamily.getVariant('700','normal').fontMaterial = MSDFNormalMaterial;
|
|
// Once set, any three-mesh-ui Text using this font variant will use the defined material
|
|
|
|
// We may encounter the following lines in other examples,
|
|
// they are adjusting font variants to display a nice baseline
|
|
RobotoFamily.getVariant('700','normal').adjustTypographicGlyphs( ROBOTO_ADJUSTMENT );
|
|
RobotoFamily.getVariant('700','italic').adjustTypographicGlyphs( ROBOTO_ADJUSTMENT );
|
|
RobotoFamily.getVariant('400','italic').adjustTypographicGlyphs( ROBOTO_ADJUSTMENT );
|
|
|
|
// Now that the font are loaded and adjusted,
|
|
|
|
init();
|
|
|
|
|
|
});
|
|
|
|
window.addEventListener( 'resize', onWindowResize );
|
|
|
|
//
|
|
|
|
function init() {
|
|
|
|
scene = new THREE.Scene();
|
|
scene.background = new THREE.Color( 0x505050 );
|
|
|
|
camera = new THREE.PerspectiveCamera( 60, WIDTH / HEIGHT, 0.1, 100 );
|
|
|
|
renderer = new THREE.WebGLRenderer( {
|
|
antialias: true
|
|
} );
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setSize( WIDTH, HEIGHT );
|
|
renderer.xr.enabled = true;
|
|
document.body.appendChild( VRButton.createButton( renderer ) );
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
stats = new Stats();
|
|
document.body.appendChild( stats.dom );
|
|
|
|
controls = new OrbitControls( camera, renderer.domElement );
|
|
camera.position.set( 0, 1.6, 0 );
|
|
controls.target = new THREE.Vector3( 0, 1, -1.8 );
|
|
controls.update();
|
|
|
|
// ROOM
|
|
|
|
const room = new THREE.LineSegments(
|
|
new BoxLineGeometry( 6, 6, 6, 10, 10, 10 ).translate( 0, 3, 0 ),
|
|
new THREE.LineBasicMaterial( { color: 0x808080 } )
|
|
);
|
|
|
|
scene.add( room );
|
|
|
|
// TEXT PANEL
|
|
|
|
makeTitlePanel();
|
|
justifyInRow = makeTextPanel( 'column' );
|
|
|
|
window.rootBlock = justifyInRow;
|
|
|
|
justifyInColumn = makeTextPanel( 'row' );
|
|
|
|
justifyInRow.position.x = -0.75;
|
|
justifyInRow.scale.setScalar( 0.75 );
|
|
|
|
justifyInColumn.position.x = 0.75;
|
|
justifyInColumn.scale.setScalar( 0.75 );
|
|
|
|
//
|
|
|
|
renderer.setAnimationLoop( loop );
|
|
|
|
}
|
|
|
|
function makeTextPanel( flexDirection ) {
|
|
|
|
container = new ThreeMeshUI.Block( {
|
|
height: DIM_HIGH + 0.2,
|
|
width: DIM_HIGH + 0.2,
|
|
flexDirection: flexDirection,
|
|
justifyContent: 'center',
|
|
backgroundOpacity: 1,
|
|
backgroundColor: new THREE.Color( 'grey' ),
|
|
overflow: 'hidden',
|
|
fontFamily: "Roboto"
|
|
} );
|
|
|
|
container.position.set( 0, 1, -1.8 );
|
|
container.rotation.x = - 0.55;
|
|
scene.add( container );
|
|
|
|
for ( let i = 0; i < justificationLegend.length; i ++ ) {
|
|
|
|
const color = new THREE.Color( justificationLegend[ i ].color );
|
|
const id = justificationLegend[ i ].id;
|
|
const panel = buildJustifiedPanel( id, color, flexDirection === 'column' ? 'row' : 'column' );
|
|
|
|
container.add( panel );
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
function buildJustifiedPanel( id, color, flexDirection ) {
|
|
|
|
const panel = new ThreeMeshUI.Block( {
|
|
width: flexDirection === 'row' ? DIM_HIGH : DIM_LOW,
|
|
height: flexDirection === 'row' ? DIM_LOW : DIM_HIGH,
|
|
flexDirection: flexDirection,
|
|
justifyContent: id,
|
|
backgroundOpacity: 0.3,
|
|
backgroundColor: 0xff9900,
|
|
padding: 0.01,
|
|
margin: 0.01,
|
|
offset:0.0001
|
|
} );
|
|
container.add( panel );
|
|
|
|
const letters = 'ABCDEF';
|
|
|
|
|
|
const step = 0xFFFFFF / 5;
|
|
for ( let i = 0; i < 5; i ++ ) {
|
|
|
|
const blockText = new ThreeMeshUI.Block( {
|
|
margin: 0.01,
|
|
borderRadius: 0.05,
|
|
backgroundColor: color,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: '0 0 0.01 0',
|
|
borderColor: Math.floor( step * (5-i) ),
|
|
offset:0.001,
|
|
visible: i !== 2,
|
|
} );
|
|
|
|
if( i === 0 ) {
|
|
blockText.set({ width:0.125, height:0.125 });
|
|
}
|
|
|
|
panel.add( blockText );
|
|
|
|
const text = new ThreeMeshUI.Text( {
|
|
textAlign: 'center',
|
|
alignItems : 'center',
|
|
lineHeight: 1,
|
|
width: 0.125,
|
|
height: 0.125,
|
|
textContent: letters[ i ],
|
|
} );
|
|
blockText.add( text );
|
|
|
|
}
|
|
|
|
return panel;
|
|
}
|
|
|
|
function makeTitlePanel(){
|
|
|
|
const panel = new ThreeMeshUI.Text( {
|
|
width: DIM_HIGH * 1.85,
|
|
height: 0.15,
|
|
padding: 0.05,
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
textAlign: 'center',
|
|
backgroundOpacity: 0.6,
|
|
fontSize: 0.1,
|
|
fontFamily: "Roboto"
|
|
} );
|
|
|
|
for ( let i = 0; i < justificationLegend.length; i ++ ) {
|
|
|
|
const color = new THREE.Color( justificationLegend[ i ].color );
|
|
const id = justificationLegend[ i ].id;
|
|
|
|
panel.add(
|
|
new ThreeMeshUI.Inline( {
|
|
textContent: id + " ",
|
|
color: color
|
|
} )
|
|
);
|
|
|
|
}
|
|
|
|
panel.scale.setScalar( 0.86 );
|
|
panel.position.set( 0, 1.8, -2.1 );
|
|
|
|
scene.add( panel );
|
|
}
|
|
|
|
// handles resizing the renderer when the viewport is resized
|
|
function onWindowResize() {
|
|
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
}
|
|
|
|
//
|
|
|
|
let isInverted = false;
|
|
|
|
setInterval( () => {
|
|
|
|
isInverted = ! isInverted;
|
|
|
|
for ( let i = 1; i < justifyInRow.children.length; i ++ ) {
|
|
justifyInRow.children[ i ].set( { flexDirection:isInverted ? 'row-reverse' : 'row' } );
|
|
}
|
|
|
|
for ( let i = 1; i < justifyInColumn.children.length; i ++ ) {
|
|
justifyInColumn.children[ i ].set( { flexDirection:isInverted ? 'column-reverse' : 'column' } );
|
|
}
|
|
|
|
}, 2500 );
|
|
|
|
let alignMode = 1;
|
|
const aligns = [ 'start', 'center', 'end', 'stretch'];
|
|
|
|
setInterval( () => {
|
|
|
|
alignMode += 1;
|
|
alignMode = alignMode >= aligns.length ? 0 : alignMode;
|
|
|
|
const mode = aligns[alignMode];
|
|
|
|
for ( let i = 1; i < justifyInRow.children.length; i ++ ) {
|
|
|
|
justifyInRow.children[ i ].set( { alignItems: mode } );
|
|
|
|
}
|
|
|
|
for ( let i = 1; i < justifyInColumn.children.length; i ++ ) {
|
|
|
|
justifyInColumn.children[ i ].set( { alignItems: mode } );
|
|
|
|
}
|
|
|
|
}, 1000 );
|
|
|
|
// let sizeMode = 1;
|
|
// const sizes = [ 0.125, 0.175, 0.225, 0.295 ];
|
|
//
|
|
// setInterval( () => {
|
|
//
|
|
// sizeMode += 1;
|
|
// sizeMode = sizeMode >= sizes.length ? 0 : sizeMode;
|
|
//
|
|
// const mode = sizes[ sizeMode ];
|
|
//
|
|
// for ( let i = 1; i < justifyInRow.children.length; i ++ ) {
|
|
//
|
|
// for ( let j = 1; j < justifyInRow.children[ i ].children.length; j ++ ) {
|
|
//
|
|
// justifyInRow.children[ i ].children[ j ].set( { width: mode } );
|
|
//
|
|
// }
|
|
//
|
|
// }
|
|
//
|
|
// for ( let i = 1; i < justifyInColumn.children.length; i ++ ) {
|
|
//
|
|
// for ( let j = 1; j < justifyInColumn.children[ i ].children.length; j ++ ) {
|
|
//
|
|
// justifyInColumn.children[ i ].children[ j ].set( { height:mode } );
|
|
//
|
|
// }
|
|
// }
|
|
//
|
|
// }, 3000 );
|
|
|
|
// let childAlignMode = 1;
|
|
// const childAligns = [ 'center', 'stretch' ];
|
|
//
|
|
// setInterval( () => {
|
|
//
|
|
// childAlignMode += 1;
|
|
// childAlignMode = childAlignMode >= childAligns.length ? 0 : childAlignMode;
|
|
//
|
|
// const mode = childAligns[ childAlignMode ];
|
|
//
|
|
// for ( let i = 1; i < justifyInRow.children.length; i ++ ) {
|
|
//
|
|
// for ( let j = 1; j < justifyInRow.children[ i ].children.length; j ++ ) {
|
|
//
|
|
// justifyInRow.children[ i ].children[ j ].set( { alignItems: mode } );
|
|
//
|
|
// }
|
|
//
|
|
// }
|
|
//
|
|
// for ( let i = 1; i < justifyInColumn.children.length; i ++ ) {
|
|
//
|
|
// for ( let j = 1; j < justifyInColumn.children[ i ].children.length; j ++ ) {
|
|
//
|
|
// justifyInColumn.children[ i ].children[ j ].set( { alignItems:mode } );
|
|
//
|
|
// }
|
|
// }
|
|
//
|
|
// }, 500 );
|
|
|
|
|
|
let currentBigSize = DIM_HIGH;
|
|
let currentSpeed = - 0.005;
|
|
|
|
function loop() {
|
|
|
|
currentBigSize += currentSpeed;
|
|
|
|
if ( currentBigSize >= DIM_HIGH ) {
|
|
|
|
currentBigSize = DIM_HIGH;
|
|
currentSpeed *= - 1;
|
|
|
|
} else if ( currentBigSize <= MIN_HIGH ) {
|
|
|
|
currentBigSize = MIN_HIGH;
|
|
currentSpeed *= -1;
|
|
|
|
}
|
|
|
|
for ( let i = 1; i < justifyInRow.children.length; i ++ ) {
|
|
|
|
justifyInRow.children[ i ].set( { width: currentBigSize } );
|
|
|
|
}
|
|
|
|
for ( let i = 1; i < justifyInColumn.children.length; i ++ ) {
|
|
|
|
justifyInColumn.children[ i ].set( { height: currentBigSize } );
|
|
|
|
}
|
|
|
|
// Don't forget, ThreeMeshUI must be updated manually.
|
|
// This has been introduced in version 3.0.0 in order
|
|
// to improve performance
|
|
ThreeMeshUI.update();
|
|
|
|
controls.update();
|
|
renderer.render( scene, camera );
|
|
|
|
stats.update();
|
|
|
|
}
|