Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (744)
Games in Android Showcase (225)
games submitted by our members
Games in WIP (825)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  joXSI - open source xsi model loader and renderer, with skinned animations  (Read 12622 times)
0 Members and 1 Guest are viewing this topic.
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Posted 2006-01-26 22:08:02 »

Click to Play

http://www.mojang.com/joXSI/

Quote
joXSI is currently far from done. It doesn't even have a version number yet.
So far, it does the following:

    * Parses all templates for xsi files version 3.5
    * Builds vertex lists for vertex positions, colors, normals and up to four texture units
    * Applies all FCurves in a clip on command (this means it supports simple srt animation)
    * If there is an envelope, they are automatically applied to the vertex lists (this means it supports skinned animation)
    * Loads a texture by name. This is the ONLY thing it does with materials so far.
    * Renders the scene via JOGL/JSR 231

Demo via webstart: http://www.mojang.com/joXSI/demo/joXSI_demo.jnlp

Play Minecraft!
Offline f.l.x

Senior Devvie


Projects: 3


there is no place like 127.0.0.1


« Reply #1 - Posted 2006-01-26 22:30:54 »

i can't get the "templates" frame to grab focus Undecided

now that i look, alt-tabbing to the "model displayer" window it doesn't grab the focus back. i'll have to kill the java procces to exit Undecided

Litterarum radices amaras, fructus dulces
http://flx.proyectoanonimo.com
figth spam!
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #2 - Posted 2006-01-26 22:32:08 »

whoa, odd

Either there's some deadlock, or it's starving the awt dispatch thread, would be my guess.

Play Minecraft!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline kaffiene
« Reply #3 - Posted 2006-01-27 04:02:35 »

Excuse my ignorance, but where does XSI fit in in the world of 3d content creators?
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #4 - Posted 2006-01-27 09:13:09 »

http://en.wikipedia.org/wiki/Softimage_XSI

It's an open and very powerful format, supporting things like inverse kinematics, NURBS, skeletal animation, hierarchical materials, and, of course, vertex data.
It's used for Half-Life 2, and there are XSI exporters available for most popular 3d modelling tools.

Play Minecraft!
Offline Spasi
« Reply #5 - Posted 2006-01-27 13:00:02 »

Hi Markus,

As you may be aware, our design pipeline for Marathon is exclusively based on XSI. So, I've created a dotXSI parser about two years ago, which is built in our main pre-processing tool. The parser is relatively old (version 3.0), but currently good enough for our needs (haven't had the chance to upgrade it). Anyway, if you're interested in building a more general/useful tool out of this and not just an importer/viewer, I may be able to contribute some of the following:

- Vertex indices optimization (for any vertex cache size)
- FCurve keys optimization
- Detection of bad bone weights + fix up
- Tangent-space creation with a Java port of NVMeshMender (although there's decent support for tangents in the latest XSI versions)
- Normal map generation with the use of ATI NormalMapper (automatic export of low & high res models to .NMF and exec of NormalMapper, with full UI defined parameters)
- Texture map generation (support for any kind of texture, mip creation, possibly hardware accelerated with shaders)
- LWJGL renderer
- Support for dotXSI 3.6
- Some kind of general/customizable binary export, for immediate inclusion to third-party file formats.

Code for everything but the last two already exists in our tool, although not general enough for a simple copy/paste. Tell me if you're interested.
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #6 - Posted 2006-01-27 13:20:49 »

I'm extremely interested in contributions. :-)
You'll get full credits, of course.

I'm going to use joXSI in wurm for all model rendering, so it needs to be flexible enough replace fairly advanced things like intercepting/replacing materials (for unique skin textures on players), retrieving the transformed location of a mesh in the model (for particle emitters and pluggable weapons), and loading clips from one scene and applying to a model from another scene.

I've got big plans for joXSI. Wink

The goal is to make this be able to handle everyting in an XSI file, including the materials, inverse kinematics and nurbs.
I call it an "importer" since the rendering code is jogl specific, and really only supplied as a refence so far, but I've got a feeling supporting the materials will force it to have slightly more integrated rendering code.


You don't happen to know the specification for the binary xsi formats? So far, I only support text encoding.
I've looked, but all I can find is that it can either be binary (but in what format? seems to be hidden in the ftk binaries somewhere), or compressed (lzw or gzip).

Play Minecraft!
Offline Spasi
« Reply #7 - Posted 2006-01-27 19:46:58 »

I'm going to use joXSI in wurm for all model rendering, so it needs to be flexible enough replace fairly advanced things like intercepting/replacing materials (for unique skin textures on players), retrieving the transformed location of a mesh in the model (for particle emitters and pluggable weapons), and loading clips from one scene and applying to a model from another scene.

I've got big plans for joXSI. Wink

The goal is to make this be able to handle everyting in an XSI file, including the materials, inverse kinematics and nurbs.
I call it an "importer" since the rendering code is jogl specific, and really only supplied as a refence so far, but I've got a feeling supporting the materials will force it to have slightly more integrated rendering code.

Cool, I was planning some of these too. One question though, what are your needs exactly wrt materials? I could imagine using the illumination shaders & Cg shaders, extracting per cluster materials, textures, etc. Do you plan something more elaborate?

You don't happen to know the specification for the binary xsi formats? So far, I only support text encoding.
I've looked, but all I can find is that it can either be binary (but in what format? seems to be hidden in the ftk binaries somewhere), or compressed (lzw or gzip).

I've no idea. That's why I recommended a more open binary file, easily exportable by joXSI. Cleaned up of course (no xsi stuff) and having anything joXSI adds to it.

BTW, I noticed the cubic() method in the Interpolator class. Do you know if that is the exact interpolation xsi does internally for cubic fcurves?
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #8 - Posted 2006-01-27 21:38:07 »

Cool, I was planning some of these too.
Awesome, let's join forces. :-)
We probably should move it to sourceforge or java.net so we get some free cvs. Much more convenient than emailing code.

One question though, what are your needs exactly wrt materials? I could imagine using the illumination shaders & Cg shaders, extracting per cluster materials, textures, etc. Do you plan something more elaborate?

Yes; replacing materials on the fly. For example, the textures for player models would be baked with the textures of the clothes they wear, so they need to be replaced with something dynamic. Probably something as simple like this:

1  
2  
3  
playerModel.setMaterial("player.torso", new PlayerMaterial(player.getClothes());
playerWalkAnimation.apply(playerModel, player.getWalkTime());
modelRenderer.render(playerModel);


I've no idea. That's why I recommended a more open binary file, easily exportable by joXSI. Cleaned up of course (no xsi stuff) and having anything joXSI adds to it.
Ack, i'll keep looking. It'd be nice to be able to import those as well.
I'm thinking that instead of a new file format, we could just prove an XSI file optimizer that removes templates not needed/used by joXSI (I know, this goes against the idea of supporting everything..) and optimses away noop fcurves (i've seen a lot of fcurves with just zeroes in them), then saves that as a gzip or lzw compressed XSI file.

BTW, I noticed the cubic() method in the Interpolator class. Do you know if that is the exact interpolation xsi does internally for cubic fcurves?
Oh how I struggled with that one. The documentation is.. wrong, so that one is the result of a lot of guessing and manual .xsi file inspection.. it seems to work with my test models, but feel free to do some more testing. =)
It makes absolutely no sense that it would use the input vectors from the next keyframe as the output from the current (instead of the output vectors from the current), so I think it's wrong. Wink

Play Minecraft!
Offline tusaki

Junior Devvie


Medals: 1


In a mad world only the mad are sane.


« Reply #9 - Posted 2006-01-27 21:45:10 »

Awesome Smiley I might want to contribute too, if there's anything specific which needs to be done. But could you comment your code please?  Grin
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #10 - Posted 2006-01-27 22:21:57 »

Haha, working on it. =)

Play Minecraft!
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #11 - Posted 2006-01-28 03:58:06 »

New version up, with plenty of comments.

The javadocs are still a bit sparse, but it's soooo boooring to write good javadocs.

Play Minecraft!
Offline Spasi
« Reply #12 - Posted 2006-01-30 23:40:44 »

BTW, I noticed the cubic() method in the Interpolator class. Do you know if that is the exact interpolation xsi does internally for cubic fcurves?
Oh how I struggled with that one. The documentation is.. wrong, so that one is the result of a lot of guessing and manual .xsi file inspection.. it seems to work with my test models, but feel free to do some more testing. =)
It makes absolutely no sense that it would use the input vectors from the next keyframe as the output from the current (instead of the output vectors from the current), so I think it's wrong. Wink

OK, I spent the whole weekend and a couple of hours today trying to finally find a solution for this. I think I succeeded! Cheesy

So, basics first. I found this definition in an FTK file (xsi_fcurve.h):

Quote
Interpolation (how the value is evaluated between FCurveKeys) can be constant, linear, or cubic. Cubic means that a Bezier curve is calculated as the interpolation between the keys. XSI uses cubic Bezier curves defined as follows:

Definition from de Casteljau's algorithm:

Four points A, B, C and D in the plane or in three-dimensional space define a cubic Bezier curve. The curve starts at A going toward B and arrives at D coming from the direction of C. In general, it will not pass through B or C; these points are only there to provide directional information. The distance between A and B determines "how long" the curve moves into direction B before turning towards D.

A is the "left" keyframe and B is its right tangent. D is the "right" keyframe and C is its left tangent. I believe that explains it well.

One thing I noticed was that de Casteljau's algorithm is not the most efficient way to do cubic interpolation. Simply evaluating an optimized version of the curve's parametric form is 4 instructions less. Compare the following:

de Casteljau's algorithm

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public static float interpolateCubic(float a, float b, float c, float d, float t) {
    // 18 instructions
    float ab = a + (b - a) * t;
    float bc = b + (c - b) * t;
    float cd = c + (d - c) * t;

    float abbc = ab + (bc - ab) * t;
    float bccd = bc + (cd - bc) * t;

    return abbc + (bccd - abbc) * t;
}


Parametric form

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public static float interpolateCubic(float a, float b, float c, float d, float t) {
    // B(t) = a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3

    // Let tInv = (1-t)
    // B(t) = a * tInv^3 + 3 * t * tInv * (b * tInv + c * t) + d * t^3
    // B(t) = tInv * (a * tInv^2 + 3 * t * (b * tInv + c * t)) + d * t^3

    // 14 instructions
    float tInv = 1.0f - t;
    return tInv * (a * tInv * tInv + 3.0f * t * (b * tInv + c * t)) + d * t * t * t;
}


Anyway, the cubic method in your Interpolator class is wrong, simply because you're not taking into account the x-axis values (time in keyframes). Imagine a key with "flat" left/right tangents (aligned to x-axis, height equal to the key). Grab one of the tangents and drag it left or right, without changing the height. Obviously, the curve profile is changing even without touching the y-axis values. We have to incorporate this effect to the interpolation algorithm. Some pics (click for hi-res, curve sampling is sparse on purpose to show the differences):

Original curve in XSI



Interpolating Y-Axis values only



So, why don't we just interpolate the x-axis values too? Yeah, that's also what I thought would easily give some kind of solution. But, no:

Interpolating X-Axis values



Well, we did get a nice curve. But there's obviously something wrong with the above image. The (not so) vertical lines show where/when exactly we asked an interpolated value and what we ended up getting. As you can see, the sampled points tend to be pulled towards areas of the curve that change significantly. This is great if you only need to draw the curve (with a given amount of samples, you get the best quality), but not so if you just want to get an interpolated value at some time offset. You want the value at *exactly* that offset.

My solution was the simplest I could think of, but I believe it's far from optimal performance-wise. Suppose I want to get the interpolated value at time t. Let Bx and By be equivalent to calling the above interpolateCubic with the x-axis and y-axis values respectively. To get the correct value, I have to interpolate the y-axis values at a time t', where t' is the value that satisfies the equation Bx(t') = t. So, the correct result would be By(t').

To solve the Bx(t') = t for t', we need to bring the curve's parametric form to a better one:

1  
2  
3  
4  
5  
6  
B(t) = a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3 ==> (1)
B(t) = (-a + 3b - 3c + d)t^3 + 3(a - 2b + c)t^2 + 3(-a + b)t + a ==> (2)
// Let A = (-a + 3b - 3c + d)
// Let B = (a - 2b + c)
// Let C = (-a + b)
B(t) = At^3 + Bt^2 + Ct + a (3)


(1) is the original equation, (2) is what occurs after some math and (3) is a standard, solvable cubic equation. So, once you've solved the equation, with something like this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
public static float interpolateCubicInverse(final float a, final float b, final float c, final float d, final float t) {
    float A = (-a + 3.0f * b - 3.0f * c + d);
    float B = (3.0f * (a - 2.0f * b + c));
    float C = (3.0f * (b - a));
    float D = a - t;

    int rootCount = solveCubic(A, B, C, D, roots, 0); // TODO: How to handle multiple roots?

    return roots[0];
}


You can finally get the proper result:

1  
2  
3  
float t = ...;
t = interpolateCubicInverse(x1, influenceX1, influenceX2, x2, t);
float value = interpolateCubic(y1, influenceY1, influenceY2, y2, t);


Correct Interpolation



Some notes:

- Solving the cubic equation is the most costly part of this algorithm. 20+ instructions, one sqrt and two cube roots for the common case.
- I'm not sure how to handle multiple roots returned by solveCubic. From my tests, it occured very few times and just picking the first one (they're ordered by value) produced no problems.
- An obvious optimization is to take advantage of equation (2). By storing A, B, C per key interval, instead of key & tangent values, interpolateCubic falls to 9 instructions, whereas solveCubic is the only cost in interpolateCubicInverse.
- I'm sure there are faster ways. One is obvious from the "Interpolating X-Axis values" image: Sample the curve at several points, optimize based on an error threshold, then store the resulting keyframes as a linear curve. There's a performance-memory trade-off here of course.

Pheew! Sorry for the long post! Wink
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #13 - Posted 2006-01-31 08:58:36 »

Very nice. :-)
Exhaustive, and good pictures so even I can understand it. Wink


What's the source code for solveCubic?
Obviously, having a bunch of srqts in there isn't exactly optimal.

Play Minecraft!
Offline Spasi
« Reply #14 - Posted 2006-01-31 11:51:03 »

Hehe, I just found an implementation in the JDK. I had forgotten Java2D supports cubic curves. You can find a solveCubic in java.awt.geom.CubicCurve2D. It also has a couple of methods that handle multiple roots.

edit: There are some nice, generic math methods in java.awt.geom (solveCubic in CubicCurve2D, solveQuadratic in QuadCurve2D). Why don't they put these in java.lang.Math?
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #15 - Posted 2006-01-31 12:06:35 »

Ah, cool. I did not know that!

I'll work some more on joXSI later this week. Right now I'm rewriting the terrain renderer in wurm, for greater fps.

(Screenies:

http://www.mojang.com/notch/screenshots/newterrain.jpg
http://www.mojang.com/notch/screenshots/newterrain2.jpg

The second one is slower because of wireframe mode only. I have no idea why it's that much slower..
There's not a single shared texel in that scene. Cheesy All unique texturing)


If anyone with access to XSI wants to save a test dotXSI file in both binary and text format, so I can get started on reverse engineering the binary format, I'd appreciate it. =)

Play Minecraft!
Offline Spasi
« Reply #16 - Posted 2006-02-04 16:09:00 »

The second one is slower because of wireframe mode only. I have no idea why it's that much slower..

Consumer-level GPUs do not hardware accelerate line rendering. The chips can do it, but it is disabled by the drivers. That's how they sell professional cards (Quadro, FireGL, etc), which essentially use the same chips.

If anyone with access to XSI wants to save a test dotXSI file in both binary and text format, so I can get started on reverse engineering the binary format, I'd appreciate it. =)

No need to, I figured it out a few days ago. Wink

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
private static final Pattern HEADER_RE = Pattern.compile("xsi ([0-9][0-9])([0-9][0-9])(txt|bin) 00(32|64)");

public static enum Format {
    TXT,
    BIN,
}

public XSIFile(File file) throws IOException, XSIParserException {
    ByteBuffer buffer = fileRead(file).order(ByteOrder.nativeOrder());

    if ( buffer.limit() < 16 )
        throw new IllegalArgumentException("Corrupt dotXSI file. Size: " + buffer.limit());

    String header = strReadBytes(buffer, 16);

    Matcher matcher = HEADER_RE.matcher(header);
    if ( !matcher.matches() )
        throw new XSIParserException("Invalid dotXSI header: " + header);

    this.majorVersion = parseInt(matcher.group(1));
    this.minorVersion = parseInt(matcher.group(2));

    this.format = Enum.valueOf(Format.class, matcher.group(3).toUpperCase());

    this.floatSize = parseInt(matcher.group(4));

    if ( format == Format.BIN ) {
        // Skip empty space
        while ( buffer.hasRemaining() && buffer.get(buffer.position()) == '\n' )
            buffer.position(buffer.position() + 1);

        if ( buffer.remaining() < 3 * 4 )
            throw new XSIParserException("Corrupt binary dotXSI file.");

        // Skip trailing zeros.
        buffer.limit(buffer.limit() - 4);

        int decompressedSize = buffer.getInt();
        int compressedSize = buffer.getInt();

        if ( decompressedSize < 0 )
            throw new XSIParserException("Corrupt binary dotXSI file. Decompressed size found: " + decompressedSize);
        if ( compressedSize < 0 || compressedSize != buffer.remaining() )
            throw new XSIParserException("Corrupt binary dotXSI file. Compressed size found: " + compressedSize);

        // Allocate a buffer to hold the decompressed data.
        ByteBuffer decompressed = ByteBuffer.allocate(decompressedSize);

        // Create the decompressor.
        InputStream input = new InflaterInputStream(new BufferInputStream(buffer));

        // Read the decompressed data.
        byte[] bytes = new byte[1024];
        int count;
        while ( (count = input.read(bytes)) > 0 ) {
            if ( decompressed.remaining() < count )
                throw new XSIParserException("Corrupt binary dotXSI file.");

            decompressed.put(bytes, 0, count);
        }

        decompressed.flip();

        new XSIParser(this, decompressed);
    } else
        new XSIParser(this, buffer);
}


Just a note, BufferInputStream is a simple InputStream implementation using a ByteBuffer as the stream source.
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #17 - Posted 2006-02-07 10:10:02 »

The "binary format" is just a deflated version of the text format? Wow. Undecided


Awesome work, btw. :-)

Play Minecraft!
Offline Spasi
« Reply #18 - Posted 2006-02-07 13:42:02 »

The "binary format" is just a deflated version of the text format? Wow. Undecided

Yeah, I got the hint from the presence of zlib headers in the FTK.

Awesome work, btw. :-)

Wink
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #19 - Posted 2006-02-07 14:13:01 »

Hm, are you sure it wasn't one of the compressed formats?

There's both gzip and.. something else, I think

Play Minecraft!
Offline Spasi
« Reply #20 - Posted 2006-02-07 15:12:51 »

I'm sure. Actually, I've never seen a "compressed" dotXSI file, and there's no option to export such a file from within XSI (only ascii and binary). Also, the header has a plain "bin", not a "com" or any compression type string after that. From the docs:

Quote
It supports dotXSI 3.0 and later, in both the text and binary compressed format.
[...]
With versions 3.0 and later of the XSI file format, you can create a binary file. The binary format provides an easy way to compress the data in the XSI file.
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #21 - Posted 2006-02-07 19:03:21 »

From the 3.6.2 XSI FTK:

Field               Size (b)  Contents  Description

Magic number          4        "xsi"    File type
Version/major number  2        01       Major version 1
Version/minor number  2        03       Minor version 3
Format type           4        "txt"    Text file
                               "bin"    Binary file
Compression type      4        "zip"    Compressed file created by PKZIP
Float size            4        0064     64-bit floats
                      4        0032     32-bit floats


Note the "compression type" field.
It doesn't seem to exist in any files with the type set to "txt" or "bin"(*), but I have seen it mentioned online, but only with the format type "com".


(* Don't you just love documentation that's just.. wrong?)

Play Minecraft!
Offline Spasi
« Reply #22 - Posted 2006-02-07 20:46:00 »

That's why I chose to ignore it. If something turns out later, support can be added then. Since apparently there's no "true" binary format, it won't be a problem.
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #23 - Posted 2006-02-08 08:55:38 »

Agreed.

Play Minecraft!
Offline darkprophet

Senior Devvie




Go Go Gadget Arms


« Reply #24 - Posted 2007-02-10 00:14:58 »

Sorry to bring this topic back to life, but whats the status of this project?

DP

Friends don't let friends make MMORPGs.

Blog | Volatile-Engine
Offline Markus_Persson

JGO Wizard


Medals: 19
Projects: 19


Mojang Specifications


« Reply #25 - Posted 2007-02-11 07:36:28 »

I'd say "abandoned, but in active use in wurm".

Play Minecraft!
Pages: [1]
  ignore  |  Print  
 
 

 
Ecumene (146 views)
2017-09-30 02:57:34

theagentd (213 views)
2017-09-26 18:23:31

cybrmynd (295 views)
2017-08-02 12:28:51

cybrmynd (284 views)
2017-08-02 12:19:43

cybrmynd (294 views)
2017-08-02 12:18:09

Sralse (287 views)
2017-07-25 17:13:48

Archive (966 views)
2017-04-27 17:45:51

buddyBro (1092 views)
2017-04-05 03:38:00

CopyableCougar4 (1663 views)
2017-03-24 15:39:42

theagentd (1425 views)
2017-03-24 15:32:08
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!