【问题标题】:C++ 3D model io, binary file reading (badbit) and #DEN issueC++ 3D 模型 io、二进制文件读取 (badbit) 和 #DEN 问题
【发布时间】:2013-08-10 17:42:06
【问题描述】:

我目前正在为我的路径跟踪器开发基本框架。我正在尝试创建一个简单的二进制模型加载器(读取和写入),但在读回文件时遇到了问题。提供更多背景信息:

写入我的数据似乎很顺利,即没有抛出异常并且文件大小似乎很好。当我开始读取数据时,一切似乎都很好,直到我的数据被破坏时(在读取了所有顶点位置和一些法线之后)。当我调试时,所有数字都是正确的,但很明显在某些时候我得到 #DEN 而不是正确的浮点值。在某些时候我的所有网格都会发生同样的情况,因此在读取 UV 或三角形等时可能会发生这种情况。

(图像#DEN 在第 54 次正常(第 3 次浮动)...没有足够的声誉)

我一口气阅读了所有法线(1 个子网格)。当我查看 ifstream 时,它会重置回文件的开头。当我开始读取我的三角形索引(一个新的 stream.read())时,badbit 已设置。

我使用 Visual Studio 2010 Ultimate,并且在 Windows 7 Professional x64 上运行。我正在使用 C 和 C++。

我将尝试只显示关键代码:

if(meshHeader.numberOfNormals > 0)
{           
    Normal* _normals = Read<Normal>(meshHeader.numberOfNormals);

    if(_normals)
    {
        // Copy from array to vector
        _normalsVec = new vector<Normal>(_normals, _normals + meshHeader.numberOfNormals);

        // Cleanup              
        delete[] _normals;
    }
    else
    {
        // Cleanup
        delete[] _normals;
        throw runtime_error("Failed reading vertex normals");
    }
}

读取方法:

template <typename T> 
T* BinaryModelIO::Read(unsigned int _size)
{
    T* _data = new T[_size];
    unsigned int _numberOfBytes = Read(sizeof(T) * _size, static_cast<void*>(_data));

    if(_numberOfBytes != (sizeof(T) * _size))
    {
        delete[] _data;
        _data = 0;
    }

    return _data;
}

(static_cast(_data) 不是原因)更多阅读:

unsigned int BinaryModelIO::Read(unsigned int _numberOfBytes, void *_buffer)
{
    if(isWriter)
        return 0;
    // no fail bit set?
    if(!readStream->fail())
    {
        try
        {
            readStream->read((char*)_buffer, _numberOfBytes);
        }
        catch(exception &e)
        {
            cerr << e.what() << endl;
            //return 0;
        }
        return _numberOfBytes;
    }
    else
    {
        if(readStream->badbit)
            cerr << "badbit" << endl;
        else if(readStream->eofbit)
            cerr << "eofbit" << endl;
        return 0;
    }
}

读取法线按预期进行,但经过一定数量的浮点数(法线)后出错。我尝试使用 for 循环读取整个 Normals 数组,然后将所有元素复制到向量中,但这不是问题。读取三角形数据时(以类似方式完成),设置了坏位(或在读取三角形之前的某个位置)。在下图中,我在读取三角形时进行调试,然后打印了 badbit。

(显示打印的图像......没有足够的声誉)

我试图读/写的数据结构不包含虚函数,并且全部由浮点数组成(三角形除外,它有 3 个无符号整数)。点(3 个浮点数)、UV(2 个浮点数)、正常(3 个浮点数)、三角形(3 个无符号整数)。

编辑: 这是法线的定义

class Normal 
{
public:
    // Vector Public Methods
    Normal(float _x = 0.0f, float _y = 0.0f, float _z = 0.0f) : x(_x), y(_y), z(_z) {   }
        explicit Normal(const Vector3& _vector);
        explicit Normal(const Point& _point);

    // Overloaded Operators
    Normal operator+(const Normal& _norm) const 
    {
        return Normal(x + _norm.x, y + _norm.y, z + _norm.z);
    }   
    Normal& operator+=(const Normal& _norm) 
    {
        x += _norm.x; 
        y += _norm.y; 
        z += _norm.z;
        return *this;
    }
    Normal operator-(const Normal& _norm) const 
    {
        return Normal(x - _norm.x, y - _norm.y, z - _norm.z);
    }   
    Normal& operator-=(const Normal& _norm) 
    {
        x -= _norm.x;
        y -= _norm.y; 
        z -= _norm.z;
        return *this;
    }
    bool operator==(const Normal& _norm) const 
    {
        return x == _norm.x && y == _norm.y && z == _norm.z;
    }
    bool operator!=(const Normal& _norm) const 
    {
        return x != _norm.x || y != _norm.y || z == _norm.z;
    }
    Normal operator*(float _f) const 
    {
        return Normal(_f * x, _f * y, _f * z);
    }   
    Normal &operator*=(float _f) 
    {
        x *= _f; 
        y *= _f; 
        z *= _f;
        return *this;
    }
    Normal operator/(float _f) const 
    {
        // One division, 3 multiplications ^^
        float _inverse = 1.f / _f;
        return Normal(x * _inverse, y * _inverse, z * _inverse);
    }
    Normal &operator/=(float _f) 
    {
        float _inverse = 1.f / _f;
        x *= _inverse; 
        y *= _inverse; 
        z *= _inverse;
        return *this;
    }
    Normal operator-() const 
    {
        return Normal(-x, -y, -z);
    }
    float operator[](int _i) const 
    {
        // Return x, y or z
        return (&x)[_i];
    }   
    float &operator[](int _i) 
    {
        return (&x)[_i];
    }

    float LengthSquared() const 
    { 
        return x * x + y * y + z * z; 
    }
    float Length() const 
    { 
        return sqrtf(LengthSquared()); 
    }

    // Public Variables
    float x, y, z;
}; // 12 Byte

编辑2: 文件打开(在 Loader 对象创建和销毁时完成)

bool AbstractStream::OpenFile()
{
    bool _return = false;

    // We should open file to write or to read
    if(isWriter)
    {
        // Check if the file is already open
        if(!writeStream->is_open())
            writeStream->open(fileName, ios::in | ios::binary);

        if(!writeStream->fail())
            _return = true; // File could be opened
    }
    else
    {
        // Check if the file is already open
        if(!readStream->is_open())
            readStream->open(fileName, ios::in | ios::binary);

        if(!readStream->fail())
            _return = true; // File could be opened
    }

    return _return; // File could be opened
}

如果我忘记了什么,请告诉我,因为这是我关于 stackoverflow 的第一个问题。

我真的希望有人可以帮助我,因为我暂时解决了这个问题(在渲染器的其他部分工作)。

提前致谢!

【问题讨论】:

  • 可能值得包括 Normal 的定义。您越接近 sscce 越好。
  • 你是如何打开文件的?您是在告诉系统该文件是二进制文件吗? (ios::binary)?
  • 我用 ios::binary 打开它。
  • 就我个人而言,我会放弃两个重载的“Read()”方法,而只替换一个好的老式“fopen()/fread()/fclose()”恕我直言...... PS:一个好的SSCCE 将读取您的文件,并将其打印为文本(到标准输出很好)。
  • 我明白你的意思,但是调用 stream->read() 的 Write 和 Read 将位于 AbstractStream 类中,供以后用于序列化对象。

标签: c++ binary ifstream


【解决方案1】:

除了您对新/删除(避免指针)的古老我们之外,我猜您的问题是写入数据。我的建议:尝试编写一个非(!)二进制文件,在编辑器中查看它。

【讨论】:

  • 谢谢Dieter,但这是正在进行的代码,创建二进制读取器/写入器是我的目标。另外,你能帮我解释一下为什么要避免指针吗?我创建法线然后删除它们的部分并不代表最终代码。对于调试,编写文本会很有用,但它需要我重写大部分代码。我已经创建了一个基于文本的波前 (.obj) 加载器,目标是拥有可以快速加载的二进制资产文件,即加速结构、纹理等。
猜你喜欢
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多