TextureRegionFactory from JSON file

  ... the case you feel the need for a new feature or want to submit one.

Re: TextureRegionFactory from JSON file

Postby natypc » Sat Aug 06, 2011 7:45 pm

Maybe you need to call:

getEngine().getTextureManager().loadTextures(mMyTexture);

at the end of onLoadResources() method.
natypc
 
Posts: 18
Joined: Thu Jul 21, 2011 2:04 am

Re: TextureRegionFactory from JSON file

Postby natypc » Sat Aug 06, 2011 8:00 pm

Nicolas Gramlich wrote:Hi,
...
I have an idea to implement rotation completely transparently (subclassing generated TextureRegions), but trimming will be harder to implement transparently and there might be no way around subclassing Sprite, like you did =/
...


Hi Nicolas

What about subclassing Sprite class with an empty VertexBuffer and the trimmed sprite like a child?
...or a sprite using a new VertexBuffer subclass that supports trim offset.
natypc
 
Posts: 18
Joined: Thu Jul 21, 2011 2:04 am

Re: TextureRegionFactory from JSON file

Postby natypc » Sat Aug 06, 2011 11:43 pm

Hi...

A improved version of Texures, Sprites supporting TexturePack application.
New:
- SpritePacked (supporting rotation and trimming) inherits from Sprite class, improving performance.
- TextureRegionFactoryJSON needs JSON-ARRAY file instead JSON format from TexturePacker.
- Support for TexturePacker PVR Textures.

JSON-ARRAY file:
Syntax: [ Download ] [ Hide ]
  1. {"frames": [ 
  2.     "filename": "body.png", 
  3.     "frame": {"x":0,"y":0,"w":159,"h":77}, 
  4.     "rotated": true, 
  5.     "trimmed": false, 
  6.     "spriteSourceSize": {"x":0,"y":0,"w":159,"h":77}, 
  7.     "sourceSize": {"w":159,"h":77} 
  8. }, 
  9.     "filename": "spr1.png", 
  10.     "frame": {"x":0,"y":160,"w":115,"h":31}, 
  11.     "rotated": false, 
  12.     "trimmed": true, 
  13.     "spriteSourceSize": {"x":40,"y":13,"w":115,"h":31}, 
  14.     "sourceSize": {"w":159,"h":77} 
  15. }, 
  16.     "filename": "spr2.png", 
  17.     "frame": {"x":78,"y":114,"w":21,"h":19}, 
  18.     "rotated": true, 
  19.     "trimmed": true, 
  20.     "spriteSourceSize": {"x":0,"y":54,"w":21,"h":19}, 
  21.     "sourceSize": {"w":159,"h":77} 
  22. }], 
  23. "meta": { 
  24.     "app": "http://www.texturepacker.com", 
  25.     "version": "1.0", 
  26.     "image": "menu.pvr", 
  27.     "format": "RGBA8888", 
  28.     "size": {"w":128,"h":256}, 
  29.     "scale": "1" 


The use is very similar to the first version:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. Texture mSprTexture;
  2. SpritePacked spr1;
  3. AnimatedSprite spr2;
  4.  
  5. @Override
  6. public void onLoadResources() {
  7.         ...
  8.         TextureRegionFactoryJSON.setPath("gfx/");
  9.         TextureRegionFactoryJSON texjson = TextureRegionFactoryJSON.createFromAsset(this, "sprites.json");
  10.         Texture mSprTexture = texjson.createTextureFromPVR(TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  11.         ...
  12.         getEngine().getTextureManager().loadTextures(mSprTexture);
  13. }
  14.  
  15. @Override
  16. public Scene onLoadScene() {
  17.         ...
  18.         //spr1
  19.         spr1 = new SpritePacked(0, 0, texjson.get("spr1.png"));
  20.  
  21.         //spr2
  22.         //Note: At this time, Texture Packer application don't
  23.         //support selective sprite for trimming/rotating, so if you
  24.         //create the texture with trimming/rotating enabled then
  25.         //TiledSprite/AnimatedSprite could not work.
  26.         TiledTextureRegion mSpr2TextureRegion = texjson.get("spr2.png").createTiledTextureRegion(4, 1);
  27.         spr2 = new AnimatedSprite(0, 0, mSpr2TextureRegion);
  28.         ...
  29. }
  30.  
Parsed in 0.012 seconds, using GeSHi 1.0.8.4


TextureRegionFactoryJSON:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.anddev.andengine.opengl.texture.atlas.json;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7.  
  8. import org.anddev.andengine.opengl.texture.ITexture;
  9. import org.anddev.andengine.opengl.texture.Texture;
  10. import org.anddev.andengine.opengl.texture.TextureOptions;
  11. import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
  12. import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas.BitmapTextureFormat;
  13. import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
  14. import org.anddev.andengine.opengl.texture.compressed.pvr.PVRTexture;
  15. import org.anddev.andengine.opengl.texture.compressed.pvr.PVRTexture.PVRTextureFormat;
  16. import org.anddev.andengine.opengl.texture.region.TextureRegion;
  17. import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
  18. import org.json.JSONArray;
  19. import org.json.JSONException;
  20. import org.json.JSONObject;
  21.  
  22. import android.content.Context;
  23.  
  24. /**
  25.  * (c) 2011 Natalia Pujol
  26.  *
  27.  * @author Natalia Pujol
  28.  */
  29. public class TextureRegionFactoryJSON {
  30.  
  31.         // ===========================================================
  32.         // Constants
  33.         // ===========================================================
  34.  
  35.         // ===========================================================
  36.         // Fields
  37.         // ===========================================================
  38.  
  39.         private static String mPath = "";
  40.  
  41.         private Context mContext;
  42.        
  43.         private String mTextureName;
  44.         private Texture mTexture;
  45.         private int mFormat;
  46.        
  47.         private int mWidth;
  48.         private int mHeight;
  49.  
  50.         private final static Object TexturePackerFormats[][] = {
  51.                 { "RGBA8888".hashCode(), BitmapTextureFormat.RGBA_8888, PVRTextureFormat.RGBA_8888 },
  52.                 { "RGBA5551".hashCode(), BitmapTextureFormat.RGBA_8888, PVRTextureFormat.RGBA_5551 },
  53.                 { "RGBA5555".hashCode(), BitmapTextureFormat.RGBA_8888, PVRTextureFormat.RGBA_8888 },
  54.                 { "RGBA4444".hashCode(), BitmapTextureFormat.RGBA_4444, PVRTextureFormat.RGBA_4444 },
  55.                 { "RGB565".hashCode(),   BitmapTextureFormat.RGB_565,   PVRTextureFormat.RGB_565 },
  56.                 { "Alpha".hashCode(),    BitmapTextureFormat.A_8,               PVRTextureFormat.A_8 },
  57.         };
  58.        
  59.         private int mNumItems;
  60.         private TextureJSONItem[] mItems;
  61.        
  62.         // ===========================================================
  63.         // Constructors
  64.         // ===========================================================
  65.  
  66.         public TextureRegionFactoryJSON(Context pContext, String pData) {
  67.                 mContext = pContext;
  68.                 JSONObject json = null;
  69.                 try {
  70.                         json = new JSONObject(pData);
  71.                 } catch (JSONException e) {
  72.                         return;
  73.                 }
  74.                 // Meta
  75.                 JSONObject section = json.optJSONObject("meta");
  76.                 mTextureName = section.optString("image");
  77.                 mFormat = section.optString("format").hashCode();
  78.                 section = section.optJSONObject("size");
  79.                 mWidth = section.optInt("w");
  80.                 mHeight = section.optInt("h");
  81.                
  82.                 // Frames (TextureRegions)
  83.                 JSONArray array = json.optJSONArray("frames");
  84.                 mNumItems = array.length();
  85.                 mItems = new TextureJSONItem[mNumItems];
  86.                 for (int i=0; i<mNumItems; i++) {
  87.                         mItems[i] = new TextureJSONItem(array.optJSONObject(i));
  88.                 }
  89.         }
  90.        
  91.         public static TextureRegionFactoryJSON createFromAsset(Context context, String filename) {
  92.                 String data = "";
  93.                 try {
  94.                         InputStreamReader reader = new InputStreamReader(context.getAssets().open(mPath+filename), "UTF-8");
  95.                         BufferedReader br = new BufferedReader(reader);
  96.                         String line;
  97.                         while ((line = br.readLine())!=null) data += line;
  98.                         br.close();
  99.                         reader.close();
  100.                 } catch (Exception e) {}
  101.                 return new TextureRegionFactoryJSON(context, data);
  102.         }
  103.        
  104.         // ===========================================================
  105.         // Getter & Setter
  106.         // ===========================================================
  107.  
  108.         public Texture getTexture() {
  109.                 return mTexture;
  110.         }
  111.        
  112.         public static void setPath(String path) {
  113.                 mPath = path;
  114.         }
  115.        
  116.         public static String getPath() {
  117.                 return mPath;
  118.         }
  119.        
  120.         public int getWidth() {
  121.                 return mWidth;
  122.         }
  123.        
  124.         public int getHeight() {
  125.                 return mHeight;
  126.         }
  127.        
  128.         public int getNumItems() {
  129.                 return mNumItems;
  130.         }
  131.        
  132.         public TextureJSONItem get(int idx) {
  133.                 return mItems[idx];
  134.         }
  135.        
  136.         public TextureJSONItem get(String name) {
  137.                 int id = name.hashCode();
  138.                 for (int i=0; i<mNumItems; i++) {
  139.                         if (mItems[i].nameid==id) {
  140.                                 return mItems[i];
  141.                         }
  142.                 }
  143.                 return null;
  144.         }
  145.        
  146.         // ===========================================================
  147.         // Methods for/from SuperClass/Interfaces
  148.         // ===========================================================
  149.  
  150.         // ===========================================================
  151.         // Private Methods
  152.         // ===========================================================
  153.  
  154.         private BitmapTextureFormat getBitmapTextureFormat() {
  155.                 BitmapTextureFormat textureFormat = BitmapTextureFormat.RGBA_8888;
  156.                 for (int i=0; i<TexturePackerFormats.length; i++) {
  157.                         if (mFormat==(Integer)TexturePackerFormats[i][0]) {
  158.                                 textureFormat = (BitmapTextureFormat)TexturePackerFormats[i][1];
  159.                                 break;
  160.                         }
  161.                 }
  162.                 return textureFormat;
  163.         }
  164.  
  165.         private PVRTextureFormat getPVRTextureFormat() {
  166.                 PVRTextureFormat textureFormat = PVRTextureFormat.RGBA_8888;
  167.                 for (int i=0; i<TexturePackerFormats.length; i++) {
  168.                         if (mFormat==(Integer)TexturePackerFormats[i][0]) {
  169.                                 textureFormat = (PVRTextureFormat)TexturePackerFormats[i][2];
  170.                                 break;
  171.                         }
  172.                 }
  173.                 return textureFormat;
  174.         }
  175.  
  176.         // ===========================================================
  177.         // Methods
  178.         // ===========================================================
  179.  
  180.         public Texture createTextureFromPNG(TextureOptions pTextOpt) {
  181.                 mTexture = new BitmapTextureAtlas(mWidth, mHeight, getBitmapTextureFormat(), pTextOpt);
  182.                 BitmapTextureAtlasTextureRegionFactory.createFromAsset((BitmapTextureAtlas)mTexture, mContext, mPath+mTextureName, 0, 0);
  183.                 return mTexture;
  184.         }
  185.        
  186.         public Texture createTextureFromPVR(TextureOptions pTextOpt) {
  187.                 try {
  188.                         mTexture = new PVRTexture(getPVRTextureFormat(), pTextOpt) {
  189.                                 @Override
  190.                                 protected InputStream getInputStream() throws IOException {
  191.                                         return mContext.getAssets().open(mPath+mTextureName);
  192.                                 }
  193.                         };
  194.                 } catch (Exception e) {
  195.                         e.printStackTrace();
  196.                 }
  197.                 return mTexture;
  198.         }
  199.  
  200.         // ===========================================================
  201.         // Inner and Anonymous Classes
  202.         // ===========================================================
  203.        
  204.         public class TextureJSONItem {
  205.                 public int nameid;
  206.                 public boolean rotated;
  207.                 public boolean trimmed;
  208.                 public int pX;
  209.                 public int pY;
  210.                 public int width;
  211.                 public int height;
  212.                 public int offsetX;
  213.                 public int offsetY;
  214.                 public int trimWidth;
  215.                 public int trimHeight;
  216.                
  217.                 public TextureJSONItem(String name, boolean rot, boolean trim, int x, int y, int w, int h) {
  218.                         nameid = name.hashCode();
  219.                         rotated = rot;
  220.                         trimmed = trim;
  221.                         pX = x;
  222.                         pY = y;
  223.                         width = w;
  224.                         height = h;
  225.                 }
  226.  
  227.                 public TextureJSONItem(String name, JSONObject json) {
  228.                         nameid = name.hashCode();
  229.                         rotated = json.optBoolean("rotated");
  230.                         trimmed = json.optBoolean("trimmed");
  231.                         // sourceSize (not-trimmed sprite size)
  232.                         JSONObject item = json.optJSONObject("sourceSize");
  233.                         width = item.optInt("w");
  234.                         height = item.optInt("h");
  235.                         // frame (size/location in texture of real sprite)
  236.                         item = json.optJSONObject("frame");
  237.                         pX = item.optInt("x");
  238.                         pY = item.optInt("y");
  239.                         trimWidth = item.optInt("w");
  240.                         trimHeight = item.optInt("h");
  241.                         // frame (size/location in not-trimmed sprite size)
  242.                         item = json.optJSONObject("spriteSourceSize");
  243.                         offsetX = item.optInt("x");
  244.                         offsetY = item.optInt("y");
  245.                 }
  246.                
  247.                 public TextureJSONItem(JSONObject json) {
  248.                         this(json.optString("filename"), json);
  249.                 }
  250.  
  251.                 public TextureRegion createTextureRegion() {
  252.                         return new TextureRegion(mTexture, pX, pY, rotated?trimHeight:trimWidth, rotated?trimWidth:trimHeight);
  253.                 }
  254.                
  255.                 public TextureRegion createTextureRegion(ITexture pTexture) {
  256.                         return new TextureRegion(pTexture, pX, pY, rotated?trimHeight:trimWidth, rotated?trimWidth:trimHeight);
  257.                 }
  258.  
  259.                 public TiledTextureRegion createTiledTextureRegion(int pTileColums, int pTileRows) {
  260.                         return new TiledTextureRegion(mTexture, pX, pY, trimWidth, trimHeight, pTileColums, pTileRows);
  261.                 }
  262.  
  263.                 public TiledTextureRegion createTiledTextureRegion(ITexture pTexture, int pTileColums, int pTileRows) {
  264.                         return new TiledTextureRegion(pTexture, pX, pY, trimWidth, trimHeight, pTileColums, pTileRows);
  265.                 }
  266.                
  267.         }
  268.        
  269. }
  270.  
Parsed in 0.025 seconds, using GeSHi 1.0.8.4


RectanglePackedVertexBuffer:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.anddev.andengine.opengl.vertex;
  2.  
  3. import org.anddev.andengine.opengl.util.FastFloatBuffer;
  4. import org.anddev.andengine.opengl.vertex.RectangleVertexBuffer;
  5.  
  6. /**
  7.  * (c) 2011 Natalia Pujol
  8.  *
  9.  * @author Natalia Pujol
  10.  */
  11. public class RectanglePackedVertexBuffer extends RectangleVertexBuffer {
  12.         // ===========================================================
  13.         // Constants
  14.         // ===========================================================
  15.  
  16.         // ===========================================================
  17.         // Fields
  18.         // ===========================================================
  19.  
  20.         protected float mOffX = 0f;
  21.         protected float mOffY = 0f;
  22.         protected boolean mRotated = false;
  23.        
  24.         // ===========================================================
  25.         // Constructors
  26.         // ===========================================================
  27.  
  28.         public RectanglePackedVertexBuffer(final float pOffX, final float pOffY, boolean pRotated, final int pDrawType, final boolean pManaged) {
  29.                 super(pDrawType, pManaged);
  30.                 mOffX = pOffX;
  31.                 mOffY = pOffY;
  32.                 mRotated = pRotated;
  33.         }
  34.  
  35.         // ===========================================================
  36.         // Getter & Setter
  37.         // ===========================================================
  38.  
  39.         public void setOffsetX(float pOffX) {
  40.                 mOffX = pOffX;
  41.         }
  42.        
  43.         public void setOffsetY(float pOffY) {
  44.                 mOffY = pOffY;
  45.         }
  46.        
  47.         // ===========================================================
  48.         // Methods for/from SuperClass/Interfaces
  49.         // ===========================================================
  50.  
  51.         // ===========================================================
  52.         // Methods
  53.         // ===========================================================
  54.  
  55.         @Override
  56.         public synchronized void update(final float pWidth, final float pHeight) {
  57.                
  58.                 final int[] bufferData = this.mBufferData;
  59.  
  60.                 if (!mRotated) {
  61.                         final int x = Float.floatToRawIntBits(mOffX);
  62.                         final int y = Float.floatToRawIntBits(mOffY);
  63.                         final int x2 = Float.floatToRawIntBits(mOffX+pWidth);
  64.                         final int y2 = Float.floatToRawIntBits(mOffY+pHeight);
  65.  
  66.                         bufferData[0] = x;
  67.                         bufferData[1] = y;
  68.  
  69.                         bufferData[2] = x;
  70.                         bufferData[3] = y2;
  71.  
  72.                         bufferData[4] = x2;
  73.                         bufferData[5] = y;
  74.  
  75.                         bufferData[6] = x2;
  76.                         bufferData[7] = y2;
  77.                 } else {
  78.                         final int x = Float.floatToRawIntBits(mOffX);
  79.                         final int y = Float.floatToRawIntBits(mOffY+pHeight);
  80.                         final int x2 = Float.floatToRawIntBits(mOffX+pWidth);
  81.                         final int y2 = Float.floatToRawIntBits(mOffY);
  82.  
  83.                         bufferData[0] = x;
  84.                         bufferData[1] = y;
  85.  
  86.                         bufferData[2] = x2;
  87.                         bufferData[3] = y;
  88.  
  89.                         bufferData[4] = x;
  90.                         bufferData[5] = y2;
  91.  
  92.                         bufferData[6] = x2;
  93.                         bufferData[7] = y2;
  94.                 }
  95.  
  96.                 final FastFloatBuffer buffer = this.getFloatBuffer();
  97.                 buffer.position(0);
  98.                 buffer.put(bufferData);
  99.                 buffer.position(0);
  100.  
  101.                 super.setHardwareBufferNeedsUpdate();
  102.         }
  103.  
  104.         // ===========================================================
  105.         // Inner and Anonymous Classes
  106.         // ===========================================================
  107. }
  108.  
Parsed in 0.015 seconds, using GeSHi 1.0.8.4


SpritePacked:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.anddev.andengine.entity.sprite;
  2.  
  3. import javax.microedition.khronos.opengles.GL11;
  4.  
  5. import org.anddev.andengine.opengl.texture.ITexture;
  6. import org.anddev.andengine.opengl.texture.atlas.json.TextureRegionFactoryJSON.TextureJSONItem;
  7. import org.anddev.andengine.opengl.texture.region.TextureRegion;
  8. import org.anddev.andengine.opengl.vertex.RectanglePackedVertexBuffer;
  9.  
  10. public class SpritePacked extends Sprite {
  11.  
  12.         // ===========================================================
  13.         // Constants
  14.         // ===========================================================
  15.  
  16.         // ===========================================================
  17.         // Fields
  18.         // ===========================================================
  19.        
  20.         protected boolean regionRotated;
  21.         protected int regionOffsetX;
  22.         protected int regionOffsetY;
  23.         protected int regionWidth;
  24.         protected int regionHeight;
  25.        
  26.         // ===========================================================
  27.         // Constructors
  28.         // ===========================================================
  29.  
  30.         public SpritePacked(float pX, float pY, TextureJSONItem json) {
  31.                 this(pX, pY, json.width, json.height, json.offsetX, json.offsetY, json.trimWidth, json.trimHeight, json.rotated, json.createTextureRegion());
  32.         }
  33.        
  34.         public SpritePacked(float pX, float pY, TextureJSONItem json, ITexture pTexture) {
  35.                 this(pX, pY, json.width, json.height, json.offsetX, json.offsetY, json.trimWidth, json.trimHeight, json.rotated, json.createTextureRegion(pTexture));
  36.         }
  37.        
  38.         public SpritePacked(float pX, float pY, float sprWidth, float sprHeight, int regionOffsetX, int regionOffsetY, int regionWidth, int regionHeight, boolean regionRotated, TextureRegion pTexReg) {
  39.                 super(pX, pY, sprWidth, sprHeight, pTexReg, new RectanglePackedVertexBuffer(regionOffsetX, regionOffsetY, regionRotated, GL11.GL_STATIC_DRAW, true));
  40.                 this.regionRotated = regionRotated;
  41.                 this.regionOffsetX = regionOffsetX;
  42.                 this.regionOffsetY = regionOffsetY;
  43.                 this.regionWidth = regionWidth;
  44.                 this.regionHeight = regionHeight;
  45.                 this.updateVertexBuffer();
  46.         }
  47.        
  48.         // ===========================================================
  49.         // Getter & Setter
  50.         // ===========================================================
  51.  
  52.         public void setFlippedHorizontal(boolean pFlippedHorizontal) {
  53.                 if (regionRotated) {
  54.                         super.setFlippedVertical(pFlippedHorizontal);
  55.                 } else {
  56.                         super.setFlippedHorizontal(pFlippedHorizontal);
  57.                 }
  58.                 if (pFlippedHorizontal) {
  59.                         ((RectanglePackedVertexBuffer)getVertexBuffer()).setOffsetX(mWidth - regionWidth - regionOffsetX);
  60.                 } else {
  61.                         ((RectanglePackedVertexBuffer)getVertexBuffer()).setOffsetX(regionOffsetX);
  62.                 }
  63.                 updateVertexBuffer();
  64.         }
  65.  
  66.         public void setFlippedVertical(boolean pFlippedVertical) {
  67.                 if (regionRotated) {
  68.                         super.setFlippedHorizontal(pFlippedVertical);
  69.                 } else {
  70.                         super.setFlippedVertical(pFlippedVertical);
  71.                 }
  72.                 if (pFlippedVertical) {
  73.                         ((RectanglePackedVertexBuffer)getVertexBuffer()).setOffsetY(mHeight - regionHeight - regionOffsetY);
  74.                 } else {
  75.                         ((RectanglePackedVertexBuffer)getVertexBuffer()).setOffsetY(regionOffsetY);
  76.                 }
  77.                 updateVertexBuffer();
  78.         }
  79.  
  80.         // ===========================================================
  81.         // Methods for/from SuperClass/Interfaces
  82.         // ===========================================================
  83.  
  84.         @Override
  85.         protected void onUpdateVertexBuffer(){
  86.                 this.getVertexBuffer().update(regionWidth, regionHeight);
  87.         }
  88.        
  89.         // ===========================================================
  90.         // Private Methods
  91.         // ===========================================================
  92.  
  93.         // ===========================================================
  94.         // Methods
  95.         // ===========================================================
  96.  
  97.         // ===========================================================
  98.         // Inner and Anonymous Classes
  99.         // ===========================================================
  100.  
  101. }
  102.  
Parsed in 0.016 seconds, using GeSHi 1.0.8.4
natypc
 
Posts: 18
Joined: Thu Jul 21, 2011 2:04 am

Re: TextureRegionFactory from JSON file

Postby davifo » Wed May 30, 2012 10:58 pm

Very useful!! The Andengine native texturepacker not have the option to trim .

But is outdated, would help upgrade??(GLESS2) THANK YOU!
davifo
 
Posts: 31
Joined: Fri Apr 20, 2012 5:05 pm
Location: Brazil

Re: TextureRegionFactory from JSON file

Postby diplomavs » Fri Mar 07, 2014 2:40 pm

Hw can i achieve this with GLES2? I'm referring to adding offset to region ...
diplomavs
 
Posts: 2
Joined: Thu Mar 06, 2014 1:17 am

Previous

Return to Features

Who is online

Users browsing this forum: No registered users and 4 guests