tag:blogger.com,1999:blog-7821375479041658292024-03-13T04:03:02.847-05:00Playing With C++ ProgrammingWillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-782137547904165829.post-65054450617812001472013-01-12T14:22:00.000-05:002013-01-14T10:27:07.197-05:00Text Adventure Games C++ Part 4Hello again!<br />
<br />
I finally get back to my C++ Programming blog, to continue a little with this fascinating business of making my own demo adventure game. This time I am going to continue a little with interacting with some NOUNS using some VERBS. <a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-3.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">Previously</span></b></a>, I implemented the usage of a simple verb (LOOK) in the parser. In this installment I will endeavor to OPEN a DOOR. Now, coding these adventure games is a good old fashioned programming exercise in recognizing conditions and executing the program based on the results of those conditions. Which program isn't? However, here the program is so basic that there is little need to resort to Classes and other, newer programming techniques. The one exception I have made, to make life easier with my limited time availability, is the use of a couple of STL features. Doubtless, if one wants to make a living (lol) off writing text adventure games, then an effective API for it is well within reach of being written in C++. Right here, I am just going to "plod on" the old fashioned way.<br />
<br />
<i><u><b>To business, verbs and nouns...</b></u></i><br />
Nouns are objects. When using nouns in a text adventure game, they simply MUST be preceded by a verb. However a verb does not necessarily need to be followed by a noun, as was proved in the last post. LOOK, for example, can be a perfectly valid command when used on its own. But when you want to open a door, it is only logical that the command be "OPEN DOOR" and not just "OPEN", in order to avoid ambiguity. For this to happen, the parser now has to handle both the words thrown out by the command line interpreter. So, that is the first objective. To simplify reading the changes in the code, I have included comments to identify the modifications that I have made to the <a href="http://pastebin.com/4ZzeCLvw" target="_blank"><span class="Apple-style-span" style="color: blue;"><b>original listing</b></span></a>.<br />
<br />
Here are the objectives for this post...<br />
<br />
1. Implement the NOUN trap and acheive the objective of opening the door.<br />
2. Making the door visible and interactive in two different rooms (modification to the LOOK command, which will make the other nouns visible at their locations, too).<br />
3. Making sure the door does what it is supposed to do, as in stopping access to the adjoining room when it is closed.<br />
<br />
So, first off, some enumerated types again to identify my nouns, and a constant integer for the number of NOUNS. At the top of the code...<br />
<br />
<pre>enum en_NOUNS {STORE_DOOR, MAGNET, METER, ROULETTE, MONEY, FISHROD};
const int NOUNS = 6;
</pre>
<br />
Now, create a structure for nouns. Nouns are going to be a little different from verbs and directions, so I cannot really reuse the previous word structure. For a start, because nouns are objects, they will need a location. Because they can be examined and seen, they also need a description (like rooms). Also, some objects can be carried, and others cannot (too heavy an object, for example), so a way to determine this must be implemented. Here is what I came up with...<br />
<br />
<pre>struct noun
{
string word;
string description;
int code;
int location;
bool can_carry;
};
</pre>
<br />
Now, that is just a guide. More complex adventures (better said, adventures with more complex objects) might require some more features for their nouns inside this structure.<br />
<br />
Then, I need the function to set values of the members of the structure...<br />
<br />
<pre>void set_nouns(noun *nns)
{
nns[STORE_DOOR].word = <span class="Apple-style-span" style="color: #0b5394;">"DOOR"</span>;
nns[STORE_DOOR].code = STORE_DOOR;
nns[STORE_DOOR].description = <span class="Apple-style-span" style="color: #0b5394;">"a store room door"</span>;
nns[STORE_DOOR].can_carry = false;
nns[STORE_DOOR].location = CORRIDOR;
nns[MAGNET].word = <span class="Apple-style-span" style="color: #0b5394;">"MAGNET"</span>;
nns[MAGNET].code = MAGNET;
nns[MAGNET].description = <span class="Apple-style-span" style="color: #0b5394;">"a magnet"</span>;
nns[MAGNET].can_carry = true;
nns[MAGNET].location = NONE;
<span class="Apple-style-span" style="color: #274e13;">// See complete code at the end of this post for the rest of this...</span>
</pre>
<br />
Then there is the declaration of the array of the structure and the call to that set_nouns function inside main()...<br />
<br />
<pre> noun nouns[NOUNS];
set_nouns(nouns);
</pre>
<br />
And finally the addition of a NOUN argument to both the parser function and the call from within main()...<br />
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// In the declaration of the parser parameters, add the noun *nns pointer.</span>
bool parser(int &loc, string wd1, string wd2, word *dir, word *vbs, room *rms, <span class="Apple-style-span" style="color: #783f04;">noun *nns</span>)
{
<span class="Apple-style-span" style="color: #274e13;">// ..... parser code ....</span>
}
<span class="Apple-style-span" style="color: #274e13;">// And, where the parser is called from within main(), pass the array of the noun structure...</span>
if(word_1 != <span class="Apple-style-span" style="color: #0b5394;">"QUIT"</span>)
{
parser(location, word_1, word_2, directions, verbs, rooms, <span class="Apple-style-span" style="color: #783f04;">nouns</span>);
}
</pre>
<br />
The change is highlighted in dark orange. That should make the code compilable again, ableit without any functionality for the nouns yet. For that I will need to implement my NOUN trap, very similar to the previously used VERB trap. Almost all the work from here on is going to be done inside the parser function. So, remember this, the VERB trap?<br />
<br />
<pre>int VERB_ACTION = NONE;
for(i = 0; i < VERBS; i++)
{
if(wd1 == vbs[i].word)
{
VERB_ACTION = vbs[i].code;
break;
}
}
</pre>
<br />
The NOUN trap is going to look like this...<br />
<br />
<pre>int NOUN_MATCH = NONE;
if(wd2 != <span class="Apple-style-span" style="color: #0b5394;">""</span>)
{
for(i = 0; i < NOUNS; i++)
{
if(wd2 == nns[i].word)
{
NOUN_MATCH = nns[i].code;
break;
}
}
}
</pre>
<br />
<br />
Except that here the contents of variable word_2 (here in its function passed form, wd2) is examined to see if it is empty. That is to say, the parser will always expect a verb (or a direction), even if on its own, but the inclusion of nouns is optional.<br />
<br />
<span class="Apple-style-span" style="color: #666666;">SUPPLEMENTARY NOTE:</span><br />
<span class="Apple-style-span" style="color: #666666;">The vbs[i].code and nns[i].code members of the verb and noun structures are a bit superfluous, at this point, for both verbs and nouns. Their value would only become apparent when using synonyms for verbs and nouns, and that only if it is done in a particular way. In future blogs I might do a demo of a couple these "synonym techniques" that I have played about with. For the moment, one could dispense completely with that "x.code" member and simply have the trap work like this, for example;</span><br />
<br />
<pre><span class="Apple-style-span" style="color: #666666;">if(wd2 == nns[i].word)</span>
<span class="Apple-style-span" style="color: #666666;">{</span>
<span class="Apple-style-span" style="color: #666666;"> NOUN_MATCH = i;</span>
<span class="Apple-style-span" style="color: #666666;"> break;</span>
<span class="Apple-style-span" style="color: #666666;">}</span>
</pre>
<span class="Apple-style-span" style="color: #666666;">...and I suspect it would still do the trick...</span><br />
<br />
Anyway, back to the noun trap. This will be located inside the parser function, right under the verb trap. The NOUN_MATCH variable will be initialized as a value of NONE (-1). After that, if the variable word_2 is not empty, the loop will scan through its list of nns.word members. If it finds a match, it will assign the respective nns.code member value to the NOUN_MATCH variable, and break out of the for-loop. Otherwise (if it does not find a match or there was no content to word_2) the value NONE of NOUN_MATCH will remain for the rest of the parser's function.<br />
<br />
Now, how to use that "trapped" noun? Well, this is where the "coding slog" comes in. First, a valid verb must have preceded the noun. The combination should also make sense. Whether it does or not is up to the programmer, really. For example, a condition for the command "OPEN MONEY" could be programmed, but it is not very logical. For these cases, a simple default answer from the parser, for each verb, should be implemented. Something like "That cannot be opened". With that, the parser returns to the main loop waiting for the next command.<br />
<br />
If the condition is coded, however, a result of the action should occur. For example, if access from one room to another is barred by a door, then the command "OPEN DOOR" should then permit access to the adjoining room. That is the idea, now I will start to implement that. Here is a bit of code that opens a door. It goes after the VERB_ACTION == LOOK segment...<br />
<br />
<pre>if(VERB_ACTION == OPEN)
{
if(NOUN_MATCH == STORE_DOOR)
{
if(loc == CORRIDOR || loc == STOREROOM)
{
if(door_state == false)
{
door_state = true;
cout << <span class="Apple-style-span" style="color: #0b5394;">"I have opened the door."</span> << endl;
return true;
}
else if(door_state == true)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"The door is already open."</span> << endl;
return true;
}
}
else
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"There is no door to open here."</span> << endl;
return true;
}
}
else
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"Opening that is not possible."</span> << endl;
return true;
}
}
</pre>
<br />
Before I say anything about this piece of code, I want to draw attention to a variable that occurs in there. It is the door_state variable. This is a "flag", and is ideally suited to being a bool type variable. The door is either open (true) or closed (false). A look at the code will make this pretty self evident, I think, if the parser response is used as a guide to what happens. This flag I have chosen to declare as a function level <a href="http://www.cprogramming.com/tutorial/statickeyword.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">static</span></b></a> bool variable. Its declaration is at the beginning of the parser code, and looks like this...<br />
<br />
<pre>static bool door_state = false;
</pre>
<br />
The section of code is not actually complete. Just setting the door_state variable to true or false does not automatically open and close the door. Some more work needs to be done elsewhere in the code. However, in effect at least, we are now able to open the door and see that change reflected in the feedback from the running program. The program could at this stage be compiled and tried again. Here is an output....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcgKSf6qBUBIyBW-aZEtD4xUjc8q6x1FBGp3olSu3xD6MS1rAtLLPW2IwSoDl_nb4sYP5ZL1YYEOVeejCxix0NuEghGDeAucByyc-UnJ9ZoHj52kX2ZTUybfRQcX40tPCtciX-xc144xw/s1600/txtadv_fig4a.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcgKSf6qBUBIyBW-aZEtD4xUjc8q6x1FBGp3olSu3xD6MS1rAtLLPW2IwSoDl_nb4sYP5ZL1YYEOVeejCxix0NuEghGDeAucByyc-UnJ9ZoHj52kX2ZTUybfRQcX40tPCtciX-xc144xw/s1600/txtadv_fig4a.png" width="400" /></a></div>
<br />
There is also one other thing to notice in that code. Doors tend to be in both the rooms that they interconnect, unless the aim is to make a game in which there is a one way door into another dimension, of course. So, the player should be able to see the door in the corridor and in the store room. The player should also be able to interact with the door from both rooms. Therefore the line in the code....<br />
<br />
<pre>if(loc == CORRIDOR || loc == STOREROOM)
</pre>
<br />
That is, "if the player's location is the corridor or the storeroom". However, the noun structure only caters for one location. So the door noun itself can only either be in the corridor or in the store room at any one time. The solution is pretty simple. When the player is in either the corridor or in the store room, change the location of the door noun to the player's current location.<br />
<br />
This part is very easy, though it does contain something to consider regarding the LOOK command and nouns. The first thing to do is to make nouns visible to the LOOK command. This will require a modification to the LOOK function itself. It is so simple that just showing a before and after sample of the code will make it all plainly obvious...<br />
<br />
BEFORE:
<br />
<pre>void look_around(int loc, room *rms, word *dir)
{
int i;
cout << <span class="Apple-style-span" style="color: #0b5394;">"I am in a "</span> << rms[loc].description << <span class="Apple-style-span" style="color: #0b5394;">"."</span> << endl;
for(i = 0; i < DIRS; i++)
{
if(rms[loc].exits_to_room[i] != NONE)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"There is an exit "</span> << dir[i].word << <span class="Apple-style-span" style="color: #0b5394;">" to a "</span> << rms[rms[loc].exits_to_room[i]].description << <span class="Apple-style-span" style="color: #0b5394;">"." </span><< endl;
}
}
}
</pre>
<br />
AFTER:
<br />
<pre>void look_around(int loc, room *rms, word *dir, noun *nns)
{
int i;
cout << <span class="Apple-style-span" style="color: #0b5394;">"I am in a " </span><< rms[loc].description << <span class="Apple-style-span" style="color: #0b5394;">"."</span> << endl;
for(i = 0; i < DIRS; i++)
{
if(rms[loc].exits_to_room[i] != NONE)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"There is an exit "</span> << dir[i].word << <span class="Apple-style-span" style="color: #0b5394;">" to a "</span> << rms[rms[loc].exits_to_room[i]].description << <span class="Apple-style-span" style="color: #0b5394;">"."</span> << endl;
}
}
for(i = 0; i < NOUNS; i++)
{
if(nns[i].location == loc)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"I see "</span> << nns[i].description << <span class="Apple-style-span" style="color: #0b5394;">"."</span> << endl;
}
}
}
</pre>
Yeah, that is it. Add a for-loop that scans through all the nouns. If the noun happens to be at the same location as the player (nns[i].location == loc), then inform the player that it can be seen with a cout output. Note the new noun *nns argument in the parameters.<br />
<br />
The call to the LOOK command also needs to be modified to include the new noun argument, passing it from within the parser.<br />
<br />
<pre> if(VERB_ACTION == LOOK)
{
look_around(loc, rms, dir, nns);
return true;
}
</pre>
<br />
Now, let us look at the consideration about LOOK command and nouns: This command is going to need further modification later on when I implement the GET and DROP commands. If an object (noun) is carried, then it is obviously(?) going to be in the same room as the player, but I will not want the LOOK command to list objects that are in the player's INVENTORY. It would be tedious, seeing as the INVENTORY command is specifically going to serve that function. In fact, there are two solutions. 1. A separate, inaccessible "pseudo-room" could be created, for the sole purpose of, say, being the players "pocket", eliminating the need for the LOOK command to filter out "carried objects". Or 2. just as simple, actually do the filtering, with an INVENTORY array that contains "slots" for the objects, which the LOOK command skips over. That, therefore, is a primer of a future issue.<br />
<br />
Finally, to wrap up this post, I want to get the door actually doing what a door should do: stopping access when it is closed and permitting it when open. The easiest way to do this is to write code that modifies the room exits depending on the door position. As the door starts in the game closed, the exits east from CORRIDOR and west from STOREROOM should be assigned a value of NONE. That is, the rooms are NOT interconnected, initially. I will therefore modify the set_rooms function.<br />
<br />
Where they looked like this...<br />
<br />
<pre>void set_rooms(room *rms)
{
<span class="Apple-style-span" style="color: #274e13;">// .....</span>
rms[CORRIDOR].description.assign(<span class="Apple-style-span" style="color: #0b5394;">"corridor"</span>);
rms[CORRIDOR].exits_to_room[NORTH] = LOBBY;
rms[CORRIDOR].exits_to_room[EAST] = STOREROOM;
rms[CORRIDOR].exits_to_room[SOUTH] = GARDEN;
rms[CORRIDOR].exits_to_room[WEST] = NONE;
rms[STOREROOM].description.assign(<span class="Apple-style-span" style="color: #0b5394;">"store room"</span>);
rms[STOREROOM].exits_to_room[NORTH] = NONE;
rms[STOREROOM].exits_to_room[EAST] = NONE;
rms[STOREROOM].exits_to_room[SOUTH] = NONE;
rms[STOREROOM].exits_to_room[WEST] = CORRIDOR;
<span class="Apple-style-span" style="color: #274e13;">// .....</span>
}
</pre>
<br />
I will rewrite that to look like this...<br />
<br />
<pre>void set_rooms(room *rms)
{
<span class="Apple-style-span" style="color: #274e13;">// .....</span>
rms[CORRIDOR].description.assign(<span class="Apple-style-span" style="background-color: white;"><span class="Apple-style-span" style="color: #0b5394;">"corridor"</span></span>);
rms[CORRIDOR].exits_to_room[NORTH] = LOBBY;
<span class="Apple-style-span" style="color: #274e13;">//rms[CORRIDOR].exits_to_room[EAST] = STOREROOM;</span>
rms[CORRIDOR].exits_to_room[EAST] = NONE;
rms[CORRIDOR].exits_to_room[SOUTH] = GARDEN;
rms[CORRIDOR].exits_to_room[WEST] = NONE;
rms[STOREROOM].description.assign(<span class="Apple-style-span" style="color: #0b5394;">"store room"</span>);
rms[STOREROOM].exits_to_room[NORTH] = NONE;
rms[STOREROOM].exits_to_room[EAST] = NONE;
rms[STOREROOM].exits_to_room[SOUTH] = NONE;
<span class="Apple-style-span" style="color: #274e13;">//rms[STOREROOM].exits_to_room[WEST] = CORRIDOR;</span>
rms[STOREROOM].exits_to_room[WEST] = NONE;
<span class="Apple-style-span" style="color: #274e13;">// .....</span>
}
</pre>
<br />
Now I will zip along to the parser function and zero in on the condition for the verb OPEN, specifically this part...<br />
<br />
<pre>if(loc == CORRIDOR || loc == STOREROOM)
{
if(door_state == false)
{
door_state = true;
cout << <span class="Apple-style-span" style="color: #0b5394;">"I have opened the door."</span> << endl;
return true;
}
else if(door_state == true)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"The door is already open."</span> << endl;
return true;
}
}
</pre>
<br />
If the door state is changed from false to true, I will want to enable the passage between the CORRIDOR and the STOREROOM, so I will add this bit of code (just two lines), beneath the door_state = true;<br />
<br />
<pre>if(door_state == false)
{
door_state = true;
rms[CORRIDOR].exits_to_room[EAST] = STOREROOM;
rms[STOREROOM].exits_to_room[WEST] = CORRIDOR;
cout << <span class="Apple-style-span" style="color: #0b5394;">"I have opened the door."</span> << endl;
return true;
}
</pre>
<br />
And the passage is enabled. When the CLOSE verb is implemented, the reverse can be done to shut the door.<br />
<br />
It would be nice to have the LOOK command report whether the door is open or closed, incidentally. There are two ways I could do this. First, I could pass the door_state variable to the look_around function, and do a conditional cout. Something like this, in the function...<br />
<br />
<pre>if(loc == CORRIDOR || loc == STOREROOM)
{
if(door_state == false)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"The door is closed"</span> << endl;
}
else
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"The door is open"</span> << endl;
}
}
</pre>
<br />
Or I could do it the cheap and nasty way, altering the nns[STORE_DOOR].description in the parser, like this...<br />
<br />
<pre>if(door_state == false)
{
door_state = true;
rms[CORRIDOR].exits_to_room[EAST] = STOREROOM;
rms[STOREROOM].exits_to_room[WEST] = CORRIDOR;
nns[STORE_DOOR].description.clear();
nns[STORE_DOOR].description.assign(<span class="Apple-style-span" style="color: #0b5394;">"an open store room door."</span>);
cout << <span class="Apple-style-span" style="color: #0b5394;">"I have opened the door."</span> << endl;
return true;
}
</pre>
<br />
Anyway, that is the beginning of integrating a verb/noun parser, and some interaction with the nouns. I now have a door in the game that actually works. Next time, I will play around with picking up an object and putting it in the inventory, and maybe even using it! However, this would be just about it for this post, really. The code can now be compiled and tested.<br />
<br />
<a href="http://pastebin.com/UFzveurA" target="_blank"><b><span class="Apple-style-span" style="color: blue;">Here is the code...</span></b></a><br />
<br />
And here is another sample output...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTqe-A5l5ufMs-DqdAVpmWd_Qjej1HbOsZ1oiSXZ1ISAI9WLkmHkViUthYHNQodwT34-SBMBXrYqbthVQdrXtYHLI_cT0Ejbu5F0cJfsfZ5YKS3B27uvFr9jFKnUcS19DlYGm31Dpmcs4/s1600/txtadv_fig4b.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTqe-A5l5ufMs-DqdAVpmWd_Qjej1HbOsZ1oiSXZ1ISAI9WLkmHkViUthYHNQodwT34-SBMBXrYqbthVQdrXtYHLI_cT0Ejbu5F0cJfsfZ5YKS3B27uvFr9jFKnUcS19DlYGm31Dpmcs4/s1600/txtadv_fig4b.png" width="400" /></a></div>
<br />
<br />
And here is a chunk of code that can be added to the parser, right after the end of OPEN command conditions, to enable the closing of the door...<br />
<br />
<pre>if(VERB_ACTION == CLOSE)
{
if(NOUN_MATCH == STORE_DOOR)
{
if(loc == CORRIDOR || loc == STOREROOM)
{
if(door_state == true)
{
door_state = false;
rms[CORRIDOR].exits_to_room[EAST] = NONE;
rms[STOREROOM].exits_to_room[WEST] = NONE;
nns[STORE_DOOR].description.clear();
nns[STORE_DOOR].description.assign(<span class="Apple-style-span" style="color: #0b5394;">"a closed store room door"</span>);
cout << <span class="Apple-style-span" style="color: #0b5394;">"I have closed the door."</span> << endl;
return true;
}
else if(door_state == true)
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"The door is already closed."</span> << endl;
return true;
}
}
else
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"There is no door to close here."</span> << endl;
return true;
}
}
else
{
cout << <span class="Apple-style-span" style="color: #0b5394;">"Closing that is not possible."</span> << endl;
return true;
}
}
</pre>
<br />
Bye now.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com6tag:blogger.com,1999:blog-782137547904165829.post-40028466710766302932012-11-20T18:43:00.000-05:002013-01-16T22:30:25.470-05:00Text Adventure Games C++ Part 3<b><u><i>Verbs introductory</i></u></b><br />
Once again picking up where I left off, this time for a little action, as in verbs. Check my previous posts on the subject of Text Adventures; <a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-1.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">FIRST POST</span></b></a> and <a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-2.html" target="_blank"><span class="Apple-style-span" style="color: blue;"><b>SECOND POST</b></span></a>. And here is what I am going to cover this time around.<br />
<br />
1. Implement some simple VERBS.<br />
2. Get the parser to handle a verb.<br />
3. Demonstrate a sub procedure from the parser (the LOOK procedure).<br />
<br />
So, once again, enumerated types come to the rescue here. Using the completed <a href="http://pastebin.com/XfszrVKS" target="_blank"><b><span class="Apple-style-span" style="color: blue;">source code</span></b></a> from my previous post as a starting point, I will specify some enumerated identifiers for some VERBS. Right after en_ROOMS, I globally set the following verbs...<br />
<br />
<pre>enum en_VERBS {GET, DROP, USE, OPEN, CLOSE, EXAMINE, INVENTORY, LOOK};
</pre>
<br />
That is, eight verbs. These are pretty common verbs in text adventures. I am not going to get all of them working in the parser by the end of this post, but just "sample"; LOOK.<br />
<br />
For the purpose of looping through the verbs (should it be necessary at some stage), I am also going to define a global constant for them, right after const int ROOMS in the source code...<br />
<br />
<pre>const int VERBS = 8;
</pre>
<br />
Now, verbs will reutilize the word structure that was previously used for directions, in order to set the words and assign the code identifiers for the verbs. In main(), before the loop, I will declare that new array, and call a function to set the verbs...<br />
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// In main()</span>
word verbs[VERBS];
set_verbs(verbs);
</pre>
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// Function to set the verbs...</span>
void set_verbs(word *vbs)
{
<span class="Apple-style-span" style="color: #274e13;">// Reminder GET, DROP, USE, OPEN, CLOSE, EXAMINE, INVENTORY, LOOK</span>
vbs[GET].code = GET;
vbs[GET].word = "GET";
vbs[DROP].code = DROP;
vbs[DROP].word = "DROP";
vbs[USE].code = USE;
vbs[USE].word = "USE";
<span class="Apple-style-span" style="color: #274e13;">// and so on, see the source code in the link for the rest...</span>
}
</pre>
<br />
Great, I have some verbs set. Now, to use them. But first, the parser must receive the list of verbs as a parameter, so this modification is in order.<br />
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// Where the parameters of the parser function read...</span>
bool parser(int &loc, string wd1, string wd2, word *dir, room *rms)
{
<span class="Apple-style-span" style="color: #274e13;">// code...</span>
}
<span class="Apple-style-span" style="color: #274e13;">// It will now read...</span>
bool parser(int &loc, string wd1, string wd2, word *dir, word *vbs, room *rms)
{
<span class="Apple-style-span" style="color: #274e13;">// code...</span>
}
</pre>
<br />
And the call to the parser from inside the loop in main() will pass verbs as a parameter...<br />
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// Where the call was...</span>
parser(location, word_1, word_2, directions, rooms);
<span class="Apple-style-span" style="color: #274e13;">// It will now be...</span>
parser(location, word_1, word_2, directions, verbs, rooms);
</pre>
<br />
With that I am all set to start interacting on a very basic level with some of the verbs held in the array. A good starting point is the LOOK verb. It is common in all adventure games I ever played, and is a simple one word command that the player enters. It is also a good example here because it will be an exceptional case of sub proceduralizing something out of the parser. Generally, my own rule of thumb is to make a procedure for anything that may be used by more parts of a program than one. LOOK could be used twice by the program, for example;<br />
<br />
1. When the player types LOOK as a command, and;<br />
2. Automatically invoked whenever the player changes location from one room to another.<br />
<br />
Okay, I will move into the parser and do that "trapping" of the VERB. In the parser I will declare a local variable to catch the verb that is passed to it, using my old NONE as -1 to initialize it...<br />
<br />
<pre><span class="Apple-style-span" style="color: #274e13;">// In the parser function...</span>
int VERB_ACTION = NONE;
</pre>
<br />
And now the simple "verb trap", which is nothing more than a for() loop that compares word_1 (wd1, as passed) to the list of verb words. When a word coincides, it assigns the code of that verb to the variable VERB_ACTION and breaks out of the loop. If no coincidence at all occurs, then the variable VERB_ACTION remains as initialized (NONE). Here is that chunk of code, to be placed in the parser function right after the direction for() loop...<br />
<br />
<pre> for(i = 0; i < VERBS; i++)
{
if(wd1 == vbs[i].word)
{
VERB_ACTION = vbs[i].code;
break;
}
}
</pre>
<br />
And with that I can go on to examine the variable VERB_ACTION. The first logical thing to do would be to deal with a value of NONE, that is to say, the player did not enter a recognized verb.<br />
<br />
<pre> if(VERB_ACTION == NONE)
{
cout << <span class="Apple-style-span" style="color: blue;">"No valid command entered."</span> << endl;
return true;
}
</pre>
<br />
Personally, I would put this condition check at the very end of the parser function and all other condition checks above it, but that's just me. Now for the LOOK command condition check, which upon validating true simply calls the look_around() function...<br />
<br />
<pre> if(VERB_ACTION == LOOK)
{
<span class="Apple-style-span" style="color: #274e13;">// This is an example of sub proceduralizing a function from the parser.</span>
look_around(loc, rms, dir);
return true;
}
</pre>
<br />
And here is that function...<br />
<br />
<pre>void look_around(int loc, room *rms, word *dir)
{
int i;
cout << <span class="Apple-style-span" style="color: blue;">"I am in a "</span> << rms[loc].description << endl;
<span class="Apple-style-span" style="color: #274e13;">// LOOK should also allow the player to see what exits exist from the current room.</span>
for(i = 0; i < DIRS; i++)
{
if(rms[loc].exits_to_room[i] != NONE)
{
cout << <span class="Apple-style-span" style="color: blue;">"There is an exit "</span> << dir[i].word << <span class="Apple-style-span" style="color: blue;">" to a "</span> << rms[rms[loc].exits_to_room[i]].description << <span class="Apple-style-span" style="color: blue;">"."</span> << endl;
}
}
}
</pre>
<br />
That can all be compiled and tested now. Not much, as yet. I can walk around my map, and use one "verb" command. But the stage is set to move onto to the next phase, on <b><u><a href="http://cplussplussatplay.blogspot.com/2013/01/text-adventure-games-c-part-4.html" target="_blank">next post</a></u></b>. That is, nouns, better known as items or objects.<br />
<br />
Here is the <a href="http://pastebin.com/4ZzeCLvw" target="_blank"><span class="Apple-style-span" style="color: blue;"><b>complete source code</b></span></a> for what has been done this time around.<br />
<br />
And here is a figure of the running program.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2ZDymWeGjE7lSdCKWNIqsSqCJgN0T5rIyXuCWtk2DhZNqKT67t7KbZgcO2HY79qCpDwd3yc1Uoa74i0ppljn194ySIYaM17GOhStf4l8Y6rjeu-j5Ak4iN3hmNXjz0Ybdn-gGgvS2aaI/s1600/txtadv_fig3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2ZDymWeGjE7lSdCKWNIqsSqCJgN0T5rIyXuCWtk2DhZNqKT67t7KbZgcO2HY79qCpDwd3yc1Uoa74i0ppljn194ySIYaM17GOhStf4l8Y6rjeu-j5Ak4iN3hmNXjz0Ybdn-gGgvS2aaI/s400/txtadv_fig3.png" width="400" /></a></div>
<br />
<br />
That's all for now.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com1tag:blogger.com,1999:blog-782137547904165829.post-17197697352471400292012-11-10T14:35:00.000-05:002012-11-20T18:46:23.808-05:00Text Adventure Games C++ Part 2<br />
So, moving on (quite literally for this post) I am <b><a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-1.html" target="_blank">picking up where I left off from my last post</a> </b>(entering commands), and am here am going to design a game map and move around inside it with some basic commands. Before actually diving in and trying to program the map, it is a good idea to draw out a map of my world, something like this...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_gXuUNK57pmwvlLOC780RLuKS_N3RQjCSI50S5EF4WhMbdDKPvGLxDGux3sLW46U9jP6JxU_frzGgP0QAiCzWyjB_jAOl2eeHVu7gNr341ChcV1oSHZoo2TmXhZcLITkbOs8SeCFFiGo/s1600/game_map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_gXuUNK57pmwvlLOC780RLuKS_N3RQjCSI50S5EF4WhMbdDKPvGLxDGux3sLW46U9jP6JxU_frzGgP0QAiCzWyjB_jAOl2eeHVu7gNr341ChcV1oSHZoo2TmXhZcLITkbOs8SeCFFiGo/s400/game_map.png" width="268" /></a></div>
<br />
It should go without saying, at this point, that anyone writing an "adventure game" should already have an engrossing "adventure story" in mind.<br />
<br />
I will keep it simple. Exits from a room will be only north, east, south and west. I will not do any northeast or up or down type of thing here (though there is no impediment for doing so in more complex maps). First I will number my rooms, from 0 to 10 (that is, eleven rooms). I will also consider my exits to be from 0 to 3, going around clockwise from north (0). Now the problem with using numbers as identifiers is that I will need to write down what each one "equates to" on a sheet of paper and keep track of it all the time I am programming. For example;<br />
<br />
0 is a SPORTSHOP<br />
1 is a CASINO... and so on,<br />
<br />
...for the rooms, and for the directions...<br />
<br />
0 is NORTH,<br />
1 is EAST... and so on.<br />
<br />
This is the perfect sitaution for using enumerated types. So, first thing; at the top of my code, just after the #includes, I will declare some enumerated types globally for the directions and the rooms...<br />
<br />
<pre>enum en_DIRS {NORTH, EAST, SOUTH, WEST};
enum en_ROOMS {SPORTSHOP, CASINO, CARPARK, LOBBY, RESTAURANT, CORRIDOR, STOREROOM, POOL, GARDEN, POND, PUMPROOM};
const int NONE = -1;
const int DIRS = 4;
const int ROOMS = 11;
</pre>
<br />
I will also want an identifier for NO EXIT; check out the map, not all rooms have exits in all directions. I will use a constant integer -1, above declared as NONE. Also, for the purpose of looping through the DIRECTIONS and ROOMS, which will probably be required later on, I will set their number with another couple of constants, DIRS and ROOMS.<br />
<br />
Now the programming will be instantly easier to understand. In pseudo code...<br />
<br />
<span class="Apple-style-span" style="color: #0c343d;"><i>if direction commanded is 1 and location is 2 then</i></span><br />
<span class="Apple-style-span" style="color: #0c343d;"><i> location = 3</i></span><br />
<br />
...is certainly more difficult to follow than...<br />
<br />
<i><span class="Apple-style-span" style="color: #0c343d;">if direction commanded is EAST and location is CARPARK then</span></i><br />
<i><span class="Apple-style-span" style="color: #0c343d;"> location = LOBBY</span></i><br />
<br />
<b><i><u>The Map</u></i></b><br />
But that is just an example. For now, it would be convenient to make a global structure for holding information about each room. And while I am at it, a global structure for words, which will be a multipurpose structure to hold both directions and verbs (ie; basically commands). So, under those constant integers, I will code this;<br />
<br />
<pre>struct word
{
string word;
int code;
};
struct room
{
string description;
int exits_to_room[DIRS];
};
</pre>
<br />
That is a basic minimum for each of those structures. I will come back to the word structure in a minute. For now, I want to focus on the room structure. First it has a description. That is just a line of text that offers a description of the room itself, for example "car park". It will be displayed in the game for the player to know where he (or she*) is. The exits_to_room variable, on the other hand is an array, and is the size of the number of directions that the game handles (ie; four of them). Each room has four exits. It does not matter that some of these exits in the game do not exist for specific rooms (for example, you cannot go south or west from the carpark). That is what the NONE constant is for. The exit is still there, it just cannot be used in the game.<br />
<br />
Just as a note, exits_to_room should be considered as "room interconnectivity". That is to say (normally) if room 0 exits south to room 2, then room 2 should exit north to room 0. I know deeper consideration of this can lead to ideas of all sorts of weird maps that have exits leading to unexpected rooms, but I sincerely think that it is generally NOT a good idea to deliberately confuse the player like this. The adventure should be the challenge, not the map, unless there is a very good reason why part of the map should be a challenge (for example, the player dies but the game continues with him/her in a strange Labyrinth of the Underworld, trying to get back to life. Heh! Heh! A "serving suggestion").<br />
<br />
With that clarified, I will continue and "set" all the rooms for the game. This first requires an array of the structure room to be created in main(). So, somewhere in the beginning of the main() block, before the while() loop, I create;<br />
<br />
<pre>room rooms[ROOMS];
set_rooms(rooms);
</pre>
<br />
That second line in there is a call to a separate function that is yet to be made, and which will set my rooms characteristics (that is, its description and its exits). Here is that function, in part (see the full listing at the end of this post for the complete version).<br />
<br />
<pre>void set_rooms(room *rms)
{
rms[SPORTSHOP].description.assign("sports shop");
rms[SPORTSHOP].exits_to_room[NORTH] = NONE;
rms[SPORTSHOP].exits_to_room[EAST] = NONE;
rms[SPORTSHOP].exits_to_room[SOUTH] = CARPARK;
rms[SPORTSHOP].exits_to_room[WEST] = NONE;
rms[CASINO].description.assign("bustling casino");
rms[CASINO].exits_to_room[NORTH] = NONE;
rms[CASINO].exits_to_room[EAST] = NONE;
rms[CASINO].exits_to_room[SOUTH] = LOBBY;
rms[CASINO].exits_to_room[WEST] = NONE;
// ...and so on untill all rooms and their exits are completed.
}
</pre>
<br />
Take care doing this. The structure array is passed to the function, and the number of rooms described in the function simply MUST not exceed the size of the array (const int ROOMS), or bad things will happen to the program when it is run. When adding or removing rooms to the map (say, if I want to expand the game) I must first change the value of the global ROOMS, then edit the enumerated type en_ROOMS to include (or suppress) rooms, and finally edit the rooms and their exits in this set_rooms function.<br />
<br />
So great, I have my map now, but it would be nice to move around it, too. Here is where I will take the first step in bringing together the command interface from the previous post with what I have done so far on this post. For this, I will start to create that thing which is the heart of an adventure game, from a programming point of view. The Parser.<br />
<br />
<b><i><u>The Parser (Rudimentary)</u></i></b><br />
So. What is the parser? It is a block of code (I am going to create it as a separate function) that takes a command entered by the player, interprets it, and then executes the command. It can be very lengthy. Efforts can be made to reduce its core size by "proceduralizing" it somewhat (other, smaller sub functions), but I will not worry about that for now. The parser must recognize "words", the structure for which has already been created above. In this case, the parser will receive either one or two words (a direction or a verb and a noun). There will be much more about the parser in future posts; this time around? Well, it stays simple. It will receive direction commands only, so that I may at least navigate my map.<br />
<br />
A good place to start, therefore, would be setting the "words" (directions, in this case). So, inside main(), I declare the array of the structure word as directions.<br />
<br />
<pre>word directions[DIRS];
set_directions(directions);
</pre>
<br />
...and I then write the function that sets the directions...<br />
<br />
<pre>void set_directions(word *dir)
{
dir[NORTH].code = NORTH;
dir[NORTH].word = "NORTH";
dir[EAST].code = EAST;
dir[EAST].word = "EAST";
// ...and so on, through SOUTH and up to WEST.
}
</pre>
<br />
Now that I have set the directions that I will use, I need the way for the player to actually employ them through text commands. Basically, this means that when the player types "east" and presses enter, the program will take the word and check the current room for exits, and if there is an exit to the east, it will transport the player's location to that room. For this to happen, I am first going to need a variable that actually holds the players current position. I will call it the "location" variable, which makes it instantly recognizable. It gets declared and initialized at the top of the main() function...<br />
<br />
<pre>int location = CARPARK; // using the enumerated type identifier, of course.
</pre>
<br />
Further down in the main() function, inside the while loop, below the section_command() function call, I will call the parser() function, like this...<br />
<br />
<pre>parser(location, word_1, word_2, directions, rooms);
</pre>
<br />
Now I will actually write the rudimentary parser function itself...<br />
<br />
<pre>bool parser(int &loc, string wd1, string wd2, word *dir, room *rms)
{
int i;
for(i = 0; i < DIRS; i++)
{
if(wd1 == dir[i].word)
{
if(rms[loc].exits_to_room[dir[i].code] != NONE)
{
loc = rms[loc].exits_to_room[dir[i].code];
cout << "I am now in a " << rms[loc].description << "." << endl;
return true;
}
else
{
cout << "No exit that way." << endl;
return true;
}
}
}
cout << "No valid command entered." << endl;
return false;
}
</pre>
<br />
Taken from the top. I create the parser as a bool function. At the moment that is not a very useful thing to do, but when that parser grows, returning a boolean may well be a useful method for error trapping later on. It does no harm at present.<br />
<br />
I then declare a local integer (i) to enable me to loop through commands. In this case, I start by looping through the directions. What I am doing is causing the program to compare what the player entered (and was passed to the parser as word_1) with the data for the member word in the structure directions. If word_1 coincides with a word member in the structure, then we have a hit, and the parser then checks if there actually is an exit form the current location in that direction (it checks the exits_to_room member of the room structure). If the result is something other than NONE, then there is an exit there, and the location variable (passed referrenced so that the function can permanently modify it) is moved to the room in that direction, using the directon code. It offers a description of the new room, and breaks out of the function, returning true. If there is no exit in the entered direction, the parser simply states so, and returns. And finally, if nothing that the player entered is recognized by the parser, then it returns false, telling the player that no valid command was entered.<br />
<br />
Though not apparent here, this method of using the word structure "code" instead of the "word" itself can considerably shorten the coding of the parser. Eventually, it will permit the use of synonyms, expanding the possibilities of what the player may enter as an acceptable text command. For example, a command like "GO" may also be expressed as "WALK", but both words will have the code GO, making the parser do the same thing for two different words that mean the same thing.<br />
<br />
The program can now be compiled and tested. I can now move around my map, entering simple direction commands like north, or east. Hooray! I am going places!<br />
<br />
<a href="http://pastebin.com/XfszrVKS" target="_blank"><b>Here is the code of what was done for this post.</b></a><br />
<br />
Here is a sample out put of what happens when the program is run.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-nVqTad044ZrDU0WHoCF-c0OHRsyMMdKqQ_uT8ixnHoF_PsCOJth0hSAIsk0sB6hveZtWj9TqsrjKK2yF8lMaQ0cgM1F0AaMuSSTsMm1nF9bNX_uF5-xWjhfglYNhcFCNT8Z7rcrKGLI/s1600/txtadv_fig2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-nVqTad044ZrDU0WHoCF-c0OHRsyMMdKqQ_uT8ixnHoF_PsCOJth0hSAIsk0sB6hveZtWj9TqsrjKK2yF8lMaQ0cgM1F0AaMuSSTsMm1nF9bNX_uF5-xWjhfglYNhcFCNT8Z7rcrKGLI/s400/txtadv_fig2.png" width="400" /></a></div>
<br />
<br />
<a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-3.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">Next time</span></b></a>, that parser will be expanded a little, and introduce verbs. As yet, it is only using word_1 of the entered text, and that word must be a direction for the present parser to accept it.<br />
<br />
That is all, for this post!<br />
<div>
<br /></div>
WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com3tag:blogger.com,1999:blog-782137547904165829.post-28394054828102126902012-11-04T14:43:00.000-05:002012-11-10T14:49:20.650-05:00Text Adventure Games C++ Part 1<br />
So, it has been a while again. Oh! How the demands of my job keep me from my beloved hobby!<br />
<br />
In the continuing exploration of C++, though short of time lately to really get into more intricate subjects, I decided to revert to something simpler and, surprisingly, never tried before by yours truly. Text adventures. Why this is surprising is two-fold. One: The <a href="http://www.youtube.com/watch?v=s4mWD-7LYSo" target="_blank"><span class="Apple-style-span" style="color: blue;"><b>first ever game I played</b></span></a> (on someone's <a href="http://oldcomputers.net/vic20.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">Commodore Vic 20</span></b></a>, lent to me for one afternoon sometime back in 1981 or so), and indeed my first ever experience of computers, was a text adventure (and it was to be my only experience of computers until my own Spectrum in 1986) . Two: It turns out to be quite easy to write a simple console style text adventure with the power of C++. Everyone should try it; it is fun!<br />
<br />
Warning: I decided to tackle the challenge completely unprimed; that is to say, without reference to any books or tutorials about the subject, in order that it be a test of my "problem solving abilities". Therefore, the results on this post may not be "standard", if such a thing exists, for text adventures.<br />
<br />
<b><u><i>First Steps:</i></u></b><br />
As far as I was able to deduce, text adventures are initially about two things, from a programming point of view. First, handling input text and second, moving around a map. Things like getting, dropping and using objects, and the occurrence of events in the adventure, will come later.<br />
<br />
Though I can see now that handling text strings could have been a little bit of a chore previously (in the days when text adventures were first popular), with the tools available in C++, it is pretty much a simple task. The idea goes something like this;<br />
<br />
1. The player inputs a line of instructions.<br />
2. The program divides the line into individual words.<br />
3. The program interprets the words and checks if the instruction makes any sense.<br />
4. The program executes the command, if it makes sense, or by default informs the player that the command made no sense.<br />
5. The program loops back to asking for a command to be input by the player.<br />
<br />
The first item is very easy, using C++'s standard iostream and string.<br />
<br />
<pre><span class="Apple-style-span" style="color: #38761d;">#include "iostream"
#include "string"</span>
using namespace std;
int main()
{
string command;
while(1 == 1) // <span class="Apple-style-span" style="color: #666666;">Temporary condition for now...</span>
{
command.clear();
cout << <span class="Apple-style-span" style="color: blue;">"What shall I do? "</span>;
getline(cin, command);
cout << <span class="Apple-style-span" style="color: blue;">"Your raw command was "</span> << command << endl;
}
return 0;
}
</pre>
<br />
I do not recommend compiling and running this fragment because of the infinite loop in the while statement, but this is as simple as simple gets for inputting a command. Now there are a few considerations, as I am about to move into the second item of the list; dividing the line into separate words. The first consideration is; how many words of the command do I want to be actually interpretted? For this example I want to keep it as simple as possible. By that I mean that I only want to interpret one or two words of the command, which restricts the player to either inputting a direction or a verb and a noun. For example, the player can input at the comand prompt...<br />
<br />
<i><span class="Apple-style-span" style="color: #444444;">What shall I do? north</span></i><br />
<br />
or...<br />
<br />
<i><span class="Apple-style-span" style="color: #444444;">What shall I do? get keys</span></i><br />
<br />
So, if the player inputs something like...<br />
<br />
<i><span class="Apple-style-span" style="color: #444444;">What shall I do? go get a club and hit yourself over the head with it</span></i><br />
<br />
...will result in the program only interpretting the words "go" and "get", which will be an invalid command input. Straight off the bat we can see that some ground rules are established. With that in mind, I will continue to expand the above snippet. I need to make a function that will section the raw command line into one or two words. So, first the expansion of main()...<br />
<br />
<pre><span class="Apple-style-span" style="color: #38761d;">#include "iostream"
#include "string"</span>
<span class="Apple-style-span" style="color: #38761d;">#include "vector"</span> <span class="Apple-style-span" style="color: #666666;">// For the command handling function.</span>
<span class="Apple-style-span" style="color: #38761d;">#include "cctype"</span> <span class="Apple-style-span" style="color: #666666;">// Will be used to eliminate case sensitivity problems.</span>
using namespace std;
int main()
{
string command;
string word_1;
string word_2;
while(word_1 != <span class="Apple-style-span" style="color: blue;">"QUIT"</span>)
<span class="Apple-style-span" style="color: #666666;">// I have provided an escape condition from the loop here</span>
{
command.clear();
cout << <span class="Apple-style-span" style="color: blue;">"What shall I do? "</span>;
getline(cin, command);
cout << <span class="Apple-style-span" style="color: blue;">"Your raw command was "</span> << command << endl;
word_1.clear();
word_2.clear();
<span class="Apple-style-span" style="color: #666666;">// Call the function that handles the command line format.</span>
section_command(command, word_1, word_2);
<span class="Apple-style-span" style="color: #666666;">// For test purposes, output the command after formatting by the function.</span>
cout << word_1 << <span class="Apple-style-span" style="color: blue;">" "</span> << word_2 << endl;
}
return 0;
}
</pre>
<br />
I have added vector and cctype headers, and two more strings (word_1 and word_2). These strings will hold the two words I want from the sectioned command line, for later parsing. In the while loop I also call a function (section_command) and pass it three arguments (command, word_1 and word_2). Here is that function, which does as its name implies; it sections the command.<br />
<br />
<pre>void section_command(string Cmd, string &wd1, string &wd2)
{
string sub_str;
vector<string> words;</string>
char search = <span class="Apple-style-span" style="color: #7f6000;">' '</span>;
size_t i, j;
<span class="Apple-style-span" style="color: #666666;">// Split Command into vector</span>
for(i = 0; i < Cmd.size(); i++)
{
if(Cmd.at(i) != search)
{
sub_str.insert(sub_str.end(), Cmd.at(i));
}
if(i == Cmd.size() - 1)
{
words.push_back(sub_str);
sub_str.clear();
}
if(Cmd.at(i) == search)
{
words.push_back(sub_str);
sub_str.clear();
}
}
<span class="Apple-style-span" style="color: #666666;">// Clear out any blanks
// I work backwords through the vectors here as a cheat not to invalidate the iterator</span>
for(i = words.size() - 1; i > 0; i--)
{
if(words.at(i) == <span class="Apple-style-span" style="color: blue;">""</span>)
{
words.erase(words.begin() + i);
}
}
<span class="Apple-style-span" style="color: #666666;">// Make words upper case
// Right here is where the functions from cctype are used</span>
for(i = 0; i < words.size(); i++)
{
for(j = 0; j < words.at(i).size(); j++)
{
if(islower(words.at(i).at(j)))
{
words.at(i).at(j) = toupper(words.at(i).at(j));
}
}
}
<span class="Apple-style-span" style="color: #666666;">// Very simple. For the moment I only want the first to words at most (verb / noun).</span>
if(words.size() == 0)
{
cout << <span class="Apple-style-span" style="color: blue;">"No command given"</span> << endl;
}
if(words.size() == 1)
{
wd1 = words.at(0);
}
if(words.size() == 2)
{
wd1 = words.at(0);
wd2 = words.at(1);
}
if(words.size() > 2)
{
cout << <span class="Apple-style-span" style="color: blue;">"Command too long. Only type one or two words (direction or verb and noun)"</span> << endl;
}
}
</pre>
<br />
This function uses both vector and cctype functions. The cctype function toupper() is used to turn any lower case letters in the command line into upper case. This process is to simplify the internal workings of the program. All text commands in the program will be interpreted in upper case, but the player is still free to input lower case.<br />
<br />
I want to go through that function bit by bit. I am going to use a space character to split the words of the command line, as it can be (reasonably) safe to assume that the command line will be input with spaces between words. A local substring will be used to "collect" the letters of each word in the command line, while no space occurs, and when a space occurs, it will push the substring (containing the word) into the local string vector. The substring is cleared, and the process is repeated for the next series of letters, either up to the next space, or to the end of the command line string, whichever happens first. By the end of the process, I will have a local string vector loaded with the individual words of the command line.<br />
<br />
That said, there is at least one possible and plausible problem. If the player, by typo mistake, entered more than one space between words, the above method will "collect" and pushback a blank word into the vector. In a rudimentary effort at error trapping, I will endevour to eliminate the occurence of these blank words. I search through the vector strings, from end to beginning (ie; backwards) looking for such blank words. If one is found, it is erased with an iterator reference from the beginning of the vector (note, I do not actually use an iterator, just the reference that is normally used to set an iterator). Yes, a bit crude, as there are other ways to do this, but doing it this way exempts me from having to reinitialize the vector iterator every time the vector changes size because a blank word was eliminated.<br />
<br />
Next, the remaining words in the vector need to be converted to upper case (as explained why above, and will become clearer later). The loop goes through each character in each vector string looking for the occurence of lower case characters and converting them to upper case.<br />
<br />
Finally, the first two words of the string vector only are returned to the main loop by the function (wd1 and wd2). The rest of what was written on the command line is, basically, wasted time by the player. So, why do I go through the trouble of "collecting" all the words in the command line? Future expansion! One day, I might want to create a text adventure that accepts more complex commands, like for example "open door using crowbar", instead of just "use crowbar". The section command function is already up to meeting that challenge as it is now.<br />
<br />
So, the program may be compiled and run now. I will have a console application that prompts me for a command and, after entering it, shows me the first two words of that command in upper case. Not very exciting, as yet, but more is coming on this somewhat archaic but none-the-less fun subject.<br />
<br />
<a href="http://pastebin.com/QR75RAMT" target="_blank"><b><span class="Apple-style-span" style="color: blue;">Click here for some code of what has been done on this post.</span></b></a><br />
<br />
Here are some references to the headers used in this post...<br />
<a href="http://www.cplusplus.com/reference/clibrary/cctype/" target="_blank"><b><span class="Apple-style-span" style="color: blue;">cctype</span></b></a><br />
<a href="http://www.cplusplus.com/reference/stl/vector/" target="_blank"><b><span class="Apple-style-span" style="color: blue;">vector</span></b></a><br />
<a href="http://www.cplusplus.com/reference/string/string/" target="_blank"><b><span class="Apple-style-span" style="color: blue;">string</span></b></a><br />
<a href="http://www.cplusplus.com/reference/iostream/" target="_blank"><b><span class="Apple-style-span" style="color: blue;">iostream</span></b></a><br />
<a href="http://www.dreamincode.net/forums/topic/33631-c-vector-tutorial/" target="_blank"><b><span class="Apple-style-span" style="color: blue;">more on vector</span></b></a><br />
<br />
And here is a sample output of the program so far...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh290IoiDv3CEfPY2GW_w945gz4u6oxkKSq19ZilFxKIiWQQVnpvvliUUxjE_UcEL6mUKQ-10uIHYDDnwOP2Y_IAUSt55OHXKPjOFfkD0pG-hwYIkZkrA8uQoSJ9qKEBAOd4p3iMo1YI2c/s1600/txtadv_fig1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh290IoiDv3CEfPY2GW_w945gz4u6oxkKSq19ZilFxKIiWQQVnpvvliUUxjE_UcEL6mUKQ-10uIHYDDnwOP2Y_IAUSt55OHXKPjOFfkD0pG-hwYIkZkrA8uQoSJ9qKEBAOd4p3iMo1YI2c/s400/txtadv_fig1.png" width="400" /></a></div>
<br />
Next time, <a href="http://cplussplussatplay.blogspot.com/2012/11/text-adventure-games-c-part-2.html" target="_blank"><b><span class="Apple-style-span" style="color: blue;">navigating a game world map</span></b></a>, where the enum types really come into their own!<br />
<br />
That's all, for now!<br />
<br />
<br />WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com6tag:blogger.com,1999:blog-782137547904165829.post-71272771165954122902012-08-02T23:07:00.001-05:002012-08-08T19:49:14.556-05:00Basic Trigonometry in Coding C/C++As I missed putting up a post for July, it is time I put up a post or two during August on this "semi-abandoned" blog. I have been busy with work, and though I have been doing the odd bit of coding, I have not had the time to compose a post for here. Nevertheless, here I am now.<br />
<br />
Nothing special this time. Just a review of some vital 2D trigonometry. There is little doubt that there is something slightly amiss with me, as I wake up each day as a blank sheet with regard to what I did or learned the day before, I have the bad feature of forgetting even important things until I refresh my memory with some trigger or another (and several coffees and a Red Bull). This post will my C++ trigonometry trigger!<br />
<br />
There will be no tags for this post to aid searching, as it is a bit of a personal rant. If you are someone other than me reading this, then you stumbled in on it. Don't worry, you may read it, if you like - just don't think of it as any sort of "lesson", please. There are many, much better, "learn trigonometry" pages out there.<br />
<br />
I'll start with the easiest thing. How does the computer figure trig? Well, quite normally, in essence. If I plotted a point using sin( ) and cos( ), this is what the computer "thinks" of as angles (in radians, of course)...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_pSdUJzWfdm4rE6qFzWhTjLXwsIv3ERI60sNytFrgbbIcPGWn0e5Oan1DMpl13BQRZePTaek6ijxkTEuFBri7vFiyzy0sheSSANcrOSERTLvCcxxax8CdVLJp_mfnJVLIuP9ihNtrUXA/s1600/trig_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_pSdUJzWfdm4rE6qFzWhTjLXwsIv3ERI60sNytFrgbbIcPGWn0e5Oan1DMpl13BQRZePTaek6ijxkTEuFBri7vFiyzy0sheSSANcrOSERTLvCcxxax8CdVLJp_mfnJVLIuP9ihNtrUXA/s400/trig_2.png" width="400" /></a></div>
<br />
...and to convert Radians to Degrees (and vice versa), the following formulas may be used...<br />
<br />
<span class="Apple-style-span" style="color: #351c75;"><b>Degrees = Radians x (180 / Pi)</b></span><br />
<span class="Apple-style-span" style="color: #351c75;"><b><br /></b></span>
<span class="Apple-style-span" style="color: #351c75;"><b>Radians = Degrees x (Pi / 180)</b></span><br />
<br />
Now it is a bit of a pain in the backside (if you are programming games that use maps) that, first; you must use radians instead of degrees, and second; your natural "north" (ie; 0.0) is pointing over to the right of the screen instead of up. However, no real hassle, as a little bit of coding can produce a neat header like <a href="http://pastebin.com/dqnkgeH8" target="_blank">this one of mine</a> to circumvent these problems, but that's another story.<br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">What are tan( ), atan( ), and atan2( )?</span></b><br />
First, here are the docs;<br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/tan/" target="_blank">tan</a><br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/atan/" target="_blank">atan</a><br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/atan2/" target="_blank">atan2</a><br />
<br />
They speak for themselves. I want to cover the basics here. Here is a right angled triangle...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHEWO0HubCFEQ6jwFcO8pJ9y1-70_C9k_i9cRuGTliMYHmKWgquvrkwd94JssbUQ0eBnN0RBEJ23DlQwJE2EC7_bWpVaTOSCoR_Rm877LTP9cS0M9s1o6wkfBc304n06eZIcJ5QHqu5Hw/s1600/trig_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHEWO0HubCFEQ6jwFcO8pJ9y1-70_C9k_i9cRuGTliMYHmKWgquvrkwd94JssbUQ0eBnN0RBEJ23DlQwJE2EC7_bWpVaTOSCoR_Rm877LTP9cS0M9s1o6wkfBc304n06eZIcJ5QHqu5Hw/s400/trig_1.png" width="400" /></a></div>
<br />
Now, I always had some trouble learning things other people's way. All that blah-blah about a line off a circle and such did not (ever) explain to me what a tangent was.<br />
<br />
A TANGENT is simply the length of one of the (right-angle) sides of the triangle divided by the other, basically ignoring the hypotenuse (c). It gives you (and is essentially) the ratio of the dividend to the divisor.<br />
<br />
So, one tangent is;<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">28 / 46 = 0.608695</span></b><br />
...the other is;<br />
<span class="Apple-style-span" style="color: #351c75;"><b>46 / 28 = 1.642857</b></span><br />
<br />
...and that's what all the hullabaloo is all about. Think of it as a fraction, if you like (28/46 or 46/28), and that is basically it. Think "The Ratio of the Side of the Triangle which is the Numerator to the side Side of the Triangle which is the Denominator in your Fraction". <br />
<br />
That ratio can recover the length of either of the sides if you know the other side's value.<br />
<span class="Apple-style-span" style="color: #351c75;"><b><span class="Apple-style-span" style="color: black;"><span class="Apple-style-span" style="font-weight: normal;"><br /></span></span></b></span>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="font-weight: normal;"><b>28 / 0.608695 = 46</b></span></span></b><br />
...and...<br />
<span class="Apple-style-span" style="color: #351c75;"><b><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;">46 x 0.608695 = 28</span></b></span></b></span><br />
<br />
Want to know the value of angle (A)? That is where ArcTangent comes in. Feed it the fraction...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Atan( 28 / 46 ) = 31.33º or 0.546788 rads</span></b><br />
<br />
...and you get the value of Angle (A). How about angle (B)? Feed it the other fraction.<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Atan( 46 / 28 ) = 58.67º or 1.024007 rads</span></b><br />
<br />
The Tan() function can be used if you know either of the angles formed by the right angled side with the hypotenuse. It will give you the ratio of the side OPPOSITE the angle you feed it...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Tan(58.67º) = 1.642772</span></b><br />
<br />
That's angle (B), giving us the ratio of side (b), with a small rounding error :).<br />
<br />
And if you know is the angle, let's say (A) and any of the sides, lets say (a), you may directly use the tangent of that angle and the known value to calculate the adjacent...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">28 / Tan(31.33º) = 46</span></b><br />
<br />
Yeah, because<span class="Apple-style-span" style="color: #351c75;"><b> Tan(31.33º) </b></span>is simply <span class="Apple-style-span" style="color: #351c75;"><b>0.608695</b></span>. Looks familiar now? No worries. Now; want to see an example of going nowhere?<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Atan( Tan( 31.33º ) ) = 31.33º</span></b><br />
<br />
Heh! Heh! Heh! Heh!<br />
<br />
But now I want to clarify a difference (C/C++) between atan( ) and atan2f( ). This snippet shows what happens when we attempt to get the angles of 8 points from an origin of zero, using both atan() and atan2f();<br />
<br />
<a href="http://pastebin.com/NWh4AdEz" target="_blank"><b><i>Pastebin_Sample_1_Link</i></b></a><br />
<div>
<br /></div>
<div>
So, let's have a look at that. Once the program is run it becomes immediately apparent that atan() and atan2f() seem to provide different answers for points 0, 6 and 7. Or do they really?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv7C_8Drv1tNwKP88bDsHfvt4XtZhdH9FKWKL_rX2Je_cx0uCaJcNwSQRN9b3qymzOIShStua98pTaIK93Hea4h85PQiyxafxKCmjE_dMJnQ9MIefu69pZqoTSZBz-8S5JM8zQXOlBRc4/s1600/atanatan2f.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv7C_8Drv1tNwKP88bDsHfvt4XtZhdH9FKWKL_rX2Je_cx0uCaJcNwSQRN9b3qymzOIShStua98pTaIK93Hea4h85PQiyxafxKCmjE_dMJnQ9MIefu69pZqoTSZBz-8S5JM8zQXOlBRc4/s400/atanatan2f.png" width="400" /></a></div>
<br />
In reality, no. The difference is that atan's answer handles the angles as four quadrants (4 x 90º), alternating positive / negative twice, and atan2f as two semi-circles (2 x 180º), one positive and one negative. In short, if you used atan instead of atan2f, you would need to do some quadrant differentiation (explained later on in this post), something that becomes unnecessary with atan2f. So, the latter is more convenient for game programming.<br />
<br />
In fact, some other trigonometric functions (like sine and cosine) will handle the whole 0.0 to 6.28318 radians, without resorting to a negative and a positive half arc. This is even more convenient, but you will need to - or should - correct the results of any atan2f return (if your program uses it) to conform. Fortunately, it is easy...<br />
<br />
<b><span class="Apple-style-span" style="color: blue;">if</span>(angle < 0.0f)</b><br />
<b>{</b><br />
<b> angle += (M_PI * 2);</b><br />
<b>}</b></div>
<br />
...and, essentially, you are away.<br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">What are sin( ), cos( ), asin( ) and acos( )?</span></b><br />
Again, the docs...<br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/sin/" target="_blank">sin</a><br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/cos/" target="_blank">cos</a><br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/asin/" target="_blank">asin</a><br />
<a href="http://www.cplusplus.com/reference/clibrary/cmath/acos/" target="_blank">acos</a><br />
<b><br /></b>
But first, a quick anecdote of the sort of stupidity I am capable of. In school, back in 1984, I hated trigonometry and never really understood it, as the teachers were equally as incapable of explaining it in a way I could relate to. Two years later I had my first computer. Suddenly mathematics was exciting, and I wanted to write my own games. Unfortunately, the only memory that remained of trigonometry was this thing of Pythagoras;<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Hypotenuse = Adjacent² + Opposite²</span></b><br />
<br />
So, I wanted to make a game where a ship sailed from port to port. I struggled and figured out this bit of code;<br />
<br />
<a href="http://pastebin.com/QcCFJb1J" target="_blank"><b><i>Pastebin_Sample_2_Link</i></b></a><br />
<div>
<br /></div>
<div>
Well, it wasn't quite THAT bit of code. It was in Spectrum BASIC, but the algorithm was the same. What I had done with the lines...</div>
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: blue;"> <b> float</b></span><b> x_ratio = x_dist / slant_distance;</b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b> </b><span class="Apple-style-span" style="color: blue;"><b>float</b></span><b> y_ratio = y_dist / slant_distance;</b></div>
<br />
<b><span class="Apple-style-span" style="font-weight: normal;">...was re-invent sine and cosine. A bit later on, about a week later, I thought to myself; </span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;">"Wouldn't it be nice if there was a function that did that calculation?" </span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;">And then I rediscovered (that is, partly recalled through the fog of forgetfulness in my head), ArcTangent, Sine, and Cosine. I believe this "rediscovery" of trigonometry is well in among the "Top Ten Most Joyous" moments of my existence. Here is the alternative code..</span></b><br />
<b></b><br />
<b></b><br />
<b></b><br />
<b></b><br />
<b><div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div style="display: inline !important;">
<div style="display: inline !important;">
<span class="Apple-style-span" style="font-weight: normal;"><b><i><a href="http://pastebin.com/k2WLuy0Y" target="_blank">Pastebin_Sample_3_Link</a></i></b></span></div>
</div>
</div>
</b><br />
<b></b><br />
<b></b><br />
<b></b><br />
<b><div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div style="display: inline !important;">
<span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span" style="color: #38761d;"><b><br /></b></span></span></div>
</div>
</b><b><span class="Apple-style-span" style="font-weight: normal;">Enough nostalgia...</span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;">Here's my sample graphic;</span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPokdqe31YjYF5qoHS-PjwXcIrQzlWrsJoLL_5aXW-m115epnXayX38jfCohpJM8EGuSOnz3xeVh2s22NCDL1aWxfQve4uGBj6JpFnOVwKEhSvddBIvSzJWeO4GsKb8WfeCSmfULejMGU/s1600/trig_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPokdqe31YjYF5qoHS-PjwXcIrQzlWrsJoLL_5aXW-m115epnXayX38jfCohpJM8EGuSOnz3xeVh2s22NCDL1aWxfQve4uGBj6JpFnOVwKEhSvddBIvSzJWeO4GsKb8WfeCSmfULejMGU/s320/trig_3.png" width="320" /></a></div>
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;">So, the Sine and Cosine are simply the result of dividing the length of the opposite or adjacent - of angle (A) - by the hypotenuse. In the graphic, a triangle is formed inside the circle. The hypotenuse (c) is equivalent to the radius of the circle, 32 cm. On a 2D Cartesian graph, the center of the circle is at points (0,0). Point (pt), where the radius (hypotenuse) ends, the values on the x and y axes are, respectively 24.5 cm and 20.6 cm.</span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<b><span class="Apple-style-span" style="font-weight: normal;">Dividing the opposite by the radius will give the ratio of </span></b><b><span class="Apple-style-span" style="font-weight: normal;">the distance (pt) is along the y axis, in relation to that radius. This is the Sine.</span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><br /></span></b>
<span class="Apple-style-span" style="color: #351c75;"><b><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;">20.6 / 32 = 0.64375</span></b></span></b></span><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div style="display: inline !important;">
<div style="display: inline !important;">
<b><span class="Apple-style-span" style="font-weight: normal;">Dividing the adjacent by the radius will give the ratio of </span></b><b><span class="Apple-style-span" style="font-weight: normal;">the distance (pt) is along the x axis, in relation to that radius. This is the Cosine.</span></b></div>
</div>
</div>
</span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b><br />
<b><span class="Apple-style-span" style="font-weight: normal;"><div style="display: inline !important; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div style="display: inline !important;">
<div style="display: inline !important;">
<b><span class="Apple-style-span" style="font-weight: normal;"></span></b></div>
</div>
</div>
</span></b><b><span class="Apple-style-span" style="font-weight: normal;"><div style="display: inline !important;">
<div style="display: inline !important;">
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;">24.5 / 32 = 0.76563</span></b></span></span></b></div>
</div>
</span></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"></span></b></span></span></b>So, the Sine is related to the opposite, and the Cosine to the adjacent. That needs to be borne in mind, because the same applies for the ArcSine and ArcCosine, which will provide us the value of angle (A), much like ArcTangent did for Tangent.<br />
<br />
If you are using the Sine value, utilize ArcSine, feeding it the fraction Opposite / Hypotenuse.<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Asin( 20.6 / 32) = 40º or in radians, 0.699 </span></b><br />
<br />
...and if you are using Cosine value, utilize ArcCosine, feeding it the fraction Adjacent / Hypotenuse.<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Acos( 24.5 / 32 ) = 40º or in radians, 0.699</span></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So, barring some rounding precision errors, using the Cos( ) and Sin( ) functions with angle (A) should produce much the same Sine and Cosine values as were calculated above.</span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">Sin( 40º ) = 0.64412</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">Cos( 40º ) = 0.7660</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So, how is that useful? Multiply each by the hypotenuse and you will get the displacements along the y and x axes that represent point (pt).</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">Y = Sin( 40º ) x 32 = 20.57</span></b></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">X = Cos( 40º ) x 32 = 24.51</span></b></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So, shall I be silly now? If we don't know the length of the hypotenuse, but we do know the angle and the length of the opposite (y axis displacement), we can shuffle the formula around to calculate the hypotenuse...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">20.57 / Sin( 40º ) = 32.0</span></b></b><br />
<b><b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">And finally, how to go nowhere again....</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">Asin( Sin( 40º) ) = 40º</span></b></b><br />
<b><b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">Acos( Cos( 40º ) ) = 40º</span></b></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">But wait, there's more! Get this now and you take away additional two items of trigonometry trivia, totally free, today;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">Acos( Sin( 40º ) ) = 50º</span></b></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">Asin( Cos( 40º) ) = 50º</span></b></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">What? Why? Well, basically, both Sine and Cosine deal only with angles up to 90º, so obtaining an ArcCosine of a Sine or an ArcSine of a Cosine will give you the remainder of the angle to complete 90º.</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">But, hey, that raises a question, doesn't it? In game programming, we almost invariably have to deal with the whole 360 degrees (or better said, the whole Pi x 2), and not just 90º. No worries. The position of the point relative to its origin will fall in one of four quadrants, each of a 90º arc...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJjj3gTLyqmPQa9q_-tQgbDn3UShwkwMwX0VSMhITv5KkeJnM_htKS1kGW_5GN0kA2L5LjpCobJg64aHsqT-u4rFdRcZeVw_WXUT4P3FHlMFj7mdvQ_V35JO8X28P1CoY6x0M8NWjaVXc/s1600/rect3616.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJjj3gTLyqmPQa9q_-tQgbDn3UShwkwMwX0VSMhITv5KkeJnM_htKS1kGW_5GN0kA2L5LjpCobJg64aHsqT-u4rFdRcZeVw_WXUT4P3FHlMFj7mdvQ_V35JO8X28P1CoY6x0M8NWjaVXc/s320/rect3616.png" width="320" /></a></div>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
</div>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Most of the time, we will be feeding sin( ) and cos( ) radians in game programs. The problem can be solved by feeding them the radians as depicted in the very first figure of this post (two semi-circles). Notice that computer screens have your normal Cartesian coordinates reversed on the Y axis, counting up as the go down the screen. X is normal. The maths, however, is still standard. </span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Some proof? Here is a runnable snippet of C/C++...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"></span></span></b></span></span></b><br />
<span class="Apple-style-span" style="font-weight: 900;"><i><a href="http://pastebin.com/c1WUCXwq" target="_blank">Pastebin_Sample_4_Link</a></i></span><br />
<span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="font-weight: 900;"><span class="Apple-style-span" style="color: #38761d;"><br /></span></span></span>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">The "angle" is negative, in radians. If the code is run, you will notice that the Y coordinate moves away from zero in the negative direction. If I plotted those coordinates on a screen, the point would be travelling diagonally UP and to the RIGHT.</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Run it with; </span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: blue;">float</span><span class="Apple-style-span" style="color: black;"> angle = -2.35619f;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Where is the point going to go?</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Or better still, feed it this...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: blue;">float</span><span class="Apple-style-span" style="color: black;"> angle = 3.92699f;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><br /></span></span></b></span></span></b>
...it's the same thing! Compare.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfjgKM40hzrxg1m_aGh5BIwWfmxhDc90U_zNOYf8vX8p7DzT2bGkTtWL69f7BE1txDCfVylkaqtBcjMTx00aAbdscs7DNRJayLwO_-BWl9SXHQ6V7ljvAkO7WnXHbsg3Ry-2l2i2TgiCM/s1600/t_rads_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfjgKM40hzrxg1m_aGh5BIwWfmxhDc90U_zNOYf8vX8p7DzT2bGkTtWL69f7BE1txDCfVylkaqtBcjMTx00aAbdscs7DNRJayLwO_-BWl9SXHQ6V7ljvAkO7WnXHbsg3Ry-2l2i2TgiCM/s400/t_rads_2.png" width="400" /></a></div>
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Why not? C++ handles it, now. I seem to remember some other languages (Spectrum BASIC, if I am not mistaken) did not, which required some serious quadrant differentiation to get things going the way you wanted them to go.</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">However, do be careful with asin() and acos(). They WILL return to the four quadrants format, and will require some interpretation by the code to get thing absolutely right. T</span></span></b></span></span></b><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">ry this;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: blue;">float</span><span class="Apple-style-span" style="color: black;"> angle = asinf(sin(4.71237)); </span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"></span><span class="Apple-style-span" style="color: #0b5394;">// That is, we are giving it 270 degrees (</span><span class="Apple-style-span" style="color: #0b5394;">in radians </span></span></b></span></span></b><b><b><span class="Apple-style-span" style="color: #0b5394;">4.71237)</span></b></b><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: #0b5394;"> and expecting it to give back</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: #0b5394;">// the same 4.71237. But it gives us minus 1.57078, equivalent to minus 90 degrees...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Oops! We need to correct for the quadrants. The next two code samples will attempt to clarify this "problem"...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">WITHOUT correction (plain straight asin)...</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><i><a href="http://pastebin.com/BgfgAxLf" target="_blank">Pastebin_Sample_5_Link</a></i></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">And now, WITH quadrant correction...</span></span></b></span></span></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"></span></span></b></span></span></b><br />
<span class="Apple-style-span" style="color: #38761d;"><b></b></span><br />
<b><i><a href="http://pastebin.com/RSW4qEAU" target="_blank">Pastebin_Sample_6_Link</a></i></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">
</span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"></span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"></span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><div>
The above snippet converts the asin() tendency to treat the full circle as four quadrants of 90º into a full 360º positive degrees (or as it really is, 0.0 to 6.28318 radians). Here's a figure to further clarify...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgknvLf-kMUHxe7niVJ1BV2OunYdvPPrxB-UHitdYaTgIczrKZg_rVPwleXYgsQShetZxwrwGIRHcxnrlxNiQQ9eevf6hCazeFx43ljj9l0bvoOOpPdq-6hRxaKkeXj6nRbtNy_Vqno-hM/s1600/asin_correct.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgknvLf-kMUHxe7niVJ1BV2OunYdvPPrxB-UHitdYaTgIczrKZg_rVPwleXYgsQShetZxwrwGIRHcxnrlxNiQQ9eevf6hCazeFx43ljj9l0bvoOOpPdq-6hRxaKkeXj6nRbtNy_Vqno-hM/s400/asin_correct.png" width="400" /></a></div>
<div>
<br /></div>
</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">Note that acos() treats the circle much as asin() does, generally, and requires a similar correction itself, but there is no reason to go into that right here, as it is much the same sort of thing, and not difficult to figure out once the above is understood. </span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So, that wraps that up. Now, onto something else; the Cosine Rule.</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><span class="Apple-style-span" style="font-size: large;">Cosine Rule</span></span></span></b></span></span></b><br />
<span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;">There are no C/C++ functions for this. The Cosine Rule is a very real world sort of application, where you might be able to measure the length of the three sides of a triangle, but cannot accurately measure the angles. It is also real worldly in that it does not necessarily have to deal only with a right angles triangle. Joy!</span></span></span></span><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So, I am a surveyor and I measure the sides of a piece of land. The dimensions are;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
a = 75 m, b = 110 m, c = 60...<br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB1T0tklnmtClacEhLHwPQi4bWdR2x9teIlD3qQ0-oTUXAWx527VN1CtwAXpBPAsm1Uk_hfnCIN-j1IaYfvZ-zdWhkeOn5BVBx97m3EOfMxkA85y1jZEfjNn5fBhTn1AmdQCMR5NvUbXA/s1600/trig_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB1T0tklnmtClacEhLHwPQi4bWdR2x9teIlD3qQ0-oTUXAWx527VN1CtwAXpBPAsm1Uk_hfnCIN-j1IaYfvZ-zdWhkeOn5BVBx97m3EOfMxkA85y1jZEfjNn5fBhTn1AmdQCMR5NvUbXA/s400/trig_5.png" width="400" /></a></div>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">...but I don't know what any of the angles in the corners are. So, the first thing to remember about the Cosine Rule (and indeed, the Sine Rule) is that it always refers to the side opposite the angle. In other words, the angle that "makes" the other side. For example, angle (A), by virtue of the lengths of side (b) and (c), makes side (a).</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;">So I want to find angle (A). The formula is this, and it is saying, if you read it, almost exactly what I wrote in words above;</span></span></b></span></span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><b><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black; font-weight: normal;"><br /></span></span></b></span></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">( ( c² + b² ) - a² ) / (2 x c x b)</span></b></b><br />
<span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="color: black;"><br /></span></span></span></span>
Let me do that;<br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">( ( 60² + 110²) - 75² ) / ( 2 x 60 x 110)</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">( ( 3600 + 12100 ) - 5625 ) / 13200</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">( 15700 - 5625 ) / 13200</span></b></b><br />
<b><b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b></b>
<b><b><span class="Apple-style-span" style="color: #351c75;">10075 / 13200 = 0.76325</span></b></b><br />
<span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="font-weight: 800;"><br /></span></span>
Okay, that answer is the <b>Cosine of angle (A)</b>. Get the ArcCosine of that, and you have the angle.<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Acos( 0.76325 ) = 40.25º</span></b><br />
<span class="Apple-style-span" style="color: #351c75;"><span class="Apple-style-span" style="font-weight: 800;"><br /></span></span>
Ace, right? The other two angles are obtainable by the same method;<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Cosine of Angle (C) = ( ( 110² + 75² ) - 60² ) / ( 2 x 110 x 75 )</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">Cosine of Angle (B) = ( ( 60² + 75² ) - 110² ) / ( 2 x 60 x 75 )</span></b><br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">Sine Rule</span></b><br />
Like the Cosine Rule, this handles non-right angles triangles, too. And it can be quite useful for some situations in games, especially for sorting out interception angles. But, before we get to that, what is Sine Rule, or at least, how does it work?<br />
<br />
The Sine Rule states that (look at the figure above, in the Cosine Rule section again);<br />
<br />
Side (a) / Sin(A) will give you the same answer as Side(b) / Sin(B), and Side (c) / Sin(C) is also the same thing.<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">a / Sin(A) = b / Sin(B) = c / Sin(C)</span></b><br />
<br />
Well simple, then. Do notice, it all has to do with the sides opposite the angles, which stands to reason as we are dealing with Sine, after all. The Rule is used this way around to determine lengths of unknown sides. So, I know that;<br />
<br />
Side (a) = 75 m, and I know Angle (A) = 40.25º and Angle (B) = 108.63º. This is enough information to give me the length of Side (b)...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">75 / Sin(40.25º) = b / Sin(108.63)</span></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">116.077 = b / 0.9476</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">b = 116.077 x 0.9476</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">b = 109.99</span></b><br />
<br />
With rounding errors, but yeah, 110 meters, which is right. You may call the result of 75 / Sin(40.25º) - that is, <b>116.077</b> - the "magic number" of this triangle, if you like. It is going to be the same result for any of the sides.<br />
<br />
Want Side (c)? Simple. I know two angles and I know that the internal angles of ANY triangle add up to 180º. So straight off the bat, I can obtain Angle (C)...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">C = 180º - (40.25º + 108.63º)</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">C = 31.12º</span></b><br />
<br />
And with that, I am armed to keep using the Sine Rule to obtain the length of Side (c). I already know from the previous computation that the "magic number" is 116.077, so I may reuse it here...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">c = 116.077 x Sin(31.12º)</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">c = 59.99 m</span></b><br />
<br />
And that's that. Or is it? I can ALSO use the Sine Rule to determine ANGLES. It involves turning the Sine Rule on its head, like this...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Sin(A) / a = Sin(B) / b = Sin(C) / c</span></b><br />
<br />
So, I know two side and an angle. For example,<br />
<br />
Side (a) = 75 m and Side(b) = 110 m, and Angle(B) = 108.63º<br />
<br />
I want Angle (A)...<br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Sin(A) / 75 = Sin(108.63º) / 110</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">Sin(A) / 75 = 0.00861456</span></b><br />
<br />
<b><span class="Apple-style-span" style="color: #351c75;">Sin(A) = 75 x 0.00861456</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">Sin(A) = 0.6461</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">A = Asin(0.6461)</span></b><br />
<b><span class="Apple-style-span" style="color: #351c75;"><br /></span></b>
<b><span class="Apple-style-span" style="color: #351c75;">A = 40.25º</span></b><br />
<br />
So yeah. That's about it, really. Here's another code snippet that uses Sine Rule to calculate an interception course. Have fun relearning in the future! I know you will...<br />
<br />
<b><i><a href="http://pastebin.com/VjXkKC6B" target="_blank">Interception_Code</a></i></b><br />
<b><br /></b>
<b>Bye for now!</b><br />
<b><br /></b>
<b><br /></b>
<b><br /></b>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com1tag:blogger.com,1999:blog-782137547904165829.post-75000019208373840682012-03-31T14:36:00.000-05:002012-04-03T22:44:36.854-05:00Allegro 5.1 / OpenGL Heightmap LoaderBack again after a break from coding, enforced by work (alas). This time I am here with some more OpenGL and something I always wanted to tackle; heightmaps. Again, I am using Allegro 5.1 as the API, and decided to use some of A5's features to accomplish the task. Two of the neat things about A5 for this purpose are the <b><a href="http://alleg.sourceforge.net/a5docs/refman/graphics.html#al_get_pixel" style="color: blue;" target="_blank">al_get_pixel()</a></b> and <a href="http://alleg.sourceforge.net/a5docs/refman/graphics.html#al_unmap_rgb" target="_blank"><b style="color: blue;">al_unmap_rgb()</b></a> functions, which allow you to get the pixel color and extract them into their rgb components, perfect for reading heightmaps. Also, to speed up the reading of a bitmap, pixel by pixel, there is the <b><a href="http://alleg.sourceforge.net/a5docs/refman/graphics.html#al_lock_bitmap" style="color: blue;" target="_blank">al_lock_bitmap()</a><span style="color: blue;"> </span></b>function, which turns the bitmap temporarily into a memory bitmap. It all helped. Now, I decided to use greyscale images, which each shade is represented by an equal value of the rgb components (eg; mid grey might be something like r = 128, g = 128, b = 128), so I would only need to use one of the components.<br />
<br />
All that is very well, but I also had another objective; that my heightmap loader be able to read (and convert into terrain) any size of bitmap, so that I would not be restricted to reading only 100 x 100 or any other fixed size greyscale heightmap. To accomplish this, I decided to use <a href="http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4027/C-Tutorial-A-Beginners-Guide-to-stdvector-Part-1.htm" target="_blank"><b><span style="color: blue;">STL vector</span></b></a>, so that any size in total pixels could be stored in the vector, depending on the dimensions of the loaded heightmap bitmap. The basic idea is that each pixel will represent a point of terrain, with 3D Cartesian coordinates, where, the X and Y dimensions of the bitmap will be the respective OpenGL X and Z coordinates, when multiplied by a land_scale factor, and the color will be the elevation (OpenGL Y coordinate), the lighter the grey, the higher the elevation. Here's the heightmap I made for the experiment...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixa5VDvSNNVdVsUPuaAywllLnOzMc08uRwfGrTZzC_wxf8Hfpkik5ZmBCQssvyfEngQHDhGa54hvsuJmEYqbwY-9oUypDB_m7CaYTdDvOgCWSphvlNMXE5DEz2NsY-6FiGuD9JMWvpu8o/s1600/landtile.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixa5VDvSNNVdVsUPuaAywllLnOzMc08uRwfGrTZzC_wxf8Hfpkik5ZmBCQssvyfEngQHDhGa54hvsuJmEYqbwY-9oUypDB_m7CaYTdDvOgCWSphvlNMXE5DEz2NsY-6FiGuD9JMWvpu8o/s320/landtile.jpg" width="320" /></a></div>
<br />
Originally, my intention was to use GL_TRIANGLE_STRIP to make the terrain, but around the web I found reports that it showed some (speed) inferiority when compared to using straight forward triangles with the now familiar glEnableClientState() function, so I dispensed with the idea of using GL_TRIANGLE_STRIP, and quite happily, too, as I was not too fond of it really.<br />
<br />
So, that constitutes the preamble of the conditions I set myself for declaring mission accomplished. As per usual, the full code listing is posted at the end of the discussion. I will get to it, now.<br />
<br />
The first task would be to load the bitmap, read the pixels, and create the 3D coordinates for each point. This is, really, the easy bit. Here's a code snippet...<br />
<br />
<br />
<pre>vector<GLfloat> verts;
int BMP_WIDTH;
int BMP_HEIGHT;
ALLEGRO_BITMAP *ht_bmp = al_load_bitmap("landtile.jpg"); <span style="color: #6aa84f;">// load the heightmap.</span>
ALLEGRO_COLOR ht_pixel;
int bmp_x = 0;
int bmp_z = 0; <span style="color: #6aa84f;">// the Y axis of the bmp, really, but considered "z" for conversion to OpenGL coords.</span>
GLfloat land_scale = 5.0f;
GLfloat height_scale = 100.0f;
GLfloat height_true = 0.0f;
unsigned char r, g, b; <span style="color: #6aa84f;">// To store the retreived r,g,b components of each pixel.</span>
<span style="color: #6aa84f;">// here I get the dimensions, in pixels, of the loaded heightmap.
</span>
BMP_WIDTH = al_get_bitmap_width(ht_map);
BMP_HEIGHT = al_get_bitmap_height(ht_bmp);
<span style="color: #6aa84f;">// In the full listing, there are a couple of "autocentering variables here, omitted in this snippet.
// Also, in the full listing, look out for the al_lock_bitmap() around here.</span>
</pre>
So, I now have my heightmap loaded and the program knows the bitmap's dimensions. Each pixel (point) needs to be "expanded" into 3 place holders for the x,y,z coords.<br />
<br />
<pre><span style="color: #6aa84f;">// Reserve the space in the vector, which is a good practice, with the total number of coordinate place holders...</span>
verts.reserve(size_t(BMP_HEIGHT * BMP_WIDTH * 3)); <span style="color: #6aa84f;">// An x,y,z for each pixel read.</span>
<span style="color: #6aa84f;">// ..and populate the verts vector...</span>
for(bmp_z = 0; bmp_z < BMP_HEIGHT; bmp_z++)
{
for(bmp_x = 0; bmp_x < BMP_WIDTH; bmp_x++)
{
verts.pushback(GLfloat(bmp_x * land_scale));
<span style="color: #6aa84f;">// Get the "height" of the OpenGL Y coord according to the "color" of the pixel...</span>
ht_pixel = al_get_pixel(ht_bmp, bmp_x, bmp_z);
al_unmap_rgb(ht_pixel, &r, &g, &b); <span style="color: #6aa84f;">// Thanks, Tomasu!</span>
height_true = GLfloat(float(r) / 255.0f) * height_scale; <span style="color: #6aa84f;">// Uses only the red component...</span>
verts.pushback(height_true); <span style="color: #6aa84f;">// Assign the value to the vector.</span>
verts.pushback(GLfloat(bmp_z * land_scale));
}
}
al_destroy_bitmap(ht_bmp); <span style="color: #6aa84f;">// Once the values are stored in the vector, no further need for the bitmap.</span>
</pre>
<br />
So yeah, the height of the bitmap times the width times three will create the required number of places for the x, y, z for each pixel.
<br />
Now, to continue, I will be using the OpenGL <b style="color: blue;"><a href="http://www.songho.ca/opengl/gl_vertexarray.html" target="_blank">glDrawElements()</a></b> function. This, essentially, does the same thing that I did manually on <a href="http://cplussplussatplay.blogspot.com/2012/01/2-dimensional-matrix-rotation-indexing.html" target="_blank"><b><span style="color: blue;">this post</span></b></a>. that is, connecting the points by indexing. Basically, it works this way;<br />
<br />
<div style="color: #134f5c;">
glEnableClientState(GL_VERTEX_ARRAY);</div>
<div style="color: #134f5c;">
glVertexPointer(3, GL_FLOAT, 0, verts);</div>
<div style="color: #134f5c;">
glDrawElements(GL_TRIANGLES, points_count, GL_UNSIGNED_INT, points);</div>
<div style="color: #134f5c;">
glDisableClientState(GL_VERTEX_ARRAY);</div>
<br />
Looks pretty simple. verts is the array that holds the vertices of each point to be connected, in sets of three (x,y,z). points is the order in which the vertices need to be connected to make the triangles. But hang on. I need to generate those connection points automatically. I will look at a simplified version of the problem. Here is an array of the points, considered a multidimesnional array, but really a contiguous vector.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2meW1xapKfsJoy1A-EPoembNVHRCvdtko5w9eT0JlWDHBOl8X6pPhYN8zFS8lJrh6xz5nc5jktD6lEWmjyfOKItsyIU-etUM02t_YbjOirLYWdlho3jDtjfZ3Q9aIGfV-Rj9CgK6sgc/s1600/dots.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2meW1xapKfsJoy1A-EPoembNVHRCvdtko5w9eT0JlWDHBOl8X6pPhYN8zFS8lJrh6xz5nc5jktD6lEWmjyfOKItsyIU-etUM02t_YbjOirLYWdlho3jDtjfZ3Q9aIGfV-Rj9CgK6sgc/s320/dots.png" width="320" /></a></div>
<br />
<br />
25 points (0-24), in this case, gives me 16 squares into which I need to draw 32 triangles. First step, I need to reduce 25 to 16. So, BMP_WIDTH is 5 and BMP_HEIGHT is 5. I can get 16 very easily...<br />
<br />
<div style="color: #0c343d;">
(BMP_WIDTH - 1) * (BMP_HEIGHT - 1) = 16<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPCNZmEBkLUnfkQgsh8c-rr6sdWXJgxp756wf1n7kq3QwfjiY1VP5UUIBVW0JPpMMn_-tIcr4GyQvCsqyUTkwLO2xce5m7FOCd8uf1dTK8a47etAX8ACsL0HMeGZiY6wC3YuzAAMdNUz8/s1600/dots_tri1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPCNZmEBkLUnfkQgsh8c-rr6sdWXJgxp756wf1n7kq3QwfjiY1VP5UUIBVW0JPpMMn_-tIcr4GyQvCsqyUTkwLO2xce5m7FOCd8uf1dTK8a47etAX8ACsL0HMeGZiY6wC3YuzAAMdNUz8/s320/dots_tri1.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit9jL6KoPQQdyuTNj7qXasGzaO46DW0zqe3pmtsXp5PSO61k8_Bk84VP1INrmoMa3jyX-YM2bL836UrIt-EdTCpOznvIsgAHjAVOzwAFgSvyoyOpdzSjTzzTBl4g1cBXYmm1FClGb25Zc/s1600/dots_tri2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit9jL6KoPQQdyuTNj7qXasGzaO46DW0zqe3pmtsXp5PSO61k8_Bk84VP1INrmoMa3jyX-YM2bL836UrIt-EdTCpOznvIsgAHjAVOzwAFgSvyoyOpdzSjTzzTBl4g1cBXYmm1FClGb25Zc/s320/dots_tri2.png" width="320" /></a></div>
</div>
<br />
And I can draw the triangles very easily using the triangle pair count (which is the 16 that I got). First triangle...<br />
<br />
<pre>vector<GLuint> points;
for(i = 0; i < triangle_count; i++)
{
<span style="color: #6aa84f;">// Triangle 1</span>
points.push_back(i);
points.push_back(i + 1);
points.push_back(i + BMP_WIDTH);
....
</pre>
<br />
And the second triangle...<br />
<br />
<pre> <span style="color: #6aa84f;">//Triangle 2</span>
points.push_back(i + 1);
points.push_back(i + BMP_WIDTH);
points.push_back(i + BMP_WIDTH + 1);
}
</pre>
<br />
Going around counter clockwise. But there's a problem. Every time the calculation gets to the end of a row, it needs to skip a triangle pair in order to start drawing a new row, otherwise I get the last triangle of the first row warping across to the first triangle of the second row.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-aBsJHgT_v6_HNPDNsYy7_gMsNIbwSNK1gv3kagmRHsgYXQnA2acKWHBpLKfhrSBl45g0pFBFkGaXAcr1BhIWd-LMUsJ7RZrIehsJbVsAtvqqy_w6iZIFT3UNCKFDEGO9S02WVk0iDv4/s1600/dots_tri3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-aBsJHgT_v6_HNPDNsYy7_gMsNIbwSNK1gv3kagmRHsgYXQnA2acKWHBpLKfhrSBl45g0pFBFkGaXAcr1BhIWd-LMUsJ7RZrIehsJbVsAtvqqy_w6iZIFT3UNCKFDEGO9S02WVk0iDv4/s320/dots_tri3.png" width="320" /></a></div>
<br />
I need to have a dummy number to skip at the end of each row, so that the drawing of the triangles breaks. I modify the triangle count by the number of rows of squares, which is, indeed, the (BMP_HEIGHT - 1). So triangle count looks like this...<br />
<br />
<div style="color: #134f5c;">
triangle_count = ((BMP_WIDTH - 1) * (BMP_HEIGHT - 1)) + (BMP_HEIGHT -1);</div>
<br />
And implement a row skip counter (call it width_count). The function now looks like this...<br />
<br />
<br />
<pre>int width_count = 0;
triangle_count = ((BMP_WIDTH - 1) * (BMP_HEIGHT - 1)) + (BMP_HEIGHT -1);
for(i = 0; i < triangle_count; i++)
{
if(width_count == (BMP_WIDTH - 1)) <span style="color: #6aa84f;">// That is to say, it counts up to the end of the row...</span>
{
continue; <span style="color: #6aa84f;">// Skip drawing that triangle pair...</span>
width_count = 0; <span style="color: #6aa84f;">// Reset width_count to 0.</span>
}
else
{
<span style="color: #6aa84f;">// Triangle 1</span>
points.push_back(i);
points.push_back(i + 1);
points.push_back(i + BMP_WIDTH);
<span style="color: #6aa84f;">//Triangle 2</span>
points.push_back(i + 1);
points.push_back(i + BMP_WIDTH);
points.push_back(i + BMP_WIDTH + 1);
width_count++; <span style="color: #6aa84f;">//count each pair of triangles drawn.</span>
}
}
</pre>
<br />
And that is, basically, how I did it. I suppose you could use a modulus of (BMP_WIDTH - 1) to accomplish the same thing, and omit the width_count, right?<br />
<br />
Here's a screen shot and the complete listing on my pastebin account (easier than formatting it here...).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaznwxygE8Ieawign5hThWkvhuOnACPXm1DPrECNhgk0hHkmpq4HA_1QqbrFXQdrp9TPwURsAFs62VZrFWStbjqLjtotcAjqGdVIJrFCSDzFrzq_HFiGFAyUc8xk26Vh3kB3GYyuJBMpo/s1600/ht_map_screenie.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaznwxygE8Ieawign5hThWkvhuOnACPXm1DPrECNhgk0hHkmpq4HA_1QqbrFXQdrp9TPwURsAFs62VZrFWStbjqLjtotcAjqGdVIJrFCSDzFrzq_HFiGFAyUc8xk26Vh3kB3GYyuJBMpo/s400/ht_map_screenie.png" width="400" /></a></div>
<br />
<b style="color: #274e13;"><a href="http://pastebin.com/DPdi7ZEy" target="_blank">THE COMPLETE ALLEGRO 5.1 LISTING HERE!</a></b><br />
<br />
And with a most basic color for height algorithm function added somewhere in there it is easy to make a pseudo scenery generator! Had some fun with this one.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-pt1OlVhAl1pEZZ7ij52sL47FqE8dquPc-Nwy2evUXX0L65SS-WkEd_h97Z_FotfiQJy9ymV0EqQdJFZ2eb0APESQvRitiKFkhuOD3wct44EAD9vbQ_YxWfwu_udEDYFHV-3CyV9INo/s1600/scenegen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-pt1OlVhAl1pEZZ7ij52sL47FqE8dquPc-Nwy2evUXX0L65SS-WkEd_h97Z_FotfiQJy9ymV0EqQdJFZ2eb0APESQvRitiKFkhuOD3wct44EAD9vbQ_YxWfwu_udEDYFHV-3CyV9INo/s400/scenegen.png" width="400" /></a></div>
<br />
That code not included here, maybe a refined version next time, so...<br />
<br />
<i><b><span style="color: #274e13;"><span style="color: black;">Until the next post; bye, for now!</span></span></b></i><b style="color: #274e13;"><br /></b><br />
<br />
<br />
<br />
<br />
<br />WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0E28, Ecuador0 -79.1015625-30.7401495 -119.53125 30.7401495 -38.671875tag:blogger.com,1999:blog-782137547904165829.post-40312087188681198202012-01-21T20:46:00.006-05:002012-01-21T21:17:26.499-05:003D object rotation<div style="text-align: justify;">So, what was all that business about <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/3x3-matrix-multiplication.html">concatenating matrices in my previous post?</a> Well, it is one of the vital steps in accomplishing the rotation of a 3D shape on a screen. Now that it is clear how to concatenate matrices, I am going to look at what actually goes into those matrices, and what they mean, in 3D graphics. Now, there are all sorts of discussions around the web concerning right hand and left hand coordinate systems. I am not bothered by them. <a style="color: rgb(0, 0, 153);" href="http://knol.google.com/k/matrices-for-3d-applications-translation-rotation#">Here's a link</a> that delves a little into that subject, quite coherently. I am just going "right handed", and be done.<br /></div><div style="text-align: justify;"><div style="text-align: justify;"><br />When dealing with this subject of 3D rotation, basically, there are these steps to keep in mind;<br /></div><br />1. Make three (3x3) matrices, one for each axis, X, Y, and Z, and another one for the concatenation result. It is also a good idea to establish the angle variables for each matrix, too.<br /><br />2. Make a function that calculates the operations in the matrices (basic trigonometry stuff).<br /><br />3. Make a function that concatenates the three matrices into one.<br /><br />4. Make a function that computes where the vertices of the shape are going to end up, applying the final, concatenated matrix.<br /><br />5. Make a function that draws the shape to the screen.<br /><br />Steps one and three have already been covered, in part, in <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/3x3-matrix-multiplication.html">my previous post</a>. Step five has also previously been brushed upon in <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/2-dimensional-matrix-rotation-indexing.html">this other previous post</a>. They will have their applicable review here, however.<br /><br />I make a globally declared float matrix type, 3x3, this way;<br /></div><br /><pre>typedef matrix[3][3];<br /></pre><br /><br />...and define my matrices in main() this way;<br /><br /><pre><span style="color: rgb(51, 102, 102);">// the matrices...</span><br />matrix X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix;<br /><br /><span style="color: rgb(51, 102, 102);">// the angles for each of the three axis matri</span><span style="color: rgb(51, 102, 102);">ces...</span><br />float x_angle = 0.0f;<br />float y_angle = 0.0f;<br />float z_angle = 0.0f;<br /></pre><br /><br /><div style="text-align: justify;">These will be in degrees, but C++ math.h trig functions use radians. I do like making my own little function for the conversion. Here it is (again)...<br /></div><br /><pre>float deg_to_rad(float deg)<br />{<br />return deg * ((float)M_PI / 180.0f);<br />}</pre><br /><br />...and that is the end of Step One!<br /><br /><div style="text-align: justify;">Now, for Step Two, here are the "formats" of my three matrices, X, Y and Z, respectively...<br /><br /></div><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7tUZNDExGv3kFlI9EQDrhDMOiKsqAxU0mmvQnXsnIqeioHog26Kc6-Xmv_78B1N9jBZnDPuhA_bIt76GCHdUSYPMyMDfjowMImUtER7_teZFA7IiA4rUo5E-DSXbF64m7UTLDCZy1BYw/s1600/X_Axis_Rot.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7tUZNDExGv3kFlI9EQDrhDMOiKsqAxU0mmvQnXsnIqeioHog26Kc6-Xmv_78B1N9jBZnDPuhA_bIt76GCHdUSYPMyMDfjowMImUtER7_teZFA7IiA4rUo5E-DSXbF64m7UTLDCZy1BYw/s320/X_Axis_Rot.png" alt="" id="BLOGGER_PHOTO_ID_5700272279565525938" border="0" /></a> <pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5gXhRUMcp_vzK5S4OmB_8aoh1DuEhPO1-EsFV-0OX5_sLfH5mmUg1VeUZHO0F0_1Xkfi4iFtDzRiCWCGJcBgktNUDA7qIedGERPPREQRZLkFjR0eq4D-jacGaNwDmFajuBl8xdtQwsd4/s1600/Z_Axis_Rot.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 236px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmE31FhhehqkNcDHTusfVtqNRKvuiyiELxaIekTjzcP-FvaaXnoQ1zpUAfk7nc65XsY6c9jadBKHhxs_wXkGBcn86KjlCJktTV75mA3Gc2dnuavrcq6KjSudMjBkbzUUv7r5pUdNtRclY/s320/Y_Axis_Rot.png" alt="" id="BLOGGER_PHOTO_ID_5700271757887682962" border="0" /><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 235px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5gXhRUMcp_vzK5S4OmB_8aoh1DuEhPO1-EsFV-0OX5_sLfH5mmUg1VeUZHO0F0_1Xkfi4iFtDzRiCWCGJcBgktNUDA7qIedGERPPREQRZLkFjR0eq4D-jacGaNwDmFajuBl8xdtQwsd4/s320/Z_Axis_Rot.png" alt="" id="BLOGGER_PHOTO_ID_5700271447032278002" border="0" /></a></pre><br /><br /><div style="text-align: justify;">These are the operations that are going to be carried out in each matrix, with its respective angle fed to it. Here's the function that does it...<br /></div><br /><pre>void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)<br />{<br /><span style="color: rgb(51, 102, 102);">// X Axis Matrix;</span><br />X_Mat[0][0] = 1.0f;<br /> X_Mat[0][1] = 0.0f;<br />X_Mat[0][2] = 0.0f;<br /><br />X_Mat[1][0] = 0.0f;<br />X_Mat[1][1] = cosf(x_ang);<br />X_Mat[1][2] = sinf(x_ang);<br /><br />X_Mat[2][0] = 0.0f;<br />X_Mat[2][1] = -sinf(x_ang);<br />X_Mat[2][2] = cosf(x_ang);<br /><br /> <span style="color: rgb(51, 102, 102);">// Y Axis Matrix</span><br />Y_Mat[0][0] = cosf(y_ang);<br />Y_Mat[0][1] = 0.0f;<br />Y_Mat[0][2] = -sinf(y_ang);<br /><br />Y_Mat[1][0] = 0.0f;<br />Y_Mat[1][1] = 1.0f;<br />Y_Mat[1][2] = 0.0f;<br /><br />Y_Mat[2][0] = sinf(y_ang);<br />Y_Mat[2][1] = 0.0f;<br />Y_Mat[2][2] = cosf(y_ang);<br /><br /> <span style="color: rgb(51, 102, 102);">// Z Axis Matrix</span><br />Z_Mat[0][0] = cosf(z_ang);<br />Z_Mat[0][1] = sinf(z_ang);<br />Z_Mat[0][2] = 0.0f;<br /><br />Z_Mat[1][0] = -sinf(z_ang);<br />Z_Mat[1][1] = cosf(z_ang);<br />Z_Mat[1][2] = 0;<br /><br />Z_Mat[2][0] = 0.0f;<br />Z_Mat[2][1] = 0.0f;<br />Z_Mat[2][2] = 1.0f;<br />}<br /></pre><br /><br /><div style="text-align: justify;">...a bit of overkill, for clarity. It can be seen that, if I preset the matrix "cells" that do not contain any cosf(), sinf() or -sinf() operations at the beginning, this function could easily look like;<br /></div><br /><pre>void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)<br />{<br /><span style="color: rgb(51, 102, 102);"> // X Axis Matrix;</span><br />X_Mat[1][1] = cosf(x_ang);<br />X_Mat[1][2] = sinf(x_ang);<br /><br />X_Mat[2][1] = -sinf(x_ang);<br />X_Mat[2][2] = cosf(x_ang);<br /><br /><span style="color: rgb(51, 102, 102);"> // Y Axis Matrix</span><br />Y_Mat[0][0] = cosf(y_ang);<br />Y_Mat[0][2] = -sinf(y_ang);<br /><br />Y_Mat[2][0] = sinf(y_ang);<br />Y_Mat[2][2] = cosf(y_ang);<br /><br /><span style="color: rgb(51, 102, 102);">// Z Axis Matrix</span><br />Z_Mat[0][0] = cosf(z_ang);<br />Z_Mat[0][1] = sinf(z_ang);<br /><br />Z_Mat[1][0] = -sinf(z_ang);<br />Z_Mat[1][1] = cosf(z_ang);<br />}<br /></pre><br /><br /><div style="text-align: justify;">...as all the other cells are always going to be either a zero or a one, according to the format of the concerned matrix. But that is all aside the point. This is all there is, really, to Step Two, so I will press on.<br /><br />Step Three is the multiplication of these matrices. Do I need to go into this again, seeing as it is already covered? Here's the functions, anyway...<br /></div><br /><pre>void mult_matrix(matrix Fst_Mat, matrix Sec_Mat, matrix Result_Mat)<br />{<br />float temp = 0.0f;<br />int a, b, c;<br /><br />for(a = 0; a < 3; a++)<br />{<br /> for(b = 0; b < 3; b++)<br /> {<br /> for(c = 0; c < 3; c++)<br /> {<br /> temp += Fst_Mat[b][c] * Sec_Mat[c][a];<br /> }<br /> Result_Mat[b][a] = temp;<br /> temp = 0.0f;<br /> }<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void Concatenate_Matrices(matrix First_Mat, matrix Second_Mat, matrix Third_Mat, matrix Fin_Mat)<br />{<br />matrix Concat_Mat;<br /><br />mult_matrix(First_Mat, Second_Mat, Concat_Mat);<br />mult_matrix(Concat_Mat, Third_Mat, Fin_Mat);<br />}<br /></pre><br /><br /><div style="text-align: justify;">The next Step, Four, is the first one that actually comes into contact with vertices or the "stored", 3D object. So, to be clear, here is the object. Yes, it is the everlasting cube. Look at the complete source code at the end of this post to see how it actually fits in.<br /></div><br /><pre><span style="color: rgb(51, 102, 102);">// a structure to hold the coordinates of the vertex...</span><br />struct verts<br />{<br /> float vx;<br />float vy;<br />float vz;<br />};<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br /><span style="color: rgb(51, 102, 102);">// a structure for the order of the drawing of the lines...</span><br />struct lines<br />{<br />int start;<br />int end;<br />};<br /><br /><span style="color: rgb(51, 102, 102);">//.....</span><br /><br /><span style="color: rgb(51, 102, 102);">// set the positions of the vertices (this is called from main(), with an array of type </span>verts)...<br />void set_shape_verts(verts *_cube_verts)<br />{<br />_cube_verts[0].vx = -100.0f;<br />_cube_verts[0].vy = -100.0f;<br />_cube_verts[0].vz = 100.0f;<br /><br />_cube_verts[1].vx = -100.0f;<br />_cube_verts[1].vy = -100.0f;<br />_cube_verts[1].vz = -100.0f;<br /><br />_cube_verts[2].vx = 100.0f;<br />_cube_verts[2].vy = -100.0f;<br />_cube_verts[2].vz = -100.0f;<br /><br />_cube_verts[3].vx = 100.0f;<br />_cube_verts[3].vy = -100.0f;<br />_cube_verts[3].vz = 100.0f;<br /><br />_cube_verts[4].vx = -100.0f;<br />_cube_verts[4].vy = 100.0f;<br />_cube_verts[4].vz = 100.0f;<br /><br />_cube_verts[5].vx = 100.0f;<br />_cube_verts[5].vy = 100.0f;<br />_cube_verts[5].vz = 100.0f;<br /><br />_cube_verts[6].vx = 100.0f;<br />_cube_verts[6].vy = 100.0f;<br />_cube_verts[6].vz = -100.0f;<br /><br />_cube_verts[7].vx = -100.0f;<br />_cube_verts[7].vy = 100.0f;<br />_cube_verts[7].vz = -100.0f;<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br /><span style="color: rgb(51, 102, 102);">// set which vertices are going to be connected by lines (this is called from main(), with an array of type lines)...</span><br />void set_shape_lines(lines *_cube_lines)<br />{<br />_cube_lines[0].start = 0;<br />_cube_lines[0].end = 1;<br /><br />_cube_lines[1].start = 1;<br />_cube_lines[1].end = 2;<br /><br />_cube_lines[2].start = 2;<br />_cube_lines[2].end = 3;<br /><br />_cube_lines[3].start = 3;<br />_cube_lines[3].end = 0;<br /><br />_cube_lines[4].start = 4;<br />_cube_lines[4].end = 5;<br /><br />_cube_lines[5].start = 5;<br />_cube_lines[5].end = 6;<br /><br />_cube_lines[6].start = 6;<br />_cube_lines[6].end = 7;<br /><br />_cube_lines[7].start = 7;<br />_cube_lines[7].end = 4;<br /><br />_cube_lines[8].start = 0;<br />_cube_lines[8].end = 4;<br /><br />_cube_lines[9].start = 1;<br />_cube_lines[9].end = 7;<br /><br />_cube_lines[10].start = 2;<br />_cube_lines[10].end = 6;<br /><br />_cube_lines[11].start = 3;<br />_cube_lines[11].end = 5;<br />}<br /></pre><br /><br />...and here is the function that sets the "rotated" position of those vertices.<br /><br /><pre>void Calc_Screen_Verts(matrix Fin_Mat, ver ts *c_verts, verts *s_verts)<br />{<br />float new_x;<br />float new_y;<br />float new_z;<br /><br />float x_center = (float)SCREEN_X / 2.0f;<br />float y_center = (float)SCREEN_Y / 2.0f;<br /><br />int i;<br /><br />for(i = 0; i < CUBE_VERTS; i++)<br />{<br /> new_x = (Fin_Mat[0][0] * c_verts[i].vx) + (Fin_Mat[0][1] * c_verts[i].vy) + (Fin_Mat[0][2] * c_verts[i].vz);<br /> new_y = (Fin_Mat[1][0] * c_verts[i].vx) + (Fin_Mat[1][1] * c_verts[i].vy) + (Fin_Mat[1][2] * c_verts[i].vz);<br /> new_z = (Fin_Mat[2][0] * c_verts[i].vx) + (Fin_Mat[2][1] * c_verts[i].vy) + (Fin_Mat[2][2] * c_verts[i].vz);<br /><br /> s_verts[i].vx = new_x + x_center;<br /> s_verts[i].vy = new_y + y_center;<br /> s_verts[i].vz = new_z;<br />}<br />}<br /></pre><br /><br /><div style="text-align: justify;">Note that another vert structure is passed to this function (s_verts). This is to receive the output of the rotated vertices held in c_verts. I do not actually want to alter the "mother" vertices of the cube, so I pass them through this function, modify (rotate) them using the concatenated Final_Matrix, and collect the rotation result in s_verts. There is no need to pass any angles, that has already been catered for by the matrix operation done previously. We need only pass the pre-rotated matrix to the function, and with the following operation, plot the new points positions...<br /><br />Finally, Step Five uses a technique I already covered (<a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/2-dimensional-matrix-rotation-indexing.html">again, here</a>) to draw lines on the screen between the vertices held in the lines type structure array. Here it is...<br /></div><br /><pre>void Draw_Shape(verts *s_verts, lines *c_lines)<br />{<br />int i;<br />for(i = 0; i < CUBE_LINES; i++)<br />{<br /> al_draw_line(s_verts[c_lines[i].start].vx, s_verts[c_lines[i].start].vy,<br /> s_verts[c_lines[i].end].vx, s_verts[c_lines[i].end].vy,<br /> al_map_rgb(0,255,50), 0);<br />}<br />}<br /></pre><br /><br /><div style="text-align: justify;">Obviously, I have missed out a great deal about perspective calculations, or vertex sorting for depth. That was not the point of these posts. All I wanted to do was review, quite superficially, the inner working of OpenGL. In any case, here is the complete working Allegro 5.1 source code for this demo.<br /></div><br /><pre><span style="color: rgb(0, 51, 0);">#define _USE_MATH_DEFINES</span><br /><span style="color: rgb(0, 51, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 51, 0);">#include "allegro5/allegro_primitives.h"</span><br /><span style="color: rgb(0, 51, 0);">#include "math.h"</span><br /><br /><span style="color: rgb(51, 102, 102);">/*</span> <span style="color: rgb(51, 102, 102);">`pkg-config --libs allegro-5.1 allegro_primitives-5.1`</span> <span style="color: rgb(51, 102, 102);">*/</span><br /><br />const int SCREEN_X = 800;<br />const int SCREEN_Y = 600;<br />const float FPS = 60.0f;<br />const int CUBE_LINES = 12;<br />const int CUBE_VERTS = 8;<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />typedef float matrix[3][3];<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />struct verts<br />{<br />float vx;<br />float vy;<br />float vz;<br />};<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />struct lines<br />{<br />int start;<br />int end;<br />};<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />float deg_to_rad(float deg)<br />{<br />return deg * ((float)M_PI / 180.0f);<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void check_angles(float &x_ang, float &y_ang, float &z_ang)<br />{<br />if(x_ang >= 180.0f)<br /> x_ang -= 360.0f;<br />else if(x_ang < -180.0f)<br /> x_ang += 360.f;<br /><br />if(y_ang >= 180.0f)<br /> y_ang -= 360.0f;<br />else if(y_ang < -180.0f)<br /> y_ang += 360.f;<br /><br />if(z_ang >= 180.0f)<br /> z_ang -= 360.0f;<br />else if(z_ang < -180.0f)<br /> z_ang += 360.f;<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void set_shape_verts(verts *_cube_verts)<br />{<br />_cube_verts[0].vx = -100.0f;<br />_cube_verts[0].vy = -100.0f;<br />_cube_verts[0].vz = 100.0f;<br /><br />_cube_verts[1].vx = -100.0f;<br />_cube_verts[1].vy = -100.0f;<br />_cube_verts[1].vz = -100.0f;<br /><br />_cube_verts[2].vx = 100.0f;<br />_cube_verts[2].vy = -100.0f;<br />_cube_verts[2].vz = -100.0f;<br /><br />_cube_verts[3].vx = 100.0f;<br />_cube_verts[3].vy = -100.0f;<br />_cube_verts[3].vz = 100.0f;<br /><br />_cube_verts[4].vx = -100.0f;<br />_cube_verts[4].vy = 100.0f;<br />_cube_verts[4].vz = 100.0f;<br /><br />_cube_verts[5].vx = 100.0f;<br />_cube_verts[5].vy = 100.0f;<br />_cube_verts[5].vz = 100.0f;<br /><br />_cube_verts[6].vx = 100.0f;<br />_cube_verts[6].vy = 100.0f;<br />_cube_verts[6].vz = -100.0f;<br /><br />_cube_verts[7].vx = -100.0f;<br />_cube_verts[7].vy = 100.0f;<br />_cube_verts[7].vz = -100.0f;<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void set_shape_lines(lines *_cube_lines)<br />{<br />_cube_lines[0].start = 0;<br />_cube_lines[0].end = 1;<br /><br />_cube_lines[1].start = 1;<br />_cube_lines[1].end = 2;<br /><br />_cube_lines[2].start = 2;<br />_cube_lines[2].end = 3;<br /><br />_cube_lines[3].start = 3;<br />_cube_lines[3].end = 0;<br /><br />_cube_lines[4].start = 4;<br />_cube_lines[4].end = 5;<br /><br />_cube_lines[5].start = 5;<br />_cube_lines[5].end = 6;<br /><br />_cube_lines[6].start = 6;<br />_cube_lines[6].end = 7;<br /><br />_cube_lines[7].start = 7;<br />_cube_lines[7].end = 4;<br /><br />_cube_lines[8].start = 0;<br />_cube_lines[8].end = 4;<br /><br />_cube_lines[9].start = 1;<br />_cube_lines[9].end = 7;<br /><br />_cube_lines[10].start = 2;<br />_cube_lines[10].end = 6;<br /><br />_cube_lines[11].start = 3;<br />_cube_lines[11].end = 5;<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)<br />{<br /><span style="color: rgb(51, 102, 102);">// X Axis Matrix;</span><br />X_Mat[0][0] = 1.0f;<br />X_Mat[0][1] = 0.0f;<br />X_Mat[0][2] = 0.0f;<br /><br />X_Mat[1][0] = 0.0f;<br />X_Mat[1][1] = cosf(x_ang);<br />X_Mat[1][2] = sinf(x_ang);<br /><br />X_Mat[2][0] = 0.0f;<br />X_Mat[2][1] = -sinf(x_ang);<br />X_Mat[2][2] = cosf(x_ang);<br /><br /><span style="color: rgb(51, 102, 102);">// Y Axis Matrix</span><br />Y_Mat[0][0] = cosf(y_ang);<br />Y_Mat[0][1] = 0.0f;<br />Y_Mat[0][2] = -sinf(y_ang);<br /><br />Y_Mat[1][0] = 0.0f;<br />Y_Mat[1][1] = 1.0f;<br />Y_Mat[1][2] = 0.0f;<br /><br />Y_Mat[2][0] = sinf(y_ang);<br />Y_Mat[2][1] = 0.0f;<br />Y_Mat[2][2] = cosf(y_ang);<br /><br /><span style="color: rgb(51, 102, 102);">// Z Axis Matrix</span><br />Z_Mat[0][0] = cosf(z_ang);<br />Z_Mat[0][1] = sinf(z_ang);<br />Z_Mat[0][2] = 0.0f;<br /><br />Z_Mat[1][0] = -sinf(z_ang);<br />Z_Mat[1][1] = cosf(z_ang);<br />Z_Mat[1][2] = 0;<br /><br />Z_Mat[2][0] = 0.0f;<br />Z_Mat[2][1] = 0.0f;<br />Z_Mat[2][2] = 1.0f;<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void mult_matrix(matrix Fst_Mat, matrix Sec_Mat, matrix Result_Mat)<br />{<br />float temp = 0.0f;<br />int a, b, c;<br /><br />for(a = 0; a < 3; a++)<br />{<br /> for(b = 0; b < 3; b++)<br /> {<br /> for(c = 0; c < 3; c++)<br /> {<br /> temp += Fst_Mat[b][c] * Sec_Mat[c][a];<br /> }<br /> Result_Mat[b][a] = temp;<br /> temp = 0.0f;<br /> }<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void Concatenate_Matrices(matrix First_Mat, matrix Second_Mat, matrix Third_Mat, matrix Fin_Mat)<br />{<br />matrix Concat_Mat;<br /><br />mult_matrix(First_Mat, Second_Mat, Concat_Mat);<br />mult_matrix(Concat_Mat, Third_Mat, Fin_Mat);<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void Calc_Screen_Verts(matrix Fin_Mat, verts *c_verts, verts *s_verts)<br />{<br />float new_x;<br />float new_y;<br />float new_z;<br /><br />float x_center = (float)SCREEN_X / 2.0f;<br />float y_center = (float)SCREEN_Y / 2.0f;<br /><br />int i;<br /><br />for(i = 0; i < CUBE_VERTS; i++)<br />{<br /> new_x = (Fin_Mat[0][0] * c_verts[i].vx) + (Fin_Mat[0][1] * c_verts[i].vy) + (Fin_Mat[0][2] * c_verts[i].vz);<br /> new_y = (Fin_Mat[1][0] * c_verts[i].vx) + (Fin_Mat[1][1] * c_verts[i].vy) + (Fin_Mat[1][2] * c_verts[i].vz);<br /> new_z = (Fin_Mat[2][0] * c_verts[i].vx) + (Fin_Mat[2][1] * c_verts[i].vy) + (Fin_Mat[2][2] * c_verts[i].vz);<br /><br /> s_verts[i].vx = new_x + x_center;<br /> s_verts[i].vy = new_y + y_center;<br /> s_verts[i].vz = new_z;<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />void Draw_Shape(verts *s_verts, lines *c_lines)<br />{<br />int i;<br />for(i = 0; i < CUBE_LINES; i++)<br />{<br /> al_draw_line(s_verts[c_lines[i].start].vx, s_verts[c_lines[i].start].vy,<br /> s_verts[c_lines[i].end].vx, s_verts[c_lines[i].end].vy,<br /> al_map_rgb(0,255,50), 0);<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">// *********************************</span><br /><br />int main(int argc, char *argv[])<br />{<br />bool loop = true;<br />bool draw = false;<br />float x_angle = 0.0f;<br />float y_angle = 0.0f;<br />float z_angle = 0.0f;<br /><br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br />ALLEGRO_TIMER *timer = NULL;<br /><br />matrix X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix;<br /><br />verts cube_verts[CUBE_VERTS];<br />set_shape_verts(cube_verts);<br /><br />verts screen_verts[CUBE_VERTS];<br /><br />lines cube_lines[CUBE_LINES];<br />set_shape_lines(cube_lines);<br /><br />al_init();<br />al_init_primitives_addon();<br /><br />al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);<br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />event_queue = al_create_event_queue();<br />timer = al_create_timer(1.0f / FPS);<br /><br />al_register_event_source(event_queue, al_get_display_event_source(display));<br />al_register_event_source(event_queue, al_get_timer_event_source(timer));<br /><br />al_start_timer(timer);<br /><br />while(loop)<br />{<br /> ALLEGRO_EVENT event;<br /> al_wait_for_event(event_queue, &event);<br /><br /> switch(event.type)<br /> {<br /> case ALLEGRO_EVENT_DISPLAY_CLOSE:<br /> loop = false;<br /> break;<br /> case ALLEGRO_EVENT_TIMER:<br /> draw = true;<br /> break;<br /> default:<br /> break;<br /> }<br /> if(draw && al_event_queue_is_empty(event_queue))<br /> {<br /> draw = false;<br /> al_clear_to_color(al_map_rgb(0,0,25));<br /> y_angle += 0.50f;<br /> x_angle += 0.25f;<br /><br /> check_angles(x_angle, y_angle, z_angle);<br /><br /> Matrix_Handler(X_Matrix, Y_Matrix, Z_Matrix, deg_to_rad(x_angle), deg_to_rad(y_angle), deg_to_rad(z_angle));<br /> Concatenate_Matrices(X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix);<br /> Calc_Screen_Verts(Final_Matrix, cube_verts, screen_verts);<br /> Draw_Shape(screen_verts, cube_lines);<br /><br /> al_flip_display();<br /> }<br />}<br /><br />al_stop_timer(timer);<br />al_flush_event_queue(event_queue);<br />al_destroy_event_queue(event_queue);<br />al_destroy_timer(timer);<br />al_destroy_display(display);<br /><br />return 0;<br />}<br /></pre><br /><br />...and a screen shot of what you should get...<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lqxSsR82E4XRxDZxp5xwL5rWdn0jotfJHQSkD0Y_zUROWgBKqDeQ7neq6jwMxjX9mBG4n82H0CS60YAIwiuWW9EzchxwGWtyWtk110xklZ_81HF2rz-o01xUBugf0ZLZM6SpAyHa3JQ/s1600/rotate_cube.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 249px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lqxSsR82E4XRxDZxp5xwL5rWdn0jotfJHQSkD0Y_zUROWgBKqDeQ7neq6jwMxjX9mBG4n82H0CS60YAIwiuWW9EzchxwGWtyWtk110xklZ_81HF2rz-o01xUBugf0ZLZM6SpAyHa3JQ/s320/rotate_cube.png" alt="" id="BLOGGER_PHOTO_ID_5700272575319763362" border="0" /></a></pre><br />That's it.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com1tag:blogger.com,1999:blog-782137547904165829.post-19030484807330044972012-01-20T13:10:00.007-05:002012-01-20T18:19:27.423-05:003x3 Matrix Multiplication<div style="text-align: justify;">Going from 2D matrix (<a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/2-dimensional-matrix-rotation-indexing.html">from my previous post</a>) to 3D matrix manipulation is a reasonably large step, and there is no real in between step to ease the transition. It is quite a leap of faith, when it is done the very first time. However, it does make sense, if the procedure is revisited (from our college days?), worked out on paper and studied a little. First, we simply must look at matrix multiplication.<br /><br />Here's a simple example,<br /><br /></div><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE7qBeJf_d0vBivehfROg7Bsiwh8MvoKoms0bYnsikf20-hbVeU63l4mnvGqTBXZR7I0bCzol83szB9jGzFEh4KGWspLLiXpap0YLpdvMGVzgLIjkIO_HCdXyonJpU6TwYMF4za5CLa3Y/s1600/matrix_mult_2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 294px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE7qBeJf_d0vBivehfROg7Bsiwh8MvoKoms0bYnsikf20-hbVeU63l4mnvGqTBXZR7I0bCzol83szB9jGzFEh4KGWspLLiXpap0YLpdvMGVzgLIjkIO_HCdXyonJpU6TwYMF4za5CLa3Y/s320/matrix_mult_2.png" alt="" id="BLOGGER_PHOTO_ID_5699778792737280370" border="0" /></a><br /><div style="text-align: justify;">Now a 2 x 2 matrix to matrix multiplication (concatenation of two similar matrices into one)...<br /></div><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheV3aX02TX2TMs9Y6FKPFKonX2ioBoWWhtBmXHo-FFAf-Vuc6XbiMdAOcTFmoqVztv72FPFE7BOSBowFSD0pqQxaNUHeRcOAQqcZECt8wV9PImrX5YY09p97JQzLtMVxrirZ-0HJxwxog/s1600/matrix_concat_3.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 280px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheV3aX02TX2TMs9Y6FKPFKonX2ioBoWWhtBmXHo-FFAf-Vuc6XbiMdAOcTFmoqVztv72FPFE7BOSBowFSD0pqQxaNUHeRcOAQqcZECt8wV9PImrX5YY09p97JQzLtMVxrirZ-0HJxwxog/s320/matrix_concat_3.png" alt="" id="BLOGGER_PHOTO_ID_5699778987026456994" border="0" /></a><br />...and that constitutes a review.<br /><br /><div style="text-align: justify;">Before making any attempt at hand made 3D graphics, procedures that multiply matrices in a program must be established. The following is a simple console program, that as usual can be strung together in the presented order and compiled to make the working demo application.<br /></div><br /><div style="text-align: justify;">I am going to make three matrices; two that are going to be multiplied together, and a third to hold the output of the multiplication (concatenation) operation. First, it is a good idea to define a type out of the matrix, so that multiple, compatible instances of it can be created, make a function to populate the two matrices to be multiplied together with some values, and a function to display (cout) the contents of any of the matrices. This is simple stuff that anyone with an understanding of C++ just a little greater that "Hello World" can comprehend (which more or less guarantees that I will catch on again from here)...<br /></div><br /><pre><span style="color: rgb(0, 51, 0);">#include </span><iostream><span style="color: rgb(0, 51, 0);">"iostream"</span><br /><br />using namespace std;<br /><br />typedef int matrix[3][3];<br /><br />void set_matrix(matrix x_mat, matrix y_mat)<br />{<br /> x_mat[0][0] = 9;<br /> x_mat[0][1] = 8;<br /> x_mat[0][2] = 7;<br /><br /> x_mat[1][0] = 6;<br /> x_mat[1][1] = 5;<br /> x_mat[1][2] = 4;<br /><br /> x_mat[2][0] = 3;<br /> x_mat[2][1] = 2;<br /> x_mat[2][2] = 1;<br /><br /> y_mat[0][0] = 1;<br /> y_mat[0][1] = 2;<br /> y_mat[0][2] = 3;<br /><br /> y_mat[1][0] = 4;<br /> y_mat[1][1] = 5;<br /> y_mat[1][2] = 6;<br /><br /> y_mat[2][0] = 7;<br /> y_mat[2][1] = 8;<br /> y_mat[2][2] = 9;<br />}<br /><br />void show_matrix(matrix mat)<br />{<br /> int i, j;<br /><br /> for(i = 0; i < 3; i++)<br /> {<br /> for(j = 0; j < 3; j++)<br /> {<br /> cout << mat[i][j] << " ";<br /> }<br /> cout << endl;<br /> }<br /> cout << endl;<br />}<br /></iostream></pre><br /><br />Then I will make a function that, in an expanded form, permits the easy following of what is happening during the matrix multiplication. The function accepts all three matrices as parameters, multiplies two of them (rows by columns, added together, in this case mat_1 and mat_2) and puts the results into the third (output matrix, in this case fin_mat)...<br /><br /><pre>void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat)<br />{<br /> fin_mat[0][0] = (mat_1[0][0] * mat_2[0][0]) +<br /> (mat_1[0][1] * mat_2[1][0]) +<br /> (mat_1[0][2] * mat_2[2][0]);<br /><br /> fin_mat[1][0] = (mat_1[1][0] * mat_2[0][0]) +<br /> (mat_1[1][1] * mat_2[1][0]) +<br /> (mat_1[1][2] * mat_2[2][0]);<br /><br /> fin_mat[2][0] = (mat_1[2][0] * mat_2[0][0]) +<br /> (mat_1[2][1] * mat_2[1][0]) +<br /> (mat_1[2][2] * mat_2[2][0]);<br /><br /><br /> fin_mat[0][1] = (mat_1[0][0] * mat_2[0][1]) +<br /> (mat_1[0][1] * mat_2[1][1]) +<br /> (mat_1[0][2] * mat_2[2][1]);<br /><br /> fin_mat[1][1] = (mat_1[1][0] * mat_2[0][1]) +<br /> (mat_1[1][1] * mat_2[1][1]) +<br /> (mat_1[1][2] * mat_2[2][1]);<br /><br /> fin_mat[2][1] = (mat_1[2][0] * mat_2[0][1]) +<br /> (mat_1[2][1] * mat_2[1][1]) +<br /> (mat_1[2][2] * mat_2[2][1]);<br /><br /><br /> fin_mat[0][2] = (mat_1[0][0] * mat_2[0][2]) +<br /> (mat_1[0][1] * mat_2[1][2]) +<br /> (mat_1[0][2] * mat_2[2][2]);<br /><br /> fin_mat[1][2] = (mat_1[1][0] * mat_2[0][2]) +<br /> (mat_1[1][1] * mat_2[1][2]) +<br /> (mat_1[1][2] * mat_2[2][2]);<br /><br /> fin_mat[2][2] = (mat_1[2][0] * mat_2[0][2]) +<br /> (mat_1[2][1] * mat_2[1][2]) +<br /> (mat_1[2][2] * mat_2[2][2]);<br /><br />}<br /></pre><br /><br />All that is left to do is create the main() loop of the program now, and we are away with a 3x3 matrix multiplication program...<br /><br /><pre>int main()<br />{<br /><span style="color: rgb(51, 102, 102);"> // locally create our three matrices of type matrix...</span><br /> matrix x_matrix, y_matrix, final_matrix;<br /><br /><span style="color: rgb(51, 102, 102);"> // populate the two matrices to be multiplied...</span><br /> set_matrix(x_matrix, y_matrix);<br /><br /><span style="color: rgb(51, 102, 102);"> // display the two matrices, so we can see they have been populated correctly...</span><br /> show_matrix(x_matrix);<br /> show_matrix(y_matrix);<br /><br /><span style="color: rgb(51, 102, 102);"> // send all three matrices to the multiplier function, so that final_matrix can be populated with the results...</span><br /> expanded_mult(x_matrix, y_matrix, final_matrix);<br /><br /><span style="color: rgb(51, 102, 102);"> // and show the results of final matrix...</span><br /> show_matrix(final_matrix);<br /><br /> return 0;<br />}<br /></pre><br /><br /><div style="text-align: justify;">Does the trick? That's the whole program. Now some modifications to "compress" the code. I want to go after that void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat) function to create something that is both a little more generic and less lengthy. for example, note that if you are doing a 4x4 matrix multiplication, that function is going to grow into an unwieldy...<br /></div><br /><pre><span style="color: rgb(51, 102, 102);">//DEMO EXAMPLE</span><br />void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat)<br />{<br /> fin_mat[0][0] = (mat_1[0][0] * mat_2[0][0]) +<br /> (mat_1[0][1] * mat_2[1][0]) +<br /> (mat_1[0][2] * mat_2[2][0]) +<br /> (mat_1[0][3] * mat_2[3][0]);<br /><br /> fin_mat[1][0] = (mat_1[1][0] * mat_2[0][0]) +<br /> (mat_1[1][1] * mat_2[1][0]) +<br /> (mat_1[1][2] * mat_2[2][0]) +<br /> (mat_1[1][3] * mat_2[3][0]);<br /><br /> fin_mat[2][0] = (mat_1[2][0] * mat_2[0][0]) +<br /> (mat_1[2][1] * mat_2[1][0]) +<br /> (mat_1[2][2] * mat_2[2][0]) +<br /> (mat_1[2][3] * mat_2[3][0]);<br /><br /> fin_mat[3][0] = (mat_1[3][0] * mat_2[0][0]) +<br /> (mat_1[3][1] * mat_2[1][0]) +<br /> (mat_1[3][2] * mat_2[2][0]) +<br /> (mat_1[3][3] * mat_2[3][0]);<br /><br /><br /><br /> fin_mat[0][1] = (mat_1[0][0] * mat_2[0][1]) +<br /> (mat_1[0][1] * mat_2[1][1]) +<br /> (mat_1[0][2] * mat_2[2][1]) +<br /> (mat_1[0][3] * mat_2[3][1]);<br /><br /> fin_mat[1][1] = (mat_1[1][0] * mat_2[0][1]) +<br /> (mat_1[1][1] * mat_2[1][1]) +<br /> (mat_1[1][2] * mat_2[2][1]) +<br /> (mat_1[1][3] * mat_2[3][1]);<br /><br /> fin_mat[2][1] = (mat_1[2][0] * mat_2[0][1]) +<br /> (mat_1[2][1] * mat_2[1][1]) +<br /> (mat_1[2][2] * mat_2[2][1]) +<br /> (mat_1[2][3] * mat_2[3][1]);<br /><br /> fin_mat[3][1] = (mat_1[3][0] * mat_2[0][1]) +<br /> (mat_1[3][1] * mat_2[1][1]) +<br /> (mat_1[3][2] * mat_2[2][1]) +<br /> (mat_1[3][3] * mat_2[3][1]);<br /><br /><br /><br /> fin_mat[0][2] = (mat_1[0][0] * mat_2[0][2]) +<br /> (mat_1[0][1] * mat_2[1][2]) +<br /> (mat_1[0][2] * mat_2[2][2]) +<br /> (mat_1[0][3] * mat_2[3][2]);<br /><br /> fin_mat[1][2] = (mat_1[1][0] * mat_2[0][2]) +<br /> (mat_1[1][1] * mat_2[1][2]) +<br /> (mat_1[1][2] * mat_2[2][2]) +<br /> (mat_1[1][3] * mat_2[3][2]);<br /><br /> fin_mat[2][2] = (mat_1[2][0] * mat_2[0][2]) +<br /> (mat_1[2][1] * mat_2[1][2]) +<br /> (mat_1[2][2] * mat_2[2][2]) +<br /> (mat_1[2][3] * mat_2[3][2]);<br /><br /> fin_mat[3][2] = (mat_1[3][0] * mat_2[0][2]) +<br /> (mat_1[3][1] * mat_2[1][2]) +<br /> (mat_1[3][2] * mat_2[2][2]) +<br /> (mat_1[3][3] * mat_2[3][2]);<br /><br /><br /><br /> fin_mat[0][3] = (mat_1[0][0] * mat_2[0][3]) +<br /> (mat_1[0][1] * mat_2[1][3]) +<br /> (mat_1[0][2] * mat_2[2][3]) +<br /> (mat_1[0][3] * mat_2[3][3]);<br /><br /> fin_mat[1][3] = (mat_1[1][0] * mat_2[0][3]) +<br /> (mat_1[1][1] * mat_2[1][3]) +<br /> (mat_1[1][2] * mat_2[2][3]) +<br /> (mat_1[1][3] * mat_2[3][3]);<br /><br /> fin_mat[2][3] = (mat_1[2][0] * mat_2[0][3]) +<br /> (mat_1[2][1] * mat_2[1][3]) +<br /> (mat_1[2][2] * mat_2[2][3]) +<br /> (mat_1[2][3] * mat_2[3][3]);<br /><br /> fin_mat[3][3] = (mat_1[3][0] * mat_2[0][3]) +<br /> (mat_1[3][1] * mat_2[1][3]) +<br /> (mat_1[3][2] * mat_2[2][3]) +<br /> (mat_1[3][3] * mat_2[3][3]);<br /><br />}<br /></pre><br /><br />Phew! This function does the same thing, using for loops...<br /><br /><pre><span style="color: rgb(51, 102, 102);">// DEMO EXAMPLE</span><br />void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)<br />{<br /> int temp = 0;<br /> int a, b, c;<br /><br /> for(a = 0; a < 3; a++)<br /> {<br /> for(b = 0; b < 3; b++)<br /> {<br /> for(c = 0; c < 3; c++)<br /> {<br /> temp += mat_1[b][c] * mat_2[c][a];<br /> }<br /> fin_mat[b][a] = temp;<br /> temp = 0;<br /> }<br /> }<br />}<br /></pre><br /><br />...for a 3x3 matrix, or...<br /><br /><pre><span style="color: rgb(51, 102, 102);">// DEMO EXAMPLE</span><br />void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)<br />{<br /> int temp = 0;<br /> int a, b, c;<br /><br /> for(a = 0; a < 4; a++)<br /> {<br /> for(b = 0; b < 4; b++)<br /> {<br /> for(c = 0; c < 4; c++)<br /> {<br /> temp += mat_1[b][c] * mat_2[c][a];<br /> }<br /> fin_mat[b][a] = temp;<br /> temp = 0;<br /> }<br /> }<br />}<br /></pre><br /><br /><div style="text-align: justify;">...for a 4x4 matrix. Put this function in (the 3x3 version) instead of the expanded_mult() function, and modify main() to look like this...<br /></div><br /><pre>int main()<br />{<br /> matrix x_matrix, y_matrix, final_matrix;<br /><br /> set_matrix(x_matrix, y_matrix);<br /> show_matrix(x_matrix);<br /> show_matrix(y_matrix);<br /><br /> mult_matrix(x_matrix, y_matrix, final_matrix);<br /><br /> show_matrix(final_matrix);<br /><br /> return 0;<br />}<br /></pre><br /><br />...and you're away.<br /><br /><div style="text-align: justify;">To wrap up, here's a complete listing of how I am handling 4x4 (or any, symmetrical matrix, depending on the value of SYM_MAT).<br /></div><br /><pre><span style="color: rgb(0, 51, 0);">#include "iostream"</span><iostream><br /><br />using namespace std;<br /><br />const int SYM_MAT = 4;<br /><br />typedef int matrix[SYM_MAT][SYM_MAT];<br /><br /><br />void set_matrix(matrix x_mat, matrix y_mat)<br />{<br /> x_mat[0][0] = 16;<br /> x_mat[0][1] = 15;<br /> x_mat[0][2] = 14;<br /> x_mat[0][3] = 13;<br /><br /> x_mat[1][0] = 12;<br /> x_mat[1][1] = 11;<br /> x_mat[1][2] = 10;<br /> x_mat[1][3] = 9;<br /><br /> x_mat[2][0] = 8;<br /> x_mat[2][1] = 7;<br /> x_mat[2][2] = 6;<br /> x_mat[2][3] = 5;<br /><br /> x_mat[3][0] = 4;<br /> x_mat[3][1] = 3;<br /> x_mat[3][2] = 2;<br /> x_mat[3][3] = 1;<br /><br /><br /> y_mat[0][0] = 1;<br /> y_mat[0][1] = 2;<br /> y_mat[0][2] = 3;<br /> y_mat[0][3] = 4;<br /><br /> y_mat[1][0] = 5;<br /> y_mat[1][1] = 6;<br /> y_mat[1][2] = 7;<br /> y_mat[1][3] = 8;<br /><br /> y_mat[2][0] = 9;<br /> y_mat[2][1] = 10;<br /> y_mat[2][2] = 11;<br /> y_mat[2][3] = 12;<br /><br /> y_mat[3][0] = 13;<br /> y_mat[3][1] = 14;<br /> y_mat[3][2] = 15;<br /> y_mat[3][3] = 16;<br />}<br /><br /><br />void show_matrix(matrix mat)<br />{<br /> int i, j;<br /><br /> for(i = 0; i < SYM_MAT; i++)<br /> {<br /> for(j = 0; j < SYM_MAT; j++)<br /> {<br /> cout << mat[i][j] << " ";<br /> }<br /> cout << endl;<br /> }<br /> cout << endl;<br />}<br /><br /><br />void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)<br />{<br /> int temp = 0;<br /> int a, b, c;<br /><br /> for(a = 0; a < SYM_MAT; a++)<br /> {<br /> for(b = 0; b < SYM_MAT; b++)<br /> {<br /> for(c = 0; c < SYM_MAT; c++)<br /> {<br /> temp += mat_1[b][c] * mat_2[c][a];<br /> }<br /> fin_mat[b][a] = temp;<br /> temp = 0;<br /> }<br /> }<br />}<br /><br /><br />int main()<br />{<br /> matrix x_matrix, y_matrix, final_matrix;<br /><br /> set_matrix(x_matrix, y_matrix);<br /> show_matrix(x_matrix);<br /> show_matrix(y_matrix);<br /><br /> mult_matrix(x_matrix, y_matrix, final_matrix);<br /><br /> show_matrix(final_matrix);<br /><br /> return 0;<br />}<br /></iostream></pre><br /><br />That's all, for this post.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-49320952444926793262012-01-07T16:45:00.013-05:002012-01-07T17:18:54.316-05:002 Dimensional Matrix Rotation - Indexing Vertices<div style="text-align: justify;"><br /></div> <style type="text/css"> <!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } --> </style> <p style="margin-bottom: 0in; text-align: justify;">The<a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2012/01/2-dimensional-matrix-rotation-simple.html"> previous post </a>was all very well for taking a first step in matrices, but there were a couple of short comings, as follows;</p><div style="text-align: justify;"> </div><p style="margin-bottom: 0in; text-align: justify;">First, it only rotated one single point (vertex). In a working program, we may want to rotate many vertices using the same rotation transformation that our matrix calculation provided. For example, a single object may be made up of several vertices, all at different displacements from the origin. Ideally, I want the transformation applied to all these points, to give the appearance of rotating the object.</p><div style="text-align: justify;"> </div><p style="margin-bottom: 0in; text-align: justify;">Second, the coordinates of the vertex were permanently altered by the rotation transformation. This is not the ideal. I would want the “source object” to be static, with its coordinates fixed, feed those coordinates through the matrix calculation and apply the rotation, and finally output the modified vertices into a “destination object”, which would then subsequently be displayed.</p><div style="text-align: justify;"> </div><br />I am going to modify that matrix_2d_handler() function so that it accepts a structure of source vertices in an array, modifies them, and copies the modified vertices to a similar, destination array structure. Naturally, it is going to be necessary to create this structure, and make sure that both the source and destination structures are made from the same struct type, and are of the same type. So, here's the structure... <pre> <p style="margin-bottom: 0in">struct vertices_2D</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> float x;</p> <p style="margin-bottom: 0in"> float y;</p> <p style="margin-bottom: 0in">};</p> </pre> <p style="margin-bottom: 0in">...here's the size I want that structure to be, plus how many coords per vertex...</p> <pre> <p style="margin-bottom: 0in">const int NUM_VERTS = 17;</p> <p style="margin-bottom: 0in">const int NUM_VERT_PTS = 2;</p> </pre> <p style="margin-bottom: 0in">...and here is how I declare my two structures in main().</p> <pre> <p style="margin-bottom: 0in"> vertices_2D points[NUM_VERTS];</p> <p style="margin-bottom: 0in"> vertices_2D screen_points[NUM_VERTS];</p> </pre> <p style="margin-bottom: 0in; text-align: justify;">Fairly straightforward. I feed those two structures into the modified matrix handler function, which is this...</p> <pre> <p style="margin-bottom: 0in">void calc_screen_points(vertices_2D *_pts, vertices_2D *_scrn_pts, float _angle)</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> float Matrix_2D[2][2];</p> <p style="margin-bottom: 0in"> Matrix_2D[0][0] = cosf(_angle);</p> <p style="margin-bottom: 0in"> Matrix_2D[0][1] = -sinf(_angle);</p> <p style="margin-bottom: 0in"> Matrix_2D[1][0] = sinf(_angle);</p> <p style="margin-bottom: 0in"> Matrix_2D[1][1] = cosf(_angle);</p> <p style="margin-bottom: 0in"> int i;</p> <p style="margin-bottom: 0in"> for(i = 0; i < NUM_VERTS; i++)</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> _scrn_pts[i].x = (Matrix_2D[0][0] * _pts[i].x) + (Matrix_2D[0][1] * _pts[i].y);</p> <p style="margin-bottom: 0in"> _scrn_pts[i].y = (Matrix_2D[1][0] * _pts[i].x) + (Matrix_2D[1][1] * _pts[i].y);</p> <p style="margin-bottom: 0in"> }</p> <p style="margin-bottom: 0in">}</p> </pre> <p style="margin-bottom: 0in">...this way...</p> <pre> <p style="margin-bottom: 0in">calc_screen_points(points, screen_points, deg_to_rad(angle));</p> </pre> <p style="margin-bottom: 0in; text-align: justify;">Now the values in the array structure points never get altered, and the array structure screen_points are altered by angle. But there is one issue outstanding; populating the points array structure with the coordinates of the “shape”. Well, figure out the coordinates, and, remembering that for a 2D shape there are 2 points per vertex, make an array of the coordinates (I made this array global, in this case)...</p> <pre> <p style="margin-bottom: 0in">float thrush_points[NUM_VERTS * NUM_VERT_PTS] =</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> -270.0f, 0.0f, <span style="color: rgb(51, 102, 102);">// 0</span></p> <p style="margin-bottom: 0in"> -240.0f, -17.0f, <span style="color: rgb(51, 102, 102);">// 1</span></p> <p style="margin-bottom: 0in"> -20.0f, -50.0f, <span style="color: rgb(51, 102, 102);">// 2</span></p> <p style="margin-bottom: 0in"> 18.0f, -90.0f, <span style="color: rgb(51, 102, 102);">// 3</span></p> <p style="margin-bottom: 0in"> 55.0f, -85.0f, <span style="color: rgb(51, 102, 102);">// 4</span></p> <p style="margin-bottom: 0in"> 85.0f, -50.0f, <span style="color: rgb(51, 102, 102);">// 5</span></p> <p style="margin-bottom: 0in"> 200.0f, -28.0f, <span style="color: rgb(51, 102, 102);">// 6</span></p> <p style="margin-bottom: 0in"> 225.0f, -95.0f, <span style="color: rgb(51, 102, 102);">// 7</span></p> <p style="margin-bottom: 0in"> 265.0f, -95.0f, <span style="color: rgb(51, 102, 102);">// 8</span></p> <p style="margin-bottom: 0in"> 275.0f, 10.0f, <span style="color: rgb(51, 102, 102);">// 9</span></p> <p style="margin-bottom: 0in"> 80.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 10</span></p> <p style="margin-bottom: 0in"> -70.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 11</span></p> <p style="margin-bottom: 0in"> -240.0f, 17.0f, <span style="color: rgb(51, 102, 102);">// 12</span></p> <p style="margin-bottom: 0in"> 35.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 13</span></p> <p style="margin-bottom: 0in"> -70.0f, -5.0f, <span style="color: rgb(51, 102, 102);"> // 14</span></p> <p style="margin-bottom: 0in"> 35.0f, 0.0f, <span style="color: rgb(51, 102, 102);">// 15</span></p> <p style="margin-bottom: 0in"> -30.0f, -10.0f <span style="color: rgb(51, 102, 102);"> // 16</span></p> <p style="margin-bottom: 0in"> };</p> </pre> <p style="margin-bottom: 0in">...and read these points into the array structure (I did it this way, this time around)...</p> <pre> <p style="margin-bottom: 0in">void read_points(vertices_2D *_pts)</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> int i, j;</p> <p style="margin-bottom: 0in"> for(i = 0, j = 0; i < NUM_VERTS; i++, j = i * 2)</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> _pts[i].x = thrush_points[j];</p> <p style="margin-bottom: 0in"> _pts[i].y = thrush_points[j+1];</p> <p style="margin-bottom: 0in"> }</p> <p style="margin-bottom: 0in">}</p> </pre> <p style="margin-bottom: 0in">...called from within main() like this...</p> <pre> <p style="margin-bottom: 0in">read_points(points);</p> </pre> <p style="margin-bottom: 0in; text-align: justify;">Yes, it can be seen, I am not much of a fan of multi dimensional arrays, considering that in reality, they are just an abstraction, in any case.</p><div style="text-align: justify;"> </div><p style="margin-bottom: 0in; text-align: justify;">Anyway, that's the core of it, but the shape is pretty uninteresting unless I connect the points with lines. Here's how I do that...</p> <pre> <p style="margin-bottom: 0in">const int NUM_LINES = 18;</p> <p style="margin-bottom: 0in">const int NUM_LINE_PTS = 2;</p> <p style="margin-bottom: 0in">float line_order[NUM_LINES * NUM_LINE_PTS] =</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> 0, 1,</p> <p style="margin-bottom: 0in"> 1, 2,</p> <p style="margin-bottom: 0in"> 2, 3,</p> <p style="margin-bottom: 0in"> 3, 4,</p> <p style="margin-bottom: 0in"> 4, 5,</p> <p style="margin-bottom: 0in"> 5, 6,</p> <p style="margin-bottom: 0in"> 6, 7,</p> <p style="margin-bottom: 0in"> 7, 8,</p> <p style="margin-bottom: 0in"> 8, 9,</p> <p style="margin-bottom: 0in"> 9, 10,</p> <p style="margin-bottom: 0in"> 10, 11,</p> <p style="margin-bottom: 0in"> 11, 12,</p> <p style="margin-bottom: 0in"> 12, 0,</p> <p style="margin-bottom: 0in"> 1, 12,</p> <p style="margin-bottom: 0in"> 11, 14,</p> <p style="margin-bottom: 0in"> 14, 16,</p> <p style="margin-bottom: 0in"> 16, 15,</p> <p style="margin-bottom: 0in"> 15, 13</p> <p style="margin-bottom: 0in"> };</p> </pre> <p style="margin-bottom: 0in; text-align: justify;">This is another globally made array, in this case. What is it doing? Each “pair” of numbers identifies two of the vertices from screen_points that need to be strung together with a line. So, to make the outline of my shape, vertex 0 will be connected to vertex 1 with a line, vertex 1 to 2, 2 to 3, and so on. Note, each vertex may easily be used more than once, to draw lines, so the number of lines may not coincide with the number of vertices available. That's okay.</p> <p style="margin-bottom: 0in">To accept this data, I will make another structure...</p> <pre> <p style="margin-bottom: 0in">struct screen_lines</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> int line_start;</p> <p style="margin-bottom: 0in"> int line_end;</p> <p style="margin-bottom: 0in">};</p> </pre> <p style="margin-bottom: 0in">...declare an instance of its type in main as...</p> <pre> <p style="margin-bottom: 0in">screen_lines lines[NUM_LINES];</p> </pre> <p style="margin-bottom: 0in">...read the data with this function...</p> <pre> <p style="margin-bottom: 0in">void read_lines(screen_lines *_lines)</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> int i, j;</p> <p style="margin-bottom: 0in"> for(i = 0, j = 0; i < NUM_LINES; i++, j = i * 2)</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> _lines[i].line_start = line_order[j];</p> <p style="margin-bottom: 0in"> _lines[i].line_end = line_order[j+1];</p> <p style="margin-bottom: 0in"> }</p> <p style="margin-bottom: 0in">}</p> <p style="margin-bottom: 0in"><br /></p> <p style="margin-bottom: 0in">// Called this way from inside main()</p> <p style="margin-bottom: 0in">read_lines(lines);</p> </pre> <p style="margin-bottom: 0in">The lines are finally drawn to the screen with this function...</p> <pre> <p style="margin-bottom: 0in">void draw_lines(vertices_2D *_scrn_pts, screen_lines *_lines, float x_cor, float y_cor)</p> <p style="margin-bottom: 0in">{</p> <p style="margin-bottom: 0in"> int i;</p> <p style="margin-bottom: 0in"> float x1, y1, x2, y2;</p> <p style="margin-bottom: 0in"> for(i = 0; i < NUM_LINES; i++)</p> <p style="margin-bottom: 0in"> {</p> <p style="margin-bottom: 0in"> x1 = _scrn_pts[_lines[i].line_start].x;</p> <p style="margin-bottom: 0in"> y1 = _scrn_pts[_lines[i].line_start].y;</p> <p style="margin-bottom: 0in"> x2 = _scrn_pts[_lines[i].line_end].x;</p> <p style="margin-bottom: 0in"> y2 = _scrn_pts[_lines[i].line_end].y;</p> <p style="margin-bottom: 0in"><br /></p> <p style="margin-bottom: 0in"> al_draw_line(x1 + x_cor, y1 + y_cor, x2 + x_cor, y2 + y_cor, al_map_rgb(0,255,0), 0);</p> <p style="margin-bottom: 0in"> }</p> <p style="margin-bottom: 0in">}</p> </pre> <p style="margin-bottom: 0in; text-align: justify;">...which indexes the coordinates of the vertices to the lines array, and draws the corresponding line for each couple. The x_cor and y_cor parameters are just for centering the shape on the screen, placing the origin dead center, and not up in the left corner as it would be without them.</p><div style="text-align: justify;"> </div> <p style="margin-bottom: 0in; text-align: justify;">Here's a screen shot and the full, working code for Allegro 5.1, to see the results...<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg10TOW-tig7Af3zQwWDOsDSjB15KQErcpjOl483z7_oSieN37yMMPQL_dQxA-eMXm22pzl5GybcsMXRWuKjWMd4Ozy-7VwAW3CkZPKdggMzpPxEKt-ioQRQjaYKiQthmE9Q_b5z4lH0C0/s1600/dusterScreenie.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 249px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg10TOW-tig7Af3zQwWDOsDSjB15KQErcpjOl483z7_oSieN37yMMPQL_dQxA-eMXm22pzl5GybcsMXRWuKjWMd4Ozy-7VwAW3CkZPKdggMzpPxEKt-ioQRQjaYKiQthmE9Q_b5z4lH0C0/s320/dusterScreenie.png" alt="" id="BLOGGER_PHOTO_ID_5695014502427736834" border="0" /></a> </p><p style="margin-bottom: 0in; text-align: justify;"><br /></p><p style="margin-bottom: 0in; text-align: justify;">...and how all the above fits in;</p><br /><pre><span style="color: rgb(0, 102, 0);">#define _USE_MATH_DEFINES</span><br /><span style="color: rgb(0, 102, 0);">#include </span><allegro5 h=""><br /><span style="color: rgb(0, 102, 0);">#include </span><allegro5 h=""><br /><span style="color: rgb(0, 102, 0);">#include </span><math.h><br /><br /><span style="color: rgb(51, 102, 102);">/*</span><br /><span style="color: rgb(51, 102, 102);">`pkg-config --libs allegro-5.1 allegro_primitives-5.1`</span><br /><span style="color: rgb(51, 102, 102);">*/</span><br /><br />struct vertices_2D<br />{<br />float x;<br />float y;<br />};<br /><br />struct screen_lines<br />{<br />int line_start;<br />int line_end;<br />};<br /><br />const int NUM_VERTS = 17;<br />const int NUM_VERT_PTS = 2;<br />const int NUM_LINES = 18;<br />const int NUM_LINE_PTS = 2;<br /><br />enum KB_KEYS {KEY_Q, KEY_W};<br /><br />float thrush_points[NUM_VERTS * NUM_VERT_PTS] =<br />{<br />-270.0f, 0.0f, <span style="color: rgb(51, 102, 102);">// 0</span><br />-240.0f, -17.0f, <span style="color: rgb(51, 102, 102);">// 1</span><br />-20.0f, -50.0f, <span style="color: rgb(51, 102, 102);">// 2</span><br />18.0f, -90.0f, <span style="color: rgb(51, 102, 102);">// 3</span><br />55.0f, -85.0f, <span style="color: rgb(51, 102, 102);">// 4</span><br />85.0f, -50.0f, <span style="color: rgb(51, 102, 102);">// 5</span><br />200.0f, -28.0f, <span style="color: rgb(51, 102, 102);">// 6</span><br />225.0f, -95.0f, <span style="color: rgb(51, 102, 102);">// 7</span><br />265.0f, -95.0f, <span style="color: rgb(51, 102, 102);">// 8</span><br />275.0f, 10.0f, <span style="color: rgb(51, 102, 102);">// 9</span><br />80.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 10</span><br />-70.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 11</span><br />-240.0f, 17.0f, <span style="color: rgb(51, 102, 102);">// 12</span><br />35.0f, 35.0f, <span style="color: rgb(51, 102, 102);">// 13</span><br />-70.0f, -5.0f, <span style="color: rgb(51, 102, 102);">// 14</span><br />35.0f, 0.0f, <span style="color: rgb(51, 102, 102);">// 15</span><br />-30.0f, -10.0f <span style="color: rgb(51, 102, 102);">// 16</span><br />};<br /><br />float line_order[NUM_LINES * NUM_LINE_PTS] =<br />{<br />0, 1,<br />1, 2,<br />2, 3,<br />3, 4,<br />4, 5,<br />5, 6,<br />6, 7,<br />7, 8,<br />8, 9,<br />9, 10,<br />10, 11,<br />11, 12,<br />12, 0,<br />1, 12,<br />11, 14,<br />14, 16,<br />16, 15,<br />15, 13<br /><br />};<br /><br /><span style="color: rgb(51, 102, 102);">//*******************</span><br />float deg_to_rad(float deg)<br />{<br />return deg * ((float)M_PI / 180.0f);<br />}<br /><br /><br /><span style="color: rgb(51, 102, 102);">//****************</span><br />void read_points(vertices_2D *_pts)<br />{<br />int i, j;<br />for(i = 0, j = 0; i < NUM_VERTS; i++, j = i * 2)<br />{<br />_pts[i].x = thrush_points[j];<br />_pts[i].y = thrush_points[j+1];<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">//****************</span><br />void read_lines(screen_lines *_lines)<br />{<br />int i, j;<br />for(i = 0, j = 0; i < NUM_LINES; i++, j = i * 2)<br />{<br />_lines[i].line_start = line_order[j];<br />_lines[i].line_end = line_order[j+1];<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">//****************</span><br />void calc_screen_points(vertices_2D *_pts, vertices_2D *_scrn_pts, float _angle)<br />{<br />float Matrix_2D[2][2];<br />Matrix_2D[0][0] = cosf(_angle);<br />Matrix_2D[0][1] = -sinf(_angle);<br />Matrix_2D[1][0] = sinf(_angle);<br />Matrix_2D[1][1] = cosf(_angle);<br /><br />int i;<br />for(i = 0; i < NUM_VERTS; i++)<br />{<br />_scrn_pts[i].x = (Matrix_2D[0][0] * _pts[i].x) + (Matrix_2D[0][1] * _pts[i].y);<br />_scrn_pts[i].y = (Matrix_2D[1][0] * _pts[i].x) + (Matrix_2D[1][1] * _pts[i].y);<br />}<br />}<br /><br /><span style="color: rgb(51, 102, 102);">//****************</span><br />void draw_lines(vertices_2D *_scrn_pts, screen_lines *_lines, float x_cor, float y_cor)<br />{<br />int i;<br />float x1, y1, x2, y2;<br /><br />for(i = 0; i < NUM_LINES; i++)<br />{<br />x1 = _scrn_pts[_lines[i].line_start].x;<br />y1 = _scrn_pts[_lines[i].line_start].y;<br />x2 = _scrn_pts[_lines[i].line_end].x;<br />y2 = _scrn_pts[_lines[i].line_end].y;<br /><br />al_draw_line(x1 + x_cor, y1 + y_cor, x2 + x_cor, y2 + y_cor, al_map_rgb(0,255,0), 0);<br />}<br /><br />}<br /><br /><span style="color: rgb(51, 102, 102);">//*******************</span><br />int main(int argc, char *argv[])<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br />ALLEGRO_TIMER *timer = NULL;<br /><br />vertices_2D points[NUM_VERTS];<br />vertices_2D screen_points[NUM_VERTS];<br />screen_lines lines[NUM_LINES];<br /><br />read_points(points);<br />read_lines(lines);<br /><br />int SCREEN_X = 800;<br />int SCREEN_Y = 600;<br />int SC_X_CENT = SCREEN_X / 2;<br />int SC_Y_CENT = SCREEN_Y / 2;<br /><br />float FPS = 60.0f;<br />float angle = 0.0f;<br /><br />bool key[2] = {false, false};<br /><br />bool draw = false;<br />bool loop = true;<br /><br />al_init();<br />al_init_primitives_addon();<br />al_install_keyboard();<br /><br />al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);<br /><br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />event_queue = al_create_event_queue();<br />timer = al_create_timer(1.0f / FPS);<br /><br />al_register_event_source(event_queue, al_get_display_event_source(display));<br />al_register_event_source(event_queue, al_get_timer_event_source(timer));<br />al_register_event_source(event_queue, al_get_keyboard_event_source());<br /><br />al_start_timer(timer);<br /><br />while(loop)<br />{<br />ALLEGRO_EVENT event;<br />al_wait_for_event(event_queue, &event);<br /><br />switch(event.type)<br />{<br />case ALLEGRO_EVENT_DISPLAY_CLOSE:<br /> loop = false;<br /> break;<br /><br />case ALLEGRO_EVENT_TIMER:<br /> draw = true;<br /> break;<br /><br />case ALLEGRO_EVENT_KEY_DOWN:<br /> switch(event.keyboard.keycode)<br /> {<br /> case ALLEGRO_KEY_Q:<br /> key[KEY_Q] = true;<br /> break;<br /><br /> case ALLEGRO_KEY_W:<br /> key[KEY_W] = true;<br /> break;<br /><br /> default:<br /> break;<br /> }<br /> break;<br /><br />case ALLEGRO_EVENT_KEY_UP:<br /> switch(event.keyboard.keycode)<br /> {<br /> case ALLEGRO_KEY_Q:<br /> key[KEY_Q] = false;<br /> break;<br /><br /> case ALLEGRO_KEY_W:<br /> key[KEY_W] = false;<br /> break;<br /><br /> default:<br /> break;<br /> }<br /> break;<br /><br />default:<br /> break;<br />}<br /><br />if(draw == true && al_event_queue_is_empty(event_queue))<br />{<br />draw = false;<br /><br />if(key[KEY_Q])<br />{<br /> angle -= 0.5f;<br /> if(angle < 0.0f)<br /> angle += 360;<br />}<br />if(key[KEY_W])<br />{<br /> angle += 0.5f;<br /> if(angle > 359.99f)<br /> angle -= 360;<br />}<br /><br />al_clear_to_color(al_map_rgb(0,0,0));<br />calc_screen_points(points, screen_points, deg_to_rad(angle));<br />draw_lines(screen_points, lines, SC_X_CENT, SC_Y_CENT);<br /><br />al_flip_display();<br />}<br />}<br /><br />al_stop_timer(timer);<br />al_flush_event_queue(event_queue);<br />al_destroy_timer(timer);<br />al_destroy_event_queue(event_queue);<br />al_destroy_display(display);<br /><br />return 0;<br />}<br /><br /><br /></math.h></allegro5></allegro5></pre> <p style="margin-bottom: 0in"><br /></p> <p style="margin-bottom: 0in;">That's all for now, except never EVER draft your blogs in LibreOffice Writer again. 3D next time...</p><p style="margin-bottom: 0in;"><br /></p>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-8588173323267315492012-01-07T12:32:00.005-05:002012-01-07T13:14:56.772-05:002 Dimensional Matrix Rotation - SimpleNow, if curiosity killed the cat, but the cat has nine lives, that makes the cat eight times the wiser before it finally does get killed. Sounds like a fair trade to me, just so long as the curiosity is well spent. Doing all that OpenGL stuff that was in my previous posts is all well and dandy, but would it not be nice to at least have a superficial idea of what (sort of) goes on "under the hood" in order to supplement one's global understanding of 3D computer graphics? The following two or three posts, therefore, will concentrate on the heart of the matter where OpenGL rotation transformations are concerned...<br /><br />THE MATRIX<br /><br />There are tons of pages that "blah - blah" on about what matrices are, how they are arranged and multiplied, etcetera, but no simple example of their implementation. In this post, I am going to attack the simplest form of them, the 2D matrix, to enable the rotation of one point around the Z axis. It will probably be obvious that the matrix itself is the least of the worries. It is easy, for 2D stuff. Here's what happens, really.<br /><br />Consider first the origin. That is, point (0, 0) on the X and Y axes. I have a point that is located at (150.0, -200.0) from that origin, which I want to rotate around the Z axis by 0.5 of a degree, every 1/60th of a second. The 2D matrix takes on this form...<br /><br />- X - Y<br />X : cos(angle) : -sin(angle)<br />Y : sin(angle) : cos(angle)<br /><br />To which the data of the old positions can be plugged in to achieve a rotation like this...<br /><br />NEW_X_POSITION = (cos(angle) * old_x_position) + (-sin(angle) * old_y_position)<br />NEW_Y_POSITION = (sin(angle) * old_x_position) + (cos(angle) * old_y_position)<br /><br />Do the math itself, on a piece of paper with a calculator, and the understanding will dawn. It is so simple, at this stage, that no further discussion is required. Here's some working source code, using Allegro 5.1.<br /><br /><pre>#define _USE_MATH_DEFINES<br />#include "allegro5/allegro.h"<br />#include "allegro5/allegro_primitives.h"<br />#include "math.h"<allegro5 h=""><allegro5 h=""><math.h><br /><br />/*<br />`pkg-config --libs allegro-5.1 allegro_primitives-5.1`<br />*/<br /><br />//*******************<br />float deg_to_rad(float deg)<br />{<br /> return deg * ((float)M_PI / 180.0f);<br />}<br /><br /><br />void matrix_2d_handler(float _angle, float &_x_pos, float &_y_pos)<br />{<br /> float Matrix_2D[2][2];<br /> float New_X, New_Y;<br /><br /> Matrix_2D[0][0] = cosf(_angle); Matrix_2D[0][1] = -sinf(_angle);<br /> Matrix_2D[1][0] = sinf(_angle); Matrix_2D[1][1] = cosf(_angle);<br /><br /> New_X = (Matrix_2D[0][0] * _x_pos) + (Matrix_2D[0][1] * _y_pos);<br /> New_Y = (Matrix_2D[1][0] * _x_pos) + (Matrix_2D[1][1] * _y_pos);<br /><br /> _x_pos = New_X;<br /> _y_pos = New_Y;<br />}<br /><br /><br /><br />//*******************<br />int main(int argc, char *argv[])<br />{<br /> ALLEGRO_DISPLAY *display = NULL;<br /> ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br /> ALLEGRO_TIMER *timer = NULL;<br /><br /> int SCREEN_X = 800;<br /> int SCREEN_Y = 600;<br /> int SC_X_CENT = SCREEN_X / 2;<br /> int SC_Y_CENT = SCREEN_Y / 2;<br /><br /> float FPS = 60.0f;<br /> float X_pos = 150.0f;<br /> float Y_pos = -200.0f;<br /> float angle = 0.5f;<br /><br /> bool draw = false;<br /> bool loop = true;<br /><br /> al_init();<br /> al_init_primitives_addon();<br /><br /><br /> al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);<br /><br /> display = al_create_display(SCREEN_X, SCREEN_Y);<br /> event_queue = al_create_event_queue();<br /> timer = al_create_timer(1.0f / FPS);<br /><br /> al_register_event_source(event_queue, al_get_display_event_source(display));<br /> al_register_event_source(event_queue, al_get_timer_event_source(timer));<br /><br /> al_start_timer(timer);<br /><br /> while(loop)<br /> {<br /> ALLEGRO_EVENT event;<br /> al_wait_for_event(event_queue, &event);<br /><br /> switch(event.type)<br /> {<br /> case ALLEGRO_EVENT_DISPLAY_CLOSE:<br /> loop = false;<br /> break;<br /><br /> case ALLEGRO_EVENT_TIMER:<br /> draw = true;<br /> break;<br /><br /> default:<br /> break;<br /> }<br /><br /> if(draw == true && al_event_queue_is_empty(event_queue))<br /> {<br /> draw = false;<br /> al_clear_to_color(al_map_rgb(0,0,0));<br /> matrix_2d_handler(deg_to_rad(angle), X_pos, Y_pos);<br /> al_draw_circle(float(X_pos + SC_X_CENT), float(Y_pos + SC_Y_CENT), 10.0f, al_map_rgb(0,255,0), 0);<br /> al_flip_display();<br /> }<br /> }<br /><br /> al_stop_timer(timer);<br /> al_flush_event_queue(event_queue);<br /> al_destroy_timer(timer);<br /> al_destroy_event_queue(event_queue);<br /> al_destroy_display(display);<br /><br /> return 0;<br />}<br /></math.h></allegro5></allegro5></pre>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com1tag:blogger.com,1999:blog-782137547904165829.post-87152454149319080022012-01-04T09:41:00.025-05:002012-01-15T18:08:35.804-05:00OpenGL / Allegro 5.1 Vertex Array with texture coords<div style="text-align: justify;">After spending some time making my own procedure to interpret arrays of vertices, to simplify making slightly more complex shapes other than cubes, and passing them to glVertex3f() sequentially, I suddenly came across - in the GL docs - a neat feature of OpenGL which had already considered this possibility;<br /></div><br /><a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnableClientState.xml"><span style="color: rgb(0, 0, 153);">glEnableClientState()</span></a><br /><br /><div style="text-align: justify;">All the various options that it can handle are listed there, but what interested me most at this stage was passing the vertex and texture coordinates to make the Python space craft from the old space trader game, <a style="color: rgb(0, 0, 153);" href="http://en.wikipedia.org/wiki/Elite_%28video_game%29">Elite</a>, by <a style="color: rgb(0, 0, 153);" href="http://www.gdcvault.com/play/1014628/Classic-Game-Postmortem">David Braben</a>,(a classic I used to play on the Spectrum: could it be at all possible that anyone has not heard of it?).<br /><br />So, with that, I will bypass my own functions for handling arrays (which I WAS going to post here) and stick to OpenGL's functions, as that is what I am learning. First, I (hand) drew the python in plan and side elevation on a piece of squared paper, and figured out the various points in X-Y-Z coordinates. These are in the array <span style="color: rgb(0, 102, 0);">GLfloat python_verts[]</span> in the code posted below. Fairly simple, that part. Here are the basics of how to implement that into code;<br /></div><br /><pre>// tell GL what you are planning to do with the array you are going to pass it...<br />glEnableClientState(GL_VERTEX_ARRAY);<br /><br />// create a pointer to the array with the following GL function...<br />glVertexPointer(3, GL_FLOAT, 0, python_verts);<br /></pre><br /><br /><div style="text-align: justify;">Basically, that pointer is causing OpenGL to select the correct glVertex..() to interpret your array. In this case <span style="color: rgb(0, 102, 0);">glVertex3f()</span>, because we specify that the vertices are in sets of three, and are GLfloat type, and that they are listed in the python_verts array.<br /><br />Now, I made my python with a series of triangles (respecting the outward faces vertex order being counter clockwise, of cour se!), so now to draw the Python;<br /></div><br /><pre>glDrawArrays(GL_TRIANGLES, 0, 54);<br /></pre><br /><div style="text-align: justify;">Where did the 54 come from? The Python is made up of 18 triangles, each of 3 vertices, so it has to iterate 54 time to draw each individual triangle. Finally, disable the set Clientstate...<br /></div><br /><pre>glDisableClientState(GL_VERTEX_ARRAY);<br /></pre><br /><br /><div style="text-align: justify;">Everything made easy. It even automatically invokes glBegin() and glEnd(), so there is no need to call this specifically to draw the triangles...<br /><br />Then (with <a style="color: rgb(0, 0, 153);" href="http://pinta-project.com/">Pinta</a>) I drew the experimental texture I wanted to try out mapped onto the Python. I made a 256 x 256 texture so that I can later experiment a bit more with mip-mapping. Here 'tis...<br /></div><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwYuo2d3fIRnQL4rfF-tlUidvGdtu3-2AjXhWMW4FCaIlTpmfzW4kxMLk-eKhuLPl8S3-KoHBs_MG7RYcfycDn7oEh-ZtlckpiuD6e0z5gL1HNpIgfa03j7RJg5KZqD-J89Nqpj3B4SFs/s1600/pythontexture.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwYuo2d3fIRnQL4rfF-tlUidvGdtu3-2AjXhWMW4FCaIlTpmfzW4kxMLk-eKhuLPl8S3-KoHBs_MG7RYcfycDn7oEh-ZtlckpiuD6e0z5gL1HNpIgfa03j7RJg5KZqD-J89Nqpj3B4SFs/s320/pythontexture.jpg" alt="" id="BLOGGER_PHOTO_ID_5693787823667094210" border="0" /></a><br /><br /><div style="text-align: justify;">Now, to map this, a couple of additional topics need to be covered. First, one must be very methodical (and patient) while doing this.<br /><br />Next, remember that the coordinates of (2D) textures are mapped this way...<br /></div><div style="text-align: justify;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLfibi3CeK8nskdgrn1of73_aTuu11c1ET1kIvgbeKqgVQ1eCfjgKbG-nzRgz0gaExJnZ1cD5euzYvpkHYwYEu5P0lPXT7uJxfnQtc7MCAzfpOewY18oTJaYOlLML4e-tOyK6bsD8YjLE/s1600/uv_coords.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLfibi3CeK8nskdgrn1of73_aTuu11c1ET1kIvgbeKqgVQ1eCfjgKbG-nzRgz0gaExJnZ1cD5euzYvpkHYwYEu5P0lPXT7uJxfnQtc7MCAzfpOewY18oTJaYOlLML4e-tOyK6bsD8YjLE/s320/uv_coords.jpg" alt="" id="BLOGGER_PHOTO_ID_5693823888010680178" border="0" /></a></div> ...with the Y axis 0 being at the bottom, and not the top like most paint programs have it. The easiest approach to overcome this issue is probably to temporarily flip the texture vertically while you plot the positions on the texture, and once you have the coordinates, flip it back again the way it was. Why do I say this? You are going to be working with some numbers to get the pixel positions in coefficients of the image size. I will do an example of texture mapping on one face to clarify. Here's the flipped image...<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcj4GlTuwzQ5RVwDM7QGeUFKDYXCSYLETcwPkMu1pdCgeGxFom_ds501_B1IMSatvBc0dRcdQr56yXXnhIeQfT_yLiz6b-3_LAZcQPXuP0JLeOPu-coxQJUfOgyfsmOj62itJbI9NCkZc/s1600/pythontexture_yflip.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcj4GlTuwzQ5RVwDM7QGeUFKDYXCSYLETcwPkMu1pdCgeGxFom_ds501_B1IMSatvBc0dRcdQr56yXXnhIeQfT_yLiz6b-3_LAZcQPXuP0JLeOPu-coxQJUfOgyfsmOj62itJbI9NCkZc/s320/pythontexture_yflip.jpg" alt="" id="BLOGGER_PHOTO_ID_5693826080015716914" border="0" /></a></pre><br /><br /><div style="text-align: justify;">I am going to texture map the very first triangle drawn, which is the upper left nose of the Python. In the picture above, it is the first triangle encountered as from the lower left of the graphic.<br /><br /><div style="text-align: justify;">But before I start mapping, i am going to look at the order of the drawing of the vertices for that triangle...<br /></div></div><pre>0.0f,0.0f,-9.0f, -3.0f,0.0f,0.0f, 0.0f,1.5f,-2.0f,</pre><br /><div style="text-align: justify;">It goes from the nose, to the "wing-tip", to the center line on the front cabin. That is the same order I want to map the texture with. Bearing this in mind, I will make a texture coordinate for each of those points, in the format required by glTexCoord2f().<br /><br />Starting at the nose of the Python, in the flipped texture graphic, I obtain the texture coordinates by dividing the actual pixel position of the required point (in X, Y) order by the total corresponding size of the image. For the very nose tip;<br /></div><br /><span style="color: rgb(51, 51, 51);">X = 63 / 256 = 0.246</span> <span style="color: rgb(51, 51, 51);"><br />Y = 256 / 256 = 1.000</span><br /><br />Write that down like this...<br /><pre>0.246f, 1.000f<br /></pre><br />Now the wing-tip, keeping in with following the order of the drawn vertices...<br /><br /><span style="color: rgb(51, 51, 51);">X = 0 / 256 = 0.000</span><br /><span style="color: rgb(51, 51, 51);">Y = 102 / 256 = 0.3</span><span style="color: rgb(51, 51, 51);">98</span><br /><br />And the last center line point...<br /><br /><span style="color: rgb(51, 51, 51);">X = 63 / 256 = 0.246</span><br /><span style="color: rgb(51, 51, 51);">Y = 140 / 256 = 0.547</span><br /><br /><div style="text-align: justify;">So, here are the UV coordinates for the "section" of the texture to be mapped to the upper left nose of the python...<br /></div><br /><pre>0.246f, 1.000f, 0.000f, 0.398f, 0.246f, 0.547f<br /></pre><br /><br /><div style="text-align: justify;">...and that needs to be done for each and ev ery vertex of the whole object, with its corresponding bit of the texture. Once all the points are mapped, put them into another array, for example <span style="color: rgb(0, 102, 0);">python_tex_coords[]</span>. Don't forget to Y axis flip the texture back to how it was, either. And now take a break after doing it or you'll go barmy.<br /><br />Finally, to activate the texture mapping in OpenGL, enable another Client State (along with the previously demonstrated one) and make a pointer to the texture coordinates. Here's how...<br /></div><br /><pre>glEnableClientState(GL_VERTEX_ARRAY);<br />glEnableClientState(GL_TEXTURE_COORD_ARRAY);<br /><br />glVertexPointer(3, GL_FLOAT, 0, python_verts);<br />glTexCoordPointer(2, GL_FLOAT, 0, python_tex_coords);<br /><br />glDrawArrays(GL_TRIANGLES, 0, 54);<br /><br />glDisableClientState(GL_TEXTURE_COORD_ARRAY);<br />glDisableClientState(GL_VERTEX_ARRAY);<br /></pre><br />Notice only that the first parameter of glTexCoordPointer() is 2. this is because there are only two uv coords for every vertex point (which are, themselves, three). It works the same as glVertexPointer, in all other respects.<br /><br />Here's a screen shot (Blue Danube, anyone?)...<br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwJnF9TrOHtPkW5Sqxq7DK5xty3H4Yn3XF5kVbpGeLLO_pDQ8CviYutl_lA_zk87_2RC_ZmWnBi3ZeZ3TndotxxMGpkjeoXFmcEw6pxaB-pth9A-_8u1dCGiLoCTN7EFNUJgJOGVX0sqc/s1600/Python_screenie.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwJnF9TrOHtPkW5Sqxq7DK5xty3H4Yn3XF5kVbpGeLLO_pDQ8CviYutl_lA_zk87_2RC_ZmWnBi3ZeZ3TndotxxMGpkjeoXFmcEw6pxaB-pth9A-_8u1dCGiLoCTN7EFNUJgJOGVX0sqc/s320/Python_screenie.png" alt="" id="BLOGGER_PHOTO_ID_5693834892865082466" border="0" /></a></pre><br /><div style="text-align: justify;">And that is it. I deliberately left some commented out things I was playing with in the code posted below, for reference, as there are more ways to draw from arrays that OpenGL, in its wisdom, can do.<br /></div><br /><pre><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_opengl.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "GL/glu.h"</span><br /><br />int SCREEN_X = 800;<br />int SCREEN_Y = 600;<br /><br />GLfloat python_verts[] =<br />{<br /><span style="color: rgb(51, 153, 153);"> // Nose</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />0.0f,0.0f,-9.0f, -3.0f,0.0f,0.0f, 0.0f,1.5f,-2.0f,<br /><span style="color: rgb(51, 153, 153);"> //upper right</span><br />0.0f,0.0f,-9.0f, 0.0f,1.5f,-2.0f, 3.0f,0.0f,0.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />0.0f,0.0f,-9.0f, 0.0f,-1.5f,-2.0f, -3.0f,0.0f,0.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.0f,0.0f,-9.0f, 3.0f,0.0f,0.0f, 0.0f,-1.5f,-2.0f,<br /><span style="color: rgb(51, 153, 153);"> // Cabin</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />-3.0f,0.0f,0.0f, 0.0f,1.5f,2.0f, 0.0f,1.5f,-2.0f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />3.0f,0.0f,0.0f, 0.0f,1.5f,-2.0f, 0.0f,1.5f,2.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />-3.0f,0.0f,0.0f, 0.0f,-1.5f,-2.0f, 0.0f,-1.5f,2.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />3.0f,0.0f,0.0f, 0.0f,-1.5f,2.0f, 0.0f,-1.5f,-2.0f,<br /><span style="color: rgb(51, 153, 153);"> // Tail forward</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />-3.0f,0.0f,0.0f, -2.0f,0.0f,4.0f, 0.0f,1.5f,2.0f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />3.0f,0.0f,0.0f, 0.0f,1.5f,2.0f, 2.0f,0.0f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />-3.0f,0.0f,0.0f, 0.0f,-1.5f,2.0f, -2.0f,0.0f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> //lower right</span><br />3.0f,0.0f,0.0f, 2.0f,0.0f,4.0f, 0.0f,-1.5f,2.0f,<br /><span style="color: rgb(51, 153, 153);"> // Tail rear</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />-2.0f,0.0f,4.0f, 0.0f,0.8f,4.0f, 0.0f,1.5f,2.0f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />0.0f,1.5f,2.0f, 0.0f,0.8f,4.0f, 2.0f,0.0f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />-2.0f,0.0f,4.0f, 0.0f,-1.5f,2.0f, 0.0f,-0.8f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.0f,-1.5f,2.0f, 2.0f,0.0f,4.0f, 0.0f,-0.8f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> // stern</span><br /><span style="color: rgb(51, 153, 153);"> // left</span><br />-2.0f,0.0f,4.0f, 0.0f,-0.8f,4.0f, 0.0f,0.8f,4.0f,<br /><span style="color: rgb(51, 153, 153);"> //right</span><br />2.0f,0.0f,4.0f, 0.0f,0.8f,4.0f, 0.0f,-0.8f,4.0f<br />};<br /><br />GLfloat python_tex_coords[] =<br />{<br /><span style="color: rgb(51, 153, 153);"> // Nose</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />0.246f,1.000f, 0.000f,0.398f, 0.246f,0.547f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />0.246f,1.000f, 0.246f,0.547f, 0.488f,0.398f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />0.746f,1.000f, 0.746f,0.547f, 1.000f,0.398f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.746f,1.000f, 0.492,0.398f, 0.746f,0.547f,<br /><span style="color: rgb(51, 153, 153);"> // Cabin</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />0.000f,0.398f, 0.246f,0.242f, 0.246f,0.547f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />0.488f,0.398f, 0.246f,0.547f, 0.246f,0.242f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />1.000f,0.398f, 0.746f,0.547f, 0.746f,0.242f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.492f,0.398f, 0.746f,0.242f, 0.746f,0.547f,<br /><span style="color: rgb(51, 153, 153);"> // Tail forward</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />0.000f,0.398f, 0.055f,0.145f, 0.246f,0.242f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />0.488f,0.398f, 0.246f,0.242f, 0.429f,0.145f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />1.000f,0.398f, 0.746f,0.242f, 0.945f,0.145f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.492f,0.398f, 0.559f,0.145f, 0.746f,0.242f,<br /><span style="color: rgb(51, 153, 153);"> // Tail rear</span><br /><span style="color: rgb(51, 153, 153);"> // upper left</span><br />0.055f,0.145f, 0.246f,0.140f, 0.246f,0.242f,<br /><span style="color: rgb(51, 153, 153);"> // upper right</span><br />0.246f,0.242f, 0.246f,0.140f, 0.429f,0.145f,<br /><span style="color: rgb(51, 153, 153);"> // lower left</span><br />0.945f,0.145f, 0.746f,0.242f, 0.746f,0.145f,<br /><span style="color: rgb(51, 153, 153);"> // lower right</span><br />0.746f,0.242f, 0.559f,0.145f, 0.746f,0.145f,<br /><span style="color: rgb(51, 153, 153);"> // Stern</span><br /><span style="color: rgb(51, 153, 153);"> // left</span><br />0.062f,0.074f, 0.246f,0.000f, 0.246f,0.140f,<br /><span style="color: rgb(51, 153, 153);"> // right</span><br />0.418f,0.074f, 0.246f,0.140f, 0.246f,0.000f<br />};<br /><br /><br />void camera_3D_setup()<br />{<br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0, (GLdouble)SCREEN_X / (GLdouble)SCREEN_Y, 1.0, 100.0);<br />}<br /><br />void draw_python(GLuint ogl_tex, GLfloat x_ang, GLfloat y_ang, GLfloat z_ang)<br />{<br />glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br />al_clear_to_color(al_map_rgb(0,0,30));<br /><br />glMatrixMode(GL_MODELVIEW);<br />glLoadIdentity();<br /><br />glTranslatef(0.0f, 0.0f, -30.0f);<br />glRotatef(x_ang, 1.0f, 0.0f, 0.0f);<br />glRotatef(y_ang, 0.0f, 1.0f, 0.0f);<br />glRotatef(z_ang, 0.0f, 0.0f, 1.0f);<br /><br />glEnable(GL_CULL_FACE);<br />glEnable(GL_DEPTH_TEST);<br />glEnable(GL_TEXTURE_2D);<br /><br />glBindTexture(GL_TEXTURE_2D, ogl_tex);<br /><br /><span style="color: rgb(51, 153, 153);">// No need to do glBegin() or glEnd() when using these GL functions</span><br /><span style="color: rgb(51, 153, 153);">// They appear to intrinsic to the functions, like glDrawElements</span><br /><span style="color: rgb(51, 153, 153);">// In a similar fashion to gluQuadricObjects, like gluSphere().</span><br />glEnableClientState(GL_VERTEX_ARRAY);<br /><span style="color: rgb(51, 153, 153);">//glEnableClientState(GL_COLOR_ARRAY);</span><br />glEnableClientState(GL_TEXTURE_COORD_ARRAY);<br /><br />glVertexPointer(3, GL_FLOAT, 0, python_verts);<br />glTexCoordPointer(2, GL_FLOAT, 0, python_tex_coords);<br /><span style="color: rgb(51, 153, 153);">//glColorPointer(3, GL_FLOAT, 0, python_colors);</span><br /><br /><span style="color: rgb(51, 153, 153);">//glDrawElements(GL_TRIANGLES, 54, GL_UNSIGNED_INT, python_vert_index);</span><br />glDrawArrays(GL_TRIANGLES, 0, 54);<br /><br />glDisableClientState(GL_TEXTURE_COORD_ARRAY);<br /><span style="color: rgb(51, 153, 153);">//glDisableClientState(GL_COLOR_ARRAY);</span><br />glDisableClientState(GL_VERTEX_ARRAY);<br /><br />glDisable(GL_TEXTURE_2D);<br />glDisable(GL_DEPTH_TEST);<br />glDisable(GL_CULL_FACE);<br /><br />glFlush();<br />}<br /><br />int main(int argc, char *argv[])<br />{<br />float FPS = 60.0f;<br />bool loop = true;<br />bool draw = false;<br /><br />GLfloat x_angle = 70.0f;<br />GLfloat y_angle = 0.0f;<br />GLfloat z_angle = 180.0f;<br />GLuint python_tex;<br /><br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_TIMER *timer = NULL;<br />ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br />ALLEGRO_BITMAP *python_bmp = NULL;<br /><br />al_init();<br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />timer = al_create_timer(1.0f / FPS);<br />event_queue = al_create_event_queue();<br /><br />python_bmp = al_load_bitmap("pythontexture.jpg");<br />python_tex = al_get_opengl_texture(python_bmp);<br /><br />al_register_event_source(event_queue, al_get_display_event_source(display));<br />al_register_event_source(event_queue, al_get_timer_event_source(timer));<br /><br />al_start_timer(timer);<br /><br />camera_3D_setup();<br /><br />while(loop)<br />{<br />ALLEGRO_EVENT event;<br />al_wait_for_event(event_queue, &event);<br /><br />switch(event.type)<br />{<br />case ALLEGRO_EVENT_DISPLAY_CLOSE:<br /> loop = false;<br /> break;<br /><br />case ALLEGRO_EVENT_TIMER:<br /> draw = true;<br /> break;<br /><br />default:<br /> break;<br />}<br /><br />if(draw == true && al_event_queue_is_empty(event_queue))<br />{<br />draw = false;<br />x_angle += 0.25f;<br /><span style="color: rgb(51, 153, 153);">//y_angle -= 0.35f;</span><br />z_angle += 0.5f;<br />draw_python(python_tex, x_angle, y_angle, z_angle);<br />al_flip_display();<br />}<br />}<br /><br /><br />return 0;<br />}<br /></pre><br /><div style="text-align: justify;">Peaceful, non-violent, involving and enthralling subjugation and distraction for the masses, if only they will have the nerve to try it...</div>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-34414052280686159682011-12-27T11:52:00.020-05:002012-01-01T20:01:47.207-05:00OpenGL / Allegro 5.1 3D Orbit PrimerSo, here again, and still learning (and what is more; still capable of learning, which is <span style="font-style: italic;">always</span> good to know). There is little new with this post, as compared to others. It is more of a confluence of some techniques learned in those other posts to make an animation of a dry earth planet in an elliptical orbit around a shrunken "brown dwarf" style sun. Let's say it is an improbable future scenario?<br /><br />Here are the textures used, followed by the source code...<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUFyY9jGDgS5buMkb1WHYqLV725GCfEwSIunluS8C4ZewAR5FjEOTAUkJfYg5d-3dLueuKxTEHqQVKy50bmSbHxaZbj_3jPA-sxq0thdBcPVHJVdxX3IFfLk-Yax_BwR2Y_C5YSzf_gpA/s1600/img_1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 160px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUFyY9jGDgS5buMkb1WHYqLV725GCfEwSIunluS8C4ZewAR5FjEOTAUkJfYg5d-3dLueuKxTEHqQVKy50bmSbHxaZbj_3jPA-sxq0thdBcPVHJVdxX3IFfLk-Yax_BwR2Y_C5YSzf_gpA/s320/img_1.jpg" alt="" id="BLOGGER_PHOTO_ID_5690853343428816626" border="0" /></a><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC8fi6P1wXbqtS6LrnVRvwU_k7Afvdv0uNB0irG2BFm3Ne_sQKtILPhEOsksCUQrzdbrA2q8LcV7UBZWBOuqbH04PhRTcVmgzBdbN8qjBA_3PbjQSxoAJlUHJzJZF-jVCLlIP5KuRNPAY/s1600/img_2.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 160px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC8fi6P1wXbqtS6LrnVRvwU_k7Afvdv0uNB0irG2BFm3Ne_sQKtILPhEOsksCUQrzdbrA2q8LcV7UBZWBOuqbH04PhRTcVmgzBdbN8qjBA_3PbjQSxoAJlUHJzJZF-jVCLlIP5KuRNPAY/s320/img_2.jpg" alt="" id="BLOGGER_PHOTO_ID_5690853423250656114" border="0" /></a><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqih0HsWo0xSXsi_dtRUwbYrLMw0Nn5geAoGG60WifswT9k5rmLTylZKdBD3z0aGN6lv8qEu2mrrutPWw-Lh4lZ6cVAFpquI1lqNpHxDGoQ0E_Z12zE92VCIOXX39F9Mn2EPgc-XA8abw/s1600/img_stars.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqih0HsWo0xSXsi_dtRUwbYrLMw0Nn5geAoGG60WifswT9k5rmLTylZKdBD3z0aGN6lv8qEu2mrrutPWw-Lh4lZ6cVAFpquI1lqNpHxDGoQ0E_Z12zE92VCIOXX39F9Mn2EPgc-XA8abw/s320/img_stars.jpg" alt="" id="BLOGGER_PHOTO_ID_5690853682752381490" border="0" /></a><br /><br /><pre><allegro5 h=""><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_opengl.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "GL/glu.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "math.h"</span><br /><br /><span style="color: rgb(51, 153, 153);">/*</span><br /><span style="color: rgb(51, 153, 153);">`pkg-config --libs allegro-5.1 allegro_image-5.1`</span><br /><span style="color: rgb(51, 153, 153);">-lGL</span><br /><span style="color: rgb(51, 153, 153);">-lGLU</span><br /><span style="color: rgb(51, 153, 153);">*/</span><br /><br />int SCREEN_X = 800;<br />int SCREEN_Y = 600;<br /><br />class planets<br />{<br />public:<br /><span style="color: rgb(51, 153, 153);">// a = G (m/r²) : Reference formula</span><br />float radius_1;<br />float radius_2;<br />float angle_1;<br />float angle_2;<br /><br />float grav_const; <span style="color: rgb(51, 153, 153);">// = 6.6742e-11; // The "G" in the</span></allegro5><allegro5 h=""><span style="color: rgb(51, 153, 153);"> formula</span><br />float star_mass; <span style="color: rgb(51, 153, 153);">// = 5.975e24; // The "m" in the formula</span><br /><br />float grav_accel; <span style="color: rgb(51, 153, 153);">// The "a" in the formula</span><br />float body_x;<br />float body_z;<br />float body_y;<br />float b_vel_x;<br />float b_vel_z;<br />float b_vel_y;<br /><br />planets(void);<br />~planets(void);<br /><br />void calc_planet_pos();<br />};<br /><br /><span style="color: rgb(51, 153, 153);">// Define class functions:</span><br /><br />planets::planets(void)<br />{<br />grav_const = 6.6742e-11;<br />star_mass = 5.975e24;<br />body_x = 1.5e6;<br />body_z = 5.5e4;<br />body_y = 1.0e6;<br />b_vel_x = -3000;<br />b_vel_z = 11000.0;<br />b_vel_y = 3000.0;<br />}<br /><br />planets::~planets(void)<br />{<br /><span style="color: rgb(51, 153, 153);">// nothing doing...</span><br />}<br /><br />void planets::calc_planet_pos()<br />{<br />angle_1 = atan2f(body_x, body_z);<br />radius_1 = sqrtf(pow(body_x, 2) + pow(body_z, 2));<br />angle_2 = atan2f(body_y, radius_1);<br />radius_2 = sqrtf(pow(body_x, 2) + pow(body_z, 2) + pow(body_y, 2));<br /><br />grav_accel = (grav_const * (star_mass / pow(radius_2, 2)));<br /><br />b_vel_x = b_vel_x + ((sin(angle_1) * cos(angle_2)) * (grav_accel));<br />b_vel_z = b_vel_z + ((cos(angle_1) * cos(angle_2)) * (grav_accel));<br />b_vel_y = b_vel_y + (sin(angle_2) * (grav_accel));<br /><br />body_x = body_x - b_vel_x;<br />body_z = body_z - b_vel_z;<br />body_y = body_y - b_vel_y;<br />}<br /><br /><span style="color: rgb(51, 153, 153);">//******* END OF CLASS ********</span><br /><br /><br /><span style="color: rgb(51, 153, 153);">//******* FUNCTIONS ********</span><br /><br />void camera_3D_setup()<br />{<br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0, (GLdouble)SCREEN_X / (GLdouble)SCREEN_Y, 1.0, 2000.0);<br />glTranslatef(-80.0f, -70.0f, -650.0f);<br /><br />}<br /><br />void camera_2D_setup()<br />{<br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />glOrtho(0.0, (GLdouble)SCREEN_X, (GLdouble)SCREEN_Y, 0.0, 0.0, 1.0);<br /><span style="color: rgb(51, 153, 153);">//glTranslatef(0.0f, 0.0f, 20.0f);</span><br />}<br /><br /><br />void draw_planets(planets *Orbit, GLUquadricObj *q_earth, GLUquadricObj *q_sun, GLuint *o_tex, ALLEGRO_BITMAP *back_gnd, GLfloat e_ang, GLfloat s_ang)<br />{<br />glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br />al_clear_to_color(al_map_rgb(0,0,0));<br />camera_2D_setup();<br />al_draw_bitmap(back_gnd, 0.0f, 0.0f, 0);<br /><br />Orbit->calc_planet_pos();<br />camera_3D_setup();<br />glEnable(GL_DEPTH_TEST);<br />glEnable(GL_TEXTURE_2D);<br /><br /><span style="color: rgb(51, 153, 153);">//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);</span><br /><span style="color: rgb(51, 153, 153);">//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);</span><br /><br />glBindTexture(GL_TEXTURE_2D, *(o_tex));<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br /><br />glMatrixMode(GL_MODELVIEW);<br />glLoadIdentity();<br /><br />glPushMatrix();<br />glTranslatef(Orbit->body_x / 5000.0f, Orbit->body_y / 5000.0f, Orbit->body_z / 5000.0f);<br />glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);<br />glRotatef(23.0f, 0.0f, 1.0f, 0.0f);<br />glRotatef(e_ang, 0.0f, 0.0f, 1.0f);<br /><br /><br />gluQuadricTexture(q_earth, GL_TRUE);<br />gluSphere(q_earth,8.0f,20,20);<br />gluQuadricTexture(q_earth, GL_FALSE);<br />glPopMatrix();<br /><br /><br />glPushMatrix();<br />glBindTexture(GL_TEXTURE_2D, *(o_tex+1));<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br /><br />glTranslatef(0.0f, 0.0f, 0.0f);<br />glRotatef(-80.0f, 1.0f, 0.0f, 0.0f);<br />glRotatef(-15.0f, 0.0f, 1.0f, 0.0f);<br />glRotatef(s_ang, 0.0f, 0.0f, 1.0f);<br /><br />gluQuadricTexture(q_sun, GL_TRUE);<br />gluSphere(q_sun,70.0f,28,28);<br />gluQuadricTexture(q_sun, GL_FALSE);<br />glPopMatrix();<br /><br />glDisable(GL_TEXTURE_2D);<br />glDisable(GL_DEPTH_TEST);<br /><br />glFlush();<br /><br />}<br /><br /><span style="color: rgb(51, 153, 153);">// ****** END OF FUNCTIONS ******</span><br /><br />int main(int argc, char *argv[])<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br />ALLEGRO_TIMER *timer = NULL;<br />ALLEGRO_BITMAP *bmp[3];<br /><br />planets A5_Orbit;<br /><br />int i = 0;<br />float FPS = 60.0f;<br />bool loop = true;<br />bool draw = false;<br /><br />GLfloat e_rot_angle = 0.0f;<br />GLfloat s_rot_angle = 0.0f;<br /><br />GLUquadricObj *quad_earth;<br />GLUquadricObj *quad_sun;<br /><br />GLuint ogl_tex[2];<br /><br />quad_earth = gluNewQuadric();<br />quad_sun = gluNewQuadric();<br /><br /><span style="color: rgb(51, 153, 153);">// Note; no error trapping as yet...</span><br />al_init();<br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUG</allegro5><allegro5 h="">GEST);<br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />event_queue = al_create_event_queue();<br />timer = al_create_timer(1.0f/FPS);<br /><br />al_set_new_bitmap_flags(ALLEGRO_MIPMAP | ALLEGRO_MIN_LINEAR);<br />bmp[0] = al_load_bitmap("img_1.jpg");<br />bmp[1] = al_load_bitmap("img_2.jpg");<br />al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);<br />bmp[2] = al_load_bitmap("img_stars.jpg");<br /><br />for(i = 0; i < 2; i++)<br />{<br />ogl_tex[i] = al_get_opengl_texture(bmp[i]);<br />}<br /><br />al_register_event_source(event_queue, al_get_display_event_source(display));<br />al_register_event_source(event_queue, al_get_timer_event_source(timer));<br /><br />al_start_timer(timer);<br /><br />while(loop == true)<br />{<br />ALLEGRO_EVENT event;<br />al_wait_for_event(event_queue, &event);<br /><br />switch(event.type)<br />{<br />case ALLEGRO_EVENT_DISPLAY_CLOSE:<br />loop = false;<br />break;<br /><br />case ALLEGRO_EVENT_TIMER:<br />draw = true;<br />break;<br /><br />default:<br />break;<br /><br />}<br /><br />if(draw == true && al_event_queue_is_empty(event_queue))<br />{<br />e_rot_angle += 0.75f;<br />s_rot_angle -= 0.15f;<br />draw = false;<br />draw_planets(&A5_Orbit, quad_earth, quad_sun, ogl_tex, bmp[2], e_rot_angle, s_rot_angle);<br /><br />al_flip_display();<br /><br />}<br />}<br /><br />for(i = 0; i < 3; i++)<br />al_destroy_bitmap(bmp[i]);<br /><br />glDeleteTextures(2, ogl_tex);<br />gluDeleteQuadric(quad_earth);<br />gluDeleteQuadric(quad_sun);<br /><br />al_flush_event_queue(event_queue);<br />al_destroy_event_queue(event_queue);<br />al_stop_timer(timer);<br />al_destroy_timer(timer);<br />al_destroy_display(display);<br /><br />return 0;<br />}<br /></allegro5></pre><br />So, this mini project uses some code from a previous post, <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2011/09/simple-orbit-in-3d-space.html">here</a>. What's different? The 3D orbit code is put into a handy class (<a style="color: rgb(0, 0, 153);" href="http://www.cplusplus.com/doc/tutorial/classes/">see here for a refresher</a>). I then use two <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2011/12/opengl-allegro-51-glusphere.html">gluSphere() objects, as used previously</a>, to make the two astral bodies, each with their own <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2011/12/opengl-allegro-51-mipmaps.html">mip-map textures, loaded by Allegro 5.1</a>. I also use a bitmap 2D star field background, the technique for which has already been discussed in <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2011/12/opengl-allegro-51-2d-bitmap-overlay.html">this post</a>, the only difference being that I used the bitmap as an underlay instead of an overlay (I draw the 2D bitmap first, each cycle of the animation, and the GL objects onto it afterward).<br /><br />Incidentally, to get the newest version of Allegro 5.1 (thanks elias!!!), use SVN as such, in Linux terminal;<br /><br /><span style="color: rgb(0, 102, 0);">svn co https://alleg.svn.sourceforge.net/svnroot/alleg/allegro/branches/5.1</span><br /><br />The main <span style="font-style: italic;">new</span> thing used here is <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glPushMatrix.xml">glPushMatrix() and glPopMatrix()</a>. I will be doing a separate post explaining myself the use of these to functions. I saw a great deal of explanations on the web on how these are functions are employed (mostly confusing), but only really "sussed" it out by trying it myself.<br /><br />Apart from all that, there are a few clarifications with the use of <span style="color: rgb(0, 102, 0);">glTexParameterf()</span>. It became apparent that this cannot just be set once (for use of mip-maps) and be expected to be applicable for the next object. It needs to be set every time <span style="color: rgb(0, 102, 0);">glBindTexture()</span> is used, or OpenGL reverts to default texture behavior, it seems.<br /><br />There is also a requirement to use this line, now;<br /><br /><pre><pre>glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);</pre></pre><br /><br />...or the GL_DEPTH_TEST does not work right during the animation. Also, Allegro-wise, a certain initial "glitchiness" in the animation was definately improved by using this line in the code...<br /><br /><pre>al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);<br /></pre><br /><br />Normally, <span style="color: rgb(0, 102, 0);">al_flip_display()</span> <span style="font-style: italic;">should </span>wait for the vertical sync (what in the old days we used to call a "vertical blank"), but it cannot be relied on entirely. The smoothness of the animation can be much improved by using <a style="color: rgb(0, 0, 153);" href="http://alleg.sourceforge.net/a5docs/refman/display.html#al_set_new_display_option">al_set_new_display_option()</a>, before creating the Allegro display, and employing <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glFlush.xml">glFlush()</a>, after the drawing of GL objects on each cycle.<br /><br />And, yes, finally, there is a certain importance to the order of placing glTranslatef() and glRotatef() functions in the code. You can rotate or translate first to get different actions of your GL models. What is <span style="font-weight: bold;"><span style="font-style: italic;">supremely </span></span>important to remember is that these functions, when applied to your object, operate in a local sense (that is, relative to the object). For example, translating (negative) first on the Z axis will send the object into the screen (farther away, analogous to the world's Z axis). But, if you rotate the object first, then translate on the Z axis, it will translate along the new orientation of the objects own Z axis, not the world's Z axis. Not difficult and fairly straightforward, but does need pointing out. And, of course, there will be another post covering this issue, soon. That said, the above applies for rotations on two axes. Something odd happens when you rotate on all three axes simultaneously, which I have yet to fathom out.<br /><br />Here's a screen shot of the finished product...<br /><pre><span style="color: rgb(51, 153, 153);"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeVRU4TNDEAk5-M5-4Aj1W763i9_6o7zCEwukvRs3oJ93v2fbnkYP-GBEGiF8_0Kl4fGUDlqadgXMcavr7-oA0-x_ocxOKllQi9rqN2TuxzzgxsIczk_jG8F7W87rDnbilU2043KUWKK4/s1600/Screenshot_3D_orbit_openGL_A51.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeVRU4TNDEAk5-M5-4Aj1W763i9_6o7zCEwukvRs3oJ93v2fbnkYP-GBEGiF8_0Kl4fGUDlqadgXMcavr7-oA0-x_ocxOKllQi9rqN2TuxzzgxsIczk_jG8F7W87rDnbilU2043KUWKK4/s320/Screenshot_3D_orbit_openGL_A51.png" alt="" id="BLOGGER_PHOTO_ID_5690854096991528930" border="0" /></a></span></pre> And that is all there is to say, this time around, but while I am on the OpenGL subject in this blog, here is another good OpenGL set of tutorials and lessons. JAVA, maybe, but the idea is still the same where OpenGL is concerned...<br /><br />OpenGL <a style="color: rgb(0, 0, 153);" href="http://jerome.jouvie.free.fr/opengl-tutorials/Tutorials1-5.php">Tutorials</a> and <a style="color: rgb(0, 0, 153);" href="http://jerome.jouvie.free.fr/opengl-tutorials/Lessons.php">Lessons</a>.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-62758373754084979172011-12-19T20:31:00.008-05:002012-01-01T17:32:10.698-05:00OpenGL / Allegro 5.1 gluSphere()Another snippet to document for posterity. I have been very busy working the last few days, but try to keep my hand in whenever I can. Here's what I tried.<br /><br /><pre><span style="color: rgb(0, 102, 0);">#include </span><allegro5 h=""><span style="color: rgb(0, 102, 0);">"allegro5/allegro.h" </span><span style="color: rgb(0, 102, 0);"><br />#include "allegro5/allegro_opengl.h"</span><span style="color: rgb(0, 102, 0);"> </span><span style="color: rgb(0, 102, 0);"><br />#include "allegro5/allegro_image.h"</span><span style="color: rgb(0, 102, 0);"> </span><span style="color: rgb(0, 102, 0);"><br />#include "GL/glu.h"</span><br /><br />const int SCREEN_X = 800;<br />const int SCREEN_Y = 600;<br /><br />int main(int argc, char *argv[])<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_BITMAP *bmp_earth = NULL;<br />GLuint tex_bmp = 0;<br /><br />al_init();<br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />bmp_earth = al_load_bitmap("dry_world.jpg");<br />tex_bmp = al_get_opengl_texture(bmp_earth);<br /><br />GLUquadricObj *gl_quad_obj;<br />gl_quad_obj=gluNewQuadric();<br /><br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0f, (GLdouble)SCREEN_X / (GLdouble)SCREEN_Y, 1.0f, 200.0f);<br />glRotatef(23.0f, 0.0f,0.0f, 1.0f); <span style="color: rgb(51, 153, 153);">// Trick photography; rotate the camera...</span><br /><br />glMatrixMode(GL_MODELVIEW);<br />glLoadIdentity();<br /><br />glTranslatef(0.0f,0.0f,-20.0f);<br />glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);<br /><br /><span style="color: rgb(51, 153, 153);">//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);</span><br /><br />glEnable(GL_DEPTH_TEST);<br />glEnable(GL_TEXTURE_2D);<br />glBindTexture(GL_TEXTURE_2D, tex_bmp);<br /><br /><span style="color: rgb(51, 153, 153);">//gluQuadricNormals(gl_quad_obj, GLU_SMOOTH</span></allegro5><allegro5 h=""><span style="color: rgb(51, 153, 153);">);</span><br />gluQuadricTexture(gl_quad_obj, GL_TRUE);<br />gluSphere(gl_quad_obj,5.0f,32,32);<br /><br />glDisable(GL_DEPTH_TEST);<br />glDisable(GL_TEXTURE_2D);<br /><br />al_flip_display();<br />al_rest(5.0);<br /><br />glDeleteTextures(1, &tex_bmp);<br />gluDeleteQuadric(gl_quad_obj);<br />al_destroy_display(display);<br />al_destroy_bitmap(bmp_earth);<br />return 0;<br />}<br /></allegro5></pre><br />Short and sweet. I had some curiosity about using the glu extension for something other than gluPerspective(), and as I had a hankering to make a planet, I experimented with <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/gluSphere.xml">gluSphere()</a>. Turns out it works okay with Allegro 5.1. The above is pretty minimalistic, just enough to get results.<br /><br />Here's the map I used, from <a style="color: rgb(0, 0, 153);" href="http://www.shatters.net/celestia/">Celestia</a>...<br /><pre><span style="color: rgb(0, 102, 0);"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6bDDT9eIqDcnZuQndiUkGip6z4PSu8VON2qpwwxPpvcW9jCyKrsnf8D2QrCer1JurTnMwm_Jc-nUYN-D-kzJalhpInjM98PBZjTktvRe8_5pA647U1tzYXE8yZOOVjyPPxl1XxR2YaI/s1600/dry_world.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 160px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6bDDT9eIqDcnZuQndiUkGip6z4PSu8VON2qpwwxPpvcW9jCyKrsnf8D2QrCer1JurTnMwm_Jc-nUYN-D-kzJalhpInjM98PBZjTktvRe8_5pA647U1tzYXE8yZOOVjyPPxl1XxR2YaI/s320/dry_world.jpg" alt="" id="BLOGGER_PHOTO_ID_5688022228697001042" border="0" /></a></span></pre> In this one, it became pretty important to enable the GL_DEPTH_TEST. Otherwise, you WILL get some funny effects. Note also, in the above code, the demo of how to rotate the camera!<br /><br />Of course, gluSphere() this is not the only gluQuadricObject that OpenGL can do. Again, the main reference <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/">OpenGL Manual</a>. Scroll the index on the left to glu, and there you have it; lots of cool stuff.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com1tag:blogger.com,1999:blog-782137547904165829.post-87801652128487096002011-12-16T13:35:00.018-05:002012-01-04T09:46:29.763-05:00OpenGL / Allegro 5.1 Alpha Transparency ExperimentVery short post this time. I stumbled around and took some shots in the dark to get Allegro 5.1's <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_convert_mask_to_alpha">al_convert_mask_to_alpha()</a> function to work with OpenGL. Strangely, the cookie crumbled correctly. I used a modified version of the <a style="color: rgb(0, 0, 153);" href="http://cplussplussatplay.blogspot.com/2011/12/so-in-this-post-i-might-say-that-i-am.html">code on my previous post</a>, and "doctored" the image of the godesses as such...<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWwwwa_G_zHPtmwg4G2b9r1adY71b7ZhSuFym764FqgfVdyrFs57BmvtPyJsW3kwS4nLxyEFq3sJJJYcmKCK5cy1msY2T-Sc-QOjgLVPIRCyuNILlWQfwON5mBUjqL8brmLV5t9b1VDi4/s1600/texture2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWwwwa_G_zHPtmwg4G2b9r1adY71b7ZhSuFym764FqgfVdyrFs57BmvtPyJsW3kwS4nLxyEFq3sJJJYcmKCK5cy1msY2T-Sc-QOjgLVPIRCyuNILlWQfwON5mBUjqL8brmLV5t9b1VDi4/s320/texture2.png" alt="" id="BLOGGER_PHOTO_ID_5686797614454229570" border="0" /></a>It now has a pink background of RGB components (255, 0, 255). That pink area will be turned into an alpha channel so that it can be transparent. Next, in the code, I added the following line into main (pale grey code implies code that was not changed from the previously posted code, slightly modified code is darker grey, and the newly added code is black)...<br /><br /><pre><span style="color: rgb(192, 192, 192);">al_set_new_display_flags(ALLEGRO_OPENGL);</span><br /><span style="color: rgb(192, 192, 192);">display = al_create_display(SCREEN_X, SCREEN_Y);</span><br /><span style="color: rgb(102, 102, 102);">tex_bmp = al_load_bitmap("texture2.png");</span><br />al_convert_mask_to_alpha(tex_bmp, al_map_rgb(255, 0, 255));<br /><span style="color: rgb(192, 192, 192);">event_queue = al_create_event_queue();</span><br /><span style="color: rgb(192, 192, 192);">timer = al_create_timer(1.0f/FPS);</span><br /><span style="color: rgb(192, 192, 192);">ogl_tex = al_get_opengl_texture(tex_bmp);</span></pre><br />The added line is taking the loaded bitmap and, using <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_convert_mask_to_alpha">al_convert_mask_to_alpha()</a> function with an <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_map_rgb">al_map_rgb()</a> value of (255, 0, 255), making any part of the image that is this color, technically, transparent.<br /><br />Now, how to make OpenGL use that channel as transparency? Somewhere, before entering the while() loop of the application, place this snippet of code...<br /><br /><pre>glAlphaFunc(GL_GREATER, 0.5);<br />glEnable(GL_ALPHA_TEST);<br /></pre><br /><br />I put it just after the calls to...<br /><br /><pre><span style="color: rgb(102, 102, 102);">al_start_timer(timer);</span> <span style="color: rgb(102, 102, 102);"><br />setup_3D_camera(); </span> </pre><br /><br />Now, what do they do? Still a bit of a mystery to me, as yet. Here is the page where I got the idea, for future reference...<br /><br /><a style="color: rgb(0, 0, 153);" href="http://www.edm2.com/0512/opengl.html">Let There Be Texture</a><br /><br />And here are the respective OpenGL reference pages to that GL function...<br /><br /><a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glAlphaFunc.xml">glAlphaFunc()</a><br /><br />...to scratch my head over in the following days. And as usual, <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml">glEnable()</a> is full of useful stuff.<br /><br />Anyway, here's the result (a bit tacky around the edges as a result of png anti-aliasing), but passable...<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCR3qJV8cbXFh6IX52lps-z4S4-TF2wkO9mk-C_16Oc1IjZAp6olAvpQq65gEA87JJLkxyRBOFqbDMr_9RSGkigCUm2Ut79lV9XI_O5pUe2jjzNVQrhhiVqR3sp-C1mrxyeGLSahEJy64/s1600/ogl_transparency.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCR3qJV8cbXFh6IX52lps-z4S4-TF2wkO9mk-C_16Oc1IjZAp6olAvpQq65gEA87JJLkxyRBOFqbDMr_9RSGkigCUm2Ut79lV9XI_O5pUe2jjzNVQrhhiVqR3sp-C1mrxyeGLSahEJy64/s320/ogl_transparency.png" alt="" id="BLOGGER_PHOTO_ID_5686803529555305170" border="0" /></a></pre><br />That's all, this time...WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-58205009707892481982011-12-14T13:12:00.023-05:002011-12-15T09:57:08.650-05:00OpenGL / Allegro 5.1 Animation and Back Face CullingSo, in this post I might say that I am now ready to tackle a slightly more complex integration of OpenGL into Allegro 5.1; Animation.<br /><br /><div style="text-align: justify;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBXTQ3wa_VfeUSCan92kSrmNMGc8Qb9c_09cS8N4J7YAzYhK9TkV6mdc_Ai9XD2HLOTd9GyiPR1-uWVGSjFcSgGUBA0hd8y6XP3L1VcMEFplKSeu0_JMkEH1wjq4JAM7NqSsJezg8zCKk/s1600/ogl_anim1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 249px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBXTQ3wa_VfeUSCan92kSrmNMGc8Qb9c_09cS8N4J7YAzYhK9TkV6mdc_Ai9XD2HLOTd9GyiPR1-uWVGSjFcSgGUBA0hd8y6XP3L1VcMEFplKSeu0_JMkEH1wjq4JAM7NqSsJezg8zCKk/s320/ogl_anim1.png" alt="" id="BLOGGER_PHOTO_ID_5686059337079042274" border="0" /></a><br />Much the same stuff that has been in my previous posts, up to a point; include the Allegro and GLU headers, set up a camera, draw a flat board with a texture on it, etcetera...</div><br /><pre><span style="color: rgb(51, 153, 153);">/*</span> <span style="color: rgb(51, 153, 153);">Comment --- reference library link line</span> <span style="color: rgb(51, 153, 153);"><br />`pkg-config --libs allegro-5.1 allegro_image-5.1`</span> <span style="color: rgb(51, 153, 153);"><br />-lGL</span><br /><span style="color: rgb(51, 153, 153);">-lGLU</span> <span style="color: rgb(51, 153, 153);"><br />*/</span><br /><br /><span style="color: rgb(0, 153, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 153, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 153, 0);">#include "allegro5/allegro_opengl.h"</span> <span style="color: rgb(0, 153, 0);"><br />#include "GL/glu.h"</span><br /><br />const int SCREEN_X = 800;<br />const int SCREEN_Y = 600;<br />const float FPS = 60.0;<br /><br /><span style="color: rgb(51, 153, 153);">// Function to keep angle values between 0 and 359</span><br />void angle_max(GLfloat &_angle)<br />{<br />if(_angle >= 360.0)<br />_angle -= 360.0;<br />else if(_angle < 0.0)<br />_angle += 360.0;<br />}<br /><br /><span style="color: rgb(51, 153, 153);">// Function to set up camera</span><br />void setup_3D_camera()<br />{<br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0, GLdouble(SCREEN_X) / GLdouble(SCREEN_Y), 1.0, 100.0);<br />}<br /></pre><div style="text-align: justify;">Except that now instead of drawing the camera straight into <span style="color: rgb(0, 51, 0);">int main()</span> function, I have the camera setting up in its own custom function. I also added a function called <span style="color: rgb(0, 51, 0);">angle_max()</span>, that receives a reference pointer of an angle. Here is a brief break down.<br /></div><br /><pre><span style="color: rgb(51, 153, 153);">//DEMO EXAMPLE</span><br />const int SCREEN_X = 800;<br />const int SCREEN_Y = 600;<br /></pre><div style="text-align: justify;">These are just the display size, in integers, to create an 800 x 600 pixel window. It may seem pretty obvoius, but I mention it here for one reason alone, one which I have not mentioned before. Allegro creates displays using integers values with <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_create_display">al_create_display()</a>. OpenGL uses the values of the display width and height to get the aspect ratio of the screen for the <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml">gluPerspective()</a> function, but requires the aspect ratio to be in a double type. These integers need to be type cast to double in the gluPerspective() function, or you get some funny perspective effects when the program runs. It caught me out. That's all!<br /></div><br /><pre><span style="color: rgb(51, 153, 153);">// DEMO EXAMPLE</span><br />const float FPS = 60.0;<br /></pre><br /><br /><div style="text-align: justify;">60 frames per second. Allegro stuff. This will be the speed of the timer to draw my animation. It is just declared here.<br /></div><br /><pre><span style="color: rgb(51, 153, 153);">// DEMO EXAMPLE</span><br />void angle_max(GLfloat &_angle)<br />{<br />if(_angle >= 360.0f)<br />_angle -= 360.0f;<br />else if(_angle < 0.0f)<br />_angle += 360.0f;<br />}<br /></pre><div style="text-align: justify;">Very simple, and needed only to keep the angle inside the 0º to 360º constraints. OpenGL uses angles in degrees. This function accepts an angle and makes sure that it never exceeds 359.99º or falls below 0º. It is a generic angle "corrector", and any angle of float type in degrees can be passed to it for the check, simplifying the code.<br /></div><div style="text-align: justify;"><br />The void <span style="color: rgb(0, 51, 51);">setup_3D_camera()</span> function is simply what has been seen in other posts on this blog, only here it is in a function. Big deal. In the end it only gets called once, anyway, to set up the <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixMode.xml">glMatrixMode(GL_PROJECTION)</a>.<br /></div><div style="text-align: justify;"><br />Now the function to draw the board, also in its own custom function this time around...<br /></div><br /><pre><span style="color: rgb(51, 153, 153);">// Function to draw the 3D bill board</span><br />void draw_board(GLuint _tex, GLfloat _ang_y)<br />{<br />glMatrixMode(GL_MODELVIEW),<br />glLoadIdentity();<br /><br />glTranslatef(0.0f, 0.0f, -20.0f);<br /><br />glRotatef(0.0f, 1.0f, 0.0f, 0.0f);<br />glRotatef(_ang_y, 0.0f, 1.0f, 0.0f);<span style="color: rgb(51, 153, 153);"> // Y axis, _ang_y variable used here.</span><br />glRotatef(0.0f, 0.0f, 0.0f, 1.0f);<br /><br />glEnable(GL_TEXTURE_2D);<br /><span style="color: rgb(51, 153, 153);"> //glEnable(GL_CULL_FACE);</span><br />glBindTexture(GL_TEXTURE_2D, _tex);<br /><br />glBegin(GL_QUADS);<br /><br />glTexCoord2f(0.0f, 0.0f);<br />glVertex3f(-2.5f, -2.5f, 0.0f);<span style="color: rgb(51, 153, 153);"> //lower lh corner</span><br />glTexCoord2f(1.0f, 0.0f);<br />glVertex3f(2.5f, -2.5f, 0.0f); <span style="color: rgb(51, 153, 153);">//lower rh corner</span><br />glTexCoord2f(1.0f, 1.0f);<br />glVertex3f(2.5f, 2.5f, 0.0f);<span style="color: rgb(51, 153, 153);"> //upper rh corner</span><br />glTexCoord2f(0.0f, 1.0f);<br />glVertex3f(-2.5f, 2.5f, 0.0f);<span style="color: rgb(51, 153, 153);"> //upper lh corner</span><br /><br />glEnd();<br /><br /><span style="color: rgb(51, 153, 153);"> //glDisable(GL_CULL_FACE);</span><br />glDisable(GL_TEXTURE_2D);<br />}<br /></pre><br /><br /><div style="text-align: justify;">Apart from the drawing of the bill board here being in a function, there are only two new and notable features. First, note that a variable for an angle is passed to this funtion (GLfloat _ang_y), and is used in the <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml">glRotatef()</a> call for the Y axis. The implicatin is that every time the function is called, a new value of the Y axis angle may be passed to the function. That would give me an "animation" of a sorts, right?<br /></div><br /><div style="text-align: justify;">The other feature of note is the inclusion of <span style="color: rgb(0, 51, 0);">glEnable(GL_CULL_FACE)</span>, though commented out for now. There will be more on this face culling business at the end of the post. Just for now, related to this topic, note that the bill board has been drawn in a counter-clockwise sense; that is, I started at the bottom left vertex, went to the bottom right, up to the top right, then to the top left. The order in which the vertices are drawn is important, when it comes to face culling.<br /></div><br /><pre>int main(int argc, char *argv[])<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_BITMAP *tex_bmp = NULL;<br />ALLEGRO_EVENT_QUEUE *event_queue = NULL;<br />ALLEGRO_TIMER *timer = NULL;<br /><br />bool loop = true;<br />bool draw = false;<br /><br />GLfloat angle_y = 0.0f;<br />GLfloat rate_y = 1.0f;<br />GLuint ogl_tex = 0;<br /><br />al_init();<span style="color: rgb(51, 153, 153);"> // Again, avoiding error trapping for clarity...</span><br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />display = al_create_display(SCREEN_X, SCREEN_Y);<br />tex_bmp = al_load_bitmap("texture1.jpg");<br />ogl_tex = al_get_opengl_texture(tex_bmp);<br /><br />event_queue = al_create_event_queue();<br />timer = al_create_timer(1.0f/FPS);<br /><br />al_register_event_source(event_queue, al_get_display_event_source(display));<br />al_register_event_source(event_queue, al_get_timer_event_source(timer));<br /><br />al_start_timer(timer);<br />setup_3D_camera();<br /><br /><span style="color: rgb(51, 153, 153);"> //glCullFace(GL_BACK);</span> <span style="color: rgb(51, 153, 153);"><br />//glCullFace(GL_FRONT);</span><br /></pre>So, what's new as from the previous posts? For starters I have declared an ALLEGRO_EVENT_QUEUE and an ALLEGRO_TIMER. There are also two booleans, loop and draw. Finally, there are two new GLfloats (angle_y and rate_y). Taken from the top...<br /><div style="text-align: justify;"><br />The <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/allegro_event_queue">ALLEGRO_EVENT_QUEUE</a> is created with <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_create_event_queue">al_create_event_queue()</a>, and it will be used to store events from different sources of input into the program (such as the display, or the timer, keyboard, mouse, and so on). However, in order for the event_queue to receive input from a ny of these sources, an "avenue" of communication must be opened from the source to the event_queue by using <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_register_event_source">al_register_event_source()</a>, or the program simply will not know that an input was intended at all. I register inputs for the event_queue from the display using <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_get_display_event_source">al_get_display_event_source()</a> and from the timer using <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_get_timer_event_source">al_get_timer_event_source()</a>. It makes the program keep an "eye open" for events from these two sources.<br /><br />The <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/allegro_timer">ALLEGRO_TIMER</a> is created with <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_create_timer">al_create_timer(1.0f/FPS)</a>. Basically (remembering that FPS = 60.0), I am dividing 1 second into 60 parts. This will create a timer that will throw an ALLEGRO_TIMER_EVENT into the event_queue every 1/60 of a second. In the code (later) I can retrieve and use that timer event to control the flow of the "animation".<br /><br />All of what is covered in the last two paragraphs is not specifically for OpenGL integration; it is plain old Allegro, and can be used in any Allegro 5 program. More on it at these two links.<br /></div><br /><a style="color: rgb(0, 0, 153);" href="http://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Events">Allegro 5 events tutorial</a><br /><br /><a style="color: rgb(0, 0, 153);" href="http://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Timers">Allegro 5 timer tutorial</a><br /><br /><div style="text-align: justify;">The GLfloat angle_y variable is the angle that will be passed to the custom <span style="color: rgb(0, 51, 0);">draw_board()</span> function, in order to set the angle of the board around its Y axis. The GLfloat rate_y is the amount by which the angle will change each frame loop of the animation.<br /><br />I then start the timer with <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_start_timer">al_start_timer(timer)</a>. With this, the timer is now running and throwing ALLEGRO_EVENT_TIMER every 1/60 of a second. And finally, I call the setup_3D_camera() function.<br /><br />I will ignore the two two commented out <a style="color: rgb(0, 0, 153);" href="http://www.opengl.org/sdk/docs/man/xhtml/glCullFace.xml">glCullFace()</a> lines for now.<br /><br />By the way, here is the bitmap I used for this sample program (an Anime "Oh! My Goddess" wallpaper cropped and "gimped")...<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPhcPOTovv9vdNV2Vuvj20Kn1lHXkE90STcsmI56XWlFfzH0eD0oQE0mNWxH3Cb1pyXsUYTLZSZ1D4m1KM8BV02L4FNlTG3lTT_wOs-qEy8gxZmmkoOTuyHTXclKo9XtPUp6_emcBzF0/s1600/texture1.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPhcPOTovv9vdNV2Vuvj20Kn1lHXkE90STcsmI56XWlFfzH0eD0oQE0mNWxH3Cb1pyXsUYTLZSZ1D4m1KM8BV02L4FNlTG3lTT_wOs-qEy8gxZmmkoOTuyHTXclKo9XtPUp6_emcBzF0/s320/texture1.jpg" alt="" id="BLOGGER_PHOTO_ID_5686050100553671730" border="0" /></a></pre><br />Now the loop that will detect the events and handle them...<br /></div><br /><pre><span style="color: rgb(51, 153, 153);"> // Start the loop</span><br />while(loop)<br />{<br />ALLEGRO_EVENT event;<br />al_wait_for_event(event_queue, &event);<br /><span style="color: rgb(51, 153, 153);"> // Detect the events</span><br />switch(event.type)<br />{<br />case ALLEGRO_EVENT_DISPLAY_CLOSE:<br />loop = false;<br />break;<br /><br />case ALLEGRO_EVENT_TIMER:<br />draw = true;<br />break;<br />}<br /><br /><span style="color: rgb(51, 153, 153);"> // Draw the screen if draw = true</span><br />if(draw == true && al_event_queue_is_empty(event_queue))<br />{<br />angle_y += rate_y;<br />angle_max(angle_y);<br />al_clear_to_color(al_map_rgb_f(0.0f, 0.0f, 0.5f));<br />draw_board(ogl_tex, angle_y);<br />al_flip_display();<br />draw = false;<br />}<br />}<br /></pre><br /><br /><div style="text-align: justify;">Yeah, while the boolean loop variable is true, the loop will keep cycling. First, I will make an empty <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/ALLEGRO_EVENT">ALLEGRO_EVENT</a> structure variable, and then wait for an event to be dropped into the event_queue with <a style="color: rgb(0, 0, 153);" href="http://www.allegro.cc/manual/5/al_wait_for_event">al_wait_for_event()</a>. When an event occurs, I can examine it in the event structure using a switch control loop.<br /><br />If I get an ALLEGRO_EVENT_DISPLAY_CLOSE event (ie; I click on the window "X" icon to close it), I set the loop variable to false, and the program exits the loop on the next while() cycle.<br /></div><br /><div style="text-align: justify;">If I get an ALLEGRO_EVENT_TIMER, and I will 1/60 of a second just so long as the timer is running, I set the draw variable to true. This will cause the drawing to take place by executing the <span style="color: rgb(0, 51, 0);">if(draw == true...)</span> statement.<br /><br />When the loop variable is set to false, the while(loop) exits and the following block is performed. The application ends.<br /></div><br /><pre> <span style="color: rgb(51, 153, 153);">// Clean up and kill the application</span><br />al_destroy_bitmap(tex_bmp);<br />al_stop_timer(timer);<br />al_destroy_timer(timer);<br />al_flush_event_queue(event_queue);<br />al_destroy_event_queue(event_queue);<br />al_destroy_display(display);<br />return 0;<br />}<br /></pre><br /><br /><div style="text-align: justify;">Now, what about that back face culling business? If the blocks of code above (barring, of course, the //DEMO EXAMPLE blocks) are strung together and compiled, I get an animation of the goddesses spinning around on a bill board. Both sides of the bill board will be seen, the second one being a reverse image of the first. The face that shows the black haired goddess with the red shawl (<span style="font-style: italic;">Skuld</span>, by name, incidentally) on the left is the front face, with the vertices arranged counter clockwise. If that board it flipped around around (as the animation does) the vertices will no longer be in a counter clockwise orientation. This is the back face, with <span style="font-style: italic;">Skuld</span> on the right. All very well, but maybe I only want the front face to be visible. In this case, I un-comment the lines in the draw_board() function;<br /></div><br /><pre>...<br /><span style="color: rgb(51, 153, 153);">//glEnable(GL_CULL_FACE);</span><br />...<br /><span style="color: rgb(51, 153, 153);">//glDisable(GL_CULL_FACE);</span><br />...<br /></pre><br /><br />By default, OpenGL culls (or suppresses the drawing of) the backface, that one with clockwise vertices. If I run the program now, we will only see the face of the board that has <span style="font-style: italic;">Skuld</span> on the left. However, we can manipulate OpenGL very easily so that it culls only the front face and leaves the back face alone. If I find the following line, just after al_start_timer()...<br /><br /><pre><span style="color: rgb(51, 153, 153);">//glCullFace(GL_FRONT);</span><br /></pre><br /><div style="text-align: justify;">...and un-comment it, then recompile and run, I will only see the face that has <span style="font-style: italic;">Skuld</span> on the right (ie; the back face).<br /><br />Back face culling is useful to speed up a program that has many objects in it, which the back faces need not be drawn (for example, they are the inside of a space ship that the user will never see anyway. Some more neat tricks with back face culling here...<br /><br /><a style="color: rgb(0, 0, 153);" href="http://www.videotutorialsrock.com/opengl_tutorial/backface_culling/text.php">Video Tutorials Rock Back face Culling</a><br /><br />That's all for this post.<br /></div>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-53810652438090700942011-12-04T11:33:00.012-05:002011-12-04T12:21:07.452-05:00OpenGL / Allegro 5.1 2D bitmap overlay panel<div style="text-align: justify;">And here's another small tidbit of using OpenGL with Allegro 5.1. Sooner or later, rendering 3D objects is not going to be enough to interact with an application. We are going to need a 2D "control panel", which will have indicators and switches, etcetera, like this very lame one I threw together...<br /></div><br /><div style="text-align: justify;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkLec09WP21cWrtILMD7qYauKfcj2U3Jx9m-Q3anfs3YXaUaB24GnDzhki9XTGAQWtrvkKhVAd7b3EJczkL-dJWaNgiFvqVBQNgOhLP5ubWmcrR8VdiH7PJrkZv0Ds0jVSiQ7ZJD6Krqo/s1600/panel.png"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 441px; height: 106px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkLec09WP21cWrtILMD7qYauKfcj2U3Jx9m-Q3anfs3YXaUaB24GnDzhki9XTGAQWtrvkKhVAd7b3EJczkL-dJWaNgiFvqVBQNgOhLP5ubWmcrR8VdiH7PJrkZv0Ds0jVSiQ7ZJD6Krqo/s320/panel.png" alt="" id="BLOGGER_PHOTO_ID_5682313053366629522" border="0" /></a></div><div style="text-align: justify;">It is a straight forward PNG bitmap, 800 x 197 pixels. The object is to put this "panel" at the bottom of my OpenGL display, after I have rendered my "3D" object.<br /><br />First some familiar stuff, except this time I'll make a flat triangle as my object just to vary a little...<br /><br /><span style="font-size:85%;"><span style="font-style: italic;">Note: Once again, all the following blocks of code can be put together to create the working sample program.</span></span><br /><br /><pre><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_opengl.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "GL/glu.h"</span><allegro5 h=""><allegro5 h=""><allegro5 h=""><gl h=""><br /><br />int main()<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_BITMAP *bmp_panel = NULL;<br /><br />al_init();<br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />display = al_create_display(800, 600);<br /><br />bmp_panel = al_load_bitmap("panel.png");<br /><br />glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0, GLdouble(800.0 / 600.0), 1.0, 200.0);<br /><br />glMatrixMode(GL_MODELVIEW);<br />glLoadIdentity();<br />glTranslatef(0.0, 0.75, -10.0);<br /><br />glBegin(GL_TRIANGLES);<br /><br />glColor3f(0.7, 0.0, 0.1);<br />glVertex3f(-1.5, -1.5, 0.0);<br />glColor3f(0.3, 0.0, 0.3);<br />glVertex3f(1.5, -1.5, 0.0);<br />glColor3f(0.2, 0.0, 0.7);<br />glVertex3f(0.0, 1.5, 0.0);<br /><br />glEnd();<br /></gl></allegro5></allegro5></allegro5></pre><br />Nothing really new here, but if I tried to put my panel onto the screen right now with <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_draw_bitmap">al_draw_bitmap()</a>, I would not get any results at all, as I would be trying to draw a 2D panel "into" a 3D world. OpenGL does not know how to fit it. However, there is a way. The display projection needs to be reset to 2D. This is where <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml">glOrtho()</a> comes in handy. The following bit of code will make the OpenGL display 2D "compatible".<br /><pre>glMatrixMode (GL_PROJECTION);<br />glLoadIdentity ();<br />glOrtho (0.0, 800.0, 600.0, 0.0, 0.0, 1.0);<br /></pre></div><br />More about 2D drawing with OpenGL <a style="color: rgb(51, 51, 255);" href="http://basic4gl.wikispaces.com/2D+Drawing+in+OpenGL">here...</a><br /><br />Now I am able to draw my bitmap onto the screen with al_draw_bitmap(). Note, I use <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_get_bitmap_height">al_get_bitmap_height()</a> to obtain how many pixels high the panel is, then subtract that value from the display Y value to place it properly on the display...<br /><pre>int bmp_height = al_get_bitmap_height(bmp_panel);<br />al_draw_bitmap(bmp_panel, 0.0, 600.0 - float(bmp_height), 0);<br /><br />al_flip_display();<br />al_rest(5.0);<br /><br />al_destroy_bitmap(bmp_panel);<br />al_destroy_display(display);<br /><br />return 0;<br />}<br /></pre><br />And that would be it. Here's the result...<br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieFFDGyW_Qs3-Qt-ovzoF1-vMyWzAVmk3b47rgZTJNLNBHfZRR-uVmedBXVSqe3WA-36myrIMAE_TbPgXZii-b4HCKuL4TwtoFWA-xuiO44OBgIuGGmnDt173DWNFx5PRBbQJoQ5xlnG4/s1600/Tri_w_Panel.png"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 460px; height: 354px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieFFDGyW_Qs3-Qt-ovzoF1-vMyWzAVmk3b47rgZTJNLNBHfZRR-uVmedBXVSqe3WA-36myrIMAE_TbPgXZii-b4HCKuL4TwtoFWA-xuiO44OBgIuGGmnDt173DWNFx5PRBbQJoQ5xlnG4/s320/Tri_w_Panel.png" alt="" id="BLOGGER_PHOTO_ID_5682319421753634274" border="0" /></a></pre>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-90817239221984833902011-12-04T10:08:00.007-05:002011-12-04T11:41:53.955-05:00OpenGL / Allegro 5.1 MipMapsWith thanks to Tomasu on #allegro IRC for the help with this...<br /><br />Here's how to enable mipmaps for OpenGL programs using Allegro 5.1.<br /><br />In the previous post, I applied a texture to a "board" on OpenGL utilizing the allegro function al_load_bitmap(), followed by al_get_opengl_texture(). However, that texture was simply an applied bitmap. If I tried using one of the mipmap options for glTexParameterf(), such as;<br /><br /><pre>glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);<br /></pre><br /><br />...the texture would not be mapped at all onto the board (it would be plain white). The solution is in making the bitmap a mipmap before using it as an OpenGL texture using <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_set_new_bitmap_flags">al_set_new_bitmap_flags()</a>. This must be called prior to loading the bitmap, in order for Allegro to generate the mipmaps. I opted for this as an experiment...<br /><br /><pre>al_set_new_bitmap_flags(ALLEGRO_MIPMAP | ALLEGRO_MIN_LINEAR);<br />bmp = al_load_bitmap("texture.jpg"); <span style="color: rgb(51, 153, 153);">// 256 x 256 pixels</span><br />ogl_tex = al_get_opengl_texture(bmp);<br /></pre><br />Then, when I called <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">glTextParameterf()</a> for setting the the GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER, I did it this way...<br /><pre>glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br /></pre><br />The result is now a mipmap texture on the board. Pretty cool. When done loading bitmaps as mipmaps, the bitmap loading can be reset to default by resetting the flags, as such...<br /><pre>al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);<br /></pre><br />That's the default, and any loading of subsequent bitmaps will no longer be mipmaps. The complete listing that can be used to test this function is in my previous post on this blog.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-58441798193355341892011-12-01T11:07:00.039-05:002012-01-04T13:50:33.408-05:00Open GL / Allegro 5.1 Basics: Textures<div style="text-align: justify;">So, after a year, I decided to tackle OpenGL again to find that I have forgotten almost everything about it. Previously, I had used glut and explored the possibility, with some limited success, of using Allegro 4's OpenGL functionality. However, now there is a new version of Allegro; 5.1.0. These posts are going to be about using OpenGL with that particular programming API, on Linux (Debian Wheezy), with the Code::Blocks IDE.<br /><br />Here are some links...<br /></div><br /><a style="color: rgb(51, 51, 255);" href="http://www.videotutorialsrock.com/">Video Tutorials Rock</a><br /><a style="color: rgb(51, 51, 255);" href="http://wiki.allegro.cc/index.php?title=Allegro_5_API_Tutorials">Allegro Tutorial</a><br /><a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/">OpenGL Reference (2.1)</a><br /><a style="color: rgb(51, 51, 255);" href="http://www.talisman.org/opengl-1.1/Reference/glTexParameter.html">OpenGL Reference (1.1)</a><br /><a style="color: rgb(51, 51, 255);" href="http://www.codeblocks.org/">Code::Blocks</a><br /><a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/">Allegro.cc (find out more about Allegro API here...)</a><br /><br />I believe the support for the version of OpenGL on my Linux (libgl1-mesa-dev and libglu-mesa-dev) is for version 2.1, complete. In any case, here's the pertinent output of my versions from glxinfo run in a Linux terminal (that goes with the binary NVIDIA-Linux driver for my on board GPU)....<br /><pre>direct rendering: Yes<br />client glx vendor string: NVIDIA Corporation<br />client glx version string: 1.4<br />...<br />OpenGL vendor string: NVIDIA Corporation<br />OpenGL renderer string: GeForce 7025 / nForce 630a/PCI/SSE2<br />OpenGL version string: 2.1.2 NVIDIA 290.10<br />OpenGL shading language version string: 1.20 NVIDIA via Cg compiler<br /></pre><br />With all that in mind, here we go...<br /><br />The objective here is simply render a texture on a GL_QUAD-rilateral. But first it will be necessary to initialize Allegro, and make it acceptable for working with OpenGL. This is relatively easy...<br /><br />(Note: The following blocks of code are completely contiguous. They can be pasted together in the order that they appear to create the complete, working listing, unless a particular block excludes itself specifically with a CAPITALIZED comment saying <span style="color: rgb(51, 153, 153);">//DEMO EXAMPLE</span>. Such commented blocks need not or should not be placed in the listing).<br /><br /><pre><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_opengl.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 102, 0);">#include "GL/glu.h"</span><br /><br /><br />int main()<br />{<br />ALLEGRO_DISPLAY *display = NULL;<br />ALLEGRO_BITMAP *bmp = NULL;<br /><br />GLuint ogl_tex; <span style="color: rgb(51, 153, 153);">// The OpenGL texture id.</span><br /><br />al_init();<br />al_init_image_addon();<br /><br />al_set_new_display_flags(ALLEGRO_OPENGL);<br />display = al_create_display(1200, 800);<br /><br /><span style="color: rgb(51, 153, 153);"> // Load a bitmap to use as a texture</span><br />bmp = al_load_bitmap("texture.jpg"); <span style="color: rgb(51, 153, 153);">// 256 x 256 pixels</span><br />ogl_tex = al_get_opengl_texture(bmp);<br /></pre><br />...and here is the "texture.jpg" I am using, by the way. It is some screen background I got off the web and "Gimped" down to 256 x 256.<br /><div style="text-align: left;"><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgStDTL4ig6TmcA9zSG2nUlKFNJAMXJL1CeIV79FA6hUyuefF9o_gb9X0gb8xVUZv7vv-BH8un7SfXNnWezFxF_J6xaqtk0ErReQKHmiEgsue5zJGOX9G13NQMrFlLuJf91vTD05kvcFp8/s1600/texture.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 256px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgStDTL4ig6TmcA9zSG2nUlKFNJAMXJL1CeIV79FA6hUyuefF9o_gb9X0gb8xVUZv7vv-BH8un7SfXNnWezFxF_J6xaqtk0ErReQKHmiEgsue5zJGOX9G13NQMrFlLuJf91vTD05kvcFp8/s320/texture.jpg" alt="" id="BLOGGER_PHOTO_ID_5681230435975865714" border="0" /></a> </pre></div><br /><div style="text-align: justify;">So, what's important here? Several things, straight off the bat, which are going to be useful, if not indispensable, for using OpenGL with Allegro 5.1.0. In the include headers lines is <span style="color: rgb(0, 102, 0);">"allegro_opengl.h"</span>, required to use OpenGL with the API. Also, there are the <span style="color: rgb(0, 102, 0);">"allegro_image.h"</span> and <span style="color: rgb(0, 102, 0);">"glu.h"</span> headers. The image addon is initiated in the code with the line <a href="http://www.allegro.cc/manual/5/al_init_image_addon"><span style="color: rgb(51, 51, 255);">al_init_image_addon()</span></a>, and will be needed for handling the loading of the jpg image that will be used as the texture. The <span style="color: rgb(0, 102, 0);">glu.h</span> is needed for for using the gluPerspective() function, which will provide the camera for my OpenGL world.<br /><br />The respective libraries are linked the following way, for Allegro-5.1.0;<br /></div><pre>`pkg-config --libs allegro-5.1 allegro_image-5.1`<br />-lGL<br />-lGLU<br /></pre><div style="text-align: justify;">Now, before creating the <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/ALLEGRO_DISPLAY">ALLEGRO_DISPLAY</a>, the call to <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_set_new_display_flags">al_set_new_display_flags(ALLEGRO_OPENGL) </a>is, apparently, required. That said, I have tried running OpenGL through Allegro-5.1.0 excluding this call, and it works perfectly well and without any notable difference, which is strange(?).<br /><br />Finally, that little block of code loads texture.jpg as an <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/ALLEGRO_BITMAP">ALLEGRO_BITMAP</a> with <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_load_bitmap">al_load_bitmap()</a>, and then assigns that bitmap as a (type) GLuint texture id (ogl_tex) with <a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_get_opengl_texture">al_get_opengl_texture()</a>. It is now usable as an OpenGL texture for later on.<br /></div><br />Onwards...<br /><pre>glMatrixMode(GL_PROJECTION);<br />glLoadIdentity();<br />gluPerspective(35.0, 1200.0 / 800.0, 1.0, 400.0);<br /></pre><br /><div style="text-align: justify;">This is the camera view on the OpenGL world.<br /><br /><a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixMode.xml">glMatrixMode(GL_PROJECTION)</a> puts OpenGL in the mode that allows the definition of the view camera. <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadIdentity.xml">glLoadIdentity()</a> then pr oceeds to enable, or execute that mode, replacing any glMatrixMode that may have been in use before. Finally, <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml"><span style="color: rgb(51, 51, 255);">gluPerspective()</span></a> sets up the qualities of the camera. In order, the first parameter sets the angle, in degrees that the camera sees. With it, you can simulate a telephoto lens (with low FOV angles), or fish eye lenses (with wide FOV angles). The second parameter is the aspect ratio of the view port, or display. It is obtained by dividing the display width by the display height, (1200 / 800) in this case. The last two parameters are the closest render distance and farthest render distance, respectively, in OpenGL units. Anything closer than 1.0 or farther than 400.0 units from the Camera will not be rendered. Yeah, okay. I understand all the above better worded that way! And that's the camera done.<br /><br />The next bit of code builds the object (a flat board!) that we are going to see, and applies the texture we prepared earlier...<br /><pre>al_clear_to_color(al_map_rgb(0,0,50));<br /><br />glMatrixMode(GL_MODELVIEW);<br />glLoadIdentity();<br /><br />glTranslatef(2.0f, -1.5f, -18.0f);<br />glRotatef(75.0, -1.0f, 0.0f, 0.0f);<br />glRotatef(10.0, 0.0f, -1.0f, 0.0f);<br />glRotatef(0.0, 0.0f, 0.0f, 1.0f);<br /><br />glEnable(GL_TEXTURE_2D);<br />glBindTexture(GL_TEXTURE_2D, ogl_tex);<br /><br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);<br />glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);<br /><br /><span style="color: rgb(51, 153, 153);">// Other options for GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T...</span><br /><span style="color: rgb(51, 153, 153);">// GL_CLAMP, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT</span><br /><br />glBegin(GL_QUADS);<br /><br />glTexCoord2f(0.0, 0.0);<span style="color: rgb(51, 153, 153);"> // Bottom left hand corner</span><br />glVertex3f(-3.5, -6.5, 0.0);<span style="color: rgb(51, 153, 153);"> // X,Y,Z</span><br />glTexCoord2f(6.0, 0.0); <span style="color: rgb(51, 153, 153);">// Bottom right hand corner</span><br />glVertex3f(3.5, -6.5, 0.0);<span style="color: rgb(51, 153, 153);"> // X,Y,Z</span><br />glTexCoord2f(6.0, 8.0);<span style="color: rgb(51, 153, 153);"> // Top right hand corner</span><br />glVertex3f(3.5, 6.5, 0.0);<span style="color: rgb(51, 153, 153);"> // X,Y,Z</span><br />glTexCoord2f(0.0, 8.0);<span style="color: rgb(51, 153, 153);"> // Top left hand corner</span><br />glVertex3f(-3.5, 6.5, 0.0);<span style="color: rgb(51, 153, 153);"> // X,Y,Z</span><br /><br />glEnd();<br /><br />glDisable(GL_TEXTURE_2D);<br /></pre><br />Okay, that looks quite long and confusing right now, but it is not the end of the world. I will dissect it...<br /><br /><a style="color: rgb(51, 51, 255);" href="http://www.allegro.cc/manual/5/al_clear_to_color">al_clear_to_color()</a> is self explanatory, really. The only reason I mention it is to show that it is perfectly compatible with OpenGL.<br /><br />And again, we use glMatrixMode() and glLoadIdentity(), though this time we set the GL_MODELVIEW mode, as we are going to make a flat quadrilateral (that is, a GL_QUAD).<br /><br /><a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glTranslate.xml">glTranslatef(X,Y,Z)</a> needs a very quick review. It moves the "origin" (let's say) of our object to be created to a point in 3D space, defined by X,Y,Z, in OpenGL units. In this case, I moved the origin +2.0 X (to the right), -1.5 Y (down) and -18.0 Z (into the screen). Basically, it is a matrix translate, as the name implies.<br /><br /><a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml">glRotatef(angle, X,Y,Z)</a> is a matrix rotate , around the origin. Here I break the rotation process up into three separate operations, one for each axis, with it's own angular change. To put it simply, positive rotation values rotate the object around it's origin counter-clockwise, as viewed parallel from the positive side of local object's local axis it is being rotated around.<br /><br />We now come to the texture handling part. The first thing to do is to enable the texture with <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml">glEnable(GL_TEXTURE_2D)</a>. There's a hell of a lot of things that can be "enabled" with this function in OpenGL. Here, suffice to say that our texture(s) are enabled so that we can use them on our object, okay? The function has an opposite number, <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml">glDisable()</a><span style="color: rgb(51, 51, 255);">,</span> which is used to "disable" just about everything that can be enabled, as the program situations require, and is described on the same reference page as glEnable(). Note that at the end of the code block above, I disable GL_TEXTURE_2D, after I have used it.<br /><br />Following that, I invoked <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glBindTexture.xml">glBindTexture(GL_TEXTURE_2D, ogl_tex)</a>. As "bind" suggests, this function "attaches" or "associates" (but does not specifically "place" or "position") the texture, referenced by its GLui nt identifier, to the object we are about to build. Enough said about that, really. Let's get onto setting that texture and making the object...<br /><br />It's time to look at setting some parameters for that texture. For this, I use <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">glTexParameterf() or glTexParameteri()</a>. GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER basically define how the texture image is "compressed" or "spread" onto the object when the rendered siz e of the object is not equal to the true size of the texture bitmap. This will occur most of the time. Right here, I set both of them as GL_LINEAR, as I am not doing any mip-mapping just yet.<br /><br />glTexParameter can also be used to set the "tiling" (wrapping) behavior characteristics of the texture on the object. GL_TEXTURE_WRAP_S determines how the texture will behave horizontally across the object, if it is repeated more than once, that is. More on how that is done in a minute. I want the texture to repeat itself mirroring itself every time it is rendered horizontally, so I use GL_MIRRORED_REPEAT.<br /><br />GL_TEXTURE_WRAP_T determines how the texture will repeat vertically. Here I just want it to repeat and be a non-mirrored replica of itself, so I use GL_REPEAT.<br /><br />In all cases above the target (the first parameter of glTexParameter) was my 2D texture, that is GL_TEXTURE_2D.<br /><br />At last, I can get down to making my object. Fir st I will tell it what, exactly, I am planning to build, so that OpenGL has an idea of how many vertices will be involved. I emp loy <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glBegin.xml">glBegin(GL_QUADS)</a> for this. A quadrilateral, with four vertices.<br /><br />Now, I could just go ahead and make a QUAD with the following;<br /><pre><span style="color: rgb(51, 153, 153);">// DEMO EXAMPLE</span><br />glVertex3f(-3.5, -6.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z bottom left</span><br />glVertex3f(3.5, -6.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z bottom right</span><br />glVertex3f(3.5, 6.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z top right</span><br />glVertex3f(-3.5, 6.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z top left</span><br /></pre><br />...which is fairly self explanatory, but I will have wasted all that effort in setting up the texture.<br /><br />I will "place", or "position" if you like, the texture on my QUAD by using <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">glTexCoord2f()</a>. Now that reference description I found a bit overly complicated for the present purposes. It would suffice here to say that these are more or less the same thing as u/v coords, as used in 3D modeling programs. In this case, take a look at the picture of the texture up above in this post. The lower left corner is (X,Y) position (0,0), the lower right (1,0), the top right (1,1) and the top left (0,1). The function glTexCoord2f() uses these coords to map the 2D texture onto the object, as many times as we want, abiding by the repeat properties set with the GL_TEXTURE_WRAP parameter. glTexCoord2f() is specified just before creating the respective vertex point with <a style="color: rgb(51, 51, 255);" href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">glVertex3f()</a>.<br /><br />The following example would map the texture exactly onto the QUAD once;<br /><pre><span style="color: rgb(51, 153, 153);">// DEMO EXAMPLE</span><br /><span style="color: rgb(51, 153, 153);">// Bottom left</span><br />glTexCoord2f(0.0, 0.0);<br />glVertex3f(-3.5, -6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Bottom right</span><br />glTexCoord2f(1.0, 0.0);<br />glVertex3f(3.5, -6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Top right</span><br />glTexCoord2f(1.0, 1.0);<br />glVertex3f(3.5, 6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Top left</span><br />glTexCoord2f(0.0, 1.0);<br />glVertex3f(-3.5, 6.5, 0.0);<br /></pre>...and this would map it 2 times horizontally and once vertically...<br /><pre><span style="color: rgb(51, 153, 153);">// DEMO EXAMPLE</span><br /><span style="color: rgb(51, 153, 153);">// Bottom left</span><br />glTexCoord2f(0.0, 0.0);<br />glVertex3f(-3.5, -6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Bottom right</span><br />glTexCoord2f(2.0, 0.0);<br />glVertex3f(3.5, -6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Top right</span><br />glTexCoord2f(2.0, 1.0);<br />glVertex3f(3.5, 6.5, 0.0);<br /><br /><span style="color: rgb(51, 153, 153);">// Top left</span><br />glTexCoord2f(0.0, 1.0);<br />glVertex3f(-3.5, 6.5, 0.0);<br /></pre>Once I have finished drawing my QUAD and placing the texture as I want it, I end the drawing with glEnd(), and disable work on the texture with glDisable(GL_TEXTURE_2D). Then it is just a case of flipping the buffer to the display, letting the image show for a few seconds, and cleaning up...<br /><pre>al_flip_display();<br />al_rest(10.0);<br /><br />glDeleteTextures(1, &ogl_tex);<br />al_destroy_bitmap(bmp);<br />al_destroy_display(display);<br />return 0;<br />}<br /></pre><br />That would be it. You would get something like this...<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp5_nW8QcbTvcutvSofDw6ZLxAAlqLr55fOWmJ6Acz0BzX8PNMzsy6-5YnFBbwyYxT3riDsQc-LBQVprDfOgG-Ita82JlA_DNwYXXCtRspsd_GACWSQ9qENmq9g8Mbrlw75lu_kHnJkMo/s1600/final_product.png"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 488px; height: 335px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp5_nW8QcbTvcutvSofDw6ZLxAAlqLr55fOWmJ6Acz0BzX8PNMzsy6-5YnFBbwyYxT3riDsQc-LBQVprDfOgG-Ita82JlA_DNwYXXCtRspsd_GACWSQ9qENmq9g8Mbrlw75lu_kHnJkMo/s320/final_product.png" alt="" id="BLOGGER_PHOTO_ID_5681314738585555058" border="0" /></a></pre><br />More coming soon...<br /><br /></div>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com2tag:blogger.com,1999:blog-782137547904165829.post-45463957926818954542011-12-01T08:54:00.009-05:002011-12-01T09:18:56.110-05:00Obtaining Normals of a 3D vectorBefore I address any 3D stuff in this blog I am going to review the process of obtaining normalized 3D vectors. It is not difficult, but with my penchant to forget everything in five minutes, I consign it to posterity in this post. Below are 3 distances (X, Y, Z) of a 3D vector. The object is to normalize each of them as if the total length (of a 3D hypotenuse) were one;<br /><pre><br /> (X, Y, Z)<br /><br /> (5.4, 3.7, 6.2)<br /></pre><br />Obtain the true total length...<br /><pre><br /> sqrt(5.4² + 3.7² + 6.2²) = 9.0161<br /></pre><br />Now simply divide each component by the total length...<br /><pre><br /> 5.4 / 9.0161 = 0.5989 (for X)<br /><br /> 3.7 / 9.0161 = 0.4104 (for Y)<br /><br /> 6.2 / 9.0161 = 0.6877 (for Z)<br /></pre><br />So our vector, normalized to one, now looks like;<br /><pre><br /> (0.5989, 0.4104, 0.6877)<br /></pre><br />And that is it, basically.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-41763127616526733152011-11-30T23:21:00.009-05:002011-11-30T23:42:18.357-05:00Code Box TestJust found <a style="color: rgb(0, 0, 153);" href="http://www.ialwayscapital.com/2009/05/how-to-insert-code-in-blog-post.html">this</a> which might help writing blocks of code in my posts...<br /><br />Let's see;<br /><br /><pre><span style="color: rgb(0, 51, 0);">#include "allegro5/allegro.h"</span><br /><span style="color: rgb(0, 51, 0);">#include "allegro5/allegro_opengl.h"</span><br /><span style="color: rgb(0, 51, 0);">#include "allegro5/allegro_image.h"</span><br /><span style="color: rgb(0, 51, 0);">#include "GL/glu.h"</span><br /><br /><br />int main()<br />{<br /> ALLEGRO_DISPLAY *display = NULL;<br /> ALLEGRO_BITMAP *bmp = NULL;<br /><br /> GLuint ogl_tex; <span style="color: rgb(51, 153, 153);">// The OpenGL texture id.</span><br /><br /> al_init();<br /> al_init_image_addon();<br /><br /> al_set_new_display_flags(ALLEGRO_OPENGL);<br /> display = al_create_display(800, 600);<br /><br /> <span style="color: rgb(51, 153, 153);">// Load a bitmap to use as a texture</span><br /> bmp = al_load_bitmap("texture.jpg"); <span style="color: rgb(51, 153, 153);">// 256 x 256 pixels</span><br /> ogl_tex = al_get_opengl_texture(bmp);<br /><br /> glMatrixMode(GL_PROJECTION);<br /> glLoadIdentity();<br /> gluPerspective(35.0, 800.0 / 600.0, 1.0, 400.0);<br /><br /> glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br /><br /> glMatrixMode(GL_MODELVIEW);<br /> glLoadIdentity();<br /> glTranslatef(0.0f, 0.0f, -15.0f);<br /><br /> glEnable(GL_DEPTH_TEST);<br /><br /> glEnable(GL_TEXTURE_2D);<br /> glBindTexture(GL_TEXTURE_2D, ogl_tex);<br /><br /> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);<br /> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);<br /><br /> glBegin(GL_QUADS);<br /><br /> glNormal3f(0.0, 0.0, 1.0);<br /><br /> glTexCoord2f(0.0, 0.0); <span style="color: rgb(51, 153, 153);">// Bottom left hand corner</span><br /> glVertex3f(-1.5, -1.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z</span><br /> glTexCoord2f(2.0, 0.0); <span style="color: rgb(51, 153, 153);">// Bottom right hand corner</span><br /> glVertex3f(1.5, -1.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z</span><br /> glTexCoord2f(2.0, 2.0); <span style="color: rgb(51, 153, 153);">// Top right hand corner</span><br /> glVertex3f(1.5, 1.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z</span><br /> glTexCoord2f(0.0, 2.0); <span style="color: rgb(51, 153, 153);">// Top left hand corner</span><br /> glVertex3f(-1.5, 1.5, 0.0); <span style="color: rgb(51, 153, 153);">// X,Y,Z</span><br /><br /> glEnd();<br /><br /> glDisable(GL_TEXTURE_2D);<br /> glDisable(GL_DEPTH_TEST);<br /><br /> al_flip_display();<br /><br /> al_rest(10.0);<br /><br /> glDeleteTextures(1, &ogl_tex);<br /> al_destroy_bitmap(bmp);<br /> al_destroy_display(display);<br /> return 0;<br />}<br /></pre><br /><br />How does that look?<br /><br />Yeah, not too bad. I am learning something about blogging, after all. I'll use that from now on. The font size is 80%.<br /><br /><"<"><br /><br />As for the listing in the test example, well, that will be the subject of my next series of posts here.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-12922555964542523452011-10-02T19:59:00.007-05:002011-10-02T20:51:42.241-05:00Increasing the time resolution<div style="text-align: justify;">Okay, I said I would conclude the installments on the naval gun, but as it is now familiar, I will use it here (once more) to introduce time update resolution problems. Yeah, I lied! Imagine that...<br /><br />In the naval gun example, the time update was at X1. Because gravity and velocity is "rated", let's say, on a "once per second" basis, I elected to do the update of the shell "state" once every second. This is all very well for a demo, but let's say I want to include the naval gun in a real time "<a href="http://www.cofepow.org.uk/pages/ships_exeter.htm">HMS Exeter in The Java Sea</a>" type of game. If I left the screen update at 1 second, it would be a very "chuggy" game indeed, a bit like the old days when over ambitious simulators were made to run on 286's.<br /><br />So, here's a simple example of a ballistic model;<br /></div><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">while(y_pos >= 0)</span> <span style="color: rgb(51, 102, 102);">// while the shell is above the sea</span><br /><span style="color: rgb(0, 0, 153);">{</span><br /><span style="color: rgb(0, 0, 153);">x_vel = cos(angle) * vel;</span><br /><span style="color: rgb(0, 0, 153);">y_vel = sin(angle) * vel;</span><br /><br /><span style="color: rgb(0, 0, 153);">angle = atan2f((y_vel - grav), x_vel);</span><br /><span style="color: rgb(0, 0, 153);">vel = sqrtf((pow((y_vel - grav), 2) + pow(x_vel, 2)));</span><br /><br /><span style="color: rgb(0, 0, 153);">x_pos += x_vel;</span><br /><span style="color: rgb(0, 0, 153);">y_pos += (y_vel - grav);</span><br /><br /><span style="color: rgb(0, 0, 153);">usleep(1000000);</span><br /><span style="color: rgb(0, 0, 153);">}</span></span><br /><br /><div style="text-align: justify;">This updates every second (<span style="color: rgb(0, 0, 153);">usleep(1000000)</span>), with a second based physics model. But I want it to update, say, 4 times every second! Here's what I do;<br /></div><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double FPS = 4; </span><br /><span style="color: rgb(51, 102, 102);">// Could be an int, but it is better to stay compatible with the types it will be used with.<br /><span style="color: rgb(0, 0, 153);">useconds_t t_factor = useconds_t(1000000 / FPS);</span><br /></span><br /></span><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">while(y_pos >= 0)</span> <span style="color: rgb(51, 102, 102);">// while the shell is above the sea</span><br /><span style="color: rgb(0, 0, 153);">{</span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">x_vel = cos(angle) * vel;</span><br /><span style="color: rgb(0, 0, 153);">y_vel = sin(angle) * vel;<br /><br /></span></span><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">angle = atan2f((y_vel - (grav / FPS)), x_vel);</span><br /><span style="color: rgb(0, 0, 153);">vel = sqrtf((pow((y_vel - (grav / FPS)), 2) + pow(x_vel, 2)));</span><br /><br /><span style="color: rgb(0, 0, 153);">x_pos += (x_vel / FPS);</span><br /><span style="color: rgb(0, 0, 153);">y_pos += ((y_vel / FPS) - (grav / FPS));</span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);"><br />usleep(t_factor);</span><br /><span style="color: rgb(0, 0, 153);">}<br /><br /></span></span><div style="text-align: justify;">What I have to consider here is that gravity is only going to be doing a quarter of its work (as it is 9.807 m/s²). Also, velocity is going to need to go only a quarter of the distance (m/s) per update. However, the velocity itself should not really be played with until it is turned into its components (read on).<br /><br />It is also important to consider that the gravity "reduction" needs to be included in the angle recalculation. If it is not, the the rate of change stays as if on a one second basis, and considerably shortens the range (as the angle would be changing at X4 of what it should). Finally, the usleep function is modified by the t_factor, making the real screen update run at 4 times a second, so a real time simulation is retained.<br /><br />Where drag is concerned, the force of drag would need to be quartered (drag / FPS), and this would be subtracted from the "real" momentum (velocity * mass). That is why it is important not to affect the net velocity by FPS (ie; do NOT turn an 800 m/s velocity into a 200 m/s to simplify the calculations), and to modify the computed components instead, when they are being added to the positions.<br /><br />Dang! I wish I had been able to see that BBC program on Exeter!<br /><br /></div>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-54976862154321266752011-10-01T19:41:00.002-05:002011-11-12T21:20:52.689-05:00Link to Naval GunSo, that concludes the installments of the naval gun. Likely I will continue to develop it a bit more. The continuing story of it now goes over to my <a href="http://sourceforge.net/projects/shipgunmodel/files/">Sourceforge account</a>. Free for all, source and configuration files.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-53197605216277678702011-09-28T10:52:00.000-05:002011-09-28T14:02:20.500-05:00Final polish to the Naval GunAnd now I want to study that interpolation function, so I have reference to return to in case I forget (again) how I did it. What happens?<br /><span style="color: rgb(0, 0, 153);font-size:78%;" ><br />double ref_Mach_table[MACH_CD_ELEMENTS] =<br />{-0.10, 0.00, 0.40, 0.60, 0.87, 1.02, 1.15, 2.40, 3.40};<br /><br />double ref_Cd_table[MACH_CD_ELEMENTS] =<br />{0.000, 0.231, 0.229, 0.172, 0.139, 0.333, 0.341, 0.268, 0.255};<br /><br /><span style="color: rgb(51, 153, 153);">//............</span><br /><br />double Cd_Calculator(double Mach)<br />{<br />int index_count;<br /><br />for(index_count = 0; index_count < MACH_CD_ELEMENTS; index_count++)<br />{<br />if(ref_Mach_table[index_count] > Mach)<br />break;<br />}<br /><br />double inter_factor =<br />(Mach - ref_Mach_table[index_count - 1]) / (ref_Mach_table[index_count] - ref_Mach_table[index_count - 1]);<br /><br />double output =<br />((ref_Cd_table[index_count] - ref_Cd_table[index_count - 1]) * inter_factor) + ref_Cd_table[index_count - 1];<br /><br />return output;<br />}</span><br /><br />The function is called using the calculated Mach number as the parameter. The for loop counts up (increasing the index_count) until the value in the Mach table (the ref_Mach_table array element of the index_count) exceeds the value of the passed Mach number, then breaks out with the corresponding index_count.<br /><br />The range of the table values, the upper and the lower, that enclose the passed Mach value is then calculated;<br /><br /><span style="font-weight: bold; font-style: italic; color: rgb(51, 51, 255);font-size:85%;" >ref_Mach_table[index_count] - ref_Mach_table[index_count - 1]</span><br /><br />For example, a value of Mach 1.9 is passed. That being between 2.40 and 1.15, the formula will calculate the Mach range of;<br /><br /><span style="color: rgb(51, 51, 255);font-size:85%;" ><span style="font-weight: bold; font-style: italic;">2.40 - 1.15 = 1.25</span></span><br /><br />Then the difference of the passed Mach value from the lower table value is calculated;<br /><br /><span style="font-weight: bold; font-style: italic; color: rgb(51, 51, 255);font-size:85%;" >1.9 - 1.15 = 0.75</span><br /><br />Then, in coefficient (percentage, if you like) of the relation of the relation of the true difference to the available table range is calculated;<br /><br /><span style="color: rgb(51, 51, 255);font-size:85%;" ><span style="font-weight: bold; font-style: italic;">0.75 / 1.25 = 0.6</span></span><br /><br />And that is the interpolation factor for the ref_Cd_table array, and what is happening in the line that calculates <span style="font-style: italic; color: rgb(0, 0, 153);">inter_factor</span>. I am just doing a straight forward linear interpolation here, by the way. Armed with inter_factor and index count, I then move to the ref_Cd_table and find the range of the two Cd values that correspond to the index_count and index_count - 1.<br /><br /><span style="font-weight: bold; font-style: italic; color: rgb(51, 51, 255);font-size:85%;" >ref_Cd_table[index_count] - ref_Cd_table[index_count - 1]</span><br /><br />These values are 0.268 and 0.341. So we get;<br /><br /><span style="font-style: italic; font-weight: bold; color: rgb(51, 51, 255);font-size:85%;" >0.268 - 0.341 = -0.073</span><br /><br />A negative value, as the Cd is <span style="font-style: italic;">reducing</span> with increase in Mach at these velocities. I multiply that by the inter_factor to get what the Cd change is between the two Cd table values at 0.6 (60%) between them.<br /><br /><span style="color: rgb(51, 51, 255);font-size:85%;" ><span style="font-weight: bold; font-style: italic;">0.6 x -0.073 = -0.0438</span></span><br /><br />I now add that to the Cd value that corresponds to the [index_count - 1] element, which is 0.341;<br /><br /><span style="color: rgb(51, 51, 255);font-size:85%;" ><span style="font-weight: bold; font-style: italic;">0.341 + -0.0438 = 0.2972</span></span><br /><br />And that is the new interpolated Cd returned by the function (result of the computations for the variable <span style="font-style: italic; color: rgb(0, 0, 153);">output</span>). This is the Cd value that gets passed to the drag calculation formula.<br /><br />Now, finally, a refinement of the drag calculations by obtaining the air density at the shell's altitude. As air density reduces with increasing altitude, the shell is going to be traveling through a less dense medium at the top of its trajectory. I believe it was <a href="http://www.prinzeugen.com/PGIND.htm">this ship</a>, that ended her days as a war trophy and was subjected to nuclear bomb tests at <a href="http://www.underwaterkwaj.com/kwaj/eugen.htm">Kwajalein atoll</a>, that had high elevation guns (37º, where most maximum elevations were in the region of 25º to 30º), which greatly enhanced the fighting capabilities of the already formidable 8" main weapon by sending the shell high into the lower density atmosphere.<br /><br />I got the density ratios for ISA out of a book I have, Aerodynamics for Naval Aviators, though I am sure it can be found also with a good internet search.<br /><span style="color: rgb(51, 102, 102);font-size:78%;" ><br /><span style="color: rgb(0, 0, 153);">const int REF_RHO_ELEMENTS = 12;</span><br /><br /><span style="color: rgb(0, 0, 153);">double ref_rhos[REF_RHO_ELEMENTS] = </span><br /><span style="color: rgb(0, 0, 153);">{1.000, 0.8617, 0.7385, 0.6292, 0.5328, 0.4481, 0.3741, 0.3099, 0.2462, 0.1936, 0.1522, 0.1197};</span></span><br /><span style="color: rgb(51, 102, 102);font-size:78%;" >// These are percentage values of the air density, relative to the SL density</span><span style="color: rgb(51, 102, 102);font-size:78%;" > of 1.225 kg/m³</span><br /><span style="font-size:78%;"><span style="color: rgb(51, 102, 102);">// The resolution of the density ratio is for every 1525 meters.</span></span><br /><br /><span style="font-size:78%;"><span style="color: rgb(51, 102, 102);">// Related declarations...</span></span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double rho_gradient = 1525; <span style="color: rgb(51, 102, 102);">// Meters</span><br />double actual_rho;<br /></span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double Rho_Calculator(double altitude, double gradient, double sl_rho)<br />{<br />double alt_ref = altitude / gradient;<br /><span style="color: rgb(51, 102, 102);">// Eg; 1872 / 1525 = 1.227541<br /><br /></span> int index_count = int(alt_ref);<br /><span style="color: rgb(51, 102, 102);">// Eg; int(1.227541) = 1. This is the index position in ref_rhos[]</span><br /><span style="color: rgb(51, 102, 102);"> // As this is a constant increment of altitude gradient,</span><br /><span style="color: rgb(51, 102, 102);"> // the interpolation factor is pretty direct, as in;<br /><br /></span> double inter_factor = alt_ref - index_count;<br /><br />double rho_coeff =<br />((ref_rhos[index_count + 1] - ref_rhos[index_count]) * inter_factor) + ref_rhos[index_count];<br /><br />return sl_rho * rho_coeff;<br />}</span><br /><br />This is actually a simpler interpolation than the previous, as it does not have a variable primary interpolation range, as the first one did. The altitude increments are constant, and only the density ratio lapse relaxes with increasing altitude. It is fairly self explanatory. It is called, BEFORE the drag calculation, like this...<br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >actual_rho = Rho_Calculator(ypos, rho_gradient, ISA_Rho);</span><br /><br />And the drag calculation, to recap, looks like this...<br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double Velocity_Calculator(double vel, double mass, double rho, double d_coeff, double FA)<br />{<br /> double energy = vel * mass;<br /> double drag = 0.5 * rho * pow(vel, 2) * d_coeff * FA;<br /> energy -= drag;<br /> return energy / mass;<br />}</span><br /><br />Note that I now have it as a separate function, called from inside the update loop, after all the Rho and Mach calculations are done, like this...<br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >velocity =<br />Velocity_Calculator(velocity, mass_kg, actual_rho, Cd, frontal_area);</span><br /><br />That would be all, for now.WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-50500281888717715172011-09-26T20:48:00.000-05:002011-09-28T14:03:42.981-05:00More for the Naval Gun<div style="text-align: justify;">As the title says, a bit more for the Naval Gun. I am going to do this in a couple of posts, to keep it in bite sized portions. Ballistics, especially of a spinning shell, is a complex, 3 dimensional arena. This model is limited to 2D for the moment. The day may come when I decide to tackle precession induced drift. For the moment, I just want to make this one, as it is, a bit more credible. One of the features of a naval shell, as fired from those magnificent warships of World War I and II, was that they were <a href="http://www.grc.nasa.gov/WWW/k-12/airplane/sound.html">supersonic</a>, a while before aircraft broke the legendary "<a href="http://www.century-of-flight.net/Aviation%20history/jet%20age/top%20speed.htm">sound barrier</a>".<br /><br />One of the notable traits of approaching "the speed of sound" is that the behavior of drag changes. There is a sharp increase in drag through the transonic region, and then a gradual fall off of this increased drag as the body accelerates beyond the speed of sound. This behavior is best represented, in a simulator model, by making the Coefficient of Drag a variable, the value of which is dependent upon the shell's velocity in relation to Mach 1. In my previous model, Cd was a fixed value. This was okay for starters, but it is NOT correct.<br /><br />Now, things get a bit more intricate. The speed of sound is itself dependent on the atmospheric temperature. If you are a pilot, or even a dedicated "simmer", you will probably have an ARC-1 or <a href="http://en.wikipedia.org/wiki/E6B">E6B Flight Computer</a>. There is a little scale on the computer side, where you match up a temperature to an M symbol, and read off the speed of an equivalent Mach number from the inner scale to knots on the outer scale. As a rough guide, a velocity of 340.278 m/s is equivalent to Mach 1 at 15º C (<a href="http://www.engineeringtoolbox.com/international-standard-atmosphere-d_985.html">International Standard Atmosphere</a> at Sea Level). And every drop of 1º C incurs a reduction of Mach 1 equivalent velocity by 0.61 m/s, approximately. So, if the temperature is 12º C, then Mach 1 is equivalent to;<br /><br /><span style="font-size:85%;"><span style="font-weight: bold; color: rgb(0, 0, 153); font-style: italic;">340.278 - ((15 - 12) * 0.61)</span></span><br /><br />To get the Mach number from this, divide the actual velocity of the shell (in m/s) by the corresponding Mach 1, in m/s, obtained for the given temperature.<br /><br /><span style="color: rgb(0, 0, 153);font-size:85%;" ><span style="font-weight: bold; font-style: italic;">Mach_Number_of_Shell = Velocity_of_Shell_ms / Velocity_of_Mach_1_ms</span></span><br /><br />And that, basically, is what I am going to do with my model as a required step in determining a variable Coefficient of Drag. The temperature drops at an environmental lapse rate of 1º C per every 152.39 meters gained.<br /><br />Declarations first, with all the others;<br /><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double ISA_temp_C = 15;</span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double temp_meters_degree_C = 152.39; <span style="color: rgb(51, 102, 102);">// Meters of altitude per every -1º C</span></span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double ref_Mach_1 = 340.278; <span style="color: rgb(51, 102, 102);">// Meters per second at 15º C</span></span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double Mach_fall_off = 0.61; <span style="color: rgb(51, 102, 102);">// Mach meters per second less per every -1º C</span></span></span><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">double velocity_Mach;</span></span><br /><br />Now, I decided to make a small function that I would call from inside the update loop. Here it is;<br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double Mach_Calculator(double altitude, double SL_temp, double alt_temp_lapse, double ref_M1, double Mach_lapse, double vel)</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >{</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" > double temp_at_alt = SL_temp - (altitude / alt_temp_lapse);</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" > double Mach_1_at_alt = ref_M1 - ((SL_temp - temp_at_alt) * Mach_lapse);</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" > return vel / Mach_1_at_alt;</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >}</span><br /><br />And that was it, in the simplest terms. I now have the shell velocity expressed in Mach number. I call the function with these parameters;<br /><br /><span style="font-size:78%;"><span style="color: rgb(0, 0, 153);">velocity_Mach =<br />Mach_Calculator(ypos, ISA_temp_C, temp_meters_degree_C, ref_Mach_1, Mach_fall_off, velocity);</span></span><br /><br />Onwards. Now let's go get that variable Coefficient of Drag. There are (very complicated) ways of computing Cd. I opted for an easy way out, at this stage. Check out <a href="http://en.wikipedia.org/wiki/External_ballistics">this page</a>. Down on the Doppler Radar-measurements section, there is a model of a bullet's coefficient of drag at different Mach velocities. I mapped something like that into two arrays, declared globally, in my program, like this;<br /><span style="color: rgb(0, 0, 153);font-size:78%;" ><br />const int MACH_CD_ELEMENTS = 9;<br /><br />double ref_Mach_table[MACH_CD_ELEMENTS] =<br />{-0.10, 0.00, 0.40, 0.60, 0.87, 1.02, 1.15, 2.40, 3.40};<br /><br />double ref_Cd_table[MACH_CD_ELEMENTS] =<br />{0.000, 0.231, 0.229, 0.172, 0.139, 0.333, 0.341, 0.268, 0.255};</span><br /><br />Simply matched up some Mach numbers (first array) to some Cd values (second array. Now it will just be a case of using the computed Mach velocity of the shell to find a corresponding Cd. So, it is all very well to say, looking at the two arrays, that if the shell is traveling at Mach 1.15, then the Cd is 0.341 (both being the seventh element in their respective arrays), but what if the shell is doing Mach 1.95? The Cd value is somewhere between 0.341 and 0.268. We need to interpolate. Here's the function that does it...<br /><span style="color: rgb(0, 0, 153);font-size:78%;" ><br /></span><div style="text-align: left;"><span style="color: rgb(0, 0, 153);font-size:78%;" >double Cd_Calculator(double Mach)</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >{</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >int index_count;</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" ><br />for(index_count = 0; index_count < MACH_CD_ELEMENTS; index_count++)</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >{</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >if(ref_Mach_table[index_count] > Mach)</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >break;</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >}</span><br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double inter_factor =<br />(Mach - ref_Mach_table[index_count - 1]) / (ref_Mach_table[index_count] - ref_Mach_table[index_count - 1]);</span><br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >double output =<br />((ref_Cd_table[index_count] - ref_Cd_table[index_count - 1]) * inter_factor) + ref_Cd_table[index_count - 1];</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" ><br />return output;</span><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >}</span><br /></div><br />So, what's it doing? That's the subject of the next post, where I will also interpolate to get a variable atmospheric density at different altitudes, adding a bit more polish to the model. For the moment, this function gets called AFTER determining the Mach number and BEFORE calculating the drag (in the update loop), like this...<br /><br /><span style="color: rgb(0, 0, 153);font-size:78%;" >Cd = Cd_Calculator(velocity_Mach);</span><br /><br />That's already improved the model. It now has a variable Coefficient of Drag, dependent on the Mach number, which is in turn dependent on the shell velocity at different air temperatures.<br /></div>WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0tag:blogger.com,1999:blog-782137547904165829.post-61495360827410518302011-09-23T09:09:00.000-05:002011-09-28T14:04:57.786-05:00Naval GunAnd now a brief intermission from all that "space stuff". I thought I might tackle the challenge of modeling a ballistic trajectory of a 15" naval gun, as used on <a href="http://www.warspite.dk/">this ship, for example</a>. Now there are a couple of factors that influence the trajectory of a projectile of a given velocity; gravity and drag.<br /><br />Gravity I have already looked into in my previous posts. For this model, fired in a local area on the surface of the Earth, the variations in its acceleration would be negligible, so I established it constant at 9.807 m/s². At this point I will just go ahead and plot a trajectory without drag (air resistance) to see what I get.<br /><br />Apart from the gravity, I established some initial data for the calculation. They would be shell velocity (initial, or muzzle velocity if you like), and angle (elevation) of the gun, so that it fires upwards like a mortar shot, the way it did, and loft the shell off towards its target in the distance. Here's how the program started off (I am not going to do that complete color formatting anymore, it is a pain. From now, comments are going to be pale blue, code in dark blue);<br /><span style="font-size:85%;"><br /><span style="color: rgb(0, 0, 153);">int main()</span><br /><span style="color: rgb(0, 0, 153);">{</span><br /><span style="color: rgb(0, 0, 153);"> <span style="color: rgb(51, 102, 102);">// variable declarations...</span><br />double grav_accel = 9.807;</span><br /><span style="color: rgb(0, 0, 153);"> double velocity = 785;</span> <span style="color: rgb(51, 102, 102);">// meters per second</span><br /><span style="color: rgb(0, 0, 153);">double xpos = 0, ypos = 0;</span> <span style="color: rgb(51, 102, 102);">// The shell positions, each unit a meter.</span><br /><span style="color: rgb(0, 0, 153);"> double xvel, yvel;</span> <span style="color: rgb(51, 102, 102);">// The shell X and Y (2D) velocity components.</span><br /><span style="color: rgb(0, 0, 153);"> double angle = 0.40;</span><span style="color: rgb(51, 102, 102);"> // Angle in radians, approximately 23º.</span><br /><span style="color: rgb(51, 102, 102);">// For the record (I am always mixing this up)....</span><br /><span style="color: rgb(51, 102, 102);"> // To convert radians to degrees, first divide them by PI, then multiply by 180.</span><br /><span style="color: rgb(51, 102, 102);"> // To convert degrees to radians, first divide them by 180, then multiply by PI.<br /><br />// Code continues after the following comment....<br /></span></span><br />Once that is established, we can set up a loop to move the shell. The thing to consider here is that during the upward travel of the shell, gravity is going to slow its velocity on the Y component (vertical). This will slowly alter the trajectory, as it will also slow its total velocity. And on the "way down" gravity will increase the projectile velocity (drag being nonexistent, to start with. Later we will see how drag affects this).<br /><br />This is what needs to be calculated.<br /><span style="font-size:85%;"><br /><span style="color: rgb(51, 102, 102);">// Code continues here...</span><br /><br /><span style="color: rgb(0, 0, 153);"> while(ypos >= 0)</span> <span style="color: rgb(51, 102, 102);">// While the shell is above the "ground", or "sea", as it were.</span><br /><span style="color: rgb(0, 0, 153);">{</span><br /><span style="color: rgb(0, 0, 153);"> xvel = cos(angle) * velocity;</span><br /><span style="color: rgb(0, 0, 153);"> yvel = sin(angle) * velocity;</span><br /><br /><span style="color: rgb(0, 0, 153);"> xpos += xvel;</span><br /><span style="color: rgb(0, 0, 153);"> ypos += (yvel - grav_accel);</span><br /><br /><span style="color: rgb(0, 0, 153);"> velocity = sqrt(pow(xvel, 2) + pow((yvel - grav_accel), 2));</span><br /><span style="color: rgb(0, 0, 153);"> angle = atan2f((yvel - grav_accel), xvel);</span><br /><br /><span style="color: rgb(0, 0, 153);">usleep(1000000);</span><br /><span style="color: rgb(0, 0, 153);">}</span><br /><span style="color: rgb(0, 0, 153);">return 0;</span><br /><span style="color: rgb(0, 0, 153);">}<br /><br /></span></span>At first glance it may appear that there is a bit much there. Why not just compute the vertical velocity component and leave it at that? There would be no change to the horizontal component of the velocity as it changed its trajectory, which is what really happens. That is why. As the shell travels upwards, a component of gravity is subtracting velocity from the shell's total velocity.<br /><br />For example, if the code were left simply as...<br /><br /><span style="font-size:85%;"><span style="color: rgb(0, 0, 153);">xvel = cos(angle) * velocity;</span><br /><span style="color: rgb(0, 0, 153);"> yvel = sin(angle) * velocity;</span><br /><br /><span style="color: rgb(0, 0, 153);"> xpos += xvel;</span><br /><span style="color: rgb(0, 0, 153);"> ypos += (yvel - grav_accel);</span></span><br /><br />...and looped through as that, the horizontal velocity would always be the result of <span style="color: rgb(0, 0, 153); font-weight: bold; font-style: italic;font-size:85%;" >Cos(angle) x velocity</span> (ie; Cos(22º) x 785 = 727.8 m/s). This would extend the range of the gun unrealistically, as there would be no degradation of its velocity caused by it "fighting its way uphill" at the beginning of its trajectory. The new velocity is calculated by invoking Pythagoras with the new components of X and Y velocity. Then the new angle of the resultant trajectory is also calculated from the new components (atan()), and that data is fed back into the loop. The program will run as it is, now.<br /><br />However, that is not the end of the story. The shell travels through the atmosphere, and the atmosphere has a density that causes aerodynamic drag. The formula for drag is;<br /><br /><span style="font-size:85%;"><span style="font-weight: bold; font-style: italic; color: rgb(0, 0, 153);">Drag = 0.5 x Velocity² x Air_Density x Drag_Coefficient x Frontal_Area</span></span><br /><br />In SI units, that would give you the force of drag in Newtons (I believe. Most of my usage of lift and drag formula in the past have been in Imperial units, which provides the force in pounds. This is the first time I am applying it with SI units). So, now I needed to supply some more data to the program in order to calculate this. Velocity we already have, so we add to the variable declarations the following;<br /><br /><span style="font-size:85%;"> <span style="color: rgb(51, 102, 102);"> <span style="color: rgb(0, 0, 153);"> double frontal_area = 0.1148;</span> // m², about the frontal area of a 15 inch diameter shell.</span> <span style="color: rgb(51, 102, 102);"> <span style="color: rgb(0, 0, 153);"><br />double Cd = 0.22; <span style="color: rgb(51, 102, 102);">// An estimated, "probable" coefficient of drag.</span></span></span> <span style="color: rgb(51, 102, 102);"> <span style="color: rgb(0, 0, 153);"><br />double Rho = 1.225;</span> // kg/m³, air density, according to SL ISA.</span> <span style="color: rgb(51, 102, 102);"> <span style="color: rgb(0, 0, 153);"><br />double drag;</span></span><br /></span><br />All very well, but now the drag has to "work" against something. That "something" would be the momentum (inertia) of a shell of a certain mass traveling at a certain velocity. Here is where I get a bit "plauged" by doubt, but I do believe that I am correct in stating that the motion inertia of the shell is equivalent to its mass multiplied by its velocity. Let's call it "momentum" for the moment, to differentiate it from kinetic energy, which is a different formula (and provides preposterously large results for the purpose of this model). The unit of the product would be the Kg Meter per Second (Newton Second). I add these two more variables to the declarations in the code;<br /><span style="color: rgb(0, 0, 153);font-size:85%;" ><br />double mass = 870;<span style="color: rgb(51, 102, 102);"> // kg</span><br />double momentum;</span><br /><br />And now to the calculations. This would go inside the loop, in the code above, just after the angle calculation and before the usleep() function call.<br /><br /><span style="color: rgb(0, 0, 153);font-size:85%;" >drag = 0.5 * Rho * pow(velocity, 2) * Cd * frontal_area;<br />momentum = (velocity * mass) - drag; <span style="color: rgb(51, 102, 102);">// I believe the units are compatible here.<br />// At least, the results of the running program are quite acceptable.<br /><br /><span style="color: rgb(0, 0, 153);">velocity = momentum / mass;<br /><span style="color: rgb(51, 102, 102);">// From the energy, devise the new velocity, and then loop back</span></span><span style="color: rgb(51, 102, 102);"> to the beginning,</span><br />// armed with the new trajectory angle and shell velocity.<br /><br /></span></span>It <span style="font-style: italic;">might</span> not be perfect, but the output (which can be seen by adding some cout statements to the code) is actually quite convincing. There is a slight excess of range, but I believe it might be because the Cd is a tad low. After all, it was only estimated. The shell goes up almost 4 km, travels 29 km, taking 55 seconds to do so, and arrives at the target at a velocity of about 480 m/s, having decelerated from its original 785 m/s, and been at about 540 m/s at the top of the trajectory. These figures are not far off the performance of the real gun.<br /><br />After note:<br />I had some fun with this one. Obviously, the code can be cleaned up, or neatly packaged away in a function, but the basic idea is there. A couple of members of a naval history forum provided me with some additional sources of information (data) on gun ranges and trajectories. I really was not far off, at all, except that it is closer to the real performance of the gun if the coefficient of drag is raised to 0.25 or 0.26, instead of 0.22.<br /><br />'Till the next installment!WillShttp://www.blogger.com/profile/13902287459910377307noreply@blogger.com0