Kaynağa Gözat

TTFLoader with example (Updated to work with last opentype.js version) (#9867)

* Added TTFLoader and opentype.min.js

* Updated TTFLoader to work with last version of opentype and added example

* Removed batch for py http server
Tentone 8 yıl önce
ebeveyn
işleme
3a58b71513

+ 1 - 0
examples/files.js

@@ -101,6 +101,7 @@ var files = {
 		"webgl_loader_pcd",
 		"webgl_loader_pdb",
 		"webgl_loader_ply",
+		"webgl_loader_ttf",
 		"webgl_loader_sea3d",
 		"webgl_loader_sea3d_hierarchy",
 		"webgl_loader_sea3d_keyframe",

BIN
examples/fonts/ttf/kenpixel.ttf


Dosya farkı çok büyük olduğundan ihmal edildi
+ 19 - 0
examples/js/libs/opentype.min.js


+ 167 - 0
examples/js/loaders/TTFLoader.js

@@ -0,0 +1,167 @@
+/**
+ * @author gero3 / https://github.com/gero3
+ * @author tentone / https://github.com/tentone
+ * Requires opentype.js to be included in the project
+ * Loads TTF files and converts them into typeface JSON that can be used directly to create THREE.Font objects 
+ */
+
+"use strict";
+
+THREE.TTFLoader = function(manager)
+{
+	this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager;
+	this.reversed = false;
+}
+
+THREE.TTFLoader.prototype.load = function(url, onLoad, onProgress, onError)
+{
+	var self = this;
+	var loader = new THREE.XHRLoader(this.manager);
+	loader.setResponseType("arraybuffer");
+	loader.load(url, function(buffer)
+	{
+		var json = self.parse( buffer );
+		if(onLoad !== undefined)
+		{
+			onLoad(json);
+		}
+	}, onProgress, onError);
+}
+
+THREE.TTFLoader.prototype.parse = function(arraybuffer)
+{
+	if(typeof opentype === "undefined")
+	{
+		console.warn("TTFLoader requires opentype.js Make sure it\'s included before using the loader");
+		return null;
+	}
+
+	var font = opentype.parse(arraybuffer);
+	return THREE.TTFLoader.convert(font, this.reversed);
+}
+
+THREE.TTFLoader.convert = function(font, reversed)
+{
+	var scale = (100000) / ((font.unitsPerEm || 2048) * 72);
+	var result = {};
+	result.glyphs = {};
+
+	for(var i = 0; i < font.glyphs.length; i++)
+	{
+		var glyph = font.glyphs.glyphs[i];
+
+		if(glyph.unicode !== undefined)
+		{
+			var token = {};
+			token.ha = Math.round(glyph.advanceWidth * scale);
+			token.x_min = Math.round(glyph.xMin * scale);
+			token.x_max = Math.round(glyph.xMax * scale);
+			token.o = ""
+			
+			if(reversed)
+			{
+				glyph.path.commands = TTFLoader.reverseCommands(glyph.path.commands);
+			}
+
+			glyph.path.commands.forEach(function(command, i)
+			{
+				if(command.type.toLowerCase() === "c")
+				{
+					command.type = "b";
+				}
+				
+				token.o += command.type.toLowerCase();
+				token.o += " "
+				
+				if(command.x !== undefined && command.y !== undefined){
+					token.o += Math.round(command.x * scale);
+					token.o += " "
+					token.o += Math.round(command.y * scale);
+					token.o += " "
+				}
+				if(command.x1 !== undefined && command.y1 !== undefined)
+				{
+					token.o += Math.round(command.x1 * scale);
+					token.o += " "
+					token.o += Math.round(command.y1 * scale);
+					token.o += " "
+				}
+				if(command.x2 !== undefined && command.y2 !== undefined)
+				{
+					token.o += Math.round(command.x2 * scale);
+					token.o += " "
+					token.o += Math.round(command.y2 * scale);
+					token.o += " "
+				}
+			});
+			result.glyphs[String.fromCharCode(glyph.unicode)] = token;
+		}
+	}
+
+	result.familyName = font.familyName;
+	result.ascender = Math.round(font.ascender * scale);
+	result.descender = Math.round(font.descender * scale);
+	result.underlinePosition = font.tables.post.underlinePosition;
+	result.underlineThickness = font.tables.post.underlineThickness;
+	result.boundingBox =
+	{
+		"yMin": font.tables.head.yMin,
+		"xMin": font.tables.head.xMin,
+		"yMax": font.tables.head.yMax,
+		"xMax": font.tables.head.xMax
+	};
+
+	result.resolution = 1000;
+	result.original_font_information = font.tables.name;
+
+	return result;
+}
+
+THREE.TTFLoader.reverseCommands = function(commands)
+{
+	var paths = [];
+	var path;
+	
+	commands.forEach(function(c)
+	{
+		if(c.type.toLowerCase() === "m")
+		{
+			path = [c];
+			paths.push(path);
+		}
+		else if(c.type.toLowerCase() !== "z")
+		{
+			path.push(c);
+		}
+	});
+	
+	var reversed = [];
+	paths.forEach(function(p)
+	{
+		var result = {"type":"m" , "x" : p[p.length-1].x, "y": p[p.length-1].y};
+		reversed.push(result);
+		
+		for(var i = p.length - 1; i > 0; i--)
+		{
+			var command = p[i];
+			result = {"type":command.type};
+			if(command.x2 !== undefined && command.y2 !== undefined)
+			{
+				result.x1 = command.x2;
+				result.y1 = command.y2;
+				result.x2 = command.x1;
+				result.y2 = command.y1;
+			}
+			else if(command.x1 !== undefined && command.y1 !== undefined)
+			{
+				result.x1 = command.x1;
+				result.y1 = command.y1;
+			}
+			result.x =  p[i-1].x;
+			result.y =  p[i-1].y;
+			reversed.push(result);
+		}
+	});
+	
+	return reversed;	
+}

+ 475 - 0
examples/webgl_loader_ttf.html

@@ -0,0 +1,475 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loader - ttf</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			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>
+		<script src="../build/three.js"></script>
+		<script src="js/utils/GeometryUtils.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src="js/loaders/TTFLoader.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/opentype.min.js"></script>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - TTFLoader using opentype by gero3
+			<br/>type to enter new text, drag to spin the text
+			<br/><span class="button" id="color">change color</span>,
+			<span class="button" id="weight">change weight</span>,
+			<span class="button" id="bevel">change bevel</span>
+		</div>
+
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			THREE.Cache.enabled = true;
+
+			var container, stats,  hex, color;
+
+			var camera, cameraTarget, scene, renderer;
+
+			var group, textMesh1, textMesh2, textGeo, material;
+
+			var firstLetter = true;
+
+			var text = "three.js",
+				height = 20,
+				size = 70,
+				hover = 30,
+				curveSegments = 4,
+				bevelThickness = 2,
+				bevelSize = 1.5,
+				bevelSegments = 3,
+				bevelEnabled = true;
+
+			var font = null;
+
+			var mirror = true;
+
+			var targetRotation = 0;
+			var targetRotationOnMouseDown = 0;
+
+			var mouseX = 0;
+			var mouseXOnMouseDown = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			init();
+			animate();
+
+			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 );
+
+				// CAMERA
+
+				camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 1500 );
+				camera.position.set( 0, 400, 700 );
+
+				cameraTarget = new THREE.Vector3( 0, 150, 0 );
+
+				// SCENE
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.Fog( 0x000000, 250, 1400 );
+
+				// LIGHTS
+
+				var dirLight = new THREE.DirectionalLight( 0xffffff, 0.125 );
+				dirLight.position.set( 0, 0, 1 ).normalize();
+				scene.add( dirLight );
+
+				var pointLight = new THREE.PointLight( 0xffffff, 1.5 );
+				pointLight.position.set( 0, 100, 90 );
+				scene.add( pointLight );
+
+				// 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 bevelhash  = hash.substring( 8, 9 );
+					var texthash   = hash.substring( 10 );
+
+					hex = colorhash;
+					pointLight.color.setHex( parseInt( colorhash, 16 ) );
+
+					fontName = reverseFontMap[ parseInt( fonthash ) ];
+					fontWeight = reverseWeightMap[ parseInt( weighthash ) ];
+
+					bevelEnabled = parseInt( bevelhash );
+
+					text = decodeURI( texthash );
+
+				} else {
+
+					pointLight.color.setHSL( Math.random(), 1, 0.5 );
+					hex = decimalToHex( pointLight.color.getHex() );
+
+				}
+
+				material = new THREE.MultiMaterial( [
+					new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ), // front
+					new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side
+				] );
+
+				group = new THREE.Group();
+				group.position.y = 100;
+
+				scene.add( group );
+
+				var loader = new THREE.TTFLoader();
+
+				loader.load( 'fonts/ttf/kenpixel.ttf', function ( json ) {
+					font = new THREE.Font(json);
+					//scene.add( font );
+					createText();
+				} );
+
+				var plane = new THREE.Mesh(
+					new THREE.PlaneBufferGeometry( 10000, 10000 ),
+					new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true } )
+				);
+				plane.position.y = 100;
+				plane.rotation.x = - Math.PI / 2;
+				scene.add( plane );
+
+				// RENDERER
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setClearColor( scene.fog.color );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				// STATS
+
+				stats = new Stats();
+				//container.appendChild( stats.dom );
+
+				// 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.setHSL( Math.random(), 1, 0.5 );
+					hex = decimalToHex( pointLight.color.getHex() );
+
+				}, false );
+
+				document.getElementById( "bevel" ).addEventListener( 'click', function(){
+
+					bevelEnabled = !bevelEnabled;
+
+					refreshText();
+
+				}, false );
+
+
+				window.addEventListener( 'resize', onWindowResize, false );
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function boolToNum( b ) {
+
+				return b ? 1 : 0;
+
+			}
+
+			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, {
+
+					font: font,
+
+					size: size,
+					height: height,
+					curveSegments: curveSegments,
+
+					bevelThickness: bevelThickness,
+					bevelSize: bevelSize,
+					bevelEnabled: bevelEnabled,
+
+					material: 0,
+					extrudeMaterial: 1
+
+				});
+
+				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.materialIndex == 1 ) {
+
+							for ( var j = 0; j < face.vertexNormals.length; j ++ ) {
+
+								face.vertexNormals[ j ].z = 0;
+								face.vertexNormals[ j ].normalize();
+
+							}
+
+							var va = textGeo.vertices[ face.a ];
+							var vb = textGeo.vertices[ face.b ];
+							var vc = textGeo.vertices[ face.c ];
+
+							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.max.x - textGeo.boundingBox.min.x );
+
+				textMesh1 = new THREE.Mesh( textGeo, material );
+
+				textMesh1.position.x = centerOffset;
+				textMesh1.position.y = hover;
+				textMesh1.position.z = 0;
+
+				textMesh1.rotation.x = 0;
+				textMesh1.rotation.y = Math.PI * 2;
+
+				group.add( textMesh1 );
+
+				if ( mirror ) {
+
+					textMesh2 = new THREE.Mesh( textGeo, material );
+
+					textMesh2.position.x = centerOffset;
+					textMesh2.position.y = -hover;
+					textMesh2.position.z = height;
+
+					textMesh2.rotation.x = Math.PI;
+					textMesh2.rotation.y = Math.PI * 2;
+
+					group.add( textMesh2 );
+
+				}
+
+			}
+
+			function refreshText() {
+
+				group.remove( textMesh1 );
+				if ( mirror ) group.remove( 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();
+			}
+
+			function render() {
+
+				group.rotation.y += ( targetRotation - group.rotation.y ) * 0.05;
+
+				camera.lookAt( cameraTarget );
+
+				renderer.clear();
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor