Reputation: 35
I have a class for reading .ASE files and need to store the variables in a binary file to have faster access the next time the application runs. I store my information in a struct to make the writting process easier. This is the struct I use, defined in the header:
struct myMesh{
std::vector<Vector3> vertices;
std::vector<Vector2> uvs;
std::vector<Vector3> normals;
int verticesSize;
int uvsSize;
int normalsSize;
};
I have created an instance of this struct also in the header to define the variables:
myMesh myInfo;
After storing the data in the struct variables, I write a binary file using C functions:
std::string path = filename + ".bin";
const char * c = path.c_str();
FILE *pBinaryFile;
pBinaryFile = fopen(c, "wb");
if (pBinaryFile==NULL){
std::cout << "error";
}
fwrite(&myInfo.vertices, sizeof(myInfo), 1, pBinaryFile);
fclose(pBinaryFile);
To test if the binary is correctly created, I read the file and create another instance of the struct to visualize the data:
myMesh _myInfo;
FILE *theFile;
theFile = fopen(c, "rb");
if (theFile==NULL){
std::cout << "error";
}
fread(&_myInfo, sizeof(_myInfo), 1, theFile);
fclose(theFile);
And this works fine. The problem appears when I only try to read the file, just not using the the writting process:
/*FILE *pBinaryFile;
pBinaryFile = fopen(c, "wb");
if (pBinaryFile==NULL){
std::cout << "error";
}
fwrite(&myInfo.vertices, sizeof(myInfo), 1, pBinaryFile);
fclose(pBinaryFile);*/
myMesh _myInfo;
FILE *theFile;
theFile = fopen(c, "rb");
if (theFile==NULL){
std::cout << "error";
}
fread(&_myInfo, sizeof(_myInfo), 1, theFile);
fclose(theFile);
And now it does not work. The int variables of the struct are correctly acquired, but the vector variables appear in the form ??? with memory errors. I'm quite new with C++ and it's probably a silly question, but I don't get the point. I have also tried the C++ functions of ofstream and ifstream and I get the same issue.
Thanks in advance.
Upvotes: 1
Views: 728
Reputation: 1691
Here is some sample code to refer to if you have trouble (NOTE: the vertices are interleaved, not separated as yours were):
Vector2D:
#ifndef VECTOR2D_H_
#define VECTOR2D_H_
struct Vector2D
{
union
{
struct { float x,y; };
struct { float s,t; };
struct { float u,v; };
float e[2];
};
Vector2D(): x(0.0f),y(0.0f) {}
Vector2D(const float _x,const float _y): x(_x),y(_y) {}
};
#endif
Vector3D:
#ifndef VECTOR3D_H_
#define VECTOR3D_H_
struct Vector3D
{
union
{
struct { float x,y,z; };
struct { float s,t,r; };
float e[3];
};
Vector3D() :x(0.0f),y(0.0f),z(0.0f) {}
Vector3D(const float _x,const float _y,const float _z): x(_x),y(_y),z(_z) {}
};
#endif
Vertex:
#ifndef VERTEX_H_
#define VERTEX_H_
#include "Vector2D.h"
#include "Vector3D.h"
struct Vertex
{
Vector3D pos;
Vector3D nrm;
Vector2D tex;
Vertex() {}
Vertex(const Vector3D& _pos,const Vector3D& _nrm,const Vector2D& _tex)
:pos(_pos),nrm(_nrm),tex(_tex) {}
};
#endif
Mesh:
#ifndef MESH_H_
#define MESH_H_
#include <vector>
#include "Vertex.h"
#include <sstream>
struct MyMesh
{
std::vector<Vertex> verts;
unsigned numVerts;
void WriteOut(std::ostringstream& oss)
{
numVerts = verts.size();
oss.write((const char*)&numVerts,sizeof(numVerts));
unsigned totalSize = numVerts * sizeof(Vertex);
oss.write((const char*)verts.data(),totalSize);
}
void ReadIn(std::istringstream& iss)
{
iss.read((char*)&numVerts,sizeof(numVerts));
verts.resize(numVerts);
iss.read((char*)verts.data(),numVerts*sizeof(Vertex));
}
};
#endif
And a test main.cpp:
#include "Mesh.h"
#include <sstream>
#include <fstream>
#include <iostream>
void PopulateMesh(MyMesh& mesh)
{
// Fill the mesh with some vertices
for(int i=0; i<3; ++i)
{
Vector3D tempVec(0.0f,i,0.0f);
Vector2D tempTex(0.0f,i);
Vertex temp(tempVec,tempVec,tempTex);
mesh.verts.push_back(temp);
}
}
void PrintMesh(const MyMesh& mesh)
{
for(auto i=0u; i<mesh.verts.size(); ++i)
{
std::cout << "Position: " << mesh.verts[i].pos.x << ' ' << mesh.verts[i].pos.y << ' ' << mesh.verts[i].pos.z << '\n';
std::cout << "Normal: " << mesh.verts[i].nrm.x << ' ' << mesh.verts[i].nrm.y << ' ' << mesh.verts[i].nrm.z << '\n';
std::cout << "Tex Coords: " << mesh.verts[i].tex.u << ' ' << mesh.verts[i].tex.v << "\n\n";
}
}
void WriteStreamToFile(std::ostringstream& oss)
{
std::ofstream fout;
fout.open("test.bin",std::ios_base::binary | std::ios_base::out);
if(fout.is_open())
{
fout.write(oss.str().c_str(),oss.str().size());
fout.close();
}
}
void ReadStreamFromFile(std::istringstream& iss)
{
std::string file;
std::ifstream fin;
fin.open("test.bin",std::ios_base::binary | std::ios_base::in | std::ios_base::_Nocreate);
if(fin.is_open())
{
std::getline(fin,file,'\x1A');
fin.close();
}
iss.str(file);
}
int main()
{
MyMesh outMesh;
unsigned numMeshes = 1;
PopulateMesh(outMesh);
PrintMesh(outMesh);
// Write to the stream
std::ostringstream oss(std::ios_base::binary | std::ios_base::out);
oss.write((const char*)&numMeshes,sizeof(numMeshes));
outMesh.WriteOut(oss);
WriteStreamToFile(oss);
std::istringstream iss(std::ios_base::binary | std::ios_base::in);
ReadStreamFromFile(iss);
// Read from the stream
iss.read((char*)&numMeshes,sizeof(numMeshes));
MyMesh inMesh;
inMesh.ReadIn(iss);
PrintMesh(inMesh);
return 0;
}
Upvotes: 0
Reputation: 1082
The std::vector will allocate space on the heap, and will keep only the pointer on the vector object itself.
It's something like you say:
Type* array = new Type[10]; // array's value is a memory address, NOT the array itself
If you print the vector to a file, you are actually printing the the address. That's the reason why it didn't fail if you perform save and load in the same run -- the address didn't change, so it just pick up where you left. But if you don't populate a vector and save it in the front, your already-saved address will point to nothing, leading to an illegal memory access.
To legitimately save a vector, either save the size of the vector first, then iterate it and save every element, or use Boost Serialization.
Upvotes: 1
Reputation: 87944
The problem is that this doesn't make the writing process easier, it just makes it wrong.
You cannot output complex objects like vectors using a single call to fwrite (or by using the C++ equivalent). You have to do it the hard way.
I can't give any specific advice because I don't know the definitions of Vector2
and Vector3
. But basically you have to output the size of the vector followed by each of it's elements. If those elements are themselves complex objects then you have to handle those specially as well. You do the reverse on input.
Upvotes: 1
Reputation: 3133
You cannot save the structure by asking for its size as the data you are saving is not basic native types. You have to write a custom serialise function that writes / reads each element of the structure.
Save the std::vector by getting hold of the length of the vector and size of a unit element such as Vector3/Vector2 and then save it.
Also, I noticed you are using C functions to create and read a file. This is so un-cool. Use C++ See http://www.cplusplus.com/doc/tutorial/files/
Hope this helps.
Upvotes: 1
Reputation: 106068
vectors don't hold their data in the vector object itself - they hold a pointer to distinct heap-allocated memory. You're saving and restoring the pointers, but doing nothing about the pointed-to data inside the vectors. I recommend you read the boost serialisation library tutorial... it should get you started in a productive direction.
Upvotes: 0