void LuxRenderer::createAreaLightMesh(mtlu_MayaObject *obj) { MString meshName(""); MFnDependencyNode depFn(obj->mobject); MObject otherSideObj = getOtherSideNode(MString("mtlu_areaLight_geo"), obj->mobject); if( otherSideObj != MObject::kNullObj) { }else{ int indices[6] = {0,1,2,2,3,0}; float floatPointArray[12] = {-1, -1, 0, -1, 1, 0, 1, 1, 0, 1, -1, 0}; float floatNormalArray[12] = {0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1}; ParamSet triParams = CreateParamSet(); triParams->AddInt("indices", indices, 6); triParams->AddPoint("P", floatPointArray, 4); triParams->AddNormal("N", floatNormalArray, 4); lux->transformBegin(); float fm[16]; MMatrix tm = obj->transformMatrices[0]; setZUp(tm, fm); this->lux->transform(fm); this->lux->shape("trianglemesh", boost::get_pointer(triParams)); lux->transformEnd(); } }
void Heightfield::Refine(vector<Reference<Shape> > &refined) const { int ntris = 2*(nx-1)*(ny-1); refined.reserve(ntris); int *verts = new int[3*ntris]; Point *P = new Point[nx*ny]; float *uvs = new float[2*nx*ny]; int nverts = nx*ny; int x, y; // Compute heightfield vertex positions int pos = 0; for (y = 0; y < ny; ++y) { for (x = 0; x < nx; ++x) { P[pos].x = uvs[2*pos] = (float)x / (float)(nx-1); P[pos].y = uvs[2*pos+1] = (float)y / (float)(ny-1); P[pos].z = z[pos]; ++pos; } } // Fill in heightfield vertex offset array int *vp = verts; for (y = 0; y < ny-1; ++y) { for (x = 0; x < nx-1; ++x) { #define VERT(x,y) ((x)+(y)*nx) *vp++ = VERT(x, y); *vp++ = VERT(x+1, y); *vp++ = VERT(x+1, y+1); *vp++ = VERT(x, y); *vp++ = VERT(x+1, y+1); *vp++ = VERT(x, y+1); } #undef VERT } ParamSet paramSet; paramSet.AddInt("indices", verts, 3*ntris); paramSet.AddFloat("uv", uvs, 2 * nverts); paramSet.AddPoint("P", P, nverts); refined.push_back(CreateTriangleMeshShape(ObjectToWorld, WorldToObject, ReverseOrientation, paramSet)); delete[] P; delete[] uvs; delete[] verts; }
void LuxRenderer::defineTriangleMesh(mtlu_MayaObject *obj, bool noObjectDef = false) { MObject meshObject = obj->mobject; MStatus stat = MStatus::kSuccess; MFnMesh meshFn(meshObject, &stat); CHECK_MSTATUS(stat); MItMeshPolygon faceIt(meshObject, &stat); CHECK_MSTATUS(stat); MPointArray points; meshFn.getPoints(points); MFloatVectorArray normals; meshFn.getNormals( normals, MSpace::kWorld ); MFloatArray uArray, vArray; meshFn.getUVs(uArray, vArray); logger.debug(MString("Translating mesh object ") + meshFn.name().asChar()); MString meshFullName = obj->fullNiceName; MIntArray trianglesPerFace, triVertices; meshFn.getTriangles(trianglesPerFace, triVertices); int numTriangles = 0; for( size_t i = 0; i < trianglesPerFace.length(); i++) numTriangles += trianglesPerFace[i]; // lux render does not have a per vertex per face normal definition, here we can use one normal and uv per vertex only // So I create the triangles with unique vertices, normals and uvs. Of course this way vertices etc. cannot be shared. int numPTFloats = numTriangles * 3 * 3; logger.debug(MString("Num Triangles: ") + numTriangles + " num tri floats " + numPTFloats); float *floatPointArray = new float[numPTFloats]; float *floatNormalArray = new float[numPTFloats]; float *floatUvArray = new float[numTriangles * 3 * 2]; logger.debug(MString("Allocated ") + numPTFloats + " floats for point and normals"); MIntArray triangelVtxIdListA; MFloatArray floatPointArrayA; MPointArray triPoints; MIntArray triVtxIds; MIntArray faceVtxIds; MIntArray faceNormalIds; int *triangelVtxIdList = new int[numTriangles * 3]; for( uint sgId = 0; sgId < obj->shadingGroups.length(); sgId++) { MString slotName = MString("slot_") + sgId; } int triCount = 0; int vtxCount = 0; for(faceIt.reset(); !faceIt.isDone(); faceIt.next()) { int faceId = faceIt.index(); int numTris; faceIt.numTriangles(numTris); faceIt.getVertices(faceVtxIds); MIntArray faceUVIndices; faceNormalIds.clear(); for( uint vtxId = 0; vtxId < faceVtxIds.length(); vtxId++) { faceNormalIds.append(faceIt.normalIndex(vtxId)); int uvIndex; faceIt.getUVIndex(vtxId, uvIndex); faceUVIndices.append(uvIndex); } int perFaceShadingGroup = 0; if( obj->perFaceAssignments.length() > 0) perFaceShadingGroup = obj->perFaceAssignments[faceId]; //logger.info(MString("Face ") + faceId + " will receive SG " + perFaceShadingGroup); for( int triId = 0; triId < numTris; triId++) { int faceRelIds[3]; faceIt.getTriangle(triId, triPoints, triVtxIds); for( uint triVtxId = 0; triVtxId < 3; triVtxId++) { for(uint faceVtxId = 0; faceVtxId < faceVtxIds.length(); faceVtxId++) { if( faceVtxIds[faceVtxId] == triVtxIds[triVtxId]) { faceRelIds[triVtxId] = faceVtxId; } } } uint vtxId0 = faceVtxIds[faceRelIds[0]]; uint vtxId1 = faceVtxIds[faceRelIds[1]]; uint vtxId2 = faceVtxIds[faceRelIds[2]]; uint normalId0 = faceNormalIds[faceRelIds[0]]; uint normalId1 = faceNormalIds[faceRelIds[1]]; uint normalId2 = faceNormalIds[faceRelIds[2]]; uint uvId0 = faceUVIndices[faceRelIds[0]]; uint uvId1 = faceUVIndices[faceRelIds[1]]; uint uvId2 = faceUVIndices[faceRelIds[2]]; floatPointArray[vtxCount * 3] = points[vtxId0].x; floatPointArray[vtxCount * 3 + 1] = points[vtxId0].y; floatPointArray[vtxCount * 3 + 2] = points[vtxId0].z; floatNormalArray[vtxCount * 3] = normals[normalId0].x; floatNormalArray[vtxCount * 3 + 1] = normals[normalId0].y; floatNormalArray[vtxCount * 3 + 2] = normals[normalId0].z; floatUvArray[vtxCount * 2] = uArray[uvId0]; floatUvArray[vtxCount * 2 + 1] = vArray[uvId0]; vtxCount++; floatPointArray[vtxCount * 3] = points[vtxId1].x; floatPointArray[vtxCount * 3 + 1] = points[vtxId1].y; floatPointArray[vtxCount * 3 + 2] = points[vtxId1].z; floatNormalArray[vtxCount * 3] = normals[normalId1].x; floatNormalArray[vtxCount * 3 + 1] = normals[normalId1].y; floatNormalArray[vtxCount * 3 + 2] = normals[normalId1].z; floatUvArray[vtxCount * 2] = uArray[uvId1]; floatUvArray[vtxCount * 2 + 1] = vArray[uvId1]; vtxCount++; floatPointArray[vtxCount * 3] = points[vtxId2].x; floatPointArray[vtxCount * 3 + 1] = points[vtxId2].y; floatPointArray[vtxCount * 3 + 2] = points[vtxId2].z; floatNormalArray[vtxCount * 3] = normals[normalId2].x; floatNormalArray[vtxCount * 3 + 1] = normals[normalId2].y; floatNormalArray[vtxCount * 3 + 2] = normals[normalId2].z; floatUvArray[vtxCount * 2] = uArray[uvId2]; floatUvArray[vtxCount * 2 + 1] = vArray[uvId2]; vtxCount++; //logger.debug(MString("Vertex count: ") + vtxCount + " maxId " + ((vtxCount - 1) * 3 + 2) + " ptArrayLen " + (numTriangles * 3 * 3)); triangelVtxIdList[triCount * 3] = triCount * 3; triangelVtxIdList[triCount * 3 + 1] = triCount * 3 + 1; triangelVtxIdList[triCount * 3 + 2] = triCount * 3 + 2; triCount++; } } //generatetangents bool Generate tangent space using miktspace, useful if mesh has a normal map that was also baked using miktspace (such as blender or xnormal) false //subdivscheme string Subdivision algorithm, options are "loop" and "microdisplacement" "loop" //displacementmap string Name of the texture used for the displacement. Subdivscheme parameter must always be provided, as load-time displacement is handled by the loop-subdivision code. none - optional. (loop subdiv can be used without displacement, microdisplacement will not affect the mesh without a displacement map specified) //dmscale float Scale of the displacement (for an LDR map, this is the maximum height of the displacement in meter) 0.1 //dmoffset float Offset of the displacement. 0 //dmnormalsmooth bool Smoothing of the normals of the subdivided faces. Only valid for loop subdivision. true //dmnormalsplit bool Force the mesh to split along breaks in the normal. If a mesh has no normals (flat-shaded) it will rip open on all edges. Only valid for loop subdivision. false //dmsharpboundary bool Try to preserve mesh boundaries during subdivision. Only valid for loop subdivision. false //nsubdivlevels integer Number of subdivision levels. This is only recursive for loop subdivision, microdisplacement will need much larger values (such as 50). 0 bool generatetangents = false; getBool(MString("mtlu_mesh_generatetangents"), meshFn, generatetangents); int subdivscheme = 0; const char *subdAlgos[] = {"loop", "microdisplacement"}; getInt(MString("mtlu_mesh_subAlgo"), meshFn, subdivscheme); const char *subdalgo = subdAlgos[subdivscheme]; float dmscale; getFloat(MString("mtlu_mesh_dmscale"), meshFn, dmscale); float dmoffset; getFloat(MString("mtlu_mesh_dmoffset"), meshFn, dmoffset); MString displacementmap; getString(MString("mtlu_mesh_displacementMap"), meshFn, displacementmap); const char *displacemap = displacementmap.asChar(); bool dmnormalsmooth = true; getBool(MString("mtlu_mesh_dmnormalsmooth"), meshFn, dmnormalsmooth); bool dmnormalsplit = false; getBool(MString("mtlu_mesh_dmnormalsplit"), meshFn, dmnormalsplit); bool dmsharpboundary = false; getBool(MString("mtlu_mesh_dmsharpboundary"), meshFn, dmsharpboundary); int nsubdivlevels = 0; getInt(MString("mtlu_mesh_subdivlevel"), meshFn, nsubdivlevels); // a displacment map needs its own texture defintion MString displacementTextureName = ""; if(displacementmap.length() > 0) { ParamSet dmParams = CreateParamSet(); dmParams->AddString("filename", &displacemap); displacementTextureName = meshFn.name() + "_displacementMap"; this->lux->texture(displacementTextureName.asChar(), "float", "imagemap", boost::get_pointer(dmParams)); } ParamSet triParams = CreateParamSet(); int numPointValues = numTriangles * 3; int numUvValues = numTriangles * 3 * 2; clock_t startTime = clock(); logger.info(MString("Adding mesh values to params.")); triParams->AddInt("indices", triangelVtxIdList, numTriangles * 3); triParams->AddPoint("P", floatPointArray, numPointValues); triParams->AddNormal("N", floatNormalArray, numPointValues); triParams->AddFloat("uv", floatUvArray, numUvValues); if( nsubdivlevels > 0) triParams->AddInt("nsubdivlevels", &nsubdivlevels, 1); triParams->AddBool("generatetangents", &generatetangents, 1); triParams->AddString("subdivscheme", &subdalgo , 1); if(displacementmap.length() > 0) { triParams->AddFloat("dmoffset", &dmoffset, 1); triParams->AddFloat("dmscale", &dmscale, 1); const char *dmft = displacementTextureName.asChar(); triParams->AddString("displacementmap", &dmft); } triParams->AddBool("dmnormalsmooth", &dmnormalsmooth, 1); triParams->AddBool("dmnormalsplit", &dmnormalsplit, 1); triParams->AddBool("dmsharpboundary", &dmsharpboundary, 1); clock_t pTime = clock(); if(!noObjectDef) this->lux->objectBegin(meshFullName.asChar()); this->lux->shape("trianglemesh", boost::get_pointer(triParams)); if(!noObjectDef) this->lux->objectEnd(); clock_t eTime = clock(); logger.info(MString("Timing: Parameters: ") + ((pTime - startTime)/CLOCKS_PER_SEC) + " objTime " + ((eTime - pTime)/CLOCKS_PER_SEC) + " all " + ((eTime - startTime)/CLOCKS_PER_SEC)); return; }
void LoopSubdiv::Refine(vector<Reference<Shape> > &refined) const { vector<SDFace *> f = faces; vector<SDVertex *> v = vertices; MemoryArena arena; for (int i = 0; i < nLevels; ++i) { // Update _f_ and _v_ for next level of subdivision vector<SDFace *> newFaces; vector<SDVertex *> newVertices; // Allocate next level of children in mesh tree for (uint32_t j = 0; j < v.size(); ++j) { v[j]->child = arena.Alloc<SDVertex>(); v[j]->child->regular = v[j]->regular; v[j]->child->boundary = v[j]->boundary; newVertices.push_back(v[j]->child); } for (uint32_t j = 0; j < f.size(); ++j) for (int k = 0; k < 4; ++k) { f[j]->children[k] = arena.Alloc<SDFace>(); newFaces.push_back(f[j]->children[k]); } // Update vertex positions and create new edge vertices // Update vertex positions for even vertices for (uint32_t j = 0; j < v.size(); ++j) { if (!v[j]->boundary) { // Apply one-ring rule for even vertex if (v[j]->regular) v[j]->child->P = weightOneRing(v[j], 1.f/16.f); else v[j]->child->P = weightOneRing(v[j], beta(v[j]->valence())); } else { // Apply boundary rule for even vertex v[j]->child->P = weightBoundary(v[j], 1.f/8.f); } } // Compute new odd edge vertices map<SDEdge, SDVertex *> edgeVerts; for (uint32_t j = 0; j < f.size(); ++j) { SDFace *face = f[j]; for (int k = 0; k < 3; ++k) { // Compute odd vertex on _k_th edge SDEdge edge(face->v[k], face->v[NEXT(k)]); SDVertex *vert = edgeVerts[edge]; if (!vert) { // Create and initialize new odd vertex vert = arena.Alloc<SDVertex>(); newVertices.push_back(vert); vert->regular = true; vert->boundary = (face->f[k] == NULL); vert->startFace = face->children[3]; // Apply edge rules to compute new vertex position if (vert->boundary) { vert->P = 0.5f * edge.v[0]->P; vert->P += 0.5f * edge.v[1]->P; } else { vert->P = 3.f/8.f * edge.v[0]->P; vert->P += 3.f/8.f * edge.v[1]->P; vert->P += 1.f/8.f * face->otherVert(edge.v[0], edge.v[1])->P; vert->P += 1.f/8.f * face->f[k]->otherVert(edge.v[0], edge.v[1])->P; } edgeVerts[edge] = vert; } } } // Update new mesh topology // Update even vertex face pointers for (uint32_t j = 0; j < v.size(); ++j) { SDVertex *vert = v[j]; int vertNum = vert->startFace->vnum(vert); vert->child->startFace = vert->startFace->children[vertNum]; } // Update face neighbor pointers for (uint32_t j = 0; j < f.size(); ++j) { SDFace *face = f[j]; for (int k = 0; k < 3; ++k) { // Update children _f_ pointers for siblings face->children[3]->f[k] = face->children[NEXT(k)]; face->children[k]->f[NEXT(k)] = face->children[3]; // Update children _f_ pointers for neighbor children SDFace *f2 = face->f[k]; face->children[k]->f[k] = f2 ? f2->children[f2->vnum(face->v[k])] : NULL; f2 = face->f[PREV(k)]; face->children[k]->f[PREV(k)] = f2 ? f2->children[f2->vnum(face->v[k])] : NULL; } } // Update face vertex pointers for (uint32_t j = 0; j < f.size(); ++j) { SDFace *face = f[j]; for (int k = 0; k < 3; ++k) { // Update child vertex pointer to new even vertex face->children[k]->v[k] = face->v[k]->child; // Update child vertex pointer to new odd vertex SDVertex *vert = edgeVerts[SDEdge(face->v[k], face->v[NEXT(k)])]; face->children[k]->v[NEXT(k)] = vert; face->children[NEXT(k)]->v[k] = vert; face->children[3]->v[k] = vert; } } // Prepare for next level of subdivision f = newFaces; v = newVertices; } // Push vertices to limit surface PbrtPoint *Plimit = new PbrtPoint[v.size()]; for (uint32_t i = 0; i < v.size(); ++i) { if (v[i]->boundary) Plimit[i] = weightBoundary(v[i], 1.f/5.f); else Plimit[i] = weightOneRing(v[i], gamma(v[i]->valence())); } for (uint32_t i = 0; i < v.size(); ++i) v[i]->P = Plimit[i]; // Compute vertex tangents on limit surface vector<Normal> Ns; Ns.reserve(v.size()); vector<PbrtPoint> Pring(16, PbrtPoint()); for (uint32_t i = 0; i < v.size(); ++i) { SDVertex *vert = v[i]; Vector S(0,0,0), T(0,0,0); int valence = vert->valence(); if (valence > (int)Pring.size()) Pring.resize(valence); vert->oneRing(&Pring[0]); if (!vert->boundary) { // Compute tangents of interior face for (int k = 0; k < valence; ++k) { S += cosf(2.f*M_PI*k/valence) * Vector(Pring[k]); T += sinf(2.f*M_PI*k/valence) * Vector(Pring[k]); } } else { // Compute tangents of boundary face S = Pring[valence-1] - Pring[0]; if (valence == 2) T = Vector(Pring[0] + Pring[1] - 2 * vert->P); else if (valence == 3) T = Pring[1] - vert->P; else if (valence == 4) // regular T = Vector(-1*Pring[0] + 2*Pring[1] + 2*Pring[2] + -1*Pring[3] + -2*vert->P); else { float theta = M_PI / float(valence-1); T = Vector(sinf(theta) * (Pring[0] + Pring[valence-1])); for (int k = 1; k < valence-1; ++k) { float wt = (2*cosf(theta) - 2) * sinf((k) * theta); T += Vector(wt * Pring[k]); } T = -T; } } Ns.push_back(Normal(Cross(S, T))); } // Create _TriangleMesh_ from subdivision mesh uint32_t ntris = uint32_t(f.size()); int *verts = new int[3*ntris]; int *vp = verts; uint32_t totVerts = uint32_t(v.size()); map<SDVertex *, int> usedVerts; for (uint32_t i = 0; i < totVerts; ++i) usedVerts[v[i]] = i; for (uint32_t i = 0; i < ntris; ++i) { for (int j = 0; j < 3; ++j) { *vp = usedVerts[f[i]->v[j]]; ++vp; } } ParamSet paramSet; paramSet.AddInt("indices", verts, 3*ntris); paramSet.AddPoint("P", Plimit, totVerts); paramSet.AddNormal("N", &Ns[0], int(Ns.size())); refined.push_back(CreateTriangleMeshShape(ObjectToWorld, WorldToObject, ReverseOrientation, paramSet)); delete[] verts; delete[] Plimit; }
MedianCutEnvironmentLight::MedianCutEnvironmentLight(const Transform &light2world, const Spectrum &L, int ns_, const string &texmap) : Light(light2world, ns_), impl(new MedCutEnvImpl {nullptr, nullptr, ns_}) { int width = 0, height = 0; RGBSpectrum *texels = NULL; // Read texel data from _texmap_ into _texels_ if (texmap != "") { texels = ReadImage(texmap, &width, &height); if (texels) for (int i = 0; i < width * height; ++i) texels[i] *= L.ToRGBSpectrum(); } if (!texels) { width = height = 1; texels = new RGBSpectrum[1]; texels[0] = L.ToRGBSpectrum(); } impl->radianceMap = new MIPMap<RGBSpectrum>(width, height, texels); impl->width = width; impl->inv_w = 1.0f/(width-1); impl->height = height; impl->inv_h = 1.0f/(height-1); impl->createDistantLight = [this](const RGBSpectrum& s, float cy, float cx) { ParamSet p; float rgb[3]; s.ToRGB(rgb); p.AddRGBSpectrum("L", rgb, 3); const float theta = impl->inv_h*cy * M_PI , phi = impl->inv_w*cx * 2.f * M_PI; const float costheta = cosf(theta), sintheta = sinf(theta); const float sinphi = sinf(phi), cosphi = cosf(phi); const Point wi(-sintheta*cosphi, -sintheta*sinphi, -costheta); p.AddPoint(string("to"), &wi, 1); #if DEBUG >= 1 fprintf(stderr, "rgb (%f,%f,%f)\n", rgb[0], rgb[1], rgb[2]); #endif return CreateDistantLight(this->LightToWorld, p); }; impl->solid_angle = ((2.f * M_PI) / (width - 1)) * (M_PI / (1.f * (height - 1))); #if DEBUG >= 1 fprintf(stderr, "solid_angle = %f\n", impl->solid_angle); #endif for (int y = 0; y < height; ++y) { float sinTheta = sinf(M_PI * float(y + 0.5f)/height); for (int x = 0; x < width; ++x) texels[y*width + x] *= impl->solid_angle * sinTheta; } // Initialize energy sum array; the array is shifted for (1,1) // i.e. (0,*) and (*,0) are inserted 0-boundaries fprintf(stderr, "[+] [%10.2f] Initializing sum array\n", clock()*1.0/CLOCKS_PER_SEC); #if DEBUG >= 3 for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { fprintf(stderr, "%.2f ", texels[y*width+x].y()); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); #endif vector<vector<float>> acc(height+1, vector<float>(width+1)); for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) acc[y+1][x+1] = acc[y+1][x] + texels[y*width + x].y(); #if DEBUG >= 3 for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { fprintf(stderr, "%.2f ", acc[y][x]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); #endif for (int x = 1; x <= width; ++x) for (int y = 1; y <= height; ++y) acc[y][x] += acc[y-1][x]; #if DEBUG >= 3 for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { fprintf(stderr, "%.2f ", acc[y][x]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); #endif // initialize median cut printf("[+] [%10.2f] Calculating median cut\n", clock()*1.0/CLOCKS_PER_SEC); subdivide(this->impl, 1, texels, acc, 0, 0, width-1, height-1); printf("[+] [%10.2f] Done with %d lights\n", clock()*1.0/CLOCKS_PER_SEC, static_cast<int>(impl->ls.size())); delete[] texels; // Initialize sampling PDFs for environment area light // Compute scalar-valued image _img_ from environment map float filter = 1.f / max(width, height); float *img = new float[width*height]; for (int v = 0; v < height; ++v) { float vp = (float)v / (float)height; float sinTheta = sinf(M_PI * float(v+.5f)/float(height)); for (int u = 0; u < width; ++u) { float up = (float)u / (float)width; img[u+v*width] = impl->radianceMap->Lookup(up, vp, filter).y(); img[u+v*width] *= sinTheta; } } // Compute sampling impl->distributions for rows and columns of image impl->distribution = new Distribution2D(img, width, height); delete[] img; }
void NURBS::Refine(vector<Reference<Shape> > &refined) const { // Compute NURBS dicing rates int diceu = 30, dicev = 30; float *ueval = new float[diceu]; float *veval = new float[dicev]; Point *evalPs = new Point[diceu*dicev]; Normal *evalNs = new Normal[diceu*dicev]; int i; for (i = 0; i < diceu; ++i) ueval[i] = Lerp((float)i / (float)(diceu-1), umin, umax); for (i = 0; i < dicev; ++i) veval[i] = Lerp((float)i / (float)(dicev-1), vmin, vmax); // Evaluate NURBS over grid of points memset(evalPs, 0, diceu*dicev*sizeof(Point)); memset(evalNs, 0, diceu*dicev*sizeof(Point)); float *uvs = new float[2*diceu*dicev]; // Turn NURBS into triangles Homogeneous3 *Pw = (Homogeneous3 *)P; if (!isHomogeneous) { Pw = (Homogeneous3 *)alloca(nu*nv*sizeof(Homogeneous3)); for (int i = 0; i < nu*nv; ++i) { Pw[i].x = P[3*i]; Pw[i].y = P[3*i+1]; Pw[i].z = P[3*i+2]; Pw[i].w = 1.; } } for (int v = 0; v < dicev; ++v) { for (int u = 0; u < diceu; ++u) { uvs[2*(v*diceu+u)] = ueval[u]; uvs[2*(v*diceu+u)+1] = veval[v]; Vector dPdu, dPdv; Point pt = NURBSEvaluateSurface(uorder, uknot, nu, ueval[u], vorder, vknot, nv, veval[v], Pw, &dPdu, &dPdv); evalPs[v*diceu + u].x = pt.x; evalPs[v*diceu + u].y = pt.y; evalPs[v*diceu + u].z = pt.z; evalNs[v*diceu + u] = Normal(Normalize(Cross(dPdu, dPdv))); } } // Generate points-polygons mesh int nTris = 2*(diceu-1)*(dicev-1); int *vertices = new int[3 * nTris]; int *vertp = vertices; // Compute the vertex offset numbers for the triangles for (int v = 0; v < dicev-1; ++v) { for (int u = 0; u < diceu-1; ++u) { #define VN(u,v) ((v)*diceu+(u)) *vertp++ = VN(u, v); *vertp++ = VN(u+1, v); *vertp++ = VN(u+1, v+1); *vertp++ = VN(u, v); *vertp++ = VN(u+1, v+1); *vertp++ = VN(u, v+1); #undef VN } } int nVerts = diceu*dicev; ParamSet paramSet; paramSet.AddInt("indices", vertices, 3*nTris); paramSet.AddPoint("P", evalPs, nVerts); paramSet.AddFloat("uv", uvs, 2 * nVerts); paramSet.AddNormal("N", evalNs, nVerts); refined.push_back(MakeShape("trianglemesh", ObjectToWorld, reverseOrientation, paramSet)); // Cleanup from NURBS refinement delete[] uvs; delete[] ueval; delete[] veval; delete[] evalPs; delete[] evalNs; delete[] vertices; }