irrlicht/source/Irrlicht/CSoftwareDriver2.cpp

3964 lines
118 KiB
C++
Raw Normal View History

// 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 "CSoftwareTexture.h"
#include "CSoftwareTexture2.h"
#include "CSoftware2MaterialRenderer.h"
#include "S3DVertex.h"
#include "S4DVertex.h"
#include "CBlit.h"
// Matrix now here
template <class T>
bool mat44_transposed_inverse(irr::core::CMatrix4<T>& out, const irr::core::CMatrix4<T>& M)
{
const T* burning_restrict m = M.pointer();
double d =
(m[0] * m[5] - m[1] * m[4]) * (m[10] * m[15] - m[11] * m[14]) -
(m[0] * m[6] - m[2] * m[4]) * (m[9] * m[15] - m[11] * m[13]) +
(m[0] * m[7] - m[3] * m[4]) * (m[9] * m[14] - m[10] * m[13]) +
(m[1] * m[6] - m[2] * m[5]) * (m[8] * m[15] - m[11] * m[12]) -
(m[1] * m[7] - m[3] * m[5]) * (m[8] * m[14] - m[10] * m[12]) +
(m[2] * m[7] - m[3] * m[6]) * (m[8] * m[13] - m[9] * m[12]);
if (fabs(d) < DBL_MIN)
{
out.makeIdentity();
return false;
}
d = 1.0 / d;
T* burning_restrict o = out.pointer();
o[0] = (T)(d*(m[5] * (m[10] * m[15] - m[11] * m[14]) + m[6] * (m[11] * m[13] - m[9] * m[15]) + m[7] * (m[9] * m[14] - m[10] * m[13])));
o[4] = (T)(d*(m[9] * (m[2] * m[15] - m[3] * m[14]) + m[10] * (m[3] * m[13] - m[1] * m[15]) + m[11] * (m[1] * m[14] - m[2] * m[13])));
o[8] = (T)(d*(m[13] * (m[2] * m[7] - m[3] * m[6]) + m[14] * (m[3] * m[5] - m[1] * m[7]) + m[15] * (m[1] * m[6] - m[2] * m[5])));
o[12] = (T)(d*(m[1] * (m[7] * m[10] - m[6] * m[11]) + m[2] * (m[5] * m[11] - m[7] * m[9]) + m[3] * (m[6] * m[9] - m[5] * m[10])));
o[1] = (T)(d*(m[6] * (m[8] * m[15] - m[11] * m[12]) + m[7] * (m[10] * m[12] - m[8] * m[14]) + m[4] * (m[11] * m[14] - m[10] * m[15])));
o[5] = (T)(d*(m[10] * (m[0] * m[15] - m[3] * m[12]) + m[11] * (m[2] * m[12] - m[0] * m[14]) + m[8] * (m[3] * m[14] - m[2] * m[15])));
o[9] = (T)(d*(m[14] * (m[0] * m[7] - m[3] * m[4]) + m[15] * (m[2] * m[4] - m[0] * m[6]) + m[12] * (m[3] * m[6] - m[2] * m[7])));
o[13] = (T)(d*(m[2] * (m[7] * m[8] - m[4] * m[11]) + m[3] * (m[4] * m[10] - m[6] * m[8]) + m[0] * (m[6] * m[11] - m[7] * m[10])));
o[2] = (T)(d*(m[7] * (m[8] * m[13] - m[9] * m[12]) + m[4] * (m[9] * m[15] - m[11] * m[13]) + m[5] * (m[11] * m[12] - m[8] * m[15])));
o[6] = (T)(d*(m[11] * (m[0] * m[13] - m[1] * m[12]) + m[8] * (m[1] * m[15] - m[3] * m[13]) + m[9] * (m[3] * m[12] - m[0] * m[15])));
o[10] = (T)(d*(m[15] * (m[0] * m[5] - m[1] * m[4]) + m[12] * (m[1] * m[7] - m[3] * m[5]) + m[13] * (m[3] * m[4] - m[0] * m[7])));
o[14] = (T)(d*(m[3] * (m[5] * m[8] - m[4] * m[9]) + m[0] * (m[7] * m[9] - m[5] * m[11]) + m[1] * (m[4] * m[11] - m[7] * m[8])));
o[3] = (T)(d*(m[4] * (m[10] * m[13] - m[9] * m[14]) + m[5] * (m[8] * m[14] - m[10] * m[12]) + m[6] * (m[9] * m[12] - m[8] * m[13])));
o[7] = (T)(d*(m[8] * (m[2] * m[13] - m[1] * m[14]) + m[9] * (m[0] * m[14] - m[2] * m[12]) + m[10] * (m[1] * m[12] - m[0] * m[13])));
o[11] = (T)(d*(m[12] * (m[2] * m[5] - m[1] * m[6]) + m[13] * (m[0] * m[6] - m[2] * m[4]) + m[14] * (m[1] * m[4] - m[0] * m[5])));
o[15] = (T)(d*(m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8])));
return true;
}
#if 0
// difference to CMatrix4<T>::getInverse . higher precision in determinant. return identity on failure
template <class T>
bool mat44_inverse(CMatrix4<T>& out, const CMatrix4<T>& M)
{
const T* m = M.pointer();
double d =
(m[0] * m[5] - m[1] * m[4]) * (m[10] * m[15] - m[11] * m[14]) -
(m[0] * m[6] - m[2] * m[4]) * (m[9] * m[15] - m[11] * m[13]) +
(m[0] * m[7] - m[3] * m[4]) * (m[9] * m[14] - m[10] * m[13]) +
(m[1] * m[6] - m[2] * m[5]) * (m[8] * m[15] - m[11] * m[12]) -
(m[1] * m[7] - m[3] * m[5]) * (m[8] * m[14] - m[10] * m[12]) +
(m[2] * m[7] - m[3] * m[6]) * (m[8] * m[13] - m[9] * m[12]);
if (fabs(d) < DBL_MIN)
{
out.makeIdentity();
return false;
}
d = 1.0 / d;
T* o = out.pointer();
o[0] = (T)(d*(m[5] * (m[10] * m[15] - m[11] * m[14]) + m[6] * (m[11] * m[13] - m[9] * m[15]) + m[7] * (m[9] * m[14] - m[10] * m[13])));
o[1] = (T)(d*(m[9] * (m[2] * m[15] - m[3] * m[14]) + m[10] * (m[3] * m[13] - m[1] * m[15]) + m[11] * (m[1] * m[14] - m[2] * m[13])));
o[2] = (T)(d*(m[13] * (m[2] * m[7] - m[3] * m[6]) + m[14] * (m[3] * m[5] - m[1] * m[7]) + m[15] * (m[1] * m[6] - m[2] * m[5])));
o[3] = (T)(d*(m[1] * (m[7] * m[10] - m[6] * m[11]) + m[2] * (m[5] * m[11] - m[7] * m[9]) + m[3] * (m[6] * m[9] - m[5] * m[10])));
o[4] = (T)(d*(m[6] * (m[8] * m[15] - m[11] * m[12]) + m[7] * (m[10] * m[12] - m[8] * m[14]) + m[4] * (m[11] * m[14] - m[10] * m[15])));
o[5] = (T)(d*(m[10] * (m[0] * m[15] - m[3] * m[12]) + m[11] * (m[2] * m[12] - m[0] * m[14]) + m[8] * (m[3] * m[14] - m[2] * m[15])));
o[6] = (T)(d*(m[14] * (m[0] * m[7] - m[3] * m[4]) + m[15] * (m[2] * m[4] - m[0] * m[6]) + m[12] * (m[3] * m[6] - m[2] * m[7])));
o[7] = (T)(d*(m[2] * (m[7] * m[8] - m[4] * m[11]) + m[3] * (m[4] * m[10] - m[6] * m[8]) + m[0] * (m[6] * m[11] - m[7] * m[10])));
o[8] = (T)(d*(m[7] * (m[8] * m[13] - m[9] * m[12]) + m[4] * (m[9] * m[15] - m[11] * m[13]) + m[5] * (m[11] * m[12] - m[8] * m[15])));
o[9] = (T)(d*(m[11] * (m[0] * m[13] - m[1] * m[12]) + m[8] * (m[1] * m[15] - m[3] * m[13]) + m[9] * (m[3] * m[12] - m[0] * m[15])));
o[10] = (T)(d*(m[15] * (m[0] * m[5] - m[1] * m[4]) + m[12] * (m[1] * m[7] - m[3] * m[5]) + m[13] * (m[3] * m[4] - m[0] * m[7])));
o[11] = (T)(d*(m[3] * (m[5] * m[8] - m[4] * m[9]) + m[0] * (m[7] * m[9] - m[5] * m[11]) + m[1] * (m[4] * m[11] - m[7] * m[8])));
o[12] = (T)(d*(m[4] * (m[10] * m[13] - m[9] * m[14]) + m[5] * (m[8] * m[14] - m[10] * m[12]) + m[6] * (m[9] * m[12] - m[8] * m[13])));
o[13] = (T)(d*(m[8] * (m[2] * m[13] - m[1] * m[14]) + m[9] * (m[0] * m[14] - m[2] * m[12]) + m[10] * (m[1] * m[12] - m[0] * m[13])));
o[14] = (T)(d*(m[12] * (m[2] * m[5] - m[1] * m[6]) + m[13] * (m[0] * m[6] - m[2] * m[4]) + m[14] * (m[1] * m[4] - m[0] * m[5])));
o[15] = (T)(d*(m[0] * (m[5] * m[10] - m[6] * m[9]) + m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8])));
return true;
}
#endif
// void CMatrix4<T>::transformVec4(T *out, const T * in) const
template <class T>
inline void transformVec4Vec4(const irr::core::CMatrix4<T>& m, T* burning_restrict out, const T* burning_restrict in)
{
const T* burning_restrict M = m.pointer();
out[0] = in[0] * M[0] + in[1] * M[4] + in[2] * M[8] + in[3] * M[12];
out[1] = in[0] * M[1] + in[1] * M[5] + in[2] * M[9] + in[3] * M[13];
out[2] = in[0] * M[2] + in[1] * M[6] + in[2] * M[10] + in[3] * M[14];
out[3] = in[0] * M[3] + in[1] * M[7] + in[2] * M[11] + in[3] * M[15];
}
#if 0
// void CMatrix4<T>::transformVect(T *out, const core::vector3df &in) const
template <class T>
inline void transformVec3Vec4(const irr::core::CMatrix4<T>& m,T* burning_restrict out, const core::vector3df &in)
{
const T* burning_restrict M = m.pointer();
out[0] = in.X*M[0] + in.Y*M[4] + in.Z*M[8] + M[12];
out[1] = in.X*M[1] + in.Y*M[5] + in.Z*M[9] + M[13];
out[2] = in.X*M[2] + in.Y*M[6] + in.Z*M[10] + M[14];
out[3] = in.X*M[3] + in.Y*M[7] + in.Z*M[11] + M[15];
}
#endif
template <class T>
inline void rotateVec3Vec4(const irr::core::CMatrix4<T>& m, T* burning_restrict out, const T* burning_restrict in)
{
const T* burning_restrict M = m.pointer();
out[0] = in[0] * M[0] + in[1] * M[4] + in[2] * M[8];
out[1] = in[0] * M[1] + in[1] * M[5] + in[2] * M[9];
out[2] = in[0] * M[2] + in[1] * M[6] + in[2] * M[10];
out[3] = 0.f;
}
//based on https://github.com/ekmett/approximate/blob/master/cbits/fast.c powf_fast_precise
static inline float powf_limit(const float a, const float b)
{
if (a <= 0.0000001f) return 0.f;
else if (a >= 1.f) return 1.f;
/* calculate approximation with fraction of the exponent */
int e = (int)b;
union { float f; int x; } u = { a };
u.x = (int)((b - e) * (u.x - 1065353216) + 1065353216);
float r = 1.0f;
float ua = a;
while (e) {
if (e & 1) {
r *= ua;
}
if (ua < 0.00000001f)
break;
ua *= ua;
e >>= 1;
}
r *= u.f;
return r;
}
#if defined(Tweak_Burning)
// run time parameter
struct tweakBurning
{
tweakBurning()
{
current = 11;
step = 0.0001f;
ndc_shrink_x = -0.75f;
ndc_scale_x = 0.5f;
ndc_trans_x = -0.5f;
ndc_shrink_y = -0.75f;
ndc_scale_y = -0.5f;
ndc_trans_y = -0.5f;
tex_w_add = 0.f;
tex_h_add = 0.f;
tex_cx_add = 0.f;
tex_cy_add = 0.f;
AreaMinDrawSize = 0.001f;
}
int current;
union
{
struct {
f32 step;
f32 ndc_shrink_x;
f32 ndc_scale_x;
f32 ndc_trans_x;
f32 ndc_shrink_y;
f32 ndc_scale_y;
f32 ndc_trans_y;
f32 tex_w_add;
f32 tex_cx_add;
f32 tex_h_add;
f32 tex_cy_add;
f32 AreaMinDrawSize; //! minimal visible covered area for primitive
};
f32 val[16];
};
static const char* const name[16];
void postEventFromUser(const SEvent& e);
};
const char* const tweakBurning::name[] = { "step",
"ndc_shrink_x","ndc_scale_x","ndc_trans_x",
"ndc_shrink_y","ndc_scale_y","ndc_trans_y",
"tex_w_add","tex_cx_add","tex_h_add","tex_cy_add",
"dc_area",0 };
void tweakBurning::postEventFromUser(const SEvent& e)
{
int show = 0;
if (e.EventType == EET_KEY_INPUT_EVENT)
{
switch (e.KeyInput.Key)
{
case KEY_KEY_1: step *= 0.9f; if (step < 0.00001f) step = 0.0001f; show = 2; break;
case KEY_KEY_2: step *= 1.1f; show = 2; break;
case KEY_KEY_3: if (!e.KeyInput.PressedDown) { current -= 1; if (current < 1) current = 11; show = 1; } break;
case KEY_KEY_4: if (!e.KeyInput.PressedDown) { current += 1; if (current > 11) current = 1; show = 1; } break;
case KEY_KEY_5: val[current] -= e.KeyInput.Shift ? step * 100.f : step; show = 1; break;
case KEY_KEY_6: val[current] += e.KeyInput.Shift ? step * 100.f : step; show = 1; break;
default:
break;
}
}
if (show)
{
if (step < 0.0001f) step = 0.0001f;
char buf[256];
if (show == 2) sprintf(buf, "%s %f\n", name[0], val[0]);
else sprintf(buf, "%s %f\n", name[current], val[current]);
os::Printer::print(buf);
}
}
void CBurningVideoDriver::postEventFromUser(const void* sevent)
{
if (sevent) Tweak.postEventFromUser(*(const SEvent*)sevent);
}
tweakBurning Tweak;
#endif //defined(Tweak_Burning)
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 )
{
//enable fpu exception
//unsigned int fp_control_state = _controlfp(_EM_INEXACT, _MCW_EM);
#ifdef _DEBUG
setDebugName("CBurningVideoDriver");
#endif
VertexCache_map_source_format();
// 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(),8);
}
DriverAttributes->setAttribute("MaxIndices", 1<<16);
DriverAttributes->setAttribute("MaxTextures", BURNING_MATERIAL_MAX_TEXTURES);
DriverAttributes->setAttribute("MaxTextureSize", SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE ? SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE : 1<<20);
DriverAttributes->setAttribute("MaxLights", 1024 ); //glsl::gl_MaxLights);
DriverAttributes->setAttribute("MaxTextureLODBias", 16.f);
DriverAttributes->setAttribute("Version", 50);
// create triangle renderers
memset( BurningShader, 0, sizeof ( BurningShader ) );
//BurningShader[ETR_FLAT] = createTRFlat2(DepthBuffer);
//BurningShader[ETR_FLAT_WIRE] = createTRFlatWire2(DepthBuffer);
BurningShader[ETR_GOURAUD] = createTriangleRendererGouraud2(this);
BurningShader[ETR_GOURAUD_NOZ] = createTriangleRendererGouraudNoZ2(this);
//BurningShader[ETR_GOURAUD_ALPHA] = createTriangleRendererGouraudAlpha2(this );
BurningShader[ETR_GOURAUD_ALPHA_NOZ] = createTRGouraudAlphaNoZ2(this ); // 2D
//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_TRANSPARENT_REFLECTION_2_LAYER] = createTriangleRendererTexture_transparent_reflection_2_layer(this);
//BurningShader[ETR_REFERENCE] = createTriangleRendererReference ( this );
BurningShader[ETR_COLOR] = create_burning_shader_color(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 ( smr ); // 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 ( tmr ); // EMT_TRANSPARENT_REFLECTION_2_LAYER,
addMaterialRenderer ( smr ); // EMT_NORMAL_MAP_SOLID,
addMaterialRenderer ( tmr ); // 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
EyeSpace.reset();
EyeSpace.resetFog();
// select the right renderer
setMaterial(Material.org);
}
//! destructor
CBurningVideoDriver::~CBurningVideoDriver()
{
// delete Backbuffer
if (BackBuffer)
{
BackBuffer->drop();
BackBuffer = 0;
}
// delete triangle renderers
for (s32 i=0; i<ETR2_COUNT; ++i)
{
if (BurningShader[i])
{
BurningShader[i]->drop();
BurningShader[i] = 0;
}
}
// delete Additional buffer
if (StencilBuffer)
{
StencilBuffer->drop();
StencilBuffer = 0;
}
if (DepthBuffer)
{
DepthBuffer->drop();
DepthBuffer = 0;
}
if (RenderTargetTexture)
{
RenderTargetTexture->drop();
RenderTargetTexture = 0;
}
if (RenderTargetSurface)
{
RenderTargetSurface->drop();
RenderTargetSurface = 0;
}
}
//! queries the features of the driver, returns true if feature is available
bool CBurningVideoDriver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
{
int on = 0;
switch (feature)
{
#ifdef SOFTWARE_DRIVER_2_BILINEAR
case EVDF_BILINEAR_FILTER:
on = 1;
break;
#endif
#ifdef SOFTWARE_DRIVER_2_MIPMAPPING
case EVDF_MIP_MAP:
on = 1;
break;
#endif
case EVDF_STENCIL_BUFFER:
on = StencilBuffer != 0;
break;
case EVDF_RENDER_TO_TARGET:
case EVDF_MULTITEXTURE:
case EVDF_HARDWARE_TL:
case EVDF_TEXTURE_NSQUARE:
case EVDF_TEXTURE_MATRIX:
on = 1;
break;
case EVDF_DEPTH_CLAMP: // shadow
on = 1;
break;
case EVDF_TEXTURE_NPOT: // for 2D
on = 0;
break;
case EVDF_ARB_FRAGMENT_PROGRAM_1:
case EVDF_ARB_VERTEX_PROGRAM_1:
on = 1;
break;
#if defined(PATCH_SUPERTUX_8_0_1)
case EVDF_TEXTURE_NPOT:
case EVDF_ARB_GLSL:
on = 1;
break;
#endif
#if defined(SOFTWARE_DRIVER_2_2D_AS_3D)
#if defined(IRRLICHT_FREE_CANVAS)
case EVDF_VIEWPORT_SCALE_GUI:
on = 1;
break;
#endif
#endif
default:
on = 0;
break;
}
return on && FeatureEnabled[feature];
}
//! Create render target.
IRenderTarget* CBurningVideoDriver::addRenderTarget()
{
CSoftwareRenderTarget2* renderTarget = new CSoftwareRenderTarget2(this);
RenderTargets.push_back(renderTarget);
return renderTarget;
}
//matrix multiplication
void CBurningVideoDriver::transform_calc(E_TRANSFORMATION_STATE_BURNING_VIDEO state)
{
size_t* flag = TransformationFlag[TransformationStack];
if (flag[state] & ETF_VALID ) return;
//check
int ok = 0;
switch ( state )
{
case ETS_PROJ_MODEL_VIEW:
if ( 0 == (flag[ETS_VIEW_PROJECTION] & ETF_VALID) ) transform_calc (ETS_VIEW_PROJECTION);
ok = flag[ETS_WORLD] & flag[ETS_VIEW] & flag[ETS_PROJECTION] & flag[ETS_VIEW_PROJECTION] & ETF_VALID;
break;
case ETS_VIEW_PROJECTION:
ok = flag[ETS_VIEW] & flag[ETS_PROJECTION] & ETF_VALID;
break;
case ETS_MODEL_VIEW:
ok = flag[ETS_WORLD] & flag[ETS_VIEW] & ETF_VALID;
break;
case ETS_NORMAL:
ok = flag[ETS_MODEL_VIEW] & ETF_VALID;
break;
default:
break;
}
if ( !ok )
{
char buf[256];
sprintf(buf,"transform_calc not valid for %d\n",state);
os::Printer::log(buf, ELL_WARNING);
}
core::matrix4* matrix = Transformation[TransformationStack];
switch ( state )
{
case ETS_PROJ_MODEL_VIEW:
if (flag[ETS_WORLD] & ETF_IDENTITY )
{
matrix[state] = matrix[ETS_VIEW_PROJECTION];
}
else
{
matrix[state].setbyproduct_nocheck(matrix[ETS_VIEW_PROJECTION], matrix[ETS_WORLD]);
}
break;
case ETS_VIEW_PROJECTION:
matrix[state].setbyproduct_nocheck (matrix[ETS_PROJECTION], matrix[ETS_VIEW]);
break;
case ETS_MODEL_VIEW:
if ( flag[ETS_WORLD] & ETF_IDENTITY )
{
matrix[state] = matrix[ETS_VIEW];
}
else
{
matrix[state].setbyproduct_nocheck(matrix[ETS_VIEW], matrix[ETS_WORLD]);
}
break;
case ETS_NORMAL:
mat44_transposed_inverse(matrix[state], matrix[ETS_MODEL_VIEW]);
break;
default:
break;
}
flag[state] |= ETF_VALID;
}
//! sets transformation
void CBurningVideoDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat)
{
size_t* flag = TransformationFlag[TransformationStack];
core::matrix4* matrix = Transformation[TransformationStack];
#if 0
int changed = 1;
if (flag[state] & ETF_VALID)
{
changed = memcmp(mat.pointer(), matrix[state].pointer(), sizeof(mat));
}
if (changed)
#endif
{
matrix[state] = mat;
flag[state] |= ETF_VALID;
}
//maybe identity (mostly for texturematrix to avoid costly multiplication)
#if defined ( USE_MATRIX_TEST )
burning_setbit( TransformationFlag[state], mat.getDefinitelyIdentityMatrix(), ETF_IDENTITY );
#else
burning_setbit(flag[state],
!memcmp(mat.pointer(), core::IdentityMatrix.pointer(),sizeof(mat)),ETF_IDENTITY
);
#endif
#if 0
if ( changed )
#endif
switch ( state )
{
case ETS_PROJECTION:
flag[ETS_PROJ_MODEL_VIEW] &= ~ETF_VALID;
flag[ETS_VIEW_PROJECTION] &= ~ETF_VALID;
break;
case ETS_VIEW:
flag[ETS_PROJ_MODEL_VIEW] &= ~ETF_VALID;
flag[ETS_VIEW_PROJECTION] &= ~ETF_VALID;
flag[ETS_MODEL_VIEW] &= ~ETF_VALID;
flag[ETS_NORMAL] &= ~ETF_VALID;
break;
case ETS_WORLD:
flag[ETS_PROJ_MODEL_VIEW] &= ~ETF_VALID;
flag[ETS_MODEL_VIEW] &= ~ETF_VALID;
flag[ETS_NORMAL] &= ~ETF_VALID;
break;
case ETS_TEXTURE_0:
case ETS_TEXTURE_1:
case ETS_TEXTURE_2:
case ETS_TEXTURE_3:
#if _IRR_MATERIAL_MAX_TEXTURES_>4
case ETS_TEXTURE_4:
#endif
#if _IRR_MATERIAL_MAX_TEXTURES_>5
case ETS_TEXTURE_5:
#endif
#if _IRR_MATERIAL_MAX_TEXTURES_>6
case ETS_TEXTURE_6:
#endif
#if _IRR_MATERIAL_MAX_TEXTURES_>7
case ETS_TEXTURE_7:
#endif
if ( 0 == (flag[state] & ETF_IDENTITY ) )
{
EyeSpace.TL_Flag |= TL_TEXTURE_TRANSFORM;
}
break;
default:
break;
}
}
//! Returns the transformation set by setTransform
const core::matrix4& CBurningVideoDriver::getTransform(E_TRANSFORMATION_STATE state) const
{
return Transformation[TransformationStack][state];
}
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();
core::dimension2d<u32> current = RenderTargetSize;
RenderTargetSurface = image;
RenderTargetSize.Width = 0;
RenderTargetSize.Height = 0;
if (RenderTargetSurface)
{
RenderTargetSurface->grab();
RenderTargetSize = RenderTargetSurface->getDimension();
}
int not_changed = current == RenderTargetSize;
burning_setbit(TransformationFlag[0][ETS_PROJECTION], not_changed, ETF_VALID);
burning_setbit(TransformationFlag[1][ETS_PROJECTION], not_changed, ETF_VALID);
setViewPort(core::recti(RenderTargetSize));
if (DepthBuffer)
DepthBuffer->setSize(RenderTargetSize);
if (StencilBuffer)
StencilBuffer->setSize(RenderTargetSize);
}
//--------- Transform from NDC to DC, transform TexCoo ----------------------------------------------
// controls subtexel and fill convention
#if defined(SOFTWARE_DRIVER_2_SUBTEXEL)
#define SOFTWARE_DRIVER_2_PIXEL_CENTER -0.5f
#else
#define SOFTWARE_DRIVER_2_PIXEL_CENTER -0.5f
#endif
#if 1
// used to scale <-1,-1><1,1> to viewport
void buildNDCToDCMatrix(f32* m, const core::rect<s32>& viewport, f32 tx)
{
m[0] = (viewport.getWidth() + tx) * 0.5f;
m[1] = SOFTWARE_DRIVER_2_PIXEL_CENTER + ((viewport.UpperLeftCorner.X + viewport.LowerRightCorner.X) * 0.5f);
m[2] = (viewport.getHeight() + tx) * -0.5f;
m[3] = SOFTWARE_DRIVER_2_PIXEL_CENTER + ((viewport.UpperLeftCorner.Y + viewport.LowerRightCorner.Y) * 0.5f);
}
#else
// used to scale <-1,-1><1,1> to viewport
void buildNDCToDCMatrix( core::matrix4& out, const core::rect<s32>& viewport)
{
//guard band to stay in screen bounds.(empirical). get holes left side otherwise or out of screen
//TODO: match openGL or D3D.
//assumption pixel center, top-left rule and rounding error projection deny exact match without additional clipping
//or triangle render scanline doesn't step on correct texel center
//or sampler is not on texel center
f32* m = out.pointer();
#if defined(Tweak_Burning) && 0
m[0] = (viewport.getWidth() + Tweak.ndc_shrink_x ) * Tweak.ndc_scale_x;
m[5] = (viewport.getHeight() + Tweak.ndc_shrink_y ) * Tweak.ndc_scale_y;
m[12] = Tweak.ndc_trans_x + ( (viewport.UpperLeftCorner.X + viewport.LowerRightCorner.X ) * 0.5f );
m[13] = Tweak.ndc_trans_y + ( (viewport.UpperLeftCorner.Y + viewport.LowerRightCorner.Y ) * 0.5f );
#endif
m[0] = (viewport.getWidth() - 0.75f ) * 0.5f;
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
m[4] = 0.f;
m[5] = (viewport.getHeight() - 0.75f ) * -0.5f;
m[6] = 0.f;
m[7] = 0.f;
m[8] = 0.f;
m[9] = 0.f;
m[10] = 1.f;
m[11] = 0.f;
m[12] = SOFTWARE_DRIVER_2_PIXEL_CENTER + ( (viewport.UpperLeftCorner.X + viewport.LowerRightCorner.X ) * 0.5f );
m[13] = SOFTWARE_DRIVER_2_PIXEL_CENTER + ( (viewport.UpperLeftCorner.Y + viewport.LowerRightCorner.Y ) * 0.5f );
m[14] = 0.f;
m[15] = 1.f;
}
#endif
//--------- Transform from NCD to DC ----------------------------------------------
//! 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);
buildNDCToDCMatrix(Transformation_ETS_CLIPSCALE[0], ViewPort,-0.375f);
buildNDCToDCMatrix(Transformation_ETS_CLIPSCALE[1], ViewPort, 0.f); // OverrideMaterial2DEnabled ? -0.375f : 0.f);
if (CurrentShader)
CurrentShader->setRenderTarget(RenderTargetSurface, ViewPort);
}
void CBurningVideoDriver::setScissor(int x, int y, int width, int height)
{
AbsRectangle v0;
v0.x0 = x;
v0.y0 = y;
v0.x1 = x+width;
v0.y1 = y+width;
const core::dimension2d<u32>& rt = getCurrentRenderTargetSize();
AbsRectangle v1;
v1.x0 = 0;
v1.y0 = 0;
v1.x1 = rt.Width;
v1.y1 = rt.Height;
intersect(Scissor, v0, v1);
}
/*
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
cam is (0,0,-1)
*/
const sVec4 CBurningVideoDriver::NDCPlane[6+2] =
{
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] );
burning_setbit32( flag, dotPlane <= 0.f, 1 << i );
}
// this is the base for ndc frustum <-w,w>,<-w,w>,<-w,w>
burning_setbit32( flag, ( v->Pos.z - v->Pos.w ) <= 0.f, 1 );
burning_setbit32( flag, (-v->Pos.z - v->Pos.w ) <= 0.f, 2 );
burning_setbit32( flag, ( v->Pos.x - v->Pos.w ) <= 0.f, 4 );
burning_setbit32( flag, (-v->Pos.x - v->Pos.w ) <= 0.f, 8 );
burning_setbit32( flag, ( v->Pos.y - v->Pos.w ) <= 0.f, 16 );
burning_setbit32( flag, (-v->Pos.y - v->Pos.w ) <= 0.f, 32 );
*/
#ifdef IRRLICHT_FAST_MATH
REALINLINE size_t CBurningVideoDriver::clipToFrustumTest ( const s4DVertex* v ) const
{
size_t flag;
f32 test[8];
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;
const u32* a = F32_AS_U32_POINTER(test);
flag = (a[0] ) >> 31;
flag |= (a[1] & 0x80000000) >> 30;
flag |= (a[2] & 0x80000000) >> 29;
flag |= (a[3] & 0x80000000) >> 28;
flag |= (a[4] & 0x80000000) >> 27;
flag |= (a[5] & 0x80000000) >> 26;
/*
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 size_t clipToFrustumTest ( const s4DVertex* v )
{
size_t flag = 0;
flag |= v->Pos.z <= v->Pos.w ? VERTEX4D_CLIP_NEAR : 0;
flag |= -v->Pos.z <= v->Pos.w ? VERTEX4D_CLIP_FAR : 0;
flag |= v->Pos.x <= v->Pos.w ? VERTEX4D_CLIP_LEFT : 0;
flag |= -v->Pos.x <= v->Pos.w ? VERTEX4D_CLIP_RIGHT : 0;
flag |= v->Pos.y <= v->Pos.w ? VERTEX4D_CLIP_BOTTOM : 0;
flag |= -v->Pos.y <= v->Pos.w ? VERTEX4D_CLIP_TOP : 0;
/*
for ( u32 i = 0; i <= 6; ++i )
{
if (v->Pos.dot_xyzw(NDCPlane[i]) <= 0.f) flag |= ((size_t)1) << i;
}
*/
return flag;
}
#endif // _MSC_VER
size_t clipToHyperPlane (
s4DVertexPair* burning_restrict dest,
const s4DVertexPair* burning_restrict source,
const size_t inCount,
const sVec4& plane
)
{
size_t outCount = 0;
s4DVertexPair* out = dest;
const s4DVertex* a;
const s4DVertex* b = source;
ipoltype bDotPlane;
bDotPlane = b->Pos.dot_xyzw( plane );
/*
for( u32 i = 1; i < inCount + 1; ++i)
{
#if 0
a = source + (i%inCount)*2;
#else
const s32 condition = i - inCount;
const s32 index = (( ( condition >> 31 ) & ( i ^ condition ) ) ^ condition ) << 1;
a = source + index;
#endif
*/
//Sutherland<6E>Hodgman
for(size_t i = 0; i < inCount; ++i)
{
a = source + (i == inCount-1 ? 0 : s4DVertex_ofs(i+1));
// current point inside
if (ipol_lower_equal_0(a->Pos.dot_xyzw( plane )) )
{
// last point outside
if (ipol_greater_0( bDotPlane ) )
{
// intersect line segment with plane
out->interpolate(*b, *a, bDotPlane / (b->Pos - a->Pos).dot_xyzw(plane) );
out += sizeof_s4DVertexPairRel;
outCount += 1;
}
// copy current to out
//*out = *a;
memcpy_s4DVertexPair( out, a);
b = out;
out += sizeof_s4DVertexPairRel;
outCount += 1;
}
else
{
// current point outside
if (ipol_lower_equal_0( bDotPlane ) )
{
// previous was inside
// intersect line segment with plane
out->interpolate(*b, *a, bDotPlane / (b->Pos - a->Pos).dot_xyzw(plane) );
out += sizeof_s4DVertexPairRel;
outCount += 1;
}
// pointer
b = a;
}
bDotPlane = b->Pos.dot_xyzw( plane );
}
return outCount;
}
/*
Clip on all planes. Clipper.data
clipmask per face
*/
size_t CBurningVideoDriver::clipToFrustum(const size_t vIn /*, const size_t clipmask_for_face*/ )
{
s4DVertexPair* v0 = Clipper.data;
s4DVertexPair* v1 = Clipper_temp.data;
size_t vOut = vIn;
//clear all clipping & projected flags
const u32 flag = v0[0].flag & VERTEX4D_FORMAT_MASK;
for (size_t g = 0; g != Clipper.ElementSize; ++g)
{
v0[g].flag = flag;
v1[g].flag = flag;
}
#if 0
for (size_t i = 0; i < 6; ++i)
{
v0 = i & 1 ? Clipper_temp.data : Clipper.data;
v1 = i & 1 ? Clipper.data : Clipper_temp.data;
//clipMask checked outside - always clip all planes
#if 0
if (0 == (clipMask & ((size_t)1<<i)))
{
vOut = vIn;
memcpy_s4DVertexPair(v1, v0);
}
else
#endif
{
vOut = clipToHyperPlane(v1, v0, vOut, NDCPlane[i]);
if (vOut < vIn) return vOut;
}
}
#endif
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 ( DC ) Space
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
*/
//aliasing problems! [dest = source + 1]
inline void CBurningVideoDriver::ndc_2_dc_and_project (s4DVertexPair* dest,const s4DVertexPair *source, const size_t vIn ) const
{
const f32* dc = Transformation_ETS_CLIPSCALE[TransformationStack];
for ( size_t g = 0; g != vIn; g += sizeof_s4DVertexPairRel)
{
//cache doesn't work anymore?
//if ( dest[g].flag & VERTEX4D_PROJECTED )
// continue;
//dest[g].flag = source[g].flag | VERTEX4D_PROJECTED;
const f32 iw = reciprocal_zero (source[g].Pos.w);
// to device coordinates
dest[g].Pos.x = iw * source[g].Pos.x * dc[0] + dc[1];
dest[g].Pos.y = iw * source[g].Pos.y * dc[2] + dc[3];
//burning uses direct Z. for OpenGL it should be -Z,[-1;1] and texture flip
#if !defined(SOFTWARE_DRIVER_2_USE_WBUFFER) || 1
dest[g].Pos.z = -iw * source[g].Pos.z * 0.5f + 0.5f;
#endif
dest[g].Pos.w = iw;
// Texture Coordinates will be projected after mipmap selection
// satisfy write-combiner
#if 1
#if BURNING_MATERIAL_MAX_TEXTURES > 0
dest[g].Tex[0] = source[g].Tex[0];
#endif
#if BURNING_MATERIAL_MAX_TEXTURES > 1
dest[g].Tex[1] = source[g].Tex[1];
#endif
#if BURNING_MATERIAL_MAX_TEXTURES > 2
dest[g].Tex[2] = source[g].Tex[2];
#endif
#if BURNING_MATERIAL_MAX_TEXTURES > 3
dest[g].Tex[3] = source[g].Tex[3];
#endif
#endif
#if BURNING_MATERIAL_MAX_COLORS > 0
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
dest[g].Color[0] = source[g].Color[0] * iw; // alpha?
#else
dest[g].Color[0] = source[g].Color[0];
#endif
#endif
#if BURNING_MATERIAL_MAX_COLORS > 1
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
dest[g].Color[1] = source[g].Color[1] * iw; // alpha?
#else
dest[g].Color[1] = source[g].Color[1];
#endif
#endif
#if BURNING_MATERIAL_MAX_COLORS > 2
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
dest[g].Color[2] = source[g].Color[2] * iw; // alpha?
#else
dest[g].Color[2] = source[g].Color[2];
#endif
#endif
#if BURNING_MATERIAL_MAX_COLORS > 3
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
dest[g].Color[3] = source[g].Color[3] * iw; // alpha?
#else
dest[g].Color[3] = source[g].Color[3];
#endif
#endif
#if BURNING_MATERIAL_MAX_LIGHT_TANGENT > 0
dest[g].LightTangent[0] = source[g].LightTangent[0] * iw;
#endif
}
}
/*!
crossproduct in projected 2D, face
*/
REALINLINE f32 CBurningVideoDriver::screenarea_inside(const s4DVertexPair* burning_restrict const face[] ) const
{
return ( ((face[1]+1)->Pos.x - (face[0]+1)->Pos.x) * ((face[2]+1)->Pos.y - (face[0]+1)->Pos.y) ) -
( ((face[2]+1)->Pos.x - (face[0]+1)->Pos.x) * ((face[1]+1)->Pos.y - (face[0]+1)->Pos.y) );
/*
float signedArea = 0;
for (int k = 1; k < output->count; k++) {
signedArea += (output->vertices[k - 1].values[0] * output->vertices[k - 0].values[1]);
signedArea -= (output->vertices[k - 0].values[0] * output->vertices[k - 1].values[1]);
}
*/
}
#if 0
static inline f32 dot(const sVec2& a,const sVec2& b) { return a.x * b.x + a.y * b.y; }
sVec2 dFdx(const sVec2& v) { return v; }
sVec2 dFdy(const sVec2& v) { return v; }
f32 MipmapLevel(const sVec2& uv, const sVec2& textureSize)
{
sVec2 dx = dFdx(uv * textureSize.x);
sVec2 dy = dFdy(uv * textureSize.y);
f32 d = core::max_(dot(dx, dx), dot(dy, dy));
return log2f(sqrtf(d));
}
#endif
#define MAT_TEXTURE(tex) ( (video::CSoftwareTexture2*) Material.org.getTexture ( (u32)tex ) )
/*!
calculate from unprojected.
attribute need not to follow winding rule from position.
Edge-walking problem
Texture Wrapping problem
Atlas problem
*/
REALINLINE s32 CBurningVideoDriver::lodFactor_inside(const s4DVertexPair* burning_restrict const face[],
const size_t m, f32 dc_area, f32 lod_bias) const
{
/*
sVec2 a(v[1]->Tex[tex].x - v[0]->Tex[tex].x,v[1]->Tex[tex].y - v[0]->Tex[tex].y);
sVec2 b(v[2]->Tex[tex].x - v[0]->Tex[tex].x,v[2]->Tex[tex].y - v[0]->Tex[tex].y);
f32 area = a.x * b.y - b.x * a.y;
*/
/*
degenerate(A, B, C, minarea) = ((B - A).cross(C - A)).lengthSquared() < (4.0f * minarea * minarea);
check for collapsed or "long thin triangles"
*/
ieee754 signedArea;
ieee754 t[4];
t[0].f = face[1]->Tex[m].x - face[0]->Tex[m].x;
t[1].f = face[1]->Tex[m].y - face[0]->Tex[m].y;
t[2].f = face[2]->Tex[m].x - face[0]->Tex[m].x;
t[3].f = face[2]->Tex[m].y - face[0]->Tex[m].y;
//crossproduct in projected 2D -> screen area triangle
signedArea.f = t[0].f * t[3].f - t[2].f * t[1].f;
//signedArea =
// ((face[1]->Tex[m].x - face[0]->Tex[m].x) * (face[2]->Tex[m].y - face[0]->Tex[m].y))
// - ((face[2]->Tex[m].x - face[0]->Tex[m].x) * (face[1]->Tex[m].y - face[0]->Tex[m].y));
//if (signedArea*signedArea <= 0.00000000001f)
if (signedArea.fields.exp == 0 )
{
ieee754 _max;
_max.u = t[0].abs.frac_exp;
if (t[1].abs.frac_exp > _max.u) _max.u = t[1].abs.frac_exp;
if (t[2].abs.frac_exp > _max.u) _max.u = t[2].abs.frac_exp;
if (t[3].abs.frac_exp > _max.u) _max.u = t[3].abs.frac_exp;
signedArea.u = _max.fields.exp ? _max.u : ieee754_one;
/*
//dot,length
ieee754 v[2];
v[0].f = t[0] * t[2];
v[1].f = t[1] * t[3];
//signedArea.f = t[4] > t[5] ? t[4] : t[5];
signedArea.u = v[0].fields.frac > v[1].fields.frac ? v[0].u : v[1].u;
if (signedArea.fields.exp == 0)
{
return -1;
}
*/
}
//only guessing: take more detail (lower mipmap) in light+bump textures
//assume transparent add is ~50% transparent -> more detail
const u32* d = MAT_TEXTURE(m)->getMipMap0_Area();
f32 texelspace = d[0] * d[1] * lod_bias; //(m ? 0.5f : 0.5f);
ieee754 ratio;
ratio.f = (signedArea.f * texelspace) * dc_area;
ratio.fields.sign = 0;
//log2(0)==denormal [ use high lod] [ only if dc_area == 0 checked outside ]
#if 0
if (ratio.fields.exp == 0)
{
int g = 1;
}
#endif
//log2
return (ratio.fields.exp & 0x80) ? ratio.fields.exp - 127 : 0; /*denormal very high lod*/
//return (ratio.f <= 1.f) ? 0 : 1;
//f32 texArea = MAT_TEXTURE(m)->getLODFactor(signedArea); // texelarea_inside(face, m);
//s32 lodFactor = s32_log2_f32(texArea * dc_area); /* avoid denorm */
//return MAT_TEXTURE(m)->getLODFactor(signedArea);
}
/*!
texcoo in current mipmap dimension (face, already clipped)
-> want to help fixpoint
*/
inline void CBurningVideoDriver::select_polygon_mipmap_inside(s4DVertex* burning_restrict face[], const size_t tex, const CSoftwareTexture2_Bound& b) const
{
#ifdef SOFTWARE_DRIVER_2_PERSPECTIVE_CORRECT
#if defined(Tweak_Burning)
(face[0] + 1)->Tex[tex].x = face[0]->Tex[tex].x * (face[0] + 1)->Pos.w * (b.w + Tweak.tex_w_add) + (b.cx + Tweak.tex_cx_add);
(face[0] + 1)->Tex[tex].y = face[0]->Tex[tex].y * (face[0] + 1)->Pos.w * (b.h + Tweak.tex_h_add) + (b.cy + Tweak.tex_cy_add);
(face[1] + 1)->Tex[tex].x = face[1]->Tex[tex].x * (face[1] + 1)->Pos.w * (b.w + Tweak.tex_w_add) + (b.cx + Tweak.tex_cx_add);
(face[1] + 1)->Tex[tex].y = face[1]->Tex[tex].y * (face[1] + 1)->Pos.w * (b.h + Tweak.tex_h_add) + (b.cy + Tweak.tex_cy_add);
(face[2] + 1)->Tex[tex].x = face[2]->Tex[tex].x * (face[2] + 1)->Pos.w * (b.w + Tweak.tex_w_add) + (b.cx + Tweak.tex_cx_add);
(face[2] + 1)->Tex[tex].y = face[2]->Tex[tex].y * (face[2] + 1)->Pos.w * (b.h + Tweak.tex_h_add) + (b.cy + Tweak.tex_cy_add);
#else
(face[0] + 1)->Tex[tex].x = face[0]->Tex[tex].x * (face[0] + 1)->Pos.w * b.w + b.cx;
(face[0] + 1)->Tex[tex].y = face[0]->Tex[tex].y * (face[0] + 1)->Pos.w * b.h + b.cy;
(face[1] + 1)->Tex[tex].x = face[1]->Tex[tex].x * (face[1] + 1)->Pos.w * b.w + b.cx;
(face[1] + 1)->Tex[tex].y = face[1]->Tex[tex].y * (face[1] + 1)->Pos.w * b.h + b.cy;
(face[2] + 1)->Tex[tex].x = face[2]->Tex[tex].x * (face[2] + 1)->Pos.w * b.w + b.cx;
(face[2] + 1)->Tex[tex].y = face[2]->Tex[tex].y * (face[2] + 1)->Pos.w * b.h + b.cy;
#endif
#else
(face[0] + 1)->Tex[tex].x = face[0]->Tex[tex].x * b.w;
(face[0] + 1)->Tex[tex].y = face[0]->Tex[tex].y * b.h;
(face[1] + 1)->Tex[tex].x = face[1]->Tex[tex].x * b.w;
(face[1] + 1)->Tex[tex].y = face[1]->Tex[tex].y * b.h;
(face[2] + 1)->Tex[tex].x = face[2]->Tex[tex].x * b.w;
(face[2] + 1)->Tex[tex].y = face[2]->Tex[tex].y * b.h;
#endif
}
// Vertex Cache
//! setup Vertex Format
void CBurningVideoDriver::VertexCache_map_source_format()
{
u32 s0 = sizeof(s4DVertex);
u32 s1 = sizeof(s4DVertex_proxy);
if ( s1 <= sizeof_s4DVertex /2 )
{
os::Printer::log ( "BurningVideo vertex format unnecessary to large", ELL_WARNING );
}
//memcpy_vertex
if ( s0 != sizeof_s4DVertex || ((sizeof_s4DVertex * sizeof_s4DVertexPairRel)&31))
{
os::Printer::log ( "BurningVideo vertex format compile problem", ELL_ERROR );
_IRR_DEBUG_BREAK_IF(1);
}
#if defined(ENV64BIT)
if (sizeof(void*) != 8)
{
os::Printer::log("BurningVideo pointer should be 8 bytes", ELL_ERROR);
_IRR_DEBUG_BREAK_IF(1);
}
#endif
SVSize* vSize = VertexCache.vSize;
//vSize[E4VT_STANDARD].Format = VERTEX4D_FORMAT_TEXTURE_1 | VERTEX4D_FORMAT_COLOR_1 | VERTEX4D_FORMAT_LIGHT_1 | VERTEX4D_FORMAT_SPECULAR;
vSize[E4VT_STANDARD].Format = VERTEX4D_FORMAT_TEXTURE_1 | VERTEX4D_FORMAT_COLOR_2_FOG;
vSize[E4VT_STANDARD].Pitch = sizeof(S3DVertex);
vSize[E4VT_STANDARD].TexSize = 1;
vSize[E4VT_STANDARD].TexCooSize = 1;
vSize[E4VT_2TCOORDS].Format = VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1;
vSize[E4VT_2TCOORDS].Pitch = sizeof(S3DVertex2TCoords);
vSize[E4VT_2TCOORDS].TexSize = 2;
vSize[E4VT_2TCOORDS].TexCooSize = 2;
//vSize[E4VT_TANGENTS].Format = VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1 | VERTEX4D_FORMAT_LIGHT_1 | VERTEX4D_FORMAT_BUMP_DOT3;
vSize[E4VT_TANGENTS].Format = VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_2_FOG | VERTEX4D_FORMAT_LIGHT_1 | VERTEX4D_FORMAT_BUMP_DOT3;
vSize[E4VT_TANGENTS].Pitch = sizeof(S3DVertexTangents);
vSize[E4VT_TANGENTS].TexSize = 2;
vSize[E4VT_TANGENTS].TexCooSize = 2;
// reflection map
vSize[E4VT_REFLECTION_MAP].Format = VERTEX4D_FORMAT_TEXTURE_2 | VERTEX4D_FORMAT_COLOR_1;
vSize[E4VT_REFLECTION_MAP].Pitch = sizeof(S3DVertex);
vSize[E4VT_REFLECTION_MAP].TexSize = 2;
vSize[E4VT_REFLECTION_MAP].TexCooSize = 1; //TexCoo2 generated
// shadow
vSize[E4VT_SHADOW].Format = 0;
vSize[E4VT_SHADOW].Pitch = sizeof(f32) * 3; // core::vector3df*
vSize[E4VT_SHADOW].TexSize = 0;
vSize[E4VT_SHADOW].TexCooSize = 0;
// color shading only (no texture)
vSize[E4VT_NO_TEXTURE].Format = VERTEX4D_FORMAT_COLOR_1 | VERTEX4D_FORMAT_LIGHT_1 | VERTEX4D_FORMAT_SPECULAR;
vSize[E4VT_NO_TEXTURE].Pitch = sizeof(S3DVertex);
vSize[E4VT_NO_TEXTURE].TexSize = 0;
vSize[E4VT_NO_TEXTURE].TexCooSize = 0;
//Line
vSize[E4VT_LINE].Format = VERTEX4D_FORMAT_COLOR_1;
vSize[E4VT_LINE].Pitch = sizeof(S3DVertex);
vSize[E4VT_LINE].TexSize = 0;
vSize[E4VT_LINE].TexCooSize = 0;
size_t size;
for ( size_t i = 0; i < E4VT_COUNT; ++i )
{
size_t& flag = vSize[i].Format;
#if !defined(SOFTWARE_DRIVER_2_USE_SEPARATE_SPECULAR_COLOR)
flag &= ~VERTEX4D_FORMAT_SPECULAR;
#endif
if ( vSize[i].TexSize > BURNING_MATERIAL_MAX_TEXTURES )
vSize[i].TexSize = BURNING_MATERIAL_MAX_TEXTURES;
size = (flag & VERTEX4D_FORMAT_MASK_TEXTURE) >> 16;
if ( size > BURNING_MATERIAL_MAX_TEXTURES )
{
flag = (flag & ~VERTEX4D_FORMAT_MASK_TEXTURE) | (BURNING_MATERIAL_MAX_TEXTURES << 16);
}
size = (flag & VERTEX4D_FORMAT_MASK_COLOR) >> 20;
if ( size > BURNING_MATERIAL_MAX_COLORS )
{
flag = (flag & ~VERTEX4D_FORMAT_MASK_COLOR) | (BURNING_MATERIAL_MAX_COLORS << 20);
}
size = (flag & VERTEX4D_FORMAT_MASK_LIGHT) >> 24;
if ( size > BURNING_MATERIAL_MAX_LIGHT_TANGENT)
{
flag = (flag & ~VERTEX4D_FORMAT_MASK_LIGHT) | (BURNING_MATERIAL_MAX_LIGHT_TANGENT << 24);
}
}
VertexCache.mem.resize(VERTEXCACHE_ELEMENT * 2);
VertexCache.vType = E4VT_STANDARD;
Clipper.resize(VERTEXCACHE_ELEMENT * 2);
Clipper_temp.resize(VERTEXCACHE_ELEMENT * 2);
TransformationStack = 0;
memset(TransformationFlag, 0, sizeof(TransformationFlag));
memset(Transformation_ETS_CLIPSCALE, 0, sizeof(Transformation_ETS_CLIPSCALE));
Material.resetRenderStates = true;
Material.Fallback_MaterialType = EMT_SOLID;
}
/*!
fill a cache line with transformed, light and clip test triangles
overhead - if primitive is outside or culled, vertexLighting and TextureTransform is still done
*/
void CBurningVideoDriver::VertexCache_fill(const u32 sourceIndex, const u32 destIndex)
{
u8* burning_restrict source;
s4DVertex* burning_restrict dest;
source = (u8*) VertexCache.vertices + ( sourceIndex * VertexCache.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 = VertexCache.mem.data + s4DVertex_ofs(destIndex);
//Irrlicht S3DVertex,S3DVertex2TCoords,S3DVertexTangents
const S3DVertex* base = ((S3DVertex*)source);
// transform Model * World * Camera * Projection * NDCSpace matrix
const core::matrix4* matrix = Transformation[TransformationStack];
matrix[ETS_PROJ_MODEL_VIEW].transformVect(&dest->Pos.x, base->Pos);
//mhm ... maybe no goto
if (VertexCache.vType == E4VT_SHADOW)
{
//core::vector3df i = base->Pos;
//i.Z -= 0.5f;
//matrix[ETS_PROJ_MODEL_VIEW].transformVect(&dest->Pos.x, i);
//GL_DEPTH_CLAMP,EVDF_DEPTH_CLAMP
//if ( dest->Pos.z < dest->Pos.w)
// dest->Pos.z = dest->Pos.w*0.99f;
//glPolygonOffset // self shadow wanted or not?
dest->Pos.w *= 1.005f;
//flag |= v->Pos.z <= v->Pos.w ? VERTEX4D_CLIP_NEAR : 0;
//flag |= -v->Pos.z <= v->Pos.w ? VERTEX4D_CLIP_FAR : 0;
goto clipandproject;
}
#if defined (SOFTWARE_DRIVER_2_LIGHTING) || defined ( SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM )
// vertex, normal in light(eye) space
if ( Material.org.Lighting || (EyeSpace.TL_Flag & (TL_TEXTURE_TRANSFORM|TL_FOG)) )
{
sVec4 vertex4; //eye coordinate position of vertex
matrix[ETS_MODEL_VIEW].transformVect ( &vertex4.x, base->Pos );
f32 iw = reciprocal_zero(vertex4.w);
EyeSpace.vertex.x = vertex4.x * iw;
EyeSpace.vertex.y = vertex4.y * iw;
EyeSpace.vertex.z = vertex4.z * iw;
EyeSpace.vertex.w = iw;
//EyeSpace.cam_distance = EyeSpace.vertex.length_xyz();
EyeSpace.cam_dir = EyeSpace.vertex;
EyeSpace.cam_dir.normalize_dir_xyz();
matrix[ETS_NORMAL].rotateVect(&EyeSpace.normal.x, base->Normal);
if (EyeSpace.TL_Flag & TL_NORMALIZE_NORMALS)
EyeSpace.normal.normalize_dir_xyz();
}
#endif
#if BURNING_MATERIAL_MAX_COLORS > 1
dest->Color[1].a = 1.f;
dest->Color[1].r = 0.f;
dest->Color[1].g = 0.f;
dest->Color[1].b = 0.f;
#endif
#if BURNING_MATERIAL_MAX_COLORS > 2
dest->Color[2].a = 1.f;
dest->Color[2].r = 0.f;
dest->Color[2].g = 0.f;
dest->Color[2].b = 0.f;
#endif
#if BURNING_MATERIAL_MAX_COLORS > 3
dest->Color[3].a = 1.f;
dest->Color[3].r = 0.f;
dest->Color[3].g = 0.f;
dest->Color[3].b = 0.f;
#endif
#if BURNING_MATERIAL_MAX_LIGHT_TANGENT > 0
dest->LightTangent[0].x = 0.f;
dest->LightTangent[0].y = 0.f;
dest->LightTangent[0].z = 0.f;
#endif
#if BURNING_MATERIAL_MAX_COLORS > 0
// apply lighting model
#if defined (SOFTWARE_DRIVER_2_LIGHTING)
if ( Material.org.Lighting )
{
lightVertex_eye ( dest, base->Color.color );
}
else
{
dest->Color[0].setA8R8G8B8 ( base->Color.color );
}
#else
dest->Color[0].setA8R8G8B8 ( base->Color.color );
#endif
#endif
//vertex fog
if (EyeSpace.TL_Flag & TL_FOG ) //Material.org.FogEnable
{
f32 fog_factor = 1.f;
// GL_FRAGMENT_DEPTH -> abs(EyeSpace.vertex.z)
ieee754 fog_frag_coord;
fog_frag_coord.f = EyeSpace.vertex.z;
fog_frag_coord.fields.sign = 0;
switch (FogType)
{
case EFT_FOG_LINEAR:
fog_factor = (FogEnd - fog_frag_coord.f) * EyeSpace.fog_scale;
break;
case EFT_FOG_EXP:
fog_factor = (f32)exp(-FogDensity * fog_frag_coord.f);
break;
case EFT_FOG_EXP2:
fog_factor = (f32)exp(-FogDensity * FogDensity * fog_frag_coord.f * fog_frag_coord.f);
break;
}
sVec4* a = dest->Color + ((VertexCache.vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_COLOR_2_FOG) ? 1 : 0);
a->a = core::clamp(fog_factor, 0.f, 1.f);
}
// Texture Transform
#if defined ( SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM )
if ( 0 == (EyeSpace.TL_Flag & TL_TEXTURE_TRANSFORM) )
#endif // SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM
{
// Irrlicht TCoords and TCoords2 must be contiguous memory. baseTCoord has no 4 byte aligned start address!
const f32* baseTCoord = &base->TCoords.X;
switch (VertexCache.vSize[VertexCache.vType].TexCooSize)
{
#if BURNING_MATERIAL_MAX_TEXTURES == 4
case 0:
dest->Tex[0].x = 0.f;
dest->Tex[0].y = 0.f;
dest->Tex[1].x = 0.f;
dest->Tex[1].y = 0.f;
dest->Tex[2].x = 0.f;
dest->Tex[2].y = 0.f;
dest->Tex[3].x = 0.f;
dest->Tex[3].y = 0.f;
break;
case 1:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = 0.f;
dest->Tex[1].y = 0.f;
dest->Tex[2].x = 0.f;
dest->Tex[2].y = 0.f;
dest->Tex[3].x = 0.f;
dest->Tex[3].y = 0.f;
break;
case 2:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = baseTCoord[2];
dest->Tex[1].y = baseTCoord[3];
dest->Tex[2].x = 0.f;
dest->Tex[2].y = 0.f;
dest->Tex[3].x = 0.f;
dest->Tex[3].y = 0.f;
break;
case 3:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = baseTCoord[2];
dest->Tex[1].y = baseTCoord[3];
dest->Tex[2].x = baseTCoord[4];
dest->Tex[2].y = baseTCoord[5];
dest->Tex[3].x = 0.f;
dest->Tex[3].y = 0.f;
break;
case 4:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = baseTCoord[2];
dest->Tex[1].y = baseTCoord[3];
dest->Tex[2].x = baseTCoord[4];
dest->Tex[2].y = baseTCoord[5];
dest->Tex[3].x = baseTCoord[6];
dest->Tex[3].y = baseTCoord[7];
break;
#endif
#if BURNING_MATERIAL_MAX_TEXTURES == 2
case 0:
dest->Tex[0].x = 0.f;
dest->Tex[0].y = 0.f;
dest->Tex[1].x = 0.f;
dest->Tex[1].y = 0.f;
break;
case 1:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = 0.f;
dest->Tex[1].y = 0.f;
break;
case 2:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
dest->Tex[1].x = baseTCoord[2];
dest->Tex[1].y = baseTCoord[3];
break;
#endif
#if BURNING_MATERIAL_MAX_TEXTURES == 1
case 0:
dest->Tex[0].x = 0.f;
dest->Tex[0].y = 0.f;
break;
case 1:
dest->Tex[0].x = baseTCoord[0];
dest->Tex[0].y = baseTCoord[1];
break;
#endif
default:
break;
}
}
#if defined ( SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM )
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
*/
const sVec4& u = EyeSpace.cam_dir; // EyeSpace.vertex.normalized
const sVec4& n = EyeSpace.normal;
sVec4 r;
const size_t* flag = TransformationFlag[TransformationStack];
for ( u32 t = 0; t != VertexCache.vSize[VertexCache.vType].TexSize; ++t )
{
// texgen
if (flag[ETS_TEXTURE_0+t] & ETF_TEXGEN_CAMERA_SPHERE )
{
//reflect(u,N) u - 2.0 * dot(N, u) * N
// cam is (0,0,-1), tex flipped
f32 dot = -2.f * n.dot_xyz(u);
r.x = u.x + dot * n.x;
r.y = u.y + dot * n.y;
r.z = u.z + dot * n.z;
//openGL
f32 m = 2.f * sqrtf(r.x*r.x+r.y*r.y+(r.z+1.f)*(r.z+1.f));
dest[0].Tex[t].x = r.x / m + 0.5f;
dest[0].Tex[t].y = -r.y / m + 0.5f;
/*
//~d3d with spheremap scale
f32 m = 0.25f / (0.00001f + sqrtf(r.x*r.x+r.y*r.y+r.z*r.z));
dest[0].Tex[t].x = r.x * m + 0.5f;
dest[0].Tex[t].y = -r.y * m + 0.5f;
*/
}
else if (flag[ETS_TEXTURE_0+t] & ETF_TEXGEN_CAMERA_REFLECTION )
{
//reflect(u,N) u - 2.0 * dot(N, u) * N
// cam is (0,0,-1), tex flipped
f32 dot = -2.f * n.dot_xyz(u);
r.x = u.x + dot * n.x;
r.y = u.y + dot * n.y;
r.z = u.z + dot * n.z;
//openGL
dest[0].Tex[t].x = r.x;
dest[0].Tex[t].y = -r.y;
/*
//~d3d with spheremap scale
dest[0].Tex[t].x = r.x;
dest[0].Tex[t].y = r.y;
*/
}
else if (VertexCache.vSize[VertexCache.vType].TexCooSize > t)
{
const f32* M = matrix[ETS_TEXTURE_0 + t].pointer();
// Irrlicht TCoords and TCoords2 must be contiguous memory. baseTCoord has no 4 byte aligned start address!
const f32* baseTCoord = &base->TCoords.X;
sVec4 srcT;
srcT.x = baseTCoord[(t * 2) + 0];
srcT.y = baseTCoord[(t * 2) + 1];
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 BURNING_MATERIAL_MAX_LIGHT_TANGENT > 0
if ((EyeSpace.TL_Flag & TL_LIGHT0_IS_NORMAL_MAP) &&
((VertexCache.vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_MASK_TANGENT) == VERTEX4D_FORMAT_BUMP_DOT3)
)
{
const S3DVertexTangents *tangent = ((S3DVertexTangents*) source );
sVec4 vp;
sVec4 light_accu;
light_accu.x = 0.f;
light_accu.y = 0.f;
light_accu.z = 0.f;
light_accu.w = 0.f;
for ( u32 i = 0; i < 2 && i < EyeSpace.Light.size (); ++i )
{
const SBurningShaderLight &light = EyeSpace.Light[i];
if ( !light.LightIsOn )
continue;
// lightcolor with standard model
// but shader is different. treating light and vertex in same space
#if 1
vp.x = light.pos.x - base->Pos.X;
vp.y = light.pos.y - base->Pos.Y;
vp.z = light.pos.z - base->Pos.Z;
#else
vp.x = light.pos4.x - EyeSpace.vertex.x;
vp.y = light.pos4.y - EyeSpace.vertex.y;
vp.z = light.pos4.z - EyeSpace.vertex.z;
#endif
// transform by tangent matrix
light_accu.x += (vp.x * tangent->Tangent.X + vp.y * tangent->Tangent.Y + vp.z * tangent->Tangent.Z );
light_accu.y += (vp.x * tangent->Binormal.X + vp.y * tangent->Binormal.Y + vp.z * tangent->Binormal.Z );
light_accu.z += (vp.x * tangent->Normal.X + vp.y * tangent->Normal.Y + vp.z * tangent->Normal.Z );
}
//normalize [-1,+1] to [0,1] -> obsolete
light_accu.normalize_pack_xyz(dest->LightTangent[0],1.f, 0.f);
dest->Tex[1].x = dest->Tex[0].x;
dest->Tex[1].y = dest->Tex[0].y;
}
else if (Material.org.Lighting)
{
//dest->LightTangent[0].x = 0.f;
//dest->LightTangent[0].y = 0.f;
//dest->LightTangent[0].z = 0.f;
}
#endif //if BURNING_MATERIAL_MAX_LIGHT_TANGENT > 0
#endif // SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM
clipandproject:
// test vertex visible
dest[0].flag = (u32) (clipToFrustumTest(dest) | VertexCache.vSize[VertexCache.vType].Format);
dest[1].flag = dest[0].flag;
// to DC Space, project homogenous vertex
if ( (dest[0].flag & VERTEX4D_CLIPMASK ) == VERTEX4D_INSIDE )
{
ndc_2_dc_and_project ( dest+1, dest, s4DVertex_ofs(1));
}
}
//todo: this should return only index
s4DVertexPair* CBurningVideoDriver::VertexCache_getVertex ( const u32 sourceIndex ) const
{
for ( size_t i = 0; i < VERTEXCACHE_ELEMENT; ++i )
{
if ( VertexCache.info[ i ].index == sourceIndex )
{
return VertexCache.mem.data + s4DVertex_ofs(i);
}
}
return VertexCache.mem.data; //error
}
/*
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
*/
void CBurningVideoDriver::VertexCache_get(s4DVertexPair* face[4])
{
// next primitive must be complete in cache
if ( VertexCache.indicesIndex - VertexCache.indicesRun < VertexCache.primitiveHasVertex &&
VertexCache.indicesIndex < VertexCache.indexCount
)
{
size_t i;
//memset(info, VERTEXCACHE_MISS, sizeof(info));
for (i = 0; i != VERTEXCACHE_ELEMENT; ++i)
{
VertexCache.info_temp[i].hit = VERTEXCACHE_MISS;
VertexCache.info_temp[i].index = VERTEXCACHE_MISS;
}
// rewind to start of primitive
VertexCache.indicesIndex = VertexCache.indicesRun;
// get the next unique vertices cache line
u32 fillIndex = 0;
u32 dIndex = 0;
u32 sourceIndex = 0;
while ( VertexCache.indicesIndex < VertexCache.indexCount &&
fillIndex < VERTEXCACHE_ELEMENT
)
{
switch ( VertexCache.iType )
{
case E4IT_16BIT:
sourceIndex = ((u16*)VertexCache.indices) [ VertexCache.indicesIndex ];
break;
case E4IT_32BIT:
sourceIndex = ((u32*)VertexCache.indices) [ VertexCache.indicesIndex ];
break;
default:
case E4IT_NONE:
sourceIndex = VertexCache.indicesIndex;
break;
}
VertexCache.indicesIndex += 1;
// if not exist, push back
s32 exist = 0;
for ( dIndex = 0; dIndex < fillIndex; ++dIndex )
{
if (VertexCache.info_temp[ dIndex ].index == sourceIndex )
{
exist = 1;
break;
}
}
if ( 0 == exist )
{
VertexCache.info_temp[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 == VertexCache.info_temp[i].index )
{
VertexCache.info_temp[i].hit = dIndex;
VertexCache.info[ dIndex ].hit = 1;
break;
}
}
}
// fill new
for ( i = 0; i!= fillIndex; ++i )
{
if (VertexCache.info_temp[i].hit != VERTEXCACHE_MISS )
continue;
for ( dIndex = 0; dIndex < VERTEXCACHE_ELEMENT; ++dIndex )
{
if ( 0 == VertexCache.info[dIndex].hit )
{
VertexCache_fill (VertexCache.info_temp[i].index, dIndex );
VertexCache.info[dIndex].hit += 1;
VertexCache.info_temp[i].hit = dIndex;
break;
}
}
}
}
//const u32 i0 = core::if_c_a_else_0 ( VertexCache.pType != scene::EPT_TRIANGLE_FAN, VertexCache.indicesRun );
const u32 i0 = VertexCache.pType != scene::EPT_TRIANGLE_FAN ? VertexCache.indicesRun : 0;
switch ( VertexCache.iType )
{
case E4IT_16BIT:
{
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 E4IT_32BIT:
{
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 E4IT_NONE:
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;
}
face[3] = face[0]; // quad unsupported
VertexCache.indicesRun += VertexCache.indicesPitch;
}
/*!
*/
int 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)
{
// These calls would lead to crashes due to wrong index usage.
// The vertex cache needs to be rewritten for these primitives.
if (0 == CurrentShader ||
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 1;
}
VertexCache.vertices = vertices;
VertexCache.vertexCount = vertexCount;
switch (Material.org.MaterialType) // (Material.Fallback_MaterialType)
{
case EMT_REFLECTION_2_LAYER:
case EMT_TRANSPARENT_REFLECTION_2_LAYER:
VertexCache.vType = E4VT_REFLECTION_MAP;
break;
default:
VertexCache.vType = (e4DVertexType)vType;
break;
}
//check material
SVSize* vSize = VertexCache.vSize;
for (int m = (int)vSize[VertexCache.vType].TexSize-1; m >= 0 ; --m)
{
ITexture* tex = MAT_TEXTURE(m);
if (!tex)
{
vSize[E4VT_NO_TEXTURE] = vSize[VertexCache.vType];
vSize[E4VT_NO_TEXTURE].TexSize = m;
vSize[E4VT_NO_TEXTURE].TexCooSize = m;
VertexCache.vType = E4VT_NO_TEXTURE;
//flags downconvert?
}
}
VertexCache.indices = indices;
VertexCache.indicesIndex = 0;
VertexCache.indicesRun = 0;
switch ( iType )
{
case EIT_16BIT: VertexCache.iType = E4IT_16BIT; break;
case EIT_32BIT: VertexCache.iType = E4IT_32BIT; break;
default:
VertexCache.iType = (e4DIndexType)iType; break;
}
if (!VertexCache.indices)
VertexCache.iType = E4IT_NONE;
VertexCache.pType = pType;
VertexCache.primitiveHasVertex = 3;
VertexCache.indicesPitch = 1;
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.indicesPitch = 1;
VertexCache.primitiveHasVertex = 1;
break;
case scene::EPT_LINE_STRIP:
VertexCache.indexCount = primitiveCount+1;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = 2;
break;
case scene::EPT_LINE_LOOP:
VertexCache.indexCount = primitiveCount+1;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = 2;
break;
case scene::EPT_LINES:
VertexCache.indexCount = 2*primitiveCount;
VertexCache.indicesPitch = 2;
VertexCache.primitiveHasVertex = 2;
break;
case scene::EPT_TRIANGLE_STRIP:
VertexCache.indexCount = primitiveCount+2;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = 3;
break;
case scene::EPT_TRIANGLES:
VertexCache.indexCount = primitiveCount + primitiveCount + primitiveCount;
VertexCache.indicesPitch = 3;
VertexCache.primitiveHasVertex = 3;
break;
case scene::EPT_TRIANGLE_FAN:
VertexCache.indexCount = primitiveCount + 2;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = 3;
break;
case scene::EPT_QUAD_STRIP:
VertexCache.indexCount = 2*primitiveCount + 2;
VertexCache.indicesPitch = 2;
VertexCache.primitiveHasVertex = 4;
break;
case scene::EPT_QUADS:
VertexCache.indexCount = 4*primitiveCount;
VertexCache.indicesPitch = 4;
VertexCache.primitiveHasVertex = 4;
break;
case scene::EPT_POLYGON:
VertexCache.indexCount = primitiveCount+1;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = primitiveCount;
break;
case scene::EPT_POINT_SPRITES:
VertexCache.indexCount = primitiveCount;
VertexCache.indicesPitch = 1;
VertexCache.primitiveHasVertex = 1;
break;
}
//memset( VertexCache.info, VERTEXCACHE_MISS, sizeof ( VertexCache.info ) );
for (size_t i = 0; i != VERTEXCACHE_ELEMENT; ++i)
{
VertexCache.info[i].hit = VERTEXCACHE_MISS;
VertexCache.info[i].index = VERTEXCACHE_MISS;
}
return 0;
}
//! draws a vertex primitive list
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);
if (VertexCache_reset(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType))
return;
if ((u32)Material.org.MaterialType < MaterialRenderers.size())
{
MaterialRenderers[Material.org.MaterialType].Renderer->OnRender(this, vType);
}
//Matrices needed for this primitive
transform_calc(ETS_PROJ_MODEL_VIEW);
if ( Material.org.Lighting || (EyeSpace.TL_Flag & (TL_TEXTURE_TRANSFORM | TL_FOG)) )
{
transform_calc(ETS_MODEL_VIEW);
transform_calc(ETS_NORMAL);
}
s4DVertexPair* face[4];
size_t vOut;
size_t vertex_from_clipper; // from VertexCache or CurrentOut
size_t has_vertex_run;
for ( size_t primitive_run = 0; primitive_run < primitiveCount; ++primitive_run)
{
//collect pointer to face vertices
VertexCache_get(face);
size_t clipMask_i;
size_t clipMask_o;
clipMask_i = face[0]->flag;
clipMask_o = face[0]->flag;
for (has_vertex_run = 1; has_vertex_run < VertexCache.primitiveHasVertex; ++has_vertex_run)
{
clipMask_i |= face[has_vertex_run]->flag; // if fully outside or outside on same side
clipMask_o &= face[has_vertex_run]->flag; // if fully inside
}
clipMask_i &= VERTEX4D_CLIPMASK;
clipMask_o &= VERTEX4D_CLIPMASK;
if (clipMask_i != VERTEX4D_INSIDE )
{
// if primitive fully outside or outside on same side
continue;
vOut = 0;
vertex_from_clipper = 0;
}
else if (clipMask_o == VERTEX4D_INSIDE )
{
// if primitive fully inside
vOut = VertexCache.primitiveHasVertex;
vertex_from_clipper = 0;
}
else
#if defined(SOFTWARE_DRIVER_2_CLIPPING)
{
// else if not complete inside clipping necessary
// check: clipping should reuse vertexcache (try to minimize clipping)
for (has_vertex_run = 0; has_vertex_run < VertexCache.primitiveHasVertex; ++has_vertex_run)
{
memcpy_s4DVertexPair(Clipper.data + s4DVertex_ofs(has_vertex_run), face[has_vertex_run]);
}
vOut = clipToFrustum(VertexCache.primitiveHasVertex);
vertex_from_clipper = 1;
// to DC Space, project homogenous vertex
ndc_2_dc_and_project(Clipper.data + s4DVertex_proj(0), Clipper.data, s4DVertex_ofs(vOut));
}
#else
{
continue;
vOut = 0;
vertex_from_clipper = 0;
}
#endif
// re-tesselate ( triangle-fan, 0-1-2,0-2-3.. )
for (has_vertex_run = 0; (has_vertex_run + VertexCache.primitiveHasVertex) <= vOut; has_vertex_run += 1)
{
// set from clipped geometry
if (vertex_from_clipper)
{
face[0] = Clipper.data + s4DVertex_ofs(0);
face[1] = Clipper.data + s4DVertex_ofs(has_vertex_run + 1);
face[2] = Clipper.data + s4DVertex_ofs(has_vertex_run + 2);
}
//area of primitive in device space
f32 dc_area = screenarea_inside(face);
//geometric clipping has problem with invisible or very small Triangles
//size_t sign = dc_area < 0.001f ? CULL_BACK : dc_area > 0.001f ? CULL_FRONT : CULL_INVISIBLE;
ieee754 t;
t.f = dc_area;
size_t sign = t.fields.sign ? CULL_BACK : CULL_FRONT;
sign |= t.abs.frac_exp < 981668463 /*0.01f*/ ? CULL_INVISIBLE : 0;
if (Material.CullFlag & sign)
break; //continue;
//select mipmap ratio between drawing space and texture space
dc_area = reciprocal_zero(dc_area);
// select mipmap
for (size_t m = 0; m < VertexCache.vSize[VertexCache.vType].TexSize; ++m)
{
video::CSoftwareTexture2* tex = MAT_TEXTURE(m);
//only guessing: take more detail (lower mipmap) in light+bump textures
//assume transparent add is ~50% transparent -> more detail
f32 lod_bias = Material.org.MaterialType == EMT_TRANSPARENT_ADD_COLOR ? 0.1f : 0.33f;
lod_bias *= tex->get_lod_bias();
s32 lodFactor = lodFactor_inside(face, m, dc_area, lod_bias);
CurrentShader->setTextureParam(m, tex, lodFactor);
//currently shader receives texture coordinate as Pixelcoo of 1 Texture
select_polygon_mipmap_inside(face, m, tex->getTexBound());
}
CurrentShader->drawWireFrameTriangle(face[0] + 1, face[1] + 1, face[2] + 1);
vertex_from_clipper = 1;
}
}
//release texture
for (size_t m = 0; m < VertexCache.vSize[VertexCache.vType].TexSize; ++m)
{
CurrentShader->setTextureParam(m, 0, 0);
}
}
//! 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)
{
EyeSpace.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 );
//should always be valid?
sVec4 nDirection;
nDirection.x = dl.Direction.X;
nDirection.y = dl.Direction.Y;
nDirection.z = dl.Direction.Z;
nDirection.normalize_dir_xyz();
switch ( dl.Type )
{
case ELT_DIRECTIONAL:
l.pos.x = dl.Position.X;
l.pos.y = dl.Position.Y;
l.pos.z = dl.Position.Z;
l.pos.w = 0.f;
l.constantAttenuation = 1.f;
l.linearAttenuation = 0.f;
l.quadraticAttenuation = 0.f;
l.spotDirection.x = -nDirection.x;
l.spotDirection.y = -nDirection.y;
l.spotDirection.z = -nDirection.z;
l.spotDirection.w = 0.f;
l.spotCosCutoff = -1.f;
l.spotCosInnerCutoff = 1.f;
l.spotExponent = 0.f;
break;
case ELT_POINT:
l.pos.x = dl.Position.X;
l.pos.y = dl.Position.Y;
l.pos.z = dl.Position.Z;
l.pos.w = 1.f;
l.constantAttenuation = dl.Attenuation.X;
l.linearAttenuation = dl.Attenuation.Y;
l.quadraticAttenuation = dl.Attenuation.Z;
l.spotDirection.x = -nDirection.x;
l.spotDirection.y = -nDirection.y;
l.spotDirection.z = -nDirection.z;
l.spotDirection.w = 0.f;
l.spotCosCutoff = -1.f;
l.spotCosInnerCutoff = 1.f;
l.spotExponent = 0.f;
break;
case ELT_SPOT:
l.pos.x = dl.Position.X;
l.pos.y = dl.Position.Y;
l.pos.z = dl.Position.Z;
l.pos.w = 1.f;
l.constantAttenuation = dl.Attenuation.X;
l.linearAttenuation = dl.Attenuation.Y;
l.quadraticAttenuation = dl.Attenuation.Z;
l.spotDirection.x = nDirection.x;
l.spotDirection.y = nDirection.y;
l.spotDirection.z = nDirection.z;
l.spotDirection.w = 0.0f;
l.spotCosCutoff = cosf(dl.OuterCone * 2.0f * core::DEGTORAD * 0.5f);
l.spotCosInnerCutoff = cosf(dl.InnerCone * 2.0f * core::DEGTORAD * 0.5f);
l.spotExponent = dl.Falloff;
break;
default:
break;
}
//which means ETS_VIEW
setTransform(ETS_WORLD,irr::core::IdentityMatrix);
transform_calc(ETS_MODEL_VIEW);
const core::matrix4* matrix = Transformation[TransformationStack];
transformVec4Vec4(matrix[ETS_MODEL_VIEW], &l.pos4.x, &l.pos.x );
rotateVec3Vec4(matrix[ETS_MODEL_VIEW], &l.spotDirection4.x, &l.spotDirection.x );
EyeSpace.Light.push_back ( l );
return EyeSpace.Light.size() - 1;
}
//! Turns a dynamic light on or off
void CBurningVideoDriver::turnLightOn(s32 lightIndex, bool turnOn)
{
if((u32)lightIndex < EyeSpace.Light.size())
{
EyeSpace.Light[lightIndex].LightIsOn = turnOn;
}
}
//! deletes all dynamic lights there are
void CBurningVideoDriver::deleteAllDynamicLights()
{
EyeSpace.reset ();
CNullDriver::deleteAllDynamicLights();
}
//! returns the maximal amount of dynamic lights the device can handle
u32 CBurningVideoDriver::getMaximalDynamicLightAmount() const
{
return 8; //no limit 8 only for convenience
}
//! sets a material
void CBurningVideoDriver::setMaterial(const SMaterial& material)
{
// ---------- Override
Material.org = material;
OverrideMaterial.apply(Material.org);
const SMaterial& in = Material.org;
// ---------- Notify Shader
// unset old material
u32 mi;
mi = (u32)Material.lastMaterial.MaterialType;
if (mi != material.MaterialType && mi < MaterialRenderers.size())
MaterialRenderers[mi].Renderer->OnUnsetMaterial();
// set new material.
mi = (u32)in.MaterialType;
if (mi < MaterialRenderers.size())
MaterialRenderers[mi].Renderer->OnSetMaterial(
in, Material.lastMaterial, Material.resetRenderStates, this);
Material.lastMaterial = in;
Material.resetRenderStates = false;
//CSoftware2MaterialRenderer sets Material.Fallback_MaterialType
//Material.Fallback_MaterialType = material.MaterialType;
//-----------------
//Material.org = material;
Material.CullFlag = CULL_INVISIBLE | (in.BackfaceCulling ? CULL_BACK : 0) | (in.FrontfaceCulling ? CULL_FRONT : 0);
size_t* flag = TransformationFlag[TransformationStack];
#ifdef SOFTWARE_DRIVER_2_TEXTURE_TRANSFORM
for (u32 m = 0; m < BURNING_MATERIAL_MAX_TEXTURES /*&& m < vSize[VertexCache.vType].TexSize*/; ++m)
{
setTransform((E_TRANSFORMATION_STATE) (ETS_TEXTURE_0 + m),in.getTextureMatrix(m));
flag[ETS_TEXTURE_0+m] &= ~ETF_TEXGEN_MASK;
}
#endif
#ifdef SOFTWARE_DRIVER_2_LIGHTING
Material.AmbientColor.setA8R8G8B8( in.AmbientColor.color );
Material.DiffuseColor.setA8R8G8B8( in.ColorMaterial ? 0xFFFFFFFF : in.DiffuseColor.color );
Material.EmissiveColor.setA8R8G8B8(in.EmissiveColor.color );
Material.SpecularColor.setA8R8G8B8( in.SpecularColor.color );
burning_setbit(EyeSpace.TL_Flag, in.Lighting, TL_LIGHT);
burning_setbit( EyeSpace.TL_Flag, (in.Shininess != 0.f) & (in.SpecularColor.color & 0x00ffffff), TL_SPECULAR );
burning_setbit( EyeSpace.TL_Flag, in.FogEnable, TL_FOG );
burning_setbit( EyeSpace.TL_Flag, in.NormalizeNormals, TL_NORMALIZE_NORMALS );
//if (EyeSpace.Flags & SPECULAR ) EyeSpace.Flags |= NORMALIZE_NORMALS;
#endif
//--------------- setCurrentShader
ITexture* texture0 = in.getTexture(0);
ITexture* texture1 = in.getTexture(1);
//ITexture* texture2 = in.getTexture(2);
//ITexture* texture3 = in.getTexture(3);
//visual studio code analysis
u32 maxTex = BURNING_MATERIAL_MAX_TEXTURES;
if (maxTex < 1) texture0 = 0;
if (maxTex < 2) texture1 = 0;
//if (maxTex < 3) texture2 = 0;
//if (maxTex < 4) texture3 = 0;
EyeSpace.TL_Flag &= ~(TL_TEXTURE_TRANSFORM | TL_LIGHT0_IS_NORMAL_MAP);
//todo: seperate depth test from depth write
Material.depth_write = getWriteZBuffer(in);
Material.depth_test = in.ZBuffer != ECFN_DISABLED && Material.depth_write;
EBurningFFShader shader = Material.depth_test ? ETR_TEXTURE_GOURAUD : ETR_TEXTURE_GOURAUD_NOZ;
switch (Material.Fallback_MaterialType) //(Material.org.MaterialType)
{
case EMT_ONETEXTURE_BLEND:
shader = ETR_TEXTURE_BLEND;
break;
case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
Material.org.MaterialTypeParam = 0.5f;
//fallthrough
case EMT_TRANSPARENT_ALPHA_CHANNEL:
if (texture0 && texture0->hasAlpha())
{
shader = Material.depth_test ? ETR_TEXTURE_GOURAUD_ALPHA : ETR_TEXTURE_GOURAUD_ALPHA_NOZ;
}
else
{
//fall back to EMT_TRANSPARENT_VERTEX_ALPHA
shader = ETR_TEXTURE_GOURAUD_VERTEX_ALPHA;
}
break;
case EMT_TRANSPARENT_ADD_COLOR:
shader = Material.depth_test ? 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:
if (texture1)
shader = ETR_TEXTURE_GOURAUD_LIGHTMAP_M1;
break;
case EMT_LIGHTMAP_M2:
case EMT_LIGHTMAP_LIGHTING_M2:
if (texture1)
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:
flag[ETS_TEXTURE_0] |= ETF_TEXGEN_CAMERA_SPHERE;
EyeSpace.TL_Flag |= TL_TEXTURE_TRANSFORM;
break;
case EMT_REFLECTION_2_LAYER:
case EMT_TRANSPARENT_REFLECTION_2_LAYER:
if (texture1)
{
shader = ETR_TRANSPARENT_REFLECTION_2_LAYER;
flag[ETS_TEXTURE_1] |= ETF_TEXGEN_CAMERA_REFLECTION;
EyeSpace.TL_Flag |= TL_TEXTURE_TRANSFORM;
}
break;
case EMT_NORMAL_MAP_SOLID:
case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
if (texture1)
{
shader = ETR_NORMAL_MAP_SOLID;
EyeSpace.TL_Flag |= TL_TEXTURE_TRANSFORM | TL_LIGHT0_IS_NORMAL_MAP;
}
break;
case EMT_PARALLAX_MAP_SOLID:
case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
if (texture1)
{
shader = ETR_NORMAL_MAP_SOLID;
EyeSpace.TL_Flag |= TL_TEXTURE_TRANSFORM | TL_LIGHT0_IS_NORMAL_MAP;
}
break;
default:
break;
}
if (!texture0)
{
shader = Material.depth_test ? ETR_GOURAUD :
shader == ETR_TEXTURE_GOURAUD_VERTEX_ALPHA ?
ETR_GOURAUD_ALPHA_NOZ: // 2D Gradient
ETR_GOURAUD_NOZ;
shader = ETR_COLOR;
}
if (in.Wireframe)
{
IBurningShader* candidate = BurningShader[shader];
if (!candidate || (candidate && !candidate->canWireFrame()))
{
shader = ETR_TEXTURE_GOURAUD_WIRE;
}
}
if (in.PointCloud)
{
IBurningShader* candidate = BurningShader[shader];
if (!candidate || (candidate && !candidate->canPointCloud()))
{
shader = ETR_TEXTURE_GOURAUD_WIRE;
}
}
//shader = ETR_REFERENCE;
// switchToTriangleRenderer
CurrentShader = BurningShader[shader];
if (CurrentShader)
{
CurrentShader->setTLFlag(EyeSpace.TL_Flag);
if (EyeSpace.TL_Flag & TL_FOG) CurrentShader->setFog(FogColor);
if (EyeSpace.TL_Flag & TL_SCISSOR) CurrentShader->setScissor(Scissor);
CurrentShader->setRenderTarget(RenderTargetSurface, ViewPort);
CurrentShader->OnSetMaterial(Material);
CurrentShader->pushEdgeTest(in.Wireframe, in.PointCloud, 0);
}
/*
mi = (u32)Material.org.MaterialType;
if (mi < MaterialRenderers.size())
MaterialRenderers[mi].Renderer->OnRender(this, (video::E_VERTEX_TYPE)VertexCache.vType);
*/
}
//! 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);
EyeSpace.fog_scale = reciprocal_zero2(FogEnd - FogStart);
}
#if defined(SOFTWARE_DRIVER_2_LIGHTING) && BURNING_MATERIAL_MAX_COLORS > 0
/*!
applies lighting model
*/
void CBurningVideoDriver::lightVertex_eye(s4DVertex *dest, u32 vertexargb)
{
//gl_FrontLightModelProduct.sceneColor = gl_FrontMaterial.emission + gl_FrontMaterial.ambient * gl_LightModel.ambient
sVec3Color ambient;
sVec3Color diffuse;
sVec3Color specular;
// the universe started in darkness..
ambient = EyeSpace.Global_AmbientLight;
diffuse.set(0.f);
specular.set(0.f);
u32 i;
f32 dot;
f32 distance;
f32 attenuation;
sVec4 vp; // unit vector vertex to light
sVec4 lightHalf; // blinn-phong reflection
f32 spotDot; // cos of angle between spotlight and point on surface
for (i = 0; i < EyeSpace.Light.size(); ++i)
{
const SBurningShaderLight& light = EyeSpace.Light[i];
if (!light.LightIsOn)
continue;
switch (light.Type)
{
case ELT_DIRECTIONAL:
//angle between normal and light vector
dot = EyeSpace.normal.dot_xyz(light.spotDirection4);
// accumulate ambient
ambient.add_rgb(light.AmbientColor);
// diffuse component
if (dot > 0.f)
diffuse.mad_rgb(light.DiffuseColor, dot);
break;
case ELT_POINT:
// surface to light
vp.x = light.pos4.x - EyeSpace.vertex.x;
vp.y = light.pos4.y - EyeSpace.vertex.y;
vp.z = light.pos4.z - EyeSpace.vertex.z;
distance = vp.length_xyz();
attenuation = light.constantAttenuation
+ light.linearAttenuation * distance
+ light.quadraticAttenuation * (distance * distance);
attenuation = reciprocal_one(attenuation);
//att = clamp(1.0 - dist/radius, 0.0, 1.0); att *= att
// accumulate ambient
ambient.mad_rgb(light.AmbientColor, attenuation);
// build diffuse reflection
//angle between normal and light vector
vp.mul_xyz(reciprocal_zero(distance)); //normalize
dot = EyeSpace.normal.dot_xyz(vp);
if (dot <= 0.f) continue;
// diffuse component
diffuse.mad_rgb(light.DiffuseColor, dot * attenuation);
if (!(EyeSpace.TL_Flag & TL_SPECULAR))
continue;
lightHalf.x = vp.x + 0.f; // EyeSpace.cam_eye_pos.x;
lightHalf.y = vp.y + 0.f; // EyeSpace.cam_eye_pos.y;
lightHalf.z = vp.z - 1.f; // EyeSpace.cam_eye_pos.z;
lightHalf.normalize_dir_xyz();
//specular += light.SpecularColor * pow(max(dot(Eyespace.normal,lighthalf),0,Material.org.Shininess)*attenuation
specular.mad_rgb(light.SpecularColor,
powf_limit(EyeSpace.normal.dot_xyz(lightHalf), Material.org.Shininess)*attenuation
);
break;
case ELT_SPOT:
// surface to light
vp.x = light.pos4.x - EyeSpace.vertex.x;
vp.y = light.pos4.y - EyeSpace.vertex.y;
vp.z = light.pos4.z - EyeSpace.vertex.z;
distance = vp.length_xyz();
//normalize
vp.mul_xyz(reciprocal_zero(distance));
// point on surface inside cone of illumination
spotDot = vp.dot_minus_xyz(light.spotDirection4);
if (spotDot < light.spotCosCutoff)
continue;
attenuation = light.constantAttenuation
+ light.linearAttenuation * distance
+ light.quadraticAttenuation * distance * distance;
attenuation = reciprocal_one(attenuation);
attenuation *= powf_limit(spotDot, light.spotExponent);
// accumulate ambient
ambient.mad_rgb(light.AmbientColor, attenuation);
// build diffuse reflection
//angle between normal and light vector
dot = EyeSpace.normal.dot_xyz(vp);
if (dot < 0.f) continue;
// diffuse component
diffuse.mad_rgb(light.DiffuseColor, dot * attenuation);
if (!(EyeSpace.TL_Flag & TL_SPECULAR))
continue;
lightHalf.x = vp.x + 0.f; // EyeSpace.cam_eye_pos.x;
lightHalf.y = vp.y + 0.f; // EyeSpace.cam_eye_pos.y;
lightHalf.z = vp.z - 1.f; // EyeSpace.cam_eye_pos.z;
lightHalf.normalize_dir_xyz();
//specular += light.SpecularColor * pow(max(dot(Eyespace.normal,lighthalf),0,Material.org.Shininess)*attenuation
specular.mad_rgb(light.SpecularColor,
powf_limit(EyeSpace.normal.dot_xyz(lightHalf), Material.org.Shininess)*attenuation
);
break;
default:
break;
}
}
// sum up lights
sVec3Color dColor;
dColor.set(0.f);
dColor.mad_rgbv(diffuse, Material.DiffuseColor);
//diffuse * vertex color.
//has to move to shader (for vertex color only this will fit [except clamping])
sVec3Color c;
c.setA8R8G8B8(vertexargb);
dColor.r *= c.r;
dColor.g *= c.g;
dColor.b *= c.b;
//separate specular
#if defined(SOFTWARE_DRIVER_2_USE_SEPARATE_SPECULAR_COLOR)
if ((VertexCache.vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_COLOR_2_FOG))
{
specular.sat_xyz(dest->Color[1], Material.SpecularColor);
}
else
if ( !(EyeSpace.TL_Flag & TL_LIGHT0_IS_NORMAL_MAP) &&
(VertexCache.vSize[VertexCache.vType].Format & VERTEX4D_FORMAT_MASK_LIGHT)
)
{
specular.sat_xyz(dest->LightTangent[0], Material.SpecularColor);
}
else
#endif
{
dColor.mad_rgbv(specular, Material.SpecularColor);
}
dColor.mad_rgbv(ambient, Material.AmbientColor);
dColor.add_rgb(Material.EmissiveColor);
dColor.sat(dest->Color[0], vertexargb);
}
#endif
CImage* getImage(const video::ITexture* texture)
{
if (!texture) return 0;
CImage* img = 0;
switch (texture->getDriverType())
{
case EDT_BURNINGSVIDEO:
img = ((CSoftwareTexture2*)texture)->getImage();
break;
case EDT_SOFTWARE:
img = ((CSoftwareTexture*)texture)->getImage();
break;
default:
os::Printer::log("Fatal Error: Tried to copy from a surface not owned by this driver.", ELL_ERROR);
break;
}
return img;
}
/*
draw2DImage with single color scales into destination quad & cliprect(more like viewport)
draw2DImage with 4 color scales on destination and cliprect is scissor
*/
static const u16 quad_triangle_indexList[6] = { 0,1,2,0,2,3 };
#if defined(SOFTWARE_DRIVER_2_2D_AS_2D)
//! 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->getOriginalSize() != texture->getSize())
{
core::rect<s32> destRect(destPos, sourceRect.getSize());
SColor c4[4] = { color,color,color,color };
draw2DImage(texture, destRect, sourceRect, clipRect, c4, useAlphaChannelOfTexture);
return;
}
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)
((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;
}
u32 argb = (colors ? colors[0].color : 0xFFFFFFFF);
eBlitter op = useAlphaChannelOfTexture ?
(argb == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND : BLITTER_TEXTURE_ALPHA_COLOR_BLEND) : BLITTER_TEXTURE;
StretchBlit(op, RenderTargetSurface, clipRect, &destRect,
((CSoftwareTexture2*)texture)->getImage(), &sourceRect, &texture->getOriginalSize(), argb);
}
}
//!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)
{
core::rect<s32> p(position);
if (clip) p.clipAgainst(*clip);
if (p.isValid()) drawRectangle(RenderTargetSurface, p, colorLeftUp);
}
#endif //defined(SOFTWARE_DRIVER_2_2D_AS_2D)
#if 0
void transform_for_BlitJob2D( SBlitJob& out,
const S3DVertex quad2DVertices[4], const core::dimension2d<u32>& renderTargetSize,
CBurningVideoDriver* driver
)
{
//assume. z = 0.f, w = 1.f
//MVP 2D
core::matrix4 m;
m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0f, 1.0f);
m.setTranslation(core::vector3df(-1.f, 1.f, 0));
//setTransform(ETS_WORLD, m);
//m.setTranslation(core::vector3df(0.375f, 0.375f, 0.0f));
//setTransform(ETS_VIEW, m);
s4DVertexPair v[4*sizeof_s4DVertexPairRel];
for (int i = 0; i < 4; ++i)
{
m.transformVect(&v[s4DVertex_ofs(i)].Pos.x, quad2DVertices[i].Pos);
v[s4DVertex_ofs(i)].Tex[0].x = quad2DVertices[i].TCoords.X;
v[s4DVertex_ofs(i)].Tex[0].y = quad2DVertices[i].TCoords.Y;
v[s4DVertex_ofs(i)].Color[0].setA8R8G8B8(quad2DVertices[i].Color.color);
v[s4DVertex_ofs(i)].flag = VERTEX4D_FORMAT_TEXTURE_1 | VERTEX4D_FORMAT_COLOR_1;
v[s4DVertex_ofs(i)].flag |= clipToFrustumTest(v + i);
}
size_t vOut = driver->clipToFrustum(4);
struct s2DVertex
{
union
{
struct {
f32 x, y;
f32 u, v;
f32 a, r, g, b;
};
f32 attr[8];
};
// f = a * t + b * ( 1 - t )
void interpolate(const s2DVertex& b, const s2DVertex& a, const f32 t)
{
for (int i = 0; i < 8; ++i)
{
attr[i] = b.attr[i] + ((a.attr[i] - b.attr[i]) * t);
}
}
};
// 0 5
f32 m2[2];
m2[0] = renderTargetSize.Width ? 2.f / renderTargetSize.Width : 0.f;
m2[1] = renderTargetSize.Height ? -2.f / renderTargetSize.Height : 0.f;
s2DVertex p[4];
for (int i = 0; i < 4; ++i)
{
p[i].x = quad2DVertices[i].Pos.X*m2[0] - 1.f;
p[i].y = quad2DVertices[i].Pos.Y*m2[1] + 1.f;
p[i].u = quad2DVertices[i].TCoords.X;
p[i].v = quad2DVertices[i].TCoords.Y;
p[i].a = quad2DVertices[i].Color.getAlpha() * (1.f / 255.f);
p[i].r = quad2DVertices[i].Color.getRed() * (1.f / 255.f);
p[i].g = quad2DVertices[i].Color.getBlue() * (1.f / 255.f);
p[i].b = quad2DVertices[i].Color.getGreen() * (1.f / 255.f);
}
}
#endif
//! Enable the 2d override material
void CBurningVideoDriver::enableMaterial2D(bool enable)
{
CNullDriver::enableMaterial2D(enable);
burning_setbit(TransformationFlag[1][ETS_PROJECTION], 0, ETF_VALID);
}
size_t compare_2d_material(const SMaterial& a, const SMaterial& b)
{
size_t flag = 0;
flag |= a.MaterialType == b.MaterialType ? 0 : 1;
flag |= a.ZBuffer == b.ZBuffer ? 0 : 2;
flag |= a.TextureLayer[0].Texture == b.TextureLayer[0].Texture ? 0 : 4;
flag |= a.TextureLayer[0].BilinearFilter == b.TextureLayer[0].BilinearFilter ? 0 : 8;
flag |= a.MaterialTypeParam == b.MaterialTypeParam ? 0 : 16;
if (flag) return flag;
flag |= a.TextureLayer[1].Texture == b.TextureLayer[1].Texture ? 0 : 32;
flag |= a.ZWriteEnable == b.ZWriteEnable ? 0 : 64;
return flag;
}
void CBurningVideoDriver::setRenderStates2DMode(const video::SColor& color, video::ITexture* texture, bool useAlphaChannelOfTexture)
{
//save current 3D Material
//Material.save3D = Material.org;
//build new 2D Material
bool vertexAlpha = color.getAlpha() < 255;
//2D uses textureAlpa*vertexAlpha 3D not..
if (useAlphaChannelOfTexture && texture && texture->hasAlpha())
{
Material.mat2D.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
}
else if (vertexAlpha)
{
Material.mat2D.MaterialType = EMT_TRANSPARENT_VERTEX_ALPHA;
}
else
{
Material.mat2D.MaterialType = EMT_SOLID;
}
Material.mat2D.ZBuffer = ECFN_DISABLED;
Material.mat2D.ZWriteEnable = EZW_OFF;
Material.mat2D.Lighting = false;
Material.mat2D.setTexture(0, (video::ITexture*)texture);
//used for text. so stay as sharp as possible (like HW Driver)
bool mip = false;
const SMaterial& currentMaterial = (!OverrideMaterial2DEnabled) ? InitMaterial2D : OverrideMaterial2D;
mip = currentMaterial.TextureLayer[0].BilinearFilter;
Material.mat2D.setFlag(video::EMF_BILINEAR_FILTER, mip);
//switch to 2D Matrix Stack [ Material set Texture Matrix ]
TransformationStack = 1;
//2D GUI Matrix
if (!(TransformationFlag[TransformationStack][ETS_PROJECTION] & ETF_VALID))
{
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
core::matrix4 m(core::matrix4::EM4CONST_NOTHING);
m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0f, 1.0f);
m.setTranslation(core::vector3df(-1.f, 1.f, 0));
setTransform(ETS_PROJECTION, m);
m.makeIdentity();
setTransform(ETS_WORLD, m);
if ( mip ) m.setTranslation(core::vector3df(0.375f, 0.375f, 0.0f));
setTransform(ETS_VIEW, m);
buildNDCToDCMatrix(Transformation_ETS_CLIPSCALE[TransformationStack], ViewPort, mip ? -0.187f : 0.f);
}
//compare
if (compare_2d_material(Material.org,Material.mat2D))
{
setMaterial(Material.mat2D);
}
if (CurrentShader)
{
CurrentShader->setPrimitiveColor(color.color);
}
}
void CBurningVideoDriver::setRenderStates3DMode()
{
//restoreRenderStates3DMode
//setMaterial(Material.save3D);
//switch to 3D Matrix Stack
TransformationStack = 0;
}
//! draws a vertex primitive list in 2d
void CBurningVideoDriver::draw2DVertexPrimitiveList(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::draw2DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType);
bool useAlphaChannelOfTexture = false;
video::SColor color(0xFFFFFFFF);
switch (Material.org.MaterialType)
{
case EMT_TRANSPARENT_ALPHA_CHANNEL:
useAlphaChannelOfTexture = true;
break;
case EMT_TRANSPARENT_VERTEX_ALPHA:
color.setAlpha(127);
break;
default:
break;
}
setRenderStates2DMode(color, Material.org.getTexture(0), useAlphaChannelOfTexture);
drawVertexPrimitiveList(vertices, vertexCount,
indexList, primitiveCount,
vType, pType, iType);
setRenderStates3DMode();
}
//setup a quad
#if defined(SOFTWARE_DRIVER_2_2D_AS_3D)
//! 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)
return;
if (!sourceRect.isValid())
return;
// clip these coordinates
core::rect<s32> targetRect(destPos, sourceRect.getSize());
if (clipRect)
{
targetRect.clipAgainst(*clipRect);
if (targetRect.getWidth() < 0 || targetRect.getHeight() < 0)
return;
}
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
targetRect.clipAgainst(core::rect<s32>(0, 0, (s32)renderTargetSize.Width, (s32)renderTargetSize.Height));
if (targetRect.getWidth() < 0 || targetRect.getHeight() < 0)
return;
// ok, we've clipped everything.
// now draw it.
const core::dimension2d<s32> sourceSize(targetRect.getSize());
const core::position2d<s32> sourcePos(sourceRect.UpperLeftCorner + (targetRect.UpperLeftCorner - destPos));
const core::dimension2d<u32>& tex_orgsize = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>(tex_orgsize.Width);
const f32 invH = 1.f / static_cast<f32>(tex_orgsize.Height);
const core::rect<f32> tcoords(
sourcePos.X * invW,
sourcePos.Y * invH,
(sourcePos.X + sourceSize.Width) * invW,
(sourcePos.Y + sourceSize.Height) * invH);
Quad2DVertices[0].Color = color;
Quad2DVertices[1].Color = color;
Quad2DVertices[2].Color = color;
Quad2DVertices[3].Color = color;
Quad2DVertices[0].Pos = core::vector3df((f32)targetRect.UpperLeftCorner.X, (f32)targetRect.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[1].Pos = core::vector3df((f32)targetRect.LowerRightCorner.X, (f32)targetRect.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[2].Pos = core::vector3df((f32)targetRect.LowerRightCorner.X, (f32)targetRect.LowerRightCorner.Y, 0.0f);
Quad2DVertices[3].Pos = core::vector3df((f32)targetRect.UpperLeftCorner.X, (f32)targetRect.LowerRightCorner.Y, 0.0f);
Quad2DVertices[0].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
Quad2DVertices[1].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
Quad2DVertices[2].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
Quad2DVertices[3].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
//SBlitJob job;
//transform_for_BlitJob2D(job, Quad2DVertices, renderTargetSize,this);
setRenderStates2DMode(color, (video::ITexture*) texture, useAlphaChannelOfTexture);
drawVertexPrimitiveList(Quad2DVertices, 4,
quad_triangle_indexList, 2,
EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT);
setRenderStates3DMode();
}
//! 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)
{
const core::dimension2d<u32>& st = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>(st.Width);
const f32 invH = 1.f / static_cast<f32>(st.Height);
const core::rect<f32> tcoords(
sourceRect.UpperLeftCorner.X * invW,
sourceRect.UpperLeftCorner.Y * invH,
sourceRect.LowerRightCorner.X * invW,
sourceRect.LowerRightCorner.Y *invH);
const video::SColor temp[4] =
{
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF
};
const video::SColor* const useColor = colors ? colors : temp;
Quad2DVertices[0].Color = useColor[0];
Quad2DVertices[1].Color = useColor[3];
Quad2DVertices[2].Color = useColor[2];
Quad2DVertices[3].Color = useColor[1];
Quad2DVertices[0].Pos = core::vector3df((f32)destRect.UpperLeftCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[1].Pos = core::vector3df((f32)destRect.LowerRightCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[2].Pos = core::vector3df((f32)destRect.LowerRightCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f);
Quad2DVertices[3].Pos = core::vector3df((f32)destRect.UpperLeftCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f);
Quad2DVertices[0].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
Quad2DVertices[1].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
Quad2DVertices[2].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
Quad2DVertices[3].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
if (clipRect)
{
if (!clipRect->isValid())
return;
//glEnable(GL_SCISSOR_TEST);
EyeSpace.TL_Flag |= TL_SCISSOR;
setScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y,
clipRect->getWidth(), clipRect->getHeight());
}
video::SColor alphaTest;
alphaTest.color = useColor[0].color & useColor[0].color & useColor[0].color & useColor[0].color;
//SBlitJob job;
//transform_for_BlitJob2D(job, Quad2DVertices, renderTargetSize, this);
setRenderStates2DMode(alphaTest, (video::ITexture*) texture, useAlphaChannelOfTexture);
drawVertexPrimitiveList(Quad2DVertices, 4,
quad_triangle_indexList, 2,
EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT);
if (clipRect)
EyeSpace.TL_Flag &= ~TL_SCISSOR;
setRenderStates3DMode();
}
//!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)
{
core::rect<s32> pos = position;
if (clip)
pos.clipAgainst(*clip);
if (!pos.isValid())
return;
Quad2DVertices[0].Color = colorLeftUp;
Quad2DVertices[1].Color = colorRightUp;
Quad2DVertices[2].Color = colorRightDown;
Quad2DVertices[3].Color = colorLeftDown;
Quad2DVertices[0].Pos = core::vector3df((f32)pos.UpperLeftCorner.X, (f32)pos.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[1].Pos = core::vector3df((f32)pos.LowerRightCorner.X, (f32)pos.UpperLeftCorner.Y, 0.0f);
Quad2DVertices[2].Pos = core::vector3df((f32)pos.LowerRightCorner.X, (f32)pos.LowerRightCorner.Y, 0.0f);
Quad2DVertices[3].Pos = core::vector3df((f32)pos.UpperLeftCorner.X, (f32)pos.LowerRightCorner.Y, 0.0f);
Quad2DVertices[0].TCoords.X = 0.f;
Quad2DVertices[0].TCoords.Y = 0.f;
Quad2DVertices[1].TCoords.X = 0.f;
Quad2DVertices[1].TCoords.Y = 0.f;
Quad2DVertices[2].TCoords.X = 0.f;
Quad2DVertices[3].TCoords.Y = 0.f;
Quad2DVertices[3].TCoords.X = 0.f;
Quad2DVertices[3].TCoords.Y = 0.f;
video::SColor alphaTest;
alphaTest.color = colorLeftUp.color & colorRightUp.color & colorRightDown.color & colorLeftDown.color;
setRenderStates2DMode(alphaTest, 0, 0);
drawVertexPrimitiveList(Quad2DVertices, 4,
quad_triangle_indexList, 2,
EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT);
setRenderStates3DMode();
}
#endif // SOFTWARE_DRIVER_2_2D_AS_3D
//! Draws a 2d line.
void CBurningVideoDriver::draw2DLine(const core::position2d<s32>& start,
const core::position2d<s32>& end,
SColor color)
{
drawLine(RenderTargetSurface, start, end, color );
}
//! Draws a pixel
void CBurningVideoDriver::drawPixel(u32 x, u32 y, const SColor & color)
{
RenderTargetSurface->setPixel(x, y, color, true);
}
//! 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 a 3d line.
void CBurningVideoDriver::draw3DLine(const core::vector3df& start,
const core::vector3df& end, SColor color_start)
{
SColor color_end = color_start;
VertexCache.primitiveHasVertex = 2;
VertexCache.vType = E4VT_LINE;
s4DVertex* v = Clipper.data;
transform_calc(ETS_PROJ_MODEL_VIEW);
const core::matrix4* matrix = Transformation[TransformationStack];
matrix[ETS_PROJ_MODEL_VIEW].transformVect ( &v[0].Pos.x, start );
matrix[ETS_PROJ_MODEL_VIEW].transformVect ( &v[2].Pos.x, end );
#if BURNING_MATERIAL_MAX_COLORS > 0
v[0].Color[0].setA8R8G8B8 ( color_start.color );
v[2].Color[0].setA8R8G8B8 ( color_end.color );
#endif
u32 i;
for (i = 0; i < 4; i += sizeof_s4DVertexPairRel)
{
v[i + 0].flag = (u32)(VertexCache.vSize[VertexCache.vType].Format);
v[i + 1].flag = v[i + 0].flag;
}
size_t g;
size_t vOut;
// vertices count per line
vOut = clipToFrustum (VertexCache.primitiveHasVertex);
if ( vOut < VertexCache.primitiveHasVertex)
return;
vOut *= sizeof_s4DVertexPairRel;
// to DC Space, project homogenous vertex
ndc_2_dc_and_project ( v + 1, v, vOut );
// unproject vertex color
#if 0
#if BURNING_MATERIAL_MAX_COLORS > 0
for ( g = 0; g != vOut; g+= 2 )
{
v[ g + 1].Color[0].setA8R8G8B8 ( color.color );
}
#endif
#endif
IBurningShader * shader = 0;
if ( CurrentShader && CurrentShader->canWireFrame() ) shader = CurrentShader;
else shader = BurningShader [ ETR_TEXTURE_GOURAUD_WIRE ];
shader = BurningShader [ ETR_TEXTURE_GOURAUD_WIRE ];
shader->pushEdgeTest(1,0,1);
shader->setRenderTarget(RenderTargetSurface, ViewPort);
for ( g = 0; g <= vOut - 4; g += sizeof_s4DVertexPairRel)
{
shader->drawLine ( v + 1 + g, v + 1 + g + sizeof_s4DVertexPairRel);
}
shader->popEdgeTest();
}
//! \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.51 beautiful";
#elif defined ( BURNINGVIDEO_RENDERER_ULTRA_FAST )
return L"Burning's Video 0.51 ultra fast";
#elif defined ( BURNINGVIDEO_RENDERER_FAST )
return L"Burning's Video 0.51 fast";
#elif defined ( BURNINGVIDEO_RENDERER_CE )
return L"Burning's Video 0.51 CE";
#else
return L"Burning's Video 0.51";
#endif
}
//! Returns the graphics card vendor name.
core::stringc CBurningVideoDriver::getVendorInfo()
{
return "Burning's Video: Ing. Thomas Alten (c) 2006-2020";
}
//! 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;
}
//! 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,this);
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(depth);
if ((flag & ECBF_STENCIL) && StencilBuffer)
StencilBuffer->clear(stencil);
}
#if 0
void CBurningVideoDriver::saveBuffer()
{
static int shotCount = 0;
char buf[256];
if (BackBuffer)
{
sprintf(buf, "shot/%04d_b.png", shotCount);
writeImageToFile(BackBuffer, buf);
}
if (StencilBuffer)
{
CImage stencil(ECF_A8R8G8B8, StencilBuffer->getSize(), StencilBuffer->lock(), true, false);
sprintf(buf, "shot/%04d_s.ppm", shotCount);
writeImageToFile(&stencil, buf);
}
shotCount += 1;
}
#endif
//! 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)
{
u32 flags =
((TextureCreationFlags & ETCF_CREATE_MIP_MAPS) ? CSoftwareTexture2::GEN_MIPMAP : 0)
| ((TextureCreationFlags & ETCF_AUTO_GENERATE_MIP_MAPS) ? CSoftwareTexture2::GEN_MIPMAP_AUTO : 0)
| ((TextureCreationFlags & ETCF_ALLOW_NON_POWER_2) ? CSoftwareTexture2::ALLOW_NPOT : 0)
#if defined(IRRLICHT_sRGB)
| ((TextureCreationFlags & ETCF_IMAGE_IS_LINEAR) ? CSoftwareTexture2::IMAGE_IS_LINEAR : 0)
| ((TextureCreationFlags & ETCF_TEXTURE_IS_LINEAR) ? CSoftwareTexture2::TEXTURE_IS_LINEAR : 0)
#endif
;
CSoftwareTexture2* texture = new CSoftwareTexture2(image, name, flags, this);
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 0x7FFFFFFF;
}
//! 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();
if (!StencilBuffer || !count)
return;
Material.org.MaterialType = video::EMT_SOLID;
Material.org.Lighting = false;
Material.org.ZWriteEnable = video::EZW_OFF;
Material.org.ZBuffer = ECFN_LESS;
CurrentShader = BurningShader[ETR_STENCIL_SHADOW];
CurrentShader->setRenderTarget(RenderTargetSurface, ViewPort);
CurrentShader->pushEdgeTest(Material.org.Wireframe, 0,0);
//setMaterial
EyeSpace.TL_Flag &= ~(TL_TEXTURE_TRANSFORM | TL_LIGHT0_IS_NORMAL_MAP);
CurrentShader->setTLFlag(EyeSpace.TL_Flag);
//glStencilMask(~0);
//glStencilFunc(GL_ALWAYS, 0, ~0);
//glEnable(GL_DEPTH_CLAMP);
if (zfail)
{
Material.org.BackfaceCulling = false;
Material.org.FrontfaceCulling = true;
Material.CullFlag = CULL_FRONT | CULL_INVISIBLE;
CurrentShader->setStencilOp( StencilOp_KEEP, StencilOp_INCR, StencilOp_KEEP);
drawVertexPrimitiveList (triangles.const_pointer(), count, 0, count/3, (video::E_VERTEX_TYPE) E4VT_SHADOW, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) E4IT_NONE);
Material.org.BackfaceCulling = true;
Material.org.FrontfaceCulling = false;
Material.CullFlag = CULL_BACK | CULL_INVISIBLE;
CurrentShader->setStencilOp( StencilOp_KEEP, StencilOp_DECR, StencilOp_KEEP);
drawVertexPrimitiveList (triangles.const_pointer(), count, 0, count/3, (video::E_VERTEX_TYPE) E4VT_SHADOW, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) E4IT_NONE);
}
else // zpass
{
Material.org.BackfaceCulling = true;
Material.org.FrontfaceCulling = false;
Material.CullFlag = CULL_BACK | CULL_INVISIBLE;
CurrentShader->setStencilOp( StencilOp_KEEP, StencilOp_KEEP, StencilOp_INCR);
drawVertexPrimitiveList(triangles.const_pointer(), count, 0, count / 3, (video::E_VERTEX_TYPE) E4VT_SHADOW, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) E4IT_NONE);
Material.org.BackfaceCulling = false;
Material.org.FrontfaceCulling = true;
Material.CullFlag = CULL_FRONT | CULL_INVISIBLE;
CurrentShader->setStencilOp(StencilOp_KEEP, StencilOp_KEEP, StencilOp_DECR);
drawVertexPrimitiveList(triangles.const_pointer(), count, 0, count / 3, (video::E_VERTEX_TYPE) E4VT_SHADOW, scene::EPT_TRIANGLES, (video::E_INDEX_TYPE) E4IT_NONE);
}
//glDisable(GL_DEPTH_CLAMP);
}
//! 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;
const tStencilSample* stencil;
#if defined(SOFTWARE_DRIVER_2_32BIT)
const u32 alpha = extractAlpha(leftUpEdge.color);
const u32 src = leftUpEdge.color;
#else
const u16 alpha = extractAlpha( leftUpEdge.color ) >> 3;
const u32 src = video::A8R8G8B8toA1R5G5B5( leftUpEdge.color );
#endif
for ( u32 y = 0; y < h; ++y )
{
dst = (tVideoSample*)RenderTargetSurface->getData() + ( y * w );
stencil = (tStencilSample*)StencilBuffer->lock() + (y * w);
for ( u32 x = 0; x < w; ++x )
{
if ( stencil[x] )
{
#if defined(SOFTWARE_DRIVER_2_32BIT)
dst[x] = PixelBlend32 ( dst[x], src,alpha );
#else
dst[x] = PixelBlend16( dst[x], src, alpha );
#endif
}
}
}
if ( clearStencilBuffer )
StencilBuffer->clear(0);
}
core::dimension2du CBurningVideoDriver::getMaxTextureSize() const
{
return core::dimension2du(SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE ? SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE : 1 << 20,
SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE ? SOFTWARE_DRIVER_2_TEXTURE_MAXSIZE : 1 << 20);
}
bool CBurningVideoDriver::queryTextureFormat(ECOLOR_FORMAT format) const
{
return format == BURNINGSHADER_COLOR_FORMAT;
}
Unify checks if materials should use transparent render pass with new IVideoDriver::needsTransparentRenderPass function. 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
2020-01-03 12:13:57 +01:00
bool CBurningVideoDriver::needsTransparentRenderPass(const irr::video::SMaterial& material) const
{
return CNullDriver::needsTransparentRenderPass(material) || material.isAlphaBlendOperation(); // || material.isTransparent();
Unify checks if materials should use transparent render pass with new IVideoDriver::needsTransparentRenderPass function. 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
2020-01-03 12:13:57 +01:00
}
s32 CBurningVideoDriver::addShaderMaterial(const c8* vertexShaderProgram,
const c8* pixelShaderProgram,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial,
s32 userData)
{
s32 materialID = -1;
IBurningShader* shader = new IBurningShader(
this, materialID,
vertexShaderProgram, 0, video::EVST_VS_1_1,
pixelShaderProgram, 0, video::EPST_PS_1_1,
0, 0, EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData);
shader->drop();
return materialID;
}
//! Adds a new material renderer to the VideoDriver, based on a high level shading language.
s32 CBurningVideoDriver::addHighLevelShaderMaterial(
const c8* vertexShaderProgram,
const c8* vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8* pixelShaderProgram,
const c8* pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8* geometryShaderProgram,
const c8* geometryShaderEntryPointName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType,
scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial,
s32 userData
)
{
s32 materialID = -1;
IBurningShader* shader = new IBurningShader(
this, materialID,
vertexShaderProgram, vertexShaderEntryPointName, vsCompileTarget,
pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget,
geometryShaderProgram, geometryShaderEntryPointName, gsCompileTarget,
inType, outType, verticesOut,
callback, baseMaterial, userData);
shader->drop();
return materialID;
}
void CBurningVideoDriver::setFallback_Material(E_MATERIAL_TYPE fallback_MaterialType)
{
//this should be in material....
Material.Fallback_MaterialType = fallback_MaterialType;
}
void CBurningVideoDriver::setBasicRenderStates(const SMaterial& material,
const SMaterial& lastMaterial,
bool resetAllRenderstates)
{
}
//! Return an index constant for the vertex shader based on a name.
s32 CBurningVideoDriver::getVertexShaderConstantID(const c8* name)
{
return -1;
}
bool CBurningVideoDriver::setVertexShaderConstant(s32 index, const f32* floats, int count)
{
return true;
}
bool CBurningVideoDriver::setVertexShaderConstant(s32 index, const s32* ints, int count)
{
return true;
}
bool CBurningVideoDriver::setVertexShaderConstant(s32 index, const u32* ints, int count)
{
return true;
}
void CBurningVideoDriver::setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount)
{
}
//! Return an index constant for the pixel shader based on a name.
s32 CBurningVideoDriver::getPixelShaderConstantID(const c8* name)
{
return -1;
}
bool CBurningVideoDriver::setPixelShaderConstant(s32 index, const f32* floats, int count)
{
return false;
}
bool CBurningVideoDriver::setPixelShaderConstant(s32 index, const s32* ints, int count)
{
return false;
}
bool CBurningVideoDriver::setPixelShaderConstant(s32 index, const u32* ints, int count)
{
return false;
}
void CBurningVideoDriver::setPixelShaderConstant(const f32* data, s32 startRegister, s32 constantAmount = 1)
{
}
//! Get pointer to the IVideoDriver interface
/** \return Pointer to the IVideoDriver interface */
IVideoDriver* CBurningVideoDriver::getVideoDriver()
{
return this;
}
} // end namespace video
} // end namespace irr
#endif // _IRR_COMPILE_WITH_BURNINGSVIDEO_
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_
return new CBurningVideoDriver(params, io, presenter);
#else
return 0;
#endif // _IRR_COMPILE_WITH_BURNINGSVIDEO_
}
} // end namespace video
} // end namespace irr