Sound Effects

After playing through the combat demo a few times, I felt a little sound would really liven things up. This turned into a bigger job than expected (doesn’t it always?) but I’m very happy with the result. This post talks about the new audio features in brief, and may be slightly technical if you’re not familiar with Unity.

So how does this work? To start with, let’s take a look at adding a sound manually. What you need to do is add the component DaggerfallAudioSource to any of your GameObjects. This scripts buddies up with Unity’s AudioSource component and feeds it sounds loaded dynamically from Daggerfall’s sound effects library.

Once the component is added, you set a preset (OnDemand, Looping, or None for no changes) and a Sound Index then click Apply(). This will load in the sound from Daggerfall as a regular AudioClip and apply it, along with your preset settings, to the Unity AudioSource. From there you just treat it as a normal audio source like any other and don’t have to worry about it any more. You can even preview sounds directly from the editor by index, id, or name.

Behind the scenes, things are a bit more complicated. As these audio clips are created dynamically they don’t have a matching file in the project hierarchy, and for some reason Unity doesn’t like to serialize procedural sound clips into the scene in the way it serializes stuff like mesh data. The other complication is that settings like “Play On Awake” don’t work as expected as the clip is not present in memory until after the component is running.

To handle all this, the DaggerfallAudioSource component will check to see if your AudioClip has been lost (from starting/stopping play, serialization/deserialization, recompile, etc.) then loads it back into memory again. It also handles booting the sound manually to simulate Play On Awake.

This is very lightweight and under normal play sessions the clip will only be loaded once at start. It’s only inside the editor where the clip may need to be loaded multiple times a session due to various reasons the editor dumps volatile data. It all just works as expected, even when using the little speaker to preview scene audio.

The other thing which caused some grief was actually converting the 8-bit PCM audio from Daggerfall into floats for AudioClip.SetData(). This needed special wrangling of the float values to convert bit depths properly, and the Unity docs weren’t much help here. A bit of Googling and face-desking saw me through to the end. Mostly, this was only hard because I can be an idiot sometimes.

On the support front, there’s a few other goodies coming with the sound update. I’ve created a SoundClips enum with about 80% of Daggerfall’s effects given a plain text, descriptive name. Where possible, I have grouped these by usage as I understand them. I also managed to work out how Daggerfall assigns sounds to enemies and action triggers, and put this into the toolset and API where appropriate. See below for updated DaggerfallAction editor window.

New DaggerfallAction Editor Window

The new number above is “Action Sound”. This is the sound ID to play when this action is triggered. I had to break out my hex editor again, as this action-sound mapping was previously unknown.

The next bit of support is you can import sound effects with dungeons, enemies, etc. Daggerfall Tools for Unity wires up all the audio sources for you to automatically bring your scenes to life. You can also create, change, and play sounds completely at run-time.

Last but not least, I’ve created a “spooky sound player” example component to randomly play those wonderful dungeon sounds (e.g wind blowing, doors opening, water dripping, etc.). This will later be joined by other example scripts as part of the tutorial series I’m working on.

I should have a new demo ready for you over the weekend with a full complement of sound effects to enjoy.

Dungeons & Dragonlings

Besides constructing horrible puns, I’ve been working on adding Daggerfall’s enemies into the toolkit. My first idea was to add something fast, but it turned into one of those rabbit-hole situations.

Sure it would be easy to just add basic monster templates, but wouldn’t it be great to actually import the correct enemies? Not just the fixed monsters like that first rat or imp in Privateer’s Hold, but support proper random encounter tables like those outlined in the Daggerfall Chronicles guide. And it all had to be easy to change once inside Unity, not just static information pulled from Daggerfall’s files.

The first thing I needed to work out was how did Daggerfall know which monster to spawn where? There are basically two kinds of enemy spawns, fixed and random. Both have an editor marker flat (from TEXTURE.199) in RDB blocks, so this was a good place to start. Fortunately, there’s only a few bytes of data in the record defining these flats, and for fixed monsters this was easy to find. The FactionID in flat resource structures also defines a monster (or mobile) ID. Basically, (FactionID & 0xFF) = MobileID. Range 0-42 are monsters like rat, imp, spriggan, etc. Range 128-146 are enemy adventurer types like Mage, Spellsword, etc.

Just to be sure I had this right, I wrote a quick tool to change IDs of every monster in S0000999.RDB (central dungeon block of Privateer’s Hold) then started a new game. As hoped, the first rat (ID=0) was now an Ancient Lich (ID=33), which promptly ate my level one character for breakfast. I tried not to think about all the ancient liches between myself and the dungeon exit.

I then documented everything into a spreadsheet by spawning each ID in turn, checking them in-game. This allowed me to build a starting template for each and every enemy’s texture file (male and female), animations, behaviour, affinity, corpse marker, and so on. The end result is EnemyBasics.cs where mobile enemies are simply defined. There are also new enumerations in DaggerfallUnityEnums.cs for MobileEnemies, MobileStates, MobileBehaviour, and MobileAffinity. This will be my foundation for adding enemy mobiles to the toolkit moving forward. I’m trying to keep everything just simple enough to get the job done. It’s up to you to build on from here.

I have added a new enemy name field to the DaggerfallBillboard editor script when you have a fixed enemy editor marker selected.

NewEnemyNameField

Next, I turned my sights on random encounters. Any Daggerfall nut knows about the various dungeon types (Crypt, Human Stronghold, Vampire Haunt, and so on), and that each dungeon type has a random encounter table described in the Daggerfall Chronicles guide book. I just had to work out which value defined the dungeon type.

Fortunately this was easy to find also. The upper 8 bits of the “Unknown2” value in a location’s MapTable data actually defines the dungeon type. It follows the same order as listed in the Chronicles, with Crypt=0x0033, Orc Stronghold=0x0133, Human Stronghold=0x0233 and so on. You can just shift right by 8 bits (DungeonType >> 8) to get the index. I’ve added this information back to the API in DFRegion.cs and MapsFile.cs so it’s all read in for you when loading a location. I have also added a field to the DaggerfallLocation editor script to display the dungeon type in your Inspector.

NewDungeonTypeField

 

With that squared away, I created a basic set of random encounter tables, one per dungeon type, matching those described in Daggerfall Chronicles. This can be found in RandomEncounters.cs.

There’s obviously more to this, as the player’s level is also used to determine which monsters spawn from the encounter table, but this should be a good start and easy to build on. Like the enemy mobiles, my goal here is to make something just functional enough for you to build on with Unity. The more independent you are of the game files, the better.

Now that Daggerfall Tools for Unity has a good foundation of enemy definitions and encounter tables, I’ll be adding a foundation mobile type to the toolkit for ready-made enemies. When starting play mode (or instancing a dungeon from code) the editor markers will spawn an appropriate monster in-place. I’m going to help with this by setting up all the initial animation smarts, but it’s up to you to extend with spells, AI behaviours, pathfinding, and so on. The good news is that you can use all the typical Unity features and there’s a ton of great resources for scripting enemies in the Unity tutorials and on the Asset Store.

Time to wrap this post up. I’ll show more of Daggerfall Tools for Unity very soon, once the above data starts becoming visual.

SVN Updated

The SVN repository has been updated to VS2010/XNA4.0 solution & code. If you are building DaggerfallModelling from source please ensure you have the latest version of the .NET Framework 4.0 and XNA Game Studio 4.0. You will also need to be using Visual Studio 2010 (or Express 2010) to load the new project.

A few bugs remain after porting to XNA 4.0. These will be ironed out over the next few weeks.

Building From Source and Other Issues

I’ve received a few emails from programmers trying to build Daggerfall Modelling from source and experiencing problems. The reason for their frustrations is that Daggerfall Modelling is built as a VS2008/XNA3.1 project. This means it will only build with Visual Studio (Express) 2008 with XNA Game Studio 3.1 installed. Attempting to build with VS2010/XNA4.0 will fail due to breaking changes in the latest version of XNA.

I also get the occasional email from Windows XP users with an older version of the .NET Framework 2.0 (pre-SP2) that experience a crash when opening a dungeon. This problem is caused by a bug in the .NET Framework 2.0 that was resolved in Service Pack 2, and can be fixed by upgrading/patching .NET.

The reason I stuck to older versions was to ensure my tools remained accessible to more people. On the flip side, not everyone wants to install an older version of Visual Studio and XNA just to compile my code. I’m also running the risk that Windows XP users will come across that crash without having read my getting started page for Daggerfall Modelling.

After serious thought, I have decided to make some changes to my source code:

  • Solution format upgraded to Visual Studio 2010 / Visual C# 2010 Express.
  • Daggerfall Modelling and XNALibrary will be refactored for XNA Game Studio 4.0.

The DaggerfallConnect class library and Daggerfall Imaging 2 will remain based on .NET 2.0 for now, but I might bring these forward at a later date.

The SVN will be updated in the next few days with these changes. The next release of Daggerfall Modelling (Beta 2) will require the latest versions of .NET and XNA Frameworks to run.

Visual Diary: XNALibrary Update

After a hectic mid-year rush things are finally settling back to normal. I will have more time to dedicate to my Daggerfall tools for another few months before the silly season begins. I am now working on getting Beta 1 of Daggerfall Modelling ready for release.

When I started Daggerfall Modelling the goal was to make a quick and easy Collada exporter for Daggerfall’s 3D assets. It didn’t take long before I realised an opportunity to revive Daggerfall Scout in a more useful form. Like all projects, escalating the scope after most of the code has been written is a recipe for trouble. While trying to wedge in action records to my scenes (levers, switches, etc.), I came to the realisation my code had become as messy as hell. To accomplish what I had in mind a total rewrite of the scene manager was needed.

As an adjunct to Daggerfall Connect, I have also been developing an XNA class library that makes using Daggerfall assets with XNA a snap (creatively called XNALibrary). This code base is something I wish reuse for other projects, so it made sense to get all the scene management and rendering out of Daggerfall Modelling and build it properly into XNALibrary. The end result is a small XNA 3D engine designed specifically for rendering Daggerfall scenes. I then had to rip out all the old code from Daggerfall Modelling and port it over to XNALibrary as the primary engine. It was a lot of work just to end up back where I started, but internally the code is much improved. Following is a quick tour of what’s new in XNALibrary, as seen from Daggerfall Modelling Beta 1.

 

Scene Graph

Previously, Daggerfall Modelling used a one-dimensional array of objects and looped through them to draw scenes. This is fine for small environments but doesn’t scale up very well.  It also makes visibility testing and render batching harder than it needs to be. When it came time to link up the levers and switches, I realised it was time to put in a scene graph.

I decided to use a simple Bounding Volume Hierarchy based around spheres. The beauty of bounding spheres is that you don’t have to worry about axis alignment as you do with boxes. Spheres may not wrap objects quite as tightly, but they’re a lot easier to handle when transformed (rotated, translated, scaled, etc.) as the volume is always the same shape and alignment in 3D space.

On the left are a couple of screen-shots of the bounding volumes, rendered as wireframe spheres. The root node is red, general models are white, and sprites are green. Resources are added to the scene from the top down, with renderable objects like models and sprites in the leaves of the tree. This hierarchy makes it easy to cull non-visible groups – if a node isn’t visible, then none of its children can be visible either. It also simplifies mouse picking and collision as these tests are only performed where necessary. Batching visible polygons by texture becomes easier, which greatly improves rendering times. And finally, linking up chained action records becomes simplified thanks to each scene node being a discrete class aware of its own state.

Action Chains

Certain objects in Daggerfall have action records that describe how this object can move, rotate, etc. An example every Daggerfall player will be familiar with is the throne room in Privateer’s Hold (pictured left). When you throw the switch an action record makes the lever rotate, which chains to another action record to make the platform rise, which finally chains to the throne to make it rise also. The end result is you throw the switch and the platform rises (along with the throne and the player).

If you open up the screenshot to the left, you will see a red line going from switch, to platform, to throne. This is the action chain linking the objects together. The next screenshot shows the same linkage in Mantellan Crux – from the axe on the floating island, to the crossbow, to the giant sword. At some point, I will number the nodes so it becomes easier to visualise where action chains begin and end.

In Daggerfall Modelling Beta 1 it will be possible to trigger action chains and watch the animations run. There are other actions, such as teleporting the player or casting spells, but I am only going to support action that cause things to move around the scene.

Once gravity and collision are implemented, you will be able to explore dungeons from a first-person perspective with the ability to operate doors and platforms. You can also just turn off collision and gravity to fly around the scene with total freedom.

Add-In Components

The renderer in XNALibrary can plug in components that add functionality. This is quite basic at the moment, but will be extended down the road as needed.

Currently, the add-ins are a billboard manager for rendering sprites like trees, animals, NPCs, and a sky manager for drawing the background sky in location exteriors. You can see these components in action to the left.

The next release of Daggerfall Modelling should be ready in 3-4 weeks. This will coincide with an update to the Daggerfall Connect library download with XNALibrary updates included. All of this is in the SVN now, if you want to download the code as it’s being written. Just keep in mind not everything may be in a stable state in the latest SVN version.

Improving UV Generation

I recently engaged in a fruitful conversation on the DaggerXL forums with another DF hacker called DigitalMonk, whose specialty is Daggerfall’s textures. His interest was piqued when I made an informal comment regarding UV generation. After bouncing a couple of posts off each either, DigitalMonk’s thoughts and observations resulted in improvement to my current method of UV generation. He also made a discovery that will result in yet another improvement down the track. If you want to track the whole conversation, read back from here. I will summarise the outcome below so you don’t have to re-read the whole thread unless you want to.

Anyone who used Daggerfall Explorer would have noticed some 3D objects have skewed textures. At the core of this problem is how Daggerfall stores UV coordinates (UV mapping is a way of describing how textures are painted on a mesh). Rather than storing a value from 0.0 to 1.0 for every point, Daggerfall only describes UV coordinates for the first three points of any face (and optionally on the fourth point, but research has shown DF does not use this value). This probably doesn’t sound so bad, triangles only have three points right? Unfortunately Daggerfall doesn’t use triangles to describe 3D objects, it uses ngons that can exceed a dozen points per face. With only the first three UV coordinates to work from, the problem is how do we calculate UVs for the remaining points? Check out the main article on the UESP for more information on the problem and the solution we’ve been using up until now. You get some feeling from this short article on how much special handling is needed to cover all possible faces.

The problem with this solution is the constructed matrix can generate UVs that look a little weird, even when the determinant for that linear equation believes the face should be solvable. There are also multiple linear equations based on whether the face is coplanar to XZ, XY, YZ, etc. When UV generation failed, branching logic was needed to post-fix the UVs for certain faces, or skip the matrix generator for another technique, or just slap the correct UVs on directly. All of this made UV generation a bloated exercise that was tricky to debug. Fixing one problem would often break something else. All in all, there are three classes of texture problems:

  • Type 1. These are faces for which UVs cannot be generated using the existing linear equations, or are generated incorrectly.
  • Type 2. These are faces with bad source UVs (i.e. the coordinates are incorrect in Daggerfall’s files). Check out the eastern side of Wayrest Castle in-game for an example of this problem. This class of problem is visible inside the game.
  • Type 3. I used to think these were Type 2 problems, but DigitalMonk discovered these source UVs actually have extra bits packed into the coordinate. By removing these bits, the UV coordinate works as expected. This class of problem is not visible in the game, meaning Daggerfall uses these bits for some purpose, or at the very least just strips them out.

After my conversation with DigitalMonk, I’ve made some improvements to my UV generation that removes all Type1 problems with no special handling required. I managed this after realising UV generation is really just a 2D problem in 3D space. All of those extra linear equations were only needed due to the extra degree of freedom. By moving all the points into 2D first, I would only need the single XY linear equation to solve for all possible faces.

This worked perfectly, and allowed me to remove all my special handling. Below are a couple of screenshots from the test block viewer. The screenshots on the left demonstrate some problem faces with all special handling disabled. You don’t see these problems in the RDB Block Viewer demo included with DFConnect because of that special handling. On the right is the same scene using the new UV generator, with absolutely no special handling. All UV coordinates “just work”.

uv fixing 1 uv fixing 2
uv fixing 3 uv fixing 4

The next step will be to strip those extra bits for Type3 problems, and eventually write a simple UV patching function for the worst Type2 problems. Once this is completed, UV generation in DFConnect will be almost perfect.

I can’t thank DigitalMonk enough for taking the time to clarify the various UV problems and help me reach a better solution. Cheers mate.