/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: b3ddeflt.cxx,v $
 * $Revision: 1.11 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_goodies.hxx"
#include "b3ddeflt.hxx"
#include "b3dtrans.hxx"
#include <vcl/outdev.hxx>
#include <vcl/bmpacc.hxx>
#include <vcl/bitmapex.hxx>
#include "b3dtex.hxx"

/*************************************************************************
|*
|* Konstruktor Base3DDefault
|*
\************************************************************************/

Base3DDefault::Base3DDefault(OutputDevice* pOutDev)
:   Base3DCommon(pOutDev),
    aZBuffer(),
    aPicture(),
    aMonoTransparence(),
    aAlphaTransparence(),
    aClearValue(Color(0x00ffffff)),
    pZBufferWrite(NULL),
    pPictureWrite(NULL),
    pTransparenceWrite(NULL),
    fDetail(1.0),
    fDetailBackup( -1.0 ),
    nMaxPixels(500000),
    bReducedDetail(FALSE),
    bDetailBackedup(FALSE),
    mbPTCorrection(sal_True)

{
}

/*************************************************************************
|*
|* Destruktor Base3DDefault
|*
\************************************************************************/

Base3DDefault::~Base3DDefault()
{
    // Alle Bitmap-Zugriffe freigeben
    ReleaseAccess();
}

/*************************************************************************
|*
|* Typbestimmung
|*
\************************************************************************/

UINT16 Base3DDefault::GetBase3DType()
{
    return BASE3D_TYPE_DEFAULT;
}

/*************************************************************************
|*
|* Darstellungsqualitaet setzen
|*
\************************************************************************/

void Base3DDefault::SetDisplayQuality(sal_uInt8 nNew)
{
    // Entsprechende PixelGrenze setzen
    SetMaxPixels(((sal_Int32)nNew * 3500) + 3500);

    // call parent
    Base3D::SetDisplayQuality(nNew);
}

/*************************************************************************
|*
|* Vergroeberungsstufe setzen
|*
\************************************************************************/

void Base3DDefault::SetDetail(double fNew)
{
    // nach unten begrenzen
    if(fNew > 1.0)
        fNew = 1.0;

    fDetail = fNew;
    if(fDetail < 1.0)
    {
        bReducedDetail = TRUE;
    }
    else
    {
        bReducedDetail = FALSE;
    }
}

/*************************************************************************
|*
|* BitmapAccess holen
|*
\************************************************************************/

void Base3DDefault::AcquireAccess()
{
    // Alle accesses holen
    pZBufferWrite = aZBuffer.AcquireWriteAccess();
    pPictureWrite = aPicture.AcquireWriteAccess();
    pTransparenceWrite = (GetTransparentPartsContainedHint())
        ? aAlphaTransparence.AcquireWriteAccess()
        : aMonoTransparence.AcquireWriteAccess();
}

/*************************************************************************
|*
|* BitmapAccess freigeben
|*
\************************************************************************/

void Base3DDefault::ReleaseAccess()
{
    // Alle accesses wieder freigeben
    if(pZBufferWrite)
    {
        delete pZBufferWrite;
        pZBufferWrite = NULL;
    }

    if(pPictureWrite)
    {
        delete pPictureWrite;
        pPictureWrite = NULL;
    }

    if(pTransparenceWrite)
    {
        delete pTransparenceWrite;
        pTransparenceWrite = NULL;
    }
}

/*************************************************************************
|*
|* Start der Szenenbeschreibung:
|*
\************************************************************************/

void Base3DDefault::StartScene()
{
    // Zugriffe freigeben
    ReleaseAccess();

    // Groesse der Bitmaps anpassen?
    sal_Bool bSizeHasChanged = (aLocalSizePixel.GetSize() != aPicture.GetSizePixel());

    // Neue BitMaps fuer ZBuffer und Picture allokieren
    if(bSizeHasChanged || !aZBuffer || !aPicture)
    {
        aZBuffer = Bitmap(aLocalSizePixel.GetSize(), 24);
        aPicture = Bitmap(aLocalSizePixel.GetSize(), 24);
    }

    // ZBuffer loeschen
    aZBuffer.Erase(aClearValue);

    // Bild loeschen
    aPicture.Erase( GetOutputDevice()->GetBackground().GetColor() );

    // Neue Transparenz-Bitmap allokieren
    if(GetTransparentPartsContainedHint())
    {
        // Alpha-Channel
        if(bSizeHasChanged || !aAlphaTransparence)
        {
            aAlphaTransparence = AlphaMask(aLocalSizePixel.GetSize());
            if(!!aMonoTransparence)
                aMonoTransparence = Bitmap();
        }

        // zu Beginn alles transparent
        aAlphaTransparence.Erase(BYTE(0xff));
    }
    else
    {
        // Mono-Channel
        if(bSizeHasChanged || !aMonoTransparence)
        {
            aMonoTransparence = Bitmap(aLocalSizePixel.GetSize(), 1);
            if(!!aAlphaTransparence)
                aAlphaTransparence = AlphaMask();
        }

        // zu Beginn alles transparent
        Color aEraseCol(COL_WHITE);
        aMonoTransparence.Erase(aEraseCol);
    }

    // Zugriffe wieder holen
    AcquireAccess();

    // lokale ClipRegion anpassen
    if(IsScissorRegionActive())
    {
        // Default specifics for scissoring
        aDefaultScissorRectangle = GetScissorRegionPixel();
        aDefaultScissorRectangle -= aSizePixel.TopLeft();

        // Detailstufe beachten
        if(bReducedDetail && fDetail != 0.0)
        {
            sal_Int32 nReducedWidth = (sal_Int32)((double)(aDefaultScissorRectangle.GetWidth() - 1) * fDetail);
            sal_Int32 nReducedHeight = (sal_Int32)((double)(aDefaultScissorRectangle.GetHeight() - 1)* fDetail);
            aDefaultScissorRectangle.SetSize(Size(nReducedWidth + 1, nReducedHeight + 1));
        }
    }

    // #i71618#
    // derive maPolygonOffset from mfPolygonOffset and use instead of old
    // POLYGONOFFSET_VALUE which was much to low with default of 120
    maPolygonOffset = (sal_uInt32)(getPolygonOffset() * ZBUFFER_DEPTH_RANGE);

    // call parent
    Base3DCommon::StartScene();
}

/*************************************************************************
|*
|* Ende der Szenenbeschreibung:
|*
\************************************************************************/

void Base3DDefault::EndScene()
{
    // Zugriffe freigeben
    ReleaseAccess();

    // Ausgabe der erzeugten BitMap
    BitmapEx aBitmapEx;

    if(GetTransparentPartsContainedHint())
    {
        // Alpha-Transparenz
        aBitmapEx = BitmapEx(aPicture, aAlphaTransparence);
    }
    else
    {
        // Mono-Transparenz
        aBitmapEx = BitmapEx(aPicture, aMonoTransparence);
    }

    // Dithern
    UINT16 nBitCount = GetOutputDevice()->GetBitCount();
    if( GetOutputDevice()->GetOutDevType() != OUTDEV_PRINTER && nBitCount <= 16 && GetDither())
    {
        aBitmapEx.Dither(nBitCount <= 8
            ? BMP_DITHER_MATRIX
            : BMP_DITHER_FLOYD_16);
    }

    if(GetOutputDevice()->GetConnectMetaFile() != NULL)
    {
        Rectangle aLogicRect;
        aLogicRect = GetOutputDevice()->PixelToLogic(aSizePixel);
        aBitmapEx.Draw(GetOutputDevice(), aLogicRect.TopLeft(), aLogicRect.GetSize());
    }
    else
    {
        sal_Bool bWasEnabled = GetOutputDevice()->IsMapModeEnabled();
        GetOutputDevice()->EnableMapMode(FALSE);

#ifdef DBG_UTIL     // draw for testing
        static sal_Bool bDoDrawBitmapForTesting(FALSE);
        if(bDoDrawBitmapForTesting)
        {
            Bitmap aBmp( aBitmapEx.GetMask() );
            aBmp.Convert( BMP_CONVERSION_4BIT_COLORS );
            aBmp.Replace( COL_WHITE, COL_LIGHTRED );
            GetOutputDevice()->DrawBitmap( aSizePixel.TopLeft(), aSizePixel.GetSize(), aBmp );
            GetOutputDevice()->SetFillColor( COL_LIGHTRED );
            GetOutputDevice()->SetLineColor( COL_LIGHTRED );
            GetOutputDevice()->DrawRect( aSizePixel );
        }
#endif

        aBitmapEx.Draw(GetOutputDevice(), aSizePixel.TopLeft(), aSizePixel.GetSize());
        GetOutputDevice()->EnableMapMode(bWasEnabled);
    }

    // Zugriffe wieder holen
    AcquireAccess();

    // eventuelle temporaere Reduzierung der Aufloesung zuruecknehmen
    if(bDetailBackedup)
    {
        SetDetail(fDetailBackup);
        bDetailBackedup = FALSE;
    }

    // call parent
    Base3DCommon::EndScene();
}

/*************************************************************************
|*
|* Callbacks bei Matrixaenderungen
|*
|* Ausgaberechteck innerhalb des OutputDevice festlegen. Die Koordinaten
|* sind device-spezifisch, muessen also evtl. erst auf pixelkoordinaten
|* umgerechnet werden
|*
\************************************************************************/

void Base3DDefault::SetTransformationSet(B3dTransformationSet* pSet)
{
    // call parent
    Base3DCommon::SetTransformationSet(pSet);

    if(GetTransformationSet())
    {
        // eventuelle temporaere Reduzierung der Aufloesung zuruecknehmen
        if(bDetailBackedup)
        {
            SetDetail(fDetailBackup);
            bDetailBackedup = FALSE;
        }

        // Neue Groesse fuer die Ausgabe
        aSizePixel = GetOutputDevice()->LogicToPixel(
            GetTransformationSet()->GetLogicalViewportBounds());

        // Eventuell durch ClipRegion eingeschraenkt? Dies
        // muss beachtet werden
        if(IsScissorRegionActive())
        {
            // draw region even smaller
            aSizePixel.Intersection(GetScissorRegionPixel());
        }

        // Testen, ob die Bitmap zu gross wird
        aLocalSizePixel = aSizePixel;

        double fQuadSize = aLocalSizePixel.GetWidth();  // sj: #i40320# solved overrun
        fQuadSize *= aLocalSizePixel.GetHeight();

        if( fQuadSize > GetMaxPixels() )
        {
            // Groesse reduzieren
            double fFactor = sqrt((double)GetMaxPixels() / fQuadSize);

            // Bei Druckjobs die Reduzierung einschraenken
            if(fFactor < 0.25 && GetOutputDevice()->GetOutDevType() == OUTDEV_PRINTER)
                fFactor = 0.25;

            // Wird hier mehr reduziert als sowieso schon eingestellt ist?
            if(fFactor < fDetail)
            {
                fDetailBackup = GetDetail();
                bDetailBackedup = TRUE;
                SetDetail(fFactor);
            }
        }

        // Detailstufe beachten
        if(bReducedDetail && fDetail != 0.0)
        {
            sal_Int32 nReducedWidth = (sal_Int32)((double)(aLocalSizePixel.GetWidth() - 1) * fDetail);
            sal_Int32 nReducedHeight = (sal_Int32)((double)(aLocalSizePixel.GetHeight() - 1)* fDetail);
            aLocalSizePixel.SetSize(Size(nReducedWidth + 1, nReducedHeight + 1));
        }

        // Falls die Groesse null ist, groesse auf 1,1 setzen
        if(aLocalSizePixel.GetSize().Width() < 1)
            aLocalSizePixel.SetSize(Size(1 , aLocalSizePixel.GetSize().Height()));
        if(aLocalSizePixel.GetSize().Height() < 1)
            aLocalSizePixel.SetSize(Size(aLocalSizePixel.GetSize().Width(), 1));
    }
}

/*************************************************************************
|*
|* Pixelkoordinaten des Punktes innerhalb der Bitmap holen
|*
\************************************************************************/

Point Base3DDefault::GetPixelCoor(B3dEntity& rEntity)
{
    if(bReducedDetail && fDetail != 0.0)
    {
        Point aRetval = GetOutputDevice()->LogicToPixel(
            Point((sal_Int32)(rEntity.Point().getX()),
            (sal_Int32)(rEntity.Point().getY()))) - aSizePixel.TopLeft();
        aRetval.X() = (sal_Int32)((double)aRetval.X() * fDetail);
        aRetval.Y() = (sal_Int32)((double)aRetval.Y() * fDetail);
        return aRetval;
    }
    else
    {
        return GetOutputDevice()->LogicToPixel(
            Point((sal_Int32)(rEntity.Point().getX()),
            (sal_Int32)(rEntity.Point().getY()))) - aSizePixel.TopLeft();
    }
}

/*************************************************************************
|*
|* 3DPunkt aus Pixelkoordinaten und Tiefe rekonstruieren
|*
\************************************************************************/

basegfx::B3DPoint Base3DDefault::Get3DCoor(Point& rPnt, double fDepth)
{
    if(bReducedDetail && fDetail != 0.0)
    {
        Point aPnt(rPnt);
        aPnt.X() = (sal_Int32)((double)aPnt.X() / fDetail);
        aPnt.Y() = (sal_Int32)((double)aPnt.Y() / fDetail);
        aPnt = GetOutputDevice()->PixelToLogic(aPnt + aSizePixel.TopLeft());
        return basegfx::B3DPoint(aPnt.X(), aPnt.Y(), fDepth);
    }
    else
    {
        Point aPnt = GetOutputDevice()->PixelToLogic(rPnt + aSizePixel.TopLeft());
        return basegfx::B3DPoint(aPnt.X(), aPnt.Y(), fDepth);
    }
}

/*************************************************************************
|*
|* ZBuffer Sichtbarkeitstest
|*
\************************************************************************/

sal_Bool Base3DDefault::IsVisibleAndScissor(sal_Int32 nX, sal_Int32 nY, sal_uInt32 nDepth)
{
    // #112303#
    // Do not allow pixels smaller then the bitmap
    if(nX < 0L || nY < 0L)
        return FALSE;

    // #112303#
    // Do not allow pixels bigger then the bitmap
    if(nX > aLocalSizePixel.GetWidth() || nY >aLocalSizePixel.GetHeight())
        return FALSE;

    if(!IsScissorRegionActive() || IsInScissorRegion(nX, nY))
    {
        const BitmapColor& rBmCol = pZBufferWrite->GetPixel(nY, nX);
        Color aColor(rBmCol.GetRed(), rBmCol.GetGreen(), rBmCol.GetBlue());

        return (aColor.GetColor() >= nDepth);
    }

    return FALSE;
}

/*************************************************************************
|*
|* Scissoring Sichtbarkeitstest
|*
\************************************************************************/

sal_Bool Base3DDefault::IsInScissorRegion(sal_Int32 nX, sal_Int32 nY)
{
    if(nX < aDefaultScissorRectangle.Left())
        return FALSE;
    if(nY < aDefaultScissorRectangle.Top())
        return FALSE;
    if(nX > aDefaultScissorRectangle.Right())
        return FALSE;
    if(nY > aDefaultScissorRectangle.Bottom())
        return FALSE;
    return TRUE;
}

/*************************************************************************
|*
|* Pixel setzen in allen Buffern
|*
\************************************************************************/

void Base3DDefault::WritePixel(sal_Int32 nX, sal_Int32 nY, Color aColor, sal_uInt32 nDepth)
{
    // #112303#
    // WritePixel requires the pixel coordinates to be safely on the buffer
    // bitmaps where the paint will take place. Thus, this asserts will
    // ensure that.
    DBG_ASSERT(nX >= 0L, "Base3DDefault::WritePixel: X-Coor negative (!)");
    DBG_ASSERT(nY >= 0L, "Base3DDefault::WritePixel: Y-Coor negative (!)");
    DBG_ASSERT(nX <= aPicture.GetSizePixel().Width(), "Base3DDefault::WritePixel: X-Coor too big (!)");
    DBG_ASSERT(nY <= aPicture.GetSizePixel().Height(), "Base3DDefault::WritePixel: Y-Coor too big (!)");

    // In Transparenz-Map eintragen
    if(GetTransparentPartsContainedHint())
    {
        if(aColor.GetTransparency())
        {
            BYTE nOldTrans = pTransparenceWrite->GetPixel(nY, nX).GetIndex();

            if(nOldTrans != (BYTE)0xff)
            {
                // Farbe mischen
                BitmapColor aOldCol = pPictureWrite->GetPixel(nY, nX);
                UINT16 nNegTrans = 0x0100 - (UINT16)aColor.GetTransparency();
                aColor.SetRed((BYTE)((((UINT16)aOldCol.GetRed() * (UINT16)aColor.GetTransparency())
                    + (aColor.GetRed() * nNegTrans)) >> 8));
                aColor.SetGreen((BYTE)((((UINT16)aOldCol.GetGreen() * (UINT16)aColor.GetTransparency())
                    + (aColor.GetGreen() * nNegTrans)) >> 8));
                aColor.SetBlue((BYTE)((((UINT16)aOldCol.GetBlue() * (UINT16)aColor.GetTransparency())
                    + (aColor.GetBlue() * nNegTrans)) >> 8));
                pPictureWrite->SetPixel(nY, nX, aColor);

                // Transparenz mischen
                pTransparenceWrite->SetPixel(nY, nX,
                    (BYTE)(((UINT16)(nOldTrans+1) * (UINT16)aColor.GetTransparency()) >> 8));
            }
            else
            {
                // Pixel setzen
                pPictureWrite->SetPixel(nY, nX, aColor);

                // Alpha-Wert setzen
                pTransparenceWrite->SetPixel(nY, nX, aColor.GetTransparency());
            }
        }
        else
        {
            // Pixel setzen
            pPictureWrite->SetPixel(nY, nX, aColor);

            // Alpha-Wert setzen
            pTransparenceWrite->SetPixel(nY, nX, (BYTE)0x00);

            // Z-Buffer setzen
            Color aZBufCol(nDepth);
            pZBufferWrite->SetPixel(nY, nX, aZBufCol);
        }
    }
    else
    {
        // Dieser Punkt in der Mono-Transparenz ist nicht transparent
        BitmapColor aColBlack(BYTE(0));
        pTransparenceWrite->SetPixel(nY, nX, aColBlack);

        // Pixel setzen
        pPictureWrite->SetPixel(nY, nX, aColor);

        // Z-Buffer setzen
        Color aZBufCol(nDepth);
        pZBufferWrite->SetPixel(nY, nX, aZBufCol);
    }
}

/*************************************************************************
|*
|* Zeichenfunktionen; alle Objekte sind geclippt
|* Einzelner Punkt
|*
\************************************************************************/

void Base3DDefault::Clipped3DPoint(sal_uInt32 nInd)
{
    B3dEntity& rEntity = aBuffers[nInd];

    // Geometrie holen
    rEntity.ToDeviceCoor(GetTransformationSet());
    Point aOutPoint = GetPixelCoor(rEntity);
    sal_uInt32 nDepth = (sal_uInt32)rEntity.Point().getZ();

    // PolygonOffset beachten
    if(GetPolygonOffset(Base3DPolygonOffsetPoint))
    {
        if(nDepth >= maPolygonOffset)
            nDepth -= maPolygonOffset;
        else
            nDepth = 0;
    }

    // Zeichnen
    if(IsVisibleAndScissor(aOutPoint.X(), aOutPoint.Y(), nDepth))
        WritePixel(aOutPoint.X(), aOutPoint.Y(), rEntity.Color(), nDepth);
}

/*************************************************************************
|*
|* Zeichenfunktionen; alle Objekte sind geclippt
|* Linie
|*
\************************************************************************/

void Base3DDefault::Clipped3DLine(sal_uInt32 nInd1, sal_uInt32 nInd2)
{
    B3dEntity& rEntity1 = aBuffers[nInd1];
    B3dEntity& rEntity2 = aBuffers[nInd2];
    bNormalsUsed = rEntity1.IsNormalUsed() && rEntity2.IsNormalUsed();
    bTextureUsed = IsTextureActive() && rEntity1.IsTexCoorUsed() && rEntity2.IsTexCoorUsed();

    // ColorModel fuer diese Punkte anwenden, falls Normale vorhanden
    // Danach Normale als ungueltig markieren, da nur noch die berechnete
    // Farbe bei Aufteilungen weiter interpoliert wird
    if(bNormalsUsed)
    {
        // Vektoren normalisieren
        rEntity1.Normal().normalize();
        rEntity2.Normal().normalize();

        if(GetShadeModel() != Base3DPhong)
        {
            // Farben auswerten
            rEntity1.Color() = SolveColorModel(GetMaterialObject(),
                rEntity1.Normal(), rEntity1.Point());
            rEntity2.Color() = SolveColorModel(GetMaterialObject(),
                rEntity2.Normal(), rEntity2.Point());

            // Die Normalen NICHT ungueltig machen, da die Entities
            // eventuell noch fuer weitere Primitive benutzt werden.
            // Aber lokal merken, dass die Normalen bereits ausgewertet sind
            bNormalsUsed = FALSE;
        }
    }

    // Geometrie holen
    rEntity1.ToDeviceCoor(GetTransformationSet());
    rEntity2.ToDeviceCoor(GetTransformationSet());
    Rectangle aPrimitiveArea;

    aOutPointTop = GetPixelCoor(rEntity1);
    aOutPointLeft = GetPixelCoor(rEntity2);

    if(IsScissorRegionActive())
    {
        aPrimitiveArea.Union(Rectangle(aOutPointTop, aOutPointTop));
        aPrimitiveArea.Union(Rectangle(aOutPointLeft, aOutPointLeft));
    }

    if(!IsScissorRegionActive()
        || (IsScissorRegionActive()
        && !aDefaultScissorRectangle.GetIntersection(aPrimitiveArea).IsEmpty()))
    {
        if(bTextureUsed)
        {
            fTexWidth = (double)GetActiveTexture()->GetBitmapSize().Width();
            fTexHeight = (double)GetActiveTexture()->GetBitmapSize().Height();
        }

        // Punkt, Farbe und Z-Wert interpolieren und die Linie gererieren
        sal_Int32 nDx = aOutPointLeft.X() - aOutPointTop.X();
        sal_Int32 nDy = aOutPointLeft.Y() - aOutPointTop.Y();
        sal_Int32 nCount;

        // Werte fuer Schleife vorbereiten
        if(labs(nDx) > labs(nDy))
            // ueber X gehen
            nCount = labs(nDx);
        else
            // ueber Y gehen
            nCount = labs(nDy);

        if(nCount)
        {
            // Interpolatoren vorbereiten
            aIntXPosLeft.Load(aOutPointTop.X(), aOutPointLeft.X(), nCount);
            aIntXPosRight.Load(aOutPointTop.Y(), aOutPointLeft.Y(), nCount);
            sal_uInt32 nDepth;

            // PolygonOffset beachten
            if(GetPolygonOffset())
            {
                double fDepthLeft = rEntity1.Point().getZ();
                double fDepthRight = rEntity2.Point().getZ();

                if(fDepthLeft >= double(maPolygonOffset))
                    fDepthLeft -= double(maPolygonOffset);
                else
                    fDepthLeft = 0.0;

                if(fDepthRight >= double(maPolygonOffset))
                    fDepthRight -= double(maPolygonOffset);
                else
                    fDepthRight = 0.0;

                aIntDepthLine.Load(fDepthLeft, fDepthRight, nCount);
            }
            else
            {
                aIntDepthLine.Load(rEntity1.Point().getZ(), rEntity2.Point().getZ(), nCount);
            }

            // Texturkoordinateninterpolation?
            if(bTextureUsed)
            {
                aIntTexSLine.Load(
                    rEntity1.TexCoor().getX() * fTexWidth,
                    rEntity2.TexCoor().getX() * fTexWidth,
                    nCount);

                aIntTexTLine.Load(
                    rEntity1.TexCoor().getY() * fTexHeight,
                    rEntity2.TexCoor().getY() * fTexHeight,
                    nCount);
            }

            if(bNormalsUsed && GetShadeModel() == Base3DPhong)
            {
                // Normalen und Geometrie interpolieren
                if(GetTransformationSet())
                {
                    basegfx::B3DVector aInvTrans(GetTransformationSet()->GetTranslate());
                    basegfx::B3DVector aInvScale(GetTransformationSet()->GetScale());

                    // Tiefe und Normale vorbereiten
                    aIntVectorLine.Load(rEntity1.Normal(), rEntity2.Normal(), nCount);

                    // Linie zeichnen
                    if(bTextureUsed)
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                            {
                                Point aTmpPoint(nDx, nDy);
                                basegfx::B3DPoint aPoint = Get3DCoor(aTmpPoint, nDepth);
                                aPoint -= aInvTrans;
                                aPoint /= aInvScale;
                                basegfx::B3DVector aNormal;
                                aIntVectorLine.GetVector3DValue(aNormal);
                                aNormal.normalize();
                                Color aCol = SolveColorModel(GetMaterialObject(), aNormal, aPoint);
                                GetActiveTexture()->ModifyColor(aCol,
                                    aIntTexSLine.GetDoubleValue(),
                                    aIntTexTLine.GetDoubleValue());
                                WritePixel(nDx, nDy, aCol, nDepth);
                            }

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntVectorLine.Increment();
                                aIntTexSLine.Increment();
                                aIntTexTLine.Increment();
                            }
                        }
                    }
                    else
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                            {
                                Point aTmpPoint(nDx, nDy);
                                basegfx::B3DPoint aPoint = Get3DCoor(aTmpPoint, nDepth);
                                aPoint -= aInvTrans;
                                aPoint /= aInvScale;
                                basegfx::B3DVector aNormal;
                                aIntVectorLine.GetVector3DValue(aNormal);
                                aNormal.normalize();
                                Color aCol = SolveColorModel(GetMaterialObject(), aNormal, aPoint);
                                WritePixel(nDx, nDy, aCol, nDepth);
                            }

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntVectorLine.Increment();
                            }
                        }
                    }
                }
            }
            else
            {
                if(rEntity1.Color() != rEntity2.Color())
                {
                    // Farbe und Geometrie interpolieren
                    // Tiefe und Farbe vorbereiten
                    aIntColorLine.Load(rEntity1.Color(), rEntity2.Color(), nCount);

                    // Linie zeichnen
                    if(bTextureUsed)
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                            {
                                Color aCol = aIntColorLine.GetColorValue();
                                GetActiveTexture()->ModifyColor(aCol,
                                    aIntTexSLine.GetDoubleValue(),
                                    aIntTexTLine.GetDoubleValue());
                                WritePixel(nDx, nDy, aCol, nDepth);
                            }

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntColorLine.Increment();
                                aIntTexSLine.Increment();
                                aIntTexTLine.Increment();
                            }
                        }
                    }
                    else
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                                WritePixel(nDx, nDy, aIntColorLine.GetColorValue(), nDepth);

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntColorLine.Increment();
                            }
                        }
                    }
                }
                else
                {
                    // Nur die Geometrie interpolieren
                    // Linie zeichnen
                    if(bTextureUsed)
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                            {
                                Color aCol = rEntity1.Color();
                                GetActiveTexture()->ModifyColor(aCol,
                                    aIntTexSLine.GetDoubleValue(),
                                    aIntTexTLine.GetDoubleValue());
                                WritePixel(nDx, nDy, aCol, nDepth);
                            }

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntTexSLine.Increment();
                                aIntTexTLine.Increment();
                            }
                        }
                    }
                    else
                    {
                        while(nCount--)
                        {
                            // weiterer Punkt
                            nDx = aIntXPosLeft.GetLongValue();
                            nDy = aIntXPosRight.GetLongValue();
                            nDepth = aIntDepthLine.GetUINT32Value();

                            if(IsVisibleAndScissor(nDx, nDy, nDepth))
                                WritePixel(nDx, nDy, rEntity1.Color(), nDepth);

                            if(nCount)
                            {
                                // Weiterschalten
                                aIntXPosLeft.Increment();
                                aIntXPosRight.Increment();
                                aIntDepthLine.Increment();
                                aIntTexSLine.Increment();
                                aIntTexTLine.Increment();
                            }
                        }
                    }
                }
            }
        }
    }
}

/*************************************************************************
|*
|* Zeichenfunktionen; alle Objekte sind geclippt
|* Polygon
|*
\************************************************************************/

void Base3DDefault::Clipped3DTriangle(sal_uInt32 nInd1, sal_uInt32 nInd2, sal_uInt32 nInd3)
{
    B3dEntity& rEntity1 = aBuffers[nInd1];
    B3dEntity& rEntity2 = aBuffers[nInd2];
    B3dEntity& rEntity3 = aBuffers[nInd3];

    bNormalsUsed = rEntity1.IsNormalUsed() && rEntity2.IsNormalUsed() && rEntity3.IsNormalUsed();
    bTextureUsed = IsTextureActive() && rEntity1.IsTexCoorUsed() && rEntity2.IsTexCoorUsed() && rEntity3.IsTexCoorUsed();
    Base3DMaterialMode eMode = Base3DMaterialFront;

    // ColorModel fuer diese Punkte anwenden, falls Normale vorhanden
    // Danach Normale als ungueltig markieren, da nur noch die berechnete
    // Farbe bei Aufteilungen weiter interpoliert wird
    if(bNormalsUsed)
    {
        // Vektoren normalisieren
        rEntity1.Normal().normalize();
        rEntity2.Normal().normalize();
        rEntity3.Normal().normalize();

        if(GetShadeModel() != Base3DPhong)
        {
            // Normale berechnen, Farben auswerten
            if(rEntity1.PlaneNormal().getZ() < 0.0 && (GetLightGroup() && GetLightGroup()->GetModelTwoSide()))
                eMode = Base3DMaterialBack;

            rEntity1.Color() = SolveColorModel(
                GetMaterialObject(eMode),
                rEntity1.Normal(),
                rEntity1.Point()
                );

            rEntity2.Color() = SolveColorModel(
                GetMaterialObject(eMode),
                rEntity2.Normal(),
                rEntity2.Point()
                );

            rEntity3.Color() = SolveColorModel(
                GetMaterialObject(eMode),
                rEntity3.Normal(),
                rEntity3.Point()
                );

            // Die Normalen NICHT ungueltig machen, da die Entities
            // eventuell noch fuer weitere Primitive benutzt werden.
            // Aber lokal merken, dass die Normalen bereits ausgewertet sind
            bNormalsUsed = FALSE;
        }
    }

    // Geometrie holen
    rEntity1.ToDeviceCoor(GetTransformationSet());
    rEntity2.ToDeviceCoor(GetTransformationSet());
    rEntity3.ToDeviceCoor(GetTransformationSet());

    // Punkte ordnen. Oberster nach pEntTop
    if(rEntity1.Point().getY() < rEntity2.Point().getY() && rEntity1.Point().getY() < rEntity3.Point().getY())
    {
        // rEntity1 ist der oberste
        pEntTop = &rEntity1;

        // Left, Right erst mal zuweisen
        pEntRight = &rEntity3;
        pEntLeft = &rEntity2;
    }
    else
    {
        if(rEntity2.Point().getY() < rEntity3.Point().getY())
        {
            // rEntity2 ist der oberste
            pEntTop = &rEntity2;

            // Left, Right erst mal zuweisen
            pEntRight = &rEntity1;
            pEntLeft = &rEntity3;
        }
        else
        {
            // rEntity3 ist der oberste
            pEntTop = &rEntity3;

            // Left, Right erst mal zuweisen
            pEntRight = &rEntity2;
            pEntLeft = &rEntity1;
        }
    }

    // Werte holen
    Rectangle aPrimitiveArea;

    aOutPointTop = GetPixelCoor(*pEntTop);
    aOutPointLeft = GetPixelCoor(*pEntLeft);
    aOutPointRight = GetPixelCoor(*pEntRight);

    if(IsScissorRegionActive())
    {
        aPrimitiveArea.Union(Rectangle(aOutPointTop, aOutPointTop));
        aPrimitiveArea.Union(Rectangle(aOutPointLeft, aOutPointLeft));
        aPrimitiveArea.Union(Rectangle(aOutPointRight, aOutPointRight));
    }

    if(!IsScissorRegionActive()
        || (IsScissorRegionActive()
        && !aDefaultScissorRectangle.GetIntersection(aPrimitiveArea).IsEmpty()))
    {
        if(bTextureUsed)
        {
            fTexWidth = (double)GetActiveTexture()->GetBitmapSize().Width();
            fTexHeight = (double)GetActiveTexture()->GetBitmapSize().Height();
        }

        // Links und rechts ordnen
        sal_Int32 nDeltaYLeft = aOutPointLeft.Y() - aOutPointTop.Y();
        sal_Int32 nDeltaYRight = aOutPointRight.Y() - aOutPointTop.Y();
        sal_Int32 nYLine;

        if((aOutPointLeft.X() - aOutPointTop.X()) * nDeltaYRight
            - nDeltaYLeft * (aOutPointRight.X() - aOutPointTop.X()) > 0)
        {
            // Links und rechts vertauschen
            // Punkte
            nYLine = aOutPointLeft.X();
            aOutPointLeft.X() = aOutPointRight.X();
            aOutPointRight.X() = nYLine;
            nYLine = aOutPointLeft.Y();
            aOutPointLeft.Y() = aOutPointRight.Y();
            aOutPointRight.Y() = nYLine;

            // Deltas
            nYLine = nDeltaYLeft; nDeltaYLeft = nDeltaYRight; nDeltaYRight = nYLine;

            // Zeiger auf Entities
            B3dEntity* pTmp = pEntLeft; pEntLeft = pEntRight; pEntRight = pTmp;
        }

        // YStart, Links und rechts laden
        nYLine = aOutPointTop.Y();

        aIntXPosLeft.Load(aOutPointTop.X(), aOutPointLeft.X(), nDeltaYLeft);
        aIntDepthLeft.Load(pEntTop->Point().getZ(), pEntLeft->Point().getZ(), nDeltaYLeft);

        aIntXPosRight.Load(aOutPointTop.X(), aOutPointRight.X(), nDeltaYRight);
        aIntDepthRight.Load(pEntTop->Point().getZ(), pEntRight->Point().getZ(), nDeltaYRight);

        if(bTextureUsed)
        {
            // #96837#
            if(mbPTCorrection)
            {
                // Load real depth interpolators (if needed)
                const double fRealDepthLeft(1.0 / GetTransformationSet()->ViewToEyeCoor(pEntLeft->Point()).getZ());
                const double fRealDepthRight(1.0 / GetTransformationSet()->ViewToEyeCoor(pEntRight->Point()).getZ());
                const double fRealDepthTop(1.0 / GetTransformationSet()->ViewToEyeCoor(pEntTop->Point()).getZ());
                aRealDepthLeft.Load(fRealDepthTop, fRealDepthLeft, nDeltaYLeft);
                aRealDepthRight.Load(fRealDepthTop, fRealDepthRight, nDeltaYRight);

                // #96837#
                aIntTexSLeft.Load(
                    pEntTop->TexCoor().getX() * fTexWidth * fRealDepthTop,
                    pEntLeft->TexCoor().getX() * fTexWidth * fRealDepthLeft,
                    nDeltaYLeft);

                aIntTexTLeft.Load(
                    pEntTop->TexCoor().getY() * fTexHeight * fRealDepthTop,
                    pEntLeft->TexCoor().getY() * fTexHeight * fRealDepthLeft,
                    nDeltaYLeft);

                aIntTexSRight.Load(
                    pEntTop->TexCoor().getX() * fTexWidth * fRealDepthTop,
                    pEntRight->TexCoor().getX() * fTexWidth * fRealDepthRight,
                    nDeltaYRight);

                aIntTexTRight.Load(
                    pEntTop->TexCoor().getY() * fTexHeight * fRealDepthTop,
                    pEntRight->TexCoor().getY() * fTexHeight * fRealDepthRight,
                    nDeltaYRight);
            }
            else
            {
                aIntTexSLeft.Load(
                    pEntTop->TexCoor().getX() * fTexWidth,
                    pEntLeft->TexCoor().getX() * fTexWidth,
                    nDeltaYLeft);

                aIntTexTLeft.Load(
                    pEntTop->TexCoor().getY() * fTexHeight,
                    pEntLeft->TexCoor().getY() * fTexHeight,
                    nDeltaYLeft);

                aIntTexSRight.Load(
                    pEntTop->TexCoor().getX() * fTexWidth,
                    pEntRight->TexCoor().getX() * fTexWidth,
                    nDeltaYRight);

                aIntTexTRight.Load(
                    pEntTop->TexCoor().getY() * fTexHeight,
                    pEntRight->TexCoor().getY() * fTexHeight,
                    nDeltaYRight);
            }
        }

        if(bNormalsUsed && GetShadeModel() == Base3DPhong)
        {
            // Normalen und Geometrie interpolieren
            aIntVectorLeft.Load(pEntTop->Normal(), pEntLeft->Normal(), nDeltaYLeft);
            aIntVectorRight.Load(pEntTop->Normal(), pEntRight->Normal(), nDeltaYRight);
            B3dMaterial& rMat = GetMaterialObject(eMode);

            if(bTextureUsed)
            {
                // Schleife
                while(nDeltaYLeft || nDeltaYRight)
                {
                    // Zeile ausgeben
                    DrawLinePhongTexture(nYLine, rMat);

                    // naechste Zeile vorbereiten rechts
                    if(!nDeltaYRight && nDeltaYLeft)
                    {
                        // Rechts ist zuende, lade neu mit Rest nach links
                        nDeltaYRight = nDeltaYLeft;
                        LoadRightTexture(nDeltaYRight);
                        aIntVectorRight.Load(pEntRight->Normal(), pEntLeft->Normal(), nDeltaYRight);
                    }

                    // naechste Zeile vorbereiten links
                    if(!nDeltaYLeft && nDeltaYRight)
                    {
                        // Links ist zuende, lade neu mit Rest nach rechts
                        nDeltaYLeft = nDeltaYRight;
                        LoadLeftTexture(nDeltaYLeft);
                        aIntVectorLeft.Load(pEntLeft->Normal(), pEntRight->Normal(), nDeltaYLeft);
                    }

                    // naechste Zeile rechts
                    if(nDeltaYRight || nDeltaYLeft)
                    {
                        nDeltaYRight--;
                        NextStepRightTexture();
                        aIntVectorRight.Increment();

                        nDeltaYLeft--;
                        NextStepLeftTexture();
                        aIntVectorLeft.Increment();

                        nYLine++;
                    }
                }
            }
            else
            {
                // Schleife
                while(nDeltaYLeft || nDeltaYRight)
                {
                    // Zeile ausgeben
                    DrawLinePhong(nYLine, rMat);

                    // naechste Zeile vorbereiten rechts
                    if(!nDeltaYRight && nDeltaYLeft)
                    {
                        // Rechts ist zuende, lade neu mit Rest nach links
                        nDeltaYRight = nDeltaYLeft;
                        LoadRight(nDeltaYRight);
                        aIntVectorRight.Load(pEntRight->Normal(), pEntLeft->Normal(), nDeltaYRight);
                    }

                    // naechste Zeile vorbereiten links
                    if(!nDeltaYLeft && nDeltaYRight)
                    {
                        // Links ist zuende, lade neu mit Rest nach rechts
                        nDeltaYLeft = nDeltaYRight;
                        LoadLeft(nDeltaYLeft);
                        aIntVectorLeft.Load(pEntLeft->Normal(), pEntRight->Normal(), nDeltaYLeft);
                    }

                    // naechste Zeile rechts
                    if(nDeltaYRight || nDeltaYLeft)
                    {
                        nDeltaYRight--;
                        NextStepRight();
                        aIntVectorRight.Increment();

                        nDeltaYLeft--;
                        NextStepLeft();
                        aIntVectorLeft.Increment();

                        nYLine++;
                    }
                }
            }
        }
        else
        {
            if(!(rEntity1.Color() == rEntity2.Color() && rEntity1.Color() == rEntity3.Color()))
            {
                // Farbe und Geometrie interpolieren
                aIntColorLeft.Load(pEntTop->Color(), pEntLeft->Color(), nDeltaYLeft);
                aIntColorRight.Load(pEntTop->Color(), pEntRight->Color(), nDeltaYRight);

                if(bTextureUsed)
                {
                    // Schleife
                    while(nDeltaYLeft || nDeltaYRight)
                    {
                        // Zeile ausgeben
                        DrawLineColorTexture(nYLine);

                        // naechste Zeile vorbereiten rechts
                        if(!nDeltaYRight && nDeltaYLeft)
                        {
                            // Rechts ist zuende, lade neu mit Rest nach links
                            nDeltaYRight = nDeltaYLeft;
                            LoadRightTexture(nDeltaYRight);
                            aIntColorRight.Load(pEntRight->Color(), pEntLeft->Color(), nDeltaYRight);
                        }

                        // naechste Zeile vorbereiten links
                        if(!nDeltaYLeft && nDeltaYRight)
                        {
                            // Links ist zuende, lade neu mit Rest nach rechts
                            nDeltaYLeft = nDeltaYRight;
                            LoadLeftTexture(nDeltaYLeft);
                            aIntColorLeft.Load(pEntLeft->Color(), pEntRight->Color(), nDeltaYLeft);
                        }

                        // naechste Zeile rechts
                        if(nDeltaYRight || nDeltaYLeft)
                        {
                            nDeltaYRight--;
                            NextStepRightTexture();
                            aIntColorRight.Increment();

                            nDeltaYLeft--;
                            NextStepLeftTexture();
                            aIntColorLeft.Increment();

                            nYLine++;
                        }
                    }
                }
                else
                {
                    // Schleife
                    while(nDeltaYLeft || nDeltaYRight)
                    {
                        // Zeile ausgeben
                        DrawLineColor(nYLine);

                        // naechste Zeile vorbereiten rechts
                        if(!nDeltaYRight && nDeltaYLeft)
                        {
                            // Rechts ist zuende, lade neu mit Rest nach links
                            nDeltaYRight = nDeltaYLeft;
                            LoadRight(nDeltaYRight);
                            aIntColorRight.Load(pEntRight->Color(), pEntLeft->Color(), nDeltaYRight);
                        }

                        // naechste Zeile vorbereiten links
                        if(!nDeltaYLeft && nDeltaYRight)
                        {
                            // Links ist zuende, lade neu mit Rest nach rechts
                            nDeltaYLeft = nDeltaYRight;
                            LoadLeft(nDeltaYLeft);
                            aIntColorLeft.Load(pEntLeft->Color(), pEntRight->Color(), nDeltaYLeft);
                        }

                        // naechste Zeile rechts
                        if(nDeltaYRight || nDeltaYLeft)
                        {
                            nDeltaYRight--;
                            NextStepRight();
                            aIntColorRight.Increment();

                            nDeltaYLeft--;
                            NextStepLeft();
                            aIntColorLeft.Increment();

                            nYLine++;
                        }
                    }
                }
            }
            else
            {
                // Nur die Geometrie interpolieren
                if(bTextureUsed)
                {
                    // Schleife
                    while(nDeltaYLeft || nDeltaYRight)
                    {
                        // Zeile ausgeben
                        DrawLineTexture(nYLine, pEntTop->Color());

                        // naechste Zeile vorbereiten rechts
                        if(!nDeltaYRight && nDeltaYLeft)
                        {
                            // Rechts ist zuende, lade neu mit Rest nach links
                            nDeltaYRight = nDeltaYLeft;
                            LoadRightTexture(nDeltaYRight);
                        }

                        // naechste Zeile vorbereiten links
                        if(!nDeltaYLeft && nDeltaYRight)
                        {
                            // Links ist zuende, lade neu mit Rest nach rechts
                            nDeltaYLeft = nDeltaYRight;
                            LoadLeftTexture(nDeltaYLeft);
                        }

                        // naechste Zeile rechts
                        if(nDeltaYRight || nDeltaYLeft)
                        {
                            nDeltaYRight--;
                            NextStepRightTexture();

                            nDeltaYLeft--;
                            NextStepLeftTexture();

                            nYLine++;
                        }
                    }
                }
                else
                {
                    // Schleife
                    while(nDeltaYLeft || nDeltaYRight)
                    {
                        // Zeile ausgeben
                        DrawLine(nYLine, pEntTop->Color());

                        // naechste Zeile vorbereiten rechts
                        if(!nDeltaYRight && nDeltaYLeft)
                        {
                            // Rechts ist zuende, lade neu mit Rest nach links
                            nDeltaYRight = nDeltaYLeft;
                            LoadRight(nDeltaYRight);
                        }

                        // naechste Zeile vorbereiten links
                        if(!nDeltaYLeft && nDeltaYRight)
                        {
                            // Links ist zuende, lade neu mit Rest nach rechts
                            nDeltaYLeft = nDeltaYRight;
                            LoadLeft(nDeltaYLeft);
                        }

                        // naechste Zeile rechts
                        if(nDeltaYRight || nDeltaYLeft)
                        {
                            nDeltaYRight--;
                            NextStepRight();

                            nDeltaYLeft--;
                            NextStepLeft();

                            nYLine++;
                        }
                    }
                }
            }
        }
    }
}

void Base3DDefault::DrawLinePhongTexture(sal_Int32 nYPos, B3dMaterial& rMat)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        basegfx::B3DVector aVectorLeft;
        aIntVectorLeft.GetVector3DValue(aVectorLeft);
        basegfx::B3DVector aVectorRight;
        aIntVectorRight.GetVector3DValue(aVectorRight);
        aIntVectorLine.Load(aVectorLeft, aVectorRight, nXLineDelta);
        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        // #96837##
        if(mbPTCorrection)
        {
            aRealDepthLine.Load(aRealDepthLeft.GetDoubleValue(), aRealDepthRight.GetDoubleValue(), nXLineDelta);
        }

        aIntTexSLine.Load(aIntTexSLeft.GetDoubleValue(), aIntTexSRight.GetDoubleValue(), nXLineDelta);
        aIntTexTLine.Load(aIntTexTLeft.GetDoubleValue(), aIntTexTRight.GetDoubleValue(), nXLineDelta);

        if(GetTransformationSet())
        {
            basegfx::B3DVector aInvTrans = GetTransformationSet()->GetTranslate();
            basegfx::B3DVector aInvScale = GetTransformationSet()->GetScale();

            while(nXLineDelta--)
            {
                // Werte vorbereiten
                sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

                // Punkt ausgeben
                if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
                {
                    Point aTmpPoint(nXLineStart, nYPos);
                    basegfx::B3DPoint aPoint = Get3DCoor(aTmpPoint, nDepth);
                    aPoint -= aInvTrans;
                    aPoint /= aInvScale;
                    basegfx::B3DVector aNormal;
                    aIntVectorLine.GetVector3DValue(aNormal);
                    aNormal.normalize();
                    Color aCol = SolveColorModel(rMat, aNormal, aPoint);

                    // #96837#
                    if(mbPTCorrection)
                    {
                        GetActiveTexture()->ModifyColor(
                            aCol,
                            aIntTexSLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue(),
                            aIntTexTLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue()
                            );
                    }
                    else
                    {
                        GetActiveTexture()->ModifyColor(
                            aCol,
                            aIntTexSLine.GetDoubleValue(),
                            aIntTexTLine.GetDoubleValue()
                            );
                    }

                    WritePixel(nXLineStart, nYPos, aCol, nDepth);
                }

                if(nXLineDelta)
                {
                    // naechste Spalte
                    nXLineStart++;

                    // naechste Tiefe und Farbe
                    aIntDepthLine.Increment();
                    aIntVectorLine.Increment();

                    // #96837#
                    if(mbPTCorrection)
                    {
                        aRealDepthLine.Increment();
                    }

                    aIntTexSLine.Increment();
                    aIntTexTLine.Increment();
                }
            }
        }
    }
}

void Base3DDefault::DrawLinePhong(sal_Int32 nYPos, B3dMaterial& rMat)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        basegfx::B3DVector aVectorLeft;
        aIntVectorLeft.GetVector3DValue(aVectorLeft);
        basegfx::B3DVector aVectorRight;
        aIntVectorRight.GetVector3DValue(aVectorRight);
        aIntVectorLine.Load(aVectorLeft, aVectorRight, nXLineDelta);
        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        if(GetTransformationSet())
        {
            basegfx::B3DVector aInvTrans = GetTransformationSet()->GetTranslate();
            basegfx::B3DVector aInvScale = GetTransformationSet()->GetScale();
            while(nXLineDelta--)
            {
                // Werte vorbereiten
                sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

                // Punkt ausgeben
                if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
                {
                    Point aTmpPoint(nXLineStart, nYPos);
                    basegfx::B3DPoint aPoint = Get3DCoor(aTmpPoint, nDepth);
                    aPoint -= aInvTrans;
                    aPoint /= aInvScale;
                    basegfx::B3DVector aNormal;
                    aIntVectorLine.GetVector3DValue(aNormal);
                    aNormal.normalize();
                    Color aCol = SolveColorModel(rMat, aNormal, aPoint);
                    WritePixel(nXLineStart, nYPos, aCol, nDepth);
                }

                if(nXLineDelta)
                {
                    // naechste Spalte
                    nXLineStart++;

                    // naechste Tiefe und Farbe
                    aIntDepthLine.Increment();
                    aIntVectorLine.Increment();
                }
            }
        }
    }
}

void Base3DDefault::DrawLineColorTexture(sal_Int32 nYPos)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        aIntColorLine.Load(aIntColorLeft.GetColorValue(), aIntColorRight.GetColorValue(), nXLineDelta);
        aIntTexSLine.Load(aIntTexSLeft.GetDoubleValue(), aIntTexSRight.GetDoubleValue(), nXLineDelta);
        aIntTexTLine.Load(aIntTexTLeft.GetDoubleValue(), aIntTexTRight.GetDoubleValue(), nXLineDelta);
        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        // #96837#
        if(mbPTCorrection)
        {
            aRealDepthLine.Load(aRealDepthLeft.GetDoubleValue(), aRealDepthRight.GetDoubleValue(), nXLineDelta);
        }

        while(nXLineDelta--)
        {
            // Werte vorbereiten
            sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

            // Punkt ausgeben
            if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
            {
                Color aCol = aIntColorLine.GetColorValue();

                // #96837#
                if(mbPTCorrection)
                {
                    GetActiveTexture()->ModifyColor(
                        aCol,
                        aIntTexSLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue(),
                        aIntTexTLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue()
                        );
                }
                else
                {
                    GetActiveTexture()->ModifyColor(
                        aCol,
                        aIntTexSLine.GetDoubleValue(),
                        aIntTexTLine.GetDoubleValue()
                        );
                }

                WritePixel(nXLineStart, nYPos, aCol, nDepth);
            }

            if(nXLineDelta)
            {
                // naechste Spalte
                nXLineStart++;

                // naechste Tiefe und Farbe
                aIntDepthLine.Increment();
                aIntColorLine.Increment();
                aIntTexSLine.Increment();
                aIntTexTLine.Increment();

                // #96837#
                if(mbPTCorrection)
                {
                    aRealDepthLine.Increment();
                }
            }
        }
    }
}

void Base3DDefault::DrawLineColor(sal_Int32 nYPos)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        aIntColorLine.Load(aIntColorLeft.GetColorValue(), aIntColorRight.GetColorValue(), nXLineDelta);
        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        while(nXLineDelta--)
        {
            // Werte vorbereiten
            sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

            // Punkt ausgeben
            if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
                WritePixel(nXLineStart, nYPos, aIntColorLine.GetColorValue(), nDepth);

            if(nXLineDelta)
            {
                // naechste Spalte
                nXLineStart++;

                // naechste Tiefe und Farbe
                aIntDepthLine.Increment();
                aIntColorLine.Increment();
            }
        }
    }
}

void Base3DDefault::DrawLineTexture(sal_Int32 nYPos, Color& rCol)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        aIntTexSLine.Load(aIntTexSLeft.GetDoubleValue(), aIntTexSRight.GetDoubleValue(), nXLineDelta);
        aIntTexTLine.Load(aIntTexTLeft.GetDoubleValue(), aIntTexTRight.GetDoubleValue(), nXLineDelta);
        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        // #96837#
        if(mbPTCorrection)
        {
            aRealDepthLine.Load(aRealDepthLeft.GetDoubleValue(), aRealDepthRight.GetDoubleValue(), nXLineDelta);
        }

        while(nXLineDelta--)
        {
            // Werte vorbereiten
            sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

            // Punkt ausgeben
            if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
            {
                // Texturkoordinateninterpolation?
                Color aCol = rCol;

                // #96837#
                if(mbPTCorrection)
                {
                    GetActiveTexture()->ModifyColor(
                        aCol,
                        aIntTexSLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue(),
                        aIntTexTLine.GetDoubleValue() / aRealDepthLine.GetDoubleValue()
                        );
                }
                else
                {
                    GetActiveTexture()->ModifyColor(
                        aCol,
                        aIntTexSLine.GetDoubleValue(),
                        aIntTexTLine.GetDoubleValue()
                        );
                }

                WritePixel(nXLineStart, nYPos, aCol, nDepth);
            }

            if(nXLineDelta)
            {
                // naechste Spalte
                nXLineStart++;

                // naechste Tiefe und Farbe
                aIntDepthLine.Increment();
                aIntTexSLine.Increment();
                aIntTexTLine.Increment();

                // #96837#
                if(mbPTCorrection)
                {
                    aRealDepthLine.Increment();
                }
            }
        }
    }
}

void Base3DDefault::DrawLine(sal_Int32 nYPos, Color& rCol)
{
    // Ausserhalb des Clipping-Bereichs?
    if(IsScissorRegionActive()
        && (nYPos < aDefaultScissorRectangle.Top()
        || nYPos > aDefaultScissorRectangle.Bottom()))
        return;

    // Von links bis rechts zeichnen
    sal_Int32 nXLineStart = aIntXPosLeft.GetLongValue();
    sal_Int32 nXLineDelta = aIntXPosRight.GetLongValue() - nXLineStart;

    if(nXLineDelta > 0)
    {
        // Ausserhalb des Clipping-Bereichs?
        if(IsScissorRegionActive()
            && ( nXLineStart+nXLineDelta < aDefaultScissorRectangle.Left()
            || nXLineStart > aDefaultScissorRectangle.Right()))
            return;

        aIntDepthLine.Load(aIntDepthLeft.GetDoubleValue(), aIntDepthRight.GetDoubleValue(), nXLineDelta);

        while(nXLineDelta--)
        {
            // Werte vorbereiten
            sal_uInt32 nDepth = aIntDepthLine.GetUINT32Value();

            // Punkt ausgeben
            if(IsVisibleAndScissor(nXLineStart, nYPos, nDepth))
                WritePixel(nXLineStart, nYPos, rCol, nDepth);

            if(nXLineDelta)
            {
                // naechste Spalte
                nXLineStart++;

                // naechste Tiefe und Farbe
                aIntDepthLine.Increment();
            }
        }
    }
}

void Base3DDefault::LoadLeftTexture(sal_Int32 nSize)
{
    aIntXPosLeft.Load(aOutPointLeft.X(), aOutPointRight.X(), nSize);
    aIntDepthLeft.Load(pEntLeft->Point().getZ(), pEntRight->Point().getZ(), nSize);

    // #96837#
    if(mbPTCorrection)
    {
        const double fRealDepthLeft = 1.0 / GetTransformationSet()->ViewToEyeCoor(pEntLeft->Point()).getZ();
        const double fRealDepthRight = 1.0 / GetTransformationSet()->ViewToEyeCoor(pEntRight->Point()).getZ();
        aRealDepthLeft.Load(fRealDepthLeft, fRealDepthRight, nSize);

        aIntTexSLeft.Load(
            pEntLeft->TexCoor().getX() * fTexWidth * fRealDepthLeft,
            pEntRight->TexCoor().getX() * fTexWidth * fRealDepthRight,
            nSize);

        aIntTexTLeft.Load(
            pEntLeft->TexCoor().getY() * fTexHeight * fRealDepthLeft,
            pEntRight->TexCoor().getY() * fTexHeight * fRealDepthRight,
            nSize);
    }
    else
    {
        aIntTexSLeft.Load(
            pEntLeft->TexCoor().getX() * fTexWidth,
            pEntRight->TexCoor().getX() * fTexWidth,
            nSize);

        aIntTexTLeft.Load(
            pEntLeft->TexCoor().getY() * fTexHeight,
            pEntRight->TexCoor().getY() * fTexHeight,
            nSize);
    }
}

void Base3DDefault::LoadLeft(sal_Int32 nSize)
{
    aIntXPosLeft.Load(aOutPointLeft.X(), aOutPointRight.X(), nSize);
    aIntDepthLeft.Load(pEntLeft->Point().getZ(), pEntRight->Point().getZ(), nSize);
}

void Base3DDefault::LoadRightTexture(sal_Int32 nSize)
{
    aIntXPosRight.Load(aOutPointRight.X(), aOutPointLeft.X(), nSize);
    aIntDepthRight.Load(pEntRight->Point().getZ(), pEntLeft->Point().getZ(), nSize);

    // #96837#
    if(mbPTCorrection)
    {
        const double fRealDepthLeft = 1.0 / GetTransformationSet()->ViewToEyeCoor(pEntLeft->Point()).getZ();
        const double fRealDepthRight = 1.0 / GetTransformationSet()->ViewToEyeCoor(pEntRight->Point()).getZ();
        aRealDepthRight.Load(fRealDepthRight, fRealDepthLeft, nSize);

        // #96837#
        aIntTexSRight.Load(
            pEntRight->TexCoor().getX() * fTexWidth * fRealDepthRight,
            pEntLeft->TexCoor().getX() * fTexWidth * fRealDepthLeft,
            nSize);

        aIntTexTRight.Load(
            pEntRight->TexCoor().getY() * fTexHeight * fRealDepthRight,
            pEntLeft->TexCoor().getY() * fTexHeight * fRealDepthLeft,
            nSize);
    }
    else
    {
        aIntTexSRight.Load(
            pEntRight->TexCoor().getX() * fTexWidth,
            pEntLeft->TexCoor().getX() * fTexWidth,
            nSize);

        aIntTexTRight.Load(
            pEntRight->TexCoor().getY() * fTexHeight,
            pEntLeft->TexCoor().getY() * fTexHeight,
            nSize);
    }
}

void Base3DDefault::LoadRight(sal_Int32 nSize)
{
    aIntXPosRight.Load(aOutPointRight.X(), aOutPointLeft.X(), nSize);
    aIntDepthRight.Load(pEntRight->Point().getZ(), pEntLeft->Point().getZ(), nSize);
}

void Base3DDefault::NextStepRightTexture()
{
    aIntXPosRight.Increment();
    aIntDepthRight.Increment();

    // #96837#
    if(mbPTCorrection)
    {
        aRealDepthRight.Increment();
    }

    aIntTexSRight.Increment();
    aIntTexTRight.Increment();
}

void Base3DDefault::NextStepRight()
{
    aIntXPosRight.Increment();
    aIntDepthRight.Increment();
}

void Base3DDefault::NextStepLeftTexture()
{
    aIntXPosLeft.Increment();
    aIntDepthLeft.Increment();

    // #96837#
    if(mbPTCorrection)
    {
        aRealDepthLeft.Increment();
    }

    aIntTexSLeft.Increment();
    aIntTexTLeft.Increment();
}

void Base3DDefault::NextStepLeft()
{
    aIntXPosLeft.Increment();
    aIntDepthLeft.Increment();
}

// eof