tag:blogger.com,1999:blog-9358006928927759162024-02-08T02:38:33.526+02:00Z-Fight ClubEngine and Game developmentTimo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.comBlogger40125tag:blogger.com,1999:blog-935800692892775916.post-21799897454371006502020-06-21T09:35:00.000+03:002020-06-21T09:35:13.082+03:00Blog Moved!This blog has moved to <a href="http://twiren.kapsi.fi/blog.html">http://twiren.kapsi.fi/blog.html</a>Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-50612683767025417852018-06-19T20:47:00.000+03:002018-06-19T20:47:32.864+03:00Optimizing Metal graphics and compute code on an iPad Pro<b>TL;DR: I got Sponza scene with 2048 point lights running on an iPad Pro at 60 FPS.</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjca97avdL8Ihm1aPK3qNwLG_UdoQmkJ6ZNDP8oSBgvJEm_tVEm3qNy53sGQjWTvjgfogNUtoCUBF6jnQHXxnFJ-yBvw-r5EmwjBuP8w8ypG5y0swZ1iWBVoNETOxUlzX1zpxMhcgoydog/s1600/sponza_ipad.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="461" data-original-width="614" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjca97avdL8Ihm1aPK3qNwLG_UdoQmkJ6ZNDP8oSBgvJEm_tVEm3qNy53sGQjWTvjgfogNUtoCUBF6jnQHXxnFJ-yBvw-r5EmwjBuP8w8ypG5y0swZ1iWBVoNETOxUlzX1zpxMhcgoydog/s320/sponza_ipad.jpg" width="320" /></a></div>
<b><br /></b>
<a href="https://github.com/bioglaze/aether3d">My engine</a> has supported Metal for a long time, but I haven't really optimised the Metal renderer before. In this post I go through the process of optimizing Sponza scene with 2048 dynamic point lights from non-interactive frame rates to 60 FPS on an iPad Pro 10.5". I learned a lot and wanted to share my learnings.<br />
<br />
<h3>
Apple's profiling tools</h3>
I started my optimisation journey by taking a GPU frame capture in Xcode. Right away I saw that Apple has added a lot of useful features that were missing or less informative in the past. I was delighted to see that it now has GPU counters, timings on source lines and optimisation tips ("Remarks"). Setting a conditional breakpoint to capture a GPU frame at a fixed frame number got me slightly more consistent results than randomly pressing the capture button. A very good feature is the ability to edit a shader during the capture and see the results without restarting. Xcode and its associated tools are still buggy and crash often. I also often got an annoying "No capture boundary detected" error.<br />
<br />
<h3>
Initial capture</h3>
<div class="separator" style="clear: both; text-align: center;">
</div>
The starting point doesn't look good, 16 FPS. My Forward+ light culling takes a whopping 55.95 ms and uses 3 600 628 000 ALU ops. Let's see what we can do. From the source line timing view I saw that calculating minimum and maximum z-value for a tile is very slow (59.9 %):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg66S441-f3RZhyjtOCi1sSj_hqWiZAtqUALE4Sh_uG1sIAHtoWvF-7vYL-hxZM5H_yL40AJDZZTPKPSopjD1qqFIEK6txPevU9KnOWj1U4AA-SidlzZ3TCeXJe8_6FAHBIr4qceADpqxI/s1600/atomic_slow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1000" data-original-width="1600" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg66S441-f3RZhyjtOCi1sSj_hqWiZAtqUALE4Sh_uG1sIAHtoWvF-7vYL-hxZM5H_yL40AJDZZTPKPSopjD1qqFIEK6txPevU9KnOWj1U4AA-SidlzZ3TCeXJe8_6FAHBIr4qceADpqxI/s320/atomic_slow.png" width="320" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqi_DVuAzZmmRWlVLuFkZnb9_GuaO2VRVStxKdXAVOCuaC6JdBbCWn16L3CcGdarPA92lL1cqmh2CuQz6k0Pz-XAOXxu8ow89xSfcS6-0_LWxvnw6qDb9JNV76oLblsNcjDZhzC9_f5KE/s1600/baseline.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="487" data-original-width="1600" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqi_DVuAzZmmRWlVLuFkZnb9_GuaO2VRVStxKdXAVOCuaC6JdBbCWn16L3CcGdarPA92lL1cqmh2CuQz6k0Pz-XAOXxu8ow89xSfcS6-0_LWxvnw6qDb9JNV76oLblsNcjDZhzC9_f5KE/s640/baseline.png" width="640" /></a></div>
<h3>
<br /></h3>
<h3>
Optimization</h3>
What if I don't use depth bounds? Light culling now takes 13.53 ms and 2 234 827 000 ALUs, and I already got 60 FPS. But let's not stop here! I watched the excellent WWDC 2016 talk <a href="https://developer.apple.com/videos/play/wwdc2016/606/">Advanced Metal Shader Optimization</a> . Xcode's Remarks section warned about buffer preloading. I <a href="https://stackoverflow.com/questions/50774473/how-to-fix-buffer-preloading-failed-compute-shader-performance-issue">tried to fix</a> them but couldn't find a way. My first optimisation was to provide the horizontal and vertical tile counts as uniforms instead of calculating them in the shader. Percentage for those code lines decreased from 1.8 % to 1.4 % and ALU from <span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"> </span>2 234 819 000 to 2 208 126 000. The WWDC video suggested to use shorts instead of ints: 2 205 648 000 ALUs.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW8fxu1oRxDTcHRD-sXaHx85tPkSnufZXLqp6Kj09LSjzuw301vStgo7zo4y3V4bkL838kp-Wp1XjwKnT6FAavBg035m8F7NvsMVcQ-7aiBhUGzObmRJ0M4gDuo1fhleOHBylFAGzwfcw/s1600/optimized.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="451" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW8fxu1oRxDTcHRD-sXaHx85tPkSnufZXLqp6Kj09LSjzuw301vStgo7zo4y3V4bkL838kp-Wp1XjwKnT6FAavBg035m8F7NvsMVcQ-7aiBhUGzObmRJ0M4gDuo1fhleOHBylFAGzwfcw/s640/optimized.png" width="640" /></a></div>
<br />
<h3>
Future</h3>
<div>
There's a lot of room for improving the results further, and it's needed. I didn't focus on my material shader in this post because my current engine's shading model is not yet physically based unike my old engine's and I'm not using post processing effects. I use floats in many places where a half would do. I also don't use texture compression, but my engine already supports ASTC so it's just a matter of encoding Sponza textures. My light culler could use parallel reduction or clustered culling.</div>
<div>
Metal 2 has some features that could help, like tile shading, function specialization, resource heaps and argument buffers. My plan is to study them next, the concepts are already familiar to me from other graphics APIs. The engine is open source and can be <a href="https://github.com/bioglaze/aether3d">downloaded from GitHub</a>.</div>
Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-81740532263941815512018-04-10T18:14:00.001+03:002018-04-10T18:14:58.766+03:00My computing habitsI thought my personal computing habits differ from the norm, so I decided to write about how I do stuff, focusing on hardware in this post.<br />
<br />
<h3>
Choosing which device to use</h3>
When deciding to do some computing task, eg. reading a website, this is my preferred order of devices to use: Phone or iPad -> old laptop (2010) -> laptop (2013) -> desktop<br />
I use my iPad Pro 10.5" more than any other device, including PCs. I like its responsiveness, security, small form factor and UX in most of my computing uses. Sadly, I can't really code on it, but I'll keep searching for solutions to that. Maybe WebGL development is possible. The desktop is my least used device, I practically only use it for VR, Vulkan and D3D12 development and some games I don't have on consoles. One of the reasons for this preferred order is power consumption iPad uses less power than a laptop. My PC OS preferred order is Ubuntu -> macOS -> Windows.<br />
<br />
<h3>
Slow hardware upgrade cycle</h3>
I rarely buy new hardware. For example some of my HDDs are spinning disks because they still work and I don't want to discard working stuff. Modern hardware is so fast that upgrade cycles can be long. My current laptop is from 2013 and I see no reason to replace it in the foreseeable<br />
future. I don't judge people who buy new stuff often, but I personally just don't have a reason to buy a new phone every 2 years or so. My last phone cycle was 4 years and the old is still in good condition. I only buy new stuff when I have a compelling reason. It's also more environmentally friendly to use devices for a longer time before getting a new one. People often criticise Apple for planned obsolescence, but I beg to differ. Their devices get security updates for longer time than competitors and I still actively use a MacBook Pro from 2010, even without an SSD.<br />
<br />
If people would replace their hardware less often, software developers would have more<br />
incentive to optimise their code and use resources more efficiently. Viznut has <a href="https://countercomplex.blogspot.fi/2013/07/slower-moores-law-wouldnt-be-that-bad.html">written more</a> on this topic and I recommend every eco-conscious or performance caring reader to read his writings.<br />
<br />Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-31383991121222520612016-08-20T16:09:00.000+03:002016-08-20T16:09:34.491+03:00How a frame is rendered in Aether3D?<a href="https://github.com/bioglaze/aether3d" target="_blank">Aether3D </a>is a component-based game engine supporting modern rendering APIs. Currently there is no lighting, but I'm porting <a href="https://bioglaze.blogspot.fi/2014/07/2048-point-lights-60-fps.html" target="_blank">my Forward+</a> implementation to it soon. While there is no lighting at the moment, there are directional and spot light shadows. In this post I will run through steps to render one frame.<br />
<br />
<b>Scene </b>object contains <b>GameObjects. </b>GameObjects containing component types <b>SpriteRendererComponent</b>, <b>MeshRendererComponent </b>and <b>TextRendererComponent </b>will be rendered by those GameObjects that contain <b>CameraComponents</b>. Cameras can render into the screen or into a render-texture. If <b>DirectionalLightComponent </b>or <b>SpotLightComponent </b>has its shadow-flag enabled, those will cause a special camera to render their shadow maps.<br />
<h3>
<br /></h3>
<h2>
Rendering steps:</h2>
<h3>
1. Scene.Render()</h3>
<br />
This method first does housekeeping work needed to render a frame: resets statistics, begins a new render pass, acquires the next swapchain image depending on API etc.<br />
<br />
Then it calculates an axis-aligned bounding box for the whole scene (needed for shadow frustum calculation) and updates transformation matrix hierarchy.<br />
<br />
Then it collects game objects with camera components into a container and sorts it depending on camera type (render-texture, normal) and its layer etc.<br />
<br />
<h3>
2. Scene.RenderWithCamera()</h3>
<br />
First, render-texture cameras are looped and this method is called, once for a normal camera and six times for a cube map camera. Then, shadows are rendererd. Finally, cameras rendering directly into the screen are rendererd. If any cameras also want to render depth and normals into a texture (can be used later in post-processing and lighting effects), it's done after this step.<br />
<br />
Camera's clear flag is applied at the beginning of this method (clear color, clear depth, don't clear).<br />
<br />
At this point, skybox is rendered.<br />
Then, camera's frustum is calculated.<br />
Now game objects are looped and objects containing sprite renderer or text renderer are rendered. Mesh renderer objects are collected and sorted by their distance, then rendered. They reference a <b>Material </b>which feeds the blending state, culling state, shader and uniforms into the renderer.<br />
<br />
<h3>
3. GfxDevice::Draw()</h3>
<br />
Everything that's rendered uses a method from GfxDevice namespace:<br />
<span style="font-family: "Courier New",Courier,monospace;">void ae3d::GfxDevice::Draw( VertexBuffer& vertexBuffer, int startIndex, int endIndex, Shader& shader, BlendMode blendMode, DepthFunc depthFunc,<br /> CullMode cullMode )</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>This method first calculates a pipeline state object (PSO) hash and if it's not found in cache, it creates a new PSO.<br />
On Vulkan and D3D12 renderer, descriptor set is filled with draw parameters and the actual drawing uses <span style="font-family: "Courier New",Courier,monospace;">vkCmdDrawIndexed() <span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">or </span>DrawIndexedInstanced(). </span><br />
<br />
<h2>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: Georgia,"Times New Roman",serif;">Future work</span></span></h2>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: Georgia,"Times New Roman",serif;">There is room for improvement, as the engine is still in its early stages (<a href="https://docs.google.com/document/d/1jKuEkIUHiFtF4Ys2-duCNKfV0SrxGgCSN_A2GhWeLDw/edit" target="_blank">v0.6 under development</a>).</span></span><br />
<br />
<span style="font-family: Times,"Times New Roman",serif;">1. PSO objects are expensive to generate so it would be better to generate them before the main loop. </span><br />
<span style="font-family: Times,"Times New Roman",serif;">2. There is no instancing support yet.</span><br />
<span style="font-family: Times,"Times New Roman",serif;">3. Too little profiling done so far as the main goal has been to get things to work on all APIs (Vulkan, OpenGL, Metal, D3D12).</span><br />
<span style="font-family: Times,"Times New Roman",serif;">4. Handling transparency, this is actually currently in development.</span><br />
<br />Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-21881839106436564242016-03-12T20:59:00.001+02:002016-03-12T20:59:24.162+02:00Debugging Graphics<h2>
Intro</h2>
I develop a lot of graphics code using Vulkan, OpenGL, D3D12 and Metal and have found the following methods to make my life easier when something doesn't render right:<br />
<br />
<h2>
Enable the debug layer</h2>
Enable your API's debug output and check for runtime errors and warnings and fix them all.<br />
<h3>
</h3>
<h3>
D3D12</h3>
<div style="text-align: left;">
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">ID3D12Debug* debugController;<br /> const HRESULT dhr = D3D12GetDebugInterface( IID_PPV_ARGS( &debugController ) );<br /> if (dhr == S_OK)<br /> {<br /> debugController->EnableDebugLayer();<br /> debugController->Release();<br /> }<br />... CreateDevice()...<br /> hr = device->QueryInterface( IID_PPV_ARGS( &infoQueue ) );<br /> infoQueue->SetBreakOnSeverity( D3D12_MESSAGE_SEVERITY_ERROR, TRUE );</span></blockquote>
</div>
<br />
<h3>
OpenGL</h3>
<br />
See <a href="https://www.opengl.org/wiki/Debug_Output">https://www.opengl.org/wiki/Debug_Output</a><br />
<br />
<h3>
Vulkan</h3>
<br />
Sidenote: On my system RenderDoc crashes if I try to attach a program that has debug layer enabled.<br />
First you need to install LunarG Vulkan SDK from <a href="http://lunarg.com/vulkan-sdk/">http://lunarg.com/vulkan-sdk/</a><br />
I contain my debug layer code inside a namespace like this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">namespace debug<br />{<br /> PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback = nullptr;<br /> PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback = nullptr;<br /> PFN_vkDebugReportMessageEXT dbgBreakCallback = nullptr;<br /><br /> VkDebugReportCallbackEXT debugReportCallback = nullptr;<br /> const int validationLayerCount = 9;<br /> const char *validationLayerNames[] =<br /> {<br /> "VK_LAYER_GOOGLE_threading",<br /> "VK_LAYER_LUNARG_mem_tracker",<br /> "VK_LAYER_LUNARG_object_tracker",<br /> "VK_LAYER_LUNARG_draw_state",<br /> "VK_LAYER_LUNARG_param_checker",<br /> "VK_LAYER_LUNARG_swapchain",<br /> "VK_LAYER_LUNARG_device_limits",<br /> "VK_LAYER_LUNARG_image",<br /> "VK_LAYER_GOOGLE_unique_objects",<br /> };<br /><br /> VkBool32 messageCallback(<br /> VkDebugReportFlagsEXT flags,<br /> VkDebugReportObjectTypeEXT,<br /> uint64_t<span style="font-family: "courier new" , "courier" , monospace;">, </span>size_t, int32_t msgCode,<br /> const char* pLayerPrefix, const char* pMsg,<br /> void* )<br /> {<br /> if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)<br /> {<br /> ae3d::System::Print( "Vulkan error: [%s], code: %d: %s\n", pLayerPrefix, msgCode, pMsg );<br /> }<br /> else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)<br /> {<br /> ae3d::System::Print( "Vulkan warning: [%s], code: %d: %s\n", pLayerPrefix, msgCode, pMsg );<br /> }<br /><br /> return VK_FALSE;<br /> }</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> void Setup( VkInstance instance )<br /> {<br /> CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( instance, "vkCreateDebugReportCallbackEXT" );<br /> DestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( instance, "vkDestroyDebugReportCallbackEXT" );<br /> dbgBreakCallback = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( instance, "vkDebugReportMessageEXT" );<br /><br /> VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;<br /> dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;<br /> dbgCreateInfo.pNext = nullptr;<br /> dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)messageCallback;<br /> dbgCreateInfo.pUserData = nullptr;<br /> dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;<br /> VkResult err = CreateDebugReportCallback( instance, &dbgCreateInfo, nullptr, &debugReportCallback );</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<br />
When creating Vulkan instance, I append VK_EXT_DEBUG_REPORT_EXTENSION_NAME into <span style="font-family: "courier new" , "courier" , monospace;">instanceCreateInfo.ppEnabledExtensionNames</span>.<br />
When creating Vulkan device, I pass validation layers like this:<span style="font-family: "courier new" , "courier" , monospace;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">deviceCreateInfo.enabledLayerCount = debug::validationLayerCount;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">deviceCreateInfo.ppEnabledLayerNames = debug::validationLayerNames;</span><br />
<br />
<h2>
Use debug names</h2>
Debug names appear in graphics debugger tools and validation layer messages so they help you find the object.<br />
<br />
<h3>
OpenGL</h3>
You'll need to make sure extension KHR_debug is available before using these functions:<br />
<span style="font-family: "courier new" , "courier" , monospace;">glObjectLabel( GL_TEXTURE, textureHandle, nameLength, name );<br />glObjectLabel( GL_PROGRAM, shaderHandle, nameLength, name );<br />glObjectLabel( GL_FRAMEBUFFER, fboHandle, nameLength, name );</span><br />
etc.<br />
<br />
<h3>
D3D12</h3>
<span style="font-family: "courier new" , "courier" , monospace;">ID3D12Resource* texture = ...;<br />texture->SetName( L"texture" );</span><br />
<br />
If you need to convert a const char* into an LPCWSTR you can do it like this:<span style="font-family: "courier new" , "courier" , monospace;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">wchar_t wstr[ 128 ];</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">std::mbstowcs( wstr, my_string.c_str(), 128 );</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">texture->SetName( wstr );</span><br />
<br />
<h3>
Metal</h3>
Many objects have a .label property:<br />
<span style="font-family: "courier new" , "courier" , monospace;">metalTexture.label = @"texture";</span><br />
<br />
<h2>
Use tools</h2>
These tools can be used to verify the rendering process by inspecting textures, render targets, buffers, rasterizer state etc.<br />
RenderDoc is a good debugger for D3D11, OpenGL and Vulkan.<br />
For D3D12 Visual Studio's own debugger is good. You can get it by installing "graphics tools" in Windows 10: Settings->System->Apps & Features -> Manage optional features<br />
OpenGL ES and Metal users on Mac will probably use Xcode's debugger.<br />
AMD and NVIDIA also have tools for this.<br />
<br />
<h2>
Shader debugging</h2>
GLSL shaders can be compiled and checked for errors by glslangValidator. You can make it a part of your build process for extra credit. You can also use general static analysis tools like PVS-Studio or CppCheck. Using these tools I have found uninitialized variables in rendering code etc. Writing a shader hot-reloading system is not a big task but it pays off: Imagine debugging a video blitting shader that shows wrong colors. You can pause your game on a frame, modify the shader and see the results in that video frame instantly. You can also make the system take a screenshot before and after recompilation to more easily compare the results in an external program like Photoshop.<br />
<br />
<h2>
Test on multiple GPUs, even from the same vendor</h2>
There are differences in how textures are initialized (garbage or white/black etc.), resource transitions are done, flags are handled etc.<br />
<br />
<h2>
Conclusion</h2>
Many things can and will go wrong when rendering but there are features like validation layers and graphics debuggers that make finding the problem easier. <br />
Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-60184264459391311632015-12-25T12:54:00.000+02:002015-12-25T12:54:53.724+02:00Aether3D Status ReportI've been busy at <a href="http://www.xbox.com/en-US/games/quantum-break" target="_blank">work</a> this fall so I don't have time to work on my own engine as often I'd like to. Anyways, I'm happy to share that I now have a working Metal renderer (both iOS and OS X). Using MetalKit I can do the view setup identically on both operating systems and there are very few different code paths. D3D12 renderer is progressing well. I'll also implement Vulkan renderer as soon as public implementation becomes available.<br />
<br />
VR rendering needs more love. I'm naively rendering the scene twice. For example culling and shadow rendering can be done only once. I don't have my own HMD yet but borrow my employer's DK2 sometimes. I'll also add Vive support as soon as possible. I started making a VR game on Unreal Engine to get to know VR and Unreal better. It's a System Shock inspired space ship exploration game. The plan is to utilize Vive's controllers or Oculus Touch to interact with computers, drawers etc.<br />
<br />
When implementing new APIs I'm focusing first on getting things working and later making the implementation as efficient as possible. For example, I'm using only one command list in my D3D12 renderer but will add more soon.<br />
<br />
Visual Studio's and Xcode's frame capture has been very useful in debugging these new renderers and to make things easier I've added debug names to every API object.<br />
<br />
Next year I plan to hit Aether3D 1.0 (0.5 is now under development).Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-39782167866075750852015-07-03T13:03:00.001+03:002015-07-03T13:03:43.848+03:00Software QualityWhen developing software, here's a partial list of things I value:<br /><br />
<h3>
Build using multiple compilers</h3>
Sometimes you forget to #include something from a standard library that works on one compiler but doesn't on another or use constructs that don't compile on all compilers.<br /><br />
<h3>
Static Analysis and high warning levels</h3>
I regularly run PVS-Studio, CppCheck and Clang static analyzers and use very high warning levels on my compilers.<br />
<blockquote class="tr_bq">
My GCC warnings: -Wall -pedantic -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization \<br /> -Wdouble-promotion -Wformat=2 -Winit-self -Winvalid-pch -Wlogical-op -Wmissing-include-dirs \<br /> -Wshadow -Wredundant-decls -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wtrampolines \<br /> -Wunsafe-loop-optimizations -Wvector-operation-performance -Wzero-as-null-pointer-constant</blockquote>
<br />
<h3>
Unit Tests</h3>
I have found many bugs using them. Excellent for math stuff, serialization testing, error handling testing etc.<br /> <br />
<h3>
Test graphics code on multiple GPUs</h3>
Some drivers accept bad syntax in shaders or have bugs. I test my graphics code on NVIDIA, AMD and Intel GPUs on Windows, OS X and on Linux.<br /><br />
<h3>
Minimize variable scope</h3>
Declare variables as close as possible to their use site and try to make them const. Ternary operator and lambdas can help here.<br /> <br />
<h3>
Avoid Boolean arguments</h3>
They make the code hard to read and often break the Single Responsibility Principle.<br /><br />
<h3>
Avoid magic numbers</h3>
Same as above.<br /><br />
<h3>
Collect metrics</h3>
Use tools like Metrix++ to get metrics like line count, cyclomatic complexity etc.<br /><br />
<h3>
Extract complex conditionals into descriptive variables and methods.</h3>
<br />
<h3>
Look out for <a href="https://en.wikipedia.org/wiki/Code_smell" target="_blank">Code Smells</a></h3>
<br />
<h3>
Comment things that can't be expressed in code</h3>
First try to name your variables/methods so the comments are unnecessary. Only after that write comments.<br />Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-37718989510201946892015-05-16T15:15:00.002+03:002015-05-16T15:15:37.859+03:00What I've been doing recentlyI've been adding graphics features into my <a href="https://github.com/bioglaze/aether3d" target="_blank">new engine</a> slowly because I don't want to write a lot of code that will be replaced by newer APIs. Regarding that, I made an <a href="https://github.com/bioglaze/aether3d/tree/ios" target="_blank">iOS branch</a> into GitHub and I'm learning Metal and already got a textured quad to render. So far the best resource for learning has been <a href="http://metalbyexample.com/">http://metalbyexample.com</a>. I also installed Windows 10 preview and VS 2015 RC into my secondary laptop and are learning D3D12. When I have more experience on D3D12 and Metal, I'll merge my renderers into the master branch. I'm also waiting for Vulkan and reading <a href="http://www.amd.com/Documents/Mantle-Programming-Guide-and-API-Reference.pdf" target="_blank">Mantle documentation</a> until it's out.<br />
<br />
Writing only engine code would not be so productive, so I'm working on two small games at the moment. The first one is a desktop FPS made using Unity 5.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbo-8iW-9AqSyCuoUBWCKgdxYmqW4yAOTb6ZQH9FrGbjsTwXomazSNIl265Zn2Z35aN7NcQh6jdcB0CYjaaI5uAaHOYFW7Xjcqr-Jy6zAiIp04a-h0i5BLobedIjhYumsowbGEJSiF6j0/s1600/fps.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbo-8iW-9AqSyCuoUBWCKgdxYmqW4yAOTb6ZQH9FrGbjsTwXomazSNIl265Zn2Z35aN7NcQh6jdcB0CYjaaI5uAaHOYFW7Xjcqr-Jy6zAiIp04a-h0i5BLobedIjhYumsowbGEJSiF6j0/s320/fps.jpg" width="320" /></a></div>
<br />
Some of the assets are downloaded from sites like <a href="https://freesound.org/">https://freesound.org</a>, <a href="http://pamargames.com/">pamargames.com</a> and <a href="http://cgtextures.com/">cgtextures.com</a> but some are done by myself. While the level design/art design/balancing is amateurish, I'm paying special attention to polishing feedback like dealing/receiving damage, transitions, sounds, bullet holes etc. When the game is ready, I'll put the player into my website along with the project folder. I also ditched the built-in MonoDevelop in favour of Xamarin Studio which is faster, but Unity overrides my formatting options.<br />
<br />
My other game under construction is a roguelike using my own engine.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgQ-nAE75JtARN9vUqt5AmH7DqXLZlHkb-W1otuK0qMEqMyf4pK9k987rtSMhypDvsCOozLPgEQG2Mh67fYc1nVMRp77Pu7rsQMrF2Z5_uxeaCE1BfFTF0SvA9-zUENciuPgtAX0Srdc8/s1600/roguelike.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgQ-nAE75JtARN9vUqt5AmH7DqXLZlHkb-W1otuK0qMEqMyf4pK9k987rtSMhypDvsCOozLPgEQG2Mh67fYc1nVMRp77Pu7rsQMrF2Z5_uxeaCE1BfFTF0SvA9-zUENciuPgtAX0Srdc8/s320/roguelike.jpg" width="320" /></a></div>
<br />
I haven't decided on the final design or platform. If my iOS/Metal renderer advances quickly, it would be nice to test the game on my iPhone 5S. Using my own engine for a game development has been productive since it has uncovered some bugs that would have bitten me later on otherwise. Also the new engine's component-based game object system has been nice to use in this game. Some of the used components are <a href="https://github.com/bioglaze/aether3d/blob/master/Engine/Include/TextRendererComponent.hpp" target="_blank">TextRendererComponent</a>, <a href="https://github.com/bioglaze/aether3d/blob/master/Engine/Include/SpriteRendererComponent.hpp" target="_blank">SpriteRendererComponent</a>, <a href="https://github.com/bioglaze/aether3d/blob/master/Engine/Include/TransformComponent.hpp" target="_blank">TransformComponent</a> and <a href="https://github.com/bioglaze/aether3d/blob/master/Engine/Include/AudioSourceComponent.hpp" target="_blank">AudioSourceComponent</a>.<br />
<br />
Next steps in my engine will be a virtual filesystem which enables faster load times by packing multiple files into one. While making the iOS port I'll also be writing NEON SIMD matrix operations. I'm also making a multi-platform scene editor using Qt and its first version will be included in the next engine release.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com4tag:blogger.com,1999:blog-935800692892775916.post-18741984777557860672015-03-08T16:45:00.000+02:002015-03-08T16:45:36.177+02:00GDC 2015, Vulkan etc.Game Developers Conference was held on March 2-6 at San Francisco.<br />
<br />
Unity 5 was finally released. I have used it since beta 1 and I'm happy about the licensing change that allows all engine features to be used on the personal edition. Epic also dropped the subscription fee for Unreal Engine lowering the entry barrier for casual developers, as those who are serious have already subscribed. With the Valve announcement of Source 2 being free there's now good competition on indie-friendly engines.<br />
<br />
John Carmack talked about mobile VR, noting that it has potential to reach far more customers than desktop VR. I have always liked Carmack's talks, he just sits there without any materials and talks as long as allowed.<br />
<br />
Khronos revealed Vulkan, the successor to OpenGL and OpenGL ES. It's evolved from Mantle and the abstractions are similar to D3D12, making the driver more simple. It also allows more control than OpenGL in that you have to allocate memory yourself, avoid hazards and handle threading. More control means more potential for performance but also more responsibility. There will, however, be an optional debug layer that should help the developer to find problems. Shaders will finally be supported in an IR, making them load faster and, more importantly, alleviating compatibility problems caused by subtle differences in parsers ie. different drivers accepting broken syntax or not accepting correct syntax.<br />
<br />
All these new APIs (Metal, Mantle, D3D12, Vulkan) provide abstractions that are different from earlier APIs. Personally, for me that means that when I begin writing my new engine's renderer, I'll follow those abstractions. I cannot use D3D12 or Vulkan today and probably will begin writing a new renderer before they are out (I'll be starting my new engine development next week), but I will try to design the renderer to allow for the sane usage of those new APIs when they are released and will be implementing D3D12 and Vulkan renderers as soon as they are released.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-3610709879140330022014-12-30T22:23:00.000+02:002014-12-30T22:28:27.505+02:00Planning Aether3D Rewrite For 2015In 2015 spring I'll rewrite my engine again.<br />
<br />
It's that time again, the moment when I look at my <a href="http://twiren.kapsi.fi/aether3d.html" target="_blank">engine</a>, started in 2013, and an accumulated list of architecture improvements that are easier to put into a completely new engine instead of shoehorning them into the current engine. I know that rewriting is a <a href="http://joelonsoftware.com/articles/fog0000000069.html" target="_blank">thing you should never do</a>, but I am quite satisfied with the current engine and can take many things into the new engine. <a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672" target="_blank">Refactoring</a> could get my current engine into the right direction, but some of the things I want are just too big for current architecture.<br />
<br />
<h3>
Rationale:</h3>
<h3>
</h3>
<h3>
Static linkage/getting rid of virtual methods</h3>
<br />
My current engine's whole API is exposed as virtual methods, but I want to use them only where it really makes sense. Static linkage also makes configuration easier, not having to worry about DLL's location or format.<br />
<br />
<h3>
Singletons/global state</h3>
<br />
Current engine has some, but I want none.<br />
<br />
<h3>
Entity-Component System</h3>
<br />
I want to compose game objects by combining components like in Unity. <a href="http://effectivesoftwaredesign.com/2012/02/05/separation-of-concerns/" target="_blank">Separation of Concerns</a> FTW.<br />
<br />
<h3>
Memory labeling/profiling</h3>
<br />
I want to have a breakdown of memory used by a game, asset by asset. That means custom allocators, which will come handy because...<br />
<br />
<h3>
Minimizing dynamic allocation</h3>
<br />
I want to have a data-oriented cache-friendly design. When I create a texture, I want to get an index into a texture pool that was allocated on startup. I will analyze my main loop and minimize dynamic allocations. I will also check cache-misses.<br />
<br />
<h3>
API inside a namespace</h3>
<br />
My current engine's public API doesn't use namespaces, so collisions are possible. Not so in my next engine.<br />
<br />
<h3>
More const-correct design</h3>
<br />
While my current engine is mostly const-correct, more objects and state could be made <a href="http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html" target="_blank">immutable</a>, making reasoning about program's behaviour easier.<br />
<br />
<h3>
Compute shaders aren't exposed</h3>
<br />
My current engine uses them internally for <a href="http://bioglaze.blogspot.com/2014/07/2048-point-lights-60-fps.html" target="_blank">light culling</a>, but I want to give the power to you, the user.<br />
<br />
<h3>
Post-Processing Effects can be pulled out</h3>
<br />
<a href="http://effectivesoftwaredesign.com/2012/02/05/separation-of-concerns/" target="_blank">Separation of Concerns</a>, baby. Unity has them as a separate package, I want to have them out of engine core, too.<br />
<br />
<h3>
<a href="http://gdcvault.com/play/1020791/" target="_blank">AZDO</a></h3>
<br />
Maybe not full-on, but definitely something to strive for.<br />
<br />
<h3>
Updated Renderer APIs</h3>
<br />
Exact versions can vary, but I aim for OpenGL 4.5, GLES 3.1, D3D11.1 and Metal. Need to figure out something for OS X, maybe 4.1.<br />
<br />
<h3>
Editor</h3>
<br />
Many open-source engines have editors that contain basic usability problems that are easy to fix. I have been prototyping a custom scene editor using Qt 5 for several months and believe I can make an editor that's not painful to use. The editor will be in sync with the engine and support new features as soon as they are implemented. It is by no means necessary to use the editor, the engine works without it, too.<br />
<br />
<h3>
<a href="http://github.com/" target="_blank">GitHub</a></h3>
<br />
The whole building of the new engine will happen on GitHub, right from the start. It gives the engine discoverability and easier management of issues/documentation/wiki etc.<br />
<br />
<h3>
<a href="http://www.joelonsoftware.com/articles/fog0000000012.html" target="_blank">Dogfooding</a></h3>
I want to have a real use-case for every feature I add to the engine. A simple game, a demo, whatever, just something real, released stuff.<br />
<br />
<h3>
Schedule</h3>
<br />
I will do research until GDC 2015 (March 2-6) and a few weeks after that I plan to make the first commit to GitHub.<br />
<br />Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-12268977334592662742014-12-07T20:54:00.001+02:002014-12-07T20:54:50.761+02:00Software Development TermsHere's a quick description of random software development terms. Most developers probably know them but someone could find something new.<br />
<br />
<b>Broken Windows Theory</b><br />
If there is a low-quality part of a codebase, people maintaining the codebase tend to produce more low-quality code.<br />
<br />
<b>Technical Debt</b><br />
Taking shortcuts by making intentionally suboptimal design or implementation decisions. Not always bad, not all debt have to be paid.<br />
<br />
<b>Dogfooding</b><br />
Using your own unfinished product in day-to-day work while developing it.<br />
<br />
<b>Code Smell</b><br />
Doing something that could lead to problems.<br />
<br />
<b>Heisenbug</b><br />
A bug that doesn't repro when debugging.<br />
<br />
<b>Cargo-Cult</b><br />
Applying a method that works in one context in a different context without knowing why it worked in the original context.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-85692631757086829352014-11-01T12:53:00.000+02:002014-11-01T12:53:59.094+02:00Use That ProfilerI was on a long bus trip and was bored, so I launched Xcode and worked on my game prototype. I wanted to see its memory usage so I opened Instruments Allocator tool. I noticed something strange, multiple OpenGL buffer creation calls on every frame. I figured out it must be in RenderQueue class because the screen was only showing 2D stuff:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPsQwj0HmHk3bX2Rcrm3BPAGHeTVF5yUwukH6ZTvZ5WNHpz-kxpu1JIUGGFGa-ELKbBZCpdAvA3DxMRaGxqXJcUaW2ljdzlV7CdZ90U4WYIs0ly-Bx4LgTFFgZ-X5Y1Nk5m8_BFMNq9Tw/s1600/game.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPsQwj0HmHk3bX2Rcrm3BPAGHeTVF5yUwukH6ZTvZ5WNHpz-kxpu1JIUGGFGa-ELKbBZCpdAvA3DxMRaGxqXJcUaW2ljdzlV7CdZ90U4WYIs0ly-Bx4LgTFFgZ-X5Y1Nk5m8_BFMNq9Tw/s1600/game.png" height="186" width="320" /></a></div>
But my render queue only creates GL buffers when it's full! Turns out I emptied the queue element container always after drawing, causing the next draw to generate it again. The solution was to let the container grow and reuse old elements. This bug has been in my engine since the beginning and only after actually starting to make a game I noticed it even though I have profiled the engine every now and then. Runtime buffer creation should be avoided as much as possible.<br />
<br />
Takeaway: profile your code early and often. Don't make engines <i>or</i> games, make both.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-67496827749487415252014-07-17T16:31:00.000+03:002016-09-18T11:40:48.642+03:002048 Point Lights @ 60 FPS<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_UytG8uG_THhJmMWzH1utcx-krDdhzo9wefStCPSsxzb2G6fhZWxU8DTtIb_-A6N7D4jwWOea6GCPMS_6PaxHcKuunCtrcr_Wgp1fb5Y23dix9XC4SPuJk8BViIROJwb3vT4xUP4ys7I/s1600/forward_plus.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_UytG8uG_THhJmMWzH1utcx-krDdhzo9wefStCPSsxzb2G6fhZWxU8DTtIb_-A6N7D4jwWOea6GCPMS_6PaxHcKuunCtrcr_Wgp1fb5Y23dix9XC4SPuJk8BViIROJwb3vT4xUP4ys7I/s1600/forward_plus.jpg" width="400" /></a></div>
Today I released an update to <a href="http://twiren.kapsi.fi/aether3d.html" target="_blank">my engine</a>. The most prominent feature<br />
is Forward+ aka Tiled Forward rendering. To my knowledge, no other<br />
open source engine implements it.<br />
<br />
Forward+ dates back to 2012 and is used in several AAA games like DiRT Showdown, Ryse (partially) and The Order: 1886. Its main advantages are support for MSAA, transparency and multiple BRDFs. The algorithm works by culling lights against a screen-space grid where cells are for example 16x16 pixels. It stores a per-tile list of light indices that are then used in shading. Culling is done with a compute shader and it uses depth texture to get each tile's extents. My engine does a depth-pass in any case, because it's needed for SSAO etc.<br />
<br />
The implementation I did is based on AMD's <a href="https://github.com/GPUOpen-LibrariesAndSDKs/ForwardPlus11/" target="_blank">ForwardPlus11 sample</a>. However, the sample is D3D11 and uses deprecated D3DX libs, but my implementation also supports OpenGL 4.3. I was able to render 2048 point lights at 60 FPS in Sponza scene on my year-old MacBook Pro (GeForce 750M). All the surfaces use Cook-Torrance with normal maps.<br />
<br />
If you want to implement it yourself or just test it, you can download <a href="http://twiren.kapsi.fi/aether3d.html" target="_blank">my engine</a>. Here's some API mappings from HLSL to GLSL:<br />
<span style="font-family: "courier new" , "courier" , monospace;">gl_WorkGroupID: SV_GroupID<br />gl_LocalInvocationID: SV_GroupThreadID<br />gl_GlobalInvocationID: SV_DispatchThreadID<br />uintBitsToFloat(): asfloat()<br />floatBitsToUint(): asuint()</span><br />
<br />
<br />
Here's some useful links:<br />
<a href="http://www.cse.chalmers.se/~olaolss/main_frame.php?contents=publications" target="_blank">http://www.cse.chalmers.se/~olaolss/main_frame.php?contents=publications</a><br />
<a href="http://aras-p.info/blog/2012/03/27/tiled-forward-shading-links/">http://aras-p.info/blog/2012/03/27/tiled-forward-shading-links/</a><br />
<a href="http://diaryofagraphicsprogrammer.blogspot.fi/2012/04/tile-based-deferred-and-forward.html">http://diaryofagraphicsprogrammer.blogspot.fi/2012/04/tile-based-deferred-and-forward.html</a><br />
<a href="http://articles.pjblewis.com/">http://articles.pjblewis.com/</a><br />
<a href="http://focus.gscept.com/2014ip19/">http://focus.gscept.com/2014ip19/</a><br />
<br />
So, what's next for <a href="http://twiren.kapsi.fi/aether3d.html" target="_blank">Aether3D</a>? I'll add spot lights soon and for the next release my focus will be in fixing open bugs and adding unit tests and support for CMake. I'm also planning to dogfood my engine this year by making a simple first-person game. Also, I'm working on an editor in Qt and C++. I'll blog more about them when they are more mature.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-83255213527780051882014-07-02T23:24:00.001+03:002014-07-02T23:24:53.886+03:006 Steps to Better CommitsWhile working on my multi-platform game engine, here's some things I have learned:<br />
<br />
<b>1. Run all unit tests or functionality tests before submitting.</b><br />
Should be a no-brainer, but laziness can lead to less testing and more bugs. Especially bad if you make many submits without testing because by then your search-space has grown needlessly. If you don't have any tests, stop reading this and write them now.<br />
<br />
<b>2. Submit only related changes.</b><br />
For example, if you are hunting down a bug and inspect a changelist with a message "Fixed texture loading" and the changelist also contains modifications to Audio.cpp, finding the bug takes more time.<br />
<br />
<b>3. Fix bugs before adding new features.</b><br />
Worst-case scenario would be when a new feature depends on a bug in an older feature and when the bug is finally fixed, the new feature stops working.<br />
<br />
<b>4. Describe all the changes you did in the commit message.</b><br />
If you browse through a commit log of a source file and find a commit with a message that has nothing to do with that file, you have not documented your commit well and it could be hard to reason about why the file was changed.<br />
<br />
<b>5. Diff all the changes to be submitted for sanity-check.</b><br />
Sometimes you have been making ad-hoc changes to test/debug a feature but forgot to revert them before submitting.<br />
<br />
<b>6. Try to submit a whole, working new feature in one submit instead of many non-working submits.</b><br />
If the feature is really big, you can split it into smaller submits if each of the smaller submits represents a logical unit that can work without other submits.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-214214830333161412014-03-23T18:16:00.000+02:002014-03-23T18:16:03.994+02:00GDC 2014Game Developers Conference 2014 was held on March 17-21, 2014 at Moscone Center, San Francisco. It was my fourth GDC so far, and in this blog post I’ll share some details.<br />
<br />
There were several big engine announcements. Unity unveiled Unity 5 which features real-time GI using Enlighten, full deferred shading, 64-bit editor, SpeedTree, physically-based built-in shader and a frame debugger among other features. PBS is great, but there are no area light sources. However, real-time GI helps a lot to that effect. Unity 4.6 will also have a new GUI system, but I avoid the topic because of an NDA.<br />
<br />
Epic launched much anticipated Unreal Engine 4 and surprised everyone by making the source code available on GitHub for developers. The licence cost is very indie-friendly at $20/mo, 5 % of sales excluding consoles. Monthly subscription (EaaS) seems to be the new trend among engines, Unity has had an option for it for a while and Crytek also announced EaaS model for CRYENGINE at GDC. I’ll definitely try all the new engines in the near future and maybe write up blog posts about them.<br />
<br />
I attended several game/engine rendering talks and PBS using Schlick Fresnel, GGX and Cook-Torrance seems to be pretty standard stuff nowadays. Khronos announced OpenGL ES 3.1 which finally features compute shaders. I’m currently evaluating which algorithm I’ll use for my engine’s lighting and Forward+ is a strong candidate so CS support in ES 3.1 would make it more feasible.<br />
<br />
Overall, this year’s GDC was great and I met many cool people and got a lot of takeaways from the sessions that I’ll start applying into my engine when the jet lag wears off.<br />
<div>
<br /></div>
Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-88009412840901178652014-01-19T20:05:00.000+02:002014-01-19T20:05:22.818+02:00Rebooted Aether3D Game Engine First ReleaseI released my new <a href="http://twiren.kapsi.fi/aether3d.html">game engine</a> today. This first release contains many of the features I planned on doing in my previous <a href="http://bioglaze.blogspot.fi/2013/05/planning-my-next-engine-iteration.html">blog post</a>.<br />
<br />
I see no reason to support deprecated systems when developing a new engine. The oldest OpenGL context I'm supporting is 3.2 and I only support it to get the engine running on a Mac. Mavericks supports 4.1 but I started developing before it was released.<br />
<br />
So, what's next for Aether3D? I try to add a performance test suite for tracking performance differences between releases, SSAO, instancing, shadows and support for vertex color to the next release.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-73248921794882710192013-07-20T15:05:00.003+03:002013-07-20T15:05:53.525+03:00Developing a New Engine<br />
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
I'm now two months into developing my new engine and here's a breakdown of currently working features:</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Scene Graph</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Models and Cameras are scene graph nodes so you can make hierarchies like in Unity.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Rapid Iteration</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
You can modify shaders and textures while the game is running, allowing fast iteration. This is implemented using a FileWatcher class that registers created objects.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Unit Testing</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
I'm not using any framework, my tests are simple programs that use return codes that are used to generate a HTML report.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
D3D11 Shader Reflection</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Because my engine has GL and D3D renderers with support for user supplied shaders, I reflect the uniform names and create constant buffers from them.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
BMFont support</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
BMFont is widely used in games industry so I wrote a reader for its ASCII and binary formats.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Texture Atlas support</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Supports Texture Packer exported Ogre/CEGUI format.</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
C++11 lambdas</div>
<div style="-webkit-composition-fill-color: rgba(130, 98, 83, 0.0976563); -webkit-composition-frame-color: rgba(191, 107, 82, 0.496094); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875); font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Useful for iterating containers of models, render queue elements etc. and applying operations on them.</div>
<div>
<br /></div>
Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-72530825762658622232013-05-06T20:54:00.000+03:002013-05-06T21:01:40.716+03:00Planning My Next Engine IterationEvery 1.5 years or so I make a new iteration of my engine, <a href="http://twiren.kapsi.fi/aether3d.html">Aether3D</a>. The reason is that in that time I have learned many new aspects of engine design and I become dissatisfied with current codebase. So, here I am again, planning for the next iteration. I want to see at least the following management/tech features in my next iteration:<br />
<br />
* Forward+ renderer (OpenGL 4.3 and D3D11 code paths)<br />
* Unit tests (including automated tests for asset import pipeline)<br />
* Better documentation, more example code/screencasts.<br />
* Level editor in Qt, working on every supported desktop OS.<br />
* Better animation support (blending, quaternions).<br />
* 3ds Max and Blender exporter.<br />
* OpenGL ES 3.0 instead of 2.0, using maybe a deferred renderer.<br />
* OS X will get whatever OpenGL context is the most recent in future.<br />
* Instancing.<br />
* Particle system.<br />
* Explore possibilities for using Compute Shaders for postprocessing.<br />
* Some kind of realtime GI solution.<br />
* No leaked resources.<br />
* Simultaneous release of new features for every platform.<br />
<br />
The common theme with many of the features above comes from this Wayne Gretzky quote:<br />
<blockquote class="tr_bq">
<i>A good hockey player plays where the puck is. A great hockey player plays where the puck is going to be.</i></blockquote>
<br />
Well, none of the listed features are really new, but not many current<br />
engines support them. I don't know when I ship all those features, but I'm starting coding NOW.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-43518572192813902922012-10-15T19:10:00.000+03:002014-04-13T16:57:29.010+03:00Hammer Time<br />
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
For the last couple of weeks I have been designing and building my first HL2 map. It has been a great way to learn level design and to explore other areas of game production besides programming.</div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
I try to tell a story with the map. You start in a cell, but where? When you break out to the hallway you can see Black Mesa employees in other cells and Black Mesa logos around you so you can assume that someone has infiltrated a BM facility. After a while you get attacked by the Combine.</div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
I introduce monsters and weapons one by one from the weakest to the strongest with a few surprising exceptions. Every time you get a new weapon, you'll get to use it very soon. I have modeled the map so that you cannot miss stuff, for example when you get the crowbar you cannot progress without using it to break some planks that are blocking your way.</div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
I will place ammo and health pickups sometimes sparingly, sometimes in abundance depending on the feelings I want to evoke at certain points of progress. Environmental sounds and music, used sparingly, will get you in the right mood.</div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
<br /></div>
<div style="font-family: '.Helvetica NeueUI'; font-size: 18px; line-height: 24px;">
Playtesting will be conducted on my friends and modifications will be made according to their reasonable feedback. I'll put the .bsp and .vmf to my <a href="http://twiren.kapsi.fi/projects.html">website</a> when the map is finished.</div>
Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-25414863063276868012012-06-10T18:13:00.001+03:002012-06-10T18:13:58.124+03:00Aether3D Test ReleaseI finally had time to package the new codebase of Aether3D Game Engine and made a <a href="http://intra.mountainsheep.net/~twiren/aether3d.html">test release</a>. If you're building it on Windows, you'll need Visual Studio 2012 because I'm using C++11 extensively. Supported operating systems are Windows, OS X, GNU/Linux and iOS. This release only contains OpenGL backend, but D3D11 backend is also in development.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-47706188451475559462012-04-19T17:08:00.000+03:002012-04-19T17:09:17.487+03:00Cyberjack ($0.99, iOS Universal)<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigsAgf-TAnLxqSnBdbcmXA0yVaWmTkjLYWzGTrAxr6ohQ7TVISv8QA6-xpOIdlCrpSu8CqGwUA_RXY0VfSTGcBtHJLZ7DuOjipHg-OhpJ2KceivvRXHutK6BOzg1iwHTkc8Fk61owgEao/s1600/cyberjack.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigsAgf-TAnLxqSnBdbcmXA0yVaWmTkjLYWzGTrAxr6ohQ7TVISv8QA6-xpOIdlCrpSu8CqGwUA_RXY0VfSTGcBtHJLZ7DuOjipHg-OhpJ2KceivvRXHutK6BOzg1iwHTkc8Fk61owgEao/s320/cyberjack.jpg" width="320" /></a></div>
<br />
Today I released a game I've been working on solo for 10 months, Cyberjack. In the game you can shoot bots, disarm explosives, hack computers and steal upgrades for your character in three maps. The game supports Game Center Achievements and Leaderboards.<br />
<br />
<a href="http://itunes.apple.com/us/app/cyberjack/id516673873?ls=1&mt=8">iTunes Link</a>Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-25239551073279062462012-02-24T16:29:00.000+02:002012-02-24T16:29:27.174+02:00New EngineI've been writing a new game engine for several months and will release it very soon. It supports OpenGL 3.2 Core Profile, OpenGL ES 2.0 and Direct3D 11. Compared to my previous engine, it has the following features:<br />
<br />
<ul><li>Texture Atlas support for batching geometry and to support NPOT textures.</li>
<li>C++11 features that work on Visual Studio, GCC and Xcode's Clang.</li>
<li>GLFW, gl3w and stb_image.c for handling context and texture loading.</li>
<li>Render Queues, to handle transparency etc. better.</li>
<li>Quaternion Camera.</li>
<li>More shared code between D3D and GL renderers.</li>
<li>Material properties are read from model file.</li>
</ul>Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-32356203366542507932011-10-28T18:08:00.000+03:002011-10-28T18:08:05.813+03:00Knight's Leap ($0.99, iOS)I co-developed a puzzle game for iOS and it was released on 27 October on the App Store ($0.99). In the game you ride your knight on the chessboard, filling empty squares as fast as you can or creating a path.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNQFTt54MBSMAZwYF_1g5DxVrOSz4L-zRz0-Ui2-xyJaRy-m3H6K-2IPjkV2JWbTxd0byACiuVtM0Gd0tkhDDJ9CkQ6beBpbfq_sSIt3CBrqy5ptuYiM0bGNNaye9uxxdEwx823KXDg2g/s1600/mode1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNQFTt54MBSMAZwYF_1g5DxVrOSz4L-zRz0-Ui2-xyJaRy-m3H6K-2IPjkV2JWbTxd0byACiuVtM0Gd0tkhDDJ9CkQ6beBpbfq_sSIt3CBrqy5ptuYiM0bGNNaye9uxxdEwx823KXDg2g/s320/mode1.png" width="213" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="http://itunes.apple.com/fi/app/knights-leap/id474815444?mt=8">View in App Store</a></div>Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com1tag:blogger.com,1999:blog-935800692892775916.post-2208944295572944302011-09-05T17:33:00.002+03:002011-09-06T18:21:12.155+03:00Shadow Volumes and non-closed meshesToday I implemented an algorithm that produces correct shadow volumes for non-closed meshes, aka meshes with open edges. I couldn't find it anywhere on the Internet so I decided to publish it here. It's from Game Programming Gems 4. The gist of the algorithm is to add inverted face normals and point edges with missing neighboring face to them. To use it, call <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">generateOpenEdgeTriangles()</span> after <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">generateEdges()</span>.<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> #define NO_NEIGHBOR 65535<br />
<br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// Indices of two vertices that belong to the same triangle. Used to construct shadow volume sides.<br />
struct TriangleEdge<br />
{<br />
TriangleEdge() : v1(0), v2(0), f1(NO_NEIGHBOR), f2(NO_NEIGHBOR) {}<br />
TriangleEdge( unsigned short a_v1, unsigned short a_v2 ) : v1(a_v1), v2(a_v2) {}<br />
<br />
unsigned short v1;<br />
unsigned short v2; <br />
unsigned short f1;<br />
unsigned short f2;<br />
};</span><br />
<br />
<br />
<br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">bool ShadowVolume::doesEdgeBelongToFace( TriangleEdge& edge, int aFaceIndex ) const</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">{</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // A and B.</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if ((almostEquals( meshVertices[ meshFaces[ aFaceIndex ].x ], meshVertices[ edge.v1 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].y ], meshVertices[ edge.v2 ] )) ||</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> (almostEquals( meshVertices[ meshFaces[ aFaceIndex ].x ], meshVertices[ edge.v2 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].y ], meshVertices[ edge.v1 ] )))</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // B and C.</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if ((almostEquals( meshVertices[ meshFaces[ aFaceIndex ].y ], meshVertices[ edge.v1 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].z ], meshVertices[ edge.v2 ] )) ||</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> (almostEquals( meshVertices[ meshFaces[ aFaceIndex ].y ], meshVertices[ edge.v2 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].z ], meshVertices[ edge.v1 ] )))</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // C and A.</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if ((almostEquals( meshVertices[ meshFaces[ aFaceIndex ].z ], meshVertices[ edge.v1 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].x ], meshVertices[ edge.v2 ] )) ||</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> (almostEquals( meshVertices[ meshFaces[ aFaceIndex ].z ], meshVertices[ edge.v2 ] ) &&</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> almostEquals( meshVertices[ meshFaces[ aFaceIndex ].x ], meshVertices[ edge.v1 ] )))</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return false;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">void ShadowVolume::generateEdges()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">{</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TriangleEdge edge;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int f = 0; f < meshFaces.getSize(); ++f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v1 = meshFaces[ f ].x;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v2 = meshFaces[ f ].y;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f1 = f;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = NO_NEIGHBOR;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Finds the other face that's touching vertices v1 and v2.</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int f2 = 0; f2 < meshFaces.getSize(); ++f2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (f2 == f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> continue;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (doesEdgeBelongToFace( edge, f2 ))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = f2;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> break;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edges[ f * 3 + 0 ] = edge;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v1 = meshFaces[ f ].y;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v2 = meshFaces[ f ].z;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f1 = f;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = NO_NEIGHBOR;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Finds the other face that's touching vertices v1 and v2.</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int f2 = 0; f2 < meshFaces.getSize(); ++f2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (f2 == f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> continue;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (doesEdgeBelongToFace( edge, f2 ))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = f2;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> break;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edges[ f * 3 + 1 ] = edge;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v1 = meshFaces[ f ].z;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.v2 = meshFaces[ f ].x;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f1 = f;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = NO_NEIGHBOR;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Finds the other face that's touching vertices v1 and v2.</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int f2 = 0; f2 < meshFaces.getSize(); ++f2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (f2 == f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> continue;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (doesEdgeBelongToFace( edge, f2 ))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edge.f2 = f2;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> break;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edges[ f * 3 + 2 ] = edge;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">void ShadowVolume::generateOpenEdgeTriangles()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">{</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> int degenFacesSize = 0;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Finds out the # of open edges.</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int e = 0; e < edges.getSize(); ++e)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (edges[ e ].f2 == NO_NEIGHBOR)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ++degenFacesSize;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> int offs = meshFaces.getSize();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> meshFaceNormals.resize( offs + degenFacesSize );</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> backFaces.resize( offs + degenFacesSize ); </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (unsigned int e = 0; e < edges.getSize(); ++e)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (edges[ e ].f2 == NO_NEIGHBOR)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> meshFaceNormals[ offs ] = -meshFaceNormals[ edges[e].f1 ];</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> edges[ e ].f2 = offs;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ++offs;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div></div>Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0tag:blogger.com,1999:blog-935800692892775916.post-45460774344658736652011-04-03T15:11:00.000+03:002011-04-03T15:11:30.172+03:00New Release of Aether3DI uploaded release 3. It mostly contains bug fixes (Debug build now works in Visual Studio etc.), but new features include support of PVR compressed textures and automatic loading of compressed textures if present. Download the release <a href="http://users.utu.fi/tmwire/aether3d/">here.</a><br />
<br />
In other news, Death Rally for iOS is selling well and the first update is coming soon. My role in the development has mostly been graphics performance profiling and optimizing and other graphics related stuff.Timo Wirénhttp://www.blogger.com/profile/11841206902163016142noreply@blogger.com0