mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-03 00:40:41 +01:00
473ab1ea58
Fix bug that AnimatedMeshSceneNode ignored ReadOnlyMaterials flag when checking materials for transparent render passes. Make IVideoDriver::getMaterialRenderer const. Fix bugs in COctreeSceneNode, CMeshSceneNode and CAnimatedMeshSceneNode where check for transparency in OnRegisterSceneNode() and in render() where no longer identical (those got added after Irrlicht 1.8). Some notes for future: - Maybe we should have a getRenderPass instead of just needsTransparentRenderPass, but this way the code didn't need so much changes and behaves (aside from fixes) pretty much as before. - Still wondering if the default implementation in CNullDriver::needsTransparentRenderPass should always return false when SMaterial.ZWriteEnable is set to EZW_ON. This might be nicer with another material flag. Thought then we might want a material enum to choose the renderpass and that's more work. And we get some recursion as needsTransparentRenderPass might want to check result of getWriteZBuffer which calls needsTransparentRenderPass, so we might need a second function or an additional flag there. But return false when SMaterial.ZWriteEnable == EZW_ON could still be done as EZW_ON is a new flag so existing behavior shouldn't break. I just don't know right now if having an extra render pass for transparent nodes might still make sense even when zbuffer is not written or if that's really the only reason to do that. Any feedback anyone? git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6033 dfc29bdd-3216-0410-991c-e03cc46cb475
2463 lines
70 KiB
C++
2463 lines
70 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "IrrCompileConfig.h"
|
|
#include "CSoftwareDriver2.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
|
|
|
|
#include "SoftwareDriver2_helper.h"
|
|
#include "CSoftwareTexture2.h"
|
|
#include "CSoftware2MaterialRenderer.h"
|
|
#include "S3DVertex.h"
|
|
#include "S4DVertex.h"
|
|
#include "CBlit.h"
|
|
|
|
|
|
#define MAT_TEXTURE(tex) ( (video::CSoftwareTexture2*) Material.org.getTexture ( tex ) )
|
|
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
//! constructor
|
|
CBurningVideoDriver::CBurningVideoDriver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, video::IImagePresenter* presenter)
|
|
: CNullDriver(io, params.WindowSize), BackBuffer(0), Presenter(presenter),
|
|
WindowId(0), SceneSourceRect(0),
|
|
RenderTargetTexture(0), RenderTargetSurface(0), CurrentShader(0),
|
|
DepthBuffer(0), StencilBuffer ( 0 ),
|
|
CurrentOut ( 16 * 2, 256 ), Temp ( 16 * 2, 256 )
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CBurningVideoDriver");
|
|
#endif
|
|
|
|
// create backbuffer
|
|
BackBuffer = new CImage(BURNINGSHADER_COLOR_FORMAT, params.WindowSize);
|
|
if (BackBuffer)
|
|
{
|
|
BackBuffer->fill(SColor(0));
|
|
|
|
// create z buffer
|
|
if ( params.ZBufferBits )
|
|
DepthBuffer = video::createDepthBuffer(BackBuffer->getDimension());
|
|
|
|
// create stencil buffer
|
|
if ( params.Stencilbuffer )
|
|
StencilBuffer = video::createStencilBuffer(BackBuffer->getDimension());
|
|
}
|
|
|
|
DriverAttributes->setAttribute("MaxTextures", 2);
|
|
DriverAttributes->setAttribute("MaxIndices", 1<<16);
|
|
DriverAttributes->setAttribute("MaxTextureSize", SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE);
|
|
DriverAttributes->setAttribute("MaxLights", 1024 ); //glsl::gl_MaxLights);
|
|
DriverAttributes->setAttribute("MaxTextureLODBias", 16.f);
|
|
DriverAttributes->setAttribute("Version", 49);
|
|
|
|
// create triangle renderers
|
|
|
|
irr::memset32 ( BurningShader, 0, sizeof ( BurningShader ) );
|
|
//BurningShader[ETR_FLAT] = createTRFlat2(DepthBuffer);
|
|
//BurningShader[ETR_FLAT_WIRE] = createTRFlatWire2(DepthBuffer);
|
|
BurningShader[ETR_GOURAUD] = createTriangleRendererGouraud2(this);
|
|
BurningShader[ETR_GOURAUD_ALPHA] = createTriangleRendererGouraudAlpha2(this );
|
|
BurningShader[ETR_GOURAUD_ALPHA_NOZ] = createTRGouraudAlphaNoZ2(this );
|
|
//BurningShader[ETR_GOURAUD_WIRE] = createTriangleRendererGouraudWire2(DepthBuffer);
|
|
//BurningShader[ETR_TEXTURE_FLAT] = createTriangleRendererTextureFlat2(DepthBuffer);
|
|
//BurningShader[ETR_TEXTURE_FLAT_WIRE] = createTriangleRendererTextureFlatWire2(DepthBuffer);
|
|
BurningShader[ETR_TEXTURE_GOURAUD] = createTriangleRendererTextureGouraud2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_LIGHTMAP_M1] = createTriangleRendererTextureLightMap2_M1(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_LIGHTMAP_M2] = createTriangleRendererTextureLightMap2_M2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_LIGHTMAP_M4] = createTriangleRendererGTextureLightMap2_M4(this);
|
|
BurningShader[ETR_TEXTURE_LIGHTMAP_M4] = createTriangleRendererTextureLightMap2_M4(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_LIGHTMAP_ADD] = createTriangleRendererTextureLightMap2_Add(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_DETAIL_MAP] = createTriangleRendererTextureDetailMap2(this);
|
|
|
|
BurningShader[ETR_TEXTURE_GOURAUD_WIRE] = createTriangleRendererTextureGouraudWire2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_NOZ] = createTRTextureGouraudNoZ2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_ADD] = createTRTextureGouraudAdd2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_ADD_NO_Z] = createTRTextureGouraudAddNoZ2(this);
|
|
BurningShader[ETR_TEXTURE_GOURAUD_VERTEX_ALPHA] = createTriangleRendererTextureVertexAlpha2 ( this );
|
|
|
|
BurningShader[ETR_TEXTURE_GOURAUD_ALPHA] = createTRTextureGouraudAlpha(this );
|
|
BurningShader[ETR_TEXTURE_GOURAUD_ALPHA_NOZ] = createTRTextureGouraudAlphaNoZ( this );
|
|
|
|
BurningShader[ETR_NORMAL_MAP_SOLID] = createTRNormalMap ( this );
|
|
BurningShader[ETR_STENCIL_SHADOW] = createTRStencilShadow ( this );
|
|
BurningShader[ETR_TEXTURE_BLEND] = createTRTextureBlend( this );
|
|
|
|
BurningShader[ETR_REFERENCE] = createTriangleRendererReference ( this );
|
|
|
|
|
|
// add the same renderer for all solid types
|
|
CSoftware2MaterialRenderer_SOLID* smr = new CSoftware2MaterialRenderer_SOLID( this);
|
|
CSoftware2MaterialRenderer_TRANSPARENT_ADD_COLOR* tmr = new CSoftware2MaterialRenderer_TRANSPARENT_ADD_COLOR( this);
|
|
CSoftware2MaterialRenderer_UNSUPPORTED * umr = new CSoftware2MaterialRenderer_UNSUPPORTED ( this );
|
|
|
|
//!TODO: addMaterialRenderer depends on pushing order....
|
|
addMaterialRenderer ( smr ); // EMT_SOLID
|
|
addMaterialRenderer ( smr ); // EMT_SOLID_2_LAYER,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP,
|
|
addMaterialRenderer ( tmr ); // EMT_LIGHTMAP_ADD,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP_M2,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP_M4,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP_LIGHTING,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP_LIGHTING_M2,
|
|
addMaterialRenderer ( smr ); // EMT_LIGHTMAP_LIGHTING_M4,
|
|
addMaterialRenderer ( smr ); // EMT_DETAIL_MAP,
|
|
addMaterialRenderer ( umr ); // EMT_SPHERE_MAP,
|
|
addMaterialRenderer ( smr ); // EMT_REFLECTION_2_LAYER,
|
|
addMaterialRenderer ( tmr ); // EMT_TRANSPARENT_ADD_COLOR,
|
|
addMaterialRenderer ( tmr ); // EMT_TRANSPARENT_ALPHA_CHANNEL,
|
|
addMaterialRenderer ( tmr ); // EMT_TRANSPARENT_ALPHA_CHANNEL_REF,
|
|
addMaterialRenderer ( tmr ); // EMT_TRANSPARENT_VERTEX_ALPHA,
|
|
addMaterialRenderer ( smr ); // EMT_TRANSPARENT_REFLECTION_2_LAYER,
|
|
addMaterialRenderer ( smr ); // EMT_NORMAL_MAP_SOLID,
|
|
addMaterialRenderer ( umr ); // EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR,
|
|
addMaterialRenderer ( tmr ); // EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA,
|
|
addMaterialRenderer ( smr ); // EMT_PARALLAX_MAP_SOLID,
|
|
addMaterialRenderer ( tmr ); // EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR,
|
|
addMaterialRenderer ( tmr ); // EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA,
|
|
addMaterialRenderer ( tmr ); // EMT_ONETEXTURE_BLEND
|
|
|
|
smr->drop ();
|
|
tmr->drop ();
|
|
umr->drop ();
|
|
|
|
// select render target
|
|
setRenderTargetImage(BackBuffer);
|
|
|
|
//reset Lightspace
|
|
LightSpace.reset ();
|
|
|
|
// select the right renderer
|
|
setCurrentShader();
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CBurningVideoDriver::~CBurningVideoDriver()
|
|
{
|
|
// delete Backbuffer
|
|
if (BackBuffer)
|
|
BackBuffer->drop();
|
|
|
|
// delete triangle renderers
|
|
|
|
for (s32 i=0; i<ETR2_COUNT; ++i)
|
|
{
|
|
if (BurningShader[i])
|
|
BurningShader[i]->drop();
|
|
}
|
|
|
|
// delete Additional buffer
|
|
if (StencilBuffer)
|
|
StencilBuffer->drop();
|
|
|
|
if (DepthBuffer)
|
|
DepthBuffer->drop();
|
|
|
|
if (RenderTargetTexture)
|
|
RenderTargetTexture->drop();
|
|
|
|
if (RenderTargetSurface)
|
|
RenderTargetSurface->drop();
|
|
}
|
|
|
|
|
|
/*!
|
|
selects the right triangle renderer based on the render states.
|
|
*/
|
|
void CBurningVideoDriver::setCurrentShader()
|
|
{
|
|
ITexture *texture0 = Material.org.getTexture(0);
|
|
ITexture *texture1 = Material.org.getTexture(1);
|
|
|
|
bool zMaterialTest = Material.org.ZBuffer != ECFN_DISABLED &&
|
|
Material.org.ZWriteEnable != video::EZW_OFF &&
|
|
( AllowZWriteOnTransparent || !Material.org.isTransparent() );
|
|
|
|
EBurningFFShader shader = zMaterialTest ? ETR_TEXTURE_GOURAUD : ETR_TEXTURE_GOURAUD_NOZ;
|
|
|
|
TransformationFlag[ ETS_TEXTURE_0] &= ~(ETF_TEXGEN_CAMERA_NORMAL|ETF_TEXGEN_CAMERA_REFLECTION);
|
|
LightSpace.Flags &= ~VERTEXTRANSFORM;
|
|
|
|
switch ( Material.org.MaterialType )
|
|
{
|
|
case EMT_ONETEXTURE_BLEND:
|
|
shader = ETR_TEXTURE_BLEND;
|
|
break;
|
|
|
|
case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
|
|
Material.org.MaterialTypeParam = 0.5f;
|
|
// fall through
|
|
case EMT_TRANSPARENT_ALPHA_CHANNEL:
|
|
if ( texture0 && texture0->hasAlpha () )
|
|
{
|
|
shader = zMaterialTest ? ETR_TEXTURE_GOURAUD_ALPHA : ETR_TEXTURE_GOURAUD_ALPHA_NOZ;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
shader = ETR_TEXTURE_GOURAUD_VERTEX_ALPHA;
|
|
}
|
|
break;
|
|
|
|
case EMT_TRANSPARENT_ADD_COLOR:
|
|
shader = zMaterialTest ? ETR_TEXTURE_GOURAUD_ADD : ETR_TEXTURE_GOURAUD_ADD_NO_Z;
|
|
break;
|
|
|
|
case EMT_TRANSPARENT_VERTEX_ALPHA:
|
|
shader = ETR_TEXTURE_GOURAUD_VERTEX_ALPHA;
|
|
break;
|
|
|
|
case EMT_LIGHTMAP:
|
|
case EMT_LIGHTMAP_LIGHTING:
|
|
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_M1;
|
|
break;
|
|
|
|
case EMT_LIGHTMAP_M2:
|
|
case EMT_LIGHTMAP_LIGHTING_M2:
|
|
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_M2;
|
|
break;
|
|
|
|
case EMT_LIGHTMAP_LIGHTING_M4:
|
|
if ( texture1 )
|
|
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_M4;
|
|
break;
|
|
case EMT_LIGHTMAP_M4:
|
|
if ( texture1 )
|
|
shader = ETR_TEXTURE_LIGHTMAP_M4;
|
|
break;
|
|
|
|
case EMT_LIGHTMAP_ADD:
|
|
if ( texture1 )
|
|
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_ADD;
|
|
break;
|
|
|
|
case EMT_DETAIL_MAP:
|
|
if ( texture1 )
|
|
shader = ETR_TEXTURE_GOURAUD_DETAIL_MAP;
|
|
break;
|
|
|
|
case EMT_SPHERE_MAP:
|
|
TransformationFlag[ ETS_TEXTURE_0] |= ETF_TEXGEN_CAMERA_REFLECTION; // ETF_TEXGEN_CAMERA_NORMAL;
|
|
LightSpace.Flags |= VERTEXTRANSFORM;
|
|
break;
|
|
case EMT_REFLECTION_2_LAYER:
|
|
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_M1;
|
|
TransformationFlag[ ETS_TEXTURE_1] |= ETF_TEXGEN_CAMERA_REFLECTION;
|
|
LightSpace.Flags |= VERTEXTRANSFORM;
|
|
break;
|
|
|
|
case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
|
|
case EMT_NORMAL_MAP_SOLID:
|
|
case EMT_PARALLAX_MAP_SOLID:
|
|
case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
|
|
shader = ETR_NORMAL_MAP_SOLID;
|
|
LightSpace.Flags |= VERTEXTRANSFORM;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if ( !texture0 )
|
|
{
|
|
shader = ETR_GOURAUD;
|
|
}
|
|
|
|
if ( Material.org.Wireframe )
|
|
{
|
|
shader = ETR_TEXTURE_GOURAUD_WIRE;
|
|
}
|
|
|
|
//shader = ETR_REFERENCE;
|
|
|
|
// switchToTriangleRenderer
|
|
CurrentShader = BurningShader[shader];
|
|
if ( CurrentShader )
|
|
{
|
|
CurrentShader->setZCompareFunc ( Material.org.ZBuffer );
|
|
CurrentShader->setRenderTarget(RenderTargetSurface, ViewPort);
|
|
CurrentShader->setMaterial ( Material );
|
|
|
|
switch ( shader )
|
|
{
|
|
case ETR_TEXTURE_GOURAUD_ALPHA:
|
|
case ETR_TEXTURE_GOURAUD_ALPHA_NOZ:
|
|
case ETR_TEXTURE_BLEND:
|
|
CurrentShader->setParam ( 0, Material.org.MaterialTypeParam );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! queries the features of the driver, returns true if feature is available
|
|
bool CBurningVideoDriver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
|
|
{
|
|
if (!FeatureEnabled[feature])
|
|
return false;
|
|
|
|
switch (feature)
|
|
{
|
|
#ifdef SOFTWARE_DRIVER_2_BILINEAR
|
|
case EVDF_BILINEAR_FILTER:
|
|
return true;
|
|
#endif
|
|
#ifdef SOFTWARE_DRIVER_2_MIPMAPPING
|
|
case EVDF_MIP_MAP:
|
|
return true;
|
|
#endif
|
|
case EVDF_STENCIL_BUFFER:
|
|
return StencilBuffer != 0;
|
|
|
|
case EVDF_RENDER_TO_TARGET:
|
|
case EVDF_MULTITEXTURE:
|
|
case EVDF_HARDWARE_TL:
|
|
case EVDF_TEXTURE_NSQUARE:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//! Create render target.
|
|
IRenderTarget* CBurningVideoDriver::addRenderTarget()
|
|
{
|
|
CSoftwareRenderTarget2* renderTarget = new CSoftwareRenderTarget2(this);
|
|
RenderTargets.push_back(renderTarget);
|
|
|
|
return renderTarget;
|
|
}
|
|
|
|
|
|
|
|
//! sets transformation
|
|
void CBurningVideoDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat)
|
|
{
|
|
Transformation[state] = mat;
|
|
core::setbit_cond ( TransformationFlag[state], mat.isIdentity(), ETF_IDENTITY );
|
|
|
|
switch ( state )
|
|
{
|
|
case ETS_VIEW:
|
|
Transformation[ETS_VIEW_PROJECTION].setbyproduct_nocheck (
|
|
Transformation[ETS_PROJECTION],
|
|
Transformation[ETS_VIEW]
|
|
);
|
|
getCameraPosWorldSpace ();
|
|
break;
|
|
|
|
case ETS_WORLD:
|
|
if ( TransformationFlag[state] & ETF_IDENTITY )
|
|
{
|
|
Transformation[ETS_WORLD_INVERSE] = Transformation[ETS_WORLD];
|
|
TransformationFlag[ETS_WORLD_INVERSE] |= ETF_IDENTITY;
|
|
Transformation[ETS_CURRENT] = Transformation[ETS_VIEW_PROJECTION];
|
|
}
|
|
else
|
|
{
|
|
//Transformation[ETS_WORLD].getInversePrimitive ( Transformation[ETS_WORLD_INVERSE] );
|
|
Transformation[ETS_CURRENT].setbyproduct_nocheck (
|
|
Transformation[ETS_VIEW_PROJECTION],
|
|
Transformation[ETS_WORLD]
|
|
);
|
|
}
|
|
TransformationFlag[ETS_CURRENT] = 0;
|
|
//getLightPosObjectSpace ();
|
|
break;
|
|
case ETS_TEXTURE_0:
|
|
case ETS_TEXTURE_1:
|
|
case ETS_TEXTURE_2:
|
|
case ETS_TEXTURE_3:
|
|
if ( 0 == (TransformationFlag[state] & ETF_IDENTITY ) )
|
|
LightSpace.Flags |= VERTEXTRANSFORM;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CBurningVideoDriver::beginScene(u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil, const SExposedVideoData& videoData, core::rect<s32>* sourceRect)
|
|
{
|
|
CNullDriver::beginScene(clearFlag, clearColor, clearDepth, clearStencil, videoData, sourceRect);
|
|
WindowId = videoData.D3D9.HWnd;
|
|
SceneSourceRect = sourceRect;
|
|
|
|
clearBuffers(clearFlag, clearColor, clearDepth, clearStencil);
|
|
|
|
memset ( TransformationFlag, 0, sizeof ( TransformationFlag ) );
|
|
return true;
|
|
}
|
|
|
|
bool CBurningVideoDriver::endScene()
|
|
{
|
|
CNullDriver::endScene();
|
|
|
|
return Presenter->present(BackBuffer, WindowId, SceneSourceRect);
|
|
}
|
|
|
|
bool CBurningVideoDriver::setRenderTargetEx(IRenderTarget* target, u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil)
|
|
{
|
|
if (target && target->getDriverType() != EDT_BURNINGSVIDEO)
|
|
{
|
|
os::Printer::log("Fatal Error: Tried to set a render target not owned by this driver.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (RenderTargetTexture)
|
|
RenderTargetTexture->drop();
|
|
|
|
CSoftwareRenderTarget2* renderTarget = static_cast<CSoftwareRenderTarget2*>(target);
|
|
RenderTargetTexture = (renderTarget) ? renderTarget->getTexture() : 0;
|
|
|
|
if (RenderTargetTexture)
|
|
{
|
|
RenderTargetTexture->grab();
|
|
setRenderTargetImage(((CSoftwareTexture2*)RenderTargetTexture)->getTexture());
|
|
}
|
|
else
|
|
{
|
|
setRenderTargetImage(BackBuffer);
|
|
}
|
|
|
|
clearBuffers(clearFlag, clearColor, clearDepth, clearStencil);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//! sets a render target
|
|
void CBurningVideoDriver::setRenderTargetImage(video::CImage* image)
|
|
{
|
|
if (RenderTargetSurface)
|
|
RenderTargetSurface->drop();
|
|
|
|
RenderTargetSurface = image;
|
|
RenderTargetSize.Width = 0;
|
|
RenderTargetSize.Height = 0;
|
|
|
|
if (RenderTargetSurface)
|
|
{
|
|
RenderTargetSurface->grab();
|
|
RenderTargetSize = RenderTargetSurface->getDimension();
|
|
}
|
|
|
|
setViewPort(core::rect<s32>(0,0,RenderTargetSize.Width,RenderTargetSize.Height));
|
|
|
|
if (DepthBuffer)
|
|
DepthBuffer->setSize(RenderTargetSize);
|
|
|
|
if (StencilBuffer)
|
|
StencilBuffer->setSize(RenderTargetSize);
|
|
}
|
|
|
|
|
|
|
|
//! sets a viewport
|
|
void CBurningVideoDriver::setViewPort(const core::rect<s32>& area)
|
|
{
|
|
ViewPort = area;
|
|
|
|
core::rect<s32> rendert(0,0,RenderTargetSize.Width,RenderTargetSize.Height);
|
|
ViewPort.clipAgainst(rendert);
|
|
|
|
Transformation [ ETS_CLIPSCALE ].buildNDCToDCMatrix ( ViewPort, 1 );
|
|
|
|
if (CurrentShader)
|
|
CurrentShader->setRenderTarget(RenderTargetSurface, ViewPort);
|
|
}
|
|
|
|
/*
|
|
generic plane clipping in homogenous coordinates
|
|
special case ndc frustum <-w,w>,<-w,w>,<-w,w>
|
|
can be rewritten with compares e.q near plane, a.z < -a.w and b.z < -b.w
|
|
*/
|
|
|
|
const sVec4 CBurningVideoDriver::NDCPlane[6] =
|
|
{
|
|
sVec4( 0.f, 0.f, -1.f, -1.f ), // near
|
|
sVec4( 0.f, 0.f, 1.f, -1.f ), // far
|
|
sVec4( 1.f, 0.f, 0.f, -1.f ), // left
|
|
sVec4( -1.f, 0.f, 0.f, -1.f ), // right
|
|
sVec4( 0.f, 1.f, 0.f, -1.f ), // bottom
|
|
sVec4( 0.f, -1.f, 0.f, -1.f ) // top
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
test a vertex if it's inside the standard frustum
|
|
|
|
this is the generic one..
|
|
|
|
f32 dotPlane;
|
|
for ( u32 i = 0; i!= 6; ++i )
|
|
{
|
|
dotPlane = v->Pos.dotProduct ( NDCPlane[i] );
|
|
core::setbit_cond( flag, dotPlane <= 0.f, 1 << i );
|
|
}
|
|
|
|
// this is the base for ndc frustum <-w,w>,<-w,w>,<-w,w>
|
|
core::setbit_cond( flag, ( v->Pos.z - v->Pos.w ) <= 0.f, 1 );
|
|
core::setbit_cond( flag, (-v->Pos.z - v->Pos.w ) <= 0.f, 2 );
|
|
core::setbit_cond( flag, ( v->Pos.x - v->Pos.w ) <= 0.f, 4 );
|
|
core::setbit_cond( flag, (-v->Pos.x - v->Pos.w ) <= 0.f, 8 );
|
|
core::setbit_cond( flag, ( v->Pos.y - v->Pos.w ) <= 0.f, 16 );
|
|
core::setbit_cond( flag, (-v->Pos.y - v->Pos.w ) <= 0.f, 32 );
|
|
|
|
*/
|
|
#ifdef IRRLICHT_FAST_MATH
|
|
|
|
REALINLINE u32 CBurningVideoDriver::clipToFrustumTest ( const s4DVertex * v ) const
|
|
{
|
|
f32 test[6];
|
|
u32 flag;
|
|
const f32 w = - v->Pos.w;
|
|
|
|
// a conditional move is needed....FCOMI ( but we don't have it )
|
|
// so let the fpu calculate and write it back.
|
|
// cpu makes the compare, interleaving
|
|
|
|
test[0] = v->Pos.z + w;
|
|
test[1] = -v->Pos.z + w;
|
|
test[2] = v->Pos.x + w;
|
|
test[3] = -v->Pos.x + w;
|
|
test[4] = v->Pos.y + w;
|
|
test[5] = -v->Pos.y + w;
|
|
|
|
flag = (IR ( test[0] ) ) >> 31;
|
|
flag |= (IR ( test[1] ) & 0x80000000 ) >> 30;
|
|
flag |= (IR ( test[2] ) & 0x80000000 ) >> 29;
|
|
flag |= (IR ( test[3] ) & 0x80000000 ) >> 28;
|
|
flag |= (IR ( test[4] ) & 0x80000000 ) >> 27;
|
|
flag |= (IR ( test[5] ) & 0x80000000 ) >> 26;
|
|
|
|
/*
|
|
flag = F32_LOWER_EQUAL_0 ( test[0] );
|
|
flag |= F32_LOWER_EQUAL_0 ( test[1] ) << 1;
|
|
flag |= F32_LOWER_EQUAL_0 ( test[2] ) << 2;
|
|
flag |= F32_LOWER_EQUAL_0 ( test[3] ) << 3;
|
|
flag |= F32_LOWER_EQUAL_0 ( test[4] ) << 4;
|
|
flag |= F32_LOWER_EQUAL_0 ( test[5] ) << 5;
|
|
*/
|
|
return flag;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
REALINLINE u32 CBurningVideoDriver::clipToFrustumTest ( const s4DVertex * v ) const
|
|
{
|
|
u32 flag = 0;
|
|
|
|
flag |= v->Pos.z <= v->Pos.w ? 1 : 0;
|
|
flag |= -v->Pos.z <= v->Pos.w ? 2 : 0;
|
|
|
|
flag |= v->Pos.x <= v->Pos.w ? 4 : 0;
|
|
flag |= -v->Pos.x <= v->Pos.w ? 8 : 0;
|
|
|
|
flag |= v->Pos.y <= v->Pos.w ? 16 : 0;
|
|
flag |= -v->Pos.y <= v->Pos.w ? 32 : 0;
|
|
|
|
/*
|
|
if ( v->Pos.z <= v->Pos.w ) flag |= 1;
|
|
if (-v->Pos.z <= v->Pos.w ) flag |= 2;
|
|
|
|
if ( v->Pos.x <= v->Pos.w ) flag |= 4;
|
|
if (-v->Pos.x <= v->Pos.w ) flag |= 8;
|
|
|
|
if ( v->Pos.y <= v->Pos.w ) flag |= 16;
|
|
if (-v->Pos.y <= v->Pos.w ) flag |= 32;
|
|
*/
|
|
/*
|
|
for ( u32 i = 0; i!= 6; ++i )
|
|
{
|
|
core::setbit_cond( flag, v->Pos.dotProduct ( NDCPlane[i] ) <= 0.f, 1 << i );
|
|
}
|
|
*/
|
|
return flag;
|
|
}
|
|
|
|
#endif // _MSC_VER
|
|
|
|
u32 CBurningVideoDriver::clipToHyperPlane ( s4DVertex * dest, const s4DVertex * source, u32 inCount, const sVec4 &plane )
|
|
{
|
|
u32 outCount = 0;
|
|
s4DVertex * out = dest;
|
|
|
|
const s4DVertex * a;
|
|
const s4DVertex * b = source;
|
|
|
|
f32 bDotPlane;
|
|
|
|
bDotPlane = b->Pos.dotProduct ( plane );
|
|
|
|
for( u32 i = 1; i < inCount + 1; ++i)
|
|
{
|
|
// i really have problem
|
|
const s32 condition = i - inCount;
|
|
const s32 index = (( ( condition >> 31 ) & ( i ^ condition ) ) ^ condition ) << 1;
|
|
|
|
a = &source[ index ];
|
|
|
|
// current point inside
|
|
if ( a->Pos.dotProduct ( plane ) <= 0.f )
|
|
{
|
|
// last point outside
|
|
if ( F32_GREATER_0 ( bDotPlane ) )
|
|
{
|
|
// intersect line segment with plane
|
|
out->interpolate ( *b, *a, bDotPlane / (b->Pos - a->Pos).dotProduct ( plane ) );
|
|
out += 2;
|
|
outCount += 1;
|
|
}
|
|
|
|
// copy current to out
|
|
//*out = *a;
|
|
irr::memcpy32_small ( out, a, SIZEOF_SVERTEX * 2 );
|
|
b = out;
|
|
|
|
out += 2;
|
|
outCount += 1;
|
|
}
|
|
else
|
|
{
|
|
// current point outside
|
|
|
|
if ( F32_LOWER_EQUAL_0 ( bDotPlane ) )
|
|
{
|
|
// previous was inside
|
|
// intersect line segment with plane
|
|
out->interpolate ( *b, *a, bDotPlane / (b->Pos - a->Pos).dotProduct ( plane ) );
|
|
out += 2;
|
|
outCount += 1;
|
|
}
|
|
// pointer
|
|
b = a;
|
|
}
|
|
|
|
bDotPlane = b->Pos.dotProduct ( plane );
|
|
|
|
}
|
|
|
|
return outCount;
|
|
}
|
|
|
|
|
|
u32 CBurningVideoDriver::clipToFrustum ( s4DVertex *v0, s4DVertex * v1, const u32 vIn )
|
|
{
|
|
u32 vOut = vIn;
|
|
|
|
vOut = clipToHyperPlane ( v1, v0, vOut, NDCPlane[0] ); if ( vOut < vIn ) return vOut;
|
|
vOut = clipToHyperPlane ( v0, v1, vOut, NDCPlane[1] ); if ( vOut < vIn ) return vOut;
|
|
vOut = clipToHyperPlane ( v1, v0, vOut, NDCPlane[2] ); if ( vOut < vIn ) return vOut;
|
|
vOut = clipToHyperPlane ( v0, v1, vOut, NDCPlane[3] ); if ( vOut < vIn ) return vOut;
|
|
vOut = clipToHyperPlane ( v1, v0, vOut, NDCPlane[4] ); if ( vOut < vIn ) return vOut;
|
|
vOut = clipToHyperPlane ( v0, v1, vOut, NDCPlane[5] );
|
|
return vOut;
|
|
}
|
|
|
|
/*!
|
|
Part I:
|
|
apply Clip Scale matrix
|
|
From Normalized Device Coordiante ( NDC ) Space to Device Coordinate Space ( DC )
|
|
|
|
Part II:
|
|
Project homogeneous vector
|
|
homogeneous to non-homogenous coordinates ( dividebyW )
|
|
|
|
Incoming: ( xw, yw, zw, w, u, v, 1, R, G, B, A )
|
|
Outgoing: ( xw/w, yw/w, zw/w, w/w, u/w, v/w, 1/w, R/w, G/w, B/w, A/w )
|
|
|
|
|
|
replace w/w by 1/w
|
|
*/
|
|
inline void CBurningVideoDriver::ndc_2_dc_and_project ( s4DVertex *dest,s4DVertex *source, u32 vIn ) const
|
|
{
|
|
u32 g;
|
|
|
|
for ( g = 0; g != vIn; g += 2 )
|
|
{
|
|
if ( (dest[g].flag & VERTEX4D_PROJECTED ) == VERTEX4D_PROJECTED )
|
|
continue;
|
|
|
|
dest[g].flag = source[g].flag | VERTEX4D_PROJECTED;
|
|
|
|
const f32 w = source[g].Pos.w;
|
|
const f32 iw = core::reciprocal ( w );
|
|
|
|
// to device coordinates
|
|
dest[g].Pos.x = iw * ( source[g].Pos.x * Transformation [ ETS_CLIPSCALE ][ 0] + w * Transformation [ ETS_CLIPSCALE ][12] );
|
|
dest[g].Pos.y = iw * ( source[g].Pos.y * Transformation [ ETS_CLIPSCALE ][ 5] + w * Transformation [ ETS_CLIPSCALE ][13] );
|
|
|
|
#ifndef SOFTWARE_DRIVER_2_USE_WBUFFER
|
|
dest[g].Pos.z = iw * source[g].Pos.z;
|
|
#endif
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_USE_VERTEX_COLOR
|
|
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
|
|
dest[g].Color[0] = source[g].Color[0] * iw;
|
|
#else
|
|
dest[g].Color[0] = source[g].Color[0];
|
|
#endif
|
|
|
|
#endif
|
|
dest[g].LightTangent[0] = source[g].LightTangent[0] * iw;
|
|
dest[g].Pos.w = iw;
|
|
}
|
|
}
|
|
|
|
|
|
inline void CBurningVideoDriver::ndc_2_dc_and_project2 ( const s4DVertex **v, const u32 size ) const
|
|
{
|
|
u32 g;
|
|
|
|
for ( g = 0; g != size; g += 1 )
|
|
{
|
|
s4DVertex * a = (s4DVertex*) v[g];
|
|
|
|
if ( (a[1].flag & VERTEX4D_PROJECTED ) == VERTEX4D_PROJECTED )
|
|
continue;
|
|
|
|
a[1].flag = a->flag | VERTEX4D_PROJECTED;
|
|
|
|
// project homogenous vertex, store 1/w
|
|
const f32 w = a->Pos.w;
|
|
const f32 iw = core::reciprocal ( w );
|
|
|
|
// to device coordinates
|
|
const f32 * p = Transformation [ ETS_CLIPSCALE ].pointer();
|
|
a[1].Pos.x = iw * ( a->Pos.x * p[ 0] + w * p[12] );
|
|
a[1].Pos.y = iw * ( a->Pos.y * p[ 5] + w * p[13] );
|
|
|
|
#ifndef SOFTWARE_DRIVER_2_USE_WBUFFER
|
|
a[1].Pos.z = a->Pos.z * iw;
|
|
#endif
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_USE_VERTEX_COLOR
|
|
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
|
|
a[1].Color[0] = a->Color[0] * iw;
|
|
#else
|
|
a[1].Color[0] = a->Color[0];
|
|
#endif
|
|
#endif
|
|
|
|
a[1].LightTangent[0] = a[0].LightTangent[0] * iw;
|
|
a[1].Pos.w = iw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
crossproduct in projected 2D -> screen area triangle
|
|
*/
|
|
inline f32 CBurningVideoDriver::screenarea ( const s4DVertex *v ) const
|
|
{
|
|
return ( ( v[3].Pos.x - v[1].Pos.x ) * ( v[5].Pos.y - v[1].Pos.y ) ) -
|
|
( ( v[3].Pos.y - v[1].Pos.y ) * ( v[5].Pos.x - v[1].Pos.x ) );
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
inline f32 CBurningVideoDriver::texelarea ( const s4DVertex *v, int tex ) const
|
|
{
|
|
f32 z;
|
|
|
|
z = ( (v[2].Tex[tex].x - v[0].Tex[tex].x ) * (v[4].Tex[tex].y - v[0].Tex[tex].y ) )
|
|
- ( (v[4].Tex[tex].x - v[0].Tex[tex].x ) * (v[2].Tex[tex].y - v[0].Tex[tex].y ) );
|
|
|
|
return MAT_TEXTURE ( tex )->getLODFactor ( z );
|
|
}
|
|
|
|
/*!
|
|
crossproduct in projected 2D
|
|
*/
|
|
inline f32 CBurningVideoDriver::screenarea2 ( const s4DVertex **v ) const
|
|
{
|
|
return ( (( v[1] + 1 )->Pos.x - (v[0] + 1 )->Pos.x ) * ( (v[2] + 1 )->Pos.y - (v[0] + 1 )->Pos.y ) ) -
|
|
( (( v[1] + 1 )->Pos.y - (v[0] + 1 )->Pos.y ) * ( (v[2] + 1 )->Pos.x - (v[0] + 1 )->Pos.x ) );
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
inline f32 CBurningVideoDriver::texelarea2 ( const s4DVertex **v, s32 tex ) const
|
|
{
|
|
f32 z;
|
|
z = ( (v[1]->Tex[tex].x - v[0]->Tex[tex].x ) * (v[2]->Tex[tex].y - v[0]->Tex[tex].y ) )
|
|
- ( (v[2]->Tex[tex].x - v[0]->Tex[tex].x ) * (v[1]->Tex[tex].y - v[0]->Tex[tex].y ) );
|
|
|
|
return MAT_TEXTURE ( tex )->getLODFactor ( z );
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
inline void CBurningVideoDriver::select_polygon_mipmap ( s4DVertex *v, u32 vIn, u32 tex, const core::dimension2du& texSize ) const
|
|
{
|
|
f32 f[2];
|
|
|
|
f[0] = (f32) texSize.Width - 0.25f;
|
|
f[1] = (f32) texSize.Height - 0.25f;
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
|
|
for ( u32 g = 0; g != vIn; g += 2 )
|
|
{
|
|
(v + g + 1 )->Tex[tex].x = (v + g + 0)->Tex[tex].x * ( v + g + 1 )->Pos.w * f[0];
|
|
(v + g + 1 )->Tex[tex].y = (v + g + 0)->Tex[tex].y * ( v + g + 1 )->Pos.w * f[1];
|
|
}
|
|
#else
|
|
for ( u32 g = 0; g != vIn; g += 2 )
|
|
{
|
|
(v + g + 1 )->Tex[tex].x = (v + g + 0)->Tex[tex].x * f[0];
|
|
(v + g + 1 )->Tex[tex].y = (v + g + 0)->Tex[tex].y * f[1];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void CBurningVideoDriver::select_polygon_mipmap2 ( s4DVertex **v, u32 tex, const core::dimension2du& texSize ) const
|
|
{
|
|
f32 f[2];
|
|
|
|
f[0] = (f32) texSize.Width - 0.25f;
|
|
f[1] = (f32) texSize.Height - 0.25f;
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
|
|
(v[0] + 1 )->Tex[tex].x = v[0]->Tex[tex].x * ( v[0] + 1 )->Pos.w * f[0];
|
|
(v[0] + 1 )->Tex[tex].y = v[0]->Tex[tex].y * ( v[0] + 1 )->Pos.w * f[1];
|
|
|
|
(v[1] + 1 )->Tex[tex].x = v[1]->Tex[tex].x * ( v[1] + 1 )->Pos.w * f[0];
|
|
(v[1] + 1 )->Tex[tex].y = v[1]->Tex[tex].y * ( v[1] + 1 )->Pos.w * f[1];
|
|
|
|
(v[2] + 1 )->Tex[tex].x = v[2]->Tex[tex].x * ( v[2] + 1 )->Pos.w * f[0];
|
|
(v[2] + 1 )->Tex[tex].y = v[2]->Tex[tex].y * ( v[2] + 1 )->Pos.w * f[1];
|
|
|
|
#else
|
|
(v[0] + 1 )->Tex[tex].x = v[0]->Tex[tex].x * f[0];
|
|
(v[0] + 1 )->Tex[tex].y = v[0]->Tex[tex].y * f[1];
|
|
|
|
(v[1] + 1 )->Tex[tex].x = v[1]->Tex[tex].x * f[0];
|
|
(v[1] + 1 )->Tex[tex].y = v[1]->Tex[tex].y * f[1];
|
|
|
|
(v[2] + 1 )->Tex[tex].x = v[2]->Tex[tex].x * f[0];
|
|
(v[2] + 1 )->Tex[tex].y = v[2]->Tex[tex].y * f[1];
|
|
#endif
|
|
}
|
|
|
|
// Vertex Cache
|
|
const SVSize CBurningVideoDriver::vSize[] =
|
|
{
|
|
{ VERTEX4D_FORMAT_TEXTURE_1 | VERTEX4D_FORMAT_COLOR_1, sizeof(S3DVertex), 1 },
|
|
{ VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1, sizeof(S3DVertex2TCoords),2 },
|
|
{ VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1 | VERTEX4D_FORMAT_BUMP_DOT3, sizeof(S3DVertexTangents),2 },
|
|
{ VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1, sizeof(S3DVertex), 2 }, // reflection map
|
|
{ 0, sizeof(f32) * 3, 0 }, // core::vector3df*
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
fill a cache line with transformed, light and clipp test triangles
|
|
*/
|
|
void CBurningVideoDriver::VertexCache_fill(const u32 sourceIndex, const u32 destIndex)
|
|
{
|
|
u8 * source;
|
|
s4DVertex *dest;
|
|
|
|
source = (u8*) VertexCache.vertices + ( sourceIndex * vSize[VertexCache.vType].Pitch );
|
|
|
|
// it's a look ahead so we never hit it..
|
|
// but give priority...
|
|
//VertexCache.info[ destIndex ].hit = hitCount;
|
|
|
|
// store info
|
|
VertexCache.info[ destIndex ].index = sourceIndex;
|
|
VertexCache.info[ destIndex ].hit = 0;
|
|
|
|
// destination Vertex
|
|
dest = (s4DVertex *) ( (u8*) VertexCache.mem.data + ( destIndex << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) );
|
|
|
|
// transform Model * World * Camera * Projection * NDCSpace matrix
|
|
const S3DVertex *base = ((S3DVertex*) source );
|
|
Transformation [ ETS_CURRENT].transformVect ( &dest->Pos.x, base->Pos );
|
|
|
|
//mhm ;-) maybe no goto
|
|
if ( VertexCache.vType == 4 ) goto clipandproject;
|
|
|
|
|
|
#if defined (SOFTWARE_DRIVER_2_LIGHTING) || defined ( SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM )
|
|
|
|
// vertex normal in light space
|
|
if ( Material.org.Lighting || (LightSpace.Flags & VERTEXTRANSFORM) )
|
|
{
|
|
if ( TransformationFlag[ETS_WORLD] & ETF_IDENTITY )
|
|
{
|
|
LightSpace.normal.set ( base->Normal.X, base->Normal.Y, base->Normal.Z, 1.f );
|
|
LightSpace.vertex.set ( base->Pos.X, base->Pos.Y, base->Pos.Z, 1.f );
|
|
}
|
|
else
|
|
{
|
|
Transformation[ETS_WORLD].rotateVect ( &LightSpace.normal.x, base->Normal );
|
|
|
|
// vertex in light space
|
|
if ( LightSpace.Flags & ( POINTLIGHT | FOG | SPECULAR | VERTEXTRANSFORM) )
|
|
Transformation[ETS_WORLD].transformVect ( &LightSpace.vertex.x, base->Pos );
|
|
}
|
|
|
|
if ( LightSpace.Flags & NORMALIZE )
|
|
LightSpace.normal.normalize_xyz();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined ( SOFTWARE_DRIVER_2_USE_VERTEX_COLOR )
|
|
// apply lighting model
|
|
#if defined (SOFTWARE_DRIVER_2_LIGHTING)
|
|
if ( Material.org.Lighting )
|
|
{
|
|
lightVertex ( dest, base->Color.color );
|
|
}
|
|
else
|
|
{
|
|
dest->Color[0].setA8R8G8B8 ( base->Color.color );
|
|
}
|
|
#else
|
|
dest->Color[0].setA8R8G8B8 ( base->Color.color );
|
|
#endif
|
|
#endif
|
|
|
|
// Texture Transform
|
|
#if !defined ( SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM )
|
|
irr::memcpy32_small ( &dest->Tex[0],&base->TCoords,
|
|
vSize[VertexCache.vType].TexSize << 3 // * ( sizeof ( f32 ) * 2 )
|
|
);
|
|
#else
|
|
|
|
if ( 0 == (LightSpace.Flags & VERTEXTRANSFORM) )
|
|
{
|
|
irr::memcpy32_small ( &dest->Tex[0],&base->TCoords,
|
|
vSize[VertexCache.vType].TexSize << 3 // * ( sizeof ( f32 ) * 2 )
|
|
);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Generate texture coordinates as linear functions so that:
|
|
u = Ux*x + Uy*y + Uz*z + Uw
|
|
v = Vx*x + Vy*y + Vz*z + Vw
|
|
The matrix M for this case is:
|
|
Ux Vx 0 0
|
|
Uy Vy 0 0
|
|
Uz Vz 0 0
|
|
Uw Vw 0 0
|
|
*/
|
|
|
|
u32 t;
|
|
sVec4 n;
|
|
sVec2 srcT;
|
|
|
|
for ( t = 0; t != vSize[VertexCache.vType].TexSize; ++t )
|
|
{
|
|
const core::matrix4& M = Transformation [ ETS_TEXTURE_0 + t ];
|
|
|
|
// texgen
|
|
if ( TransformationFlag [ ETS_TEXTURE_0 + t ] & (ETF_TEXGEN_CAMERA_NORMAL|ETF_TEXGEN_CAMERA_REFLECTION) )
|
|
{
|
|
n.x = LightSpace.campos.x - LightSpace.vertex.x;
|
|
n.y = LightSpace.campos.x - LightSpace.vertex.y;
|
|
n.z = LightSpace.campos.x - LightSpace.vertex.z;
|
|
n.normalize_xyz();
|
|
n.x += LightSpace.normal.x;
|
|
n.y += LightSpace.normal.y;
|
|
n.z += LightSpace.normal.z;
|
|
n.normalize_xyz();
|
|
|
|
const f32 *view = Transformation[ETS_VIEW].pointer();
|
|
|
|
if ( TransformationFlag [ ETS_TEXTURE_0 + t ] & ETF_TEXGEN_CAMERA_REFLECTION )
|
|
{
|
|
srcT.x = 0.5f * ( 1.f + (n.x * view[0] + n.y * view[4] + n.z * view[8] ));
|
|
srcT.y = 0.5f * ( 1.f + (n.x * view[1] + n.y * view[5] + n.z * view[9] ));
|
|
}
|
|
else
|
|
{
|
|
srcT.x = 0.5f * ( 1.f + (n.x * view[0] + n.y * view[1] + n.z * view[2] ));
|
|
srcT.y = 0.5f * ( 1.f + (n.x * view[4] + n.y * view[5] + n.z * view[6] ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
irr::memcpy32_small ( &srcT,(&base->TCoords) + t,
|
|
sizeof ( f32 ) * 2 );
|
|
}
|
|
|
|
switch ( Material.org.TextureLayer[t].TextureWrapU )
|
|
{
|
|
case ETC_CLAMP:
|
|
case ETC_CLAMP_TO_EDGE:
|
|
case ETC_CLAMP_TO_BORDER:
|
|
dest->Tex[t].x = core::clamp ( (f32) ( M[0] * srcT.x + M[4] * srcT.y + M[8] ), 0.f, 1.f );
|
|
break;
|
|
case ETC_MIRROR:
|
|
dest->Tex[t].x = M[0] * srcT.x + M[4] * srcT.y + M[8];
|
|
if (core::fract(dest->Tex[t].x)>0.5f)
|
|
dest->Tex[t].x=1.f-dest->Tex[t].x;
|
|
break;
|
|
case ETC_MIRROR_CLAMP:
|
|
case ETC_MIRROR_CLAMP_TO_EDGE:
|
|
case ETC_MIRROR_CLAMP_TO_BORDER:
|
|
dest->Tex[t].x = core::clamp ( (f32) ( M[0] * srcT.x + M[4] * srcT.y + M[8] ), 0.f, 1.f );
|
|
if (core::fract(dest->Tex[t].x)>0.5f)
|
|
dest->Tex[t].x=1.f-dest->Tex[t].x;
|
|
break;
|
|
case ETC_REPEAT:
|
|
default:
|
|
dest->Tex[t].x = M[0] * srcT.x + M[4] * srcT.y + M[8];
|
|
break;
|
|
}
|
|
switch ( Material.org.TextureLayer[t].TextureWrapV )
|
|
{
|
|
case ETC_CLAMP:
|
|
case ETC_CLAMP_TO_EDGE:
|
|
case ETC_CLAMP_TO_BORDER:
|
|
dest->Tex[t].y = core::clamp ( (f32) ( M[1] * srcT.x + M[5] * srcT.y + M[9] ), 0.f, 1.f );
|
|
break;
|
|
case ETC_MIRROR:
|
|
dest->Tex[t].y = M[1] * srcT.x + M[5] * srcT.y + M[9];
|
|
if (core::fract(dest->Tex[t].y)>0.5f)
|
|
dest->Tex[t].y=1.f-dest->Tex[t].y;
|
|
break;
|
|
case ETC_MIRROR_CLAMP:
|
|
case ETC_MIRROR_CLAMP_TO_EDGE:
|
|
case ETC_MIRROR_CLAMP_TO_BORDER:
|
|
dest->Tex[t].y = core::clamp ( (f32) ( M[1] * srcT.x + M[5] * srcT.y + M[9] ), 0.f, 1.f );
|
|
if (core::fract(dest->Tex[t].y)>0.5f)
|
|
dest->Tex[t].y=1.f-dest->Tex[t].y;
|
|
break;
|
|
case ETC_REPEAT:
|
|
default:
|
|
dest->Tex[t].y = M[1] * srcT.x + M[5] * srcT.y + M[9];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// tangent space light vector, emboss
|
|
if ( Lights.size () && ( vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_BUMP_DOT3 ) )
|
|
{
|
|
const S3DVertexTangents *tangent = ((S3DVertexTangents*) source );
|
|
const SBurningShaderLight &light = LightSpace.Light[0];
|
|
|
|
sVec4 vp;
|
|
|
|
vp.x = light.pos.x - LightSpace.vertex.x;
|
|
vp.y = light.pos.y - LightSpace.vertex.y;
|
|
vp.z = light.pos.z - LightSpace.vertex.z;
|
|
|
|
vp.normalize_xyz();
|
|
|
|
LightSpace.tangent.x = vp.x * tangent->Tangent.X + vp.y * tangent->Tangent.Y + vp.z * tangent->Tangent.Z;
|
|
LightSpace.tangent.y = vp.x * tangent->Binormal.X + vp.y * tangent->Binormal.Y + vp.z * tangent->Binormal.Z;
|
|
//LightSpace.tangent.z = vp.x * tangent->Normal.X + vp.y * tangent->Normal.Y + vp.z * tangent->Normal.Z;
|
|
LightSpace.tangent.z = 0.f;
|
|
LightSpace.tangent.normalize_xyz();
|
|
|
|
f32 scale = 1.f / 128.f;
|
|
if ( Material.org.MaterialTypeParam > 0.f )
|
|
scale = Material.org.MaterialTypeParam;
|
|
|
|
// emboss, shift coordinates
|
|
dest->Tex[1].x = dest->Tex[0].x + LightSpace.tangent.x * scale;
|
|
dest->Tex[1].y = dest->Tex[0].y + LightSpace.tangent.y * scale;
|
|
//dest->Tex[1].z = LightSpace.tangent.z * scale;
|
|
}
|
|
#endif
|
|
|
|
if ( LightSpace.Light.size () && ( vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_BUMP_DOT3 ) )
|
|
{
|
|
const S3DVertexTangents *tangent = ((S3DVertexTangents*) source );
|
|
|
|
sVec4 vp;
|
|
|
|
dest->LightTangent[0].x = 0.f;
|
|
dest->LightTangent[0].y = 0.f;
|
|
dest->LightTangent[0].z = 0.f;
|
|
for ( u32 i = 0; i < 2 && i < LightSpace.Light.size (); ++i )
|
|
{
|
|
const SBurningShaderLight &light = LightSpace.Light[i];
|
|
|
|
if ( !light.LightIsOn )
|
|
continue;
|
|
|
|
vp.x = light.pos.x - LightSpace.vertex.x;
|
|
vp.y = light.pos.y - LightSpace.vertex.y;
|
|
vp.z = light.pos.z - LightSpace.vertex.z;
|
|
|
|
/*
|
|
vp.x = light.pos_objectspace.x - base->Pos.X;
|
|
vp.y = light.pos_objectspace.y - base->Pos.Y;
|
|
vp.z = light.pos_objectspace.z - base->Pos.Z;
|
|
*/
|
|
|
|
vp.normalize_xyz();
|
|
|
|
|
|
// transform by tangent matrix
|
|
sVec3 l;
|
|
#if 1
|
|
l.x = (vp.x * tangent->Tangent.X + vp.y * tangent->Tangent.Y + vp.z * tangent->Tangent.Z );
|
|
l.y = (vp.x * tangent->Binormal.X + vp.y * tangent->Binormal.Y + vp.z * tangent->Binormal.Z );
|
|
l.z = (vp.x * tangent->Normal.X + vp.y * tangent->Normal.Y + vp.z * tangent->Normal.Z );
|
|
#else
|
|
l.x = (vp.x * tangent->Tangent.X + vp.y * tangent->Binormal.X + vp.z * tangent->Normal.X );
|
|
l.y = (vp.x * tangent->Tangent.Y + vp.y * tangent->Binormal.Y + vp.z * tangent->Normal.Y );
|
|
l.z = (vp.x * tangent->Tangent.Z + vp.y * tangent->Binormal.Z + vp.z * tangent->Normal.Z );
|
|
#endif
|
|
|
|
|
|
/*
|
|
f32 scale = 1.f / 128.f;
|
|
scale /= dest->LightTangent[0].b;
|
|
|
|
// emboss, shift coordinates
|
|
dest->Tex[1].x = dest->Tex[0].x + l.r * scale;
|
|
dest->Tex[1].y = dest->Tex[0].y + l.g * scale;
|
|
*/
|
|
dest->Tex[1].x = dest->Tex[0].x;
|
|
dest->Tex[1].y = dest->Tex[0].y;
|
|
|
|
// scale bias
|
|
dest->LightTangent[0].x += l.x;
|
|
dest->LightTangent[0].y += l.y;
|
|
dest->LightTangent[0].z += l.z;
|
|
}
|
|
dest->LightTangent[0].setLength ( 0.5f );
|
|
dest->LightTangent[0].x += 0.5f;
|
|
dest->LightTangent[0].y += 0.5f;
|
|
dest->LightTangent[0].z += 0.5f;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
clipandproject:
|
|
dest[0].flag = dest[1].flag = vSize[VertexCache.vType].Format;
|
|
|
|
// test vertex
|
|
dest[0].flag |= clipToFrustumTest ( dest);
|
|
|
|
// to DC Space, project homogenous vertex
|
|
if ( (dest[0].flag & VERTEX4D_CLIPMASK ) == VERTEX4D_INSIDE )
|
|
{
|
|
ndc_2_dc_and_project2 ( (const s4DVertex**) &dest, 1 );
|
|
}
|
|
|
|
//return dest;
|
|
}
|
|
|
|
//
|
|
|
|
REALINLINE s4DVertex * CBurningVideoDriver::VertexCache_getVertex ( const u32 sourceIndex )
|
|
{
|
|
for ( s32 i = 0; i < VERTEXCACHE_ELEMENT; ++i )
|
|
{
|
|
if ( VertexCache.info[ i ].index == sourceIndex )
|
|
{
|
|
return (s4DVertex *) ( (u8*) VertexCache.mem.data + ( i << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) );
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Cache based on linear walk indices
|
|
fill blockwise on the next 16(Cache_Size) unique vertices in indexlist
|
|
merge the next 16 vertices with the current
|
|
*/
|
|
REALINLINE void CBurningVideoDriver::VertexCache_get(const s4DVertex ** face)
|
|
{
|
|
SCacheInfo info[VERTEXCACHE_ELEMENT];
|
|
|
|
// next primitive must be complete in cache
|
|
if ( VertexCache.indicesIndex - VertexCache.indicesRun < 3 &&
|
|
VertexCache.indicesIndex < VertexCache.indexCount
|
|
)
|
|
{
|
|
// rewind to start of primitive
|
|
VertexCache.indicesIndex = VertexCache.indicesRun;
|
|
|
|
irr::memset32 ( info, VERTEXCACHE_MISS, sizeof ( info ) );
|
|
|
|
// get the next unique vertices cache line
|
|
u32 fillIndex = 0;
|
|
u32 dIndex = 0;
|
|
u32 i = 0;
|
|
u32 sourceIndex = 0;
|
|
|
|
while ( VertexCache.indicesIndex < VertexCache.indexCount &&
|
|
fillIndex < VERTEXCACHE_ELEMENT
|
|
)
|
|
{
|
|
switch ( VertexCache.iType )
|
|
{
|
|
case 1:
|
|
sourceIndex = ((u16*)VertexCache.indices) [ VertexCache.indicesIndex ];
|
|
break;
|
|
case 2:
|
|
sourceIndex = ((u32*)VertexCache.indices) [ VertexCache.indicesIndex ];
|
|
break;
|
|
case 4:
|
|
sourceIndex = VertexCache.indicesIndex;
|
|
break;
|
|
}
|
|
|
|
VertexCache.indicesIndex += 1;
|
|
|
|
// if not exist, push back
|
|
s32 exist = 0;
|
|
for ( dIndex = 0; dIndex < fillIndex; ++dIndex )
|
|
{
|
|
if ( info[ dIndex ].index == sourceIndex )
|
|
{
|
|
exist = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( 0 == exist )
|
|
{
|
|
info[fillIndex++].index = sourceIndex;
|
|
}
|
|
}
|
|
|
|
// clear marks
|
|
for ( i = 0; i!= VERTEXCACHE_ELEMENT; ++i )
|
|
{
|
|
VertexCache.info[i].hit = 0;
|
|
}
|
|
|
|
// mark all existing
|
|
for ( i = 0; i!= fillIndex; ++i )
|
|
{
|
|
for ( dIndex = 0; dIndex < VERTEXCACHE_ELEMENT; ++dIndex )
|
|
{
|
|
if ( VertexCache.info[ dIndex ].index == info[i].index )
|
|
{
|
|
info[i].hit = dIndex;
|
|
VertexCache.info[ dIndex ].hit = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill new
|
|
for ( i = 0; i!= fillIndex; ++i )
|
|
{
|
|
if ( info[i].hit != VERTEXCACHE_MISS )
|
|
continue;
|
|
|
|
for ( dIndex = 0; dIndex < VERTEXCACHE_ELEMENT; ++dIndex )
|
|
{
|
|
if ( 0 == VertexCache.info[dIndex].hit )
|
|
{
|
|
VertexCache_fill ( info[i].index, dIndex );
|
|
VertexCache.info[dIndex].hit += 1;
|
|
info[i].hit = dIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const u32 i0 = core::if_c_a_else_0 ( VertexCache.pType != scene::EPT_TRIANGLE_FAN, VertexCache.indicesRun );
|
|
|
|
switch ( VertexCache.iType )
|
|
{
|
|
case 1:
|
|
{
|
|
const u16 *p = (const u16 *) VertexCache.indices;
|
|
face[0] = VertexCache_getVertex ( p[ i0 ] );
|
|
face[1] = VertexCache_getVertex ( p[ VertexCache.indicesRun + 1] );
|
|
face[2] = VertexCache_getVertex ( p[ VertexCache.indicesRun + 2] );
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
const u32 *p = (const u32 *) VertexCache.indices;
|
|
face[0] = VertexCache_getVertex ( p[ i0 ] );
|
|
face[1] = VertexCache_getVertex ( p[ VertexCache.indicesRun + 1] );
|
|
face[2] = VertexCache_getVertex ( p[ VertexCache.indicesRun + 2] );
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
face[0] = VertexCache_getVertex ( VertexCache.indicesRun + 0 );
|
|
face[1] = VertexCache_getVertex ( VertexCache.indicesRun + 1 );
|
|
face[2] = VertexCache_getVertex ( VertexCache.indicesRun + 2 );
|
|
break;
|
|
default:
|
|
face[0] = face[1] = face[2] = VertexCache_getVertex(VertexCache.indicesRun + 0);
|
|
break;
|
|
}
|
|
|
|
VertexCache.indicesRun += VertexCache.primitivePitch;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
REALINLINE void CBurningVideoDriver::VertexCache_getbypass ( s4DVertex ** face )
|
|
{
|
|
const u32 i0 = core::if_c_a_else_0 ( VertexCache.pType != scene::EPT_TRIANGLE_FAN, VertexCache.indicesRun );
|
|
|
|
if ( VertexCache.iType == 1 )
|
|
{
|
|
const u16 *p = (const u16 *) VertexCache.indices;
|
|
VertexCache_fill ( p[ i0 ], 0 );
|
|
VertexCache_fill ( p[ VertexCache.indicesRun + 1], 1 );
|
|
VertexCache_fill ( p[ VertexCache.indicesRun + 2], 2 );
|
|
}
|
|
else
|
|
{
|
|
const u32 *p = (const u32 *) VertexCache.indices;
|
|
VertexCache_fill ( p[ i0 ], 0 );
|
|
VertexCache_fill ( p[ VertexCache.indicesRun + 1], 1 );
|
|
VertexCache_fill ( p[ VertexCache.indicesRun + 2], 2 );
|
|
}
|
|
|
|
VertexCache.indicesRun += VertexCache.primitivePitch;
|
|
|
|
face[0] = (s4DVertex *) ( (u8*) VertexCache.mem.data + ( 0 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) );
|
|
face[1] = (s4DVertex *) ( (u8*) VertexCache.mem.data + ( 1 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) );
|
|
face[2] = (s4DVertex *) ( (u8*) VertexCache.mem.data + ( 2 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) );
|
|
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void CBurningVideoDriver::VertexCache_reset ( const void* vertices, u32 vertexCount,
|
|
const void* indices, u32 primitiveCount,
|
|
E_VERTEX_TYPE vType,
|
|
scene::E_PRIMITIVE_TYPE pType,
|
|
E_INDEX_TYPE iType)
|
|
{
|
|
VertexCache.vertices = vertices;
|
|
VertexCache.vertexCount = vertexCount;
|
|
|
|
VertexCache.indices = indices;
|
|
VertexCache.indicesIndex = 0;
|
|
VertexCache.indicesRun = 0;
|
|
|
|
if ( Material.org.MaterialType == video::EMT_REFLECTION_2_LAYER )
|
|
VertexCache.vType = 3;
|
|
else
|
|
VertexCache.vType = vType;
|
|
VertexCache.pType = pType;
|
|
|
|
switch ( iType )
|
|
{
|
|
case EIT_16BIT: VertexCache.iType = 1; break;
|
|
case EIT_32BIT: VertexCache.iType = 2; break;
|
|
default:
|
|
VertexCache.iType = iType; break;
|
|
}
|
|
|
|
switch ( VertexCache.pType )
|
|
{
|
|
// most types here will not work as expected, only triangles/triangle_fan
|
|
// is known to work.
|
|
case scene::EPT_POINTS:
|
|
VertexCache.indexCount = primitiveCount;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_LINE_STRIP:
|
|
VertexCache.indexCount = primitiveCount+1;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_LINE_LOOP:
|
|
VertexCache.indexCount = primitiveCount+1;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_LINES:
|
|
VertexCache.indexCount = 2*primitiveCount;
|
|
VertexCache.primitivePitch = 2;
|
|
break;
|
|
case scene::EPT_TRIANGLE_STRIP:
|
|
VertexCache.indexCount = primitiveCount+2;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_TRIANGLES:
|
|
VertexCache.indexCount = primitiveCount + primitiveCount + primitiveCount;
|
|
VertexCache.primitivePitch = 3;
|
|
break;
|
|
case scene::EPT_TRIANGLE_FAN:
|
|
VertexCache.indexCount = primitiveCount + 2;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_QUAD_STRIP:
|
|
VertexCache.indexCount = 2*primitiveCount + 2;
|
|
VertexCache.primitivePitch = 2;
|
|
break;
|
|
case scene::EPT_QUADS:
|
|
VertexCache.indexCount = 4*primitiveCount;
|
|
VertexCache.primitivePitch = 4;
|
|
break;
|
|
case scene::EPT_POLYGON:
|
|
VertexCache.indexCount = primitiveCount+1;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
case scene::EPT_POINT_SPRITES:
|
|
VertexCache.indexCount = primitiveCount;
|
|
VertexCache.primitivePitch = 1;
|
|
break;
|
|
}
|
|
|
|
irr::memset32 ( VertexCache.info, VERTEXCACHE_MISS, sizeof ( VertexCache.info ) );
|
|
}
|
|
|
|
|
|
void CBurningVideoDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
|
|
const void* indexList, u32 primitiveCount,
|
|
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
|
|
|
|
{
|
|
if (!checkPrimitiveCount(primitiveCount))
|
|
return;
|
|
|
|
CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType);
|
|
|
|
// These calls would lead to crashes due to wrong index usage.
|
|
// The vertex cache needs to be rewritten for these primitives.
|
|
if (pType==scene::EPT_POINTS || pType==scene::EPT_LINE_STRIP ||
|
|
pType==scene::EPT_LINE_LOOP || pType==scene::EPT_LINES ||
|
|
pType==scene::EPT_POLYGON ||
|
|
pType==scene::EPT_POINT_SPRITES)
|
|
return;
|
|
|
|
if ( 0 == CurrentShader )
|
|
return;
|
|
|
|
VertexCache_reset ( vertices, vertexCount, indexList, primitiveCount, vType, pType, iType );
|
|
|
|
const s4DVertex * face[3];
|
|
|
|
f32 dc_area;
|
|
s32 lodLevel;
|
|
u32 i;
|
|
u32 g;
|
|
u32 m;
|
|
video::CSoftwareTexture2* tex;
|
|
|
|
for ( i = 0; i < (u32) primitiveCount; ++i )
|
|
{
|
|
VertexCache_get(face);
|
|
|
|
// if fully outside or outside on same side
|
|
if ( ( (face[0]->flag | face[1]->flag | face[2]->flag) & VERTEX4D_CLIPMASK )
|
|
!= VERTEX4D_INSIDE
|
|
)
|
|
continue;
|
|
|
|
// if fully inside
|
|
if ( ( face[0]->flag & face[1]->flag & face[2]->flag & VERTEX4D_CLIPMASK ) == VERTEX4D_INSIDE )
|
|
{
|
|
dc_area = screenarea2 ( face );
|
|
if ( Material.org.BackfaceCulling && F32_LOWER_EQUAL_0( dc_area ) )
|
|
continue;
|
|
else
|
|
if ( Material.org.FrontfaceCulling && F32_GREATER_EQUAL_0( dc_area ) )
|
|
continue;
|
|
|
|
// select mipmap
|
|
dc_area = core::reciprocal ( dc_area );
|
|
for ( m = 0; m != vSize[VertexCache.vType].TexSize; ++m )
|
|
{
|
|
if ( 0 == (tex = MAT_TEXTURE ( m )) )
|
|
{
|
|
CurrentShader->setTextureParam(m, 0, 0);
|
|
continue;
|
|
}
|
|
|
|
lodLevel = s32_log2_f32 ( texelarea2 ( face, m ) * dc_area );
|
|
CurrentShader->setTextureParam(m, tex, lodLevel );
|
|
select_polygon_mipmap2 ( (s4DVertex**) face, m, tex->getSize() );
|
|
}
|
|
|
|
// rasterize
|
|
CurrentShader->drawTriangle ( face[0] + 1, face[1] + 1, face[2] + 1 );
|
|
continue;
|
|
}
|
|
|
|
// else if not complete inside clipping necessary
|
|
irr::memcpy32_small ( ( (u8*) CurrentOut.data + ( 0 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) ), face[0], SIZEOF_SVERTEX * 2 );
|
|
irr::memcpy32_small ( ( (u8*) CurrentOut.data + ( 1 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) ), face[1], SIZEOF_SVERTEX * 2 );
|
|
irr::memcpy32_small ( ( (u8*) CurrentOut.data + ( 2 << ( SIZEOF_SVERTEX_LOG2 + 1 ) ) ), face[2], SIZEOF_SVERTEX * 2 );
|
|
|
|
const u32 flag = CurrentOut.data->flag & VERTEX4D_FORMAT_MASK;
|
|
|
|
for ( g = 0; g != CurrentOut.ElementSize; ++g )
|
|
{
|
|
CurrentOut.data[g].flag = flag;
|
|
Temp.data[g].flag = flag;
|
|
}
|
|
|
|
u32 vOut;
|
|
vOut = clipToFrustum ( CurrentOut.data, Temp.data, 3 );
|
|
if ( vOut < 3 )
|
|
continue;
|
|
|
|
vOut <<= 1;
|
|
|
|
// to DC Space, project homogenous vertex
|
|
ndc_2_dc_and_project ( CurrentOut.data + 1, CurrentOut.data, vOut );
|
|
|
|
// check 2d backface culling on first
|
|
dc_area = screenarea ( CurrentOut.data );
|
|
if ( Material.org.BackfaceCulling && F32_LOWER_EQUAL_0 ( dc_area ) )
|
|
continue;
|
|
else if ( Material.org.FrontfaceCulling && F32_GREATER_EQUAL_0( dc_area ) )
|
|
continue;
|
|
|
|
// select mipmap
|
|
dc_area = core::reciprocal ( dc_area );
|
|
for ( m = 0; m != vSize[VertexCache.vType].TexSize; ++m )
|
|
{
|
|
if ( 0 == (tex = MAT_TEXTURE ( m )) )
|
|
{
|
|
CurrentShader->setTextureParam(m, 0, 0);
|
|
continue;
|
|
}
|
|
|
|
lodLevel = s32_log2_f32 ( texelarea ( CurrentOut.data, m ) * dc_area );
|
|
CurrentShader->setTextureParam(m, tex, lodLevel );
|
|
select_polygon_mipmap ( CurrentOut.data, vOut, m, tex->getSize() );
|
|
}
|
|
|
|
|
|
// re-tesselate ( triangle-fan, 0-1-2,0-2-3.. )
|
|
for ( g = 0; g <= vOut - 6; g += 2 )
|
|
{
|
|
// rasterize
|
|
CurrentShader->drawTriangle ( CurrentOut.data + 0 + 1,
|
|
CurrentOut.data + g + 3,
|
|
CurrentOut.data + g + 5);
|
|
}
|
|
|
|
}
|
|
|
|
// dump statistics
|
|
/*
|
|
char buf [64];
|
|
sprintf ( buf,"VCount:%d PCount:%d CacheMiss: %d",
|
|
vertexCount, primitiveCount,
|
|
VertexCache.CacheMiss
|
|
);
|
|
os::Printer::log( buf );
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
//! Sets the dynamic ambient light color. The default color is
|
|
//! (0,0,0,0) which means it is dark.
|
|
//! \param color: New color of the ambient light.
|
|
void CBurningVideoDriver::setAmbientLight(const SColorf& color)
|
|
{
|
|
LightSpace.Global_AmbientLight.setColorf ( color );
|
|
}
|
|
|
|
|
|
//! adds a dynamic light
|
|
s32 CBurningVideoDriver::addDynamicLight(const SLight& dl)
|
|
{
|
|
(void) CNullDriver::addDynamicLight( dl );
|
|
|
|
SBurningShaderLight l;
|
|
// l.org = dl;
|
|
l.Type = dl.Type;
|
|
l.LightIsOn = true;
|
|
|
|
l.AmbientColor.setColorf ( dl.AmbientColor );
|
|
l.DiffuseColor.setColorf ( dl.DiffuseColor );
|
|
l.SpecularColor.setColorf ( dl.SpecularColor );
|
|
|
|
switch ( dl.Type )
|
|
{
|
|
case video::ELT_DIRECTIONAL:
|
|
l.pos.x = -dl.Direction.X;
|
|
l.pos.y = -dl.Direction.Y;
|
|
l.pos.z = -dl.Direction.Z;
|
|
l.pos.w = 1.f;
|
|
break;
|
|
case ELT_POINT:
|
|
case ELT_SPOT:
|
|
LightSpace.Flags |= POINTLIGHT;
|
|
l.pos.x = dl.Position.X;
|
|
l.pos.y = dl.Position.Y;
|
|
l.pos.z = dl.Position.Z;
|
|
l.pos.w = 1.f;
|
|
/*
|
|
l.radius = (1.f / dl.Attenuation.Y) * (1.f / dl.Attenuation.Y);
|
|
l.constantAttenuation = dl.Attenuation.X;
|
|
l.linearAttenuation = dl.Attenuation.Y;
|
|
l.quadraticAttenuation = dl.Attenuation.Z;
|
|
*/
|
|
l.radius = dl.Radius * dl.Radius;
|
|
l.constantAttenuation = dl.Attenuation.X;
|
|
l.linearAttenuation = 1.f / dl.Radius;
|
|
l.quadraticAttenuation = dl.Attenuation.Z;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LightSpace.Light.push_back ( l );
|
|
return LightSpace.Light.size() - 1;
|
|
}
|
|
|
|
//! Turns a dynamic light on or off
|
|
void CBurningVideoDriver::turnLightOn(s32 lightIndex, bool turnOn)
|
|
{
|
|
if(lightIndex > -1 && lightIndex < (s32)LightSpace.Light.size())
|
|
{
|
|
LightSpace.Light[lightIndex].LightIsOn = turnOn;
|
|
}
|
|
}
|
|
|
|
//! deletes all dynamic lights there are
|
|
void CBurningVideoDriver::deleteAllDynamicLights()
|
|
{
|
|
LightSpace.reset ();
|
|
CNullDriver::deleteAllDynamicLights();
|
|
|
|
}
|
|
|
|
//! returns the maximal amount of dynamic lights the device can handle
|
|
u32 CBurningVideoDriver::getMaximalDynamicLightAmount() const
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
|
|
//! sets a material
|
|
void CBurningVideoDriver::setMaterial(const SMaterial& material)
|
|
{
|
|
Material.org = material;
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM
|
|
for (u32 i = 0; i < 2; ++i)
|
|
{
|
|
setTransform((E_TRANSFORMATION_STATE) (ETS_TEXTURE_0 + i),
|
|
material.getTextureMatrix(i));
|
|
}
|
|
#endif
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_LIGHTING
|
|
Material.AmbientColor.setR8G8B8 ( Material.org.AmbientColor.color );
|
|
Material.DiffuseColor.setR8G8B8 ( Material.org.DiffuseColor.color );
|
|
Material.EmissiveColor.setR8G8B8 ( Material.org.EmissiveColor.color );
|
|
Material.SpecularColor.setR8G8B8 ( Material.org.SpecularColor.color );
|
|
|
|
core::setbit_cond ( LightSpace.Flags, Material.org.Shininess != 0.f, SPECULAR );
|
|
core::setbit_cond ( LightSpace.Flags, Material.org.FogEnable, FOG );
|
|
core::setbit_cond ( LightSpace.Flags, Material.org.NormalizeNormals, NORMALIZE );
|
|
#endif
|
|
|
|
setCurrentShader();
|
|
}
|
|
|
|
|
|
/*!
|
|
Camera Position in World Space
|
|
*/
|
|
void CBurningVideoDriver::getCameraPosWorldSpace ()
|
|
{
|
|
Transformation[ETS_VIEW_INVERSE] = Transformation[ ETS_VIEW ];
|
|
Transformation[ETS_VIEW_INVERSE].makeInverse ();
|
|
TransformationFlag[ETS_VIEW_INVERSE] = 0;
|
|
|
|
const f32 *M = Transformation[ETS_VIEW_INVERSE].pointer ();
|
|
|
|
/* The viewpoint is at (0., 0., 0.) in eye space.
|
|
Turning this into a vector [0 0 0 1] and multiply it by
|
|
the inverse of the view matrix, the resulting vector is the
|
|
object space location of the camera.
|
|
*/
|
|
|
|
LightSpace.campos.x = M[12];
|
|
LightSpace.campos.y = M[13];
|
|
LightSpace.campos.z = M[14];
|
|
LightSpace.campos.w = 1.f;
|
|
}
|
|
|
|
void CBurningVideoDriver::getLightPosObjectSpace ()
|
|
{
|
|
if ( TransformationFlag[ETS_WORLD] & ETF_IDENTITY )
|
|
{
|
|
Transformation[ETS_WORLD_INVERSE] = Transformation[ETS_WORLD];
|
|
TransformationFlag[ETS_WORLD_INVERSE] |= ETF_IDENTITY;
|
|
}
|
|
else
|
|
{
|
|
Transformation[ETS_WORLD].getInverse ( Transformation[ETS_WORLD_INVERSE] );
|
|
TransformationFlag[ETS_WORLD_INVERSE] &= ~ETF_IDENTITY;
|
|
}
|
|
|
|
for ( u32 i = 0; i < 1 && i < LightSpace.Light.size(); ++i )
|
|
{
|
|
SBurningShaderLight &l = LightSpace.Light[i];
|
|
|
|
Transformation[ETS_WORLD_INVERSE].transformVec3 ( &l.pos_objectspace.x, &l.pos.x );
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef SOFTWARE_DRIVER_2_LIGHTING
|
|
|
|
//! Sets the fog mode.
|
|
void CBurningVideoDriver::setFog(SColor color, E_FOG_TYPE fogType, f32 start,
|
|
f32 end, f32 density, bool pixelFog, bool rangeFog)
|
|
{
|
|
CNullDriver::setFog(color, fogType, start, end, density, pixelFog, rangeFog);
|
|
LightSpace.FogColor.setA8R8G8B8 ( color.color );
|
|
}
|
|
|
|
/*!
|
|
applies lighting model
|
|
*/
|
|
void CBurningVideoDriver::lightVertex ( s4DVertex *dest, u32 vertexargb )
|
|
{
|
|
sVec3 dColor;
|
|
|
|
dColor = LightSpace.Global_AmbientLight;
|
|
dColor.add ( Material.EmissiveColor );
|
|
|
|
if ( Lights.size () == 0 )
|
|
{
|
|
dColor.saturate( dest->Color[0], vertexargb);
|
|
return;
|
|
}
|
|
|
|
sVec3 ambient;
|
|
sVec3 diffuse;
|
|
sVec3 specular;
|
|
|
|
|
|
// the universe started in darkness..
|
|
ambient.set ( 0.f, 0.f, 0.f );
|
|
diffuse.set ( 0.f, 0.f, 0.f );
|
|
specular.set ( 0.f, 0.f, 0.f );
|
|
|
|
|
|
u32 i;
|
|
f32 dot;
|
|
f32 len;
|
|
f32 attenuation;
|
|
sVec4 vp; // unit vector vertex to light
|
|
sVec4 lightHalf; // blinn-phong reflection
|
|
|
|
for ( i = 0; i!= LightSpace.Light.size (); ++i )
|
|
{
|
|
const SBurningShaderLight &light = LightSpace.Light[i];
|
|
|
|
if ( !light.LightIsOn )
|
|
continue;
|
|
|
|
// accumulate ambient
|
|
ambient.add ( light.AmbientColor );
|
|
|
|
switch ( light.Type )
|
|
{
|
|
case video::ELT_SPOT:
|
|
case video::ELT_POINT:
|
|
// surface to light
|
|
vp.x = light.pos.x - LightSpace.vertex.x;
|
|
vp.y = light.pos.y - LightSpace.vertex.y;
|
|
vp.z = light.pos.z - LightSpace.vertex.z;
|
|
//vp.x = light.pos_objectspace.x - LightSpace.vertex.x;
|
|
//vp.y = light.pos_objectspace.y - LightSpace.vertex.x;
|
|
//vp.z = light.pos_objectspace.z - LightSpace.vertex.x;
|
|
|
|
len = vp.get_length_xyz_square();
|
|
if ( light.radius < len )
|
|
continue;
|
|
|
|
len = core::reciprocal_squareroot ( len );
|
|
|
|
// build diffuse reflection
|
|
|
|
//angle between normal and light vector
|
|
vp.mul ( len );
|
|
dot = LightSpace.normal.dot_xyz ( vp );
|
|
if ( dot < 0.f )
|
|
continue;
|
|
|
|
attenuation = light.constantAttenuation + ( 1.f - ( len * light.linearAttenuation ) );
|
|
|
|
// diffuse component
|
|
diffuse.mulAdd ( light.DiffuseColor, 3.f * dot * attenuation );
|
|
|
|
if ( !(LightSpace.Flags & SPECULAR) )
|
|
continue;
|
|
|
|
// build specular
|
|
// surface to view
|
|
lightHalf.x = LightSpace.campos.x - LightSpace.vertex.x;
|
|
lightHalf.y = LightSpace.campos.y - LightSpace.vertex.y;
|
|
lightHalf.z = LightSpace.campos.z - LightSpace.vertex.z;
|
|
lightHalf.normalize_xyz();
|
|
lightHalf += vp;
|
|
lightHalf.normalize_xyz();
|
|
|
|
// specular
|
|
dot = LightSpace.normal.dot_xyz ( lightHalf );
|
|
if ( dot < 0.f )
|
|
continue;
|
|
|
|
//specular += light.SpecularColor * ( powf ( Material.org.Shininess ,dot ) * attenuation );
|
|
specular.mulAdd ( light.SpecularColor, dot * attenuation );
|
|
break;
|
|
|
|
case video::ELT_DIRECTIONAL:
|
|
|
|
//angle between normal and light vector
|
|
dot = LightSpace.normal.dot_xyz ( light.pos );
|
|
if ( dot < 0.f )
|
|
continue;
|
|
|
|
// diffuse component
|
|
diffuse.mulAdd ( light.DiffuseColor, dot );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// sum up lights
|
|
dColor.mulAdd (ambient, Material.AmbientColor );
|
|
dColor.mulAdd (diffuse, Material.DiffuseColor);
|
|
dColor.mulAdd (specular, Material.SpecularColor);
|
|
|
|
dColor.saturate ( dest->Color[0], vertexargb );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//! draws an 2d image, using a color (if color is other then Color(255,255,255,255)) and the alpha channel of the texture if wanted.
|
|
void CBurningVideoDriver::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_BURNINGSVIDEO)
|
|
{
|
|
os::Printer::log("Fatal Error: Tried to copy from a surface not owned by this driver.", ELL_ERROR);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
// 2d methods don't use viewPort
|
|
core::position2di dest = destPos;
|
|
core::recti clip=ViewPort;
|
|
if (ViewPort.getSize().Width != ScreenSize.Width)
|
|
{
|
|
dest.X=ViewPort.UpperLeftCorner.X+core::round32_fast(destPos.X*ViewPort.getWidth()/(f32)ScreenSize.Width);
|
|
dest.Y=ViewPort.UpperLeftCorner.Y+core::round32_fast(destPos.Y*ViewPort.getHeight()/(f32)ScreenSize.Height);
|
|
if (clipRect)
|
|
{
|
|
clip.constrainTo(*clipRect);
|
|
}
|
|
clipRect = &clip;
|
|
}
|
|
#endif
|
|
if (useAlphaChannelOfTexture)
|
|
((CSoftwareTexture2*)texture)->getImage()->copyToWithAlpha(
|
|
RenderTargetSurface, destPos, sourceRect, color, clipRect);
|
|
else
|
|
((CSoftwareTexture2*)texture)->getImage()->copyTo(
|
|
RenderTargetSurface, destPos, sourceRect, clipRect);
|
|
}
|
|
}
|
|
|
|
|
|
//! Draws a part of the texture into the rectangle.
|
|
void CBurningVideoDriver::draw2DImage(const video::ITexture* texture, const core::rect<s32>& destRect,
|
|
const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect,
|
|
const video::SColor* const colors, bool useAlphaChannelOfTexture)
|
|
{
|
|
if (texture)
|
|
{
|
|
if (texture->getDriverType() != EDT_BURNINGSVIDEO)
|
|
{
|
|
os::Printer::log("Fatal Error: Tried to copy from a surface not owned by this driver.", ELL_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (useAlphaChannelOfTexture)
|
|
StretchBlit(BLITTER_TEXTURE_ALPHA_BLEND, RenderTargetSurface, &destRect, &sourceRect,
|
|
((CSoftwareTexture2*)texture)->getImage(), (colors ? colors[0].color : 0));
|
|
else
|
|
StretchBlit(BLITTER_TEXTURE, RenderTargetSurface, &destRect, &sourceRect,
|
|
((CSoftwareTexture2*)texture)->getImage(), (colors ? colors[0].color : 0));
|
|
}
|
|
}
|
|
|
|
//! Draws a 2d line.
|
|
void CBurningVideoDriver::draw2DLine(const core::position2d<s32>& start,
|
|
const core::position2d<s32>& end,
|
|
SColor color)
|
|
{
|
|
drawLine(BackBuffer, start, end, color );
|
|
}
|
|
|
|
|
|
//! Draws a pixel
|
|
void CBurningVideoDriver::drawPixel(u32 x, u32 y, const SColor & color)
|
|
{
|
|
BackBuffer->setPixel(x, y, color, true);
|
|
}
|
|
|
|
|
|
//! draw an 2d rectangle
|
|
void CBurningVideoDriver::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;
|
|
|
|
drawRectangle(BackBuffer, p, color);
|
|
}
|
|
else
|
|
{
|
|
if(!pos.isValid())
|
|
return;
|
|
|
|
drawRectangle(BackBuffer, pos, color);
|
|
}
|
|
}
|
|
|
|
|
|
//! Only used by the internal engine. Used to notify the driver that
|
|
//! the window was resized.
|
|
void CBurningVideoDriver::OnResize(const core::dimension2d<u32>& size)
|
|
{
|
|
// make sure width and height are multiples of 2
|
|
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.UpperLeftCorner.X = 0;
|
|
ViewPort.UpperLeftCorner.Y = 0;
|
|
ViewPort.LowerRightCorner.X = realSize.Width;
|
|
ViewPort.LowerRightCorner.X = realSize.Height;
|
|
}
|
|
|
|
ScreenSize = realSize;
|
|
|
|
bool resetRT = (RenderTargetSurface == BackBuffer);
|
|
|
|
if (BackBuffer)
|
|
BackBuffer->drop();
|
|
BackBuffer = new CImage(BURNINGSHADER_COLOR_FORMAT, realSize);
|
|
|
|
if (resetRT)
|
|
setRenderTargetImage(BackBuffer);
|
|
}
|
|
}
|
|
|
|
|
|
//! returns the current render target size
|
|
const core::dimension2d<u32>& CBurningVideoDriver::getCurrentRenderTargetSize() const
|
|
{
|
|
return RenderTargetSize;
|
|
}
|
|
|
|
|
|
//!Draws an 2d rectangle with a gradient.
|
|
void CBurningVideoDriver::draw2DRectangle(const core::rect<s32>& position,
|
|
SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown,
|
|
const core::rect<s32>* clip)
|
|
{
|
|
#ifdef SOFTWARE_DRIVER_2_USE_VERTEX_COLOR
|
|
|
|
core::rect<s32> pos = position;
|
|
|
|
if (clip)
|
|
pos.clipAgainst(*clip);
|
|
|
|
if (!pos.isValid())
|
|
return;
|
|
|
|
const core::dimension2d<s32> renderTargetSize ( ViewPort.getSize() );
|
|
|
|
const s32 xPlus = -(renderTargetSize.Width>>1);
|
|
const f32 xFact = 1.0f / (renderTargetSize.Width>>1);
|
|
|
|
const s32 yPlus = renderTargetSize.Height-(renderTargetSize.Height>>1);
|
|
const f32 yFact = 1.0f / (renderTargetSize.Height>>1);
|
|
|
|
// fill VertexCache direct
|
|
s4DVertex *v;
|
|
|
|
VertexCache.vertexCount = 4;
|
|
|
|
VertexCache.info[0].index = 0;
|
|
VertexCache.info[1].index = 1;
|
|
VertexCache.info[2].index = 2;
|
|
VertexCache.info[3].index = 3;
|
|
|
|
v = &VertexCache.mem.data [ 0 ];
|
|
|
|
v[0].Pos.set ( (f32)(pos.UpperLeftCorner.X+xPlus) * xFact, (f32)(yPlus-pos.UpperLeftCorner.Y) * yFact, 0.f, 1.f );
|
|
v[0].Color[0].setA8R8G8B8 ( colorLeftUp.color );
|
|
|
|
v[2].Pos.set ( (f32)(pos.LowerRightCorner.X+xPlus) * xFact, (f32)(yPlus- pos.UpperLeftCorner.Y) * yFact, 0.f, 1.f );
|
|
v[2].Color[0].setA8R8G8B8 ( colorRightUp.color );
|
|
|
|
v[4].Pos.set ( (f32)(pos.LowerRightCorner.X+xPlus) * xFact, (f32)(yPlus-pos.LowerRightCorner.Y) * yFact, 0.f ,1.f );
|
|
v[4].Color[0].setA8R8G8B8 ( colorRightDown.color );
|
|
|
|
v[6].Pos.set ( (f32)(pos.UpperLeftCorner.X+xPlus) * xFact, (f32)(yPlus-pos.LowerRightCorner.Y) * yFact, 0.f, 1.f );
|
|
v[6].Color[0].setA8R8G8B8 ( colorLeftDown.color );
|
|
|
|
s32 i;
|
|
u32 g;
|
|
|
|
for ( i = 0; i!= 8; i += 2 )
|
|
{
|
|
v[i + 0].flag = clipToFrustumTest ( v + i );
|
|
v[i + 1].flag = 0;
|
|
if ( (v[i].flag & VERTEX4D_INSIDE ) == VERTEX4D_INSIDE )
|
|
{
|
|
ndc_2_dc_and_project ( v + i + 1, v + i, 2 );
|
|
}
|
|
}
|
|
|
|
|
|
IBurningShader * render;
|
|
|
|
render = BurningShader [ ETR_GOURAUD_ALPHA_NOZ ];
|
|
render->setRenderTarget(RenderTargetSurface, ViewPort);
|
|
|
|
static const s16 indexList[6] = {0,1,2,0,2,3};
|
|
|
|
s4DVertex * face[3];
|
|
|
|
for ( i = 0; i!= 6; i += 3 )
|
|
{
|
|
face[0] = VertexCache_getVertex ( indexList [ i + 0 ] );
|
|
face[1] = VertexCache_getVertex ( indexList [ i + 1 ] );
|
|
face[2] = VertexCache_getVertex ( indexList [ i + 2 ] );
|
|
|
|
// test clipping
|
|
u32 test = face[0]->flag & face[1]->flag & face[2]->flag & VERTEX4D_INSIDE;
|
|
|
|
if ( test == VERTEX4D_INSIDE )
|
|
{
|
|
render->drawTriangle ( face[0] + 1, face[1] + 1, face[2] + 1 );
|
|
continue;
|
|
}
|
|
// Todo: all vertices are clipped in 2d..
|
|
// is this true ?
|
|
u32 vOut = 6;
|
|
memcpy ( CurrentOut.data + 0, face[0], sizeof ( s4DVertex ) * 2 );
|
|
memcpy ( CurrentOut.data + 2, face[1], sizeof ( s4DVertex ) * 2 );
|
|
memcpy ( CurrentOut.data + 4, face[2], sizeof ( s4DVertex ) * 2 );
|
|
|
|
vOut = clipToFrustum ( CurrentOut.data, Temp.data, 3 );
|
|
if ( vOut < 3 )
|
|
continue;
|
|
|
|
vOut <<= 1;
|
|
// to DC Space, project homogenous vertex
|
|
ndc_2_dc_and_project ( CurrentOut.data + 1, CurrentOut.data, vOut );
|
|
|
|
// re-tesselate ( triangle-fan, 0-1-2,0-2-3.. )
|
|
for ( g = 0; g <= vOut - 6; g += 2 )
|
|
{
|
|
// rasterize
|
|
render->drawTriangle ( CurrentOut.data + 1, &CurrentOut.data[g + 3], &CurrentOut.data[g + 5] );
|
|
}
|
|
|
|
}
|
|
#else
|
|
draw2DRectangle ( colorLeftUp, position, clip );
|
|
#endif
|
|
}
|
|
|
|
|
|
//! Draws a 3d line.
|
|
void CBurningVideoDriver::draw3DLine(const core::vector3df& start,
|
|
const core::vector3df& end, SColor color)
|
|
{
|
|
Transformation [ ETS_CURRENT].transformVect ( &CurrentOut.data[0].Pos.x, start );
|
|
Transformation [ ETS_CURRENT].transformVect ( &CurrentOut.data[2].Pos.x, end );
|
|
|
|
u32 g;
|
|
u32 vOut;
|
|
|
|
// no clipping flags
|
|
for ( g = 0; g != CurrentOut.ElementSize; ++g )
|
|
{
|
|
CurrentOut.data[g].flag = 0;
|
|
Temp.data[g].flag = 0;
|
|
}
|
|
|
|
// vertices count per line
|
|
vOut = clipToFrustum ( CurrentOut.data, Temp.data, 2 );
|
|
if ( vOut < 2 )
|
|
return;
|
|
|
|
vOut <<= 1;
|
|
|
|
IBurningShader * line;
|
|
line = BurningShader [ ETR_TEXTURE_GOURAUD_WIRE ];
|
|
line->setRenderTarget(RenderTargetSurface, ViewPort);
|
|
|
|
// to DC Space, project homogenous vertex
|
|
ndc_2_dc_and_project ( CurrentOut.data + 1, CurrentOut.data, vOut );
|
|
|
|
// unproject vertex color
|
|
#ifdef SOFTWARE_DRIVER_2_USE_VERTEX_COLOR
|
|
for ( g = 0; g != vOut; g+= 2 )
|
|
{
|
|
CurrentOut.data[ g + 1].Color[0].setA8R8G8B8 ( color.color );
|
|
}
|
|
#endif
|
|
|
|
|
|
for ( g = 0; g <= vOut - 4; g += 2 )
|
|
{
|
|
// rasterize
|
|
line->drawLine ( CurrentOut.data + 1, CurrentOut.data + g + 3 );
|
|
}
|
|
}
|
|
|
|
|
|
//! \return Returns the name of the video driver. Example: In case of the DirectX8
|
|
//! driver, it would return "Direct3D8.1".
|
|
const wchar_t* CBurningVideoDriver::getName() const
|
|
{
|
|
#ifdef BURNINGVIDEO_RENDERER_BEAUTIFUL
|
|
return L"Burning's Video 0.49 beautiful";
|
|
#elif defined ( BURNINGVIDEO_RENDERER_ULTRA_FAST )
|
|
return L"Burning's Video 0.49 ultra fast";
|
|
#elif defined ( BURNINGVIDEO_RENDERER_FAST )
|
|
return L"Burning's Video 0.49 fast";
|
|
#else
|
|
return L"Burning's Video 0.49";
|
|
#endif
|
|
}
|
|
|
|
//! Returns the graphics card vendor name.
|
|
core::stringc CBurningVideoDriver::getVendorInfo()
|
|
{
|
|
return "Burning's Video: Ing. Thomas Alten (c) 2006-2015";
|
|
}
|
|
|
|
|
|
//! Returns type of video driver
|
|
E_DRIVER_TYPE CBurningVideoDriver::getDriverType() const
|
|
{
|
|
return EDT_BURNINGSVIDEO;
|
|
}
|
|
|
|
|
|
//! returns color format
|
|
ECOLOR_FORMAT CBurningVideoDriver::getColorFormat() const
|
|
{
|
|
return BURNINGSHADER_COLOR_FORMAT;
|
|
}
|
|
|
|
|
|
//! Returns the transformation set by setTransform
|
|
const core::matrix4& CBurningVideoDriver::getTransform(E_TRANSFORMATION_STATE state) const
|
|
{
|
|
return Transformation[state];
|
|
}
|
|
|
|
|
|
//! Creates a render target texture.
|
|
ITexture* CBurningVideoDriver::addRenderTargetTexture(const core::dimension2d<u32>& size,
|
|
const io::path& name, const ECOLOR_FORMAT format)
|
|
{
|
|
IImage* img = createImage(BURNINGSHADER_COLOR_FORMAT, size);
|
|
ITexture* tex = new CSoftwareTexture2(img, name, CSoftwareTexture2::IS_RENDERTARGET );
|
|
img->drop();
|
|
addTexture(tex);
|
|
tex->drop();
|
|
return tex;
|
|
}
|
|
|
|
void CBurningVideoDriver::clearBuffers(u16 flag, SColor color, f32 depth, u8 stencil)
|
|
{
|
|
if ((flag & ECBF_COLOR) && RenderTargetSurface)
|
|
RenderTargetSurface->fill(color);
|
|
|
|
if ((flag & ECBF_DEPTH) && DepthBuffer)
|
|
DepthBuffer->clear();
|
|
|
|
if ((flag & ECBF_STENCIL) && StencilBuffer)
|
|
StencilBuffer->clear();
|
|
}
|
|
|
|
|
|
//! Returns an image created from the last rendered frame.
|
|
IImage* CBurningVideoDriver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RENDER_TARGET target)
|
|
{
|
|
if (target != video::ERT_FRAME_BUFFER)
|
|
return 0;
|
|
|
|
if (BackBuffer)
|
|
{
|
|
IImage* tmp = createImage(BackBuffer->getColorFormat(), BackBuffer->getDimension());
|
|
BackBuffer->copyTo(tmp);
|
|
return tmp;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
ITexture* CBurningVideoDriver::createDeviceDependentTexture(const io::path& name, IImage* image)
|
|
{
|
|
CSoftwareTexture2* texture = new CSoftwareTexture2(image, name, (getTextureCreationFlag(ETCF_CREATE_MIP_MAPS) ? CSoftwareTexture2::GEN_MIPMAP : 0) |
|
|
(getTextureCreationFlag(ETCF_ALLOW_NON_POWER_2) ? 0 : CSoftwareTexture2::NP2_SIZE));
|
|
|
|
return texture;
|
|
}
|
|
|
|
ITexture* CBurningVideoDriver::createDeviceDependentTextureCubemap(const io::path& name, const core::array<IImage*>& image)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//! Returns the maximum amount of primitives (mostly vertices) which
|
|
//! the device is able to render with one drawIndexedTriangleList
|
|
//! call.
|
|
u32 CBurningVideoDriver::getMaximalPrimitiveCount() const
|
|
{
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
|
|
//! Draws a shadow volume into the stencil buffer. To draw a stencil shadow, do
|
|
//! this: First, draw all geometry. Then use this method, to draw the shadow
|
|
//! volume. Next use IVideoDriver::drawStencilShadow() to visualize the shadow.
|
|
void CBurningVideoDriver::drawStencilShadowVolume(const core::array<core::vector3df>& triangles, bool zfail, u32 debugDataVisible)
|
|
{
|
|
const u32 count = triangles.size();
|
|
IBurningShader *shader = BurningShader [ ETR_STENCIL_SHADOW ];
|
|
|
|
CurrentShader = shader;
|
|
shader->setRenderTarget(RenderTargetSurface, ViewPort);
|
|
|
|
Material.org.MaterialType = video::EMT_SOLID;
|
|
Material.org.Lighting = false;
|
|
Material.org.ZWriteEnable = video::EZW_OFF;
|
|
Material.org.ZBuffer = ECFN_LESSEQUAL;
|
|
LightSpace.Flags &= ~VERTEXTRANSFORM;
|
|
|
|
//glStencilMask(~0);
|
|
//glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
if (true)// zpass does not work yet
|
|
{
|
|
Material.org.BackfaceCulling = true;
|
|
Material.org.FrontfaceCulling = false;
|
|
shader->setParam ( 0, 0 );
|
|
shader->setParam ( 1, 1 );
|
|
shader->setParam ( 2, 0 );
|
|
drawVertexPrimitiveList (triangles.const_pointer(), count, 0, count/3, (video::E_VERTEX_TYPE) 4, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) 4 );
|
|
//glStencilOp(GL_KEEP, incr, GL_KEEP);
|
|
//glDrawArrays(GL_TRIANGLES,0,count);
|
|
|
|
Material.org.BackfaceCulling = false;
|
|
Material.org.FrontfaceCulling = true;
|
|
shader->setParam ( 0, 0 );
|
|
shader->setParam ( 1, 2 );
|
|
shader->setParam ( 2, 0 );
|
|
drawVertexPrimitiveList (triangles.const_pointer(), count, 0, count/3, (video::E_VERTEX_TYPE) 4, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) 4 );
|
|
//glStencilOp(GL_KEEP, decr, GL_KEEP);
|
|
//glDrawArrays(GL_TRIANGLES,0,count);
|
|
}
|
|
else // zpass
|
|
{
|
|
Material.org.BackfaceCulling = true;
|
|
Material.org.FrontfaceCulling = false;
|
|
shader->setParam ( 0, 0 );
|
|
shader->setParam ( 1, 0 );
|
|
shader->setParam ( 2, 1 );
|
|
//glStencilOp(GL_KEEP, GL_KEEP, incr);
|
|
//glDrawArrays(GL_TRIANGLES,0,count);
|
|
|
|
Material.org.BackfaceCulling = false;
|
|
Material.org.FrontfaceCulling = true;
|
|
shader->setParam ( 0, 0 );
|
|
shader->setParam ( 1, 0 );
|
|
shader->setParam ( 2, 2 );
|
|
//glStencilOp(GL_KEEP, GL_KEEP, decr);
|
|
//glDrawArrays(GL_TRIANGLES,0,count);
|
|
}
|
|
}
|
|
|
|
//! Fills the stencil shadow with color. After the shadow volume has been drawn
|
|
//! into the stencil buffer using IVideoDriver::drawStencilShadowVolume(), use this
|
|
//! to draw the color of the shadow.
|
|
void CBurningVideoDriver::drawStencilShadow(bool clearStencilBuffer, video::SColor leftUpEdge,
|
|
video::SColor rightUpEdge, video::SColor leftDownEdge, video::SColor rightDownEdge)
|
|
{
|
|
if (!StencilBuffer)
|
|
return;
|
|
// draw a shadow rectangle covering the entire screen using stencil buffer
|
|
const u32 h = RenderTargetSurface->getDimension().Height;
|
|
const u32 w = RenderTargetSurface->getDimension().Width;
|
|
tVideoSample *dst;
|
|
u32 *stencil;
|
|
u32* const stencilBase=(u32*) StencilBuffer->lock();
|
|
|
|
for ( u32 y = 0; y < h; ++y )
|
|
{
|
|
dst = (tVideoSample*)RenderTargetSurface->getData() + ( y * w );
|
|
stencil = stencilBase + ( y * w );
|
|
|
|
for ( u32 x = 0; x < w; ++x )
|
|
{
|
|
if ( stencil[x] > 1 )
|
|
{
|
|
dst[x] = PixelBlend32 ( dst[x], leftUpEdge.color );
|
|
}
|
|
}
|
|
}
|
|
|
|
StencilBuffer->clear();
|
|
}
|
|
|
|
|
|
core::dimension2du CBurningVideoDriver::getMaxTextureSize() const
|
|
{
|
|
return core::dimension2du(SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE, SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE);
|
|
}
|
|
|
|
bool CBurningVideoDriver::queryTextureFormat(ECOLOR_FORMAT format) const
|
|
{
|
|
return format == BURNINGSHADER_COLOR_FORMAT;
|
|
}
|
|
|
|
bool CBurningVideoDriver::needsTransparentRenderPass(const irr::video::SMaterial& material) const
|
|
{
|
|
return CNullDriver::needsTransparentRenderPass(material) || material.isTransparent();
|
|
}
|
|
|
|
|
|
} // end namespace video
|
|
} // end namespace irr
|
|
|
|
#endif // _IRR_COMPILE_WITH_BURNINGSVIDEO_
|
|
|
|
|
|
#if defined(_IRR_WINDOWS_) && defined(_IRR_COMPILE_WITH_BURNINGSVIDEO_)
|
|
#include <windows.h>
|
|
|
|
struct dreadglobal
|
|
{
|
|
DWORD dreadid;
|
|
HANDLE dread;
|
|
irr::video::CBurningVideoDriver *driver;
|
|
HANDLE sync;
|
|
|
|
const irr::SIrrlichtCreationParameters* params;
|
|
irr::io::IFileSystem* io;
|
|
irr::video::IImagePresenter* presenter;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
dreadglobal burning_dread;
|
|
}
|
|
|
|
DWORD WINAPI dreadFun( void *p)
|
|
{
|
|
printf("Hi This is burning dread\n");
|
|
burning_dread.driver = new irr::video::CBurningVideoDriver(*burning_dread.params, burning_dread.io, burning_dread.presenter);
|
|
|
|
SetEvent (burning_dread.sync );
|
|
while ( 1 )
|
|
{
|
|
Sleep ( 1000 );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
//! creates a video driver
|
|
IVideoDriver* createBurningVideoDriver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, video::IImagePresenter* presenter)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
|
|
|
|
#ifdef _IRR_WINDOWS_
|
|
burning_dread.sync = CreateEventA ( 0, 0, 0, "burnevent0" );
|
|
burning_dread.params = ¶ms;
|
|
burning_dread.io = io;
|
|
burning_dread.presenter = presenter;
|
|
burning_dread.dread = CreateThread ( 0, 0, dreadFun, 0, 0, &burning_dread.dreadid );
|
|
WaitForSingleObject (burning_dread.sync, INFINITE );
|
|
return burning_dread.driver;
|
|
#else
|
|
return new CBurningVideoDriver(params, io, presenter);
|
|
#endif
|
|
|
|
#else
|
|
return 0;
|
|
#endif // _IRR_COMPILE_WITH_BURNINGSVIDEO_
|
|
}
|
|
|
|
|
|
|
|
} // end namespace video
|
|
} // end namespace irr
|
|
|