Once you have something to see, you will probably want to control what you see. It should come as no surprise that this is all controlled by Rules.
Control over appearance is a large topic. The good news is that you don’t need to understand it all to get started; almost everything has a reasonable default. You only need to understand a topic when you want to change what the system is doing already.
Even whether some geometry appears or not is subject to built-in behavior that can be changed via Rule. Your first goal should be to understand the default behavior; after that, you will know what rule needs adjustment for your particular needs. When faced with a project defined by someone else, you should anticipate that they may have made changes to system defaults, and research accordingly before making your own changes.
Before you can understand this section, you need to understand the update process. See Updating in kBridge.
The rendering in KB is based on three.js, a powerful and ubiquitous graphics library which is very fast and capable of amazing image generation. KB does not directly expose most of three.js; it only presents the basics of materials and lighting and other sophisticated topics. But because of the rich foundation, KB can be extended to support nearly anything three.js can do.
Furthermore, the terminology used is (mostly) taken from three.js. A Camera is a three.js Camera, a Scene is a three.js scene, and so on.
However, there are several topics which have no simple parallel in three.js. The notion of drawing sheets is a completely KB concept built on top of three.js, and designed to be conceptually similar to AutoCAD or similar systems.
The KB kernel runs the rules on the server side. You see images in your browser (the “client” side). How does that happen?
Rules are always evaluated on the server. When you enter some source to be evaluated in the Immediate Pane, the source is collected into a temporary rule and sent to the server, where it is evaluated in the proper context (ensuring that “this” has the correct meaning) and the value is returned.
During the update process, special rules determine what needs to be sent to the client browser, and these are collected into an array of commands called “the galley.” At some point, but typically immediately after the update, the galley is requested by the KB client side. Once obtained, the commands in the galley are processed in order. These can generate new things on the client side, like geometry, or they can take actions, like delete old geometry or change the position of existing geometry.
The galley is one-way. The server has no idea whether the items in the galley were seen by the user, and has no idea what the user does with the data. Things like dragging and selecting are entirely client-side operations, which may or may not result in new Rules being created and sent to the server.
The server is also running three.js. We use three.js primitives for geometry. When you get a bounding box for an item, it was created and processed on the server, usually to feed other rules, so that rule-based decisions can be made.
Controlling whether something appears or not is the most fundamental graphic operation. But before you can show something, you have to have something to show, so we begin at existence. There are several ways to control whether something exists or not, but these are the primary ones:
Demand: If an object is never demanded, it will never exist. This is the most fundamental control over everything. Designs are never loaded, rules are never run, nothing is sent to the client if they are never demanded. Note, however, that you need to take action to prevent demand, since the default behavior of the update process is to traverse the tree and show everything.
Quantity: If the quantity is zero, the object exists, but as an instance of NullDesign.
NullDesign: If the Design evaluates to NullDesign, the object exists, but is a special null model which acts like it doesn’t exist.
Invisible: The special color “invisible” causes an object to be not included in rendering. The object does still exist. If you have a million invisible spheres, they will all be sent to the client. Thus, setting things to invisible is not good for performance.
The show parameter is the primary way to allow branches in the model tree to appear or not. This works with the renderNode and renderChildren rules to provide visibility control in a per-model way. Things which are not shown never get sent to the client, even though they exist on the server. Rules can be written which reference them, and because all rules evaluate on the server, everything still works.
Because a model is a “tree”, and it is possible that interior nodes (“assemblies”) can have their own geometry, it is necessary to allow the geometry of the node itself to be controlled. We can set the show rule to true, but set renderNode to false to do this. The default expression for renderNode is this.children.length === 0, which causes leaf nodes to be rendered, but interior nodes to be hidden. Note that this is any children, not just geometry.
Controlling entire branches is supported by the default expressions for show and renderChildren, which work together:
Show: this.nha.renderChildren
RenderChildren: this.show
By the above, show is true if its next-higher-assembly is allowing its children to be rendered, and renderChildren is just whatever show is. The effect is that show is always true, and renderChildren is always true, and the entire tree will be rendered. Because renderNode looks at leaf/non-leaf status, it will ensure that only leaves will be shown.
It is important to note that as soon as you break this chain of references, then things will go differently. If you set show to true, for example, it will no longer respect it’s nha’s renderChildren setting.
RenderNode does nothing unless show is true. RenderNode also does nothing if there is no geometry at that node, such as for a BaseAssembly-derived assembly.
Render styles are options for how to display an object once it has been determined that it should be shown. There are only five at present:
ShadedEdges The default, shows shaded faces with black edges. Shaded faces are subject to colors and lighting; edges are not. In ShadedEdges style, “wire” objects like Line, LineSegments, Arc, and Polyline respect their color parameter, but they are not shaded (darker when away from light) nor do they also have edges.
Shaded This is the same as ShadedEdges, except the edges are suppressed. This is sometimes a better style for certain objects, like STL models, which are intended to represent smooth contours but are in fact built with triangulated faces.
HiddenLine This style is like ShadedEdges except that the faces are all white and unshaded. This is intended for use in drawings or black-and-white PDFs.
HiddenLineDashed This style is like HiddenLine, except that the hidden edges are shown as dashed. This is the standard for engineering drawings.
Wireframe This shows only edges. All faces are removed.
Render styles are per scene, not set on individual objects. You cannot set a renderStyle on an object. In a drawing, different renderStyles can be set in different viewports. These override whatever style is set in the scene they depict.
Each object can reside on a named “layer.” Multiple layers can be turned on or off, and the objects on those layers are shown or not, respectively. Layers are primarily a way to control the visibility of certain objects in drawings. While they can be used to control the content of 3D scenes, it is important to note that layers do not control model existence, and have no rule effect. Generally speaking, show and its related rules renderNode and renderChildren should be used in “model” scenes, and layers should be used to control the visible content of Viewports.
Scenes are distinct, separate worlds. An object can be a member only of a single Scene. If two objects are in different scenes, they can never be seen at the same time by a single camera.
Scenes are used to create DrawingSheets. A DrawingSheet is always its own scene. It has a different coordinate system and usually different units. You place Viewports onto the drawing to see images from other scenes.
When you set a Color on an object, you are actually setting its material in three.js terms. A material is a large collection of properties that are controllable, such as how the object reflects light, how it lets light pass through (transparency), whether it is rough or smooth, and many other factors. The “base” color is only one of these properties. But in keeping traditional uses of color in CAD systems, we use a default base material which is sort of neutral in most properties (except transparency). The result is a sort of “plastic” appearance with the color you specify as the “diffuse” color.
We use the CSS color names for our known colors, and colors can also be specified using hex or RGB components. When you specify a color name, we automatically create a new material by that name with the diffuse color set to the CSS color, and all of the other properties taken from the default material. [https://www.w3schools.com/colors/colors_hex.asp]
When you create a new material, using the Material Design, you give it a colorName and this is what the material is stored under. This allows you to override the “system” colors with your own. This is especially useful if you are using materials that have names that overlap with colors. You can redefine the default “gold” color to be much more shiny, for example, to look more like the metal gold.
Like most named items, materials are defined in the RenderLibrary. This is a special model, which is automatically created as a child of the world, and is updated before your root model when the system starts up. In this way, any materials you place there (in your CustomRenderLibrary Design) will be created and available when your objects refer to them by name.
Texture-based materials are a large topic and have their own document. See Using Textures in KnowledgeBridge.
Shadow_Test project: A simple scene showing shadows. Default model scene (useDefaultLights=true), a single yellow Point light is positioned at upper right, with castShadow=true. The floor is white, other objects default gray.
Lights are physical, but not directly visible objects in the scene. Some lights have “helpers” which are visible geometry to assist in placement, but otherwise the only way to know a light is present is to see what effects it has on other objects in the scene.
You must have at least one light, or you will not see anything.
There are 5 supported light types:
LightType |
Description |
Shadows |
Ambient |
General “background” light. This doesn’t actually work like a light at all, it just ensures that all surfaces generate some color. It is usually used to ensure that areas not seen by other lights can be seen, and to generally control the brightness of the scene. |
N |
Point |
A point light source, like a single bulb. The light radiates in all directions. |
Y |
Spot |
A directed conical light. This light can focus on a particular spot, so it is useful when trying to mimic actual room lighting. It supports a number of options to control its unique aspects. |
Y |
Directional |
A light with parallel rays, like the sun. Directional lights have no actual location, just light direction. It does not matter if they are “behind” or even inside your geometry. |
Y |
Hemisphere |
A light source that varies from a “sky” color to a “ground” color. This can provide a more natural outdoor lighting to objects. [Not Yet Implemented] |
N |
The default “model” scene, and any scene that is automatically created (such as when you supply an unknown sceneName to an object), comes with “default lights”, which are 3 lights designed to support general use. The Scene parameter useDefaultLights can be set to false to turn these off (actually they are not even created). The default lights include an ambient light, a directional light from p<100,140,130>, and another from p<-100,-140,-130>, both aimed at the world origin. Note that because of the way directional lights work, it doesn’t matter whether they are inside geometry or not.
Three.js supports a large number of options on each type of light, and these would bloat the Light Design if a rule was created for each one. Instead, a single options parameter is used to supply values for particular properties.
For lists of options which can be applied, see the three.js documentation.
Shadows are castable by Point, Spot, and Directional lights. Shadow casting is off by default on all lights. Objects can both cast shadows on other objects, and/or receive shadows cast by other objects. Both are enabled by default for all “solid” objects, and are disabled for wire or reference objects (like Plane).
Shadows only work in shaded render styles. This is because wireframes don’t cast shadows, and the hidden line styles force all faces to be white.
NOTE: Shadows are relatively expensive to compute. They will affect the frame rate of any viewer interactions. Each light casting shadows requires a full render pass of the entire scene, so the cost is a function of how many shadow-casting lights there are times the number of shadow-casting and receiving objects there are in the scene.
In order to see shadows, there must be at least one light casting them, and objects able to receive them.