欢迎阅读指正和转载,但请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com
 
深度探索DxFramework
 
第10章 进入三维的世界 9
 
10.8.6.创建图元
 
经过上面的一系列和dxframework图元类相关知识点的梳理,相信读者都有图元类的实现原理有坚实的准备了。接下来就开始逐个分析图元类的相关源代码了。首先分析一下如何创建图元。创建图元首先应该分析C_Primitive类的构造函数。很明显,一个类的数据成员应该在它的构造函数中初始化的。C_Primitive函数正是这样做,如下:
 
C_Primitive::C_Primitive() 
{
    //设置图元的初始纹理和材质属性。
    SetDefaultMaterialAndTextureProperties();
    //缺省地使用材质漫反射
    SetVertexDiffuseColorEnable(false);
    //用逆时针拣选
    SetCullMode(D3DCULL_CCW);        
    //填充模式为solid
    SetRenderMode(D3DFILL_SOLID);
    //缺省地图元不启动alpha混合和alpha测试
    SetAlphaTestEnable(false);   
    SetAlphaBlendEnable(false); 
    
    //运行运行时纹理数可变
    allowChangeNumTexture = true;
    //一开始纹理阶段数为1
    SetNumberofTextureStage(1);        
    //不使用参数0
    SetUseArgument0Enable(false);

    int i;
    for (i = 0; i < 8; i++)
    {
        p_Texture[i] = NULL;
        //第i阶段参数1为颜色缓冲器中的颜色。
        //参数2为当前阶段所使用的纹理的颜色
        SetColorArgument1(i, D3DTA_CURRENT);
        SetColorArgument2(i, D3DTA_TEXTURE);    
        
        //第i阶段的alpha参数1为当前状态的下的alpha值
        //参数2为当前纹理的alpha值。
        SetAlphaArgument1(i, D3DTA_CURRENT);    
        SetAlphaArgument2(i, D3DTA_TEXTURE);
        //颜色混合操作。alpha混合操作,都为两参数相乘
        SetColorOperation(i, TOP_MODULATE);
        SetAlphaOperation(i, TOP_MODULATE);    }

        SetVertexBuffer(NULL); //先不创建顶点缓冲区和索引缓冲区
        SetIndexBuffer(NULL);  //
        numVertices = 0;
        numIndices = 0;
        createVertexBuffer = false;
        createIndexBuffer = false;
        //先不创建命令列表
        numCommands = 0;
        createCommandList = false;                  
        commands = NULL;
        p_BoundingBox = new C_BoundingBox();
        updateMemory = TRUE;                 
        updateVertices = TRUE;                 
        quality = 1;                         
        p_VertexBufferData = NULL;
        p_IndexBufferData = NULL;
        allPrimitives.push_back(this);
}
 
图元类封装了顶点和顶点索引。那么创建图元的重点就在于如何创建顶点和顶点索引。C_Primitive类提供了相关的接口来创建并操作顶点和顶点索引。
 
//此函数封装了IDirect3DDevice8::CreateVertexBuffer函数。
//根据顶点个数创建顶点缓冲区
void C_Primitive::CreateVertexBuffer(int numVertices) 
{
    //注意要把先前创建的缓冲区释放掉
    SAFE_RELEASE(p_DeviceVB);
    HRESULT hr =
     C_View::p_D3DDevice->CreateVertexBuffer((numVertices) * GetVertexSize(),
        D3DUSAGE_DYNAMIC,GetVertexFormat(),D3DPOOL_DEFAULT,&p_DeviceVB);
    if (FAILED(hr)) 
    {            
        throw Error( _T("C_Primitive::CreateVertexBuffer():Failed to create a vertex buffer "),
                     __FILE__,__LINE__,hr);
    }
    //记下本图元的顶点数。并把创建了顶点缓冲区标志设置为真
    this->numVertices = numVertices;
    createVertexBuffer = true;
}

//此函数封装了IDirect3DDevice8::CreateIndexBuffer函数
void C_Primitive::CreateIndexBuffer(int numIndices) 
{
    SAFE_RELEASE(p_DeviceIB);
    HRESULT hr = C_View::p_D3DDevice->CreateIndexBuffer(numIndices * sizeof(short),     D3DUSAGE_DYNAMIC, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &p_DeviceIB);    
   if (FAILED(hr)) 
   {            
        throw Error(          _T("C_Primitive::CreateIndexBuffer():Failed to create an index buffer "),          __FILE__,__LINE__,hr);     }
    this->numIndices = numIndices;
    createIndexBuffer = true;
}
 
除了创建缓冲区之外,当对缓冲区进行读写访问的时候,要把缓冲区给锁定,使用完之后要把缓冲区的锁定解除。同样的C_Primitive类也提供了类似的操作。C_Primitive类封装了顶点,但是对顶点的操作是通过C_VertexManipulator来管理的。C_Primitive类调用IDirect3DDevice8::LockVertexBuffer函数锁定了顶点缓冲区,获取到指向缓冲区的起始地址指针之后,要把这个指针交给C_VertexManipulator类处理,代码如下:
 
C_Primitive类封装了顶点,但是对顶点的操作是通过C_VertexManipulator来管理的。C_Primitive类调用IDirect3DDevice8::LockVertexBuffer函数锁定了顶点缓冲区,获取到指向缓冲区的起始地址指针之后,要把这个指针交给C_VertexManipulator类处理,代码如下:
 
//锁定顶点缓冲区
void C_Primitive::LockVertexBuffer(C_VertexManipulator* p_VertexManipulator)
{
    BYTE* p_DeviceVBMem;    //指向顶点缓冲区起始地址的指针
    if (p_DeviceVB == NULL)
        throw Error( _T("C_Primitive::LockVertexBuffer(): Trying to lock a NULL vertex buffer"));

    HRESULT hr = p_DeviceVB->Lock( 0, 0, 
    (BYTE**)&p_DeviceVBMem, 0);
    
    if (FAILED(hr))
    {
        throw Error( _T("C_Primitive::LockVertexBuffer():Failed to lock the vertex buffer "), 
        __FILE__,__LINE__,hr);
    }

    p_VertexManipulator->SetPointer( p_DeviceVBMem,numTexture);
}

//解除对顶点缓冲区的锁定
void C_Primitive::UnlockVertexBuffer() 
{
    if (p_DeviceVB == NULL) 
        throw Error(_T("C_Primitive::UnlockVertexBuffer():Trying to unlock a NULL vertex buffer"));
    
    HRESULT hr = p_DeviceVB->Unlock();
    
    if (FAILED(hr)) 
    {
        throw Error(_T("C_Primitive::UnlockVertexBuffer():Failed to unlock the vertex buffer "),
        __FILE__,__LINE__,hr);
    }
}

//锁定索引缓冲区
void C_Primitive::LockIndexBuffer(unsigned short** pp_IndexMemory) 
{
    if (p_DeviceIB == NULL) 
        throw Error( _T("C_Primitive::LockIndexBuffer(): Trying to lock a NULL index buffer"));

    HRESULT hr = p_DeviceIB->Lock(0, 0, 
    (BYTE**)pp_IndexMemory, 0);

    if (FAILED(hr)) 
    {
        throw Error( _T("C_Primitive::LockIndexBuffer(): Failed to lock the index buffer "),
        __FILE__,__LINE__,hr);
    }
}

//解除对索引缓冲区的锁定
void C_Primitive::UnlockIndexBuffer() 
{
    if (p_DeviceIB == NULL) 
        throw Error(_T("C_Primitive::UnlockIndexBuffer(): Trying to unlock a NULL index buffer"));

    HRESULT hr = p_DeviceIB->Unlock();
    
    if (FAILED(hr)) 
    {
        throw Error( _T("C_Primitive::UnlockIndexBuffer():Failed to unlock the index buffer "),
        __FILE__,__LINE__,hr);
    }
}
 
dxframework还提供了一个方法,使得可以把本图元转化成一个使用索引缓冲的图元,方法如下:
 
C_Primitive* C_Primitive::BuildNewPrimitiveToUseDrawIndexedPrimitive() 
{
    //本方法需要渲染命令,没有的话就要抛出异常
    if (!createCommandList) 
    {
        // Need command list !!
        throw Error(_T("C_Primitive::BuildNewPrimitiveToUseDrawIndexedPrimitive:"
        "Need to have command list to do this operation"));
         return NULL;
    }

    //创建一个新的图元
    C_Primitive* p_TempPrimitive;
    p_TempPrimitive = new C_Primitive();

    //把本图元的属性复制到新图元
    p_TempPrimitive->CopyProperties(this);

    //给新图元创建顶点缓冲区,把本图元的顶点复制到新图元的顶点缓冲区
    p_TempPrimitive->numVertices = numVertices;
    p_TempPrimitive->CreateVertexBuffer( p_TempPrimitive->numVertices);
    C_VertexManipulator oldVertexMemory;
    C_VertexManipulator newVertexMemory; 
    LockVertexBuffer(&oldVertexMemory);
    p_TempPrimitive->LockVertexBuffer(&newVertexMemory);

    //把顶点数据复制
    int i, j;
    for (i = 0; i < numVertices; i++) 
    {
        newVertexMemory[i].position =oldVertexMemory[i].position;
        newVertexMemory[i].diffuseColor =oldVertexMemory[i].diffuseColor;
        newVertexMemory[i].normal = oldVertexMemory[i].normal;

        for (j = 0; j < (int)numTexture; j++) 
        {
            newVertexMemory[i].texCoords[j] =oldVertexMemory[i].texCoords[j];
        }
    }
    UnlockVertexBuffer();
    p_TempPrimitive->UnlockVertexBuffer();

    //复制好顶点后,处理索引缓冲
    int indicesCount = 0; 
    for (i = 0; i < numCommands; i++) 
    {
        //点和线不使用索引缓冲
        if ((commands[i].primitiveType == D3DPT_POINTLIST) || 
        (commands[i].primitiveType == D3DPT_LINELIST) || 
        (commands[i].primitiveType == D3DPT_LINESTRIP))
        {
            throw Error(_T("C_Primitive::BuildNewPrimitiveToUseDrawIndexedPrimitive :"
           This function doesn't "support line or point!"));
            return NULL;
        }
        //索引缓冲区的索引总数为所有渲染命令中的基本图元个数乘以3
        //因为只有三角形是使用索引缓冲,所以乘以3
        indicesCount += commands[i].numPrimitive * 3;
    }

    p_TempPrimitive->numIndices = indicesCount;
    p_TempPrimitive->CreateIndexBuffer(p_TempPrimitive->numIndices);
    unsigned short* oldIndex; // Pointer to old index buffer
    unsigned short* newIndex; // Pointer to new index buffer

    //如果原来的图元就有索引缓冲区
    if (p_DeviceIB) 
        LockIndexBuffer(&oldIndex);

    p_TempPrimitive->LockIndexBuffer(&newIndex);
    indicesCount = 0;
    int numPrimitive;
    int minVertexIndex;
    int minIndexIndex;

    for (i = 0; i < numCommands; i++) 
    {
        if (commands[i].commandType == COMMAND_DRAWPRIMITIVE) 
        {
            numPrimitive = commands[i].numPrimitive;
            minVertexIndex = commands[i].minVertexIndex;

            //计算索引,三角形列表,记录列表中每个顶点的编号到索引缓冲区中
            if (commands[i].primitiveType == D3DPT_TRIANGLELIST)
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    newIndex[indicesCount++]=j*3+minVertexIndex;
                    newIndex[indicesCount++]=j*3+1+minVertexIndex;
                    newIndex[indicesCount++]=j*3+2+minVertexIndex;
                }
            } 
            //计算索引,三角形条带,记录条带中每个顶点的编号到索引缓冲区中
            else if (commands[i].primitiveType == D3DPT_TRIANGLESTRIP) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    if (j % 2 == 0) 
                    {
                        newIndex[indicesCount++]= j + minVertexIndex;
                        newIndex[indicesCount++]= j + 1 + minVertexIndex;
                        newIndex[indicesCount++]= j + 2 + minVertexIndex;
                    } 
                    else 
                    {
                        newIndex[indicesCount++]=j + minVertexIndex;
                        newIndex[indicesCount++]= j + 2 + minVertexIndex;
                        newIndex[indicesCount++]=j + 1 + minVertexIndex;
                    }
                }
            } 
            //计算索引,三角形扇面,记录扇面中每个顶点的编号到索引缓冲区中
            else if (commands[i].primitiveType == D3DPT_TRIANGLEFAN) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    newIndex[indicesCount++]=j + 1 + minVertexIndex;
                    newIndex[indicesCount++]=j + 2 + minVertexIndex;
                    newIndex[indicesCount++]= minVertexIndex;
                }
            }
        } 
        //如果本来就有索引缓冲区的话,则复制索引缓冲区的值便可
        else if (commands[i].commandType ==COMMAND_DRAWINDEXEDPRIMITIVE) 
        {
            numPrimitive = commands[i].numPrimitive;
            minIndexIndex = commands[i].minIndexIndex;

            if (commands[i].primitiveType == D3DPT_TRIANGLELIST)
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    newIndex[indicesCount++] = oldIndex[j * 3 + minIndexIndex];
                    newIndex[indicesCount++] = oldIndex[j * 3 + 1 + minIndexIndex];
                    newIndex[indicesCount++] = oldIndex[j * 3 + 2 + minIndexIndex];
                }
            } 
            else if (commands[i].primitiveType == D3DPT_TRIANGLESTRIP) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    if (j % 2 == 0) 
                    {
                        newIndex[indicesCount++] = oldIndex[j + minIndexIndex];
                        newIndex[indicesCount++] = oldIndex[j + 1 + minIndexIndex];
                        newIndex[indicesCount++] = oldIndex[j + 2 + minIndexIndex];
                    } 
                    else 
                    {
                        newIndex[indicesCount++] = oldIndex[j + minIndexIndex];
                        newIndex[indicesCount++] = oldIndex[j + 2 + minIndexIndex];
                        newIndex[indicesCount++] = oldIndex[j + 1 + minIndexIndex];
                    }
                }
            } 
            else if (commands[i].primitiveType == D3DPT_TRIANGLEFAN) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    newIndex[indicesCount++] = oldIndex[j + 1 + minIndexIndex];
                    newIndex[indicesCount++] = oldIndex[j + 2 + minIndexIndex];
                    newIndex[indicesCount++] = oldIndex[minIndexIndex];
                 }
            }
         }
    }

    if (p_DeviceIB) 
        UnlockIndexBuffer();

    p_TempPrimitive->UnlockIndexBuffer();

    //复制完毕之后,创建相关的渲染命令
    // Create the command list
    p_TempPrimitive->CreateCommandList(1);
    p_TempPrimitive->SetDrawIndexedPrimitiveCommand(0,D3DPT_TRIANGLELIST, 0, numVertices, 
        0, indicesCount / 3);

    p_TempPrimitive->DisableChangeNumberofTextureStage();
    p_TempPrimitive->SaveBuffersData();
    p_TempPrimitive->Update();
    return p_TempPrimitive;
}
 
10.8.7.渲染图元
 
在渲染图元之前,首先要设置好各种渲染这个图元所需的渲染状态。渲染需要指定图元的材质。决定用顶点颜色还是用所对应的材质颜色来执行各种反射的光照计算,等等。
 
void C_Primitive::SetupRendering() 
{
    // 设置图元的材质
    C_View::p_D3DDevice->SetMaterial(&material);
    //计算是否使用顶点漫反射颜色的标志,决定使用顶点漫反射色还是材质漫反射色来执行漫反射光照。
    if (useVertexDiffuse) 
    {
         C_View::p_D3DDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1); 
    }
    else 
    {
        C_View::p_D3DDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); 
    }
}
 
上面的章节提到,计算漫反射光照的时候,漫反射光由以下公式描述:
 
Diffuse Lighting = sum[Cd*Ld*(N.Ldir)*Atten*Spot]
 
Cd的值可以是:
顶点颜色1,如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR1,并且顶点声明中给出了第一个顶点的颜色。则使用第一个顶点的漫反射颜色。
顶点颜色2,如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR2,并且顶点声明中给出了第二个顶点的颜色。则使用第二个顶点的漫反射颜色。
材质的漫反射色。 如果使用了任何一种DIFFUSEMATERIALSOURCE,但是没有提供顶点颜色,那么系统会使用材质的漫反射色。那么上面的代码就是确定究竟是用顶点的还是材质的漫反射颜色来执行漫反射光照计算。
 
//接上面的代码 primitive.cpp
//void C_Primitive::SetupRendering()
    for (i = 0; i < numTexture; i++) 
    {
        if (p_Texture[i] == NULL) 
        {
            C_View::p_D3DDevice->SetTexture(i, NULL);
        }
        else 
        {
            C_View::p_D3DDevice->SetTexture(i,p_Texture[i]->GetTexture());
        }
        //设置纹理的采样属性,在Direct3D9版本中,设置采样方式
        //的接口函数变为IDirect3DDevice9::SetSamplerState
        //指定纹理的放大过滤器、缩小过滤器和多级渐进过滤器
        //的采样方式为线性过滤
        C_View::p_D3DDevice->SetTextureStageState(i, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_MINFILTER, D3DTEXF_LINEAR);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
        //设置纹理的U坐标和V坐标方向上的纹理寻址方式为重叠纹理方式
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_ADDRESSU,D3DTADDRESS_WRAP);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);
        //设定了颜色操作的两个参数和操作方式
        C_View::p_D3DDevice->SetTextureStageState(i, D3DTSS_COLOROP,colorOperation[i]);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_COLORARG1,colorArgument1[i]);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_COLORARG2,colorArgument2[i]);
        //设定了alpha操作的两个参数和操作方式
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_ALPHAOP,alphaOperation[i]);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_ALPHAARG1,alphaArgument1[i]);
        C_View::p_D3DDevice->SetTextureStageState(i,D3DTSS_ALPHAARG2,alphaArgument2[i]);
    }

    if (useArgument0) 
    {
        for (i = 0; i < numTexture; i++) 
        {
            C_View::p_D3DDevice->SetTextureStageState(i, D3DTSS_COLORARG0, colorArgument0[i]);
            C_View::p_D3DDevice->SetTextureStageState(i,
            D3DTSS_ALPHAARG0, alphaArgument0[i]);
        }
    }

    if (numTexture != 8) 
    {
        //把最后一个编号所对应的纹理层关掉
        C_View::p_D3DDevice->SetTextureStageState(numTexture,D3DTSS_COLOROP, D3DTOP_DISABLE);
        C_View::p_D3DDevice->SetTextureStageState(numTexture,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
    }

    //如果启动了alpha测试则设置好测试参数和测试比较方式
    if (alphaTestEnable) 
    {
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHAREF,alphaTestReference);
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHAFUNC, alphaTestFunction);
    } 
    else
    {
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    }

    //如果启动了alpha混合则设置好源系数和目标系数,以及混合方式
    if (alphaBlendEnable) 
    {
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
        C_View::p_D3DDevice->SetRenderState(D3DRS_SRCBLEND,alphaSourceBlendFactor);
        C_View::p_D3DDevice->SetRenderState(D3DRS_DESTBLEND,alphaDestinationBlendFactor);
        C_View::p_D3DDevice->SetRenderState(D3DRS_BLENDOP,alphaBlendOperation);
    } 
    else 
    {
        C_View::p_D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    }

    C_View::p_D3DDevice->SetRenderState(D3DRS_CULLMODE, cullMode);
}
 
void C_Primitive::Update() 
{
    if (updateMemory) {
        LoadBuffersData();
    }
    updateMemory = false;
    updateVertices = false;
}
 
当渲染某图元的各种相关的渲染状态都设置好之后,便可以准备渲染了。C_Primitive类的Render3D成员函数便是渲染图元的函数。C_Primitive类使用了命令列表方式来绘制图元。C_Primitive类允许把顶点缓冲区和索引缓冲区分成几个逻辑部分。每一部分可以有不同的绘制方式。可以把顶点分几次绘制。每一次的绘制操作,也就是说每一次对IDirect3DDevice8::DrawPrimitive函数,或者是IDriect3DDevice8::DrawIndexPrimitive函数的调用。都由一个绘制命令来控制,绘制命令和命令的类型如下:
 
enum COMMAND_TYPE 
{
    COMMAND_INVALID,
    COMMAND_DRAWPRIMITIVE,
    COMMAND_DRAWINDEXEDPRIMITIVE
};
 
typedef struct 
{
    BYTE  commandType;     //绘制命令的类型
    DWORD primitiveType;  //要绘制的Direct3D基本图元的类型
    DWORD minVertexIndex; //那部分图元的顶点在缓冲区中的起始编号
    DWORD numVertexUsed; //使用的顶点个数
    DWORD minIndexIndex; //那部分图元的顶点索引在缓冲区中的起始编号
    DWORD numPrimitive; //本命令中要绘制的Direct3D基本图元个数
} CommandType;
 
在CommandType结构中,成员DWORD primitiveType的“图元”不能和C_Primitive类所表示的“图元”所混淆。C_Primitive类所表示的图元起始就是一个顶点的集合。其含义要比DWORD primitiveType所代表的广泛得多。成员DWORD primitiveType就是Direct3D中的基础图元类型,就是下面的这些:
 

typedef enum _D3DPRIMITIVETYPE {
    D3DPT_POINTLIST             = 1,
    D3DPT_LINELIST              = 2,
    D3DPT_LINESTRIP             = 3,
    D3DPT_TRIANGLELIST          = 4,
    D3DPT_TRIANGLESTRIP         = 5,
    D3DPT_TRIANGLEFAN           = 6,
    D3DPT_FORCE_DWORD           = 0x7fffffff, 
} D3DPRIMITIVETYPE;

 
C_Primitive类用一个渲染命令列表成员变量:CommandType* commands。列表中的命令数量由成员变量int numCommands所指定。C_Primitive类提供了创建渲染命令列表和设置命令列表参数的函数,如下:
 
// 根据要求的渲染命令个数创建列表
void C_Primitive::CreateCommandList(int numCommands) 
{
    // 如果本图元已经有命令列表存在了,则重新创建
    if (commands) 
    {
        SAFE_ARRAY_DELETE(commands);
    }
    commands = new CommandType[numCommands];
    int i;
    for (i = 0; i < numCommands; i++) 
    {
        commands[i].commandType = COMMAND_INVALID;
    }
    // 记录下渲染命令个数
    this->numCommands = numCommands;
    createCommandList = true;
}
 
void C_Primitive::SetDrawPrimitiveCommand(int commandNum,DWORD primitiveType,
                        DWORD minVertexIndex, DWORD numPrimitive)
{
    if ((commandNum < 0) || (commandNum >= numCommands)) 
        throw Error(_T("C_Primitive::SetDrawPrimitiveCommand(): Command number is out of range"));

    commands[commandNum].commandType = COMMAND_DRAWPRIMITIVE;
    commands[commandNum].primitiveType = primitiveType;
    commands[commandNum].minVertexIndex = minVertexIndex;
    commands[commandNum].numPrimitive = numPrimitive;
}

void C_Primitive::SetDrawIndexedPrimitiveCommand(int commandNum, DWORD primitiveType, 
        DWORD minVertexIndex, DWORD numVertexUsed, DWORD minIndexIndex, DWORD numPrimitive)
{
    if ((commandNum < 0) || (commandNum >= numCommands))
        throw Error(
        _T("C_Primitive::SetDrawIndexedPrimitiveCommand() : Command number is out of range"));

    commands[commandNum].commandType = COMMAND_DRAWINDEXEDPRIMITIVE;
    commands[commandNum].primitiveType = primitiveType;
    commands[commandNum].minVertexIndex = minVertexIndex;
    commands[commandNum].numVertexUsed = numVertexUsed;
    commands[commandNum].minIndexIndex = minIndexIndex;
    commands[commandNum].numPrimitive = numPrimitive;
}

void C_Primitive::Render3D() 
{
    if (createCommandList)
    {
        if (updateMemory)
            return;

        SetupRendering();

        //指定渲染数据源。
        C_View::p_D3DDevice->SetStreamSource(0, p_DeviceVB,GetVertexSize());

        //设置顶点格式
        C_View::p_D3DDevice->SetVertexShader(GetVertexFormat());
        C_View::p_D3DDevice->SetIndices(p_DeviceIB, 0);
        C_View::p_D3DDevice->SetRenderState(D3DRS_FILLMODE, renderMode);

        // 图元类的渲染方法还要根据当前图元所使用的拣选模式来决定渲染次数。如果当前的
        // 拣选模式为不拣选的话,就要正反两面都要渲染。如下:
        int i;
        if (cullMode == D3DCULL_NONE) 
        {
            //按顺时钟拣选顺序渲染一面
            this->SetCullMode(D3DCULL_CW);
            Update();

            for (i = 0; i < numCommands; i++) 
            {
                if (commands[i].commandType ==COMMAND_DRAWPRIMITIVE) 
                {    //按命令绘制各部分顶点。
                    C_View::p_D3DDevice->DrawPrimitive
                    ((_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex, commands[i].numPrimitive);
                } 
                else if (commands[i].commandType == COMMAND_DRAWINDEXEDPRIMITIVE) 
                {
                    C_View::p_D3DDevice->DrawIndexedPrimitive(
                    (_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex, commands[i].numVertexUsed,
                    commands[i].minIndexIndex,commands[i].numPrimitive);
                }
            }

            //按逆时针拣选顺序绘制另一面。
            this->SetCullMode(D3DCULL_CCW);
            Update();
            SetupRendering();
            for (i = 0; i < numCommands; i++) 
            {
                if (commands[i].commandType == COMMAND_DRAWPRIMITIVE) 
                {
                    C_View::p_D3DDevice->DrawPrimitive(
                    (_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex,commands[i].numPrimitive);
                } 
                else if (commands[i].commandType == COMMAND_DRAWINDEXEDPRIMITIVE) 
                {
                    C_View::p_D3DDevice->DrawIndexedPrimitive(
                    (_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex, commands[i].numVertexUsed,
                    commands[i].minIndexIndex,commands[i].numPrimitive);
                }
            }

            cullMode = D3DCULL_NONE;
        } 
        else 
        {
            for (i = 0; i < numCommands; i++) 
            {
                if (commands[i].commandType == COMMAND_DRAWPRIMITIVE) 
                {
                    HRESULT hr = C_View::p_D3DDevice->DrawPrimitive(
                    (_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex, commands[i].numPrimitive);

                    if (FAILED(hr)) 
                    {
                        throw Error( _T("C_Primitive::Render3D Cannot render primitive"),
_                        _FILE__,__LINE__,hr);
                    }
                }
                else if (commands[i].commandType == COMMAND_DRAWINDEXEDPRIMITIVE) 
                {
                    C_View::p_D3DDevice->DrawIndexedPrimitive(
                    (_D3DPRIMITIVETYPE)commands[i].primitiveType,
                    commands[i].minVertexIndex, commands[i].numVertexUsed,
                    commands[i].minIndexIndex, commands[i].numPrimitive);
                }
            }
        }
    }
}
 
上面的Render3D函数,是使用上一次状态设置时所保持的世界矩阵,观察矩阵和投影矩阵来进行渲染的。对于不同的渲染对象,往往它有着自身的世界变换矩阵。而不是直接使用上一次状态设置时所保持的世界矩阵,因此,C_Primitive类也提供一个可以让用户先设置世界矩阵再渲染的函数,如下:
 
void C_Primitive::Render3D(D3DXMATRIX *p_WorldMatrix) 
{
    if (createCommandList){
        if (updateMemory)
            return;
        //在渲染之前先设置世界变换矩阵
        C_View::p_D3DDevice->SetTransform( D3DTS_WORLD, p_WorldMatrix);
        Render3D();
    }
}

 
11.8.8.图元的几何操作
 
int C_Primitive::CheckPenetrations( D3DXMATRIX* p_WorldMatrix,D3DXVECTOR3 p_Edge[2], 
                             int maxNumPoints, D3DXVECTOR3* p_IntersectionPoints)
{
    if (updateMemory || updateVertices)
    {
        return 0;
    }

    if (createCommandList == false) 
    {
        throw Error(
        _T("C_Primitive::IsPenetrated() : Cannot use this function without command list"));
        return 0;
    }

    // 如果图元有包围盒的话,就把问题转化为检查线段和图元的包围盒相交与否。
    if (p_BoundingBox->valid) 
    {
        C_BoundingBox tempBox;
        tempBox.SetBoundingBox(p_BoundingBox->point, p_BoundingBox->plane);
        //把包围盒变换到世界空间中去。再作检查
        tempBox.TransformBoundingBox(p_WorldMatrix);
        if (!tempBox.IsPenetrated(p_Edge)) 
            return 0;
    }

    // 如果图元还没有包围盒,则检查线段是否和顶点组成的三角形相交
    C_VertexManipulator vertexMemory;
    unsigned short *indexMemory;
    if (numVertices > 0)
    {
        LockVertexBuffer(&vertexMemory);
    }
    
    if (numIndices > 0) 
    {
        LockIndexBuffer(&indexMemory);
    }
    D3DXVECTOR3 triangle[3];
    int i, j;
    int numPrimitive;
    int minVertexIndex;
    int minIndexIndex;
    int intersectNum = 0;
    D3DXPLANE tempPlane;

    //遍历每一个渲染命令中,检查此命令中的三角形是否和线段相交
    //命令有两种,一种是使用索引缓冲区的,另一种是不使用索引缓冲区
    for (i = 0; i < numCommands; i++) 
    {
        if (commands[i].commandType == COMMAND_DRAWPRIMITIVE) 
        {
            numPrimitive = commands[i].numPrimitive;
            minVertexIndex = commands[i].minVertexIndex;
            // 如果该command的基本图元是三角形列表,则缓冲区中每三个
            // 顶点组成一个三角形
            if (commands[i].primitiveType == D3DPT_TRIANGLELIST)
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    triangle[0]=vertexMemory[j*3+minVertexIndex].position;
                    triangle[1]=vertexMemory[j*3+1+minVertexIndex].position;
                    triangle[2]=vertexMemory[j*3+2+minVertexIndex].position;

                    //把三角形变换到世界空间中去
                    D3DXVec3TransformCoord(&triangle[0],&triangle[0],p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1], &triangle[1],p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2], &triangle[2], p_WorldMatrix);

                    //检查线段是否和三角形相交
                    if( checkEdgeIntersectsFace(p_Edge, triangle,3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0],&triangle[1],&triangle[2]),
                        &p_IntersectionPoints[intersectNum])) 
                    {
                        intersectNum++;
                        if (intersectNum == maxNumPoints) 
                        {
                            return intersectNum;
                        }
                    }
                }
            } 
            //如果是三角形条带的话,从每个顶点开始,它和它后
            //续编号的两个顶点组成一个三角形
            else if (commands[i].primitiveType == D3DPT_TRIANGLESTRIP)
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    if (j % 2 == 0) 
                    {
                        triangle[0] = vertexMemory[j+minVertexIndex].position;
                        triangle[1] = vertexMemory[j+1+minVertexIndex].position;
                        triangle[2] = vertexMemory[j+2+minVertexIndex].position;
                    } 
                    else 
                    {
                        triangle[0] = vertexMemory[j+minVertexIndex].position;
                        triangle[1] = vertexMemory[j+2+minVertexIndex].position;
                        triangle[2] = vertexMemory[j+1+minVertexIndex].position;
                    }
                    // Transform points
                    D3DXVec3TransformCoord(&triangle[0], &triangle[0],p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1], &triangle[1],p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2], &triangle[2],p_WorldMatrix);

                    if (checkEdgeIntersectsFace(
                        p_Edge, triangle, 3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0],&triangle[1],&triangle[2]), 
                        &p_IntersectionPoints[intersectNum])) 
                    {
                        intersectNum++;    
                        if (intersectNum == maxNumPoints)
                        {
                            return intersectNum;
                        }
                    }
                }
            } 
            //如果是三角形扇的话,
            else if( commands[i].primitiveType == D3DPT_TRIANGLEFAN) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    triangle[0]=vertexMemory[j+1+minVertexIndex].position;
                    triangle[1]=vertexMemory[j+2+minVertexIndex].position;
                    triangle[2]=vertexMemory[minVertexIndex].position;
                    D3DXVec3TransformCoord(&triangle[0],&triangle[0],p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1],&triangle[1], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2],&triangle[2], p_WorldMatrix);

                    if (checkEdgeIntersectsFace(
                        p_Edge, triangle, 3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0], &triangle[1], &triangle[2]),
                        &p_IntersectionPoints[intersectNum])) 
                    {
                        intersectNum++;    
                        if (intersectNum == maxNumPoints) 
                        {
                            return intersectNum;
                        }
                    }
                }
            }
        } 
        //使用了索引缓冲区,要根据索引来计算三角形
        else if (commands[i].commandType == COMMAND_DRAWINDEXEDPRIMITIVE) 
        {
            numPrimitive = commands[i].numPrimitive;
            minIndexIndex = commands[i].minIndexIndex;
            if (commands[i].primitiveType == D3DPT_TRIANGLELIST)
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    triangle[0] = vertexMemory[indexMemory[j*3+minIndexIndex]].position;
                    triangle[1] = vertexMemory[indexMemory[j*3+1+minIndexIndex]].position;
                    triangle[2] = vertexMemory[indexMemory[j*3+2+minIndexIndex]].position;

                    D3DXVec3TransformCoord(&triangle[0],&triangle[0], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1],&triangle[1], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2],&triangle[2], p_WorldMatrix);

                    if(checkEdgeIntersectsFace(p_Edge, triangle, 3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0], &triangle[1], &triangle[2]),
                        &p_IntersectionPoints[intersectNum])) 
                    {
                        intersectNum++;
                        if (intersectNum == maxNumPoints) 
                        {
                            return intersectNum;
                        }
                    }
                }
            } 
            //三角形条带
            else if (commands[i].primitiveType == D3DPT_TRIANGLESTRIP) 
            {
                for (j = 0; j < numPrimitive; j++) 
                {
                    if (j % 2 == 0) 
                    {
                        triangle[0] = vertexMemory[indexMemory[j + minIndexIndex]].position;
                        triangle[1] = vertexMemory[indexMemory[j+1+minIndexIndex]].position;
                        triangle[2] = vertexMemory[indexMemory[j+2+minIndexIndex]].position;
                    } 
                    else 
                    {
                        triangle[0] = vertexMemory[indexMemory[j+minIndexIndex]].position;
                        triangle[1] = vertexMemory[indexMemory[j+2+minIndexIndex]].position;
                        triangle[2] = vertexMemory[indexMemory[j+1+minIndexIndex]].position;
                    }

                    D3DXVec3TransformCoord(&triangle[0],&triangle[0], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1],&triangle[1], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2],&triangle[2], p_WorldMatrix);

                    if (checkEdgeIntersectsFace(p_Edge, triangle, 3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0], &triangle[1], &triangle[2]),
                        &p_IntersectionPoints[intersectNum])) 
                    {
                        intersectNum++;
                        if (intersectNum == maxNumPoints) 
                        {
                            return intersectNum;
                        }
                    }
                }
            } 
            else if (commands[i].primitiveType == D3DPT_TRIANGLEFAN) 
            {
                for (j = 0; j < numPrimitive; j++)
                {
                    triangle[0] = vertexMemory[indexMemory[j + 1 + minIndexIndex]].position;
                    triangle[1] = vertexMemory[indexMemory[j + 2 + minIndexIndex]].position;
                    triangle[2] = vertexMemory[indexMemory[minIndexIndex]].position;

                    D3DXVec3TransformCoord(&triangle[0],&triangle[0], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[1],&triangle[1], p_WorldMatrix);
                    D3DXVec3TransformCoord(&triangle[2],&triangle[2], p_WorldMatrix);

                    if (checkEdgeIntersectsFace(p_Edge, triangle, 3, 
                        D3DXPlaneFromPoints(&tempPlane, 
                        &triangle[0], &triangle[1], &triangle[2]),
                        &p_IntersectionPoints[intersectNum]))
                    {
                        intersectNum++;
                        if(intersectNum == maxNumPoints) 
                        {
                            return intersectNum;
                        }
                    }
                }
            }
        }
    }

    if (numVertices > 0)
    {
        UnlockVertexBuffer();
    }

    if (numIndices > 0)
    {
        UnlockIndexBuffer();
    }

    return intersectNum;
}

 
用一个平面切割一个图元把它分成两个,具体的实现代码如下:
 
void C_Primitive::SplitPrimitive(D3DXPLANE* p_Plane,C_Primitive** pp_Positive,C_Primitive** pp_Negative) 
{
    //顶点数超过65535个则不处理
    if (numVertices > 65535)
    {
        Error(_T("C_Primitive::SplitPrimitive() Can only be used with primitive that has less 
        than 65536 vertices"));
    }
    C_Primitive* p_Primitive = BuildNewPrimitiveToUseDrawIndexedPrimitive();

    int i, j, k;
    vector<CUSTOMVERTEX8TEXTURE> negativeVertices;
    vector<CUSTOMVERTEX8TEXTURE> positiveVertices;
    vector<unsigned short> negativeIndices;
    vector<unsigned short> positiveIndices;

    EdgeIndex* indexToIndices = new EdgeIndex[numVertices];
    map<DWORD, EdgeIndex> mapEdgeToIndices;    
    
    unsigned short numNegativeVertices = 0; 
    unsigned short numPositiveVertices = 0; 
    unsigned short numNegativeIndices = 0; 
    unsigned short numPositiveIndices = 0; 
 
    C_VertexManipulator vertexMemory;
    unsigned short* indexMemory;

    p_Primitive->LockVertexBuffer(&vertexMemory);
    p_Primitive->LockIndexBuffer(&indexMemory);

    for (i = 0; i < p_Primitive->numVertices; i++) 
    {
        indexToIndices[i].negativeIndex = 
        indexToIndices[i].positiveIndex = 65535;
    }

    DWORD numTriangles = p_Primitive->numIndices / 3;
    DWORD startIndex;

    unsigned short posIndices[6];
    unsigned short negIndices[6];
    DWORD numPosIndices;
    DWORD numNegIndices;
    unsigned short writeIndex;

    DWORD edgeIndex; 
    CUSTOMVERTEX8TEXTURE temp; 

    for (i = 0; i < (int)numTriangles; i++) 
    {
        startIndex = i * 3;
        splitTriangle(p_Plane, 
        &vertexMemory[indexMemory[startIndex]].position, 
        &vertexMemory[indexMemory[startIndex + 1]].position,
        &vertexMemory[indexMemory[startIndex + 2]].position, 
        posIndices, &numPosIndices, 
        negIndices, &numNegIndices);
        
        // Deal with positive side
        for (j = 0; j < (int)numPosIndices; j++) 
        {
            //如果处于正半空间的顶点是被分割的三角形上的三个端点之一
            //的话。(被分割的三角形的三个端点的三个编号是0,1,2)
            if (posIndices[j] < 3) 
            {
                // From original vertex
                if (indexToIndices
                    [
                    indexMemory[startIndex + posIndices[j]]
                    ].positiveIndex == 65535) 
                {
                    //记录下该顶点在正半空间的编号,0,或者1,或者2
                    indexToIndices
                    [
                    indexMemory[startIndex + posIndices[j]]
                    ].positiveIndex = numPositiveVertices;

                    //取出顶点编号,根据编号在顶点缓冲区中取出顶点
                    int index = indexMemory[startIndex + posIndices[j]];
                    temp.position = vertexMemory[index].position;
                    temp.diffuseColor = vertexMemory[index].diffuseColor;
                    temp.normal = vertexMemory[index].normal;

                    for (k = 0; k < numTexture; k++) 
                        temp.texCoords[k] = 
                        vertexMemory[index].texCoords[k];

                    positiveVertices.push_back(temp);
                    //递增在正半空间中的计数
                    numPositiveVertices++;
                }

                //
                writeIndex = indexToIndices
                             [
                             indexMemory[startIndex + posIndices[j]]
                             ].positiveIndex;
            } 
            else //如果是在分割平面上的线上的点
            {
                //根据两个顶点编号找到以这
                //两顶点为端点的线段编号
                edgeIndex = EncodeTwoIndices( 
                indexMemory[startIndex+posIndices[j]-3], 
                indexMemory[startIndex +(posIndices[j]-2)%3]);

                //检查究竟有没有以这两顶点为端点的边
                //如果没有,则创建,并且分割
                if (mapEdgeToIndices.find(edgeIndex) == mapEdgeToIndices.end())
                {
                    // Does not exist, create it
                    mapEdgeToIndices[edgeIndex].positiveIndex = numPositiveVertices;
                    mapEdgeToIndices[edgeIndex].negativeIndex = numNegativeVertices;

                    splitLine(p_Plane, &vertexMemory
                    [
                    indexMemory[startIndex+posIndices[j]-3]
                    ], 
                    &vertexMemory
                    [
                    indexMemory[startIndex+(posIndices[j]-2)%3]
                    ], 
                    &temp, numTexture);
                    
                    positiveVertices.push_back(temp);
                    numPositiveVertices++;
                    negativeVertices.push_back(temp);
                    numNegativeVertices++;
                }

                // Specify the index that should be written
                writeIndex =  mapEdgeToIndices[edgeIndex].positiveIndex;
            }

            positiveIndices.push_back(writeIndex);
            numPositiveIndices++;
        }

        // Deal with negative side
        // 处理负半空间的顶点
        for (j = 0; j < (int)numNegIndices; j++) 
        {
            // Check if the vertex is from the original 
            //vertex or the point of intersection 
            //between an edge of the triangle and the plane
            if (negIndices[j] < 3) 
            {
                // From original vertex
                if (indexToIndices
                    [
                    indexMemory[startIndex+negIndices[j]]
                    ].negativeIndex == 65535) 
                {

                    indexToIndices
                    [
                    indexMemory[startIndex + negIndices[j]]
                    ].negativeIndex = numNegativeVertices;

                    // We cannot simply use pushback the 
                    // vertexMemory[index] because there may
                    // be fewer textures than 8, and hence, 
                    // if we try to copy the 8 texture 
                    // coordinates, we may have access violation 
                    
                    int index = indexMemory
                    [startIndex + negIndices[j]];
                    
                    temp.position = vertexMemory[index].
                                    position;
                    temp.diffuseColor = vertexMemory[index].
                                        diffuseColor;
                    temp.normal = vertexMemory[index].normal;
                    
                    for (k = 0; k < numTexture; k++) 
                        temp.texCoords[k] = 
                        vertexMemory[index].texCoords[k];

                    negativeVertices.push_back(temp);
                    numNegativeVertices++;
                }

                // Specify the index that should be written
                writeIndex = indexToIndices
                        [
                        indexMemory[startIndex +negIndices[j]]
                        ].negativeIndex;
            } 
            else 
            {
                // From a point of intersection between 
                // the edges and the plane
                // Obtain the index of the edge
                edgeIndex = EncodeTwoIndices(
                indexMemory[startIndex+negIndices[j]-3], 
                indexMemory[startIndex+(negIndices[j]-2)%3]);

                // Check if the pair exist or not
                if (mapEdgeToIndices.find(edgeIndex) == 
                    mapEdgeToIndices.end())
                {
                    // Does not exist, create it
                    mapEdgeToIndices[edgeIndex].positiveIndex  = numPositiveVertices;
                    mapEdgeToIndices[edgeIndex].negativeIndex= numNegativeVertices;

                    splitLine(p_Plane, 
                    &vertexMemory
                    [
                    indexMemory[startIndex+negIndices[j]-3]
                    ], 
                    &vertexMemory
                    [
                    indexMemory[startIndex+(negIndices[j]-2)%3]
                    ]
                    , &temp, numTexture);

                    positiveVertices.push_back(temp);
                    numPositiveVertices++;
                    negativeVertices.push_back(temp);
                    numNegativeVertices++;
                }

                // Specify the index that should be written
                writeIndex =  mapEdgeToIndices[edgeIndex].negativeIndex;
            }

            // Record the newly added index to the negative primitive
            negativeIndices.push_back(writeIndex);
            numNegativeIndices++;
        }
            
    }

    // Unlock the buffers
    p_Primitive->UnlockIndexBuffer();
    p_Primitive->UnlockVertexBuffer();

    // Delete the temporary primitive
    SAFE_DELETE(p_Primitive);

    // Now, we have indices and vertices information of both
    // positive and negative primitives
    // We need to build them, if exists
    C_Primitive* p_Positive;
    C_Primitive* p_Negative;
    
    // Do the positive side
    // Check if there is at least a vertex in the positive side
    if (numPositiveVertices > 0) 
    {
        p_Positive = new C_Primitive();
    
        p_Positive->CopyProperties(this);
        p_Positive->numVertices = numPositiveVertices;
        p_Positive->numIndices = numPositiveIndices;

        // Create the buffers
        p_Positive->CreateVertexBuffer(numPositiveVertices);
        p_Positive->CreateIndexBuffer(numPositiveIndices);

        // Write the vertex buffer
        C_VertexManipulator vMem;
        p_Positive->LockVertexBuffer(&vMem);
        for (i = 0; i < numPositiveVertices; i++) 
        {
            vMem[i].position = positiveVertices[i].position;
            vMem[i].normal = positiveVertices[i].normal;
            vMem[i].diffuseColor=positiveVertices[i].diffuseColor;

            for (j = 0; j < numTexture; j++) 
            {
                vMem[i].texCoords[j] =positiveVertices[i].texCoords[j];
            }
        }
        p_Positive->UnlockVertexBuffer();

        // Write the index buffer
        unsigned short *iMem;
        p_Positive->LockIndexBuffer(&iMem);
        for (i = 0; i < numPositiveIndices; i++) 
        {
            iMem[i] = positiveIndices[i];
        }
        p_Positive->UnlockIndexBuffer();

        // Build the command list
        p_Positive->CreateCommandList(1);
        p_Positive->SetDrawIndexedPrimitiveCommand(0, 
        D3DPT_TRIANGLELIST, 0, numPositiveVertices, 
        0, numPositiveIndices / 3);
        
        p_Positive->DisableChangeNumberofTextureStage();
        p_Positive->SaveBuffersData();
        p_Positive->Update();
    } 
    else 
    {
        p_Positive = NULL;
    }

    // Do the negative side
    // Check if there is at least a vertex in the negative side
    if (numNegativeVertices > 0) 
    {
        p_Negative = new C_Primitive();
    
        p_Negative->CopyProperties(this);
        p_Negative->numVertices = numNegativeVertices;
        p_Negative->numIndices = numNegativeIndices;

        // Create the buffers
        p_Negative->CreateVertexBuffer(numNegativeVertices);
        p_Negative->CreateIndexBuffer(numNegativeIndices);

        // Write the vertex buffer
        C_VertexManipulator vMem;
        p_Negative->LockVertexBuffer(&vMem);
        for (i = 0; i < numNegativeVertices; i++) 
        {
            vMem[i].position = negativeVertices[i].position;
            vMem[i].normal = negativeVertices[i].normal;
            vMem[i].diffuseColor= negativeVertices[i].diffuseColor;

            for (j = 0; j < numTexture; j++) 
            {
                vMem[i].texCoords[j] = negativeVertices[i].texCoords[j];
            }
        }
        p_Negative->UnlockVertexBuffer();

        // Write the index buffer
        unsigned short *iMem;
        p_Negative->LockIndexBuffer(&iMem);
        for (i = 0; i < numNegativeIndices; i++) 
        {
            iMem[i] = negativeIndices[i];
        }
        p_Negative->UnlockIndexBuffer();

        // Build the command list
        p_Negative->CreateCommandList(1);
        p_Negative->SetDrawIndexedPrimitiveCommand(0, 
                    D3DPT_TRIANGLELIST, 0, numNegativeVertices,
                    0, numNegativeIndices / 3);

        p_Negative->DisableChangeNumberofTextureStage();
        p_Negative->SaveBuffersData();
        p_Negative->Update();
    } 
    else 
    {
        p_Negative = NULL;
    }

    if (p_Positive)    
        p_Positive->GetBoundingBox()->FindBoundingBox(p_Positive);

    if (p_Negative) 
        p_Negative->GetBoundingBox()->FindBoundingBox(p_Negative);

    // Copy to output
    *pp_Positive = p_Positive;
    *pp_Negative = p_Negative;
    
    SAFE_DELETE(indexToIndices);
}