feat: Set faces one or two-sided, conservatively
Set VRML97 translation of IndexedFaceSets to specify `solid false` unless the faces were explicitly indicated to have CLOCKWISE or COUNTERCLOCKWISE vertex ordering. This is conservative, but otherwise shapes may be "see-through" in undesirable ways, if the original VRML 1 vertices are not ordered consistently with the VRML97 default. Also fixes a typo/bug in picking up numerical values of parameters, and ensures that fields in the output VRML97 are always separated by at least a space, comma, or parenthesis.
This commit is contained in:
parent
bb0ef18903
commit
92be417845
12
README.md
12
README.md
@ -62,3 +62,15 @@ Currently this package exports just two functions:
|
|||||||
than broken into node names and property values. If there is a need to
|
than broken into node names and property values. If there is a need to
|
||||||
generate XML syntax output, for example, the code would need to be
|
generate XML syntax output, for example, the code would need to be
|
||||||
modified to break the tree down further to be ready for conversion to XML.
|
modified to break the tree down further to be ready for conversion to XML.
|
||||||
|
|
||||||
|
## Conversion notes
|
||||||
|
|
||||||
|
One sort of geometry common to VRML 1 and VRML97 is the IndexedFaceSet. These
|
||||||
|
often used to render solids. Indeed, the default in VRML97 is to assume they
|
||||||
|
do represent a solid, with the normals pointing outward. Unusual visual effects
|
||||||
|
ensue if the normals are not properly directed (basically, you see through the
|
||||||
|
"front" of the solid and see the "backs" of the faces on the opposite side).
|
||||||
|
As a result, unless the input VRML 1 explicitly includes an explicit
|
||||||
|
vertexOrdering of CLOCKWISE or COUNTERCLOCKWISE, the default solid treatment
|
||||||
|
will be turned off in the VRML97 output, meaning that all faces will be
|
||||||
|
rendered opaque in both directions.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
name: 'vrml1to97',
|
name: 'vrml1to97',
|
||||||
version: '0.2.2',
|
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,11 +40,11 @@
|
|||||||
},
|
},
|
||||||
type: 'module',
|
type: 'module',
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
'@danielx/civet': '^0.6.38',
|
'@danielx/civet': '^0.6.71',
|
||||||
'@types/moo': '^0.5.6',
|
'@types/moo': '^0.5.9',
|
||||||
'http-server': '^14.1.1',
|
'http-server': '^14.1.1',
|
||||||
json5: '^2.2.3',
|
json5: '^2.2.3',
|
||||||
typescript: '^5.2.2',
|
typescript: '^5.3.3',
|
||||||
},
|
},
|
||||||
dependencies: {
|
dependencies: {
|
||||||
moo: '^0.5.2',
|
moo: '^0.5.2',
|
||||||
|
@ -11,11 +11,11 @@ dependencies:
|
|||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@danielx/civet':
|
'@danielx/civet':
|
||||||
specifier: ^0.6.38
|
specifier: ^0.6.71
|
||||||
version: 0.6.38(typescript@5.2.2)
|
version: 0.6.71(typescript@5.3.3)
|
||||||
'@types/moo':
|
'@types/moo':
|
||||||
specifier: ^0.5.6
|
specifier: ^0.5.9
|
||||||
version: 0.5.6
|
version: 0.5.9
|
||||||
http-server:
|
http-server:
|
||||||
specifier: ^14.1.1
|
specifier: ^14.1.1
|
||||||
version: 14.1.1
|
version: 14.1.1
|
||||||
@ -23,8 +23,8 @@ devDependencies:
|
|||||||
specifier: ^2.2.3
|
specifier: ^2.2.3
|
||||||
version: 2.2.3
|
version: 2.2.3
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.2.2
|
specifier: ^5.3.3
|
||||||
version: 5.2.2
|
version: 5.3.3
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@ -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.38(typescript@5.2.2):
|
/@danielx/civet@0.6.71(typescript@5.3.3):
|
||||||
resolution: {integrity: sha512-R63YGIfvV4DQianNPUfMfBX60ozlv5htnRXI1wK3Pg6+d4NZ2V3RifFRH0NkmXXoFkRcULzJ+9BzVeI2/yX+yA==}
|
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:
|
||||||
@ -44,8 +44,8 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@cspotcode/source-map-support': 0.8.1
|
'@cspotcode/source-map-support': 0.8.1
|
||||||
'@typescript/vfs': 1.5.0
|
'@typescript/vfs': 1.5.0
|
||||||
typescript: 5.2.2
|
typescript: 5.3.3
|
||||||
unplugin: 1.4.0
|
unplugin: 1.7.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
@ -66,8 +66,8 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/moo@0.5.6:
|
/@types/moo@0.5.9:
|
||||||
resolution: {integrity: sha512-Q60hZhulhl2Ox4LjbJvhH+HzsKrwzLPjEB8dZw0fK1MH2HyOLe6LDou68yTfsWasxGv7DPZe5VNM5vgpzOa2nw==}
|
resolution: {integrity: sha512-ZsFVecFi66jGQ6L41TonEaBhsIVeVftTz6iQKWTctzacHhzYHWvv9S0IyAJi4BhN7vb9qCQ3+kpStP2vbZqmDg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript/vfs@1.5.0:
|
/@typescript/vfs@1.5.0:
|
||||||
@ -78,8 +78,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/acorn@8.10.0:
|
/acorn@8.11.3:
|
||||||
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
|
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
@ -139,8 +139,8 @@ packages:
|
|||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chokidar@3.5.3:
|
/chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
@ -462,8 +462,8 @@ packages:
|
|||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/typescript@5.2.2:
|
/typescript@5.3.3:
|
||||||
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
|
resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
@ -475,13 +475,13 @@ packages:
|
|||||||
qs: 6.11.2
|
qs: 6.11.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/unplugin@1.4.0:
|
/unplugin@1.7.1:
|
||||||
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
|
resolution: {integrity: sha512-JqzORDAPxxs8ErLV4x+LL7bk5pk3YlcWqpSNsIkAZj972KzFZLClc/ekppahKkOczGkwIG6ElFgdOgOlK4tXZw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.10.0
|
acorn: 8.11.3
|
||||||
chokidar: 3.5.3
|
chokidar: 3.6.0
|
||||||
webpack-sources: 3.2.3
|
webpack-sources: 3.2.3
|
||||||
webpack-virtual-modules: 0.5.0
|
webpack-virtual-modules: 0.6.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/url-join@4.0.1:
|
/url-join@4.0.1:
|
||||||
@ -493,8 +493,8 @@ packages:
|
|||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/webpack-virtual-modules@0.5.0:
|
/webpack-virtual-modules@0.6.1:
|
||||||
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
|
resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/whatwg-encoding@2.0.0:
|
/whatwg-encoding@2.0.0:
|
||||||
|
@ -67,7 +67,7 @@ function findNumbersAtTopLevel(
|
|||||||
{type: 'cbrace'} depth -= 1
|
{type: 'cbrace'} depth -= 1
|
||||||
{type: 'word'} if depth === 1 and tok.value in fields
|
{type: 'word'} if depth === 1 and tok.value in fields
|
||||||
selector = tok.value
|
selector = tok.value
|
||||||
{tye: 'number'} if selecting then fields[selecting] = tok.value
|
{type: 'number'} if selecting then fields[selecting] = tok.value
|
||||||
{type: 'obrace'} depth += 1
|
{type: 'obrace'} depth += 1
|
||||||
selecting = selector
|
selecting = selector
|
||||||
|
|
||||||
@ -105,7 +105,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
${renderList newKids} ] }\n`, tree
|
${renderList newKids} ] }\n`, tree
|
||||||
'ShapeHints'
|
'ShapeHints'
|
||||||
subTree := parse stream
|
subTree := parse stream
|
||||||
if 'vertexOrdering' in subTree then tree.ccw = ['true']
|
if 'vertexOrdering' in subTree
|
||||||
|
tree.ccw = [
|
||||||
|
subTree.vertexOrdering[0] is 'ccw' ? 'true' : 'false']
|
||||||
if 'creaseAngle' in subTree
|
if 'creaseAngle' in subTree
|
||||||
tree.creaseAngle = subTree.creaseAngle
|
tree.creaseAngle = subTree.creaseAngle
|
||||||
'Coordinate3'
|
'Coordinate3'
|
||||||
@ -153,8 +155,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
...tree.TextureCoordinate, " }\n"
|
...tree.TextureCoordinate, " }\n"
|
||||||
if isFaces and 'creaseAngle' in tree
|
if isFaces and 'creaseAngle' in tree
|
||||||
params.push `creaseAngle ${tree.creaseAngle[0]}`
|
params.push `creaseAngle ${tree.creaseAngle[0]}`
|
||||||
if isFaces and 'ccw' in tree
|
if isFaces
|
||||||
params.push `ccw true`
|
if 'ccw' in tree then params.push `ccw ${tree.ccw[0]}`
|
||||||
|
else params.push 'solid false'
|
||||||
params.push ...contents
|
params.push ...contents
|
||||||
addShape held.value, params, tree
|
addShape held.value, params, tree
|
||||||
/Light$/
|
/Light$/
|
||||||
@ -166,6 +169,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
||||||
tree.vertexOrdering = ['ccw']
|
tree.vertexOrdering = ['ccw']
|
||||||
held = filtered stream
|
held = filtered stream
|
||||||
|
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'CLOCKWISE'}
|
||||||
|
tree.vertexOrdering = ['cw']
|
||||||
|
held = filtered stream
|
||||||
{ht: 'word', hv: 'creaseAngle', nt: 'number'}
|
{ht: 'word', hv: 'creaseAngle', nt: 'number'}
|
||||||
tree.creaseAngle = [ next.value ]
|
tree.creaseAngle = [ next.value ]
|
||||||
held = filtered stream
|
held = filtered stream
|
||||||
@ -236,7 +242,7 @@ function render(t: string | Tree): string
|
|||||||
if 'type' in t and 'value' in t
|
if 'type' in t and 'value' in t
|
||||||
val := renderList t.value
|
val := renderList t.value
|
||||||
return switch t.type[0]
|
return switch t.type[0]
|
||||||
/string|number|word/ ` ${val}`
|
/string|number|word/ val
|
||||||
/comma|oparen|cparen/ val
|
/comma|oparen|cparen/ val
|
||||||
/obrace|cbrace|obracket|cbracket/ `${val}\n`
|
/obrace|cbrace|obracket|cbracket/ `${val}\n`
|
||||||
else `\nUNKNOWN TYPE ${t.type}\n\n`
|
else `\nUNKNOWN TYPE ${t.type}\n\n`
|
||||||
@ -251,7 +257,10 @@ function renderList(l: (string | Tree)[]): string
|
|||||||
commaNewlineOnce .= false
|
commaNewlineOnce .= false
|
||||||
commaTriggersNewline .= false
|
commaTriggersNewline .= false
|
||||||
for each item of l
|
for each item of l
|
||||||
return.value += render item
|
next := render item
|
||||||
|
if return.value and not ',()'.includes next[0]
|
||||||
|
return.value += ' '
|
||||||
|
return.value += next
|
||||||
switch item
|
switch item
|
||||||
{type: ['word'], value: ['point']}
|
{type: ['word'], value: ['point']}
|
||||||
commaTriggersNewline = true
|
commaTriggersNewline = true
|
||||||
|
Loading…
Reference in New Issue
Block a user