SimpleJavaEngine/src/main/java/speiger/src/coreengine/rendering/gui/renderer/UIRenderer.java

713 lines
22 KiB
Java

package speiger.src.coreengine.rendering.gui.renderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue;
import speiger.src.collections.objects.queues.ObjectPriorityDequeue;
import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.math.vector.floats.Vec2f;
import speiger.src.coreengine.math.vector.floats.Vec3f;
import speiger.src.coreengine.math.vector.floats.Vec4f;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.math.vector.quaternion.Quaternion;
import speiger.src.coreengine.rendering.gui.GuiManager;
import speiger.src.coreengine.rendering.gui.helper.Align;
import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer;
import speiger.src.coreengine.rendering.models.DrawCall;
import speiger.src.coreengine.rendering.shader.uniforms.UniformVec2f;
import speiger.src.coreengine.rendering.tesselation.GLCall;
import speiger.src.coreengine.rendering.tesselation.Tesselator;
import speiger.src.coreengine.rendering.tesselation.VertexType;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.utils.collections.pools.SimplePool;
public class UIRenderer implements IReloadableResource
{
SimplePool<Matrix4f> recylcePool = new SimplePool<>(100, Matrix4f::new);
Stack<Matrix4f> transformStack = new ObjectArrayList<>();
Matrix4f transform = new Matrix4f();
boolean isFastTransform = true;
SimplePool<Vec4f> fastPool = new SimplePool<>(100, () -> Vec4f.newMutable(0F, 0F, 0F, 1F));
Stack<Vec4f> fastStack = new ObjectArrayList<>();
Vec4f fastTransform = Vec4f.newMutable(0F, 0F, 0F, 1F);
SimplePool<GLCall> callPool = new SimplePool<>(100, GLCall::new);
ObjectPriorityDequeue<GLCall> activeCalls = new ObjectArrayFIFOQueue<>();
Tesselator bufferHelper = new Tesselator(655340);
FloatBuffer renderBuffer = MemoryUtil.memAllocFloat(655340);
Tesselator renderer = new Tesselator(renderBuffer);
ITexture currentTexture;
boolean drawing = false;
boolean useTexture = false;
Vec4f currentFrame = Vec4f.newMutable();
float roundedNess = 0F;
GuiModel texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2);
Align horizontal = Align.CENTER;
Align vertical = Align.CENTER;
Vec3f helper = Vec3f.newMutable();
Vec4f transformHelper = Vec4f.newMutable();
Vec4f alignHelper = Vec4f.newMutable();
Vec3f alignTranslation = Vec3f.newMutable(0F);
Vec3f alignScale = Vec3f.newMutable(1F);
GuiManager manager;
public UIRenderer(GuiManager manager)
{
this.manager = manager;
TextureManager.INSTANCE.getReloader().addReloadableResource(this);
}
public RenderBuffer createBuffer()
{
return new RenderBuffer(bufferHelper);
}
public UIRenderer push()
{
if(isFastTransform)
{
fastStack.push(fastPool.get().set(fastTransform));
return this;
}
transformStack.push(recylcePool.get().load(transform));
return this;
}
public UIRenderer pop()
{
if(isFastTransform)
{
Vec4f pop = fastStack.pop();
fastTransform.set(pop);
fastPool.accept(pop);
return this;
}
Matrix4f pop = transformStack.pop();
transform.load(pop);
pop.getScale(alignScale);
recylcePool.accept(pop);
return this;
}
public UIRenderer setVisibility(float visibility)
{
sanityCheck("Visibilty Change");
renderer.setVisibilty(visibility);
return this;
}
public UIRenderer setBrightness(float brightness)
{
sanityCheck("Brightness change");
renderer.setBrightness(brightness);
return this;
}
public UIRenderer resetEffects()
{
renderer.setBrightness(1F).setVisibilty(1F);
return this;
}
public UIRenderer setActiveTexture(ITexture texture)
{
if(getTextureId(currentTexture) != getTextureId(texture))
{
addDrawCall();
currentTexture = texture;
}
return this;
}
public UIRenderer setRoundness(IGuiBox box, float roundedNess)
{
if(roundedNess <= 0)
{
if(this.roundedNess > 0F) addDrawCall();
this.roundedNess = roundedNess;
return this;
}
alignHelper.set(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY());
if(!currentFrame.equals(alignHelper) || this.roundedNess != roundedNess)
{
addDrawCall();
currentFrame.set(alignHelper);
this.roundedNess = roundedNess;
}
return this;
}
protected void applyEffects(UniformVec2f vec)
{
vec.storeData(renderer.getBrightness(), renderer.getVisibility());
}
public UIRenderer translate(float x, float y)
{
return translate(x, y, 0F);
}
public UIRenderer translate(Vec2f offset)
{
return translate(offset.getX(), offset.getY(), 0F);
}
public UIRenderer translate(float x, float y, float z)
{
if(isFastTransform)
{
fastTransform.add(x, y, z, 0F);
return this;
}
transform.translate(helper.set(x, y, z));
return this;
}
public UIRenderer translate(Vec3f offset)
{
if(isFastTransform)
{
fastTransform.add(offset.getX(), offset.getY(), offset.getZ(), 0F);
return this;
}
transform.translate(offset);
return this;
}
public UIRenderer rotate(Quaternion quad)
{
if(isFastTransform) return this;
transform.rotate(quad);
return this;
}
public UIRenderer rotateX(float angle)
{
if(isFastTransform) return this;
transform.rotateX((float)Math.toRadians(angle));
return this;
}
public UIRenderer rotateY(float angle)
{
if(isFastTransform) return this;
transform.rotateY((float)Math.toRadians(angle));
return this;
}
public UIRenderer rotateZ(float angle)
{
if(isFastTransform) return this;
transform.rotateZ((float)Math.toRadians(angle));
return this;
}
public UIRenderer scale(float x, float y, float z)
{
if(isFastTransform) return this;
transform.scale(helper.set(x, y, z));
alignScale.multiply(x, y, z);
return this;
}
public UIRenderer scale(float scale)
{
if(isFastTransform)
{
fastTransform.multiply(1F, 1F, 1F, scale);
return this;
}
transform.scale(helper.set(scale, scale, scale));
alignScale.multiply(scale);
return this;
}
public UIRenderer unscale(float x, float y, float z)
{
if(isFastTransform) return this;
transform.scale(helper.set(1.0F / x, 1.0F / y, 1.0F / z));
alignScale.multiply(helper);
return this;
}
public UIRenderer unscale(float scale)
{
scale = 1.0F / scale;
if(isFastTransform)
{
fastTransform.multiply(1F, 1F, 1F, scale);
return this;
}
transform.scale(helper.set(scale, scale, scale)).getScale(alignScale);
alignScale.multiply(scale);
return this;
}
public UIRenderer resetTransform()
{
if(isFastTransform)
{
fastTransform.set(0F, 0F, 0F, 1F);
return this;
}
transform.setIdentity();
alignScale.set(Vec3f.ONE);
return this;
}
public float getCurrentZ()
{
return isFastTransform ? fastTransform.getZ() : transform.get(3, 2);
}
public ITexture getActiveTexture()
{
return currentTexture;
}
protected Matrix4f getCurrentMatrix()
{
return transform;
}
public Matrix4f createCurrentMatrix(Matrix4f matrix)
{
return isFastTransform ? matrix.setIdentity().translate(fastTransform.getX(), fastTransform.getY(), fastTransform.getZ()) : matrix.load(transform);
}
public UIRenderer setFastTransform(boolean fast)
{
isFastTransform = fast;
return this;
}
public UIRenderer beginFrame()
{
if(drawing)
{
throw new RuntimeException("Already Drawing!");
}
drawing = true;
fastTransform.set(0F, 0F, 0F, 1F);
roundedNess = 0F;
currentFrame.negate();
transform.setIdentity();
alignScale.set(Vec3f.ONE);
transformStack.clear();
fastStack.clear();
return this;
}
public UIRenderer flush()
{
if(!renderer.isDrawing())
{
return this;
}
renderer.finishData();
int count = renderer.getVertexCount();
if(count <= 0)
{
return this;
}
if(count > (activeCalls.isEmpty() ? 0 : activeCalls.last().getCount()))
{
addDrawCall();
}
texturedModel.storeData(renderBuffer);
GuiShader shader = manager.getShader();
shader.bind(manager.getOrthoMatrixBuffer());
texturedModel.bindArray();
int last = 0;
while(!activeCalls.isEmpty())
{
GLCall call = activeCalls.dequeue();
shader.texture.storeTexture(call.getTexture());
shader.useTexture.storeData(call.getTexture() > 0 ? 1F : 0F);
shader.roundeness.storeData(call.getRoundedNess());
if(call.getRoundedNess() > 0F) shader.bounds.storeData(call.getFrame());
GLUtils.drawArrays(call.getType(), last, call.getCount() - last);
last = call.getCount();
callPool.accept(call);
}
activeCalls.clear();
texturedModel.unbindArray();
shader.stopShader();
renderer.resetVertexes();
return this;
}
public UIRenderer endFrame()
{
if(!drawing)
{
throw new RuntimeException("Not Drawing!");
}
drawing = false;
isFastTransform = true;
flush();
fastTransform.set(0F, 0F, 0F, 1F);
transform.setIdentity();
renderer.resetEffects();
useTexture = false;
while(!transformStack.isEmpty())
{
recylcePool.accept(transformStack.pop());
}
while(!fastStack.isEmpty())
{
fastPool.accept(fastStack.pop());
}
return this;
}
@Override
public void reload()
{
texturedModel.remove();
MemoryUtil.memFree(renderBuffer);
texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2);
renderBuffer = MemoryUtil.memAllocFloat(655340);
renderer = new Tesselator(renderBuffer);
}
@Override
public void destroy()
{
texturedModel.remove();
MemoryUtil.memFree(renderBuffer);
TextureManager.INSTANCE.getReloader().removeReloadableResource(this);
}
public UIRenderer drawBuffers(Iterable<DrawCall> drawCalls, float width, float height)
{
sanityCheck("Buffer Drawing");
applyAlignment(0, 0, width, height, 0, alignHelper);
for(DrawCall call : drawCalls)
{
ensureDrawing(call.getGLType(), call.getTextureId() > 0);
FloatBuffer buffer = ByteBuffer.wrap(call.getData()).order(ByteOrder.nativeOrder()).asFloatBuffer();
for(int i = 0,m=buffer.remaining();i+9<=m;i+=9)
{
pos(transformHelper.set(buffer.get(i)+alignHelper.getX(), buffer.get(i+1)+alignHelper.getY(), buffer.get(i+2), 1F))
.tex(buffer.get(i+3), buffer.get(i+4))
.color4f(buffer.get(i+5), buffer.get(i+6), buffer.get(i+7), buffer.get(i+8)).endVertex();
}
}
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer drawQuad(IGuiBox box, int color)
{
return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color);
}
public UIRenderer drawQuad(IGuiBox box, float zLevel, int color)
{
return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color);
}
public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, int color)
{
return drawQuad(minX, minY, maxX, maxY, 0.0F, color);
}
public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, float zLevel, int color)
{
ensureDrawing(GL11.GL_TRIANGLES, false);
applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper);
pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer drawTexturedQuad(IGuiBox box, int color, float minU, float minV, float maxU, float maxV)
{
return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color, minU, minV, maxU, maxV);
}
public UIRenderer drawTexturedQuad(IGuiBox box, float zLevel, int color, float minU, float minV, float maxU, float maxV)
{
return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color, minU, minV, maxU, maxV);
}
public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, int color, float minU, float minV, float maxU, float maxV)
{
return drawTexturedQuad(minX, minY, maxX, maxY, 0F, color, minU, minV, maxU, maxV);
}
public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, float zLevel, int color, float minU, float minV, float maxU, float maxV)
{
ensureDrawing(GL11.GL_TRIANGLES, true);
applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper);
pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(minU, maxV).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(maxU, minV).color4f(color).endVertex();
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer drawGradientQuad(IGuiBox box, int from, int to, Facing direction)
{
return drawGradientQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), from, to, direction);
}
public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, int from, int to, Facing direction)
{
return drawGradientQuad(minX, minY, maxX, maxY, 0F, from, to, direction);
}
public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, float zLevel, int start, int end, Facing direction)
{
if(!direction.isPositive())
{
int wrapper = start;
start = end;
end = wrapper;
}
ensureDrawing(GL11.GL_TRIANGLES, false);
applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper);
pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : end).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : start).endVertex();
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer drawFrame(IGuiBox box, int color)
{
return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color);
}
public UIRenderer drawFrame(IGuiBox box, float zLevel, int color)
{
return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color);
}
public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, int color)
{
return drawFrame(minX, minY, maxX, maxY, 0F, color);
}
public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, float zLevel, int color)
{
ensureDrawing(GL11.GL_LINES, false);
applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper);
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, int color)
{
return drawLine(minX, minY, maxX, maxY, 0F, color);
}
public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, float zLevel, int color)
{
ensureDrawing(GL11.GL_LINES, false);
applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper);
pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex();
alignTranslation.set(0F, 0F, 0F);
return this;
}
public UIRenderer startCustomShape(int shapeType, boolean needsTexture)
{
sanityCheck("Custom Shape");
ensureDrawing(shapeType, needsTexture);
return this;
}
public UIRenderer pos(float x, float y, float z)
{
pos(transformHelper.set(x, y, z, 1F));
return this;
}
public UIRenderer pos(Vec3f pos)
{
return pos(pos.getX(), pos.getY(), pos.getZ());
}
public UIRenderer color(float r, float g, float b)
{
renderer.color4f(r, g, b);
return this;
}
public UIRenderer color(float r, float g, float b, float a)
{
renderer.color4f(r, g, b, a);
return this;
}
public UIRenderer color(int color)
{
renderer.color4f(color);
return this;
}
public UIRenderer tex(float u, float v)
{
renderer.tex(u, v);
return this;
}
public UIRenderer tex(Vec2f tex)
{
return tex(tex.getX(), tex.getY());
}
public UIRenderer endVertex()
{
renderer.endVertex();
return this;
}
protected void sanityCheck(String task)
{
if(drawing)
{
return;
}
throw new RuntimeException(task);
}
protected Tesselator pos(Vec4f pos)
{
if(isFastTransform)
{
return renderer.pos(fastTransform(pos.getX(), fastTransform.getX(), alignTranslation.getX()), fastTransform(pos.getY(), fastTransform.getY(), alignTranslation.getY()), fastTransform(pos.getZ(), fastTransform.getZ(), alignTranslation.getZ()));
}
float x = (transform.get(0) * pos.getX() + transform.get(4) * pos.getY() + transform.get(8) * pos.getZ() + transform.get(12)) + (alignTranslation.getX() * alignScale.getX());
float y = (transform.get(1) * pos.getX() + transform.get(5) * pos.getY() + transform.get(9) * pos.getZ() + transform.get(13)) + (alignTranslation.getY() * alignScale.getY());
float z = (transform.get(2) * pos.getX() + transform.get(6) * pos.getY() + transform.get(10) * pos.getZ() + transform.get(14)) + (alignTranslation.getZ() * alignScale.getZ());
return renderer.pos(x, y, z);
}
private float fastTransform(float pos, float offset, float align)
{
return ((pos + align) * fastTransform.getW()) + offset;
}
protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Vec4f result)
{
applyAlignment(minX, minY, maxX, maxY, zLevel, vertical, horizontal, renderer, result);
}
protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Align vertical, Align horizontal, Tesselator tes, Vec4f result)
{
float width = maxX - minX;
float height = maxY - minY;
float xOffset = 0F;
float yOffset = 0F;
switch(vertical)
{
case LEFT_TOP:
yOffset = 0;
result.setY(0F).setW(height);
break;
case CENTER:
yOffset = height / 2F;
result.setY(-yOffset).setW(yOffset);
break;
case RIGHT_BOTTOM:
yOffset = maxY;
result.setY(-height).setW(0F);
break;
}
switch(horizontal)
{
case LEFT_TOP:
xOffset = 0;
result.setX(0F).setZ(width);
break;
case CENTER:
xOffset = width / 2F;
result.setX(-xOffset).setZ(xOffset);
break;
case RIGHT_BOTTOM:
xOffset = maxX;
result.setX(-width).setZ(0F);
break;
}
alignTranslation.set(minX + xOffset, minY + yOffset, zLevel);
}
protected void ensureDrawing(int glState, boolean textureNeeded)
{
if(!renderer.isDrawing())
{
renderer.begin(glState, VertexType.UI);
}
if(glState != renderer.getGLDrawType() || useTexture != textureNeeded)
{
addDrawCall();
renderer.changeGLType(glState);
useTexture = textureNeeded;
}
}
protected void addDrawCall()
{
if(getDrawCall() >= renderer.getVertexCount())
{
return;
}
GLCall call = callPool.get();
call.setType(renderer.getGLDrawType());
call.setCount(renderer.getVertexCount());
call.setTexture(getTextureId(useTexture ? currentTexture : null));
call.setFrame(currentFrame);
call.setRounded(roundedNess);
activeCalls.enqueue(call);
}
private int getDrawCall()
{
return activeCalls.isEmpty() ? 0 : activeCalls.last().getCount();
}
private int getTextureId(ITexture texture)
{
return texture == null ? 0 : texture.getTextureId();
}
}