bool Any::operator==(const Any& x) const { beforeRead(); x.beforeRead(); if (m_type != x.m_type) { return false; } switch (m_type) { case NONE: return true; case BOOLEAN: return (m_simpleValue.b == x.m_simpleValue.b); case NUMBER: return (m_simpleValue.n == x.m_simpleValue.n); case STRING: debugAssert(m_data != NULL); return (*(m_data->value.s) == *(x.m_data->value.s)); case TABLE: { if (size() != x.size()) { return false; } debugAssert(m_data != NULL); if (m_data->name != x.m_data->name) { return false; } Table<std::string, Any>& cmptable = *( m_data->value.t); Table<std::string, Any>& xcmptable = *(x.m_data->value.t); for (Table<std::string,Any>::Iterator it1 = cmptable.begin(), it2 = xcmptable.begin(); it1 != cmptable.end() && it2 != xcmptable.end(); ++it1, ++it2) { if (*it1 != *it2) { return false; } } return true; } case ARRAY: { if (size() != x.size()) { return false; } debugAssert(m_data != NULL); if (m_data->name != x.m_data->name) { return false; } Array<Any>& cmparray = *( m_data->value.a); Array<Any>& xcmparray = *(x.m_data->value.a); for (int ii = 0; ii < size(); ++ii) { if (cmparray[ii] != xcmparray[ii]) { return false; } } return true; } default: alwaysAssertM(false, "Unknown type."); return false; } // switch (m_type) }
void Any::resize(int n) { beforeRead(); alwaysAssertM(n >= 0, "Cannot resize less than 0."); verifyType(ARRAY); m_data->value.a->resize(n); }
uint32 Crypto::crc32(const void* byte, size_t numBytes) { alwaysAssertM(numBytes < 0xFFFFFFFF, "Not implemented for arrays larger than 2^32 bytes"); return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), (int)numBytes); }
std::string System::findDataFile (const std::string& full, bool errorIfNotFound) { // Places where specific files were most recently found. This is // used to cache seeking of common files. static Table<std::string, std::string> lastFound; // First check if the file exists as requested. This will go // through the FileSystemCache, so most calls do not touch disk. if (fileExists(full)) { return full; } // Now check where we previously found this file. std::string* last = lastFound.getPointer(full); if (last != NULL) { if (fileExists(*last)) { // Even if cwd has changed the file is still present. // We won't notice if it has been deleted, however. return *last; } else { // Remove this from the cache it is invalid lastFound.remove(full); } } // Places to look static Array<std::string> directoryArray; if (directoryArray.size() == 0) { // Initialize the directory array RealTime t0 = System::time(); Array<std::string> baseDirArray; std::string initialAppDataDir(instance().m_appDataDir); baseDirArray.append(""); if (! initialAppDataDir.empty()) { baseDirArray.append(initialAppDataDir); } const char* g3dPath = getenv("G3DDATA"); if (g3dPath && (initialAppDataDir != g3dPath)) { baseDirArray.append(g3dPath); } static const std::string subdirs[] = {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""}; for (int j = 0; j < baseDirArray.size(); ++j) { std::string d = baseDirArray[j]; if (fileExists(d)) { directoryArray.append(d); for (int i = 0; ! subdirs[i].empty(); ++i) { const std::string& p = pathConcat(d, subdirs[i]); if (fileExists(p)) { directoryArray.append(p); } } } } logLazyPrintf("Initializing System::findDataFile took %fs\n", System::time() - t0); } for (int i = 0; i < directoryArray.size(); ++i) { const std::string& p = pathConcat(directoryArray[i], full); if (fileExists(p)) { lastFound.set(full, p); return p; } } if (errorIfNotFound) { // Generate an error message std::string locations; for (int i = 0; i < directoryArray.size(); ++i) { locations += pathConcat(directoryArray[i], full) + "\n"; } alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations); } // Not found return ""; }
//=========================================== //TextContent //=========================================== void ImageContent::init(const TiXmlElement* root) { string name = root->ValueStr(); if (name == "Image") { string left_name = root->Attribute("filename"); left_name = Story::resolve_rel_path(left_name); try { left = the_app.tex_mgr.loadTexture(left_name); if (left.isNull()) { msgBox("Image file \"" + left_name + "\" was not found"); return; } } catch (GImage::Error& err) { left = NULL; msgBox("Image file \"" + left_name + "\" is invalid and could not be loaded"); return; } is_stereo = false; width = left->texelWidth(); height = left->texelHeight(); } else if (name == "StereoImage") { string left_name = root->Attribute("left-image"); left_name = Story::resolve_rel_path(left_name); try { left = the_app.tex_mgr.loadTexture(left_name); if (left.isNull()) { msgBox("Image file \"" + left_name + "\" was not found"); return; } } catch (GImage::Error& err) { left = NULL; msgBox("Image file \"" + left_name + "\" is invalid and could not be loaded"); return; } width = left->texelWidth(); height = left->texelHeight(); string right_name = root->Attribute("right-image"); right_name = Story::resolve_rel_path(right_name); try { right = the_app.tex_mgr.loadTexture(right_name); if (right.isNull()) { msgBox("Image file \"" + right_name + "\" was not found"); return; } } catch (GImage::Error& err) { right = NULL; msgBox("Image file \"" + right_name + "\" is invalid and could not be loaded"); return; } is_stereo = true; if (right->texelWidth() != width) { msgBox("Width of \"" + left_name + "\" does not match width of \"" + right_name + "\".\nThe widths of left and right image must be the same"); return; } if (right->texelHeight() != height) { msgBox("Height of \"" + left_name + "\" does not match height of \"" + right_name + "\".\nThe heights of left and right image must be the same"); return; } } else { alwaysAssertM(0, "Invalid Image Element"); } //scale height to image proportions height_ratio = height / width; //scale *= 2.f; float max_scale = 1.f; if (height > width) { width *= (max_scale / height); height = max_scale; } else { height *= (max_scale / width); width = max_scale; } // float width = scale; // float height = height_ratio * scale; float min_x = -width * .5; float max_x = width * .5; float min_y = -height * .5; float max_y = height * .5; local_bbox.set(Vector3(min_x, min_y, 0), Vector3(max_x, max_y, 0.02)); update_bbox(); verts.append(Vector3(min_x, max_y, 0)); verts.append(Vector3(min_x, min_y, 0)); verts.append(Vector3(max_x, min_y, 0)); verts.append(Vector3(max_x, max_y, 0)); coords.append(Vector2(0, 0)); coords.append(Vector2(0, 1)); coords.append(Vector2(1, 1)); coords.append(Vector2(1, 0)); valid = true; }
void ArticulatedModel::initOBJ(const std::string& filename, const Preprocess& preprocess) { Stopwatch loadTimer; TextInput::Settings set; set.cppBlockComments = false; set.cppLineComments = false; set.otherCommentCharacter = '#'; set.generateNewlineTokens = true; // Notes on OBJ file format. See also: // // - http://www.martinreddy.net/gfx/3d/OBJ.spec // - http://en.wikipedia.org/wiki/Obj // - http://www.royriggs.com/obj.html // // OBJ indexing is 1-based. // Line breaks are significant. // The first token on a line indicates the contents of the line. // // Faces contain separate indices for normals and texcoords. // We load the raw vertices and then form our own optimized // gl indices from them. // // Negative indices are relative to the last coordinate seen. // Raw arrays with independent indexing, as imported from the file Array<Vector3> rawVertex; Array<Vector3> rawNormal; Array<Vector2> rawTexCoord; // part.geometry.vertexArray[i] = rawVertex[cookVertex[i]]; Array<int> cookVertex; Array<int> cookNormal; Array<int> cookTexCoord; // Put everything into a single part // Convert to a Part Part& part = partArray.next(); part.cframe = CoordinateFrame(); part.name = "root"; part.parent = -1; // v,t,n repeated for each vertex Array<int> faceTempIndex; // Count of errors from mismatched texcoord and vertex int texCoordChanged = 0; int normalChanged = 0; Table<std::string, Material::Ref> materialLibrary; Table<std::string, TriListSpec*> groupTable; TriListSpec* currentTriList = NULL; int numTris = 0; const Matrix3 normalXform = preprocess.xform.upper3x3().transpose().inverse(); const std::string& basePath = FilePath::parent(FileSystem::resolve(filename)); { TextInput ti(filename, set); while (ti.hasMore()) { // Consume comments/newlines while (ti.hasMore() && (ti.peek().type() == Token::NEWLINE)) { // Consume the newline ti.read(); } if (! ti.hasMore()) { break; } // Process one line const std::string& cmd = ti.readSymbol(); if (cmd == "mtllib") { // Specify material library const std::string& mtlFilename = ti.readUntilNewlineAsString(); loadMTL(FilePath::concat(basePath, mtlFilename), materialLibrary, preprocess); } else if (cmd == "g") { // New trilist const std::string& name = ti.readUntilNewlineAsString(); if (! groupTable.containsKey(name)) { currentTriList = new TriListSpec(); currentTriList->name = name; groupTable.set(name, currentTriList); } else { currentTriList = groupTable[name]; } } else if (cmd == "usemtl") { if (currentTriList) { currentTriList->materialName = ti.readUntilNewlineAsString(); } } else if (cmd == "v") { rawVertex.append(readVertex(ti, preprocess.xform)); } else if (cmd == "vt") { // Texcoord Vector2& t = rawTexCoord.next(); t.x = ti.readNumber(); t.y = 1.0f - ti.readNumber(); } else if (cmd == "vn") { // Normal rawNormal.append(readNormal(ti, normalXform)); } else if ((cmd == "f") && currentTriList) { // Face // Read each vertex while (ti.hasMore() && (ti.peek().type() != Token::NEWLINE)) { // Read one 3-part index int v = ti.readNumber(); if (v < 0) { v = rawVertex.size() + 1 + v; } int n = 0; int t = 0; if (ti.peek().type() == Token::SYMBOL) { ti.readSymbol("/"); if (ti.peek().type() == Token::NUMBER) { t = ti.readNumber(); if (t < 0) { t = rawTexCoord.size() + 1 + t; } } if (ti.peek().type() == Token::SYMBOL) { ti.readSymbol("/"); if (ti.peek().type() == Token::NUMBER) { n = ti.readNumber(); if (n < 0) { n = rawNormal.size() + 1 + n; } } } } // Switch to zero-based indexing --v; --n; --t; faceTempIndex.append(v, t, n); } alwaysAssertM(faceTempIndex.size() >= 3*3, "Face with fewer than three vertices in model."); numTris += (faceTempIndex.size()/3) - 2; // The faceTempIndex is now a triangle fan. Convert it to a triangle list and use unique vertices for (int i = 2; i < faceTempIndex.size()/3; ++i) { // Always start with vertex 0 cookVertex.append(faceTempIndex[0]); cookTexCoord.append(faceTempIndex[1]); cookNormal.append(faceTempIndex[2]); // The vertex just before the one we're adding int j = (i - 1) * 3; cookVertex.append(faceTempIndex[j]); cookTexCoord.append(faceTempIndex[j+1]); cookNormal.append(faceTempIndex[j+2]); // The vertex we're adding j = i * 3; cookVertex.append(faceTempIndex[j]); cookTexCoord.append(faceTempIndex[j+1]); cookNormal.append(faceTempIndex[j+2]); // Update the index array to contain the three vertices we just added currentTriList->cpuIndex.append(cookVertex.size() - 3, cookVertex.size() - 2, cookVertex.size() - 1); } faceTempIndex.fastClear(); } // Read until the end of the line while (ti.hasMore() && (ti.read().type() != Token::NEWLINE)); } } debugPrintf("Creating TriLists\n"); // Copy geometry const int N = cookVertex.size(); part.geometry.vertexArray.resize(N); for (int i = 0; i < N; ++i) { part.geometry.vertexArray[i] = rawVertex[cookVertex[i]]; } // Optional normals if (rawNormal.size() > 0) { part.geometry.normalArray.resize(N); for (int i = 0; i < N; ++i) { part.geometry.normalArray[i] = rawNormal[cookNormal[i]]; } } // Optional texcoords if (rawTexCoord.size() > 0) { part.texCoordArray.resize(N); for (int i = 0; i < N; ++i) { part.texCoordArray[i] = rawTexCoord[cookTexCoord[i]]; } } // Create trilists for (Table<std::string, TriListSpec*>::Iterator it = groupTable.begin(); it.hasMore(); ++it) { TriListSpec* s = it->value; Material::Ref material; if (materialLibrary.containsKey(s->materialName)) { material = materialLibrary[s->materialName]; } else { material = Material::createDiffuse(Color3::white() * 0.8f); debugPrintf("Warning: unrecognized material: %s\n", s->materialName.c_str()); } Part::TriList::Ref triList = part.newTriList(material); triList->twoSided = false; triList->indexArray = s->cpuIndex; } groupTable.deleteValues(); groupTable.clear(); debugPrintf("Done loading. %d vertices, %d faces, %d frames\n\n", cookVertex.size(), numTris, N); loadTimer.after("Loading"); }