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

wip: track and add glsl attributes for char/word/line indices #109

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/troika-examples/text/TextExample.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,33 @@ const MATERIALS = {
normal.xyz = normalize(vec3(-cos(waveX) * waveAmplitude, 0.0, 1.0));
position.z += waveZ;
`
}),
'charIndex': indexAnimatorMaterial('charIndex', 'totalChars'),
'charInWordIndex': indexAnimatorMaterial('charInWordIndex', 'totalCharsInWord'),
'charInLineIndex': indexAnimatorMaterial('charInLineIndex', 'totalCharsInLine'),
'wordIndex': indexAnimatorMaterial('wordIndex', 'totalWords'),
'wordInLineIndex': indexAnimatorMaterial('wordInLineIndex', 'totalWordsInLine'),
'lineIndex': indexAnimatorMaterial('lineIndex', 'totalLines'),
}

function indexAnimatorMaterial(indexVar, totalVar) {
return createDerivedMaterial(new MeshBasicMaterial(), {
timeUniform: 'elapsed',
// vertexTransform: `
// // float angle = mix(0.0, PI / 2.0, ${indexVar} / ${totalVar});
// // position.z = position.y * cos(angle);
// // position.y *= sin(angle);
// //
// // position *= clamp(0.0, 1.0, (elapsed / 10.0 - ${indexVar}) / 10.0);
// `,
fragmentColorTransform: `
float angle = ${indexVar} / ${totalVar} * PI * 2.0;
gl_FragColor = vec4( sin(angle) / 2.0 + 0.5, cos(angle) / 2.0 + 0.5, tan(angle) / 2.0 + 0.5, 1.0 );
`
})
}


const MATERIAL_OPTS = Object.keys(MATERIALS)
Object.keys(MATERIALS).forEach(name => {
MATERIALS[name + '+Texture'] = MATERIALS[name].clone()
Expand Down Expand Up @@ -116,7 +141,7 @@ class TextExample extends React.Component {
curveRadius: 0,
fog: false,
animTextColor: true,
animTilt: true,
animTilt: false,
animRotate: false,
material: 'MeshStandardMaterial',
useTexture: false,
Expand Down
12 changes: 8 additions & 4 deletions packages/troika-three-text/src/GlyphsGeometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ const GlyphsGeometry = /*#__PURE__*/(() => {
const tempVec3 = new Vector3()

const glyphBoundsAttrName = 'aTroikaGlyphBounds'
const glyphIndexAttrName = 'aTroikaGlyphIndex'
const atlasIndexAttrName = 'aTroikaGlyphAtlasIndex'
const glyphColorAttrName = 'aTroikaGlyphColor'
const charIndicesAttrName = 'aTroikaCharIndices'
const wordAndLineIndicesAttrName = 'aTroikaWordLineIndices'

/**
@class GlyphsGeometry
Expand Down Expand Up @@ -108,11 +110,13 @@ const GlyphsGeometry = /*#__PURE__*/(() => {
* used with `applyClipRect` to choose an optimized `instanceCount`.
* @param {Uint8Array} [glyphColors] - An array holding r,g,b values for each glyph.
*/
updateGlyphs(glyphBounds, glyphAtlasIndices, blockBounds, chunkedBounds, glyphColors) {
updateGlyphs(glyphBounds, glyphAtlasIndices, blockBounds, chunkedBounds, glyphColors, charIndices, wordAndLineIndices) {
// Update the instance attributes
updateBufferAttr(this, glyphBoundsAttrName, glyphBounds, 4)
updateBufferAttr(this, glyphIndexAttrName, glyphAtlasIndices, 1)
updateBufferAttr(this, atlasIndexAttrName, glyphAtlasIndices, 1)
updateBufferAttr(this, glyphColorAttrName, glyphColors, 3)
updateBufferAttr(this, charIndicesAttrName, charIndices, 4)
updateBufferAttr(this, wordAndLineIndicesAttrName, wordAndLineIndices, 4)
this._chunkedBounds = chunkedBounds
setInstanceCount(this, glyphAtlasIndices.length)

Expand Down Expand Up @@ -145,7 +149,7 @@ const GlyphsGeometry = /*#__PURE__*/(() => {
* @param {Vector4} clipRect
*/
applyClipRect(clipRect) {
let count = this.getAttribute(glyphIndexAttrName).count
let count = this.getAttribute(atlasIndexAttrName).count
let chunks = this._chunkedBounds
if (chunks) {
for (let i = chunks.length; i--;) {
Expand Down
7 changes: 6 additions & 1 deletion packages/troika-three-text/src/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ const Text = /*#__PURE__*/(() => {
anchorY: this.anchorY,
colorRanges: this.colorRanges,
includeCaretPositions: true, //TODO parameterize
includeCharIndices: true, //TODO parameterize
includeWordAndLineIndices: true, //TODO parameterize
sdfGlyphSize: this.sdfGlyphSize
}, textRenderInfo => {
this._isSyncing = false
Expand All @@ -406,7 +408,9 @@ const Text = /*#__PURE__*/(() => {
textRenderInfo.glyphAtlasIndices,
textRenderInfo.blockBounds,
textRenderInfo.chunkedBounds,
textRenderInfo.glyphColors
textRenderInfo.glyphColors,
textRenderInfo.charIndices,
textRenderInfo.wordAndLineIndices,
)

// If we had extra sync requests queued up, kick it off
Expand Down Expand Up @@ -540,6 +544,7 @@ const Text = /*#__PURE__*/(() => {
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds)
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors
uniforms.uTroikaCharWordLineTotals.value.set(textInfo.totalChars, textInfo.totalWords, textInfo.totalLines)

let distanceOffset = 0
let blurRadius = 0
Expand Down
51 changes: 26 additions & 25 deletions packages/troika-three-text/src/TextBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,34 +194,35 @@ function getTextRenderInfo(args, callback) {
}

// Invoke callback with the text layout arrays and updated texture
callback(Object.freeze({
const textRenderInfo = {
parameters: args,
sdfTexture: atlas.sdfTexture,
sdfGlyphSize,
sdfExponent,
glyphBounds: result.glyphBounds,
glyphAtlasIndices: result.glyphAtlasIndices,
glyphColors: result.glyphColors,
caretPositions: result.caretPositions,
caretHeight: result.caretHeight,
chunkedBounds: result.chunkedBounds,
ascender: result.ascender,
descender: result.descender,
lineHeight: result.lineHeight,
topBaseline: result.topBaseline,
blockBounds: result.blockBounds,
visibleBounds: result.visibleBounds,
timings: result.timings,
get totalBounds() {
console.log('totalBounds deprecated, use blockBounds instead')
return result.blockBounds
},
get totalBlockSize() {
console.log('totalBlockSize deprecated, use blockBounds instead')
const [x0, y0, x1, y1] = result.blockBounds
return [x1 - x0, y1 - y0]
}
}))
sdfExponent
}
;[ //copy specific props from result object
'glyphBounds',
'glyphAtlasIndices',
'glyphColors',
'caretPositions',
'caretHeight',
'chunkedBounds',
'charIndices',
'wordAndLineIndices',
'totalChars',
'totalWords',
'totalLines',
'ascender',
'descender',
'lineHeight',
'topBaseline',
'blockBounds',
'visibleBounds',
'timings'
].forEach(prop => {
textRenderInfo[prop] = result[prop]
})
callback(Object.freeze(textRenderInfo))
})
}

Expand Down
61 changes: 56 additions & 5 deletions packages/troika-three-text/src/TextDerivedMaterial.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
import { createDerivedMaterial, voidMainRegExp } from 'troika-three-utils'
import { Color, Vector2, Vector4, Matrix3 } from 'three'
import { Color, Vector2, Vector4, Matrix3, Vector3 } from 'three'

const CHAR_WORD_LINE_VAR_DECLS = `
float charIndex;
float totalChars;
float charInWordIndex;
float totalCharsInWord;
float charInLineIndex;
float totalCharsInLine;
float wordIndex;
float totalWords;
float wordInLineIndex;
float totalWordsInLine;
float lineIndex;
float totalLines;
`

const CHAR_WORD_LINE_VAR_ASSIGNMENTS = `
totalChars = uTroikaCharWordLineTotals.x;
totalWords = uTroikaCharWordLineTotals.y;
totalLines = uTroikaCharWordLineTotals.z;

charIndex = vTroikaCharIndices.x;
charInLineIndex = vTroikaCharIndices.y;
totalCharsInLine = vTroikaCharIndices.z;
charInWordIndex = floor(vTroikaCharIndices.w / 256.0 + 0.5);
totalCharsInWord = mod(vTroikaCharIndices.w, 256.0);

wordIndex = vTroikaWordLineIndices.x;
wordInLineIndex = vTroikaWordLineIndices.y;
totalWordsInLine = vTroikaWordLineIndices.z;
lineIndex = vTroikaWordLineIndices.w;
`

// language=GLSL
const VERTEX_DEFS = `
Expand All @@ -13,14 +45,19 @@ uniform float uTroikaDistanceOffset;
uniform float uTroikaBlurRadius;
uniform vec2 uTroikaPositionOffset;
uniform float uTroikaCurveRadius;
uniform vec3 uTroikaCharWordLineTotals;
attribute vec4 aTroikaGlyphBounds;
attribute float aTroikaGlyphIndex;
attribute float aTroikaGlyphAtlasIndex;
attribute vec3 aTroikaGlyphColor;
attribute vec4 aTroikaCharIndices;
attribute vec4 aTroikaWordLineIndices;
varying vec2 vTroikaGlyphUV;
varying vec4 vTroikaTextureUVBounds;
varying float vTroikaTextureChannel;
varying vec3 vTroikaGlyphColor;
varying vec2 vTroikaGlyphDimensions;
varying vec4 vTroikaCharIndices;
varying vec4 vTroikaWordLineIndices;
`

// language=GLSL prefix="void main() {" suffix="}"
Expand Down Expand Up @@ -64,11 +101,11 @@ ${''/* NOTE: it seems important to calculate the glyph's bounding texture UVs he
float txCols = uTroikaSDFTextureSize.x / uTroikaSDFGlyphSize;
vec2 txUvPerSquare = uTroikaSDFGlyphSize / uTroikaSDFTextureSize;
vec2 txStartUV = txUvPerSquare * vec2(
mod(floor(aTroikaGlyphIndex / 4.0), txCols),
floor(floor(aTroikaGlyphIndex / 4.0) / txCols)
mod(floor(aTroikaGlyphAtlasIndex / 4.0), txCols),
floor(floor(aTroikaGlyphAtlasIndex / 4.0) / txCols)
);
vTroikaTextureUVBounds = vec4(txStartUV, vec2(txStartUV) + txUvPerSquare);
vTroikaTextureChannel = mod(aTroikaGlyphIndex, 4.0);
vTroikaTextureChannel = mod(aTroikaGlyphAtlasIndex, 4.0);
`

// language=GLSL
Expand All @@ -84,11 +121,14 @@ uniform float uTroikaBlurRadius;
uniform vec3 uTroikaStrokeColor;
uniform float uTroikaStrokeWidth;
uniform float uTroikaStrokeOpacity;
uniform vec3 uTroikaCharWordLineTotals;
uniform bool uTroikaSDFDebug;
varying vec2 vTroikaGlyphUV;
varying vec4 vTroikaTextureUVBounds;
varying float vTroikaTextureChannel;
varying vec2 vTroikaGlyphDimensions;
varying vec4 vTroikaCharIndices;
varying vec4 vTroikaWordLineIndices;

float troikaSdfValueToSignedDistance(float alpha) {
// Inverse of encoding in SDFGenerator.js
Expand Down Expand Up @@ -230,11 +270,18 @@ export function createTextDerivedMaterial(baseMaterial) {
uTroikaStrokeOpacity: {value: 1},
uTroikaOrient: {value: new Matrix3()},
uTroikaUseGlyphColors: {value: true},
uTroikaCharWordLineTotals: {value: new Vector3()},
uTroikaSDFDebug: {value: false}
},
vertexDefs: VERTEX_DEFS,
vertexMainIntro: `
vTroikaCharIndices = aTroikaCharIndices;
vTroikaWordLineIndices = aTroikaWordLineIndices;
${CHAR_WORD_LINE_VAR_ASSIGNMENTS}
`,
vertexTransform: VERTEX_TRANSFORM,
fragmentDefs: FRAGMENT_DEFS,
fragmentMainIntro: CHAR_WORD_LINE_VAR_ASSIGNMENTS,
fragmentColorTransform: FRAGMENT_TRANSFORM,
customRewriter({vertexShader, fragmentShader}) {
let uDiffuseRE = /\buniform\s+vec3\s+diffuse\b/
Expand All @@ -251,6 +298,10 @@ export function createTextDerivedMaterial(baseMaterial) {
)
}
}

vertexShader = `${CHAR_WORD_LINE_VAR_DECLS}\n${vertexShader}`
fragmentShader = `${CHAR_WORD_LINE_VAR_DECLS}\n${fragmentShader}`

return { vertexShader, fragmentShader }
}
})
Expand Down