/** A simple routine to read an AmiraMesh file that defines a scalar/vector field on a uniform grid. */ float* LoadField(const char* FileName, int* xDim, int* yDim, int* zDim, float* xmin, float* ymin, float* zmin, float* xmax, float* ymax, float* zmax) { float* pData = NULL; FILE* fp = fopen(FileName, "rb"); if (!fp) { printf("Could not find %s\n", FileName); return NULL; } printf("Reading %s\n", FileName); //We read the first 2k bytes into memory to parse the header. //The fixed buffer size looks a bit like a hack, and it is one, but it gets the job done. char buffer[2048]; fread(buffer, sizeof(char), 2047, fp); buffer[2047] = '\0'; //The following string routines prefer null-terminated strings if (!strstr(buffer, "# AmiraMesh BINARY-LITTLE-ENDIAN 2.1")) { printf("Not a proper AmiraMesh file.\n"); fclose(fp); return NULL; } //Find the Lattice definition, i.e., the dimensions of the uniform grid sscanf(FindAndJump(buffer, "define Lattice"), "%d %d %d", xDim, yDim, zDim); printf("\tGrid Dimensions: %d %d %d\n", *xDim, *yDim, *zDim); //Find the BoundingBox sscanf(FindAndJump(buffer, "BoundingBox"), "%g %g %g %g %g %g", xmin, xmax, ymin, ymax, zmin, zmax); printf("\tBoundingBox in x-Direction: [%g ... %g]\n", *xmin, *xmax); printf("\tBoundingBox in y-Direction: [%g ... %g]\n", *ymin, *ymax); printf("\tBoundingBox in z-Direction: [%g ... %g]\n", *zmin, *zmax); //Is it a uniform grid? We need this only for the sanity check below. const bool bIsUniform = (strstr(buffer, "CoordType \"uniform\"") != NULL); printf("\tGridType: %s\n", bIsUniform ? "uniform" : "UNKNOWN"); //Type of the field: scalar, vector int NumComponents(0); if (strstr(buffer, "Lattice { float Data }")) { //Scalar field NumComponents = 1; } else { //A field with more than one component, i.e., a vector field sscanf(FindAndJump(buffer, "Lattice { float["), "%d", &NumComponents); } printf("\tNumber of Components: %d\n", NumComponents); //Sanity check if (*xDim <= 0 || *yDim <= 0 || *zDim <= 0 || *xmin > *xmax || *ymin > *ymax || *zmin > *zmax || !bIsUniform || NumComponents <= 0) { printf("Something went wrong\n"); fclose(fp); return NULL; } //Find the beginning of the data section const long idxStartData = strstr(buffer, "# Data section follows") - buffer; if (idxStartData > 0) { //Set the file pointer to the beginning of "# Data section follows" fseek(fp, idxStartData, SEEK_SET); //Consume this line, which is "# Data section follows" fgets(buffer, 2047, fp); //Consume the next line, which is "@1" fgets(buffer, 2047, fp); //Read the data // - how much to read const size_t NumToRead = *xDim * *yDim * *zDim * NumComponents; // - prepare memory; use malloc() if you're using pure C pData = new float[NumToRead]; if (pData) { // - do it const size_t ActRead = fread((void*)pData, sizeof(float), NumToRead, fp); // - ok? if (NumToRead != ActRead) { printf("Something went wrong while reading the binary data section.\nPremature end of file?\n"); delete[] pData; fclose(fp); return NULL; } /* //Test: Print all data values //Note: Data runs x-fastest, i.e., the loop over the x-axis is the innermost printf("\nPrinting all values in the same order in which they are in memory:\n"); int Idx(0); for(int k=0;k<zDim;k++) { for(int j=0;j<yDim;j++) { for(int i=0;i<xDim;i++) { //Note: Random access to the value (of the first component) of the grid point (i,j,k): // pData[((k * yDim + j) * xDim + i) * NumComponents] assert(pData[((k * yDim + j) * xDim + i) * NumComponents] == pData[Idx * NumComponents]); for(int c=0;c<NumComponents;c++) { printf("%g ", pData[Idx * NumComponents + c]); } printf("\n"); Idx++; } } } delete[] pData; */ } } fclose(fp); return pData; }
VolumeList* AmiraMeshReader::readMetaFile(const std::string &fileName, size_t firstSlice, size_t lastSlice, int timeframe) throw (tgt::FileException, std::bad_alloc) { bool error = false; const char* FileName = fileName.c_str(); FILE* fp = fopen(FileName, "rb"); if (!fp) { LERROR("Could not find :" << FileName); error = true; goto K; } char buffer[2048]; fread(buffer, sizeof(char), 2047, fp); buffer[2047] = '\0'; //The following string routines prefer null-terminated strings if (!strstr(buffer, "# AmiraMesh BINARY-LITTLE-ENDIAN 2.1") && !strstr(buffer, "# AmiraMesh 3D BINARY 2.0")) { LERROR("Not a proper AmiraMesh file."); fclose(fp); error = true; goto K; } //Find the Lattice definition, i.e., the dimensions of the uniform grid int xDim(0), yDim(0), zDim(0); sscanf(FindAndJump(buffer, "define Lattice"), "%d %d %d", &xDim, &yDim, &zDim); LDEBUG("Grid Dimensions: " << xDim << " " << yDim << " " << zDim); //Find the BoundingBox float xmin(1.0f), ymin(1.0f), zmin(1.0f); float xmax(-1.0f), ymax(-1.0f), zmax(-1.0f); sscanf(FindAndJump(buffer, "BoundingBox"), "%g %g %g %g %g %g", &xmin, &xmax, &ymin, &ymax, &zmin, &zmax); LDEBUG("BoundingBox in x-Direction: [" << xmin << " " << xmax << "]"); LDEBUG("BoundingBox in x-Direction: [" << ymin << " " << ymax << "]"); LDEBUG("BoundingBox in x-Direction: [" << zmin << " " << zmax << "]"); //Is it a uniform grid? We need this only for the sanity check below. const bool bIsUniform = (strstr(buffer, "CoordType \"uniform\"") != NULL); LDEBUG("GridType: " << bIsUniform ? "uniform" : "UNKNOWN"); //Type of the field: scalar, vector int NumComponents(0); if (strstr(buffer, "Lattice { float Data }")) { //Scalar field NumComponents = 1; } else { //A field with more than one component, i.e., a vector field sscanf(FindAndJump(buffer, "Lattice { float["), "%d", &NumComponents); } LDEBUG("Number of Components: " << NumComponents); //Sanity check if (xDim <= 0 || yDim <= 0 || zDim <= 0 || xmin > xmax || ymin > ymax || zmin > zmax || !bIsUniform || NumComponents <= 0) { printf("Something went wrong\n"); fclose(fp); error = true; goto K; } K : RawVolumeReader::ReadHints h; std::string objectFilename = fileName; h.headerskip_ = strstr(buffer, "# Data section follows") - buffer; //Set the file pointer to the beginning of "# Data section follows" fseek(fp, h.headerskip_, SEEK_SET); //Consume this line, which is "# Data section follows" char buf1[2048]; fgets(buf1, 2047, fp); int l1 = strlen(buf1); //Consume the next line, which is "@1" char buf2[2048]; fgets(buf2, 2047, fp); int l2 = strlen(buf2); vec3 sliceThickness = vec3(1.f, 1.f, 1.f); int numFrames = NumComponents; h.dimensions_.x = xDim; h.dimensions_.y = yDim; h.dimensions_.z = zDim; h.format_ = "FLOAT"; h.objectModel_ = "I"; h.bigEndianByteOrder_ = false; h.headerskip_ += (l1 + l2); LDEBUG("Header size : " << h.headerskip_); if (hor(lessThanEqual(h.dimensions_, ivec3(0)))) { LERROR("Invalid resolution or resolution not specified: " << h.dimensions_); error = true; } h.spacing_ = sliceThickness; h.timeStep_ = 0; if (!error) { RawVolumeReader rawReader(getProgressBar()); // do we have a relative path? if ((objectFilename.substr(0, 1) != "/") && (objectFilename.substr(0, 1) != "\\") && (objectFilename.substr(1, 2) != ":/") && (objectFilename.substr(1, 2) != ":\\")) { size_t p = fileName.find_last_of("\\/"); // construct path relative to dat file objectFilename = fileName.substr(0, p + 1) + objectFilename; } int start = 0; int end = numFrames; if (timeframe != -1) { if (timeframe >= numFrames) throw tgt::FileException("Specified time frame not in volume", fileName); start = timeframe; end = timeframe+1; } VolumeList* toReturn = new VolumeList(); for (int frame = start; frame < end; ++frame) { h.timeframe_ = frame; rawReader.setReadHints(h); VolumeList* volumeList = rawReader.readSlices(objectFilename, firstSlice, lastSlice); if (!volumeList->empty()) { VolumeURL origin(fileName); origin.addSearchParameter("timeframe", itos(frame)); Volume* vh = static_cast<Volume*>(volumeList->first()); vh->setOrigin(origin); vh->setTimestep(static_cast<float>(frame)); oldVolumePosition(vh); if(!h.hash_.empty()) vh->setHash(h.hash_); toReturn->add(volumeList->first()); } delete volumeList; } return toReturn; } else { throw tgt::CorruptedFileException("error while reading data", fileName); } }