Difference between revisions of "Virtual Calit2 Building"
(→Working Elevators) |
(→Realistic Environment) |
||
(58 intermediate revisions by one user not shown) | |||
Line 6: | Line 6: | ||
[http://www.calit2.net/newsroom/release.php?id=1352 In the news: Calit2 Collaborates with Architects and UC San Diego Neuroscientists to Study How 'Way-Finding' Affects the Brain] | [http://www.calit2.net/newsroom/release.php?id=1352 In the news: Calit2 Collaborates with Architects and UC San Diego Neuroscientists to Study How 'Way-Finding' Affects the Brain] | ||
+ | |||
+ | [http://www.calit2.net/newsroom/article.php?id=1377 In the news: Take 'Audio-Visual' Research to the Next Level] | ||
==Contributors== | ==Contributors== | ||
Line 15: | Line 17: | ||
[http://ivl.calit2.net/wiki/index.php/User:Vhuynh Vinh Huynh]. | [http://ivl.calit2.net/wiki/index.php/User:Vhuynh Vinh Huynh]. | ||
− | [http://ivl.calit2.net/wiki/index.php/Mabel_Zhang Mabel Zhang] | + | [http://ivl.calit2.net/wiki/index.php/Mabel_Zhang Mabel Zhang] completed adding floors 3-6; completed the stairs; added the first set of interactive dynamic elements (automatic doors, working elevators, indoor light, 24-hour sunlight animation, sunlight according to system clock) to make the model more realistic; optimized the model (by adding MultiRes modifier in Max, shrinking the textures, adding the DEFs/USEs for ImageTextures, eliminating continuously generated polygons, recreating instances from copies, etc.); added the metaloader to selectively turn on/off building floors or layers; and divided the copy of the building into individual files for more object-oriented organization. |
==General Structure== | ==General Structure== | ||
Line 48: | Line 50: | ||
* Light switch; include code for light object's on and off controlled by 2 separate switches; physical objects are Max-exported, logic is manual VRML script with JavaScript. | * Light switch; include code for light object's on and off controlled by 2 separate switches; physical objects are Max-exported, logic is manual VRML script with JavaScript. | ||
+ | |||
+ | * Sunlight animation and position according to system clock; include code for sun object rotating around hemisphere; physical objects are Max-exported, logic is manual VRML script with JavaScript | ||
+ | |||
+ | |||
* (Elevator physical objects; not directly required for run-time, but contains elevator objects exported from Max; the entire content of this file is pasted into the Elevator prototype file in order for the logic to link to objects.) | * (Elevator physical objects; not directly required for run-time, but contains elevator objects exported from Max; the entire content of this file is pasted into the Elevator prototype file in order for the logic to link to objects.) | ||
− | * (Light switch physical objects; not directly required for run-time, but contains light objects exported from Max; the entire content of this file is pasted into the Light switch file in order for | + | * (Light switch physical objects; not directly required for run-time, but contains indoor light objects exported from Max; the entire content of this file is pasted into the Light switch file in order for logic to link to objects.) |
− | * (A | + | * (Sunlight physical objects; not directly required for run-time, but contains sunlight objects exported from Max; the entire content of this file is pasted into Sunlight file in order for logic to link to objects.) |
+ | |||
+ | |||
+ | |||
+ | * (A README file prepared for future maintenance; explains most, if not all, of reasons and the necessary changes that need to be made to a directly Max-exported file for all the parts to run correctly.) | ||
==Modeling in Autodesk 3ds Max== | ==Modeling in Autodesk 3ds Max== | ||
Line 61: | Line 71: | ||
Because the final version of the model is in VRML format, during the modeling process in 3ds Max, the most important thing to keep in mind is that not all effects will export to VRML. 3ds Max comes with a detailed user reference that includes some useful tips and exporting rules on VRML. Not all materials are exported, not all lights are exported, different geometry types export to files of significantly different sizes, etc. | Because the final version of the model is in VRML format, during the modeling process in 3ds Max, the most important thing to keep in mind is that not all effects will export to VRML. 3ds Max comes with a detailed user reference that includes some useful tips and exporting rules on VRML. Not all materials are exported, not all lights are exported, different geometry types export to files of significantly different sizes, etc. | ||
− | === | + | ===Manual Work After Export=== |
− | + | ||
− | + | ||
− | + | ||
+ | From many websites focusing on projects using 3ds Max + VRML, it is suggested that the Max-exported VRML file should be checked and edited manually for the final version. For a static model, this is quite simple; the most often hand operation would probably be commenting out automatically generated looping timers and interpolations, which will continuously generate polygons during run-time. However, for a model with logic scripts, the exported code must be combined with the manual script into a single file for the effect to work; simply inlining the exported object file or the script will not suffice, because VRML requires that the script and the objects be in the same file for the linking to take effect, otherwise syntax errors will result. This means that either the exported file contents will need to be pasted into the script file, or vise versa, so a clear structure and documentation is even more important. This is the reason that we placed the script and the objects in the same file, instead of separating them for easy maintenance. | ||
− | . | + | For more manual modifications required, see relevant sections below. |
==Dynamic Elements== | ==Dynamic Elements== | ||
Line 100: | Line 108: | ||
Now the door will open when the user approaches it and close when the user leaves it. | Now the door will open when the user approaches it and close when the user leaves it. | ||
+ | ===Working Elevators=== | ||
+ | We used the German Mailand Mercedes car dealership model's elevator core code as a reference and built our own set of code to fit the Calit2 elevators. | ||
+ | The elevator has 2 parts, an intializer and a user-defined PROTOtype file. (PROTO is a keyword in VRML, stands for PROTOtype, and just means a user-defined data type.) The PROTO file defines one set of elevator, including its physical objects and all necessary scripts. The initializer creates 3 instances of this PROTOtype at the corresponding locations of the 3 main elevators in the building. | ||
+ | The length of the the core PROTO script, including the documentation, is more than 1000 lines, not including the exported objects, so it might be inconvenient to scroll through them here. However, detail documentation is included in the actual files. | ||
− | ....will | + | A drawback of the current approach is that it cannot simulate the exact logic of the elevators in the real building. The virtual elevators currently operate independently, while the real ones are dependent. For example, in reality, there are 2 sets of buttons to call the elevator, and when a user calls the elevator from a floor, whichever elevator cab that is the most "convenient" will come to the user; in the virtual building, there are 3 sets of call buttons, and each elevator responds only to the set of buttons linked to it, meaning that the user controls whichever elevator will come by pressing on the corresponding set of buttons. We took the current approach because it involves less logic and linking and is less time-consuming; it only requires 1 set of definitions that can be initialized 3 times to generate 3 elevators. However, simulating the realistic logic will make the model more intelligent and believable. |
− | |||
− | |||
− | + | ===Light Effects=== | |
− | + | VRML has three types of lights built in: | |
+ | * PointLight | ||
+ | * DirectionalLight | ||
+ | * Spotlight | ||
+ | Each one corresponds to these 3ds Max lights, respectively: | ||
+ | * Omni | ||
+ | * Free/Target Direct | ||
+ | * Free/Target Spot | ||
+ | where the target lights can be used to help define the rotation of the light objects, but the automatically generated loop TRUE timer might need to be commented out. See the "Automatically Generated INTERPs and TIMERs" section below. | ||
− | + | See the [http://www.lighthouse3d.com/vrml/tutorial/index.shtml?light Lighthouse 3D description on lighting nodes] for detailed information about VRML lights. | |
− | |||
− | .. | + | VRML does not support real shadows. To work around it, we can precalculate the texture maps and save them to the maps directory, then apply the textures to the objects in Max. |
+ | Because no shadows are calculated during run-time, don't be surprised if a light leaks all throughout the world. Adjust the attenuation by hand if needed so that the light leak is not as obvious. Sometimes turning on the headlight can help reduce the effect. | ||
+ | |||
+ | |||
+ | Like in Max, VRML browsers have a headlight on by default, if no lights are present in the scene. However, as soon as one light is added into the scene, the headlight is automatically turned off at startup. We can set the headlight to be on by manually editing the exported code: | ||
+ | |||
+ | Original code: | ||
+ | |||
+ | <pre> | ||
+ | # This code is automatically exported from 3ds Max when a light is present in the scene | ||
+ | NavigationInfo { headlight FALSE } | ||
+ | </pre> | ||
+ | |||
+ | Change to: | ||
+ | |||
+ | <pre> | ||
+ | # To turn on headlight at world startup | ||
+ | NavigationInfo { headlight TRUE } | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | |||
+ | ====Indoor Lights==== | ||
+ | |||
+ | These are usually simulated using point lights (Omni in Max) because a spotlight only lights up a cone-shape area, and a directional light is located far far away from the world and therefore lights up everything in the direction it faces, which is usually not the desired result of an indoor light. | ||
+ | |||
+ | Point light fields taken from the above website, see [http://www.lighthouse3d.com/vrml/tutorial/index.shtml?plight here] for detailed information about point lights: | ||
+ | |||
+ | <pre> | ||
+ | PointLight { | ||
+ | on TRUE | ||
+ | intensity 1 | ||
+ | ambientIntensity 0 | ||
+ | color 1 1 1 | ||
+ | location 0 0 0 | ||
+ | attenuation 1 0 0 | ||
+ | radius 100 | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Attenuation: | ||
+ | |||
+ | Only "Far Attenuation" defined in 3ds Max is exported. "Near Attenuation" is not. Even so, Max does not export the correct far attenuation either; the values will need to be changed by hand after export. | ||
+ | See "Misc. Debugging Ideas and Tricky Surprises" > "Light Attenuation" section below for manually adjusting the attenuation. | ||
+ | |||
+ | Radius: | ||
+ | |||
+ | The radius field does not seem to have an effect in OpenCOVER. We tried radii ranging from 5 to 500, and the effect always came out to be the same. Thus we only adjusted the attenuation field to generate lighting effect close to reality. | ||
+ | |||
+ | |||
+ | |||
+ | ====Sunlight==== | ||
+ | |||
+ | While the 3ds Max Daylight system, which includes a sunlight and a skylight object, seems perfect for the goal, it does not export to VRML with the light effects. Therefore to simulate sunlight, the options are directional or point lights. | ||
+ | |||
+ | We used a point light for the sun because it was much easier to animate than a directional light; although the animation process are the same for both in Max, they are different in VRML. VRML light objects are exported in a hierarchy with the root parent being a group name to hold the animation time sensor, position interpolator, rotation interpolator, and the actual light source (e.g. the DirectionalLight node). When an animation is created for a directional light, the animation time sensor routes to the root parent's position/rotation interpolater's key, which will set the corresponding keyValue, and this affects all its children's position. The problem is, VRML directional lights do not have a position/location field but instead, a direction field. Thus when the position of the root parent changes, although the light source's position also changes, the direction field does not. This results in no change in light direction. For the light direction to be changed, we need to manually route the time sensor to the direction field of the light source, not the position/rotation interpolator keyValue of the parent. So the routing would go from the time sensor's fraction_changed event to some 3-value vector, which then routes to the light source's direction field. The most convenient 3-value vector is the position interpolator's keyValue, which will need to be defined to be exactly the same as the values we want the direction field to have. It can be defined this way by moving the light object in max, or by writting the vector array manually. For our purpose, we needed the sun to rotate around the hemisphere, so the light direction will have both positive and negative values in its vector. This meant moving the sun object far, far away from our hemisphere which is not around the origin of the world, otherwise writing the coordinates manually. Therefore, we chose the point light approach, which does not require any manual effort to animate a 24-hour sun rotation around the hemisphere. | ||
+ | |||
+ | |||
+ | We implemented two features for the sunlight: | ||
+ | * 24-hour sunlight animation at the press of a button (8 seconds animation) | ||
+ | * Sunlight effect according to the current system clock | ||
+ | |||
+ | |||
+ | The 24-hour animation can be entirely done in Max. We used a circular spline to define the sun's path at 60 degrees to the grass plane, and set a path constraint on the omni sun object to follow the circular path in 240 frames, equivalent to 8 seconds in real time. | ||
+ | |||
+ | |||
+ | The system clock function requires some JavaScript to be inserted into the exported VRML file. It uses the JavaScript "date" object to create an instance of it, and uses the objects getHours() member function to obtain the system clock hour, and then move the omni sun object to its appropriate place in the sky. Other member functions can be used to obtain the weekday, date, minutes, etc., but we only used the hour for the sun's approximate position. | ||
+ | |||
+ | To move the sun to its appropriate position, we made a correspondance table for the hour, frame, and the key array in the exported sun object's position interpolator field. An if-statement or switch-statement can be used to check the current system hour returned by the date object instance, then assign the corresponding key value to an SFFloat type eventOut, which will be set to route to the sun's position interpolator to translate the sun to its position specified in the keyValue array. Each value in the key array automatically corresponds to the value with the same index in the keyValue array within the sun object's definition. | ||
+ | |||
+ | There are detailed information in the README file and the text files placed with the actual model, but here is the current correspondance table for the sun object mentioned above, it may change as the sun's animation frames are changed in Max: | ||
+ | |||
+ | <pre> | ||
+ | For the following table, | ||
+ | hour = hour in the day; | ||
+ | frame = hour * 10 as set in Max when doing animation; | ||
+ | frmNw = multples of 3 closest to frame (because each keyValue (aka. sun's position) stays for 3 frames); | ||
+ | thkey = corresponding #th key in code, thkey = frmNw / 3; | ||
+ | key = corresponding keyValue[thkey-1] in code. | ||
+ | |||
+ | |||
+ | hour frame frmNw thkey key | ||
+ | 0 0 0 1st 0 | ||
+ | 1 40 39 13th 0.0375 | ||
+ | 2 80 81 27 0.08125 | ||
+ | 3 120 120 40 0.121875 | ||
+ | 4 160 159 53 0.1625 | ||
+ | 5 200 201 67 0.20625 | ||
+ | 6 240 240 80 0.246875 | ||
+ | 7 280 279 93 0.2875 | ||
+ | 8 320 321 107 0.33125 | ||
+ | 9 360 360 120 0.371875 | ||
+ | 10 400 399 133 0.4125 | ||
+ | 11 440 441 147 0.45625 | ||
+ | 12 480 480 160 0.496875 | ||
+ | 13 520 519 173 0.5375 | ||
+ | 14 560 561 187 0.58125 | ||
+ | 15 600 600 200 0.621875 | ||
+ | 16 640 639 213 0.6625 | ||
+ | 17 680 681 227 0.70625 | ||
+ | 18 720 720 240 0.746875 | ||
+ | 19 760 759 253 0.7875 | ||
+ | 20 800 801 267 0.83125 | ||
+ | 21 840 840 280 0.871875 | ||
+ | 22 880 879 293 0.9125 | ||
+ | 23 920 921 307 0.95625 | ||
+ | (24 960 960 320) 0.996875 | ||
+ | </pre> | ||
==VRML Scripting + JavaScript in General== | ==VRML Scripting + JavaScript in General== | ||
Line 169: | Line 293: | ||
* Use VRML style commenting (# single-line) anywhere in the file except in the inlined url JavaScript section - use JavaScript style commenting (// single-line, /* */ multi-line). | * Use VRML style commenting (# single-line) anywhere in the file except in the inlined url JavaScript section - use JavaScript style commenting (// single-line, /* */ multi-line). | ||
* If there are hand-written scripts, use a VRML text editor to find syntax or symmantic errors before running the wrl file in a browser. There aren't many VRML editors, but a good one to use is [http://www.softempire.com/vrmlpad-v-2-0.html VrmlPad]. It's a shareware and is not too useful when need to save large files (the shared version only allows 64K max), but it's very handy in finding syntax or symmantic errors. VrmlPad's status bar shows the type of present errors in the lower right corner in highlighted background colors; double-click on the highlighted error type, and it will take you to the next line with the chosen error, which will be dot-underlined in color. There is also a "Next Error" button on the toolbar, the icon is a hammer with a curved arrow. | * If there are hand-written scripts, use a VRML text editor to find syntax or symmantic errors before running the wrl file in a browser. There aren't many VRML editors, but a good one to use is [http://www.softempire.com/vrmlpad-v-2-0.html VrmlPad]. It's a shareware and is not too useful when need to save large files (the shared version only allows 64K max), but it's very handy in finding syntax or symmantic errors. VrmlPad's status bar shows the type of present errors in the lower right corner in highlighted background colors; double-click on the highlighted error type, and it will take you to the next line with the chosen error, which will be dot-underlined in color. There is also a "Next Error" button on the toolbar, the icon is a hammer with a curved arrow. | ||
− | |||
− | |||
− | |||
− | |||
==Optimization== | ==Optimization== | ||
Line 179: | Line 299: | ||
Our main approach was to reduce the polygon count, to shrink texture file size, to reuse textures, and some other more minor methods. | Our main approach was to reduce the polygon count, to shrink texture file size, to reuse textures, and some other more minor methods. | ||
+ | |||
+ | |||
+ | |||
+ | ===Recreating meshes as primitives when possible=== | ||
+ | |||
+ | VRML defines meshes by listing the coordinates for each vertex, which can be in terms of thousands when the mesh is even a bit complicated. On the other hand, it defines primitives by a built-in node type like Box and defines its few fields. According to the 3ds Max User Reference, spheres, cylinders, and cones also export as primitives. There might be others. | ||
+ | |||
+ | Thus, creating primitives instead of meshes can reduce file size. A primitive can be converted to a mesh at two mouse-clicks, but converting backwards is not as easy. We remade some cylinders and boxes that were originally meshes as primitives so that the exported file is much smaller. Making identical objects as "instances" of these new primitives will reduce file size even more. See the instances section. | ||
+ | |||
+ | |||
+ | |||
+ | ===(Re)Making identical objects as instances=== | ||
+ | |||
+ | This reduces file size by telling 3ds Max to automatically use DEFs/USEs keywords for geometry's -FACES and other nodes. | ||
+ | |||
+ | The exporter uses DEF for the first appearance of the object (the master) to assign it a name, then uses the USE keyword for all subsequent appearances of the object to reference to the master and to use its properties. | ||
+ | |||
+ | This saves the code to redefine the same shape every time and shrinks down the code to only 1 line. | ||
Line 198: | Line 336: | ||
− | |||
+ | ===Shrinking texture map image file sizes=== | ||
− | .. | + | Texture map files occupy memory space and render time. We reduced a large amount of texture files from 512 * 512 to 256 * 256, which is four times smaller in terms of area. This reduced the size of the maps directory by half (from about 120 MB to about 57 MB), the texture memory, and thus sped up loading and rendering time. |
+ | A tip from [http://www.vrmlworlds.com/developer/tutorials/3ds_max/3dsmax_optomize.html VRMLworlds.com] is to make map resolutions powers of 2, e.g. 512*512, 256*256, etc. "Doing this makes the textures look better in the vrml world, and also helps the scene render a little faster or use less video memory." | ||
− | |||
+ | We mostly used TIF or PNG formats because of their preservation of image quality. PNG does have the advantage over TIF to preserve full image quality at a smaller file size, but TIF is more commonly used for professional and archival purposes. Most VRML articles recommend JPEG, GIF, and PNG formats because VRML worlds are commonly used for web applications and prefers a smaller downloading time. We use the TIF format instead because the virtual building is not meant for the web, and we have enough memory to render large texture maps to achieve an effect as realistic as possible. With JPG's compression, parts of the realistic feel will be lost. | ||
− | |||
===Reusing textures=== | ===Reusing textures=== | ||
− | + | We need to manually edit the exported VRML file to use DEFs/USEs keyword for ImageTexture nodes so that texture memory can be conserved. | |
+ | 3ds Max exporter does not do this for us; instead, it uses a url field for all ImageTexture nodes, which results in a reload of a fresh copy of the texture file every time it is used. So if a texture file is being used 100 times in the model, the Max-exported VRML code will freshly reload that file everytime it is used, instead of just loading it once and using the copy in memory. This results in 100 times more memory use than necessary. | ||
+ | To edit the code, use any text editor with multiline replace function. We used [http://www.softsea.com/review/NoteTab-Light.html NoteTab]. | ||
− | ... | + | <pre> |
+ | #old syntax example for all original Max-exported ImageTexture nodes | ||
+ | texture ImageTexture { | ||
+ | url "SomeMapsDirectory/SomePic.tif" | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | <pre> | ||
+ | #new memory-efficient syntax for ImageTexture node of the 1st appearance of the texture file | ||
+ | texture DEF SomePicName ImageTexture { | ||
+ | url "SomeMapsDirectory/SomePic.tif" | ||
+ | ) | ||
+ | |||
+ | #new memory-efficient syntax for ImageTexture node of the 2nd-and-on appearances of the texture file | ||
+ | texture USE SomePicName | ||
+ | </pre> | ||
+ | |||
+ | The DEF keyword gives the first appearance of the file a name; and then the USE keyword in later appearances of the map file references that name so that the texture map does not have to be reloaded into memory again. | ||
==Misc. Debugging Ideas and Tricky Surprises== | ==Misc. Debugging Ideas and Tricky Surprises== | ||
− | Like anything else, the 3ds Max VRML exporter is not always perfect; it sometimes generates unwanted results that needs to be manually taken care of. | + | When testing the model, one important thing to keep in mind is that while navigating in a VRML world, the viewer can only be in one place at a time, meaning that the user can be immune to some effects that are obviously not reasonable to the developer. This is even more true for this virtual building since it is mostly used for demos. Because of this, we can ignore some specific details that have to be taken care of in real-life. An example is, when an elevator cab is in the process of travelling, say from the 6th floor to the 3rd, if the user presses a call button from the 4th floor, then the animation will start over from the 6th floor to the 4th, jumping from whereever the cab was in its travel process. When testing from far away with all the walls turned off so that the entire set of elevators from the 1st to the 6th floor can be seen, this effect is obviously incorrect. However, during a normal demo, the user will not be able to access more than 1 floor at a time, and they will not be seeing the interior of the elevator without walls, so it is unlikely that they will notice this effect. This saves some time for coding to ignore the call button event when the elevator is moving, especially when the cab movement process is the most complicated and involves most of the logic back and forth in the script. |
+ | |||
+ | Like anything else, the 3ds Max VRML exporter is not always perfect; it sometimes generates unwanted results that needs to be manually taken care of. Some instances of this are listed in the following sections. | ||
+ | |||
+ | |||
+ | |||
+ | ===Can't see the object from its inside=== | ||
+ | '''Symptom:''' | ||
+ | Have an object, can see its face from one side, but not from the other side (usually a wall, the inside of a box, etc.) | ||
+ | |||
+ | There are many approaches in 3ds Max to solve this. 3 are listed here, these can usually solve the problem. | ||
+ | * Check to see that the object's map ID is correct. Incorrect map IDs lead to maps not being shown correctly in the scene. Even if you can see the object with maps in Max, it won't appear in the VRML browser. To check the map ID, select the object, go to Modify Panel, select Editable Mesh (or Poly) from the modifier stack, select Polygon to go to the polygon sub-object level, scroll down to Surface Properties rollout, in Material group, enter the correct ID in Set ID. The correct ID can be obtained from the object's material in the Material Editor. See below for obtaining the map ID from Material Editor. | ||
+ | |||
+ | * Use a 2-sided map: In Material Editor, select Pick Material from Object, in Shader Basic Parameters rollout, check 2-sided. | ||
+ | |||
+ | * If neither of the above methods work, apply a Shell modifier by going to the Modify Panel, select the object that needs to be seen from both sides, then select Shell from the modifier dropdown-list. Use this only when necessary because it adds many polygons to the scene and increases the exported wrl file size by having to define the coordinate triplets for every single vertex on the shell. With the shell, we can change the inside and outside map of the object easily by changing some parameters on the Modify Panel. (Modify Panel > Shell > Param > check Override inner map ID and/or Override outer map ID, then type in Map ID (see below for details).) | ||
+ | |||
+ | Where to get Map ID? | ||
+ | The map applied to the obj must be a Multi/Sub-Object map to have Map ID. If the current map is not already Multi/Sub-Object, open Material Editor, make a new map, choose Multi/Sub-Object for its type, drag the original map to the button list under Sub-Material ID 1, then drag some other map to ID 2. You can delete all other ID rows if only need 2 maps for inside and outside. | ||
+ | In Modify Panel, type in whatever desired ID # for Inner map ID, then another ID # for outer map ID. | ||
+ | |||
===Automatically Generated INTERPs and TIMERs=== | ===Automatically Generated INTERPs and TIMERs=== | ||
− | Symptom: | + | '''Symptom:''' |
In the Terminal running OpenCOVER, the line showing the polygon and vertex count keeps flying up the screen, and the two numbers of counts keeps increasing. | In the Terminal running OpenCOVER, the line showing the polygon and vertex count keeps flying up the screen, and the two numbers of counts keeps increasing. | ||
Line 231: | Line 408: | ||
− | .... | + | |
+ | ===No Reusage of texture maps=== | ||
+ | '''Symptom:''' | ||
+ | At OpenCOVER loading time, texture loading is very long, and when loading finally finishes, the Texture size printed on Terminal is no where near the maps directory size on disk. | ||
+ | |||
+ | 3ds-Max-exported VRMl code tells the browser to freshly load the texture map every time it is used instead of reusing a previously loaded identical file. | ||
+ | |||
+ | To fix this, see "Optimization" > "Reusing textures" section above. | ||
+ | |||
+ | |||
+ | |||
+ | ===Light Direction / Leak=== | ||
+ | '''Symptom:''' | ||
+ | Light leaks throughout the world. | ||
+ | |||
+ | Unlike real-life lights, VRML lights do not light up themselves but light up other objects. Therefore the light source itself will not appear to be lit; the effect can only be seen from object being lit around the light source. Because of this, a problem in our virtual building is that when the sun is in the east, the western sky appears lit because the lighting direction is from the east and therefore towards the west, and the eastern sky appears dark. If we attempt to solve this by placing another light source in the west to light up the eastern sky, then the buildings would have both east and west sides lit, which is still not reasonable since the sunlight cannot be coming from both east and west. A VRML directional light is supposed to only shine on the objects in the same group as the light, so we tried grouping a directional light in separate groups from the other objects, but the directional light still shines on every object regardless of the hierarchy. We haven't found a real way to fix this. However, a practical solution is to look at the model from a direction that shows only the building, or shows half of the sky, etc., that will hide the incorrectly lit sky effect. One important thing to keep in mind is that while navigating in a VRML world, the viewer can only be in one place at a time, and it is less likely that the viewer would go outside the globe. Even if he/she does, the current buttons to control the sunlight will be far from reach, so most likely, it will not as noticeable to the viewer as it is to the model builder. | ||
+ | |||
+ | Because there are no real shadows calculated in VRML, an indoor light does not only light up the room, but "leaks" through all walls, ceilings, and floors since VRML ignores these planes that are supposed to block the light from entering other rooms. We had the problem of a light source placed on the 5th floor, and the light effect is seen on the 1st floor ground, which is lit. The supposed way to fix this is to change the radius field of the lighting node, but it seems that the radius field has no noticeable effect in OpenCOVER (we tried radius 5 and 500, but they turned out to be the same). So we used the attenuation field to dim the lights quicker using a value larger than 1 for the first value so that the leak does not reach too far. See the "Light Attenuation" section below for more detail. | ||
===Light Attenuation=== | ===Light Attenuation=== | ||
− | Symptom: | + | '''Symptom:''' |
3ds Max exporter simply does not export the correct attenuation. | 3ds Max exporter simply does not export the correct attenuation. | ||
Line 244: | Line 438: | ||
Attenuation field does not exist for directional light; it exists for both point light and spotlight. | Attenuation field does not exist for directional light; it exists for both point light and spotlight. | ||
− | |||
− | |||
− | |||
− | |||
==Future Work== | ==Future Work== | ||
Line 257: | Line 447: | ||
* Sound effect when cab arrives at user's floor | * Sound effect when cab arrives at user's floor | ||
* Sound effect recorded from building's elevator (current ones are taken from another model; they aren't fully tested yet) | * Sound effect recorded from building's elevator (current ones are taken from another model; they aren't fully tested yet) | ||
+ | * If this will work better, implement the real elevator logic such that when a cab is called, the most convenient cab comes to the user (Currently each cab operates individually, each linked to its own set of call buttons; this reduces some scripting and uses one prototype for all 3 cabs by creating instances of the prototype) | ||
+ | |||
+ | |||
+ | ===Lighting=== | ||
+ | |||
+ | * Look for a solution to the sunlight problem (Currently the western sky, instead of the eastern, is lit when the sun is in the east; this is due to the lighting nature that does not lit up the light source itself but instead whatever object the light ray hits) | ||
+ | * Make a dial for the user to select the desired hour for the sunlight, then move the sun to the appropriate position to generate correct light effects. | ||
Line 264: | Line 461: | ||
* Stylized people walking along defined paths in the building | * Stylized people walking along defined paths in the building | ||
* Realistic sensor information obtained from building's temperature, CO2, humidity, etc. sensors | * Realistic sensor information obtained from building's temperature, CO2, humidity, etc. sensors | ||
+ | * Possibly more sound effects. Currently (summer 2008), the elevator sound effects have not been fully tested. There can be more sound effects like when doors automatically close and lock; light-volume talking noises playing at random times or when pass by conference rooms and coffee areas to simulate the building environment; keyboard/office sounds when pass lab areas. | ||
+ | * ... | ||
− | + | ==Images== | |
− | + | ||
− | + | ||
− | == | + | |
[[Image:exterior1_medium.jpg]] | [[Image:exterior1_medium.jpg]] | ||
Line 284: | Line 480: | ||
A view of the interior lobby of Calit2. | A view of the interior lobby of Calit2. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Links and References== | ==Links and References== | ||
Line 296: | Line 486: | ||
[http://www.web3d.org/x3d/specifications/vrml/ VRML Specs main page (links to VRML 1.0 specs and zip files)] | [http://www.web3d.org/x3d/specifications/vrml/ VRML Specs main page (links to VRML 1.0 specs and zip files)] | ||
+ | |||
[http://www.lighthouse3d.com/vrml/tutorial/index.shtml?timer Lighthouse 3D Tutorials: TimeSensor Specs] | [http://www.lighthouse3d.com/vrml/tutorial/index.shtml?timer Lighthouse 3D Tutorials: TimeSensor Specs] | ||
Line 302: | Line 493: | ||
[http://www.edgewise-consulting.com/products/vrmlexp/ Mark Callow's VRML97-X3D 3ds max Export Plug-in] | [http://www.edgewise-consulting.com/products/vrmlexp/ Mark Callow's VRML97-X3D 3ds max Export Plug-in] | ||
+ | |||
+ | [http://www.vrmlworlds.com/developer/tutorials/3ds_max/3dsmax_optomize.html VRMLworlds: Exporting VRML from 3ds Max] | ||
+ | |||
[http://www.softempire.com/vrmlpad-v-2-0.html Download VrmlPad 2.0 (shareware) from SoftEmpire] | [http://www.softempire.com/vrmlpad-v-2-0.html Download VrmlPad 2.0 (shareware) from SoftEmpire] | ||
+ | |||
+ | [http://www.softsea.com/review/NoteTab-Light.html Download NoteTab Light 5.7 (shareware) from SoftSea] |
Latest revision as of 09:30, 14 September 2008
The goal the Virtual Calit2 Building is to provide an accurate and accessible virtual replica of the Calit2 building that can be used by any researcher or scientist to conduct projects or tests that require the need for a detailed architectural layout.
Contents |
Proposed Projects Using the Virtual Calit2 Building
In the news: Take 'Audio-Visual' Research to the Next Level
Contributors
Daniel Rohrlick continues making further additions to the existing model. The first floor of the Calit2 building is coming closer to completion.
Philip Weber provided excellent assistance with model loading techniques.
Mabel Zhang completed adding floors 3-6; completed the stairs; added the first set of interactive dynamic elements (automatic doors, working elevators, indoor light, 24-hour sunlight animation, sunlight according to system clock) to make the model more realistic; optimized the model (by adding MultiRes modifier in Max, shrinking the textures, adding the DEFs/USEs for ImageTextures, eliminating continuously generated polygons, recreating instances from copies, etc.); added the metaloader to selectively turn on/off building floors or layers; and divided the copy of the building into individual files for more object-oriented organization.
General Structure
As dynamic elements were added, a more object-oriented structure is needed to maintain the model files.
The entire model is saved as one Max file, then from an individual or multiple layers, smaller files are saved again for final export. Currently, the entire model is organized into these parts:
- A loader to run the model and allows turning on and off layers to allow for maximum speed; manual VRML script.
- Main model, this is the exterior of the building and miscellaneous parts; this inlines inlineDoorAnimated.wrl, inlineElevInit.wrl, and inlineLightSwitScript.wrl for corresponding dynamic effects.
- Floor 1 in the main building and furnitures; exported directly from 3ds Max file's corresponding layers.
- Floor 1 New Media Arts Wing rooms; exported directly from 3ds Max file's corresponding layers.
- Floor 2; exported directly from 3ds Max file's corresponding layers.
- Floor 3; exported directly from 3ds Max file's corresponding layers.
- Floor 4; exported directly from 3ds Max file's corresponding layers.
- Floor 5; exported directly from 3ds Max file's corresponding layers.
- Floor 6; exported directly from 3ds Max file's corresponding layers.
- All animated doors; this file contains only those doors that are animated; physical door objects are exported directly from 3ds Max, partial VRML code is added from modifying exported code.
- Elevator initializer; initialize and create 3 elevators from a script PROTOtype (see below); mostly manual VRML script.
- Elevator prototype; include code for 1 set of elevator's movement, audio, and other operations; the initializer creates 3 copies of this prototype to create 3 independent sets of elevators; physical objects are exported directly from Max with 1 line of code changed to link to the script, core logic is done by manual VRML script with JavaScript.
- Light switch; include code for light object's on and off controlled by 2 separate switches; physical objects are Max-exported, logic is manual VRML script with JavaScript.
- Sunlight animation and position according to system clock; include code for sun object rotating around hemisphere; physical objects are Max-exported, logic is manual VRML script with JavaScript
- (Elevator physical objects; not directly required for run-time, but contains elevator objects exported from Max; the entire content of this file is pasted into the Elevator prototype file in order for the logic to link to objects.)
- (Light switch physical objects; not directly required for run-time, but contains indoor light objects exported from Max; the entire content of this file is pasted into the Light switch file in order for logic to link to objects.)
- (Sunlight physical objects; not directly required for run-time, but contains sunlight objects exported from Max; the entire content of this file is pasted into Sunlight file in order for logic to link to objects.)
- (A README file prepared for future maintenance; explains most, if not all, of reasons and the necessary changes that need to be made to a directly Max-exported file for all the parts to run correctly.)
Modeling in Autodesk 3ds Max
Modeling in Max with VRML export in mind
Because the final version of the model is in VRML format, during the modeling process in 3ds Max, the most important thing to keep in mind is that not all effects will export to VRML. 3ds Max comes with a detailed user reference that includes some useful tips and exporting rules on VRML. Not all materials are exported, not all lights are exported, different geometry types export to files of significantly different sizes, etc.
Manual Work After Export
From many websites focusing on projects using 3ds Max + VRML, it is suggested that the Max-exported VRML file should be checked and edited manually for the final version. For a static model, this is quite simple; the most often hand operation would probably be commenting out automatically generated looping timers and interpolations, which will continuously generate polygons during run-time. However, for a model with logic scripts, the exported code must be combined with the manual script into a single file for the effect to work; simply inlining the exported object file or the script will not suffice, because VRML requires that the script and the objects be in the same file for the linking to take effect, otherwise syntax errors will result. This means that either the exported file contents will need to be pasted into the script file, or vise versa, so a clear structure and documentation is even more important. This is the reason that we placed the script and the objects in the same file, instead of separating them for easy maintenance.
For more manual modifications required, see relevant sections below.
Dynamic Elements
Automatic Doors
These are probably among the simplest dynamic elements to implement; nevertheless, they are a nice addition.
Key elements:
- VRML97 Proximity Sensor (to detect user entrance to the area around the door)
- VRML97 Time Sensor (to control animation of door's opening or closing)
The method is very straight forward. For each set of door(s), first we create the door's opening animation in Max by turning on AutoKey, scrolling to a frame (e.g. 30), rotating the door about its axis, then copying frame 0 to a later frame (e.g. 60). Now create a proximity sensor around the door(s), extending the sensor's length and width as far as desired. Then we created two time sensors, one for the door opening animation (frame 0-30), the other for the closing animation (frame 30-60). The time sensors' controlled frames can be set in 3ds Max Modify Panel. Finally, link the proximity sensor to the time sensor in 3ds Max (This appears as a ROUTEing in exported VRML code), and now the door(s) will open as the user enters the space enclosed by the proximity sensor.
To close the doors when the user leaves the space, although we have seen a few discussions online that suggest that it can be done within 3ds Max, we could not find a way in Max. Thus we accomplished the automatic door closing by manually editing the exported code. The code we need to change is at the very end of the exported wrl file. For a door with the above linking done, its corresponding code at the end of the wrl file looks similar to:
ROUTE sensor_prox_floor1aud3.enterTime TO sensor_time_floor1aud3_1-TIMER.startTime
where sensor_prox_floor1aud3 is a proximity sensor of a door named floor1aud3, sensor_time_floor1aud3_1-TIMER is the time sensor controlling this door's opening animation (indicated by the suffix _1). To make the door automatically close, copy the above line and replace 2 places so that the new additional line looks like:
ROUTE sensor_prox_floor1aud3.exitTime TO sensor_time_floor1aud3_0-TIMER.startTime
where the proximity sensor's enterTime is changed to exitTime, and the time sensor's name's suffix is changed from _1 to _0 because our naming strategy is _1 for the sensor controlling opening animation, _0 for the one controlling closing.
Keep both lines and do the same with the rest of the ROUTEing lines at the end of the exported file. (The other ROUTEing codes in the file are for the time sensors, door objects, etc., not related to the proximity sensor.)
Now the door will open when the user approaches it and close when the user leaves it.
Working Elevators
We used the German Mailand Mercedes car dealership model's elevator core code as a reference and built our own set of code to fit the Calit2 elevators.
The elevator has 2 parts, an intializer and a user-defined PROTOtype file. (PROTO is a keyword in VRML, stands for PROTOtype, and just means a user-defined data type.) The PROTO file defines one set of elevator, including its physical objects and all necessary scripts. The initializer creates 3 instances of this PROTOtype at the corresponding locations of the 3 main elevators in the building.
The length of the the core PROTO script, including the documentation, is more than 1000 lines, not including the exported objects, so it might be inconvenient to scroll through them here. However, detail documentation is included in the actual files.
A drawback of the current approach is that it cannot simulate the exact logic of the elevators in the real building. The virtual elevators currently operate independently, while the real ones are dependent. For example, in reality, there are 2 sets of buttons to call the elevator, and when a user calls the elevator from a floor, whichever elevator cab that is the most "convenient" will come to the user; in the virtual building, there are 3 sets of call buttons, and each elevator responds only to the set of buttons linked to it, meaning that the user controls whichever elevator will come by pressing on the corresponding set of buttons. We took the current approach because it involves less logic and linking and is less time-consuming; it only requires 1 set of definitions that can be initialized 3 times to generate 3 elevators. However, simulating the realistic logic will make the model more intelligent and believable.
Light Effects
VRML has three types of lights built in:
- PointLight
- DirectionalLight
- Spotlight
Each one corresponds to these 3ds Max lights, respectively:
- Omni
- Free/Target Direct
- Free/Target Spot
where the target lights can be used to help define the rotation of the light objects, but the automatically generated loop TRUE timer might need to be commented out. See the "Automatically Generated INTERPs and TIMERs" section below.
See the Lighthouse 3D description on lighting nodes for detailed information about VRML lights.
VRML does not support real shadows. To work around it, we can precalculate the texture maps and save them to the maps directory, then apply the textures to the objects in Max.
Because no shadows are calculated during run-time, don't be surprised if a light leaks all throughout the world. Adjust the attenuation by hand if needed so that the light leak is not as obvious. Sometimes turning on the headlight can help reduce the effect.
Like in Max, VRML browsers have a headlight on by default, if no lights are present in the scene. However, as soon as one light is added into the scene, the headlight is automatically turned off at startup. We can set the headlight to be on by manually editing the exported code:
Original code:
# This code is automatically exported from 3ds Max when a light is present in the scene NavigationInfo { headlight FALSE }
Change to:
# To turn on headlight at world startup NavigationInfo { headlight TRUE }
Indoor Lights
These are usually simulated using point lights (Omni in Max) because a spotlight only lights up a cone-shape area, and a directional light is located far far away from the world and therefore lights up everything in the direction it faces, which is usually not the desired result of an indoor light.
Point light fields taken from the above website, see here for detailed information about point lights:
PointLight { on TRUE intensity 1 ambientIntensity 0 color 1 1 1 location 0 0 0 attenuation 1 0 0 radius 100 }
Attenuation:
Only "Far Attenuation" defined in 3ds Max is exported. "Near Attenuation" is not. Even so, Max does not export the correct far attenuation either; the values will need to be changed by hand after export. See "Misc. Debugging Ideas and Tricky Surprises" > "Light Attenuation" section below for manually adjusting the attenuation.
Radius:
The radius field does not seem to have an effect in OpenCOVER. We tried radii ranging from 5 to 500, and the effect always came out to be the same. Thus we only adjusted the attenuation field to generate lighting effect close to reality.
Sunlight
While the 3ds Max Daylight system, which includes a sunlight and a skylight object, seems perfect for the goal, it does not export to VRML with the light effects. Therefore to simulate sunlight, the options are directional or point lights.
We used a point light for the sun because it was much easier to animate than a directional light; although the animation process are the same for both in Max, they are different in VRML. VRML light objects are exported in a hierarchy with the root parent being a group name to hold the animation time sensor, position interpolator, rotation interpolator, and the actual light source (e.g. the DirectionalLight node). When an animation is created for a directional light, the animation time sensor routes to the root parent's position/rotation interpolater's key, which will set the corresponding keyValue, and this affects all its children's position. The problem is, VRML directional lights do not have a position/location field but instead, a direction field. Thus when the position of the root parent changes, although the light source's position also changes, the direction field does not. This results in no change in light direction. For the light direction to be changed, we need to manually route the time sensor to the direction field of the light source, not the position/rotation interpolator keyValue of the parent. So the routing would go from the time sensor's fraction_changed event to some 3-value vector, which then routes to the light source's direction field. The most convenient 3-value vector is the position interpolator's keyValue, which will need to be defined to be exactly the same as the values we want the direction field to have. It can be defined this way by moving the light object in max, or by writting the vector array manually. For our purpose, we needed the sun to rotate around the hemisphere, so the light direction will have both positive and negative values in its vector. This meant moving the sun object far, far away from our hemisphere which is not around the origin of the world, otherwise writing the coordinates manually. Therefore, we chose the point light approach, which does not require any manual effort to animate a 24-hour sun rotation around the hemisphere.
We implemented two features for the sunlight:
- 24-hour sunlight animation at the press of a button (8 seconds animation)
- Sunlight effect according to the current system clock
The 24-hour animation can be entirely done in Max. We used a circular spline to define the sun's path at 60 degrees to the grass plane, and set a path constraint on the omni sun object to follow the circular path in 240 frames, equivalent to 8 seconds in real time.
The system clock function requires some JavaScript to be inserted into the exported VRML file. It uses the JavaScript "date" object to create an instance of it, and uses the objects getHours() member function to obtain the system clock hour, and then move the omni sun object to its appropriate place in the sky. Other member functions can be used to obtain the weekday, date, minutes, etc., but we only used the hour for the sun's approximate position.
To move the sun to its appropriate position, we made a correspondance table for the hour, frame, and the key array in the exported sun object's position interpolator field. An if-statement or switch-statement can be used to check the current system hour returned by the date object instance, then assign the corresponding key value to an SFFloat type eventOut, which will be set to route to the sun's position interpolator to translate the sun to its position specified in the keyValue array. Each value in the key array automatically corresponds to the value with the same index in the keyValue array within the sun object's definition.
There are detailed information in the README file and the text files placed with the actual model, but here is the current correspondance table for the sun object mentioned above, it may change as the sun's animation frames are changed in Max:
For the following table, hour = hour in the day; frame = hour * 10 as set in Max when doing animation; frmNw = multples of 3 closest to frame (because each keyValue (aka. sun's position) stays for 3 frames); thkey = corresponding #th key in code, thkey = frmNw / 3; key = corresponding keyValue[thkey-1] in code. hour frame frmNw thkey key 0 0 0 1st 0 1 40 39 13th 0.0375 2 80 81 27 0.08125 3 120 120 40 0.121875 4 160 159 53 0.1625 5 200 201 67 0.20625 6 240 240 80 0.246875 7 280 279 93 0.2875 8 320 321 107 0.33125 9 360 360 120 0.371875 10 400 399 133 0.4125 11 440 441 147 0.45625 12 480 480 160 0.496875 13 520 519 173 0.5375 14 560 561 187 0.58125 15 600 600 200 0.621875 16 640 639 213 0.6625 17 680 681 227 0.70625 18 720 720 240 0.746875 19 760 759 253 0.7875 20 800 801 267 0.83125 21 840 840 280 0.871875 22 880 879 293 0.9125 23 920 921 307 0.95625 (24 960 960 320) 0.996875
VRML Scripting + JavaScript in General
To use JavaScript in a VRML wrl file, create a script node, and use an inline url to include the javascript:
DEF WhateverScriptName Script { eventIn SFWhateverType WhateverName1 eventOut SFWhateverType WhateverName2 field SFWhateverType WhateverName3 # JavaScript begins here url "javascript: function WhateverFunctionName (Param1, Param2) { // Function definition } " # JavaScript ends here } # ... # Usually at the end of the file ROUTE WhateverObjectName1.WhateverFieldName1 TO WhateverObjectName2.WhateverFieldName2
A Script node can have however many eventIns, eventOuts, and fields. eventIn is used to ROUTE some outside node into the script, where the necessary logic is handled; eventOut is used to ROUTE some value set within the script to some outside node, usually to trigger an action; field is the local variables in the Script node. For a list of data types and detail information, see the Official VRML Specs Website.
Each eventIn should have a corresponding function with the same name as the eventIn field. The first parameter of the function is the value ROUTEd in to the eventIn. The second parameter (optional) is the currentTime time stamp when the event is ROUTEd in. To trigger a function in the script, ROUTE some node (e.g. a TimeSensor) outside the script to an eventIn corresponding to the function (same name as the function) in the script.
Once an eventOut is given a value (usually from within a function), the eventOut automatically ROUTEs out from the Script node to whatever they are ROUTEd to. To trigger an outside action from within the script, set the value of an eventOut, and ROUTE that eventOut to some node (e.g. a PositionInterpolator, a TimeSensor) outside the script.
ROUTEing scripts are usually placed at the end of the file.
Hints:
- Putting the functions and the ROUTEing codes in a logical order helps in the debugging process, especially when the actions involve a long process that goes back and forth between the Script node and the outside (like the elevator code).
- Use VRML style commenting (# single-line) anywhere in the file except in the inlined url JavaScript section - use JavaScript style commenting (// single-line, /* */ multi-line).
- If there are hand-written scripts, use a VRML text editor to find syntax or symmantic errors before running the wrl file in a browser. There aren't many VRML editors, but a good one to use is VrmlPad. It's a shareware and is not too useful when need to save large files (the shared version only allows 64K max), but it's very handy in finding syntax or symmantic errors. VrmlPad's status bar shows the type of present errors in the lower right corner in highlighted background colors; double-click on the highlighted error type, and it will take you to the next line with the chosen error, which will be dot-underlined in color. There is also a "Next Error" button on the toolbar, the icon is a hammer with a curved arrow.
Optimization
The difference between a very nicely and smoothly rendered architectural scene and a real-time 3D model is that the former allows for rendering ahead of the time, and the latter requires rendering on the fly. Because of this, in order to reach the maximum navigation speed in the architectural model, some details need to be sacrificed; this includes the smoothness of surfaces, details in objects, resolution of textures, etc. The VRML language itself already has some features to enable fast real-time rendering such as the lack of shadows so that no shadows will need to be calculated for rendering; this is also part of the reason that VRML does not support all of the effects from 3ds Max, and the non-supported features are not exported to VRML files when using the 3ds Max exporter.
Our main approach was to reduce the polygon count, to shrink texture file size, to reuse textures, and some other more minor methods.
Recreating meshes as primitives when possible
VRML defines meshes by listing the coordinates for each vertex, which can be in terms of thousands when the mesh is even a bit complicated. On the other hand, it defines primitives by a built-in node type like Box and defines its few fields. According to the 3ds Max User Reference, spheres, cylinders, and cones also export as primitives. There might be others.
Thus, creating primitives instead of meshes can reduce file size. A primitive can be converted to a mesh at two mouse-clicks, but converting backwards is not as easy. We remade some cylinders and boxes that were originally meshes as primitives so that the exported file is much smaller. Making identical objects as "instances" of these new primitives will reduce file size even more. See the instances section.
(Re)Making identical objects as instances
This reduces file size by telling 3ds Max to automatically use DEFs/USEs keywords for geometry's -FACES and other nodes.
The exporter uses DEF for the first appearance of the object (the master) to assign it a name, then uses the USE keyword for all subsequent appearances of the object to reference to the master and to use its properties.
This saves the code to redefine the same shape every time and shrinks down the code to only 1 line.
Reducing polygon count in 3ds Max
3 Max modifiers to help reduce polygons and vertices:
- MultiRes
- Optimize
- Vertex Weld
These can be found in the Modify Panel > Modifier dropdown list
MultiRes is the most extensively used method in the Calit2 virtual building. It allows directly typing in the desired percentage of vertex count, then automatically generates the image according to the set percentage. It is officially better than the Optimize modifier, as stated in the 3ds Max 9 User Reference from the Help menu.
Optimize sometimes can have an effect when MultiRes does not. However, most of the time, MultiRes reduces more polygons and gives better images that aren't distorted.
Vertex Weld combines nearby vertices according to the user-defined distance between vertices so that the mesh is clean. This works even when the Merge Vertex option in MultiRes modifier does not have an effect.
In this model, MultiRes usually reduces quite a large amount of polygons (reduction ranges from around 10% to 80% of the original), but sometimes we use MultiRes in combination with Vertex Weld. The Optimize modifier is seldom used in this model.
Shrinking texture map image file sizes
Texture map files occupy memory space and render time. We reduced a large amount of texture files from 512 * 512 to 256 * 256, which is four times smaller in terms of area. This reduced the size of the maps directory by half (from about 120 MB to about 57 MB), the texture memory, and thus sped up loading and rendering time.
A tip from VRMLworlds.com is to make map resolutions powers of 2, e.g. 512*512, 256*256, etc. "Doing this makes the textures look better in the vrml world, and also helps the scene render a little faster or use less video memory."
We mostly used TIF or PNG formats because of their preservation of image quality. PNG does have the advantage over TIF to preserve full image quality at a smaller file size, but TIF is more commonly used for professional and archival purposes. Most VRML articles recommend JPEG, GIF, and PNG formats because VRML worlds are commonly used for web applications and prefers a smaller downloading time. We use the TIF format instead because the virtual building is not meant for the web, and we have enough memory to render large texture maps to achieve an effect as realistic as possible. With JPG's compression, parts of the realistic feel will be lost.
Reusing textures
We need to manually edit the exported VRML file to use DEFs/USEs keyword for ImageTexture nodes so that texture memory can be conserved.
3ds Max exporter does not do this for us; instead, it uses a url field for all ImageTexture nodes, which results in a reload of a fresh copy of the texture file every time it is used. So if a texture file is being used 100 times in the model, the Max-exported VRML code will freshly reload that file everytime it is used, instead of just loading it once and using the copy in memory. This results in 100 times more memory use than necessary.
To edit the code, use any text editor with multiline replace function. We used NoteTab.
#old syntax example for all original Max-exported ImageTexture nodes texture ImageTexture { url "SomeMapsDirectory/SomePic.tif" }
#new memory-efficient syntax for ImageTexture node of the 1st appearance of the texture file texture DEF SomePicName ImageTexture { url "SomeMapsDirectory/SomePic.tif" ) #new memory-efficient syntax for ImageTexture node of the 2nd-and-on appearances of the texture file texture USE SomePicName
The DEF keyword gives the first appearance of the file a name; and then the USE keyword in later appearances of the map file references that name so that the texture map does not have to be reloaded into memory again.
Misc. Debugging Ideas and Tricky Surprises
When testing the model, one important thing to keep in mind is that while navigating in a VRML world, the viewer can only be in one place at a time, meaning that the user can be immune to some effects that are obviously not reasonable to the developer. This is even more true for this virtual building since it is mostly used for demos. Because of this, we can ignore some specific details that have to be taken care of in real-life. An example is, when an elevator cab is in the process of travelling, say from the 6th floor to the 3rd, if the user presses a call button from the 4th floor, then the animation will start over from the 6th floor to the 4th, jumping from whereever the cab was in its travel process. When testing from far away with all the walls turned off so that the entire set of elevators from the 1st to the 6th floor can be seen, this effect is obviously incorrect. However, during a normal demo, the user will not be able to access more than 1 floor at a time, and they will not be seeing the interior of the elevator without walls, so it is unlikely that they will notice this effect. This saves some time for coding to ignore the call button event when the elevator is moving, especially when the cab movement process is the most complicated and involves most of the logic back and forth in the script.
Like anything else, the 3ds Max VRML exporter is not always perfect; it sometimes generates unwanted results that needs to be manually taken care of. Some instances of this are listed in the following sections.
Can't see the object from its inside
Symptom: Have an object, can see its face from one side, but not from the other side (usually a wall, the inside of a box, etc.)
There are many approaches in 3ds Max to solve this. 3 are listed here, these can usually solve the problem.
- Check to see that the object's map ID is correct. Incorrect map IDs lead to maps not being shown correctly in the scene. Even if you can see the object with maps in Max, it won't appear in the VRML browser. To check the map ID, select the object, go to Modify Panel, select Editable Mesh (or Poly) from the modifier stack, select Polygon to go to the polygon sub-object level, scroll down to Surface Properties rollout, in Material group, enter the correct ID in Set ID. The correct ID can be obtained from the object's material in the Material Editor. See below for obtaining the map ID from Material Editor.
- Use a 2-sided map: In Material Editor, select Pick Material from Object, in Shader Basic Parameters rollout, check 2-sided.
- If neither of the above methods work, apply a Shell modifier by going to the Modify Panel, select the object that needs to be seen from both sides, then select Shell from the modifier dropdown-list. Use this only when necessary because it adds many polygons to the scene and increases the exported wrl file size by having to define the coordinate triplets for every single vertex on the shell. With the shell, we can change the inside and outside map of the object easily by changing some parameters on the Modify Panel. (Modify Panel > Shell > Param > check Override inner map ID and/or Override outer map ID, then type in Map ID (see below for details).)
Where to get Map ID? The map applied to the obj must be a Multi/Sub-Object map to have Map ID. If the current map is not already Multi/Sub-Object, open Material Editor, make a new map, choose Multi/Sub-Object for its type, drag the original map to the button list under Sub-Material ID 1, then drag some other map to ID 2. You can delete all other ID rows if only need 2 maps for inside and outside. In Modify Panel, type in whatever desired ID # for Inner map ID, then another ID # for outer map ID.
Automatically Generated INTERPs and TIMERs
Symptom: In the Terminal running OpenCOVER, the line showing the polygon and vertex count keeps flying up the screen, and the two numbers of counts keeps increasing.
If there are grouped objects in a scene, after exporting the scene, open the VRML file and search for "INTERP" or "-TIMER". It is very likely that a ...-TIMER object has been automatically generated below the group name DEFinition, and its characteristic is that its definition is single-lined, its loop is TRUE, and its CycleInterval is the 3ds Max animation track bar time divided by 30 fps (if using the default track bar, 0-100 frames, then the CycleInterval will be 3.333). Along with the timer, sometimes a corresponding INTERP is generated and is ROUTEd by the timer. These timers and interpolations are the culprit of continuously generated polygons during run-time in OpenCOVER. The solution is to simply comment out the timer's definition and the corresponding ROUTEing lines; there are usually 3 in a set. If it seems that the interpolation is not routed to by anything else, it might be harmless to delete the entire definition, since it is often many lines long; but we have not test this thoroughly as to guarentee it.
On the other hand, if there are mesh animations in the scene, then these INTERPs and -TIMERs are necessary for the animation; leave them as they are in the file. Mesh animations are animations that involve sub-object level changes such as vertex or polygon generations. Simple translations, rotations, and scaling do not fall into this category; these usually do not need the automatic generated INTERPs mentioned above, which can be commented out in the usual case.
No Reusage of texture maps
Symptom: At OpenCOVER loading time, texture loading is very long, and when loading finally finishes, the Texture size printed on Terminal is no where near the maps directory size on disk.
3ds-Max-exported VRMl code tells the browser to freshly load the texture map every time it is used instead of reusing a previously loaded identical file.
To fix this, see "Optimization" > "Reusing textures" section above.
Light Direction / Leak
Symptom: Light leaks throughout the world.
Unlike real-life lights, VRML lights do not light up themselves but light up other objects. Therefore the light source itself will not appear to be lit; the effect can only be seen from object being lit around the light source. Because of this, a problem in our virtual building is that when the sun is in the east, the western sky appears lit because the lighting direction is from the east and therefore towards the west, and the eastern sky appears dark. If we attempt to solve this by placing another light source in the west to light up the eastern sky, then the buildings would have both east and west sides lit, which is still not reasonable since the sunlight cannot be coming from both east and west. A VRML directional light is supposed to only shine on the objects in the same group as the light, so we tried grouping a directional light in separate groups from the other objects, but the directional light still shines on every object regardless of the hierarchy. We haven't found a real way to fix this. However, a practical solution is to look at the model from a direction that shows only the building, or shows half of the sky, etc., that will hide the incorrectly lit sky effect. One important thing to keep in mind is that while navigating in a VRML world, the viewer can only be in one place at a time, and it is less likely that the viewer would go outside the globe. Even if he/she does, the current buttons to control the sunlight will be far from reach, so most likely, it will not as noticeable to the viewer as it is to the model builder.
Because there are no real shadows calculated in VRML, an indoor light does not only light up the room, but "leaks" through all walls, ceilings, and floors since VRML ignores these planes that are supposed to block the light from entering other rooms. We had the problem of a light source placed on the 5th floor, and the light effect is seen on the 1st floor ground, which is lit. The supposed way to fix this is to change the radius field of the lighting node, but it seems that the radius field has no noticeable effect in OpenCOVER (we tried radius 5 and 500, but they turned out to be the same). So we used the attenuation field to dim the lights quicker using a value larger than 1 for the first value so that the leak does not reach too far. See the "Light Attenuation" section below for more detail.
Light Attenuation
Symptom: 3ds Max exporter simply does not export the correct attenuation.
A third-party exporter called Mark's shows a comparison of the 3ds Max exported attenuation effect with the supposed effect.
Unfortunately, the said exporter seems to only support up to Max version 4. Thus, to solve this problem, we can manually play with the attenuation field of the light node by trial-and-error. The attenuation field is a three-value vector, and in OpenCOVER, it seems that using any positive number for the second or the third value would just result in no lighting at all. For the first value, the larger the number is, the stronger the attenuation, and thus the dimmer the light. We have not experiment with negative numbers in these fields.
Attenuation field does not exist for directional light; it exists for both point light and spotlight.
Future Work
Elevators
Implement functions to simulate the building's:
- Arrow light lighting up when cab arrives at user's floor
- Sound effect when cab arrives at user's floor
- Sound effect recorded from building's elevator (current ones are taken from another model; they aren't fully tested yet)
- If this will work better, implement the real elevator logic such that when a cab is called, the most convenient cab comes to the user (Currently each cab operates individually, each linked to its own set of call buttons; this reduces some scripting and uses one prototype for all 3 cabs by creating instances of the prototype)
Lighting
- Look for a solution to the sunlight problem (Currently the western sky, instead of the eastern, is lit when the sun is in the east; this is due to the lighting nature that does not lit up the light source itself but instead whatever object the light ray hits)
- Make a dial for the user to select the desired hour for the sunlight, then move the sun to the appropriate position to generate correct light effects.
Realistic Environment
- Stylized people walking along defined paths in the building
- Realistic sensor information obtained from building's temperature, CO2, humidity, etc. sensors
- Possibly more sound effects. Currently (summer 2008), the elevator sound effects have not been fully tested. There can be more sound effects like when doors automatically close and lock; light-volume talking noises playing at random times or when pass by conference rooms and coffee areas to simulate the building environment; keyboard/office sounds when pass lab areas.
- ...
Images
The Calit2 building viewed in wireframe mode.
The Calit2 Building shown with photo-realistic textures.
A view of the interior lobby of Calit2.
Links and References
VRML Specs main page (links to VRML 1.0 specs and zip files)
Lighthouse 3D Tutorials: TimeSensor Specs
Lighthouse 3D Tutorials: VRML Lighting Nodes
Mark Callow's VRML97-X3D 3ds max Export Plug-in
VRMLworlds: Exporting VRML from 3ds Max