Speed up compile times with forward declarations

I am a self taught programmer and developed many bad habits as I was learning. I never read any books on proper programming structure, and really regret it now. I made so many easily avoidable mistakes.

This post describes a simple concept but when I initially figured it out it was a bit of a “eureka” moment for me.

In a large project, compile times can really get bogged down, especially if you are using things like templates. A bad design will have a horrible web of classes, all files referring to each other in some way. The problem is that when you touch one header file, it has a domino effect and triggers a rebuild of the majority of source files.

Here are a few files to explain the issue:

————- A.h ————-

#pragma once
struct SimpleStruct { int dummy_value; };

————- B.h ————-

#include "A.h"
void doSomethingToASimpleStruct(SimpleStruct* TheStruct);

————- B.cpp ————-

#include "B.h"
void doSomethingToASimpleStruct(SimpleStruct* TheStruct) {
   TheStruct->dummy_value = 10;
}

————- C.h ————-

#include "B.h"
void theCallingFunction();

————- C.cpp ————-

#include "C.h"
void theCallingFunction() {
   SimpleStruct* a_simple_struct = new SimpleStruct();
   doSomethingToASimpleStruct(a_simple_struct);
   delete a_simple_struct;
}

With the above code, what happens if we modify our struct? Well, it is included in B.h, which is included by both B.cpp and C.cpp, causing a full rebuild of both cpp files. However, we are not actually using the structure of SimpleStruct in B.h. We just use a pointer to one, without needing to know what it is. If only there was some way of saying to the compiler, “we’re using a pointer to something here, but we want to declare it later, so we’re just letting you know it will exist.”

That’s where forward declarations come in.
With a slight modification:

————- B.h ————-

struct SimpleStruct; // FORWARD DECLARATION
void doSomethingToASimpleStruct(SimpleStruct* TheStruct);

————- B.cpp ————-

#include "B.h"
#include "A.h"
void doSomethingToASimpleStruct(SimpleStruct* TheStruct) {
   TheStruct->dummy_value = 10;
}

We have moved the include of A.h from B.h to B.cpp. B.h just knows OF the SimpleStruct, so that it can legally use a pointer to it, but it doesn’t need to know what’s in it. It will all get linked up later no problem. Now, when SimpleStruct changes, B.cpp does get recompiled, but C.cpp doesn’t. This is just a very simple contrived example, but if you can apply this to larger projects, you might see a drastic increase in compile/rebuild times.

I know this kind of thing is obvious to people who learned programming a proper way, but like I say, it was a revelation for me. Hopefully, this post might make a few beginner programmers nod and say, “ah, I get it now,” in the same way as I did when I finally understood the point of forward declarations.

Calculating sine and cosine together with vectors

Very often you want to find the sine AND cosine of an angle. It is used all the time in computer graphics for all kinds of rotations.
The most obvious way to do it is something like this:

float sin_angle = sin(angle);
float cos_angle = cos(angle);

But trigonometric functions are not that fast, and here we are using two of them. However, if you are working with a system capable of vector processing, such as GLSL shaders, then you can find both sine and cosine in a single function call:

#define HALFPI 1.57079633
vec2 sin_cos_angle = sin(vec2(angle,HALPI-angle));

It’s quite obvious in hindsight but I have to admit I just never thought of that. I recently saw it on some code on ShaderToy but I can’t remember where. I’d like to give credit so I will continue to search for the person whose code I saw.

Getting started with GLFW

It’s quite a lot of work to get a window with an OpenGL context on screen in modern operating systems. And if you want to make anything cross-platform, then you need to learn different ways of GUI programming. It’s obviously much better to use a library to get all that stuff out of the way so you can concentrate on just the graphics part of your application.

The OpenGL library I like most at the moment is GLFW. It is very easy to use and not bloated like some other systems. I just want to get an OpenGL window and access to input like keyboard and mouse. I can handle the rest, thank you very much.

So, here is a small “Hello World” example for using GLFW to get something on screen. I won’t tell you how to link libraries or add include directories, you’ll have to find that out for yourself as it highly depends on your OS and IDE. But here is the code for a simple rotating triangle.

Note: This example uses the old deprecated fixed function immediate mode. If you really want to learn OpenGL, you should learn the newer version. I do it this way because it is a LOT less code and is much easier to understand.

#define GLFW_INCLUDE_GL_3
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <iostream>

static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if( key==GLFW_KEY_ESCAPE && action==GLFW_PRESS ) // press escape to quit
    {
        glfwSetWindowShouldClose(window,GL_TRUE);
    }
}

int main()
{
    // Initialise GLFW
    if( !glfwInit() ) exit(EXIT_FAILURE);
    
    // Open GLFW window
    GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World!", NULL, NULL);
    if( !window )
    {
        std::cout << "Cannot open GLFW window\n";
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    
    // Set callbacks here. Just keyboard for now
    glfwSetKeyCallback(window, keyCallback);
    glfwMakeContextCurrent(window);
    
    // initialise GLEW
    glewExperimental = GL_TRUE;
    if( glewInit()!=GLEW_OK )
    {
        std::cout << "Cannot initialise GLEW\n";
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    
    glMatrixMode(GL_MODELVIEW);
    while( !glfwWindowShouldClose(window) )
    {
        // Clear the screen. We are not using zbuffer so just clear colours
        glClear(GL_COLOR_BUFFER_BIT);
        
        // Set up the modelview matrix
        glLoadIdentity();
        glRotatef(glfwGetTime()*100.f, 0, 1, 0);
        
        // Draw a triangle
        glBegin(GL_TRIANGLES);
        glColor3f(1,0,0); glVertex2f( 0.0f, 0.5f);
        glColor3f(0,1,0); glVertex2f(-0.5f,-0.5f);
        glColor3f(0,0,1); glVertex2f( 0.5f,-0.5f);
        glEnd();
        
        // Perform double buffering and poll events
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    
    glfwTerminate();
    
    return EXIT_SUCCESS;
}

An old, obfuscated raytracer

During my undergraduate days, I had a module in programming. We were taught the basics of C++, but I had already been programming in it for many years so I got very little out of it.

We had to do a small project so I chose to do a raytracer. I am of the very strong opinion that writing a raytracing is an amazing way to learn not just graphics, but maths and general programming too.

The beauty of writing a raytracer is that very quickly you get to see something. The joy of seeing a perfect white circle on a black background (which means your ray set up and sphere intersection functions are working properly) for the first time is indescribable. Not only that, but it’s great for visual debugging. You can easily see if your normals are inverted or your camera matrix is skewed. Very convenient.

I will be talking about raytracing more on this blog, perhaps writing a tutorial and hopefully developing a simple raytracer from scratch.

But for now, I will just dump my old (nearly 20 year old) obfuscated raytracer here. It includes fake soft shadows which deserves a post all on its own.

#include <stdio.h>
#include <math.h>
typedef struct{float x,y,z;}v;const int S=1024,NS=5,NL=2;float T,Tm,A,sh;float s[][9]={{-12.73,-12.73,50,1,0,0,8,50,.3},{-12.73,12.73,50,0,1,0,8,50,.3},{12.73,12.73,50,0,0,1,8,50,.3},{12.73,-12.73,50,1,1,0,8,50,.3},{0,0,50,1,1,1,10,50,.6}};float l[][7]={{-150,400,-20,.7,.7,.7,50},{350,100,-400,.4,.3,.35,50}};float dot(v a,v b){return a.x*b.x+a.y*b.y+a.z*b.z;}float norm(v *V){T=sqrt(dot(*V,*V));V->x/=T,V->y/=T,V->z/=T;return T;}int sect(int j,v o,v d,float &t){o.x-=s[j][0],o.y-=s[j][1],o.z-=s[j][2];t=-dot(o,d);t-=sqrt(s[j][6]*s[j][6]-dot(o,o)+t*t);if(t>.01)return 1;return 0;}v trace(v o,v d,int D){int h,i,j,k;v c,n,L,J;c.x=.3,c.y=.5,c.z=.8;for(Tm=999,h=0,i=NS;i--; )if(sect(i,o,d,T))if(T<Tm)Tm=T,h=i+1;if(h--){o.x+=d.x*Tm,o.y+=d.y*Tm,o.z+=d.z*Tm;n.x=o.x-s[h][0],n.y=o.y-s[h][1],n.z=o.z-s[h][2],norm(&n);T=dot(n,d)*2,d.x-=n.x*T,d.y-=n.y*T,d.z-=n.z*T;if(D--)c=trace(o,d,D);c.x*=s[h][8],c.y*=s[h][8],c.z*=s[h][8];c.x+=.1,c.y+=.1,c.z+=.1;for(j=NL;j--; ){L.x=l[j][0]-o.x,L.y=l[j][1]-o.y,L.z=l[j][2]-o.z,A=norm(&L);for(sh=1,i=NS;i--; ){J.x=s[i][0]-o.x,J.y=s[i][1]-o.y,J.z=s[i][2]-o.z;T=dot(L,J);if(T>0&&T<A){J.x-=L.x*T,J.y-=L.y*T,J.z-=L.z*T,T=l[j][6]*T/A;T=(sqrt(dot(J,J))-s[i][6]+T)/(T+T);T=(T<0)?0:(T>1)?1:T;sh*=T;}}T=dot(L,n),T=(T>0)?T*(1-s[h][8])*sh:0;c.x+=l[j][3]*s[h][3]*T,c.y+=l[j][4]*s[h][4]*T,c.z+=l[j][5]*s[h][5]*T;T=dot(L,d),T=(T>0)?pow(T,s[h][7]):0;T*=sh;c.x+=l[j][3]*T,c.y+=l[j][4]*T,c.z+=l[j][5]*T;}}return c;}int main(){char P[3];v o,d,c;short h[]={0,2,0,0,0,0,S,S,24};FILE *f=fopen("1.tga","wb");fwrite(h,18,1,f);for(int i=S*S;i--; ){o.x=o.y=o.z=0,d.x=S/2-i%S,d.y=S/2-i/S,d.z=S,norm(&d);c=trace(o,d,5);P[0]=(c.z>1)?255:(char)(c.z*255),P[1]=(c.y>1)?255:(char)(c.y*255),P[2]=(c.x>1)?255:(char)(c.x*255);fwrite(&P,3,1,f);if(!(i%S))printf("%d ",i/S);}fclose(f);return 0;}

This code, when compiled and run, will spit out a TGA file that looks like this:

Output render of obfuscated raytracer
Output render of obfuscated raytracer

If you are learning programming, I highly recommend pulling the above code apart. Start off by making an empty C++ project and copying the code into main.cpp. Get it building and running and spitting out an image (in the same directory as the project). Then start putting in sane whitespace, separating out structures and functions, and also changing the names of variables into something more meaningful once you’ve worked out what they are for.

The code is not great, I was still learning a lot myself when I wrote it, but you might learn a trick or two. Let me know how you get on, or any problems with compilation.

Have fun!

Introduction

Welcome to my blog. My name is Dale and this is a place where I can write down my thoughts about various topics such as programming, speculative fiction and Japanese culture, among other things.

I am a professional game developer with a background in Artificial Life, so this blog will probably have a lot of technical stuff covering areas such as AI, graphics and physics.

Anyway, I hope you get something out of the inane drivel that I call “my blog.”