Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - luiscubal

#1
I wrote a simple OpenGL/GLSL program to generate a TV-like noise effect. (using SFML to setup the opengl context and for audio purposes).
The idea is simple: we use a fragment shader to compute the color of each pixel randomly. We can also force "groups" of pixels to have a single color to give an illusion of a lower resolution.

The requirements: basic OpenGL and GLSL knowledge. Some code is taken from Durian Software's OpenGL tutorial.

Our source code is made of four files: The Makefile, the C++ code, the fragment shader and the vertex shader.
I named them "Makefile", "noise.cpp", "noise.f.glsl" and "noise.v.glsl".

Let's start with the Makefile(tested under Linux GCC)

CXX:=g++
CXXFLAGS:=-Wall -Wextra -Werror -g -O0
LDFLAGS:=-lsfml-window -lsfml-audio -lsfml-system -lGLEW -lGL
OUTFILES:=noise.cpp.o
APP:=noise

all: $(OUTFILES)
$(CXX) $(CXXFLAGS) $(OUTFILES) $(LDFLAGS) -o $(APP)

clean:
rm -f $(OUTFILES) $(APP)

%.cpp.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@


If you're familiar with Makefiles, this should be trivial to understand.

Our program basically just uses a shape with four vertexes - the window rectangle. Since we use no matrixes nor any tricks, the vertex shader is super-simple:

#version 110

attribute vec2 position;

void main() {
gl_Position = vec4(position, 0.0, 1.0);
}

Essentially: The position of the vertex is the position the program sends, with Z=0.0 and W=1.0.

Our fragment shader is, unfortunately, more complex. Although GLSL does include some noise(random) functions, it seems some(most? all?) implementations - including the proprietary driver for my NVidia GPU - are very xkcd-ish.

(Seriously - they actually do this)

So, unfortunately, if we want reliable random, we'll have to get our own. stackoverflow to the rescue!

Our second problem is this:
QuoteWe can also force "groups" of pixels to have a single color to give an illusion of a lower resolution.
So how do we do this?
For this, I used floor. Essentially, I have a variable "pixels_per_block"(which would perhaps be more adequately called "sqrt_pixels_per_block", since the actual number of pixels is the square of that variable).
OpenGL provides a variable named gl_FragCoord which gives us the position of the pixel in the screen. By dividing gl_FragCoord.x or y by pixels_per_block and then using floor, we obtain the desired effect.
If pixels per block is 3, we get this
0 -> 0
1 -> 0
2 -> 0
3 -> 1
4 -> 1
5 -> 1
6 -> 2
which is exactly what we wanted.

Keep in mind that GLSL(at least the version I used) does not allow implicit type casts, and we can't divide a float by an integer, so we'll have to explicitly cast pixels_per_block to float.

Finally, we want the noise pattern to change on each frame. To achieve this effect I used something I called "seed", computed on the C++ side.
So, let's see what we've got:

#version 110

uniform int seed1, seed2;
uniform int pixels_per_block;

float rand(vec2 co) {
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main() {
float xpos = floor(gl_FragCoord.x/float(pixels_per_block));
float ypos = floor(gl_FragCoord.y/float(pixels_per_block));
vec2 src = vec2(xpos, ypos) * vec2(seed1, seed2);
float val = rand(src);
gl_FragColor = vec4(val, val, val, 1.0);
}


To generate audio noise, we also use random. SFML provides us with a class named SoundStream that enables us to compute sounds as they are needed - in this case, using the rand() function

If you haven't read the Durian Software OpenGL tutorial, this would be a good time to do so, since I'm going to skip lots of OpenGL explanations and assume you understand it all.
I provide the code here, for those who are interested:

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <GL/glew.h>
#include <SFML/Window.hpp>
#include <SFML/Audio.hpp>

#define PIXELS_PER_BLOCK 2
#define NOISE_SOUND_NBSAMPLES 16000

using std::cerr;
using std::endl;
using std::vector;

class NoiseAudioStream : public sf::SoundStream
{
public:
vector<sf::Int16> myBuffer;

NoiseAudioStream() {
myBuffer.reserve(NOISE_SOUND_NBSAMPLES*2);
Initialize(2, 32000);
}

virtual bool OnStart() {
for (int i = 0; i < NOISE_SOUND_NBSAMPLES*2; ++i) {
myBuffer[i] = rand();
}
return true;
}

virtual bool OnGetData(sf::SoundStream::Chunk& data) {
data.NbSamples = NOISE_SOUND_NBSAMPLES;
data.Samples = &myBuffer[0];
return true;
}
};

GLuint vertex_buffer, element_buffer;
GLuint vertex_shader, fragment_shader, program;
GLuint shader_position;
GLuint shader_seed1, shader_seed2, shader_pixels_per_block;

static const GLfloat vertex_buffer_data[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLushort element_buffer_data[] = { 0, 1, 2, 3 };

static GLuint make_buffer(GLenum target, const void* buffer_data, GLsizei buffer_size) {
GLuint buffer;

glGenBuffers(1, &buffer);
glBindBuffer(target, buffer);
glBufferData(target, buffer_size, buffer_data, GL_STATIC_DRAW);

return buffer;
}

static GLchar* file_contents(const char* filename, GLint* length) {
FILE* f = fopen(filename, "r");
if (!f) {
cerr << "Could not open " << filename << endl;
return 0;
}
fseek(f, 0, SEEK_END);
*length = ftell(f);
fseek(f, 0, SEEK_SET);

void* buffer = malloc(*length + 1);
*length = fread(buffer, 1, *length, f);
fclose(f);
((char*) buffer)[*length] = '\0';

return (GLchar*) buffer;
}

static void show_info_log(GLuint object, PFNGLGETSHADERIVPROC glGet__iv, PFNGLGETSHADERINFOLOGPROC glGet__InfoLog) {
    GLint log_length;
    char *log;

    glGet__iv(object, GL_INFO_LOG_LENGTH, &log_length);
    log = (char*) malloc(log_length);
    glGet__InfoLog(object, log_length, NULL, log);
    cerr << log << endl;
    free(log);
}

static GLuint make_shader(GLenum type, const char* filename) {
GLint length;
GLchar* source = file_contents(filename, &length);
GLuint shader;
GLint shader_ok;

if (!source)
return 0;

shader = glCreateShader(type);
glShaderSource(shader, 1, (const GLchar**)&source, &length);
free(source);
glCompileShader(shader);

glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
if (!shader_ok) {
cerr << "Failed to compile " << filename << ":" << endl;
show_info_log(shader, glGetShaderiv, glGetShaderInfoLog);
glDeleteShader(shader);
return 0;
}

return shader;
}

static GLuint make_program(GLuint vertex_shader, GLuint fragment_shader) {
GLint program_ok;

GLuint program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);

glGetProgramiv(program, GL_LINK_STATUS, &program_ok);
if (!program_ok) {
cerr << "Failed to link shader program:" << endl;
show_info_log(program, glGetProgramiv, glGetProgramInfoLog);
glDeleteProgram(program);
return 0;
}
return program;
}

static void make_resources() {
vertex_buffer = make_buffer(GL_ARRAY_BUFFER, vertex_buffer_data, sizeof(vertex_buffer_data));
   element_buffer = make_buffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_data, sizeof(element_buffer_data));

vertex_shader = make_shader(GL_VERTEX_SHADER, "noise.v.glsl");
fragment_shader = make_shader(GL_FRAGMENT_SHADER, "noise.f.glsl");
program = make_program(vertex_shader, fragment_shader);

shader_seed1 = glGetUniformLocation(program, "seed1");
shader_seed2 = glGetUniformLocation(program, "seed2");
shader_pixels_per_block = glGetUniformLocation(program, "pixels_per_block");
shader_position = glGetAttribLocation(program, "position");
}

static void init() {
glewInit();

glClearDepth(1.f);
glClearColor(0.f, 0.f, 0.f, 0.f);

make_resources();
}

int main() {
srand(time(NULL));

sf::Window App(sf::VideoMode(800, 600, 32), "SFML OpenGL");
App.SetFramerateLimit(60);

NoiseAudioStream noise;

init();
noise.Play();

while (App.IsOpened()) {
sf::Event Event;
while (App.GetEvent(Event)) {
if (Event.Type == sf::Event::Resized)
glViewport(0, 0, Event.Size.Width, Event.Size.Height);
else if (Event.Type == sf::Event::Closed)
App.Close();
else if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
App.Close();
}

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(program);
glUniform1i(shader_seed1, rand());
glUniform1i(shader_seed2, rand());
glUniform1i(shader_pixels_per_block, PIXELS_PER_BLOCK);

glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glVertexAttribPointer(shader_position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, (void*)0);
glEnableVertexAttribArray(shader_position);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void*)0);

glDisableVertexAttribArray(shader_position);

App.Display();
}

return 0;
}


If there's something you do not understand, feel free to ask.
#2
Tips, Tutorials, Examples / Undefined behavior
November 08, 2011, 07:15:37 PM
Some guys from LLVM/Clang have published a bunch of posts explaining what undefined behavior in C is and what are its implications.

http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html

This gets tricky because they add info about optimizers, so undefined behavior really doesn't always behave like one would expect.
For instance, one of the examples they give is this:

void contains_null_check(int *P) {
  int dead = *P;
  if (P == 0)
    return;
  *P = 4;
}{/code]
In the above example, one could expect that "dead" would be removed by the optimizer, so the code would be safe.
However, depending on how the optimizer is made, this may not be the case. This is a perfectly valid optimizer result:
[code]void contains_null_check_after_RNCE(int *P) {
  int dead = *P;
  if (false)  // P was dereferenced by this point, so it can't be null
    return;
  *P = 4;
}


void contains_null_check_after_RNCE_and_DCE(int *P) {
//removed dead variable since it's never used.
//if(false) removed
*P = 4;
}


The whole thing is very interesting and, in my opinion, a must-read for all C/C++ programmers.
#3
Proposals / EDL V.Next
November 03, 2011, 10:21:02 PM
In the announcement post Josh made, I wrote about making EDL independent of C++, with only a few select features and some interoperability functions.
I think I ought to be more specific. So I decided to post some thoughts here for discussion.

So here goes:

some script:

//Basic EDL
var x = 1; //Single-line comment
/*block comment*/
x = 2; //reassign

//Enter static typing
int y = 3;
y = x;

x = "str";
y = x; //enigma::cast_exception

const z = 2; //type of z is inferred.
z = 3; //error

if z > 1 then begin //GML compatibility
    show_message("foo");
end


So, essentially, GML with a few new features.
But that's not all.

some function file. Different from plain scripts since it can contain multiple functions.

int foo = 2;
string bar = "xyz";
vector<float> baz;

type xyz = struct __cpp_pod__ /*plain old data*/ {
   enigma::cpp_interop::type_int x;
   enigma::cpp_interop::type_int y;
   enigma::cpp_interop::type_float z;
};

foreign(cpp) void some_namespace::some_cpp_function(xyz foo, xyz* bar, xyz& baz);


C++ file:

#include <iostream>
#include "enigma.h"

using std::cout;
using std::endl;

struct xyz{
    int x;
    int y;
    float z;
};

namespace some_namespace {
    ENIGMA_EXPORT void some_cpp_function(xyz foo, xyz* bar, xyz& baz) {
          cout << "hello, world\n" << endl;
    }
}


So, basic types: void, int, uint, char, byte, sbyte, short, ushort, long, ulong, float, double, string, vector<T>, vector<T>::iterator/const_iterator, var(if it can be considered a type at all), T*, T&, map<K, V>, map<K, V>::iterator, lambda types(?), maybe others, such as types from C++ people might find useful. Maybe vector types too? I'm pretty sure LLVM handles vector types really well.
Extra types would be defined in enigma::cpp_interop, to ensure maximum compatibility.
Additionally, one could define other types (enigma::object_id?)


type mycallback = void(var sender, string message);
...
mycallback self.on_talk; //statically typed fields? Maybe some other syntax?
...
self.on_talk = [](var sender, string message) { /* do something here*/ };



//Unified API to easily handle save games, in the proper directories
var app = new GameApplication("my-game-name");

var saveslot = app.createSaveSlot("slot-" + 3); //c++-style strings are the default, although I guess one could make C-style strings
let(const savegame = saveslot.open("mydata", FileAccess::Write, FileMode::Binary)) {

byte bytes[] = {12, 13, 14, 15};
savegame.write(bytes);

} //savegame goes out of scope here, so it's destructed(and therefore closed)

//also, app.deleteSlot, app.slotExists, app.openSlot, etc.
//Essentially, each game has a global GameApplication instance, each save slot has a SaveSlot, and each save slot has as many files as the developer sees fit.
//Internally, ENIGMA may decide to compress all save slot files in a single zlib file, if Josh wishes to do so


I thought about all of this literally as I was writing it, so except inconsistencies, bad solutions, etc.
Fell free to discuss and take only the good parts. As you may have noticed, I have quickly scripted not only a language, but also some APIs, feel free to discard some parts of this post.
#4
Off-Topic / Terrena a14 - my game
October 10, 2011, 06:10:39 PM
EDIT 5: Alpha 14 released - MUCH improved artificial intelligence(special thanks Wikipedia for helping me implement a binary heap that allowed me to implement a fast dijkstra's algorithm).
Fixed bug that caused AI to be aborted when taking a card from the deck after doing some actions before.
Added new terrain: Midnight Field, which is really hard to get right now
Changed some terrain costs/gains. I believe the game is a bit more balanced now.
Added new unit: War Balloon, which added some needed diversity to the Sky Civs deck units.

EDIT 4: Alpha 13 released - Made threaded loading optional - disabled by default because it was causing trouble on some computers. Both threaded loading and render textures can be activated by editing the Data/config.xml file.
Added the "Sea Adventures" deck, which includes several new cards, including one new terrain, some previously hard-to-get terrains, new units and a new event/magic card.
Now using mipmaps to ensure smoother zoom out appearance.

EDIT 3: Alpha 12 released - Improved general interface, added a few new cards and added a timeout to computer actions that forces AI to end turn if "next move" computations take too long.

EDIT 2: Alpha 11 released - Fixed a bug in the unit "quick action" menu, added a couple of new cards, modified a few old ones and introduced the concept of "picking a deck". As a result of these changes, the game should be much more balanced, but some harder-to-use cards(such as the "Swamp") will be harder to come by now. I intend to fix this by adding further decks in the future.

EDIT: Alpha 10 released - New version includes a number of improvements, including support for Intel GPUs and .NET 3.5. The new version is now a bit more configurable, a little bit prettier and (I hope) easier to understand(actions like "Move unit" should now be easier to figure out)

Not ENIGMA related, but I figured I'd post it here anyway.

Today, I'm releasing the 7th alpha of my game - Terrena - to the world.
This is the first public version of my game. I'm hoping to get valuable feedback so I can improve the game.



So what is it?
Terrena is a strategy game that combines elements of TCG(trading card games) with board games.
Players start with a tile called "capital" which they must defend from other players and the goal is to conquer all capitals.
For that, players must expand their territory by placing on the board terrain and unit cards, and balancing it all so they have enough resources
Additionally, event cards allow special effects to happen to improve the gameplay experience.

How does it work?
There are seven types of resources: Money, Food, Earth, Water, Fire, Air and Ether.
To place a terrain in the game, one must spend resources(typically money for some of the basic types of terrain) but, in exchange, terrains may give resources at the start of a turn.
With enough resources, one can also place units on the game, which can be used to conquer enemy territory(and so get more terrain). Conquering all enemy capitals means victory.
Each player has a deck of cards and one may take a card from the deck to his/her hand by "buying it"(losing 1 of the resource money). Once in the hand, the players can use the cards they have.

Is it complete?
Not even close. There are not enough types of terrain, units and events. Additionally, there are some images that could use improvement.
The AI is also not perfect and there are some gameplay features that I haven't added yet.
Also, the game is not very balanced right now. I hope future new cards will improve this.

Computer requirements
In spite of being a 2D game, Terrena seems to be pretty demanding in terms of requirements. On one hand, you'll need a decent resolution(which pretty much everyone has anyway), but even on some GPUs the game can sometimes drop to 10FPS. Generally, the more resolution the slower the game.
Also, owners of old Intel cards with no FBO support are out of luck. If the game is essentially an empty black window after taking a card from the deck, you may not have the minimum requirements.
In terms of software, I tested this on Linux Mono and it probably works on Windows too. It requires .NET framework 4.0(tested in Mono 2.10)

Note: If you are using Linux, please run the game with "sh terrena.sh" instead of directly running the executable with Mono.

Download
http://dl.dropbox.com/u/6459451/terrena-a14.zip
#5
Tips, Tutorials, Examples / How to properly use OpenGL
October 04, 2011, 08:35:58 PM
This is really a must-have these days.
Seriously, if you use or are planning to ever use OpenGL, this tutorial is a MUST.

http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Table-of-Contents.html

It's sad that it was never completed, but every single chapter is totally worth it.

Remember: if you use glBegin/glVertex/glEnd, then you are doing it WRONG.

This has been a public service announcement. Thank you and have a nice day.
#6
Announcements / ENIGMA forums are dead again
September 24, 2011, 08:34:56 PM

This is merely a parody.
All characters appearing in this work are fictitious. Any resemblance to real persons, living or dead, is purely coincidental.


It happens once in a while, people stop posting, and then nothing happens.
So, now, this is the time when Josh comes along and posts an Announcement with a title like "Totally New Stuff".
But since he's taking a while(maybe it's the curse. I mentioned Aurora a while back and this might have killed ENIGMA), I figured I'd save him the trouble and do it myself.

Quote
[insert troll/obscure reference greeting here]

I know I haven't posted in a while, but that's because I was busy rewriting the parser again. It now handles stuff like "int x; = 2; 3" which turns out to be valid GML. It also serves coffee to the user, just not Java coffee, because Java sucks.
The new parser is much better than the old one, although it causes about 20 regressions.
I was also working in implementing user events, but those don't work because IsmAvatar refuses to be my slave. She claims she has a life besides LGM. *BLASPHEMY*
I also managed to get a new LGM option to improve compatibility with GM. It says "Be slow". Because GM is so slow ROFLCOPTER, and being fast breaks compatibility with some GM games. So this option injects a sleep() every 20 statements, to keep things balanced. But it's not just any sleep. It's a C++ sleep. Yeah! Damn straight.
Also, Windows 8 is retarded.

[insert random see you later note here]

Then Rusky comes along and posts
Quote
If you used decent parsers and LLVM, you wouldn't have to keep rewriting the parser. It'd be perfect and flawless in the first try.

Then an ENIGMA fanboy a honorable gentleman comes along and says something like
Quote
LLVM sucks! It's interpreted and JIT compiled, so it's slower than C++. Everybody knows GCC is the best thing ever. Except GCJ. GCJ sucks.

Then Josh replies to Rusky
Quote
My goal is to create a simple and powerful non-garbage collected C++ environment, and LLVM pollutes that idea. I can add Clang to the config later, but having ENIGMA directly emit LLVM opcodes? No way, it pollutes my idea of a perfect world.

IsmAvatar comes in and comments
Quote
You do know the code is open-source and you could patch it yourself, right?

Josh replies
Quote
I'm allergic to Java. Even using a computer with Java installed makes me itchy.
I once wrote a Java Hello World program and had to stay in the hospital for one whole week.

Then a noob an early adopter comes in:
Quote
I tried to compile a simple game but it gives me an error:

GCC error 1: Make love not war

What does this even mean?

Josh:
Quote
Are you using the very latest SVN revision?
What are the contents of your platform.ini file?

Early adopter:
Quote
I'm using revision XYZ.
Here's my file:

(Contents of platform.ini here)


Josh:
Quote
I'll look into it.
But meanwhile you can use revision ABC.

Me:
Quote
The next C# will have async. Will ENIGMA also add something like that in a future version?

Josh:
Quote
Are you MAD? I haven't even got C++0x lambdas to work.

Me:
Quote
Quote
Are you MAD?
Possibly.

(insert 3 more pages of essentially the same)

Then a few more topics come along, and after a while the forums die again.

And then Josh posts a new announcement. And it all starts all over again. A never ending cycle.
I wonder if it keeps going on forever or if it ever reaches a stack overflow...

That's it. Nothing else to see here. Move along.
#7
QuoteAt some point, though, before we start mass introducing these GM-incompatible features, we need some proposals on how to deal with when the user tries to press "Save As GMK" on a game full of EGM-specific features... This is not the topic to discuss that, though.

Ism posted that in another post. Since this *is* an interesting topic, I guess it would be good to discuss, so here's a brand new topic for it.

I suppose GM-incompatible features can be classified in threetwo categories:
1. Entirely and hopelessly incompatible with GM;
2. Potentially compatible with GM. For instance, let's say Josh implements an "animation order" in the sprite editor(just an example). If the animation order is 1-2-3-2, then this can be emulated by repeating 2 at the end of 3;

For 1, I'd say LGM should either show an error message and refuse to support GMK at all, or strip those features and risk a completely broken game.
For 2, LGM can bother to try to become compatible with GM, or not(depending on how much of an effort it is).

So what do you say?
#8
Proposals / Animated tiles/tilesets
August 08, 2011, 01:34:45 PM
Using RPG Maker 2000, I remembered the concept of animated tiles.
Of course, I don't suggest a tiling algorithm nearly as complex as RPG Maker's, but still I figured a small useful subset of it could be interesting in ENIGMA.

Here's what I propose for ENIGMA:
Create an image. Specify that the tileset has only one column, and each tile has 32x32.
Now, make the tileset have (32*N)x32, and specify in the tile editor that the animation has N frames.

Of course, some extra options would be useful, to better specify animations and ensure backwards compatibility with GM, but I just wanted to give an idea of what I meant.

Here's some options ENIGMA would need to have:
Number of animations
X delta per animation(32 in the example above)
Y delta per animation(0 in the example above)
Animation speed?
Animation order?(1-2-3 or 1-2-3-2) RPG Maker has this but I don't know if it's really necessary. What do you guys think?
#9
There are several things I hate in programming, such as regular expressions, segmentation faults, those annoying Swing exceptions that pop once in a while out of nowhere and contain only Swing/AWT functions in the stack trace, massive compile times, GCC C++ template error messages, the lack of a standard UI library in .NET, Java's pseudo-generics, the loading times of slow IDEs and the protected access modifier.

Today, I feel like insulting protected to hell. Why? Because it's worthless. Because I hate it almost as much as I hate regular expressions, and because I apparently have nothing better to do with my time.
It's worthless in C++(I'm talking about fields/methods, I haven't thought much about "class foo : protected bar" so I won't comment on that). It's worthless in Java. It's worthless in C#. It's worthless in pretty much any language.

Seriously, we can classify classes in two categories: Those that can be inherited, and those that can not.
Often languages provide special keywords(such as "sealed" and "final") to prevent a class from being inherited. Even if that is not used, private constructors can often put a stop to inheritance.

Protected is worthless in both types of classes.

Let's assume field(or method) Foo is declared in class Bar as protected.
Now class Bar can either be inherited in a given context or it can not.

If, in that context, Bar can't be inherited, then Foo is effectively private(or, at most, the equivalent of C#'s "internal", which is a much better approach to this problem).

If, in that context, Bar can be inherited, then one can create a dummy class BarExtension with only dummy constructors, dummy implementations of abstract methods, and functions declared like "TypeOfFoo GetFoo(Bar* bar)" and "void SetFoo(Bar* bar, TypeOfFoo foo)". These functions can possibly be static(or not, whatever, both would work).
Since now one can modify the Foo of ANY Bar with the help of BarExtension(which can be defined/declared pretty much anywhere), this means that Foo is effectively "public".

So, in any given context, Foo is either private or public.
It just gives a false sensation of security, without solving anything.

Just wanted to share this. Feel free to move along with your lives now.
#10
Issues Help Desk / Help with GM's instance ID system
February 20, 2011, 10:13:38 PM
I guess I understand how room/object/script IDs work. GM sets them at compile time, then adds some variable that contains the last ID and then "object_add"-like functions just increment that variable and use it as ID.

However, instance IDs are more problematic. Instances can be persistent, there are multiple rooms to be considered, etc.
How are instance IDs handled? In particular: instances in different rooms, having a persistent instance and going back to the room where it was created, and interaction with instance_create.

GM is in general relatively well documented, but the GML language itself and these kinds of "small details" are not. Knowing this sort of thing would be helpful.
#11
**Split into multiple posts because it exceeds the forums max size limit**

For quite some time, I've used GM for platform and puzzle games. However, I never used GM to make RPGs. I wanted to change that. Here's how I implemented a basic RPG engine in Game Maker. Much of this design can easily be ported to SDL, SFML and other APIs.

Before getting started
1. Make sure you have Game Maker installed. I don't think ENIGMA has the required functionality to run this yet.
2. Make sure you have some knowledge of GML, as I use it heavily.

Getting Started
The goal is to have a "classic" 2D RPG. I will not be covering random encounters here and the game will have no battles. Just moving around the world
The world must be organized in 32x32 tiles.
Each instance is precisely located in one of these tiles. However, when moving between tiles, the sprite should have a moving animation and gradually move between tiles, rather than instantly "jump". For all collision detection purposes, however, each instance is always in one of these tiles - only the drawing subsystem moves gradually.
The system should nicely fit with windows and pauses. This means freezing all movement on pause and some kind of "keyboard focus" system.
We want to apply the DRY(Don't Repeat Yourself) principle to this engine, so we'll be heavily using base classes("parent objects" in GM).
The engine reserves two base objects - "actor_obj" and "window_obj".
An Actor is an instance that is located in the world and interacts with other actors. An actor can be the main game hero, NPCs, doors, rocks and walls. It is important to know that only actors exist in the collision system.

Because of our very specific requirements, we will not be using GM's built-in hspeed/vspeed nor collision engine. Also, no object in our game will be "solid". We will be implementing our own hspeed/vspeed/collision engine.

Since only actors exist in the collision engine, the first thing we'll need is a function("script") to find if a given instance is an actor. We'll soon need a similar function for windows, so we'll have multiple scripts as a way to ensure compliance with DRY.

bool instance_is_actor(instance)
Code ("gml") Select

return object_type_is_actor(argument0.object_index);


bool object_type_is_actor(object_type)
Code ("gml") Select

return object_is_ancestor(argument0, actor_obj);


Now, we'll go to the actor_obj object. Our actor_obj is essentially a big part of our DRY plan, since we'll put all common stuff here. actor_obj is not meant to be used directly and, instead, should be seen as an abstract base class.
We will require several variables in actor_obj, related to the multiple features we want to have.
We are going to put sensible defaults for some of those variables in the Create Event.

First of all, we have to address the problem of mismatch between draw(GM) location and collision engine location.
For that, we will create the "target_x" and "target_y" variables. In addition, we will have to know when to stop("!moving") and where we're heading to("orientation").
Also, different actors may move at different speeds.

actor_obj Create Event
Code ("gml") Select

self.target_x = x;
self.target_y = y;
self.moving = false;
self.orientation = 0;
self.motion_speed = 2; //In tiles per second


We will need some sort of convention for orientation. I used down=0, left=1, right=2, up=3. Choosing this order has the nice property that 3 - orientation equals the inverse orientation.

One important part of our engine is in the step event.
In our step event, we will have to move our character if needed and also we'll have to stop it when the motion is complete.

actor_obj Step Event
Code ("gml") Select

if (self.moving) {   
    if (orientation == 0) {
        self.y += self.motion_speed * 32 / room_speed;
    }
    if (orientation == 1) {
        self.x -= self.motion_speed * 32 / room_speed;
    }
    if (orientation == 2) {
        self.x += self.motion_speed * 32 / room_speed;
    }
    if (orientation == 3) {
        self.y -= self.motion_speed * 32 / room_speed;
    }
   
    if (self.x mod 32 == 0 && (self.orientation == 1 || self.orientation == 2)) {
        self.moving = false;
        self.target_x = x;
    }
   
    if (self.y mod 32 == 0 && (self.orientation == 0 || self.orientation == 3)) {
        self.moving = false;
        self.target_y = y;
    }
}


One important thing to note is that we're trying to be somewhat independent from room_speed. However, it is possible that this results in rounding trouble, so I recommend using a power of 2 room_speed for your rooms, such as 16 or 32.
I have tested this engine with 32.
Here we move as much as needed, and check if we arrived to wherever we want to arrive.
However, notice we don't yet have a good way to "start moving". In particular, we can't just set the moving variable since we also need to update target_x/target_y.
In the name of DRY, we'll isolate "start moving" in a separated method - "perform_motion":

void perform_motion()
Code ("gml") Select

self.moving = true;
self.target_x = x_in_direction(self.orientation);
self.target_y = y_in_direction(self.orientation);


real x_in_direction(direction)
Code ("gml") Select

if (argument0 == 1)
    return x - 32;
if (argument0 == 2)
    return x + 32;
return x;


real y_in_direction(direction)
Code ("gml") Select

if (argument0 == 0)
    return y + 32;
if (argument0 == 3)
    return y - 32;
return y;


Now, let's say we want to have something to test.
We're going to create our room(room_speed=32), and we'll want in it an instance of hero_obj. It is important to always place actors in x/y that are multiple of 32. GM room editor's grid will certainly help here.
So, first we'll be creating our sprite. Put *anything* there, just to test it. Remember, however, that the engine expects a 32*32 actor. If you pick a different size, you may also want to take a look at the sprite origin.
Now, our hero_obj will have actor_obj as parent.
The first thing we'll want to have is our hero move when the player presses one of the directional keys.
Again, we want to abide by the DRY principle, so we'll want as much of the 4 keys code to be shared.

What happens when the user presses a directional key?
If the hero is moving, nothing happens.
If the hero is stopped and looking elsewhere, then the hero should look at the direction of the key.
If the hero is stopped and looking at the pressed direction, then the hero should start moving in that direction.
So we'll implement this:

void perform_dirkeypress(direction)
Code ("gml") Select

if (!self.moving) {
    if (self.orientation == argument0)
        perform_motion();
    else
        self.orientation = argument0;
}


Now, we'll have to implement keyboard events(Keyboard, NOT KeyPress). It'll be very simple KeyDown=perform_dirkeypress(0), KeyUp=perform_dirkeypress(3), etc.
In addition, we don't necessarily have to use the directional keys. We can instead decide to use the ASDW keys, for example.

Now, it's time to test our game.
Our lonely hero should be moving across an empty room in whatever direction you tell it to go.
#12
Ideas and Design / Extension API
January 22, 2011, 10:34:42 PM
I propose an extension API for ENIGMA based on the following concepts:

1. Each extension will typically have a Java/LGM part and an "execution" part(C++/ENIGMA or whatever the backend is)
3. Each execution component has a part of it that describes its function. This might be in Java, XML or any other cross-platform language that LGM can understand.

-------------------------------------

Core Extension API


LGM.requestRestart(String msg); //If the extension requires a restart
LGM.installExtension(String path, Object params); //Install "with args"
LGM.installExtension(String path); //Same as installExtension(path, null)
LGM.uninstallExtension(Extension extension);



class Extension
public String getName()
public String getDescription()
public String[] getAuthors()
public java.awt.Image getIcon()
public boolean getEnabled()
public void setEnabled(boolean enabled);
public ExtensionContent load();



interface or class ExtensionContent
public void install();
public void load(); //Extension code and resources were loaded, now handle the load logic
public void enable(); //Enable the extension


This is the barebones, now the interesting part:


void LGM.registerResourceType(ResourceType resource);
void LGM.unregisterResourceType(ResourceType resource);
GameFolder LGM.createGameFolder(String folderName, ResourceType resource, boolean canRename);
void LGM.renameGameFolder(GameFolder folder);
void LGM.deleteGameFolder(GameFolder folder);



class or interface ResourceType
public String getName();
public Image getIcon();
public Designer createDesigner(Object resource);
//Missing is the serialization API for loading/saving. I don't think I can properly design this without also designing a file format, which is not within the scope of this topic, so someone else will have to do this part.



class Designer extends JPanel
//Stuff like designer window title, etc.


Now for the "execution" part:

LGM.registerExecutionPlugin(ExecutionPlugin plugin);



class or interface BackendInfo
public String getName(); //return "enigma";
public String getLanguage(); //return "cpp";
public boolean isDebugMode();
public String getFullInfo(); //something like "enigma/cpp debug windows+opengl+openal"
//Possibly more functions



class or interface ExecutionPlugin
public String getName();
public boolean acceptsBackend(BackendInfo info); //Whether the execution plugin was made for this backend
public Script[] getScripts(); //Creates GML scripts for the plugin, useful for pure GML plugins
//The rest would be backend-specific. For instance, ENIGMA could provide a EnigmaExecutionPlugin.
//Do note that even if a plugin was not EnigmaExecutionPlugin, ENIGMA would accept it based ONLY on the acceptsBackend function, in order to allow pure GML extensions.


I am not sure how to allow pure GML scripts to handle custom resources, though.

This is just a proposal. Feel free to use, modify and/or ignore it. I release this proposal under the WTFPL.
#13
Disclaimer: This is NOT an operating system discussion.

ENIGMA uses rooms, a concept it inherits from Game Maker.
I'm going to ignore the issue of compatibility for now.

The idea behind Windows/Panes(a concept based on RPG Maker) is *NOT* to have a game with multiple windowing system windows. That'd be awful. I mean in-game windows/panes, such as dialogs and menus.
If you think about it, most games feature some sort of controls system, which I typically implement in Game Maker using the Draw event.
"Pause" is typically kind of an ugly hack, and so is keyboard management with menus.

Windows/Panes(along with focus) solve this problem by ensuring only the focused window/pane receives keyboard events(or something like having a "Global Keyboard Event" like what happens with the mouse). For RPGs, this means messages are incredibly simple to implement.
For some windows/panes, this might require some more effort, to ensure the world "stops" when e.g. pausing the game(set hspeed=0, etc.)
Still, simple compared to the situation of what we have in GM today, where I find myself reimplementing the system for some feature subset.

What do you think about this? Surely, Windows/Panes can be implemented using Rooms and Objects. But... what about doing things the other way around? Like implementing views using windows/panes.
#14
OpenAL is a 3D audio library that is available across multiple platforms.
Although it supports 3D sound, it can also be used as a 2D audio library.

First, some concepts:

1. The context: Pretty much like OpenGL, you first need a context to work with. You have to create at least one context before having any sound. I'd recommend creating one for the default device(NULL) and then just keep using that one.
2. The listener: It doesn't matter what is playing around the world if there's nobody there to hear it. To have OpenAL working we have to initialize a listener.
3. The source: Similarly, it doesn't matter who is listening if there's nothing being played. Just like listeners *receive* sounds, sources *emit* sounds.
4. The buffer: So, we have a context, a listener and a source. The listener listens to sounds and the source plays those sounds. But exactly what sounds does the source play? The buffers store data to be played by sources. We will load our audio data to buffers, and then assign those buffers to sources.

So, let's get started:
Code (c++) Select
#include <AL/al.h>
#include <AL/alc.h>
#include <cstdio>

int main() {
return 0;
}


Compile the code above using:
g++ -Wall -lopenal file.cpp -o program

Make sure you have everything you need installed, in particular the headers and libraries.

Now, our program doesn't do anything.
So we'll start by creating a context.

Code (c++) Select
ALCdevice* device = alcOpenDevice(NULL);
ALCcontext* context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);


In the example above, we open the default audio device(NULL), and then create a context for that device.
Finally, we use that context.

Now, we're going to define our listener:

Code (c++) Select

alListener3f(AL_POSITION, 0, 0, 0);
alListener3f(AL_VELOCITY, 0, 0, 0);
alListener3f(AL_ORIENTATION, 0, 0, -1);


Since we only want 2D sound, leave those values as they are. We're telling OpenAL where the listener is, where it is moving, etc.
We're going to put all listeners and sources in the origin with no speed.

Finally, we are going to have to load a audio file to a buffer and play it.
In this case, we're going to load the entire file to memory and play it all at once.

So first we are going to create a source:

Code (c++) Select

ALuint source;
alGenSources(1, &source);

alSourcef(source, AL_PITCH, 1);
alSourcef(source, AL_GAIN, 1);
alSource3f(source, AL_POSITION, 0, 0, 0);
alSource3f(source, AL_VELOCITY, 0, 0, 0);
alSourcei(source, AL_LOOPING, AL_FALSE);


Note how you first allocate a source. We're also setting the properties of the source.
In particular, you might find the LOOPING value to be interesting. Set it to AL_TRUE to loop the audio instead of just playing it once.
AL_PITCH means how "fast" the sound is. 1 is the normal speed. Below 1 the sound will take longer to play. For instance, a 1 minute sound with pitch 0.5 will take 2 minutes, and only 30 seconds with a pitch of 2.
Do note that modifying the pitch of the sound will make the track sound differently.  Try it yourself and you'll see what I mean.

However, you won't listen to anything yet because the source has no buffer.
So, we must create a buffer first:

Code (c++) Select

alGenBuffers(1, &buffer);

//TODO Load data to buffer

alSourcei(source, AL_BUFFER, buffer);


So our big problem is how to load data to the buffer.
I am going to be using the WAV sound format for now. Other formats may be added later.

The code I am using is the following. I will paste it once and then explain it:

Code (c++) Select

FILE* f = fopen("audio.wav", "fb");
char xbuffer[5];
xbuffer[4] = '\0';
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "RIFF") != 0)
throw "Not a WAV file";

file_read_int32_le(xbuffer, file);

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "WAVE") != 0)
throw "Not a WAV file";

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "fmt ") != 0)
throw "Invalid WAV file";

file_read_int32_le(xbuffer, file);
short audioFormat = file_read_int16_le(xbuffer, file);
short channels = file_read_int16_le(xbuffer, file);
int sampleRate = file_read_int32_le(xbuffer, file);
int byteRate = file_read_int32_le(xbuffer, file);
file_read_int16_le(xbuffer, file);
short bitsPerSample = file_read_int16_le(xbuffer, file);

if (audioFormat != 16) {
short extraParams = file_read_int16_le(xbuffer, file);
file_ignore_bytes(file, extraParams);
}

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "data") != 0)
throw "Invalid WAV file";

int dataChunkSize = file_read_int32_le(xbuffer, file);
unsigned char* bufferData = file_allocate_and_read_bytes(file, (size_t) dataChunkSize);

float duration = float(dataChunkSize) / byteRate;
alBufferData(buffer, GetFormatFromInfo(channels, bitsPerSample), bufferData, dataChunkSize, sampleRate);
free(bufferData);
fclose(f);


So now what is that big thing?
First we read the WAV header and extract the information from it, such as number of channels and rate.
Then, when we reach the section that contains the actual audio data, we load the entire thing to memory and load it to the buffer using alBufferData.

I have used multiple auxiliary functions:

1. file_read_int32_le(xbuffer, file) - I am using this function to read an integer of 32 bits from a file in little endian
2. file_read_int16_le(xbuffer, file) - This one is used to read 16 bits in little endian
3. file_ignore_bytes(file, nbytes) - Ignores N bytes from the file
4. file_allocate_and_read_bytes(file, nbytes) - Allocates a char* with N bytes and loads those bytes from the file
5. GetFormatFromInfo(channels, bitsPerSample) - Gets the AL format for the sound.

One possible (though incomplete) implementation of GetFormatFromInfo is:

Code (c++) Select

static inline ALenum GetFormatFromInfo(short channels, short bitsPerSample) {
if (channels == 1)
return AL_FORMAT_MONO16;
return AL_FORMAT_STEREO16;
}


file_ignore_bytes can be implemented with a while+fgetc, or more efficiently in other ways.
file_allocate_and_read_bytes is essentially a malloc and a fread.
file_read_int32_le/file_read_int16_le is essentially a fread to the buffer, using count=4, and then using bit shifts and bitwise ors to format the data.

These functions are pretty easy to implement, so I'll leave them to you (the reader) as a C exercise.
You can also load WAV using ALUT, if you have it installed.

So now that we have this working, we're going to play the sound (and let it keep playing)

Code (c++) Select

alSourcePlay(source);
fgetc(stdin);


This will keep playing the sound until the user presses enter in the console.

Finally, we'll do some cleanup:

Code (c++) Select

alDeleteSources(1, &source);
alDeleteBuffers(1, &buffer);
alcDestroyContext(context);
alcCloseDevice(device);


Now, try it. It should be playing whatever file named "audio.wav" you have in your current path.

Some gems:
1. You can use alSourcePause(source) to pause the source, and then alSourcePlay(source) to start it again
2. You can use alSourceStop(source) to stop the source. Calling alSourcePlay(source) after that will start it over from the beginning
3. You can change your mind about looping at the middle of the stream. Want to loop the sound? No problem, just set AL_LOOPING to AL_TRUE.
4. You can also change the pitch while the sound is playing. For instance, if you have a game and your character dies, you could have the pitch go progressively lower to indicate a "Game Over".

Some limitations:
1. GetFormatFromInfo is incomplete. For instance, MONO8 and STEREO8 aren't properly supported.
2. The entire file is loaded to memory. Depending on the size of the file it might be a problem.
3. Loading the entire file at once might be slow. However, in my experience, even for large files, this isn't much of a problem for WAV files. Memory consumption, as indicated in 2, might be significantly worse.

Bonus Tricks:

1. How to find the current position of the sound being played?

Code (c++) Select

int byteoffset;
alGetSourcei(source, AL_BYTE_OFFSET, &byteoffset);
return float(byteoffset) / byteRate;


Will return the number of seconds since the beginning of the sound file.

2. How to change the current position (e.g. skip some part of the sound)?

Code (c++) Select

alSourcei(source, AL_BYTE_OFFSET, int(position * byteRate));


Both of these tricks only work when the entire file is in a single buffer.

EDIT 27 Apr 2012 - Fixed bug that could potentially corrupt memory. (Credits to Stephan Z.)
#15
Function Peer Review / file_delete
December 31, 2010, 12:46:53 AM
This one looks so easy that it is a shame that ENIGMA doesn't have it already.

Code (cpp) Select

#include <stdio.h>
#include <string>

using namespace std;

inline void file_delete(string filename) {
    remove(filename.c_str());
}
#16
Off-Topic / C++0x and garbage collection
November 14, 2010, 11:06:41 PM
What happens to variables you forgot to free?
Previously, you'd expect to get a memory leak in C++, but in C++0x, according to wikipedia:

QuoteIt is implementation defined whether unreachable dynamically allocated objects are automatically reclaimed.

Of course, considering how permissive C++ is regarding memory, decent garbage collectors are nearly impossible, so we're most left with stuff like Boehm's conservative GC.

Note that implementation defined doesn't mean that all implementations GC. Only if the compiler makers decide so.
On GCC, I'd guess garbage collector would end up as some compiler flag.
#17
Off-Topic / Wayland (aka DIE X11 DIE)
November 05, 2010, 02:25:28 PM
The FOSS guys finally discovered that X11 sucks so bad, so they're finally starting to develop a replacement.
So eventually X11 will only be a backwards compatibility layer, like it is on Mac right now.

Awesome.
#18
Off-Topic / Slow compile times for C++?
September 11, 2010, 03:20:45 PM
Interesting: http://blog.mozilla.com/nnethercote/2010/09/10/another-go-at-language-design/

QuoteCompile times of Go programs are small.  This is because the compiler doesn't need to know about transitive dependencies between packages (their name for modules). (...) He said with a completely straight face that their goal was to have a 1,000,000x speed-up over C++ for the compilation time of large programs, though they'd probably be satisfied with 100,000x.

I'd say that's a pretty ambitious goal for Go engineers...
It also makes the advantage of scripting languages(just run instead of first compile then run) much smaller.
#19
Function Peer Review / Progressions
September 10, 2010, 11:22:21 PM
I've made two scripts for progress management: one for linear progress and one for parabolic progress.
The concept for linear progress is very simple. You define a "initial state" and "intended state", and then a "progression position".
It will then linearly go from one to another.

A simple example would be in the step event:


while (self.iter < 1000) {
self.iter += 1;
self.x = linear_progression(10, 20, self.iter / 1000);
}


In the code above, the object x would go from 10 to 20, linearly, and take 1000 steps to do it.

Another piece of code I wrote are parabolic progressions. In this case, there is also an initial state and a "position", but instead of "intended state" there is a "middle state", that is, the state of the progression when position is 0.5.
In my code, I use this for jumps.

I would also like to have cubic progressions, which would behave very similarly to linear progressions.

Anyway, here are the scripts:

linear_progression.gml (initial, final, t)
return argument0 + (argument1 - argument0) * argument2;

parabolic_progression.gml (initial, middle, t)
var a;
a = 4 * (argument0 - argument1);

return a * argument2 * argument2 - a * argument2 + argument0;


Hope this is useful for the rest of you.
#20
General ENIGMA / ENIGMA on the iPhone - Once again legal?
September 09, 2010, 02:37:51 PM
Apple relaxes restrictions on iOS app code, iAd analytics

It appears that Apple is loosening some of its most controversial iOS development rules.
Although the article doesn't seem to show the new language of the agreement, it is a good sign for ENIGMA developers who hope to see their games on iPhone in the future.
#21
Proposals / ENIGMA Wiki
September 08, 2010, 02:53:30 PM
I was wondering, just like there is wiki.yoyogames.com, there could be a wiki for ENIGMA development, including detailed documentation of the multiple ENIGMA(and GM) functions.
Documentation really helps development.
#22
Issues Help Desk / Needed ENIGMA functions
September 06, 2010, 10:28:48 PM
I'm trying ENIGMA and I've noticed several functions aren't available. I'm curious regarding whether the following functions are just not-yet-implemented or if any of those won't ever be:

object_get_parent
instance_change
instance_place

I could easily implement/contribute instance_place, but I'll need a working implementation of object_get_parent for that...

BTW: Regarding compile syntax errors, ENIGMA says which errors occur and in what files, but not in which line. You really should address this.
#23
General ENIGMA / GML
September 04, 2010, 02:47:34 PM
In my understanding, GML only has a two data types: strings and reals. Most variables, such as rooms, objects, etc. are in fact IDs(reals). surface_create does not return a surface, but rather the ID of a surface. The same applies to stacks, etc.

I'm curious about something in the ID system. Are all IDs completely unique or just unique to that data type? (there can be no more than one single stack with any given ID, but can a stack and a queue have the same ID? Same to instances, objects, etc.)
#24
General ENIGMA / Windows OpenGL detection
July 17, 2010, 08:39:58 PM
I've created a simple command line Windows application do detect which OpenGL version is installed and what extensions are available.
I called it "glversion".

/*
The MIT License

Copyright (c) 2010 Luís Reis <luiscubal@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

const wchar_t g_szClassName[] = L"myWindowClass";

int main(int argc, char* argv[])
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Step 1: Registering the Window Class
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_OWNDC;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = GetModuleHandle(NULL);
    wc.hIcon         = 0;
    wc.hCursor       = 0;
    wc.hbrBackground = 0;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = 0;

    if(!RegisterClassEx(&wc))
    {
        printf("Class registering failed\n");
        return 1;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        L"The title of my window",
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, GetModuleHandle(NULL), NULL);

    if(hwnd == NULL)
    {
        printf("Window creation failed!\n");
        return 1;
    }

    HDC hdc;
    HGLRC hrc;
   
    if (!(hdc = GetDC(hwnd))) {
        printf("Could not create drawing context\n");
        return 1;
    }
   
    static    PIXELFORMATDESCRIPTOR pfd= {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        32,
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        16,
        0,
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };

    GLuint        PixelFormat;
    if (!(PixelFormat=ChoosePixelFormat(hdc,&pfd)))
    {
        printf("Could not find pixel format\n");
        return 1;
    }

   
    if(!SetPixelFormat(hdc,PixelFormat,&pfd))
    {
        printf("Could not set pixel format\n");
        return 1;
    }


    if (!(hrc = wglCreateContext(hdc))) {
        printf("Could not create OpenGL context\n");
        return 1;
    }
    wglMakeCurrent(hdc, hrc);

    const char* vendor = (const char*) glGetString(GL_VENDOR);
    const char* renderer = (const char*) glGetString(GL_RENDERER);
    const char* version = (const char*) glGetString(GL_VERSION);
    const char* extensions = (const char*) glGetString(GL_EXTENSIONS);

    printf("Vendor: %s\nRenderer: %s\nVersion: %s\nExtensions: %s\n", vendor, renderer, version, extensions);
    printf("OpenGL Error: %s\n", gluErrorString(glGetError()));

    return 0;
}


To use it, just run it from the command line. You'll get as output something like this:
Vendor: Intel
Renderer: Intel 945GM
Version: 1.4.0 - Build 8.14.10.1930
Extensions: GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_blend_color GL_EXT_abgr GL_EXT_texture3D GL_EXT_clip_volume_hint GL_EXT_compiled_vertex_array GL_EXT_cull_vertex GL_SGIS_texture_edge_clamp GL_SGIS_generate_mipmap GL_EXT_draw_range_elements GL_SGIS_texture_lod GL_EXT_rescale_normal GL_EXT_packed_pixels GL_EXT_separate_specular_color GL_ARB_multitexture GL_EXT_texture_env_combine GL_EXT_bgra GL_EXT_blend_func_separate GL_EXT_secondary_color GL_EXT_fog_coord GL_EXT_texture_env_add GL_ARB_texture_cube_map GL_ARB_transpose_matrix GL_ARB_texture_env_add GL_IBM_texture_mirrored_repeat GL_EXT_multi_draw_arrays GL_NV_blend_square GL_ARB_texture_compression GL_3DFX_texture_compression_FXT1 GL_EXT_texture_filter_anisotropic GL_ARB_texture_border_clamp GL_ARB_point_parameters GL_ARB_texture_env_combine GL_ARB_texture_env_dot3 GL_ARB_texture_env_crossbar GL_EXT_texture_compression_s3tc GL_ARB_shadow GL_ARB_window_pos GL_EXT_shadow_funcs GL_EXT_stencil_wrap GL_ARB_vertex_program GL_ARB_fragment_program GL_EXT_stencil_two_side GL_ARB_vertex_buffer_object GL_EXT_texture_lod_bias GL_NV_texgen_reflection GL_ARB_depth_texture GL_WIN_swap_hint
OpenGL Error: no errors


With this application, you can realize how awful your GPU truly is(no FBO :().
I mean, OpenGL 1.4!  >:( We're in OpenGL FOUR now and the GPU comes with OPENGL 1.4!!? >:( >:( >:(
I guess people are right when they say Intel is garbage when it comes to graphics.
Anyway, I hope you find this useful.
#25
This is my collection of important APIs and articles useful when developing games or game engines.

APIs:
SDL
SFML
OpenGL
Direct3D
DevIL
OpenAL
libvorbis
Bullet Collision Detection & Physics Library
Lua
V8 JavaScript library
GStreamer

Articles and tutorials:
http://www.swiftless.com/
http://www.gamedev.net/reference/articles/article2008.asp
http://www.gamedev.net/reference/articles/article2031.asp
http://www.gamedev.net/reference/articles/article2003.asp
http://www.gamedev.net/reference/articles/article709.asp
http://www.gamedev.net/reference/articles/article2251.asp
http://code.google.com/intl/pt-PT/apis/v8/get_started.html
http://code.google.com/intl/pt-PT/apis/v8/embed.html

Incomplete list, might add more later. Also, feel free to suggest articles you fell are relevant.