Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Rendering improvements #160

Open
wants to merge 94 commits into
base: master
Choose a base branch
from

Conversation

fallenoak
Copy link
Member

@fallenoak fallenoak commented Jun 14, 2016

NOTICE: PR and Description Work in Progress

This PR covers a wide range of rendering improvements for M2s, ADTs, and WMOs.

Read on for the key highlights.

Faster Loaders

Previously, all data parsed by Blizzardry for M2s, ADTs, and WMOs was copied back to the main thread using a structured clone. Unfortunately, the quantity of data being copied created a noticeable performance degradation. In particular, when the chunk render radius for the map would cause a new ADT tile to be pulled in, the main thread would hang for as much as 0.25 - 0.5 seconds.

As part of this PR, all loaders have been rewritten to make extensive use of transferable arrays. Typed arrays holding vertex, normal, color, and animation data are created on the worker threads. These typed arrays are then passed back to the main thread using the transferable feature of web workers. As a result, most data is no longer copied, but is instead passed by reference back to the main thread. This has eliminated the performance degradation, and generally reduced CPU load while navigating the world.

Simplified Geometry Data

Our old friend,

// Mirror geometry over X and Y axes and rotate
const matrix = new THREE.Matrix4();
matrix.makeScale(-1, -1, 1);
geometry.applyMatrix(matrix);
geometry.rotateX(-Math.PI / 2);

is now gone from M2 and WMO geometry.

In addition, vertices and normals are now read from disk verbatim. No more manipulation of axes / signs at load time.

Full M2 Shading

The overly broad M2 fragment and vertex shaders have been replaced by a full suite of shaders matching the shaders used by the WotLK client. Note that shader accuracy may still be somewhat off--but this is a journey, not a race.

Among other things, full shader support means env mapped textures are actually env mapped! Check out the shiny polish on the Ironforge steam tanks near the IF entrance, the sheen on the Northrend Penguin's feathers, or the fancy reflection on the glass tunnels at the midway point of the Deeprun Tram.

Vertex Shaders

  • Diffuse_T1
  • Diffuse_T1_T2
  • Diffuse_Env
  • Diffuse_T1_Env
  • Diffuse_Env_Env

Fragment Shaders

  • Combiners_Opaque
  • Combiners_Mod
  • Combiners_Opaque_Add
  • Combiners_Opaque_Mod2xNA_Alpha

Area Lighting

The exterior world is lit using something called area lights. These lights are effectively spheres with an inner and outer radius, and are used to fill in color data for things like sun diffuse, sun ambient, fog, sky bands, and etc.

The values are interpolated according to the time of day, with midnight being represented as 0.0, and 23:59:59.999 being represented as 0.999 (repeating).

Check out WorldLight for the implementation of area lighting and other functions handled by the WoW client's DayNight classes.

Corrected M2 Billboarding

M2 billboarding now properly obeys transformed bone parents. This significantly cuts down on rendering oddities presented by billboarded M2s such as lamps.

Additionally, because M2 geometry is no longer rotated / inverted / etc, billboarding logic no longer involves sign changes.

Better WMO Rendering

WMO rendering has been extensively overhauled.

Self Illumination

Materials with MOMT flag 0x10 enabled are self illuminated. These materials, typically things like windows on building exteriors, are lit using normal world light, a vertex color of 0.5, 0.5, 0.5, and an emissive color that is added based on a factor from the client's self illumination table.

The emissive color used for self illumination in WMO materials is present in the MOMT data. On Wowdev.wiki, it is called color0.

The self illumination table is present in the client as a set of static values. The self illumination factor can range from 0.0 to 1.0.

You can observe self illumination at work by paying a visit to Darkshire (or any other settlement, really). During the day time, exterior windows on things like the Darkshire inn will look like any other exterior material on the building. As night rolls in, the windows begin to glow, as if the interior of the inn was shining through the windows.

Vertex Color Attenuation

MOCV values in batch type A WMO group batches are attenuated based on distance to the nearest exterior portal. This attenuation is performed at runtime unless flag 0x01 is set on the MOHD flags in the WMO root.

This resolves issues like the overly dark triangle in inn entrances in old world inn WMOs.

Distance Culling

Map doodads are now culled based on bounding radius and distance from camera. As objects go out of view, they smoothly fade out. As objects come into view, they smoothly fade in. Fully out of view objects skip rendering, which increases framerates in particularly doodad-dense map regions.

Portal Culling

Special thanks: to @Deamon87 for guidance while implementing portal culling. He had the idea to use BSP trees to determine camera location relative to WMO groups, and his portal culling implementation in WebWoWViewer was one of the primary aids in figuring out how to implement this feature in Wowser.

The retail WoW client makes extensive use of something called portal culling to reduce scene complexity. In particular, capital city WMOs like Stormwind and Ironforge benefit heavily from the use of portal culling.

Portal culling works like this:

  1. Portal polygons are defined at entrances / exits of geometry regions. In other engines and games, these regions are often called sectors. In World of Warcraft, the regions are called groups--as in WMO groups. The placement of these polygons is done by the level designer, and the values that define the portal are baked in to the WMO data files in the client.
  2. At runtime, the camera is located relative to all loaded WMO groups. Its location is defined as one of: outside of all WMO groups present in the scene; or inside a single, specific WMO group present in the scene. This location is tricky to determine, as bounding boxes for WMOs and WMO groups often overlap with one another. In order to determine the location, we make use of the BSP tree defined in the WMO groups.
  3. Based on the camera location from step 2, scene traversal starts. When starting from the exterior world, all WMO portals facing the exterior world and visible to the current camera frustum are entered. When starting from a specific WMO group, only portals in that group leading to other groups and visible to the current camera frustum are entered.
  4. As portals are entered, a frustum is projected based on the current camera position, and clipped to the edges of the portal polygon. This clipped frustum is then used when entering all subsequently nested portals, which acts as a safeguard to reduce overdraw.
  5. WMO doodads are only marked as visible if the doodad bounding box intersects with the clipped portal frustum as the portals in the scene are traversed.
  6. Similarly, WMO groups are only marked as visible if the group bounding box intersects with the clipped portal frustum as the portals in the scene are traversed.

fallenoak added 15 commits June 12, 2016 14:49
* WMO materials are now shared between groups, provided the group
  referencing the material uses the material in the same way.
  Currently, this is defined as having the same interior/exterior
  flagging, the same batch type (A, B, or C), and the same index
  in the WMO root's MOMT chunk.

* WMO materials are now created by the WMO root, rather than the
  individual groups.

* WMO materials now properly copy the data out of MOMT, rather than
  directly manipulating it. This prevents issues that occurred when
  different WMO groups used the same material, but had different
  flags for interior / exterior.
* Removed branching logic from shaders in favor of preprocessor defines.

* Switched from passing separate values for vertex color and alpha to
  passing single vec4.

* Removed several now unnecessary uniforms from material.

* Switched shaders to use .glsl suffix.

* Moved shaders to dedicated subdirectory.
The Wrath of the Lich King client modifies MOCV values prior to passing
them to the shader. The fixVertexColors() function attempts to implement
approximately the same logic in Wowser.

Additionally, root ambient color is now added (when appropriate) to
vertex colors before being passed to the shader.
Also added an ifdef to still permit rendering without world lighting.
* All lighting is now calculated in the vertex shader.

* Restored light modifier uniform functionality.

* Changed expectation: all defines used as conditional checks are
  now expected to be defined (ie no more ifdef before if with
  conditional-use-only defines).
* Removed unused textureCount uniform.

* Moved to Float32Array types for all vector uniforms.

* Consolidated light and fog params into single vectors.

* Corrected type attributes for uniforms (eg: 3fv, not f3v).
fallenoak added 14 commits June 14, 2016 15:14
* Worker results can now take advantage of transferable objects
  when passing results back to the main thread.

* Worker results now pass back objects with success and value
  properties. This replaces the array-based style used previously.
* Transferable typed arrays are now used for several buffer geometry
  attributes in WMO groups. On recent browsers, this greatly reduces
  the cost of transfering data back to the main thread when parsing
  WMO groups.

* WMO roots now use a similar structure as WMO groups, although
  there's currently no transferable arrays in the root.

* Loader logic has been separated out from the blueprint classes into
  dedicated loader classes.

* The existing loader files that define the tasks that run on worker
  threads have been moved in to dedicated worker.js files.
* Switched from setTimeout to requestAnimationFrame. The latter seems
  more appropriate, since the queue is used to help smooth out
  loading to improve framerates.

* Fixed a bug that resulted in always doing the minimum amount of work
  per queue run.
WMODefinition is used to set up transferable arrays of data on
worker threads.

WMOBlueprint is used to set up various objects (including rendering
related objects) on the main thread.

As of this change, blueprints are no longer confusingly used in two
separate contexts. Additionally, the worker is back to being
relatively small, and with far fewer dependencies.
* Changed WMO class to WMORoot to clarify its role.

* Implemented proper (ie ref count based) unloading for WMO materials.

* Eliminated texture count uniform from WMOMaterial.

* Improved texture handling in WMOMaterial to support swapping textures
  after initial load.

* WMO material defs now have a dedicated class.

* Migrated WMO blueprint classes to be adjacent with the WMORoot and
  WMOGroup classes.

* Removed some cruft from WMO blueprint classes.
* WMORootView and WMOGroupView are the classes expected to appear in
  the THREE.js scene.

* Blueprint logic now lives in the WMORoot and WMOGroup classes.

* WMOHandler creates the views necessary to render a given WMO in
  the THREE.js scene. The loaders no longer produce the view when
  loading.
The WorldLight class is meant to handle many or most of the
functions served by the DayNight class in the retail client.

Currently, it has the following abilities:

* Calculation of sun direction based on time of day

* Interpolation of color values based on time of day

* Calculation of fog params based on time of day

Additional functionality will be added in future commits.
The geometry on disk is fine as is (it's already Z-up). Placement of
the WMOs will need to be altered to correctly compensate.
@fallenoak fallenoak changed the title [WIP] Refactor: WMO rendering [WIP] Rendering improvements Oct 6, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant