Example #1
0
void App::onRender() {
    // Show message
    message("Rendering...");

    Stopwatch timer;
    rayTraceImage(1.0f, m_raysPerPixel);
    timer.after("Trace");
    debugPrintf("%f s\n", timer.elapsedTime());
//    m_result->toImage3uint8()->save("result.png");
}
void ArticulatedModel::load3DS(const Specification& specification) {
    // During loading, we make no attempt to optimize the mesh.  We leave that until the
    // Parts have been created.  The vertex arrays are therefore much larger than they
    // need to be.
    Stopwatch timer;

    Parse3DS parseData;
    {
        BinaryInput bi(specification.filename, G3D_LITTLE_ENDIAN);
        timer.after(" open file");
        parseData.parse(bi);
        timer.after(" parse");
    }

    name = FilePath::base(specification.filename);

    const std::string& path = FilePath::parent(specification.filename);

    /*
    if (specification.stripMaterials) {
        stripMaterials(parseData);
    }

    if (specification.mergeMeshesByMaterial) {
        mergeGroupsAndMeshesByMaterial(parseData);
        }*/

    for (int p = 0; p < parseData.objectArray.size(); ++p) {
        Parse3DS::Object& object = parseData.objectArray[p];

        // Create a unique name for this part
        std::string name = object.name;
        int count = 0;
        while (this->part(name) != NULL) {
            ++count;
            name = object.name + format("_#%d", count);
        }

        // Create the new part
        // All 3DS parts are promoted to the root in the current implementation.
        Part* part = addPart(name);

        // Process geometry
        part->cpuVertexArray.vertex.resize(object.vertexArray.size());
        part->cframe = object.keyframe.approxCoordinateFrame();
        debugAssert(isFinite(part->cframe.rotation.determinant()));
        debugAssert(part->cframe.rotation.isOrthonormal());

        if (! part->cframe.rotation.isRightHanded()) {
            // TODO: how will this impact other code?  I think we can't just force it like this -- Morgan
            part->cframe.rotation.setColumn(0, -part->cframe.rotation.column(0));
        }

        debugAssert(part->cframe.rotation.isRightHanded());

        //debugPrintf("%s %d %d\n", object.name.c_str(), object.hierarchyIndex, object.nodeID);

        if (part->cpuVertexArray.vertex.size() > 0) {
            // Convert vertices to object space (there is no surface normal data at this point)
            Matrix4 netXForm = part->cframe.inverse().toMatrix4();
            
            debugAssertM(netXForm.row(3) == Vector4(0,0,0,1), 
                        "3DS file loading requires that the last row of the xform matrix be 0, 0, 0, 1");

            if (object.texCoordArray.size() > 0) {
                part->m_hasTexCoord0 = true;
                part->cpuVertexArray.hasTexCoord0 = true;
            }
            
            const Matrix3& S = netXForm.upper3x3();
            const Vector3& T = netXForm.column(3).xyz();
            for (int v = 0; v < part->cpuVertexArray.vertex.size(); ++v) {
#               ifdef G3D_DEBUG
                {
                    const Vector3& vec = object.vertexArray[v];
                    debugAssert(vec.isFinite());
                }
#               endif

                CPUVertexArray::Vertex& vertex = part->cpuVertexArray.vertex[v];
                vertex.position = S * object.vertexArray[v] + T;
                vertex.tangent = Vector4::nan();
                vertex.normal  = Vector3::nan();

                if (part->m_hasTexCoord0) {
                    vertex.texCoord0 = object.texCoordArray[v];
                }

#               ifdef G3D_DEBUG
                {
                    const Vector3& vec = vertex.position;
                    debugAssert(vec.isFinite());
                }
#               endif
            }


            if (object.faceMatArray.size() == 0) {

                // Merge all geometry into one mesh since there are no materials
                Mesh* mesh = addMesh("mesh", part);
                mesh->cpuIndexArray = object.indexArray;
                debugAssert(mesh->cpuIndexArray.size() % 3 == 0);

            } else {
                for (int m = 0; m < object.faceMatArray.size(); ++m) {
                    const Parse3DS::FaceMat& faceMat = object.faceMatArray[m];

                    if (faceMat.faceIndexArray.size() > 0) {

                        Material::Ref mat;
                        bool twoSided = false;

                        const std::string& materialName = faceMat.materialName;
                        if (parseData.materialNameToIndex.containsKey(materialName)) {
                            int i = parseData.materialNameToIndex[materialName];
                            const Parse3DS::Material& material = parseData.materialArray[i];
                            
                            //if (! materialSubstitution.get(material.texture1.filename, mat)) {
                            const Material::Specification& spec = compute3DSMaterial(&material, path, specification);
                            mat = Material::create(spec);
                            //}
                            twoSided = material.twoSided || mat->hasAlphaMask();
                        } else {
                            mat = Material::create();
                            logPrintf("Referenced unknown material '%s'\n", materialName.c_str());
                        }                        

                        Mesh* mesh = addMesh(materialName, part);
                        debugAssert(isValidHeapPointer(mesh));
                        mesh->material = mat;
                        mesh->twoSided = twoSided;

                        // Construct an index array for this part
                        for (int i = 0; i < faceMat.faceIndexArray.size(); ++i) {
                            // 3*f is an index into object.indexArray
                            int f = faceMat.faceIndexArray[i];
                            debugAssert(f >= 0);
                            for (int v = 0; v < 3; ++v) {
                                mesh->cpuIndexArray.append(object.indexArray[3 * f + v]);
                            }
                        }
                        debugAssert(mesh->cpuIndexArray.size() > 0);
                        debugAssert(mesh->cpuIndexArray.size() % 3 == 0);

                    }
                } // for m
            } // if has materials 
        }
    }

    timer.after(" convert");
}
Example #3
0
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");
}