mirror of
https://github.com/wahyd4/three.js.git
synced 2026-08-09 04:56:01 +10:00
I knew for loops iterating over properties were slower than numerical for loops but I didn't realize how bad it was. Gained about 10 fps in performance test, and that was for for loop iterating over single property. Also fixed broken examples with postprocessing.
638 lines
15 KiB
HTML
638 lines
15 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en">
|
|
<head>
|
|
<title>three.js webgl - geometry - text</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
|
|
<style type="text/css">
|
|
body {
|
|
font-family: Monospace;
|
|
background-color: #000;
|
|
color: #fff;
|
|
margin: 0px;
|
|
overflow: hidden;
|
|
}
|
|
#info {
|
|
position: absolute;
|
|
top: 10px;
|
|
width: 100%;
|
|
text-align: center;
|
|
z-index: 100;
|
|
display:block;
|
|
}
|
|
#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="info">
|
|
<a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - procedural 3D text by <a href="http://www.lab4games.net/zz85/blog" target="_blank">zz85</a> & alteredq
|
|
(fonts from <a href="http://typeface.neocracy.org/">typeface.js</a> and <a href="http://en.wikipedia.org/wiki/Droid_%28font%29">Droid</a>)
|
|
<br/>type to enter new text, drag to spin the text
|
|
<br/><span class="button" id="color">change color</span>,
|
|
<span class="button" id="font">change font</span>,
|
|
<span class="button" id="weight">change weight</span>,
|
|
<span class="button" id="bevel">change bevel</span>,
|
|
<span class="button" id="bend">bend</span>,
|
|
<span class="button" id="postprocessing">change postprocessing</span>,
|
|
<a id="permalink" href="#">permalink</a>
|
|
</div>
|
|
|
|
|
|
<script type="text/javascript" src="../build/Three.js"></script>
|
|
|
|
<script type="text/javascript" src="js/ShaderExtras.js"></script>
|
|
|
|
<script type="text/javascript" src="js/postprocessing/EffectComposer.js"></script>
|
|
<script type="text/javascript" src="js/postprocessing/RenderPass.js"></script>
|
|
<script type="text/javascript" src="js/postprocessing/ShaderPass.js"></script>
|
|
<script type="text/javascript" src="js/postprocessing/MaskPass.js"></script>
|
|
<script type="text/javascript" src="js/postprocessing/BloomPass.js"></script>
|
|
<script type="text/javascript" src="js/postprocessing/FilmPass.js"></script>
|
|
|
|
<script type="text/javascript" src="js/Detector.js"></script>
|
|
<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
|
|
<script type="text/javascript" src="js/Stats.js"></script>
|
|
|
|
<!-- load the font files -->
|
|
|
|
<script type="text/javascript" src="fonts/gentilis_bold.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/gentilis_regular.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/optimer_bold.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/optimer_regular.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/helvetiker_bold.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/helvetiker_regular.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/droid/droid_sans_regular.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/droid/droid_sans_bold.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/droid/droid_serif_regular.typeface.js"></script>
|
|
<script type="text/javascript" src="fonts/droid/droid_serif_bold.typeface.js"></script>
|
|
|
|
<!-- todo async loader for fonts -->
|
|
|
|
<script type="text/javascript">
|
|
|
|
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
|
|
|
|
var container, stats, permalink, hex, color;
|
|
|
|
var camera, scene, renderer;
|
|
|
|
var composer;
|
|
|
|
var textMesh1, textMesh2, textGeo, faceMaterial, textMaterialFront, textMaterialSide, parent;
|
|
|
|
var firstLetter = true;
|
|
|
|
var text = "three.js",
|
|
|
|
height = 20,
|
|
size = 70,
|
|
hover = 30,
|
|
|
|
curveSegments = 4,
|
|
|
|
bevelThickness = 2,
|
|
bevelSize = 1.5,
|
|
bevelSegments = 3,
|
|
bevelEnabled = true,
|
|
bend = true,
|
|
|
|
font = "optimer", // helvetiker, optimer, gentilis, droid sans, droid serif
|
|
weight = "bold", // normal bold
|
|
style = "normal"; // normal italic
|
|
|
|
var mirror = true;
|
|
|
|
var fontMap = {
|
|
"helvetiker" : 0,
|
|
"optimer" : 1,
|
|
"gentilis" : 2,
|
|
"droid sans" : 3,
|
|
"droid serif" : 4
|
|
|
|
};
|
|
|
|
var weightMap = {
|
|
"normal" : 0,
|
|
"bold" : 1
|
|
}
|
|
|
|
var reverseFontMap = {};
|
|
var reverseWeightMap = {};
|
|
|
|
for ( var i in fontMap ) reverseFontMap[ fontMap[i] ] = i;
|
|
for ( var i in weightMap ) reverseWeightMap[ weightMap[i] ] = i;
|
|
|
|
var targetRotation = 0;
|
|
var targetRotationOnMouseDown = 0;
|
|
|
|
var mouseX = 0;
|
|
var mouseXOnMouseDown = 0;
|
|
|
|
var windowHalfX = window.innerWidth / 2;
|
|
var windowHalfY = window.innerHeight / 2;
|
|
|
|
var postprocessing = { enabled : false };
|
|
var glow = 0.9;
|
|
|
|
init();
|
|
animate();
|
|
|
|
function capitalize( txt ) {
|
|
|
|
return txt.substring( 0, 1 ).toUpperCase() + txt.substring( 1 );
|
|
|
|
}
|
|
|
|
function decimalToHex( d ) {
|
|
|
|
var hex = Number( d ).toString( 16 );
|
|
hex = "000000".substr( 0, 6 - hex.length ) + hex;
|
|
return hex.toUpperCase();
|
|
|
|
}
|
|
|
|
function init() {
|
|
|
|
container = document.createElement( 'div' );
|
|
document.body.appendChild( container );
|
|
|
|
permalink = document.getElementById( "permalink" );
|
|
|
|
// CAMERA
|
|
|
|
camera = new THREE.Camera( 30, window.innerWidth / window.innerHeight, 1, 1500 );
|
|
camera.position.y = 400;
|
|
camera.position.z = 700;
|
|
camera.target.position.y = 100;
|
|
|
|
// SCENE
|
|
|
|
scene = new THREE.Scene();
|
|
|
|
scene.fog = new THREE.Fog( 0x000000, 250, 1400 );
|
|
|
|
var dirLight = new THREE.DirectionalLight( 0xffffff, 0.125 );
|
|
dirLight.position.set( 0, 0, 1 );
|
|
dirLight.position.normalize();
|
|
scene.addLight( dirLight );
|
|
|
|
var pointLight = new THREE.PointLight( 0xffffff, 1.5 );
|
|
pointLight.position.set( 0, 100, 90 );
|
|
scene.addLight( pointLight );
|
|
|
|
//text = capitalize( font ) + " " + capitalize( weight );
|
|
//text = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
//text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
// Get text from hash
|
|
|
|
var hash = document.location.hash.substr( 1 );
|
|
|
|
if ( hash.length !== 0 ) {
|
|
|
|
var colorhash = hash.substring( 0, 6 );
|
|
var fonthash = hash.substring( 6, 7 );
|
|
var weighthash = hash.substring( 7, 8 );
|
|
var pphash = hash.substring( 8, 9 );
|
|
var bevelhash = hash.substring( 9, 10 );
|
|
var bendhash = hash.substring( 10, 11 );
|
|
var texthash = hash.substring( 12 );
|
|
|
|
hex = colorhash;
|
|
pointLight.color.setHex( parseInt( colorhash, 16 ) );
|
|
|
|
font = reverseFontMap[ parseInt( fonthash ) ];
|
|
weight = reverseWeightMap[ parseInt( weighthash ) ];
|
|
|
|
postprocessing.enabled = parseInt( pphash );
|
|
bevelEnabled = parseInt( bevelhash );
|
|
bend = parseInt( bendhash );
|
|
|
|
text = decodeURI( texthash );
|
|
|
|
updatePermalink();
|
|
|
|
} else {
|
|
|
|
pointLight.color.setHSV( Math.random(), 0.95, 0.85 );
|
|
hex = decimalToHex( pointLight.color.getHex() );
|
|
|
|
}
|
|
|
|
faceMaterial = new THREE.MeshFaceMaterial();
|
|
|
|
textMaterialFront = new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } );
|
|
textMaterialSide = new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } );
|
|
|
|
parent = new THREE.Object3D();
|
|
parent.position.y = 100;
|
|
|
|
scene.addChild( parent );
|
|
|
|
createText();
|
|
|
|
var plane = new THREE.Mesh( new THREE.PlaneGeometry( 10000, 10000 ), new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true } ) );
|
|
plane.rotation.x = -1.57;
|
|
plane.position.y = 100;
|
|
scene.addChild( plane );
|
|
|
|
// RENDERER
|
|
|
|
renderer = new THREE.WebGLRenderer( { antialias: true } );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
renderer.setClearColor( scene.fog.color, 1 );
|
|
|
|
container.appendChild( renderer.domElement );
|
|
|
|
// STATS
|
|
|
|
stats = new Stats();
|
|
stats.domElement.style.position = 'absolute';
|
|
stats.domElement.style.top = '0px';
|
|
//container.appendChild( stats.domElement );
|
|
|
|
// EVENTS
|
|
|
|
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
|
|
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
|
|
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
|
|
document.addEventListener( 'keypress', onDocumentKeyPress, false );
|
|
document.addEventListener( 'keydown', onDocumentKeyDown, false );
|
|
|
|
document.getElementById( "color" ).addEventListener( 'click', function() {
|
|
|
|
pointLight.color.setHSV( Math.random(), 0.95, 0.85 );
|
|
hex = decimalToHex( pointLight.color.getHex() );
|
|
|
|
updatePermalink();
|
|
|
|
}, false );
|
|
|
|
document.getElementById( "font" ).addEventListener( 'click', function() {
|
|
|
|
if ( font == "helvetiker" ) {
|
|
|
|
font = "optimer";
|
|
|
|
} else if ( font == "optimer" ) {
|
|
|
|
font = "gentilis";
|
|
|
|
} else if ( font == "gentilis" ) {
|
|
|
|
font = "droid sans";
|
|
|
|
} else if ( font == "droid sans" ) {
|
|
|
|
font = "droid serif";
|
|
|
|
} else {
|
|
|
|
font = "helvetiker";
|
|
|
|
}
|
|
|
|
refreshText();
|
|
|
|
}, false );
|
|
|
|
document.getElementById( "weight" ).addEventListener( 'click', function() {
|
|
|
|
if ( weight == "bold" ) {
|
|
|
|
weight = "normal";
|
|
|
|
} else {
|
|
|
|
weight = "bold";
|
|
|
|
}
|
|
|
|
refreshText();
|
|
|
|
}, false );
|
|
|
|
document.getElementById( "bevel" ).addEventListener( 'click', function() {
|
|
|
|
bevelEnabled = !bevelEnabled;
|
|
|
|
refreshText();
|
|
|
|
}, false );
|
|
|
|
document.getElementById( "bend" ).addEventListener( 'click', function() {
|
|
|
|
bend = !bend;
|
|
refreshText();
|
|
|
|
}, false );
|
|
|
|
document.getElementById( "postprocessing" ).addEventListener( 'click', function() {
|
|
|
|
postprocessing.enabled = !postprocessing.enabled;
|
|
updatePermalink();
|
|
|
|
}, false );
|
|
|
|
|
|
// POSTPROCESSING
|
|
|
|
renderer.autoClear = false;
|
|
|
|
var renderModel = new THREE.RenderPass( scene, camera );
|
|
var effectBloom = new THREE.BloomPass( 0.25 );
|
|
var effectFilm = new THREE.FilmPass( 0.5, 0.125, 2048, false );
|
|
|
|
effectFilm.renderToScreen = true;
|
|
|
|
composer = new THREE.EffectComposer( renderer );
|
|
|
|
composer.addPass( renderModel );
|
|
composer.addPass( effectBloom );
|
|
composer.addPass( effectFilm );
|
|
|
|
}
|
|
|
|
//
|
|
|
|
function boolToNum( b ) {
|
|
|
|
return b ? 1 : 0;
|
|
|
|
}
|
|
|
|
function updatePermalink() {
|
|
|
|
var link = hex + fontMap[ font ] + weightMap[ weight ] + boolToNum( postprocessing.enabled ) + boolToNum( bevelEnabled )
|
|
+ boolToNum( bend ) + "#" + encodeURI( text );
|
|
|
|
permalink.href = "#" + link;
|
|
window.location.hash = link;
|
|
|
|
}
|
|
|
|
function onDocumentKeyDown( event ) {
|
|
|
|
if ( firstLetter ) {
|
|
|
|
firstLetter = false;
|
|
text = "";
|
|
|
|
}
|
|
|
|
var keyCode = event.keyCode;
|
|
|
|
// backspace
|
|
|
|
if ( keyCode == 8 ) {
|
|
|
|
event.preventDefault();
|
|
|
|
text = text.substring( 0, text.length - 1 );
|
|
refreshText();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onDocumentKeyPress( event ) {
|
|
|
|
var keyCode = event.which;
|
|
|
|
// backspace
|
|
|
|
if ( keyCode == 8 ) {
|
|
|
|
event.preventDefault();
|
|
|
|
} else {
|
|
|
|
var ch = String.fromCharCode( keyCode );
|
|
text += ch;
|
|
|
|
refreshText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function createText() {
|
|
|
|
textGeo = new THREE.TextGeometry( text, {
|
|
|
|
size: size,
|
|
height: height,
|
|
curveSegments: curveSegments,
|
|
|
|
font: font,
|
|
weight: weight,
|
|
style: style,
|
|
|
|
bevelThickness: bevelThickness,
|
|
bevelSize: bevelSize,
|
|
bevelEnabled: bevelEnabled,
|
|
|
|
bend: bend,
|
|
|
|
material: textMaterialFront,
|
|
extrudeMaterial: textMaterialSide
|
|
|
|
});
|
|
|
|
textGeo.computeBoundingBox();
|
|
textGeo.computeVertexNormals();
|
|
|
|
// "fix" side normals by removing z-component of normals for side faces
|
|
// (this doesn't work well for beveled geometry as then we lose nice curvature around z-axis)
|
|
|
|
if ( ! bevelEnabled ) {
|
|
|
|
var triangleAreaHeuristics = 0.1 * ( height * size );
|
|
|
|
for ( var i = 0; i < textGeo.faces.length; i ++ ) {
|
|
|
|
var face = textGeo.faces[ i ];
|
|
|
|
if ( face.materials[ 0 ].id == textMaterialSide.id ) {
|
|
|
|
for ( var j = 0; j < face.vertexNormals.length; j ++ ) {
|
|
|
|
face.vertexNormals[ j ].z = 0;
|
|
face.vertexNormals[ j ].normalize();
|
|
|
|
}
|
|
|
|
var va = textGeo.vertices[ face.a ].position;
|
|
var vb = textGeo.vertices[ face.b ].position;
|
|
var vc = textGeo.vertices[ face.c ].position;
|
|
|
|
var s = THREE.GeometryUtils.triangleArea( va, vb, vc );
|
|
|
|
if ( s > triangleAreaHeuristics ) {
|
|
|
|
for ( var j = 0; j < face.vertexNormals.length; j ++ ) {
|
|
|
|
face.vertexNormals[ j ].copy( face.normal );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var centerOffset = -0.5 * ( textGeo.boundingBox.x[ 1 ] - textGeo.boundingBox.x[ 0 ] );
|
|
|
|
textMesh1 = new THREE.Mesh( textGeo, faceMaterial );
|
|
|
|
textMesh1.position.x = centerOffset;
|
|
textMesh1.position.y = hover;
|
|
textMesh1.position.z = 0;
|
|
|
|
textMesh1.rotation.x = 0;
|
|
textMesh1.rotation.y = Math.PI * 2;
|
|
|
|
parent.addChild( textMesh1 );
|
|
|
|
if ( mirror ) {
|
|
|
|
textMesh2 = new THREE.Mesh( textGeo, faceMaterial );
|
|
|
|
textMesh2.position.x = centerOffset;
|
|
textMesh2.position.y = -hover;
|
|
textMesh2.position.z = height;
|
|
|
|
textMesh2.rotation.x = Math.PI;
|
|
textMesh2.rotation.y = Math.PI * 2;
|
|
|
|
parent.addChild( textMesh2 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function refreshText() {
|
|
|
|
updatePermalink();
|
|
|
|
scene.removeChild( textMesh1 );
|
|
if ( mirror ) scene.removeChild( textMesh2 );
|
|
|
|
if ( !text ) return;
|
|
|
|
createText();
|
|
|
|
}
|
|
|
|
function onDocumentMouseDown( event ) {
|
|
|
|
event.preventDefault();
|
|
|
|
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
|
|
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
|
|
document.addEventListener( 'mouseout', onDocumentMouseOut, false );
|
|
|
|
mouseXOnMouseDown = event.clientX - windowHalfX;
|
|
targetRotationOnMouseDown = targetRotation;
|
|
|
|
}
|
|
|
|
function onDocumentMouseMove( event ) {
|
|
|
|
mouseX = event.clientX - windowHalfX;
|
|
|
|
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;
|
|
|
|
}
|
|
|
|
function onDocumentMouseUp( event ) {
|
|
|
|
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
|
|
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
|
|
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
|
|
|
|
}
|
|
|
|
function onDocumentMouseOut( event ) {
|
|
|
|
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
|
|
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
|
|
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
|
|
|
|
}
|
|
|
|
function onDocumentTouchStart( event ) {
|
|
|
|
if ( event.touches.length == 1 ) {
|
|
|
|
event.preventDefault();
|
|
|
|
mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
|
|
targetRotationOnMouseDown = targetRotation;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onDocumentTouchMove( event ) {
|
|
|
|
if ( event.touches.length == 1 ) {
|
|
|
|
event.preventDefault();
|
|
|
|
mouseX = event.touches[ 0 ].pageX - windowHalfX;
|
|
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
function animate() {
|
|
|
|
requestAnimationFrame( animate );
|
|
|
|
render();
|
|
stats.update();
|
|
|
|
}
|
|
|
|
var delta, time, oldTime;
|
|
|
|
function render() {
|
|
|
|
if ( ! oldTime ) oldTime = new Date().getTime();
|
|
|
|
time = new Date().getTime();
|
|
delta = 0.1 * ( time - oldTime );
|
|
oldTime = time;
|
|
|
|
parent.rotation.y += ( targetRotation - parent.rotation.y ) * 0.05;
|
|
|
|
renderer.clear();
|
|
|
|
if ( postprocessing.enabled ) {
|
|
|
|
composer.render( 0.05 );
|
|
|
|
} else {
|
|
|
|
renderer.render( scene, camera );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|