Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (566)
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  
  Help on view culling algo  (Read 1858 times)
0 Members and 1 Guest are viewing this topic.
Offline Spasi
« Posted 2002-11-24 16:42:34 »

Hi guys, I have a little maths problem and I'd like some help.

I decided to have a go at LWJGL, and after some basic setting up stuff, I'm now trying to implement a view culling algorithm. I have some OpenGL tutorials in my hands, but no code, so this is what I tried to do:

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  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
private final static int RIGHT_PLANE = 0;
private final static int LEFT_PLANE = 4;
private final static int TOP_PLANE = 8;
private final static int BOTTOM_PLANE = 12;
private final static int NEAR_PLANE = 16;
private final static int FAR_PLANE = 20;
     
private double[] frustrum;

// Computes the frustrum plane equations      
public void computeFrustrum() {
      gl.loadIdentity();
           
      // Camera Transformation
     gl.translatef(0.0f, 0.0f, camZ);
      gl.rotatef(camRotX, 1.0f, 0.0f, 0.0f);
           
      // Interest Transformation
     gl.rotatef(intRotY, 0.0f, 1.0f, 0.0f);
      gl.translatef(intX, intY, intZ);
           
      // Get ModelView Matrix
     BufferCache.loadMatrix(GL.MODELVIEW_MATRIX);
      matrix.set(BufferCache.matrix);
           
      // Multiply by Projection Matrix
     matrix.mul(EngineGraph.PROJECT_MATRIX);
           
      // Right
     frustrum[0] = matrix.m03-matrix.m00;
      frustrum[1] = matrix.m13-matrix.m10;
      frustrum[2] = matrix.m23-matrix.m20;
      frustrum[3] = matrix.m33-matrix.m30;
           
      // Left
     frustrum[4] = matrix.m03+matrix.m00;
      frustrum[5] = matrix.m13+matrix.m10;
      frustrum[6] = matrix.m23+matrix.m20;
      frustrum[7] = matrix.m33+matrix.m30;
           
      // Top
     frustrum[8] = matrix.m03-matrix.m01;
      frustrum[9] = matrix.m13-matrix.m11;
      frustrum[10] = matrix.m23-matrix.m21;
      frustrum[11] = matrix.m33-matrix.m31;
           
      // Bottom
     frustrum[12] = matrix.m01+matrix.m03;
      frustrum[13] = matrix.m11+matrix.m13;
      frustrum[14] = matrix.m21+matrix.m23;
      frustrum[15] = matrix.m31+matrix.m33;
           
      // Near
     frustrum[16] = matrix.m02-matrix.m03;
      frustrum[17] = matrix.m12-matrix.m13;
      frustrum[18] = matrix.m22-matrix.m23;
      frustrum[19] = matrix.m32-matrix.m33;
     
      // Far
     frustrum[20] = matrix.m03-matrix.m02;
      frustrum[21] = matrix.m13-matrix.m12;
      frustrum[22] = matrix.m23-matrix.m22;
      frustrum[23] = matrix.m33-matrix.m32;
}

// Returns true when point is within the view      
public boolean isVisible(float x, float y, float z) {
      if ( visibleInPlane(RIGHT_PLANE, x, y, z)
      && visibleInPlane(LEFT_PLANE, x, y, z)
      && visibleInPlane(TOP_PLANE, x, y, z)
      && visibleInPlane(BOTTOM_PLANE, x, y, z)
      && !visibleInPlane(NEAR_PLANE, x, y, z)
      && visibleInPlane(FAR_PLANE, x, y, z) )
            return true;
      else
            return false;
}

// Returns true if the point is "inside" the plane      
private boolean visibleInPlane(int plane, float x, float y, float z) {
      double xc = frustrum[plane++] * x;
      double yc = frustrum[plane++] * y;
      double zc = frustrum[plane++] * z;
      double wc = frustrum[plane];
     
      if ( -wc < xc && xc < wc && -wc < yc && yc < wc && -wc < zc && zc < wc )
            return true;
      else
            return false;
}


It works pretty nice, but I don't know why I have to put that ! in front of the near plane comparison (it works that way, I just can't understand why it doesn't without it Huh). And there is a problem with the bottom plane. When the view looks straight down the y axis or straight forward, down the z axis, it works correctly, but at other angles it fails. It returns false even when the point is just below the middle of the screen, or lower depending on the view angle.

I'm not very good at math, but I think I implemented what I read in the tutorials properly. I just can't see where the problem is. Any hints people?

- Any comments on the algo itself will be appreciated (I'm not even sure if it is a "decent" way to do it). And of course suggestions for making it better.

Spasi
Offline Exocet

Senior Newbie





« Reply #1 - Posted 2002-12-08 03:08:04 »

I think most likely you just have your normal for the near plane reversed.   It is probably in the same direction as your far plane, thus needing the ! for your inside/outside test.  Am I correct in assuming each plane is defined in the form Ax + By + Cz + D = 0?
plane = A
plane[i+1] = B.... etc

- Tristan
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #2 - Posted 2002-12-08 08:12:59 »

heh, view frustum clipping is somewhere on my todo list for my next project, but I most of your code looks good to me.

However, I'm wondering what happens in your code when an object is sufficiantly large enough to cover the entire screen? Individual points may be outside the view, but the object is still visible. I was thinking of doing a 3d version of the Cohen-Sutherland algorithm, which should be nice and fast...

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Herkules

Senior Member




Friendly fire isn't friendly!


« Reply #3 - Posted 2002-12-08 08:36:16 »

[rant]
Oh, how I hated this kind of problems when I did D3D earlier...... Holes in the terrain, object popping in and out ... why why why... ?

I'm so glad there Java3D around now....


[/rant]

HARDCODE    --     DRTS/FlyingGuns/JPilot/JXInput  --    skype me: joerg.plewe
Offline Spasi
« Reply #4 - Posted 2002-12-08 10:45:39 »

I solve my culling problems last week and I thought I should share my solution. It's quite different from what I started with and it works wonderfully! I'll just paste the whole code and hope it might help someone here...

I'll post it in two messages, because it's too long for YABB...

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  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
public final class Frustum {

    private final double[] frustum;

    private final Point3d corner;
    private final Point3d lower;
    private final Point3d upper;

    public Frustum() {
        frustum = new double[24];

        corner = new Point3d();
        lower = new Point3d();
        upper = new Point3d();
    }

    public final void calculate(final Matrix4d matrix) {
        // Left
       frustum[0] = matrix.m03 + matrix.m00;
        frustum[1] = matrix.m13 + matrix.m10;
        frustum[2] = matrix.m23 + matrix.m20;
        frustum[3] = matrix.m33 + matrix.m30;

        double norm = Math.sqrt(frustum[0] * frustum[0] + frustum[1] * frustum[1] + frustum[2] * frustum[2]);
        frustum[0] /= norm;
        frustum[1] /= norm;
        frustum[2] /= norm;
        frustum[3] /= norm;

        // Right
       frustum[4] = matrix.m03 - matrix.m00;
        frustum[5] = matrix.m13 - matrix.m10;
        frustum[6] = matrix.m23 - matrix.m20;
        frustum[7] = matrix.m33 - matrix.m30;

        norm = Math.sqrt(frustum[4] * frustum[4] + frustum[5] * frustum[5] + frustum[6] * frustum[6]);
        frustum[4] /= norm;
        frustum[5] /= norm;
        frustum[6] /= norm;
        frustum[7] /= norm;

        // Top
       frustum[8] = matrix.m03 - matrix.m01;
        frustum[9] = matrix.m13 - matrix.m11;
        frustum[10] = matrix.m23 - matrix.m21;
        frustum[11] = matrix.m33 - matrix.m31;

        norm = Math.sqrt(frustum[8] * frustum[8] + frustum[9] * frustum[9] + frustum[10] * frustum[10]);
        frustum[8] /= norm;
        frustum[9] /= norm;
        frustum[10] /= norm;
        frustum[11] /= norm;

        // Bottom
       frustum[12] = matrix.m01 + matrix.m03;
        frustum[13] = matrix.m11 + matrix.m13;
        frustum[14] = matrix.m21 + matrix.m23;
        frustum[15] = matrix.m31 + matrix.m33;

        norm = Math.sqrt(frustum[12] * frustum[12] + frustum[13] * frustum[13] + frustum[14] * frustum[14]);
        frustum[12] /= norm;
        frustum[13] /= norm;
        frustum[14] /= norm;
        frustum[15] /= norm;

        // Near
       frustum[16] = matrix.m02 + matrix.m03;
        frustum[17] = matrix.m12 + matrix.m13;
        frustum[18] = matrix.m22 + matrix.m23;
        frustum[19] = matrix.m32 + matrix.m33;

        norm = Math.sqrt(frustum[16] * frustum[16] + frustum[17] * frustum[17] + frustum[18] * frustum[18]);
        frustum[16] /= norm;
        frustum[17] /= norm;
        frustum[18] /= norm;
        frustum[19] /= norm;

        // Far
       frustum[20] = matrix.m03 - matrix.m02;
        frustum[21] = matrix.m13 - matrix.m12;
        frustum[22] = matrix.m23 - matrix.m22;
        frustum[23] = matrix.m33 - matrix.m32;

        norm = Math.sqrt(frustum[20] * frustum[20] + frustum[21] * frustum[21] + frustum[22] * frustum[22]);
        frustum[20] /= norm;
        frustum[21] /= norm;
        frustum[22] /= norm;
        frustum[23] /= norm;

        lower.x = lower.y = lower.z = Double.MAX_VALUE;
        upper.x = upper.y = upper.z = Double.MIN_VALUE;

        calcPoint(0, 8, 16);
        calcPoint(0, 12, 16);
        calcPoint(4, 12, 16);
        calcPoint(4, 8, 16);
        calcPoint(4, 8, 20);
        calcPoint(0, 8, 20);
        calcPoint(0, 12, 20);
        calcPoint(4, 12, 20);
    }

    private void calcPoint(final int a, final int b, final int c) {
        MarathonMath.plane3Cut(frustum, a, b, c, corner);

        if ( corner.x < lower.x )
            lower.x = corner.x;
        if ( corner.x > upper.x )
            upper.x = corner.x;

        if ( corner.y < lower.y )
            lower.y = corner.y;
        if ( corner.y > upper.y )
            upper.y = corner.y;

        if ( corner.z < lower.z )
            lower.z = corner.z;
        if ( corner.z > upper.z )
            upper.z = corner.z;
    }
Offline Spasi
« Reply #5 - Posted 2002-12-08 10:47:50 »

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  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
    public final boolean isVisible(final float x, final float y, final float z) {
        int plane = 0;

        // LEFT PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane++] < 0 )
            return false;

        // RIGHT PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane++] < 0 )
            return false;

        // TOP PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane++] < 0 )
            return false;

        // BOTTOM PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane++] < 0 )
            return false;

        // NEAR PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane++] < 0 )
            return false;

        // FAR PLANE
       if ( frustum[plane++] * x + frustum[plane++] * y + frustum[plane++] * z + frustum[plane] < 0 )
            return false;

        return true;
    }

    public final boolean isVisible(final BoundingSphere sphere) {
        int plane = 0;
        final double invRadius = -sphere.radius;

        // LEFT PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane++] < invRadius ) {
            return false;
        }

        // RIGHT PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane++] < invRadius ) {
            return false;
        }

        // TOP PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane++] < invRadius ) {
            return false;
        }

        // BOTTOM PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane++] < invRadius ) {
            return false;
        }

        // NEAR PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane++] < invRadius ) {
            return false;
        }

        // FAR PLANE
       if ( frustum[plane++] * sphere.x + frustum[plane++] * sphere.y + frustum[plane++] * sphere.z + frustum[plane] < invRadius ) {
            return false;
        }

        return true;
    }

    public final int isVisible(final BoundingBox box) {
        // First check intersection with the frustum's bounding box
       if ( box.upper.x > lower.x && box.lower.x < upper.x && box.upper.y > lower.y && box.lower.y < upper.y && box.upper.z > lower.z && box.lower.z < upper.z ) {
            double x, y, z, w;
            boolean inside = true;
            // For each of the six frustum planes
           for ( int index = 0; index <= 20; index += 4 ) {
                if ( inside ) { // There's still a chance the box is entirely inside the frustum
                   boolean pointIn = false;

                    // Check if any of the boxes' points is visible
                   x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.lower.y;
                    z = frustum[index + 2] * box.lower.z;
                    w = frustum[index + 3];
                    if ( x + y + z + w > 0 )
                        pointIn = true;

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 ) { // The point is visible
                       if ( !pointIn ) { // If previous points were not visible
                           inside = false; // No chance for the box to be entirely inside frustum, so continue
                           continue;
                        }
                    } else { // The point is not visible
                       inside = false;
                        if ( pointIn ) // If there was a previous visible point
                           continue;
                    }

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.upper.y;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.lower.y;
                    z = frustum[index + 2] * box.upper.z;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.upper.y;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 ) {
                        if ( !pointIn ) {
                            inside = false;
                            continue;
                        }
                    } else {
                        inside = false;
                        if ( pointIn )
                            continue;
                    }

                    if ( !pointIn )
                        return 0;
                } else { // If we get here, the box is not entirely inside the frustum
                   // Check if any of the boxes' points is visible
                   x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.lower.y;
                    z = frustum[index + 2] * box.lower.z;
                    w = frustum[index + 3];
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.upper.y;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.lower.y;
                    z = frustum[index + 2] * box.upper.z;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.lower.x;
                    y = frustum[index + 1] * box.upper.y;
                    if ( x + y + z + w > 0 )
                        continue;

                    x = frustum[index] * box.upper.x;
                    if ( x + y + z + w > 0 )
                        continue;

                    return 0;
                }
            }
            return inside ? 2 : 1;
        } else // Does not intersect the frustum's bounding box
           return 0;
    }
}


The method MarathonMath.plane3Cut(...) is this:

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  
public static void plane3Cut(final double[] planes, final int a, final int b, final int c, final Point3d p) {
        System.arraycopy(planes, a, system[0], 0, 3);
        System.arraycopy(planes, b, system[1], 0, 3);
        System.arraycopy(planes, c, system[2], 0, 3);

        system[0][3] = -planes[a + 3];
        system[1][3] = -planes[b + 3];
        system[2][3] = -planes[c + 3];

        double l;
        for ( int i = 0; i < 3; i++ ) {
            for ( int j = 0; j < 3; j++ ) {
                if ( i == j )
                    continue;

                l = system[j][i] / system[i][i];
                for ( int k = i + 1; k < 4; k++ ) {
                    system[j][k] = system[j][k] - l * system[i][k];
                }
            }
        }

        p.x = system[0][3] / system[0][0];
        p.y = system[1][3] / system[1][1];
        p.z = system[2][3] / system[2][2];
}
Offline Spasi
« Reply #6 - Posted 2002-12-08 10:49:21 »

I'll try to explain some of it:

- I call calculate(...) whenever the view changes. I pass MODELVIEW_MATRIX * PROJECT_MATRIX. From this matrix I get the plane equations and normalize the coefficients (it's in the form Ax + By + Cz + D = 0). Normalization is necessary for the BoundingSphere culling to work.

- From there I find the intersections of three planes for each point of the frustum (calling calcPoint with the starting indices of the proper planes as parameters). calcPoint(...) calls plane3cut(...) which returns to <corner> the intersection of the three planes. plane3cut(...) is an implementation of the Gauss-Jordan method, which solves multiple equations with multiple variables. In this case we have three plane equations and want to find the values of three variables (x,y,z). From the points that I find, I just hold the maximum and minimum values, that define a "BoundingBox" for the frustum.

- I believe point and sphere culling are simple. I just find the distances from each plane.

- The BoundingBox culling is a little mess. It's the method I'm using most in my engine, that's why it's more improved than the others. It doesn't return boolean, but rather 0 for not visible at all, 1 for visible and 2 for completely inside the view. I first try an intersection with the frustum bounding box (which saves a lot of calculations) and then I go plane by plane. The mess begins here because I have split the method in two ( if ( inside ) ... else ... ). In the first part there's still a chance for the BoundingBox to be completely inside the frustum and in the second there's not. I did it this way because I believe it saves me a lot of calculation.

- I could make the sphere culling to work as the box (0,1,2), I just don't need it yet. It should be really easy.

That's it! If anyone takes the time to study it, I would really appreciate any comments.

Spasi
Offline Absolution

Senior Newbie




Java games rock!


« Reply #7 - Posted 2002-12-09 22:57:32 »

Quote

heh, view frustum clipping is somewhere on my todo list for my next project, but I most of your code looks good to me.

However, I'm wondering what happens in your code when an object is sufficiantly large enough to cover the entire screen? Individual points may be outside the view, but the object is still visible. I was thinking of doing a 3d version of the Cohen-Sutherland algorithm, which should be nice and fast...


I just implemented frustum clipping in my software renderer and I had the same questions myself when I started.  It actually turned out to be fairly easy using a 3D version of the Sutherland-Hodgman re-entrant polygon clipper.  When clipping against a canonical view volume you can really optimize it.  Culling is next on my list.  I think gamasutra has a nice article on bounding box and sphere frustum culling in their archives.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Pippogeek (35 views)
2014-09-24 16:13:29

Pippogeek (28 views)
2014-09-24 16:12:22

Pippogeek (17 views)
2014-09-24 16:12:06

Grunnt (40 views)
2014-09-23 14:38:19

radar3301 (23 views)
2014-09-21 23:33:17

BurntPizza (58 views)
2014-09-21 02:42:18

BurntPizza (29 views)
2014-09-21 01:30:30

moogie (34 views)
2014-09-21 00:26:15

UprightPath (47 views)
2014-09-20 20:14:06

BurntPizza (51 views)
2014-09-19 03:14:18
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!