Compare commits
No commits in common. "main" and "0.2.3" have entirely different histories.
37
README.md
37
README.md
@ -1,28 +1,13 @@
|
|||||||
# vrml1to97
|
# vrml1to97
|
||||||
|
|
||||||
JavaScript converter from VRML 1.0 to VRML97 file format.
|
JavaScript converter from VRML 1.0 to VRML97 file format, based on Wings 3D
|
||||||
|
conversion logic.
|
||||||
|
|
||||||
This converter was originally a JavaScript reimplementation of the algorithm
|
Essentially, this is a JavaScript reimplementation of the algorithm of the
|
||||||
of the "token rearranger" found in the
|
"token rearranger" found in the
|
||||||
[Wings 3D x3d importer](https://github.com/dgud/wings/blob/master/plugins_src/import_export/x3d_import.erl)
|
[Wings 3D x3d importer](https://github.com/dgud/wings/blob/master/plugins_src/import_export/x3d_import.erl)
|
||||||
(which was written in Erlang).
|
(which was written in Erlang).
|
||||||
|
|
||||||
However, from version 0.3, it adds support for a significantly larger subset of
|
|
||||||
VRML 1.0. Overall, it recognizes and converts
|
|
||||||
|
|
||||||
* Title, SceneInfo, BackgroundColor, and View "Info" nodes [this node type
|
|
||||||
was removed in VRML97].
|
|
||||||
* All Light nodes
|
|
||||||
* PerspectiveCamera nodes (converted into Viewpoint nodes)
|
|
||||||
* Grouping nodes including Separator, Group, Switch, and WWWAnchor
|
|
||||||
* Interprets a Switch named "Cameras" (by a DEF) as a list of Viewpoints
|
|
||||||
* ShapeHints
|
|
||||||
* Transformation nodes including Translation, Rotation, Scale, and Transform
|
|
||||||
* All shape nodes including Cube, Cone, Cylinder, Sphere, IndexedFaceSet,
|
|
||||||
IndexedLineSet, PointSet, Coordinate3, and Normal
|
|
||||||
* All material nodes including Material, TextureCoordinate2, and Texture2
|
|
||||||
* DEF and USE constructs to share subtrees
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
From an es6 module under Node (for example)
|
From an es6 module under Node (for example)
|
||||||
@ -59,8 +44,8 @@ Currently this package exports just two functions:
|
|||||||
actually check that its input is VRML 1, so its behavior is undefined
|
actually check that its input is VRML 1, so its behavior is undefined
|
||||||
if given anything but valid VRML 1 syntax). Returns VRML 97 syntax for
|
if given anything but valid VRML 1 syntax). Returns VRML 97 syntax for
|
||||||
the same scene, as nearly as it can translate. Note that not all of
|
the same scene, as nearly as it can translate. Note that not all of
|
||||||
VRML 1 is recognized (see above for a list of constructs that should
|
VRML 1 is recognized, and some of the translations may be somewhat
|
||||||
be handled), and some of the translations may be somewhat approximate.
|
approximate.
|
||||||
|
|
||||||
If the optional second argument `source` is supplied, `convert` adds
|
If the optional second argument `source` is supplied, `convert` adds
|
||||||
a comment indicating that the original vrml1 came from the specified
|
a comment indicating that the original vrml1 came from the specified
|
||||||
@ -81,12 +66,10 @@ Currently this package exports just two functions:
|
|||||||
## Conversion notes
|
## Conversion notes
|
||||||
|
|
||||||
One sort of geometry common to VRML 1 and VRML97 is the IndexedFaceSet. These
|
One sort of geometry common to VRML 1 and VRML97 is the IndexedFaceSet. These
|
||||||
entities are often used to render solids. Indeed, the default in VRML97 is to
|
often used to render solids. Indeed, the default in VRML97 is to assume they
|
||||||
assume they do represent a solid, with the normals pointing outward. Unusual
|
do represent a solid, with the normals pointing outward. Unusual visual effects
|
||||||
visual effects ensue if the normals are not properly directed (basically, you
|
ensue if the normals are not properly directed (basically, you see through the
|
||||||
see through the "front" of the solid and see the "backs" of the faces on the
|
"front" of the solid and see the "backs" of the faces on the opposite side).
|
||||||
opposite side).
|
|
||||||
|
|
||||||
As a result, unless the input VRML 1 explicitly includes an explicit
|
As a result, unless the input VRML 1 explicitly includes an explicit
|
||||||
vertexOrdering of CLOCKWISE or COUNTERCLOCKWISE, the default solid treatment
|
vertexOrdering of CLOCKWISE or COUNTERCLOCKWISE, the default solid treatment
|
||||||
will be turned off in the VRML97 output, meaning that all faces will be
|
will be turned off in the VRML97 output, meaning that all faces will be
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
name: 'vrml1to97',
|
name: 'vrml1to97',
|
||||||
version: '0.4.0',
|
version: '0.2.3',
|
||||||
description: 'JavaScript converter from VRML 1 to VRML97',
|
description: 'JavaScript converter from VRML 1 to VRML97',
|
||||||
scripts: {
|
scripts: {
|
||||||
test: 'echo "Error: no test specified" && exit 1',
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
@ -40,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
type: 'module',
|
type: 'module',
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
'@danielx/civet': '^0.6.72',
|
'@danielx/civet': '^0.6.71',
|
||||||
'@types/moo': '^0.5.9',
|
'@types/moo': '^0.5.9',
|
||||||
'http-server': '^14.1.1',
|
'http-server': '^14.1.1',
|
||||||
json5: '^2.2.3',
|
json5: '^2.2.3',
|
||||||
|
@ -11,8 +11,8 @@ dependencies:
|
|||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@danielx/civet':
|
'@danielx/civet':
|
||||||
specifier: ^0.6.72
|
specifier: ^0.6.71
|
||||||
version: 0.6.72(typescript@5.3.3)
|
version: 0.6.71(typescript@5.3.3)
|
||||||
'@types/moo':
|
'@types/moo':
|
||||||
specifier: ^0.5.9
|
specifier: ^0.5.9
|
||||||
version: 0.5.9
|
version: 0.5.9
|
||||||
@ -35,8 +35,8 @@ packages:
|
|||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@danielx/civet@0.6.72(typescript@5.3.3):
|
/@danielx/civet@0.6.71(typescript@5.3.3):
|
||||||
resolution: {integrity: sha512-jumnIbXbdFs0ZiKN62fmD+p8QGi+E0jmtc02dKz9wIIoPkODsa4XXlBrS5BRR5fr3w5d3ah8Vq7gWt+DL9Wa0Q==}
|
resolution: {integrity: sha512-piOoHtJARe6YqRiXN02Ryb+nLU9JwU8TQLknvPwmlaNm6krdKN+X9dM+C9D4LRoAnAySC2wVshR0wf7YDIUV1Q==}
|
||||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
316
src/index.civet
316
src/index.civet
@ -2,7 +2,6 @@ moo from ../deps/moo.js
|
|||||||
import type {Lexer, Token} from ../deps/moo.d.ts
|
import type {Lexer, Token} from ../deps/moo.d.ts
|
||||||
|
|
||||||
type Tree = {[key:string]: (string | Tree)[]}
|
type Tree = {[key:string]: (string | Tree)[]}
|
||||||
type DefTree = {_definitions?: Tree} & Tree
|
|
||||||
|
|
||||||
lexer := moo.compile
|
lexer := moo.compile
|
||||||
comment: /#.*?$/
|
comment: /#.*?$/
|
||||||
@ -87,35 +86,100 @@ function addWorldParameter(name: string, value: string, tree: Tree): void
|
|||||||
children.push world
|
children.push world
|
||||||
world.WorldInfo.push(` ${name}`, ` ${value}`)
|
world.WorldInfo.push(` ${name}`, ` ${value}`)
|
||||||
|
|
||||||
// Current best way to re-use a pattern
|
function parse(stream: Lexer, tree: Tree = {}): Tree
|
||||||
matches := (str: string, pat: RegExp) => pat.test(str)
|
|
||||||
operator matches
|
|
||||||
GroupNode := /(?:Transform)?Separator|Group|Switch|WWWAnchor/
|
|
||||||
TransformNode := /^(?:Rotation|Scale|Transform|Translation)$/
|
|
||||||
SetNode := /IndexedFaceSet|IndexedLineSet|PointSet/
|
|
||||||
LightNode := /Light$/
|
|
||||||
|
|
||||||
function parse(stream: Lexer, tree: DefTree = {}): DefTree
|
|
||||||
held .= filtered stream // for lookahead
|
held .= filtered stream // for lookahead
|
||||||
currentDefinition .= '' // if we have just started a DEF
|
|
||||||
|
|
||||||
while next := filtered stream
|
while next := filtered stream
|
||||||
unless held then break
|
unless held then break
|
||||||
// May need to know the current definition, but it clears each round
|
|
||||||
lastDefinition := currentDefinition
|
|
||||||
currentDefinition = ''
|
|
||||||
|
|
||||||
switch data :=
|
switch data :=
|
||||||
{nt: next.type, nv: next.value, ht: held.type, hv: held.value}
|
{nt: next.type, nv: next.value, ht: held.type, hv: held.value}
|
||||||
{ht: 'word', hv: 'Info'}
|
{ht: 'word', nt: 'obrace'}
|
||||||
// Here the meaning is entirely dependent on what DEF we are in
|
switch held.value
|
||||||
if next.type !== 'obrace'
|
/(?:Transform)?Separator|Group|Switch|WWWAnchor/
|
||||||
// Not sure what this construct is, so ignore it
|
parent :=
|
||||||
held = next
|
held.value.endsWith('Separator') ? 'Transform' : 'Group'
|
||||||
continue
|
{children, ...context} := tree
|
||||||
|
subTree := parse stream, context
|
||||||
|
if newKids := subTree.children
|
||||||
|
addChild `${parent} { children [
|
||||||
|
${renderList newKids} ] }\n`, tree
|
||||||
|
'ShapeHints'
|
||||||
|
subTree := parse stream
|
||||||
|
if 'vertexOrdering' in subTree
|
||||||
|
tree.ccw = [
|
||||||
|
subTree.vertexOrdering[0] is 'ccw' ? 'true' : 'false']
|
||||||
|
if 'creaseAngle' in subTree
|
||||||
|
tree.creaseAngle = subTree.creaseAngle
|
||||||
|
'Coordinate3'
|
||||||
|
tree.Coordinate = toksUntilClose stream
|
||||||
|
'Normal'
|
||||||
|
tree.Normal = toksUntilClose stream
|
||||||
|
'TextureCoordinate2'
|
||||||
|
tree.TextureCoordinate = toksUntilClose stream
|
||||||
|
'Texture2'
|
||||||
|
tree.Texture = translatedToksUntilClose stream
|
||||||
|
'Material'
|
||||||
|
tree.Material = translatedToksUntilClose stream
|
||||||
|
'Cube'
|
||||||
|
dims := width: '', height: '', depth: ''
|
||||||
|
findNumbersAtTopLevel stream, dims
|
||||||
|
params := [`size ${dims.width} ${dims.height} ${dims.depth}`]
|
||||||
|
addShape 'Box', params, tree
|
||||||
|
'Cone'
|
||||||
|
dims := bottomRadius: '', height: ''
|
||||||
|
findNumbersAtTopLevel stream, dims
|
||||||
|
params := [`bottomRadius ${dims.bottomRadius}`,
|
||||||
|
`height ${dims.height}`]
|
||||||
|
addShape 'Cone', params, tree
|
||||||
|
'Cylinder'
|
||||||
|
dims := radius: '', height: ''
|
||||||
|
findNumbersAtTopLevel stream, dims
|
||||||
|
params := [`radius ${dims.radius} height ${dims.height}`]
|
||||||
|
addShape 'Cylinder', params, tree
|
||||||
|
'Sphere'
|
||||||
|
dims := radius: ''
|
||||||
|
findNumbersAtTopLevel stream, dims
|
||||||
|
addShape 'Sphere', [`radius ${dims.radius}`], tree
|
||||||
|
/IndexedFaceSet|IndexedLineSet|PointSet/
|
||||||
|
isFaces := held.value is 'IndexedFaceSet'
|
||||||
|
contents := translatedToksUntilClose stream
|
||||||
|
params := []
|
||||||
|
if 'Coordinate' in tree
|
||||||
|
params.push "coord Coordinate {\n",
|
||||||
|
...tree.Coordinate, " }\n"
|
||||||
|
if 'Normal' in tree
|
||||||
|
params.push "normal Normal {\n",
|
||||||
|
...tree.Normal, " }\n"
|
||||||
|
if isFaces and 'TextureCoordinate' in tree
|
||||||
|
params.push "texCoord TextureCoordinate {\n",
|
||||||
|
...tree.TextureCoordinate, " }\n"
|
||||||
|
if isFaces and 'creaseAngle' in tree
|
||||||
|
params.push `creaseAngle ${tree.creaseAngle[0]}`
|
||||||
|
if isFaces
|
||||||
|
if 'ccw' in tree then params.push `ccw ${tree.ccw[0]}`
|
||||||
|
else params.push 'solid false'
|
||||||
|
params.push ...contents
|
||||||
|
addShape held.value, params, tree
|
||||||
|
/Light$/
|
||||||
contents := toksUntilClose stream
|
contents := toksUntilClose stream
|
||||||
|
addChild {[held.value]: contents}, tree
|
||||||
|
else
|
||||||
|
parse stream // discard the subgroup
|
||||||
held = filtered stream
|
held = filtered stream
|
||||||
switch lastDefinition
|
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
||||||
|
tree.vertexOrdering = ['ccw']
|
||||||
|
held = filtered stream
|
||||||
|
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'CLOCKWISE'}
|
||||||
|
tree.vertexOrdering = ['cw']
|
||||||
|
held = filtered stream
|
||||||
|
{ht: 'word', hv: 'creaseAngle', nt: 'number'}
|
||||||
|
tree.creaseAngle = [ next.value ]
|
||||||
|
held = filtered stream
|
||||||
|
{ht: 'word', nt: 'word', nv: 'Info'}
|
||||||
|
opener := filtered stream
|
||||||
|
if opener and opener.type === 'obrace'
|
||||||
|
contents := toksUntilClose stream
|
||||||
|
switch held.value
|
||||||
'BackgroundColor'
|
'BackgroundColor'
|
||||||
// Find the first string token and assume it is the color
|
// Find the first string token and assume it is the color
|
||||||
stok := contents.find .type[0] === 'string'
|
stok := contents.find .type[0] === 'string'
|
||||||
@ -149,209 +213,13 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree
|
|||||||
|> .map((x) => x in tr ? tr[x] : x.toUpperCase())
|
|> .map((x) => x in tr ? tr[x] : x.toUpperCase())
|
||||||
|> .join ' '
|
|> .join ' '
|
||||||
addChild `NavigationInfo { type [ ${viewers} ] }\n`, tree
|
addChild `NavigationInfo { type [ ${viewers} ] }\n`, tree
|
||||||
{ht: 'word', hv: 'DEF'}
|
|
||||||
unless next.type is 'word'
|
|
||||||
// Don't understand this construction, ignore the DEF
|
|
||||||
held = next
|
|
||||||
continue
|
|
||||||
currentDefinition = next.value
|
|
||||||
held = filtered stream
|
held = filtered stream
|
||||||
// Special case from VRML 1: DEF Cameras Switch { ... }
|
else held = opener
|
||||||
// needs to be hoisted to a list of viewpoints at this level
|
|
||||||
if currentDefinition is 'Cameras' and held?.value is 'Switch'
|
|
||||||
held = filtered stream
|
|
||||||
unless held?.type is 'obrace'
|
|
||||||
console.error
|
|
||||||
`DEF Cameras Switch followed by ${held?.value}, ignoring`
|
|
||||||
continue
|
|
||||||
{children, ...context} := tree
|
|
||||||
subTree := parse stream, context
|
|
||||||
addChild renderList(subTree.children), tree
|
|
||||||
held = filtered stream
|
|
||||||
continue
|
|
||||||
if held?.type is 'word'
|
|
||||||
clause := `DEF ${currentDefinition}`
|
|
||||||
role .= held.value
|
|
||||||
switch role
|
|
||||||
matches GroupNode
|
|
||||||
role = 'Child'
|
|
||||||
matches TransformNode
|
|
||||||
role = 'Transform'
|
|
||||||
'Coordinate3'
|
|
||||||
role = 'Coordinate'
|
|
||||||
/Cube|Cone|Cylinder|Sphere/
|
|
||||||
role = 'Shape'
|
|
||||||
'PerspectiveCamera'
|
|
||||||
role = 'Viewpoint'
|
|
||||||
'ShapeHints'
|
|
||||||
role = 'Dummy' // will fill in when we parse the ShapeHints
|
|
||||||
'Texture2'
|
|
||||||
role = 'Texture'
|
|
||||||
'TextureCoordinate2'
|
|
||||||
role = 'TextureCoordinate'
|
|
||||||
matches SetNode
|
|
||||||
role = 'Shape'
|
|
||||||
matches LightNode
|
|
||||||
role = 'Child'
|
|
||||||
(tree._definitions ??= {})[currentDefinition] = [role]
|
|
||||||
unless role is 'Info'
|
|
||||||
// Info nodes don't directly generate a node in translation
|
|
||||||
addChild clause, tree
|
|
||||||
{ht: 'word', hv: 'USE'}
|
|
||||||
unless next.type is 'word'
|
|
||||||
// Don't understand this construction, ignore the USE
|
|
||||||
held = next
|
|
||||||
continue
|
|
||||||
known := tree._definitions
|
|
||||||
if known and next.value in known
|
|
||||||
role := known[next.value][0]
|
|
||||||
clause := `USE ${next.value}`
|
|
||||||
switch role
|
|
||||||
'Child'
|
|
||||||
addChild clause, tree
|
|
||||||
'Shape'
|
|
||||||
addShape clause, [], tree
|
|
||||||
'Transform'
|
|
||||||
// VRML97 doesn't allow just the transform part of
|
|
||||||
// a Transform to be USEd (the whole node must be), so
|
|
||||||
// we have to recreate the transform. FIXME: dedupe code
|
|
||||||
content := known[next.value][1..]
|
|
||||||
{children, ...context} := tree
|
|
||||||
restOfTree := parse stream, context
|
|
||||||
if remainingKids := restOfTree.children
|
|
||||||
newChild := `Transform {\n ${renderList content}\n `
|
|
||||||
+ `children [\n ${renderList remainingKids}`
|
|
||||||
+ "] }\n"
|
|
||||||
addChild newChild, tree
|
|
||||||
return tree
|
|
||||||
{}
|
|
||||||
mergeTree role, tree
|
|
||||||
else
|
else
|
||||||
tree[role] = [clause]
|
|
||||||
else
|
|
||||||
console.error 'USE of unDEFined identifier', next.value
|
|
||||||
held = filtered stream
|
|
||||||
{ht: 'word', nt: 'obrace'}
|
|
||||||
switch held.value
|
|
||||||
matches GroupNode
|
|
||||||
{children, ...context} := tree
|
|
||||||
subTree := parse stream, context
|
|
||||||
if newKids := subTree.children
|
|
||||||
newChild .= ''
|
|
||||||
if held.value is 'Switch'
|
|
||||||
newChild = "Switch {\n "
|
|
||||||
if 'whichChoice' in subTree
|
|
||||||
newChild += `whichChoice ${subTree.whichChoice[0]}\n `
|
|
||||||
newChild += `choice [\n ${renderList newKids} ] }\n`
|
|
||||||
else newChild =
|
|
||||||
`Group { children [\n ${renderList newKids} ] }\n`
|
|
||||||
addChild newChild, tree
|
|
||||||
matches TransformNode
|
|
||||||
content := toksUntilClose stream
|
|
||||||
if (lastDefinition
|
|
||||||
and tree._definitions?[lastDefinition][0] is 'Transform')
|
|
||||||
tree._definitions[lastDefinition].push ...content
|
|
||||||
{children, ...context} := tree
|
|
||||||
restOfTree := parse stream, context
|
|
||||||
if remainingKids := restOfTree.children
|
|
||||||
newChild := `Transform {\n ${renderList content}\n `
|
|
||||||
+ `children [\n ${renderList remainingKids} ] }\n`
|
|
||||||
addChild newChild, tree
|
|
||||||
return tree // used the rest of the tree, so done
|
|
||||||
'ShapeHints'
|
|
||||||
subTree := parse stream
|
|
||||||
hints: Tree := {}
|
|
||||||
if 'vertexOrdering' in subTree
|
|
||||||
hints.ccw = [
|
|
||||||
subTree.vertexOrdering[0] is 'ccw' ? 'TRUE' : 'FALSE']
|
|
||||||
if 'creaseAngle' in subTree
|
|
||||||
hints.creaseAngle = subTree.creaseAngle
|
|
||||||
mergeTree hints, tree
|
|
||||||
if lastDefinition and tree._definitions
|
|
||||||
tree._definitions[lastDefinition] = [hints]
|
|
||||||
'PerspectiveCamera'
|
|
||||||
contents := toksUntilClose stream
|
|
||||||
hasDescription := contents.find (e) =>
|
|
||||||
e.type?[0] is 'word' and e.value?[0] is 'description'
|
|
||||||
if lastDefinition and hasDescription is undefined
|
|
||||||
contents.push type: ['word'], value: ['description']
|
|
||||||
contents.push type: ['word'], value: [`"${lastDefinition}"`]
|
|
||||||
addChild {Viewpoint: contents}, tree
|
|
||||||
'Coordinate3'
|
|
||||||
tree.Coordinate = toksUntilClose stream
|
|
||||||
'Normal'
|
|
||||||
tree.Normal = toksUntilClose stream
|
|
||||||
'TextureCoordinate2'
|
|
||||||
tree.TextureCoordinate = toksUntilClose stream
|
|
||||||
'Texture2'
|
|
||||||
tree.Texture = translatedToksUntilClose stream
|
|
||||||
'Material'
|
|
||||||
tree.Material = translatedToksUntilClose stream
|
|
||||||
'Cube'
|
|
||||||
dims := width: '', height: '', depth: ''
|
|
||||||
findNumbersAtTopLevel stream, dims
|
|
||||||
params := [`size ${dims.width} ${dims.height} ${dims.depth}`]
|
|
||||||
addShape 'Box', params, tree
|
|
||||||
'Cone'
|
|
||||||
dims := bottomRadius: '', height: ''
|
|
||||||
findNumbersAtTopLevel stream, dims
|
|
||||||
params := [`bottomRadius ${dims.bottomRadius}`,
|
|
||||||
`height ${dims.height}`]
|
|
||||||
addShape 'Cone', params, tree
|
|
||||||
'Cylinder'
|
|
||||||
dims := radius: '', height: ''
|
|
||||||
findNumbersAtTopLevel stream, dims
|
|
||||||
params := [`radius ${dims.radius} height ${dims.height}`]
|
|
||||||
addShape 'Cylinder', params, tree
|
|
||||||
'Sphere'
|
|
||||||
dims := radius: ''
|
|
||||||
findNumbersAtTopLevel stream, dims
|
|
||||||
addShape 'Sphere', [`radius ${dims.radius}`], tree
|
|
||||||
matches SetNode
|
|
||||||
isFaces := held.value is 'IndexedFaceSet'
|
|
||||||
contents := translatedToksUntilClose stream
|
|
||||||
params := []
|
|
||||||
if 'Coordinate' in tree
|
|
||||||
params.push "coord Coordinate {\n",
|
|
||||||
...tree.Coordinate, " }\n"
|
|
||||||
if 'Normal' in tree
|
|
||||||
params.push "normal Normal {\n",
|
|
||||||
...tree.Normal, " }\n"
|
|
||||||
if isFaces and 'TextureCoordinate' in tree
|
|
||||||
params.push "texCoord TextureCoordinate {\n",
|
|
||||||
...tree.TextureCoordinate, " }\n"
|
|
||||||
if isFaces and 'creaseAngle' in tree
|
|
||||||
params.push `creaseAngle ${tree.creaseAngle[0]}`
|
|
||||||
if isFaces
|
|
||||||
if 'ccw' in tree then params.push `ccw ${tree.ccw[0]}`
|
|
||||||
else params.push 'solid FALSE'
|
|
||||||
params.push ...contents
|
|
||||||
addShape held.value, params, tree
|
|
||||||
matches LightNode
|
|
||||||
contents := toksUntilClose stream
|
|
||||||
addChild {[held.value]: contents}, tree
|
|
||||||
else
|
|
||||||
parse stream // discard the subgroup
|
|
||||||
held = filtered stream
|
|
||||||
{ht: 'word', hv: 'vertexOrdering', nt: 'word'}
|
|
||||||
switch next.value
|
|
||||||
'COUNTERCLOCKWISE'
|
|
||||||
tree.vertexOrdering = ['ccw']
|
|
||||||
'CLOCKWISE'
|
|
||||||
tree.vertexOrdering = ['cw']
|
|
||||||
held = filtered stream
|
|
||||||
{ht: 'word', hv: 'creaseAngle', nt: 'number'}
|
|
||||||
tree.creaseAngle = [ next.value ]
|
|
||||||
held = filtered stream
|
|
||||||
{ht: 'word', hv: 'whichChild'}
|
|
||||||
tree.whichChoice = [ next.value ]
|
|
||||||
held = filtered stream
|
|
||||||
else
|
|
||||||
console.error 'Ignoring unparseable token', held
|
|
||||||
held = next // ignore unknown words
|
held = next // ignore unknown words
|
||||||
if not held or held.type === 'cbrace' then break
|
if not held or held.type === 'cbrace' then break
|
||||||
if held and held.type !== 'cbrace'
|
if held and held.type !== 'cbrace'
|
||||||
console.error 'Incomplete parse, symbol at end:', held
|
console.log 'Oddly ended up with held', held
|
||||||
tree
|
tree
|
||||||
|
|
||||||
function addShape(nodeType: string, params: (string | Tree)[], tree: Tree): void
|
function addShape(nodeType: string, params: (string | Tree)[], tree: Tree): void
|
||||||
@ -365,15 +233,9 @@ function addShape(nodeType: string, params: (string | Tree)[], tree: Tree): void
|
|||||||
shape.Shape.push "texture ImageTexture {\n",
|
shape.Shape.push "texture ImageTexture {\n",
|
||||||
...tree.Texture, " }\n"
|
...tree.Texture, " }\n"
|
||||||
shape.Shape.push " }\n"
|
shape.Shape.push " }\n"
|
||||||
geometry: (string|Tree)[] := [`geometry ${nodeType}`]
|
shape.Shape.push `geometry ${nodeType} {\n`, ...params, " }\n"
|
||||||
if params.length then geometry.push " {\n", ...params, " }\n"
|
|
||||||
shape.Shape.push ...geometry
|
|
||||||
addChild shape, tree
|
addChild shape, tree
|
||||||
|
|
||||||
function mergeTree(subtree: Tree, tree: Tree): void
|
|
||||||
for key, value in subtree
|
|
||||||
tree[key] = value
|
|
||||||
|
|
||||||
function render(t: string | Tree): string
|
function render(t: string | Tree): string
|
||||||
if typeof t is 'string' then return t
|
if typeof t is 'string' then return t
|
||||||
if 'children' in t then return renderList t.children
|
if 'children' in t then return renderList t.children
|
||||||
|
Loading…
Reference in New Issue
Block a user