Jump to content

Recommended Posts

Posted

Hello

 

I have blocks with a texture composed of a background and a foreground layer, both of which can be of any color. The colors are static for each block, but they are "discovered" on initialisation (i.e in preInit()), and unknown at compile time.

 

I have been told this could be achieved with either the "tintindex" field of the json models, or with a custom baked model, however, I can't figure out how to use either of those :(

 

I also thought I could generate and save the textures to disk on the first run, but I'm not too sure how to integrate the external folder in the json models

 

Posted

No answer in a while, so I guess I'll clarify the question: what would be the best (easiest) way to do this, and how would I go about implementing it?

 

Thanks

Posted

Hi

 

Some examples of these

Some info on the tint index

http://greyminecraftcoder.blogspot.com.au/2014/12/block-models-18.html

using Block.colorMultiplier()

 

Alternatively, this tutorial project shows how you can bake your own models with custom faces / face colours

https://github.com/TheGreyGhost/MinecraftByExample

(see MBE04, MBE05 for block model generation and MBE15 for how to generate quads)

 

-TGG

Posted

Thanks, I had seen those already (and the example on the TESR has been quite useful), but I don't seem to be able to understand how to put it together to do what I want...

 

My biggest issue is probably because my models don't need to be "smart" (they're static for each block), they just need to be generated on startup

Posted

Hi

 

The key bits to put together are:

- the loading methods in MBE04 or MBE05 which are used to insert your model into the registry.  Your model can be just an IBakedModel, it doesn't have to be smart.

- your IBakedModel class should return the custom quads for your model in getGeneralQuads in MBE15.

 

-TGG

 

 

Posted

Hi

 

If you're using arbitrary colours, then yes you will.  If you want, you could copy the face information from the loaded json model and overwrite just the colour ints in the int[], leaving the x,y,z, u,v information unchanged.

 

-TGG

 

  /**
   * Converts the vertex information to the int array format expected by BakedQuads.
   * @param x x coordinate
   * @param y y coordinate
   * @param z z coordinate
   * @param color RGBA colour format - white for no effect, non-white to tint the face with the specified colour
   * @param texture the texture to use for the face
   * @param u u-coordinate of the texture (0 - 16) corresponding to [x,y,z]
   * @param v v-coordinate of the texture (0 - 16) corresponding to [x,y,z]
   * @return
   */
  private int[] vertexToInts(float x, float y, float z, int color, TextureAtlasSprite texture, float u, float v)
  {
    return new int[] {
            Float.floatToRawIntBits(x),
            Float.floatToRawIntBits(y),
            Float.floatToRawIntBits(z),
            color,
            Float.floatToRawIntBits(texture.getInterpolatedU(u)),
            Float.floatToRawIntBits(texture.getInterpolatedV(v)),
            0
    };
  }

Posted

Also you can use some IModel implementation for that.

It allows to render multiple IBakedModel-form models.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

  • 2 weeks later...
Posted

I've been messing around with this again, but I still can't get my blocks to render...

 

The code I've tried most recently is in this commit (note that i'm trying to get them to render as dirt to test that stuff is working)

Posted

1. You should wrap the existing model.

  First just set all methods return the result of same method in existing model.

  Then change some methods in your needs

2. You can change the tint value when quads are baked.

  You can first get the quads from existing model's getGeneralQuads, and change the tint, after that return that in your own getGeneralQuads.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Posted

thing is, I don't have an existing modelfor this block...

 

You know what TGG said earlier about copying the code from the .JSON to generate the quads yourself? First, make a regular block model in .JSON and register it normally. Then, wrap it in the IBakedModel type to change the color or do whatever you need. Do this in the ModelBakeEvent.

Development of Plugins [2012 - 2014] Development of Mods [2012 - Current]

Posted

So I managed to get the models to load (they render as dirt when returning the sane thing as dirt in all methods), but when I try to make the BakedQuads I get an IndexOutOfBoundsException

 

Model class:

/** Runes of Wizardry Mod for Minecraft
*  Licensed under the GNU GPL version 3
*  
*  this file was created by Xilef11 on 2015-09-06
*/
package com.zpig333.runesofwizardry.client.model;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javafx.scene.shape.VertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.FaceBakery;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;

import org.apache.commons.lang3.ArrayUtils;

import com.zpig333.runesofwizardry.api.IDustStorageBlock;
import com.zpig333.runesofwizardry.client.TextureStitchEventHandler;
import com.zpig333.runesofwizardry.core.References;
import com.zpig333.runesofwizardry.core.WizardryLogger;

/**
* @author Xilef11
*
*/
public class ModelDustStorage implements IBakedModel {
private static Map<String,ModelResourceLocation> resourceMap = new HashMap<String, ModelResourceLocation>();
private IDustStorageBlock block;
private int meta;
private int bgColor,fgColor;
// create a tag (ModelResourceLocation) for our model.
  public final ModelResourceLocation modelResourceLocation;

  public ModelDustStorage(IDustStorageBlock block, int meta) {
	WizardryLogger.logInfo("Creating model for block: "+block.getName()+" "+meta);
	this.block=block;
	this.meta=meta;
	this.modelResourceLocation = new ModelResourceLocation(getModelResourceLocationPath(block,meta));
	this.bgColor = block.getIDust().getPrimaryColor(new ItemStack(block.getIDust(),1,meta));
	this.fgColor = block.getIDust().getSecondaryColor(new ItemStack(block.getIDust(),1,meta));
}
public static String getModelResourceLocationPath(IDustStorageBlock block, int meta){
	return References.texture_path+block.getName()+"_"+meta;
}
public static ModelResourceLocation getModelResourceLocation(String path){
	ModelResourceLocation current = resourceMap.get(path);
    if(current==null){
    	current=new ModelResourceLocation(path);
    	resourceMap.put(path, current);
    }
    return current;
}
public static ModelResourceLocation getModelResourceLocation(IDustStorageBlock block, int meta){
	return getModelResourceLocation(getModelResourceLocationPath(block, meta));
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#getFaceQuads(net.minecraft.util.EnumFacing)
 */
@Override
public List getFaceQuads(EnumFacing face) {
	List r = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(Blocks.dirt.getDefaultState()).getFaceQuads(face);
	List<BakedQuad> result = new LinkedList<BakedQuad>();
	int[] bg =null;
	int[] fg = null;
	TextureAtlasSprite bgTex = TextureStitchEventHandler.getDustStorageBG();
	TextureAtlasSprite fgTex = TextureStitchEventHandler.getDustStorageFG();
	//looks like a bakedquad is a full square, and we have to pass it all its vertices in the int array...
	//also, tintindex should be -1
	//FIXME no more crash, but rendering is broken again
	//Feels like the ints are not in the right order...
	if(face==EnumFacing.EAST){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 0, 0, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 1, 0, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 1, 1, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 0, 1, fgColor, fgTex, 0, 16));
	}else if(face==EnumFacing.WEST){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 0, 1, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 1, 1, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 1, 0, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 0, 0, fgColor, fgTex, 0, 16));
	}else if(face==EnumFacing.NORTH){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 0, -0.001F, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 1, -0.001F, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(1, 1, -0.001F, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(1, 0, -0.001F, fgColor, fgTex, 0, 16));
	}else if(face==EnumFacing.SOUTH){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(1, 0, 1.001F, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(1, 1, 1.001F, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 1, 1.001F, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 0, 1.001F, fgColor, fgTex, 0, 16));
	}else if(face==EnumFacing.DOWN){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(1,-0.001F,0, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(1,-0.001F,1, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, -0.001F,1, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, -0.001F,0, fgColor, fgTex, 0, 16));
	}else if(face==EnumFacing.UP){
		//BG color
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 16, 16));
		bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 16, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 0, 0));
		bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 0, 16));
		//fg
		fg = ArrayUtils.addAll(fg, vertexToInts(1,1.001F,1, fgColor, fgTex, 16, 16));
		fg = ArrayUtils.addAll(fg, vertexToInts(1,1.001F,0, fgColor, fgTex, 16, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 1.001F,0, fgColor, fgTex, 0, 0));
		fg = ArrayUtils.addAll(fg, vertexToInts(0, 1.001F,1, fgColor, fgTex, 0, 16));
	}else{
		throw new IllegalArgumentException("Wrong EnumFacing: "+face);//is that even possible...
	}
	//int[] test = ArrayUtils.addAll(new int[]{1,2,3,4},new int[]{5,6,7,8});
	result.add(new BakedQuad(bg, -1, face));
	result.add(new BakedQuad(fg, -1, face));
	return result;
	// TODO Auto-generated method stub
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#getGeneralQuads()
 */
@Override
public List getGeneralQuads() {
	List<BakedQuad> res = new LinkedList<BakedQuad>();
	for(EnumFacing face : EnumFacing.VALUES){
		res.addAll(getFaceQuads(face));
	}
	//return res;
	// TODO Auto-generated method stub
	List r = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(Blocks.dirt.getDefaultState()).getGeneralQuads();
	return r;
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#isAmbientOcclusion()
 */
@Override
public boolean isAmbientOcclusion() {
	//return true;
	return false;
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#isGui3d()
 */
@Override
public boolean isGui3d() {
	return true;
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#isBuiltInRenderer()
 */
@Override
public boolean isBuiltInRenderer() {
	return false;
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#getTexture()
 */
@Override
public TextureAtlasSprite getTexture() {
	//TODO getTexture might need to get tweaked
	return TextureStitchEventHandler.getDustStorageBG();
}

/* (non-Javadoc)
 * @see net.minecraft.client.resources.model.IBakedModel#getItemCameraTransforms()
 */
@Override
public ItemCameraTransforms getItemCameraTransforms() {
	return ItemCameraTransforms.DEFAULT;
}

  /**
   * Converts the vertex information to the int array format expected by BakedQuads.
   * @param x x coordinate
   * @param y y coordinate
   * @param z z coordinate
   * @param color RGBA colour format - white for no effect, non-white to tint the face with the specified colour
   * @param texture the texture to use for the face
   * @param u u-coordinate of the texture (0 - 16) corresponding to [x,y,z]
   * @param v v-coordinate of the texture (0 - 16) corresponding to [x,y,z]
   * @return
   */
  private static int[] vertexToInts(float x, float y, float z, int color, TextureAtlasSprite texture, float u, float v)
  {
    return new int[] {
            Float.floatToRawIntBits(x),
            Float.floatToRawIntBits(y),
            Float.floatToRawIntBits(z),
            color,
            Float.floatToRawIntBits(texture.getInterpolatedU(u)),
            Float.floatToRawIntBits(texture.getInterpolatedV(v)),
            0
    };
  }


}

 

ModelBake Event Handler

/** Runes of Wizardry Mod for Minecraft
*  Licensed under the GNU GPL version 3
*  
*  this file was created by Xilef11 on 2015-09-06
*/
package com.zpig333.runesofwizardry.client;

import com.zpig333.runesofwizardry.api.DustRegistry;
import com.zpig333.runesofwizardry.api.IDustStorageBlock;
import com.zpig333.runesofwizardry.client.model.ModelDustStorage;
import com.zpig333.runesofwizardry.core.WizardryLogger;

import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

/**
* @author Xilef11
*
*/
public class ModelBakeEventHandler {
public static final ModelBakeEventHandler instance=new ModelBakeEventHandler();
private ModelBakeEventHandler() {};

@SubscribeEvent
public void onModelBake(ModelBakeEvent event){
	WizardryLogger.logInfo("Registering models on ModelBakeEvent");
	// Find the existing mapping for the block - it will have been added automatically because
    //  we registered a custom BlockStateMapper for it (using ModelLoader.setCustomStateMapper)
    // Replace the mapping with our ISmartBlockModel.
	for(IDustStorageBlock block: DustRegistry.getAllBlocks()){
		WizardryLogger.logInfo("ModelBake: processing "+block.getName());//XXX this happens
		for(int meta : block.getIDust().getMetaValues()){
			WizardryLogger.logInfo("meta is "+meta);//XXX this happens
			ModelResourceLocation location = ModelDustStorage.getModelResourceLocation(block, meta);
			Object object =  event.modelRegistry.getObject(location);
			WizardryLogger.logInfo("object is "+object);
			if (object instanceof IBakedModel) {//FIXME object is null
				IBakedModel existingModel = (IBakedModel)object;
				ModelDustStorage customModel = new ModelDustStorage(block, meta);
				event.modelRegistry.putObject(location, customModel);
			}else if(object==null){
				ModelDustStorage model = new ModelDustStorage(block, meta);
				event.modelRegistry.putObject(location, model);

			}
		}
	}
}
}

Methods (in client proxy) that load the stuff...

	/* (non-Javadoc)
 * @see com.zpig333.runesofwizardry.proxy.CommonProxy#registerDustStorageRendering()
 */
@Override
public void registerDustStorageRendering() {
	WizardryLogger.logInfo("Registering Dust Storage rendering");
	// We need to tell Forge how to map our BlockCamouflage's IBlockState to a ModelResourceLocation.
	// For example, the BlockStone granite variant has a BlockStateMap entry that looks like
	//   "stone[variant=granite]" (iBlockState)  -> "minecraft:granite#normal" (ModelResourceLocation)
	// For the camouflage block, we ignore the iBlockState completely and always return the same ModelResourceLocation,
	//   which is done using the anonymous class below
	for(final IDustStorageBlock block : DustRegistry.getAllBlocks()){
		WizardryLogger.logInfo("Creating StateMapper for "+block.getName());//XXX This happens
		StateMapperBase mapper = new StateMapperBase() {
			@Override
			protected ModelResourceLocation getModelResourceLocation(IBlockState iBlockState) {
				int meta = (Integer) iBlockState.getValue(IDustStorageBlock.PROPERTYMETA);
				return ModelDustStorage.getModelResourceLocation(block, meta);
			}
		};
		ModelLoader.setCustomStateMapper(block, mapper);
	}

	// ModelBakeEvent will be used to add our ISmartBlockModel to the ModelManager's registry (the
	//  registry used to map all the ModelResourceLocations to IBlockModels).  For the stone example there is a map from
	// ModelResourceLocation("minecraft:granite#normal") to an IBakedModel created from models/block/granite.json.
	// For the camouflage block, it will map from
	// CamouflageISmartBlockModelFactory.modelResourceLocation to our CamouflageISmartBlockModelFactory instance
	MinecraftForge.EVENT_BUS.register(ModelBakeEventHandler.instance);
	//register the handler to create the textures
	MinecraftForge.EVENT_BUS.register(new TextureStitchEventHandler());
}

public void registerDustStorageItemRendering() {
	WizardryLogger.logInfo("Registering dust storage item rendering");
	// This is currently necessary in order to make your block render properly when it is an item (i.e. in the inventory
    //   or in your hand or thrown on the ground).
    // Minecraft knows to look for the item model based on the GameRegistry.registerBlock.  However the registration of
    //  the model for each item is normally done by RenderItem.registerItems(), and this is not currently aware
    //   of any extra items you have created.  Hence you have to do it manually.  This will probably change in future.
    // It must be done in the init phase, not preinit, and must be done on client only.
	for(IDustStorageBlock b:DustRegistry.getAllBlocks()){
		WizardryLogger.logInfo("Processing item: "+b.getName());
		Item itemBlockDustStorage = GameRegistry.findItem(References.modid, b.getName());
		for(int meta: b.getIDust().getMetaValues()){
			WizardryLogger.logInfo("meta: "+meta);
			//ModelResourceLocation itemModelResourceLocation = new ModelResourceLocation(ModelDustStorage.getModelResourceLocationPath(b, meta), "inventory");
			ModelResourceLocation itemModelResourceLocation = ModelDustStorage.getModelResourceLocation(b, meta);				Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(itemBlockDustStorage, meta, itemModelResourceLocation);
		}
	}
}

TextureStitch Event Handler

package com.zpig333.runesofwizardry.client;

import com.zpig333.runesofwizardry.core.References;

import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

public class TextureStitchEventHandler {
private static TextureAtlasSprite dust_storage_bg, dust_storage_fg;
public static TextureAtlasSprite getDustStorageBG(){
	return dust_storage_bg;
}
public static TextureAtlasSprite getDustStorageFG(){
	return dust_storage_fg;
}
@SubscribeEvent
public void onTextureStitch(TextureStitchEvent.pre event){
	dust_storage_bg = event.map.registerSprite(new ResourceLocation(References.texture_path+"blocks/dustStorage_bg"));
	dust_storage_fg = event.map.registerSprite(new ResourceLocation(References.texture_path+"blocks/dustStorage_fg"));
}
}

EDIT: Turns out I derped and didn't notice the int[] was supposed to contian the data for all four vertices of the quad... I no longer crash, but the blocks get rendered in dark colors with no texture

EDIT2: textures were fixed, I had to register for TextureStitchEvent.pre . However, the two layers don't blend (alpha in the foreground renders as black) and the colors are off.

Posted

I managed to fix the color, turns out I had to modify TGG's vertexToInts, since my color was in the 0x00RRGGBB format, while the int[] expected 0x00BBGGRR ( so I used Integer.reverseBytes(color)>>8 ).

 

I still have a few issues though,

  • the transparent pixels in the foreground layer are rendered as black instead of transparent - solved by setting my block to render in the CUTOUT layer and adding 0xFF000000 to my colors
  • my block extends BlockFalling, but when it falls it becomes invisible instead of using my model - working now, don't know why
  • In the inventory, the texture returned from MyModel#gettexture() is used instead of the quads I made - fixed by using ColoredBakedQuad instead of BakedQuad
  • The console still gets spammed with model not found messages

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.