Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextGeometry changes and support in editor #27931

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
13 changes: 11 additions & 2 deletions build/three.cjs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions editor/js/Editor.js
@@ -1,5 +1,7 @@
import * as THREE from 'three';

import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { Config } from './Config.js';
import { Loader } from './Loader.js';
import { History as _History } from './History.js';
Expand Down Expand Up @@ -657,6 +659,8 @@ Editor.prototype = {
fromJSON: async function ( json ) {

var loader = new THREE.ObjectLoader();
loader.registerGeometry( 'TextGeometry', TextGeometry );

var camera = await loader.parseAsync( json.camera );

this.camera.copy( camera );
Expand Down
41 changes: 41 additions & 0 deletions editor/js/Menubar.Add.js
@@ -1,5 +1,8 @@
import * as THREE from 'three';

import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { UIPanel, UIRow, UIHorizontalRule } from './libs/ui.js';

import { AddObjectCommand } from './commands/AddObjectCommand.js';
Expand Down Expand Up @@ -248,6 +251,44 @@ function MenubarAdd( editor ) {
} );
options.add( option );

// Text

option = new UIRow();
option.setClass( 'option' );
option.setTextContent( strings.getKey( 'menubar/add/text' ) );
option.onClick( function () {

const loader = new FontLoader();
loader.load( '../examples/fonts/helvetiker_bold.typeface.json', function ( font ) {

const text = 'THREE.JS';

const geometry = new TextGeometry( text, {
text: text,
font,
size: 70,
depth: 20,
curveSegments: 4,

bevelEnabled: false,
bevelThickness: 10,
bevelSize: 8,
bevelOffset: 0,
bevelSegments: 3,
scale: 0.01

} );

const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
mesh.name = 'Text';

editor.execute( new AddObjectCommand( editor, mesh ) );

} );

} );
options.add( option );

// Torus

option = new UIRow();
Expand Down
150 changes: 150 additions & 0 deletions editor/js/Sidebar.Geometry.TextGeometry.js
@@ -0,0 +1,150 @@
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { UIDiv, UIRow, UIText, UINumber, UIInteger, UIInput, UICheckbox } from './libs/ui.js';

import { SetGeometryCommand } from './commands/SetGeometryCommand.js';

function GeometryParametersPanel( editor, object ) {

const strings = editor.strings;

const container = new UIDiv();

const geometry = object.geometry;
const parameters = geometry.parameters.options;

// text

const textRow = new UIRow();
const text = new UIInput().setValue( parameters.text ).onChange( update );

textRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/text' ) ).setClass( 'Label' ) );
textRow.add( text );

container.add( textRow );

// size

const sizeRow = new UIRow();
const size = new UINumber().setPrecision( 3 ).setValue( parameters.size ).onChange( update );

sizeRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/size' ) ).setClass( 'Label' ) );
sizeRow.add( size );

container.add( sizeRow );

// depth

const depthRow = new UIRow();
const depth = new UINumber().setPrecision( 3 ).setValue( parameters.depth ).onChange( update );

depthRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/depth' ) ).setClass( 'Label' ) );
depthRow.add( depth );

container.add( depthRow );

// curveSegments

const curveSegmentsRow = new UIRow();
const curveSegments = new UIInteger( parameters.curveSegments ).setRange( 1, Infinity ).onChange( update );

curveSegmentsRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/curveseg' ) ).setClass( 'Label' ) );
curveSegmentsRow.add( curveSegments );

container.add( curveSegmentsRow );


// scale

const scaleRow = new UIRow();
const scale = new UINumber().setPrecision( 4 ).setValue( parameters.scale ).onChange( update );

scaleRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/scale' ) ).setClass( 'Label' ) );
scaleRow.add( scale );

container.add( scaleRow );

// bevelEnabled

const bevelEnabledRow = new UIRow();
const bevelEnabled = new UICheckbox( parameters.bevelEnabled ).onChange( update );

bevelEnabledRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelenabled' ) ).setClass( 'Label' ) );
bevelEnabledRow.add( bevelEnabled );

container.add( bevelEnabledRow );

// bevelThickness

const bevelThicknessRow = new UIRow();
const bevelThickness = new UINumber( parameters.bevelThickness ).setPrecision( 3 ).setRange( 0, Infinity ).onChange( update );

bevelThicknessRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelthickness' ) ).setClass( 'Label' ) );
bevelThicknessRow.add( bevelThickness );

container.add( bevelThicknessRow );

// bevelSize

const bevelSizeRow = new UIRow();
const bevelSize = new UINumber( parameters.bevelSize ).setRange( 0, Infinity ).onChange( update );

bevelSizeRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelsize' ) ).setClass( 'Label' ) );
bevelSizeRow.add( bevelSize );

container.add( bevelSizeRow );

// bevelOffset

const bevelOffsetRow = new UIRow();
const bevelOffset = new UINumber( parameters.bevelOffset ).setRange( 0, Infinity ).onChange( update );

bevelOffsetRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelOffset' ) ).setClass( 'Label' ) );
bevelOffsetRow.add( bevelOffset );

container.add( bevelOffsetRow );


// bevelSegments

const bevelSegmentsRow = new UIRow();
const bevelSegments = new UIInteger( parameters.bevelSegments ).setRange( 0, Infinity ).onChange( update );

bevelSegmentsRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelseg' ) ).setClass( 'Label' ) );
bevelSegmentsRow.add( bevelSegments );

container.add( bevelSegmentsRow );

function update() {

const options = {

text: text.getValue(),
font: parameters.font,

size: size.getValue(),
depth: depth.getValue(),
curveSegments: curveSegments.getValue(),

bevelEnabled: bevelEnabled.getValue(),
bevelThickness: bevelThickness.getValue(),
bevelSize: bevelSize.getValue(),
bevelOffset: bevelOffset.getValue(),
bevelSegments: bevelSegments.getValue(),

scale: scale.getValue(),

};

const geometry = new TextGeometry( options.text, options );

editor.execute( new SetGeometryCommand( editor, object, geometry ) );

}

return container;

}

export { GeometryParametersPanel };

14 changes: 13 additions & 1 deletion editor/js/Strings.js
Expand Up @@ -39,6 +39,7 @@ function Strings( config ) {
'menubar/add/icosahedron': 'Icosahedron',
'menubar/add/octahedron': 'Octahedron',
'menubar/add/tetrahedron': 'Tetrahedron',
'menubar/add/text': 'Text',
'menubar/add/torus': 'Torus',
'menubar/add/tube': 'Tube',
'menubar/add/torusknot': 'TorusKnot',
Expand Down Expand Up @@ -208,6 +209,17 @@ function Strings( config ) {
'sidebar/geometry/sphere_geometry/thetastart': 'Theta start',
'sidebar/geometry/sphere_geometry/thetalength': 'Theta length',

'sidebar/geometry/text_geometry/text': 'Text',
'sidebar/geometry/text_geometry/size': 'Font size',
'sidebar/geometry/text_geometry/depth': 'Extrude depth',
Fixed Show fixed Hide fixed
'sidebar/geometry/text_geometry/scale': 'Scale',
'sidebar/geometry/text_geometry/curveseg': 'Curve segments',
'sidebar/geometry/text_geometry/bevelenabled': 'Bevel enabled',
'sidebar/geometry/text_geometry/bevelthickness': 'Bevel thickness',
'sidebar/geometry/text_geometry/bevelsize': 'Bevel size',
'sidebar/geometry/text_geometry/bevelOffset': 'Bevel offset',
'sidebar/geometry/text_geometry/bevelseg': 'Bevel segments',

'sidebar/geometry/torus_geometry/radius': 'Radius',
'sidebar/geometry/torus_geometry/tube': 'Tube',
'sidebar/geometry/torus_geometry/radialsegments': 'Radial segments',
Expand Down Expand Up @@ -1422,7 +1434,7 @@ function Strings( config ) {

getKey: function ( key ) {

return values[ language ][ key ] || '???';
return values[ language ][ key ] || `???${key.split('/').pop().substring(0, 8)}`;

}

Expand Down
28 changes: 28 additions & 0 deletions examples/jsm/geometries/TextGeometry.js
Expand Up @@ -15,6 +15,7 @@
* }
*/

import { Font } from '../loaders/FontLoader.js';
import {
ExtrudeGeometry
} from 'three';
Expand Down Expand Up @@ -53,12 +54,39 @@ class TextGeometry extends ExtrudeGeometry {

super( shapes, parameters );

// for conversion of font to object units (ie. px -> m)

const scale = parameters.scale;

if ( scale !== undefined ) {

this.computeBoundingBox();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the bounding box computed at this point? It will be automatically recomputed when calling BufferGeometry.scale()?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: Is the introduction of a scale parameter really necessary? Couldn't apps just use Object3D.scale instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason to do it here is getting a scaled down (0.01) geometry mostly for connivence to be used in Editor.

the object.scale could be used, but I wasn't sure if it was a good idea to insert a TextGeometry at a custom object scale, hence having it in the geometry seems like a more convenient way.

Copy link
Collaborator

@Mugen87 Mugen87 Apr 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to keep the parameters of TextGeometry to a minimum, it would be better to use Object3D.scale and not introduce an additional way for transforming geometry, imo.

this.scale( scale, scale, scale );

}

}

this.type = 'TextGeometry';

}

toJSON() {

const data = super.toJSON();
return data;

}

static fromJSON( data ) {

const options = data.options;

options.font = new Font( options.font.data );
return new TextGeometry( options.text, options );

}

}


Expand Down
12 changes: 10 additions & 2 deletions src/loaders/ObjectLoader.js
Expand Up @@ -69,6 +69,8 @@ class ObjectLoader extends Loader {

super( manager );

this.geometryTypes = Object.assign( {}, Geometries );

}

load( url, onLoad, onProgress, onError ) {
Expand Down Expand Up @@ -261,6 +263,12 @@ class ObjectLoader extends Loader {

}

registerGeometry( type, klass ) {
Copy link
Collaborator

@Mugen87 Mugen87 Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrdoob Are you okay with this kind of new interface?

This approach is in general interesting since it could be used in context of other classes like materials as well. Meaning custom/addon materials implement a static fromJSON() (factory) method that ObjectLoader/MaterialLoader can use to create an instance. The internal material lib can be enhanced via:

loader.registerMaterial( 'MeshGouraudMaterial', MeshGouraudMaterial );

Related #21265, #11266.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting indeed...

I'd rename this.Geometries to this.geometryTypes though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated ObjectLoader manually but there still seems to be a diff in build/three.cjs.

Apart from that, the PR looks good to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated a merge from mrdoob/dev so there should be no conflicts now


this.geometryTypes[ type ] = klass;

}

parseGeometries( json, shapes ) {

const geometries = {};
Expand All @@ -284,9 +292,9 @@ class ObjectLoader extends Loader {

default:

if ( data.type in Geometries ) {
if ( data.type in this.geometryTypes ) {

geometry = Geometries[ data.type ].fromJSON( data, shapes );
geometry = this.geometryTypes[ data.type ].fromJSON( data, shapes );

} else {

Expand Down