int EditPatchMod::DoAttach(INode *node, PatchMesh *attPatch, RPatchMesh *rattPatch, bool & canUndo) { ModContextList mcList; INodeTab nodes; if (!ip) return 0; ip->GetModContexts(mcList, nodes); if (mcList.Count() != 1) { nodes.DisposeTemporary(); return 0; } EditPatchData *patchData =(EditPatchData*)mcList[0]->localData; if (!patchData) { nodes.DisposeTemporary(); return 0; } patchData->BeginEdit(ip->GetTime()); // If the mesh isn't yet cached, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) { nodes.DisposeTemporary(); return 0; } patchData->RecordTopologyTags(patch); RecordTopologyTags(); // Transform the shape for attachment: // If reorienting, just translate to align pivots // Otherwise, transform to match our transform Matrix3 attMat(1); if (attachReorient) { Matrix3 thisTM = nodes[0]->GetNodeTM(ip->GetTime()); Matrix3 thisOTMBWSM = nodes[0]->GetObjTMBeforeWSM(ip->GetTime()); Matrix3 thisPivTM = thisTM * Inverse(thisOTMBWSM); Matrix3 otherTM = node->GetNodeTM(ip->GetTime()); Matrix3 otherOTMBWSM = node->GetObjTMBeforeWSM(ip->GetTime()); Matrix3 otherPivTM = otherTM * Inverse(otherOTMBWSM); Point3 otherObjOffset = node->GetObjOffsetPos(); attMat = Inverse(otherPivTM) * thisPivTM; } else { attMat = node->GetObjectTM(ip->GetTime()) * Inverse(nodes[0]->GetObjectTM(ip->GetTime())); } // RB 3-17-96 : Check for mirroring AffineParts parts; decomp_affine(attMat, &parts); if (parts.f < 0.0f) { int v[8], ct, ct2, j; Point3 p[9]; for (int i = 0; i < attPatch->numPatches; i++) { // Re-order rpatch if (attPatch->patches[i].type == PATCH_QUAD) { UI_PATCH rpatch=rattPatch->getUIPatch (i); int ctU=rpatch.NbTilesU<<1; int ctV=rpatch.NbTilesV<<1; int nU; for (nU=0; nU<ctU; nU++) { for (int nV=0; nV<ctV; nV++) { rattPatch->getUIPatch (i).getTileDesc (nU+nV*ctU)=rpatch.getTileDesc (ctU-1-nU+(ctV-1-nV)*ctU); } } for (nU=0; nU<ctU+1; nU++) { for (int nV=0; nV<ctV+1; nV++) { rattPatch->getUIPatch (i).setColor (nU+nV*(ctU+1), rpatch.getColor (ctU-nU+(ctV-nV)*ctU)); } } } // Re-order vertices ct = attPatch->patches[i].type == PATCH_QUAD ? 4 : 3; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].v[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].v[j] = v[ct - j - 1]; } // Re-order vecs ct = attPatch->patches[i].type == PATCH_QUAD ? 8 : 6; ct2 = attPatch->patches[i].type == PATCH_QUAD ? 5 : 3; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].vec[j]; } for (j = 0; j < ct; j++, ct2--) { if (ct2 < 0) ct2 = ct - 1; attPatch->patches[i].vec[j] = v[ct2]; } // Re-order enteriors if (attPatch->patches[i].type == PATCH_QUAD) { ct = 4; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].interior[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].interior[j] = v[ct - j - 1]; } } // Re-order aux if (attPatch->patches[i].type == PATCH_TRI) { ct = 9; for (j = 0; j < ct; j++) { p[j] = attPatch->patches[i].aux[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].aux[j] = p[ct - j - 1]; } } // Re-order TV faces if present for (int chan = 0; chan < patch->getNumMaps(); ++chan) { if (attPatch->tvPatches[chan]) { ct = 4; for (j = 0; j < ct; j++) { v[j] = attPatch->tvPatches[chan][i].tv[j]; } for (j = 0; j < ct; j++) { attPatch->tvPatches[chan][i].tv[j] = v[ct - j - 1]; } } } } } int i; for (i = 0; i < attPatch->numVerts; ++i) attPatch->verts[i].p = attPatch->verts[i].p * attMat; for (i = 0; i < attPatch->numVecs; ++i) attPatch->vecs[i].p = attPatch->vecs[i].p * attMat; attPatch->computeInteriors(); theHold.Begin(); // Combine the materials of the two nodes. int mat2Offset = 0; Mtl *m1 = nodes[0]->GetMtl(); Mtl *m2 = node->GetMtl(); bool condenseMe = FALSE; if (m1 && m2 &&(m1 != m2)) { if (attachMat == ATTACHMAT_IDTOMAT) { int ct = 1; if (m1->IsMultiMtl()) ct = m1->NumSubMtls(); for (int i = 0; i < patch->numPatches; ++i) { int mtid = patch->getPatchMtlIndex(i); if (mtid >= ct) patch->setPatchMtlIndex(i, mtid % ct); } FitPatchIDsToMaterial(*attPatch, m2); if (condenseMat) condenseMe = TRUE; } // the theHold calls here were a vain attempt to make this all undoable. // This should be revisited in the future so we don't have to use the SYSSET_CLEAR_UNDO. theHold.Suspend(); if (attachMat == ATTACHMAT_MATTOID) { m1 = FitMaterialToPatchIDs(*patch, m1); m2 = FitMaterialToPatchIDs(*attPatch, m2); } Mtl *multi = CombineMaterials(m1, m2, mat2Offset); if (attachMat == ATTACHMAT_NEITHER) mat2Offset = 0; theHold.Resume(); // We can't be in face subobject mode, else we screw up the materials: DWORD oldSL = patch->selLevel; DWORD roldSL = patch->selLevel; patch->selLevel = PATCH_OBJECT; rpatch->SetSelLevel (EP_OBJECT); nodes[0]->SetMtl(multi); patch->selLevel = oldSL; rpatch->SetSelLevel (roldSL); m1 = multi; canUndo = FALSE; // Absolutely cannot undo material combinations. } if (!m1 && m2) { // We can't be in face subobject mode, else we screw up the materials: DWORD oldSL = patch->selLevel; DWORD roldSL = rpatch->GetSelLevel(); patch->selLevel = PATCH_OBJECT; rpatch->SetSelLevel (EP_OBJECT); nodes[0]->SetMtl(m2); patch->selLevel = oldSL; rpatch->SetSelLevel (roldSL); m1 = m2; } // Start a restore object... if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, this, patch, rpatch, "DoAttach")); // Do the attach patch->Attach(attPatch, mat2Offset); rpatch->Attach(rattPatch, *patch); patchData->UpdateChanges(patch, rpatch); patchData->TempData(this)->Invalidate(PART_TOPO | PART_GEOM); // Get rid of the original node ip->DeleteNode(node); ResolveTopoChanges(); theHold.Accept(GetString(IDS_TH_ATTACH)); if (m1 && condenseMe) { // Following clears undo stack. patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); m1 = CondenseMatAssignments(*patch, m1); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); NotifyDependents(FOREVER, PART_TOPO | PART_GEOM, REFMSG_CHANGE); ip->RedrawViews(ip->GetTime(), REDRAW_NORMAL); return 1; }
void ExtrudeMod::BuildPatchFromShape(TimeValue t,ModContext &mc, ObjectState * os, PatchMesh &pmesh) { ShapeObject *shape = (ShapeObject *)os->obj; float amount; int levels,capStart,capEnd,capType; pblock->GetValue(PB_AMOUNT,t,amount,FOREVER); pblock->GetValue(PB_SEGS,t,levels,FOREVER); if (levels<1) levels = 1; pblock->GetValue(PB_CAPSTART,t,capStart,FOREVER); pblock->GetValue(PB_CAPEND,t,capEnd,FOREVER); pblock->GetValue(PB_CAPTYPE,t,capType,FOREVER); BOOL texturing; pblock->GetValue(PB_MAPPING, TimeValue(0), texturing, FOREVER); BOOL genMatIDs; pblock->GetValue(PB_GEN_MATIDS, TimeValue(0), genMatIDs, FOREVER); BOOL useShapeIDs; pblock->GetValue(PB_USE_SHAPEIDS, TimeValue(0), useShapeIDs, FOREVER); BOOL smooth; pblock->GetValue(PB_SMOOTH, TimeValue(0), smooth, FOREVER); LimitValue(amount, -1000000.0f, 1000000.0f); // Get the basic dimension stuff float zSize = (float)fabs(amount); float baseZ = 0.0f; if(amount < 0.0f) baseZ = amount; // If the shape can convert itself to a BezierShape, have it do so! BezierShape bShape; if(shape->CanMakeBezier()) shape->MakeBezier(t, bShape); else { PolyShape pShape; shape->MakePolyShape(t, pShape); bShape = pShape; // UGH -- Convert it from a PolyShape -- not good! } //DebugPrint(_T("Extrude organizing shape\n")); ShapeHierarchy hier; bShape.OrganizeCurves(t, &hier); // Need to flip the reversed polys... bShape.Reverse(hier.reverse); // ...and tell the hierarchy they're no longer reversed! hier.reverse.ClearAll(); // Our shapes are now organized for patch-making -- Let's do the sides! int polys = bShape.splineCount; int poly, knot; int levelVerts = 0, levelVecs = 0, levelPatches = 0, nverts = 0, nvecs = 0, npatches = 0; int TVlevels = levels + 1, levelTVerts = 0, ntverts = 0, ntpatches = 0; BOOL anyClosed = FALSE; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; if(spline->Closed()) anyClosed = TRUE; levelVerts += spline->KnotCount(); levelTVerts += (spline->Segments() + 1); levelVecs += (spline->Segments() * 2); levelPatches += spline->Segments(); } nverts = levelVerts * (levels + 1); npatches = levelPatches * levels; nvecs = (levelVecs * (levels + 1)) + levels * levelVerts * 2 + npatches * 4; if(texturing) { ntverts = levelTVerts * TVlevels; ntpatches = npatches; } pmesh.setNumVerts(nverts); pmesh.setNumVecs(nvecs); pmesh.setNumPatches(npatches); pmesh.setNumTVerts(ntverts); pmesh.setNumTVPatches(ntpatches); // Create the vertices! int vert = 0; int level; Point3 offset1, offset2; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; int knots = spline->KnotCount(); for(level = 0; level <= levels; ++level) { Point3 offset = Point3(0.0f, 0.0f, baseZ + (float)level / (float)levels * zSize); if(level == 0) offset1 = offset; else if(level == levels) offset2 = offset; for(knot = 0; knot < knots; ++knot) { Point3 p = spline->GetKnotPoint(knot); pmesh.setVert(vert++, p + offset); } } } assert(vert == nverts); BOOL usePhysUVs = GetUsePhysicalScaleUVs(); // Maybe create the texture vertices if(texturing) { int tvert = 0; int level; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; // Make it a polyline PolyLine pline; spline->MakePolyLine(pline, 10); int knots = spline->KnotCount(); for(level = 0; level < TVlevels; ++level) { float tV = (float)level / (float)(TVlevels - 1); float vScale = usePhysUVs ? amount : 1.0f; int lverts = pline.numPts; int tp = 0; int texPts = spline->Segments() + 1; float cumLen = 0.0f; float totLen = pline.CurveLength(); float uScale = usePhysUVs ? totLen : 1.0f; Point3 prevPt = pline.pts[0].p; int plix = 0; while(tp < texPts) { Point3 &pt = pline[plix].p; cumLen += Length(pt - prevPt); prevPt = pt; if(pline[plix].flags & POLYPT_KNOT) { float tU; if(tp == (texPts - 1)) tU = 1.0f; else tU = cumLen / totLen; pmesh.setTVert(tvert++, UVVert(uScale*tU, vScale*tV, 0.0f)); tp++; } plix = (plix + 1) % pline.numPts; } } } assert(tvert == ntverts); } // Create the vectors! int seg; int vec = 0; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; int segs = spline->Segments(); int knots = spline->KnotCount(); // First, the vectors on each level for(level = 0; level <= levels; ++level) { Point3 offset = Point3(0.0f, 0.0f, baseZ + (float)level / (float)levels * zSize); for(seg = 0; seg < segs; ++seg) { int seg2 = (seg + 1) % knots; if(spline->GetLineType(seg) == LTYPE_CURVE) { Point3 p = spline->GetOutVec(seg); pmesh.setVec(vec++, p + offset); p = spline->GetInVec(seg2); pmesh.setVec(vec++, p + offset); } else { Point3 p = spline->InterpBezier3D(seg, 0.333333f); pmesh.setVec(vec++, p + offset); p = spline->InterpBezier3D(seg, 0.666666f); pmesh.setVec(vec++, p + offset); } } } // Now, the vectors between the levels int baseVec = vec; for(level = 0; level < levels; ++level) { Point3 offsetA = Point3(0.0f, 0.0f, baseZ + (float)level / (float)levels * zSize); Point3 offsetB = Point3(0.0f, 0.0f, baseZ + (float)(level + 1) / (float)levels * zSize); Point3 offset1 = offsetA + (offsetB - offsetA) * 0.333333333f; Point3 offset2 = offsetA + (offsetB - offsetA) * 0.666666666f; for(knot = 0; knot < knots; ++knot) { Point3 p = spline->GetKnotPoint(knot); pmesh.setVec(vec++, p + offset1); pmesh.setVec(vec++, p + offset2); } } } // Create the patches! int np = 0; int baseVert = 0; int baseVec = 0; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; int knots = spline->KnotCount(); int segs = spline->Segments(); int baseVec1 = baseVec; // Base vector index for this level int baseVec2 = baseVec + segs * 2 * (levels + 1); // Base vector index for between levels for(level = 0; level < levels; ++level) { int sm = 0; BOOL firstSmooth = (spline->GetLineType(0) == LTYPE_CURVE && spline->GetLineType(segs-1) == LTYPE_CURVE && (spline->GetKnotType(0) == KTYPE_AUTO || spline->GetKnotType(0) == KTYPE_BEZIER)) ? TRUE : FALSE; for(seg = 0; seg < segs; ++seg, vec += 4) { int prevseg = (seg + segs - 1) % segs; int seg2 = (seg + 1) % knots; int a,b,c,d,ab,ba,bc,cb,cd,dc,da,ad; MtlID mtl = useShapeIDs ? spline->GetMatID(seg) : 2; a = baseVert + seg; b = baseVert + seg2; c = b + knots; d = a + knots; ab = baseVec1 + seg * 2; ba = ab + 1; bc = baseVec2 + seg2 * 2; cb = bc + 1; cd = ba + (segs * 2); dc = ab + (segs * 2); da = baseVec2 + seg * 2 + 1; ad = da - 1; //DebugPrint(_T("Making patch %d: %d (%d %d) %d (%d %d) %d (%d %d) %d (%d %d)\n"),np, a, ab, ba, b, bc, cb, c, cd, dc, d, da, ad); // If the vertex is not smooth, go to the next group! if(seg > 0 && !(spline->GetLineType(prevseg) == LTYPE_CURVE && spline->GetLineType(seg) == LTYPE_CURVE && (spline->GetKnotType(seg) == KTYPE_AUTO || spline->GetKnotType(seg) == KTYPE_BEZIER))) { sm++; if(sm > 2) sm = 1; } DWORD smoothGroup = 1 << sm; if(seg == segs - 1 && firstSmooth) { smoothGroup |= 1; } pmesh.MakeQuadPatch(np, a, ab, ba, b, bc, cb, c, cd, dc, d, da, ad, vec, vec+1, vec+2, vec+3, smooth ? smoothGroup : 0); pmesh.setPatchMtlIndex(np++, genMatIDs ? mtl : 0); } baseVert += knots; baseVec1 += (segs * 2); baseVec2 += (knots * 2); } baseVert += knots; baseVec += (segs * 2 * (levels + 1) + knots * 2 * levels); } assert(vec == nvecs); assert(np == npatches); // Maybe create the texture patches! if(texturing) { int ntp = 0; int baseTVert = 0; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; int pknots = spline->Segments() + 1; int pverts = pknots * TVlevels; int segs = spline->Segments(); for(level = 0; level < levels; ++level) { for(seg = 0; seg < segs; ++seg) { int prevseg = (seg + segs - 1) % segs; int seg2 = seg + 1; int a,b,c,d; a = baseTVert + seg; b = baseTVert + seg2; c = b + pknots; d = a + pknots; TVPatch &tp = pmesh.getTVPatch(ntp++); tp.setTVerts(a, b, c, d); } baseTVert += pknots; } baseTVert += pknots; } assert(ntp == ntpatches); } // If capping, do it! if(anyClosed && (capStart || capEnd)) { PatchCapInfo capInfo; bShape.MakeCap(t, capInfo); // Build information for capping PatchCapper capper(bShape); if(capStart) { vert = 0; int baseVec = 0; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; PatchCapPoly &capline = capper[poly]; int lverts = spline->KnotCount(); for(int v = 0; v < lverts; ++v) capline.SetVert(v, vert++); // Gives this vert's location in the mesh! vert += lverts * levels; vec = baseVec; int lvecs = spline->Segments() * 2; for(int v = 0; v < lvecs; ++v) capline.SetVec(v, vec++); // Gives this vec's location in the mesh! baseVec += lvecs * (levels + 1) + spline->KnotCount() * levels * 2; } // Create a work matrix for capping Matrix3 mat = TransMatrix(offset1); int oldPatches = pmesh.numPatches; capper.CapPatchMesh(pmesh, capInfo, TRUE, 16, &mat, genMatIDs ? -1 : 0); // If texturing, create the texture patches and vertices if(texturing) MakePatchCapTexture(pmesh, Inverse(mat), oldPatches, pmesh.numPatches, usePhysUVs); } if(capEnd) { int baseVert = 0; int baseVec = 0; for(poly = 0; poly < polys; ++poly) { Spline3D *spline = bShape.splines[poly]; if(!spline->KnotCount()) continue; PatchCapPoly &capline = capper[poly]; int lverts = spline->KnotCount(); int vert = baseVert + lverts * levels; for(int v = 0; v < lverts; ++v) capline.SetVert(v, vert++); // Gives this vert's location in the mesh! baseVert += lverts * (levels + 1); int lvecs = spline->Segments()*2; int vec = baseVec + lvecs * levels; for(int v = 0; v < lvecs; ++v) capline.SetVec(v, vec++); // Gives this vec's location in the mesh! baseVec += lvecs * (levels + 1) + spline->KnotCount() * levels * 2; } // Create a work matrix for grid capping Matrix3 mat = TransMatrix(offset2); int oldPatches = pmesh.numPatches; capper.CapPatchMesh(pmesh, capInfo, FALSE, 16, &mat, genMatIDs ? -1 : 0); // If texturing, create the texture patches and vertices if(texturing) MakePatchCapTexture(pmesh, Inverse(mat), oldPatches, pmesh.numPatches, usePhysUVs); } } //watje new mapping if(texturing) { if (ver < 4) { for (int i = 0; i < pmesh.numPatches; i++) pmesh.patches[i].flags |= PATCH_LINEARMAPPING; } } // Ready the patch representation! if( !pmesh.buildLinkages() ) { assert(0); } pmesh.computeInteriors(); }