Oolite Bulletins

For information and discussion about Oolite.
It is currently Sun Dec 17, 2017 11:32 pm

All times are UTC




Post new topic  Reply to topic  [ 411 posts ]  Go to page Previous 13 4 5 6 728 Next
Author Message
 Post subject:
PostPosted: Tue Oct 09, 2007 7:27 pm 
Offline
---- E L I T E ----
---- E L I T E ----
User avatar

Joined: Fri Mar 30, 2007 8:32 am
Posts: 1474
Location: Witchspace
Quote:
How do you calculate these coordinates actually?

I have been surfing through wiki, wikipedia and openGL tutorials, but I'm not finding much sense out of it all. :oops:

DLing rendermonkey, maybe that will do it for me. :)


I guess decals would only be practical for vehicles that all use the same texture/model and are expected to have decals? Racers, police etc.

As each different model would need to have a different decal-shader...in order to apply the effect on the right place on the model... ?

Just coming to grips with the texcoord calculus now.
I can see how you could use the method you used in the animation-example to apply a given frame on a model.

Not sure how exactly tex1 is mapped on tex0 (which is AFAIK the model-base-texture.) I'll figure it out...someday
i´m not sure if this is what you mean... but here goes..

Take A Imaginary Image with Width and Height of 256 by 256....

X range 0-255 (remember we count in ZERO)
Y range 0-255

coordinates in paint program x=255,y=255 would be in OpenGL terms

X 255/255 = 1.0 = OpenGL coordinate
y 255/255 = 1.0 = OpenGL coordinate

likewise coordinates in paint program x=127, y=127

X 127/255 = 0,498039 = OpenGL coordinate
Y 127/255 = 0,498039 = OpenGL coordinate

yes you could use 0.5 instead... ;-).

The reason for this is that we can change the image size to what we want, which is usefull for example Mipmapping,
without actually having to write code to recalculate the Actual coordinates...

_________________
Bounty Scanner
Number 935


Top
   
 Post subject:
PostPosted: Tue Oct 09, 2007 7:59 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Ah..yes. I think so.

_________________
Riding the Rocket!


Top
   
 Post subject:
PostPosted: Tue Oct 09, 2007 9:58 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
Quote:
I guess decals would only be practical for vehicles that all use the same texture/model and are expected to have decals? Racers, police etc.

As each different model would need to have a different decal-shader...in order to apply the effect on the right place on the model... ?
...
Not sure how exactly tex1 is mapped on tex0 (which is AFAIK the model-base-texture.) I'll figure it out...someday
Right. Oolite’s model format only allows for one set of texture co-ordinates, so if you want to work with multiple textures you need to work out the relationships between the textures in the GLSL code. For things like specular maps and glow maps, you generally want all the textures to have the same co-ordinates anyway. For a decal, you’ll need to apply a transformation to the texture co-ordinates.

For a concrete example, let’s take a Cobra 3:
Image

and apply this highly original decal to the bottom:
Image

First, lets have the base texture with the decal composited on top:
Code:
uniform sampler2D uBaseTexture;
uniform sampler2D uDecalTexture;


void main()
{
    vec2 baseTexCoord = gl_TexCoord[0].st;
    
    // Calculate decal texture co-ordinates.
    vec2 decalTexCoord = baseTexCoord;
    
    // Get texture values.
    vec4 baseTex = texture2D(uBaseTexture, baseTexCoord);
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
    
    // Composite decal over base.
    float alpha = decalTex.a;
    vec4 color = baseTex * (1.0 - alpha) + decalTex * alpha;
    
    // Insert lighting calculations here
    
    gl_FragColor = color;
}
Next, we need to scale it down.
Code:
const float kDecalSize = 1.0/9.0;
...
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord /= kDecalSize;
In GLSLEditorSample, this covers the base with a 9x9 grid of decals. This wouldn’t happen in Oolite, because it defaults to using the “clamp to edge” texture mode rather than “repeat”. In principle, you could make do with using a decal texture whose edge is fully transparent. However, this won’t work with mip-mapping and is generally not robust, so I’ll clamp the texture explicitly in the code:
Code:
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
	decalTex *= step(0.0, decalTexCoord.s) *
	            step(0.0, decalTexCoord.t) *
	            step(-1.0, -decalTexCoord.s) *
	            step(-1.0, -decalTexCoord.t);
Digression: in a traditional programming language, this would probably be written if (decalTexCoord.s < 0.0 || ...) decalTex = vec4(0.0);. However, conditional expressions are generally to be avoided in GLSL, even though they work fine in this case. Getting used to using things like step() instead where possible is a good idea. step(edge, x) returns 0.0 if x < edge, otherwise 1.0.

It is especially important to avoid writing code like:
Code:
if (/* coords are inside texture */)
{
    // do alpha blending here
}
else
{
    // just use the base texture
}
This type of code will be handled by first doing the body of the if, then the body of the else, then blending the results together based on the condition – in other words, instead of increasing efficiency by reducing the work for fragments outside the decal, it increases work for all fragments. This is a necessary consequence of the way graphics hardware works, by performing the same action in parallel on lots of fragments. Parallelism is also a major part of what makes graphics hardware fast, so it’s not really avoidable. </digression>

Anyway, we now have a clipped decal in the top-left corner of the base texture. All that remains is to move it to the right place, which is a simple matter of adding the required offset to the texture co-ordinates… or rather, of subtracting it. (If you want the decal’s left edge to be at 0.2, you want decalTexCoord.s to be 0 when baseTex.s is 0.2, so you subtract 0.2.)

This can be done either before or after scaling the texture co-ordinates; I chose before, so we’re working in units of the base texture co-ordinate system. We want to offset the s co-ordinate some amount to the right, and we want the t co-ordinate to be right in the middle. To get it in the middle, we use an offset of 0.5 minus half the height of the decal.
Code:
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS, 0.5 - (0.5 * kDecalSize));
    decalTexCoord *= kDecalSize;
Through experimentation I’ve determined that a kDecalS value of about 0.35 works well in this case.

Complete shader:
Code:
uniform sampler2D uBaseTexture;
uniform sampler2D uDecalTexture;

const float kDecalSize = 1.0/9.0;
const float kDecalS = 0.35;


void main()
{
    vec2 baseTexCoord = gl_TexCoord[0].st;
    
    // Calculate decal texture co-ordinates.
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS, 0.5 - (0.5 * kDecalSize));
    decalTexCoord /= kDecalSize;
    
    // Get texture values.
    vec4 baseTex = texture2D(uBaseTexture, baseTexCoord);
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
    decalTex *= step(0.0, decalTexCoord.s) *
                step(0.0, decalTexCoord.t) *
                step(-1.0, -decalTexCoord.s) *
                step(-1.0, -decalTexCoord.t);
    
    // Composite decal over base.
    float alpha = decalTex.a;
    vec4 color = baseTex * (1.0 - alpha) + decalTex * alpha;
    
    // Insert lighting calculations here
    
    gl_FragColor = color;
}
Output:
Image

This shader allows you to add a static decal that’s more detailed than the base texture, but generally you’ll want to select a decal from a set. The technique used in the animation shader should be easy to adapt. Instead of universalTime, bind the controlling uniform to entityPersonality to select a random decal for each ship, or use a Behemoth-like technique with multiple shipdata.plist entries with different constant uniforms. (To specify a constant float uniform, use { uDecalOffset = { type = "float"; value = "0.25"; }} in the uniforms list.)

Oh, yeah… on the topic of the Behemoth technique, I should probably point out that you can replace textures on a model using the materials dictionary, without requiring shaders at all. But you can’t select a part of a texture this way, so the name signs would have to be put in separate files.

_________________
E-mail: jens@oolite.org


Last edited by JensAyton on Wed Oct 22, 2008 9:11 pm, edited 3 times in total.

Top
   
 Post subject:
PostPosted: Tue Oct 09, 2007 10:00 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
On an unrelated note, the animation example contains a texture that’s 256 by 1280 pixels. This is a Naughty, Bad, Non-power-of-two size which will be rescaled when it is loaded. In this one instance, do not follow my example. (256 by 1024 would be fine.)

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Tue Oct 09, 2007 10:16 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
That is a very nice tutorial/explanation Ahruman, thanks.

Also I saw that entity_personality used before in a shader, is this a random seed number chosen on spawning (like number of seconds on clock)?

'vec' could you explain this term to a fool (me).


---
I've been trying to combine the animation shader on top of a diffuse layer all evening. (without the cutt-off <0.5 colour code)

Probably when I have digested your previous tutorial I'll have more success. ;)

_________________
Riding the Rocket!


Top
   
 Post subject:
PostPosted: Tue Oct 09, 2007 10:56 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
Quote:
That is a very nice tutorial/explanation Ahruman, thanks.

Also I saw that entity_personality used before in a shader, is this a random seed number chosen on spawning (like number of seconds on clock)?
It’s a value that’s chosen randomly when an entity is created.
Quote:
'vec' could you explain this term to a fool (me).
“Vec” is short for vector. A vector in GLSL is a series of 2, 3 or 4 numbers (vec2, vec3 and vec4 respectively). The numbers are identified by letter. There are three sets of letters that can be used, with conventional meanings:
  • x, y, z and w: spacial co-ordinates (i.e., points) and normals.
  • r, g, b and a: colour components.
  • s, t, p and q: texture co-ordinates. Since only 2D textures are used in Oolite, s and t are the interesting ones.
To access a component, you use a dot and the name of a component: myVec.r (which is equivalent to myVec.x or myVec.s). A single component accessed this way is a float, that is, a number with a fractional part. You can also access more than one component using component names from the same set; for instance, myVec.rgb extracts a vec3, and works on either a vec3 or a vec4, but not a vec2 since it doesn’t have a b component. In this usage, components can be specified in any order, for instance: myVec.zx. Components can be rearranged and copied this way: myVec.spq = myVec.rrs. This is called swizzling.

Vectors can be constructed from a list of integers or vectors using syntax like:
Code:
vec2 a = vec2(1.0, 2.0); // [1.0, 2.0]
vec3 b = vec3(3.0); // [3.0, 3.0, 3.0]
vec4 c = vec4(a, 4.0, 5.0); // [1.0, 2.0, 4.0, 5.0]
The arithmetic operators +, -, * and / can be applied to vectors of the same size, in which case they work on an element-by-element basis. The can also be applied with a float and a vector, in which case the float acts like an appropriately-sized vector:
Code:
vec4 d = c + 2.0; // [3.0, 4.0, 6.0, 7.0]
For more information, written even more pedantically and technically than my stuff, see the language specification, chapter 5. Like the JavaScript class references on the wiki, it’s not a great teacher, but it’s the right place for questions like “what does smoothstep(0.3, 0.7, x) mean?”. The GLSL Quick Reference Guide is also useful, mostly for questions like “how do I spell gl_ModelViewProjectionMatrixInverseTranspose?” or (more commonly) “what order are the parameters to smoothstep() supposed to be in?”
Quote:
I've been trying to combine the animation shader on top of a diffuse layer all evening. (without the cutt-off <0.5 colour code)
The very first code block from the decal example simply composites one texture on top of another using the alpha channel of the top image.

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Wed Oct 10, 2007 7:23 am 
Offline
Grand High Clock-Tower Poobah
Grand High Clock-Tower Poobah
User avatar

Joined: Tue Sep 19, 2006 1:10 pm
Posts: 2312
Location: Anywhere I can sell Trumbles.....
Would this method solve the idea regarding the Dream Team Anaconda with randomly determined haulage company skins/decals?

Captain Hesperus

_________________
The truth, revealed!!
Image


Top
   
 Post subject:
PostPosted: Wed Oct 10, 2007 9:00 am 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
For decals, yes. For entire skins, multiple shipdata.plist entries would be simpler (and work without shader support).

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Wed Oct 10, 2007 2:40 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Another one:
could one in theory give a subentity a seperate set of shaders, or should all used shaders be defined in the main-entity?

_________________
Riding the Rocket!


Top
   
 Post subject:
PostPosted: Wed Oct 10, 2007 2:54 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
Quote:
could one in theory give a subentity a seperate set of shaders
Yes, trivially. See the Shady Griff Krait.

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Wed Oct 10, 2007 3:21 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Oh, right. added link to first post.

Ooh! just had a small idea that could have big affect.

-use the ship_personality number to choose a random colour on a colourgradient and add as a lowalpha additive shader.
This would give each ship it's own 'personality'.

(Maybe using a mask would be better, but much more laboursome.)

edit:
I could use this idea to colour 'default racers' in individual colours.
Combine this with a roles-system (racer-1, racer-2, racer-n, etc), each distinct model would get all available roles.
Populating race: Each role-number needs to be called for each contestant once and uniquely.
[variable] contestants : while [variable]>0: addShipAtPrecicely: role-[variable], decrement: variable. (I know: not working code)
The shader could refer to the 'active role' (i.e. the one used to call it) of the entity to select 'number decal' etc?
identity, shipID etc could maybe be used for seperate decal-shaders for comercial adds etc.

This way, I'd need to use only one shipdata and one model.file and one diffuse tex in greyscale.
Plus all the decals artwork and one big-ass complicated shader-complex. Complex to me in any case. ;)
Sorry this might belong more in the scripters' cove. :roll:

_________________
Riding the Rocket!


Top
   
PostPosted: Wed Oct 10, 2007 11:07 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
Extending to multiple decals
First, we need a uniform variable to select the decal, and a constant specifying the number of decals in the decal texture (equivalent to frames in the animation):
Code:
uniform float uDecalSelect;
...
const float kDecalCount = 4.0;
Oh yeah, a decal texture might be useful, too. I decided to lay it out horizontally this time, for variation. Oh, and also because it’s easier to work with wide pictures than tall ones on a landscape-mode screen.
Image
The code changes turned out to be simpler than I thought. We want to stretch the texture horizontally by a factor of
Code:
kDecalCount
. We’re already compressing the texture to shrink it to scale, so we combine this into one operation.
Code:
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS, 0.5 - (0.5 / kDecalSize));
    decalTexCoord *= vec2(kDecalSize / kDecalCount, kDecalSize);
All that’s left is to select the “frame” from the decal texture we wish to use, in the same way as with the animation:
Code:
    // Select the desired decal from the set.
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount;
    decalTexCoord.s += offset;
and modify the clipping to exclude the other frames:
Code:
    decalTex *= step(offset, decalTexCoord.s) *
                step(0.0, decalTexCoord.t) *
                step(-1.0 / kDecalCount - offset, -decalTexCoord.s) *
                step(-1.0, -decalTexCoord.t);
This will work as-is for 0 ≤ uDecalSelect < 1. However, entityPersonality can be exactly 1, in which case no decal will be drawn. This can be rectified by specifying the repeat_s attribute in the texture specifier in the shaders dictionary. If repeat_s is true, an uDecalSelect of 1 will work like an uDecalSelect of 0, showing the leftmost decal. In fact, any value will work; if you bind uDecalSelect to universalTime, it will cycle through the decals once per second.

Complete code:
Code:
uniform sampler2D uBaseTexture;
uniform sampler2D uDecalTexture;
uniform float uDecalSelect;

const float kDecalSize = 9.0;
const float kDecalS = 0.35;
const float kDecalCount = 4.0;


vec4 diffuseColor()
{
    vec2 baseTexCoord = gl_TexCoord[0].st;
    
    // Calculate decal texture co-ordinates.
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS, 0.5 - (0.5 / kDecalSize));
    decalTexCoord *= vec2(kDecalSize / kDecalCount, kDecalSize);
    
    // Select the desired decal from the set.
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount;
    decalTexCoord.s += offset;
    
    // Get texture values.
    vec4 baseTex = texture2D(uBaseTexture, baseTexCoord);
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
    decalTex *= step(offset, decalTexCoord.s) *
                step(0.0, decalTexCoord.t) *
                step(-1.0 / kDecalCount - offset, -decalTexCoord.s) *
                step(-1.0, -decalTexCoord.t);
    
    // Composite decal over base.
    float alpha = decalTex.a;
    return baseTex * (1.0 - alpha) + decalTex * alpha;
}


void main()
{
    vec4 color = diffuseColor();
    // Insert lighting calculations here
    
    gl_FragColor = color;
}

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Thu Oct 11, 2007 3:14 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
okay. great progress. :D

Are we going to standardise decals?
if so
we need a specifier for orientation (rotation) of their projection.
Not all ships have the same texture maps.

(Unless you plan on having seperate sets for each ship that may use the same decal.)

_________________
Riding the Rocket!


Top
   
 Post subject:
PostPosted: Thu Oct 11, 2007 3:57 pm 
Offline
Grand Admiral Emeritus
Grand Admiral Emeritus
User avatar

Joined: Sat Apr 02, 2005 2:43 pm
Posts: 6657
Location: Sweden
“Unless I plan…?” I’m not planning anything here.

In the first version, the decal is rotated around its top corner by kOrientation radians clockwise. In the second version, it is rotated around its own centre, at the cost of an additional two-component addition.
Code:
uniform sampler2D uBaseTexture;
uniform sampler2D uDecalTexture;
uniform float uDecalSelect;

const float kDecalSize = 9.0;
const float kDecalS = 0.35;
const float kDecalCount = 4.0;
const float kOrientation = 0.7853981634; // pi / 4


vec4 diffuseColor()
{
    vec2 baseTexCoord = gl_TexCoord[0].st;
    

    // Calculate decal texture co-ordinates.
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS, 0.5 - (0.5 / kDecalSize));
    float s = sin(kOrientation);
    float c = cos(kOrientation);
    decalTexCoord *= mat2(c, s, -s, c);
    decalTexCoord *= vec2(kDecalSize / kDecalCount, kDecalSize);
    
    // Select the desired decal from the set.
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount;
    decalTexCoord.s += offset;
    
    // Get texture values.
    vec4 baseTex = texture2D(uBaseTexture, baseTexCoord);
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
    decalTex *= step(offset, decalTexCoord.s) *
                step(0.0, decalTexCoord.t) *
                step(-1.0 / kDecalCount - offset, -decalTexCoord.s) *
                step(-1.0, -decalTexCoord.t);
    
    // Composite decal over base.
    float alpha = decalTex.a;
    return baseTex * (1.0 - alpha) + decalTex * alpha;
}


void main()
{
    vec4 color = diffuseColor();
    // Insert lighting calculations here
    
    gl_FragColor = color;
}
Code:
uniform sampler2D uBaseTexture;
uniform sampler2D uDecalTexture;
uniform float uDecalSelect;

const float kDecalSize = 9.0;
const float kDecalS = 0.35;
const float kDecalCount = 4.0;
const float kOrientation = 0.7853981634; // pi / 4


vec4 diffuseColor()
{
    vec2 baseTexCoord = gl_TexCoord[0].st;
    

    // Calculate decal texture co-ordinates.
    vec2 decalTexCoord = baseTexCoord;
    decalTexCoord -= vec2(kDecalS + (0.5 / kDecalSize), 0.5);
    float s = sin(kOrientation);
    float c = cos(kOrientation);
    decalTexCoord *= mat2(c, s, -s, c);
    decalTexCoord += vec2(0.5 / kDecalSize);
    decalTexCoord *= vec2(kDecalSize / kDecalCount, kDecalSize);
    
    // Select the desired decal from the set.
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount;
    decalTexCoord.s += offset;
    
    // Get texture values.
    vec4 baseTex = texture2D(uBaseTexture, baseTexCoord);
    vec4 decalTex = texture2D(uDecalTexture, decalTexCoord);
    decalTex *= step(offset, decalTexCoord.s) *
                step(0.0, decalTexCoord.t) *
                step(-1.0 / kDecalCount - offset, -decalTexCoord.s) *
                step(-1.0, -decalTexCoord.t);
    
    // Composite decal over base.
    float alpha = decalTex.a;
    return baseTex * (1.0 - alpha) + decalTex * alpha;
}


void main()
{
    vec4 color = diffuseColor();
    // Insert lighting calculations here
    
    gl_FragColor = color;
}
Oh, you want to understand how it works? Well, study some linear algebra, then. :-)

_________________
E-mail: jens@oolite.org


Top
   
 Post subject:
PostPosted: Thu Oct 11, 2007 4:41 pm 
Offline
Dangerous Subversive Element
Dangerous Subversive Element
User avatar

Joined: Tue Jun 07, 2005 7:32 pm
Posts: 1876
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
I meant 'you' in the general sense, as in 'one must do it'. ;)

Algebra is not really the big problem here
(...although I must admit it's not my strongest talent.)
'tis more that I just do not know the methods, command and conventions in GL (or JS) yet.

Sorry if I sound ungratefull. :p

present for Griff: ;)

TheWormingHole

_________________
Riding the Rocket!


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 411 posts ]  Go to page Previous 13 4 5 6 728 Next

All times are UTC


Who is online

Users browsing this forum: Baidu [Spider], Google [Bot] and 14 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
cron
Powered by phpBB® Forum Software © phpBB Limited