Serious-Engine/Sources/Engine/Light/LayerMixer.cpp
Ryan C. Gordon ee754e7edf Made static variables that inline asm needs to touch non-static.
I dislike having to do this, but Clang sees them as unused and removes them
from the object file, causing linking to fail.

The real solution here is to remove all the assembly code because it's 2016
and this game doesn't have to run on 133MHz Pentium now.  :)
2016-03-31 13:04:57 -04:00

2064 lines
71 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "Engine/StdH.h"
#include <Engine/Brushes/Brush.h>
#include <Engine/Brushes/BrushTransformed.h>
#include <Engine/Light/LightSource.h>
#include <Engine/Light/Gradient.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Base/Statistics_Internal.h>
#include <Engine/Graphics/Color.h>
#include <Engine/Math/FixInt.h>
#include <Engine/Entities/Entity.h>
#include <Engine/Graphics/GfxLibrary.h>
#include <Engine/Math/Clipping.inl>
#include <Engine/Light/Shadows_internal.h>
#include <Engine/World/WorldEditingProfile.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
// asm shortcuts
#define O offset
#define Q qword ptr
#define D dword ptr
#define W word ptr
#define B byte ptr
#if (defined USE_PORTABLE_C)
#define ASMOPT 0
#elif (defined __MSVC_INLINE__)
#define ASMOPT 1
#elif (defined __GNU_INLINE__)
#define ASMOPT 1
#else
#define ASMOPT 0
#endif
extern INDEX shd_bFineQuality;
extern INDEX shd_iFiltering;
extern INDEX shd_iDithering;
extern const UBYTE *pubClipByte;
extern UBYTE aubSqrt[ SQRTTABLESIZE];
extern UWORD auw1oSqrt[SQRTTABLESIZE];
extern UWORD auw1oSqrt[SQRTTABLESIZE];
// static FLOAT3D _v00;
// internal class for layer mixing
class CLayerMixer
{
public:
CBrushShadowMap *lm_pbsmShadowMap; // shadow map whose layers are mixed
CBrushPolygon *lm_pbpoPolygon; // polygon of the shadow map
BOOL lm_bDynamic; // set while doing dynamic light mixing
// dimensions of currently processed shadow map
MEX lm_mexOffsetU; // offsets in mex
MEX lm_mexOffsetV;
INDEX lm_iFirstLevel; // mip level of first possible mip-map
INDEX lm_iMipLevel; // mip level
INDEX lm_iMipShift; // relative mip level (miplevel-firstlevel)
PIX lm_pixCanvasSizeU; // shadowmap canvas size in pixels
PIX lm_pixCanvasSizeV;
PIX lm_pixPolygonSizeU; // polygon (used part of shadowmap size in pixels
PIX lm_pixPolygonSizeV;
ULONG*lm_pulShadowMap; // buffer for final shadow map
ULONG*lm_pulStaticShadowMap; // precached static shadow map
// gradients for shadow map walking
FLOAT3D lm_vO; // upper left corner of shadow map in 3D
FLOAT3D lm_vStepU; // step between pixels in same row
FLOAT3D lm_vStepV; // step between rows
FLOAT3D lm_vLightDirection; // light direction for directional light sources
CLightSource *lm_plsLight; // current light source
// color components of current light
COLOR lm_colLight;
COLOR lm_colAmbient;
// constructor
CLayerMixer( CBrushShadowMap *pbsm, INDEX iFirstMip, INDEX iLastMip, BOOL bDynamic);
// remember general data
void CalculateData( CBrushShadowMap *pbsm, INDEX iMipmap);
// mix one mip-map
void MixOneMipmap( CBrushShadowMap *pbsm, INDEX iMipmap);
// mix dynamic lights
void MixOneMipmapDynamic(CBrushShadowMap *pbsm, INDEX iMipmap);
// find start of a mip-map inside a layer
void FindLayerMipmap( CBrushShadowLayer *pbsl, UBYTE *&pub, UBYTE &ubMask);
// add one point layer to the shadow map
void AddAmbientPoint(void);
void AddAmbientMaskPoint( UBYTE *pubMask, UBYTE ubMask);
void AddDiffusionPoint(void);
void AddDiffusionMaskPoint( UBYTE *pubMask, UBYTE ubMask);
BOOL PrepareOneLayerPoint( CBrushShadowLayer *pbsl, BOOL bNoMask);
void AddOneLayerPoint( CBrushShadowLayer *pbsl, UBYTE *pub, UBYTE ubMask=0);
// add one directional layer to the shadow map
void AddDirectional(void);
void AddMaskDirectional( UBYTE *pubMask, UBYTE ubMask);
void AddOneLayerDirectional( CBrushShadowLayer *pbsl, UBYTE *pub, UBYTE ubMask=0);
// add one gradient layer to the shadow map
void AddOneLayerGradient( CGradientParameters &gp);
// add the intensity to the pixel
inline void AddToCluster( UBYTE *pub);
inline void AddAmbientToCluster( UBYTE *pub);
inline void AddToCluster( UBYTE *pub, FLOAT fIntensity);
// additional functions
__forceinline void CopyShadowLayer(void);
__forceinline void FillShadowLayer( COLOR col);
// inline FLOAT IntensityAtDistance( FLOAT fDistance, FLOAT fMinDistance);
// FLOAT lm_fLightMax, lm_fLightStep;
};
// increment a byte without overflowing it
static inline void IncrementByteWithClip( UBYTE &ub, SLONG slAdd)
{
ub = pubClipByte[(SLONG)ub+slAdd];
}
// increment a color without overflowing it
static inline void IncrementColorWithClip( UBYTE &ubR, UBYTE &ubG, UBYTE &ubB,
SLONG slR, SLONG slG, SLONG slB)
{
IncrementByteWithClip( ubR, slR);
IncrementByteWithClip( ubG, slG);
IncrementByteWithClip( ubB, slB);
}
// add the intensity to the pixel
inline void CLayerMixer::AddToCluster( UBYTE *pub)
{
IncrementByteWithClip(pub[0], ((UBYTE*)&lm_colLight)[3]);
IncrementByteWithClip(pub[1], ((UBYTE*)&lm_colLight)[2]);
IncrementByteWithClip(pub[2], ((UBYTE*)&lm_colLight)[1]);
}
inline void CLayerMixer::AddAmbientToCluster( UBYTE *pub)
{
IncrementByteWithClip(pub[0], ((UBYTE*)&lm_colAmbient)[3]);
IncrementByteWithClip(pub[1], ((UBYTE*)&lm_colAmbient)[2]);
IncrementByteWithClip(pub[2], ((UBYTE*)&lm_colAmbient)[1]);
}
inline void CLayerMixer::AddToCluster( UBYTE *pub, FLOAT fIntensity)
{
IncrementByteWithClip(pub[0], (long) (((UBYTE*)&lm_colLight)[3] *fIntensity));
IncrementByteWithClip(pub[1], (long) (((UBYTE*)&lm_colLight)[2] *fIntensity));
IncrementByteWithClip(pub[2], (long) (((UBYTE*)&lm_colLight)[1] *fIntensity));
}
// remember general data
void CLayerMixer::CalculateData( CBrushShadowMap *pbsm, INDEX iMipmap)
{
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_CALCULATEDATA);
// cache class vars
lm_pbsmShadowMap = pbsm;
lm_pbpoPolygon = pbsm->GetBrushPolygon();
lm_mexOffsetU = pbsm->sm_mexOffsetX;
lm_mexOffsetV = pbsm->sm_mexOffsetY;
lm_iFirstLevel = pbsm->sm_iFirstMipLevel;
lm_iMipLevel = iMipmap;
lm_iMipShift = lm_iMipLevel - lm_iFirstLevel;
lm_pixCanvasSizeU = pbsm->sm_mexWidth >>lm_iMipLevel;
lm_pixCanvasSizeV = pbsm->sm_mexHeight>>lm_iMipLevel;
lm_pixPolygonSizeU = Min( lm_pixCanvasSizeU, (PIX)(lm_pbsmShadowMap->sm_pixPolygonSizeU >>lm_iMipShift)+1L);
lm_pixPolygonSizeV = Min( lm_pixCanvasSizeV, (PIX)(lm_pbsmShadowMap->sm_pixPolygonSizeV >>lm_iMipShift)+1L);
// determine where this mip-map is relative to the allocated shadow map memory
PIX pixOffset = pbsm->sm_slMemoryUsed/BYTES_PER_TEXEL
- GetMipmapOffset( 15, lm_pixCanvasSizeU, lm_pixCanvasSizeV);
// get right pointers to the shadow mipmap
if( lm_bDynamic) {
lm_pulShadowMap = pbsm->sm_pulDynamicShadowMap + pixOffset;
lm_pulStaticShadowMap = pbsm->sm_pulCachedShadowMap + pixOffset;
} else {
lm_pulShadowMap = pbsm->sm_pulCachedShadowMap + pixOffset;
lm_pulStaticShadowMap = NULL;
}
// prepare 3D positions
CEntity *penWithPolygon = lm_pbpoPolygon->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
ASSERT(penWithPolygon!=NULL);
const FLOATmatrix3D &mPolygonRotation = penWithPolygon->en_mRotation;
const FLOAT3D &vPolygonTranslation = penWithPolygon->GetPlacement().pl_PositionVector;
// get first pixel in texture in 3D
Vector<MEX, 2> vmex0;
vmex0(1) = -lm_mexOffsetU+(1<<(lm_iMipLevel-1));
vmex0(2) = -lm_mexOffsetV+(1<<(lm_iMipLevel-1));
lm_pbpoPolygon->bpo_mdShadow.GetSpaceCoordinates(
lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmex0, lm_vO);
lm_vO = lm_vO*mPolygonRotation+vPolygonTranslation;
// get steps for walking in texture in 3D
Vector<MEX, 2> vmexU, vmexV;
vmexU(1) = (1<<lm_iMipLevel)-lm_mexOffsetU+(1<<(lm_iMipLevel-1));
vmexU(2) = (0<<lm_iMipLevel)-lm_mexOffsetV+(1<<(lm_iMipLevel-1));
vmexV(1) = (0<<lm_iMipLevel)-lm_mexOffsetU+(1<<(lm_iMipLevel-1));
vmexV(2) = (1<<lm_iMipLevel)-lm_mexOffsetV+(1<<(lm_iMipLevel-1));
lm_pbpoPolygon->bpo_mdShadow.GetSpaceCoordinates(
lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmexU, lm_vStepU);
lm_vStepU = lm_vStepU*mPolygonRotation+vPolygonTranslation;
lm_pbpoPolygon->bpo_mdShadow.GetSpaceCoordinates(
lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmexV, lm_vStepV);
lm_vStepV = lm_vStepV*mPolygonRotation+vPolygonTranslation;
lm_vStepU-= lm_vO;
lm_vStepV-= lm_vO;
ASSERT( lm_pixPolygonSizeU>0 && lm_pixPolygonSizeV>0);
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_CALCULATEDATA);
}
// find start of a mip-map inside a layer
void CLayerMixer::FindLayerMipmap( CBrushShadowLayer *pbsl, UBYTE *&pub, UBYTE &ubMask)
{
struct MipmapTable mmtLayer;
// find mip-mapping information for the layer
MakeMipmapTable(pbsl->bsl_pixSizeU, pbsl->bsl_pixSizeV, mmtLayer);
// get pixel offset of the mipmap
SLONG slPixOffset = mmtLayer.mmt_aslOffsets[lm_iMipLevel-lm_iFirstLevel];
// convert offset to bits
pub = pbsl->bsl_pubLayer + (slPixOffset>>3);
ubMask = 1<<(slPixOffset&7);
}
// BEWARE: Changing these #defines WILL screw up the GNU C inline asm...
#define FTOX 0x10000000
#define SHIFTX (28-SQRTTABLESIZELOG2)
// variables for easier transfers
const FLOAT3D *_vLight;
FLOAT _fMinLightDistance;
FLOAT _f1oFallOff;
INDEX _iPixCt;
INDEX _iRowCt;
SLONG _slModulo;
ULONG _ulLightFlags;
ULONG _ulPolyFlags;
SLONG _slL2Row;
SLONG _slDDL2oDU;
SLONG _slDDL2oDV;
SLONG _slDDL2oDUoDV;
SLONG _slDL2oDURow;
SLONG _slDL2oDV;
SLONG _slLightMax;
SLONG _slHotSpot;
SLONG _slLightStep;
ULONG *_pulLayer;
// !!! FIXME : rcg01072001 These statics are a pain in the ass.
extern "C" {
__int64 mmDDL2oDU_AddAmbientPoint;
__int64 mmDDL2oDV_AddAmbientPoint;
}
// add one layer point light without diffusion and mask
void CLayerMixer::AddAmbientPoint(void)
{
// prepare some local variables
mmDDL2oDU_AddAmbientPoint = _slDDL2oDU;
mmDDL2oDV_AddAmbientPoint = _slDDL2oDV;
ULONG ulLightRGB = ByteSwap(lm_colLight);
_slLightMax<<=7;
_slLightStep>>=1;
#if (ASMOPT == 1)
#if (defined __MSVC_INLINE__)
__asm {
// prepare interpolants
movd mm0,D [_slL2Row]
movd mm1,D [_slDL2oDURow]
psllq mm1,32
por mm1,mm0 // MM1 = slDL2oDURow | slL2Row
movd mm0,D [_slDL2oDV]
movd mm2,D [_slDDL2oDUoDV]
psllq mm2,32
por mm2,mm0 // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
pxor mm0,mm0
movd mm7,D [ulLightRGB]
punpcklbw mm7,mm0
psllw mm7,1
// loop thru rows
mov edi,D [_pulLayer]
mov ebx,D [_iRowCt]
rowLoop:
push ebx
movd ebx,mm1 // EBX = slL2Point
movq mm3,mm1
psrlq mm3,32 // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
mov ecx,D [_iPixCt]
pixLoop:
// check if pixel need to be drawn
cmp ebx,FTOX
jge skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
movd mm4,ecx
mov eax,ebx
sar eax,SHIFTX
and eax,(SQRTTABLESIZE-1)
movzx eax,B aubSqrt[eax]
mov ecx,D [_slLightMax]
cmp eax,D [_slHotSpot]
jle skipInterpolation
mov ecx,255
sub ecx,eax
imul ecx,D [_slLightStep]
skipInterpolation:
// calculate rgb pixel to add
movd mm6,ecx
punpcklwd mm6,mm6
punpckldq mm6,mm6
pmulhw mm6,mm7
// add dynamic light pixel to underlying pixel
movd mm5,D [edi]
punpcklbw mm5,mm0
paddw mm5,mm6
packuswb mm5,mm0
movd D [edi],mm5
movd ecx,mm4
skipPixel:
// advance to next pixel
add edi,4
movd eax,mm3
add ebx,eax
paddd mm3,Q [mmDDL2oDU_AddAmbientPoint]
dec ecx
jnz pixLoop
// advance to the next row
pop ebx
add edi,D [_slModulo]
paddd mm1,mm2
paddd mm2,Q [mmDDL2oDV_AddAmbientPoint]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
// prepare interpolants
"pushl %%ebx \n\t"
"movd (" ASMSYM(_slL2Row) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDL2oDURow) "), %%mm1 \n\t"
"psllq $32, %%mm1 \n\t"
"por %%mm0, %%mm1 \n\t" // MM1 = slDL2oDURow | slL2Row
"movd (" ASMSYM(_slDL2oDV) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDDL2oDUoDV) "), %%mm2 \n\t"
"psllq $32, %%mm2 \n\t"
"por %%mm0, %%mm2 \n\t" // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
"pxor %%mm0, %%mm0 \n\t"
"movd %%eax, %%mm7 \n\t"
"punpcklbw %%mm0, %%mm7 \n\t"
"psllw $1, %%mm7 \n\t"
// loop thru rows
"movl (" ASMSYM(_pulLayer) "), %%edi \n\t"
"movl (" ASMSYM(_iRowCt) "), %%ebx \n\t"
"0: \n\t" // rowLoop
"pushl %%ebx \n\t"
"movd %%mm1, %%ebx \n\t" // EBX = slL2Point
"movq %%mm1, %%mm3 \n\t"
"psrlq $32, %%mm3 \n\t" // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
"movl (" ASMSYM(_iPixCt) "), %%ecx \n\t"
"1: \n\t" // pixLoop
// check if pixel need to be drawn
"cmpl $0x10000000, %%ebx \n\t"
"jge 3f \n\t" // skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
"movd %%ecx, %%mm4 \n\t"
"movl %%ebx, %%eax \n\t"
"sarl $15, %%eax \n\t"
"andl $8191, %%eax \n\t"
"movzbl " ASMSYM(aubSqrt) "(%%eax), %%eax \n\t"
"movl (" ASMSYM(_slLightMax) "), %%ecx \n\t"
"cmpl (" ASMSYM(_slHotSpot) "), %%eax \n\t"
"jle 2f \n\t" // skipInterpolation
"movl $255, %%ecx \n\t"
"subl %%eax, %%ecx \n\t"
"imull (" ASMSYM(_slLightStep) "), %%ecx \n\t"
"2: \n\t" // skipInterpolation
// calculate rgb pixel to add
"movd %%ecx, %%mm6 \n\t"
"punpcklwd %%mm6, %%mm6 \n\t"
"punpckldq %%mm6, %%mm6 \n\t"
"pmulhw %%mm7, %%mm6 \n\t"
// add dynamic light pixel to underlying pixel
"movd (%%edi), %%mm5 \n\t"
"punpcklbw %%mm0, %%mm5 \n\t"
"paddw %%mm6, %%mm5 \n\t"
"packuswb %%mm0, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"movd %%mm4, %%ecx \n\t"
"3: \n\t" // skipPixel
// advance to next pixel
"addl $4, %%edi \n\t"
"movd %%mm3, %%eax \n\t"
"addl %%eax, %%ebx \n\t"
"paddd (" ASMSYM(mmDDL2oDU_AddAmbientPoint) "), %%mm3 \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
// advance to the next row
"popl %%ebx \n\t"
"addl (" ASMSYM(_slModulo) "), %%edi \n\t"
"paddd %%mm2, %%mm1 \n\t"
"paddd (" ASMSYM(mmDDL2oDV_AddAmbientPoint) "), %%mm2 \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"popl %%ebx \n\t"
"emms \n\t"
: // no outputs.
: "a" (ulLightRGB)
: "ecx", "edx", "edi", "esi", "cc", "memory"
);
#else
#error Write inline asm for your platform.
#endif
#else
// !!! FIXME WARNING: I have not checked this code, and it could be
// !!! FIXME totally and utterly wrong. --ryan.
STUBBED("may not work");
for( PIX pixV=0; pixV<_iRowCt; pixV++)
{
SLONG slL2Point = _slL2Row;
SLONG slDL2oDU = _slDL2oDURow;
for( PIX pixU=0; pixU<_iPixCt; pixU++)
{
// if the point is not masked
if( slL2Point < FTOX ) {
SLONG slL = (slL2Point>>SHIFTX)&(SQRTTABLESIZE-1); // and is just for degenerate cases
SLONG slIntensity = _slLightMax;
slL = aubSqrt[slL];
if( slL>_slHotSpot) slIntensity = ((255-slL)*_slLightStep)>>8;
// add the intensity to the pixel
AddToCluster( (UBYTE*)_pulLayer, slIntensity/255.0f);
}
// go to the next pixel
_pulLayer++;
slL2Point += slDL2oDU;
slDL2oDU += _slDDL2oDU;
}
// go to the next row
_pulLayer += _slModulo/BYTES_PER_TEXEL;
_slL2Row += _slDL2oDV;
_slDL2oDV += _slDDL2oDV;
_slDL2oDURow += _slDDL2oDUoDV;
}
#endif
}
extern "C" {
__int64 mmDDL2oDU_addAmbientMaskPoint;
__int64 mmDDL2oDV_addAmbientMaskPoint;
}
// add one layer point light without diffusion and with mask
void CLayerMixer::AddAmbientMaskPoint( UBYTE *pubMask, UBYTE ubMask)
{
// prepare some local variables
mmDDL2oDU_addAmbientMaskPoint = _slDDL2oDU;
mmDDL2oDV_addAmbientMaskPoint = _slDDL2oDV;
ULONG ulLightRGB = ByteSwap(lm_colLight);
_slLightMax<<=7;
_slLightStep>>=1;
#if (ASMOPT == 1)
#if (defined __MSVC_INLINE__)
__asm {
// prepare interpolants
movd mm0,D [_slL2Row]
movd mm1,D [_slDL2oDURow]
psllq mm1,32
por mm1,mm0 // MM1 = slDL2oDURow | slL2Row
movd mm0,D [_slDL2oDV]
movd mm2,D [_slDDL2oDUoDV]
psllq mm2,32
por mm2,mm0 // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
pxor mm0,mm0 // MM0 = 0 | 0 (for unpacking purposes)
movd mm7,D [ulLightRGB]
punpcklbw mm7,mm0
psllw mm7,1
// loop thru rows
mov esi,D [pubMask]
mov edi,D [_pulLayer]
movzx edx,B [ubMask]
mov ebx,D [_iRowCt]
rowLoop:
push ebx
movd ebx,mm1 // EBX = slL2Point
movq mm3,mm1
psrlq mm3,32 // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
mov ecx,D [_iPixCt]
pixLoop:
// check if pixel need to be drawn; i.e. draw if( [esi] & ubMask && (slL2Point<FTOX))
cmp ebx,FTOX
jge skipPixel
test dl,B [esi]
je skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
movd mm4,ecx
mov eax,ebx
sar eax,SHIFTX
and eax,(SQRTTABLESIZE-1)
movzx eax,B aubSqrt[eax]
mov ecx,D [_slLightMax]
cmp eax,D [_slHotSpot]
jle skipInterpolation
mov ecx,255
sub ecx,eax
imul ecx,D [_slLightStep]
skipInterpolation:
// mix underlaying pixels with the calculated one
movd mm6,ecx
punpcklwd mm6,mm6
punpckldq mm6,mm6
pmulhw mm6,mm7
// add light pixel to underlying pixel
movd mm5,D [edi]
punpcklbw mm5,mm0
paddw mm5,mm6
packuswb mm5,mm0
movd D [edi],mm5
movd ecx,mm4
skipPixel:
// advance to next pixel
add edi,4
movd eax,mm3
add ebx,eax
paddd mm3,Q [mmDDL2oDU_addAmbientMaskPoint]
rol dl,1
adc esi,0
dec ecx
jnz pixLoop
// advance to the next row
pop ebx
add edi,D [_slModulo]
paddd mm1,mm2
paddd mm2,Q [mmDDL2oDV_addAmbientMaskPoint]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
// prepare interpolants
"pushl %%ebx \n\t"
"movl %%ecx, %%ebx \n\t"
"movd (" ASMSYM(_slL2Row) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDL2oDURow) "), %%mm1 \n\t"
"psllq $32, %%mm1 \n\t"
"por %%mm0, %%mm1 \n\t" // MM1 = slDL2oDURow | slL2Row
"movd (" ASMSYM(_slDL2oDV) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDDL2oDUoDV) "), %%mm2 \n\t"
"psllq $32, %%mm2 \n\t"
"por %%mm0, %%mm2 \n\t" // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
"pxor %%mm0, %%mm0 \n\t" // MM0 = 0 | 0 (for unpacking purposes)
"movd %%eax, %%mm7 \n\t" // eax == ulLightRGB
"punpcklbw %%mm0, %%mm7 \n\t"
"psllw $1, %%mm7 \n\t"
// loop thru rows
"movl (" ASMSYM(_pulLayer) "), %%edi \n\t"
"movzbl (%%ebx), %%edx \n\t" // ebx == &ubMask
"movl (" ASMSYM(_iRowCt) "), %%ebx \n\t"
"0: \n\t" // rowLoop
"pushl %%ebx \n\t"
"movd %%mm1, %%ebx \n\t" // EBX = slL2Point
"movq %%mm1, %%mm3 \n\t"
"psrlq $32, %%mm3 \n\t" // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
"movl (" ASMSYM(_iPixCt) "), %%ecx \n\t"
"1: \n\t" // pixLoop
// check if pixel need to be drawn; i.e. draw if( [esi] & ubMask && (slL2Point<FTOX))
"cmpl $0x10000000, %%ebx \n\t"
"jge 3f \n\t" // skipPixel
"testb (%%esi), %%dl \n\t"
"je 3f \n\t" // skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
"movd %%ecx, %%mm4 \n\t"
"movl %%ebx, %%eax \n\t"
"sarl $15, %%eax \n\t"
"andl $8191, %%eax \n\t"
"movzbl " ASMSYM(aubSqrt) "(%%eax), %%eax \n\t"
"movl (" ASMSYM(_slLightMax) "), %%ecx \n\t"
"cmpl (" ASMSYM(_slHotSpot) "), %%eax \n\t"
"jle 2f \n\t" // skipInterpolation
"movl $255, %%ecx \n\t"
"subl %%eax, %%ecx \n\t"
"imull (" ASMSYM(_slLightStep) "), %%ecx \n\t"
"2: \n\t" // skipInterpolation
// mix underlaying pixels with the calculated one
"movd %%ecx, %%mm6 \n\t"
"punpcklwd %%mm6, %%mm6 \n\t"
"punpckldq %%mm6, %%mm6 \n\t"
"pmulhw %%mm7, %%mm6 \n\t"
// add light pixel to underlying pixel
"movd (%%edi), %%mm5 \n\t"
"punpcklbw %%mm0, %%mm5 \n\t"
"paddw %%mm6, %%mm5 \n\t"
"packuswb %%mm0, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"movd %%mm4, %%ecx \n\t"
"3: \n\t" // skipPixel
// advance to next pixel
"addl $4, %%edi \n\t"
"movd %%mm3, %%eax \n\t"
"addl %%eax, %%ebx \n\t"
"paddd (" ASMSYM(mmDDL2oDU_addAmbientMaskPoint) "), %%mm3 \n\t"
"rolb $1, %%dl \n\t"
"adcl $0, %%esi \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
// advance to the next row
"popl %%ebx \n\t"
"addl (" ASMSYM(_slModulo) "), %%edi \n\t"
"paddd %%mm2, %%mm1 \n\t"
"paddd (" ASMSYM(mmDDL2oDV_addAmbientMaskPoint) "), %%mm2 \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"popl %%ebx \n\t"
"emms \n\t"
: // no outputs.
: "a" (ulLightRGB), "S" (pubMask), "c" (&ubMask)
: "edx", "edi", "cc", "memory"
);
#else
#error Please write inline assembly for your platform.
#endif
#else // Portable C version...
for( PIX pixV=0; pixV<_iRowCt; pixV++)
{
SLONG slL2Point = _slL2Row;
SLONG slDL2oDU = _slDL2oDURow;
for( PIX pixU=0; pixU<_iPixCt; pixU++)
{
// if the point is not masked
if( *pubMask & ubMask && (slL2Point<FTOX)) {
SLONG slL = (slL2Point>>SHIFTX)&(SQRTTABLESIZE-1); // and is just for degenerate cases
SLONG slIntensity = _slLightMax;
slL = aubSqrt[slL];
if( slL>_slHotSpot) slIntensity = ((255-slL)*_slLightStep)>>8;
// add the intensity to the pixel
AddToCluster( (UBYTE*)_pulLayer, slIntensity/255.0f);
}
// go to the next pixel
_pulLayer++;
slL2Point += slDL2oDU;
slDL2oDU += _slDDL2oDU;
ubMask<<=1;
if( ubMask==0) {
pubMask++;
ubMask = 1;
}
}
// go to the next row
_pulLayer += _slModulo/BYTES_PER_TEXEL;
_slL2Row += _slDL2oDV;
_slDL2oDV += _slDDL2oDV;
_slDL2oDURow += _slDDL2oDUoDV;
}
#endif
}
extern "C" {
__int64 mmDDL2oDU_AddDiffusionPoint;
__int64 mmDDL2oDV_AddDiffusionPoint;
}
// add one layer point light with diffusion and without mask
void CLayerMixer::AddDiffusionPoint(void)
{
// adjust params for diffusion lighting
SLONG slMax1oL = MAX_SLONG;
_slLightStep = FloatToInt(_slLightStep * _fMinLightDistance * _f1oFallOff);
if( _slLightStep!=0) slMax1oL = (256<<8) / _slLightStep +256;
// prepare some local variables
mmDDL2oDU_AddDiffusionPoint = _slDDL2oDU;
mmDDL2oDV_AddDiffusionPoint = _slDDL2oDV;
ULONG ulLightRGB = ByteSwap(lm_colLight);
_slLightMax<<=7;
_slLightStep>>=1;
#if ASMOPT == 1
#if (defined __MSVC_INLINE__)
__asm {
// prepare interpolants
movd mm0,D [_slL2Row]
movd mm1,D [_slDL2oDURow]
psllq mm1,32
por mm1,mm0 // MM1 = slDL2oDURow | slL2Row
movd mm0,D [_slDL2oDV]
movd mm2,D [_slDDL2oDUoDV]
psllq mm2,32
por mm2,mm0 // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
pxor mm0,mm0
movd mm7,D [ulLightRGB]
punpcklbw mm7,mm0
psllw mm7,1
// loop thru rows
mov edi,D [_pulLayer]
mov ebx,D [_iRowCt]
rowLoop:
push ebx
movd ebx,mm1 // EBX = slL2Point
movq mm3,mm1
psrlq mm3,32 // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
mov ecx,D [_iPixCt]
pixLoop:
// check if pixel need to be drawn
cmp ebx,FTOX
jge skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
movd mm4,ecx
mov eax,ebx
sar eax,SHIFTX
and eax,(SQRTTABLESIZE-1)
movzx eax,W auw1oSqrt[eax*2]
mov ecx,D [_slLightMax]
cmp eax,D [slMax1oL]
jge skipInterpolation
lea ecx,[eax-256]
imul ecx,D [_slLightStep]
skipInterpolation:
// calculate rgb pixel to add
movd mm6,ecx
punpcklwd mm6,mm6
punpckldq mm6,mm6
pmulhw mm6,mm7
// add dynamic light pixel to underlying pixel
movd mm5,D [edi]
punpcklbw mm5,mm0
paddw mm5,mm6
packuswb mm5,mm0
movd D [edi],mm5
movd ecx,mm4
skipPixel:
// advance to next pixel
add edi,4
movd eax,mm3
add ebx,eax
paddd mm3,Q [mmDDL2oDU_AddDiffusionPoint]
dec ecx
jnz pixLoop
// advance to the next row
pop ebx
add edi,D [_slModulo]
paddd mm1,mm2
paddd mm2,Q [mmDDL2oDV_AddDiffusionPoint]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
"pushl %%ebx \n\t"
"movl %%ecx, %%ebx \n\t"
"pushl %%ebx \n\t"
// prepare interpolants
"movd (" ASMSYM(_slL2Row) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDL2oDURow) "), %%mm1 \n\t"
"psllq $32, %%mm1 \n\t"
"por %%mm0, %%mm1 \n\t" // MM1 = slDL2oDURow | slL2Row
"movd (" ASMSYM(_slDL2oDV) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDDL2oDUoDV) "), %%mm2 \n\t"
"psllq $32, %%mm2 \n\t"
"por %%mm0, %%mm2 \n\t" // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
"pxor %%mm0, %%mm0 \n\t"
"movd %%eax, %%mm7 \n\t"
"punpcklbw %%mm0, %%mm7 \n\t"
"psllw $1, %%mm7 \n\t"
// loop thru rows
"movl (" ASMSYM(_pulLayer) "), %%edi \n\t"
"movl (" ASMSYM(_iRowCt) "), %%ebx \n\t"
"0: \n\t" // rowLoop
"pushl %%ebx \n\t"
"movd %%mm1, %%ebx \n\t" // EBX = slL2Point
"movq %%mm1, %%mm3 \n\t"
"psrlq $32, %%mm3 \n\t" // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
"movl (" ASMSYM(_iPixCt) "), %%ecx \n\t"
"1: \n\t" // pixLoop
// check if pixel need to be drawn
"cmpl $0x10000000, %%ebx \n\t"
"jge 3f \n\t" // skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
"movd %%ecx, %%mm4 \n\t"
"movl %%ebx, %%eax \n\t"
"sarl $15, %%eax \n\t"
"andl $8191, %%eax \n\t"
"movzwl " ASMSYM(auw1oSqrt) "(, %%eax, 2), %%eax \n\t"
"movl (" ASMSYM(_slLightMax) "), %%ecx \n\t"
"cmpl 4(%%esp), %%eax \n\t"
"jge 2f \n\t" // skipInterpolation
"leal -256(%%eax), %%ecx \n\t"
"imull (" ASMSYM(_slLightStep) "), %%ecx \n\t"
"2: \n\t" // skipInterpolation
// calculate rgb pixel to add
"movd %%ecx, %%mm6 \n\t"
"punpcklwd %%mm6, %%mm6 \n\t"
"punpckldq %%mm6, %%mm6 \n\t"
"pmulhw %%mm7, %%mm6 \n\t"
// add dynamic light pixel to underlying pixel
"movd (%%edi), %%mm5 \n\t"
"punpcklbw %%mm0, %%mm5 \n\t"
"paddw %%mm6, %%mm5 \n\t"
"packuswb %%mm0, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"movd %%mm4, %%ecx \n\t"
"3: \n\t" // skipPixel
// advance to next pixel
"addl $4, %%edi \n\t"
"movd %%mm3, %%eax \n\t"
"addl %%eax, %%ebx \n\t"
"paddd (" ASMSYM(mmDDL2oDU_AddDiffusionPoint) "), %%mm3 \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
// advance to the next row
"popl %%ebx \n\t"
"addl (" ASMSYM(_slModulo) "), %%edi \n\t"
"paddd %%mm2, %%mm1 \n\t"
"paddd (" ASMSYM(mmDDL2oDV_AddDiffusionPoint) "), %%mm2 \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"addl $4, %%esp \n\t"
"popl %%ebx \n\t"
"emms \n\t"
: // no outputs.
: "a" (ulLightRGB), "c" (slMax1oL)
: "edx", "edi", "esi", "cc", "memory"
);
#else
#error Write inline assembly for your platform.
#endif
#else
STUBBED("Some layer junk");
#endif
}
extern "C" {
__int64 mmDDL2oDU_AddDiffusionMaskPoint;
__int64 mmDDL2oDV_AddDiffusionMaskPoint;
}
// add one layer point light with diffusion and mask
void CLayerMixer::AddDiffusionMaskPoint( UBYTE *pubMask, UBYTE ubMask)
{
// adjust params for diffusion lighting
SLONG slMax1oL = MAX_SLONG;
_slLightStep = FloatToInt(_slLightStep * _fMinLightDistance * _f1oFallOff);
if( _slLightStep!=0) slMax1oL = (256<<8) / _slLightStep +256;
// prepare some local variables
mmDDL2oDU_AddDiffusionMaskPoint = _slDDL2oDU;
mmDDL2oDV_AddDiffusionMaskPoint = _slDDL2oDV;
ULONG ulLightRGB = ByteSwap(lm_colLight);
_slLightMax<<=7;
_slLightStep>>=1;
#if (ASMOPT == 1)
#if (defined __MSVC_INLINE__)
__asm {
// prepare interpolants
movd mm0,D [_slL2Row]
movd mm1,D [_slDL2oDURow]
psllq mm1,32
por mm1,mm0 // MM1 = slDL2oDURow | slL2Row
movd mm0,D [_slDL2oDV]
movd mm2,D [_slDDL2oDUoDV]
psllq mm2,32
por mm2,mm0 // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
pxor mm0,mm0 // MM0 = 0 | 0 (for unpacking purposes)
movd mm7,D [ulLightRGB]
punpcklbw mm7,mm0
psllw mm7,1
// loop thru rows
mov esi,D [pubMask]
mov edi,D [_pulLayer]
movzx edx,B [ubMask]
mov ebx,D [_iRowCt]
rowLoop:
push ebx
movd ebx,mm1 // EBX = slL2Point
movq mm3,mm1
psrlq mm3,32 // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
mov ecx,D [_iPixCt]
pixLoop:
// check if pixel need to be drawn; i.e. draw if( [esi] & ubMask && (slL2Point<FTOX))
cmp ebx,FTOX
jge skipPixel
test dl,B [esi]
je skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
movd mm4,ecx
mov eax,ebx
sar eax,SHIFTX
and eax,(SQRTTABLESIZE-1)
movzx eax,W auw1oSqrt[eax*2]
mov ecx,D [_slLightMax]
cmp eax,D [slMax1oL]
jge skipInterpolation
lea ecx,[eax-256]
imul ecx,D [_slLightStep]
skipInterpolation:
// mix underlaying pixels with the calculated one
movd mm6,ecx
punpcklwd mm6,mm6
punpckldq mm6,mm6
pmulhw mm6,mm7
// add light pixel to underlying pixel
movd mm5,D [edi]
punpcklbw mm5,mm0
paddw mm5,mm6
packuswb mm5,mm0
movd D [edi],mm5
movd ecx,mm4
skipPixel:
// advance to next pixel
add edi,4
movd eax,mm3
add ebx,eax
paddd mm3,Q [mmDDL2oDU_AddDiffusionMaskPoint]
rol dl,1
adc esi,0
dec ecx
jnz pixLoop
// advance to the next row
pop ebx
add edi,D [_slModulo]
paddd mm1,mm2
paddd mm2,Q [mmDDL2oDV_AddDiffusionMaskPoint]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
// prepare interpolants
"pushl %%edx \n\t" // slMax1oL
"movd (" ASMSYM(_slL2Row) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDL2oDURow) "), %%mm1 \n\t"
"psllq $32, %%mm1 \n\t"
"por %%mm0, %%mm1 \n\t" // MM1 = slDL2oDURow | slL2Row
"movd (" ASMSYM(_slDL2oDV) "), %%mm0 \n\t"
"movd (" ASMSYM(_slDDL2oDUoDV) "), %%mm2 \n\t"
"psllq $32, %%mm2 \n\t"
"por %%mm0, %%mm2 \n\t" // MM2 = slDDL2oDUoDV | slDL2oDV
// prepare color
"pxor %%mm0, %%mm0 \n\t" // MM0 = 0 | 0 (for unpacking purposes)
"movd %%eax, %%mm7 \n\t" // eax == ulLightRGB
"punpcklbw %%mm0, %%mm7 \n\t"
"psllw $1, %%mm7 \n\t"
// loop thru rows
"movl (" ASMSYM(_pulLayer) "), %%edi \n\t"
"movzbl (%%ecx), %%edx \n\t" // ecx == &ubMask
"movl (" ASMSYM(_iRowCt) "), %%ebx \n\t"
"0: \n\t" // rowLoop
"pushl %%ebx \n\t"
"movd %%mm1, %%ebx \n\t" // EBX = slL2Point
"movq %%mm1, %%mm3 \n\t"
"psrlq $32, %%mm3 \n\t" // MM3 = 0 | slDL2oDU
// loop thru pixels in current row
"movl (" ASMSYM(_iPixCt) "), %%ecx \n\t"
"1: \n\t" // pixLoop
// check if pixel need to be drawn; i.e. draw if( [esi] & ubMask && (slL2Point<FTOX))
"cmpl $0x10000000, %%ebx \n\t"
"jge 3f \n\t" // skipPixel
"testb (%%esi), %%dl \n\t"
"je 3f \n\t" // skipPixel
// calculate intensities and do actual drawing of shadow pixel ARGB
"movd %%ecx, %%mm4 \n\t"
"movl %%ebx, %%eax \n\t"
"sarl $15, %%eax \n\t"
"andl $8191, %%eax \n\t"
"movzwl " ASMSYM(auw1oSqrt) "(, %%eax, 2), %%eax \n\t"
"movl (" ASMSYM(_slLightMax) "), %%ecx \n\t"
"cmpl 4(%%esp), %%eax \n\t" // slMax1oL
"jge 2f \n\t" // skipInterpolation
"leal -256(%%eax), %%ecx \n\t"
"imull (" ASMSYM(_slLightStep) "), %%ecx \n\t"
"2: \n\t" // skipInterpolation
// mix underlaying pixels with the calculated one
"movd %%ecx, %%mm6 \n\t"
"punpcklwd %%mm6, %%mm6 \n\t"
"punpckldq %%mm6, %%mm6 \n\t"
"pmulhw %%mm7, %%mm6 \n\t"
// add light pixel to underlying pixel
"movd (%%edi), %%mm5 \n\t"
"punpcklbw %%mm0, %%mm5 \n\t"
"paddw %%mm6, %%mm5 \n\t"
"packuswb %%mm0, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"movd %%mm4, %%ecx \n\t"
"3: \n\t" // skipPixel
// advance to next pixel
"addl $4, %%edi \n\t"
"movd %%mm3, %%eax \n\t"
"addl %%eax, %%ebx \n\t"
"paddd (" ASMSYM(mmDDL2oDU_AddDiffusionMaskPoint) "), %%mm3 \n\t"
"rolb $1, %%dl \n\t"
"adcl $0, %%esi \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
// advance to the next row
"popl %%ebx \n\t"
"addl (" ASMSYM(_slModulo) "), %%edi \n\t"
"paddd %%mm2, %%mm1 \n\t"
"paddd (" ASMSYM(mmDDL2oDV_AddDiffusionMaskPoint) "), %%mm2 \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"addl $4, %%esp \n\t" // ditch our temporaries.
"emms \n\t"
: // no outputs.
: "a" (ulLightRGB), "S" (pubMask), "c" (&ubMask), "d" (slMax1oL)
: "cc", "memory"
);
#else
#error Write inline ASM for your platform.
#endif
#else
// for each pixel in the shadow map
for( PIX pixV=0; pixV<_iRowCt; pixV++)
{
SLONG slL2Point = _slL2Row;
SLONG slDL2oDU = _slDL2oDURow;
for( PIX pixU=0; pixU<_iPixCt; pixU++)
{
// if the point is not masked
if( *pubMask&ubMask && (slL2Point<FTOX)) {
SLONG sl1oL = (slL2Point>>SHIFTX)&(SQRTTABLESIZE-1); // and is just for degenerate cases
sl1oL = auw1oSqrt[sl1oL];
SLONG slIntensity = _slLightMax;
if( sl1oL<slMax1oL) slIntensity = ((sl1oL-256)*_slLightStep)>>16;
// add the intensity to the pixel
AddToCluster( (UBYTE*)_pulLayer, slIntensity/255.0f);
}
// advance to next pixel
_pulLayer++;
slL2Point += slDL2oDU;
slDL2oDU += _slDDL2oDU;
ubMask<<=1;
if( ubMask==0) {
pubMask++;
ubMask = 1;
}
}
// advance to next row
_pulLayer += _slModulo/BYTES_PER_TEXEL;
_slL2Row += _slDL2oDV;
_slDL2oDV += _slDDL2oDV;
_slDL2oDURow += _slDDL2oDUoDV;
}
#endif
}
// prepares point light that creates layer (returns TRUE if there is infulence)
BOOL CLayerMixer::PrepareOneLayerPoint( CBrushShadowLayer *pbsl, BOOL bNoMask)
{
// determine light infulence dimensions
_iPixCt = pbsl->bsl_pixSizeU >>lm_iMipShift;
_iRowCt = pbsl->bsl_pixSizeV >>lm_iMipShift;
PIX pixMinU = pbsl->bsl_pixMinU >>lm_iMipShift;
PIX pixMinV = pbsl->bsl_pixMinV >>lm_iMipShift;
// clamp influence to polygon size
if( (pixMinU+_iPixCt) > lm_pixPolygonSizeU && bNoMask) _iPixCt = lm_pixPolygonSizeU-pixMinU;
if( (pixMinV+_iRowCt) > lm_pixPolygonSizeV) _iRowCt = lm_pixPolygonSizeV-pixMinV;
_slModulo = (lm_pixCanvasSizeU-_iPixCt) *BYTES_PER_TEXEL;
_pulLayer = lm_pulShadowMap + (pixMinV*lm_pixCanvasSizeU)+pixMinU;
ASSERT( pixMinU>=0 && pixMinU<lm_pixCanvasSizeU && pixMinV>=0 && pixMinV<lm_pixCanvasSizeV);
// get the light source properties of the layer
lm_plsLight = pbsl->bsl_plsLightSource;
_vLight = &lm_plsLight->ls_penEntity->GetPlacement().pl_PositionVector;
_fMinLightDistance = lm_pbpoPolygon->bpo_pbplPlane->bpl_plAbsolute.PointDistance(*_vLight);
_f1oFallOff = 1.0f / lm_plsLight->ls_rFallOff;
_ulLightFlags = lm_plsLight->ls_ulFlags;
_ulPolyFlags = lm_pbpoPolygon->bpo_ulFlags;
lm_colLight = lm_plsLight->GetLightColor();
pbsl->bsl_colLastAnim = lm_colLight;
// if there is no influence, do nothing
if( (pbsl->bsl_pixSizeU>>lm_iMipShift)==0 || (pbsl->bsl_pixSizeV>>lm_iMipShift)==0
|| _iPixCt<=0 || _iRowCt<=0) return FALSE;
// adjust for sector ambient
if( _ulLightFlags&LSF_SUBSTRACTSECTORAMBIENT) {
COLOR colAmbient = lm_pbpoPolygon->bpo_pbscSector->bsc_colAmbient;
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[1], -((UBYTE*)&colAmbient)[1]);
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[2], -((UBYTE*)&colAmbient)[2]);
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[3], -((UBYTE*)&colAmbient)[3]);
if( _ulPolyFlags&BPOF_HASDIRECTIONALAMBIENT)
{ // find directional layers for each shadow layer
FOREACHINLIST( CBrushShadowLayer, bsl_lnInShadowMap, lm_pbsmShadowMap->bsm_lhLayers, itbsl)
{ // loop thru layers
CBrushShadowLayer &bsl = *itbsl;
if( bsl.bsl_plsLightSource->ls_ulFlags&LSF_DIRECTIONAL)
{ // skip if no ambient color
colAmbient = bsl.bsl_plsLightSource->ls_colAmbient & 0xFFFFFF00;
if( IsBlack(colAmbient)) continue;
// substract ambient
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[1], -((UBYTE*)&colAmbient)[1]);
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[2], -((UBYTE*)&colAmbient)[2]);
IncrementByteWithClip( ((UBYTE*)&lm_colLight)[3], -((UBYTE*)&colAmbient)[3]);
}
}
}
}
// prepare intermediate light interpolants
FLOAT3D v00 = (lm_vO+lm_vStepU*pixMinU + lm_vStepV*pixMinV) - *_vLight;
FLOAT fFactor = FTOX * _f1oFallOff*_f1oFallOff;
FLOAT fL2Row = v00%v00;
FLOAT fDDL2oDU = lm_vStepU%lm_vStepU;
FLOAT fDDL2oDV = lm_vStepV%lm_vStepV;
FLOAT fDDL2oDUoDV = lm_vStepU%lm_vStepV;
FLOAT fDL2oDURow = fDDL2oDU + 2*(lm_vStepU%v00);
FLOAT fDL2oDV = fDDL2oDV + 2*(lm_vStepV%v00);
//_v00 = v00;
#if ((ASMOPT == 1) && (!defined __GNU_INLINE__))
#if (defined __MSVC_INLINE__)
__asm {
fld D [fDDL2oDU]
fadd D [fDDL2oDU]
fld D [fDDL2oDV]
fadd D [fDDL2oDV]
fld D [fDDL2oDUoDV]
fadd D [fDDL2oDUoDV]
// st0=2*fDDL2oDUoDV, st1=2*fDDL2oDV, st2=2*fDDL2oDU
fld D [fL2Row]
fmul D [fFactor]
fld D [fDL2oDURow]
fmul D [fFactor]
fld D [fDL2oDV]
fmul D [fFactor]
// st0=fDL2oDV*fFactor, st1=fDL2oDURow*fFactor, st2=fL2Row*fFactor,
// st3=2*fDDL2oDUoDV, st4=2*fDDL2oDV, st5=2*fDDL2oDU
fld D [fFactor]
fmul st(4),st(0)
fmul st(5),st(0)
fmulp st(6),st(0)
fistp D [_slDL2oDV]
fistp D [_slDL2oDURow]
fistp D [_slL2Row]
fistp D [_slDDL2oDUoDV]
fistp D [_slDDL2oDV]
fistp D [_slDDL2oDU]
}
#elif (defined __GNU_INLINE__)
STUBBED("inline asm.");
#else
#error Please write inline assembly for your platform.
#endif
#else
fDDL2oDU *= 2;
fDDL2oDV *= 2;
fDDL2oDUoDV *= 2;
_slL2Row = FloatToInt( fL2Row * fFactor);
_slDDL2oDU = FloatToInt( fDDL2oDU * fFactor);
_slDDL2oDV = FloatToInt( fDDL2oDV * fFactor);
_slDDL2oDUoDV = FloatToInt( fDDL2oDUoDV * fFactor);
_slDL2oDURow = FloatToInt( fDL2oDURow * fFactor);
_slDL2oDV = FloatToInt( fDL2oDV * fFactor);
#endif
// prepare final light interpolants
_slLightMax = 255;
_slHotSpot = FloatToInt( 255.0f * lm_plsLight->ls_rHotSpot * _f1oFallOff);
_slLightStep = FloatToInt( 65535.0f / (255.0f - _slHotSpot));
// dark light inverts parameters
if( _ulLightFlags & LSF_DARKLIGHT) {
_slLightMax = -_slLightMax;
_slLightStep = -_slLightStep;
}
// saturate light color
lm_colLight = AdjustColor( lm_colLight, _slShdHueShift, _slShdSaturation);
// all done
return TRUE;
}
// add one layer to the shadow map (pubMask=NULL for no mask)
void CLayerMixer::AddOneLayerPoint( CBrushShadowLayer *pbsl, UBYTE *pubMask, UBYTE ubMask)
{
// try to prepare layer for this point light
_pfWorldEditingProfile.StartTimer( CWorldEditingProfile::PTI_ADDONELAYERPOINT);
if( !PrepareOneLayerPoint( pbsl, pubMask==NULL)) {
_pfWorldEditingProfile.StopTimer( CWorldEditingProfile::PTI_ADDONELAYERPOINT);
return;
}
// determine diffusion presence and corresponding routine
BOOL bDiffusion = (_ulLightFlags&LSF_DIFFUSION) && !(_ulPolyFlags&BPOF_NOPLANEDIFFUSION);
// masked or non-masked?
if( pubMask==NULL) {
// non-masked
if( !lm_bDynamic && bDiffusion) {
// non-masked diffusion
AddDiffusionPoint();
} else {
// non-masked ambient
AddAmbientPoint();
}
} else {
// masked
if( bDiffusion) {
// masked diffusion
AddDiffusionMaskPoint( pubMask, ubMask);
} else {
AddAmbientMaskPoint( pubMask, ubMask);
}
}
// all done
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_ADDONELAYERPOINT);
}
// apply gradient to layer
void CLayerMixer::AddOneLayerGradient( CGradientParameters &gp)
{
// convert gradient parameters for plane
ASSERT( Abs(gp.gp_fH1-gp.gp_fH0)>0.0001f);
FLOAT f1oDH = 1.0f / (gp.gp_fH1-gp.gp_fH0);
FLOAT fGr00 = (lm_vO % gp.gp_vGradientDir - gp.gp_fH0) *f1oDH;
FLOAT fDGroDI = (lm_vStepU % gp.gp_vGradientDir) *f1oDH;
FLOAT fDGroDJ = (lm_vStepV % gp.gp_vGradientDir) *f1oDH;
fDGroDI += fDGroDI/lm_pixPolygonSizeU;
fDGroDJ += fDGroDJ/lm_pixPolygonSizeV;
SLONG fixDGroDI = FloatToInt(fDGroDI*32767.0f); // 16:15
SLONG fixDGroDJ = FloatToInt(fDGroDJ*32767.0f); // 16:15
COLOR col0 = gp.gp_col0;
COLOR col1 = gp.gp_col1;
_pulLayer = lm_pulShadowMap;
FLOAT fStart = Clamp( fGr00-(fDGroDJ+fDGroDI)*0.5f, 0.0f, 1.0f);
#if ((ASMOPT == 1) && (!defined __GNU_INLINE__))
#if (defined __MSVC_INLINE__)
__int64 mmRowAdv;
SLONG fixGRow = (fGr00-(fDGroDJ+fDGroDI)*0.5f)*32767.0f; // 16:15
SLONG slModulo = (lm_pixCanvasSizeU-lm_pixPolygonSizeU) *BYTES_PER_TEXEL;
COLOR colStart = LerpColor( col0, col1, fStart);
INDEX ctCols = lm_pixPolygonSizeU;
INDEX ctRows = lm_pixPolygonSizeV;
BOOL bDarkLight = gp.gp_bDark;
__asm {
mov eax,D [colStart]
mov ebx,D [col0]
mov edx,D [col1]
bswap eax
bswap ebx
bswap edx
movd mm1,eax
movd mm2,ebx
movd mm3,edx
punpcklbw mm1,mm1
punpcklbw mm2,mm2
punpcklbw mm3,mm3
psrlw mm1,2
psrlw mm2,1
psrlw mm3,1
// eventually adjust for dark light
cmp D [bDarkLight],0
je skipDark
pcmpeqd mm0,mm0
pxor mm1,mm0
pxor mm2,mm0
pxor mm3,mm0
psubw mm1,mm0
psubw mm2,mm0
psubw mm3,mm0
skipDark:
// keep colors
movq mm0,mm2
movq mm7,mm3
// find row and column advancers
psubw mm3,mm2
movq mm2,mm3
movd mm5,D [fixDGroDI]
movd mm6,D [fixDGroDJ]
punpcklwd mm5,mm5
punpcklwd mm6,mm6
punpckldq mm5,mm5
punpckldq mm6,mm6
pmulhw mm2,mm5 // column color advancer (8:6)
pmulhw mm3,mm6 // row color advancer (8:6)
movq Q [mmRowAdv],mm3
// prepare starting variables
movq mm5,mm0
movq mm6,mm7
psraw mm5,1 // starting color
psraw mm6,1 // ending color
pxor mm0,mm0
mov esi,D [fixGRow]
mov edi,D [_pulLayer]
mov edx,D [ctRows]
rowLoop:
mov ebx,esi
movq mm4,mm1
mov ecx,D [ctCols]
pixLoop:
// add or substract light pixel to underlying pixel
movq mm7,mm4
psraw mm7,6
movd mm3,D [edi]
punpcklbw mm3,mm0
paddw mm3,mm7
packuswb mm3,mm0
movd D [edi],mm3
// advance to next pixel
add ebx,D [fixDGroDI]
cmp ebx,0x8000
ja pixClamp
paddw mm4,mm2
add edi,4
dec ecx
jnz pixLoop
jmp pixDone
pixClamp:
movq mm4,mm6
jg pixNext
movq mm4,mm5
pixNext:
add edi,4
dec ecx
jnz pixLoop
pixDone:
// advance to next row
add esi,D [fixDGroDJ]
cmp esi,0x8000
ja rowClamp
paddw mm1,Q [mmRowAdv]
add edi,D [slModulo]
dec edx
jnz rowLoop
jmp rowDone
rowClamp:
movq mm1,mm6
jg rowNext
movq mm1,mm5
rowNext:
add edi,D [slModulo]
dec edx
jnz rowLoop
rowDone:
emms
}
#elif (defined __GNU_INLINE__)
STUBBED("WRITE ME. Argh.");
#else
#error Need inline assembly for your platform.
#endif
#else
// well, make gradient ...
SLONG slR0=0,slG0=0,slB0=0;
SLONG slR1=0,slG1=0,slB1=0;
ColorToRGB( col0, (UBYTE&)slR0,(UBYTE&)slG0,(UBYTE&)slB0);
ColorToRGB( col1, (UBYTE&)slR1,(UBYTE&)slG1,(UBYTE&)slB1);
if( gp.gp_bDark) {
slR0 = -slR0; slG0 = -slG0; slB0 = -slB0;
slR1 = -slR1; slG1 = -slG1; slB1 = -slB1;
}
fixDGroDI >>= 1; // 16:14
fixDGroDJ >>= 1; // 16:14
SWORD fixRrow = Lerp( slR0,slR1,fStart) <<6; // 8:6
SWORD fixGrow = Lerp( slG0,slG1,fStart) <<6; // 8:6
SWORD fixBrow = Lerp( slB0,slB1,fStart) <<6; // 8:6
SWORD fixDRoDI = ((slR1-slR0)*fixDGroDI)>>8;
SWORD fixDGoDI = ((slG1-slG0)*fixDGroDI)>>8;
SWORD fixDBoDI = ((slB1-slB0)*fixDGroDI)>>8;
SWORD fixDRoDJ = ((slR1-slR0)*fixDGroDJ)>>8;
SWORD fixDGoDJ = ((slG1-slG0)*fixDGroDJ)>>8;
SWORD fixDBoDJ = ((slB1-slB0)*fixDGroDJ)>>8;
// loop it, baby
FLOAT fGrRow = fGr00 - (fDGroDJ+fDGroDI)*0.5f;
PIX pixOffset = 0;
PIX pixModulo = lm_pixCanvasSizeU-lm_pixPolygonSizeU;
for( INDEX j=0; j<lm_pixPolygonSizeV; j++)
{ // prepare row
FLOAT fGrCol = fGrRow;
SWORD fixRcol = fixRrow;
SWORD fixGcol = fixGrow;
SWORD fixBcol = fixBrow;
for( INDEX i=0; i<lm_pixPolygonSizeU; i++)
{ // loop pixels
SLONG slR = Clamp( fixRcol>>6, -255, +255);
SLONG slG = Clamp( fixGcol>>6, -255, +255);
SLONG slB = Clamp( fixBcol>>6, -255, +255);
IncrementByteWithClip( ((UBYTE*)&_pulLayer[pixOffset])[0], slR);
IncrementByteWithClip( ((UBYTE*)&_pulLayer[pixOffset])[1], slG);
IncrementByteWithClip( ((UBYTE*)&_pulLayer[pixOffset])[2], slB);
// advance to next pixel
fGrCol += fDGroDI;
pixOffset++;
if( fGrCol<0) {
fixRcol = slR0<<6;
fixGcol = slG0<<6;
fixBcol = slB0<<6;
} else if( fGrCol>1) {
fixRcol = slR1<<6;
fixGcol = slG1<<6;
fixBcol = slB1<<6;
} else {
fixRcol += fixDRoDI;
fixGcol += fixDGoDI;
fixBcol += fixDBoDI;
}
}
// advance to next row
fGrRow += fDGroDJ;
pixOffset += pixModulo;
if( fGrRow<0) {
fixRrow = slR0<<6;
fixGrow = slG0<<6;
fixBrow = slB0<<6;
} else if( fGrRow>1) {
fixRrow = slR1<<6;
fixGrow = slG1<<6;
fixBrow = slB1<<6;
} else {
fixRrow += fixDRoDJ;
fixGrow += fixDGoDJ;
fixBrow += fixDBoDJ;
}
}
#endif
}
// apply directional light or ambient to layer
void CLayerMixer::AddDirectional(void)
{
#if ASMOPT == 1
ULONG ulLight = ByteSwap( lm_colLight);
#if (defined __MSVC_INLINE__)
__asm {
// prepare pointers and variables
mov edi,D [_pulLayer]
mov ebx,D [_iRowCt]
movd mm6,D [ulLight]
punpckldq mm6,mm6
rowLoop:
mov ecx,D [_iPixCt]
shr ecx,1
jz pixRest
pixLoop:
// mix underlaying pixels with the constant color pixel
movq mm5,Q [edi]
paddusb mm5,mm6
movq Q [edi],mm5
// advance to next pixel
add edi,8
dec ecx
jnz pixLoop
pixRest:
test D [_iPixCt],1
jz rowNext
movd mm5,D [edi]
paddusb mm5,mm6
movd D [edi],mm5
add edi,4
rowNext:
// advance to the next row
add edi,D [_slModulo]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
// prepare pointers and variables
"pushl %%ebx \n\t"
"movl %%ecx,%%ebx \n\t"
"movd (%%eax), %%mm6 \n\t"
"punpckldq %%mm6, %%mm6 \n\t"
"0: \n\t" // rowLoop
"movl %%edx, %%ecx \n\t"
"shrl $1, %%ecx \n\t"
"jz 2f \n\t" // pixRest
"1: \n\t" // pixLoop
// mix underlaying pixels with the constant color pixel
"movq (%%edi), %%mm5 \n\t"
"paddusb %%mm6, %%mm5 \n\t"
"movq %%mm5, (%%edi) \n\t"
// advance to next pixel
"addl $8, %%edi \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
"2: \n\t" // pixRest
"testl $1, %%edx \n\t"
"jz 3f \n\t" // rowNext
"movd (%%edi), %%mm5 \n\t"
"paddusb %%mm6, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"addl $4, %%edi \n\t"
"3: \n\t" // rowNext
// advance to the next row
"addl %%esi, %%edi \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"popl %%ebx \n\t"
"emms \n\t"
: // no outputs.
: "S" (_slModulo), "D" (_pulLayer), "a" (&ulLight), "c" (_iRowCt),
"d" (_iPixCt)
: "cc", "memory"
);
#else
#error Write inline assembly for your platform.
#endif
#else
// for each pixel in the shadow map
for( PIX pixV=0; pixV<_iRowCt; pixV++) {
for( PIX pixU=0; pixU<_iPixCt; pixU++) {
// add the intensity to the pixel
AddToCluster( (UBYTE*)_pulLayer);
_pulLayer++; // go to the next pixel
} // go to the next row
_pulLayer += _slModulo;
}
#endif
}
// apply directional light thru mask to layer
void CLayerMixer::AddMaskDirectional( UBYTE *pubMask, UBYTE ubMask)
{
#if ASMOPT == 1
ULONG ulLight = ByteSwap( lm_colLight);
#if (defined __MSVC_INLINE__)
// prepare some local variables
__asm {
// prepare pointers and variables
movzx edx,B [ubMask]
mov esi,D [pubMask]
mov edi,D [_pulLayer]
mov ebx,D [_iRowCt]
movd mm6,D [ulLight]
rowLoop:
mov ecx,D [_iPixCt]
pixLoop:
// mix underlaying pixels with the constant light color if not shaded
test dl,B [esi]
jz skipLight
movd mm5,D [edi]
paddusb mm5,mm6
movd D [edi],mm5
skipLight:
// advance to next pixel
add edi,4
rol dl,1
adc esi,0
dec ecx
jnz pixLoop
// advance to the next row
add edi,D [_slModulo]
dec ebx
jnz rowLoop
emms
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
// prepare pointers and variables
"pushl %%ebx \n\t" // save GCC's register.
"movl (" ASMSYM(_iRowCt) "), %%ebx \n\t"
"pushl %%ecx \n\t"
"movzbl (%%edx), %%edx \n\t"
"movd (%%eax), %%mm6 \n\t"
"0: \n\t" // rowLoop
"movl (%%esp), %%ecx \n\t"
"1: \n\t" // pixLoop
// mix underlaying pixels with the constant light color if not shaded
"testb (%%esi), %%dl \n\t"
"jz 2f \n\t" // skipLight
"movd (%%edi), %%mm5 \n\t"
"paddusb %%mm6, %%mm5 \n\t"
"movd %%mm5, (%%edi) \n\t"
"2: \n\t" // skipLight
// advance to next pixel
"addl $4, %%edi \n\t"
"rolb $1, %%dl \n\t"
"adcl $0, %%esi \n\t"
"decl %%ecx \n\t"
"jnz 1b \n\t" // pixLoop
// advance to the next row
"addl (" ASMSYM(_slModulo) "), %%edi \n\t"
"decl %%ebx \n\t"
"jnz 0b \n\t" // rowLoop
"emms \n\t"
"popl %%ebx \n\t" // lose _iPixCt we pushed.
"popl %%ebx \n\t" // restore GCC's register.
:
: "d" (&ubMask), "S" (pubMask), "D" (_pulLayer),
"a" (&ulLight), "c" (_iPixCt)
: "cc", "memory"
);
#else
#error Please write inline assembly for your platform.
#endif
#else
// for each pixel in the shadow map
for( PIX pixV=0; pixV<_iRowCt; pixV++) {
for( PIX pixU=0; pixU<_iPixCt; pixU++) {
// if the point is not masked
if( *pubMask&ubMask) {
// add the intensity to the pixel
AddToCluster( (UBYTE*)_pulLayer);
} // go to the next pixel
_pulLayer++;
ubMask<<=1;
if( ubMask==0) {
pubMask ++;
ubMask = 1;
}
} // go to the next row
_pulLayer += _slModulo;
}
#endif
}
// apply directional light to layer
// (pubMask=NULL for no mask, ubMask = 0xFF for full mask)
void CLayerMixer::AddOneLayerDirectional( CBrushShadowLayer *pbsl, UBYTE *pubMask, UBYTE ubMask)
{
// only if there is color light (ambient is added at initial fill)
if( !(lm_pbpoPolygon->bpo_ulFlags&BPOF_HASDIRECTIONALLIGHT)) return;
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_ADDONELAYERDIRECTIONAL);
// determine light influence dimensions
_iPixCt = pbsl->bsl_pixSizeU >>lm_iMipShift;
_iRowCt = pbsl->bsl_pixSizeV >>lm_iMipShift;
PIX pixMinU = pbsl->bsl_pixMinU >>lm_iMipShift;
PIX pixMinV = pbsl->bsl_pixMinV >>lm_iMipShift;
ASSERT( pixMinU==0 && pixMinV==0);
// clamp influence to polygon size
if( _iPixCt > lm_pixPolygonSizeU && pubMask==NULL) _iPixCt = lm_pixPolygonSizeU;
if( _iRowCt > lm_pixPolygonSizeV) _iRowCt = lm_pixPolygonSizeV;
_slModulo = (lm_pixCanvasSizeU-_iPixCt) *BYTES_PER_TEXEL;
_pulLayer = lm_pulShadowMap;
// if there is no influence, do nothing
if( (pbsl->bsl_pixSizeU>>lm_iMipShift)==0 || (pbsl->bsl_pixSizeV>>lm_iMipShift)==0
|| _iPixCt<=0 || _iRowCt<=0) {
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_ADDONELAYERDIRECTIONAL);
return;
}
// get the light source of the layer
lm_plsLight = pbsl->bsl_plsLightSource;
const FLOAT3D &vLight = lm_plsLight->ls_penEntity->GetPlacement().pl_PositionVector;
AnglesToDirectionVector( lm_plsLight->ls_penEntity->GetPlacement().pl_OrientationAngle,
lm_vLightDirection);
// calculate intensity
FLOAT fIntensity = 1.0f;
if( !(lm_pbpoPolygon->bpo_ulFlags&BPOF_NOPLANEDIFFUSION)) {
fIntensity = -((lm_pbpoPolygon->bpo_pbplPlane->bpl_plAbsolute)%lm_vLightDirection);
fIntensity = ClampDn( fIntensity, 0.0f);
}
// calculate light color and ambient
lm_colLight = lm_plsLight->GetLightColor();
pbsl->bsl_colLastAnim = lm_colLight;
ULONG ulIntensity = NormFloatToByte(fIntensity);
ulIntensity = (ulIntensity<<CT_RSHIFT)|(ulIntensity<<CT_GSHIFT)|(ulIntensity<<CT_BSHIFT);
lm_colLight = MulColors( lm_colLight, ulIntensity);
lm_colLight = AdjustColor( lm_colLight, _slShdHueShift, _slShdSaturation);
// masked or non-masked?
if( pubMask==NULL) {
// non-masked
AddDirectional();
} else {
// masked
AddMaskDirectional( pubMask, ubMask);
}
// all done
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_ADDONELAYERDIRECTIONAL);
}
// clamper helper
static INDEX GetDither(void)
{
shd_iDithering = Clamp( shd_iDithering, 0L, 5L);
INDEX iDither = shd_iDithering;
if( iDither>2) iDither++;
return iDither;
}
// mix one mip-map
void CLayerMixer::MixOneMipmap(CBrushShadowMap *pbsm, INDEX iMipmap)
{
// remember general data
CalculateData( pbsm, iMipmap);
const BOOL bDynamicOnly = lm_pbpoPolygon->bpo_ulFlags&BPOF_DYNAMICLIGHTSONLY;
// fill with sector ambient
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_AMBIENTFILL);
// eventually add ambient component of all directional layers that might contribute
COLOR colAmbient = 0x80808000UL; // overide ambient light color for dynamic lights only
if( !bDynamicOnly) {
colAmbient = AdjustColor( lm_pbpoPolygon->bpo_pbscSector->bsc_colAmbient, _slShdHueShift, _slShdSaturation);
if( lm_pbpoPolygon->bpo_ulFlags&BPOF_HASDIRECTIONALAMBIENT) {
{FOREACHINLIST( CBrushShadowLayer, bsl_lnInShadowMap, lm_pbsmShadowMap->bsm_lhLayers, itbsl) {
CBrushShadowLayer &bsl = *itbsl;
CLightSource &ls = *bsl.bsl_plsLightSource;
ASSERT( &ls!=NULL); if( &ls==NULL) continue; // safety check
if( !(ls.ls_ulFlags&LSF_DIRECTIONAL)) continue; // skip non-directional layers
COLOR col = AdjustColor( ls.GetLightAmbient(), _slShdHueShift, _slShdSaturation);
colAmbient = AddColors( colAmbient, col);
}}
}
} // set initial color
#if (defined USE_PORTABLE_C)
register ULONG count = this->lm_pixCanvasSizeU * this->lm_pixCanvasSizeV;
#if PLATFORM_LITTLEENDIAN
// Forces C fallback; BYTESWAP itself is a no-op on little endian.
register ULONG swapped = BYTESWAP32_unsigned(colAmbient);
#else
STUBBED("actually need byteswap?");
// (uses inline asm on MacOS PowerPC)
register ULONG swapped = colAmbient;
BYTESWAP(swapped);
#endif
for (ULONG *ptr = this->lm_pulShadowMap; count; count--)
{
*ptr = swapped;
ptr++;
}
#elif (defined __MSVC_INLINE__)
__asm {
cld
mov ebx,D [this]
mov ecx,D [ebx].lm_pixCanvasSizeU
imul ecx,D [ebx].lm_pixCanvasSizeV
mov edi,D [ebx].lm_pulShadowMap
mov eax,D [colAmbient]
bswap eax
rep stosd
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
"cld \n\t"
"imull %%esi, %%ecx \n\t"
"bswapl %%eax \n\t"
"rep \n\t"
"stosl \n\t"
: // no outputs.
: "c" (this->lm_pixCanvasSizeU), "S" (this->lm_pixCanvasSizeV),
"a" (colAmbient), "D" (this->lm_pulShadowMap)
: "cc", "memory"
);
#else
#error Please write inline assembly for your platform.
#endif
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_AMBIENTFILL);
// find gradient layer
CGradientParameters gpGradient;
BOOL bHasGradient = FALSE;
ULONG ulGradientType = lm_pbpoPolygon->bpo_bppProperties.bpp_ubGradientType;
if( ulGradientType>0) {
CEntity *pen = lm_pbpoPolygon->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
if( pen!=NULL) bHasGradient = pen->GetGradient( ulGradientType, gpGradient);
}
// add gradient if gradient is light
if( bHasGradient && !gpGradient.gp_bDark) AddOneLayerGradient( gpGradient);
// for each shadow layer
lm_pbsmShadowMap->sm_ulFlags &= ~SMF_ANIMATINGLIGHTS;
{FORDELETELIST( CBrushShadowLayer, bsl_lnInShadowMap, lm_pbsmShadowMap->bsm_lhLayers, itbsl)
{
CBrushShadowLayer &bsl = *itbsl;
CLightSource &ls = *bsl.bsl_plsLightSource;
ASSERT( &ls!=NULL); if( &ls==NULL) continue; // safety check
// skip if should not be applied
if( (bDynamicOnly && !(ls.ls_ulFlags&LSF_NONPERSISTENT)) || ls.ls_ulFlags&LSF_DYNAMIC) continue;
// set corresponding shadowmap flag if this is an animating light
if( ls.ls_paoLightAnimation!=NULL) lm_pbsmShadowMap->sm_ulFlags |= SMF_ANIMATINGLIGHTS;
// if the layer is calculated
if( bsl.bsl_pubLayer!=NULL)
{
UBYTE *pub;
UBYTE ubMask;
FindLayerMipmap( itbsl, pub, ubMask);
// add the layer to the shadow map with masking
if( ls.ls_ulFlags&LSF_DIRECTIONAL) {
AddOneLayerDirectional( itbsl, pub, ubMask);
} else {
AddOneLayerPoint( itbsl, pub, ubMask);
}
}
// if the layer is all light
else if( !(bsl.bsl_ulFlags&BSLF_CALCULATED) || (bsl.bsl_ulFlags&BSLF_ALLLIGHT))
{
// add the layer to the shadow map without masking
if( ls.ls_ulFlags&LSF_DIRECTIONAL) {
AddOneLayerDirectional( itbsl, NULL);
} else {
AddOneLayerPoint( itbsl, NULL);
}
}
}}
// if gradient is dark, substract gradient
if( bHasGradient && gpGradient.gp_bDark) AddOneLayerGradient( gpGradient);
// do eventual filtering of shadow layer
shd_iFiltering = Clamp( shd_iFiltering, 0L, +6L);
if( shd_iFiltering>0) {
FilterBitmap( shd_iFiltering, lm_pulShadowMap, lm_pulShadowMap,
lm_pixPolygonSizeU, lm_pixPolygonSizeV, lm_pixCanvasSizeU, lm_pixCanvasSizeV);
}
// do eventual dithering of shadow layer
const INDEX iDither = GetDither();
if( !(_pGfx->gl_ulFlags&GLF_32BITTEXTURES)) shd_bFineQuality = FALSE;
if( iDither && !(shd_bFineQuality)) {
DitherBitmap( iDither, lm_pulShadowMap, lm_pulShadowMap,
lm_pixPolygonSizeU, lm_pixPolygonSizeV, lm_pixCanvasSizeU, lm_pixCanvasSizeV);
}
}
// copy from static shadow map to dynamic layer
__forceinline void CLayerMixer::CopyShadowLayer(void)
{
#if (defined USE_PORTABLE_C)
STUBBED("shadow layer stuff");
#elif (defined __MSVC_INLINE__)
__asm {
cld
mov ebx,D [this]
mov ecx,D [ebx].lm_pixCanvasSizeU
imul ecx,D [ebx].lm_pixCanvasSizeV
mov esi,D [ebx].lm_pulStaticShadowMap
mov edi,D [ebx].lm_pulShadowMap
rep movsd
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
"cld \n\t"
"imull %%eax, %%ecx \n\t"
"rep \n\t"
"movsl \n\t"
: // no outputs.
: "c" (this->lm_pixCanvasSizeU), "a" (this->lm_pixCanvasSizeV),
"S" (this->lm_pulStaticShadowMap), "D" (this->lm_pulShadowMap)
: "cc", "memory"
);
#else
#error Please write inline assembly for your platform.
#endif
}
// copy from static shadow map to dynamic layer
__forceinline void CLayerMixer::FillShadowLayer( COLOR col)
{
#if (defined USE_PORTABLE_C)
STUBBED("FillShadowLayer");
#elif (defined __MSVC_INLINE__)
__asm {
cld
mov ebx,D [this]
mov ecx,D [ebx].lm_pixCanvasSizeU
imul ecx,D [ebx].lm_pixCanvasSizeV
mov edi,D [ebx].lm_pulShadowMap
mov eax,D [col]
bswap eax // convert to R,G,B,A memory format!
rep stosd
}
#elif (defined __GNU_INLINE__)
__asm__ __volatile__ (
"cld \n\t"
"imull %%edx, %%ecx \n\t"
"bswapl %%eax \n\t" // convert to R,G,B,A memory format!
"rep \n\t"
"stosl \n\t"
: // no outputs.
: "c" (this->lm_pixCanvasSizeU), "d" (this->lm_pixCanvasSizeV),
"a" (col), "D" (this->lm_pulShadowMap)
: "cc", "memory"
);
#else
#error Please write inline assembly for your platform.
#endif
}
// mix dynamic lights
void CLayerMixer::MixOneMipmapDynamic( CBrushShadowMap *pbsm, INDEX iMipmap)
{
// remember general data
CalculateData( pbsm, iMipmap);
// if static shadow map is all flat
if( pbsm->sm_pulCachedShadowMap==&pbsm->sm_colFlat) {
// just fill dynamic shadow map with flat color
FillShadowLayer( pbsm->sm_colFlat);
} // if not flat
else {
// copy static layer
CopyShadowLayer();
}
// for each shadow layer
{FORDELETELIST( CBrushShadowLayer, bsl_lnInShadowMap, lm_pbsmShadowMap->bsm_lhLayers, itbsl)
{ // the layer's light source must be valid
CBrushShadowLayer &bsl = *itbsl;
CLightSource &ls = *bsl.bsl_plsLightSource;
ASSERT( &ls!=NULL);
if( !(ls.ls_ulFlags&LSF_DYNAMIC)) continue;
COLOR colLight = ls.GetLightColor() & ~CT_AMASK;
if( IsBlack(colLight)) continue;
// apply one layer
colLight = AdjustColor( colLight, _slShdHueShift, _slShdSaturation);
AddOneLayerPoint( itbsl, NULL);
}}
}
// constructor
CLayerMixer::CLayerMixer( CBrushShadowMap *pbsm, INDEX iFirstMip, INDEX iLastMip, BOOL bDynamic)
{
lm_bDynamic = bDynamic;
if( bDynamic) {
// check dynamic layers for complete blackness
BOOL bAllBlack = TRUE;
pbsm->sm_ulFlags &= ~SMF_DYNAMICBLACK;
{FORDELETELIST( CBrushShadowLayer, bsl_lnInShadowMap, pbsm->bsm_lhLayers, itbsl) {
CLightSource &ls = *itbsl->bsl_plsLightSource;
ASSERT( &ls!=NULL);
if( !(ls.ls_ulFlags&LSF_DYNAMIC)) continue;
COLOR colLight = ls.GetLightColor() & ~CT_AMASK;
itbsl->bsl_colLastAnim = colLight;
if( !IsBlack(colLight)) bAllBlack = FALSE; // must continue because of layer info update (light anim and such stuff)
}}
// skip mixing if dynamic layers were all black
if( bAllBlack) {
pbsm->sm_ulFlags |= SMF_DYNAMICBLACK;
return;
}
// need to mix in
for( INDEX iMipmap=iFirstMip; iMipmap<=iLastMip; iMipmap++) MixOneMipmapDynamic( pbsm, iMipmap);
}
// mix static layers
else {
for( INDEX iMipmap=iFirstMip; iMipmap<=iLastMip; iMipmap++) MixOneMipmap( pbsm, iMipmap);
}
}
// mix all layers into cached shadow map
void CBrushShadowMap::MixLayers( INDEX iFirstMip, INDEX iLastMip, BOOL bDynamic/*=FALSE*/)
{
_sfStats.StartTimer( CStatForm::STI_SHADOWUPDATE);
_pfWorldEditingProfile.StartTimer( CWorldEditingProfile::PTI_MIXLAYERS);
// mix the layers with a shadow mixer
CLayerMixer lmMixer( this, iFirstMip, iLastMip, bDynamic);
_pfWorldEditingProfile.StopTimer( CWorldEditingProfile::PTI_MIXLAYERS);
_sfStats.StopTimer( CStatForm::STI_SHADOWUPDATE);
}