tag:blogger.com,1999:blog-4610143435806245392024-03-06T08:37:58.628+01:00Game School GemsThis blog will contain different tips and tricks that are loosely based around the material I teach at The Game Assembly. Basically it will become one large tutorial site covering different areas of game development.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-461014343580624539.post-52689031878990657312010-11-02T10:32:00.000+01:002010-11-02T10:32:18.174+01:00Written Gems - Course Litterature post mortemIt has been a while since last time I wrote here. Real Life(tm) aka getting married and other stuff has meant I have been really busy and the time to write 10 page articles are kinda restricted. While I have a couple of articles being partially written I can't vouch for when they will be ready.<br />
<br />
<br />
Instead I will try to produce a series of shorter less in-depth articles that focus more on individual tricks and tips and less on complexer systems and we'll see how this pans out. So first out today are some book recomendations.<br />
<br />
While working at a game development school you quickly discovers the importance of good course literature. While of course you can teach the students everything they need to know yourself it takes a lot of time and effort and since these educations aren't 5 years educations you won't have time to give them all they need.<br />
<br />
But if you got good literature as a base it saves a lot of time. Not only has someone sat down and worked really hard to put in a written format what you need to tell the students but they can also study the book interdependently of the lessons when they are looking for now information.<br />
<br />
A good book also gives a great base to build a course around. The problem is that there are so much literature out there that are either crap, focused on a purely academic stand point or otherwise simply not targeted to game students. And a lot of the game books are just shameless cash in attempts or written by authors who lack the necessary skills as either a programmer or a writer or just simply out of date.<br />
<br />
And some books while great are more useful after a few years in the industry or simply not build in a way that you can base a course around them. But through the years we have started to build up quite a nice library of books here to use and I will try to go through them during the coming articles. This might not be the best idea from a competitive advantage point as a school as the books are really important. But my main goal is for as many people as possible to become competent programmers and good books really helps here.<br />
<br />
So lets start with the basic courses and move our way upwards. (btw I don't use cash links or anything like that so I won't earn a penny if anyone purchases these books, even though blogger has support for this I feel it would only detract from my main goal which is to put these books into the hands of as many people as possible)<br />
<br />
<br />
<b>C++ Primer (4th Edition) [Paperback]</b><br />
Stanley B. Lippman<br />
Paperback: 912 pages<br />
Publisher: Addison-Wesley Professional; 4 edition (February 24, 2005)<br />
Language: English<br />
ISBN-10: 0201721481<br />
ISBN-13: 978-0201721485<br />
<b><a href="http://www.amazon.com/Primer-4th-Stanley-B-Lippman/dp/0201721481/ref=ntt_at_ep_dpi_1">Amazon link</a></b><br />
<br />
This is our basic C++ book we use for our introductionary courses, but also refers people back to pretty much every time they have a question about C++ releated things. Picking a course litterature for C++ for this education was really hard since it was years since I read any such base books. But a good friend recommended this one and I am really grateful for it. Despite having spent my last 12 years programming C++ as my job I still found new information in this book that I didn't knew about earlier.<br />
<br />
This is however not a "learn to program" style book. It's based on learning C++. This this well into our unique approach here where we run two parallel courses one about how to program and one about how to program in C++.<br />
<br />
The book starts out slowly walking though all the different parts of C++ syntax while quickly focusing on giving the reader enough knowledge to actually be able to program in C++. After that it starts going in-depth though the language, with amazingly well detailed descriptions of how things work under the hood and why some things works while others don't. Pretty much every area of the C++ language is covered with detailed explanations while never loosing readability. Things like implicit casting and how it conflicts with operator overloading, How virtual functions works behind the hood etc are described in great details telling you what rules the compiler follows so that you easily can write good code that works. If you are pretty new to C++ this is a gold mine. If you are an experienced programmer there are some nice stuff in here but probably not enough to make it worth your while. However I will cover a few books that didn't make it to course litterature but are awesome anyway in later posts.<br />
<br />
Conclusion: This is an awesome learn C++ book with enough in depth information that unless you have been working with C++ actively for a couple of years you will still learn a lot of new stuff here it is also a good reference for more tricky parts of the language.<br />
<br />
<b>3D Math Primer for Graphics and Game Development</b><br />
Fletcher Dunn, Ian Parberry<br />
Paperback: 429 pages<br />
Publisher: Jones & Bartlett Publishers; 1 edition (June 21, 2002)<br />
Language: English<br />
ISBN-10: 1556229119<br />
ISBN-13: 978-1556229114<br />
<a href="http://www.amazon.com/Primer-Graphics-Development-Wordware-Library/dp/1556229119/ref=sr_1_3?ie=UTF8&s=books&qid=1288688637&sr=1-3">Amazon Link</a><br />
<br />
The linear algebra for 3D math was a tough course to crack here. The first year we had in an algebra teacher with classic algebra knowledge and sat down with him and discussed what we wanted the students to know. This didn't work out however. Not due to a fault of the teacher the students were excellent at solving linear algebra with pen and paper but had problems transferring this to usable facts in 3D math and we had to spend a lot of time to work on this. For the second year we ditched the classical math texts and run the course completely based on lectures and compendiums. This worked out decently but I felt that with a good book as base we could have gotten further as there is only so much time you have to prepare a lecture. But all the books we looked at was either to academic or required to much prior knowledge until a student that was having some trouble with the course purchased and recommended this book.<br />
<br />
When I got it in my hand I was amazed. Here was a book that tried to teach Linear Algebra in almost the exact same way as I wanted to. By focusing on approaching in not only from a purely mathematical stand point but also showing what it all means from a geometric standpoint. It also explains coordinate systems or spaces as they are often called as concept quite well and this is an area where in my experience students often have problems really grasping the basic ideas.<br />
<br />
It covers normal vector,matrix math of course and all kinds of operations on vector you would need to know about for 3D math again with geometric explanations of things like the dot product and cross product.I can't vouch for it's sample code since I never used it. But then again sample code is not really relevant you want to learn things not just copy them. It gives a sensible explanation of matrixes using the Affine Transform vs Linear transform to explain things I myself always had been using other terms for but basically derived the same idea independantly.<br />
<br />
It contains a good discussion of the different ways to contain an orientation in 3d with advantages and disadvantages and covers quaternions in the process. After that it covers collision detection with a diverse set of geometric primitives working with basic 3d meshes and discussing and showing how the math applies to basic 3d operations like lightning fog and culling. If finishes of with discussing spatial representations for culling and occlusion.<br />
<br />
Conclusion : This really is a complete 3d primer from the first step to actually applying it in game. This is an absolute gold mine of information for anyone getting into 3D math and I simply can't recommend this book enough I simply love it.<br />
<br />
<br />
<br />
It seems these posts are gonna be a bit longer than I thought when I started this one so I will have to split it up over a couple of posts to make it manageable. So I will continue in the next posts.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com2tag:blogger.com,1999:blog-461014343580624539.post-2475642129294990922010-06-28T14:00:00.001+02:002010-06-28T14:00:07.632+02:00The Wonders of Fixed Point Math - Or how I got XNA to cull thousnands of objects fastSo what is fixed point math ? Well you could say that it's an old technique to approximate decimal numbers on computers without floating point processors.<br />
<br />
So why should we care about it then ?<br />
<br />
A good questions there are multiple instances where using a fixed point representation can help you considerably. One is if you are running into the problems of the inconsistencies that are at the core of how we handle floating point math on modern computers. Or if you are simply running on a platform where the floating point performance is way to low and you don't need 6-7 decimals of accuracy.<br />
<br />
For me while working professionally it has been along time since I had to use fixed point. But then again there has been plenty of cases it has been under serious discussion to resolve issues with floating point inconsistencies and I know of numerous companies that have used them to battle the problems.<br />
<br />
<br />
<a name='more'></a>So what is fixed point math ? Well you could say that it's an old technique to approximate decimal numbers on computers without floating point processors.<br />
<br />
So why should we care about it then ?<br />
<br />
A good questions there are multiple instances where using a fixed point representation can help you considerably. One is if you are running into the problems of the inconsistencies that are at the core of how we handle floating point math on modern computers. Or if you are simply running on a platform where the floating point performance is way to low and you don't need 6-7 decimals of accuracy.<br />
<br />
For me while working professionally it has been along time since I had to use fixed point. But then again there has been plenty of cases it has been under serious discussion to resolve issues with floating point inconsistencies and I know of numerous companies that have used them to battle the problems.<br />
<br />
So first out what are the problems with floating point ? Well to represent it we have to understand how the computer works with floating point numbers. Detailed explanations of the exact formats are done at numerous places for example at <a href="http://en.wikipedia.org/wiki/Floating_point">Wikipedia</a> instead of trying to act like an encyclopedia we will here use a simplified view of how floating point number works that despite being simplified works very well for all uses unless you are building the numbers yourself from byte data.<br />
<br />
A regular float is 32 bits large on most computer systems, And a double is 64 bits, Half sometimes called single is 16 bits. So what does those bits means. Well do you remember exponents from school ?<br />
<br />
12345 = 1.2345 *10^4<br />
<br />
In computers we have a special term for the 10th exponent that would write it like this.<br />
<br />
<br />
12345 = 1.2345E4<br />
<br />
floating point numbers on a computer works like this too. you have two parts the first part called Significand which is a representation of the decimal number of the base. and then Exponent which is the exponent used to calculate the real number.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBgQl1V4Jp6216KgPXdkCUUPQYaYe65S3SPQaCD4X14VlVJAzM54gDcPzv1K5MHYHAt8T4PM6tyHpnXOLtbgWXShkxKZkHWKqymn4_oq4jbpG7pb0rs4kCdbeqbOYfoArHfoapOmV1IrHU/s1600/floating+Point.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBgQl1V4Jp6216KgPXdkCUUPQYaYe65S3SPQaCD4X14VlVJAzM54gDcPzv1K5MHYHAt8T4PM6tyHpnXOLtbgWXShkxKZkHWKqymn4_oq4jbpG7pb0rs4kCdbeqbOYfoArHfoapOmV1IrHU/s640/floating+Point.png" width="640" /></a></div><br />
However since computers are binary there are some differences to what you are used to. The general formula is as follows.<br />
<br />
RealNumber=Significand*Base^Exponent.<br />
<br />
Where significand can never be a higher value than base. so the extra data bits are used to represent decimal information. In fact this part works exactly like a fixed point number except the bits you use for the exponent. Since computers works in binary systems the base of course is 2.<br />
<br />
So the Formula is<br />
<br />
<br />
RealNumber=Significand*2^Exponent<br />
<br />
The interesting parts here is the max value of the significand and of the exponent. The exponent has a range from -127 to 128. while the significand ranges from -8388607 to 8388608 so this might seem meningless to you but this gives us two valuable observations first out we have the max size of a float if we take 2^128 and then convert that to a 10 exponent we will get a value range of aproximately ± ~10<sup>-44.85</sup> to ~10<sup>38.53 </sup>According to the standard specification. The more interesting value for me is 8388608. or 8.388608 as you can think of it as. because this gives you and idea of the number of decimals in a standard floating number as you can see we have 6 decimals here. But due to conversion effects etc we will have slightly less than this in reality but lets go with 6 for now. We should also mention here that performing a multiplication or division invalidates 1-2 decimals lets say one for the sake of simplicity leaving us with 5 decimals.<br />
<br />
This is obviously aplenty if we deal with numbers between 1 and 10 then we have 5 decimals of free use and this should be enough for most things. but what happens if we work on numbers between 10 000 and 100 000 for example in a really large game world. What will happens is that we have only 1 decimal of precision left. which means if this is a map we only have 0.1 M as our smallest granularity. and this is discounting the problems inherent in the conversions between binary and decimal numbers.<br />
<br />
So any advances calculations like intersecting lines with lines etc. Will have vastly different precision depending on where on the map it happens. This can't be good. A consistent precision is vital to a consistent game. So there is one of the biggest problems with floating point.<br />
<br />
And is fixed point better ? Well theoretically you could create a pretty neat fixed point using 64 bit values but then again 64 bits doubles has a lot better precision too. So fixed point doesn't have a better precision in fact it's probably worse and even worse fixed point numbers normally range from -32767 to 32768 only. But if you can live with this range (or well you can actually trade range for precision freely) it has a constant precision which means if you get your calculations to work somewhere in the world they will work everywhere.<br />
<br />
And of course if you are developing for a platform with a very weak floating point performance it (like XNA on the Xbox360) it can be a real performance saver. Actually as an example for the rest of the article we will look at this from the perspective of a XNA programmer getting desperate when he gets CPU limited on xbox performing culling on a measly 10 000 objects (in a oct tree of course but they has culling costs too, and with cascaded shadow volumes you have to perform the culling test a lot of times.).<br />
<br />
So lets look at how fixed point works. Well do you remember fractions from school, that is good cause they are going to be handy now.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqzl-BfpVjxTiA5IHupX7g2ft8EYPiXMfrd4OxSAkaAD_M4JfqWQiqjJL-X77ywcp3t6fkERx8FZQaZSdmaGfn7EBfoppNyWysyg-gViAIeBhpy3WMhGvPCFU3tOfK2RcbM1PKNXvkQcYj/s1600/Fixed.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqzl-BfpVjxTiA5IHupX7g2ft8EYPiXMfrd4OxSAkaAD_M4JfqWQiqjJL-X77ywcp3t6fkERx8FZQaZSdmaGfn7EBfoppNyWysyg-gViAIeBhpy3WMhGvPCFU3tOfK2RcbM1PKNXvkQcYj/s640/Fixed.png" width="640" /></a></div>A fixed point works by dividing the 32 bit number into two parts for general use the division I have proposed here with 16 bits is the most normal but any division that works for your case is good.<br />
<br />
The idea is that the two parts of the numbers represent different parts of the number you want to represent. The integer part represent well the integer part of the number. While for the decimal part you are using fractions to represent the number. In this case the precision for my fractional part is 1/66536 so the decimal part of my number will be represented as this fraction. This makes it quite convenient to transform between fixed and float (btw I normally use signed ints to keep my fixed point numbers due to the slowness of C# on xbox with function calls).<br />
<br />
float fp=1.234567;<br />
<br />
int fixed = (int)fp*65536;<br />
<br />
And to convert it back<br />
<br />
fp=(float)fixed/65536.0f;<br />
<br />
<br />
if you do run through this procedure you will notice a hughe loss of precision. Of course this was the bestcase for floating point. if we would have assigned fp to 12345.6789123 we would probabely gotten a higher precision form the fixed point. But the important point is that fixed points precision is consistent.<br />
<br />
So how do we work with fixed point numbers ? First we are gonna go through the 4 usual calculation methods what you can do if you have assembler access and what you have to do else. And finally we will look at preshifted fixed point which is a special case optimization useful if you only work with your numbers in a certain way.<br />
<br />
<br />
First out addition. This is an easy one it just works straight of the bat.<br />
<br />
int FixedAdd(int aNumber,aSecondNumber)<br />
{<br />
return(aNumber+aSecondNumber);<br />
}<br />
<br />
Why this works is pretty obvious of course you can add together the integer parts and if you add together the fractional part and they overflow into the integer part they just add 1 just as what should happen. Subtraction works just as fine too.<br />
<br />
int FixedSubtract(int aNumber,aSecondNumber)<br />
{<br />
return(aNumber-aSecondNumber);<br />
<br />
}<br />
<br />
<br />
So far so good fixed is just as fast as regular int. So now lets look at multiplications this is where things get though for starter if you multiply together two 32 bits number the maximum resulting number is a 64 bit number. This can't be solved easily in a high level language without using a 64 bit number which would be slow. However in Assembly language there isn't a problem since they full 64 bits result is stored in registers.<br />
<br />
But what happens if we multiply together 2 fixed point numbers let's look at a case where we call the numbers A and B.<br />
The end result is A.Integer(*65536)*B.Integer*(65536) +A.Integer*(65536)*B.Fractional+B.Integer*(65536)*A.Fractional+A.Fractional*B.Fractional.<br />
<br />
If you wonder about all those 65536 you have to remember that the integer part begins after the 16th bit so that is why. Lets first look at the integer parts. What we get is A.Integer(*65536)*B.Integer*(65536).. But what we want to get is A.Integer*B.Integer*(65536). We need one 65536 to keep the integer in the integer part of the number but we get an extra *65536 or as we will call it a leftshift 16. (<< 16 in most high level languages this equals a multiplication with 65536 for all integer numbers)<br />
<br />
If we look at the other parts we will notice the same pattern for a fractional we get A.Integer*(65536)*B.Fractional. Which means that the fractional also gets an extra 65536.<br />
<br />
So what we want to to is to take the end result and divide it by 65536 (which is the same as rightshift 16 , >> 16)<br />
<br />
If we do this in assembly we can do it quite neatly. (if you don't know assembly no worry just skip this part) btw this code isn't even trying to be optimized<br />
<br />
mov eax,A // places the number A in the register EAX<br />
mov ebx,B // places B in the register EBX they are booth 32 bit registers<br />
mul ebx<br />
// multipliex whatever is in EAX with ebx, and stores the lower 32 bits in eax and the higher in <br />
// EDX<br />
<br />
<br />
<br />
<br />
<br />
shl edx,16<br />
// shifts edx leftward so that the lower 16 bits are the higher 16 This means that if <br />
// the end result of the multiplication is above 65536 it will be cut of. <br />
shr eax 16.<br />
// shifts right eax 16 bits meaning the higher part of eax is now in the lower par.<br />
mov dx,ax<br />
// moves the lower 16 bits of eax called ax into the lower 16 bits of edx called dx<br />
<br />
Even the assembly version can't handle numbers that ends above 65536 but it can perform the calculations at full precision what can we do if we work with a high level language ? Well we have to sacrifice precision so that we are sure that we stay in 32 bits unless we overflow.<br />
<br />
<br />
<br />
int FixedMulint aNumber,aSecondNumber)<br />
{<br />
return((aNumber >> 8)*(aSecondNumber>>8));<br />
<br />
}<br />
<br />
So we left shift booth the numbers lowering our precision to 1/256 part which still is plenty for a lot of stuff like culling etc.<br />
<br />
Division works similarely except that we need to shift the other way. This means that division for numbers above 256 won't work properly this is again a constraint of working in a high level language an assembly verision can be constructed that can handle the full numbers. you could also handle the the full numbers if you sacrifice the decimal part of the result or make any kind of balancing between. but the default verision is like this.<br />
<br />
int FixeDiv(int aNumber,aSecondNumber)<br />
{<br />
return((aNumber << 8)/(aSecondNumber<<8));<br />
<br />
}<br />
<br />
you can also construct fixed point numbers that are 24:8 or 8:24 or any numbers in between or even using 64 bits. We used 16 bit fixed point back in the software rendering days so it all depends.<br />
<br />
But I normally use 16:16 as it gives a good range and well I avoid using divisions if i can in any way. most divisions with a fixed number can be converted into multiplications so.<br />
<br />
<br />
So out last now is Preshifted Fixed point. If you know that you are only ever gonna use additions,subtractions and multiplications for your fixed point and that there is only going to be one multiplication and you are going to use it last you can use preshifted fixed point. This is especially useful in cases like culling.<br />
<br />
Preshifted means that you right shift your numbers 8 bits when you create them this works great for static numbers this way you won't have to perform the shift every frame and an even get multiplication at full integer speed (which is great for XNA).<br />
<br />
<br />
<span style="font-size: x-small;"><i>struct FixedPointPlane<br />
{<br />
public FixedPointPlane(Plane aPlane)<br />
{<br />
myNormal = new Vector3DFixed(aPlane.Normal);<br />
D = FixedPoints.FloatToFixed(aPlane.D);<br />
myPreShiftedNormal.X = myNormal.X >> 8;<br />
myPreShiftedNormal.Y = myNormal.Y >> 8;<br />
myPreShiftedNormal.Z = myNormal.Z >> 8;<br />
}<br />
<br />
public int DistFromPlane(Vector3DFixed aPosition)<br />
{<br />
return (0);<br />
}<br />
<br />
public int DistFromPlanePreShifted(Vector3DFixed aPosition)<br />
{<br />
return (myPreShiftedNormal.X * aPosition.X + myPreShiftedNormal.Y * aPosition.Y + myPreShiftedNormal.Z * aPosition.Z + D);<br />
}<br />
<br />
Vector3DFixed myNormal;<br />
Vector3DFixed myPreShiftedNormal;<br />
int D;<br />
}</i></span><br />
<br />
using the above planes instead of XNA planes allowed my culling to go from 50% of a cpu to 1-2%. Which allowed me to for example use 6 cascades of 512x512 instead of 3 1024x1024 and even get a better visual quality and better framerate (if you are unfamiliar with cascade shadow maps I will cover them in a later article). As you can see I never even bothered to implement the non preshifted version because the preshifted is so much easier and well you only create those planes once per frame so the cost is nothing. Of course aPosition is preshifted also. I'm actually gonna go aganist my instincts that says it's better for you guys to learn by creating and drop the full version of the naive sphere vs frustum code here (it can be optimized a bit more but this is best for readability)<br />
<br />
<span style="font-size: x-small;"><i> class FixedPoints<br />
{<br />
public static int FloatToFixed(float aFloat)<br />
{<br />
return (int)(aFloat * 65536.0f);<br />
}<br />
public static int FixedMul(int aFirstFixed, int aSecondFixed)<br />
{<br />
aFirstFixed = aFirstFixed >> 8;<br />
aSecondFixed = aSecondFixed >> 8;<br />
aFirstFixed *= aSecondFixed;<br />
return (aFirstFixed);<br />
}<br />
// Only as References never meant to be used;<br />
public static int FixedAdd(int aFirstFixed, int aSecondFixed)<br />
{<br />
return (aFirstFixed + aSecondFixed);<br />
}<br />
<br />
public static int FixedSub(int aFirstFixed, int aSecondFixed)<br />
{<br />
return (aFirstFixed - aSecondFixed);<br />
}<br />
}</i></span><br />
<span style="font-size: x-small;"><i> </i></span><br />
<span style="font-size: x-small;"><i> public struct Vector3DFixed<br />
{<br />
public Vector3DFixed(Vector3 aVector)<br />
{<br />
X = FixedPoints.FloatToFixed(aVector.X);<br />
Y = FixedPoints.FloatToFixed(aVector.Y);<br />
Z = FixedPoints.FloatToFixed(aVector.Z);<br />
}<br />
public int X;<br />
public int Y;<br />
public int Z;<br />
}</i></span><br />
<span style="font-size: x-small;"><i>public struct FixedPointBoundingSphere<br />
{<br />
public Vector3DFixed myPosition;<br />
public Vector3DFixed myPreShiftedPosition;<br />
public int myRadius;<br />
<br />
public FixedPointBoundingSphere(BoundingSphere aSphere)<br />
{<br />
myPosition = new Vector3DFixed(aSphere.Center);<br />
myPreShiftedPosition.X = myPosition.X >> 8;<br />
myPreShiftedPosition.Y = myPosition.Y >> 8;<br />
myPreShiftedPosition.Z = myPosition.Z >> 8;<br />
myRadius = FixedPoints.FloatToFixed(aSphere.Radius);<br />
<br />
}<br />
public void Move(Vector3 aPosition)<br />
{<br />
myPosition = new Vector3DFixed(aPosition);<br />
myPreShiftedPosition.X = myPosition.X >> 8;<br />
myPreShiftedPosition.Y = myPosition.Y >> 8;<br />
myPreShiftedPosition.Z = myPosition.Z >> 8;<br />
}<br />
}<br />
<br />
public class FixedPointFrustrum<br />
{<br />
FixedPointPlane[] myPlanes = new FixedPointPlane[6];<br />
Plane[] myOrigPlanes = new Plane[6];<br />
<br />
FixedPointFrustrum()<br />
{<br />
}<br />
<br />
// To do Add Fast box vs frustrum test<br />
<br />
public bool Intersects(FixedPointBoundingSphere aBoundingSphere)<br />
{<br />
// To Do<br />
// add Octant test<br />
// add Near far paralellOptimization.<br />
// For Shadow culling use a Special Paralell Frustrum;<br />
<br />
if (myPlanes[0].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
if (myPlanes[1].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
if (myPlanes[2].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
if (myPlanes[3].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
if (myPlanes[4].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
if (myPlanes[5].DistFromPlanePreShifted(aBoundingSphere.myPreShiftedPosition) > aBoundingSphere.myRadius) return (false);<br />
return (true);<br />
}<br />
<br />
public void Set(BoundingFrustum aFrustrum)<br />
{<br />
myPlanes[0] = new FixedPointPlane(aFrustrum.Near);<br />
myPlanes[1] = new FixedPointPlane(aFrustrum.Far);<br />
myPlanes[2] = new FixedPointPlane(aFrustrum.Bottom);<br />
myPlanes[3] = new FixedPointPlane(aFrustrum.Left);<br />
myPlanes[4] = new FixedPointPlane(aFrustrum.Right);<br />
myPlanes[5] = new FixedPointPlane(aFrustrum.Top);<br />
<br />
myOrigPlanes[0] = aFrustrum.Near;<br />
myOrigPlanes[1] = aFrustrum.Far;<br />
myOrigPlanes[2] = aFrustrum.Bottom;<br />
myOrigPlanes[3] = aFrustrum.Left;<br />
myOrigPlanes[4] = aFrustrum.Right;<br />
myOrigPlanes[5] = aFrustrum.Top;<br />
}<br />
<br />
public FixedPointFrustrum(BoundingFrustum aFrustrum)<br />
{<br />
myPlanes[0] = new FixedPointPlane(aFrustrum.Near);<br />
myPlanes[1] = new FixedPointPlane(aFrustrum.Far);<br />
myPlanes[2] = new FixedPointPlane(aFrustrum.Bottom);<br />
myPlanes[3] = new FixedPointPlane(aFrustrum.Left);<br />
myPlanes[4] = new FixedPointPlane(aFrustrum.Right);<br />
myPlanes[5] = new FixedPointPlane(aFrustrum.Top);<br />
<br />
myOrigPlanes[0] = aFrustrum.Near;<br />
myOrigPlanes[1] = aFrustrum.Far;<br />
myOrigPlanes[2] = aFrustrum.Bottom;<br />
myOrigPlanes[3] = aFrustrum.Left;<br />
myOrigPlanes[4] = aFrustrum.Right;<br />
myOrigPlanes[5] = aFrustrum.Top;<br />
<br />
}</i><span style="font-size: small;">As</span><span style="font-size: small;"> you can see from my comments I didn't even bother to move the box vs frustum to fixed point I got the performance necessary already. Once it becomes a problem I will do that too of course.</span><i><br />
</i></span><br />
<br />
Remember this is not the fastest way of doing this but it was enough.Never spend time optimizing things that doesn't matter. I think there is a good chance I will have to fix this later but for now I can spend my time on better things.<br />
<br />
Dropping code still feels weird but tell me if you like it better this way or not.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com1tag:blogger.com,1999:blog-461014343580624539.post-42974909202411803672010-05-18T16:08:00.001+02:002010-06-27T20:16:05.992+02:00Auto balancing Decision trees aka Resource Trees IIJust continuing from last time.<br />
<br />
So after we called Act on the economy node it checks that it isn't a leaf node and therefore it checks it resources.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBwqFmWImrokqwbGNlU_0RCKEsxu7rDEsuAGij5PXZkbp9oPQU8st-5TJmXaboI4XCwPJtfEbsPR7jkf0S7gkI-htH9-oHAfebgtaMTjhIAsZ33SPw0okSuks1_nPZkqExk0YCvVqLEN2j/s1600/Bild10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBwqFmWImrokqwbGNlU_0RCKEsxu7rDEsuAGij5PXZkbp9oPQU8st-5TJmXaboI4XCwPJtfEbsPR7jkf0S7gkI-htH9-oHAfebgtaMTjhIAsZ33SPw0okSuks1_nPZkqExk0YCvVqLEN2j/s640/Bild10.png" width="640" /></a></div><br />
<br />
<a name='more'></a>Just continuing from last time.<br />
<br />
So after we called Act on the economy node it checks that it isn't a leaf node and therefore it checks it resources.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBwqFmWImrokqwbGNlU_0RCKEsxu7rDEsuAGij5PXZkbp9oPQU8st-5TJmXaboI4XCwPJtfEbsPR7jkf0S7gkI-htH9-oHAfebgtaMTjhIAsZ33SPw0okSuks1_nPZkqExk0YCvVqLEN2j/s1600/Bild10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBwqFmWImrokqwbGNlU_0RCKEsxu7rDEsuAGij5PXZkbp9oPQU8st-5TJmXaboI4XCwPJtfEbsPR7jkf0S7gkI-htH9-oHAfebgtaMTjhIAsZ33SPw0okSuks1_nPZkqExk0YCvVqLEN2j/s640/Bild10.png" width="640" /></a></div><br />
So we want to have 60% of our economic resources in spice and 40% in gold. These values might have important meanings because what kind of resources the AI tries to collect decides what kind of units etc it can afford to buy. These values are what makes up your AI characters personalities. However currently they are fixed so they can't really adapt to what happens. We will look at the possibility of building an AI that uses a resource tree but also adapts to what happens in the field and weigh all of these factors into it's decision.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmtifSKAvA1i0Dd-9ksynC0u5F9jSuqTxAdhm1XAH1LHyEN9CGKEHRa0TTYJ9FUnYTrdnJ5J22F-n_ye1ueZXnqtORSMSiAaPag9ns0NZ3h7bkN4YUgPKUIFShn5SNYtbggyeZu_BxeHIX/s1600/Bild11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmtifSKAvA1i0Dd-9ksynC0u5F9jSuqTxAdhm1XAH1LHyEN9CGKEHRa0TTYJ9FUnYTrdnJ5J22F-n_ye1ueZXnqtORSMSiAaPag9ns0NZ3h7bkN4YUgPKUIFShn5SNYtbggyeZu_BxeHIX/s640/Bild11.png" width="640" /></a></div><br />
<br />
And the current factors are 0.5 as we currently have booth 50 spice and 50 gold. Again this is gained simply by running the contribution formula from above.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUcoEdvuQwV4nS_PPOQoL1knc3wNBBI9G4MIAKGrult_WodPjGcUwkaVfkfTgdHwm-dgDmLwGb2LdpqLFiEsecDAD4Yfm3VfO0oiqb3pgiu7XcXhKFIXNNIokOyp_is34DADdx6RYP-TFG/s1600/Bild12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUcoEdvuQwV4nS_PPOQoL1knc3wNBBI9G4MIAKGrult_WodPjGcUwkaVfkfTgdHwm-dgDmLwGb2LdpqLFiEsecDAD4Yfm3VfO0oiqb3pgiu7XcXhKFIXNNIokOyp_is34DADdx6RYP-TFG/s640/Bild12.png" width="640" /></a></div><br />
Running the completed formula shows that spice has a difference of -0.1 and gold has a difference of +0.1 which means spice has the lowest difference so we call Act on Spice.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXbFSVMoPE0HKxOrLE8Xzs6cgWhKMin3EUidVJka0HRpAScLD1s_cvsB-xfGc9sX5oY_7_CK91llOIwZ_kVtET52_UuYILch1aCI-3fVYO_bbpWbkqajzkoR7f6_puWEcGvYvqp0cXIo4X/s1600/Bild13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXbFSVMoPE0HKxOrLE8Xzs6cgWhKMin3EUidVJka0HRpAScLD1s_cvsB-xfGc9sX5oY_7_CK91llOIwZ_kVtET52_UuYILch1aCI-3fVYO_bbpWbkqajzkoR7f6_puWEcGvYvqp0cXIo4X/s640/Bild13.png" width="640" /></a></div><br />
The current node is a leaf node. This means that the tree has deduced that the most important thing for the AI according to it's personality is to collect more spice. Now that the AI has a goal the rest of the AI can kick in and start to collect the spice.<br />
<br />
This shows an issue with the naive implementation of the tree If I run through the tree again it will make the same decision. So I can't use the tree as it is now to aquire a secondary goal. a Way to solve this is to add another function to the tree that we can call Try() if a node is already selected as a target Try() returns false if a try returns false you disregard that node when calculating contribution. This is an imperfect system however. As It would still select economy as a secondary goal only it will select Gold this time.<br />
<br />
So by now I hope that the basic algorithm is pretty clear to you, but the question as always is how to implement it in a simple and flexible way.<br />
<br />
It feels really obvious that we need a Node class since the tree is consisting of nodes and traverses nodes to select an goal. But it seems we have basically two kind of nodes, nodes that select which of it's child nodes to traverse too and the nodes that sets a goal. So it seems logical that we will have to use some kind of hierarchy here.<br />
<br />
So we decide for a base class called DecisionTreeNode it provides the interface for the structure.<br />
bool Act();<br />
float GetValue();<br />
float GetGoalValue();<br />
<br />
Inheriting from this one we add a class called DecisionTreeNode_Branching. This is a node that doesn't do anything except selecting what node to call Act on. All non leaf nodes must be branching nodes. So now we have a way to select what nodes that should set a goal or perform an action.<br />
<br />
So we also needs a node type that can handle the leaf nodes. A lead node both has to be able to get the value of it's resource and set a goal or perform an action that aquires more of that resource. So it manages the resource. Therefore we call It DecinsionTreNode_Managing.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRBmEmoyenItiZoodgOLuXX_x2kN4qmCr93RS-1FTz4BlpySZX-dqH37GNFa9qmvj7l4rNglbRhH_g0ZdveZOiAPbduxH_M6wc6RR6ktVfNRSKN2_vmFu2xnFH5HLmzLpFP-A3eWADW0l/s1600/Bild14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="556" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRBmEmoyenItiZoodgOLuXX_x2kN4qmCr93RS-1FTz4BlpySZX-dqH37GNFa9qmvj7l4rNglbRhH_g0ZdveZOiAPbduxH_M6wc6RR6ktVfNRSKN2_vmFu2xnFH5HLmzLpFP-A3eWADW0l/s640/Bild14.png" width="640" /></a></div><br />
GetGoalValue is missing here but it's only because of clarity and also because well you can place it elsewhere.<br />
<br />
The quick study notices that under DecisionTree_managing we have a class called a manager. This is because every resource will need it's own special code for how to aquire it and measure it's current value so if we tried to place that code in the decisiontree we will get a lot of different nodes Instead we introduce a new interface class called a manager. It has only the Act and GetValue Functions. And a managing node has a pointer to it's manager. This neatly decouples the tree from the resource it manages so that you can have the manager anywhere in the program and the only connection is through the pointer.<br />
<br />
The flow of the program will be as follows. We call act on our root node which for obvious reasons needs to be a branching node. It goes through all it's childs and calls Getvalue on them and then calculates it's total sum and the contribution for each of them. and then it calls Act on the child node with the lowest Contribution-Goal. This is the behavior of a branching node.<br />
<br />
When GetValue is called on a node it iterates all it's children and calls GetValue on them and add together the result and returns it This behavior continues until you hit a leaf node. This is because this is how a branching node works. But a leaf node is a managing node so it instead calls it's manager and return that value.<br />
<br />
So GetValue on a managing node returns the manager Value and calling Act on a managing node calls Act on it's manager.<br />
<br />
I should mention by now that this flow and these functions are a bare bone implementation. And you would probably like to extend it with extra functions like Try() Which checks if it is even possible so you don't spend time evaluating nodes that does nothing<br />
<br />
An important part is that you want a way to pass information about the state of the world to the parts of the tree. There are numerous ways to do this. One really simple way is to have the manager access all the information it needs about the world but this creates a huge data dependency. So I would prefer that you pass a struct to the tree with the necessary information even though the only nodes using this information are the managers it means that the part of the system collecting the data doesn't need to know about the manager.<br />
<br />
A good example of information to pass id enemy damage vs a type. For exampel is the enemy has 10 anti ground units and 0 anti air units you want to build aircraft if you send this data to the manager then the GetGoalValue can be modified based on ths information to increase the weight of that resource (yes this adds a GetGoalValue to the manager not much to do about it) There are others ways to place it. What's is important is that these values doesn't propagate upwards but even so it allows your Ai to make well informed decisions based on actual battlefield information by just crunching simple data in a tree structure.<br />
<br />
Just to make this clear you need an inheritance hierarchy on the managers and you need to find a way to represent it's value to send upwards. But this works quite well even for complex things like scouting.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com5tag:blogger.com,1999:blog-461014343580624539.post-32921629682419755792010-05-10T20:47:00.002+02:002010-06-27T20:16:55.356+02:00Auto balancing Decision trees aka Resource Trees IToday were gonna talk about a process for decision making that you can use for higher level Ai functions in games. Basically this method can handle purchases, tech level up. In some case even goal selection for a computer opponent. I will discuss this in the context of trying to make an Ai for an RTS game but the same principles will hold for a turn based strategy game and depending on context in a lot of other games too.<br />
So the basic idea here is the following. Computers are really good at managing tree structures we have tons of efficient algorithms that works well with them and we can search in them easily. This however is not just limited to existing algorithms, basically any kinda of data is normally easy to analyze if you can represent it as a tree.<br />
<br />
This comes from the fact that when you represent data as a tree you have already performed a divide and conquer on that data to divide it into smaller parts. Those making the code to crunch the data for each node of the tree a lot simpler than if you had to crunch all that data in one piece.<br />
<br />
<br />
<a name='more'></a>Today were gonna talk about a process for decision making that you can use for higher level Ai functions in games. Basically this method can handle purchases, tech level up. In some case even goal selection for a computer opponent. I will discuss this in the context of trying to make an Ai for an RTS game but the same principles will hold for a turn based strategy game and depending on context in a lot of other games too.<br />
<br />
So the basic idea here is the following. Computers are really good at managing tree structures we have tons of efficient algorithms that works well with them and we can search in them easily. This however is not just limited to existing algorithms, basically any kinda of data is normally easy to analyze if you can represent it as a tree.<br />
<br />
This comes from the fact that when you represent data as a tree you have already performed a divide and conquer on that data to divide it into smaller parts. Those making the code to crunch the data for each node of the tree a lot simpler than if you had to crunch all that data in one piece.<br />
<br />
This allows us to solve quite complex problems by dividing them into several simple problems instead. especially inside the field of AI the problems we want to solve are always complex but they can almost always be modeled in many small but simple parts. Then you just has to have the processing power to crunch through them. So if we manage to break down and restructure our data in such a way we have a significantly better chance of implementing a working algorithm.<br />
<br />
So with the background out of the way lets look at our algorithm for balancing strategic decisions by utilizing a the possibilities in multi connected directed graphs to propagate simple values into complex results. The basic idea is the same as of a neural network whee the end result is much more complex than the sum of it's part. But this contains no funky human based simulation or reasoning. Just clever use of tree structures.<br />
<br />
But the actual algorithm is just one of the pillars of this technique, the design of your tree structure is vital to getting a good result out of this technique. Pretty much like most things in AI where it is the implementation and not the algorithm that matters.<br />
<br />
The gist of the idea is that if you build a tree structure that contains all the resources that are possible to attain in the game then you can depending on how this data is propagated around in the tree get different calculations and evaluations.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQlJLrW1IR2ifXKVPkmbDMZzDu8Hvrj9W90ZHAFOtLinJfoZJ8xbp2VuWbPOM711VSlJdsXAp2UcYPTUWv5cyLcuSWeN0MsvjEea-RoBbnfOpAsrvvt_0eFy1Se8j2cxwffbtl6FKJ9fU1/s1600/Bild1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQlJLrW1IR2ifXKVPkmbDMZzDu8Hvrj9W90ZHAFOtLinJfoZJ8xbp2VuWbPOM711VSlJdsXAp2UcYPTUWv5cyLcuSWeN0MsvjEea-RoBbnfOpAsrvvt_0eFy1Se8j2cxwffbtl6FKJ9fU1/s640/Bild1.png" width="640" /></a></div>The above structure represent a simple tree from a fictive RTS game. It has 2 economical recourses spice and gold. It also have two types of units infantry and cavalry.<br />
<br />
The interesting part of these is that using this tree structure allows us to calculate the relations between the different part for example we can easily calculate how much of our economy that is defendant on spice vs gold. Or how much of our total resources are in troops compared to how much is in economics. This tree is obviously way to simple to allow us to see the true strength of the concept so before we go down and look at this case in detail and walk you through how the algorithm works let's take a look at a slightly more complex example first.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBJxXISFfv0G7JEoSZMcBnmQV1J2Yi_rJvz7DRv4Wu54OACMgXkhLn1wU3Ry_rfcxNUTqK2mznpy7btZFtDt709UwpbHfcCTXCOhGITByQWDl9bVXHriXBvs_q4X7TX7Ubqrt6TifUdaHV/s1600/Bild2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="380" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBJxXISFfv0G7JEoSZMcBnmQV1J2Yi_rJvz7DRv4Wu54OACMgXkhLn1wU3Ry_rfcxNUTqK2mznpy7btZFtDt709UwpbHfcCTXCOhGITByQWDl9bVXHriXBvs_q4X7TX7Ubqrt6TifUdaHV/s640/Bild2.png" width="640" /></a></div>While this tree still is really simple it starts to look a little more like the complexity you would face using this technique in reality. An interesting point to note is that some nodes have multiple parents in the tree which mean that they contribute to multiple categories in the tree. So their values will add upwards on multiple occasions.<br />
<br />
For example as an infantry unit is usable to booth attack and defense it contributes to both of those categories. And a tower is booth a building and a defensive unit so it contributes to booth of these. When you create a tree like this and try to actually map out all the relations that exists in a game then it will become quite complex. But the beauty is that you can map up really complex relations with a system like this<br />
<br />
These complex branches are the principle the algorithm is based on because with them you can map up all the complex relation ships that occurs in reality (or get close enough to fake it at least). A problem is that as you can see the tree very quickly grows complex and becomes hard to grasp. This is sadly an unavoidable effect of trying to map out those complex relations but you can get quite far by being intelligent about how you design your trees.<br />
<br />
A good start is to think of every part of the tree seperately and look at which nodes this node is connected to. So rather than trying to solve the big problems of making sure everything in the tree seems proper you just make sure that every node is connected to the other nodes like it should be and this in the end creates the tree structure for you. Divide and conquer is a powerful tool indeed and you can make it work for yourself too.<br />
<br />
Thanks to the advanced connections that this will create we can model complex behaviours down to a simple comparision of values that are propagated from the nodes. For example it would have been quite easy to get a realistic balance between military and civilian priorities by just looking at the values at those nodes in the graph. You can also control those propagations by using weights on the connections for example you can say that a Tower adds to military with 70% of it's current value but it adds to buildings with 50%. There is no need for these values to add up to a total 100% since you can balance this out by lowering the value for a tower to begin with (but it might be easier to just keep it to 100% in total for most people).<br />
<br />
The goal of all of this is that in the end for every node you can calculate the percentage of that node that is contributed by this child. Then in the AI personality you can have goal percentage values that represents what it would like this contribution to be. And the Ai can then work to purchase units or perform actions that will help to steer those values towards the goal values.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div>This allows for an easy way of making sure than at AI keeps a balance in it's behavior in regards to how different ares should be weighted towards each other in priority and thanks to the complex tree structure it can also create a pretty accurate view of the current situation. And thanks to the fact that the percentage values are personality based it's easy to create different Ai personalities with quite different behaviors by changing those values.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM4Fypwu88A49HV0yUoiwJZDLbV8D2cpAslh9Gw76GKxyD1gT9aP4_-mJDPioHLJOJg6rWolblKSV3FfAfr-Y39tYJNBNsWG_ABVx5KuKHmMR3gkNFQKoVzi1nzb7EsrHo3IkquREAg7q8/s1600/Bild3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM4Fypwu88A49HV0yUoiwJZDLbV8D2cpAslh9Gw76GKxyD1gT9aP4_-mJDPioHLJOJg6rWolblKSV3FfAfr-Y39tYJNBNsWG_ABVx5KuKHmMR3gkNFQKoVzi1nzb7EsrHo3IkquREAg7q8/s640/Bild3.png" width="640" /></a></div><br />
The 0.7 number here on the link means that the AI wants 70% of the value of total resources to come from troops and the 0.3 on economics means he wants 30% of the total resources to come from economics. And from the troops he wants 30% of the points to come from infantry and 60% from cavalry and the lat 10% seems to have gone missing :) well in Economics the numbers adds up at least and thats what we will be using for our example so.<br />
<br />
So we now have numbers on what kind of percentages the Ai wants to have but we also needs values on what kind of numbers it has right now. Because as we mentioned before the goal of the AI is to make the real numbers match these numbers as closely as possible. To achieve this we start at the root node and traverse the tree and in every branch we select to traverse the node which has the real percentage which is most below it's wanted percentage. And if you reach a leaf node you purchase that kind of unit or perform it's type of action or try to aquire that sort of resource or basically what ever you need to do to increase the value of that node.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrCGOJPXWB_TUu-LeDuj9cNpcSfUkF0G418ZykAbNaz-dE0qF4qCGOfZ1FpB1O2FfIWs_bQ4jYxBoWriIegaoWnmzE1UXPf403sEo9_86LI6Eaxa875TM2ZWqF9iFvlkBT0krUQ-uXATUp/s1600/Bild5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrCGOJPXWB_TUu-LeDuj9cNpcSfUkF0G418ZykAbNaz-dE0qF4qCGOfZ1FpB1O2FfIWs_bQ4jYxBoWriIegaoWnmzE1UXPf403sEo9_86LI6Eaxa875TM2ZWqF9iFvlkBT0krUQ-uXATUp/s640/Bild5.png" width="640" /></a></div><br />
So in this diagram we add in the base values for each node this is calculated differently depending on what kind of data the node contains. For units you can use a value that is depending on the units strength multiplied with the number of units of that type. For resources the resource amount currently in your treasury. You can basically handle any kind of value you want here. How much of the map you have scouted etc. It doesn't matter to the system the important point is that the value means what you are trying to represent and that the end result you perform of that node is selected can icnrease that value.<br />
<br />
So the way to resolve this all is to create a recursive algorithm we call GetValue which calls it's childrens GetValue functions and adds it all together and return that value. This functions allows us to calculate the value of any node we call it on.<br />
<br />
This will propagate the values up through the tree towards the root. So that you at each position can se the values at that specific point. This is a good time to remind you that there is nothing built into the system that forces a child to propagate all it's value to it's parent it could be multiplied by a factor if you find that suitable to what you are trying to achieve. There are no limits to this it's all about which way maps easier to your way of thinking.<br />
<br />
So to make it all work we first add in the values we calculated for the leaf nodes. which we have done in the diagram above. And then we run the algorithm to calculate the values for the parent nodes.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlhcbEvwko_OSiRiR188MVSvPuiBg2C9fUUEfSwiWHcptYdkD9gxm1QtZIox7dbCX4tb4ELF36M3BSS-oCHpimKTd2BJJb6sbf3J6A0BDIWH-G29be3zoDY7jzS-ybbV7Q6hTZdRj6uU_b/s1600/Bild6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlhcbEvwko_OSiRiR188MVSvPuiBg2C9fUUEfSwiWHcptYdkD9gxm1QtZIox7dbCX4tb4ELF36M3BSS-oCHpimKTd2BJJb6sbf3J6A0BDIWH-G29be3zoDY7jzS-ybbV7Q6hTZdRj6uU_b/s640/Bild6.png" width="640" /></a></div>As you can see we have propagated the values from all the leaf nodes upwards one step. We have also removed the goal values we had earlier trying to make the image clearer. We only kept them for the first 2 links. So Troops has a value of 1000 as that is what infantry+Cavalry equals and economics got a value of 100. This makes the total amount of resources in the system 1100.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBKso4Y1VNUkKOOIUtcnpo74mCwPnwlpww5eNK-8OdIJFurPuYShPGxASqPT5rCplwlI0Ep0avzVC1384Ule8EHXJO6RM1LVI26EPWfUBwIlKKCEZGEuiB3_L6i4Ixl6l3fV3Mm8z5a9CF/s1600/Bild7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBKso4Y1VNUkKOOIUtcnpo74mCwPnwlpww5eNK-8OdIJFurPuYShPGxASqPT5rCplwlI0Ep0avzVC1384Ule8EHXJO6RM1LVI26EPWfUBwIlKKCEZGEuiB3_L6i4Ixl6l3fV3Mm8z5a9CF/s640/Bild7.png" width="640" /></a></div><br />
<br />
To calculate which child node from the root node we should traverse down we have to calculate their contribution to the value of the node. and then compare that to the personality value for the AI.<br />
in the first case we can make a quick calculate 1000/1100 is 0.91 which is 91 % and 100/1100 is 0.09 which means 9%. So 9% of our resources are spend in economics and 91% of them are spent on troops. The code to perform these calculations are quite simple to write down but we'll look at the code just in case. To make sure everybody can follow the process.<br />
<br />
totalValue=0;<br />
<br />
for(i=0;i<mychilds.size();i++)><br />
{<br />
totalValue+=mychilds[i].GetValue();<br />
}<br />
<br />
for(i=0;i<mychilds.size();i++)><br />
{<br />
myChildContribution[0]=mychilds[i].GetValue()/totalValue;<br />
}<br />
<br />
This code follows a common patter for this algorithm. Since no node can know if it is the root node or not every node will have to assume it's the root node and perform the calculation sby adding togetehr all it's children to get it's own total value and after that get their comparative differences.</mychilds.size();i++)></mychilds.size();i++)><br />
But this simple code gets us their percentage values so that we can start using them.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYvHnlxtJo5KvyrWY5NM4hHCS5YlSSwXnDwp4EwmjGjKgx382q2bOWOCcZWrAFfsNWvL9ZCa8-SpFgNQP_mzPqymEDbjgmdzG7Jk6M7A_k_zYbO5Yly2jVBlKw7EePLVYG6knfuPSD7wa4/s1600/Bild8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYvHnlxtJo5KvyrWY5NM4hHCS5YlSSwXnDwp4EwmjGjKgx382q2bOWOCcZWrAFfsNWvL9ZCa8-SpFgNQP_mzPqymEDbjgmdzG7Jk6M7A_k_zYbO5Yly2jVBlKw7EePLVYG6knfuPSD7wa4/s640/Bild8.png" width="640" /></a></div><br />
If we for each child runs the formula of Current Value- Goal Value we get their difference value ( Positive differences are shown in green and negative are shown in red). What we are looking for is the child with the lowest difference because that is what we want the most in our attempt to balance the values. So in the case of our diagram we have focused way to much on troop sand let our Economics lag behind so we need to correct for this and perform actions that improve our Economics.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisVsGN4rEWHjb_WbehV5Qpa8g2q7Edw0g6q5lYdEOeAOWx99IHyfBxMUAkhGxmAYHSy-p3Iv29tH-NIutRjGZV87QKD3Ntgpl75_-2woqghTQosXwF4_WcnBimR8yIQIANci5xSLnUsBnw/s1600/Bild9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisVsGN4rEWHjb_WbehV5Qpa8g2q7Edw0g6q5lYdEOeAOWx99IHyfBxMUAkhGxmAYHSy-p3Iv29tH-NIutRjGZV87QKD3Ntgpl75_-2woqghTQosXwF4_WcnBimR8yIQIANci5xSLnUsBnw/s640/Bild9.png" width="640" /></a></div><br />
<br />
So we step through the tree to the economics node, when we do that we discover that it isn't a lead node but has nodes under itself so we repeat the process for this node too.<br />
<br />
By now we should look a bit at the functions of a node. The progress we have done so far call for a couple of functions.<br />
<br />
float GetValue();<br />
Recursively calculates the total value of all contributions to the node throughout the entire tree.<br />
<br />
float GetGoalValue();<br />
Returns the personality base goal percentage for the node. <br />
<br />
bool Act();<br />
This is the code called at the root to make the tree do something. This is the code that caluculates the child contributions as above and decide which one has the lowest value. Then it calls Act on that Child. If you call Act on a leaf node it will perform some kind of action to increase the value of that node we will cover this in more detail later.<br />
<br />
So what has happened so far is that we called Act on the Root node.<br />
It called get value on it's children that called Get Value on it's children and so on. And used this to calculate the differences. After it have decided that we needed more Economy it called Act on the Economy node.<br />
<br />
That's all for now, in the next post we are going to finish our example and also look closely at the different kinds of nodes in the tree and the concept of managers which is what ties this all together.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div>Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com6tag:blogger.com,1999:blog-461014343580624539.post-70115359163873073362010-05-03T09:50:00.001+02:002010-06-27T20:18:21.058+02:00Still alive here<b>3D: </b><br />
<br />
<b>Shadow Maps</b><br />
This is pretty much the standards for doing shadows in games nowdays, and the dafacto standard for doing them for movies, Shadow buffers in difference form shadow volumes is image based and can therefore handle alpha masked polygons like a fence, It also allows techniques for blurring or otherwise distorting the shadows<b>. </b><br />
<br />
<b>Percentage Closer Filtering </b><br />
A problem with shadow maps is that they easily get pixelized even with the best intentions. Percentage closer filtering is a technique to combat those jaggies and making your shadow edges soft, We will look at different implementations for example randomised kernel rotation with a poisson disc.<br />
<br />
<b>Percentage Closer Soft Shadows</b><br />
In the real world shadows become softer and softer the further they come from the object casting the shadow. This is how to do the same in games using a technique that builds on the Percentage Closer Filtering technique.<br />
<br />
<br />
<a name='more'></a><br />
I was supposed to get out a post for this Monday but between the volcanic ash cloud forcing me to to stay an week in paris and nordic game conference time has been slim. But now things are normalizing. I have some interesting subjects lined up to write about.<br />
<br />
<b>AI:</b><br />
<br />
<br />
<b>Automatically balancing decision trees.</b><br />
<br />
This is a simple and effective way to model the strategic level thinking part of an enemy that works by balancing out needs based on a personality with an assessment of the actual situation and the resources current available to determine the next logical course of action.<br />
<br />
<br />
<br />
<b>3D: </b><br />
<br />
<b>Shadow Maps</b><br />
This is pretty much the standards for doing shadows in games nowdays, and the dafacto standard for doing them for movies, Shadow buffers in difference form shadow volumes is image based and can therefore handle alpha masked polygons like a fence, It also allows techniques for blurring or otherwise distorting the shadows<b>. </b><br />
<br />
<b>Percentage Closer Filtering </b><br />
A problem with shadow maps is that they easily get pixelized even with the best intentions. Percentage closer filtering is a technique to combat those jaggies and making your shadow edges soft, We will look at different implementations for example randomised kernel rotation with a poisson disc.<br />
<br />
<b>Percentage Closer Soft Shadows</b><br />
In the real world shadows become softer and softer the further they come from the object casting the shadow. This is how to do the same in games using a technique that builds on the Percentage Closer Filterign technique.<br />
<br />
<b>Parallel split shadow maps aka Cascaded Shadow maps</b><br />
With all the above optimizations there are still problems<b> </b>with shadow map resolution. Various techniques has been invented to combat this problem all building on a distortion of the perspective matrixes (Perspective shadow mapping, trapezoid shadow mapping ,light space shadow mapping etc) but all of these have problems and requires a lot of tweaking. Cascade shadow mapping tries to solve these problems by using multiple shadow maps.<br />
<br />
<b>Deferred Rendering ( 3 lesson series)</b><br />
Deferred rendering is a technique to decouple lighting from the geometry allowing thousands of lights visible in the screen with decent performance and without need of extensive culling schemes. We willl talk about it's advantages and disadvantages and look at different implementations in games today and what people are doing with the techniques like Prelightining, light idnexed renderer and so on. This Series will bridge at least 3 lessons and require quite a bit of prior knowledge.<br />
<b> </b><br />
<b>Screen Space Ambient Occlusion</b><br />
As made popular by crysis. This technique models the ambient occlusion terms we have been showing earlier but allows calculation of them in real time instead. This allows us to have correct dynamic occlusion with moving objects in a living world.We might even go on to Screen Space Directional Occlusion.<br />
<b><br />
</b><br />
<b><br />
</b><br />
So these are the ideas I have lined up right now, They are quite 3D heavy at the moment, but as always I will gladly take suggestions or even votes no which of these that seem the more interesting.<br />
<br />
At worst we will start with one of these on next monday but I might get out an update before that pending on my time schedule.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com0tag:blogger.com,1999:blog-461014343580624539.post-78417277231901578532010-04-12T02:00:00.000+02:002010-04-12T02:00:01.050+02:00Slight hiatusDue to me getting married and going on my honey moon game school gems will have a short 3 week hiatus until I return.<br />
<br />
Wish me luck :)Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com2tag:blogger.com,1999:blog-461014343580624539.post-78162851966958913512010-04-05T14:00:00.003+02:002010-06-27T20:18:47.743+02:00Code Standards - Why we need themIt might be a bit far fetched to call this a gem, at the same time for those who have never worked in a team before or are trying to organize one this might be life saving. I also feel that I need to add some more articles that cater more to the beginning programmer, while these sorts of articles won't become the standard for the site they will appear from time to time.<br />
<br />
So what is important about code standards you may ask ? There are two main cases, one is when working with other people and the other is when you are working with code you haven't touched in a along time. The problem with a lot of code you find on the net and tutorials are that they are inconsistent in how the code looks or hard to read, obviously that lats part is subjective when you are used to it any way of coding might be easy to read. But when you are working with code you haven't touched in a long time you are unlikely to remember all the details of that code so any code standard that makes the code easier to read and understand helps.<br />
<br />
<br />
<a name='more'></a>It might be a bit far fetched to call this a gem, at the same time for those who have never worked in a team before or are trying to organize one this might be life saving. I also feel that I need to add some more articles that cater more to the beginning programmer, while these sorts of articles won't become the standard for the site they will appear from time to time.<br />
<br />
So what is important about code standards you may ask ? There are two main cases, one is when working with other people and the other is when you are working with code you haven't touched in a along time. The problem with a lot of code you find on the net and tutorials are that they are inconsistent in how the code looks or hard to read, obviously that lats part is subjective when you are used to it any way of coding might be easy to read. But when you are working with code you haven't touched in a long time you are unlikely to remember all the details of that code so any code standard that makes the code easier to read and understand helps.<br />
<br />
And when working in a team this is absolutely essential, imagine if everyone on the team had their own way of writing code. This would mean that every member would have a hard time reading the code the other members produce since they are written in their style. This leads to segregation of the code base with the different members working only on code within a certain area that they feel familiar with and can read easily (because it follows their standards). This is contra productive as ideally you want every member of the team to be able to read and maintain any part of the code so that you aren't tied down by the loss or absence of a key member.<br />
<br />
It also is important then looking for bugs that you can easily read and understand all the code that interacts with the code you are currently writing. Having a single unified code standard for the entire team makes this a lot easier as no matter who wrote the code it looks the same way and all you have to work with is actually understanding how the code works not the actual code itself. This tie neatly in with having a solid architecture in helping everyone to be familiar everywhere in the code because the same patterns are followed.<br />
<br />
I hope by now you can see the need of having a code standard as anything that helps readability of code lessens the chance of anyone misunderstanding the code and creating a bug. Of course they will still happen but the more you understand the code you are working with the fewer bugs you will produce.<br />
<br />
And if you opt to use a code standard it makes sense to try to create a code standard that makes as much sense as possible when reading the code, you could say that this is the job of comments. But the problem is comments easily get out of date when someone changes the code and not the comments, if the code could be clear enough that comments are unnecessary you have taken a huge step in the direction of code readability.<br />
<br />
So what should a good code standard contain ? Well that is a very open question for the rest of this gem we will look at the code standard used at <a href="http://www.thegameassembly.com/">thegameassembly</a> which is based on the one we used at <a href="http://www.massive.se/">massive entertainment</a>. We will look at which part we standardized and why.<br />
<br />
First out is variable naming one key point of our standard is to have describing names for everything without takign into consideration how long those names gets, the reason for this is that the time spent writing the code is incredibly small comapred to the time we spend thinkign about how to write it and debugging it. So any time we loose on writing those longer variable names are easily amde back on the shorter debugging time we gain by amkign the code more readable. We also enforce some standard on how the names should be written to make it easier to differer between data types and functions etc.<br />
<br />
Functions is written with a big letter first and after that every new word starts with a new big letter.<br />
<br />
Good Examples<br />
<br />
<br />
<div style="color: #274e13;">LoadNormalMap();</div><div style="color: #274e13;">GetYAndNormal();</div><div style="color: #274e13;">GenerateHeightfield();</div><br />
Bad Examples<br />
<br />
<div style="color: #660000;">getOwned();</div><div style="color: #660000;">readyfiringAnimation()</div><br />
A class name also begins with a large letter for example<br />
<br />
CollisionObject_Sphere <br />
CollisionObject_Box <br />
<br />
A variable always start with a small letter and after that every new world starts with a large letter, but this is not all depending on the variable it might have a prefix. As readability is our goal here and I think you can see how we strive for the text to read as plain English the m_,s_ etc from Hungarian notation isn't really going to fit in well so we are going to use prefixes that makes sense from an English perspective (Btw all of these was taught to the founders at <a href="http://www.massive.se/">Mas<span id="goog_1262626195288"></span><span id="goog_1262626195289"></span>sive</a> from people at Ericsson so it is not just a standard we made up even though we have modified it). So the standard works as follows an member of a class is prefixed with my a argument to a function is prefixed a,an or some depending on what makes most sense grammatically. Static members of a class is prefixed our instead of my, and global variables local to the file is prefixed local while truly global variables are prefixed global<br />
<br />
Example<br />
<br />
void SetHealth(int aHealth)<br />
{<br />
myHealth=aHealth;<br />
}<br />
<br />
You can clearly se on the variable name if it is the internal variable or the argument by just looking at the prefix this helps with avoiding name collisions while making the code nice and readable.<br />
<br />
<br />
Factory* GetInstance()<br />
{<br />
return(ourInstance);<br />
}<br />
<br />
By prefixing the variable with our it is clear that it's a static member this way we can easily make sure that we are only trying to access static variables from static functions.<br />
<br />
The beauty of all of this is that by a simple look at the variable name I can easily see where it comes from and act accordingly and since we don't mix any weird signs or short codes the code still feels easy readable as long as you get used to the amount of text(which goes quickly, and you will be happy for it because your code will read like comments).<br />
<br />
I will not say that these are the only way to flag the differences between variables but it is a good way, the important part is that you have a way to see from where the variable is coming this way it's easier if you have to look at a piece of code to quickly figure out what it is doing.<br />
<br />
While naming standards is one of the more important parts of the code standard it isn't everything there are still other parts that will help you when developing. I went with those first because they are relatively uncontroversial (Get 6 programmers in a room and start talking about code standards and blood will most likely fly after a while) these other are a bit more discusses but also really important.<br />
<br />
For if cases always uses the brackets, this way you can easily place breakpoints inside the statement.,<br />
<br />
Good example<br />
<br />
if(myHealth<10)<br />
{<br />
myScore-=10;<br />
}<br />
<br />
<br />
Bad example<br />
<br />
if(myHealth<10) myScore-=10;<br />
<br />
<br />
While the code example is silly the huge difference is that in the first version I can put a break point inside the brackets to see when it happens and in the second version I can't.<br />
<br />
So always use the brackets even when it feels silly because one day you will want to put that breakpoint there and if you use brackets somewhere and not elsewhere your code will be inconsistent.<br />
<br />
A topic that is often under heated discussing is where to declare your variables, there are basically two schools. One says that you should declare all your variables at the top of your function. That way you wont create any variable naming conflicts unknowingly and if a person wants to see what data you are using he can find it all safely in one place. It's actually a hard case to argument against the major argument is most of the time that it is bothersome to scroll to the top of your function to see all the variables, of course here is a hint if your functions are so long that you are having trouble scrolling in them then they are too long and should be divided into smaller parts. However all of this is subjective and some people really hate having to scroll around.<br />
<br />
The goes argument that can be said for declaring your variables when you use them is that you can see the variables easier and if you are working on a small fix you can add a new variable really quickly (and I absolutely love anything that speeds up development) , but the big part is that when declaring variables inside for loops etc the compiler knows that they wont be changed outside of it and can perform various optimizations. If you unroll a loop once to use booth the execution pipes on your processor this is essential.<br />
<br />
So there are good cases for booth sides. For me personally I leave most of my variables at the top of the function but I also add them in the middle of the function where it makes sense in an attempt to get the best from booth worlds (But then also the worst but ohh well you can't win this one)<br />
<br />
A code standard also normally talks about how you use enums, constants and #defines<br />
<br />
We used the standard that everything we defined should be CAPS only to make certain that we know that this is a define and those dangerous.<br />
<br />
Lets define the same data as the different type <br />
<br />
#define MAX_NR_OF_CLIENTS 16<br />
#define RANDOMMACRO() ;<br />
<br />
Clear caps and stands out amongst the rest, this is most important for macros as they are inherently dangerous.<br />
<br />
const int localMaxNrOfClients = 16; <br />
<br />
Observe the local prefix it means that it is restricted to this file so it is defined in a .cpp file (In some cases we have opted to sue CAPS after local for consts too to make them stand out more<br />
<br />
<br />
const int globalMaxNrOfClients = 16;<br />
<br />
Since this is declared as global it is declared in a header file so everyone can read it, the caps issue is the same for this one.<br />
<br />
For enums we also decided to have all them as CAPS.<br />
<br />
enum classType<br />
{<br />
RANGER,<br />
WARRIOR,<br />
MAGE<br />
};<br />
<br />
This is so that people that see them knows they are not normal variables and don't try to treat them as such.<br />
<br />
Finally we are down on the last part the project/file/class naming conventions.(Yep we did class once but now we look at a possible prefix to put before the class definition) a problem for C++ development is name collisions and also knowing in what project a certain class resides, as with many things there are two major schools of this, one that I will call the old school and one which tries to rely on namespaces. Having worked with booth they booth have their advantages and disadvantages.<br />
<br />
Common to booth is that you are trying to create a short acronym to use to represent your project (2-4 letters are the standard)<br />
<br />
Examples<br />
<br />
WIC_Player becomes WICP_<br />
WIC_Common becomes WICO_<br />
<br />
Msound becomes MS_<br />
Nemesis3D becomes N3_<br />
<br />
etc<br />
<br />
When using the old school method every file is prefixed with this tag and so is the class name. This has several really neat advantages, you minimize the risk of naming collisions which makes it perfectly ok for you to have several classes with the same name except for the prefix and you avoid any possible file collisions by changing the file names too. When you see a class or a file you know directly what project it belongs too. Of course the problem arises if someone else uses the same prefix as you and also your classes are permanently renamed. In C++ a feature has been added called namespaces, this is intended to be used to remove naming collisions by grouping your classes under a namespace. This can be used on a per project bases and it has a very important feature which allows you to create a short name for the namespace dynamically when you are using it. So your Namespace could be Called Nemesis3D and then in the project using it you just declare that N3 = Nemesis3D the beauty of this is that you aren't changing the name of your class and in the case of a collision you will only have to change what short name you are using for your namespace, you don’t have to physically change the code.<br />
<br />
Of course nothing is perfect and the big hassle with namespaces is that they don't help with file collisions therefore you are often forced to use a prefix on the files still which kind of goes against the idea. I have worked with booth and would have a hard time deciding which one to use for the moment. The flexibility and correctness of using namespaces is really nice especially that I can sue a using declaration when working inside of the project to remove the prefixes and cut down on code amount. But the hack with prefixes son files are still bothering me so I would say it depends on project length but I will definitely look to see if there isn't any nice way to solve the file prefix issue first.<br />
<br />
This has been a brief look at a coding standard, a real standard needs to contain allot more for example how we handle switch cases, how we handle errors, what we are allowed to do in a constructor, What design patterns we commonly use and why, the list can go on. But the big point here is that what is important is to have a standard not the exact standard I have tried to show you an example of a part of a standard and I have also tried to show and discuss some of the issues that might appear when trying to select a standard but the rest is up to you.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com3tag:blogger.com,1999:blog-461014343580624539.post-66486324383388184192010-03-28T14:00:00.001+02:002010-06-27T20:21:27.398+02:00Influence Maps II - Practical ApplicationsNow it is time to look at how we can use influence mapping in a practical application showing how we can use a simple influence mapping based scheme to create an AI that reacts surprisingly realistic and intelligent as long as it works within certain perimeters and you aren't trying to control it too precisely. This test as I have implemented it will only be using the basic Influence map but we will discuss interesting possibilities of using the other maps too. I am also sure that you by yourself will imagine a ton of ways to use the maps and even create new maps after these two articles. But for now we look at creative ways to use what we have learned so far.<br />
<br />
What we are going to be looking on is what we used early on as an experimental Ai for <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2</a> it actually become the basis for that AI though heavily modified, partially because it did defeat our project lead 1 on 1 but mostly because the single player team needed a lot more control to create interesting single player missions. But within it's limitations it performed admirably and as I mentioned did become the basis of the AI.<br />
<br />
<br />
<a name='more'></a>Now it is time to look at how we can use influence mapping in a practical application showing how we can use a simple influence mapping based scheme to create an AI that reacts surprisingly realistic and intelligent as long as it works within certain perimeters and you aren't trying to control it too precisely. This test as I have implemented it will only be using the basic Influence map but we will discuss interesting possibilities of using the other maps too. I am also sure that you by yourself will imagine a ton of ways to use the maps and even create new maps after these two articles. But for now we look at creative ways to use what we have learned so far.<br />
<br />
What we are going to be looking on is what we used early on as an experimental Ai for <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2</a> it actually become the basis for that AI though heavily modified, partially because it did defeat our project lead 1 on 1 but mostly because the single player team needed a lot more control to create interesting single player missions. But within it's limitations it performed admirably and as I mentioned did become the basis of the AI.<br />
<br />
The actual implementation contained a couple of maps some of which will be familiar by now and some that aren't because they where specific to our exact implementation.<br />
<br />
<ul><li>myInfluenceMap</li>
<ul><li>contains all the influence from my troops </li>
</ul>
<li>EnemyInfluenceMap</li>
<ul><li>contains all the influence from enemy troops. This is adjusted for how well I know the area in an attempt to try to make the Ai realize when he's just seeing the tip of an iceberg. It also handles the influence of enemies I am currently not seeing but I know are there, or can make reasonable assumptions that they should still be there.</li>
</ul>
<li>Influence map </li>
<ul><li>The combination of the above maps. myInfluenceMap-enemyInfluenceMap</li>
</ul>
<li>Block map</li>
<ul><li>Contains information about whatever the center of a tile in the influence map is blocked.</li>
</ul>
<li>EnemyBlockMap</li>
<ul><li>Contains information about whatever the center of a tile in the influence map is blocked by being inside to strong a enemy influence</li>
</ul>
<li>Goal map</li>
<ul><li>A map where we can pain in extra influence used to guide our troops to different locations. </li>
</ul>
<li>Area map</li>
<ul><li>A map containing an ID for each tile. Having the same ID means there is a possible path between the tiles. So basically it was created by flood filling all passable areas on the map and increasing the value written with one every time you had filled a complete area.<br />
</li>
</ul>
<li>Strength map</li>
<ul><li>Contains for each tile value that tries to represent the important of that tile for fighting and strategic value. All Influence on this tile is multiplied with that value. This allows for hand painting and marking of important areas.<br />
</li>
</ul>
<li>Final Influence Map</li>
<ul><li>Influence Map+Goal map</li>
</ul></ul>First out we will go into some detail about the new maps, especially the goal map and then later on we will explain what we are going to use all of this for. First out is the goal map, the goal map is a way to add or remove influence around an area, and the question might arise why this of any use is for us? That is a good question and the answer is it depends, depends on what you are using your influence map for.<br />
<br />
Our basic idea for now is that we will create a simple AI which has as its main goal to eradicate enemy influence in the world. This means that if we put a strong marker of enemy influence somewhere with the goal map our troops would want to go there and smash it. But at the same time since we are using the influence map, the same map that is influence by opponent troopers and buildings etc, they wouldn’t ignore something really obvious just to get to that far away spot with some enemy influence. They should calmly exterminate influence and our maps are just hints that will be taken in and mixed with the overall position to create a reasonable plan. Following our hints when they make sense, but ignoring them if there are something urgent nearby. This creates an AI that fight reasonable within the limits we have put on him he won't just drive by a huge enemy troop ignoring it to accomplish his goal or any of the other weird behaviours an over controlled AI might exhibit.<br />
<br />
So if we work with that as the basic thesis for our AI then using a goal influence map makes perfect sense. Basically we can order our troops around just by writing in a map, we can also give them goals of different importance and they will prioritize base on their distance to the goals and the surrounding circumstances. Without bothering our high level AI with the small details.<br />
<br />
<br />
This system opened up a number of interesting possibilities, Booth <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2 </a>and <a href="http://worldinconflict.uk.ubi.com/">World In conflict</a> are based upon the concept of command points. By dominating these points you win the game in the end, the implementation differs in <a href="http://www.massive.se/games/ground-control-series/#gc2">GC2 </a>multilayer you won by taking all of them, in <a href="http://worldinconflict.uk.ubi.com/">WIC </a> by controlling a majority of them for a majority of the time. But the basic idea is the same there are command points and you win by taking them over. So we painted goal influence on the command points. How much we painted however was calculated based on a formula depending on how many the AI controlled and how many the opponent controlled. Generally neutral command points had a higher importance since a battle that can be won without a fight is more important, Then second is the enemies points and third my own. The part of adding opponent influence at my own zones is a way to make sure that some troops are left behind to guard the zones and keep them safe. However the different ratios differed depending on how many zones where captured by either side. For example if I have 4 zones out of 5 defending my current 4 is a lot higher priority than if I have 1 zone the opponent 1 and 3 are neutral. All this was taken care of when evaluating the influence amount used in the goal map.<br />
<br />
<br />
We also had what we called a desperation factor which basically meant that if the AI was clearly loosing then all bets was off and just try to take something to turn the tide even if it is improbable to succeed since it's loosing anyway playing they way it's currently playing.<br />
<br />
This weren't the only use of the goal influence map however. It was also used for allowing mission designers to flag up areas in the map that was important and that the AI should probably want to hold allowing specifications of areas for the AI to fortify in, again without breaking the system, however all if this have an disadvantage too, you loose the amount of control you have over the AI so it is hard to plan exactly what it is going to do. But with this you can mark important choke points or generally spots you want the AI to gather at. This was also combined with the strength map we talked about earlier, together this allowed for a powerful hint system that gave the AI a lot of information while not tying its hands.<br />
<br />
We also used this is we had a group of troops that really wanted to go to an area but the enemy in that area was simply to strong so the tiles with the high influence we wanted to reach got blocked. Then we made a strong goal influence map circle around the enemy troops allowing our troops to be drawn there and gather strength until they where strong enough to attack.<br />
<br />
Another important map is the scout map, this map contained information for every tile how long it was since one of our players saw it. This is achieving by increasing every tile with 1 each AI frame (the AI isn't evaluating all this stuff every frame that would be senseless so the AI will run on a different frame rate compared to the game). And then for all tiles seen by a unit it is set to 0. Even with a low resolution this gives a surprisingly accurate view of the AI's knowledge of the world. Based on this we could with reasonable probability estimate if the unit we are seeing is really alone or part of a group just behind my line of sight.<br />
Also using a simple algorithm that searched the highest value/distance to the tile proved a very effective scouting algorithm that sweeps the play field booth doubling back to recheck earlier checked tiles and exploring the big unknown. A couple of units doing this made short work of an entire map. <br />
<br />
Actually the algorithm described above can be used with great success for every unit in the game if we just check a different map. Obviously we did more stuff above this but with only the following simple algorithm you get a very interesting AI that will detail troops from a larger group to strike down enemies it encounters on the way without necessarily spending all its forces and forget its goal.<br />
<br />
<i>int myId=GetTerrainID(myPosition);<br />
</i><br />
<i>// gets the ID from the area map for that position<br />
For(i=0;i<nrtiles;i++)><br />
// could be all the tiles in the world or the tiles within a certain distance<br />
{<br />
if(myBlockedMap[i]==false)<br />
{<br />
// well then I cant move there so skip this tile <br />
if(myEnemyBlockedMap[i]==false)<br />
// the enemy is too strong here currently so I can't move there either.<br />
{<br />
if(myAreaMap[i]==myID)<br />
// Make sure there is a path to the position before I continue<br />
{<br />
int eval=finalInfluenceMap[i]/distTo(i);<br />
// influence divided by distance gives a good measure for how important that tile is for me right now<br />
if(eval>bestVal)<br />
{<br />
// if this is the most promising tile for me to eradicate enemy influence store it.<br />
<br />
bestVal=eval;<br />
bestTile=i;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
myEnemyBlockedMap[bestTile]=true; </nrtiles;i++)></i><br />
<i>// Important step else multiple units might try to move to the same tile</i><br />
<i>// Give the unit an order to move to that tile.<br />
// Or if you want to be fancy a position that is good for attacking that tile.</i><br />
<br />
This code is deceptively simple but because your units detailed to deal with a force will block the tiles for other units you can't detach too large a force. Also once your units gets closer they will start to negate the influence at that point and troops that we have proved aren't needed anymore can move on to other targets. It all happens quite gracefully, almost like magic sometimes. Of course like all magic sometimes it fails and breaks down, but what can you expect from such a simple algorithm. But it is a good start for an RTS game AI.<br />
<br />
While it is a sort of instant AI a lot of it behaviour depends on how you modify the maps governing its behaviour. You could for example put in the influence of a unit on the position it's decided to move towards to make certain that you won't send to many units, but then again only blocking tiles go far and you might get away with it. It might be better for the AI to send to many guys and let the rest move on than to send to few and get hammered. Obviously I wouldn't base an entire game AI on only this but it does look quite incredible in action. Especially considering it's nothing more than individual unit evaluations and some maps.<br />
<br />
So how did we do it then? Well the AI was almost entirely influence based on some levels but we had booth higher level mission dynamics and request messages and a lot of stuff that would take way to much detail to cover here, so I will instead cover what we did with the influence mapping based part of the game. All combat was based on influence maps and the attack position data base. The influence map pointed a target and the attack position database found the best position to attack that target from.<br />
<br />
We also always had our units grouped in squads this meant the AI never sent a single unit unless it was a squad that had been decimated. So the influence map worked on a slightly higher level allowing the grouping behaviour to seem more cohesive, this did loose some of the power however but it was an important trade-off for singe player.<br />
<br />
We also analyzed the entire play field and all units on it and mapped them into groups (Because we had a platoon system we could optimize this a bit and only look at relevant units.) based on their location (and goal but again we will ignore that for now) This created logical combat groupings booth on the AI's side and the opponents side. Then for each group he decided what groups was to strong for him to attack and blocked the tiles those groups could attack so that they AI won't walk into an ambush. If they should be blocked or not was based on a calculation of the AI estimating it's percentage probability to win a battle, these percentages was different for different personalities. The percentages was also affected by the desperation factor if you are desperate you don't have time to be chicken. If we had a clear strong goal that was blocked we painted the earlier described circle around it in the goal map, units would mass there until the group became strong enough to fight the enemy at which time the block would be removed and the goal map reset to pointing to the goal and the AI troops would be storming in from all sides. Obviously any artillery he possessed could happily pound the encircled group while it waited on reaching its full strength.<br />
<br />
Of course the opponent could do the same so we had code blocking tiles the opponent were currently targeting with artillery fire so the AI's unit moved out of the way. As you can see this isn't a perfect be all end all solution it still requires tweaking but it is a good start.<br />
So this is what we can discuss about what we did the interesting questions however are what can we do now with all these nice maps we have created? There are endless possibilities but lets explore some of them. An obvious one is weighting the importance of a command point in the goal map based in its vulnerability, more vulnerable command points either needs more defence or are good targets for attack.<br />
<br />
We might even change so that the value it calculates in the search isn't influence/distance but (influence+vulnerability) instead. The world is ripe with things to try out and that's probably the most important step to try them out and play around with them and see what you can use them for. I think that especially the higher level goal selecting AI can do a lot of nice stuff with this extra information.<br />
<br />
We can also use the group concept we discussed earlier and move it to another level and making sure that the entire group gets a strong goal that is only for that group, this would be easy to achieve with a goal map per group this way we keep the flowing movements of the troops but we also get's focused troops going after specific targets and in this high level analysis the tension and vulnerability maps are essential.<br />
<br />
I have given you a basis the rest is playing around with it and see what happens, remember to play around and have fun with your AI that is the key to success with itNiklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com2tag:blogger.com,1999:blog-461014343580624539.post-17758299895943013072010-03-22T14:00:00.001+01:002010-06-27T20:21:52.411+02:00Influence Maps IIn artificial intelligence one of the biggest problems is how to convert a complex world into a set of data that the AI can actually understand and work on. The real world or even a game world is infinitely complex and different techniques to compress that infinite data set into a usable format is a classic problem in ai research. Thankfully for games we don't have the trouble of having to interpret the image from a camera to create a 3dimensional world to move around in.<br />
<br />
We have the advantage of having access to extremely detailed data of everything in the game. But the amount of data we have access to makes any sort of planning by the means of calculations impossible. The world is simply to complex, so in order to be able to work with it we have to employ a series of different algorithms the one we are going to look at today is called influence mapping and it's a way to resolve the actual strength relation ship on a map and helps to base tactical decisions on this. It has nothing to do with purchase planning or high level strategical decisions It's simply a analysis tool to create an accurate and usable view of the world.<br />
<br />
Influence maps are based around the concept that objects in the game influences the strength relationships between the players and this influence spreads from their current position outwards throughout the map. If you add in all the influence of all the objects then you would get a view of the relative strength relationships for that part of the map. You should also be able to detect the actual front lines between the different sides with it too. As we will show later on we are able to calculate a lot of different data from the influence maps but for now I think it is time to go back to explaining how this all works. But as a tease in the end of this we will discuss how to create a simple army level opponent behaviour by only using influence maps. There will be limits to it of course but it is still pretty cool.<br />
<br />
<br />
<a name='more'></a>In artificial intelligence one of the biggest problems is how to convert a complex world into a set of data that the AI can actually understand and work on. The real world or even a game world is infinitely complex and different techniques to compress that infinite data set into a usable format is a classic problem in ai research. Thankfully for games we don't have the trouble of having to interpret the image from a camera to create a 3dimensional world to move around in.<br />
<br />
We have the advantage of having access to extremely detailed data of everything in the game. But the amount of data we have access to makes any sort of planning by the means of calculations impossible. The world is simply to complex, so in order to be able to work with it we have to employ a series of different algorithms the one we are going to look at today is called influence mapping and it's a way to resolve the actual strength relation ship on a map and helps to base tactical decisions on this. It has nothing to do with purchase planning or high level strategical decisions It's simply a analysis tool to create an accurate and usable view of the world.<br />
<br />
Influence maps are based around the concept that objects in the game influences the strength relationships between the players and this influence spreads from their current position outwards throughout the map. If you add in all the influence of all the objects then you would get a view of the relative strength relationships for that part of the map. You should also be able to detect the actual front lines between the different sides with it too. As we will show later on we are able to calculate a lot of different data from the influence maps but for now I think it is time to go back to explaining how this all works. But as a tease in the end of this we will discuss how to create a simple army level opponent behaviour by only using influence maps. There will be limits to it of course but it is still pretty cool.<br />
<br />
So lets start with the default case, to keep everything simple we will for now assume that we are writing the ai for an RTS game where you command different kinds of modern war units with the goal of obliterating your opponent. So you have a unit doesn't matter what type yet just a generic tank or anything, this unit's spreads influence over the play field, the amount of influence he spreads is relative to his strength and how much he can affect the area so it fades away as we get further and further from the unit. As a side not for all the test images here I have been using the ancient game of GO as it allows me to place different colored bricks as unit's and easily measure the influence of them.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyLVQzWfN9JZD2f7xWYeQ1xA11N8FDt5fPF1VVPmcvfUnsWT5mIR-rYEac5VkmP87M3lNUgDX-1u_ZKqyoQPyhpTPheBnmO_IN6w9csL_XWoywndpLRCRS-dNh3GwcGvuTJjEhmlJz7CX1/s1600-h/influence_step_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyLVQzWfN9JZD2f7xWYeQ1xA11N8FDt5fPF1VVPmcvfUnsWT5mIR-rYEac5VkmP87M3lNUgDX-1u_ZKqyoQPyhpTPheBnmO_IN6w9csL_XWoywndpLRCRS-dNh3GwcGvuTJjEhmlJz7CX1/s400/influence_step_1.png" /></a></div>Here we have place a single black unit towards the upper corner for the rest of these examples the black units will be yours and you will be fighting towards the horrible evil white player. Your units project a positive influence around them which dissipates based on distance from the unit (we will later go into detail for different methods to decide how the dissipation should work).<br />
<br />
We will soon be placing your opponents units too. As your units will project a positive influence your opponent will project a negative influence which means if the value of a position is 0 it is even if it is above 0 you are having more control and below 0 your opponent has more control. We have selected to use yellow to represent the positive values and blue as the negative values, and don't mind the + and 0 signs they are just leftovers because I base the images on GO.<br />
<br />
<br />
<br />
<span id="goog_1261001263622">TThis example feels pretty clear on the concept of how influence spreads through the playing field. I hope you can see that the influence actually moves all the way to the edge of the playing field it is just so weak there that it is hard to see it. This is a basis of some of the methods based on influence mapping because else it will be tough to detect where areas of influence meets. But in a real game in a filled world this won't really matter and we can save some performance. <br />
<br />
<br />
Now however is time to talk about the playing field, since we are translating from an infinite world to simplify it then it makes sense that we should have a fixed resolution on the map we are using to keep our influence data. And this is the important step, the higher resolution you use the more details you have in the map however the more performance it takes to use and the less you can use it. And the smaller it is the more you can use it but the less information you can have. In both <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2</a> and <a href="http://worldinconflict.uk.ubi.com/">World In Conflict</a> we used a 200x200 influence map on a 512x512 tiles play field. This allowed us to perform a lot of calculations while still maintaining a decent level of detail. However you will have to discover for yourself what resolution is suitable for your game, since all our visual examples are based on go they will have a 19x19 resolution this doesn't mean you should use this resolution, in fact it would probably be a horrible decision unless all you're after is a very very high level view.<br />
<br />
</span><br />
<span id="goog_1261001263622"><br />
</span><span id="goog_1261001263623"></span><br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnP7wDNUKpWSVmXvKOO9rcul7SVyRxbtyWHVqB9ivhm7B6Nk9nZM3_x8jltu6F8W0foW6oKIo05YKCidWAogYpVKlVvvdpM2LmHZx5_nWGTytus63vWTnlPkIuYKdsiS69Mp1tjM-B9wXi/s1600-h/influence_step_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnP7wDNUKpWSVmXvKOO9rcul7SVyRxbtyWHVqB9ivhm7B6Nk9nZM3_x8jltu6F8W0foW6oKIo05YKCidWAogYpVKlVvvdpM2LmHZx5_nWGTytus63vWTnlPkIuYKdsiS69Mp1tjM-B9wXi/s400/influence_step_2.png" /></a>Here we have added an opposing unit, notice how the white unit's influence is negating your influence creating a clear conflict zone in the middle between your forces. This image clearly shows that black is in control of the top and white is in control of he lower side. We can also see that there is some sort of conflict in the middle. We could even judge the intensity of the conflict by analyzing the gradients of the influence values. But we will later look at more efficient and reliable ways to calculate the tension of a tile. By now I think the basic concept of the influence map and how it works should feel clear however there should be a lot of question about the details like how far do I influence how does the dissipation of strength works etc. So I will try to cover it right now so you feel you can grasp the complete algorithm and then we will move onto more ways of using it.</div><div class="separator" style="clear: both; text-align: left;">First let’s make the dissipation process a bit clearer. Keep in mind while you are reading this that there are many ways of implementing the dissipation and none of them are really incorrect in fact after this paragraph I will discuss how dissipation worked in <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2 </a>and <a href="http://worldinconflict.uk.ubi.com/">World In Conflict</a> which will differ a bit from the base methods for reasons I will discuss later on.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div>First out is quite an easy dissipation method for every tile you move away from the original influence creator its value is divided with two. This has the advantage of making sure that every unit is somehow influencing the entire play field making sure that you can detect some kind of front line even if the opposing units are really far away from each other. So how does this look in practice? <br />
<br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghhBKqn92O66uzNXhFk0oRrWs5kLV0_YDqqGkBJIlESRFzyikAeZGtIaMMZ-odH9694h08SnEQy_Z3CsJrbJxI7HGEh2WZS-Jg7-CGfsByiOuq3tDKyJhLZ0VSGgaWgNIITVjITOSuNCP9/s1600-h/Influence_Numbers_logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghhBKqn92O66uzNXhFk0oRrWs5kLV0_YDqqGkBJIlESRFzyikAeZGtIaMMZ-odH9694h08SnEQy_Z3CsJrbJxI7HGEh2WZS-Jg7-CGfsByiOuq3tDKyJhLZ0VSGgaWgNIITVjITOSuNCP9/s320/Influence_Numbers_logo.png" /></a>The tile marked with the black circle is the originator tile where our unit is standing. So the tiles adjacent to it have 0.5 and the tiles adjacent to them have 0.25 and so on, in this case a diagonal tile isn't counted as adjacent. There is nothing that stops you from doing it like that though. In fact a diagonal tiles centre is 1.41 tiles away so it is probably more correct to count diagonal. This method has some disadvantages though, it is hard to properly represent a unit that has a strong influence within a large area but doesn't really affect much outside of it like a stationary artillery piece. Overall you can’t easily map that the units full influence hits a certain area and then starts dissipating after that. These problems are what lead us to use a different view in <a href="http://worldinconflict.uk.ubi.com/">World In Conflict</a>.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaGoQrJWWe9sjqY7ROmtk-5Kj7nWKZEiSfVotJd4MZ0MZuk-88DVitm3oQ8F58gjULp1QGxPDJVPtb56u8_0Jtw7tlUpiRYM9-jcsaWUPIuRP0cVwH0qm-u0Xjx_cPPY-7sYePhgNNGvfN/s1600-h/Influence_Wic_logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaGoQrJWWe9sjqY7ROmtk-5Kj7nWKZEiSfVotJd4MZ0MZuk-88DVitm3oQ8F58gjULp1QGxPDJVPtb56u8_0Jtw7tlUpiRYM9-jcsaWUPIuRP0cVwH0qm-u0Xjx_cPPY-7sYePhgNNGvfN/s320/Influence_Wic_logo.png" /></a>We instead opted for a version where we could specify a influence strength and a fall off distance for that strength, in fact we had booth an area that was the full strength area of each unit and a fall off area after that. But to keep this image simply I have used only a single unit with an influence of 3 and a fall off distance of 3 as a side note despite me using floating point numbers for these diagrams in the real world I would instead use higher influence values so I could get away with integer numbers instead. One important part of the <a href="http://worldinconflict.uk.ubi.com/">WIC</a> method is that we calculated the true distance from the unit to the centre of the tiles, This means that you might not get your full influence value on a single tile if you where standing near the edge of a tile, it also meant that diagonal distances etc was taken care of correctly.</div><br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFYp8NinwM_FilEG9DiCLLobeuxs2eFFW0MPRJl2pNxcbQd_9rTfpLganWW9SDTWw-xjxLjEVkNk-1CZjAaQB3KIxAUUipAgr_0679hJFRS_8UD4jstFtVi07mQKTfhTVAuwNSbKGAJbD1/s1600-h/Influence_Linear_complete_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFYp8NinwM_FilEG9DiCLLobeuxs2eFFW0MPRJl2pNxcbQd_9rTfpLganWW9SDTWw-xjxLjEVkNk-1CZjAaQB3KIxAUUipAgr_0679hJFRS_8UD4jstFtVi07mQKTfhTVAuwNSbKGAJbD1/s640/Influence_Linear_complete_logo.png" /></a></div><div class="separator" style="clear: both; text-align: left;"> Another approach is to take what is referred to as the <a href="http://en.wikipedia.org/wiki/Taxicab_geometry">manhattan distance</a> which basically is how many tiles you will have to pass through to reach that position if diagonal moves are forbidden. Use that as a distance factor instead of the true distance. But no matter how we calculate the distance we still plug it into the formula below.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>influence_contribuition(x,y)=UnitInfluence*_max(0,1-(FallOfDistance/(FallOfDistance-DistanceToXY)));<br />
</i></span></div><br />
<br />
An interesting case that occurs is how should we handle if we have blocked tiles that we can't move too? This all depends on what you are mapping up with your influence but if it’s areas your unit can reach (and not areas it can shoot at) Then you need to find away to not give it influence to tiles that it can't reach. Using the Manhattan distance it is easy to do this as you just calculate the real distance to the tile considering that you can't move through black squares. This way your unit won't give undue influence (if you’re influence consists of two parts where one is where you can shoot and one where you can move you should only do this for the movement part, unless of course you are able to flag areas you cant shoot at too). In the image above we follow what happens in two cases here if we start out with the red arrow nothing changes because the block isn't increasing the path to the target, While the blue arrow has to take a large detour making it travel through 4 tiles instead of 2 negating it's influence.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8PokVaYaVg8J7YEVAT8XO6KewDPqolTI-x5wRn8omvhO-zRPkcjs4w43O9HXyDnGgvntOkNfX3zgfP7_0c18Pa6mWQWOa9QHVaohVPTZ_-fg_iZ-siL4cgy3Mo8qMhj1xg0I00k41O0ty/s1600-h/Influence_Wic_Blocked_logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8PokVaYaVg8J7YEVAT8XO6KewDPqolTI-x5wRn8omvhO-zRPkcjs4w43O9HXyDnGgvntOkNfX3zgfP7_0c18Pa6mWQWOa9QHVaohVPTZ_-fg_iZ-siL4cgy3Mo8qMhj1xg0I00k41O0ty/s320/Influence_Wic_Blocked_logo.png" /></a></div>Applying the same test to the <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control 2</a> & <a href="http://worldinconflict.uk.ubi.com/">World In Conflict</a> case we will notice that since they calculate the real distance it's quite hard to handle blocks correctly. You could perform a path finding to every tile and use that but that wouldn’t be useable. Since we mostly use influence to map out firing power in these games however it isn't such a big trouble and the influence only dissipates over the distance a unit can travel in a few seconds. This means that any obstacle that is big enough to mean anything would probably be big enough that the influence didn’t reach through it. Of course as time progresses we as programmers should be searching for better and better methods to perform our AI calculations so going with the above method makes sense if your circumstances differs.<br />
<br />
Well all of this leads to the question how should we decide on what method to use and more importantly how do we want to cast our influence. Influence is supposed to mark what areas the unit in question can influence so lets start with that, If we for the time assumes a modern warfare game a unit should cast a strong influence everywhere he can shoot. If his accuracy is lower father away the influence should represent this since our goal is to represent the actual power of the unit at that place.<br />
<br />
So a reasonable Assumption is UnitPower*Accruacy(distance). Since we obviously don't want to perform that calculations on the fly it makes sense to make an area based approached. Where we use the full influence for everything within a certain distance and then dissipate it over a falloff distance until we have the power at the end of its firing range. However a unit can move around on the map which would make it influence areas beyond where it currently can fire. How much should be based on how far the unit can move and what type of game you are making. But as a basis this would make sense.<br />
<ul><li>FullInfluence Area described by two distances from the unit, normally 0 and a number depending on it's accuracy& attacking range.</li>
<li>AccuracyDissipationArea during this area the influence dissipates from fullstrength to a strength determined by it's accuracy at that distance.</li>
<li>MovementDissipation during this area the units influence dissipates from the lowest value of the earlier area to 0, The size of the area is depending on how fast the unit can move into a position where it has a firing solution on that position.</li>
</ul>This is a good basis for everything I just want to show a special case of all of this. In the case of an artillery piece he will be helpless vs. a unit that manages to come inside of his firing range and even if it ha some sort of mounted machine gun the influence of this unit in this area would be completely different from in the other areas.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfXHxfL4IePehol8Z1buTkW5H93eFMpL0E7yYXze7jnpl7WLylvQoNnuXzr4pJpljYA49IR674oxTMxDsXrICcqefkH5DikqHY4cGhQeKqMXrGnK_Fiy8y1f0-DSly0lxtEm7NxIDGvCNo/s1600-h/artillery.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfXHxfL4IePehol8Z1buTkW5H93eFMpL0E7yYXze7jnpl7WLylvQoNnuXzr4pJpljYA49IR674oxTMxDsXrICcqefkH5DikqHY4cGhQeKqMXrGnK_Fiy8y1f0-DSly0lxtEm7NxIDGvCNo/s320/artillery.png" /></a></div>T <br />
This is the reason why the full influence area didn't have to start at 0 distance. For a case like this the dissipation areas needs to go in two direction booth increasing the max distance but also increasing the min distance to compensate for the fact that the unit can move backwards trying to get a firing solution. This will cause a circular band of influence that fades out towards it edges, just like the example we have here to our right. And if we do have units with weapons with radically different ranges we will have to add multiple influences for them but really you should be able to avoid that. Another interesting question is what do you do with a unit that can fire very far but sees very little, This means he influences those areas but only if he knows the enemy is there, The opposite is also true a scouting unit influence a large area by making units there visible but he can't fire in that area. This is a really interesting problem without a perfect solution, but in many cases you can get away by making a simple approximation by adding together the view range and firing range and then dividing them with 2.<br />
<br />
That kind of approximation however breaks down when you are looking at a unit like an artillery piece that can fire over a kilometre away but just spot for 200 meters, In these kind of cases you basically have two available options. One is to simply ignore it and say the artillery gives influence where it can fire and just assumes your spotting units will be doing a good enough work that you don't have to worry about this (The approach chosen in <a href="http://www.massive.se/games/ground-control-series/">GC2</a> and <a href="http://worldinconflict.uk.ubi.com/">WIC</a>) or you can actually calculate what tiles you have visibility of and add influence to them, this will have some issues however that if the opponent has buildings etc you know the position of your artillery can sill influence them as long as you know they are there, also it will play havoc with the frontline detection, You can avoid this by having mask giving full strength to visible tiles and half to invisible tiles but this might be a lot of hassle for very little effect but it is always worth trying it and then deciding for yourself.<br />
<br />
So what can we use this all for. Well I have talked a bit about front lines it is actually really easy to detect a front line it will be where the influence goes from being positive to being negative because this will be the point where the influences collides. But normally you aren't fighting on the whole area of the front line so we want to be able to pinpoint the part of the frontline where the tensions between the influences are the highest because that will be the strongest area of conflict. Tension can be seen as the difference in influence for the different sides at the point in question. So let’s take a look at what we need to calculate that. First of all we need to calculate the influence for booth sides separately so we can see for each tile how strong the influence of either player is.<br />
<br />
To create the basic influence map we have been looking at earlier you just creates a map that contains your Influence - your opponents influence. You can however just add together the two influences to get a count of how high the tension is at a a point in the map(observe that in circumstances when there is no conflict on the map this will give the greatest assembly of troops as the highest tension which in a way is true because where there are many troops there's a big chance of something happening, a way to generate a tension map that avoids this problem is to multiply the 2 influences with each other instead, this will always result in 0 tension where only one side is located, the problem however might bee that very small changes in real world strength can make huge differences in the tension map. You can of course create any combinations of these too, but for the rest of this article we will assume that the tension maps we create are created using the algorithm originally described.).<br />
There are many ways to combine those maps to analyse different stats of the battlefield but for this article we will keep it simple by just working with Influence, Tension and vulnerability.<br />
<br />
As a raw strategic decision it's probably a good idea to send your reinforcements to the area that has highest tension, except the one problem with our tension map it will give a high tension even if our forces are superior there are a lot of ways you can calculate tension with all with their advantages and disadvantages. But if you have a frontline and there are battles going on or about to start the area with highest tension is very probable to be the proper area to attack. But the exact heuristics will have to differ from AI to AI the maps are thee just to give you a simple way to look at complex data.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5ycvfXiHKtuHav089AXs8k_Z-YDOIrLWADjsG8igTPzPEVFE4dzvpZbFgD7FtBJ6jMBY7ph76ziJzArlRc3w7gRxMSeaM_3djkENw-L40zAJf0GNIZ7CXiG7Fo0Ad4wcQAHFff-THCrCY/s1600-h/infl_ten_2_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5ycvfXiHKtuHav089AXs8k_Z-YDOIrLWADjsG8igTPzPEVFE4dzvpZbFgD7FtBJ6jMBY7ph76ziJzArlRc3w7gRxMSeaM_3djkENw-L40zAJf0GNIZ7CXiG7Fo0Ad4wcQAHFff-THCrCY/s640/infl_ten_2_logo.png" /></a></div><br />
This is a much complex battle than the one we used in our earlier example here black has mapped up area at the top as his while white has taken a solid control of the lower area, but whites third unit has gone deep into blacks sphere of influence partially negating it but it also has very little positive influence around itself, a clear sign that it is in danger especially with the frontline basically turning around it. Black’s front troop is in a slightly better situation because it has some form of backup from the leftmost unit. To a human the area with the most tension should be extremely clear here, but how will our algorithm work? Well the right image shows clearly that the strongest tension on the board is the one between the two central units.<br />
<br />
Analyzing these kind of things is where tension maps excels, since everything in the end is just down to where you have high values in a two dimensional array (or one dimensional if you are like me but that’s an implementation detail) it is extremely easy for the computer to calculate this kind of stuff. But before we jump up and down of happiness lets check how well the technique handles more complex cases. Since the technique scaled well with multiple units lets look at a map with multiple different areas of conflict and different areas of interest.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYpaIh56ndDNvgknqc767P_5F8nHSwizeF4fDoEUJyRxccUXxVyAInhcD6xKZFt1-48gWuN67pArE6-aXi7H1nILWefJRsyNabkClYrBg15crbNcBLmuU6IAnMB4cj168wn1d7f6hHZ0r-/s1600-h/infl_ten_4_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYpaIh56ndDNvgknqc767P_5F8nHSwizeF4fDoEUJyRxccUXxVyAInhcD6xKZFt1-48gWuN67pArE6-aXi7H1nILWefJRsyNabkClYrBg15crbNcBLmuU6IAnMB4cj168wn1d7f6hHZ0r-/s640/infl_ten_4_logo.png" /></a></div><br />
This time around we clearly have some quite complex conflicts going on we have a huge battle at the top left and a smaller one at the top right and looking at the map it can be considered to be divided into 3-5 areas with as many frontlines while the influence map properly finds and categorises these areas and frontlines for us it sadly can't help us much with what to do. But looking at the tension map we see that the battle in up the upper left is obviously the area of highest tensions with booth of the white groups creating a huge tension with the middle lack group, While there is some potential for conflict in the upper right the left it is dwarfed by the conflict in the left which seems to be of game winning /loosing potential. All of these can be clearly analysed by the computer from the tension map.<br />
<br />
However we earlier mentioned a third kind of map vulnerability map, the objective of this map is to calculate which parts of the map that are most vulnerable no matter from which side's point of view you are looking at them. An easy way to calculate vulnerability is tension-abs(InfluenceMap) It will produce a high value where there is high tension but the sides are pretty equal while it will produce a lower value where there is high tension but one side is much superior.<br />
<br />
Before we start looking at our vulnerability maps let’s quickly run through the different maps we have right now so there are no misunderstandings. To make it easy for me I will assume that the game is either 2 players or 2 teams competing, it can easily scale higher but it would be a lot of work to make all the text safe for all cases.<br />
<br />
<ul><li>My Influence</li>
<ul><li>All Influence coming from my units,buildings etc</li>
</ul>
<li>Opponent Influence</li>
<ul><li>All influence coming from opposing units,buildings etc</li>
</ul>
<li>Influence map</li>
<ul><li>Calculated as My Influence-Opponent Influence</li>
</ul>
<li>Tension map</li>
<ul><li> Calculated as My Influence+OpponentInfluence</li>
</ul>
<li>Vulnerability Map</li>
<ul><li>Calculated as Tension map -Abs(Influence map)<br />
</li>
</ul></ul>It turned out to be quite a few maps to calculate everything we want, but the positive side is that only My Influence & Opponent Influence needs to be calculated based on the units in the battle field, the rest are just functions acting on them and can either be quickly calculated or implemented as wrapper functionality above the original maps. So in the end you can keep the calculation costs at a decent level.<br />
<br />
The vulnerability map can be used for many things like deciding where it is a good idea to build a new building or tower or to find opponent towers etc that are vulnerable for attack, Observe that after you have move your troops there it won't be vulnerable since it will have become on of your strongholds and it will start to get safer. Also notice that areas with high concentration of troops will get a higher value than areas with no troops since it means there is a larger chance of something happening there. If you are looking at a good position to make your charge versus, look for areas on the frontline with a high vulnerability. As always you have to use these maps with consideration the actual game you are working on.<br />
<br />
You can also create a directed vulnerability map that will tell you where your opponent is weak but this will not differ as much from the influence map because you will have strong influence in those areas but if you want to play around try this formula Tension map +Influence map, It will give high values in area of conflict where you are strong and low values in areas of conflict where you are weak. For the rest of this article we will assume the original formula though.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5DrbaAFfcnIawjiPySvIVnQKe1tjLF5-O4popc6RM65tK9jwe1qstZL0aN8Zyo2EbZ1fO-c2g0Ighis-pmEfs_gSbGwTFyI_RywI8MZ1YIf_A9BeBYPg-SV8VFDXWYYVIkYm-z5ztOaAX/s1600-h/infl_ten_vul_2_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5DrbaAFfcnIawjiPySvIVnQKe1tjLF5-O4popc6RM65tK9jwe1qstZL0aN8Zyo2EbZ1fO-c2g0Ighis-pmEfs_gSbGwTFyI_RywI8MZ1YIf_A9BeBYPg-SV8VFDXWYYVIkYm-z5ztOaAX/s640/infl_ten_vul_2_logo.png" /></a></div>As we can see the area with the great conflict and turmoil in the middle created by these two opposing units are the most vulnerable areas while the rest of the front line is also vulnerable these areas are the extreme parts. While the upper left corner and lower right corners feels like pretty safe areas. So if I want to build s safe base as white here I would place it around my two lower units and if I where black I would place it deep inside my area where it looks safe. And if there are any nice targets like a house in the big yellow area that is a prime area to attack.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyIbF_gLZksf8OZaWmfLJCkk32Dit1RkM98QesE7c3RdUoK6_6rH-4HLXBFCsZ4Sd31sVQ5Y3aoDXRrhTUy8p9eXOME9iTCeZZGfbRbIC1bfxGuinnn7T05uYNJKE4YAml_8s6BhcA_p7x/s1600-h/infl_ten_vul_5_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyIbF_gLZksf8OZaWmfLJCkk32Dit1RkM98QesE7c3RdUoK6_6rH-4HLXBFCsZ4Sd31sVQ5Y3aoDXRrhTUy8p9eXOME9iTCeZZGfbRbIC1bfxGuinnn7T05uYNJKE4YAml_8s6BhcA_p7x/s640/infl_ten_vul_5_logo.png" /></a></div><br />
In this example booth sides has slowly build up their strength created a very clearly defined front line. Our vulnerability map quickly identifies the battle towards the lower left and the frontline areas around it as the most vulnerable areas of the playing field. And this time around the map is filled with safe areas to create buildings and bases on. By now the vulnerability map is starting to look really good. We will just run it through the last example to be certain.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidU-dPmwlMf0Zi0QjJmbxAC8iQncEV0FuP6AUFxuFK6v7a3oJIeNRP3UesLmsXNp5fRaUuLmzQGVjdiy7F2uiO6eSyMmYehr83jIuRxRu7r4pd5C9TcVJ8OX8fejxxzpPHXVt8y9VftfXl/s1600-h/infl_ten_vul_4_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidU-dPmwlMf0Zi0QjJmbxAC8iQncEV0FuP6AUFxuFK6v7a3oJIeNRP3UesLmsXNp5fRaUuLmzQGVjdiy7F2uiO6eSyMmYehr83jIuRxRu7r4pd5C9TcVJ8OX8fejxxzpPHXVt8y9VftfXl/s640/infl_ten_vul_4_logo.png" /></a></div><div style="text-align: left;"><br />
</div><div style="text-align: left;"><br />
</div>Even on this our worst case scenario the vulnerability map effectively separates out the uncertain areas and what could be called safe, Not that there is many of them on this battle field. One really nice feature of workings with maps like this is that you can combine them together for even more interesting positions for example if you look at the differences in booth the tension and the vulnerability map it's easy to analyse out where you want to place towers. You just place the tower where you think it should be recalculate the maps and evaluate the difference. To see the real world practical effect of your choice.<br />
<br />
To be continued later on :)Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com9tag:blogger.com,1999:blog-461014343580624539.post-55102284100567408892010-03-15T14:00:00.001+01:002010-03-15T14:00:10.844+01:00Trivial navigation mesh generation IIThis one better not turn out as long as the last one or I will have to split this out over even more articles. The reason I allowed the last article to become so long is because I wanted to leave you with enough meat to implement it so that if you wished you could have it done by this post and then start refining it.<br />
<br />
<span style="font-size: small;"><b>The meaning of Updating</b></span> <br />
<br />
The reason I always refers to updating is because you can’t just set a triangle to being blocked or unblocked. Well you can do that but what if the same triangle gets blocked by more than one obstacle for example and then that obstacle is destroyed. If you just set the triangle to unblocked you will be able to drive through one of the objects. And if you don't then you can't make objects that you can destroy dynamically at all.<br />
<br />
<a name='more'></a><br />
<br />
This one better not turn out as long as the last one or I will have to split this out over even more articles. The reason I allowed the last article to become so long is because I wanted to leave you with enough meat to implement it so that if you wished you could have it done by this post and then start refining it.<br />
<br />
<span style="font-size: small;"><b>The meaning of Updating</b></span> <br />
<br />
The reason I always refers to updating is because you can’t just set a triangle to being blocked or unblocked. Well you can do that but what if the same triangle gets blocked by more than one obstacle for example and then that obstacle is destroyed. If you just set the triangle to unblocked you will be able to drive through one of the objects. And if you don't then you can't make objects that you can destroy dynamically at all. <br />
<br />
Another case is if you are laying down a bridge. A bridge is an object that opens up an earlier blocked triangle (preferably one that was blocked by water) what should we do with the case where there is an object standing in the water blocking our way (well don't allow it to build a bridge in that case but some borderline cases will be hard to detect) if we just allow it to open up a triangle that was blocked it could be used to make earlier unpassable areas passable no matter what was blocking them and so on. You could try to catch all these cases with outside code but in the end you would be fighting a loosing battle. Wouldn't it be nice if the navigation mesh system could support all these things? Well of course and that's what updating it all about, you don't just set the triangle to blocked or unblocked. Instead you update its internal data state and allows it to make its own decision about if it is going to be blocked or not after the update.<br />
<br />
This allows for quite complex dynamics in the code that determines the state of a triangle instead of just being a simply on or off flag we can make a complex structure that takes into account multiple data sets to make a decision. There are many sets of data that can achieve this and I'm not going to cover all of them. I will show you a simple way which will not be very memory friendly but if you only have like 10-20 000 triangles in your meshes then it won't really matter but we will also look at an easy way to perform memory optimization to carry it into a usable range.<br />
<br />
<br />
<b>The two lists</b><br />
<br />
The first step in the solution I propose is that for every triangle you have a list of all the objects that are blocking that triangle. The basic idea is that if that list is empty the triangle is passable and if it isn't empty then the triangle is blocked. So if we have a triangle that gets Updated with "Add House1" and then with "Remove House1" then it will be blocked between these two updates but after the remove House1 update it will again be passable.<br />
<br />
However if we first have a "Add House1" followed with "Add House2" for the same triangle (meaning that triangle is blocked by both the houses) then updating with "Remove House1" won't make it passable because the triangle is still blocked by House 2.<br />
<br />
So far all makes sense and it easy to follow I hope but there is as problem with this approach and that is the reason this section is called the two lists... Before we venture any further in this I just want to make clear that the "Add House1" etc command's doesn't mean I would communicate with text based update messages I would probably make one RemovingUpdate and AddingUpdate but writing in this article I have chosen to leave all messages as text based to make it easier to read and follow the steps of the algorithm.<br />
<br />
So why two lists? Well it's because of the following problem if I have an object that should open up the path list for example I am building a stair over a fence. Then that stair should call a "Remove Fence" update on the triangles inside of the stair. But what happens when we want to remove the stair? The stair isn't in the list, and we have no way of knowing what objects it has removed either so we can't add them back.<br />
<br />
It seems clear that we need an extra list to handle objects that clear a path through other objects not by removing them but by bridging over them and that is removable/destroyable. Please note that things like open up destroyed buildings removing trees etc and most common actions in a game does not fall under this category that is simply removing an existing block we are talking about things that open up a path through a block without removing it like a bridge over water, or a stair over an obstacle. Or any other object that allows access over earlier impassable areas.<br />
<br />
If we call the first list the blocked list we can refer to this list as the opening list. However what information should we put there. We can't just put the openers ID there because we need to know what it opened so we can close it or even that it does open something. If you place a stair over a triangle that is blocked by both by a house and a fence you should only open for the fence while the house still stands there you should not be able to pass through.<br />
<br />
So each object added to the opening list should also contain a list of which objects it opens through (is this getting complex? well slightly but this code path will be executed very seldom so it's ok). So to detect whatever a triangle is blocked we first take the list of blocking objects and makes a copy of it. Then we iterate through the opening list. For every object in the opening list we iterate through what it opens. If we find an object that it opens in the temporary blocking list we remove it. If the temporary blocking list is empty after this has been performed for every object in the opening list then the triangle is unblocked. In reality this code will only be run if the opening list isn't empty which is most of the time.<br />
<br />
bool Triangle::IsBlocked()<br />
{<br />
if(openingList.size()==0)<br />
{<br />
/* This is the code that will be executed except in very special cases those we only add an extra compare and as this code shouldn't be evaluated every frame instead it should only be called when the triangle has been updated to select the value of a flag in the triangle this won't matter */<br />
<br />
if(closedList.size()==0)<br />
{<br />
return(true);<br />
}<br />
return(false);<br />
}<br />
tempList=closedList;<br />
/* Assumes we can make a straight copy like this of whatever data type we are using */ <br />
for(int i=0;i<openinglist.size() ;i++)=""></openinglist.size()><br />
{<br />
for(j=0;j<openinglist[i].myopeners.size();j++)></openinglist[i].myopeners.size();j++)><br />
<br />
{<br />
<br />
tempList.Remove(openingList[i].myOpeners[j]);<br />
/* This assumes that the remove functions works even if the object isn't present in the list) <br />
<br />
}<br />
}<br />
<b>}</b><br />
<br />
One interesting question remains what is it that we store in those lists? Well you could store a string based name of the object in question but that would be quite expensive. So I would suggest an unique ID for that object for example a hash ID of it's name or any kind of unique ID you want to use. This way the comparisons are only int comparisons which cut's down drastically on performance but especially makes the memory usage of the algorithm a lot more humane. And since you get a 100% flexible path finding system for blocking and unblocking that can be considered cheap.<br />
<br />
Some of you might notice that if every object has a unique ID how can we for example make a stair that allows us to move over fences if every fence has it's own unique ID. This is an interesting question and can be resolved in two easy ways. One is that expect for it's unique ID a block also has a typeID and the opener is allowed to remove any objects with the same type ID as it's opener. This allows easy handling of multiple objects etc but it means the blocked list will take twice the memory while this probably won't affect the memory consumption of the algorithm overall it might feel too much for some persons. They can go for the way of actually detecting which objects that are near the stair and of the correct type in the game code and add their ID's to the opener list. I would side with the first option in most cases because it is elegant and easily extendable and doesn’t need any special code anywhere except for 1 line or so in the IsBlocked function.<br />
<br />
That’s all for the two lists so I think it is time to move on.<br />
<br />
<b>Complex objects</b><br />
All the examples in the earlier post were about single triangles. How does the algorithm really cope when dealing with complex objects of hundreds of updating polygons? Well obviously the fewer the better simply because there are less calculations to perform that way. Well you can actually call the system one triangle at a time you will perform a lot of unnecessary calculations but it will work.<br />
<br />
So let’s look at what we can do to speed it up a bit. One obvious step is to make sure that every cutting line is only used once. This means that if you have a square you should only use the centre line for the first triangle and skip using it as a cutting line for the second triangle. This might not sound as that big a saving but as the objects get more complex it can really help a ton.<br />
<br />
Of course you can take that one step further since any edge that is internal to the object so that it doesn't affect the outside it will hit a point where two other edges also meet. So there is guaranteed to already be a split there. This means that you can actually discard all internal edges and not even use them for splitting at all. So how do I detect if an edge is internal? Well if your complex object is built with the same rules as the rest of the world which means that if two triangles share a edge it is the same edge and it contains booth triangles it means that any edge connected to two triangles in the object is internal and doesn’t need to be calculated. This means that if the object is complex because it was sloppily modelled that all those completely internal polygons won't generate any splits.<br />
<br />
You can also make some optimizations in selecting which triangles to get the edges to be split from by first getting all triangles near the complex object using a circle or another structure and then only make the tests vs. those. Also only those triangles that are interacting with a triangle that has outer edges are relevant for splitting.<br />
<br />
You can probably do even more funky stuff in selecting which triangles to do what with and how to perform the culling. But this is starting to get case to case dependant as in all optimization matters play around a bit with it and <b>measure</b> besides make sure that it is a performance problem before you spend all this time trying to fix it.<br />
<br />
<b>Building ComplexObjects From a Heightfield</b><br />
A normal case for a lot of games is that the terrain is based upon a height field and you want to select what is passable and not passable based on this height field. In our case it means that areas of the height field that is supposed to be unpassable should generate complex objects that are used to update the world with to add them as block. But an interesting question is how to build these objects.<br />
<br />
If you just make every tile a square with two polygons it would obviously work but it wouldn’t be very efficient for generation and also it would cause the edges of the passable world to show tile jaggies. A more efficient way would be if you could smooth over these jaggies to create an object with a smooth edge and with a lot lower complexity.<br />
<br />
It's just a matter of selecting how to do it but the important thing is to create an outer hull and then create a new triangulation of it to reduce triangle complexity. If people comment that they want to know more about this I can go through it in detail but for now I am going to skip it by in order to allow this article to focus in the meat of the method. <br />
<br />
<b>Storing Extra information in the Triangles</b><br />
So far we have talked about updating as only updating the two lists that decides if a triangle is blocked or not. And if this is all the information you needs to store then that is fine. But a lot of the times when working with games you want to store extra data in your navigation mesh.<br />
<br />
This might be other path finding related data as the cost to travel inside this area. For example if the triangle is apart of a road then you would expect travel to go faster on that triangle than on one that is inside a swamp. They way you do this is to store a cost multiplier inside the triangle that tells you with what factor the current cost for travel inside the triangle should be multiplied. Of course you need to have a reliable way to know if a triangle is part of a road and if you have just generated your navigation mesh chances are big that some of the triangles that cross the road will also have parts that are outside the road and the same for the swamp.<br />
<br />
Thankfully updating is again a big help since updating guarantees to respect the edges of the updating triangles. So far we have only made updates that change the blocked lists but there is no problem to make an update that changes the cost values of the triangles inside the update instead. This way you can easily layout a road and get a cheaper cost inside it or a swamp to increase the cost of the triangle. The power of updating is that you can store any value in a triangle and you can change it anywhere as long as you can make a shape of triangles that represent the area you want to affect. <br />
<br />
You can store information like that this triangle is composed of hot coal and does one point of damage to any player that is inside it. Or a healing area that is holy to your tribe. You can store any data you want into the triangles. And update any shape without worrying about it.<br />
<br />
A word of warning goes out here however if you get crazy with updating you might increase you’re triangle count to a level where it will start affecting the actual path finding which would be counter productive. So there might be a good case for only embedding data relating to the path finding in the mesh. But there is nothing to stop you from making another mesh containing game play area effects. I'm not saying you should do it, but it does contain intriguing possibilities but most of the time we will probably just be better of faking it.<br />
<b><br />
</b><br />
<b>Precalculating destructability</b><br />
Well I promised quick dynamic updates especially for removals of objects that block the path which is the most standard case in games still. So how am I going to come through on that promise? Well obviously there is no free lunch so there are some gotchas with the method. Not in the result mind you but in the process one you are possible generating more triangles in the navigation mesh that didn't need to be there until after the block has been removed (This will not happen in all cases but it might in some) There is also some work involved by the programmer in a pre processing step. This should not come as a great surprise as pre-processing is involved in a lot of optimizations the basic idea is so simple if you can calculate it in advance do it!<br />
<br />
And that's what we are going to do, we will precalculate the data for the destroyed blockers. Before we go into detail I just want to talk about the two possible cases for what can happen when a blocker is destroyed. <br />
<ul><li>The path blocking influence of that blocker is completely removed leaving no trace of it ever having been there</li>
<li>The path blocking influence is removed. But in it's place a new blocker appears representing the remains of the destroyed blocker it will normally block a much smaller area but it will still be there.</li>
</ul>Let's start with the first case, so basically all we have to do is to remove the influence of the object well that sounds easy. We should just perform an update on the area it occupied with the input to remove its influence. While this would no doubt work it would result in creating more triangle sin the area unless the update perfectly matches up with the original area. Which of course we can easily make it do. But then we will instead perform an update that forces multiple polygons to detect which polygons are inside them to select which polygons to update and that would not be free while not to expensive either I said quick updating and that seems to expect something better. <br />
<br />
Of course I can do better what about just finding all triangles within a radius of the object, that sounds pretty fast and pretty quick. And then just remove the ID from all triangles you get in that list. Well we are getting pretty fast now triangle inside circle for these cases where we can assume the circle is a lot larger than the triangles can be optimized down to point inside circle which is pretty damn fast. This would definitely be workable but we can do even better. What if we during pre-processing when we create the original navigation mesh, we also for every object generate a list of every triangle it is affecting? Then all we would have to do is that when we remove the object we run through its triangles and removes it from the lists. Well that would be lighting quick, there is however a catch like always. But this one is rather small it is if a triangle that is on one of the objects list is split we have to update that list but since every triangle already has an ID for every object affecting it we can do it quite simply by having the triangles themselves call the object with two new triangles and telling it to remove the original. Also this is a special case since we will be precalcualting almost all updates anyway so it will happen very rarely.<br />
<br />
The second case is a bit tougher, we can't just remove the influence from the object we also have to add the wreck object's influence too. The method I propose is shockingly simple, what if you add all the wreck objects in advance too directly at the beginning? This way the triangles inside the object that is going to be occupied by the wreck object has the wreck object added from the start making the act of removing the original object as a full modification since it won't open up the areas that are still blocked by the wreck. So booth removing the original object and adding the wreck is accomplished by just running through the list of the original object and remove it. This might cause more triangles to be generated of parts of the wreck object is close enough to the edges of the original object to affect it's edges but we will look at ways to optimize this later on. <br />
<br />
This will only work properly if the wreck is smaller than the original object though. What to do if the wreck extends outside of the original object? Well we can still solve that case with precalculations it's a little bit more work but still perfectly manageable. Inside out triangle we add a third list called the passive blockers list. We allow updates that add data to the passive blockers list. And updates that moves an ID from the passive blockers list to the blockers list. This way we can still precalculate everything by adding all the wrecks as passive blockers. And just as for the original objects we also store in each wreck which triangles it has been added to as a passive blocker. And now we first run through all the triangles in the original objects list and remove them from the blocking list. And then we run through all the triangles in the Wrecks list and move their ID from the passive blocker list to the blocker list.<br />
<br />
As you can see the concept of updating allows for a high degree of flexibility in working with solutions like this. Even seemingly complex problems yields easily. But again I give the warning of trying to do too much with it. If all you have is hammers everything might look like nails but they aren't. So while this is powerful use it with caution, but to get free path finding updates for most cases we can be prepared to do a little work after all.<br />
<br />
<br />
<br />
<b>Optimizing for runtime.</b><br />
To wrap it up before this post turns into another monster I have been talking about possible optimizations that can increase the performance of the algorithm without turning it into a monster of numerical stability. Like trying to perform all calculations on convex hulls instead of triangles would do. It is possible to do of course, it's a bit tougher to do it well so that you don't get unnecessary hull splits But going through that topic would require another lecture perhaps called "Non Trivial navigation mesh generation" anyway that's way beyond our current scope what I wanted to do was give you something that is simple yet powerful and just basically works and won't take weeks to implement.<br />
<br />
So what can we do to optimize it? Well the problem is still obviously the triangles. A triangle isn't the most efficient primitive to describe certain shapes or let's be honest any shape that isn’t triangular a quad would take 2 triangles a hexagon would take 4 it's just more efficient to use more complex shapes if we can, but down that path lies a lot of work so can't we fake it somehow ?<br />
<br />
Fortunately we can, we can reap some of the benefits of using convex shapes while maintaining the simple update code for the triangles, now bear in mind that just won't be as good as complete correct convex hull code would be we won't be bale to perform as many merges (20-30% less when I ran tests) but we can improve our node code by cutting it into at least half or more and this without risking it all (which isn't very interesting unless you are a professional studio in which case you should already know all that I am telling you. Because in that case it is worth it spending some extra time to make it perfect but for students or independents this will be plenty).<br />
<br />
The basic idea is the following, what if we could keep our data as convex hulls when path finding but as triangles when updating? Then we would get the advantages of both the sides. Of course as always there are no free rides. What you do is that after you have made an update you go over the triangles and merge them into convex hulls. And before you does an update when you select which triangles you are going to take the edges from to split you triangulate the relevant convex hulls. So the process goes like this.<br />
<br />
<ol><li>Detect which convex hulls are intersected by the updating triangles (don't do this for every triangle that would be madness)</li>
<li>Triangulate those hulls.</li>
<li>Run the updating algorithm a usual using those newly created triangles as data.</li>
<li>Merge back into new convex hulls all triangles that can create a convex hull and has identical lists and cost. <br />
</li>
</ol>That last paragraph of identical list and costs are important. When you merge back to a convex hull you can't merge together triangles that have different data because then you will either loose some data or the entire hull will contain all the data in which case the data for some areas will change. So you have to preserve the edges between objects with different data sets so they don't break down. Of course if you have precalculated all of this you wouldn’t need to run the algorithm during runtime. But if you have to you have some issues to consider.<br />
<br />
What data should you have links to from your objects? The convex hull or triangles? Well you have to have whatever form is accurate now. But the good news is that while you are performing an update nothing else can change the data so you can keep the original convex hull in the owner and then after the final step remove it and add any new hulls created during this process.<br />
<br />
Obviously this process does make updating more expensive. But you do get a lower path finding cost and the compromise is something you have to decide for yourself. While this last step isn't trivial it is very possible to perform in a reasonable amount of time.<br />
<br />
Well that's all about navigation mesh generation for now and remember to come back next week.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com0tag:blogger.com,1999:blog-461014343580624539.post-76047898990972685942010-03-08T14:00:00.000+01:002010-03-08T14:00:07.602+01:00Trivial navigation mesh generation IThis is going to be a intermediate level lecture so before you read this lessons there are a couple of things I'm going to assume that you have prior knowledge of.<br />
<ul><li>A*</li>
<li>The different search space representations for it and how to use it on them.<br />
</li>
<li>2D Line vs 2D Line intersection</li>
<li>2D Triangle vs 2D Triangle Intersection</li>
<li>2D Triangle inside Triangle tests</li>
<li>2D Point inside Triangle test</li>
</ul>That's it so it's not a too hefty list basically it means you understand A* and that it is a graph based algorithm and that the natures of the graph might change, and basic linear algebra knowledge or well access to a basic 2D intersection library. I'm sorry I can't touch on everything but I have to try to keep the articles short and focused on the target. I will spread the difficulty level around quite a bit but at the moment I am mostly looking at the things we teach at the beginning of the second year at <a href="http://www.thegameassembly.com/">TheGameAssembly</a> but there will appear articles targeted to beginners too and some like the last series that can be enjoyed booth by beginners and intermediate students. <br />
<br />
<br />
<a name='more'></a>This is going to be a intermediate level lecture so before you read this lessons there are a couple of things I'm going to assume that you have prior knowledge of.<br />
<ul><li>A*</li>
<li>The different search space representations for it and how to use it on them.<br />
</li>
<li>2D Line vs 2D Line intersection</li>
<li>2D Triangle vs 2D Triangle Intersection</li>
<li>2D Triangle inside Triangle tests</li>
<li>2D Point inside Triangle test</li>
</ul>That's it so it's not a too hefty list basically it means you understand A* and that it is a graph based algorithm and that the natures of the graph might change, and basic linear algebra knowledge or well access to a basic 2D intersection library. I'm sorry I can't touch on everything but I have to try to keep the articles short and focused on the target. I will spread the difficulty level around quite a bit but at the moment I am mostly looking at the things we teach at the beginning of the second year at <a href="http://www.thegameassembly.com/">TheGameAssembly</a> but there will appear articles targeted to beginners too and some like the last series that can be enjoyed booth by beginners and intermediate students. <br />
<br />
With that out of the way let's get down to the subject of this article. I admit that the title is kind of challenging as a lot of people reading this will think that this is far from trivial in complexity. Well that might be true but at the same time the basic process is very simple and most important numerically stable on modern PC processors. I don't have the time here to rag around why you can't trust floating point numbers on a computer but to make it clear I will make one simple example.<br />
<br />
If you have a line and intersects it with another line to find an intersection point, and thereafter splits the first line into two different lines one being the part of the line that was left of the intersection point and one being the part of the line that was right of the intersection point then these new lines won't be parallel to the original line. And they won't even be parallel to each either. This is because when you create the new line you have to select a position which to use as the new point on the line. We use the intersection point. The problem is that the original line was defined as having infinite precision mathematically between its start and end point, at the time we actually decided a intersection point we had to throw that away and use the computers floating points precision and as it is not possible for it to perfectly match the true intersection point due to precision differences the intersection point won't be on the actual mathematical line (except for trivial lines) which means that the direction of the new lines have changed compared to the original. in fact if you put them together with the original and zoom in it will form a small triangle but you won't be able to see this with your eyes. But it creates problems when trying to detect if lines are really parallel and so on.<br />
<br />
Because of this the algorithm we propose here has no need to rely on floating point numbers to be consistent or even correct. It is not the best algorithm for the job let's be honest with that. But it is trivial to implement and does a good job of getting something decent output which can later be optimized. But overall it is simple to implement and that's why I teach it. So our goal is not to create the most optimized navigation mesh ever. But it will have good support for handling dynamic updating especially removal of earlier blocks which can be completely precalculated.<br />
<br />
But before that let's look at the basics of the algorithm. One of the basic concepts is that we are going to use triangles to <b>update</b> areas of the world. An <b>update</b> is something that changes the data in the triangles that are inside the triangle that is used to <b>update</b> it might for example change a flag telling if the triangle is blocked for movement or not or change the cost of movement inside it. An <b>update</b> is required to only affect the area inside of the triangle used to <b>update</b> with. If the triangles of the mesh don't exactly match the updating triangle the algorithm will split those triangles until it achieves a perfect match up with the updating triangle.<br />
<br />
So basically we can use any triangle to change the value of a triangle anywhere in the world or well change the value of the area defined by it. By combining multiple triangles we can <b>update</b> an area of any shape. I will stop typing the word <b>update</b> in bold now since I hope we have established that it is used as a special term with a special meaning during the scope of this article.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhkoNN1QTMlvD9BuzLlaWm8th9APjMe_rE9Ljjey4f5NN5Gfrc1M2iAieu9NuSwWzMaEVx6gOHO9EzgRFCFtQxYse0rH1N_b6ZnzKBvoAFGLouQolw1r2Oj9-eLonbppv1vZBxIvX3xbeQ/s1600-h/CuttingAHole.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhkoNN1QTMlvD9BuzLlaWm8th9APjMe_rE9Ljjey4f5NN5Gfrc1M2iAieu9NuSwWzMaEVx6gOHO9EzgRFCFtQxYse0rH1N_b6ZnzKBvoAFGLouQolw1r2Oj9-eLonbppv1vZBxIvX3xbeQ/s320/CuttingAHole.png" /></a></div><br />
Here we have an example of where the technique will be usable our world consists of the 2 triangles showed by the red lines. But for some reason we want to update the area inside the blue triangle. It might be that a building has been placed there blocking any paths through that area or it might be that it is a swamp and we will move slower through it. We don't know and we don't need to. All we need to do is detect which triangles we should call the update function on with the data we have been called with. That is the meaning of an update.<br />
<br />
The first step however must be to split the existing triangles in a way so that we only have triangles by the end and so that the edges of the triangles inside of that blue area perfectly matches up with the original blue triangle. The algorithm to perform this is what we will spend most of this article exploring. But before we delve into the specifics let's take a look at the expected result of the algorithm.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihj21-WPADr_70T-evOZVPYMRtWSYIlU0W0f-MYPn4brw1hbamnkJb4Z4L6j3iCZXC5bBMq_Hs_G7ZRtws7VGOXRZondGNTZ4E0xbnXxUfPKJArnAzx6V8hCCvBg0WXC_4SzjzojKS151s/s1600-h/AfterPreUpdatePass.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihj21-WPADr_70T-evOZVPYMRtWSYIlU0W0f-MYPn4brw1hbamnkJb4Z4L6j3iCZXC5bBMq_Hs_G7ZRtws7VGOXRZondGNTZ4E0xbnXxUfPKJArnAzx6V8hCCvBg0WXC_4SzjzojKS151s/s320/AfterPreUpdatePass.png" /></a></div><div class="separator" style="clear: both; text-align: left;">You can still see the original blue triangle here and hopefully you can see that along each blue line there is also a red line showing the edge between 2 triangles if we look at it we should find that we have 4 triangles that are inside the area and will be called with the update data. All 4 of this are newly created by the triangle splitting step. The rest of the triangles are artifacts of the algorithm that can't be removed in a simple way. While it might look like it we cant remove a single edge without turning a triangle into a polygon this doesn't mean the new mesh is optimal I think I could have removed 2 polygons. But it is pretty tight for being triangles only.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">This article will consist of two main parts the first part is just teaching you how to perform the algorithm that does the polygon splitting and the second will discuss the power of the concept of updating and how it can be used to create a dynamic world. This is really important modern games need to be dynamic we solved most problems of precalculated path finding years ago. But times have moved on and just as graphics are looking at dynamic solutions so must the AI.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">For the rest of the article for simplicity I will assume that the world that the unit can path find on is a 2D space so there can be no room above a room. This is not a problem in the algorithm by itself it's just to keep the math and steps simply. To make it in 3d you exchange the updating triangle with a triangular rod which height and position in 3d space is used to find it's intersections with triangles and edges instead after which it is projected so that it is 2 dimensional compared to the surface you are updating which allows all the math in the innermost step to be executed in 2D. </div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">Since the meat of the algorithm is splitting triangles the first thing we are going to look at is what happens when we try to split a triangle along a single line and the different results that we can achieve. The idea is that if we can resolve splitting around a line robustly we should be able to perform any kind of splits we want later to achieve the end result since we already know how to split with the line.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEfde_u0Ivj1iYpj_zV_r-w2BAOHtk62DguoNAuZRmrnH1haNuFEeZ8YhT1l3BR-Wo1TszPZWDLNe9wC1ebeLb_wZOMIOqenzoYHAt1SdOE1r1pQOoUMzFIUW55dy8FyX5UnDWn7rkbFRs/s1600-h/CuttingProofStart.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEfde_u0Ivj1iYpj_zV_r-w2BAOHtk62DguoNAuZRmrnH1haNuFEeZ8YhT1l3BR-Wo1TszPZWDLNe9wC1ebeLb_wZOMIOqenzoYHAt1SdOE1r1pQOoUMzFIUW55dy8FyX5UnDWn7rkbFRs/s320/CuttingProofStart.png" /></a></div><div style="text-align: left;">Again Red is the color of edges of out triangle and this time the blue line is the line we want to split the triangle along. While making sure that the end result only consists of triangles.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">The obvious first step is to just split the triangle straight along the cutting line. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-Ydm6JMf56TDpf-CBKKtSdMF99A9KBgh3cB1lBll2i0A9VBRVnP4VkJP_kYQD3qWoIm-ZO4ZeXrU_iJYD-cLPQbO1VwLTs_6e_Lex_q1UjfrJwZyuwEzJemi04ExBRl-9RkRi0j4Pg-F3/s1600-h/CuttingProofStart_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-Ydm6JMf56TDpf-CBKKtSdMF99A9KBgh3cB1lBll2i0A9VBRVnP4VkJP_kYQD3qWoIm-ZO4ZeXrU_iJYD-cLPQbO1VwLTs_6e_Lex_q1UjfrJwZyuwEzJemi04ExBRl-9RkRi0j4Pg-F3/s320/CuttingProofStart_1.png" /></a></div><div style="text-align: left;">You should be able to see that inside the triangle the blue line has been replaced with a red denoting a new edge between triangles. However this split has resulted in a case where while the upper part of the triangle is still a triangle the lower part has turned into a 4 sided polygon. Since we can only accept triangles as a valid output we have to turn this polygon into triangles. There are only 2 possible ways to turn a 4 point polygon into two triangles since for each vertex there is only one vertex that it isn't directly connected to. And while there are 4 pairs of these vertexes 2 of them are identical except for which vertex is the first and the second we end up with only 2 choices.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg2u_fmMMnBm73HgkimCyQEpQZJosEen_OoRDGkUYUkglJ-EtG-zp2HmiU5bzNL-YxsM-W12CxFTY6AIfXisDNIOpsjYIvA9bLPMIEkSGlsqixwZ0jXXV6RECSGuwIe5nr6vnsdb_CVfpM/s1600-h/CuttingProofend_alt1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg2u_fmMMnBm73HgkimCyQEpQZJosEen_OoRDGkUYUkglJ-EtG-zp2HmiU5bzNL-YxsM-W12CxFTY6AIfXisDNIOpsjYIvA9bLPMIEkSGlsqixwZ0jXXV6RECSGuwIe5nr6vnsdb_CVfpM/s200/CuttingProofend_alt1.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcf_7Msy6M0t2aLDW9pTcoIj0zrjyDyKngU4fEBkf-ZBgGBvpRNN-mr29gT-67h2U4PYoVBHFWaD4rm3L8yvt5I3ODsaoFenZ0yWvHn89BiyD3aqO8PByy6Z1AcalvOpQb5RtQZb96u6nC/s1600-h/CuttingProofend_alt2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcf_7Msy6M0t2aLDW9pTcoIj0zrjyDyKngU4fEBkf-ZBgGBvpRNN-mr29gT-67h2U4PYoVBHFWaD4rm3L8yvt5I3ODsaoFenZ0yWvHn89BiyD3aqO8PByy6Z1AcalvOpQb5RtQZb96u6nC/s200/CuttingProofend_alt2.png" /></a></div><div style="text-align: left;"><br />
</div><div style="text-align: left;">These are our two possible options you can take out a pen and paper for yourself and try to find different ways of doing it if you want to. But you won't find any other options than these 2 that results in only 3 triangles in the end. Since these are the minimal number of triangles to represent this data. Because of this we can say that both of these splits are optimal. Neither of them produced any extra triangles or edges so either of these is fine.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">Now that we have examined how a optimal splitting looks like it is time to sit down and try to figure out an algorithm that can duplicate this (And of course work no matter what direction the splitting line comes from and no matter how the triangle looks. While I am using this case as an example the algorithm is perfectly general.). But first of all let's look at the data structures we are going to be using during this article.<br />
<br />
The normal approach to think about a triangle is to think that it consists of 3 points and that these points form 3 edges. We are going to try to think about it differently. One of the reasons is that we want an edge to know what triangles it belongs to. And if we want to split an edge between two triangles we don't want to perform two intersection tests one for each triangles representation of that edge. One of the big reasons for this is obviously performance 2 tests are twice as slow as one. But this is not the main reason. The big reason is that there is no guarantee that the intersection points on the two triangles will be identical since the ordering of the points inside the triangles might be different causing the limited floating point precision to select different intersection points. And as this is the problem of going from infinite precision to finite precision using doubles instead of floats won't solve the problems.<br />
<br />
So because of all of this we are going to use an unorthodox data structure where the edge is the main component and the Triangle is just a container of 3 Edges.<br />
<br />
Vector2f is the data format of a 2D vector it has two members x and y which are both floats and can perform all standard vector operations on those.<br />
<br />
<i>class Edge</i><br />
<i>{</i><br />
<i>public:</i><br />
<i> Edge(const Vector2f aFirstPoint,const Vector2f aFirstPoint); </i><br />
<i> ~Edge();</i><br />
<br />
<i> Vector2f GetFirstPoint(); </i><br />
<i> Vector2f GetSecondPoint(); </i><br />
<i> void ConnectToTriangle(Triangle* aTriangle);</i><br />
<i>// Adds the triangles to myTriangles</i><br />
<i> void DisconnectFromTriangle(Triangle* aTriangle);</i><br />
<i>// Removes the Triangle from myTriangles </i><br />
<i> </i><br />
<i>private:</i><br />
<i> Vector2f myFirstPoint;</i><br />
<i> Vector2f mySecondPoint;</i><br />
<i> Triangle myTriangles[2];</i><br />
<i> // Because an Edge can at most be connected to two triangles at a time</i><br />
<i> </i><br />
<i> </i><br />
<i>};</i><br />
<i><br />
</i><br />
This looks pretty basic, one question that can rise is why I went through the trouble of using accessor functions. There are a couple of reasons for this. First an edge is constant from the time it is created until the time it is destroyed if you split an edge you create two new ones you don't modify the original. Another reason is because you will most of the time use a number of helper functions to get access to the two positions because you don't have a fixed winding order in your triangles. I will get to that in a second but first we'll look at the Triangle class before moving on. I am also using straight C style arrays just to keep this example code clean from external libraries.<br />
<br />
<i>class Triangle()</i><br />
<i>{</i><br />
<i>public:</i><br />
<i> Triangle(Edge* someEdges[3]);</i><br />
<i> ~Triangle();</i><br />
<i> </i><br />
<br />
<i> Edge* GetEdges();</i><br />
<i> </i><br />
<i>private:</i><br />
<i> Edge* myEdges[3];</i><br />
<i>}</i><br />
<i><br />
</i><br />
You will also notice that booth our classes have forced constructors you can't create a edge without booth its points and you can't create a triangle without 3 edges. This is to guarantee that we always work on complete data types. Now before we go on we will quickly add some really useful helper functions that will make your life a lot easier later on.<br />
<br />
We'll start out with Edge<br />
<i><br />
</i><br />
<i>bool IsConnected(Edge* aEdge);</i><br />
<i>/* This function checks if this edge is sharing a point with the edge sent in. */</i><br />
<i>bool IsConnectedTo(Vector2f aPosition);</i><br />
<i>/* This function checks if the point is a part of the Edge */</i><br />
<i><br />
</i><br />
<i>Vector2f GetOtherPoint(Vector2f aPosition);</i><br />
<i>/* This assumes that the point is a part of the edge it returns the other point of the edge*/</i><br />
<i><br />
</i><br />
<i>Vector2f GetconnectedPoint(Edge* aEdge);</i><br />
<i>/* This functions returns the shared point of these two edges, You should use IsConnected to verify that these edges are connected */</i><br />
<i><br />
</i><br />
<i>Vector2f GetUnconnectedPoint(Edge* aEdge);</i><br />
<i>/* This function which assumes the edges are connected returns the point on the first edge that is not connected to the edge that is sent in */</i><br />
<i>bool IsPartOf(Triangle* aTriangle)</i><br />
<i>/* Validation function asserts that the triangle is connected to the edge */</i><br />
<i><br />
</i><br />
<i>Triangle* GetTriangle()</i><br />
<i>/* Returns aTriangle if the edges is connected to triangles else it returns NULL*/</i><br />
<i>Triangle* GetOtherTriangle(Triangle* aTriangle)</i><br />
<i>/* This function assumes that aTriangle is connected to the edge it returns the other triangle connected to it. If there is only one triangle connected to the edge it will return NULL*/</i><br />
<br />
This should keep us safe for now. Don't worry if you don't realize the needs of those functions yet it will become apparent once you get down and dirty with the code.<br />
<br />
And on the Triangle Class we have a few helper functions too <br />
<br />
<br />
<i>void GetVertexes(Vector2f someVertices[3]);</i><br />
<i>/* Iterates through the edges and collects the unique vertexes and fills the list with them */</i><br />
<i>bool IsPartOf(Edge* aEdge);</i></div><div style="text-align: left;"><i>/* Validation function returns true if the edge is a part of this triangle */</i></div><div style="text-align: left;"><i>Edge* GetEdgeLeftOf(Edge* aEdge);</i></div><div style="text-align: left;"><i>/* Returns the other edge connected to the first point of the edge sent in */</i></div><div style="text-align: left;"><div style="text-align: left;"><i>Edge* GetEdgeRightOf(Edge* aEdge);</i></div><div style="text-align: left;"><i>/* Returns the other edge connected to the second point of the edge sent in */</i></div><div style="text-align: left;"><i>Vector2f GetUnconnectedPoint(Edge* aEdge)</i><br />
<i>/* Returns the point on the triangle that isn't connected to the edge */</i><br />
<br />
</div><div style="text-align: left;">You will notice that in order to implement the triangle functions you will need to use the edge functions and even that the edge functions are implemented using other edge functions so all of this is just building a toolkit to allow us to later perform the operations we want to do easily. And the reason for this is that we can't be guaranteed any specific order of the vertexes in an edge. the first point in one might be the same as the second in another but it might also be the same as the first. So you can't assume any kind of ordering. This is because the triangles share the edges and there doesn't exist an ordering that will be consistent for the entire world as for different triangles you are using the same edge but walking around the triangle in different directions.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">Enough preparations now let's get on with our algorithm for splitting triangles. The algorithm I propose is quite simple but for our default case of a single triangle splitting it will guarantee the same optimal results as in our earlier test.<br />
<br />
Again it's time to define some terminology. We will call that blue Line a cutting line and all the red lines are Edges using the classes we just defined and together creates a Triangle.<br />
<br />
Well first formulate the algorithm in pseudo code and then we'll move to actual C++ code.<br />
<br />
For Each Edge Intersected by The CuttingLine<br />
Split that edge into two edges.<br />
For each Triangle connected to the original Edge<br />
Split it into two triangles by drawing an Edge from the intersection point to the point on the triangle not connected to the original edge<br />
<br />
Actual code for performing these tasks is a bit more complex mostly taking care of the destruction and creation of new edges and triangles. But we'll walk through our test case before we start to look at that. Most of the visuals are the same as earlier but we have added one more visual clue we show an intersection between an edge and a cuttingLine.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL1vm3IJpJpmtFbqjsWQNlt-53TpTKWmsSFVN5Sn9eEO0JgqMQhx3ZBrKiuJ1t4a1rBxi9zZeKBZQniBEiYFDbncZSaFrDpBMyUP5-uAessPgxN9DOHHsJ4L2k3cH7IgMZxZ2WZ8D_qBga/s1600-h/CuttingProofStart.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL1vm3IJpJpmtFbqjsWQNlt-53TpTKWmsSFVN5Sn9eEO0JgqMQhx3ZBrKiuJ1t4a1rBxi9zZeKBZQniBEiYFDbncZSaFrDpBMyUP5-uAessPgxN9DOHHsJ4L2k3cH7IgMZxZ2WZ8D_qBga/s400/CuttingProofStart.png" /></a></div>Here is our basic case again we have a a triangle consisting of 3 red edges and a blue cuttingline.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7c6DHcsDzkMmnKEo7IZCMXzfo9umoUjmggXEKUJNTIM1zz7TdVDtPB-0UuTRVEjfKsZolzR-D8IkZUkXsBWzBzrDKFMqJOSnulVjeK1ZyF05uXU5MB0CFDqQyS0HIYe7aDcZDap4Vx8AV/s1600-h/CuttingProofCutPoint_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7c6DHcsDzkMmnKEo7IZCMXzfo9umoUjmggXEKUJNTIM1zz7TdVDtPB-0UuTRVEjfKsZolzR-D8IkZUkXsBWzBzrDKFMqJOSnulVjeK1ZyF05uXU5MB0CFDqQyS0HIYe7aDcZDap4Vx8AV/s320/CuttingProofCutPoint_1.png" /></a></div><div class="separator" style="clear: both; text-align: left;">The green point shows an intersection with an edge. We could as easily have gone with the other edge as we are going to cover later.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_EOjbfTCI0jU97BkfWcTUjTzBrTlHPa2HhPZ4Yb0LCta4j87cBWdtaDIA39C3y50sYtPyMa5FBuHX3NBAfonPq-Mp1dsAfq6JbaP0ujMjqEk2_tdFtboW7_BWLebif5FEHLd9DgBe2MyI/s1600-h/CuttingProofCutLine_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_EOjbfTCI0jU97BkfWcTUjTzBrTlHPa2HhPZ4Yb0LCta4j87cBWdtaDIA39C3y50sYtPyMa5FBuHX3NBAfonPq-Mp1dsAfq6JbaP0ujMjqEk2_tdFtboW7_BWLebif5FEHLd9DgBe2MyI/s320/CuttingProofCutLine_1.png" /></a></div><div style="text-align: center;"><br />
</div><div style="text-align: left;">Since the edge that we intersected is only linked to a single triangle we split that triangle and the edge creating two new triangles instead of the original one. Remember that the original edge is gone replaced by two new to create the two triangles. </div><div style="text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsbO0EFcEIVSb2Kim6lqlbFrRqBDFY1pBtxdmSf1X4f9CSHVZBVZ0bE75dhOjqc59rLhCm3u4vEyjvn5p4AmGNzu3E4amfDVvl7lsOnA7OHUKBOkKNztuv3L2gKckXCPrNUW2CRIpZSa0g/s1600-h/CuttingProofCutPoint_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsbO0EFcEIVSb2Kim6lqlbFrRqBDFY1pBtxdmSf1X4f9CSHVZBVZ0bE75dhOjqc59rLhCm3u4vEyjvn5p4AmGNzu3E4amfDVvl7lsOnA7OHUKBOkKNztuv3L2gKckXCPrNUW2CRIpZSa0g/s320/CuttingProofCutPoint_2.png" /></a></div><div class="separator" style="clear: both; text-align: left;">We identify the second intersection. An important point is that you should only intersect vs edges that existed before you made the first split (They will only be intersected on one of their vertexes anyway so it's just a waste of performance) . This edge is also connected to only one triangle and those will split that triangle.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii5TOBgaexnR6mdRqEZYi7vVuF4h2IVot7LQGhfuiAdG3dbXfgHApDiKln7BIeR8ML9pWB9NUHnDZnzvcKmume7F1UUGnPd6crkOmKiSPhgH4yM8min8kgAC0J7mhuycSj1sjO7I6a3_6K/s1600-h/CuttingProofCutLine_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii5TOBgaexnR6mdRqEZYi7vVuF4h2IVot7LQGhfuiAdG3dbXfgHApDiKln7BIeR8ML9pWB9NUHnDZnzvcKmume7F1UUGnPd6crkOmKiSPhgH4yM8min8kgAC0J7mhuycSj1sjO7I6a3_6K/s320/CuttingProofCutLine_2.png" /></a></div><div style="text-align: left;">This shape should be familiar as one of our two optimal splits from earlier. And if we had intersected the rightmost edge first it would have resulted in the other configuration. You are free to try this out on paper to prove it to yourself. </div><div style="text-align: left;"><br />
</div><div style="text-align: left;">Now we should see if we can't convert the pseudo code for this to something closer to real code. This part is the splitting part. This code is written for clarity and not performance in the hope that this will help you grasp the algorithm.<br />
<br />
The function SplitEdgesWithCuttingLine is not a part of the triangle or edge structure but rather a part of your TriangleContainer. SomeOriginalEdges is a temporary list of edges created for this specific update before this function is called that contains the edges relevant for the algorithm .It won't be used again after this function so we can do what we want with it.<br />
<br />
For the record I have booth a TriangleContainer and an EdgeContainer class any edges created is automatically added to an edge container and each triangle to a triangle container this is done by forcing an appropriate container to be sent through their constructor this means that every edge or triangle created or deleted automatically handles its own memory. Also if A triangle is deleted it automatically removes itself from all its edges and any edge with no connected triangles after this will be deleted. This is the reason this code might seem to be sloppy with memory handling because it's all taken care of elsewhere.<br />
<br />
<span style="font-size: x-small;"><i>void SplitEdgesWithCuttingLine(std::vector<edge*>& someOriginalEdges,</edge*></i></span><span style="font-size: x-small;"><i>Vector2f aFirstPoint,Vector2f aSecondPoint</i></span><span style="font-size: x-small;"><i><edge*>)<br />
</edge*></i></span></div><div style="text-align: left;">{</div><div style="text-align: left;"><span style="font-size: x-small;"><i><edge*></edge*></i></span></div><div style="text-align: left;"><span style="font-size: x-small;"><i> std::vector<edge*><edge*> newEdges; // starts empty<br />
</edge*></edge*></i></span><br />
<span style="font-size: x-small;"><i> std::vector<edge*><edge*> edgesToDelete; // starts empty<br />
</edge*></edge*></i></span><br />
<br />
<span style="font-size: x-small;"><i><edge*> for(i=0;i<</edge*>someOriginalEdges<edge*>. size();i++)</edge*></i></span><br />
<span style="font-size: x-small;"><i><edge*> {</edge*></i></span><br />
<span style="font-size: x-small;"><i><edge*> if(</edge*>someOriginalEdges[i]->IsIntersectedByInfiniteLine(a</i></span><span style="font-size: x-small;"><i>FirstPoint,</i></span><span style="font-size: x-small;"><i>aSecondPoint</i></span><span style="font-size: x-small;"><i><edge*></edge*></i></span><span style="font-size: x-small;"><i>)</i></span><br />
<span style="font-size: x-small;"><i> {</i></span><br />
<span style="font-size: x-small;"><i> /* we have an intersection . To keep things easy we always assume that our cutting lines are infinite. While the edges aren't */</i></span><br />
<span style="font-size: x-small;"><i> someOriginalEdges[i]->Split(</i></span><span style="font-size: x-small;"><i>a</i></span><span style="font-size: x-small;"><i>FirstPoint,</i></span><span style="font-size: x-small;"><i>aSecondPoint</i></span><span style="font-size: x-small;"><i>);</i></span><br />
<span style="font-size: x-small;"><i> delete(someOriginalEdges[i]);</i></span><br />
<span style="font-size: x-small;"><i> }<br />
</i></span><br />
<span style="font-size: x-small;"><i><edge*></edge*></i></span><br />
<span style="font-size: x-small;"><i><edge*> }</edge*></i></span><br />
<span style="font-size: x-small;"><i><edge*>} <br />
</edge*></i></span><br />
<span style="font-size: x-small;"><i><edge*><br />
</edge*></i></span><br />
<span style="font-size: x-small;"><edge*><span style="font-size: small;">As we can see we are calling two functions on the Edge class that we haven't talked about earlier. The first one is trivial it performs line to line intersection between the incoming line and the edges considering the incoming line as an infinite line while the Edge is finite and returns the result of the test.</span></edge*></span><br />
<br />
<span style="font-size: x-small;"><edge*><span style="font-size: small;">The split function assumes that the lines are intersecting before being called and it's there some of the work happens so let's look at it. A short disclaimer</span></edge*></span> here about the code in this article it is all written specifically for this blog and is not the real code I would use so it might have some small compilation errors or even logical mistakes in it.</div><br />
<span style="font-size: x-small;"><i>void Edge::Split(Vector2f aFirstPoint,Vector2f aSecondPoint)</i></span><br />
<span style="font-size: x-small;"><i>{</i></span><br />
<span style="font-size: x-small;"><i> Vector2f intersectionPoint;</i></span><br />
<span style="font-size: x-small;"><i> bool intersects = LineVsInfineLineIntersection(myFirstPoint,mySecondPoint,aFirstPoint,aSecondPoint,intersectionPoint); // Calculates the intersection point of the two lines <br />
</i></span><br />
<span style="font-size: x-small;"><i> assert(</i></span><span style="font-size: x-small;"><i>intersects==true);</i></span><br />
<span style="font-size: x-small;"><i> Edge* firstEdge= new Edge( </i></span><span style="font-size: x-small;"><i>myFirstPoint,</i></span><span style="font-size: x-small;"><i>intersectionPoint);</i></span><br />
<span style="font-size: x-small;"><i> Edge* secondEdge= new Edge( </i></span><span style="font-size: x-small;"><i>intersectionPoint</i></span><span style="font-size: x-small;"><i>,</i></span><span style="font-size: x-small;"><i>mySecondPoint</i></span><span style="font-size: x-small;"><i>);</i></span><br />
<span style="font-size: x-small;"><i> // Creates 2 new edges to replace this edge please observe that they currently aren't connected to any triangles <br />
</i></span><br />
<span style="font-size: x-small;"><i> for(int i=0 ;i<2;i++)</i></span><br />
<span style="font-size: x-small;"><i> {</i></span><br />
<span style="font-size: x-small;"><i> if(myTriangles[i]!=NULL) </i></span><br />
<span style="font-size: x-small;"><i> // Only do this if the Edge is connected to triangles <br />
</i></span><br />
<span style="font-size: x-small;"><i> {</i></span><br />
<span style="font-size: x-small;"><i> myTriangles[i]->SplitByEdge(this,firstEdge,secondEdge);</i></span><br />
<span style="font-size: x-small;"><i> // inform the triangle that should be split what edge should be split and what the replacing edges look like. <br />
</i></span><br />
<span style="font-size: x-small;"><i> }<br />
</i></span><br />
<span style="font-size: x-small;"><i> }</i></span><br />
<span style="font-size: x-small;"><i>}<br />
</i></span><br />
<br />
That was a bit of code but I would say it's hopefully really simple to grasp so far because we have divided the problem down into small parts. First we detect intersections. Then we determine the intersection points. And then we will split the triangles by telling it which edge that was split and what edges will be replacing it. So let's look at code for performing that split.<br />
<br />
<i><span style="font-size: x-small;">void Triangle::SplitByEdge(Edge* aSplitEdge,Edge* aFirstNewEdge,Edge* aSecondNewEdge)</span></i><br />
<i><span style="font-size: x-small;">{</span></i><br />
<i><span style="font-size: x-small;"> assert(IsPartof(aSplitEdge)==true); </span></i><br />
<i><span style="font-size: x-small;"> // Make certain it is really a part of this triangle else we have a bug somewhere.</span></i><br />
<i><span style="font-size: x-small;"> </span></i><br />
<i><span style="font-size: x-small;"> Edge* edgeConnectTheFirstNewEdge=GetEdgeLeftOf(aSplitEdge);</span></i><br />
<i><span style="font-size: x-small;"> Edge* edgeConnectTheSecondNewEdge=GetEdgeRightOf(aSplitEdge);</span></i><br />
<i><span style="font-size: x-small;">/* get the two other edges of the triangle first get the one connected to a FirstNewEdge if your code is flexible you should even be able to send in aFirstNewEdge and aSecond new Edge instead of aSplitEdge*/</span></i><br />
<i><span style="font-size: x-small;"> Edge* middleEdge= new Edge(GetUnconnectedPoint(aSplitEdge),aFirstNewEdge->GetconnectedPoint(aSecondNewEdge));</span></i><br />
<i><span style="font-size: x-small;">/* This creates an edge that goes from the point of the triangle that isn't a part of the original edge to the cutting point. This works because the cutting point is the only point that will be connected between the two new edges. Else you could just pass the intersection point to the function too */ <br />
</span></i><br />
<i><span style="font-size: x-small;"> aSplitEdge->DisconnectFromTriangle(this);</span></i><br />
<i><span style="font-size: x-small;"> edgeConnectTheFirstNewEdge->DisconnectFromTriangle(this);</span></i></div><div style="text-align: left;"><i><span style="font-size: x-small;"> edgeConnectTheSecondNewEdge->DisconnectFromTriangle(this);</span></i><br />
<i><span style="font-size: x-small;">/* prepare for the creation of the new triangles by removing the old one from the edges this could and should be done in the current triangles destructor but then you wouldn't see the code so */</span></i><br />
<i><span style="font-size: x-small;"> Triangle* firstTriangle=new Triangle (edgeConnectTheFirstNewEdge,aFirstNewEdge,middleEdge);</span></i><br />
<i><span style="font-size: x-small;"> Triangle* secondTriangle=new Triangle (edgeConnectTheSecondNewEdge,aSecondNewEdge,middleEdge);</span></i><br />
<i><span style="font-size: x-small;">/* Here we create the new triangles that is the result of the split. Observe that they connect to the edges by themselves in their constructor this is the reason we had to disconnect the edges earlier so that we could add new ones*/</span></i><br />
<i><span style="font-size: x-small;"> delete (this);</span></i><br />
<i><span style="font-size: x-small;">/* The final step. The triangle is already not connected to anything anymore so it's free to delete without destroying anything it will just peacefully remove itself from it's container and die */<br />
</span></i><br />
<i><span style="font-size: x-small;">}</span></i></div><div style="text-align: left;"><br />
</div><div style="text-align: left;">So we have written quite a bit of code now however observe that except for the comments for you readers, none of the functions is larger than 20-30 lines of code so we have managed to split our big problems into a few really small parts which has helped us to solve it smoothly.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">But how do we use this algorithm to update an area and does this algorithm really work on more complex cases? Let's take a look at the case that we showed in the start of this article it can be considered kind of a nightmare case even though there exists more complex cases for a human to follow from the perspective of the algorithm it doesn't get any worse than this.</div><div style="text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgp1ICtIBgkjw1Tsze_CUff4WMNR8CZhyphenhyphenMyA275NeQwgDh7JvbKCA6BiHQNbdvB6d9mN_MuZR6b0sTesWbx35wP-Mbo86sMicnKYvpBMDBHZs5ah6joMCDUOJtXDzLX0oi6L9I-YWC9Goce/s1600-h/CuttingAHole.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgp1ICtIBgkjw1Tsze_CUff4WMNR8CZhyphenhyphenMyA275NeQwgDh7JvbKCA6BiHQNbdvB6d9mN_MuZR6b0sTesWbx35wP-Mbo86sMicnKYvpBMDBHZs5ah6joMCDUOJtXDzLX0oi6L9I-YWC9Goce/s400/CuttingAHole.png" /></a></div><div class="separator" style="clear: both; text-align: left;">First out how are we going to use this to perform an update. The basic idea is that we will use all three edges of the blue triangle as cutting lines. And apply each of them separately. After this we will identify which of the triangles that are inside of the blue triangle and call their update. We will look into this later. But for now we assume that we call <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine </span></span></b><span style="font-size: x-small;"><span style="font-size: small;">one time for every </span></span><span style="font-size: x-small;"><span style="font-size: small;">cutting edge and input all the edges in the world in our list </span></span><span style="font-size: small;"><b><i>someOriginalEdges</i></b></span><span style="font-size: x-small;"> <span style="font-size: small;">after this step we will look at how to optimize that so that we don't have to intersect as many edges.</span></span></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2fVpGu91A2UDuAUMXosDpk8GuWFyWd7IFhy9BPMyRbmvBgCwnlYvSRNUzkyDesL5QjCUJp6nP1mZl48g96Q6cT1115ejGKqLR0frezl-Mzf2rcjoPl7x2PfkjYByG-QcT_Q5hBsd01IjO/s1600-h/CuttingLines.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2fVpGu91A2UDuAUMXosDpk8GuWFyWd7IFhy9BPMyRbmvBgCwnlYvSRNUzkyDesL5QjCUJp6nP1mZl48g96Q6cT1115ejGKqLR0frezl-Mzf2rcjoPl7x2PfkjYByG-QcT_Q5hBsd01IjO/s320/CuttingLines.png" /></a><span style="font-size: x-small;"><span style="font-size: small;"> </span></span></div><div style="text-align: left;"> Here we have an image of the three cutting lines generated from our blue triangle. As they cutting lines counts as infinitely long we have drawn them so that they pass through the entire world.</div><div style="text-align: left;"><br />
</div><div style="text-align: left;">You will notice that we are using green cutting lines here instead of blue as in the earlier examples. The reason for this is that we want to be able to keep the blue triangle visual in the world and therefore we need another colour to denote our cutting lines.</div><div style="text-align: left;"><br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKEfqO6cRuefUbH-wz-723GbpbN0sMU81xt8j0M7SH08DmmMfpyXzfnrJpt_Skt_cMfsIeiPDWiJSXbLruQ_ElZykcyn44bBHk_i2PoHId7T4YfUoZIXEEZrRHYNfwshfR8qIOq-iyyZaO/s1600-h/TriangleCutting01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKEfqO6cRuefUbH-wz-723GbpbN0sMU81xt8j0M7SH08DmmMfpyXzfnrJpt_Skt_cMfsIeiPDWiJSXbLruQ_ElZykcyn44bBHk_i2PoHId7T4YfUoZIXEEZrRHYNfwshfR8qIOq-iyyZaO/s320/TriangleCutting01.png" /></a></div><br />
</div><div style="text-align: left;">Following the algorithm explained above we are going to call <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine</span></span></b> once for each of these lines. We could start with anyone of them but since we only want to perform one example we will select a order and start with the one marked as a green line in the image to the left.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrWE8uJXVOPtWttlR3rhhL1jPDMu9DpebVvblbc30BdVYnEsDYZEs9LHHlZQ2pq49WAU2Gv6VdQA45m4I2R5RrcanQGIdLfGHRkjiZRf7VyvfSMxVoomKKIjsZDszyA3Afbbpi-JQ9jkjD/s1600-h/TriangleCutting02.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrWE8uJXVOPtWttlR3rhhL1jPDMu9DpebVvblbc30BdVYnEsDYZEs9LHHlZQ2pq49WAU2Gv6VdQA45m4I2R5RrcanQGIdLfGHRkjiZRf7VyvfSMxVoomKKIjsZDszyA3Afbbpi-JQ9jkjD/s320/TriangleCutting02.png" /></a></div><br />
The deep green point shows us the position of an intersection. Since the edges in someOriginalEdges can be in any order there is no guarantee that the first edge we intersect will be one of the leftmost or rightmost edges. We will use a fixed order for the rest of these images but I want to be sure you know that this isn't necessary or even easily enforceable. All the triangles connected with the intersected edge is marked with a light green color so that you can easily follow what is happening.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZCVJDPYLXraJWhPBQxW0bCkZxZK3vq6x1xJEttyoBlHpLcwaZRi4GVriQKcjlTaeVM3njPGC0NLMy2GxKAceBcsvwe5lerctJAL2WlXRPHNp9wWuplz_CLY6HpwTZX58sGDWSSo84-7Uc/s1600-h/TriangleCutting03.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZCVJDPYLXraJWhPBQxW0bCkZxZK3vq6x1xJEttyoBlHpLcwaZRi4GVriQKcjlTaeVM3njPGC0NLMy2GxKAceBcsvwe5lerctJAL2WlXRPHNp9wWuplz_CLY6HpwTZX58sGDWSSo84-7Uc/s320/TriangleCutting03.png" /></a></div><br />
Following the steps of the algorithm if an intersection is created we split that edge and all the triangles sharing that edge. Once we have done this the rest is just repetition of the same steps but for sake of clarity I will walk you through them. For newly created triangles we will mark them in a light blue color so that it will be easy to follow the flow. You will surely notice that what is green always become blue as we split the old green triangles into the new blue ones.<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPd4OxSDf_0pBz5JAmNsr27zbfmWKKiMrS5Xr_CIEZVGtpLeVZIBECvvPwsv-5ajQJZh5ejiyexhr42IL1C_atl84hIBMSzSjIGfPASQHaqXEFrpHaNI_OI29uKk2Zueab8ULXmu-3ihXs/s1600-h/TriangleCutting04.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPd4OxSDf_0pBz5JAmNsr27zbfmWKKiMrS5Xr_CIEZVGtpLeVZIBECvvPwsv-5ajQJZh5ejiyexhr42IL1C_atl84hIBMSzSjIGfPASQHaqXEFrpHaNI_OI29uKk2Zueab8ULXmu-3ihXs/s320/TriangleCutting04.png" /></a></div><br />
As we have resolved the edge in the earlier case we move forward to the next Edge intersection again marked with the deep green marker. As you can see clearly this edge is only connected to a single triangle and this is why we have only one light green triangle.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLTDb__266xKpdeGHbxUGj2iqAiie7z6pUWUv1zU3vOXiR4ajluaQqGbaKp1ZjHdGhiRcoUI-NIAkPk7sfTpCsW3IeHvXn5PjNPDgdSZGXZT2NTklkzsjpl1Cv1mR41-1-hlnAPrkSZB09/s1600-h/TriangleCutting05.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLTDb__266xKpdeGHbxUGj2iqAiie7z6pUWUv1zU3vOXiR4ajluaQqGbaKp1ZjHdGhiRcoUI-NIAkPk7sfTpCsW3IeHvXn5PjNPDgdSZGXZT2NTklkzsjpl1Cv1mR41-1-hlnAPrkSZB09/s320/TriangleCutting05.png" /></a> <br />
<br />
<br />
All the triangles connected to the intersected edge are split and so is the edge. It might be slightly unclear but you should be able to see the red line over the green for the blue triangle pair.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9YN36zze2C0xXBPw-_gI5iEdAxS7lcyxNJoC7G0bZCNUg7vLZjliRbRbrlwPvAF3dEEI-mIjhs_2YRJDX1fmgDpfic0GIuAPiqdQ9bjZecD2slbJ6Cphm64HABudyX4LftEJuYnT9y1C/s1600-h/TriangleCutting06.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9YN36zze2C0xXBPw-_gI5iEdAxS7lcyxNJoC7G0bZCNUg7vLZjliRbRbrlwPvAF3dEEI-mIjhs_2YRJDX1fmgDpfic0GIuAPiqdQ9bjZecD2slbJ6Cphm64HABudyX4LftEJuYnT9y1C/s320/TriangleCutting06.png" /></a></div><br />
And finally here is the last edge that is intersected by the cutting line. Again it is marked with a deep green marker and the triangles to be split is slightly green,<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpX8Xj4MlFjDXPlH77xqjPsDCVwgMT9ULiKiUYQsXf_z_jGTySQkPxbdUFQtZgDuul4zKLbk_ClIWWOgJMeXFZ-lVwGq7VpnOZYBjsUEpJYVdn4z95Eq8tvAgBW3mi5tDpFR-gEQ9KWfQX/s1600-h/TriangleCutting07.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpX8Xj4MlFjDXPlH77xqjPsDCVwgMT9ULiKiUYQsXf_z_jGTySQkPxbdUFQtZgDuul4zKLbk_ClIWWOgJMeXFZ-lVwGq7VpnOZYBjsUEpJYVdn4z95Eq8tvAgBW3mi5tDpFR-gEQ9KWfQX/s320/TriangleCutting07.png" /></a></div><br />
<br />
And we perform the final split for this line. Resulting in the two light blue triangles<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIl7tDiA_bDrpNrye4bIZjJ7KFwvPSv8IDKeLGOkXxfWkLaZebpNnupqQpq2Mmm3bk346DQcxbGzDkG5a3tJlVGeFFrApGTdvztPcoKwS4fjj3_-bd2NwDbdxIrGnaIzpk48lm-nu4RcIR/s1600-h/TriangleCutting08.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIl7tDiA_bDrpNrye4bIZjJ7KFwvPSv8IDKeLGOkXxfWkLaZebpNnupqQpq2Mmm3bk346DQcxbGzDkG5a3tJlVGeFFrApGTdvztPcoKwS4fjj3_-bd2NwDbdxIrGnaIzpk48lm-nu4RcIR/s320/TriangleCutting08.png" /></a></div><br />
<span id="goog_1260723557875"></span><span id="goog_1260723557876"></span><br />
As we have split all lines intersecting the cutting line it is time to move on to the next one. This means we call <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine</span></span></b> again with all the current edges in someOriginalEdges. So all the new edges created through the last pass are now possible to intersect vs.<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHrYUeSNgvoaTWojSteZV5VUN5tecLhIPN2mkh1iJ3NQ2kbszE_hAKIyP0DxxSkUHbPA_axFU63sHIrUKAQr0usFOPFjazpXTXM3axJXi0Tiafvcyq7s5KeoHRKBrVNu9l4gpRdBFZR1-V/s1600-h/TriangleCutting09.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHrYUeSNgvoaTWojSteZV5VUN5tecLhIPN2mkh1iJ3NQ2kbszE_hAKIyP0DxxSkUHbPA_axFU63sHIrUKAQr0usFOPFjazpXTXM3axJXi0Tiafvcyq7s5KeoHRKBrVNu9l4gpRdBFZR1-V/s320/TriangleCutting09.png" /></a></div><br />
We intersect the first edge marked with a deep green node. The triangles connected to that node are marked with light green.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm0m-VIDsHtjZ57Hd9DWG9ODZWjF2pRc2QI-ukp30g6obXMBlju72yIRDq7YtT76ZSUhtbH6RMyScl8aHTChmd2yvTtQLWedyngAjqnmP4YRx6ATGTbzlY3hzlMx56Sq9G2Q6_31o541BB/s1600-h/TriangleCutting10.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm0m-VIDsHtjZ57Hd9DWG9ODZWjF2pRc2QI-ukp30g6obXMBlju72yIRDq7YtT76ZSUhtbH6RMyScl8aHTChmd2yvTtQLWedyngAjqnmP4YRx6ATGTbzlY3hzlMx56Sq9G2Q6_31o541BB/s320/TriangleCutting10.png" /></a></div><br />
And the light green marked triangle is split by a line from the deep green intersection point to the corner not a part of the edge being intersected into two light blue triangles.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCw7f8U99Z6AcGjPNv8QApi1KUxyYFDeh5OGEjTDSES53zbxe7CSCvORg01aUHz1LKi1Mq9YmYNs4gDVNHZzZE-3i1WJ94OE2lMAKgNcB-G66GtCVabxLZ-xpKydD1n_Nr0lkiK1Uyw2Zd/s1600-h/TriangleCutting11.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCw7f8U99Z6AcGjPNv8QApi1KUxyYFDeh5OGEjTDSES53zbxe7CSCvORg01aUHz1LKi1Mq9YmYNs4gDVNHZzZE-3i1WJ94OE2lMAKgNcB-G66GtCVabxLZ-xpKydD1n_Nr0lkiK1Uyw2Zd/s320/TriangleCutting11.png" /></a></div><br />
And we move on to the next edge which connects the two light green triangles (If this sounds like a repeating record it's because it is. But this kind of clarity has been requested by my students).<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOCTWQciZcqv4FOcOSxM3ZgAeCf5DG-JOf4QCLdzj1sivikHML2GwxFju5hP_4Bv2DCj54kAc07Uw0ddfkF68_vgbGoM1r_qw6QlE6e6DTSqQplXZNKQDkv9imTdTXBDXOBD1-afSprPdj/s1600-h/TriangleCutting12.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOCTWQciZcqv4FOcOSxM3ZgAeCf5DG-JOf4QCLdzj1sivikHML2GwxFju5hP_4Bv2DCj54kAc07Uw0ddfkF68_vgbGoM1r_qw6QlE6e6DTSqQplXZNKQDkv9imTdTXBDXOBD1-afSprPdj/s320/TriangleCutting12.png" /></a></div><br />
And the two light green triangles were split into 4 light blue triangles.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYRPblBI1P4HF1u4fhAMfs_yqRMf09HpIi2jVTx9872bvauG6zfw2jHzI79K5bTzeg_305b3uF0UyacYVr8HElRDTA9XB5iPVKDEleyiIhQeJVedsiaBBah4_V0vUolhHCU8JSSXomcPBT/s1600-h/TriangleCutting13.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYRPblBI1P4HF1u4fhAMfs_yqRMf09HpIi2jVTx9872bvauG6zfw2jHzI79K5bTzeg_305b3uF0UyacYVr8HElRDTA9XB5iPVKDEleyiIhQeJVedsiaBBah4_V0vUolhHCU8JSSXomcPBT/s320/TriangleCutting13.png" /></a></div><br />
The next edge is connected to by the two light green triangles. The deep green dot is the intersection point between the edge and the cutting line.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG0Rf8LVU3KjD74jkzqYMZys14M5pA-mbFEjalSJQW15L_aGW-68ZNLDLjCoyjPEF2vI73jgAgs0j9vbVREe5H6X9Sn69SgTw5E0T9HmsO7szRy9mmuf3n21l-IA8t_WZG79okgGCU7I3J/s1600-h/TriangleCutting14.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG0Rf8LVU3KjD74jkzqYMZys14M5pA-mbFEjalSJQW15L_aGW-68ZNLDLjCoyjPEF2vI73jgAgs0j9vbVREe5H6X9Sn69SgTw5E0T9HmsO7szRy9mmuf3n21l-IA8t_WZG79okgGCU7I3J/s320/TriangleCutting14.png" /></a></div><br />
<br />
And the light blue triangles show the split created by the intersection of the line at the deep green dot.<br />
<br />
I think we can start speeding this up now as you know the drill so we're going to start just showing the before and after split pictures.<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNQOkzZ_Ox4nPIjLJrcoQN2yv5SezeWF4fGdE3oZGFk0pgHaa13EOG80q1y7Ws3TGgY1w-hUFGvvWW0RvUZ1Dkz9r6PXGbwIeKUumEUW9qbvw-gNwxNCAsa9r8eLP1c29VAm_8vlXOJLSm/s1600-h/TriangleCutting15.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNQOkzZ_Ox4nPIjLJrcoQN2yv5SezeWF4fGdE3oZGFk0pgHaa13EOG80q1y7Ws3TGgY1w-hUFGvvWW0RvUZ1Dkz9r6PXGbwIeKUumEUW9qbvw-gNwxNCAsa9r8eLP1c29VAm_8vlXOJLSm/s320/TriangleCutting15.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjM_Y3ar7AiaGPQnpa-FGYNdKP_ngtENMIjTykDZRIZDl-anuw1TbmwJhyEsVOay8rnhoYf9rcStzgTiJXIkW0aNZqGIZLXzVZn9icKtmVXfz2musvpR2_-us4VxokE59yEozkbfqb7gfj/s1600-h/TriangleCutting16.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjM_Y3ar7AiaGPQnpa-FGYNdKP_ngtENMIjTykDZRIZDl-anuw1TbmwJhyEsVOay8rnhoYf9rcStzgTiJXIkW0aNZqGIZLXzVZn9icKtmVXfz2musvpR2_-us4VxokE59yEozkbfqb7gfj/s320/TriangleCutting16.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitRhlkYe8m4qvW9JAvhLkcWZqmAK12JrPApg3mBBkNcx6Of4LBozeYcE80Xt50WgoRZQo06J5MECCGtw9oCAJyh5tqO4Yxyxv7K2W26lcqdVT901VtAK-wthrK3BGngeGgzlomZ4y-PwGP/s1600-h/TriangleCutting17.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitRhlkYe8m4qvW9JAvhLkcWZqmAK12JrPApg3mBBkNcx6Of4LBozeYcE80Xt50WgoRZQo06J5MECCGtw9oCAJyh5tqO4Yxyxv7K2W26lcqdVT901VtAK-wthrK3BGngeGgzlomZ4y-PwGP/s320/TriangleCutting17.png" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinqv42A1wRG6MritTO5uvFj0hhJSa-TYdRCQ4xwcEfhc-m-mUzbsrhQR24cajENnjKBD5afKI2CP1_ob1sbzqv7p5a_JK09iNe5c6n2_hEUPy0Z3fhVVSu_geAe7XA7EQE46ENm3X-kbrC/s1600-h/TriangleCutting18.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinqv42A1wRG6MritTO5uvFj0hhJSa-TYdRCQ4xwcEfhc-m-mUzbsrhQR24cajENnjKBD5afKI2CP1_ob1sbzqv7p5a_JK09iNe5c6n2_hEUPy0Z3fhVVSu_geAe7XA7EQE46ENm3X-kbrC/s320/TriangleCutting18.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy1qTkqpKSKS8DO_Ej7ZjnAXOS3VzXEaq5fi5YtIQwjgw0M8KChVEGQnc-6hryk9tg1kXnZ8MXoPQxSTiWSBeMn958SY40N4DbGH67lCpSm4YjyHVwECuiX-nDHLss-A57uP7SwbFPaR1z/s1600-h/TriangleCutting19.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy1qTkqpKSKS8DO_Ej7ZjnAXOS3VzXEaq5fi5YtIQwjgw0M8KChVEGQnc-6hryk9tg1kXnZ8MXoPQxSTiWSBeMn958SY40N4DbGH67lCpSm4YjyHVwECuiX-nDHLss-A57uP7SwbFPaR1z/s320/TriangleCutting19.png" /></a></div>With the last cutting line we restart the process again and all lines that were created by the last line is also feed to the <span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine</span></span> function.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoNghNnOf4qiGbxI_m_KtjoRGtzngCg3PR2faZ2Tkt_8QzZst0cSMZwaIt9552hHLP0sA2eHQiAHCU6awL_7DM1oRNl-aGTghC1nZQ4R7xfNcO-QGd2LO-vuGdmt6AegBXbwSOVe1OmUkc/s1600-h/TriangleCutting20.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoNghNnOf4qiGbxI_m_KtjoRGtzngCg3PR2faZ2Tkt_8QzZst0cSMZwaIt9552hHLP0sA2eHQiAHCU6awL_7DM1oRNl-aGTghC1nZQ4R7xfNcO-QGd2LO-vuGdmt6AegBXbwSOVe1OmUkc/s320/TriangleCutting20.png" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAw2jkbFMy8D6TnjaINrOvVMBnHnj3ar1GlIcRY84R6wZIiyt239tJ2f5VgBiW6Hole6lyyA8maHblttp4copVwpgR6DmLRGNx5yVFiHMUdHtbyJwOYZOjk6gcCbaVXdLNl_Nh2yP8Ka3W/s1600-h/TriangleCutting21.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAw2jkbFMy8D6TnjaINrOvVMBnHnj3ar1GlIcRY84R6wZIiyt239tJ2f5VgBiW6Hole6lyyA8maHblttp4copVwpgR6DmLRGNx5yVFiHMUdHtbyJwOYZOjk6gcCbaVXdLNl_Nh2yP8Ka3W/s320/TriangleCutting21.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFyACoBrhyDMyRaLRSQ-XYbUB4V3oJmbOOgc5x-CxItJNdX4gWOoBI40GUSU0xV76bbywPbraasq3dUDMfhROU-IRkpzQkRaOL3yh-OUogTyyfPXS6z8_NK8Z-enx4W61t_0FgpgB-IvFl/s1600-h/TriangleCutting22.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFyACoBrhyDMyRaLRSQ-XYbUB4V3oJmbOOgc5x-CxItJNdX4gWOoBI40GUSU0xV76bbywPbraasq3dUDMfhROU-IRkpzQkRaOL3yh-OUogTyyfPXS6z8_NK8Z-enx4W61t_0FgpgB-IvFl/s320/TriangleCutting22.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk4N0YSV76PyMX_vQ1pr7Fms2oeqWj_RPriinF009etQ6FfOKR86AAI8Fm5v3mMbhJvUXGisgQdioK5UGfmWQui9l9ZKa7Oo7J8OMxzQggqQAH5_VktGW3IWEMxyOVbFdQnDa8DeKxVQ9W/s1600-h/TriangleCutting23.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk4N0YSV76PyMX_vQ1pr7Fms2oeqWj_RPriinF009etQ6FfOKR86AAI8Fm5v3mMbhJvUXGisgQdioK5UGfmWQui9l9ZKa7Oo7J8OMxzQggqQAH5_VktGW3IWEMxyOVbFdQnDa8DeKxVQ9W/s320/TriangleCutting23.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4S3Cnoq51IGg7mDENCNZ5K3SYL7JlASXxWroT8WTovRq6o3ZQL4XizOfF50b4apDZaIAwCfSe4qEylWj_kdL91oIMeCND5r4eSfagDsG_A-Zje3_VtPQATr0UO3-b1ylSjU3I7pUR5aW1/s1600-h/TriangleCutting24.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4S3Cnoq51IGg7mDENCNZ5K3SYL7JlASXxWroT8WTovRq6o3ZQL4XizOfF50b4apDZaIAwCfSe4qEylWj_kdL91oIMeCND5r4eSfagDsG_A-Zje3_VtPQATr0UO3-b1ylSjU3I7pUR5aW1/s320/TriangleCutting24.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlPNzlQgXfSPadzIvTxrRfS2QOxmMJfz0Ls9pbqq5JDMT-rSP-NT6Ss8_oVZpSwA5y23FEFJlGTiCokv8L5NyBZTCCVIHi0ykVjGI_VP_fk_gq7e2BlsH-iQlDYOydtYusbChTYViedY6P/s1600-h/TriangleCutting25.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlPNzlQgXfSPadzIvTxrRfS2QOxmMJfz0Ls9pbqq5JDMT-rSP-NT6Ss8_oVZpSwA5y23FEFJlGTiCokv8L5NyBZTCCVIHi0ykVjGI_VP_fk_gq7e2BlsH-iQlDYOydtYusbChTYViedY6P/s320/TriangleCutting25.png" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP42SzKWi7JTSxhGIY7nu-FhQ6HmIoV8fUCr0oVd4EnnWUX_8XaOs3nH-byMILs_WfXZ0kKubemgt09uEy_Fi9hkx0U0_niJoxXatx5BPbqworLJR2-ondU4t7OmO9yT-0eBnZbGvTA7kP/s1600-h/TriangleCutting26.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP42SzKWi7JTSxhGIY7nu-FhQ6HmIoV8fUCr0oVd4EnnWUX_8XaOs3nH-byMILs_WfXZ0kKubemgt09uEy_Fi9hkx0U0_niJoxXatx5BPbqworLJR2-ondU4t7OmO9yT-0eBnZbGvTA7kP/s320/TriangleCutting26.png" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg78GOEW2g82-rwrQ0xX8mQSBL9QhyphenhypheniEDPf2SOCb2Pq8BZx6fTpSb_sWBE40iqOuLkxNEaFomjw1PiKHA95aveMUr4dbK0gFKVFQ14kq-27MIpiD20Ad0PCwfDJ0goyc5djuqL8mnGcOeCk/s1600-h/TriangleCutting27.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg78GOEW2g82-rwrQ0xX8mQSBL9QhyphenhypheniEDPf2SOCb2Pq8BZx6fTpSb_sWBE40iqOuLkxNEaFomjw1PiKHA95aveMUr4dbK0gFKVFQ14kq-27MIpiD20Ad0PCwfDJ0goyc5djuqL8mnGcOeCk/s320/TriangleCutting27.png" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxPdKAO3posQAJQefdWfL9FBEjE4xAlHUGjH7-RnU9njL5MW8VLLFoG2zFTc8qdgmEoiGPP5lwVuk5V1eitUHgNJNs4qgSOf-uir3khEQvwgrv4S_oRTgTnrW_mKzVMLqbF7ZpN4BdbTba/s1600-h/TriangleCutting28.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxPdKAO3posQAJQefdWfL9FBEjE4xAlHUGjH7-RnU9njL5MW8VLLFoG2zFTc8qdgmEoiGPP5lwVuk5V1eitUHgNJNs4qgSOf-uir3khEQvwgrv4S_oRTgTnrW_mKzVMLqbF7ZpN4BdbTba/s320/TriangleCutting28.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdox_9sPrZktaprS-IhnSdqruDTlE-qYYBHWPbzIe0AJVnLjGPgKI_AJSF0sfv96a5g2C97uISUE_j3ih0admj4ISPs0X9LegG1Bq8EpCUZrfe5EsGGWznofI7WWpYMYq8LkzzN0qw2AKf/s1600-h/TriangleCutting29.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdox_9sPrZktaprS-IhnSdqruDTlE-qYYBHWPbzIe0AJVnLjGPgKI_AJSF0sfv96a5g2C97uISUE_j3ih0admj4ISPs0X9LegG1Bq8EpCUZrfe5EsGGWznofI7WWpYMYq8LkzzN0qw2AKf/s320/TriangleCutting29.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrHeICIcDof3WtNZeyEcbwkzMLpLHj78K5p1CG53cQ2uUGfyrLia6rzQR4AeYTE7o_GmZEP0CgBocuemo0Nfs66GOjiTNVXO0EOYcxrt7OOyKLBJHPYjc4-5mz8kMBuJwL4B-80UIDhDn1/s1600-h/TriangleCutting30.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrHeICIcDof3WtNZeyEcbwkzMLpLHj78K5p1CG53cQ2uUGfyrLia6rzQR4AeYTE7o_GmZEP0CgBocuemo0Nfs66GOjiTNVXO0EOYcxrt7OOyKLBJHPYjc4-5mz8kMBuJwL4B-80UIDhDn1/s320/TriangleCutting30.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju9-vdyJagBDb-t3EObUhk5Bd30kKdyKSVpSRnBKdV6K7IiIkMjEdVK652d1V0c8XskXvGLfzwH-F44TwXN1TCt4s5sKgwN2M1yrEbHxFFICi3IhF8XZi-GcwCce9IYoGvB-9l0aLtWJSP/s1600-h/TriangleCutting31.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju9-vdyJagBDb-t3EObUhk5Bd30kKdyKSVpSRnBKdV6K7IiIkMjEdVK652d1V0c8XskXvGLfzwH-F44TwXN1TCt4s5sKgwN2M1yrEbHxFFICi3IhF8XZi-GcwCce9IYoGvB-9l0aLtWJSP/s320/TriangleCutting31.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsAz4JlHJf2tJdZORIkt3YbRLJCmiO2Eyx9k0OGfbZp0asyF3mxxJgGEk9zHJ_29jstl0P7ESUAbzHBAK-q6mB3DEQBmNzONsCwus-O41jZSqoaEJsQlARbhwBKlFVEYsWuDm26zJ59B89/s1600-h/TriangleCutting32.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsAz4JlHJf2tJdZORIkt3YbRLJCmiO2Eyx9k0OGfbZp0asyF3mxxJgGEk9zHJ_29jstl0P7ESUAbzHBAK-q6mB3DEQBmNzONsCwus-O41jZSqoaEJsQlARbhwBKlFVEYsWuDm26zJ59B89/s320/TriangleCutting32.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1RG9aDCnRMLYUjKCWX1NCMCxUMdBOkEsbaRRgwTaeFcbIwkCJndaKfnhysiVvBE3FHJZPHHuWTs7RxxZ8W4tbCGGDIDFg9G1Nduew0-CDXRRHffcN4Do4CS8aV8Z_jgn5R6TXtuUBIMAF/s1600-h/TriangleCutting33.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1RG9aDCnRMLYUjKCWX1NCMCxUMdBOkEsbaRRgwTaeFcbIwkCJndaKfnhysiVvBE3FHJZPHHuWTs7RxxZ8W4tbCGGDIDFg9G1Nduew0-CDXRRHffcN4Do4CS8aV8Z_jgn5R6TXtuUBIMAF/s320/TriangleCutting33.png" /></a></div><div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilJJwS0OS7GEov8xZYEgCkgbYuWpNeL9RJ-WN4HfXehn199uK4pP3PUD9eprKNrWJjK9ANQSk0xIFbFZpdRsrJAWzaheLQ9Waq05mNEpvl7SGoMLoYjYe9gpKis6Nrufj2bIPrO_cAQjz9/s1600-h/TriangleCutting34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilJJwS0OS7GEov8xZYEgCkgbYuWpNeL9RJ-WN4HfXehn199uK4pP3PUD9eprKNrWJjK9ANQSk0xIFbFZpdRsrJAWzaheLQ9Waq05mNEpvl7SGoMLoYjYe9gpKis6Nrufj2bIPrO_cAQjz9/s640/TriangleCutting34.png" /></a></div><div class="separator" style="clear: both; text-align: left;">In this final picture after we have cut with all the cutting lines we go through all the triangles in the world and detect which are inside the original blue triangle (those are marked blue in the above image). We should notice here that this isn't as expensive as it seems. In reality we only need to test newly created triangles or triangles which was inside the updating triangle before the splitting started. Also we don't need to perform a true triangle vs triangle test. Because no triangle will ever cross the edge of the updating triangle all we need to check is if the middle of the triangle is inside the updating triangle. Then the entire triangle is too.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">But you might have noticed that we had to do a lot of splitting that seemed unnecessary in this example splitting triangles that wasn't affected by the original blue triangle in any way. This is a correct observation. It is however not a fault within <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine </span></span></b>it's rather that we feed it the entire world as it's input data instead of just what is necessary.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">The solution is to only feed <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine </span></span></b><span style="font-size: x-small;"><span style="font-size: small;">with</span></span><b><span style="font-size: x-small;"><span style="font-size: small;"> </span></span></b><span style="font-size: x-small;"><span style="font-size: small;">edges</span></span><span style="font-size: x-small;"><span style="font-size: small;"> from triangles intersecting with the updating triangle or those that are containing it. You do need to flag which triangles are inside it so that you can update them later but you do not need to take their edges unless of course that edge is shared with a triangle that is intersecting or containing the updating triangle.</span></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><span style="font-size: small;"><br />
</span></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>void TriangleContainer::UpdateTriangle(Triangle* aUpdatingTriangle);</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>{</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> for(int cuttingLineIndex=0;cuttingLineIndex<3;cuttingLineIndex++) </i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> { <br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> std::vector<triangle*> intersectignOrContainingTriangles;</triangle*></i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetInteractingTriangles( aUpdatingTriangle,intersectignOrContainingTriangles);</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> /* This function calculates the intersecting or containing while strong contained triangles in a special array in the triangle container so that it can remove triangles that gets deleted from that queue */</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> UniqueVector<edge*> uniqueEdges;</edge*></i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>/* Any vector that can detect and avoid duplicates works fine you can even do the checks by yourself but for code simplicity I am assuming this class */ <br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> for(int i=0;i<intersectignorcontainingtriangles.size();i++)></intersectignorcontainingtriangles.size();i++)></i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> { </i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> for(int j=0;j<3;j++)</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> { </i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> uniqueEdges.AddIfUnique(intersectignOrContainingTriangles[i]-></i><i>GetEdges()[j]);</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }<br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> /* this code adds all edges from all the triangles that interacts with the updating triangle making sure that no edge is copied twice */</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> SplitEdgesWithCuttingLine(uniqueEdges,aUpdatingTriangle->GetEdges()[cuttingLineIndex]->GetFirstPoint(),UpdatingTriangle->GetEdges()[cuttingLineIndex]->GetSecondPoint());</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>// calls split edges with the cutting edge <br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>/*</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetContainedTriangles() is a function that returns a reference to the std::vector that contains all the triangles that was contained by the updating triangle and haven't been deleted so far */<br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> for(int i=0;i<getcontainedtriangles().size();i++)></getcontainedtriangles().size();i++)></i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> {</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetContainedTriangles()[i]->Update();<br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetContainedTriangles().Empty()</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>// empties the temproary contained triangle vector </i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>/*</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetNewTriangles() is a function that returns a reference to the std::vector that contains all the triangles that was created during the splitting by the updating triangle and haven't been deleted so far </i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>The way this is implemented is simple each triangles constructor is forced to take a triangle container as input and in the constructor it adds itself to it. And when it does this it also adds itself to this queue and in the triangle destructor it calls the triangle container and tells it to remove it from it. And at this time it removes it from the new triangle queue if it is in it and from the contained triangle queue if it is in it.*/<br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> for(int i=0;i<getnewtriangles().size();i++)></getnewtriangles().size();i++)></i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> {</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> if( </i></span><span style="font-size: x-small;"><i>aUpdatingTriangle->PointInsideTriangle(</i></span><span style="font-size: x-small;"><i>GetNewTriangles()[i]->GetCenter()</i></span><span style="font-size: x-small;"><i>)==true)</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> { <br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetNewTriangles()[i]->Update();</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }<br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> }</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i> GetNewTriangles().Empty()</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><i>// empties the temporary new Triangles vector <br />
</i></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;"><span style="font-size: small;"><span style="font-size: x-small;"><i>}</i></span></span></span></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: small;">That was it, actually there isn't more to it and we even discussed some of the optimizations that we use that complicate things a bit. if the two vectors in the triangle container confuses you then you can just test all the triangles after the cutting lines lope has finished and update those that are inside the updating triangle. As a last step of this article I will walk you through the optimized updating process and during the next article we will look at how we can use this system to do really cool stuff.<br />
</span></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR7-M95o80rQ__X4-_-O8VjUgrlqcWFqjBHQSjzZolxEEKf_G7PYwGXu_7UsqPmciazP3XAvw519tpOg7l21x81GLq7ZoSGnBReaSsPmWScrzDQgagI10HgLc-3zEsPX6gY4IJjAuxGCce/s1600-h/TriangleCuttingOptimized01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR7-M95o80rQ__X4-_-O8VjUgrlqcWFqjBHQSjzZolxEEKf_G7PYwGXu_7UsqPmciazP3XAvw519tpOg7l21x81GLq7ZoSGnBReaSsPmWScrzDQgagI10HgLc-3zEsPX6gY4IJjAuxGCce/s320/TriangleCuttingOptimized01.png" /></a> Here we go with the default case again not much to say about it.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRbpwtRjTaCKDD0agsLJZK8wE5ZXQXLQ3ldoLuTegzoqCwxDDNF7KtYktshZipXA3Fq7QcfknDcaOgzvRQ6z6yPh9SMBAEDVS_7QRUHFL82n9C4NccXl94uw7t3Y0TjXQok8iomdE0SIUz/s1600-h/TriangleCuttingOptimized02.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRbpwtRjTaCKDD0agsLJZK8wE5ZXQXLQ3ldoLuTegzoqCwxDDNF7KtYktshZipXA3Fq7QcfknDcaOgzvRQ6z6yPh9SMBAEDVS_7QRUHFL82n9C4NccXl94uw7t3Y0TjXQok8iomdE0SIUz/s320/TriangleCuttingOptimized02.png" /></a>Here you can see a beige color on the rightmost triangle. We use this beige color to show which triangles are interacting (intersecting, containing or being contained by counts as interacting). So that you can clearly see what each step of the algorithm does. This is the result of the call to <span style="font-size: small;"><b><i>GetInteractingTriangles</i></b></span>.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJJ3xgPLsNsW8gN2IK216hoRm85fd3uBul9fte8jIEHr6DJ8tXlqJaXkQ2zir3eXukmtxsN6bTxQK800wgpDhKZRJ3V9DPaCSDwTY2GD7nxcmLMGRnJ_Kq1eJ5VqJbE8ljmpij1drWPbiB/s1600-h/TriangleCuttingOptimized03.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJJ3xgPLsNsW8gN2IK216hoRm85fd3uBul9fte8jIEHr6DJ8tXlqJaXkQ2zir3eXukmtxsN6bTxQK800wgpDhKZRJ3V9DPaCSDwTY2GD7nxcmLMGRnJ_Kq1eJ5VqJbE8ljmpij1drWPbiB/s320/TriangleCuttingOptimized03.png" /></a> After we had gotten the interacting triangles we collected all the edges from them until we had a list of edges from those triangles where every edge only exists once. We use the pink thick line coloring to show lines that are being sent to <b><span style="font-size: x-small;"><span style="font-size: small;">SplitEdgesWithCuttingLine</span></span></b>.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUsuk9_YoJ8YiA05VbsScquvj-paUjQ8TWmnm1wc0QcyL1s27MAoQPQMlq6YE4ewIXPRwSPVYeazw9d3DoPtK57-9pU_7W4VI1W1oACZKFWEy96b7U7d0HENzewSaST7eFYcn8e9eqHyH6/s1600-h/TriangleCuttingOptimized04.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUsuk9_YoJ8YiA05VbsScquvj-paUjQ8TWmnm1wc0QcyL1s27MAoQPQMlq6YE4ewIXPRwSPVYeazw9d3DoPtK57-9pU_7W4VI1W1oACZKFWEy96b7U7d0HENzewSaST7eFYcn8e9eqHyH6/s320/TriangleCuttingOptimized04.png" /></a> And here is the first cutting line again. I have decided to try to match the length of the cutting line visually here to the edges it actually intersect, this doesn't change the fact that when making the intersection it is treated as infinite all we have changed is which edges we send a input to<b><span style="font-size: x-small;"><span style="font-size: small;"> SplitEdgesWithCuttingLine.</span></span></b></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEH2UbYlIMzFmrwPpPi2DwnYC_c0ZnV9enMRRaVzhwfH6a64K_Cp7yMimXOeryxJJ4WmIS4hRf0-cadMxtiQqjxReV90ZLNFwlt-To8w7b28JL5_1p9_gYxmwlR2NylYO6uc-hVn0Zpc2S/s1600-h/TriangleCuttingOptimized05.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEH2UbYlIMzFmrwPpPi2DwnYC_c0ZnV9enMRRaVzhwfH6a64K_Cp7yMimXOeryxJJ4WmIS4hRf0-cadMxtiQqjxReV90ZLNFwlt-To8w7b28JL5_1p9_gYxmwlR2NylYO6uc-hVn0Zpc2S/s320/TriangleCuttingOptimized05.png" /></a><b><span style="font-size: x-small;"><span style="font-size: small;"> </span></span></b>We still use the color coding of light green for triangles that are going to be split. Worth of notice here is that the lower left triangle is going to be split despite not interacting with the blue triangle. This is because it shares an edge with a triangle that needs to be split. If we didn't split that triangle we would end up with a triangle with 4 edges and those even though it may look like a triangle visually it would no longer be one and that would cause all sorts of problems in the long run.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5XzzNx4JY1Jkh16V3BIGiFU9QN0TyOutnFHMd1Iim_tfWDgCKldEhg0W9OpNLrZWPcUXrV_o59S5rqQo2jYrPfI2wmP7zmJh9XJhw6rW5CIWJhlwTP7e3aMYjB1wIFCynig60wiWFYVr-/s1600-h/TriangleCuttingOptimized06.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5XzzNx4JY1Jkh16V3BIGiFU9QN0TyOutnFHMd1Iim_tfWDgCKldEhg0W9OpNLrZWPcUXrV_o59S5rqQo2jYrPfI2wmP7zmJh9XJhw6rW5CIWJhlwTP7e3aMYjB1wIFCynig60wiWFYVr-/s320/TriangleCuttingOptimized06.png" /></a>And we perform the splits just as usual and we color the new triangles light blue. So far the only difference is that we have an ugly pink color here too.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizNQgfCRhSpY15JrTsn3povODDirn7dBbl3HkZlPHlYDfmTYdzXCYWTzTtzsutur-spjbtm22ituIxF7792a52YWUQO_pf2Rbrpi5M6JLaX7EG6_VFs7OD0SHXaEFIlSVpXw3kvRSH9vq_/s1600-h/TriangleCuttingOptimized07.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizNQgfCRhSpY15JrTsn3povODDirn7dBbl3HkZlPHlYDfmTYdzXCYWTzTtzsutur-spjbtm22ituIxF7792a52YWUQO_pf2Rbrpi5M6JLaX7EG6_VFs7OD0SHXaEFIlSVpXw3kvRSH9vq_/s320/TriangleCuttingOptimized07.png" /></a>Moving on to your second and final split for this cutting edge, this is one less than the last time thanks to detecting which edges are part of interacting triangles and which aren't we can skip unnecessary splits.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9c4pY7xCp4NpAo96IXejZtupd50wVjfcL3bTpXVYYLvgAss52SJBYq3LXOSfh7vdFwD9vtoybIVrqYoNdjkqLwPj0rBnbFHj3QQ-2JCmMPaz8S-B1XMq5Q8F6Q9VFlnh2nTlB6t_VHgSI/s1600-h/TriangleCuttingOptimized08.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9c4pY7xCp4NpAo96IXejZtupd50wVjfcL3bTpXVYYLvgAss52SJBYq3LXOSfh7vdFwD9vtoybIVrqYoNdjkqLwPj0rBnbFHj3QQ-2JCmMPaz8S-B1XMq5Q8F6Q9VFlnh2nTlB6t_VHgSI/s320/TriangleCuttingOptimized08.png" /></a>And we split it. This should be very familiar by now.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0OmPHc5MTAWS6jDNby8rxWzG3_Jy7wE4apCXXqxJn-mS6wSAXpPi71vcvVhfbLMGJ4GpwsDTa6JHmpbb0d5h3h6VHJ8l5Q62odGh6FAbntUNnPXdN8sRwWXXNSMLn4_d5HoeGsiPwQF81/s1600-h/TriangleCuttingOptimized09.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0OmPHc5MTAWS6jDNby8rxWzG3_Jy7wE4apCXXqxJn-mS6wSAXpPi71vcvVhfbLMGJ4GpwsDTa6JHmpbb0d5h3h6VHJ8l5Q62odGh6FAbntUNnPXdN8sRwWXXNSMLn4_d5HoeGsiPwQF81/s320/TriangleCuttingOptimized09.png" /></a></div>We move onto the second cutting line. So again we detect which triangles are interacting with ours. Please note that due to floating point precision problems the lower triangle on the right side may also be selected as an interacting triangle this is perfectly normal as its edge is parallel to one of the blue triangles. But for this case we are going to assume our intersection tests works exactly as we want them to.<br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvF5cTOiFAlXb4wndOHjeOlHeklNl1WwNwL7_47I__XzUFSmetGf-QZCeQ3Kn3YmMLv0m9ddWr9JhJlOL9GN9dckxegr7qPBcHwUn1zp3GF5x9JjCG6l1RL1HdGojZEbSi9P1_6zPpeVio/s1600-h/TriangleCuttingOptimized10.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvF5cTOiFAlXb4wndOHjeOlHeklNl1WwNwL7_47I__XzUFSmetGf-QZCeQ3Kn3YmMLv0m9ddWr9JhJlOL9GN9dckxegr7qPBcHwUn1zp3GF5x9JjCG6l1RL1HdGojZEbSi9P1_6zPpeVio/s320/TriangleCuttingOptimized10.png" /></a> And here is the list of edges being connected to the edges of the triangles that was interacting with the blue triangle.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLhR9f8beVpMuM2N6yVTfapHkUC-6OaGS3K2ELN014o6ugEMON8LCGJHDar3LjJXpyNNbudv-wuAjyGEr_Kg1xCG0UvQ4Q9CiNLZ726Qpzgt9OzwxFsL21CO3j-RWsdawyQFZLpbHxOSOP/s1600-h/TriangleCuttingOptimized11.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLhR9f8beVpMuM2N6yVTfapHkUC-6OaGS3K2ELN014o6ugEMON8LCGJHDar3LjJXpyNNbudv-wuAjyGEr_Kg1xCG0UvQ4Q9CiNLZ726Qpzgt9OzwxFsL21CO3j-RWsdawyQFZLpbHxOSOP/s320/TriangleCuttingOptimized11.png" /></a> And we run through the process using the next cutting line. It feels kind of like magic that if it works for a single line then it will work for any triangle and if it works for any triangle it will work for any mesh consisting of triangles (we will talk about optimizations for those in the next article)</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkequWYGqedSKCa6H4ZxM3p2Ygjr7fKf0K9lKjAxCSMfoj8og5gpc79kejPQ1I-x9pO5L8z7xP1fHHm8Ld8urvmbFPX7aOpmfgngzUmSRBO5ObsSL78da8mC6uQA-5hegUOYLyNpcle219/s1600-h/TriangleCuttingOptimized12.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkequWYGqedSKCa6H4ZxM3p2Ygjr7fKf0K9lKjAxCSMfoj8og5gpc79kejPQ1I-x9pO5L8z7xP1fHHm8Ld8urvmbFPX7aOpmfgngzUmSRBO5ObsSL78da8mC6uQA-5hegUOYLyNpcle219/s320/TriangleCuttingOptimized12.png" /></a>Seems the light blue color has changed slightly here but it still means the same thing new triangles after the split.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5vAgTKbc9CC-Df6JraeO_Y27TrLRss0TJn2m_9pWNfR9ZzSxFpUxRbwDpGJIDyW5JPaMspM7-rg7GFWwvBraVtYbYWsMrGX-7FxWqGGLd7fB-2qOuBSSVtVUEV4rrO0lv7R7OmXq4OtCc/s1600-h/TriangleCuttingOptimized13.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5vAgTKbc9CC-Df6JraeO_Y27TrLRss0TJn2m_9pWNfR9ZzSxFpUxRbwDpGJIDyW5JPaMspM7-rg7GFWwvBraVtYbYWsMrGX-7FxWqGGLd7fB-2qOuBSSVtVUEV4rrO0lv7R7OmXq4OtCc/s320/TriangleCuttingOptimized13.png" /></a>And the splitting keeps running it's natural course.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2mNPt3zaB_dL3PvaqSKM0f8uewJhETEl3zyzAAdRLQ9VEzBGBmM6OGzPUFLkT0tfQw28eRXhcOwebXhfeDBIBsSb63n24Qjdcvl87yaSKtN3XMvIkprUPHIJaUSZ_Q7mndMqUwzS32Fz1/s1600-h/TriangleCuttingOptimized14.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2mNPt3zaB_dL3PvaqSKM0f8uewJhETEl3zyzAAdRLQ9VEzBGBmM6OGzPUFLkT0tfQw28eRXhcOwebXhfeDBIBsSb63n24Qjdcvl87yaSKtN3XMvIkprUPHIJaUSZ_Q7mndMqUwzS32Fz1/s320/TriangleCuttingOptimized14.png" /></a>The correct blue color seems to be back again.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_AF0WMTyDlWBQv4GSV7UF7vYri0mOVrEm3iy5mWHWlkf7IqNIAC1PxK_w6k5hriqoOjWNkxHXhzInPm4Qm2v98TQqZlBWLFvISQ5xzoYo7eIlevlGqGE4vwFVEhjeZZDb3pQLR2fzp2Gm/s1600-h/TriangleCuttingOptimized15.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_AF0WMTyDlWBQv4GSV7UF7vYri0mOVrEm3iy5mWHWlkf7IqNIAC1PxK_w6k5hriqoOjWNkxHXhzInPm4Qm2v98TQqZlBWLFvISQ5xzoYo7eIlevlGqGE4vwFVEhjeZZDb3pQLR2fzp2Gm/s320/TriangleCuttingOptimized15.png" /></a>And we're going for the final split. This time we saved two splits compared to if we had to split all the edges but we also saved extra on some triangles not even being generated on the last pass.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0_gdpKu-b6VPmXffB_1w1F6puqD1vyi4c-7tuSiauOVWYyRYHyQVZDvsEQ4Tx8PXfU_ftQzww_BbkD2NkcxX7jWv6KtoHEtE0l20N-kIsRsvXXUY1CA3_b3ePQsZJ5UPM2WPyvfAqS4I/s1600-h/TriangleCuttingOptimized16.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0_gdpKu-b6VPmXffB_1w1F6puqD1vyi4c-7tuSiauOVWYyRYHyQVZDvsEQ4Tx8PXfU_ftQzww_BbkD2NkcxX7jWv6KtoHEtE0l20N-kIsRsvXXUY1CA3_b3ePQsZJ5UPM2WPyvfAqS4I/s320/TriangleCuttingOptimized16.png" /></a>And the second cutting line is a wrap so time to repeat the process one last time for the third line.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdi1ZPYriT38EuLZrp8Q46WnzaZJ77cBMSRQfyYjcwfwZ1BvjpTa2HkjzLCFqbkHoqxsgnTChu4u3E5_uJYrZHS_MPdaBiIwq4qZtBJ3kDbwu6NHCkpdCHbrk6R6UwV-o-THcKSufXyZRd/s1600-h/TriangleCuttingOptimized17.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdi1ZPYriT38EuLZrp8Q46WnzaZJ77cBMSRQfyYjcwfwZ1BvjpTa2HkjzLCFqbkHoqxsgnTChu4u3E5_uJYrZHS_MPdaBiIwq4qZtBJ3kDbwu6NHCkpdCHbrk6R6UwV-o-THcKSufXyZRd/s320/TriangleCuttingOptimized17.png" /></a>We detect the triangles interacting with our blue triangle. Once again assuming the intersection detection code does exactly what we want it to.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1VzwfMzonPL6oeQllPCT-ByZFgBx8O5zSEZsf1oLOjwEoEXKmmNilnyN5nvo0LgCWB9rIuJ-aYM86gGZ2LHd-oQiszDup1JwaMyjDctmqWvaBVKgRtlcX8sy4sLukJGD7lTWzZvY7CV9s/s1600-h/TriangleCuttingOptimized18.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1VzwfMzonPL6oeQllPCT-ByZFgBx8O5zSEZsf1oLOjwEoEXKmmNilnyN5nvo0LgCWB9rIuJ-aYM86gGZ2LHd-oQiszDup1JwaMyjDctmqWvaBVKgRtlcX8sy4sLukJGD7lTWzZvY7CV9s/s320/TriangleCuttingOptimized18.png" /></a>And here we have the Edges belonging to the interacting triangles. It should be clear that the optimization works better as there are more triangles in the world because then there are more triangles that can be rejected.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFPtXiw-OyBg96jqPLHkeQV-Dm1qem3jQXL-t6HgUTCGdNAapLRiQcpkhw14eo3dURQEoFiZHAODPckxRPIKo40-SgdtS2FGLhIF09Coa06kpXOOfpZFx1UTP67D7ZRCGGrFAQ-EiffB5p/s1600-h/TriangleCuttingOptimized19.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFPtXiw-OyBg96jqPLHkeQV-Dm1qem3jQXL-t6HgUTCGdNAapLRiQcpkhw14eo3dURQEoFiZHAODPckxRPIKo40-SgdtS2FGLhIF09Coa06kpXOOfpZFx1UTP67D7ZRCGGrFAQ-EiffB5p/s320/TriangleCuttingOptimized19.png" /></a>Once again triangles outside of the interacting area is affected because of a shared edge but please observe that it can't spread furtehr away cause the split on those triangles doesn't affects any of its edges except the split one.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPM_UR_kQ4BaWunaLJpMKQWGLSwc_cPVa7bel7PzhP3GJutahSV9rl_YLxPsEP-QiGJtVWDylgeYnHfE5pqfpW2nBxOTQUM9fEkHWpRIUUU6kxSNXiPbmCWwKy2Pl_jfEU18BI3i3Y377i/s1600-h/TriangleCuttingOptimized20.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPM_UR_kQ4BaWunaLJpMKQWGLSwc_cPVa7bel7PzhP3GJutahSV9rl_YLxPsEP-QiGJtVWDylgeYnHfE5pqfpW2nBxOTQUM9fEkHWpRIUUU6kxSNXiPbmCWwKy2Pl_jfEU18BI3i3Y377i/s320/TriangleCuttingOptimized20.png" /></a></div><br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFDmWZv4i099ecDhQs7hZ8GKHRzTpKkNC4YuSrmIrVLNH6YlAOK_S6nxeoRbs2onQ4bSzOAsR0KLCiWF6s2mwfHWSLhWht-dP6fHlr41iOJpjm0j8sYj5sN39jiXtlcQPK3Os7jNDJrsEn/s1600-h/TriangleCuttingOptimized21.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFDmWZv4i099ecDhQs7hZ8GKHRzTpKkNC4YuSrmIrVLNH6YlAOK_S6nxeoRbs2onQ4bSzOAsR0KLCiWF6s2mwfHWSLhWht-dP6fHlr41iOJpjm0j8sYj5sN39jiXtlcQPK3Os7jNDJrsEn/s320/TriangleCuttingOptimized21.png" /></a> I really have nothing to add this is just repeating the process once more.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhINskxWUuF0Mx29MEcBY4kCTAtOTwsBsBzGVk_dJ1EJMSQq1HH5jknVecZV2CY-BCyfx17vao8zUJKK07IULwkX88bpb7FWwW4277AyjaVFZvVP4nUWZoERp7ufZ7FbSFDTQJJNxyD7QgQ/s1600-h/TriangleCuttingOptimized22.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhINskxWUuF0Mx29MEcBY4kCTAtOTwsBsBzGVk_dJ1EJMSQq1HH5jknVecZV2CY-BCyfx17vao8zUJKK07IULwkX88bpb7FWwW4277AyjaVFZvVP4nUWZoERp7ufZ7FbSFDTQJJNxyD7QgQ/s320/TriangleCuttingOptimized22.png" /></a></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpVED5IPJIjBkV7rzwaRwlCEDszNv03V6dRUH9omN6Vp4g98utwYQ3KcTX4JpC7iazZOViaeL65Zh1tGyDuia2pUszaWblJ8-GWdt1AWd1n26q0NeAA5SnujZv6J-Hl9h7Qv8DlqIQq2O8/s1600-h/TriangleCuttingOptimized23.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpVED5IPJIjBkV7rzwaRwlCEDszNv03V6dRUH9omN6Vp4g98utwYQ3KcTX4JpC7iazZOViaeL65Zh1tGyDuia2pUszaWblJ8-GWdt1AWd1n26q0NeAA5SnujZv6J-Hl9h7Qv8DlqIQq2O8/s320/TriangleCuttingOptimized23.png" /></a> It might be tough seeing the triangles coloring here as they are getting smaller but hopefully you know enough about how it works by now to follow along.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNXXa5waECpmKbO8lDm4YJ0uOlmU2CE03hGYT-XSkfO_yrkVSn-URlI354I0dfHBiM6EJHYv0LFMdfoEzOfwKlTDznKoaj6PKzqRbzRr0RUFSA2oVRnCkyglz7G2mKJ8joU8luzWy7GP9/s1600-h/TriangleCuttingOptimized24.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNXXa5waECpmKbO8lDm4YJ0uOlmU2CE03hGYT-XSkfO_yrkVSn-URlI354I0dfHBiM6EJHYv0LFMdfoEzOfwKlTDznKoaj6PKzqRbzRr0RUFSA2oVRnCkyglz7G2mKJ8joU8luzWy7GP9/s320/TriangleCuttingOptimized24.png" /></a></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcIUXDqIcRV6W2Hni402xfWMduHYi-ub9x0ykyqAKkathpgP5kzhhlGjsJQ_YAcMVm1nI4pFnvkitbEgmjTjLQWAooAhcZ0z57Ox64_YiPSu_UpALo4KfYVUotde_YIubWc6Z0YIJ-H7lv/s1600-h/TriangleCuttingOptimized25.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcIUXDqIcRV6W2Hni402xfWMduHYi-ub9x0ykyqAKkathpgP5kzhhlGjsJQ_YAcMVm1nI4pFnvkitbEgmjTjLQWAooAhcZ0z57Ox64_YiPSu_UpALo4KfYVUotde_YIubWc6Z0YIJ-H7lv/s320/TriangleCuttingOptimized25.png" /></a></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO8PzTShnZSzPx1ZCVOWNzxuvX74E1YeOQp3d6ikWNaHEqGXWlsOK-1RXzE6vkB7wMeR79SlS5Nes1Um7dQk6czrVEgCPAGRdm__FsDRdDdx1Pbk3vfYMKbFNwhbPGH4X5ZuGDa1ptnx5k/s1600-h/TriangleCuttingOptimized26.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO8PzTShnZSzPx1ZCVOWNzxuvX74E1YeOQp3d6ikWNaHEqGXWlsOK-1RXzE6vkB7wMeR79SlS5Nes1Um7dQk6czrVEgCPAGRdm__FsDRdDdx1Pbk3vfYMKbFNwhbPGH4X5ZuGDa1ptnx5k/s320/TriangleCuttingOptimized26.png" /></a> And it's a wrap no more splits are necessary.</div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4B1pvy91OSCzpUoY8bZbHmqfRPMtHL_17veEa5rufNeoWPMD8hPW5HYYOXe91Kiu7HjKPykAI0_G7-PAeZAIJ0a8kXC8uI4UkHCAccR_e9w-YFOBMUUKsbF4p8xX9PrR2zQIguSeaV9tJ/s1600-h/TriangleCuttingOptimized27.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4B1pvy91OSCzpUoY8bZbHmqfRPMtHL_17veEa5rufNeoWPMD8hPW5HYYOXe91Kiu7HjKPykAI0_G7-PAeZAIJ0a8kXC8uI4UkHCAccR_e9w-YFOBMUUKsbF4p8xX9PrR2zQIguSeaV9tJ/s320/TriangleCuttingOptimized27.png" /></a> And here we can see the results of the tests for which triangles are inside the updating triangle. I went on quite a bit about updating and why I called it that. Why couldn't I just say that I was setting the blocked flag on triangle?</div><div class="separator" style="clear: both; text-align: left;">The reason is that we can do a lot more cool stuff with updating but that will have to wait to the next article together with some optimization stuff because by now this is the monster post from hell. I have one last thing to think about before we can sign this off however and you can start implementing it.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">It's about how to handle intersections on a point of an edge or really close to them. If you do intersect exactly at one of the points of an edge it means you already have an edge following the cutting line there and you don't need to perform any splits. In fact I wouldn't perform any splits if i was within 0.01 meters of the point of an edge (I am assuming you work in a metric system because anything else wouldn't make much sense) This is because I have no use for triangles smaller than 1 cm no one will notice the difference in the path finding anyway and this helps me from getting swamped with invisible triangles that drags down performance. And of course if I am having any problems with numerical stability in parallel lien intersection tests etc this will make them go away. It's the only magical epsilon value I would allow in my system and I can allow it because the system works without it. It's not essential just an optimization.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">That's all for this time the rest will come in part II which should be smaller than this monster post.</div><div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
</div></div>Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com1tag:blogger.com,1999:blog-461014343580624539.post-65083433505402827622010-03-01T14:00:00.002+01:002010-03-01T14:00:13.535+01:00Lighting models for games IIILast time we had just made the move to per pixel lighting which made a huge visual difference without changing our formulas even the tiniest bit. You might wonder why I didn't start using it and it's a valid question as the per pixel vs. per normal really doesn't matter for the lighting model. The reason I did so was to shoe the huge difference of applying high quality content can do to the look of an object. No matter how much work we do with our lighting models and formulas it doesn't matter unless we have a skilled artist by our side.<br />
<br />
<br />
<a name='more'></a><br />
Last time we had just made the move to per pixel lighting which made a huge visual difference without changing our formulas even the tiniest bit. You might wonder why I didn't start using it and it's a valid question as the per pixel vs. per normal really doesn't matter for the lighting model. The reason I did so was to shoe the huge difference of applying high quality content can do to the look of an object. No matter how much work we do with our lighting models and formulas it doesn't matter unless we have a skilled artist by our side.<br />
<br />
So we start with looking at what we had the last time.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3LFlzley1OLpzc9Quxr4r7gfmXQ1uoOBtoOekHHPG6GYzZKcJ6Cmcprvg_hXutL032ejKf-15YM5P_g3ah7CAfRlEie5M8Ycq-rMSu5FOjOgM1qtIY_-gB6Ns7dG8mwo7ZdeaIjhpxoz8/s1600-h/normalMapping_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3LFlzley1OLpzc9Quxr4r7gfmXQ1uoOBtoOekHHPG6GYzZKcJ6Cmcprvg_hXutL032ejKf-15YM5P_g3ah7CAfRlEie5M8Ycq-rMSu5FOjOgM1qtIY_-gB6Ns7dG8mwo7ZdeaIjhpxoz8/s640/normalMapping_Logo.png" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>That specular is still looking awfully plastic but we remember that we didn't had a material functions for the specular yet. The one thing we want to control is how shiny the specular gets on different parts of the surface for example pieces of iron in the rock should have a bright specular while dirt shouldn't when in doubted simply look around and trust your eyes. However it is the artist’s eyes that should spot this difference all we need to do is to give them a way to control it. So in the same way as we use a colormap to control the diffuse light and the ambient light we will have a specular map to control the specular light. But this one can be a simple gray scale telling how much specular should be applied at that point with 1 as its greatest value for full spec and 0 as its lowest for no spec.<br />
<br />
So taking our final code from last time<br />
<br />
<br />
<i>specucularStrength=</i><i>pow(reflectionDifference,SurfaceConstant)</i><br />
<i>pixelColor+=lightSource.LightIntensity*</i><i>specucularStrength;</i><br />
<br />
And then just add our specular map and we end up with this <br />
<br />
<i></i><br />
<i>pixelColor+=lightSource.LightIntensity*</i><i>pow(reflectionDifference,SurfaceConstant)*specularMap</i><i>;</i><br />
<br />
And here it is in action<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFUgDhh2lw5Qoksoy7DmMAs_Z8E362Y27wjnX_wxD4GjEyGaBjrBSb6b9N1Fl15ZowTpUACAwGboGZ4agbxwD4Gb22gCh3aTWrIHosWD7CpCmpTVlE0Z-oSBwQGxphkkDt6a3mhwrgihkv/s1600-h/spec_map_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFUgDhh2lw5Qoksoy7DmMAs_Z8E362Y27wjnX_wxD4GjEyGaBjrBSb6b9N1Fl15ZowTpUACAwGboGZ4agbxwD4Gb22gCh3aTWrIHosWD7CpCmpTVlE0Z-oSBwQGxphkkDt6a3mhwrgihkv/s640/spec_map_Logo.jpg" /></a></div><i><br />
</i><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
This final step was quite simple and we are left with an impressive looking image this can be considered the base line in lighting using a normal map and a spec map. But looking at it doesn’t the shadow side looks just as ugly as in out first image with ambient light. Well it does because it is the same all formulas we have been working on so far only affects the surfaces facing towards the light to some degree so it's time to get back to our ambient lighting model.<br />
<br />
Do you remember that I wrote that ambient light was sometimes occluded because of the access of rays being blocked by other surfaces, If you are unsure go back to the first part and look at the image of the ball without direct lighting. This effect of less rays of ambient light hitting a point because of other surfaces being in the way is called ambient occlusion. And this has the power to create forms in the ambient side because deep crevices etc won’t receive much ambient light because there is just a really small hole for the light to go into. For how you are going to calculate this ambient occlusion there are a lot of different methods with varying speed and accuracies. The basic idea is that you throw around some rays from the point and see how many are blocked. But if you decide to calculate this in real time or try to precalculate it doesn't matter for this test. Remember that ambient light is supposed to be directionless so therefore the ambient occlusion of an object upon itself can be precalculated.<br />
<br />
However when you place objects in an environment the different objects in the environment will start occluding each other and the ambient occlusion will be based upon where in the world the object is standing and therefore can't be precalculated (though you can make some pretty good cheats, but then again games like crysis nowadays calculates the ambient occlusion in real time). This however won't matter for our discussing but our ambient occlusion is as usual stored as a map. And we simply add this to our ambient lighting equation so it looks like this.<br />
<div class="separator" style="clear: both; text-align: left;"><i>pixelColor+=</i><i>Ambient.LightIntensity*ambientOcclusionMap*colorMap</i><i>;</i></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">Let's take a look at how one of these maps looks.</div><div class="separator" style="clear: both; text-align: left;"><i> <br />
</i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2T36atCQhqpvtMaYfv-x5zlpSuQp7rVXtVvcQIBqggb8FH1oeK0J0bL2D_FRrFiepMTSxzHE0MyAtiA70h6CFYeJgYoL6Yj9hRZGH80WXeyd6PIuKZYgXvfmrTaueurFVoBQAUSGWHpxw/s1600-h/ambientOcclusion_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2T36atCQhqpvtMaYfv-x5zlpSuQp7rVXtVvcQIBqggb8FH1oeK0J0bL2D_FRrFiepMTSxzHE0MyAtiA70h6CFYeJgYoL6Yj9hRZGH80WXeyd6PIuKZYgXvfmrTaueurFVoBQAUSGWHpxw/s640/ambientOcclusion_Logo.jpg" /></a></div> <br />
The brighter the map the more ambient light hits it. The darker it is the less light hits it. You can see at the top of the light house how the windows edges and the top blocks of the ambient light leaving dark edges. We can also clearly see how the different bulges and indents in the base shows up clearly. With this we would get a lot more life in the dark side as we would still see shapes and we will even get improvements in the light side as ambient now will behave more correctly and leave darker areas in corners for example.<br />
<br />
So it is now time to re-examine one of our assumptions. We have assumed that ambient light is directionless because it represents the light being bounced from all surfaces in all directions. And this is true however the light that comes from the different directions will in reality have different colours because of the different materials it has bounced off. This means that there should be some sort of colour difference between different facings even in the area that is only hit by ambient light. For obvious reasons we can't calculate this for real so we have to select a fake method for it. How you fake it is a lot depending on your type of game. If you are in an outdoor world on a grassy landscape the reflected light from below will be tinted slightly green and the light from above having bounced at clouds etc will have a slightly blue tint. However by now we are cheating so much that I can't say that what we are doing is based in physics. The idea of what we want to simulate is sound and based on physics however the methods are not. So for simplicity we are going to use an environmental map for selecting the colours of our ambient light. it is nice because the artist have total control and you can blend between different maps as you move from an area to an area but it is in no way the best method but for a single object like ours it gives a god enough approximation. This map should have a pretty similar lightness value over most of it and also only contain subtle colour variations. But in the end it is down to the artists.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR6owTPMRvJObN6AI6sAY2uKL87TFQuw2ESrqCCdpP3HUD9-Z6y8Dv1BLfvOsSRRoY19Lrleby3XyunjS8xELqMZ3wdsx8wHroY9NRwd2IUyvEfTjmW64NrH-rhpC9VB2rfOUJfeDSXrn4/s1600-h/ENVAMB_AMBOCC_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR6owTPMRvJObN6AI6sAY2uKL87TFQuw2ESrqCCdpP3HUD9-Z6y8Dv1BLfvOsSRRoY19Lrleby3XyunjS8xELqMZ3wdsx8wHroY9NRwd2IUyvEfTjmW64NrH-rhpC9VB2rfOUJfeDSXrn4/s640/ENVAMB_AMBOCC_Logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>The differences towards the version that only used the ambient occlusion is subtle but also clear it is obviously darker but you also detect subtle colour differences depending on facing that gives us something that starts to look as a decent lighting model already on its own without all the other stuff.<br />
<br />
So we once again has a new formula for our ambient light<br />
<br />
<div class="separator" style="clear: both; text-align: left;"><i>pixelColor+=</i><i>AmbientEnviromentalMap*ambientOcclusionMap*colorMap</i><i>;</i></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div> You sample from the ambient environmental map by using the world space normals of the object. This should be done with the per pixel normals as a matter of choice because they are more accurate. To be honest on this map it doesn't make that much of a visual difference because the effect is so subtle but it is a good idea to get in the habit of doing everything per pixel because the artists will add a lot of detail in those maps and anything that aren't using them will help to flatten the entire surface visually imagine if a surface looks like it has tones of detail and then suddenly there is a reflection that doesn't follow the details, this kind of things can kill your looks really quickly if you are going for high fidelity looks. And if you aren't well then you can obvious fake everything but you still need to know how it should work because hardware is just getting faster and faster and someday you want to do it for real.<br />
<br />
But now I am going to stick this lighting on our object and see what happens when we add in the colormap.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4fyMWMHDANP3H09pOvZVveF12nwRefkY4qk2ER22lKhqNde-V5cQTonATf4gyqsT7GhhkHtR9cdAdV_5UYX4sR3r-BDiHBM3yMWZ5hLCgZuxPWA_oZCALG41Wpb8uRTN3MtSALb4rBMoC/s1600-h/AO_AE_TEX_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4fyMWMHDANP3H09pOvZVveF12nwRefkY4qk2ER22lKhqNde-V5cQTonATf4gyqsT7GhhkHtR9cdAdV_5UYX4sR3r-BDiHBM3yMWZ5hLCgZuxPWA_oZCALG41Wpb8uRTN3MtSALb4rBMoC/s640/AO_AE_TEX_Logo.jpg" /></a></div><div class="separator" style="clear: both; text-align: left;"> </div>As you can see the final contribution of the effect might feel subtle and you might even wonder if it is all worth it the lighting looked much better and sharper before we applied the colormap but now it looks kind of bland. And well selecting how strong ambient light you have in the world is a tough choice but it is also the artist’s choice and not yours. And while this might look subtle it makes big difference when everything is combined together for the final step. So before we move onto that let’s combine this with our normal mapped and spec mapped object to see how it looks.<br />
<br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyXk0wdt3mCKbEo1JWSQKXYyUaNtfoEEzL4bNDWYvsDengvmDDDRihx40vGSuZIYSCOUyE15hWQZLFcbr1Cw5tGKh3p_e-WrYOV1oyBGnTZGW5fB1smlP1ATGt7s9owXgi5dtvAdjVTRMc/s1600-h/All_No_Reflect_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyXk0wdt3mCKbEo1JWSQKXYyUaNtfoEEzL4bNDWYvsDengvmDDDRihx40vGSuZIYSCOUyE15hWQZLFcbr1Cw5tGKh3p_e-WrYOV1oyBGnTZGW5fB1smlP1ATGt7s9owXgi5dtvAdjVTRMc/s640/All_No_Reflect_Logo.jpg" /></a></div><br />
<br />
I think you will agree that those subtle effects make a huge difference now when the rest of the light is applied. The parts that was formless and boring earlier now suddenly has form and shape and feels real even though our ambient is very weak. And even the parts that were lighted by our diffuse and specular light feels like they have more depth and detail than earlier. So I believe we can say that we have improved greatly on the base model we started this post with. But we also have the last part of our series of components left we still have to add reflections.<br />
<br />
During this last step I will gladly admit I am exaggerating the effect greatly when doing the visuals. This is to make sure that it is visible in these static and compress low res images. In actual running code the effect can be toned down a lot and still add a lot of life to the image. I might release a video compendium to this lecture series showing the different effects life however compression artifacts makes that questionable as some of our effects just disappears unless exaggerated.<br />
<br />
I'll just reiterate the basics in case someone has forgotten since the first article. The brightness of the reflection has nothing to do with the amount of light that the object it is being in reflected is under. Instead it is only affected about the lighting conditions on the reflected object (In the real world all light is just bounced photons no matter if they are reflections or not of course but for our in game model we try to simplify things). This means that the reflections will be most clearly visible in the shadow side of the object because it is a larger percentage of the light hitting that area than it is in the lit side.<br />
<br />
This is exactly what we need as the dark side still looks dead (this is much more visible in motion but not in YouTube quality motion so you just have to trust me) but it will also cause slight effects to add more complexity to the looks of the shading in the bright side. And of course it will allow us to simulate highly reflective objects.<br />
<br />
Since we can't simulate reflections perfectly, (well we could in certain circumstances like a limited number of reflecting items or as a special case effect for mirrors water etc, but for this we are still talking about a general lighting equation that we can use pretty much on anything.) we just want to try to get a visual change in the surfaces that says we are reflecting something for true reflective objects obviously this something would have to representative for the world around it. To do this we will use an environmental map as a representation of the world that can be reflected and for most objects we will just use a fixed map. But for truly reflective materials where it matters we can generate a new map on the fly every frame (or more likely every 10 frames or something like that to keep costs down)<br />
<br />
But we want to use the environmental map as a view of the environment around us.<br />
<br />
<br />
The look up this time will be done differently for the ambient light we could just use the world space normal to make a lookup in a cube map, but here what we want to see is what objects light would have bounced at this spot and then hit our eye. You surely remember that to get an outgoing vector after a reflection from an ingoing vector you just reflect it around the surface normal so that the angles between the normal and the ingoing vector and the normal and the outgoing vector match. We are going to do it pretty much the same as that except the vector from our eye to the point on the surface is going to be the incoming vector and the outgoing vector is the vector that we are going to use to look up into our cube map (if the process of doing environmental mapping with a cube map feels unfamiliar this is explained in detail in the <a href="http://msdn.microsoft.com/en-us/directx/default.aspx">DirectX SDK</a>).<br />
<br />
Since the reflected light doesn't come from any of our other light sources we just add it together to get the total light amount for our object.<br />
<br />
<br />
So the light hitting a surface is Ambient+Diffuse+Specular+Reflection. Putting this in equation form will give us.<br />
<br />
<i>pixelColor=0<br />
pixelColor+=(Ambient.LightStrength*AmbientOcclusionFactor)*ColorMapValue;<br />
pixelColor+= EnviromentalImage*ReflectivityMap<br />
<br />
For(i=0;i<nroflights;i++)><br />
{<br />
PixelColor+=FalloffFunction(lightStrength[i],DistancetoLight[i])*colorMap* dot(surfaceNormal,lightDirection[i])));<br />
<br />
pixelColor+= pow((Dot(HalfVector,Normal)*FalloffFunction(lightStrength[i],DistancetoLight[i])SmoothnessFaktor)*specularMap);<br />
}</nroflights;i++)></i><br />
<i></i><br />
<i><br />
</i>If you look closely at the above formula you see that reflections and ambient light are the same no matter how many light sources you are using this is because they aren't direct effects of the light rather they are indirect effects by bounced light and as we can simulate it correctly we just fake it. But the rest of the lighting equation is calculated per light. Let’s first look at what we have before we delve into that.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiINsTYyMb2NjkdeFTn130Nr_PzS3CD9r0m0p5_wpGcAHBKSB8NscMuy0ntCb-jCKHlrIUDyFv64cF5A2QU-3xXQON72bZD0c4TdzNGh1wwN1SQwsEG8U43s9stLHYZnwRmkjq8JgxpmUe/s1600-h/combined_All_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiINsTYyMb2NjkdeFTn130Nr_PzS3CD9r0m0p5_wpGcAHBKSB8NscMuy0ntCb-jCKHlrIUDyFv64cF5A2QU-3xXQON72bZD0c4TdzNGh1wwN1SQwsEG8U43s9stLHYZnwRmkjq8JgxpmUe/s640/combined_All_Logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>You should see the clear reflected light in the shadow side now. And you should also realize why I told you I exaggerated the effect, obviously it shouldn’t be this strong but it should be there to bring out life in the dark sides.<br />
<br />
We have one last step to talk about now shadows. The reason this is important is that I have seen so many games that just throw a generic shadow behind an object cast from some light and are so happy about it. This isn't what a shadow is in real life. If something looks like being in shadow it is because some objects are blocking the way of the photons from a light source. What’s important to ember is that it should only remove the light from that single light source as all other photons will still be hitting it just fine. If that light source is weak or its light is fading out then the shadow should also be weak or fading out. An interesting part about reality is that we have objects that allow some parts of the photons to pass through. This means that their shadows darkness should be relative to how many photons that are actually getting through the material. So for the moment let’s consider adding a new function to our system LightOcclusionFunction() it returns a value from 0 to 1 with 0 meaning total occlusion and 1 meaning no occlusion of the photons.<br />
<i>pixelColor=0<br />
pixelColor+=(Ambient.LightStrength*AmbientOcclusionFactor)*ColorMapValue;<br />
pixelColor+= EnviromentalImage*ReflectivityMap<br />
<br />
For(i=0;i<nroflights;i++)><br />
{<br />
PixelColor+=FalloffFunction(LightOcclusionFunction()*lightStrength[i],DistancetoLight[i])*colorMap* dot(surfaceNormal,lightDirection[i])));<br />
<br />
pixelColor+= pow((Dot(HalfVector,Normal)*FalloffFunction(</nroflights;i++)></i><i>LightOcclusionFunction()*</i><i>lightStrength[i],DistancetoLight[i])SmoothnessFactor)*specularMap);<br />
}</i><br />
<br />
This is all fine and dandy but in most games the value of the lightOcclusionfunction is either 0 or 1 so it is a blocker and you have to perform a lot of code for each light to determine this which leads to rendering the world with a multi pass model. First you render the ambient and reflective contribution then you render each light by its own adding it's light to the pixel value this gives exactly the same result as before but allows the computer to do a ton of calculations on where light is hitting etc for each light.<br />
<i> <br />
</i><br />
This changes our code into this.<br />
<br />
<b><i>Pass 1</i></b><br />
<i> pixelColor=0<br />
pixelColor+=(Ambient.LightStrength*AmbientOcclusionFactor)*ColorMapValue;<br />
pixelColor+= EnviromentalImage*ReflectivityMap<br />
<br />
For(i=0;i<nroflights;i++)><br />
{</nroflights;i++)></i><br />
Pass 2+i<i><br />
PixelColor+=FalloffFunction(LightOcclusionFunction()*lightStrength[i],DistancetoLight[i])*colorMap* dot(surfaceNormal,lightDirection[i])));<br />
<br />
pixelColor+= pow((Dot(HalfVector,Normal)*FalloffFunction(</i><i>LightOcclusionFunction()*</i><i>lightStrength[i],DistancetoLight[i])SmoothnessFactor)*specularMap);<br />
}</i><br />
<br />
<br />
This is of course from the view of a single pixel if we looked at it from a rendering function of view it would work out like this.<br />
<br />
Pass 1<br />
For each Instance Render with following PS<br />
<i> pixelColor=0<br />
pixelColor+=(Ambient.LightStrength*AmbientOcclusionFactor)*ColorMapValue;<br />
pixelColor+= EnviromentalImage*ReflectivityMap<br />
screenColor=pixelColor;<br />
<br />
For(i=0;i<nroflights;i++)><br />
{</nroflights;i++)></i><br />
Pass 2+i<br />
For Each Instance Render With Following PS<br />
<i> pixelColor=0;<br />
PixelColor+=FalloffFunction(LightOcclusionFunc(lightStrength[i]),DistancetoLight[i ])*colorMap* dot(surfaceNormal,lightDirection[i])));<br />
pixelColor+= pow((Dot(HalfVector,Normal)*FalloffFunction(LightOcclusionFunc (lightStrength[i]),DistancetoLight[i])SmoothnessFaktor)*specularMap);<br />
screenColor+=pixelColor</i><br />
}<br />
<br />
This will allow us to block light realistically on each object from each light source allowing some real subtle effects. One interesting thing that can happen when doing rendering with a lot of lights is that the colour value of a pixel may surpass 255 which is the maximum colour a monitor can display. We might later talk about how to fix this using High Dynamic Range rendering techniques. But for now you just have to be aware so that you don't over light the scene.<br />
<br />
I know this series of articles has been heavy and there is tons of stuff I have left out and things I have assumed from the reader that’s the only way to do this without writing an entire book. But I hope that by starting with grounding it clearly in the real world and using visual examples that I have made it easier to follow along.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com0tag:blogger.com,1999:blog-461014343580624539.post-60382493391765117402010-02-22T14:00:00.002+01:002010-02-22T14:00:00.619+01:00Lighting models for games IIAlright last time there was a lot of dusty theory of light that we had to cover. This time we will get down with the more practical side of modeling the effects of the light and actually look at the visual effects. For these examples I will use a model from one of our projects at <a href="http://www.thegameassembly.com/">TheGameAssembly.</a><br />
<br />
I have to mention now in the case of artists watching this that most of the maps here has not been properly artists tweaked in fact I am working with none final content. Besides this I have tampered around a bit with the maps to make the effects I am trying to show more obvious.<br />
<a name='more'></a><br />
Alright last time there was a lot of dusty theory of light that we had to cover. This time we will get down with the more practical side of modeling the effects of the light and actually look at the visual effects. For these examples I will use a model from one of our projects at <a href="http://www.thegameassembly.com/">TheGameAssembly.</a><br />
<br />
I have to mention now in the case of artists watching this that most of the maps here has not been properly artists tweaked in fact I am working with none final content. Besides this I have tampered around a bit with the maps to make the effects I am trying to show more obvious.<br />
<br />
First out I will talk a bit about how most games resolve their material functions. And why this while is highly incorrect it actually works out fine. What most games do is that at the base of the model they have a colormap which tells us what colour it want to be displayed in that space. This neatly represents the material Functions for most stuff as what the colour maps tells is what colours are reflected by the surface at the point. What we do is that we simply multiply the RGB values of the total incoming light with the RGB values of the colormap to get the colour of the light that is reflected. And this actually works out all right despite being physically unsound what you should do is convert the light colour to a wave length do any combinations there and then convert back to RGB space. And this does create some artefacts but for most cases and users it will work out just fine so we will assume from here on that this is in fact a working approximation.<br />
<br />
<br />
<br />
So let’s look at our model with only its colormap visible. I have selected 3 camera angles we will use consistently throughout these examples to highlight the differences between the different steps. These angles are selected because they show clearly the differences between the different lighting models.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiGesqWvBbHV1xXqAdxSeXRxt1-CIA8LNvPPm1mGCVN16wCakVTCgQ9jy2NeD4tFlK9X5CEsK3oTIc-Ou-wJIQwKuxT1bB9eLYnb3qrcfzTMU2fXdi9BMQTNq4KSYYuXItbBEEvHPzq36v/s1600-h/Base_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiGesqWvBbHV1xXqAdxSeXRxt1-CIA8LNvPPm1mGCVN16wCakVTCgQ9jy2NeD4tFlK9X5CEsK3oTIc-Ou-wJIQwKuxT1bB9eLYnb3qrcfzTMU2fXdi9BMQTNq4KSYYuXItbBEEvHPzq36v/s640/Base_Logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>As you can see it does not look very exciting but this is just the colour map without any lighting effects being calculated. As all we see in the world are light reflected into our eyes it stands to reason that the basic colormap would be quite uninteresting. But if we had no light we should actually not be seeing anything at all these objects should be completely black. I only show this to you so that you can notice the huge difference proper lighting can do.<br />
<br />
So let’s start with the ambient lighting. Ambient light hits the object from all different sides so it hits all surfaces identically. However because different areas of the object has different material functions we will get different visual looks at different parts of the model. If we move to code terms it should look something like this.<br />
<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><i>Color3f reflectedLight.Set(0,0,0); </i></div><div class="separator" style="clear: both; text-align: left;"><i>// Color3f is simply a class containing the R,G and B Color Values</i></div><div class="separator" style="clear: both; text-align: left;"><i>Color3f incomingLight.Set(0,0,0);</i></div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><i>incomingLight+=Ambient.LightIntensity;</i></div><div class="separator" style="clear: both; text-align: left;"><i>reflectedLight=MaterialFunc(incomingLight);</i></div><div class="separator" style="clear: both; text-align: left;"><br />
</div>As our material func is simply a multiply with our colour map we can simplify this code even more. <br />
<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><i>reflectedLight+=</i><i>Ambient.LightIntensity*colorMap</i><i>;</i></div><div class="separator" style="clear: both; text-align: left;"><br />
</div>So for each pixel the value of the colour at that pixel is the value of the reflected part of the ambient Light. It is important to remember that the colormap reflects the colour that it holds. This means that it absorbs the inverse of that colour. So for example a black colour map would absorb bright white so it would absorb all light (since white light contains all types of light). A white map would absorb black meaning it would absorb nothing and reflect all incoming light. All other colours are there in between.<br />
<br />
We will return to ambient in a while to look at it more in depth but for the moment let's look at what our test model would look like with ambient light only.<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcEugKn4jKh7yYUkxZTCTI3DT7hnU7VMN_jxXeUM14lzmhXE-azxGSBxV2bedJe11hjwf0taVAZq9ybeHH3Y4ImuoRZuNRu4bJCDYf6hSoeo8fkdVdZQ0Aez_5fNmo8vjMpOc9FFmG9vbk/s1600-h/ambient_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcEugKn4jKh7yYUkxZTCTI3DT7hnU7VMN_jxXeUM14lzmhXE-azxGSBxV2bedJe11hjwf0taVAZq9ybeHH3Y4ImuoRZuNRu4bJCDYf6hSoeo8fkdVdZQ0Aez_5fNmo8vjMpOc9FFmG9vbk/s640/ambient_Logo.jpg" /></a><br />
<br />
You will notice it looks exactly like the colour map only darker. This is because the ambient light strength is the same from all directions so it has only one light intensity.<br />
<br />
Ambient wasn't that exiting but it is necessary else the side of your model that is facing away from the light will become pitch black. But let's move on to diffuse lighting for now, before I get into that I will talk a bit about per pixel vs. per vertex lighting it's just a question of granularity in one case you calculate the light at the vertexes and interpolate inside the polygon with per pixel lighting you have an extra map that gives you surface data per pixel which means you can calculate things with a much greater visual fidelity without having to use too many polygons. For these lectures we will start with per vertex lighting because the effects are clearer with that and then change to per pixel during the later part of the lectures.<br />
<br />
Let’s first go back to our basic formulas from the last lecture<br />
<br />
<br />
<i>float irradiance=0;</i><br />
<i>float exitance=0; </i><br />
<i>for(int i=0;i<nrofincomingphotons;i++)></nrofincomingphotons;i++)></i><br />
<i>{</i><br />
<i> irradiance+=photon[i].Energy;</i><br />
<i> }</i><br />
<i>exitance=MaterialAbsorbationFunction(irradiance);</i><br />
<i>radiance=MaterialReflectionFunction( eyePosition,surfacePosition,exitance);</i><br />
<i><br />
</i><br />
Since we aren't actually tracing photons we can simplify this quite a bit. We can also for a moment assume that we will take care of the surface reflection function at another time so that exitance is the light that hits us.<br />
<br />
<i>float irradiance=0;</i><br />
<i>float </i><i>exitance</i><i>=0; </i><br />
<i>for(int i=0;i<lights.count();i++)><nrofincomingphotons;i++)></nrofincomingphotons;i++)></lights.count();i++)></i><br />
<i>{</i><br />
<i> irradiance+=Light[i].</i><i>lightIntensity;</i><br />
<i> }</i><br />
<i>exitance=</i><i>MaterialAbsorbationFunction(irradiance)</i><i>);</i><br />
<br />
Lightintensity is the value of the light that actually reaches the surface. So we have a simple formula for that one.<br />
<br />
<i>LightIntensity=FalloffFunction(lightStrength,DistanceToLight).</i><br />
<br />
Light looses its strength while travelling through the air partly because the photons loose their energy and partly because they collide with different particles in the air that stops them, but also because since the photons are travelling in different directions they will get further and further away from each other. I will not go into detail here but it suffices to say that the approximation most used for physical light is the following.<br />
<br />
<i>LightIntensity=lightStrength/(DistanceToLight*DistanceToLight)</i><br />
<br />
For games however this is sometimes impractical because it doesn't have a clear distance that it ends at so for booth games and rendered films we often use our own falloff Functions with the only real criteria being that is looks good.<br />
<br />
But since we aren't working with a physics simulation we would like to start looking at this as calculating pixel colours instead of radiance etc because we are going to be working in pixels in the end. And the code will look extremely similar anyway.<br />
<br />
<br />
<br />
<i>Color3f pixelColor(0,0,0);</i><br />
<i></i><i></i><br />
<i>for(int i=0;i<lights.count();i++)><nrofincomingphotons;i++)></nrofincomingphotons;i++)></lights.count();i++)></i><br />
<i>{</i><br />
<i> </i><i>pixelColor</i><i>+=</i><i>LightReflectionFunction(</i><i>MaterialAbsorbationFunction(</i><i>Light[i].</i><i>lightIntensity));</i><br />
<i> }</i><br />
<i></i><i><br />
</i><br />
That’s it. We already know the MaterialAbsorbation function it's just multiplying with the colormap but what does the lightReflection function do ? Well it tries to calculate how much of the light reflected from this area that actually hits your eyes. And it's what we are going to work on now when we go on to diffuse lighting.<br />
<br />
In physics you might have learned that when and ray hits a surface it is reflected around that surface normal so that the exit vector has the same angle to it as the incoming angle (I have mentioned this quickly before). So if you look at it like this it should only be about calculating from what direction incoming light needs to hit the surface for it to hit your eye. The problem is that the surface isn't perfectly smooth (well a mirror is pretty close but not perfect) this means that your rays will hit the surface with different normals and those will be reflected in different angles. Obviously more parts of the surfaces will be facing in the surfaces general direction so it will become lighter there. But even on a part of the surface that starts turning away from you there will be rays that bounce towards your eyes,<br />
<br />
You could say that diffuse lighting models the light beams that does not reflect perfectly straight into your eyes but are instead diffused by hitting a surface and being spread in different directions or being diffused by the small particles in the air. Modelling all of this is again pretty much impossible so what we do is that we let the distance function take care of the part when the photons are travelling through the air.<br />
<br />
We also assume that all materials have around the same molecular structure so that we can use the same code to decide how light is bounced. The basic idea is that the less the surface is facing towards you the fewer rays will bounce on it and hit your eyes therefore it gets darker. And if it's facing more than 90 degrees away from you won't see anything because the light will be hitting the other side of the surface. It is generally assumed that the amount of light that hits your eye is proportional to the cosine of the angle difference between the surface normal and the reflected light vector, if the difference is 0 the light would be hitting the surface from straight on and reflects straight back. The larger the difference the more of an angle would be between the surface and the light source.<br />
<br />
If we try to make it into code it would look like this<br />
<i>PixelIntensity=Lightsource.</i><i>lightIntensity</i><i>*cos(AngleToReflectionVector)</i><br />
<br />
This could be written as<br />
<i><br />
</i><br />
<i>PixelIntensity=Lightsource.</i><i>lightIntensity</i><i>*cos(acos(dot(surfacenormal,lightDirection)))</i><br />
<br />
A quick mathematical proof of the exchange<i></i><br />
<i><br />
</i><br />
<i>cos(a)=dot(v1,v2)</i><br />
<br />
This should be familiar if it isn’t the dot product between two normalized vectors produces the value cosine returns if you input the angle between them. So the following steps go smoothly.<br />
<i>cos(a)=dot(v1,v2)</i><br />
<i>acos(cos(a))=acos(dot(v1,v2))</i><br />
<i>a=acos(v1,v2)</i><i> </i><i> </i><br />
<br />
Which is just what we did we replace an angle with the acos between the 2 vectors that produces the angle. So now let’s clean that formula up a bit.<br />
<br />
<i>PixelIntensity=Lightsource.</i><i>lightIntensity</i><i>*(dot(surfacenormal,lightDirection))</i><br />
<br />
This gives us the final formula <i> <br />
</i><br />
<br />
<i>pixelColor=MaterialFunction( </i><i>Lightsource.</i><i>lightIntensity</i><i>*(dot(surfacenormal,lightDirection)))</i><br />
<i> <br />
</i><br />
Remember that lightIntensity also needs to be calculated we just leave that out of the formulas to keep them clean. So there we have the contribution of the diffuse part.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZZHcmT24qGGECD0nWtXvL-zrQtAdRQo1CrfiebVZX_tMdpmJFoyoeeP_2WNACDCIgsIXnrY1agVbZ2yqE2qCieImfsq41xZ6NIHOBEGpPFEWtKnsWdHLPzs9MhnH63sZ6JuxlF2JkO9Ly/s1600-h/amb+dif_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZZHcmT24qGGECD0nWtXvL-zrQtAdRQo1CrfiebVZX_tMdpmJFoyoeeP_2WNACDCIgsIXnrY1agVbZ2yqE2qCieImfsq41xZ6NIHOBEGpPFEWtKnsWdHLPzs9MhnH63sZ6JuxlF2JkO9Ly/s640/amb+dif_Logo.jpg" /></a></div><br />
<br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
<br />
For the first time we are seeing some definitions of form on the object. If you feel that this doesn't look that good it's partly because we are still doing all our work for each vertex and partly because we have just gotten started on what we want to include.<br />
<br />
Specular is the third term of our lighting system. I said to simply things that diffuse light represents the light that are diffused on the surface before hitting your eye so that you don't get a straight reflected ray hitting your eye. This is all a matter of definition since the rays consists of photons and it's the amount of photons hitting your eye that determines how light it looks so there is no diffuse or specular light just different amount of photons. But again we are using approximations here so for now we consider specular light to be the rays of light that perfectly reflects on the surface and directly hits your eyes, this means that the specular doesn't need to be in the area that has the brightest diffuse light. So let’s consider them as undiffused rays of light. As they hits directly in the eye they are not only based on direction of the light and the normal of the surface, but also on the direction between the viewer and the surface. If the angle between the vector of the viewer to the surface and the surfaces normal are the same as the angle between the lightsourceDirection and the surface normal we will have a perfect reflection.<br />
<br />
Now how much of this you see depends on the material you are watching some material like steel and plastic has a very clear specular while paper normally doesn't but for the moment we will assume that all materials have a full specular and later look at how to compensate for the material differences.<br />
<br />
Depending on the surface the specular might be large or small as a rule materials with small unevenness will have rays over a bigger part that directly hits they eye but they will also generally have less of a specular component because since they are more uneven more light will also be reflected away from the viewers eyes. <br />
<br />
So let’s look at how to calculate all of this because preferring two dot products for each angle and then trying to compare the results is quite expensive most of the time we look for an easier solution. The idea is as follow if the combined vector of the two vectors is calculate and then normalized you will get a vector that is the vector of a surface that would produce a perfect reflection and then you can consider how much this vector differs from the surfaces normal to see how close we are to a perfect reflection. This vector is normally called half vector because it's halfway between the two original vectors. So the half vector will be representing the vector that light should have been reflected about.<br />
<br />
<br />
<br />
<i>halfVector=normalize(lightvector+eyeToSurfaceVector);</i><br />
<i>reflectionDifference=dot(surfaceNormal,halfVector)</i><br />
<br />
So now we have the value of acos(difference between eyevector and perfect reflection vector) now we just need a factor to compensate for how far the perfect reflection spreads so that it doesn't occupy the entire material.<br />
<br />
<i>specucularStrength=</i><i>pow(reflectionDifference,SurfaceConstant)</i><br />
<i>pixelColor+=lightSource.LightIntensity*</i><i>specucularStrength;</i><br />
<br />
Observe that we just added the specular component straight off I am a bit shaky on the physics but I believe that because it is a perfect reflection the intensity of it is so strong that it isn't colored by the material it hits. However notice the <i>SurfaceConstant </i>part of the code that’s the factor that determines the materials specular reflective properties somewhere between 8 and 32 are normally sane values. But this is all for the artist to play around with to simulate different materials.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg27BCfYFFFZcUjsbM4yVTmrphwmx7ByJ0gippo6azlYUaprZLLegN7FktsLWDGg3Ta1A-WxZcWt03hb-Rdg07F2yJqhPm3D1DWh7AaAlnnPajW3OhkPbBUaOghtRR0_zBET4JGtVUIzxsw/s1600-h/amb_dif_spec_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg27BCfYFFFZcUjsbM4yVTmrphwmx7ByJ0gippo6azlYUaprZLLegN7FktsLWDGg3Ta1A-WxZcWt03hb-Rdg07F2yJqhPm3D1DWh7AaAlnnPajW3OhkPbBUaOghtRR0_zBET4JGtVUIzxsw/s640/amb_dif_spec_Logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>The big white shiny specular spot should be quite visible by now and you should also notice that it exists even in the third image even though the diffuse light is pretty much nonexistent. I mentioned before that specularity also has a material function and it's not as simply as multiplying with the colour map. We will look at that one later on. For now as the last step of this update I want us to consider the problem with our model. Even with all the 3 lighting steps run on it, it still looks quite bland. This is because there is so little information in the mesh. There are very few polygons and vertexes and this gives our lighting very little to work with.<br />
<br />
Since we can't handle models with to high vertex counts in real-time on current hardware we have to make some kind of cheat to get in extra detail. What is used for this is a bump map. A bump map is a map that tells the rendering code about how the surface changes within a polygon. It's just like the colormap but it contains information on surface deformations instead. The most commonly used type of bump map today is called normal mapping it means that the bump map contains the actual normals of the surface for that point (or in tangent space bump mapping a modification to the normals at that point). This allows us to simulate a lot more detail as long as we accept that the bumps we see are always flat to the polygonal surface, they can't add details to the outline only inside the surfaces.<br />
<br />
The most common way to generate a normal map is to create a highly detailed version of the object and one low detail version and project the details from the high detail version into the normal map for the low detail version. This way you can create an object that looks like it has millions of polygons without the cost for them. So as a final step lets look at our model using per pixel lighting that uses the normals from the normal map instead of the normals of the vertexes.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5aMvlLiaAKIZejJ_wIjHkj096VfhAaX_yNU0PGvK97KCtM8WEWneVAtzRv-P0FOmP8kgUNgZqhgImaZQYB1ykdh45qDrzisEvsxJwZiHj0tGQh4pdCSE8tID1Dc2InfkE-t00PLnQvTev/s1600-h/normalMapping_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5aMvlLiaAKIZejJ_wIjHkj096VfhAaX_yNU0PGvK97KCtM8WEWneVAtzRv-P0FOmP8kgUNgZqhgImaZQYB1ykdh45qDrzisEvsxJwZiHj0tGQh4pdCSE8tID1Dc2InfkE-t00PLnQvTev/s640/normalMapping_Logo.png" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
Quite a difference don't you agree ? The specular really sticks out now and makes it look plastic however but we will look in the next instance how to fix it and also how to take it all to the next level visually.<i> </i>Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com0tag:blogger.com,1999:blog-461014343580624539.post-71148296665133718982010-02-15T14:00:00.000+01:002010-02-15T14:00:03.204+01:00Lighting models for games I<div class="separator" style="clear: both; text-align: left;">This is going to be the first in a series of lectures focusing on covering some lighting models for games and also the actual physical properties of light and materials they are based upon. We will also work with examples booth from real life and from a simple engine using some art courtesy of the students from <a href="http://www.thegameassembly.com/">TheGameAssembly</a>.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">But for this first part we will be focusing our attention on the different properties of light and actually understanding what is happening in the world around us. As in the end this is what we are going to try to simulate in the computer.</div><a name='more'></a><br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">This is going to be the first in a series of lectures focusing on covering some lighting models for games and also the actual physical properties of light and materials they are based upon. We will also work with examples booth from real life and from a simple engine using some art courtesy of the students from <a href="http://www.thegameassembly.com/">TheGameAssembly</a>.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">But for this first part we will be focusing our attention on the different properties of light and actually understanding what is happening in the world around us. As in the end this is what we are going to try to simulate in the computer.</div><br />
However before we go deeper into this we have to remember the golden rule of Computer Graphics, "If it looks right it is right". So with this in mind why are we obsessing about how reality works when our only goal is to make it look good? The problem is what I will refer to as visual dead ends. This is what happens when you are pursuing a certain technology with great results and your game is looking better and better but you hit a wall front on visually and you just can't raise the visual fidelity of your game.<br />
<br />
The main reason for those dead ends is when you are working with technology you just don't understand you know the effect but you don't know the physical backings and therefore you can adapt it properly or get it to coexist with other techniques. So the reason to obsess about the real world is that the real world doesn’t have dead ends, because everything is coexisting in an extremely complex system. We don't need to understand this system to make nice graphics but it does help. It tells us what it is we are trying to simulate so we know what techniques to use to achieve it. This allows us to recognize how far back we need to go and where to restart when we reach a dead end. Because the answer is almost always go back to the real world and watch what you are trying to model. Because reaching a dead end is often the result of applying a technique in a way it was not meant to be used instead of using it for what it is simulating.<br />
<br />
All right enough about why we need to learn this stuff and over to the meat of the update. Before we start I will issue a warning to anyone who has seriously studied the properties of light and its effect. I will try to use laymen terms for these updates so anyone who has studied radiometry or colorimetry will notice that I will grossly simplify things in order to keep it simple and to the point.<br />
<br />
You are probably aware of that all we see in the world is light. We don't object we just see the light reflected of objects. What makes this all interesting is that you don't just see light that has come from the light source and bounced on the object instead light bounces on everything multiple times until finally the light dissipates this creates an effect of objects being nearer to each other mixes their colours together,<br />
<br />
All the light we see in the world around us is composed of multiple sources, it is really rare in reality that we are watching light that comes straight from a light source it has already bounced or passed through multiple surfaces.<br />
<br />
Consider a normal table lamp, it emits light in all possible directions from a wide area of the copper thread, then this light passes through the material of the glow bulb which might be glass but it can also be a dimmed or colored material that affects the light the reason for using a dimmed light bulb is to diffuse the light to make it feel softer. Then the light that passes through the light bulb is bounced vs. the inside of the table lamp in an attempt to direct it in a correct direction so that we get a sort of directed light from the copper thread who just emits light in all directions.<br />
<br />
So the light we are watching coming out of the lamp has already been bounced around a bit, but this is not all it also bounces and hits a lot of small particles which floats around in the air around us.<br />
<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div>Let’s look at an example to make this effect of bouncing clearer.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjanlNUf0fqAWxAKDi3hK4fR5TXe0RiuyMpAZ9fSe_2dOrHDtzsMNIechuUqFJUVdsxqsfzi-7FhUKooyEyBgRX5QjjIYZhvnwoyNP9hVSDoSAhdLi5jxQ_f8_rJ5txKPoOLQYELIpiWK98/s1600-h/StrongBounce_01_Logo.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjanlNUf0fqAWxAKDi3hK4fR5TXe0RiuyMpAZ9fSe_2dOrHDtzsMNIechuUqFJUVdsxqsfzi-7FhUKooyEyBgRX5QjjIYZhvnwoyNP9hVSDoSAhdLi5jxQ_f8_rJ5txKPoOLQYELIpiWK98/s320/StrongBounce_01_Logo.jpg" /></a><span id="goog_1260721086589"></span><span id="goog_1260721086590"></span></div><br />
<br />
<br />
<br />
If you look at this image it should be obvious that we have a light source coming from the upper left.<br />
<br />
You should also be noticing the clear red and green colouring on the sphere. This is due to light bouncing on the red and green papers place there.<br />
<br />
If you look closely at the bottom of the sphere you will notice that it is not only greener at the bottom but it is also brighter.<br />
<br />
This is because there is a larger area that the light might bounce vs. to hit that part.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidWxmMv1yXaaAbPCng8sN9CX4nGBnnVstFbAFcFiAme_UdSmRL4pMBHM_Agv_RPVj9ZQnfAqL3rMS5AnaidcWv1Ah149rckkCTDDudMu1Ql44hOvD7X7xJ3GF06LV_apejtWwSggSyvKq3/s1600-h/StrongBounce_explain_01_Logo.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidWxmMv1yXaaAbPCng8sN9CX4nGBnnVstFbAFcFiAme_UdSmRL4pMBHM_Agv_RPVj9ZQnfAqL3rMS5AnaidcWv1Ah149rckkCTDDudMu1Ql44hOvD7X7xJ3GF06LV_apejtWwSggSyvKq3/s320/StrongBounce_explain_01_Logo.jpg" /></a></div>If we look at this example image I have decided to represent rays of light as arrows and when they have reflected vs. a material I have colored the arrows accordingly.<br />
<br />
Now is a good time to mention that light does not really consists of rays it consists of small particles called photons.<br />
<br />
So what we are seeing is photons bouncing on the different surfaces they encounter. And some of them bounce into our eyes and that makes up what we are seeing.<br />
<br />
<br />
<br />
Or rather it is interpreted by our mind as an image. We will later on discuss how the inner workings of our eyes and our brains also affect what we see but that is for a much later lecture.<br />
<br />
By now it should feel obvious that there is no way we can simulate all of this in real time on the current generation of hardware. So what do we do? We cheat, we use different approximations to try to get a light that looks like reality without having to perform all the calculations necessary to really approximate reality.<br />
<br />
We will look at those approximations and also look at their effect in real life using a pair of spheres. I have opted to represent light as we see it as 4 different components (I have not made these up myself obviously these are based on a long tradition of research in computer graphics)<br />
<ul><li> <b>Ambient Light</b></li>
<li><b> Diffuse Light</b></li>
<li> <b>Specular Light</b></li>
<li> <b>Reflections</b> (these also consists of light since all we see is light)</li>
</ul> Alright now we have selected our representation. These might be a bit mysterious as to what they mean right now(or not) So we are going to find them one by one by examining reality and se how they occur and how we can reproduce that effect.<br />
<br />
I wrote earlier that what we physically see is light that is reflected vs. our eyes from a surface. When light hits a surface part of it is absorbed by the surface and part of it is reflected back it is this reflected light we see.<br />
<br />
This isn’t exactly completely true the light that is absorbed does in reality pass into the surface. If the surface is thin or the material isn't too dense some of this light might pass through it. The human skin as an example of a material light easily passes though this is quite observable on the ears. But what happens inside the material is also interesting materials in real life don’t have a perfectly consistent density which means that inside the material the light might hit more denser areas that change its direction this causes the light to scatter around inside the material and some of this light might pass back out though the material again and out in the rest of the world. This effect is known as sub surface scattering. But for what we are going to look at for now the approximation I mentioned first is sufficient.<br />
<br />
Let’s first establish a couple of terms and concepts that we are going to use to discuss the rest of the material with.<br />
<ul><li><b>Radiant flux</b> is the amount of energy it emits per second. Simply put the collected energy from the photons emitted under a second.</li>
<li><b>Irradiance</b> is the term for radiant flux with respect to an area, normally the surface of an object, Sp irradiance is used for the light flowing into a surface with each light source in the world having their own irradiance contribution to the surface. Quite simply it's the incoming light to that area.</li>
<li><b>Exitance</b> Exitance is the amount of light that exits a surface the difference between exitance and irradiance is what we will refer to as the material function the function the represents the properties of the material.<br />
</li>
<li><b>Radianc</b>e is what we actually see what eyes and cameras capture radiance is basically the flow of light for an area in a specific direction. The direction towards the sensor(eye or camera etc) that captures the light. This is different form exitance that is just what leaves the surface this is what leaves it in a specific direction.</li>
</ul>I think we have enough terms now to get started. This might have seen as overkill for what we are going to do but this means we can use these terms in our equations.<br />
<br />
So how do we calculate the light at a specific point on a surface ? Well first there are no such things as a point as everything is areas but to simplify this let's assume that we can use an areas with the size of a pixel.<br />
<i>float irradiance=0;</i><br />
<i>float exitance=0; </i><br />
<i>for(int i=0;i<photon.count();i++)><nrofincomingphotons;i++)></nrofincomingphotons;i++)></photon.count();i++)></i><br />
<i>{</i><br />
<i> irradiance+=photon[i].Energy;</i><br />
<i> }</i><br />
<i>exitance=MaterialAbsorbationFunction(irradiance);</i><br />
<i>radiance=MaterialReflectionFunction( eyePosition,surfacePosition,exitance);</i><br />
<br />
As you can see I have split our material function into two parts one that determines the properties of the material in regards to absorption of light. And one that represents the material according to how it reflects light, Observes that this has nothing to do with its absorption but is simply a representation of the roughness of the surface which affects how scattered the directions of the photons will be after they have hit the surface.<br />
<br />
Basic physics says that the exit direction of a bouncing ray on a surface will have the same angular relation to the surface normal as the incoming direction. The problem is that our surfaces has small microscopic irregularities that will cause the normal to change and the light to scatter since a pixel does represent quite a large area in this perspective. So the photons will bounce around in all different directions while obviously more will bounce according to the basic angle equation there will be a gradient of light strength depending on how far away from that angle you are. And that's what this function represents in our equation observe that will refer to code as equations because that's what they are representing just in a format that is more readable for more people.<br />
<br />
Now let’s talk about those light terms we talked about earlier. First out is Ambient light, This is the term we use to represent bounced light in the world, Since we obviously can't calculate all the bounces we use this term instead The ambient light is considered to have bounced on so many surfaces that it can be considered direction less so we think it's coming equally much from all directions. This means that no matter what direction a surface is facing it will receive the same ambient light ( we will later talk on methods for directional ambient but for now this will hold true) considering this it would mean that if there are no direct light sources all objects in the scene would receive the same amount of light. Basically ambient light represents all the bounced light in the world and all global illumination models like radiosity etc are working on getting a more accurate version of this than what we have just described. Important is that in the end the ambient light just adds to the irradiance of the surface as an irradiance contributor.<br />
<br />
For now let's move on to the Diffuse light component We talked earlier about how the surface aren’t perfectly flat on the molecular level so the light that hits it will bounce in different directions diffusing the light that hits our eye compared to the case where it is all reflected sprightly according to the angle vs. the normal. The diffuse lightning component models these reflections and contributes different amount of lights depending on the angle between the eye and the surface. Of course there won’t be any diffuse light in the case of the photons being blocked by an object because this means we are in the shadow seen from that light source.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiczJRq5d-wGf0-Mvwha_xtPrxHzIS85ZVLLAn17yLWOR2eZzGV6PiIgjTXmQnwQp-u6FILuMO2narLd3dl5dYGbZRSy6M_LPvB2LETga1zGnSO84T0mkCVcEg-wYQBsQfAsM3GdKugFRwT/s1600-h/BounceLight_logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiczJRq5d-wGf0-Mvwha_xtPrxHzIS85ZVLLAn17yLWOR2eZzGV6PiIgjTXmQnwQp-u6FILuMO2narLd3dl5dYGbZRSy6M_LPvB2LETga1zGnSO84T0mkCVcEg-wYQBsQfAsM3GdKugFRwT/s400/BounceLight_logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><br />
</div>Let’s reflect on it based on this image this is from a one light source environment but with tons of bounced light in the room. We can clearly see the sphere being lighter on the left half where the light source is. And if you look closely you will see a line around the sphere where the diffuse light stops contributing. If you have trouble seeing it please look at the image below.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuJcCwgi6ZNvlAzeKEF_5GQ8BIeEpD1BbKgskhAMXuxTN-__XmRYeGBKw4rxjUSZyHV53PD5C0hm9Ym1eFXbarobi_RgXhTCautw2focs_kKfhxHLDG2AsHqpNlC1U361x2Zx3yhX0W-4_/s1600-h/BounceLight_BedBugLine_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuJcCwgi6ZNvlAzeKEF_5GQ8BIeEpD1BbKgskhAMXuxTN-__XmRYeGBKw4rxjUSZyHV53PD5C0hm9Ym1eFXbarobi_RgXhTCautw2focs_kKfhxHLDG2AsHqpNlC1U361x2Zx3yhX0W-4_/s400/BounceLight_BedBugLine_Logo.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="" style="clear: both; text-align: left;">This line has many names in artistic tradition, the most formal is the termination line. But most famous is probably the bed bug line, showing how horrible environments art students used to live in where the bugs appeared as soon as the light disappears. What’s important with this line is that it is where the diffuse contribution from our light source stops. All other visual effects on the sphere are created by bounced light, which we models as ambient. So all that happens in there is the ambient light, the same holds for the area in the shadow from the sphere while the rest is lit with diffuse light. So what happens if we remove our diffuse light which in real world is removing the light source.</div><br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWvkuXTim1qeMrbE9LLrGg3Mc5G1Iy8mlQxDFJniX77eTi95wSrQYig2VGqqokToiLmifAB-LxxA6ylQ9ALBEFtDbbo8WTex8gAAKzu958Czcvt65PFnLk25pSFunn67j1gFUZriKCWTy3/s1600-h/Shadows_Bouncedlight_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWvkuXTim1qeMrbE9LLrGg3Mc5G1Iy8mlQxDFJniX77eTi95wSrQYig2VGqqokToiLmifAB-LxxA6ylQ9ALBEFtDbbo8WTex8gAAKzu958Czcvt65PFnLk25pSFunn67j1gFUZriKCWTy3/s400/Shadows_Bouncedlight_Logo.jpg" /></a></div><br />
One thing to notice except everything getting a lot darker is that we can hardly se any bounced light hitting the sphere. This is because we have removed the light source that cast those strong rays that bounced on the ground and hit the sphere. So because we no longer have any strong light source we aren't seeing a bounced light. This might be confusing because this means what happened in our ambient part of the light changed when we removed the light creating our diffuse light. The reason for this is that the lighting models we use in games by necessity are gross oversimplifications of reality we simply can’t model these light bounces in real time.<br />
<br />
One thing however to notice is that there is still some kind of shadow near the bottom of the sphere. If all we saw was ambient light which is directionless then there shouldn’t have been any shadow there. Is it because or ambient model is flawed ? Well it is flawed but what we see here is something we can model with our current model. Even if the ambient light is coming from all directions at once if there is something blocking of some of these directions then the bounce light can’t reach the position because it is occluded, this is called ambient occlusion. What it means is that down there at the bottom of the sphere the light can't come much from below because it is blocked and it can't come from above because it is blocked too so that area receives less of the ambient light because some of the ambient directions are occluded. How to calculate this occlusion is however beyond the scope of this lecture. Thankfully we don’t need to, modern 3d programs can calculate if for us.<br />
<br />
Now let’s get on to specular light. Diffuse light was representing the bouncing of the light when it hits a surface, Specular represent the rays that are reflected straight into our eyes on an even reflection angle. How much specularity we see depends on the smoothness of the material on the molecular lever. Plastic and iron have very visible specular if you look at them being lit by a lamp you will see a very bright spot and as you moves your head that spot will move. This is because it represents the light directly hitting your eye on the bounce which means if you move your eyes you will change what point on the surface that creates the correct angle for a direct reflection towards you. The area of the reflected light depends on the smoothness of the material. In real life you will almost always perceive specular light as white. This isn't because it is white it's because it so much brighter than the rest that your eyes thinks of it as white. In the real world also almost all light sources have some small part of all colours. if you had a perfectly red lamp for example however it would produce a red specular highlight this can be confirmed by experimenting, but remember your eyes are trying to trick you because they are used to assign the brightest spot as white (which incidentally is why cameras has white balance correction to make sure that it gets white no matter what colour it really does have)<br />
<br />
Finally we have the last term the Reflections if we think back reflections are just light that had bounced of an object and hit your eyes, Which means that all reflections are at least second degree bounces for light must first have hit an object bounced on that hit the reflecting object and bounced on that before it hit's our eye. Now a lot of people will say that reflections are only useful for modelling special effects on very reflective objects like glass, steel etc while it is true that we mostly see reflections on those objects it doesn’t mean reflections are limited to them in fact there are reflections on all objects they might just not be clearly visible to the eye (This makes sense as all light we see are bounced that all materials might receive bounced light from other objects) So how come that we see it clearly in some materials and not others ? Once again it is done to the structure of the material some materials scatter the light to much for there to be any meaning full reflection for us to see just mixing the reflection in with the rest of the bounced light while some structures like a mirror has an almost perfectly flat surface and surface structure and incredibly low absorption allowing it to return the light virtually as it was seen creating a very clear reflective image.<br />
<br />
One interesting properties of reflections is that they are not affected at all by the lighting conditions of the object they are being reflected in. Which means that reflections is a technique that brings a lot of life into those dull shadow areas that easily turns out to have only one intensity everywhere. I know a lot of people would say this is gibberish so I will here show 3 images showing how reflections react in reality with changing light conditions.<br />
<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDj6zltk-D9fpbnash-Anr2ZYsCVd7ISaz4czS5F40junF8jOCxHnTFL1_AITZsBmdQm6n6a1U2GOnx7KfyB6DTQIiIEHa2y4ykpnDRkV_afBB6apNaf5XApW7x72vXzJ7PfP9B6FLDpHm/s1600-h/Step01_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDj6zltk-D9fpbnash-Anr2ZYsCVd7ISaz4czS5F40junF8jOCxHnTFL1_AITZsBmdQm6n6a1U2GOnx7KfyB6DTQIiIEHa2y4ykpnDRkV_afBB6apNaf5XApW7x72vXzJ7PfP9B6FLDpHm/s640/Step01_Logo.jpg" /></a><br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;">If you look at the small sphere you will clearly see the large sphere reflected in it, these are normal light conditions both object are in the light from a single light source and the reflection is passing over the bedbug line. If you look closely you will see that the reflection looks slightly cleared below the bedbug line it’s because there is less light there the light from the reflection stands out more. If you can’t see it then lets look at this next image.</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBEbR-Nbm5FwgTPWUy6GzlIrMVTTSFRxfpIjiokDABwtZ6CBLwuuQEkm1sNeAJ-pfrEKg6VUORcttSI-sxvEdBhWIM2932eYaVibkrblAjBkZ862kFRFoGanwJxdvdXRS0IcuEYhtSNAmm/s1600-h/Step02_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBEbR-Nbm5FwgTPWUy6GzlIrMVTTSFRxfpIjiokDABwtZ6CBLwuuQEkm1sNeAJ-pfrEKg6VUORcttSI-sxvEdBhWIM2932eYaVibkrblAjBkZ862kFRFoGanwJxdvdXRS0IcuEYhtSNAmm/s640/Step02_Logo.jpg" /></a><br />
<br />
<div class="separator" style="clear: both; text-align: left;"><br />
</div>What I am doing here is that I am holding up credit card blocking the light source, I have controlled this so that it only blocks the light on the right part of the left sphere. if you look just at the reflection you will see that the reflection is just as bright as before despite that spot on the sphere being deprived of direct light. It well even seems clearer and stronger now but that is just due to the increased contrast due to the rest of the sphere being darker. You can also again notice that we have much less bounced light on the left sphere since we have blocked the photons that would produce it.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-QTNrrJ0nZSkdYSvR_RLlik8pDqH3v0vDi4dsTy-bbIcywgGHUkvhJnv06TX0pjMu38gG3JML8b2_BcWBZrubBRc3tlI35mdn2c1P44vgRwwrexkXNYn3AlBavItyaxjPLLN8QelsREO2/s1600-h/Step03_Logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-QTNrrJ0nZSkdYSvR_RLlik8pDqH3v0vDi4dsTy-bbIcywgGHUkvhJnv06TX0pjMu38gG3JML8b2_BcWBZrubBRc3tlI35mdn2c1P44vgRwwrexkXNYn3AlBavItyaxjPLLN8QelsREO2/s640/Step03_Logo.jpg" /></a></div><br />
In this final experiment I hold the credit card over the right sphere instead blocking the light to it, what you will notice is that the reflection has disappeared almost completely. This is because there are not as many photons bouncing of the right sphere because we are blocking them out.<br />
<br />
So based on this we can state clearly that the strength of reflections are dependent on the lighting conditions of the object being reflected and is not concerned at all with the lighting conditions of the object it is being reflected in. (just put a mirror in a dark room and put yourself so you can see it and shine light on yourself and you will see your reflection clearly.)<br />
<br />
Well this was all for today. The next time around we will actually look at creating equations for what we have talked about for games and look at the visual differences we get playing around with the different parts of light.Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com1tag:blogger.com,1999:blog-461014343580624539.post-44110673293379685142010-02-15T13:15:00.000+01:002010-02-15T13:18:41.239+01:00IntroductionHello and welcome to Game School Gems. The goal of this blog is to collect various gems (short and useful techniques) that I have come upon during my 12 years in the game industry. It will be things that I either find important by themselves or things I have noticed an extra need for in my students at <a href="http://www.thegameassembly.com/">TheGameAssembly</a>.<br />
<br />
This blog is not intended to be an online game curriculum, there are things I won't touch simply because I can't make a meaningful explanation of them without going over a lot of updates. I feel it would be incorrect to monopolize the blog for half a year to cover linear algebra in depth for example. The focus is on finding gems that are directly usable and can be explained well in below 50 pages(my current max limit for a subject and I hope I can keep it).<br />
<br />
In the same way this is not intended as a course literature for <a href="http://www.thegameassembly.com/">TheGameAssembly</a> either, for those of my students that does find their way here you will notice that though you will find the same subjects in my classes the way they are discussed here is in much greater detail. This is because this is meant as an online learning resource, I can't have the same expectations of everyone who reads this one as I have for my students that are excepted to fill in a lot of this information on their own to learn practical problem solving which is one of the basic programming skills.<br />
<br />
That however is a different path that can be taken but for now I am just trying to get this knowledge out to as many people as possible so that I in some small can way give back all the things I learned in the industry and also hopefully rising the lower bar for what knowledge we can except when getting job applications.<br />
<br />
Basically this is a site for you to learn those gems and in order for you to be able to do so I have gone to great lengths in trying to make the explanations complete but there will always be material for you to fill in.<br />
<br />
If anyone wonders who I am here is a short introduction:<br />
My name is Niklas Hansson and I started working in the games industry back in 1997 when <a href="http://www.massive.se/">Massive Entertainment</a> was founded. Back then I was probably a lot like you guys reading this, I was young and interested in programming and computers, The difference is that back then it was almost impossible to find information about games programming there was some books and some BBS:es but internet was just in it's birth even though #coders on efnet contained alot of really bright people from the demo scene. I am doing this blog to help other people in the same position i was in back then to make the same transition from a hobbyist to a professional. I worked at massive for 12 years working as the Lead programmer on booth <a href="http://www.massive.se/games/ground-control-series/#gc2">Ground Control II</a> and <a href="http://www.worldinconflict.com/">World In Conflict</a>. I handled the hiring of programmers for massive during my last 5 years there or so, I also worked on the original <a href="http://www.massive.se/games/ground-control-series/#gc">Ground Control</a> and other projects.<br />
<br />
When working with hiring I often met bright young people who sadly hadn't gotten the education they needed to work in the industry this lead me to at last in 2008 to leave my position as Software Development Manager at <a href="http://www.massive.se/">Massive</a> to help with launcher what was then known as <a href="http://www.thegameassembly.com/">theGameAcademy</a> but turned into <a href="http://www.thegameassembly.com/">TheGameAssembly </a>due to legal reasons. For now I have worked there as a teacher for almost two years learning how to put my practical know-how into knowledge that I can pass onto my students.<br />
<br />
So now I finally feel ready to move on to launching this blog to reach an even greater number of people. I have had to make so many people sad that they didn't get their dream job so now I want to help you to make your dream true just as I did.<br />
<br />
Well this sounds cheesy but that's the bottom line and that's what this blog is all about. I hope you find it a useful source of information. Also you can suggest topics you are interested in during my Career I have been lucky enough to work within many different disciplines of game development so I can write about most things. But if you don't send in suggestions the blog will probably mostly be talking about the subjects we handle during the second year of school.<br />
<br />
Updates should be released weekly on Mondays.<br />
<h5 class="student_name"><br />
</h5>Niklas Hanssonhttp://www.blogger.com/profile/12203854253190428924noreply@blogger.com0