代码之家  ›  专栏  ›  技术社区  ›  thecoshman

在运行前不知道DirectX顶点是什么类型的情况下尝试创建一个数组

  •  0
  • thecoshman  · 技术社区  · 14 年前

    对那些不了解DirectX的人来说有点背景。顶点不只是一个XYZ位置,它 可以 还有其他数据。DirectX使用一个称为Flexible Vertex Format(FVF)的系统来定义您希望顶点采用的格式。您可以通过将一个数字传递给DirectX来定义这些数字,DirectX使用按位或建立数字,例如 (D3DFVF_XYZ | D3DFVF_DIFFUSE)

    为了将顶点传递到图形卡,基本上可以将内存锁定在缓冲区所在的图形卡中,然后使用memcpy将数组传输过来。

    你的数组是一个结构数组,你可以分解你自己,所以在这种情况下,你可以做一个类似于。。。

    struct CUSTOMVERTEX {
        FLOAT X, Y, Z;
        DWORD COLOR;
    };
    

    然后创建一个CUSTOMVERTEX类型的数组并填充数据字段。

    struct pos{ flaot x,y,z;}; 一系列 struct colour{ DWROD colour;}; 等。

    但是我需要将它们合并在一起,这样我就有了一个类似CUSTOMVERTEX的数组结构。

    现在,我想我已经做了一个函数,它将合并到数组中,但我不确定它是否会按预期工作,这里是(目前缺少实际返回这个“交错”数组的能力)

    void Utils::MergeArrays(char *ArrayA, char *ArrayB, int elementSizeA, int elementSizeB, int numElements)
    {
        char *packedElements = (char*)malloc(numElements* (elementSizeA, elementSizeB));
        char *nextElement = packedElements;
        for(int i = 0; i < numElements; ++i)
        {
            memcpy(nextElement, (void*)ArrayA[i], elementSizeA);
            nextElement += elementSizeA;
            memcpy(nextElement, (void*)ArrayB[i], elementSizeB);
            nextElement += elementSizeB;
        }
    }
    

    调用此函数时,将传入要合并的两个数组,以及每个数组中元素的大小和数组中元素的数量。

    我在聊天中问了一会儿,所以很沮丧。有几件事要说。

    我处理的是相当小的数据集,比如100个top,而这(理论上)更多的是一个初始化任务,所以应该只完成一次,所以一点时间对我来说没问题。

    我的最后一个数组,我想能够使用memcpy转换成图形卡需要没有填充,它必须是连续的数据。

    编辑 /编辑

    最后,他们是一个很好的机会,我用这个来找错树,所以他们很可能是一个简单得多的方法,一开始就不存在这个问题,但是我的一部分已经挖掘了我的治疗并且想让这个系统工作,所以我希望知道如何让这样的系统工作(交错数组的事情)

    非常感谢你。。。我现在需要放松一下,所以我期待着听到任何关于这个问题的想法。

    4 回复  |  直到 14 年前
        1
  •  0
  •   Bahbar    14 年前

    更改代码时,应该这样做:

    void* Utils::MergeArrays(char *ArrayA, char *ArrayB, int elementSizeA, int elementSizeB, int numElements)
    {
        char *packedElements = (char*)malloc(numElements* (elementSizeA + elementSizeB));
        char *nextElement = packedElements;
        for(int i = 0; i < numElements; ++i)
        {
            memcpy(nextElement, ArrayA + i*elementSizeA, elementSizeA);
            nextElement += elementSizeA;
            memcpy(nextElement, ArrayB + i*elementSizeB, elementSizeB);
            nextElement += elementSizeB;
        }
        return packedElements;
    }
    

    请注意,可能需要一些同时合并所有属性的代码,而不是一次合并2个属性(考虑位置+法线+纹理坐标+颜色+…)。还要注意的是,您可以在填写顶点缓冲区时进行合并,这样就不需要分配 packedElements

    类似于:

    //pass the Locked buffer in as destArray
    void Utils::MergeArrays(char* destArray, char **Arrays, int* elementSizes, int numArrays, int numElements)
    {
        char* nextElement = destArray;
        for(int i = 0; i < numElements; ++i)
        {
            for (int array=0; array<numArrays; ++array)
            {
                int elementSize = elementSizes[array];
                memcpy(nextElement, Arrays[array] + i*elementSize, elementSize);
                nextElement += elementSize;
            }
        }
    }
    
        2
  •  1
  •   Puppy    14 年前

    不,不,不。FVF系统已经被弃用多年,甚至在D3D10或更高版本中都不可用。D3D9使用顶点系统。示例代码:

    D3DVERTEXELEMENT9 VertexColElements[] =
    {
        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
        {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
        D3DDECL_END(),
    };
    

    除此之外,如果你想创建一个运行时变量顶点数据格式,那么你需要为你想要的每一个可能的变量编写一个明暗器,然后编译它们,并用你的生命来交换它们。而且,对最终产品的影响将是疯狂的-例如,如果你决定拿出你需要的灯光数据来制作阴影,你怎么可能编写一个有竞争力的渲染引擎?

    不过,我想我还是帮个忙吧。你真正需要的是一个多态的函数对象和一些普通的内存-D3D使用void*s或者其他类似的东西,所以这不是什么大问题。当您调用函数对象时,它会添加到FVF声明并将数据复制到内存中。

    class FunctionBase {
    public:
        virtual ~FunctionBase() {}
        virtual void Call(std::vector<std::vector<char>>& vertices, std::vector<D3DVERTEXELEMENT9>& vertexdecl, int& offset) = 0;
    };
    // Example implementation
    class Position : public FunctionBase {
        virtual void Call(std::vector<std::vector<char>>& vertices, std::vector<D3DVERTEXELEMENT9>& vertexdecl, int& offset) {
            std::for_each(vertices.begin(), vertices.end(), [&](std::vector<char>& data) {
                float x[3] = {0};
                char* ptr = (char*)x;
                for(int i = 0; i < sizeof(x); i++) {
                    data.push_back(ptr[i]);
                }
            }
            vertexdecl.push_back({0, offset, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0});
            offset += sizeof(x);
        }
    };
    
    std::vector<std::vector<char>> vertices;
    std::vector<D3DVERTEXELEMENT9> vertexdecl;
    vertices.resize(vertex_count);
    std::vector<std::shared_ptr<FunctionBase>> functions;
    // add to functions here
    int offset = 0;
    std::for_each(functions.begin(), functions.end(), [&](std::shared_ptr<FunctionBase>& ref) {
        ref->Call(vertices, vertexdecl, offset);
    });
    vertexdecl.push_back(D3DDECL_END());
    

    请原谅我使用lambda,我使用C++ 0x编译器。

        3
  •  1
  •   Benjamin Lindley    14 年前

    你的解决方案看起来不错。但是如果你想要更多的C++,你可以试试这样的:

    编辑 我以前的解决方案基本上重新创建了一些已经存在的东西,std::pair。我不知道我在想什么,这里是更为C++的解决方案:

    template<typename InIt_A, typename InIt_B, typename OutIt>
    void MergeArrays(InIt_A ia, InIt_B ib, OutIt out, std::size_t size)
    {
        for(std::size_t i=0; i<size; i++)
        {
            *out = make_pair(*ia,*ib);
            ++out;
            ++ia;
            ++ib;
        }
    }
    
    int main()
    {
        pos p[100];
        color c[100];
        typedef pair<pos,color> CustomVertex;
        CustomVertex cv[100];
    
        MergeArrays(p,c,cv,100);
    }
    

    您不必担心填充,因为D3D顶点中的所有元素要么是32位浮点,要么是32位整数。

    编辑

    这是一个可行的解决方案。它可以同时满足您的所有需求,而且您不必担心尺寸的问题:

    // declare a different struct for each possible vertex element
    struct Position { FLOAT x,y,z; };
    struct Normal { FLOAT x,y,z; };
    struct Diffuse { BYTE a,r,g,b; };
    struct TextureCoordinates { FLOAT u,v; };
    // etc...
    
    // I'm not all too sure about all the different elements you can have in a vertex
    // But you would want a parameter for each one in this function.  Any element that
    // you didn't use, you would just pass in a null pointer.  Since it's properly
    // typed, you won't be able to pass in an array of the wrong type without casting.
    std::vector<char> MergeArrays(Position * ppos, Normal * pnorm, Diffuse * pdif, TextureCoordinates * ptex, int size)
    {
        int element_size = 0;
        if(ppos) element_size += sizeof(Position);
        if(pnorm) element_size += sizeof(Normal);
        if(pdif) element_size += sizeof(Diffuse);
        if(ptex) element_size += sizeof(TextureCoordinates);
    
        vector<char> packed(element_size * size);
    
        vector<char>::iterator it = packed.begin();
        while(it != packed.end())
        {
            if(ppos)
            {
                it = std::copy_n(reinterpret_cast<char*>(ppos), sizeof(Position), it);
                ppos++;
            }
    
            if(pnorm)
            {
                it = std::copy_n(reinterpret_cast<char*>(pnorm), sizeof(Normal), it);
                pnorm++;
            }
    
            if(pdif)
            {
                it = std::copy_n(reinterpret_cast<char*>(pdif), sizeof(Diffuse), it);
                pdif++;
            }
    
            if(ptex)
            {
                it = std::copy_n(reinterpret_cast<char*>(ptex), sizeof(TextureCoordinates), it);
                ptex++;
            }
    
        }
    
        return packed;
    }
    
    // Testing it out.  We'll create an array of 10 each of some of the elements.
    // We'll use Position, Normal, and Texture Coordinates.  We'll pass in a NULL
    // for Diffuse.
    int main() 
    {
        Position p[10];
        Normal n[10];
        TextureCoordinates tc[10];
    
        // Fill in the arrays with dummy data that we can easily read.  In this
        // case, what we'll do is cast each array to a char*, and fill in each
        // successive element with an incrementing value.
        for(int i=0; i<10*sizeof(Position); i++)
        {
            reinterpret_cast<char*>(p)[i] = i;
        }
    
        for(int i=0; i<10*sizeof(Normal); i++)
        {
            reinterpret_cast<char*>(n)[i] = i;
        }
    
        for(int i=0; i<10*sizeof(TextureCoordinates); i++)
        {
            reinterpret_cast<char*>(tc)[i] = i;
        }
    
        vector<char> v = MergeArrays(p,n,NULL,tc,10);
    
        // Output the vector.  It should be interlaced:
        // Position-Normal-TexCoordinates-Position-Normal-TexCoordinates-etc...
        for_each(v.begin(), v.end(),
            [](const char & c) { cout << (int)c << endl; });
        cout << endl;
    }
    
        4
  •  0
  •   Ben Jackson    14 年前

    我不知道DirectX,但在OpenGL中存在完全相同的概念,在OpenGL中可以指定每个顶点属性的位置和跨距。您可以使用交替的属性(如第一个结构)或扫描将它们存储在不同的块中。在OpenGL中,使用glVertexPointer来设置这些东西。考虑到DirectX最终运行在同一个底层硬件上,我怀疑在DirectX中有某种方法可以做同样的事情,但我不知道它是什么。

    一些以DirectX和glVertexPointer为关键字的google会显示SetFVF和SetVertexDeclaration

    MSDN on SetFVF , gamedev discussion comparing them