diff --git a/README.md b/README.md index 95fc666..41f85cd 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ 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, diff --git a/package.json5 b/package.json5 index 4683d9c..c99d3c5 100644 --- a/package.json5 +++ b/package.json5 @@ -1,6 +1,6 @@ { name: 'vrml1to97', - version: '0.3.2', + version: '0.4.0', description: 'JavaScript converter from VRML 1 to VRML97', scripts: { test: 'echo "Error: no test specified" && exit 1', diff --git a/src/index.civet b/src/index.civet index 8b842f8..1fa2c45 100644 --- a/src/index.civet +++ b/src/index.civet @@ -156,6 +156,19 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree continue currentDefinition = next.value held = filtered stream + // Special case from VRML 1: DEF Cameras Switch { ... } + // 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 @@ -164,16 +177,18 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree role = 'Child' matches TransformNode role = 'Transform' - 'ShapeHints' - role = 'Dummy' // will fill in when we parse the ShapeHints 'Coordinate3' role = 'Coordinate' - 'TextureCoordinate2' - role = 'TextureCoordinate' - 'Texture2' - role = 'Texture' /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 @@ -222,7 +237,13 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree {children, ...context} := tree subTree := parse stream, context if newKids := subTree.children - newChild := + 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 @@ -248,6 +269,14 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree 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' @@ -314,6 +343,9 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree {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