<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8479224368899661003</id><updated>2011-12-27T10:17:48.868+01:00</updated><category term='linux'/><category term='hack'/><category term='x41'/><category term='maths'/><category term='howto'/><category term='tablet'/><category term='C/C++'/><category term='programming'/><category term='reverse engineering'/><category term='Summer of Code'/><category term='assembly'/><category term='Mobile Arena'/><category term='internship'/><category term='perlin'/><category term='C#'/><category term='console'/><category term='OIIO GSoC'/><category term='SDL'/><category term='thefarm51'/><category term='windows'/><category term='inequation.org'/><category term='nasza-klasa'/><category term='usability'/><category term='Destination'/><category term='noise'/><category term='OpenGL'/><category term='google'/><category term='Crystal Space GSoC'/><title type='text'>I can do engineering, me!</title><subtitle type='html'>Ramblings of an overeager wannabe on open source software and game development.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.inequation.org/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-8853734091760453360</id><published>2011-11-16T12:45:00.012+01:00</published><updated>2011-11-17T14:02:52.677+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internship'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>On Google's internship recruitment process</title><content type='html'>I've recently been into their internship program and I get asked about this a lot, so I'll just wrap it up in a blog post for everyone to see how it works. I'll mostly let the Google employees' emails describe the thing.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;UPDATE:&lt;/b&gt; Due to some concerns about the classification of the emails I quoted, I temporarily removed them. I'm awaiting Google's response on the subject, hopefully they'll allow me to put them back up again.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;First Contact&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It all started with &lt;a href="http://www.linkedin.com/"&gt;LinkedIn&lt;/a&gt; (if you work in IT and don't have an account there, I strongly suggest you to get one!) - a Google recruiter stumbled upon my profile (email date, so that you can get an idea of the timeline: March 2, 2011):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;i&gt;[here was an email inviting me to take part in Google's full-time engineer recruitment]&lt;/i&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Huh? You can imagine my surprise upon receiving this email. Anyway, I replied with something along the lines of "I'm flattered by your offer, but I've still got 2 years until graduation, yadda yadda." I thought that'd be the last time I heard from her, but then (March 2, 2011):&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;i&gt;[here was an email offering me to go for an internship instead]&lt;/i&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Hmm. Hell, I thought, it's nothing binding, bring it on. Besides, I didn't really expect them to actually contact me. And, guess what... I was wrong again! May 11, 2011 brings me this email:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;i&gt;[here was an email giving an overview of the internship and detailing the initial requirements - CV, education, going back to university after the internship etc., also stating that if I want to go for the 2011 tour, I need to decide quickly, the other option being waiting a few months and contacting again later for a 2012 tour]&lt;/i&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;The Email Exchange&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We arranged to wait until autumn 2011 for further contact. In September I emailed Frida again with another inquiry and received further information about the entire program, and got a response on September 15:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;i&gt;[here was an email detailing how the recruitment process works (2 to 3 phone interviews + an informal chat with the project manager once you're assigned to one) and a list of information you need to provide - preferred languages, interesting projects you've recently taken part in, areas of interest, university GPA, possible internship time spans and preferred Google office locations (you need to work on site)]&lt;/i&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;After submitting this information, Frida arranged my first interview. In the meantime I received some tips for preparation. The following came in on the 23rd of September.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Warning - a pretty lengthy read ahead!&lt;/b&gt; It's not all relevant though, you can safely skip to the next non-indented paragraph.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;i&gt;[here was an email that they allegedly usually send to full-time applicators only, but the recruiter thought it'd be useful to me; it's a lengthy list of tips on how to prepare for the interview, mostly stuff that's good to know, including the basics of algorithms and data structures, networks, computer graphics, maths etc., together with some suggestions on how to brush your skills]&lt;/i&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;The Interviews&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;The interviews take the form of a phone call (Google pays) with a Google Document shared between you and the interviewer (who is a Google engineer) and lasts from 45 minutes to 1 hour. A hands-free phone set, a broadband Internet connection and sufficient power supply (for the phone and/or the laptop) are needed for the basic comfort. Every interview begins with a short chat about your background - your work, university, areas of interest, then you go on to solving some programming problems, coding inside the Google document. After completing the task, you are asked to evaluate the algorithm complexity and, if possible, offer ideas for optimizations.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;In the first interview I was tasked with coding a non-uniform random number generator - I had an array of numbers that were supposed to be returned and a corresponding array of probabilities.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;In the second one, I were given 2 strings, and was supposed to return the first, minus the characters from the second one; as a second task, I was asked to find the maximum sum of a subsequence in a finite sequence of numbers.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;In the third one, the interviewer started by asking me to explain, what exactly happens from the moment a user types in a URL into the address bar of a web browser to when the website actually appears on the screen. A few more questions about DNS and routing followed, and then we moved on to a programming task - a reader of a very simple image format (1 bit per pixel, black &amp;amp; white) together with a unit test.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;After each interview there is a bit of time when you can ask any questions you might have about how Google works.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"&gt;The Resolution&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;And that's how I got where I am currently - after a positive resolution (I received word on the November 11) of the feedback gathering process you are asked to once again reconfirm your areas of interest, preferred Google office locations and availability. It should take 4 to 6 weeks for them to find a project that matches your profile.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"&gt;And that's pretty much all there is to it!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-8853734091760453360?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/8853734091760453360/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=8853734091760453360' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/8853734091760453360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/8853734091760453360'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2011/11/on-googles-internship-recruitment.html' title='On Google&apos;s internship recruitment process'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-5213534108698014214</id><published>2011-04-28T17:57:00.005+02:00</published><updated>2011-04-28T18:14:43.589+02:00</updated><title type='text'>Thoughts on cheating in online gaming</title><content type='html'>And now for something completely different. I've written this short analysis of the cheating phenomenon in multiplayer first person shooter games as a preface to my C++ university project report. It summarizes my thoughts on the subjects from the past few years, and I'm posting it here to use it to save my breath/keyboard strokes when explaining the weakness of the PunkBuster anticheat model (or any of its clones) to those who still believe it will protect their server from cheaters.&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Problem definition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Multiplayer games, akin to sports, have been plagued by cheaters almost since their inception. While in the latter case the community struggles against artificial, biochemical augmentation of the players' bodies (doping), in digital competition we are running up against digital "skill boosters".&lt;br /&gt;&lt;br /&gt;Narrowing the problem down to FPS (first-person shooter) games, we can define the two basic cheat classes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;those that augment the player's motor skills and reaction time and aid them in aiming at enemies and pulling the trigger - these are called &lt;span style="font-weight: bold;"&gt;aimbots&lt;/span&gt;,&lt;/li&gt;&lt;li&gt;and those that augment the player's perception by revealing the location of enemies and/or important gameplay objects, using various means (turning walls transparent, giving text or audio hints to the player etc.) - we'll put them under the umbrella term of a &lt;span style="font-weight: bold;"&gt;wallhack&lt;/span&gt;.&lt;/li&gt;&lt;/ul&gt;There is also the &lt;span style="font-weight: bold;"&gt;speedhack&lt;/span&gt; class of cheats, which we are not considering here. They exploit the fact that most popular engines' player locomotion code utilizes client clock for movement physics; older game engines, when fed with client input data at a double rate, would also move the player around the game world at double the speed. The reason we are not dealing with them is that they can be easily denied existence by well thought-out game logic alone, which is the case with practically all modern technologies.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Traditional countermeasures and their weaknesses&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The traditional approach to combating cheats is to use methods similar to antivirus software: scanning the client systems for violations of game code and content integrity and/or traces of cheater programs, and sending the scan results over the network to the server. However, this method is &lt;span style="font-weight: bold;"&gt;fundamentally flawed&lt;/span&gt; - since the entire system relies on the honesty of the client component, which is &lt;span style="font-weight: bold;"&gt;beyond any reliable control of the server&lt;/span&gt;, a smart hacker may create a cheating program which will be able to either modify the anticheat system's transmission to send false negatives, or spoof the system entirely, claiming to be the actual anticheat client. Alternatively, having full, physical control of the client machine, the hacker can opt to sandbox the anticheat scanner (i.e. run it in a contained environment, intercepting and manipulating all of its system calls that perform the actual scanning etc.), effectively concealing the presence of the cheater program from it. Surely, it requires the hackers to present a remarkable level of skill; there are numerous examples, however, where this has been done, and it will be done even more frequently in the future, as e-sports tournaments and leagues gain prestige and real-world profits from gaming become involved.&lt;br /&gt;&lt;br /&gt;This should leave you with no doubt that these vulnerabilities render the traditional model completely useless. Dependence on client-provided data is unsafe and should be discarded in favour of an untrusted approach: it should actually be assumed, from the very start, that the client is in fact attempting to cheat.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Proposed solution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When an experienced player spectates another person's performance in game, they are well capable of reliable recognition whether that person is playing honestly or they're using a cheat; especially when they're equipped with appropriate means, e.g. a wallhack of their own, which enables them to see what a cheating player would see, and connect the cheater's actions with gameplay events. I assume, then, that it is also possible to teach a computer to do just that.  I'm trying to develop a scanner which will not be prone to these flaws, as it follows an entirely different principle, based on heuristics. My solution will run server-side only, which will eliminate the risk of spoofing and the clients feeding us false negatives. Machine learning will be utilized to teach the decision algorithms to tell cheaters from honest players via the means of behaviour analysis alone.&lt;br /&gt;&lt;br /&gt;The project is in active development, even though it still has a long way to go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-5213534108698014214?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/5213534108698014214/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=5213534108698014214' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5213534108698014214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5213534108698014214'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2011/04/thoughts-on-cheating-in-online-gaming.html' title='Thoughts on cheating in online gaming'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-6273219119779199115</id><published>2011-03-05T23:25:00.010+01:00</published><updated>2011-03-07T07:56:51.755+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='hack'/><category scheme='http://www.blogger.com/atom/ns#' term='assembly'/><title type='text'>HOWTO: Running MASM 6.11/6.14 in Linux</title><content type='html'>OK, let me start with an explanation why I'm trying to run this 19 years old antique. Yeah, you guessed it right - we're using it at the university to learn x86 assembly. Sheesh.&lt;br /&gt;&lt;br /&gt;Anyway, I'm positive I don't want any Windoze crap on my laptop, so the only option for me to use this piece of software is to emulate its native environment. Starting with Dosbox, I easily got MASM 6.11 to work. But then I applied the 6.14 patch (which is supposed to enable support for CPUs newer than 486) and suddenly the build process started failing. I checked the build logs and guess what - &lt;span style="font-weight: bold;"&gt;the actual assembler in 6.14 is in fact a Windows app!&lt;/span&gt; What the hell?!&lt;br /&gt;&lt;br /&gt;So I thought, OK, let's try Wine. And guess what? While the assembler works fine, the WorkBench (the IDE) complains about the environment being too small. Damn it!&lt;br /&gt;&lt;br /&gt;But hey, I'm a hacker! I came up with the following idea: write a fake Dosbox command that launches the assembler in Wine and redirect all output back to Dosbox. And I put the idea into code. Here's a little HOWTO.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Disclaimer&lt;/span&gt;&lt;br /&gt;This is a quick and ugly hack. It's about as makeshift as it can possibly be, so please don't go flaming me about its quality and the awkwardness of steps you need to take to get it working.&lt;br /&gt;&lt;br /&gt;You have been warned.&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Prerequisites&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Dosbox source code - grab it from the SVN or download a tarball from &lt;a href="http://www.sf.net/projects/dosbox/"&gt;their Sourceforge page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Installed &lt;a href="http://www.winehq.com/"&gt;Wine&lt;/a&gt; - any version that can run the 6.14 ML.EXE, preferably from your distro's packages.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;MASM 6.11 with the 6.14 patch, either an already set-up install or the installation files.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.inequation.org/masmhack.diff"&gt;My patch&lt;/a&gt; for the ML.EXE cross-emulation hack. ;)&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;How it works&lt;/span&gt;&lt;br /&gt;The hack simply registers a Dosbox virtual command, ML.EXE. When run, the command opens a pipe and executes the real assembler in Wine, grabbing its stdout output and sending it back to Dosbox. When NMAKE tries to build your project, it simply runs the virtual command instead of the compiler. As simple as that!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Setup&lt;/span&gt;&lt;br /&gt;OK, so we're going to use a separate build of Dosbox just for the sake of MASM development. Here are the steps:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Get your Dosbox source tree up and compiling. Don't apply the patch yet, just make a vanilla build.&lt;/li&gt;&lt;li&gt;Use the build created in the first step to install MASM 6.11 and the 6.14 patch.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Rename BIN/ML.EXE and BIN/ML.ERR to something else&lt;/span&gt;, e.g. MLREAL.EXE. &lt;span style="font-weight: bold;"&gt;This step is absolutely crucial,&lt;/span&gt; otherwise the locally-found ML.EXE will be run instead of the virtual command.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Make sure Wine can reach the MASM installation (e.g. by creating a symlink for the MASM611 directory from Dosbox's C: directory to Wine's).&lt;/li&gt;&lt;li&gt;Before applying the patch, open it in your favourite text editor and change the lines at the top with the &lt;span style="font-family: courier new;"&gt;MLReal&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;BINR&lt;/span&gt; constants, according to your own setup.&lt;/li&gt;&lt;li&gt;Apply the patch (e.g. &lt;span style="font-family:courier new;"&gt;$ patch -p0 &amp;lt; masmhack.diff&lt;/span&gt;).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Rebuild Dosbox.&lt;/li&gt;&lt;/ol&gt;Voilla! If you now run WorkBench in our custom Dosbox build, it should be able to compile our code with the 6.14 assembler, no problem. It might just be a little slow, though, because Wine takes a while to start up, but since I won't be using it much (just the first 2 classes of the x86 assembly course), I'm okay with it.&lt;br /&gt;&lt;br /&gt;There, hope this helps someone. If anything's not clear enough, feel free to shoot a question in the comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-6273219119779199115?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/6273219119779199115/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=6273219119779199115' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6273219119779199115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6273219119779199115'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2011/03/howto-running-masm-611614-in-linux.html' title='HOWTO: Running MASM 6.11/6.14 in Linux'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-3870538753399578150</id><published>2011-02-22T11:20:00.005+01:00</published><updated>2011-02-22T11:36:56.004+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='thefarm51'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='console'/><title type='text'>Yakuake/Tilda-style console in Windows (bonus: unix shell with MSYS!)</title><content type='html'>So I decided a yakuake-style shell would be useful at work. After googling around a bit I got it to work:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-YF-6YZGqWBQ/TWORfdwfPDI/AAAAAAAABG8/P1Tcw4AM2Qk/s1600/console2.jpg"&gt;&lt;img style="text-align:center;margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 240px;" src="http://2.bp.blogspot.com/-YF-6YZGqWBQ/TWORfdwfPDI/AAAAAAAABG8/P1Tcw4AM2Qk/s400/console2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5576460733364780082" /&gt;&lt;/a&gt;&lt;br /&gt;I came up with the following software combination:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.autohotkey.com/"&gt;AutoHotkey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.sourceforge.net/projects/console"&gt;Console2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;(optional - if you want the GNU bash shell) &lt;a href="http://www.mingw.org/"&gt;MSYS&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Cutting straight through the bullcrap, here's the AutoHotkey script I used (binding the console to the F12 key):&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;&lt;pre&gt;f12::&lt;br /&gt;DetectHiddenWindows, on&lt;br /&gt;IfWinExist ahk_class Console_2_Main&lt;br /&gt;{&lt;br /&gt;   IfWinActive ahk_class Console_2_Main&lt;br /&gt;   {&lt;br /&gt;       WinHide ahk_class Console_2_Main&lt;br /&gt;       WinActivate ahk_class Shell_TrayWnd&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;       WinShow ahk_class Console_2_Main&lt;br /&gt;       WinActivate ahk_class Console_2_Main&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;   Run c:\console2\console.exe&lt;br /&gt;DetectHiddenWindows, off&lt;br /&gt;return&lt;/pre&gt;&lt;/blockquote&gt;And the Console2 configuration file (&lt;span style="font-family:courier new;"&gt;console.xml&lt;/span&gt;), which gives me my favourite settings - transparent background, Ctrl+Shift+T for a new tab and the GNU bash shell:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;settings&amp;gt;&lt;br /&gt; &amp;lt;console change_refresh="10" refresh="100" rows="25" columns="80" buffer_rows="500" buffer_columns="0" shell="C:\msys\1.0\bin\sh.exe --login -i" init_dir="C:\msys\1.0\bin" start_hidden="0" save_size="1"&amp;gt;&lt;br /&gt;  &amp;lt;colors&amp;gt;&lt;br /&gt;   &amp;lt;color id="0" r="0" g="0" b="0"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="1" r="0" g="0" b="128"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="2" r="0" g="150" b="0"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="3" r="0" g="150" b="150"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="4" r="170" g="25" b="25"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="5" r="128" g="0" b="128"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="6" r="128" g="128" b="0"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="7" r="192" g="192" b="192"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="8" r="128" g="128" b="128"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="9" r="0" g="100" b="255"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="10" r="0" g="255" b="0"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="11" r="0" g="255" b="255"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="12" r="255" g="50" b="50"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="13" r="255" g="0" b="255"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="14" r="255" g="255" b="0"/&amp;gt;&lt;br /&gt;   &amp;lt;color id="15" r="255" g="255" b="255"/&amp;gt;&lt;br /&gt;  &amp;lt;/colors&amp;gt;&lt;br /&gt; &amp;lt;/console&amp;gt;&lt;br /&gt; &amp;lt;appearance&amp;gt;&lt;br /&gt;  &amp;lt;font name="Courier New" size="10" bold="0" italic="0" smoothing="0"&amp;gt;&lt;br /&gt;   &amp;lt;color use="0" r="0" g="0" b="0"/&amp;gt;&lt;br /&gt;  &amp;lt;/font&amp;gt;&lt;br /&gt;  &amp;lt;window title="Console" icon="" use_tab_icon="1" use_console_title="0" show_cmd="1" show_cmd_tabs="1" use_tab_title="1" trim_tab_titles="20" trim_tab_titles_right="0"/&amp;gt;&lt;br /&gt;  &amp;lt;controls show_menu="0" show_toolbar="0" show_statusbar="0" show_tabs="1" hide_single_tab="1" show_scrollbars="1" flat_scrollbars="0" tabs_on_bottom="1"/&amp;gt;&lt;br /&gt;  &amp;lt;styles caption="0" resizable="0" taskbar_button="0" border="0" inside_border="2" tray_icon="0"&amp;gt;&lt;br /&gt;   &amp;lt;selection_color r="255" g="255" b="255"/&amp;gt;&lt;br /&gt;  &amp;lt;/styles&amp;gt;&lt;br /&gt;  &amp;lt;position x="0" y="0" dock="0" snap="0" z_order="1" save_position="1"/&amp;gt;&lt;br /&gt;  &amp;lt;transparency type="1" active_alpha="230" inactive_alpha="230" r="0" g="0" b="0"/&amp;gt;&lt;br /&gt; &amp;lt;/appearance&amp;gt;&lt;br /&gt; &amp;lt;behavior&amp;gt;&lt;br /&gt;  &amp;lt;copy_paste copy_on_select="0" clear_on_copy="1" no_wrap="1" trim_spaces="1" copy_newline_char="0"/&amp;gt;&lt;br /&gt;  &amp;lt;scroll page_scroll_rows="0"/&amp;gt;&lt;br /&gt;  &amp;lt;tab_highlight flashes="3" stay_highligted="1"/&amp;gt;&lt;br /&gt; &amp;lt;/behavior&amp;gt;&lt;br /&gt; &amp;lt;hotkeys use_scroll_lock="1"&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="83" command="settings"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="112" command="help"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="1" extended="0" code="115" command="exit"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="1" alt="0" extended="0" code="84" command="newtab1"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="113" command="newtab2"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="114" command="newtab3"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="115" command="newtab4"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="116" command="newtab5"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="117" command="newtab6"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="118" command="newtab7"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="119" command="newtab8"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="120" command="newtab9"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="121" command="newtab10"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="49" command="switchtab1"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="50" command="switchtab2"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="51" command="switchtab3"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="52" command="switchtab4"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="53" command="switchtab5"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="54" command="switchtab6"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="55" command="switchtab7"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="56" command="switchtab8"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="57" command="switchtab9"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="48" command="switchtab10"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="9" command="nexttab"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="1" alt="0" extended="0" code="9" command="prevtab"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="87" command="closetab"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="0" code="82" command="renametab"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="1" code="45" command="copy"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="0" alt="0" extended="1" code="46" command="clear_selection"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="1" alt="0" extended="1" code="45" command="paste"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="stopscroll"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollrowup"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollrowdown"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollpageup"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollpagedown"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollcolleft"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollcolright"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollpageleft"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="scrollpageright"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="1" shift="1" alt="0" extended="0" code="112" command="dumpbuffer"/&amp;gt;&lt;br /&gt;  &amp;lt;hotkey ctrl="0" shift="0" alt="0" extended="0" code="0" command="activate"/&amp;gt;&lt;br /&gt; &amp;lt;/hotkeys&amp;gt;&lt;br /&gt; &amp;lt;mouse&amp;gt;&lt;br /&gt;  &amp;lt;actions&amp;gt;&lt;br /&gt;   &amp;lt;action ctrl="0" shift="0" alt="0" button="1" name="copy"/&amp;gt;&lt;br /&gt;   &amp;lt;action ctrl="0" shift="1" alt="0" button="1" name="select"/&amp;gt;&lt;br /&gt;   &amp;lt;action ctrl="0" shift="0" alt="0" button="3" name="paste"/&amp;gt;&lt;br /&gt;   &amp;lt;action ctrl="1" shift="0" alt="0" button="1" name="drag"/&amp;gt;&lt;br /&gt;   &amp;lt;action ctrl="0" shift="0" alt="0" button="2" name="menu"/&amp;gt;&lt;br /&gt;  &amp;lt;/actions&amp;gt;&lt;br /&gt; &amp;lt;/mouse&amp;gt;&lt;br /&gt; &amp;lt;tabs&amp;gt;&lt;br /&gt;  &amp;lt;tab title="Console2" use_default_icon="0"&amp;gt;&lt;br /&gt;   &amp;lt;console shell="" init_dir="" run_as_user="0" user=""/&amp;gt;&lt;br /&gt;   &amp;lt;cursor style="0" r="255" g="255" b="255"/&amp;gt;&lt;br /&gt;   &amp;lt;background type="0" r="0" g="0" b="0"&amp;gt;&lt;br /&gt;    &amp;lt;image file="" relative="0" extend="0" position="0"&amp;gt;&lt;br /&gt;     &amp;lt;tint opacity="0" r="0" g="0" b="0"/&amp;gt;&lt;br /&gt;    &amp;lt;/image&amp;gt;&lt;br /&gt;   &amp;lt;/background&amp;gt;&lt;br /&gt;  &amp;lt;/tab&amp;gt;&lt;br /&gt; &amp;lt;/tabs&amp;gt;&lt;br /&gt;&amp;lt;/settings&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;So, this. Now I have an easily accessible unix shell at work, yay!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-3870538753399578150?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/3870538753399578150/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=3870538753399578150' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3870538753399578150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3870538753399578150'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2011/02/yakuaketilda-style-console-in-windows.html' title='Yakuake/Tilda-style console in Windows (bonus: unix shell with MSYS!)'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-YF-6YZGqWBQ/TWORfdwfPDI/AAAAAAAABG8/P1Tcw4AM2Qk/s72-c/console2.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-3976436593542505969</id><published>2011-02-13T12:16:00.003+01:00</published><updated>2011-02-13T12:36:47.136+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='x41'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tablet'/><title type='text'>X41 tablet screen rotation script</title><content type='html'>I bought an IBM Thinkpad X41 tablet/laptop recently. I won't go into details, just dump a little script I wrote to handle screen rotation:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;xmessage -buttons 0:0,90:3,180:2,270:1 -center Clockwise rotation&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;dir=$?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;xrandr -o $dir&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;case "$dir" in&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;"0") w="NONE";;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;"1") w="CCW";;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;"2") w="HALF";;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;"3") w="CW";;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;esac&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;xsetwacom --set cursor Rotate $w&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Simply dump this code in a shell script, chmod +x it and there you go - you'll get a nice little window asking you which direction you want it to rotate.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-3976436593542505969?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/3976436593542505969/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=3976436593542505969' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3976436593542505969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3976436593542505969'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2011/02/x41-tablet-screen-rotation-script.html' title='X41 tablet screen rotation script'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-3941546952184603341</id><published>2010-06-12T00:25:00.004+02:00</published><updated>2010-06-12T00:37:32.730+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>The AC-130 is in the air</title><content type='html'>So I turned my C programming project in earlier today. It got full marks, and the teacher was so impressed he didn't even take the test results into account when giving me my final mark in the subject. Even though it's not finished yet (i.e. it basically doesn't have gameplay yet), I'm quite proud of it!&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_cr1ai2QjIl0/TBK4nqKPINI/AAAAAAAAA58/VlEGd9YuMvw/s1600/ac130.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 300px;" src="http://2.bp.blogspot.com/_cr1ai2QjIl0/TBK4nqKPINI/AAAAAAAAA58/VlEGd9YuMvw/s400/ac130.png" alt="" id="BLOGGER_PHOTO_ID_5481646687934947538" border="0" /&gt;&lt;/a&gt;Despite the performance problems on ATI/AMD GPUs, the renderer works very well on most hardware, and both it and the content generator (oh yeah, did I mention that almost everything in the above screenshot is generated procedurally?) use some interesting techniques. There's some technical babble on them (large terrain rendering, pseudo-instancing in OpenGL, Perlin noise etc.) in &lt;a href="https://sourceforge.net/projects/ac130/files/Preview%20release/ac130-doc-11062010-r79.zip/download"&gt;the  report&lt;/a&gt; I've written for the project, if you're interested.&lt;br /&gt;&lt;br /&gt;Well, the main reason for this post, however, is the fact that the game's begun a life of its own. This game has potential, despite its simplicity, so I created &lt;a href="http://www.sf.net/projects/ac130"&gt;a SourceForge.org project for it&lt;/a&gt; to continue its development. You can see the source code and download the preview release (the one I turned in at the university) there. When I find the time, I'll finish the game, giving it proper collision detection, ground troops with AI and a sound rendering engine.&lt;br /&gt;&lt;br /&gt;Oh, the link:&lt;br /&gt;&lt;a href="http://ac130.inequation.org/"&gt;http://ac130.inequation.org/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-3941546952184603341?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/3941546952184603341/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=3941546952184603341' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3941546952184603341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3941546952184603341'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2010/06/ac-130-is-in-air.html' title='The AC-130 is in the air'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_cr1ai2QjIl0/TBK4nqKPINI/AAAAAAAAA58/VlEGd9YuMvw/s72-c/ac130.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-539547071813772176</id><published>2010-05-29T00:04:00.006+02:00</published><updated>2010-05-29T00:13:07.032+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OIIO GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><title type='text'>Multi-window OpenGL rendering</title><content type='html'>Here's a little lesson from two weeks ago: if you try to share the resources (display list spaces) between two GL contexts (e.g. when doing multi-window rendering), remember to check the return values. You might spend a week trying to debug the problem while in reality you've just been sending NULL as one of the arguments. Sheesh.&lt;br /&gt;&lt;br /&gt;On a side note, my university assignment project is nearing completion. You can have a look at the source code here:&lt;br /&gt;&lt;a href="http://www.assembla.com/code/iqpk/subversion/nodes/ac130"&gt;http://www.assembla.com/code/iqpk/subversion/nodes/ac130&lt;/a&gt;&lt;br /&gt;And it looks a little something like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cr1ai2QjIl0/TAA_Gf19OvI/AAAAAAAAA50/emml3EFWoEc/s1600/ac130.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_cr1ai2QjIl0/TAA_Gf19OvI/AAAAAAAAA50/emml3EFWoEc/s400/ac130.png" alt="" id="BLOGGER_PHOTO_ID_5476446527742098162" border="0" /&gt;&lt;/a&gt;Yes, Modern Warfare connotations are relevant.&lt;br /&gt;&lt;br /&gt;I'll move it to a SourceForge repository once it's finished. Hopefully it will start a life of its own and I can focus on the OIIO development I'm supposed to do as part of Google Summer of Code. Oh, right, I haven't announced it yet! So yeah, I got in this year. I'm supposed to finish my DDS plugin and then write another one for the DPX and Cineon formats (they're in use by the film industry).&lt;br /&gt;&lt;br /&gt;So, yeah. There.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-539547071813772176?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/539547071813772176/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=539547071813772176' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/539547071813772176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/539547071813772176'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2010/05/multi-window-opengl-rendering.html' title='Multi-window OpenGL rendering'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_cr1ai2QjIl0/TAA_Gf19OvI/AAAAAAAAA50/emml3EFWoEc/s72-c/ac130.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-613642042871384669</id><published>2010-03-21T00:43:00.006+01:00</published><updated>2010-03-21T00:55:32.450+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='noise'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='perlin'/><title type='text'>Perlin cloud noise</title><content type='html'>Note to self: here's how to do obtain Perlin cloud noise:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Generate  a Perlin noise map.&lt;/li&gt;&lt;li&gt;Double the frequency, generate another map,  half its values, add to the first one.&lt;/li&gt;&lt;li&gt;Repeat step 2 two more  times.&lt;/li&gt;&lt;/ol&gt;It took me half a day today to figure out how to do it  properly. Sheesh.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cr1ai2QjIl0/S6Vfagz6rRI/AAAAAAAAA2I/UK96EE5uFbo/s1600-h/wkurw2.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://4.bp.blogspot.com/_cr1ai2QjIl0/S6Vfagz6rRI/AAAAAAAAA2I/UK96EE5uFbo/s400/wkurw2.png" alt="" id="BLOGGER_PHOTO_ID_5450867833090780434" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-613642042871384669?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/613642042871384669/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=613642042871384669' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/613642042871384669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/613642042871384669'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2010/03/perlin-cloud-noise.html' title='Perlin cloud noise'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cr1ai2QjIl0/S6Vfagz6rRI/AAAAAAAAA2I/UK96EE5uFbo/s72-c/wkurw2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-2473909055969824748</id><published>2010-03-17T21:52:00.003+01:00</published><updated>2010-03-17T22:32:13.946+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='thefarm51'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>C# lessons</title><content type='html'>Phew! I've been working at The Farm 51 for over a week now, and it's been an ultimately interesting experience. I probably shouldn't be writing about this here as I've signed an NDA, but since what I'm working on isn't exactly connected to any of the confidential stuff that TF51 is doing, I will share some of my thoughts.&lt;br /&gt;&lt;br /&gt;Have you noticed that game developers are attempting to make their products more and more film-like? My job has a direct connection to this fact - the so-called &lt;span style="font-style: italic;"&gt;cutscenes&lt;/span&gt; (or &lt;span style="font-style: italic;"&gt;cinematic sequences&lt;/span&gt;) have become a key device of any modern game, used to advance the storyline. In the past, they usually had to be programmed in a scripting language, which required the cinematic artist to either learn the basics of coding (with miserable results, usually - have a look at the Medal of Honor Allied Assault single player map scripts to see some &lt;span style="font-style: italic;"&gt;unbelievably poor&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;programming practices), or work closely with a programmer, or combine the roles of the artist and the programmer. Obviously, this didn't work too well, so a few years ago together with the advent of WYSIWYG game editors (I think Crytek's Sandbox for Far Cry was the first of the kind), cutscene creation has been brought to a different level. Nowadays, most engine toolsets contain a piece of software designed specifically for this purpose. And due to the similarities of the process to filmmaking, they closely resemble the software tools of the latter trade (especially non-linear video editors).&lt;br /&gt;&lt;br /&gt;The technology that TF51 is about to use, however, does not contain such a tool. So, here I am, trying to fill the gap! This kind of a task called for RAD (Rapid Application Development) visual design, so even though I'm strongly prejudiced against Microsoft and their technologies, I decided to pick up C# and use it for the development of the tool. I've written about 1,5k lines of code so far in this language, so I think I have something of value to say.&lt;br /&gt;&lt;br /&gt;First off, the language, its syntax and design is... Well, it's definitely organized and intuitive, but there are some constructs and conventions that I can't help but question. For instance, the idea of everything being a reference to an object and the way of creating new instances. Sure, it is intuitive, it is quite easy to write something usable in it very quickly, without a lot of thought put into the design. This has some unpleasant implications, though. My impression is that it's plagued by the same issue as Delphi - its ease of use encourages disorganisation and mess. If something just works if you put minimal effort into it without thinking it through, then why try to engineer and improve it? I've already bade the MVC design model farewell in my application because of this - the idea to integrate UI elements that represent the abstract objects with the objects themselves is just too tempting. This provides for extremely messy code that I'll probably have to spend a lot of time refactoring in the future.&lt;br /&gt;&lt;br /&gt;While the language itself is bearable, its realization is utter horror to me. It's a performance nightmare! Not only is the executable actually half-interpreted, but also many of the linguistic luxuries come at an enormous performance cost. For instance, the whole "everything is a reference" model implies that a garbage collection mechanism must be put in place. Some of the language constructs, such as the &lt;span style="font-style: italic;"&gt;foreach&lt;/span&gt; loop, require some additional memory allocations. When drawing custom controls, you often won't be arsed to cache the resources you use to draw, because it's easier to create new instances of them (this also contributes to the messiness of the code that I mentioned earlier). As a faithful follower of C, it is unthinkable for me trade the performance for a bit of the programmer's comfort by emulating (at a huge cost) concepts which are totally artificial to the hardware layer. And just as I've always been curious about how stuff is really done internally, C# leaves me with the feeling that the less I know about the "magic" behind it, the better I'll sleep.&lt;br /&gt;&lt;br /&gt;The last paragraph, however, will not be bitching! Actually, as much as I hate the M$ guys and their ideas, I'm quite impressed by WinForms. It has very clean and streamlined design, it's documented well enough (it had pretty much everything I needed so far to create all the custom UI controls for the track editor) and the APIs are very easy to use. There are many similarities to Borland's VCL, which as far as I can remember (mind you that I haven't done &lt;span style="font-style: italic;"&gt;anything&lt;/span&gt;  in visual RAD ever since I dropped Delphi some six years ago to pursue  more serious development tools) was a soddy mess, but as I said, it's much better designed, starting even from control names.&lt;br /&gt;&lt;br /&gt;Whoa, I didn't expect this post to be this long. Anyway, if anyone reads all of this, thank you for your attention and I hope my insight is worth anything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-2473909055969824748?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/2473909055969824748/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=2473909055969824748' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2473909055969824748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2473909055969824748'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2010/03/c-lessons.html' title='C# lessons'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-3553830959421867310</id><published>2010-03-01T18:47:00.003+01:00</published><updated>2010-03-01T19:58:10.554+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='inequation.org'/><category scheme='http://www.blogger.com/atom/ns#' term='thefarm51'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Address migration</title><content type='html'>Whoa! Almost a year of inactivity! Well, it appears it ends now.&lt;br /&gt;&lt;br /&gt;I bought myself &lt;a href="http://www.inequation.org/"&gt;a domain&lt;/a&gt;, yay! It'll be a place for me to put various interesting stuff I make and/or find, like university projects etc. Plus this blog has moved to &lt;a href="http://blog.inequation.org/"&gt;blog.inequation.org&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In other news, I'll be applying for GSoC again this year (more successfully than last year, I hope), and I've just been accepted as a programming intern at &lt;a href="http://www.thefarm51.com/"&gt;The Farm 51&lt;/a&gt;! :) So, I'm on my way to becoming a &lt;span style="font-weight: bold;"&gt;real&lt;/span&gt; game developer, woohoo!&lt;span style="display: block;" id="formatbar_Buttons"&gt;&lt;span class=" on down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;img src="img/blank.gif" alt="Link" class="gl_link" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;IneQuation out.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-3553830959421867310?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/3553830959421867310/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=3553830959421867310' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3553830959421867310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3553830959421867310'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2010/03/address-migration.html' title='Address migration'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-2520218346206725021</id><published>2009-04-20T22:01:00.001+02:00</published><updated>2009-04-20T22:01:55.655+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OIIO GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Summer of Code</title><content type='html'>It's over! I haven't made it. Good luck to everyone that did! :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-2520218346206725021?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/2520218346206725021/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=2520218346206725021' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2520218346206725021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2520218346206725021'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/summer-of-code.html' title='Summer of Code'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-3894000681381434549</id><published>2009-04-14T10:27:00.000+02:00</published><updated>2009-04-14T10:29:06.784+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Lighter2: Spot- and directional lights support in CS trunk!</title><content type='html'>My patch (ticket &lt;a href="http://crystalspace3d.org/trac/CS/ticket/644"&gt;#644&lt;/a&gt;) has been merged into the Crystal Space trunk as of &lt;a href="http://crystalspace3d.org/trac/CS/changeset/32304"&gt;revision 32304&lt;/a&gt;. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-3894000681381434549?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/3894000681381434549/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=3894000681381434549' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3894000681381434549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/3894000681381434549'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/lighter2-spot-and-directional-lights.html' title='Lighter2: Spot- and directional lights support in CS trunk!'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-5213588670473530297</id><published>2009-04-12T23:47:00.000+02:00</published><updated>2009-04-13T00:06:41.101+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OIIO GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>OIIO: Revision of views on DDS support and a patch for ICO</title><content type='html'>ICO support patch chucked out. It's still a bit work-in progress, but core functionality is there.&lt;br /&gt;&lt;br /&gt;Working on it enabled me to take a good look at OIIO's architecture and API, and now I can see that it actually won't pose any problem at all to achieve the features I considered tricky at the time of writing of my original proposal. Since both volumetric and multi-image formats are supported, there won't be any problem with 3D textures, cubemaps and mipmaps (well, maybe apart from the resulting layout of the subimages, but I'm sure it can be overcome, perhaps with a convention and by putting additional info in the attributes), or describing the compression type (attributes again), or retrieving native DDS data (same way raw JPEG coefficients are retrieved). There are no further obstacles I can see, and I'm estimating I could actually code stable DDS support in within two weeks at most.&lt;br /&gt;&lt;br /&gt;Should I manage to finish Targa and DDS support before GSoC time is up, well, there's plenty of other formats that need supporting. :)&lt;br /&gt;&lt;br /&gt;Once I get down with ICO support, I'll start coding the Targa loader.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-5213588670473530297?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/5213588670473530297/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=5213588670473530297' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5213588670473530297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5213588670473530297'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/oiio-revision-of-views-on-dds-support.html' title='OIIO: Revision of views on DDS support and a patch for ICO'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-7350267227766643890</id><published>2009-04-12T23:41:00.000+02:00</published><updated>2009-04-12T23:47:03.209+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Lighter2: Directional light issues</title><content type='html'>Not without a spectacular slip-up, but I managed to &lt;a href="http://trac.crystalspace3d.org/trac/CS/ticket/644"&gt;chuck out a patch&lt;/a&gt; that adds spot- and directional light support to &lt;span style="font-style: italic;"&gt;lighter2&lt;/span&gt;, yay! While the spotlight works all right, the problem with the directional one is that shadows aren't cast if not all of its direction vector's components are non-zero (sic!). To me it looks like a possible bug in the raytracer, but I'd better spend some more time on this. Going to have to figure out how to use the occlusion ray debugging feature.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-7350267227766643890?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/7350267227766643890/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=7350267227766643890' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7350267227766643890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7350267227766643890'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/lighter2-directional-light-issues.html' title='Lighter2: Directional light issues'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-7974733903883248864</id><published>2009-04-11T12:30:00.001+02:00</published><updated>2009-04-12T23:41:09.901+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OIIO GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Regarding my education and eligibility for GSoC</title><content type='html'>Just to clarify some things. I'm a Polish citizen, age 19, and since April 24th I'm formally no longer a high school student, but practically I still am until I'm done with the &lt;a href="http://en.wikipedia.org/wiki/Matura"&gt;Matura exams&lt;/a&gt; in May. After this summer I hope to go to university.&lt;br /&gt;&lt;br /&gt;GSoC's requires a student to be at least 18 years of age and be formally enrolled in their school on April 20th, 2009 [1]. GSoC's program manager Leslie Hawthorn has also updated (after several questions regarding the issue [2]) the FAQ to state that it's OK for high school students to apply, as long as they conform to other requirements.&lt;br /&gt;&lt;br /&gt;Hope this helps. :)&lt;br /&gt;&lt;br /&gt;References:&lt;br /&gt;[1] &lt;a href="http://en.wikipedia.org/wiki/Matura"&gt;&lt;span style="text-decoration: underline;"&gt;Google Summer of Code Frequently Asked Questions: Eligibility&lt;/span&gt;&lt;br /&gt;&lt;/a&gt;[2] &lt;a href="http://groups.google.com/group/google-summer-of-code-discuss/browse_thread/thread/64b57d65733f196a/bc72f6741b10e45f?lnk=gst&amp;amp;q=high+school#bc72f6741b10e45f"&gt;Google Summer of Code Discuss newsgroup: Are highschool students eligible for applying as students?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-7974733903883248864?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/7974733903883248864/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=7974733903883248864' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7974733903883248864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7974733903883248864'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/regarding-my-education-and-eligibility.html' title='Regarding my education and eligibility for GSoC'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-644351844870698115</id><published>2009-04-08T18:32:00.001+02:00</published><updated>2009-04-08T23:02:22.301+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Lighter2: Further observations and a patch</title><content type='html'>OK, my timing observations from the previous post were influenced by the fact I wasn't compiling directional lightmaps for bump-mapping. After turning this feature on, it becomes perfectly clear that sampling indeed is the most time-consuming part of the lighting process.&lt;br /&gt;&lt;br /&gt;In other news I've been working in my non-occupied-by-school-work time on a patch to add spot- and directional lights support to &lt;span style="font-style: italic;"&gt;lighter2&lt;/span&gt;. As far as I can tell, spotlights are done, now I'm awaiting clarification of some doubts from the &lt;span style="font-style: italic;"&gt;crystal-devel&lt;/span&gt; mailing list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-644351844870698115?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/644351844870698115/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=644351844870698115' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/644351844870698115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/644351844870698115'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/further-observations-and-patch.html' title='Lighter2: Further observations and a patch'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-2971768742340833355</id><published>2009-04-08T18:28:00.000+02:00</published><updated>2009-04-08T23:02:12.218+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Lighter2: Timing observations</title><content type='html'>&lt;span style="font-size:100%;"&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;This is an update originally posted on April 5th.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Having run &lt;span style="font-style: italic;"&gt;lighter2&lt;/span&gt; a couple of times I noticed that it isn't the sampling that takes longest to complete, at least in the &lt;span style="font-style: italic;"&gt;castle-staticlit&lt;/span&gt; demo map - kd-tree generation needs a while, too, and the stage described as "Initialize objects: Lightmaps" is also pretty time-consuming (actually, it stops at 30% task 19% total and stays that way for a minute or two, then proceeds). These results, however, might be influenced by a possible bug that I sent an email to &lt;span style="font-style: italic;"&gt;cs-main&lt;/span&gt; about. Let's see what this resolves to.&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-2971768742340833355?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/2971768742340833355/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=2971768742340833355' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2971768742340833355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/2971768742340833355'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/lighter2-timing-observations.html' title='Lighter2: Timing observations'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-6258908015287804377</id><published>2009-04-03T18:53:00.000+02:00</published><updated>2009-04-08T18:21:55.195+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Google Summer of Code</title><content type='html'>Phew! It's over, we're past the application deadline for this year's &lt;a href="http://socghop.appspot.com/"&gt;Summer of Code&lt;/a&gt;. I've done the best I could, now it's all in the hands of the mentors. I've left myself a kind of an open back door, though - linked to the two preceding blog posts in my proposals.&lt;br /&gt;&lt;br /&gt;I've applied to work with &lt;a href="http://www.openimageio.org/"&gt;OpenImageIO&lt;/a&gt; (TGA and DDS support) and &lt;a href="http://www.crystalspace3d.org/"&gt;Crystal Space&lt;/a&gt; (&lt;span style="font-style: italic;"&gt;lighter2&lt;/span&gt; multithreading), hope one of them gets picked up. I'm a bit worried, though, because OIIO's lead developer doesn't seem to take me seriously (I'm a high school student; he seemed doubtful of my eligibility for the program, but I made sure I am with Leslie Hawthorn, GSoC's manager), but perhaps a patch for ICO support can convince him I'm capable of getting the job done.&lt;br /&gt;&lt;br /&gt;I was also thinking of trying &lt;a href="http://www.bzflag.org/"&gt;BZFlag&lt;/a&gt;, &lt;a href="http://www.thousandparsec.net/"&gt;Thousand Parsec&lt;/a&gt; and &lt;a href="http://www.wesnoth.org/"&gt;Battle for Wesnoth&lt;/a&gt;, but I played the former two and honestly, I disliked them. And in the latter there was a regular dev team member applying to work on the only idea that was within my grasp, so with the obvious advantage that he has (he knows the game's internal workings well), I don't think I stood any chance of being accepted.&lt;br /&gt;&lt;br /&gt;That's it for now. Let's see how it turns out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-6258908015287804377?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/6258908015287804377/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=6258908015287804377' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6258908015287804377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6258908015287804377'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/google-summer-of-code.html' title='Google Summer of Code'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-6339692405051794493</id><published>2009-04-03T18:44:00.001+02:00</published><updated>2009-04-08T23:01:26.242+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OIIO GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Targa and DDS support: Appendix</title><content type='html'>Hello!&lt;br /&gt;&lt;br /&gt;The most up-to-date text of the proposal is available &lt;a href="http://docs.google.com/View?docID=dgsz9sqj_6gdzfz82t"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I decided to somewhat reorganize my GSoC updates - now they're separate posts, appropriately tagged. You can now find them &lt;a href="http://iqdev.blogspot.com/search/label/OIIO%20GSoC"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-6339692405051794493?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/6339692405051794493/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=6339692405051794493' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6339692405051794493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6339692405051794493'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/targa-and-dds-support-appendix.html' title='Targa and DDS support: Appendix'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-1150609961247054968</id><published>2009-04-03T18:42:00.000+02:00</published><updated>2009-04-08T18:30:47.702+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Space GSoC'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Summer of Code'/><title type='text'>Lighter2 multi-threading: Appendix</title><content type='html'>Hello!&lt;br /&gt;&lt;br /&gt;The most up-to-date text of the proposal is available &lt;a href="http://docs.google.com/View?docID=dgsz9sqj_5c8fxbj7d"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I decided to somewhat reorganize my GSoC updates - now they're separate posts, appropriately tagged. You can now find them &lt;a href="http://iqdev.blogspot.com/search/label/Crystal%20Space%20GSoC"&gt;here&lt;/a&gt;.&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-1150609961247054968?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/1150609961247054968/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=1150609961247054968' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/1150609961247054968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/1150609961247054968'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2009/04/lighter2-multi-threading-appendix.html' title='Lighter2 multi-threading: Appendix'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-4091217511005305946</id><published>2008-12-27T14:58:00.000+01:00</published><updated>2009-04-08T18:20:23.933+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Fast sine and cosine approximation</title><content type='html'>Time for a pseudo-scientific publication. Yay!&lt;br /&gt;&lt;br /&gt;Some background: I needed to calculate the sine of an angle in a little MoHAA (Medal of Honor Allied Assault) gameplay modification for one of the features to work properly. However, the game's scripting engine doesn't provide trigonometric functions. My initial solution was to use a Taylor expansion implementation ported to the MoHAA scripting language by my good friend and former mentor Jeroen Vrijkorte. It provided pretty accurate values, however, I thought that its computational complexity is a tad excessive. So I went on to create my own approximation.&lt;br /&gt;&lt;br /&gt;I noticed that the sine in half its period strongly resembles a parabola of a quadratic function. After a little experimentation I found 2 quadratic equations whose plots almost overlap that of a sine:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cr1ai2QjIl0/SVY4vkDxD8I/AAAAAAAAAj0/X2TfsikB8ag/s1600-h/sine.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 400px;" src="http://4.bp.blogspot.com/_cr1ai2QjIl0/SVY4vkDxD8I/AAAAAAAAAj0/X2TfsikB8ag/s400/sine.png" alt="" id="BLOGGER_PHOTO_ID_5284473602550337474" border="0" /&gt;&lt;/a&gt;So, how does it work?&lt;br /&gt;&lt;br /&gt;Basically, we have 2 quadratic functions:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://forummatematyka.pl/latexrender/pictures/ca7424c0b6b2fd03a28d88b98496165d.gif"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 283px; height: 72px;" src="http://forummatematyka.pl/latexrender/pictures/ca7424c0b6b2fd03a28d88b98496165d.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;First we normalize the &lt;span style="font-style: italic;"&gt;x&lt;/span&gt; to fall within the &lt;0;2π&gt; range, then apply the corresponding equation. As simple as that. Here's an implementation in MoHAA's scripting language (similar to C):&lt;br /&gt;&lt;pre&gt;sin local.x:&lt;br /&gt; // normalize the input&lt;br /&gt; //local.x = local.x / 3.14159265358979323846&lt;br /&gt; local.x = local.x * 0.31830988618379067154    // 1/pi, because multiplication is faster than division&lt;br /&gt; // calculate the value relative to period&lt;br /&gt; if (local.x &amp;lt; 0.0)&lt;br /&gt;     local.x += float(int(-local.x * 0.5))) * 2.0&lt;br /&gt; if (local.x &amp;gt; 2.0)&lt;br /&gt;     local.x -= float(int(local.x * 0.5)) + 1) * 2.0&lt;br /&gt;goto _sin_normalized    // moved to a separate label so that this part of the code can be shared between sin and cos&lt;br /&gt;&lt;br /&gt;cos local.x:&lt;br /&gt; // normalize the input&lt;br /&gt; local.x = 0.5 + local.x * 0.31830988618379067154    // move the plot by a half to the left to achieve cosine&lt;br /&gt; // calculate the value relative to period&lt;br /&gt; if (local.x &amp;lt; 0.0)&lt;br /&gt;     local.x += float(int(-local.x * 0.5))) * 2.0&lt;br /&gt; if (local.x &amp;gt; 2.0)&lt;br /&gt;     local.x -= float(int(local.x * 0.5)) + 1) * 2.0&lt;br /&gt;goto _sin_normalized    // moved to a separate label so that this part of the code can be shared between sin and cos&lt;br /&gt;&lt;br /&gt;_sin_normalized:&lt;br /&gt; // fast special case&lt;br /&gt; if (local.x == 0.0 || local.x == 1.0 || local.x == 2.0)&lt;br /&gt;     end 0.0&lt;br /&gt; // pick range and calculate result&lt;br /&gt; if (local.x &lt; 1) {&lt;br /&gt;     local.x -= 0.5&lt;br /&gt;     end ( -4.0 * local.x * local.x + 1.0)&lt;br /&gt; }&lt;br /&gt; local.x -= 1.5&lt;br /&gt;end (4.0 * local.x * local.x - 1.0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The normalization process performs 3 multiplications and 1 or 2 additions/subtractions, while the actual quadratic function solving performs 2 multiplications and 2 additions/subtractions. Branching is also very limited, which makes for a nice, efficient algorithm, which is less computationally expensive than a Taylor expansion and doesn't have the memory cost of a lookup table.&lt;br /&gt;&lt;br /&gt;As you can see in the plot above, the error (the blue curve; calculated as &lt;span style="font-style: italic;"&gt;a(x) - sin(x)&lt;/span&gt;, where a(x) is the approximation) ranges from 0 to about 0.06, which is unnoticeable in most non-scientific application (e.g. computer games). However, if further precision is required, the error can be reduced by several orders of magnitude down to an omittable value, as it can quite easily approximated with a 3rd degree polynomial. With the error subtracted from the quadratic equations we're getting polynomials which are still relatively cheap to solve and produce very accurate results.&lt;br /&gt;&lt;br /&gt;Hope anyone finds this useful.&lt;br /&gt;&lt;br /&gt;P.S. If you use this method, please drop me a line. It's just nice to know. ;)&lt;br /&gt;P.P.S. I really wouldn't mind if this was known as the "Godlewski sine approximation" in my own honour. :P&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-4091217511005305946?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/4091217511005305946/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=4091217511005305946' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/4091217511005305946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/4091217511005305946'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/12/fast-sine-and-cosine-approximation.html' title='Fast sine and cosine approximation'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cr1ai2QjIl0/SVY4vkDxD8I/AAAAAAAAAj0/X2TfsikB8ag/s72-c/sine.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-7673864377776206482</id><published>2008-11-05T16:47:00.000+01:00</published><updated>2009-04-08T18:23:09.600+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>GPU terrain in OpenGL tutorial - part 2</title><content type='html'>This post contains the actual tutorial. For an introduction and some theoretical background on it, see &lt;a href="http://iqdev.blogspot.com/2008/11/gpu-terrain-rendering-in-opengl-part-1.html"&gt;the previous post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First off, if you just want to see an exemplary working implementation of the method, check out the &lt;a href="http://mobilearena.svn.sourceforge.net/viewvc/mobilearena/code/client/video/"&gt;Destination engine source code&lt;/a&gt;. The code related to terrain resides in the class &lt;span style="font-family:courier new;"&gt;terrain&lt;/span&gt;, which is declared in &lt;span style="font-family:courier new;"&gt;v_local.h&lt;/span&gt;, and its implementation spans across files &lt;span style="font-family:courier new;"&gt;v_assets_world.cpp&lt;/span&gt; (procedural creation of terrain patch data and uploading it to buffer objects) and &lt;span style="font-family:courier new;"&gt;v_scene_world.cpp&lt;/span&gt; (actual drawing).&lt;br /&gt;&lt;br /&gt;For the purposes of this tutorial I will assume that the reader has intermediate knowledge of C/C++ and OpenGL. Also, the entire terrain mesh should be square to avoid singularities, which of course can be resolved, but they're out of this tutorial's scope.&lt;br /&gt;&lt;br /&gt;Let's start with creating some data for our to-be terrain renderer to show. &lt;a href="http://www.bundysoft.com/L3DT/"&gt;L3DT&lt;/a&gt; is a very nice piece of software for designing large terrains. It runs in Windows and also in Linux via Wine (flawlessly, I should say). Go along with the built-in tutorial and create your terrain maps.&lt;br /&gt;&lt;br /&gt;When all your maps are calculated, export the heightfield as a 16-bit unsigned raw image: select RAW from the drop-down list, then open the Options dialog and change the top-most option to "16-bit unsigned (metres)".&lt;br /&gt;&lt;br /&gt;Export your texture map, too. Save it to a single image, the bigger in size the better. Choose the format to your preference, i.e. the easiest for you to load into your application.&lt;br /&gt;&lt;br /&gt;Let's get rolling with the code! First, let's declare a couple of constants:&lt;p&gt;&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;#define TERRAIN_PATCH_SIZE 17&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#define TERRAIN_NUM_VERTS (TERRAIN_PATCH_SIZE * TERRAIN_PATCH_SIZE + TERRAIN_PATCH_SIZE * 4 - 4) // plus skirt verts at each edge minus duplicated corners&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#define TERRAIN_NUM_BODY_INDICES ((2 * TERRAIN_PATCH_SIZE + 2) * (TERRAIN_PATCH_SIZE - 1))&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;#define TERRAIN_NUM_SKIRT_INDICES (2 * (TERRAIN_PATCH_SIZE * 2 + (TERRAIN_PATCH_SIZE - 2) * 2) + 2)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#define TERRAIN_NUM_INDICES (TERRAIN_NUM_BODY_INDICES + TERRAIN_NUM_SKIRT_INDICES)&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;These will be used to avoid redundant typing and calculations.&lt;br /&gt;&lt;br /&gt;Then, load your heightmap and textures into texture units 0 and 1:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;glActiveTexture(GL_TEXTURE_0);&lt;br /&gt;glBindTexture(heightmapID);&lt;br /&gt;glTexImage2D(GL_TEXTURE_2D,&lt;br /&gt;   0,&lt;br /&gt;   GL_LUMINANCE8_ALPHA8,&lt;br /&gt;   heightMapWidth, heightMapHeight,&lt;br /&gt;   0,&lt;br /&gt;   GL_LUMINANCE_ALPHA,&lt;br /&gt;   GL_UNSIGNED_SHORT,&lt;br /&gt;   heightMapData);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);&lt;br /&gt;&lt;br /&gt;glActiveTexture(GL_TEXTURE_1);&lt;br /&gt;glBindTexture(textureID);&lt;br /&gt;glTexImage2D(GL_TEXTURE_2D,&lt;br /&gt;   0,&lt;br /&gt;   GL_RGBA8,    // set according to your texture's format&lt;br /&gt;   texWidth, texHeight,&lt;br /&gt;   0,&lt;br /&gt;   GL_RGBA,&lt;br /&gt;   GL_UNSIGNED_BYTE,    // set according to your texture's format&lt;br /&gt;   texData);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Note that for the heightmap, we're setting the texture parametres to clamp texture coordinates (this is to avoid ugly skirt seam artifacts) andthe nearest filtering mode (increases hardware compatibility). Also, the internal format parametre for the heightmap is GL_LUMINANCE8_ALPHA8. This usually means a grayscale image with an alpha (transparency) channel, but here it's a hack to enable us to use full 16-bit precision of the heightmap. Of course, there's also the GL_LUMINANCE16 format available. You might have heard, though, that it's not supported by all hardware. This is true. Thus the hack. It will be explained in detail once we get to the shaders.&lt;br /&gt;&lt;br /&gt;Next, let's generate our buffers:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;GLuint bufferIDs[2];&lt;br /&gt;&lt;br /&gt;glGenBuffers(2, bufferIDs);&lt;br /&gt;&lt;br /&gt;// vertices&lt;br /&gt;glBindBuffer(GL_ARRAY_BUFFER, bufferIDs[0]);&lt;br /&gt;float vertices[TERRAIN_NUM_VERTS * 3];&lt;br /&gt;fillVerts(vertices);&lt;br /&gt;glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);&lt;br /&gt;&lt;br /&gt;// indices&lt;br /&gt;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIDs[1]);&lt;br /&gt;unsigned short indices[TERRAIN_NUM_INDICES];&lt;br /&gt;fillIndices(indices);&lt;br /&gt;glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Then, we fill the buffers with data:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;void terrain::fillVerts(float *verts) {&lt;br /&gt;   int i, j;&lt;br /&gt;   const float invScaleX = 1.f / (TERRAIN_PATCH_SIZE - 1.f);&lt;br /&gt;   const float invScaleZ = 1.f / (TERRAIN_PATCH_SIZE - 1.f);&lt;br /&gt;   float s, t;&lt;br /&gt;&lt;br /&gt;   // fill patch body vertices&lt;br /&gt;   float *v = verts;&lt;br /&gt;   for (i = 0; i &lt; t =" i" j =" 0;" s =" j" j =" 0;" s =" j" i =" 1;" t =" i" p =" indices;" i =" 0;" j =" 0;" i =" 0;" i =" 1;" i =" TERRAIN_PATCH_SIZE"&gt;= 0; i--) {&lt;br /&gt;       *(p++) = i + TERRAIN_PATCH_SIZE * (TERRAIN_PATCH_SIZE - 1);&lt;br /&gt;       *(p++) = i * 2 + TERRAIN_PATCH_SIZE * TERRAIN_PATCH_SIZE + 1;&lt;br /&gt;   }&lt;br /&gt;   // -X edge&lt;br /&gt;   for (i = TERRAIN_PATCH_SIZE - 2; i &gt;= 1; i--) {&lt;br /&gt;       *(p++) = i * TERRAIN_PATCH_SIZE;&lt;br /&gt;       *(p++) = i * 2 + TERRAIN_PATCH_SIZE * (TERRAIN_PATCH_SIZE + 2);&lt;br /&gt;   }&lt;br /&gt;   *(p++) = 0;&lt;br /&gt;   *(p++) = TERRAIN_PATCH_SIZE * TERRAIN_PATCH_SIZE;&lt;br /&gt;   assert(p - indices == TERRAIN_NUM_INDICES);&lt;br /&gt;}&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;a name="BLOGGER_PHOTO_ID_5265224174242172338"&gt;&lt;/a&gt;Some parts of it may seem enigmatic. Basically, we're creating a so-called &lt;a href="http://en.wikipedia.org/wiki/Triangle_strip"&gt;triangle strip&lt;/a&gt; to cut on memory usage. Hopefully these 2 diagrams showing vertex indices will explain things. The first diagram also shows the &lt;i&gt;l&lt;/i&gt; and &lt;i&gt;d&lt;/i&gt; segments, which were described in the previous post and we will use later on.&lt;br /&gt;&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_cr1ai2QjIl0/SRHVfXiBDbI/AAAAAAAAAhQ/6AvNyUi2jSo/s1600-h/ter_diagrams.png"&gt;&lt;img src="http://2.bp.blogspot.com/_cr1ai2QjIl0/SRHVfXiBDbI/AAAAAAAAAhQ/6AvNyUi2jSo/s400/ter_diagrams.png" name="grafika1" vspace="5" width="280" align="bottom" border="0" height="400" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;The second diagram shows the skirt. Starting at the last patch body vertex #288 (marked with the red dot), the arrows show the skirt indexing order, in the repeating sequence red-green-blue.&lt;br /&gt;&lt;br /&gt;The terrain's dimensions in its basic form will span from (0, 0) to (1, 1), with its centre at (0.5, 0.5). This is not what we want. Let's move it so that its centre is at (0, 0) and scale it up to make 1 heightmap texel equal 1 space unit. To achieve this, we need a special model view matrix:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;float terMVMat[16];&lt;br /&gt;&lt;br /&gt;void terrainSetMatrix(void) {&lt;br /&gt;   // create an OpenGL matrix to centre the terrain at (0, 0, 0) and scale it&lt;br /&gt;   // up to its full dimensions&lt;br /&gt;   // we can put all of this stuff together in 1 matrix because we're not&lt;br /&gt;   // applying any other transformations&lt;br /&gt;   terMVMat[0] = heightMapWidth;&lt;br /&gt;   terMVMat[1] = 0.f;&lt;br /&gt;   terMVMat[2] = 0.f;&lt;br /&gt;   terMVMat[3] = 0.f;&lt;br /&gt;   terMVMat[4] = 0.f;&lt;br /&gt;   terMVMat[5] = 1.f;&lt;br /&gt;   terMVMat[6] = 0.f;&lt;br /&gt;   terMVMat[7] = 0.f;&lt;br /&gt;   terMVMat[8] = 0.f;&lt;br /&gt;   terMVMat[9] = 0.f;&lt;br /&gt;   terMVMat[10] = heightMapWidth;&lt;br /&gt;   terMVMat[11] = 0.f;&lt;br /&gt;   terMVMat[12] = -(heightMapWidth / 2);&lt;br /&gt;   terMVMat[13] = 0.f;&lt;br /&gt;   terMVMat[14] = -(heightMapWidth / 2);&lt;br /&gt;   terMVMat[15] = 1.f;&lt;br /&gt;}&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;We need to set one more parametre (the starting level for quadtree recursion) before we can proceed:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;void terrainCalcMaxLevels(void) {&lt;br /&gt;   int pow2 = (m_heightMapWidth - 1) / (TERRAIN_PATCH_SIZE - 1);&lt;br /&gt;   terrainMaxLevels = 0;&lt;br /&gt;   for (int i = 1; i &lt;&gt;&lt;/blockquote&gt; &lt;p&gt;After the buffers have been created, we can move on to setting up the shaders. Write the code for creating and compiling the vertex and fragment shaders and linking the program (a nice OpenGL shaders tutorial can be found &lt;a href="http://www.lighthouse3d.com/opengl/glsl/"&gt;here&lt;/a&gt;). Then, request uniform parametres:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;GLint biasID, scaleID;&lt;br /&gt;if ((biasID = glGetUniformLocation(programID, "bias")) == -1) {&lt;br /&gt;   // handle error here&lt;br /&gt;}&lt;br /&gt;if ((scaleID = glGetUniformLocation(programID, "scale")) == -1) {&lt;br /&gt;   // handle error here&lt;br /&gt;}&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Also, post texture unit indices (0 and 1) to variables &lt;span style="font-family:courier new;"&gt;heightMap&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;tex&lt;/span&gt;, respectively.&lt;br /&gt;&lt;br /&gt;Time for the shaders! Here's the vertex processing code:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;uniform sampler2D heightMap;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;uniform vec2 bias;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;uniform float scale;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;vec2 scaleBiasPos(vec2 pos) {&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;return vec2(pos.x * scale + bias.x, pos.y * scale + bias.y);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;void main() {&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;vec4 pos = gl_Vertex;&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;pos.xz = scaleBiasPos(pos.xz);&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;vec4 c = texture2D(tex0, pos.xz);&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;&lt;b&gt;pos.y *= c.x * 255.0 + c.a * 65280.0;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;gl_Position = gl_ModelViewProjectionMatrix * pos;&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;gl_TexCoord[0].xy = pos.xz;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Note the part in bold. This is the actual hack for using full 16-bit precision of the heightmap. By uploading it as GL_LUMINANCE8_ALPHA8, we fooled OpenGL into thinking this is a grayscale image with transparency. Thus, for each pixel, it read the first byte as the grayscale component, and the second as the alpha channel. Now, OpenGL converts all colour values to normalized floating point variables (i.e. in the range 0..1). This line of code does approximately the same work as the following (except on floating point numbers, as opposed to integers):&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;unsigned short in = 12345;    // some random number&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;unsigned short out;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;unsigned char *p = (unsigned char *)&amp;in;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;// getting the result from unsigned chars&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;out = p[0] + (p[1] &lt;&lt; face="courier new"&gt;// getting same result from normalized floats&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;// note that (x &lt;&lt; face="courier new"&gt;// 2^8 = 256&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;// (x &lt;&lt; face="courier new"&gt;float x = p[0] / 255.f;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;float a = p[1] / 255.f;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;out = x * 255.f + (a * 255.f) * 256.f;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;// 255 * 256 = 65280&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;out = x * 255.f + a * 65280.f;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Of course, there is some precision loss due to conversions, but the value of &lt;span style="font-family:courier new;"&gt;out&lt;/span&gt; after the last line should be close enough to 12345 for our purposes.&lt;br /&gt;&lt;br /&gt;And the fragment shader:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;uniform sampler2D tex;    // colour map&lt;br /&gt;&lt;br /&gt;void main() {&lt;br /&gt;   gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);&lt;br /&gt;}&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;Now, on to the actual rendering part. Start the recursion like this:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;// use our shaders&lt;br /&gt;glUseProgram(programID);&lt;br /&gt;// apply our model view matrix&lt;br /&gt;glPushMatrix();&lt;br /&gt;glLoadMatrixf(terMVMat);&lt;br /&gt;// set up geometry buffers for rendering&lt;br /&gt;glBindBuffer(GL_ARRAY_BUFFER, bufferIDs[0]);&lt;br /&gt;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIDs[1]);&lt;br /&gt;glEnableClientState(GL_VERTEX_ARRAY);&lt;br /&gt;glVertexPointer(3, GL_FLOAT, 0, 0);&lt;br /&gt;&lt;br /&gt;// recurse - call for the entire terrain mesh first&lt;br /&gt;terrainRender(0.f, 0.f, 1.f, 1.f, terMaxLevels, 1.f);&lt;br /&gt;&lt;br /&gt;// restore previous state when we're done&lt;br /&gt;glPopMatrix();&lt;br /&gt;glBindBuffer(GL_ARRAY_BUFFER, 0);&lt;br /&gt;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);&lt;br /&gt;glDisableClientState(GL_VERTEX_ARRAY);&lt;/span&gt;&lt;/blockquote&gt; &lt;p&gt;These 2 functions make up the terrain renderer's core:&lt;/p&gt; &lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;void terrainRender(float minU, float minV, float maxU, float maxV, int level, float scale) {&lt;br /&gt;   float halfU = (minU + maxU) * 0.5;&lt;br /&gt;   float halfV = (minV + maxV) * 0.5;&lt;br /&gt;&lt;br /&gt;   if (!noCull) {    // global variable to control frustum culling&lt;br /&gt;       float bounds[2][3] = {&lt;br /&gt;           {(minU - 0.5) * heightMapWidth, 0.f, (minV - 0.5) * heightMapWidth},&lt;br /&gt;           {(maxU - 0.5) * heightMapWidth, 65536.f, (maxV - 0.5) * heightMapWidth}&lt;br /&gt;       };&lt;br /&gt;       if (frustumCullBBox(bounds))    // patch out of view frustum?&lt;br /&gt;           return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   float d2 = (maxU - minU) * heightMapWidth / (TERRAIN_PATCH_SIZE_F - 1.f);&lt;br /&gt;   d2 *= d2;&lt;br /&gt;&lt;br /&gt;   float v[3] = {(halfU - 0.5) * heightMapWidth, terMVMat[5] * 0.5, (halfV - 0.5) * heightMapWidth};&lt;br /&gt;   VectorSubtract(v, camera, v);   // vector operation: v = v - camera&lt;br /&gt;&lt;br /&gt;   // use distances squared to avoid expensive square roots&lt;br /&gt;   float f2 = VectorLength(v) / d2;&lt;br /&gt;&lt;br /&gt;   if (f2 &gt; C * C || level &lt;&gt;&lt;/blockquote&gt; &lt;p&gt;For a graphical explanation of &lt;span style="font-style: italic;"&gt;l&lt;/span&gt; (actually, &lt;span style="font-family:courier new;"&gt;VectorLength(v)&lt;/span&gt;) and &lt;span style="font-style: italic;"&gt;d&lt;/span&gt;, check out the second diagram in the picture above.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;And that's it! Consolidate all of this in your project and you should get a nice, fast-rendering terrain. Enjoy!&lt;br /&gt;&lt;br /&gt;And as I said - for a working implementation, check out the &lt;a href="http://mobilearena.sourceforge.net/"&gt;Destination&lt;/a&gt; engine.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-7673864377776206482?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/7673864377776206482/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=7673864377776206482' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7673864377776206482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7673864377776206482'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/11/gpu-terrain-in-opengl-tutorial-part-2.html' title='GPU terrain in OpenGL tutorial - part 2'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_cr1ai2QjIl0/SRHVfXiBDbI/AAAAAAAAAhQ/6AvNyUi2jSo/s72-c/ter_diagrams.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-5858382407154988072</id><published>2008-11-05T15:10:00.000+01:00</published><updated>2009-04-08T18:23:22.891+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>GPU terrain in OpenGL tutorial - part 1</title><content type='html'>This post provides some theoretical background. If you want to go straight to the tutorial, check out &lt;a href="http://iqdev.blogspot.com/2008/11/gpu-terrain-in-opengl-tutorial-part-2.html"&gt;the next post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Terrain rendering is one of the most important things in an outdoor game engine like &lt;a href="http://mobilearena.sourceforge.net/"&gt;Destination&lt;/a&gt;. It makes for the better part of all the triangles drawn in a scene, so the method employed must be as efficient as possible, while maintaining reasonable flexibility. The performace criteria essentially means simplifying the terrain mesh (i.e. reducing the number of triangles it is comprised of).&lt;br /&gt;&lt;br /&gt;I have evaluated several terrain simplification algorithms and while some of them provided remarkably good results (like the &lt;a href="http://www.cognigraph.com/ROAM_homepage/"&gt;Realtime Optimally-Adapting Meshes&lt;/a&gt; or Peter Lindstrom's &lt;a href="http://www.gvu.gatech.edu/people/peter.lindstrom/papers/tvcg2002/paper.pdf"&gt;Terrain Simplification Simplified&lt;/a&gt;), they all had two disadvantages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the calculations are performed by the CPU, which is undesirable, as a lot of CPU power is required for physics simulation&lt;br /&gt;&lt;/li&gt;&lt;li&gt;the number of triangles and vertices was not constant, which made memory usage optimizations difficult.&lt;/li&gt;&lt;/ul&gt;I was just about to go with Lindstrom's algorithm when I remembered I have a copy of the book Game Programming Gems vol. 6 by Mike Dickheiser. There, in the chapter 5.5, Harald Vistnes describes an alternative method, which turns out to be perfect for my application.&lt;br /&gt;&lt;br /&gt;You see, historically, due to 3D hardware's poor performance (in relation to CPUs) it was more feasible to reduce the geometry before rendering - calculate very accurate Potentially Visible Sets (a perfect example of this approach is the id Tech 3 a.k.a. Quake 3 engine's Binary Space Partition system) and simplify complex meshes, as geometry throughput was the main determinant of performance. Nowadays, with the unified shading processor-based GPUs and high bandwidth buses (PCIe) at our disposal, it actually makes more sense to compute less accurate PVSs and to move as much rendering-related calculations load from the CPU to the GPU in order to save time for physics and AI.&lt;br /&gt;&lt;br /&gt;In accordance with this, Harald Vistnes suggests a method, which doesn't include any complex mesh simplification (well, apart from LODing). The idea is to divide the terrain in patches and do the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Generate vertex and index buffer objects and fill them with data for a single flat, square 17x17 vertices patch of ground (other sizes are also possible, but it turned out 17x17 provides most consistent performance across different sizes of the entire terrain grid), plus the so-called &lt;span style="font-weight: bold;"&gt;skirt&lt;/span&gt; - a vertical border going around the terrain patch expanding along the negative Y axis that covers up cracks between patches at differents levels of detail.&lt;/li&gt;&lt;li&gt;Upload the terrain's heightmap to video memory as a grayscale texture.&lt;/li&gt;&lt;li&gt;Use a vertex shader that uses the Vertex Texture Fetch technique to offset the terrain's vertices along the Y axis according to the heightmap.&lt;/li&gt;&lt;li&gt;To draw, recurse the terrain mesh in a quadtree-like manner. Start with a patch covering the entire terrain grid:&lt;ol&gt;&lt;li&gt;Calculate the level of detail criteria: &lt;span style="font-style: italic;"&gt;f = l/d&lt;/span&gt;, where &lt;span style="font-style: italic;"&gt;l&lt;/span&gt; is the distance of the current patch's centre from the camera and &lt;span style="font-style: italic;"&gt;d&lt;/span&gt; is the size (width) of a single quad in the patch.&lt;/li&gt;&lt;li&gt;If &lt;span style="font-style: italic;"&gt;f &gt; C&lt;/span&gt;, where &lt;span style="font-style: italic;"&gt;C&lt;/span&gt; is a constant that determines the terrain rendering quality (must be chosen via trial and error), or we've reached the bottom of the tree, draw the current patch and return.&lt;/li&gt;&lt;li&gt;Else divide the current patch in 4 subpatches and go back to point 4.1 for each of them.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;The drawing of each individual subpatch is realized by drawing the contents of the buffer objects, providing a proper model view matrix and 2 uniform shader parametres (describing horizontal displacement and scale, respectively). Nice and simple, isn't it?&lt;br /&gt;&lt;br /&gt;One main point is that Harald Vistnes's implementation is based on DirectX. In the next post I will describe how to achieve the effect in OpenGL.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-5858382407154988072?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/5858382407154988072/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=5858382407154988072' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5858382407154988072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/5858382407154988072'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/11/gpu-terrain-rendering-in-opengl-part-1.html' title='GPU terrain in OpenGL tutorial - part 1'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-8960583208151750538</id><published>2008-09-25T18:15:00.000+02:00</published><updated>2009-04-08T18:23:39.657+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Destination'/><category scheme='http://www.blogger.com/atom/ns#' term='Mobile Arena'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='SDL'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>Destination</title><content type='html'>&lt;div style="text-align: justify;"&gt;...is the name of my very own game technology. Yeah, a game engine. It's called "Destination" as a mockery of Valve Software's &lt;a href="http://en.wikipedia.org/wiki/Source_engine"&gt;Source engine&lt;/a&gt;. It's free software under the GNU GPL. Its coded in object-oriented C++ and its design is mostly inspired by the &lt;a href="http://en.wikipedia.org/wiki/Id_Tech"&gt;id Tech&lt;/a&gt; family of engines (a.k.a. Quake engines), in particular by id Tech 3 and id Tech 4 (Quake 3 and Doom 3/Quake 4, respectively). The first game based on the technology will be Mobile Arena (not much more than a playable technical demo, actually):&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:130%;"&gt;&lt;a href="http://mobilearena.sourceforge.net/"&gt;mobilearena.sourceforge.net&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;The engine is devloped simultanously with this game. After Mobile Arena is finished, I will move on to new projects utilising the engine.&lt;br /&gt;&lt;br /&gt;The project is still in its infancy, but it already compiles and produces working executables, has a console to interface with the user etc. It doesn't draw anything in 3D as of yet, but that is about to change in the next couple of days.&lt;br /&gt;&lt;br /&gt;OpenGL 2.1 is cool.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-8960583208151750538?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/8960583208151750538/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=8960583208151750538' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/8960583208151750538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/8960583208151750538'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/09/destination.html' title='Destination'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-7891726243862221058</id><published>2008-09-14T01:24:00.000+02:00</published><updated>2009-04-08T18:16:20.227+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><category scheme='http://www.blogger.com/atom/ns#' term='nasza-klasa'/><title type='text'>Nasza-klasa.pl mutual friend finder</title><content type='html'>As promised, I wrote a small proof-of-concept C app to download the contact lists of 2 people and find their mutual friends. It uses &lt;a href="http://curl.haxx.se/"&gt;CURL&lt;/a&gt; for HTTP handling. Here goes the code (it's quite lengthy, actually - much lengthier than I had expected; the parsing part could probably be shortened, but I couldn't be arsed to refine it):&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:130%;"&gt;&lt;a href="http://www.pastebin.ca/1202182"&gt;http://www.pastebin.ca/1202182&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Going to give a &lt;a href="http://www.pidgin.im/"&gt;Pidgin&lt;/a&gt; plugin for NK.pl support a shot soon.&lt;br /&gt;&lt;br /&gt;Also, I would like to mention that you'll need to provide a user agent header in any HTTP requests to the NK.pl server (any random blah will do), otherwise it'll keep returning 403 errors and irrelevant content (won't let you log in either). Plus the &lt;span style="font-style: italic;"&gt;version&lt;/span&gt; parameter in the friends list request seems insignificant - it would accept any number I gave it.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-7891726243862221058?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/7891726243862221058/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=7891726243862221058' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7891726243862221058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/7891726243862221058'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/09/nasza-klasapl-mutual-friend-finder.html' title='Nasza-klasa.pl mutual friend finder'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-6712382753157081552</id><published>2008-09-08T23:13:00.001+02:00</published><updated>2009-05-02T14:47:54.653+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reverse engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='nasza-klasa'/><title type='text'>Nasza-klasa.pl's AJAX friend list protocol</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; Szymon Gruszecki of the Cracovian University of Technology found out that nasza-klasa.pl changed the way the friends list JSON file is accessed and contributed a description.&lt;br /&gt;&lt;br /&gt;OK, time for my first finding. What's nasza-klasa.pl? To quote the &lt;a href="http://en.wikipedia.org/wiki/Nasza-klasa.pl"&gt;Wikipedia&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;nasza-klasa.pl&lt;/b&gt; (&lt;i&gt;Our Class&lt;/i&gt;) is a large &lt;a href="http://en.wikipedia.org/wiki/Social_networking_platform" title="Social networking platform" class="mw-redirect"&gt;social networking platform&lt;/a&gt; for people in &lt;a href="http://en.wikipedia.org/wiki/Poland" title="Poland"&gt;Poland&lt;/a&gt;, a social &lt;a href="http://en.wikipedia.org/wiki/Networking" title="Networking" class="mw-redirect"&gt;networking&lt;/a&gt; site bringing together former students from the same schools. People are able to keep and maintain a personal page containing information about their name, age, study subjects, interests and courses. Apart from many discussion forums it allows to share photos. It does not require an invitation from anyone of the members to register. The leader of the project is Maciej Popowicz from &lt;a href="http://en.wikipedia.org/wiki/Wroc%C5%82aw" title="Wrocław"&gt;Wrocław&lt;/a&gt;.&lt;/blockquote&gt;The portal has recently received significant functionality enhancements, based on AJAX, including an improved friends list browser and an instant messenger-like contact list and messaging centre. I'm actually quite surprised there hasn't been a surge yet in data mining software based on it, like what happened with Audioscrobbler (Last.fm) - you know, programs that calculate your musical taste's mainstreamness rating, generate tag clouds etc. Given the field in which nasza-klasa.pl operates, I find it even more surprising, as it seems that much more interesting conclusions can be drawn using the data available from it than what can be achieved with Last.fm.&lt;br /&gt;&lt;br /&gt;So, as a little research project, I'm going to try and write a piece of software to compare the contact lists of two persons and find mutual friends. Then I might possibly write a &lt;a href="http://www.pidgin.im/"&gt;Pidgin&lt;/a&gt; plugin to support the messaging centre as an instant messenger protocol.&lt;br /&gt;&lt;br /&gt;I spent a couple of minutes trying to discover how the contact list works. The server requires you to be logged in as a nasza-klasa.pl user. The lists are located at URLs using the following pattern:&lt;br /&gt;&lt;blockquote&gt;http://nasza-klasa.pl/friends_list/&lt;span style="font-style: italic;"&gt;target_ID&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;target&gt;&lt;/target&gt;&lt;/span&gt;/&lt;span style="font-style: italic;"&gt;flags&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;flags&gt;&lt;/flags&gt;&lt;/span&gt;/&lt;span style="font-style: italic;"&gt;version&lt;/span&gt;&lt;span&gt;/t=&lt;/span&gt;&lt;span style="font-style: italic;"&gt;basic_auth&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;version&gt;&lt;/version&gt;&lt;/span&gt;&lt;/blockquote&gt;Where:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;target_ID&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;target&gt;&lt;/target&gt;&lt;/span&gt; is the ID of the user whose contact list we're looking at,&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;flags&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;flags&gt;&lt;/flags&gt;&lt;/span&gt; is a bit field that tells the server which parts of the friends' profiles to return; possible values (can be ORed together):&lt;/li&gt;&lt;ul&gt;&lt;li&gt;UID: 1,&lt;/li&gt;&lt;li&gt;first name: 2,&lt;/li&gt;&lt;li&gt;last name: 4,&lt;/li&gt;&lt;li&gt;avatar (main photo thumbnail): 8,&lt;/li&gt;&lt;li&gt;friends count: 16,&lt;/li&gt;&lt;li&gt;location (city): 32,&lt;/li&gt;&lt;li&gt;messaging centre status: 64,&lt;/li&gt;&lt;li&gt;messaging centre status description: 128,&lt;/li&gt;&lt;li&gt;"artificiality" (whether or not the person in the account is a fictitious one or a real one): 512,&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;version&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;version&gt;&lt;/version&gt;&lt;/span&gt; - a string in the form &lt;span style="font-style: italic;"&gt;number1/number2&lt;/span&gt;; &lt;span style="font-weight: bold;"&gt;note:&lt;/span&gt; it appears to be different for every account, in order to get it right you must download the contacts subpage first and use the value of &lt;span style="font-style: italic;"&gt;MY_VERSION&lt;/span&gt; in the &lt;span style="font-style: italic;"&gt;FRIENDS_CONFIG&lt;/span&gt; object,&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;basic_auth&lt;/span&gt; - the value of the &lt;span style="font-style: italic;"&gt;basic_auth&lt;/span&gt; cookie that nasza-klasa.pl creates on login.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The server returns a &lt;a href="http://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt; file (MIME type application/json) of a name which seems to be a random number sequence. Or perhaps a Unix timestamp, haven't really made an effort to check. Anyway, it's irrelevant. It starts with a BOM - a sequence of 3 bytes that serves as an indication that the data that follows is UTF-8-encoded: EF BB BF (thanks Deathplanter and maverix). Then, ASCII-encoded with escaped UTF-8 characters (\uXXXX format) text data follows. Punctuation marks make it extremely easy to parse. Here is an exemplary output of the URL "http://nasza-klasa.pl/friends_list/14646653/3/5289" (retrieve UIDs and first names only; it's my friends' band's account - cheers, dudes!), with the 3 leading bytes stripped:&lt;br /&gt;&lt;blockquote&gt;({"VERSION":{"LAST_ADD":"1219583764","FRIENDS_COUNT":40},"UID":[3053592,3583955,14442691,219291,2579249,4381854,5288144,3725481,3123442,5993186,1696561,3684784,3969330,4334375,7098585,4276143,5140616,4245903,6725224,2023542,3724862,6445366,6509887,4984429,1584950,6496380,7960261,3563520,8182916,10758767,3236560,6462111,2975713,4771329,4084833,3350043,2613772,16211826,14143543,3017104],"FIRST_NAME":["Mateusz","Micha\u0142","Jumanji","Piotr","Micha\u0142","Alicja","Katarzyna","Anka","\u266bZosia","Zuzanna","Kuba","Gosia","Magda","Adam","Sebastian","Paulina","Aleksandra","Krzysztof","Dawid","Leszek","Aleksandra","Monika","Mateusz","Helena","Agnieszka","Agnieszka","\u266bRafa\u0142","Maciek","Ewa","Agata","Agata","Bartek","Madzia","Kajtek","Aleksandra","Patrycja","Anna","Ola","Oskar","Tomek"]})&lt;/blockquote&gt;With input data like this it should pose no problem to produce all kinds of interesting output.&lt;br /&gt;&lt;br /&gt;In the next post I'll examine the messaging centre.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-6712382753157081552?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/6712382753157081552/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=6712382753157081552' title='Komentarze (4)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6712382753157081552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/6712382753157081552'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/09/nasza-klasapls-ajax-friend-list.html' title='Nasza-klasa.pl&apos;s AJAX friend list protocol'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8479224368899661003.post-4601937620053802635</id><published>2008-09-08T23:07:00.000+02:00</published><updated>2009-04-08T18:14:38.547+02:00</updated><title type='text'>Hello World!</title><content type='html'>I have promised myself time and time again I'll never ever start a blog, but here I am doing this. I have an excuse, though. I'm not going to write about my personal life, but rather about various things I feel the need of sharing with the world that puzzle and intrigue me. They're mostly related to software development, but you'll also find some Linux tricks and quirks and hardware hacks here.&lt;br /&gt;&lt;br /&gt;My name's Leszek, I'm a high school student from Poland, an aspiring software (especially games!) developer and an amateur musician.&lt;br /&gt;&lt;br /&gt;Guess this does it for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8479224368899661003-4601937620053802635?l=blog.inequation.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.inequation.org/feeds/4601937620053802635/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8479224368899661003&amp;postID=4601937620053802635' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/4601937620053802635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8479224368899661003/posts/default/4601937620053802635'/><link rel='alternate' type='text/html' href='http://blog.inequation.org/2008/09/hello-world.html' title='Hello World!'/><author><name>Leszek Godlewski</name><uri>https://profiles.google.com/102829663202789069509</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-p0kzEmEaSEY/AAAAAAAAAAI/AAAAAAAABI4/AHkz_WcJrVA/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry></feed>
