#include "IrrCompileConfig.h"
#include "CSoftwareDriver.h"
#ifdef _IRR_COMPILE_WITH_SOFTWARE_
#include "CSoftwareTexture.h"
#include "os.h"
#include "S3DVertex.h"
namespace irr
{
namespace video
{
CSoftwareDriver::CSoftwareDriver(const core::dimension2d<u32>& windowSize, bool fullscreen, io::IFileSystem* io, video::IImagePresenter* presenter)
: CNullDriver(io, windowSize), BackBuffer(0), Presenter(presenter), WindowId(0),
SceneSourceRect(0), RenderTargetTexture(0), RenderTargetSurface(0),
CurrentTriangleRenderer(0), ZBuffer(0), Texture(0)
{
#ifdef _DEBUG
setDebugName("CSoftwareDriver");
#endif
BackBuffer = new CImage(ECF_A1R5G5B5, windowSize);
if (BackBuffer)
{
BackBuffer->fill(SColor(0));
ZBuffer = video::createZBuffer(BackBuffer->getDimension());
}
DriverAttributes->setAttribute("MaxTextures", 1);
DriverAttributes->setAttribute("MaxIndices", 1<<16);
DriverAttributes->setAttribute("MaxTextureSize", 1024);
DriverAttributes->setAttribute("Version", 1);
TriangleRenderers[ETR_FLAT] = createTriangleRendererFlat(ZBuffer);
TriangleRenderers[ETR_FLAT_WIRE] = createTriangleRendererFlatWire(ZBuffer);
TriangleRenderers[ETR_GOURAUD] = createTriangleRendererGouraud(ZBuffer);
TriangleRenderers[ETR_GOURAUD_WIRE] = createTriangleRendererGouraudWire(ZBuffer);
TriangleRenderers[ETR_TEXTURE_FLAT] = createTriangleRendererTextureFlat(ZBuffer);
TriangleRenderers[ETR_TEXTURE_FLAT_WIRE] = createTriangleRendererTextureFlatWire(ZBuffer);
TriangleRenderers[ETR_TEXTURE_GOURAUD] = createTriangleRendererTextureGouraud(ZBuffer);
TriangleRenderers[ETR_TEXTURE_GOURAUD_WIRE] = createTriangleRendererTextureGouraudWire(ZBuffer);
TriangleRenderers[ETR_TEXTURE_GOURAUD_NOZ] = createTriangleRendererTextureGouraudNoZ();
TriangleRenderers[ETR_TEXTURE_GOURAUD_ADD] = createTriangleRendererTextureGouraudAdd(ZBuffer);
setRenderTarget(BackBuffer);
selectRightTriangleRenderer();
}
CSoftwareDriver::~CSoftwareDriver()
{
if (BackBuffer)
BackBuffer->drop();
for (s32 i=0; i<ETR_COUNT; ++i)
if (TriangleRenderers[i])
TriangleRenderers[i]->drop();
if (ZBuffer)
ZBuffer->drop();
if (Texture)
Texture->drop();
if (RenderTargetTexture)
RenderTargetTexture->drop();
if (RenderTargetSurface)
RenderTargetSurface->drop();
}
void CSoftwareDriver::switchToTriangleRenderer(ETriangleRenderer renderer)
{
video::IImage* s = 0;
if (Texture)
s = ((CSoftwareTexture*)Texture)->getTexture();
CurrentTriangleRenderer = TriangleRenderers[renderer];
CurrentTriangleRenderer->setBackfaceCulling(Material.BackfaceCulling == true);
CurrentTriangleRenderer->setTexture(s);
CurrentTriangleRenderer->setRenderTarget(RenderTargetSurface, ViewPort);
}
void CSoftwareDriver::selectRightTriangleRenderer()
{
ETriangleRenderer renderer = ETR_FLAT;
if (Texture)
{
if (!Material.GouraudShading)
renderer = (!Material.Wireframe) ? ETR_TEXTURE_FLAT : ETR_TEXTURE_FLAT_WIRE;
else
{
if (Material.Wireframe)
renderer = ETR_TEXTURE_GOURAUD_WIRE;
else
{
if (Material.MaterialType == EMT_TRANSPARENT_ADD_COLOR ||
Material.MaterialType == EMT_TRANSPARENT_ALPHA_CHANNEL ||
Material.MaterialType == EMT_TRANSPARENT_VERTEX_ALPHA)
{
renderer = ETR_TEXTURE_GOURAUD_ADD;
}
else
if ((Material.ZBuffer==ECFN_NEVER) && !Material.ZWriteEnable)
renderer = ETR_TEXTURE_GOURAUD_NOZ;
else
{
renderer = ETR_TEXTURE_GOURAUD;
}
}
}
}
else
{
if (!Material.GouraudShading)
renderer = (!Material.Wireframe) ? ETR_FLAT : ETR_FLAT_WIRE;
else
renderer = (!Material.Wireframe) ? ETR_GOURAUD : ETR_GOURAUD_WIRE;
}
switchToTriangleRenderer(renderer);
}
bool CSoftwareDriver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
{
switch (feature)
{
case EVDF_RENDER_TO_TARGET:
case EVDF_TEXTURE_NSQUARE:
return FeatureEnabled[feature];
default:
return false;
};
}
void CSoftwareDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat)
{
TransformationMatrix[state] = mat;
}
bool CSoftwareDriver::setActiveTexture(u32 stage, video::ITexture* texture)
{
if (texture && texture->getDriverType() != EDT_SOFTWARE)
{
os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR);
return false;
}
if (Texture)
Texture->drop();
Texture = texture;
if (Texture)
Texture->grab();
selectRightTriangleRenderer();
return true;
}
void CSoftwareDriver::setMaterial(const SMaterial& material)
{
Material = material;
OverrideMaterial.apply(Material);
for (u32 i = 0; i < 1; ++i)
{
setActiveTexture(i, Material.getTexture(i));
setTransform ((E_TRANSFORMATION_STATE) ( ETS_TEXTURE_0 + i ),
material.getTextureMatrix(i));
}
}
bool CSoftwareDriver::beginScene(bool backBuffer, bool zBuffer, SColor color,
const SExposedVideoData& videoData, core::rect<s32>* sourceRect)
{
CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, sourceRect);
WindowId=videoData.D3D9.HWnd;
SceneSourceRect = sourceRect;
if (backBuffer && BackBuffer)
BackBuffer->fill(color);
if (ZBuffer && zBuffer)
ZBuffer->clear();
return true;
}
bool CSoftwareDriver::endScene()
{
CNullDriver::endScene();
return Presenter->present(BackBuffer, WindowId, SceneSourceRect);
}
ITexture* CSoftwareDriver::createDeviceDependentTexture(IImage* surface, const io::path& name, void* mipmapData)
{
return new CSoftwareTexture(surface, name, false, mipmapData);
}
bool CSoftwareDriver::setRenderTarget(video::ITexture* texture, bool clearBackBuffer,
bool clearZBuffer, SColor color)
{
if (texture && texture->getDriverType() != EDT_SOFTWARE)
{
os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR);
return false;
}
if (RenderTargetTexture)
RenderTargetTexture->drop();
RenderTargetTexture = texture;
if (RenderTargetTexture)
{
RenderTargetTexture->grab();
setRenderTarget(((CSoftwareTexture*)RenderTargetTexture)->getTexture());
}
else
{
setRenderTarget(BackBuffer);
}
if (RenderTargetSurface && (clearBackBuffer || clearZBuffer))
{
if (clearZBuffer)
ZBuffer->clear();
if (clearBackBuffer)
((video::CImage*)RenderTargetSurface)->fill( color );
}
return true;
}
void CSoftwareDriver::setRenderTarget(video::CImage* image)
{
if (RenderTargetSurface)
RenderTargetSurface->drop();
RenderTargetSurface = image;
RenderTargetSize.Width = 0;
RenderTargetSize.Height = 0;
Render2DTranslation.X = 0;
Render2DTranslation.Y = 0;
if (RenderTargetSurface)
{
RenderTargetSurface->grab();
RenderTargetSize = RenderTargetSurface->getDimension();
}
setViewPort(core::rect<s32>(0,0,RenderTargetSize.Width,RenderTargetSize.Height));
if (ZBuffer)
ZBuffer->setSize(RenderTargetSize);
}
void CSoftwareDriver::setViewPort(const core::rect<s32>& area)
{
ViewPort = area;
core::rect<s32> rendert(0,0,RenderTargetSize.Width,RenderTargetSize.Height);
ViewPort.clipAgainst(rendert);
ViewPortSize = core::dimension2du(ViewPort.getSize());
Render2DTranslation.X = (ViewPortSize.Width / 2) + ViewPort.UpperLeftCorner.X;
Render2DTranslation.Y = ViewPort.UpperLeftCorner.Y + ViewPortSize.Height - (ViewPortSize.Height / 2);
if (CurrentTriangleRenderer)
CurrentTriangleRenderer->setRenderTarget(RenderTargetSurface, ViewPort);
}
void CSoftwareDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
switch (iType)
{
case (EIT_16BIT):
{
drawVertexPrimitiveList16(vertices, vertexCount, (const u16*)indexList, primitiveCount, vType, pType);
break;
}
case (EIT_32BIT):
{
os::Printer::log("Software driver can not render 32bit buffers", ELL_ERROR);
break;
}
}
}
void CSoftwareDriver::drawVertexPrimitiveList16(const void* vertices, u32 vertexCount, const u16* indexList, u32 primitiveCount, E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType)
{
const u16* indexPointer=0;
core::array<u16> newBuffer;
switch (pType)
{
case scene::EPT_LINE_STRIP:
{
switch (vType)
{
case EVT_STANDARD:
{
for (u32 i=0; i < primitiveCount-1; ++i)
draw3DLine(((S3DVertex*)vertices)[indexList[i]].Pos,
((S3DVertex*)vertices)[indexList[i+1]].Pos,
((S3DVertex*)vertices)[indexList[i]].Color);
}
break;
case EVT_2TCOORDS:
{
for (u32 i=0; i < primitiveCount-1; ++i)
draw3DLine(((S3DVertex2TCoords*)vertices)[indexList[i]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[i+1]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[i]].Color);
}
break;
case EVT_TANGENTS:
{
for (u32 i=0; i < primitiveCount-1; ++i)
draw3DLine(((S3DVertexTangents*)vertices)[indexList[i]].Pos,
((S3DVertexTangents*)vertices)[indexList[i+1]].Pos,
((S3DVertexTangents*)vertices)[indexList[i]].Color);
}
break;
}
}
return;
case scene::EPT_LINE_LOOP:
drawVertexPrimitiveList16(vertices, vertexCount, indexList, primitiveCount-1, vType, scene::EPT_LINE_STRIP);
switch (vType)
{
case EVT_STANDARD:
draw3DLine(((S3DVertex*)vertices)[indexList[primitiveCount-1]].Pos,
((S3DVertex*)vertices)[indexList[0]].Pos,
((S3DVertex*)vertices)[indexList[primitiveCount-1]].Color);
break;
case EVT_2TCOORDS:
draw3DLine(((S3DVertex2TCoords*)vertices)[indexList[primitiveCount-1]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[0]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[primitiveCount-1]].Color);
break;
case EVT_TANGENTS:
draw3DLine(((S3DVertexTangents*)vertices)[indexList[primitiveCount-1]].Pos,
((S3DVertexTangents*)vertices)[indexList[0]].Pos,
((S3DVertexTangents*)vertices)[indexList[primitiveCount-1]].Color);
break;
}
return;
case scene::EPT_LINES:
{
switch (vType)
{
case EVT_STANDARD:
{
for (u32 i=0; i < 2*primitiveCount; i+=2)
draw3DLine(((S3DVertex*)vertices)[indexList[i]].Pos,
((S3DVertex*)vertices)[indexList[i+1]].Pos,
((S3DVertex*)vertices)[indexList[i]].Color);
}
break;
case EVT_2TCOORDS:
{
for (u32 i=0; i < 2*primitiveCount; i+=2)
draw3DLine(((S3DVertex2TCoords*)vertices)[indexList[i]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[i+1]].Pos,
((S3DVertex2TCoords*)vertices)[indexList[i]].Color);
}
break;
case EVT_TANGENTS:
{
for (u32 i=0; i < 2*primitiveCount; i+=2)
draw3DLine(((S3DVertexTangents*)vertices)[indexList[i]].Pos,
((S3DVertexTangents*)vertices)[indexList[i+1]].Pos,
((S3DVertexTangents*)vertices)[indexList[i]].Color);
}
break;
}
}
return;
case scene::EPT_TRIANGLE_FAN:
{
newBuffer.reallocate(primitiveCount*3);
for( u32 t=0; t<primitiveCount; ++t )
{
newBuffer.push_back(indexList[0]);
newBuffer.push_back(indexList[t+1]);
newBuffer.push_back(indexList[t+2]);
}
indexPointer = newBuffer.pointer();
}
break;
case scene::EPT_TRIANGLES:
indexPointer=indexList;
break;
default:
return;
}
switch (vType)
{
case EVT_STANDARD:
drawClippedIndexedTriangleListT((S3DVertex*)vertices, vertexCount, indexPointer, primitiveCount);
break;
case EVT_2TCOORDS:
drawClippedIndexedTriangleListT((S3DVertex2TCoords*)vertices, vertexCount, indexPointer, primitiveCount);
break;
case EVT_TANGENTS:
drawClippedIndexedTriangleListT((S3DVertexTangents*)vertices, vertexCount, indexPointer, primitiveCount);
break;
}
}
template<class VERTEXTYPE>
void CSoftwareDriver::drawClippedIndexedTriangleListT(const VERTEXTYPE* vertices,
s32 vertexCount, const u16* indexList, s32 triangleCount)
{
if (!RenderTargetSurface || !ZBuffer || !triangleCount)
return;
if (!checkPrimitiveCount(triangleCount))
return;
core::array<VERTEXTYPE> clippedVertices;
core::array<u16> clippedIndices;
core::matrix4 worldinv(TransformationMatrix[ETS_WORLD]);
worldinv.makeInverse();
scene::SViewFrustum frustum(TransformationMatrix[ETS_PROJECTION] * TransformationMatrix[ETS_VIEW]);
core::plane3df planes[5];
for (int p=0; p<5; ++p)
worldinv.transformPlane(frustum.planes[p+1], planes[p]);
core::EIntersectionRelation3D inout[3];
core::array<VERTEXTYPE> tClpBuf;
int t;
int i;
for (i=0; i<triangleCount; ++i)
{
for (t=0; t<3; ++t)
tClpBuf.push_back(vertices[indexList[(i*3)+t]]);
for (int p=0; p<5; ++p)
for (int v=0; v<(int)tClpBuf.size(); v+=3)
{
int inside = 0;
int outside = 0;
for (t=0; t<3; ++t)
{
inout[t] = planes[p].classifyPointRelation(tClpBuf[v+t].Pos);
if (inout[t] != core::ISREL3D_FRONT)
++inside;
else
if (inout[t] == core::ISREL3D_FRONT)
++outside;
}
if (!outside)
{
continue;
}
if (!inside)
{
tClpBuf.erase(v,3);
v -= 3;
continue;
}
#ifndef __SOFTWARE_CLIPPING_PROBLEM__
tClpBuf.erase(v,3);
v -= 3;
#endif
}
if (clippedIndices.size() + tClpBuf.size() < 65535)
for (t=0; t<(int)tClpBuf.size(); ++t)
{
clippedIndices.push_back(clippedVertices.size());
clippedVertices.push_back(tClpBuf[t]);
}
tClpBuf.clear();
}
CNullDriver::drawVertexPrimitiveList(clippedVertices.pointer(), clippedVertices.size(),
clippedIndices.pointer(), clippedIndices.size()/3, EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT);
if (TransformedPoints.size() < clippedVertices.size())
TransformedPoints.set_used(clippedVertices.size());
if (TransformedPoints.empty())
return;
const VERTEXTYPE* currentVertex = clippedVertices.pointer();
S2DVertex* tp = &TransformedPoints[0];
core::dimension2d<u32> textureSize(0,0);
f32 zDiv;
if (Texture)
textureSize = ((CSoftwareTexture*)Texture)->getTexture()->getDimension();
f32 transformedPos[4];
core::matrix4 matrix(TransformationMatrix[ETS_PROJECTION]);
matrix *= TransformationMatrix[ETS_VIEW];
matrix *= TransformationMatrix[ETS_WORLD];
s32 ViewTransformWidth = (ViewPortSize.Width>>1);
s32 ViewTransformHeight = (ViewPortSize.Height>>1);
for (i=0; i<(int)clippedVertices.size(); ++i)
{
transformedPos[0] = currentVertex->Pos.X;
transformedPos[1] = currentVertex->Pos.Y;
transformedPos[2] = currentVertex->Pos.Z;
transformedPos[3] = 1.0f;
matrix.multiplyWith1x4Matrix(transformedPos);
zDiv = transformedPos[3] == 0.0f ? 1.0f : (1.0f / transformedPos[3]);
tp->Pos.X = (s32)(ViewTransformWidth * (transformedPos[0] * zDiv) + (Render2DTranslation.X));
tp->Pos.Y = (Render2DTranslation.Y - (s32)(ViewTransformHeight * (transformedPos[1] * zDiv)));
tp->Color = currentVertex->Color.toA1R5G5B5();
tp->ZValue = (TZBufferType)(32767.0f * zDiv);
tp->TCoords.X = (s32)(currentVertex->TCoords.X * textureSize.Width);
tp->TCoords.X <<= 8;
tp->TCoords.Y = (s32)(currentVertex->TCoords.Y * textureSize.Height);
tp->TCoords.Y <<= 8;
++currentVertex;
++tp;
}
CurrentTriangleRenderer->drawIndexedTriangleList(&TransformedPoints[0],
clippedVertices.size(), clippedIndices.pointer(), clippedIndices.size()/3);
}
void CSoftwareDriver::draw3DLine(const core::vector3df& start,
const core::vector3df& end, SColor color)
{
core::vector3df vect = start.crossProduct(end);
vect.normalize();
vect *= Material.Thickness*0.3f;
S3DVertex vtx[4];
vtx[0].Color = color;
vtx[1].Color = color;
vtx[2].Color = color;
vtx[3].Color = color;
vtx[0].Pos = start;
vtx[1].Pos = end;
vtx[2].Pos = start + vect;
vtx[3].Pos = end + vect;
u16 idx[12] = {0,1,2, 0,2,1, 0,1,3, 0,3,1};
drawIndexedTriangleList(vtx, 4, idx, 4);
}
void CSoftwareDriver::clipTriangle(f32* transformedPos)
{
}
void CSoftwareDriver::OnResize(const core::dimension2d<u32>& size)
{
core::dimension2d<u32> realSize(size);
if (realSize.Width % 2)
realSize.Width += 1;
if (realSize.Height % 2)
realSize.Height += 1;
if (ScreenSize != realSize)
{
if (ViewPort.getWidth() == (s32)ScreenSize.Width &&
ViewPort.getHeight() == (s32)ScreenSize.Height)
{
ViewPort = core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(realSize));
}
ScreenSize = realSize;
bool resetRT = (RenderTargetSurface == BackBuffer);
if (BackBuffer)
BackBuffer->drop();
BackBuffer = new CImage(ECF_A1R5G5B5, realSize);
if (resetRT)
setRenderTarget(BackBuffer);
}
}
const core::dimension2d<u32>& CSoftwareDriver::getCurrentRenderTargetSize() const
{
return RenderTargetSize;
}
void CSoftwareDriver::draw2DImage(const video::ITexture* texture, const core::position2d<s32>& destPos,
const core::rect<s32>& sourceRect,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture)
{
if (texture)
{
if (texture->getDriverType() != EDT_SOFTWARE)
{
os::Printer::log("Fatal Error: Tried to copy from a surface not owned by this driver.", ELL_ERROR);
return;
}
if (useAlphaChannelOfTexture)
((CSoftwareTexture*)texture)->getImage()->copyToWithAlpha(
RenderTargetSurface, destPos, sourceRect, color, clipRect);
else
((CSoftwareTexture*)texture)->getImage()->copyTo(
RenderTargetSurface, destPos, sourceRect, clipRect);
}
}
void CSoftwareDriver::draw2DLine(const core::position2d<s32>& start,
const core::position2d<s32>& end,
SColor color)
{
RenderTargetSurface->drawLine(start, end, color );
}
void CSoftwareDriver::drawPixel(u32 x, u32 y, const SColor & color)
{
BackBuffer->setPixel(x, y, color, true);
}
void CSoftwareDriver::draw2DRectangle(SColor color, const core::rect<s32>& pos,
const core::rect<s32>* clip)
{
if (clip)
{
core::rect<s32> p(pos);
p.clipAgainst(*clip);
if(!p.isValid())
return;
RenderTargetSurface->drawRectangle(p, color);
}
else
{
if(!pos.isValid())
return;
RenderTargetSurface->drawRectangle(pos, color);
}
}
void CSoftwareDriver::draw2DRectangle(const core::rect<s32>& pos,
SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown,
const core::rect<s32>* clip)
{
draw2DRectangle(colorLeftUp, pos, clip);
}
const wchar_t* CSoftwareDriver::getName() const
{
return L"Irrlicht Software Driver 1.0";
}
E_DRIVER_TYPE CSoftwareDriver::getDriverType() const
{
return EDT_SOFTWARE;
}
ECOLOR_FORMAT CSoftwareDriver::getColorFormat() const
{
if (BackBuffer)
return BackBuffer->getColorFormat();
else
return CNullDriver::getColorFormat();
}
const core::matrix4& CSoftwareDriver::getTransform(E_TRANSFORMATION_STATE state) const
{
return TransformationMatrix[state];
}
ITexture* CSoftwareDriver::addRenderTargetTexture(const core::dimension2d<u32>& size,
const io::path& name,
const ECOLOR_FORMAT format)
{
CImage* img = new CImage(video::ECF_A1R5G5B5, size);
ITexture* tex = new CSoftwareTexture(img, name, true);
img->drop();
addTexture(tex);
tex->drop();
return tex;
}
void CSoftwareDriver::clearZBuffer()
{
if (ZBuffer)
ZBuffer->clear();
}
IImage* CSoftwareDriver::createScreenShot()
{
if (BackBuffer)
{
CImage* tmp = new CImage(BackBuffer->getColorFormat(), BackBuffer->getDimension());
BackBuffer->copyTo(tmp);
return tmp;
}
else
return 0;
}
u32 CSoftwareDriver::getMaximalPrimitiveCount() const
{
return 0x00800000;
}
}
}
#endif
namespace irr
{
namespace video
{
IVideoDriver* createSoftwareDriver(const core::dimension2d<u32>& windowSize, bool fullscreen, io::IFileSystem* io, video::IImagePresenter* presenter)
{
#ifdef _IRR_COMPILE_WITH_SOFTWARE_
return new CSoftwareDriver(windowSize, fullscreen, io, presenter);
#else
return 0;
#endif
}
}
}