|
|
@@ -35,7 +35,8 @@
|
|
|
|
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
import { SVGLoader } from 'three/addons/loaders/SVGLoader.js';
|
|
|
- import { FontLoader } from 'three/addons/loaders/FontLoader.js';
|
|
|
+ import { Font } from 'three/addons/loaders/FontLoader.js';
|
|
|
+ import { unzipSync, strFromU8 } from 'three/addons/libs/fflate.module.js';
|
|
|
|
|
|
let camera, scene, renderer;
|
|
|
|
|
|
@@ -44,108 +45,144 @@
|
|
|
function init( ) {
|
|
|
|
|
|
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
|
|
|
- camera.position.set( 0, - 400, 600 );
|
|
|
+ camera.position.set( 0, - 400, 1000 );
|
|
|
|
|
|
scene = new THREE.Scene();
|
|
|
scene.background = new THREE.Color( 0xf0f0f0 );
|
|
|
|
|
|
- const loader = new FontLoader();
|
|
|
- loader.load( 'fonts/helvetiker_regular.typeface.json', function ( font ) {
|
|
|
+ new THREE.FileLoader()
|
|
|
+ .setResponseType( 'arraybuffer' )
|
|
|
+ .load( 'fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip', function ( data ) {
|
|
|
+ const zip = unzipSync( new Uint8Array( data ) );
|
|
|
+ const strArray = strFromU8( new Uint8Array( zip[ 'MPLUSRounded1c-Regular.typeface.json' ].buffer ) );
|
|
|
|
|
|
- const color = new THREE.Color( 0x006699 );
|
|
|
+ const font = new Font( JSON.parse( strArray ) );
|
|
|
+ const color = new THREE.Color( 0x006699 );
|
|
|
|
|
|
- const matDark = new THREE.MeshBasicMaterial( {
|
|
|
- color: color,
|
|
|
- side: THREE.DoubleSide
|
|
|
- } );
|
|
|
+ const matDark = new THREE.MeshBasicMaterial( {
|
|
|
+ color: color,
|
|
|
+ side: THREE.DoubleSide
|
|
|
+ } );
|
|
|
|
|
|
- const matLite = new THREE.MeshBasicMaterial( {
|
|
|
- color: color,
|
|
|
- transparent: true,
|
|
|
- opacity: 0.4,
|
|
|
- side: THREE.DoubleSide
|
|
|
- } );
|
|
|
+ const matLite = new THREE.MeshBasicMaterial( {
|
|
|
+ color: color,
|
|
|
+ transparent: true,
|
|
|
+ opacity: 0.4,
|
|
|
+ side: THREE.DoubleSide
|
|
|
+ } );
|
|
|
|
|
|
- const message = ' Three.js\nStroke text.';
|
|
|
+ const material = {
|
|
|
+ dark: matDark,
|
|
|
+ lite: matLite,
|
|
|
+ color: color
|
|
|
+ };
|
|
|
|
|
|
- const shapes = font.generateShapes( message, 100 );
|
|
|
+ const english = ' Three.js\nStroke text.'; // Left to right
|
|
|
|
|
|
- const geometry = new THREE.ShapeGeometry( shapes );
|
|
|
+ const hebrew = 'טקסט קו'; // Right to left
|
|
|
|
|
|
- geometry.computeBoundingBox();
|
|
|
+ const chinese = '文字描邊'; // Top to bottom
|
|
|
|
|
|
- const xMid = - 0.5 * ( geometry.boundingBox.max.x - geometry.boundingBox.min.x );
|
|
|
+ const message1 = generateStrokeText( font, material, english, 80, 'ltr' );
|
|
|
|
|
|
- geometry.translate( xMid, 0, 0 );
|
|
|
+ const message2 = generateStrokeText( font, material, hebrew, 80, 'rtl' );
|
|
|
|
|
|
- // make shape ( N.B. edge view not visible )
|
|
|
+ const message3 = generateStrokeText( font, material, chinese, 80, 'tb' );
|
|
|
|
|
|
- const text = new THREE.Mesh( geometry, matLite );
|
|
|
- text.position.z = - 150;
|
|
|
- scene.add( text );
|
|
|
+ message1.position.x = - 100;
|
|
|
|
|
|
- // make line shape ( N.B. edge view remains visible )
|
|
|
+ message2.position.x = - 100;
|
|
|
+ message2.position.y = - 300;
|
|
|
|
|
|
- const holeShapes = [];
|
|
|
+ message3.position.x = 300;
|
|
|
+ message3.position.y = - 300;
|
|
|
|
|
|
- for ( let i = 0; i < shapes.length; i ++ ) {
|
|
|
+ scene.add( message1, message2, message3 );
|
|
|
|
|
|
- const shape = shapes[ i ];
|
|
|
+ render();
|
|
|
|
|
|
- if ( shape.holes && shape.holes.length > 0 ) {
|
|
|
+ } ); //end load function
|
|
|
|
|
|
- for ( let j = 0; j < shape.holes.length; j ++ ) {
|
|
|
+ renderer = new THREE.WebGLRenderer( { antialias: true } );
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ document.body.appendChild( renderer.domElement );
|
|
|
|
|
|
- const hole = shape.holes[ j ];
|
|
|
- holeShapes.push( hole );
|
|
|
+ const controls = new OrbitControls( camera, renderer.domElement );
|
|
|
+ controls.target.set( 0, 0, 0 );
|
|
|
+ controls.update();
|
|
|
|
|
|
- }
|
|
|
+ controls.addEventListener( 'change', render );
|
|
|
|
|
|
- }
|
|
|
+ window.addEventListener( 'resize', onWindowResize );
|
|
|
|
|
|
- }
|
|
|
+ } // end init
|
|
|
+
|
|
|
+ function generateStrokeText(font, material, message, size, direction = 'ltr') {
|
|
|
+
|
|
|
+ const shapes = font.generateShapes( message, size, direction );
|
|
|
+
|
|
|
+ const geometry = new THREE.ShapeGeometry( shapes );
|
|
|
+
|
|
|
+ const strokeText = new THREE.Group();
|
|
|
+
|
|
|
+ geometry.computeBoundingBox();
|
|
|
+
|
|
|
+ const xMid = - 0.5 * ( geometry.boundingBox.max.x - geometry.boundingBox.min.x );
|
|
|
+
|
|
|
+ geometry.translate( xMid, 0, 0 );
|
|
|
+
|
|
|
+ // make shape ( N.B. edge view not visible )
|
|
|
|
|
|
- shapes.push( ...holeShapes );
|
|
|
+ const text = new THREE.Mesh( geometry, material.lite );
|
|
|
|
|
|
- const style = SVGLoader.getStrokeStyle( 5, color.getStyle() );
|
|
|
+ text.position.z = - 150;
|
|
|
|
|
|
- const strokeText = new THREE.Group();
|
|
|
+ strokeText.add(text);
|
|
|
|
|
|
- for ( let i = 0; i < shapes.length; i ++ ) {
|
|
|
+ // make line shape ( N.B. edge view remains visible )
|
|
|
|
|
|
- const shape = shapes[ i ];
|
|
|
+ const holeShapes = [];
|
|
|
|
|
|
- const points = shape.getPoints();
|
|
|
+ for ( let i = 0; i < shapes.length; i ++ ) {
|
|
|
|
|
|
- const geometry = SVGLoader.pointsToStroke( points, style );
|
|
|
+ const shape = shapes[ i ];
|
|
|
|
|
|
- geometry.translate( xMid, 0, 0 );
|
|
|
+ if ( shape.holes && shape.holes.length > 0 ) {
|
|
|
|
|
|
- const strokeMesh = new THREE.Mesh( geometry, matDark );
|
|
|
- strokeText.add( strokeMesh );
|
|
|
+ for ( let j = 0; j < shape.holes.length; j ++ ) {
|
|
|
+
|
|
|
+ const hole = shape.holes[ j ];
|
|
|
+ holeShapes.push( hole );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- scene.add( strokeText );
|
|
|
+ }
|
|
|
|
|
|
- render();
|
|
|
+ shapes.push( ...holeShapes );
|
|
|
|
|
|
- } ); //end load function
|
|
|
+ const style = SVGLoader.getStrokeStyle( 5, material.color.getStyle() );
|
|
|
|
|
|
- renderer = new THREE.WebGLRenderer( { antialias: true } );
|
|
|
- renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
- renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
- document.body.appendChild( renderer.domElement );
|
|
|
+ for ( let i = 0; i < shapes.length; i ++ ) {
|
|
|
|
|
|
- const controls = new OrbitControls( camera, renderer.domElement );
|
|
|
- controls.target.set( 0, 0, 0 );
|
|
|
- controls.update();
|
|
|
+ const shape = shapes[ i ];
|
|
|
|
|
|
- controls.addEventListener( 'change', render );
|
|
|
+ const points = shape.getPoints();
|
|
|
|
|
|
- window.addEventListener( 'resize', onWindowResize );
|
|
|
+ const geometry = SVGLoader.pointsToStroke( points, style );
|
|
|
|
|
|
- } // end init
|
|
|
+ geometry.translate( xMid, 0, 0 );
|
|
|
+
|
|
|
+ const strokeMesh = new THREE.Mesh( geometry, material.dark );
|
|
|
+ strokeText.add( strokeMesh );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return strokeText;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
function onWindowResize() {
|
|
|
|