// Copyright (C) 2002-2007 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CTRTextureGouraud.h" #ifdef _IRR_COMPILE_WITH_SOFTWARE_ namespace irr { namespace video { //! constructor CTRTextureGouraud::CTRTextureGouraud(IZBuffer* zbuffer) : RenderTarget(0), ZBuffer(zbuffer), SurfaceWidth(0), SurfaceHeight(0), BackFaceCullingEnabled(true), lockedZBuffer(0), lockedSurface(0), lockedTexture(0), lockedTextureWidth(0), textureXMask(0), textureYMask(0), Texture(0) { #ifdef _DEBUG setDebugName("CTRTextureGouraud"); #endif if (ZBuffer) zbuffer->grab(); } //! destructor CTRTextureGouraud::~CTRTextureGouraud() { if (RenderTarget) RenderTarget->drop(); if (ZBuffer) ZBuffer->drop(); if (Texture) Texture->drop(); } //! sets the Texture void CTRTextureGouraud::setTexture(video::IImage* texture) { if (Texture) Texture->drop(); Texture = texture; if (Texture) { Texture->grab(); lockedTextureWidth = Texture->getDimension().Width; textureXMask = lockedTextureWidth-1; textureYMask = Texture->getDimension().Height-1; } } //! en or disables the backface culling void CTRTextureGouraud::setBackfaceCulling(bool enabled) { BackFaceCullingEnabled = enabled; } //! sets a render target void CTRTextureGouraud::setRenderTarget(video::IImage* surface, const core::rect& viewPort) { if (RenderTarget) RenderTarget->drop(); RenderTarget = surface; if (RenderTarget) { SurfaceWidth = RenderTarget->getDimension().Width; SurfaceHeight = RenderTarget->getDimension().Height; RenderTarget->grab(); ViewPortRect = viewPort; } } //! draws an indexed triangle list void CTRTextureGouraud::drawIndexedTriangleList(S2DVertex* vertices, s32 vertexCount, const u16* indexList, s32 triangleCount) { const S2DVertex *v1, *v2, *v3; f32 tmpDiv; // temporary division factor f32 longest; // saves the longest span s32 height; // saves height of triangle u16* targetSurface; // target pointer where to plot pixels s32 spanEnd; // saves end of spans f32 leftdeltaxf; // amount of pixels to increase on left side of triangle f32 rightdeltaxf; // amount of pixels to increase on right side of triangle s32 leftx, rightx; // position where we are f32 leftxf, rightxf; // same as above, but as f32 values s32 span; // current span u16 *hSpanBegin, *hSpanEnd; // pointer used when plotting pixels s32 leftR, leftG, leftB, rightR, rightG, rightB; // color values s32 leftStepR, leftStepG, leftStepB, rightStepR, rightStepG, rightStepB; // color steps s32 spanR, spanG, spanB, spanStepR, spanStepG, spanStepB; // color interpolating values while drawing a span. s32 leftTx, rightTx, leftTy, rightTy; // texture interpolating values s32 leftTxStep, rightTxStep, leftTyStep, rightTyStep; // texture interpolating values s32 spanTx, spanTy, spanTxStep, spanTyStep; // values of Texturecoords when drawing a span core::rect TriangleRect; s32 leftZValue, rightZValue; s32 leftZStep, rightZStep; s32 spanZValue, spanZStep; // ZValues when drawing a span TZBufferType* zTarget, *spanZTarget; // target of ZBuffer; lockedSurface = (u16*)RenderTarget->lock(); lockedZBuffer = ZBuffer->lock(); lockedTexture = (u16*)Texture->lock(); for (s32 i=0; iPos.X - v1->Pos.X) * (v3->Pos.Y - v2->Pos.Y)) - ((v3->Pos.Y - v1->Pos.Y) * (v3->Pos.X - v2->Pos.X)); if (z < 0) continue; } //near plane clipping if (v1->ZValue<0 && v2->ZValue<0 && v3->ZValue<0) continue; // sort for width for inscreen clipping if (v1->Pos.X > v2->Pos.X) swapVertices(&v1, &v2); if (v1->Pos.X > v3->Pos.X) swapVertices(&v1, &v3); if (v2->Pos.X > v3->Pos.X) swapVertices(&v2, &v3); if ((v1->Pos.X - v3->Pos.X) == 0) continue; TriangleRect.UpperLeftCorner.X = v1->Pos.X; TriangleRect.LowerRightCorner.X = v3->Pos.X; // sort for height for faster drawing. if (v1->Pos.Y > v2->Pos.Y) swapVertices(&v1, &v2); if (v1->Pos.Y > v3->Pos.Y) swapVertices(&v1, &v3); if (v2->Pos.Y > v3->Pos.Y) swapVertices(&v2, &v3); TriangleRect.UpperLeftCorner.Y = v1->Pos.Y; TriangleRect.LowerRightCorner.Y = v3->Pos.Y; if (!TriangleRect.isRectCollided(ViewPortRect)) continue; // höhe des dreiecks berechnen height = v3->Pos.Y - v1->Pos.Y; if (!height) continue; // calculate longest span longest = (v2->Pos.Y - v1->Pos.Y) / (f32)height * (v3->Pos.X - v1->Pos.X) + (v1->Pos.X - v2->Pos.X); spanEnd = v2->Pos.Y; span = v1->Pos.Y; leftxf = (f32)v1->Pos.X; rightxf = (f32)v1->Pos.X; leftZValue = v1->ZValue; rightZValue = v1->ZValue; leftR = rightR = video::getRedSigned(v1->Color)<<8; leftG = rightG = video::getGreenSigned(v1->Color)<<8; leftB = rightB = video::getBlueSigned(v1->Color)<<8; leftTx = rightTx = v1->TCoords.X; leftTy = rightTy = v1->TCoords.Y; targetSurface = lockedSurface + span * SurfaceWidth; zTarget = lockedZBuffer + span * SurfaceWidth; if (longest < 0.0f) { tmpDiv = 1.0f / (f32)(v2->Pos.Y - v1->Pos.Y); rightdeltaxf = (v2->Pos.X - v1->Pos.X) * tmpDiv; rightZStep = (s32)((v2->ZValue - v1->ZValue) * tmpDiv); rightStepR = (s32)(((video::getRedSigned(v2->Color)<<8) - rightR) * tmpDiv); rightStepG = (s32)(((video::getGreenSigned(v2->Color)<<8) - rightG) * tmpDiv); rightStepB = (s32)(((video::getBlueSigned(v2->Color)<<8) - rightB) * tmpDiv); rightTxStep = (s32)((v2->TCoords.X - rightTx) * tmpDiv); rightTyStep = (s32)((v2->TCoords.Y - rightTy) * tmpDiv); tmpDiv = 1.0f / (f32)height; leftdeltaxf = (v3->Pos.X - v1->Pos.X) * tmpDiv; leftZStep = (s32)((v3->ZValue - v1->ZValue) * tmpDiv); leftStepR = (s32)(((video::getRedSigned(v3->Color)<<8) - leftR) * tmpDiv); leftStepG = (s32)(((video::getGreenSigned(v3->Color)<<8) - leftG) * tmpDiv); leftStepB = (s32)(((video::getBlueSigned(v3->Color)<<8) - leftB) * tmpDiv); leftTxStep = (s32)((v3->TCoords.X - leftTx) * tmpDiv); leftTyStep = (s32)((v3->TCoords.Y - leftTy) * tmpDiv); } else { tmpDiv = 1.0f / (f32)height; rightdeltaxf = (v3->Pos.X - v1->Pos.X) * tmpDiv; rightZStep = (s32)((v3->ZValue - v1->ZValue) * tmpDiv); rightStepR = (s32)(((video::getRedSigned(v3->Color)<<8) - rightR) * tmpDiv); rightStepG = (s32)(((video::getGreenSigned(v3->Color)<<8) - rightG) * tmpDiv); rightStepB = (s32)(((video::getBlueSigned(v3->Color)<<8) - rightB) * tmpDiv); rightTxStep = (s32)((v3->TCoords.X - rightTx) * tmpDiv); rightTyStep = (s32)((v3->TCoords.Y - rightTy) * tmpDiv); tmpDiv = 1.0f / (f32)(v2->Pos.Y - v1->Pos.Y); leftdeltaxf = (v2->Pos.X - v1->Pos.X) * tmpDiv; leftZStep = (s32)((v2->ZValue - v1->ZValue) * tmpDiv); leftStepR = (s32)(((video::getRedSigned(v2->Color)<<8) - leftR) * tmpDiv); leftStepG = (s32)(((video::getGreenSigned(v2->Color)<<8) - leftG) * tmpDiv); leftStepB = (s32)(((video::getBlueSigned(v2->Color)<<8) - leftB) * tmpDiv); leftTxStep = (s32)((v2->TCoords.X - leftTx) * tmpDiv); leftTyStep = (s32)((v2->TCoords.Y - leftTy) * tmpDiv); } // do it twice, once for the first half of the triangle, // end then for the second half. for (s32 triangleHalf=0; triangleHalf<2; ++triangleHalf) { if (spanEnd > ViewPortRect.LowerRightCorner.Y) spanEnd = ViewPortRect.LowerRightCorner.Y; // if the span <0, than we can skip these spans, // and proceed to the next spans which are really on the screen. if (span < ViewPortRect.UpperLeftCorner.Y) { // we'll use leftx as temp variable if (spanEnd < ViewPortRect.UpperLeftCorner.Y) { leftx = spanEnd - span; span = spanEnd; } else { leftx = ViewPortRect.UpperLeftCorner.Y - span; span = ViewPortRect.UpperLeftCorner.Y; } leftxf += leftdeltaxf*leftx; rightxf += rightdeltaxf*leftx; targetSurface += SurfaceWidth*leftx; zTarget += SurfaceWidth*leftx; leftZValue += leftZStep*leftx; rightZValue += rightZStep*leftx; leftR += leftStepR*leftx; leftG += leftStepG*leftx; leftB += leftStepB*leftx; rightR += rightStepR*leftx; rightG += rightStepG*leftx; rightB += rightStepB*leftx; leftTx += leftTxStep*leftx; leftTy += leftTyStep*leftx; rightTx += rightTxStep*leftx; rightTy += rightTyStep*leftx; } // the main loop. Go through every span and draw it. while (span < spanEnd) { leftx = (s32)(leftxf); rightx = (s32)(rightxf + 0.5f); // perform some clipping // thanks to a correction by hybrid // calculations delayed to correctly propagate to textures etc. s32 tDiffLeft=0, tDiffRight=0; if (leftxViewPortRect.LowerRightCorner.X) tDiffLeft=ViewPortRect.LowerRightCorner.X-leftx; if (rightxViewPortRect.LowerRightCorner.X) tDiffRight=ViewPortRect.LowerRightCorner.X-rightx; // draw the span if (rightx + tDiffRight - leftx - tDiffLeft) { tmpDiv = 1.0f / (f32)(rightx - leftx); spanZStep = (s32)((rightZValue - leftZValue) * tmpDiv); spanZValue = leftZValue+tDiffLeft*spanZStep; spanStepR = (s32)((rightR - leftR) * tmpDiv); spanR = leftR+tDiffLeft*spanStepR; spanStepG = (s32)((rightG - leftG) * tmpDiv); spanG = leftG+tDiffLeft*spanStepG; spanStepB = (s32)((rightB - leftB) * tmpDiv); spanB = leftB+tDiffLeft*spanStepB; spanTxStep = (s32)((rightTx - leftTx) * tmpDiv); spanTx = leftTx + tDiffLeft*spanTxStep; spanTyStep = (s32)((rightTy - leftTy) * tmpDiv); spanTy = leftTy+tDiffLeft*spanTyStep; hSpanBegin = targetSurface + leftx+tDiffLeft; spanZTarget = zTarget + leftx+tDiffLeft; hSpanEnd = targetSurface + rightx+tDiffRight; while (hSpanBegin < hSpanEnd) { if (spanZValue > *spanZTarget) { *spanZTarget = spanZValue; u16 color = lockedTexture[((spanTy>>8)&textureYMask) * lockedTextureWidth + ((spanTx>>8)&textureXMask)]; *hSpanBegin = video::RGB16(video::getRedSigned(color) * (spanR>>8) >>2, video::getGreenSigned(color) * (spanG>>8) >>2, video::getBlueSigned(color) * (spanB>>8) >>2); } spanR += spanStepR; spanG += spanStepG; spanB += spanStepB; spanTx += spanTxStep; spanTy += spanTyStep; spanZValue += spanZStep; ++hSpanBegin; ++spanZTarget; } } leftxf += leftdeltaxf; rightxf += rightdeltaxf; ++span; targetSurface += SurfaceWidth; zTarget += SurfaceWidth; leftZValue += leftZStep; rightZValue += rightZStep; leftR += leftStepR; leftG += leftStepG; leftB += leftStepB; rightR += rightStepR; rightG += rightStepG; rightB += rightStepB; leftTx += leftTxStep; leftTy += leftTyStep; rightTx += rightTxStep; rightTy += rightTyStep; } if (triangleHalf>0) // break, we've gout only two halves break; // setup variables for second half of the triangle. if (longest < 0.0f) { tmpDiv = 1.0f / (v3->Pos.Y - v2->Pos.Y); rightdeltaxf = (v3->Pos.X - v2->Pos.X) * tmpDiv; rightxf = (f32)v2->Pos.X; rightZValue = v2->ZValue; rightZStep = (s32)((v3->ZValue - v2->ZValue) * tmpDiv); rightR = video::getRedSigned(v2->Color)<<8; rightG = video::getGreenSigned(v2->Color)<<8; rightB = video::getBlueSigned(v2->Color)<<8; rightStepR = (s32)(((video::getRedSigned(v3->Color)<<8) - rightR) * tmpDiv); rightStepG = (s32)(((video::getGreenSigned(v3->Color)<<8) - rightG) * tmpDiv); rightStepB = (s32)(((video::getBlueSigned(v3->Color)<<8) - rightB) * tmpDiv); rightTx = v2->TCoords.X; rightTy = v2->TCoords.Y; rightTxStep = (s32)((v3->TCoords.X - rightTx) * tmpDiv); rightTyStep = (s32)((v3->TCoords.Y - rightTy) * tmpDiv); } else { tmpDiv = 1.0f / (v3->Pos.Y - v2->Pos.Y); leftdeltaxf = (v3->Pos.X - v2->Pos.X) * tmpDiv; leftxf = (f32)v2->Pos.X; leftZValue = v2->ZValue; leftZStep = (s32)((v3->ZValue - v2->ZValue) * tmpDiv); leftR = video::getRedSigned(v2->Color)<<8; leftG = video::getGreenSigned(v2->Color)<<8; leftB = video::getBlueSigned(v2->Color)<<8; leftStepR = (s32)(((video::getRedSigned(v3->Color)<<8) - leftR) * tmpDiv); leftStepG = (s32)(((video::getGreenSigned(v3->Color)<<8) - leftG) * tmpDiv); leftStepB = (s32)(((video::getBlueSigned(v3->Color)<<8) - leftB) * tmpDiv); leftTx = v2->TCoords.X; leftTy = v2->TCoords.Y; leftTxStep = (s32)((v3->TCoords.X - leftTx) * tmpDiv); leftTyStep = (s32)((v3->TCoords.Y - leftTy) * tmpDiv); } spanEnd = v3->Pos.Y; } } RenderTarget->unlock(); ZBuffer->unlock(); Texture->unlock(); } } // end namespace video } // end namespace irr #endif // _IRR_COMPILE_WITH_SOFTWARE_ namespace irr { namespace video { //! creates a flat triangle renderer ITriangleRenderer* createTriangleRendererTextureGouraud(IZBuffer* zbuffer) { #ifdef _IRR_COMPILE_WITH_SOFTWARE_ return new CTRTextureGouraud(zbuffer); #else return 0; #endif // _IRR_COMPILE_WITH_SOFTWARE_ } } // end namespace video } // end namespace irr