#include "stdafx.h" #include "nel_patch_paint.h" #include "paint_vcolor.h" #include "paint_undo.h" #include "paint_to_nel.h" #include "nel/3d/landscape.h" #include "nel/3d/nelu.h" // User color 1 and 2 extern COLORREF color1; extern COLORREF color2; extern float opa1; extern float opa2; extern float hard1; extern float hard2; CRGBA maxToNel (COLORREF ref); /*-------------------------------------------------------------------*/ float distance (PaintPatchMod *pobj) { return COLOR_BRUSH_MIN+(COLOR_BRUSH_MAX-COLOR_BRUSH_MIN)*(float)pobj->ColorBushSize/(float)COLOR_BRUSH_STEP; } /*-------------------------------------------------------------------*/ void CPaintColor::paint (int mesh, int tile, const CVector& hit, const CVector& topVector, std::vector &vectMesh) { // Set the brush base vector if (fabs (topVector * CVector::K) > fabs (topVector * CVector::J)) { _PaintBaseX = CVector::J ^ topVector; _PaintBaseX.normalize (); _PaintBaseY = topVector ^ _PaintBaseX; _PaintBaseY.normalize (); } else { _PaintBaseX = topVector ^ CVector::K; _PaintBaseX.normalize (); _PaintBaseY = topVector ^ _PaintBaseX; _PaintBaseY.normalize (); } // Only if the landscape is valid if (_Landscape) { // Set of visited tiles std::set visited; // Nel patch changement manager CNelPatchChanger nelPatchChg (_Landscape); // Frozen vertex color static std::vector frozenVertices; frozenVertices.reserve (100); frozenVertices.clear (); // Start at the first tile paintATile (&_MouseProc->metaTile[mesh][tile], visited, hit, vectMesh, nelPatchChg, frozenVertices); // Force frozen color vertex forceFrozen (frozenVertices, nelPatchChg, vectMesh); // Flush nel chgt nelPatchChg.applyChanges (false); } } /*-------------------------------------------------------------------*/ bool CPaintColor::getVertexInNeighbor (EPM_PaintTile *pTile, int curU, int curV, int neighbor, int &finalMesh, int &finalPatch, int &finalS, int &finalT) { // Get the vertex id /* 0 3 x***x * * x***x 1 2 */ static const int remap[4] = { 0, 3, 1, 2}; int vertexId = (curU - pTile->u) + (curV - pTile->v) * 2; nlassert (vertexId<4); vertexId = remap[vertexId]; // Check neighbor asked is valid /* 3 x***x 0* *2 x***x 1 */ nlassert (( vertexId == neighbor ) || ( vertexId == ((neighbor+1)&3) )); // Neighbor empty ? if (pTile->voisins[neighbor] == NULL) return false; // Index of the vertex in the neighbor int neighborVertexId = (((vertexId == neighbor)?vertexId-1:vertexId+1) + pTile->rotate[neighbor])&3; // ** Fill the result // Final mesh finalMesh = pTile->voisins[neighbor]->Mesh; // Final patch finalPatch = pTile->voisins[neighbor]->patch; // Final s coordinate finalS = pTile->voisins[neighbor]->u + ( ( (neighborVertexId==2) || (neighborVertexId==3) )?1:0 ); // Final t coordinate finalT = pTile->voisins[neighbor]->v + ( ( (neighborVertexId==1) || (neighborVertexId==2) )?1:0 ); // Ok return true; } /*-------------------------------------------------------------------*/ void CPaintColor::paintATile (EPM_PaintTile *pTile, std::set& visited, const CVector& hit, std::vector &vectMesh, CNelPatchChanger& nelPatchChg, std::vector &frozenVertices) { // Check some args nlassert (_PObj->brushSize>=0); nlassert (_PObj->brushSize<3); // Already visited ? if (visited.find (pTile)!=visited.end()) return; // Check if we are in patch subobject and if this patch is selected if ((vectMesh[pTile->Mesh].PMesh->selLevel==EP_PATCH)&&(!vectMesh[pTile->Mesh].PMesh->patchSel[pTile->patch])) return; // Check if this tile is in the range of the brush if ((pTile->Center-hit).norm()>distance (_PObj)+pTile->Radius) return; // Is locked on left or top ? //if (isLocked (_PObj, pTile, (1<<0)|(1<<3))) // Is frozen ? bool frozen = pTile->frozen; // Paint the upper left vertex if (!frozen) { // Is left or top frozen ? bool leftFrozen = (pTile->voisins[0]!=NULL) && pTile->voisins[0]->frozen; bool bottomFrozen = (pTile->voisins[1]!=NULL) && pTile->voisins[1]->frozen; bool rightFrozen = (pTile->voisins[2]!=NULL) && pTile->voisins[2]->frozen; bool topFrozen = (pTile->voisins[3]!=NULL) && pTile->voisins[3]->frozen; // Is left or top locked ? bool leftLocked = ((pTile->voisins[0]==NULL) || leftFrozen) && _PObj->lockBorders; bool bottomLocked = ((pTile->voisins[1]==NULL) || bottomFrozen) && _PObj->lockBorders; bool rightLocked = ((pTile->voisins[2]==NULL) || rightFrozen) && _PObj->lockBorders; bool topLocked = ((pTile->voisins[3]==NULL) || topFrozen) && _PObj->lockBorders; // Color to copy CRGBA colorCopy; // Not locked ? if (!leftLocked && !topLocked) { // Is this vertex frozen ? if (leftFrozen || topFrozen) frozenVertices.push_back (CVertexColorFrozed (pTile, pTile->u, pTile->v)); // Set the vertex color paintAVertex (pTile->Mesh, pTile->patch, pTile->u, pTile->v, hit, vectMesh, nelPatchChg); } // Tile on a right, bottom or right bottom border ? bool borderRight=pTile->u == (1<Mesh].RMesh->getUIPatch (pTile->patch).NbTilesU)-1; bool borderBottom=pTile->v == (1<Mesh].RMesh->getUIPatch (pTile->patch).NbTilesV)-1; if (borderRight) { // Not locked ? if (!rightLocked && !topLocked) { // Is this vertex frozen ? if (rightFrozen || topFrozen) frozenVertices.push_back (CVertexColorFrozed (pTile, pTile->u+1, pTile->v)); // Set the vertex color paintAVertex (pTile->Mesh, pTile->patch, pTile->u+1, pTile->v, hit, vectMesh, nelPatchChg); } } if (borderBottom) { // Not locked ? if (!leftLocked && !bottomLocked) { // Is this vertex frozen ? if (leftFrozen || bottomFrozen) frozenVertices.push_back (CVertexColorFrozed (pTile, pTile->u, pTile->v+1)); // Set the vertex color paintAVertex (pTile->Mesh, pTile->patch, pTile->u, pTile->v+1, hit, vectMesh, nelPatchChg); } } if (borderRight&&borderBottom) { // Not locked ? if (!rightLocked && !bottomLocked) { // Is this vertex frozen ? if (rightFrozen || bottomFrozen) frozenVertices.push_back (CVertexColorFrozed (pTile, pTile->u+1, pTile->v+1)); // Set the vertex color paintAVertex (pTile->Mesh, pTile->patch, pTile->u+1, pTile->v+1, hit, vectMesh, nelPatchChg); } } } // Visited visited.insert (pTile); // Visite the neighborhood for (int neightbor=0; neightbor<4; neightbor++) { // Is there a neighbor ? if (pTile->voisins[neightbor]) // Ok, recusive call paintATile (pTile->voisins[neightbor], visited, hit, vectMesh, nelPatchChg, frozenVertices); } } /*-------------------------------------------------------------------*/ void CPaintColor::paintAVertex (int mesh, int patch, int s, int t, const CVector& hit, std::vector &vectMesh, CNelPatchChanger& nelPatchChg) { // Check some args nlassert (_PObj->brushSize>=0); nlassert (_PObj->brushSize<3); // Get the zone for this mesh const CZone* zone=_Landscape->getZone (mesh); nlassert (zone); // Get the patch pointer const CPatch *pPatch=zone->getPatch(patch); // Eval the vertex position CVector vertexPos=pPatch->computeVertex ((float)s/(float)pPatch->getOrderS(), (float)t/(float)pPatch->getOrderT()); // Compute dist from the brush CVector deltaPos = vertexPos-hit; float distBrush = deltaPos.norm(); // Brush size float brushSize = distance (_PObj); // Check if it is in distance if (distBrush<=brushSize) { // *** Compute new vertex color // Get the old value CRGBA old; vectMesh[mesh].RMesh->getVertexColor (patch, s, t, old); // Blend with distance float blendDist=(brushSize-distBrush)/brushSize; // Blend the two colors float finalFactor=256.f*opa1*((1.f-hard1)*blendDist+hard1); uint16 blend=(uint16)(std::max (std::min (finalFactor, 256.f), 0.f) ); // The color CRGBA theColor = maxToNel (color1); // Use a brush ? if (_bBrush) { // Compute the projection on the brush plane float bitmapX = (1 + (_PaintBaseX * deltaPos) / brushSize) / 2; float bitmapY = (1 + (_PaintBaseY * deltaPos) / brushSize) / 2; // Read the pixel CRGBAF colorF = _BrushBitmap.getColor (bitmapX, bitmapY); colorF *= 255.f; CRGBA color; color.R = (uint8)colorF.R; color.G = (uint8)colorF.G; color.B = (uint8)colorF.B; color.A = (color.R + color.G + color.B ) / 3; // Adjust color and blend blend = (uint16)(blend * color.A / 255); } // Set the vertex color setVertexColor (mesh, patch, s, t, theColor, blend, vectMesh, nelPatchChg, true); } } /*-------------------------------------------------------------------*/ void CPaintColor::pickVertexColor (int mesh, int patch, int s, int t, CVector& pos, CRGBA& color, std::vector &vectMesh) { // Check some args nlassert (_PObj->brushSize>=0); nlassert (_PObj->brushSize<3); // Get the zone for this mesh const CZone* zone=_Landscape->getZone (mesh); nlassert (zone); // Get the patch pointer const CPatch *pPatch=zone->getPatch(patch); // Eval the vertex position pos=pPatch->computeVertex ((float)s/(float)pPatch->getOrderS(), (float)t/(float)pPatch->getOrderT()); // Get the old value vectMesh[mesh].RMesh->getVertexColor (patch, s, t, color); } /*-------------------------------------------------------------------*/ void CPaintColor::setVertexColor (int mesh, int patch, int s, int t, const CRGBA& newColor, uint16 blend, std::vector &vectMesh, CNelPatchChanger& nelPatchChg, bool undo) { // Get the old value CRGBA old; vectMesh[mesh].RMesh->getVertexColor (patch, s, t, old); if (undo) { // Create an undo element CUndoElement elmt (mesh, CUndoStruct (patch, s, t, old, 256), CUndoStruct (patch, s, t, newColor, blend)); // Put an undo entry _Undo->toUndo (elmt); } // Get the Nel patch mesh RPatchMesh *pMesh=vectMesh[mesh].RMesh; // Blend color CRGBA color; color.blendFromui ( old, newColor, blend ); // Get order S of this patch int OrderS=(1<getUIPatch (patch).NbTilesU)+1; // For each mesh for (uint i=0; i& copyZone = *nelPatchChg.getColorArray (i, patch); // Set the color in if (vectMesh[i].Symmetry) { uint newS = OrderS - s - 1; copyZone[newS+t*OrderS].Color565=color.get565(); } else copyZone[s+t*OrderS].Color565=color.get565(); } } // 3dsmax update vectMesh[mesh].RMesh->setVertexColor (patch, s, t, color); } /*-------------------------------------------------------------------*/ bool CPaintColor::loadBrush (const char *brushFileName) { // Open the file try { // Open the bitmap CIFile inputFile; if (inputFile.open (brushFileName)) { // Read it in RGBA if (_BrushTexture == NULL) _BrushTexture = new CTextureFile(); _BrushTexture->loadGrayscaleAsAlpha (false); _BrushBitmap.loadGrayscaleAsAlpha (false); _BrushTexture->setFileName (brushFileName); _BrushBitmap.load (inputFile); // Convert in RGBA _BrushBitmap.convertToType (CBitmap::RGBA); } else { // Error message char msg[512]; smprintf (msg, 512, "Can't open the file %s.", brushFileName); MessageBox ((HWND)CNELU::Driver->getDisplay(), msg, "NeL Painter", MB_OK|MB_ICONEXCLAMATION); // Return false return false; } } catch (Exception &e) { // Error message MessageBox ((HWND)CNELU::Driver->getDisplay(), e.what(), "NeL Painter", MB_OK|MB_ICONEXCLAMATION); // Return false return false; } // Ok return true; } /*-------------------------------------------------------------------*/ void CPaintColor::forceFrozen (const std::vector &frozenVertices, CNelPatchChanger& nelPatchChg, std::vector &vectMesh) { // 1. Referenced frozen vertices will copy their color in non frozen color uint vertex; for (vertex=0; vertexMesh].RMesh) && (mesh != (uint)vertexFrozen.Tile->Mesh) ) { // Reference tile pointer EPM_PaintTile *refTile = &_MouseProc->metaTile[mesh][vertexFrozen.Tile->tile]; nlassert (refTile != vertexFrozen.Tile); // Neighbor frozen bool neighborNotFrozen[4]; uint n; for (n=0; n<4; n++) // Flag neighborNotFrozen[n] = (refTile->voisins[n] != NULL) && (!refTile->voisins[n]->frozen); // Get the vertex id static const int remap[4] = { 0, 3, 1, 2}; uint vertexId = (vertexFrozen.S - refTile->u) + (vertexFrozen.T - refTile->v) * 2; nlassert (vertexId<4); vertexId = remap[vertexId]; // Choose the neighbor uint neighbor = 0xffffffff; if (neighborNotFrozen[vertexId]) { neighbor = vertexId; } else if (neighborNotFrozen[(vertexId-1)&3]) { // Should be the other one neighbor = (vertexId-1)&3; } // Find ? if (neighbor != 0xffffffff) { // Get the source color CRGBA color; vectMesh[mesh].RMesh->getVertexColor (refTile->patch, vertexFrozen.S, vertexFrozen.T, color); // Get the corresponding vertex int finalMesh, finalPatch, finalS, finalT; nlverify (getVertexInNeighbor (refTile, vertexFrozen.S, vertexFrozen.T, neighbor, finalMesh, finalPatch, finalS, finalT)); // Set the vertex color setVertexColor (finalMesh, finalPatch, finalS, finalT, color, 255, vectMesh, nelPatchChg, true); } } } } // 2. Non frozen vertices will get their color from the frozen vertices for (vertex=0; vertexvoisins[n] != NULL) && vertexFrozen.Tile->voisins[n]->frozen; // Get the vertex id static const int remap[4] = { 0, 3, 1, 2}; uint vertexId = (vertexFrozen.S - vertexFrozen.Tile->u) + (vertexFrozen.T - vertexFrozen.Tile->v) * 2; nlassert (vertexId<4); vertexId = remap[vertexId]; // Choose the neighbor uint neighbor; if (neighborFrozen[vertexId]) { // This neighbor neighbor = vertexId; } else { // Should be the other one nlassert (neighborFrozen[(vertexId-1)&3]); neighbor = (vertexId-1)&3; } // Get the frozen neighbor int sourceMesh, sourcePatch, sourceS, sourceT; nlverify (getVertexInNeighbor (vertexFrozen.Tile, vertexFrozen.S, vertexFrozen.T, neighbor, sourceMesh, sourcePatch, sourceS, sourceT)); // Get the source color CRGBA color; vectMesh[sourceMesh].RMesh->getVertexColor (sourcePatch, sourceS, sourceT, color); // Set the vertex color setVertexColor (vertexFrozen.Tile->Mesh, vertexFrozen.Tile->patch, vertexFrozen.S, vertexFrozen.T, color, 255, vectMesh, nelPatchChg, true); } } /*-------------------------------------------------------------------*/