Custom Render Styles

Is it possible to customize the render styles without editing the shader code directly to combine a number of things like:

  • silhouette edges on / off
  • edges on / off
  • normals (flat or smooth) on / off
  • override mesh color with a single color, like all white
  • hidden line on / off

Yep, Render modes are actually achieved by configuring materials and render settings. You can see how all the existing render modes are implemented here:

silhouette edges on / off
To add/remove outlines you can change the outlineThickness. Setting it to zero, to disable the silhouette edges

renderer.outlineThickness = 1

edges on / of
The edges are just a collection of Lines geometries. So we show/hide them by iterating over all the lines and hiding them.
Note: just a recap on the architecture. A GeomItem holds a Material and a Geometry and is transformed using its GlobalXfo. A LinesProxy is a lines that was loaded from zcad file. It cannot be edited. Lines are usually procedural geometries that are generated in the browser.

To hide all the lines for a given RenderMode, just traverse the tree and hide each lines and lines proxy.

    assets.traverse((item) => {
      if (item instanceof GeomItem) {
        const geom = item.getParameter('Geometry').getValue()
        if (geom instanceof Lines || geom instanceof LinesProxy) {
          item.getParameter('Visible').setValue(false)
        }
      }
    })

normals (flat or smooth) on / off

To make something ‘flat’, e.g. not shaded, we assign the ‘FlatSurfaceShader’ which is a super simple shader that doesn’t apply lighting.

To make something ‘smooth’, we assign the ‘SimpleSurfaceShader’ which performs basic lighting.
To make something ‘lit’, we assign the ‘StandartSurfaceShader’ which performs PBR lighting.

Note: the method ‘createAndAssignMaterial’ simply tries to see if the material that was assigned to this item was already cloned. Its an optimization to avoid creating thousands of materials when the source data contained a couple hundred.

    assets.traverse((item) => {
      if (item instanceof GeomItem) {
        if (geom instanceof Mesh || geom instanceof MeshProxy) {
          createAndAssignMaterial(
            item,
            RENDER_MODES.SHADED_AND_EDGES,
            (newMaterial) => {
              newMaterial.setName('ShadedAndEdges')
              newMaterial.setShaderName('SimpleSurfaceShader')
            }
          )
        }
      }
    })

** hidden line on / off**

The LinesShader can now render occluded lines, and has separate settings for stippling for occluded and unoccluded.

Setting the stipple value to 0.0 means the line is solid, a stipple value of 1.0 makes it disappear. A value of 0.6 means the dash values make up 40% of the line and the gaps are 60%

  material.getParameter('StippleScale').setValue(0.02)
  material.getParameter('StippleValue').setValue(0)
  material.getParameter('OccludedStippleValue').setValue(0.6)

Setting up a new render style.

Lets say we want a new ‘flat’ mode, and surfaces are all white. Lets call it FlatWhite (like the coffee).

This is a simple render mode, because all the surfaces will get the same material.

const handleChangeRenderModeFlat = () => {
    if (mode == RENDER_MODES.FLAT_WHITE) {
      return
    }
    mode = RENDER_MODES.FLAT_WHITE
    const { assets, scene, renderer } = $APP_DATA
    renderer.outlineThickness = 1
    renderer.outlineColor = new Color(0.2, 0.2, 0.2, 1)
    const whiteMaterial = new Material()
    // make it flat 
    whiteMaterial.setShaderName('FlatSurfaceShader')
    // make it white
    whiteMaterial.getParameter('BaseColor').setValue(new Color(1, 1, 1, 1))

    assets.traverse((item) => {
      if (item instanceof GeomItem) {
        const geom = item.getParameter('Geometry').getValue()
        if (geom instanceof Mesh || geom instanceof MeshProxy) {
          item.getParameter('Visible').setValue(true)
          item.getParameter('Material').setValue(whiteMaterial)
        }
      }
    })
    mode = RENDER_MODES.FLAT_WHITE
  }

Now all you need to do then is add the icon to the toolbar and connect it up. This step is just copy and paste.

Here is the SVG for the hidden line icon. (this was created using the cube shape in Google drawings.
You can import this into a vector drawing tool and edit the colors.

<svg version="1.1" viewBox="0.0 0.0 108.9238845144357 108.94750656167979" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l108.92388 0l0 108.94751l-108.92388 0l0 -108.94751z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l108.92388 0l0 108.94751l-108.92388 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m30.911938 7.158131l0 69.5748" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" d="m30.911938 7.158131l0 69.5748" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m6.6742845 31.222435l69.35433 0l0 69.35433l-69.35433 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m76.02862 31.222435l23.11811 -23.11811l0 69.35433l-23.11811 23.11811z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m6.6742845 31.222435l23.11811 -23.11811l69.35433 0l-23.11811 23.11811z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m6.6742845 31.222435l23.11811 -23.11811l69.35433 0l0 69.35433l-23.11811 23.11811l-69.35433 0zm0 0l69.35433 0l23.11811 -23.11811m-23.11811 23.11811l0 69.35433" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.2" d="m76.02862 31.222435l23.11811 -23.11811l0 69.35433l-23.11811 23.11811z" fill-rule="evenodd"/><path fill="#ffffff" fill-opacity="0.2" d="m6.6742845 31.222435l23.11811 -23.11811l69.35433 0l-23.11811 23.11811z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="4.0" stroke-linejoin="round" stroke-linecap="butt" d="m6.6742845 31.222435l23.11811 -23.11811l69.35433 0l0 69.35433l-23.11811 23.11811l-69.35433 0zm0 0l69.35433 0l23.11811 -23.11811m-23.11811 23.11811l0 69.35433" fill-rule="evenodd"/><path fill="#f9ce03" d="m7.8129287 32.24648l67.18111 0l0 67.40158l-67.18111 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m7.8129287 32.24648l67.18111 0l0 67.40158l-67.18111 0z" fill-rule="evenodd"/><path fill="#f9ce03" d="m28.990873 7.5595646l-22.749775 22.97511l68.47466 0.45067215l23.501457 -23.599335z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.990873 7.5595646l-22.749775 22.97511l68.47466 0.45067215l23.501457 -23.599335z" fill-rule="evenodd"/><path fill="#f9ce03" d="m75.39114 31.120354l0.42572784 69.36592l23.830978 -23.618652l0 -69.15306z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" d="m75.39114 31.120354l0.42572784 69.36592l23.830978 -23.618652l0 -69.15306z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m7.3956966 101.59434l25.417322 -25.41732" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="12.0,9.0" d="m7.3956966 101.59434l25.417322 -25.41732" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m99.86645 77.66052l-68.125984 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="12.0,9.0" d="m99.86645 77.66052l-68.125984 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m30.447966 8.738557l0 69.13386" fill-rule="evenodd"/><path stroke="#000000" stroke-width="3.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="12.0,9.0" d="m30.447966 8.738557l0 69.13386" fill-rule="evenodd"/></g></svg>

And here that svg is integrated into a Svelte component. Just copy paste the SVG code.

Don’t worry if this seems a bit more than you were expecting. We will implement the various modes you have requested. I just wanted to show you the process we will take.