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:
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.
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.
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.
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.
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.
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.
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,
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
, and23:59:59.999
being represented as0.999
(repeating).Check out
WorldLight
for the implementation of area lighting and other functions handled by the WoW client'sDayNight
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
flag0x10
enabled are self illuminated. These materials, typically things like windows on building exteriors, are lit using normal world light, a vertex color of0.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 calledcolor0
.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 theMOHD
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: