#include "stdafx.h" #include "nel_patch_paint.h" #include "resource.h" #include "nel/3d/scene.h" #include "nel/3d/camera.h" #include "nel/3d/nelu.h" #include "nel/3d/light.h" #include "nel/3d/landscape_model.h" #include "nel/3d/landscape.h" #include "nel/3d/event_mouse_listener.h" #include "nel/3d/dru.h" #include "nel/3d/texture_mem.h" #include "nel/3d/transform_shape.h" #include "nel/3d/zone_corner_smoother.h" #include "nel/3d/zone_symmetrisation.h" #include "nel/misc/vector.h" #include "nel/misc/event_server.h" #include "nel/misc/event_listener.h" #include "nel/misc/events.h" #include "nel/misc/file.h" #include "nel/ligo/ligo_config.h" #include "paint_ui.h" #include "paint_vcolor.h" #include "paint_to_nel.h" #include "paint_fill.h" #include "paint_tileset.h" #include "paint_light.h" #include "../nel_mesh_lib/export_nel.h" using namespace NL3D; using namespace NLMISC; using namespace NLLIGO; // Paint mouse proc #define MAIN_Width 800 #define MAIN_Height 600 #define MOD_WIDTH 8 #define MOD_HEIGHT 6 #define TILE_SIZE_COUNT 2 #define DEPTH_SEARCH_MAX 8 #define REGKEY_EDIT_PATCH "Software\\Nevrax\\Ryzom\\edit_patch" // Bank bitmaps CBankCont* bankCont; // Tileset land CTileSetSelection tileSetSelector; bool bWarningInvalidTileSet; CVector maxToNel (const Point3& p) { return CVector (p.x, p.y, p.z); } CRGBA maxToNel (COLORREF ref) { return CRGBA ((uint8)(ref&0xff), (uint8)((ref>>8)&0xff), (uint8)((ref>>16)&0xff)); } COLORREF nelToMax (CRGBA ref) { return RGB (ref.R, ref.G, ref.B); } void WarningInvalidTileSet () { if (!bWarningInvalidTileSet) { bWarningInvalidTileSet=true; MessageBox (NULL, "The tile bank is not compatible with your zone.\nPlease use the good bank or erase and repaint the zone.", "Tile paint", MB_OK|MB_ICONEXCLAMATION); } } int brushValue[BRUSH_COUNT]= { 0, 4, 8 }; // Alocate and load a bitmap ITexture* allocateAndLoadBitmap (uint8* bitmap, uint size) { return new CTextureMem (bitmap, size, false); } // Call when painter starts void enterPainter (CTileBank& banktoLoad) { // Load some tiles from the bank bankCont=new CBankCont (banktoLoad, hInstance); // Get background color HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_EDIT_PATCH, 0, KEY_READ, &hKey)==ERROR_SUCCESS) { DWORD len=4; DWORD type; RegQueryValueEx (hKey, "Background", 0, &type, (LPBYTE)&backGround, &len); RegQueryValueEx (hKey, "Color1", 0, &type, (LPBYTE)&color1, &len); RegQueryValueEx (hKey, "Color2", 0, &type, (LPBYTE)&color2, &len); RegQueryValueEx (hKey, "Opa1", 0, &type, (LPBYTE)&opa1, &len); RegQueryValueEx (hKey, "Opa2", 0, &type, (LPBYTE)&opa2, &len); RegQueryValueEx (hKey, "Hard1", 0, &type, (LPBYTE)&hard1, &len); RegQueryValueEx (hKey, "Hard2", 0, &type, (LPBYTE)&hard2, &len); RegCloseKey (hKey); } } // Call when painter quit void exitPainter () { // Delete some tiles from the bank delete bankCont; // Set background color HKEY hKey; if (RegCreateKey(HKEY_CURRENT_USER, REGKEY_EDIT_PATCH, &hKey)==ERROR_SUCCESS) { RegSetValueEx(hKey, "Background", 0, REG_DWORD, (LPBYTE)&backGround, 4); RegSetValueEx(hKey, "Color1", 0, REG_DWORD, (LPBYTE)&color1, 4); RegSetValueEx(hKey, "Color2", 0, REG_DWORD, (LPBYTE)&color2, 4); RegSetValueEx(hKey, "Opa1", 0, REG_DWORD, (LPBYTE)&opa1, 4); RegSetValueEx(hKey, "Opa2", 0, REG_DWORD, (LPBYTE)&opa2, 4); RegSetValueEx(hKey, "Hard1", 0, REG_DWORD, (LPBYTE)&hard1, 4); RegSetValueEx(hKey, "Hard2", 0, REG_DWORD, (LPBYTE)&hard2, 4); RegCloseKey (hKey); } } /*-------------------------------------------------------------------*/ std::vector symVector; /*-------------------------------------------------------------------*/ // Painter modes enum TModePaint { ModeTile, ModeColor, ModeDisplace}; enum TModeMouse { ModePaint, ModeSelect, ModePick, ModeFill, ModeGetState }; /*-------------------------------------------------------------------*/ // Draw GUI void drawVertexColorBox (float x0, float y0, float x1, float y1, CRGBA color, float opacity, float hardness, IDriver& driver) { // Draw a white frame behind CDRU::drawQuad (x0, y0, x1, y1, driver, CRGBA (0x40, 0x40, 0x40), CViewport()); // Compute the colors with alpha to show the hardness CRGBA fullFull=color; CRGBA full=color; full.A=(uint8)(opacity*255.f); CRGBA medium=color; medium.A=(uint8)(255.f*hardness*opacity); CRGBA empty=color; empty.A=(uint8)(255.f*std::max (std::min (opacity*(1.f+1.414213f*(hardness-1.f)), 1.f), 0.f)); // Center float xc=(x0+x1)/2.f; float yc=(y0+y1)/2.f; // Draw 4 quads if texture exist CDRU::drawQuad (x0, y0, xc, yc, empty, medium, full, medium, driver, CViewport()); CDRU::drawQuad (xc, y0, x1, yc, fullFull, fullFull, fullFull, fullFull, driver, CViewport()); CDRU::drawQuad (xc, yc, x1, y1, fullFull, fullFull, fullFull, fullFull, driver, CViewport()); CDRU::drawQuad (x0, yc, xc, y1, medium, full, medium, empty, driver, CViewport()); } // Draw GUI void drawInterface (TModeMouse select, TModePaint mode, PaintPatchMod *pobj, IDriver& driver, CLandscapeModel& landscape, CPaintColor &paintColor) { // Select a texture.. if (select==ModeSelect) { // Select a tileSet if (mode==ModeTile) { uint num=tileSetSelector.getTileCount (); for (uint t=0; tgetNumTile128()>0) { // Get a 128 tile number int nTile=bank.getTileSet (tileSet)->getTile128(0); nlassert (nTile!=-1); // Get a texture of this tile ITexture *texture=bankCont->TileSet[tileSet].MainBitmap; if (texture) CDRU::drawBitmap ( (float)((t+1)%MOD_WIDTH)/(float)MOD_WIDTH, (float)((t+1)/MOD_WIDTH)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *texture, driver, CViewport(), false); } } } // Select a tileSet else if (mode==ModeDisplace) { // No tileset if (pobj->DisplaceTileSet!=-1) { // A is a valid tile set selected ? for (uint t=0; tTileSet[pobj->DisplaceTileSet].DisplaceBitmap[t]; // Draw the texture if (texture) CDRU::drawBitmap ( (float)(t%MOD_WIDTH)/(float)MOD_WIDTH, (float)(t/MOD_WIDTH)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *texture, driver, CViewport(), false); else CDRU::drawQuad ( (float)(t%MOD_WIDTH)/(float)MOD_WIDTH, (float)(t/MOD_WIDTH)/(float)MOD_HEIGHT, (float)(t%MOD_WIDTH)/(float)MOD_WIDTH+1.f/(float)MOD_WIDTH, (float)(t/MOD_WIDTH)/(float)MOD_HEIGHT+1.f/(float)MOD_HEIGHT, driver, CRGBA (128, 128, 128), CViewport()); } } } } else { // Mode paint, draw the GUI // Automatic lighting ? if (pobj->automaticLighting) { // Get pointer ITexture *tex=bankCont->lightBitmap; // Draw the bitmap CDRU::drawBitmap ( (float)(MOD_WIDTH-1)/(float)MOD_WIDTH, (float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); } // Lock borders ? if (pobj->lockBorders) { // Get pointer ITexture *tex=bankCont->lockBitmap; // Draw the bitmap CDRU::drawBitmap ( (float)(MOD_WIDTH-1)/(float)MOD_WIDTH, (float)(MOD_HEIGHT-2)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); } // *** Draw the current tile if ((pobj->CurrentTileSet!=-1)&&(mode==ModeTile)) { // Draw the oriented icon if (bank.getTileSet (pobj->CurrentTileSet)->getOriented()) { // Get pointer ITexture *tex=bankCont->orientedBitmap; // Draw the bitmap CDRU::drawBitmap ( (float)(MOD_WIDTH-1)/(float)MOD_WIDTH, (float)(0)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); } if (pobj->TileGroup==0) { // Get pointer ITexture *tex=bankCont->TileSet[pobj->CurrentTileSet].MainBitmap; // Default: all tile are used if (tex) CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); } else { bool full=false; // 128 or 256 ? if (pobj->tileSize==0) { if (bankCont->TileSet[pobj->CurrentTileSet].GroupTile128[pobj->TileGroup-1].size()!=0) full=true; } else { if (bankCont->TileSet[pobj->CurrentTileSet].GroupTile256[pobj->TileGroup-1].size()!=0) full=true; } if (full) { // Get pointer ITexture *tex=bankCont->TileSet[pobj->CurrentTileSet].GroupBitmap[pobj->TileGroup-1]; // Show the tile from a group if (tex) CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); } } } // *** Draw the current noise if (mode==ModeDisplace) { if (pobj->DisplaceTileSet!=-1) { // Get pointer ITexture *tex=bankCont->TileSet[pobj->DisplaceTileSet].DisplaceBitmap[pobj->DisplaceTile]; // Draw if texture exist if (tex) CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *tex, driver, CViewport(), true); else { // 4 Corners float x0=0.f; float y0=(float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT; float x1=1.f/(float)MOD_WIDTH; float y1=1.f; CDRU::drawQuad (x0, y0, x1, y1, driver, CRGBA (128, 128, 128), CViewport()); } } } // *** Draw the current color if (mode==ModeColor) { // 4 Corners float x0=0.f; float y0=(float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT; float x1=1.f/(float)MOD_WIDTH; float y1=1.f; #define BORDER_COLOR (0.2f) // Draw second color drawVertexColorBox (x0+(x1-x0)*BORDER_COLOR, y0, x1, y1+(y0-y1)*BORDER_COLOR, maxToNel (color2), opa2, hard2, driver); // Draw first color drawVertexColorBox (x0, y0+(y1-y0)*BORDER_COLOR, x1+(x0-x1)*BORDER_COLOR, y1, maxToNel (color1), opa1, hard1, driver); } // *** Draw the current bursh size if ((mode==ModeTile)||(mode==ModeDisplace)) { ITexture* burshSize[BRUSH_COUNT]={bankCont->_smallBitmap, bankCont->mediumBitmap, bankCont->largeBitmap}; // Draw brush icon CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-3)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *(burshSize[pobj->brushSize]), driver, CViewport(), true); } else if (mode==ModeColor) { // 4 points float x0=0.f; float y0=(float)(MOD_HEIGHT-2)/(float)MOD_HEIGHT; float x1=1.f/(float)MOD_WIDTH; float y1=(float)(MOD_HEIGHT-1)/(float)MOD_HEIGHT; // Center float xc=(x0+x1)/2.f; float yc=(y0+y1)/2.f; // Scale factor float scaleFactor=0.9f*(float)PaintPatchMod::ColorBushSize/(float)COLOR_BRUSH_STEP+0.1f; clamp (scaleFactor, 0.f, 1.f); // Scale it x0=(x0-xc)*scaleFactor+xc; y0=(y0-yc)*scaleFactor+yc; x1=(x1-xc)*scaleFactor+xc; y1=(y1-yc)*scaleFactor+yc; // Draw brush icon CDRU::drawBitmap ( x0, y0, x1-x0, y1-y0, *bankCont->largeBitmap, driver, CViewport(), true); } // For tiles only if (mode==ModeTile) { // *** Draw the current size ITexture* size[2]={bankCont->_128Bitmap, bankCont->_256Bitmap}; // Draw brush icon CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-2)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *(size[pobj->tileSize]), driver, CViewport(), true); } if ((pobj->CurrentTileSet!=-1)&&(mode==ModeTile)) { // *** Draw the current group ITexture* group[NL3D_CTILE_NUM_GROUP+1]={bankCont->allBitmap, bankCont->_0Bitmap, bankCont->_1Bitmap, bankCont->_2Bitmap, bankCont->_3Bitmap, bankCont->_4Bitmap, bankCont->_5Bitmap, bankCont->_6Bitmap, bankCont->_7Bitmap, bankCont->_8Bitmap, bankCont->_9Bitmap, bankCont->_10Bitmap, bankCont->_11Bitmap}; // Draw group icon CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-4)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *(group[pobj->TileGroup]), driver, CViewport(), true); } // For color only if (paintColor.getBrushMode ()&&(mode==ModeColor)) { // Draw brush icon CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-3)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, paintColor.getBrush (), driver, CViewport(), false); } // For color only if (pobj->ShowCurrentState && ((int)pobj->CurrentState<3) && ((int)pobj->CurrentState>=0)) { // Draw brush icon ITexture* bitmaps[3]={bankCont->nothingBitmap, bankCont->regularBitmap, bankCont->goofyBitmap}; CDRU::drawBitmap ( 0, (float)(MOD_HEIGHT-5)/(float)MOD_HEIGHT, 1.f/(float)MOD_WIDTH, 1.f/(float)MOD_HEIGHT, *(bitmaps[(int)pobj->CurrentState]), driver, CViewport(), true); } } } /*-------------------------------------------------------------------*/ #define WELD_THRESOLD 1.f /*-------------------------------------------------------------------*/ void addNeLZone ( Object *object, INode *current, std::vector &vectMesh, PatchMesh *patch, RPatchMesh *rpatch, PaintPatchData *patchData, ModContext *mod, INode *original, uint &nelIndex) { // Not the same if ((current->GetObjectRef() == object) && (!current->IsHidden())) { vectMesh.push_back (EPM_Mesh (patch, rpatch, patchData, current, mod, nelIndex++, original == current)); } // Call to child for (uint child=0; child<(uint)current->NumberOfChildren(); child++) addNeLZone ( object, current->GetChildNode(child), vectMesh, patch, rpatch, patchData, mod, original, nelIndex); } // Tile painting algorithm. watch "doc/3d/tile_algorithm.doc" for details void makeVectMesh (std::vector& vectMesh, INodeTab& nodes, ModContextList& mcList, PaintPatchMod *pobj, TimeValue t) { // Clear the mesh vector vectMesh.clear (); // Nel zone indexies uint nelIndex = 0; for (int i = 0; i < mcList.Count (); i++) { // PaintPatchData *patchData = (PaintPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(pobj)->GetPatch(t, rpatch); if (!patch) continue; // Look for another node addNeLZone ( nodes[i]->GetObjectRef(), pobj->ip->GetRootNode(), vectMesh, patch, rpatch, patchData, mcList[i], nodes[i], nelIndex ); } } void transformDesc (tileDesc &desc, bool symmetry, uint rotate, uint mesh, uint tile, std::vector& vectMesh) { uint patch=tile/NUM_TILE_SEL; uint ttile=tile%NUM_TILE_SEL; uint v=ttile/MAX_TILE_IN_PATCH; uint u=ttile%MAX_TILE_IN_PATCH; // Get the patch int OrderS=(1<getUIPatch (patch).NbTilesU); uint symTile = OrderS*v+u; // 256 ? if (desc.getCase()!=0) { // Transform the case uint8 case256 = desc.getCase()-1; // Get rot and symmetry for this tile uint tileRotate = rotate; bool tileSymmetry = symmetry; uint tile = desc.getLayer (0).Tile; // XRef int tileSet; int number; CTileBank::TTileType type; bank.getTileXRef (tile, tileSet, number, type); // Transform the transfo CPatchInfo::getTileSymmetryRotate (bank, desc.getLayer(0).Tile, tileSymmetry, tileRotate); // Get the state of the layer 0 bool goofy = false; uint tileRotation = desc.getLayer (0).Rotate; if (symmetry) { if (bank.getTileSet (tileSet)->getOriented ()) { if ((symVector[mesh].getOrientedTileBorderState (patch, symTile) != CZoneSymmetrisation::Nothing)) goofy = (symVector[mesh].getOrientedTileBorderState (patch, symTile) == CZoneSymmetrisation::Goofy); else goofy = (symVector[mesh].getTileState (patch, symTile, 0) == CZoneSymmetrisation::Goofy); } else { if ((symVector[mesh].getTileBorderState (patch, symTile) != CZoneSymmetrisation::Nothing)) goofy = (symVector[mesh].getTileBorderState (patch, symTile) == CZoneSymmetrisation::Goofy); else goofy = (symVector[mesh].getTileState (patch, symTile, 0) == CZoneSymmetrisation::Goofy); } } CPatchInfo::transform256Case (bank, case256, tileRotation, tileSymmetry, tileRotate, goofy); desc.setCase (case256+1); } for (int l=0; lgetOriented ()) { if ((symVector[mesh].getOrientedTileBorderState (patch, symTile) != CZoneSymmetrisation::Nothing)) goofy = (symVector[mesh].getOrientedTileBorderState (patch, symTile) == CZoneSymmetrisation::Goofy); else goofy = (symVector[mesh].getTileState (patch, symTile, l) == CZoneSymmetrisation::Goofy); } else { if ((symVector[mesh].getTileBorderState (patch, symTile) != CZoneSymmetrisation::Nothing)) goofy = (symVector[mesh].getTileBorderState (patch, symTile) == CZoneSymmetrisation::Goofy); else goofy = (symVector[mesh].getTileState (patch, symTile, l) == CZoneSymmetrisation::Goofy); } } // Transform the transfo if (CPatchInfo::getTileSymmetryRotate (bank, tile, tileSymmetry, tileRotate)) { // Transform it if (CPatchInfo::transformTile (bank, tile, tileRotation, tileSymmetry, tileRotate, goofy)) { desc.getLayer (l).Tile = tile; desc.getLayer (l).Rotate = tileRotation; } } } } void transformInvDesc (tileDesc &desc, bool symmetry, uint rotate, uint mesh, uint tile, std::vector& vectMesh) { transformDesc (desc, false, 4-rotate, mesh, tile, vectMesh); transformDesc (desc, symmetry, 0, mesh, tile, vectMesh); } void EPM_PaintMouseProc::SetTile (int mesh, int tile, const tileDesc& desc, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg, std::vector* backupStack, bool undo, bool updateDisplace) { // Undo: get old value tileDesc oldDesc; GetTile (mesh, tile, oldDesc, vectMesh, land); // New desc tileDesc maxDesc=desc; // Backup if error if (backupStack) backupStack->push_back (CBackupValue (oldDesc, mesh, tile)); // Update displace ? if (!updateDisplace) { // Copy old displacement map maxDesc.setDisplace (oldDesc.getDisplace()); } // Transform tile tileDesc copyDesc=maxDesc; transformInvDesc (maxDesc, vectMesh[mesh].Symmetry, 4-vectMesh[mesh].Rotate, mesh, tile, vectMesh); transformInvDesc (copyDesc, vectMesh[mesh].Symmetry, 0, mesh, tile, vectMesh); // 3dsmax update vectMesh[mesh].RMesh->setTileDesc (tile, maxDesc); // NeL update if (land) { // For each mesh for (uint i=0; i& copyZone = *nelPatchChg.getTileArray (i, patch); RPatchMesh *pMesh=vectMesh[i].RMesh; int OrderS=(1<getUIPatch (patch).NbTilesU); // Get a desc for this tile tileDesc nelDesc=copyDesc; transformInvDesc (nelDesc, vectMesh[mesh].Symmetry, 4-vectMesh[mesh].Rotate, mesh, tile, vectMesh); transformDesc (nelDesc, vectMesh[i].Symmetry, 4-vectMesh[i].Rotate, mesh, tile, vectMesh); // Symmetry ? if (vectMesh[i].Symmetry) { u = OrderS - u - 1; } for (int l=0; l<3; l++) { if (l>=nelDesc.getNumLayer ()) { copyZone[u+v*OrderS].Tile[l]=0xffff; } else { int toto=copyZone[u+v*OrderS].Tile[l]; copyZone[u+v*OrderS].Tile[l]=nelDesc.getLayer (l).Tile; copyZone[u+v*OrderS].setTileOrient (l, nelDesc.getLayer (l).Rotate); } } if (copyZone[u+v*OrderS].Tile[0]==0xffff) copyZone[u+v*OrderS].setTile256Info (false, 0); else { if (nelDesc.getCase()==0) copyZone[u+v*OrderS].setTile256Info (false, 0); else copyZone[u+v*OrderS].setTile256Info (true, nelDesc.getCase()-1); } // Displace tile copyZone[u+v*OrderS].setTileSubNoise (nelDesc.getDisplace()); // Undo: push new value if (undo) { CUndoElement undoEmlt (i, tile, CUndoStruct (oldDesc), CUndoStruct (desc)); bankCont->Undo.toUndo (undoEmlt); } } } } } void EPM_PaintMouseProc::GetTile (int mesh, int tile, tileDesc& desc, std::vector& vectMesh, CLandscape* land) { // NeL update if (land) { // Get the patch RPatchMesh *pMesh=vectMesh[mesh].RMesh; // Get tile desc desc=pMesh->getTileDesc (tile); // Transform it transformDesc (desc, vectMesh[mesh].Symmetry, 4-vectMesh[mesh].Rotate, mesh, tile, vectMesh); } } int EPM_PaintMouseProc::selectTile (uint tileSet, bool selectCycle, bool _256, uint group, const CTileBank& bank) { // TileSet const CTileSet *TileSet=bank.getTileSet (tileSet); // The number int nTile; // Calc next tile index uint32 index; if (selectCycle) // Select a tile in a cycle index=TileIndex++; else // Select a random tile index=(uint32)rand(); if (_256) { // Select in a group ? if (group==0) { // No, select in the global list nTile=TileSet->getTile256 (index%(uint32)TileSet->getNumTile256 ()); } else { // Ref on the group tile array const std::vector& groupArray=bankCont->TileSet[tileSet].GroupTile256[group-1]; // Check there is tiles in this group if (groupArray.size()==0) nTile=-1; else // No, select in the global list nTile=TileSet->getTile256 (groupArray[index%(uint32)groupArray.size ()]); } } else { // Select in a group ? if (group==0) { // No, select in the global list nTile=TileSet->getTile128 (index%(uint32)TileSet->getNumTile128 ()); } else { // Ref on the group tile array const std::vector& groupArray=bankCont->TileSet[tileSet].GroupTile128[group-1]; // Check there is tiles in this group if (groupArray.size()==0) nTile=-1; else // No, select in the global list nTile=TileSet->getTile128 (groupArray[index%(uint32)groupArray.size ()]); } } return nTile; } bool EPM_PaintMouseProc::isLockedEx (PaintPatchMod *pobj, EPM_PaintTile* pTile, std::vector& vectMesh, CLandscape* land) { // Lock border ? if (pobj->lockBorders) { // get the tile desc tileDesc backup; GetTile (pTile->Mesh, pTile->tile, backup, vectMesh, land); // It is a 256 ? if (backup.getCase()>0) { // Align on the grid if (pTile->u&1) pTile=pTile->voisins[0]; if (pTile->v&1) pTile=pTile->voisins[3]; // Return status int nRot; return (pTile->locked!=0) || (pTile->getRight256 (0, nRot)->locked!=0) || (pTile->getBottom256 (0, nRot)->locked!=0) || (pTile->getRightBottom256 (0, nRot)->locked!=0); } else // Return lock status return (pTile->locked!=0); } // Not locked return false; } bool EPM_PaintMouseProc::isLocked256 (PaintPatchMod *pobj, EPM_PaintTile* pTile) { // Lock border ? if (pobj->lockBorders) { // Align on the grid if (pTile->u&1) pTile=pTile->voisins[0]; if (pTile->v&1) pTile=pTile->voisins[3]; // Return status return (pTile->locked!=0) || (pTile->voisins[2]->locked!=0) || (pTile->voisins[1]->locked!=0) || (pTile->voisins[2]->voisins[1]->locked!=0); } // Not locked return false; } bool EPM_PaintMouseProc::ClearATile ( EPM_PaintTile* pTile, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg, bool _256, bool _force128) { // ** 1) Backup of the tile tileDesc backup; GetTile (pTile->Mesh, pTile->tile, backup, vectMesh, land); // If we are over a 256, force mode 256 if ((backup.getCase()>0) && !_force128) _256 = true; // If tile 256, must have delta pos aligned on 2x2 if (_256) { if (pTile->u&1) pTile=pTile->voisins[0]; if (pTile->v&1) pTile=pTile->voisins[3]; } // Erase a 256 area ? if (_256) { tileDesc desc; desc.setTile (0, 0, 0, tileIndex (0,0), tileIndex (0,0), tileIndex (0,0)); // Get right tile int nRot; EPM_PaintTile *neighbor[4] = { pTile, pTile->getRight256 (0, nRot), pTile->getBottom256 (0, nRot), pTile->getRightBottom256 (0, nRot) }; // Check not locked uint n; for (n=0; n<4; n++) { // Ok ? nlassert (neighbor[n]); // Locked ? if (isLocked (pobj, neighbor[n])) return false; } // Clear them for (n=0; n<4; n++) { // Get the displace index tileDesc descOrig; GetTile (neighbor[n]->Mesh, neighbor[n]->tile, descOrig, vectMesh, land); // Not compatible, clear it desc.setDisplace (descOrig.getDisplace()); SetTile (neighbor[n]->Mesh, neighbor[n]->tile, desc, vectMesh, land, nelPatchChg, NULL); } } else { // Check locked if (isLocked (pobj, pTile)) return false; // Cleared descriptor tileDesc desc; desc.setTile (0, 0, 0, tileIndex (0,0), tileIndex (0,0), tileIndex (0,0)); // Get the displace index tileDesc descOrig; GetTile (pTile->Mesh, pTile->tile, descOrig, vectMesh, land); // Not compatible, clear it desc.setDisplace (descOrig.getDisplace()); SetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land, nelPatchChg, NULL); } // Erased return true; } bool EPM_PaintMouseProc::PutATile ( EPM_PaintTile* pTile, int tileSet, int curRotation, const CTileBank& bank, bool selectCycle, std::set& visited, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg, bool _256) { // If tile 256, must have delta pos aligned on 2x2 if (_256) { if (pTile->u&1) pTile=pTile->voisins[0]; if (pTile->v&1) pTile=pTile->voisins[3]; } // 256 valide ? if ((_256)&&(!pTile->validFor256 (0))) return false; // Frozen ? if (pTile->frozen) return false; // Locked ? if (isLocked (pobj, pTile)) return false; // *** Clip select patch // Patch number int patch=pTile->tile/NUM_TILE_SEL; // 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[patch])) return false; // ** 1) Backup of the tile tileDesc backup; GetTile (pTile->Mesh, pTile->tile, backup, vectMesh, land); tileDesc backupRight, backupBottom, backupCorner; // Backup stack static std::vector backupStack; backupStack.reserve (300); backupStack.resize(0); if (_256) { // get right int nRot; EPM_PaintTile* other=pTile->getRight256 (0, nRot); nlassert (other); if (isLocked (pobj, other)) return false; GetTile (other->Mesh, other->tile, backupRight, vectMesh, land); // get bottom other=pTile->getBottom256 (0, nRot); nlassert (other); if (isLocked (pobj, other)) return false; GetTile (other->Mesh, other->tile, backupBottom, vectMesh, land); // get corner other=pTile->getRightBottom256 (0, nRot); nlassert (other); nlassert (other==pTile->getBottomRight256 (0, nRot)); if (isLocked (pobj, other)) return false; GetTile (other->Mesh, other->tile, backupCorner, vectMesh, land); } // ** 2) Add to visited tile set visited.insert (pTile); // Clear... if (tileSet==-1) { return ClearATile ( pTile, vectMesh, land, nelPatchChg, _256); } else { // Select a tile int nTile=selectTile (tileSet, selectCycle, _256, pobj->TileGroup, bank); // No tile, return if (nTile==-1) return false; // ** 3) Put the tile if (_256) { // Main tile tileDesc desc; desc.setTile (1, 1+((-curRotation)&3), 0, tileIndex (nTile, curRotation), tileIndex (0,0), tileIndex (0,0)); // Main tile SetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land, nelPatchChg, &backupStack); // get right int nRot; EPM_PaintTile* other=pTile->getRight256 (0, nRot); desc.setTile (1, 1+((-curRotation-1)&3), 0, tileIndex (nTile, (curRotation-nRot)&3), tileIndex (0,0), tileIndex (0,0)); nlassert (other); SetTile (other->Mesh, other->tile, desc, vectMesh, land, nelPatchChg, &backupStack); // Add to visited tile set visited.insert (other); // get bottom other=pTile->getBottom256 (0, nRot); desc.setTile (1, 1+((-curRotation+1)&3), 0, tileIndex (nTile, (curRotation-nRot)&3), tileIndex (0,0), tileIndex (0,0)); nlassert (other); SetTile (other->Mesh, other->tile, desc, vectMesh, land, nelPatchChg, &backupStack); // Add to visited tile set visited.insert (other); // get corner other=pTile->getRightBottom256 (0, nRot); desc.setTile (1, 1+((-curRotation+2)&3), 0, tileIndex (nTile, (curRotation-nRot)&3), tileIndex (0,0), tileIndex (0,0)); nlassert (other); nlassert (other==pTile->getBottomRight256 (0, nRot)); SetTile (other->Mesh, other->tile, desc, vectMesh, land, nelPatchChg, &backupStack); // Add to visited tile set visited.insert (other); } else { tileDesc desc; desc.setTile (1, 0, 0, tileIndex (nTile, curRotation), tileIndex (0,0), tileIndex (0,0)); SetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land, nelPatchChg, &backupStack); } } // return value bool bContinue=true; // Random offset uint offset=rand(); if (_256) { // Visit neighbourhood in a random order for (int n=0; n<4; n++) { int nRot; EPM_PaintTile* other; switch ((offset+n)&0x3) { case 0: // Main if (pTile->voisins[3]) if (!PropagateBorder (pTile->voisins[3], (pTile->rotate[3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } if (pTile->voisins[0]) if (!PropagateBorder (pTile->voisins[0], (pTile->rotate[0]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } break; case 1: // Bottom other=pTile->getBottom256 (0, nRot); if (other->voisins[(0-nRot)&3]) if (!PropagateBorder (other->voisins[(0-nRot)&3], (other->rotate[(0-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } if (other->voisins[(1-nRot)&3]) if (!PropagateBorder (other->voisins[(1-nRot)&3], (other->rotate[(1-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } break; case 2: // Corner other=pTile->getBottomRight256 (0, nRot); if (other->voisins[(1-nRot)&3]) if (!PropagateBorder (other->voisins[(1-nRot)&3], (other->rotate[(1-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } if (other->voisins[(2-nRot)&3]) if (!PropagateBorder (other->voisins[(2-nRot)&3], (other->rotate[(2-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } break; case 3: // Right other=pTile->getRight256 (0, nRot); if (other->voisins[(2-nRot)&3]) if (!PropagateBorder (other->voisins[(2-nRot)&3], (other->rotate[(2-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } if (other->voisins[(3-nRot)&3]) if (!PropagateBorder (other->voisins[(3-nRot)&3], (other->rotate[(3-nRot)&3]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; goto zap; } break; default: nlassert (0); // no! } } zap:; } else { // For all patches voisins for (int i=0; i<4; i++) { // Random offset uint ii=(offset+i)&0x3; // 4) Propagate the borders to voisins if (pTile->voisins[ii]) { if (!PropagateBorder (pTile->voisins[ii], (pTile->rotate[ii]+curRotation)&3, tileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack)) { bContinue=false; break; } } } } // Backup !! if (!bContinue) { // Backup all tiles for (int back=backupStack.size()-1; back>=0; back--) { // Yo! SetTile (backupStack[back].Mesh, backupStack[back].Tile, backupStack[back].Desc, vectMesh, land, nelPatchChg, NULL); } // Try to put a transition tile ? bool backup256 = (backup.getCase()>0); if (!_256 && !backup256) { // 4 cases // ***** // *0*3* // ***** // *1*2* // ***** tileSetIndex tileSetCases[4][4]; for (uint i=0; i<4; i++) for (uint j=0; j<4; j++) { tileSetCases[i][j].TileSet = -1; tileSetCases[i][j].Rotate = 0; } CTileSet::TFlagBorder borderEdges[4][2]; // For each edge for (uint edge=0; edge<4; edge++) { // Neighbor ? if (pTile->voisins[edge]) { // Get the neighbor corner tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; // Tile is filled ? if (GetBorderDesc (pTile->voisins[edge], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land)) { // Neighbor edge int neigborEdge = (2+edge+pTile->rotate[edge])&3; // Copy the tiles tileSetCases[edge][edge] = pVoisinCorner[(neigborEdge+1)&3]; tileSetCases[edge][edge].Rotate -= pTile->rotate[edge]; tileSetCases[edge][edge].Rotate &= 3; tileSetCases[edge][(edge+1)&3] = pVoisinCorner[neigborEdge]; tileSetCases[edge][(edge+1)&3].Rotate -= pTile->rotate[edge]; tileSetCases[edge][(edge+1)&3].Rotate &= 3; // Change the rotation // Get the transition used for (uint subTile=0; subTile<2; subTile++) { // Tileset int slot=getLayer (pTile, edge, pVoisinCorner[(neigborEdge+subTile)&3].TileSet, (pVoisinCorner[(neigborEdge+subTile)&3].Rotate - pTile->rotate[edge])&3, vectMesh, land); // Should be found nlassert (slot>=0); // Get the border borderEdges[edge][1-subTile] = CTileSet::getInvertBorder (pBorder[neigborEdge][slot]); } } } } // Make the final corner descriptor tileSetIndex finalCorner[4]; for (uint corner=0; corner<4; corner++) { finalCorner[corner].TileSet = -1; // All the same or empty ? for (uint layer=0; layer<4; layer++) { // Compatible ? if ( ( finalCorner[corner].TileSet == -1 ) || ( tileSetCases[layer][corner].TileSet == -1 ) || ( tileSetCases[layer][corner] == finalCorner[corner] ) ) { // Copy the tile if ( tileSetCases[layer][corner].TileSet != -1 ) finalCorner[corner] = tileSetCases[layer][corner]; } else { // Not compatible return false; } } // Empty ? if (finalCorner[corner].TileSet == -1) { // New tileSet finalCorner[corner].TileSet = tileSet; finalCorner[corner].Rotate = curRotation; } } // Set of index std::vector setIndex; // Set count for (uint v=0; v<4; v++) { // Should not be empty nlassert (finalCorner[v].TileSet!=-1); // Check for same tile with a +2 rotation bool bFind=false; for (int vv=0; vv<(int)setIndex.size(); vv++) { if (setIndex[vv].TileSet==finalCorner[v].TileSet) { tileSetIndex complet=finalCorner[v]; complet.Rotate=(complet.Rotate+2)&3; if (setIndex[vv].Rotate==complet.Rotate) return false; if (finalCorner[v]==setIndex[vv]) bFind=true; } } // no, ok push it back. if (!bFind) setIndex.push_back (finalCorner[v]); } // Sort the tile set std::sort (setIndex.begin(), setIndex.end()); // Check for more than 3 materials if (setIndex.size()>3) return false; // Count materiaux tileIndex finalIndex[3]; finalIndex[0].Tile=0; finalIndex[1].Tile=0; finalIndex[2].Tile=0; // For each layer for (int l=0; l<(int)setIndex.size(); l++) { if (l==0) { // Look for a tile without group finalIndex[l].Tile=selectTile (setIndex[l].TileSet, false, false, 0, bank); finalIndex[l].Rotate=(setIndex[l].Rotate&3); } else { // The 4 borders CTileSet::TFlagBorder border[4]; // Corner filled or not bool bFilled[4]; for (int c=0; c<4; c++) bFilled[c]=!(finalCorner[c]voisins[e]) { // Filled corner uint filledCorner = bFilled[e]?e:(e+1)&3; // Get the neighbor corner tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; // Tile is filled ? if (GetBorderDesc (pTile->voisins[e], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land)) { // Neighbor edge int neigborEdge = (2+e+pTile->rotate[e])&3; // Get the slot int slot=getLayer (pTile, e, setIndex[l].TileSet, setIndex[l].Rotate, vectMesh, land); if ((slot != -1) && (pBorder[neigborEdge][slot] != CTileSet::dontcare)) { // Invert the border border[e]=CTileSet::getInvertBorder (pBorder[neigborEdge][slot]); // Found it found = true; } } } // Found ? if ( !found ) { // First filled ? if (bFilled[e]) border[e]=CTileSet::_1000; else border[e]=CTileSet::_0001; } } } // Find the transitions const CTileSetTransition *tileTrans = FindTransition (setIndex[l].TileSet, setIndex[l].Rotate, border, bank); if (!tileTrans) { return false; } finalIndex[l].Rotate=(setIndex[l].Rotate&3); finalIndex[l].Tile=tileTrans->getTile(); } } // Set the border desc tileDesc desc; GetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land); switch (setIndex.size()) { case 1: desc.setTile (1, 0, desc.getDisplace (), finalIndex[0], tileIndex (0,0), tileIndex (0,0)); break; case 2: desc.setTile (2, 0, desc.getDisplace (), finalIndex[0], finalIndex[1], tileIndex (0,0)); break; case 3: desc.setTile (3, 0, desc.getDisplace (), finalIndex[0], finalIndex[1], finalIndex[2]); break; default: nlassert (0); // no! break; } SetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land, nelPatchChg, NULL); } else { // Can't pos 256 transition tile return false; } } return true; } /*-------------------------------------------------------------------*/ void EPM_PaintMouseProc::PutADisplacetile ( EPM_PaintTile* pTile, const CTileBank& bank, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg) { // Get tile description tileDesc desc; GetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land); // Get the info about this tile int tileSet; int number; CTileBank::TTileType type; int tile=desc.getLayer (0).Tile; if ((tile>=0)&&(tileDisplaceTile); SetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land, nelPatchChg, NULL, true, true); } } /*-------------------------------------------------------------------*/ bool EPM_PaintMouseProc::GetBorderDesc (EPM_PaintTile* tile, tileSetIndex *pVoisinCorner, CTileSet::TFlagBorder pBorder[4][3], tileDesc *pVoisinIndex, const CTileBank& bank, std::vector& vectMesh, CNelPatchChanger& nelPatchChg, CLandscape *land) { // Tile info tileDesc backup; GetTile (tile->Mesh, tile->tile, backup, vectMesh, land); if (backup.isEmpty()) return false; int nLayer=backup.getNumLayer (); tileIndex pIndexx[3]; for (int nL=0; nL=bank.getTileCount()) { WarningInvalidTileSet (); return false; // Problem in tile info. Wrong tileSet ? } bank.getTileXRef (index.Tile, tileSet, number, type); // For each edge for (int i=0; i<4; i++) { // Border type CTileSet::TBorder toBorder[4]={CTileSet::left, CTileSet::bottom, CTileSet::right, CTileSet::top}; CTileSet::TFlagBorder border=CTileSet::_1111; if (type==CTileBank::transition) border=CTileSet::getOrientedBorder (toBorder[i], CTileSet::getEdgeType ((CTileSet::TTransition)number, toBorder[i])); if (nL==0) { //nlassert (border==CTileSet::_1111); if (border!=CTileSet::_1111) { WarningInvalidTileSet (); return false; // Problem in tile info. Wrong tileSet ? } } // Setup corner switch (border) { case CTileSet::_1111: case CTileSet::_1110: case CTileSet::_1000: pVoisinCorner[(i+backup.getLayer(nL).Rotate)&3].TileSet=tileSet; pVoisinCorner[(i+backup.getLayer(nL).Rotate)&3].Rotate=index.Rotate; break; } // Store border pBorder[(i+backup.getLayer(nL).Rotate)&3][nL]=border; } } pVoisinIndex->setTile (nLayer, backup.getCase(), backup.getDisplace(), pIndexx[0], pIndexx[1], pIndexx[2]); return true; } /*-------------------------------------------------------------------*/ const CTileSetTransition* EPM_PaintMouseProc::FindTransition (int nTileSet, int nRotate, const CTileSet::TFlagBorder *border, const CTileBank& bank) { // Convert border to tile format CTileSet::TFlagBorder pBorderConverted[4]; for (int i=0; i<4; i++) { CTileSet::TBorder toBorder[4]={CTileSet::left, CTileSet::bottom, CTileSet::right, CTileSet::top}; pBorderConverted[i]=CTileSet::getOrientedBorder (toBorder[i], border[(i+nRotate)&3]); } // Look for good tile.. CTileSet::TTransition nTransition=CTileSet::getTransitionTile (pBorderConverted[3], pBorderConverted[1], pBorderConverted[0], pBorderConverted[2]); //nlassert (nTransition!=CTileSet::notfound); if (nTransition==CTileSet::notfound) return NULL; // Tile description return bank.getTileSet (nTileSet)->getTransition (nTransition); } /*-------------------------------------------------------------------*/ int EPM_PaintMouseProc::getLayer (EPM_PaintTile* tile, int border, int tileSet, int rotate, std::vector& vectMesh, CLandscape *land) { int nLayer=-1; tileDesc desc; GetTile (tile->voisins[border]->Mesh, tile->voisins[border]->tile, desc, vectMesh, land); for (int o=0; orotate[border]; index.Rotate&=3; CTileBank::TTileType type; int TileSet, number; if ((int)index.Tile& visited, const CTileBank& bank, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg, std::vector& backupStack, bool recurseNoDiff) { // 1) Already visited if (visited.find (tile)!=visited.end()) return true; // Frozen ? /* if (vectMesh[tile->Mesh].Node->IsFrozen()) return true;*/ // Big trick if (pobj->TileTrick) return true; // 3) Backup tile tileDesc backup; GetTile (tile->Mesh, tile->tile, backup, vectMesh, land); // 2) Tile empty if (backup.isEmpty()) return true; // *** Clip select patch // Patch number int patch=tile->tile/NUM_TILE_SEL; // Check if we are in patch subobject and if this patch is selected if ((vectMesh[tile->Mesh].PMesh->selLevel==EP_PATCH)&&(!vectMesh[tile->Mesh].PMesh->patchSel[patch])) return false; // 3) Add to visited tiles visited.insert (tile); // 4) 256 ? bool _256=(backup.getCase()>0); // Corner type bool bModified[4]= { false, false, false, false }; // Corner type bool bTouched[4]= { false, false, false, false }; bool bSameEdge[4]= { true, true, true, true }; bool bVisited[4]= { false, false, false, false }; int extraOrdinary[4]= { 0, 0, 0, 0 }; int extraOrdinarySmallEdge[4]= { 0, 0, 0, 0 }; tileSetIndex nCorner[4]; int i; for (i=0; i<4; i++) nCorner[i].TileSet=-1; CTileSet::TFlagBorder nBorder[4][3]; tileDesc pIndex; bool bFill=GetBorderDesc (tile, nCorner, nBorder, &pIndex, bank, vectMesh, nelPatchChg, land); //nlassert (bFill); // Problem in tile info. Wrong tileSet ? if (!bFill) { WarningInvalidTileSet (); return false; } bool bDiff=false; // For each voisin int v; for (v=0; v<4; v++) { // Voisin already visited ? if (tile->voisins[v]) { // ok.. already visited, so copy border tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; bFill=GetBorderDesc (tile->voisins[v], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land); if (bFill) { int edge=(2+v+tile->rotate[v])&3; // Already visited or frozen ? if ((visited.find (tile->voisins[v])!=visited.end()) /*|| (vectMesh[tile->voisins[v]->Mesh].Node->IsFrozen())*/) { bVisited[v]=true; pVoisinCorner[(edge+1)&3].Rotate-=tile->rotate[v]; pVoisinCorner[(edge+1)&3].Rotate&=3; pVoisinCorner[edge].Rotate-=tile->rotate[v]; pVoisinCorner[edge].Rotate&=3; if (bTouched[v]) { if (nCorner[v]!=pVoisinCorner[(edge+1)&3]) { // Check if it is a valid corner int delta=(pVoisinCorner[(edge+1)&3].Rotate-nCorner[v].Rotate)&3; if (delta != 2) { if (nCorner[v]frozen) { // Case number int nCase=backup.getCase()-1; int nRotate=backup.getLayer(0).Rotate; nlassert (nCase>=0); nlassert (nCase<4); // Flag the voisin to force the visite EPM_PaintTile* other=tile->voisins[(1+nCase+nRotate)&3]; if (other) { int rot=tile->rotate[(1+nCase+nRotate)&3]; tileDesc desc1; GetTile (other->Mesh, other->tile, desc1, vectMesh, land); if (!desc1.isEmpty()) { if ( (desc1.isEmpty())|| (desc1.getCase()!=(1+((nCase+1)&3)))|| (desc1.getLayer(0).Tile!=backup.getLayer(0).Tile)|| (desc1.getLayer(0).Rotate!=((backup.getLayer(0).Rotate-rot)&3)) ) bDiff=true; } else bDiff=true; if (isLocked (pobj, other)) _isLocked = true; } other=tile->voisins[(2+nCase+nRotate)&3]; if (other) { int rot=tile->rotate[(2+nCase+nRotate)&3]; tileDesc desc1; GetTile (other->Mesh, other->tile, desc1, vectMesh, land); if (!desc1.isEmpty()) { if ( (desc1.isEmpty())|| (desc1.getCase()!=(1+((nCase+3)&3)))|| (desc1.getLayer(0).Tile!=backup.getLayer(0).Tile)|| (desc1.getLayer(0).Rotate!=((backup.getLayer(0).Rotate-rot)&3)) ) bDiff=true; } else bDiff=true; if (isLocked (pobj, other)) _isLocked = true; } } // C) Invalide tile (same tile and rotation only on the diagonal) for (i=0; i<2; i++) { if ((nCorner[i]==nCorner[(i+2)&3])&& (nCorner[(i+1)&3]!=nCorner[(i+2)&3])&& (nCorner[(i+3)&3]!=nCorner[(i+2)&3])) { return false; } } // Same edge ? for (v=0; v<4; v++) { if (tile->voisins[v]==NULL) bSameEdge[v]=false; else { tileDesc desc; GetTile (tile->voisins[v]->Mesh, tile->voisins[v]->tile, desc, vectMesh, land); if (bModified[v]|| bModified[(v+1)&3]|| (desc.isEmpty())) bSameEdge[v]=false; } } if ((!bDiff)&&pIndex.getNumLayer()==1) return true; // Frozen or locked ? if (tile->frozen || _isLocked) return false; // Count materiaux //set setIndex; std::vector setIndex; // Set count for (v=0; v<4; v++) { nlassert (nCorner[v].TileSet!=-1); // B) Check for same tile with a +2 rotation bool bFind=false; for (int vv=0; vv<(int)setIndex.size(); vv++) { if (setIndex[vv].TileSet==nCorner[v].TileSet) { tileSetIndex complet=nCorner[v]; complet.Rotate=(complet.Rotate+2)&3; if (setIndex[vv].Rotate==complet.Rotate) return false; if (nCorner[v]==setIndex[vv]) bFind=true; } } // no, ok push it back. if (!bFind) setIndex.push_back (nCorner[v]); } std::sort (setIndex.begin(), setIndex.end()); // Check validity // A) Check for more than 3 materials if (setIndex.size()>3) return false; // Count materiaux std::vector::iterator ite=setIndex.begin(); tileIndex finalIndex[3]; finalIndex[0].Tile=0; finalIndex[1].Tile=0; finalIndex[2].Tile=0; // Look for a tile resolving the constraints // Fill ? for (int l=0; l<(int)setIndex.size(); l++) { if (l==0) { // Filled, choose a random tile // Get tge tileSet // TileSet const CTileSet *TileSet=bank.getTileSet (ite->TileSet); // Select group int group=0; int nTile=-1; if (backup.getNumLayer ()==1) { // Get information about the tileSet int tileSet; int number; CTileBank::TTileType type; // Get XRef bank.getTileXRef (backup.getLayer (0).Tile, tileSet, number, type); // Check if it is the same tileSet if (tileSet==ite->TileSet) { // Get the group flags uint flags=bank.getTile (backup.getLayer (0).Tile)->getGroupFlags (); for (int f=0; f<32; f++) { // Group ? if (flags&(1<TileSet, false, false, f+1, bank); // Find a tile ? if (nTile!=-1) break; } } } } // A tile as been found ? if (nTile==-1) // Look for a tile without group nTile=selectTile (ite->TileSet, false, false, 0, bank); // Set the tile slot finalIndex[0].Tile=nTile; finalIndex[0].Rotate=(ite->Rotate&3); } else { // Transition, choose the good tile // Get tge tileSet // TileSet // Borders CTileSet::TFlagBorder border[4]; bool bFilled[4]; int c; for (c=0; c<4; c++) bFilled[c]=!(nCorner[c]<*ite); for (c=0; c<4; c++) { if (bFilled[c]) { if (bFilled[(c+1)&3]) border[c]=CTileSet::_1111; else { // If extraordinary vertex and the small edge if ((extraOrdinary[c])&&(extraOrdinarySmallEdge[c]==c)) { border[c]=CTileSet::_1000; } // If 3 mat, use a 3/4 border else if (setIndex.size()==3) { CTileSet::TFlagBorder wanted; CTileSet::TFlagBorder invWanted; // Last on the stack ? if (*itevoisins[c]) { tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; if (GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land)) { // Find the good layer in the dest tileDesc int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); if (nLayer!=-1) { int edge=(2+c+tile->rotate[c])&3; if (pBorder[edge][nLayer]==CTileSet::getInvertBorder (invWanted)) { pBorder[edge][nLayer]=CTileSet::getInvertBorder (wanted); // Voisin tile set CTileSet::TFlagBorder newBorder[4]; for (int nB=0; nB<4; nB++) newBorder[nB]=pBorder[nB][nLayer]; const CTileSetTransition* pTile=FindTransition (ite->TileSet, ite->Rotate+tile->rotate[c], newBorder, bank); if (!pTile) { WarningInvalidTileSet (); return false; } pVoisinIndex.getLayer(nLayer).Tile=pTile->getTile(); // Is frozen ? if (tile->voisins[c]->frozen) // Yes, can't change it! return false; // Is locked ? if (isLocked (pobj, tile->voisins[c])) return false; // Set the tile.. SetTile (tile->voisins[c]->Mesh, tile->voisins[c]->tile, pVoisinIndex, vectMesh, land, nelPatchChg, &backupStack); } /*if (pBorder[edge][nLayer]==CTileSet::_0001) border[c]=CTileSet::_1000;*/ } /*else border[c]=CTileSet::_1000;*/ } } } // Normal, else { // Voisin visited or frozen ? if (tile->voisins[c]&&((visited.find (tile->voisins[c])!=visited.end()) /*|| (vectMesh[tile->voisins[c]->Mesh].Node->IsFrozen())*/ )) { // Yes, visited. Copy the border tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; bool bOk=GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land); // Should not be empty nlassert (bOk); int edge=(2+c+tile->rotate[c])&3; // Should have one of the following transition int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); if (nLayer!=-1) { nlassert ((pBorder[edge][nLayer]==CTileSet::_0111)||(pBorder[edge][nLayer]==CTileSet::_0001)); } // Copy inverted! border[c]=CTileSet::getInvertBorder (pBorder[edge][nLayer]); } else { // No, not yet visited // Choose transition by random bool bComputed=false; if ((bVisited[c]||!recurseNoDiff)&&bSameEdge[c]) { bSameEdge[c]=false; // Yes, visited. Copy the border of the voisin tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; bool bOk=GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land); // ok voisin is here ? if (bOk) { // edge in the voisin int edge=(2+c+tile->rotate[c])&3; // layer where to find transition in the voisin int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); // transition ok? if ((pBorder[edge][nLayer]==CTileSet::_0111)||(pBorder[edge][nLayer]==CTileSet::_0001)) { // copy! border[c]=CTileSet::getInvertBorder (pBorder[edge][nLayer]); bSameEdge[c]=true; bComputed=true; } } } // No transition computed, random if (!bComputed) { bSameEdge[c]=false; if (rand()&1) border[c]=CTileSet::_1000; else border[c]=CTileSet::_1110; } } } } } else { int nNextCorner=(c+1)&3; if (!bFilled[nNextCorner]) border[c]=CTileSet::_0000; else { // If extraordinary vertex and the small edge if ((extraOrdinary[nNextCorner])&&(extraOrdinarySmallEdge[nNextCorner]==c)) { border[c]=CTileSet::_0001; } // If 3 mat, use a 3/4 border else if (setIndex.size()==3) { CTileSet::TFlagBorder wanted; CTileSet::TFlagBorder invWanted; // Last on the stack ? if (*itevoisins[c])//&&visited.find (tile->voisins[c])!=visited.end()) { tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; if (GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land)) { // Find the good layer in the dest tileDesc int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); if (nLayer!=-1) { int edge=(2+c+tile->rotate[c])&3; if (pBorder[edge][nLayer]==CTileSet::getInvertBorder (invWanted)) { pBorder[edge][nLayer]=CTileSet::getInvertBorder (wanted); // Voisin tile set CTileSet::TFlagBorder newBorder[4]; for (int nB=0; nB<4; nB++) newBorder[nB]=pBorder[nB][nLayer]; const CTileSetTransition* pTile=FindTransition (ite->TileSet, ite->Rotate+tile->rotate[c], newBorder, bank); if (!pTile) { WarningInvalidTileSet (); return false; } pVoisinIndex.getLayer(nLayer).Tile=pTile->getTile(); // Is frozen ? if (tile->voisins[c]->frozen) // Yes, can't change it! return false; // Is locked ? if (isLocked (pobj, tile->voisins[c])) return false; // Set the tile.. SetTile (tile->voisins[c]->Mesh, tile->voisins[c]->tile, pVoisinIndex, vectMesh, land, nelPatchChg, &backupStack); } /*if (pBorder[edge][nLayer]==CTileSet::_1000) border[c]=CTileSet::_0001;*/ } /*else border[c]=CTileSet::_0001;*/ } } } // Normal, else { // Voisin visited ? if ((tile->voisins[c])&& ((visited.find (tile->voisins[c])!=visited.end()) /*|| (vectMesh[tile->voisins[c]->Mesh].Node->IsFrozen())*/) ) { // Yes, visited. Copy the border tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; bool bOk=GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land); // Should not be empty nlassert (bOk); int edge=(2+c+tile->rotate[c])&3; // Should have one of the following transition // Find the layer of the tile.. int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); if (nLayer!=-1) { nlassert ((pBorder[edge][nLayer]==CTileSet::_1110)||(pBorder[edge][nLayer]==CTileSet::_1000)); } // Copy inverted! border[c]=CTileSet::getInvertBorder (pBorder[edge][nLayer]); } else { // No, not yet visited // Choose transition by random bool bComputed=false; if ((bVisited[c]||!recurseNoDiff)&&bSameEdge[c]) { bSameEdge[c]=false; // Yes, visited. Copy the border of the voisin tileSetIndex pVoisinCorner[4]; CTileSet::TFlagBorder pBorder[4][3]; tileDesc pVoisinIndex; bool bOk=GetBorderDesc (tile->voisins[c], pVoisinCorner, pBorder, &pVoisinIndex, bank, vectMesh, nelPatchChg, land); // ok voisin is here ? if (bOk) { // edge in the voisin int edge=(2+c+tile->rotate[c])&3; // layer where to find transition in the voisin int nLayer=getLayer (tile, c, ite->TileSet, ite->Rotate, vectMesh, land); // transition ok? if ((pBorder[edge][nLayer]==CTileSet::_1110)||(pBorder[edge][nLayer]==CTileSet::_1000)) { // copy! border[c]=CTileSet::getInvertBorder (pBorder[edge][nLayer]); bSameEdge[c]=true; bComputed=true; } } } // No transition computed, random if (!bComputed) { bSameEdge[c]=false; if (rand()&1) border[c]=CTileSet::_0001; else border[c]=CTileSet::_0111; } } } } } } // ok, find the good transition.. const CTileSetTransition* pTile=FindTransition (ite->TileSet, ite->Rotate, border, bank); if (!pTile) { WarningInvalidTileSet (); return false; } finalIndex[l].Rotate=ite->Rotate; finalIndex[l].Tile=pTile->getTile(); } ite++; } // Set the border desc tileDesc desc; GetTile (tile->Mesh, tile->tile, desc, vectMesh, land); switch (setIndex.size()) { case 1: desc.setTile (1, 0, desc.getDisplace (), finalIndex[0], tileIndex (0,0), tileIndex (0,0)); break; case 2: desc.setTile (2, 0, desc.getDisplace (), finalIndex[0], finalIndex[1], tileIndex (0,0)); break; case 3: desc.setTile (3, 0, desc.getDisplace (), finalIndex[0], finalIndex[1], finalIndex[2]); break; default: nlassert (0); // no! break; } SetTile (tile->Mesh, tile->tile, desc, vectMesh, land, nelPatchChg, &backupStack); // Force to visite tile in the same 256 in 256 mode.. if (_256) { // Case number int nCase=backup.getCase()-1; int nRotate=backup.getLayer(0).Rotate; nlassert (nCase>=0); nlassert (nCase<4); // Flag the voisin to force the visite bSameEdge[(1+nCase+nRotate)&3]=false; bSameEdge[(2+nCase+nRotate)&3]=false; } // ** For all voisin tiles not visited on a border with modified corners : // Idem ? if ((!bDiff)&&(!recurseNoDiff)) return true; bool bContinue=true; // For each voisin for (v=0; v<4; v++) { // Voisin not already visited and not frozen ? if ((tile->voisins[v]) && (visited.find (tile->voisins[v])==visited.end()) /* && (vectMesh[tile->voisins[v]->Mesh].Node->IsFrozen()==0)*/) { // ok.. not visited, border with modified corner ? if (bModified[v]||bModified[(v+1)&3]||(!bSameEdge[v])) if (!PropagateBorder (tile->voisins[v], (tile->rotate[v]+curRotation)&3, curTileSet, visited, bank, vectMesh, land, nelPatchChg, backupStack, false)) { bContinue=false; break; } } } if (!bContinue) { return false; } return true; } /*-------------------------------------------------------------------*/ uint8 EPM_PaintMouseProc::CalcRotPath (EPM_PaintTile* from, EPM_PaintTile* to, int depth, int rotate, int& deltaX, int& deltaY, int& cost) { static const int x[4]={-1, 0, 1, 0}; static const int y[4]={0, 1, 0, -1}; if (from==to) { cost=0; deltaX=0; deltaY=0; return 0; } if (depth>0) { uint8 ret=0xff; cost=1000000; int best; for (int i=0; i<4; i++) { if (from->voisins[i]) { int myDeltaX; int myDeltaY; int myCost; int myRet=CalcRotPath (from->voisins[i], to, depth-1, (from->rotate[i]+rotate)&3, myDeltaX, myDeltaY, myCost); if (myRet!=0xff) { myDeltaX+=x[(i+rotate)&3]; myDeltaY+=y[(i+rotate)&3]; myCost++; if (myCostrotate[best]+ret)&3; } return 0xff; } /*-------------------------------------------------------------------*/ static TModePaint nModeTexture=ModeTile; static TModeMouse modeSelect=ModePaint; void EPM_PaintMouseProc::RecursTile (EPM_PaintTile* pTile, const CTileBank& bank, int tileSet, std::vector& vectMesh, CLandscape* land, int recurs, std::set& alreadyRecursed, bool first, int rotation, CNelPatchChanger& nelPatchChg, bool _256) { if (alreadyRecursed.find (pTile)==alreadyRecursed.end()) { alreadyRecursed.insert (pTile); std::set visited; // Mode displace ? if (nModeTexture==ModeDisplace) PutADisplacetile ( pTile, bank, vectMesh, land, nelPatchChg); else PutATile ( pTile, tileSet, rotation, bank, first, visited, vectMesh, land, nelPatchChg, _256); } // Call fill if (recurs>0) { for (int i=0; i<4; i++) { if (_256) { if (pTile->get2Voisin(i)) RecursTile (pTile->get2Voisin(i), bank, tileSet, vectMesh, land, recurs-2, alreadyRecursed, false, (rotation+pTile->get2VoisinRotate(i))&3, nelPatchChg, true); } else { if (pTile->voisins[i]) RecursTile (pTile->voisins[i], bank, tileSet, vectMesh, land, recurs-1, alreadyRecursed, false, (rotation+pTile->rotate[i])&3, nelPatchChg, false); } } } } BOOL EPM_PaintMouseProc::PutDisplace (int tile, int mesh, const CTileBank& bank, std::vector& vectMesh, CLandscape* land, int recurs, std::set& alreadyRecursed, CNelPatchChanger& nelPatchChg) { static sint32 MeshOld=-1; static int tileOld=-1; bool lost=false; EPM_PaintTile* pTile=&metaTile[mesh][tile]; // Ok recurse tiles RecursTile (pTile, bank, 0, vectMesh, land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, true, EPM_PaintMouseProc::Rotation, nelPatchChg, false); MeshOld=mesh; tileOld=tile; return TRUE; } BOOL EPM_PaintMouseProc::PutTile (int tile, int mesh, bool first, const CTileBank& bank, int tileSet, std::vector& vectMesh, CLandscape* land, int recurs, std::set& alreadyRecursed, CNelPatchChanger& nelPatchChg, bool _256) { static sint32 MeshOld=-1; static int tileOld=-1; bool lost=false; EPM_PaintTile* pTile=&metaTile[mesh][tile]; alreadyRecursed.insert (pTile); if (first) { tileDesc desc; GetTile (pTile->Mesh, pTile->tile, desc, vectMesh, land); if (desc.isEmpty ()) EPM_PaintMouseProc::Rotation=0; else { int i; for (i=0; i alreadyRecursed; // Ok recurse tiles RecursTile (pTile, bank, tileSet, vectMesh, land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, first, EPM_PaintMouseProc::Rotation, nelPatchChg, _256); } MeshOld=mesh; tileOld=tile; } return TRUE; } /*-------------------------------------------------------------------*/ int getOffset (int edge, int nU, int nV, bool symmetry) { switch ((edge+(symmetry?1:0))&3) { case 0: return 0; case 1: return (nV-1)*MAX_TILE_IN_PATCH; case 2: return (nV-1)*MAX_TILE_IN_PATCH+nU-1; case 3: return nU-1; } nlassert (0); //no! return 0; } /*int offset[4]={ 0, (nV-1)*MAX_TILE_IN_PATCH, (nV-1)*MAX_TILE_IN_PATCH+nU-1, nU-1 }; int offsetOther[4]={ 0, (nVOther-1)*MAX_TILE_IN_PATCH, (nVOther-1)*MAX_TILE_IN_PATCH+nUOther-1, nUOther-1 };*/ struct callThread { callThread (std::vector& vectMesh) : VectMesh (vectMesh) {} EPM_PaintMouseProc *eproc; PaintPatchMod *pobj; CVector center; TimeValue T; std::vector& VectMesh; }; void mainproc(CScene& scene, CEventListenerAsync& AsyncListener, CEvent3dMouseListener& mouseListener, CLandscapeModel& landscape, IDriver& driver, callThread *pData, CPaintColor &paintColor) { // Default mode is paint modeSelect=ModePaint; // Mode selection if (AsyncListener.isKeyDown ((TKey)PainterKeys[Select])) // Set mode modeSelect=ModeSelect; // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[Pick])) // Set mode modeSelect=ModePick; // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[GetState])) // Set mode modeSelect=ModeGetState; // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[Fill0])) { // Set mode modeSelect=ModeFill; // Set fill rotation pData->pobj->TileFillRotation=0; } // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[Fill1])) { // Set mode modeSelect=ModeFill; // Set fill rotation pData->pobj->TileFillRotation=1; } // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[Fill2])) { // Set mode modeSelect=ModeFill; // Set fill rotation pData->pobj->TileFillRotation=2; } // Mode picking if (AsyncListener.isKeyDown ((TKey)PainterKeys[Fill3])) { // Set mode modeSelect=ModeFill; // Set fill rotation pData->pobj->TileFillRotation=3; } // Choose a color if ((AsyncListener.isKeyPushed ((TKey)PainterKeys[Select]))&&(nModeTexture==ModeColor)) chooseAColor (); // Mode tile ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[MModeTile])) nModeTexture=ModeTile; // Mode color ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[MModeColor])) nModeTexture=ModeColor; // Mode displace ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[MModeDisplace])) nModeTexture=ModeDisplace; // Toggle couleur ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[ToggleColor])) { if (nModeTexture==ModeColor) { COLORREF tmp=color1; color1=color2; color2=tmp; float tmpF=opa1; opa1=opa2; opa2=tmpF; tmpF=hard1; hard1=hard2; hard2=tmpF; } } // Change brush ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[SizeDown])) { if ((nModeTexture==ModeTile)||(nModeTexture==ModeDisplace)) { pData->pobj->brushSize--; if (pData->pobj->brushSize<0) pData->pobj->brushSize=0; } else if (nModeTexture==ModeColor) { pData->pobj->ColorBushSize--; if (pData->pobj->ColorBushSize<0) pData->pobj->ColorBushSize=0; } } // Change brush ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[SizeUp])) { if ((nModeTexture==ModeTile)||(nModeTexture==ModeDisplace)) { pData->pobj->brushSize++; if (pData->pobj->brushSize>2) pData->pobj->brushSize=2; } else if (nModeTexture==ModeColor) { pData->pobj->ColorBushSize++; if (pData->pobj->ColorBushSize>COLOR_BRUSH_STEP) pData->pobj->ColorBushSize=COLOR_BRUSH_STEP; } } // Change size of tile ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[ToggleTileSize])) { pData->pobj->tileSize++; pData->pobj->tileSize&=1; } // Change group ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[GroupUp])) { pData->pobj->TileGroup++; if (pData->pobj->TileGroup>NL3D_CTILE_NUM_GROUP) pData->pobj->TileGroup=0; } // Change group ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[GroupDown])) { pData->pobj->TileGroup--; if (pData->pobj->TileGroup<0) pData->pobj->TileGroup=NL3D_CTILE_NUM_GROUP; } // Change background color ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[BackgroundColor])) { setBackgroundColor (); } // Toggle arrows ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[ToggleArrows])) { // Toggle pData->pobj->additiveTile^=true; // Go if (pData->pobj->additiveTile) { // Add a additive tile... int i; for (i=0; isetFileName (CTile::additive, "arrow.tga"); landscape.Landscape.releaseTiles(i, 1); } } else { // Copy original bank landscape.Landscape.TileBank = bank; // Add a additive tile... int i; for (i=0; iVectMesh.size(); zone++) { // Get the zone CZone *pZone=landscape.Landscape.getZone (zone); if (pZone) { // For each patch uint numPatch=pZone->getNumPatchs(); for (uint patch=0; patchchangePatchTextureAndColor (patch, NULL, NULL); //pZone->refreshTesselationGeometry (patch); } } } } // Toggle automatic lighting ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[AutomaticLighting])) { // Toggle pData->pobj->automaticLighting^=true; // Enable / disable automatic lighting landscape.Landscape.enableAutomaticLighting (pData->pobj->automaticLighting); // Touch all patches for (uint zone=0; zoneVectMesh.size(); zone++) { // Get the zone CZone *pZone=landscape.Landscape.getZone (zone); nlassert (pZone); // For each patch uint numPatch=pZone->getNumPatchs(); for (uint patch=0; patchchangePatchTextureAndColor (patch, NULL, NULL); pZone->refreshTesselationGeometry (patch); } } } // Toggle lock borders ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[LockBorders])) { // Toggle pData->pobj->lockBorders^=true; } // Select color brush ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[SelectColorBrush])) { // Create a dialog filter static char szFilter[] = "Targa Files (*.tga)\0*.tga\0" "All Files (*.*)\0*.*\0\0"; // Filename buffer char buffer[65535]; buffer[0]=0; // Fill the (big) struct OPENFILENAME openFile; memset (&openFile, 0, sizeof (OPENFILENAME)); openFile.lStructSize = sizeof (OPENFILENAME); openFile.hwndOwner = (HWND)CNELU::Driver->getDisplay(); openFile.lpstrFilter = szFilter; openFile.nFilterIndex = 0; openFile.lpstrFile = buffer; openFile.nMaxFile = 65535; openFile.Flags = OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_ENABLESIZING|OFN_EXPLORER; openFile.lpstrDefExt = "*.tga"; // Open the dialog if (GetOpenFileName(&openFile)) { // Load the file paintColor.loadBrush (buffer); paintColor.setBrushMode (true); } } // Toggle brush mode ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[ToggleColorBrushMode])) { paintColor.setBrushMode (!paintColor.getBrushMode()); } // Change hardness ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[HardnessUp])) { hard1+=0.2f; if (hard1>1.f) hard1=1.f; } // Change hardness ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[HardnessDown])) { hard1-=0.2f; if (hard1<0.f) hard1=0.f; } // Change opacity ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[OpacityUp])) { opa1+=0.2f; if (opa1>1.f) opa1=1.f; } // Change opacity ? if (AsyncListener.isKeyPushed ((TKey)PainterKeys[OpacityDown])) { opa1-=0.2f; if (opa1<0.f) opa1=0.f; } // Change hardness ? if (AsyncListener.isKeyDown ((TKey)PainterKeys[Zouille])) pData->pobj->TileTrick=true; else pData->pobj->TileTrick=false; // Set cursor if (modeSelect==ModeGetState) SetCursor (bankCont->HInspect); else if (modeSelect==ModePick) SetCursor (bankCont->HCur); else if (modeSelect==ModeFill) SetCursor (bankCont->HFill); else if (pData->pobj->TileTrick) SetCursor (bankCont->HTrick); else SetCursor (LoadCursor (NULL, IDC_ARROW)); // Clear buffers CNELU::clearBuffers(CRGBA((uint8)(backGround&0xff), (uint8)((backGround>>8)&0xff), (uint8)((backGround>>16)&0xff))); if (modeSelect!=ModeSelect) { CVector CameraRot(CVector::Null), CameraPos(0,0,0); // time mgt. static float t0= timeGetTime()*0.001f; static float t1; t1= timeGetTime()*0.001f; float dt= t1-t0; // User Interaction. //================== // Speed. #define NSPEED 5 static float Speed[NSPEED]= {1.66f, 3.33f, 8.33f, 40.0f, 200.0f}; // 6km/h, 12km/h, 30km/h, 144km/h, 700km/h. static sint idSpeed= 2; float speed= Speed[idSpeed]; float rotSpeed= 80*(float)Pi/180; // Rot. if(AsyncListener.isKeyDown(KeyLEFT)) CameraRot.z+=rotSpeed*dt; if(AsyncListener.isKeyDown(KeyRIGHT)) CameraRot.z-=rotSpeed*dt; // Move. CVector dir(0,speed*dt,0); CMatrix mat; mat.identity(); mat.rotateZ(CameraRot.z); mat.rotateX(CameraRot.x); dir= mat.mulVector(dir); if(AsyncListener.isKeyDown(KeyUP)) CameraPos+= dir; if(AsyncListener.isKeyDown(KeyDOWN)) CameraPos-= dir; /*if(AsyncListener.isKeyDown(KeyR)) { CameraPos.set(0,0,0); CameraRot.set(0,0,0); }*/ // Straff. float straff=0; if(straff) { CMatrix m; m.identity(); m.rotateZ(CameraRot.z); m.rotateX(CameraRot.x); CameraPos+= m*CVector(straff,0,0); } CMatrix camKey; camKey.identity (); camKey.rotate(CameraRot, CMatrix::ZXY); // Zoom distance float zoom = 0; if (AsyncListener.isKeyDown ((TKey)PainterKeys[ZoomIn])) zoom -= ZoomSpeed * dt; if (AsyncListener.isKeyDown ((TKey)PainterKeys[ZoomOut])) zoom += ZoomSpeed * dt; // Add zoom CameraPos += (zoom * -camKey.getJ()); camKey.setPos(CameraPos); camKey=mouseListener.getViewMatrix()*camKey; CNELU::Camera->setMatrix(camKey); mouseListener.setMatrix(camKey); // Render. //================== landscape.enableAdditive (true); scene.render(); // first time: disable RefineMode, and compute all patchs tesselation. if(landscape.Landscape.getRefineMode()) { landscape.Landscape.setRefineMode (false); landscape.Landscape.refineAll(camKey.getPos()); } t0= t1; } // Draw interface drawInterface (modeSelect, nModeTexture, pData->pobj, driver, landscape, paintColor); CNELU::swapBuffers(); } class MouseListener : public IEventListener { private: IObjParam* _Ip; CCamera* _Camera; CViewport* _Viewport; PaintPatchMod* _Pobj; EPM_PaintMouseProc* _Eproc; CLandscape* _Land; CEventListenerAsync* _Async; CEvent3dMouseListener* _3dMouseListener; CFillPatch _FillTile; std::vector& _VectMesh; TimeValue _T; public: bool WindowActive; CPaintColor PaintColor; public: MouseListener (IObjParam *ip, CCamera *camera, CViewport *viewport, PaintPatchMod *pobj, EPM_PaintMouseProc *eproc, CLandscape* land, CEventListenerAsync* async, CEvent3dMouseListener* mouseList, std::vector& vectMesh, TimeValue t) : PaintColor (pobj, land, &bankCont->Undo, eproc), _FillTile (pobj, land, &bankCont->Undo, eproc), _VectMesh(vectMesh) { _Ip=ip; _Camera=camera; _Viewport=viewport; _Pobj=pobj; _Eproc=eproc; _Land=land; _Async=async; _3dMouseListener=mouseList; WindowActive=true; _T=t; } private: // Pick a tile void pick (int mesh, int tile, const CVector& hit, TModePaint mode); // Get the state of a tile CZoneSymmetrisation::TState MouseListener::getState (int mesh, int tile, const CVector& hit, std::vector& vectMesh); // Callback on mouse events virtual void operator ()(const CEvent& event) { if (event==EventDestroyWindowId) { WindowActive=false; } if (modeSelect!=ModeSelect) { int res = TRUE; static PatchMesh *shape1 = NULL; static int poly1, tile1, tile2, mesh1, mesh2, seg1; static bool pressed=false; static IPoint2 anchor, lastPoint; // Mouse down ? CEventMouse* mouse=(CEventMouse*)&event; CEventKeyDown* keyDown=(CEventKeyDown*)&event; if (event==EventMouseDownId) { if (pressed&&((mouse->Button&rightButton)&&((mouse->Button&(ctrlButton|shiftButton|altButton))==0))) { pressed=false; // Undo step bankCont->Undo.pushUndo (); // Call undo now bankCont->Undo.undo (*_Eproc, _VectMesh, _Land, _Pobj, PaintColor); } if (mouse->Button==leftButton) { CVector hotSpot; CVector topVector; if (_Eproc->HitATile(*_Viewport, *_Camera, mouse->X, mouse->Y, &tile1, &mesh1, _T, _VectMesh, hotSpot, topVector)) { // Set hit as next hotspot if ( (modeSelect!=ModePick) && (modeSelect!=ModeGetState) ) _3dMouseListener->setHotSpot (hotSpot); // Patch number int patch=tile1/NUM_TILE_SEL; // Mode paint ? if (modeSelect==ModePaint) { if ((nModeTexture==ModeTile)||(nModeTexture==ModeDisplace)) { // Check if we are in patch subobject and if this patch is selected if ((_VectMesh[mesh1].PMesh->selLevel!=EP_PATCH)||(_VectMesh[mesh1].PMesh->patchSel[patch])) { std::set alreadyRecursed; // A nel manager CNelPatchChanger nelPatchChg (_Land); if (nModeTexture==ModeTile) { // Put the tile _Eproc->PutTile (tile1, mesh1, true, bank, _Pobj->CurrentTileSet, _VectMesh, _Land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, nelPatchChg, _Pobj->tileSize!=0); } else // (nModeTexture==ModeDisplace) { // Put the tile _Eproc->PutDisplace (tile1, mesh1, bank, _VectMesh, _Land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, nelPatchChg); } // Flush nel chgt nelPatchChg.applyChanges ((nModeTexture==ModeDisplace)&&(_Pobj->automaticLighting)); } } else if (nModeTexture==ModeColor) { // Paint PaintColor.paint (mesh1, tile1, hotSpot, topVector, _VectMesh); } // Button pressed pressed = true; } else if (modeSelect==ModePick) { // Pick tile pick (mesh1, tile1, hotSpot, nModeTexture); } else if (modeSelect==ModeGetState) { // Pick tile state CZoneSymmetrisation::TState state = getState (mesh1, tile1, hotSpot, _VectMesh); _Pobj->CurrentState = state; // Active show state _Pobj->ShowCurrentState = true; } else if (modeSelect==ModeFill) { // Paint mode if (nModeTexture==ModeTile) // Fill this patch with the current tile _FillTile.fillTile (mesh1, patch, _VectMesh, _Pobj->CurrentTileSet, _Pobj->TileFillRotation, _Pobj->TileGroup, _Pobj->tileSize!=0, bank); else if (nModeTexture==ModeColor) // Fill this patch with the current color _FillTile.fillColor (mesh1, patch, _VectMesh, maxToNel (color1), (uint16)(256.f*opa1), PaintColor); else if (nModeTexture==ModeDisplace) // Fill this patch with the current color _FillTile.fillDisplace (mesh1, patch, _VectMesh, bank); } } } // Pick with right mouse if (mouse->Button==rightButton) { // Pick tile CVector hotSpot; pick (mesh1, tile1, hotSpot, nModeTexture); } } if (event==EventMouseUpId) { if (mouse->Button==leftButton) { pressed = false; _Pobj->ShowCurrentState = false; // Undo step bankCont->Undo.pushUndo (); } } // Mouse move ? if (event==EventMouseMoveId) { if ((pressed)&&(mouse->Button==leftButton)) { CVector hotSpot; CVector topVector; if (_Eproc->HitATile(*_Viewport, *_Camera, mouse->X, mouse->Y, &tile2, &mesh2, _T, _VectMesh, hotSpot, topVector)) { _3dMouseListener->setHotSpot (hotSpot); if ((tile1!=tile2)||(mesh1!=mesh2)) { // Paint tiles ? if ((nModeTexture==ModeTile)||(nModeTexture==ModeDisplace)) { // Patch number int patch=tile2/NUM_TILE_SEL; // Check if we are in patch subobject and if this patch is selected if ((_VectMesh[mesh1].PMesh->selLevel!=EP_PATCH)||(_VectMesh[mesh2].PMesh->patchSel[patch])) { std::set alreadyRecursed; // A nel manager CNelPatchChanger nelPatchChg (_Land); if (nModeTexture==ModeTile) { // Put the tile _Eproc->PutTile (tile2, mesh2, false, bank, _Pobj->CurrentTileSet, _VectMesh, _Land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, nelPatchChg, _Pobj->tileSize!=0); } else // (nModeTexture==ModeDisplace) { // Put the tile _Eproc->PutDisplace (tile2, mesh2, bank, _VectMesh, _Land, brushValue[PaintPatchMod::brushSize], alreadyRecursed, nelPatchChg); } // Flush nel chgt nelPatchChg.applyChanges ((nModeTexture==ModeDisplace)&&(_Pobj->automaticLighting)); } } // Paint colors ? else if (nModeTexture==ModeColor) { // Paint PaintColor.paint (mesh2, tile2, hotSpot, topVector, _VectMesh); } // New tile touched tile1=tile2; mesh1=mesh2; } } } } // Key down ? if (event==EventKeyDownId) { // First time ? if (keyDown->FirstTime) { // Undo ? if ((keyDown->Key==KeyZ)&&(keyDown->Button==ctrlKeyButton)) { // Undo ! bankCont->Undo.undo (*_Eproc, _VectMesh, _Land, _Pobj, PaintColor); } // Redo ? if ((keyDown->Key==KeyE)&&(keyDown->Button==ctrlKeyButton)) { // Redo ! bankCont->Undo.redo (*_Eproc, _VectMesh, _Land, _Pobj, PaintColor); } } } } else if ((nModeTexture==ModeTile)||(nModeTexture==ModeDisplace)) { // bTexture CEventMouse* mouse=(CEventMouse*)&event; if (event==EventMouseDownId) { int x=(int)(mouse->X*(float)MOD_WIDTH); int y=(int)((mouse->Y)*(float)MOD_HEIGHT); int tile=x+y*MOD_WIDTH; // Mode tiles ? if (nModeTexture==ModeTile) { if ((tile>=0)&&(tile<=bank.getTileSetCount())) { // Select bank tileset ? if (tile==0) { _Pobj->CurrentTileSet=-1; } // no... else { // Ask for the tileSet _Pobj->CurrentTileSet=tileSetSelector.getTileSet (tile-1); _Pobj->DisplaceTileSet=_Pobj->CurrentTileSet; } } } // Mode displace ? if (nModeTexture==ModeDisplace) { if ((tile>=0)&&(tileDisplaceTile=tile; } } } } } }; /*-------------------------------------------------------------------*/ // Pick a tile void MouseListener::pick (int mesh, int tile, const CVector& hit, TModePaint mode) { // Pick tile if ((mode==ModeTile)||(mode==ModeDisplace)) { // Pick tile under the cursor tileDesc desc; _Eproc->GetTile (mesh, tile, desc, _VectMesh, _Land); if (desc.getNumLayer ()>0) { // Get info about this tile int tileSet; int number; CTileBank::TTileType type; bank.getTileXRef (desc.getLayer (0).Tile, tileSet, number, type); // Pickup a tile ? if (mode==ModeTile) { // Set the tileSet if this tile set is in the land if (tileSetSelector.isInArray (tileSet)) _Pobj->CurrentTileSet=tileSet; } // Pickup a displace ? else { // Get the displace tile index _Pobj->DisplaceTile=desc.getDisplace (); _Pobj->DisplaceTileSet=tileSet; } } } else if (mode==ModeColor) { // Get the tile pointer EPM_PaintTile *pTile=&_Eproc->metaTile[mesh][tile]; // Coordoninates int u[4]={pTile->u, pTile->u, pTile->u+1, pTile->u+1}; int v[4]={pTile->v, pTile->v+1, pTile->v+1, pTile->v}; CRGBA bestColor; float fBestDist=FLT_MAX; // 4 coordinates for (int i=0; i<4; i++) { // Get another color CVector pos; CRGBA color; PaintColor.pickVertexColor (mesh, pTile->patch, u[i], v[i], pos, color, _VectMesh); // Get new dist float fDist=(pos-hit).norm(); // Better dist ? if (fDist& vectMesh) { if (vectMesh[mesh].Symmetry || vectMesh[mesh].Rotate) { uint patch=tile/NUM_TILE_SEL; uint ttile=tile%NUM_TILE_SEL; uint v=ttile/MAX_TILE_IN_PATCH; uint u=ttile%MAX_TILE_IN_PATCH; // Get the patch int OrderS=(1<getUIPatch (patch).NbTilesU); uint symTile = OrderS*v+u; // Pick tile under the cursor return symVector[mesh].getTileState (patch, symTile, 0); } else return CZoneSymmetrisation::Nothing; } /*-------------------------------------------------------------------*/ DWORD WINAPI myThread (LPVOID vData); int getBindedEdge (int nPatch, int nVertInPatch, const PatchMesh& patch, const RPatchMesh& rpatch) { // Vert number in the patxhmesh int nVertInMesh=patch.patches[nPatch].v[nVertInPatch]; // Should be binded nlassert (rpatch.getUIVertex (nVertInMesh).Binding.bBinded); // Some vertex int nVertexBefore=patch.patches[nPatch].v[(nVertInPatch-1)&3]; int nVertexAfter=patch.patches[nPatch].v[(nVertInPatch+1)&3]; // switch binding sort switch (rpatch.getUIVertex (nVertInMesh).Binding.nType) { case BIND_SINGLE: // Check if the point after is binded to the same patch, same edge if (patch.patches[rpatch.getUIVertex (nVertInMesh).Binding.nPatch].v[rpatch.getUIVertex (nVertInMesh).Binding.nEdge]== patch.patches[nPatch].v[(nVertInPatch+1)&3]) { return nVertInPatch; } else { #ifdef NL_DEBUG const UI_VERTEX &uiv = rpatch.getUIVertex (nVertInMesh); Patch &patch0 = patch.patches[uiv.Binding.nPatch]; Patch &patch1 = patch.patches[nPatch]; uint vertIndex0 = (uiv.Binding.nEdge+1)&3; uint vertIndex1 = (nVertInPatch-1)&3; uint vert0 = patch0.v[vertIndex0]; uint vert1 = patch1.v[vertIndex1]; #endif // NL_DEBUG nlassert (patch.patches[rpatch.getUIVertex (nVertInMesh).Binding.nPatch].v[(rpatch.getUIVertex (nVertInMesh).Binding.nEdge+1)&3]== patch.patches[nPatch].v[(nVertInPatch-1)&3]); return (nVertInPatch-1)&3; } break; case BIND_25: // Check if the point after is binded to the same patch, same edge if ((rpatch.getUIVertex (nVertexBefore).Binding.bBinded)&& (rpatch.getUIVertex (nVertexBefore).Binding.nPatch==rpatch.getUIVertex (nVertInMesh).Binding.nPatch)&& (rpatch.getUIVertex (nVertexBefore).Binding.nEdge==rpatch.getUIVertex (nVertInMesh).Binding.nEdge)&& (rpatch.getUIVertex (nVertexBefore).Binding.nType==BIND_50)) { return (nVertInPatch-1)&3; } else { nlassert (patch.patches[rpatch.getUIVertex (nVertInMesh).Binding.nPatch].v[rpatch.getUIVertex (nVertInMesh).Binding.nEdge]== patch.patches[nPatch].v[(nVertInPatch+1)&3]); return nVertInPatch; } break; case BIND_50: // Check if the point after is binded to the same patch, same edge if ((rpatch.getUIVertex (nVertexBefore).Binding.bBinded)&& (rpatch.getUIVertex (nVertexBefore).Binding.nPatch==rpatch.getUIVertex (nVertInMesh).Binding.nPatch)&& (rpatch.getUIVertex (nVertexBefore).Binding.nEdge==rpatch.getUIVertex (nVertInMesh).Binding.nEdge)&& (rpatch.getUIVertex (nVertexBefore).Binding.nType==BIND_75)) { return (nVertInPatch-1)&3; } else { nlassert (rpatch.getUIVertex (nVertexAfter).Binding.bBinded); nlassert (rpatch.getUIVertex (nVertexAfter).Binding.nPatch==rpatch.getUIVertex (nVertInMesh).Binding.nPatch); nlassert (rpatch.getUIVertex (nVertexAfter).Binding.nEdge==rpatch.getUIVertex (nVertInMesh).Binding.nEdge); nlassert (rpatch.getUIVertex (nVertexAfter).Binding.nType==BIND_25); return nVertInPatch; } break; case BIND_75: // Check if the point after is binded to the same patch, same edge if ((rpatch.getUIVertex (nVertexAfter).Binding.bBinded)&& (rpatch.getUIVertex (nVertexAfter).Binding.nPatch==rpatch.getUIVertex (nVertInMesh).Binding.nPatch)&& (rpatch.getUIVertex (nVertexAfter).Binding.nEdge==rpatch.getUIVertex (nVertInMesh).Binding.nEdge)&& (rpatch.getUIVertex (nVertexAfter).Binding.nType==BIND_50)) { return nVertInPatch; } else { nlassert (patch.patches[rpatch.getUIVertex (nVertInMesh).Binding.nPatch].v[(rpatch.getUIVertex (nVertInMesh).Binding.nEdge+1)&3]== patch.patches[nPatch].v[(nVertInPatch-1)&3]); return (nVertInPatch-1)&3; } break; } nlassert (0); // no! return 0; } void EPM_PaintCMode::EnterMode () { if (pobj->hOpsPanel) { ICustButton *but = GetICustButton(GetDlgItem(pobj->hOpsPanel, IDC_PAINT)); but->SetCheck(TRUE); ReleaseICustButton(but); } } void EPM_PaintCMode::DoPaint () { // Toremove static float best = 10000.f; // Set local to english setlocale (LC_NUMERIC, "English"); if (pobj->hOpsPanel) { bWarningInvalidTileSet=false; // Build paint tile infos.. nlassert (eproc.ip); ModContextList mcList; INodeTab nodes; TimeValue t = eproc.ip->GetTime(); // Build modifier context list eproc.ip->GetModContexts(mcList, nodes); pobj->ClearPatchDataFlag(mcList, EPD_BEENDONE); theHold.Begin(); // Mesh table std::vector vectMesh; makeVectMesh (vectMesh, nodes, mcList, pobj, t); // Size of the map eproc.metaTile.resize (vectMesh.size()); eproc.bitArray.resize (vectMesh.size()); // Calculate the boundind box.. float fMinX=FLT_MAX; float fMinY=FLT_MAX; float fMinZ=FLT_MAX; float fMaxX=-FLT_MAX; float fMaxY=-FLT_MAX; float fMaxZ=-FLT_MAX; int i; for (i = 0; i < (int)vectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = vectMesh[i].PatchData; RPatchMesh *rpatch = vectMesh[i].RMesh; PatchMesh *patch = vectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // hold patchData->RecordTopologyTags(patch); patchData->BeginEdit(t); if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, pobj, patch, rpatch)); // fill the tile map for (int p=0; pnumPatches; p++) { int nU=1<getUIPatch (p).NbTilesU; int nV=1<getUIPatch (p).NbTilesV; for (int u=0; u<=nU; u++) for (int v=0; v<=nV; v++) { Point3 pos=patch->patches[p].interp (patch, (float)u/(float)(nU), (float)v/(float)(nV)); pos=pos*(vectMesh[i].Node->GetObjectTM (t)); if (fMaxXpos.x) fMinX=pos.x; if (fMaxYpos.y) fMinY=pos.y; if (fMaxZpos.z) fMinZ=pos.z; } } } // Create the quad to select CVector center ((fMinX+fMaxX)/2.f, (fMinY+fMaxY)/2.f, (fMinZ+fMaxZ)/2.f); CVector lookFrom (fMaxX, fMaxY, fMaxZ); eproc.quadTreeSelect.create (8, center, (float)std::max (std::max (fMaxX-fMinX, fMaxY-fMinY), fMaxZ-fMinZ)); for (i = 0; i < (int)vectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = vectMesh[i].PatchData; RPatchMesh *rpatch = vectMesh[i].RMesh; PatchMesh *patch = vectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // paint mode rpatch->paint=true; rpatch->BuildMesh(t, *patch); // copy edge eproc.bitArray eproc.bitArray[i]=patch->edgeSel; // clear tile map eproc.metaTile[i].clear(); eproc.metaTile[i].resize (NUM_TILE_SEL*patch->numPatches); // fill the tile map for (int p=0; pnumPatches; p++) { int nU=1<getUIPatch (p).NbTilesU; int nV=1<getUIPatch (p).NbTilesV; for (int u=0; uMesh=i; pTile->patch=p; pTile->tile=p*NUM_TILE_SEL+v*MAX_TILE_IN_PATCH+u; pTile->voisins[3]= v==0 ? NULL : &eproc.metaTile[i][p*NUM_TILE_SEL+(v-1)*MAX_TILE_IN_PATCH+u]; pTile->voisins[1]= v==(nV-1) ? NULL : &eproc.metaTile[i][p*NUM_TILE_SEL+(v+1)*MAX_TILE_IN_PATCH+u]; pTile->voisins[0]= u==0 ? NULL : &eproc.metaTile[i][p*NUM_TILE_SEL+v*MAX_TILE_IN_PATCH+u-1]; pTile->voisins[2]= u==(nU-1) ? NULL : &eproc.metaTile[i][p*NUM_TILE_SEL+v*MAX_TILE_IN_PATCH+u+1]; pTile->rotate[3]=0; pTile->rotate[1]=0; pTile->rotate[0]=0; pTile->rotate[2]=0; pTile->u=u; pTile->v=v; // Compute bouding box of the tile fMinX=FLT_MAX; fMinY=FLT_MAX; fMaxX=-FLT_MAX; fMaxY=-FLT_MAX; // Compute the 4 corners of the tile Point3 pos[4]; pos[0]=patch->patches[p].interp (patch, (float)u/(float)(nU), (float)v/(float)(nV)); pos[0]=pos[0]*(vectMesh[i].Node->GetObjectTM (t)); pos[1]=patch->patches[p].interp (patch, (float)(u+1)/(float)(nU), (float)v/(float)(nV)); pos[1]=pos[1]*(vectMesh[i].Node->GetObjectTM (t)); pos[2]=patch->patches[p].interp (patch, (float)(u+1)/(float)(nU), (float)(v+1)/(float)(nV)); pos[2]=pos[2]*(vectMesh[i].Node->GetObjectTM (t)); pos[3]=patch->patches[p].interp (patch, (float)u/(float)(nU), (float)(v+1)/(float)(nV)); pos[3]=pos[3]*(vectMesh[i].Node->GetObjectTM (t)); // Store its center pTile->Center=(maxToNel (pos[0])+maxToNel (pos[1])+maxToNel (pos[2])+maxToNel (pos[3]))/4.f; // Store its radius pTile->Radius=std::max ( std::max ( (maxToNel (pos[0])-pTile->Center).norm(), (maxToNel (pos[1])-pTile->Center).norm() ), std::max ( (maxToNel (pos[2])-pTile->Center).norm(), (maxToNel (pos[3])-pTile->Center).norm() ) ); // Bounding for (int i=0; i<4; i++) { if (fMaxXpos[i].x) fMinX=pos[i].x; if (fMaxYpos[i].y) fMinY=pos[i].y; if (fMaxZpos[i].z) fMinZ=pos[i].z; } // Insert the tile in the quad tree eproc.quadTreeSelect.insert (CVector (fMinX, fMinY, fMinZ), CVector (fMaxX, fMaxY, fMaxZ), pTile); } } } // Watch for patch voisin for (i = 0; i < (int)vectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = vectMesh[i].PatchData; RPatchMesh *rpatch = vectMesh[i].RMesh; PatchMesh *patch = vectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // fill the tile map for (int p=0; pnumPatches; p++) { // Find a voisin for (int e=0; e<4; e++) { EPM_PaintPatch patchVoisin; patchVoisin.patch=-1; int edgeVoisin; int offsetEdge=0; int dividEdge=0; int mYedge=patch->patches[p].edge[e]; if (mYedge == -1) { std::string error = NLMISC::toString("Invalid edge '%i' with value '%i' in patch '%i' in PatchMesh", p, mYedge, e); nlwarning(error.c_str()); MessageBox(NULL, error.c_str(), "NeL Patch Painter", MB_OK | MB_ICONSTOP); return; } #if (MAX_RELEASE < 4000) int otherPatch=(patch->edges[mYedge].patch1==p)?patch->edges[mYedge].patch2:patch->edges[mYedge].patch1; #else // (MAX_RELEASE < 4000) int otherPatch = -1; if(patch->edges[mYedge].patches.Count()>0) { if (patch->edges[mYedge].patches[0]==p) { if (patch->edges[mYedge].patches.Count() > 1) otherPatch = patch->edges[mYedge].patches[1]; } else otherPatch = patch->edges[mYedge].patches[0]; } #endif // (MAX_RELEASE < 4000) int nMeshIndex; if (otherPatch!=-1) { patchVoisin.patch=otherPatch; patchVoisin.Mesh=i; edgeVoisin=WhereIsTheEdge (otherPatch, mYedge, *patch); nlassert (edgeVoisin!=-1); nMeshIndex=i; } else { // Binding ? int vertBinded=-1; if (rpatch->getUIVertex (patch->patches[p].v[e]).Binding.bBinded) vertBinded=e; if (rpatch->getUIVertex (patch->patches[p].v[(e+1)&3]).Binding.bBinded) vertBinded=(e+1)&3; if ((vertBinded!=-1)&&(getBindedEdge (p, vertBinded, *patch, *rpatch)==e)) { int nVert=patch->patches[p].v[vertBinded]; patchVoisin.patch=rpatch->getUIVertex (nVert).Binding.nPatch; otherPatch=rpatch->getUIVertex (nVert).Binding.nPatch; patchVoisin.Mesh=i; edgeVoisin=rpatch->getUIVertex (nVert).Binding.nEdge; nlassert (edgeVoisin!=-1); nMeshIndex=i; switch (rpatch->getUIVertex (nVert).Binding.nType) { case BIND_25: dividEdge=2; if (vertBinded==e) offsetEdge=3; else offsetEdge=2; break; case BIND_75: dividEdge=2; if (vertBinded==e) offsetEdge=1; else offsetEdge=0; break; case BIND_50: dividEdge=2; if (vertBinded==e) offsetEdge=2; else offsetEdge=1; break; case BIND_SINGLE: dividEdge=1; if (vertBinded==e) offsetEdge=2; else offsetEdge=0; break; } } else // Look in the neighborhood for a voisin { // Vertex Number //Matrix3 m1=*mcList[i]->tm; Matrix3 m1=vectMesh[i].Node->GetObjectTM(t); Point3 vA1=patch->verts[patch->patches[p].v[e]].p * m1; Point3 vB1=patch->verts[patch->patches[p].v[(e+1)&3]].p * m1; // If symmetry, invert if (vectMesh[i].Symmetry) { Point3 tmp = vA1; vA1 = vB1; vB1 = tmp; } // Look for in other patchMesh for (int ii = 0; ii < (int)vectMesh.size(); ii++) { if (ii!=i) { // Get pointers PaintPatchData *patchData2 = vectMesh[ii].PatchData; RPatchMesh *rpatch2 = vectMesh[ii].RMesh; PatchMesh *patch2 = vectMesh[ii].PMesh; if ((!patchData2)||(!patch2)||(!rpatch2)) continue; // Look for an open edge for (int ee=0; eenumEdges; ee++) { #if (MAX_RELEASE < 4000) if ((patch2->edges[ee].patch1==-1)||(patch2->edges[ee].patch2==-1)) { // int pp=patch2->edges[ee].patch1==-1 ? patch2->edges[ee].patch2:patch2->edges[ee].patch1; #else // (MAX_RELEASE < 4000) if (patch2->edges[ee].patches.Count()>0) { // int pp=patch2->edges[ee].patches[0]; #endif // (MAX_RELEASE < 4000) nlassert (pp!=-1); // Edge number int edge=WhereIsTheEdge (pp, ee, *patch2); //Matrix3 m2=*mcList[ii]->tm; Matrix3 m2=vectMesh[ii].Node->GetObjectTM(t); Point3 vA2=patch2->verts[patch2->patches[pp].v[edge]].p * m2; Point3 vB2=patch2->verts[patch2->patches[pp].v[(edge+1)&3]].p * m2; // If symmetry, invert if (vectMesh[ii].Symmetry) { Point3 tmp = vA2; vA2 = vB2; vB2 = tmp; } // The same ? // Toremove if ((vA1-vB2).Length () < best) { best = (vA1-vB2).Length (); } if ((vA2-vB1).Length () < best) { best = (vA2-vB1).Length (); } if (((vA1-vB2).Length ()GetName(); std::string second = vectMesh[patchVoisin.Mesh].Node->GetName(); int rot = (2-((vectMesh[i].Symmetry)?(2-e):e)+((vectMesh[patchVoisin.Mesh].Symmetry)?(2-edgeVoisin):edgeVoisin))&3; int nU = 1 << rpatch->getUIPatch (p).NbTilesU; int nV = 1 << rpatch->getUIPatch (p).NbTilesV; int nUOther = 1 << vectMesh[patchVoisin.Mesh].RMesh->getUIPatch (patchVoisin.patch).NbTilesU; int nVOther = 1 << vectMesh[patchVoisin.Mesh].RMesh->getUIPatch (patchVoisin.patch).NbTilesV; int nTile= (e&1) ? nU : nV; int nTile2= (edgeVoisin&1) ? nUOther : nVOther; if (nTile==(nTile2>>dividEdge)) { int offset=getOffset (e, nU, nV, vectMesh[i].Symmetry); int offsetOther=getOffset (edgeVoisin, nUOther, nVOther, vectMesh[patchVoisin.Mesh].Symmetry); static int delta[4] = { MAX_TILE_IN_PATCH, 1, -MAX_TILE_IN_PATCH, -1 }; int symIndex = vectMesh[i].Symmetry?-1:1; int symIndexOther = vectMesh[patchVoisin.Mesh].Symmetry?-1:1; EPM_PaintTile *pTile1=&eproc.metaTile[i][p*NUM_TILE_SEL+offset]; EPM_PaintTile *pTile2=&eproc.metaTile[nMeshIndex][patchVoisin.patch*NUM_TILE_SEL+offsetOther]; pTile2+=(nTile2-1)*delta[edgeVoisin]*symIndexOther; pTile2-=(delta[edgeVoisin]*symIndexOther*nTile2*offsetEdge)>>2; for (int end=0; endvoisins[e]=pTile2; pTile1->rotate[e]=rot; // tile src.. nlassert ((pTile2->voisins[edgeVoisin]==NULL)||(pTile2->voisins[edgeVoisin]==pTile1)); pTile2->voisins[edgeVoisin]=pTile1; pTile2->rotate[edgeVoisin]=(-rot)&3; pTile1+=delta[e]*symIndex; pTile2-=delta[edgeVoisin]*symIndexOther; } } patch->edgeSel.Clear (mYedge); } else { patch->edgeSel.Set (mYedge); } } } } // Flag frozen and locked tiles for (i = 0; i < (int)vectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = vectMesh[i].PatchData; RPatchMesh *rpatch = vectMesh[i].RMesh; PatchMesh *patch = vectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // fill the tile map for (int p=0; pnumPatches; p++) { int nU=1<getUIPatch (p).NbTilesU; int nV=1<getUIPatch (p).NbTilesV; for (int u=0; ufrozen = vectMesh[pTile->Mesh].Node->IsFrozen() != 0; // Not locked pTile->locked = 0; // Check that neighbor tiles are not frozen uint neighbor; for (neighbor=0; neighbor<4; neighbor++) { // Neighbor exist ? EPM_PaintTile *neighborTile = pTile->voisins[neighbor]; if (neighborTile) { // Not freezed ? if (vectMesh[neighborTile->Mesh].Node->IsFrozen()) pTile->locked |= 1<locked |= 1<eproc=&eproc; pData->pobj=pobj; pData->center=center; pData->T=t; myThread (pData); // Do it without thread // Invalidate all objects for (i = 0; i < (int)vectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = vectMesh[i].PatchData; RPatchMesh *rpatch = vectMesh[i].RMesh; PatchMesh *patch = vectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // End of mode paint rpatch->paint=false; // Invalidate all modified parts patchData->UpdateChanges(patch, rpatch); patchData->TempData(pobj)->Invalidate(PART_ALL); patch->edgeSel=eproc.bitArray[vectMesh[i].McListIndex]; patchData->SetFlag(EPD_BEENDONE, TRUE); } theHold.Accept("Patch change"); nodes.DisposeTemporary(); pobj->ClearPatchDataFlag(mcList, EPD_BEENDONE); pobj->NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); eproc.ip->RedrawViews(t, REDRAW_NORMAL); // Exit painter mode exitPainter (); } // Set locale to current setlocale (LC_NUMERIC, ""); } extern HINSTANCE hInstance; bool loadLigoConfigFile (CLigoConfig& config, Interface& it) { // Get the module path HMODULE hModule = hInstance; if (hModule) { // Get the path char sModulePath[256]; int res=GetModuleFileName(hModule, sModulePath, 256); // Success ? if (res) { // Path char sDrive[256]; char sDir[256]; _splitpath (sModulePath, sDrive, sDir, NULL, NULL); _makepath (sModulePath, sDrive, sDir, "ligoscape", ".cfg"); try { // Load the config file config.readConfigFile (sModulePath, false); // ok return true; } catch (Exception& e) { // Print an error message char msg[512]; smprintf (msg, 512, "Error loading the config file ligoscape.cfg: %s", e.what()); nlwarning (msg); } } } // Can't found the module return false; } DWORD WINAPI myThread (LPVOID vData) { // Mega try try { /*************** new mode paint.. **************/ callThread *pData=(callThread*)vData; // Build paint tile infos.. nlassert (pData->eproc->ip); // Viewport parameters ViewExp* vp; Matrix3 affineTM; float minx,maxx,miny,maxy; vp=pData->eproc->ip->GetActiveViewport(); vp->GetAffineTM(affineTM); if ( vp->IsPerspView() ) { vp->GetGridDims(&minx,&maxx,&miny,&maxy); } // The scene // Loaf cfg files LoadKeyCfg (); LoadVarCfg (); // Init the scene CViewport viewport; try { CNELU::init(MAIN_Width, MAIN_Height, viewport); // Create a Landscape. CLandscapeModel *TheLand= (CLandscapeModel*)CNELU::Scene->createModel(LandscapeModelId); TheLand->Landscape.setTileNear (1000.f); TheLand->Landscape.TileBank=bank; // Enbable automatique lighting TheLand->Landscape.enableAutomaticLighting (false); TheLand->Landscape.setupAutomaticLightDir (LightDirection); TheLand->Landscape.setupStaticLight (LightDiffuse, LightAmbiant, LightMultiply); // ******************* CExportNel export_ (true, true, true, pData->eproc->ip, "NeL Patch Painter", NULL); // Add meshes in the scene if (pData->pobj->includeMeshes) { // Get the root node INode *root=pData->eproc->ip->GetRootNode (); // View all selected objects for (uint nNode=0; nNode<(uint)root->NumberOfChildren(); nNode++) { // Get the node INode* pNode=root->GetChildNode (nNode); // It is a zone ? if (RPO::isZone (*pNode, pData->T)) { } // Try to export a mesh else if (CExportNel::isMesh (*pNode, pData->T)) { // Build skined ? bool skined=false; // Export the shape IShape *pShape; pShape=export_.buildShape (*pNode, pData->T, NULL, true); // Export successful ? if (pShape) { // Add the shape to the view CNELU::ShapeBank->add (CExportNel::getName (*pNode), pShape); // Create an instance CTransformShape *tShape=CNELU::Scene->createInstance (CExportNel::getName (*pNode)); // Big hack to sort TheLand->clipAddChild(tShape); } } } // Setup ambient light CNELU::Driver->setAmbientColor (export_.getAmbientColor (pData->T)); // Build light vector std::vector vectLight; export_.getLights (vectLight, pData->T); // Insert each lights for (uint light=0; lightsetLight (light, vectLight[light]); } // ******************* // Init the camera CMatrix mat; mat.identity(); CVector I,J,K,P; Matrix3 matInvert; matInvert.SetRow (0, Point3(1.f, 0.f, 0.f)); matInvert.SetRow (1, Point3(0.f, 0.f, 1.f)); matInvert.SetRow (2, Point3(0.f, -1.f, 0.f)); matInvert.SetRow (3, Point3(0.f, 0.f, 0.f)); matInvert.Invert(); affineTM.Invert(); affineTM=matInvert*affineTM; I.x= affineTM.GetRow(0).x; I.y= affineTM.GetRow(0).y; I.z= affineTM.GetRow(0).z; J.x= affineTM.GetRow(1).x; J.y= affineTM.GetRow(1).y; J.z= affineTM.GetRow(1).z; K.x= affineTM.GetRow(2).x; K.y= affineTM.GetRow(2).y; K.z= affineTM.GetRow(2).z; P.x= affineTM.GetTrans().x; P.y= affineTM.GetTrans().y; P.z= affineTM.GetTrans().z; mat.setRot(I, J, K); mat.setPos(P); CNELU::Camera->setTransformMode (ITransformable::DirectMatrix); CNELU::Camera->setMatrix (mat); CNELU::Camera->setPerspective( 75.f*(float)Pi/180.f/*vp->GetFOV()*/, 1.33f, 0.1f, 1000.f); // Resize the sym vector symVector.resize (pData->VectMesh.size()); // Form each zone uint i; for (i = 0; i <(int)pData->VectMesh.size(); i++) { // Get pointers PaintPatchData *patchData = pData->VectMesh[i].PatchData; RPatchMesh *rpatch = pData->VectMesh[i].RMesh; PatchMesh *patch = pData->VectMesh[i].PMesh; if ((!patchData)||(!patch)||(!rpatch)) continue; // Get the symmetry flag CLigoConfig config; if (!loadLigoConfigFile (config, *pData->eproc->ip)) { config.CellSize = 100; config.Snap = 1; config.ZoneSnapShotRes = 128; } // Create the zone.. CZone zone; if (rpatch->exportZone (pData->VectMesh[i].Node, patch, zone, symVector[i], i, config.CellSize, config.Snap, true)) { // Smooth corner CZoneCornerSmoother cornerSmoother; std::vector emptyVector; cornerSmoother.computeAllCornerSmoothFlags (&zone, emptyVector); // Add the zone TheLand->Landscape.addZone (zone); } else { char message[512]; smprintf (message, 512, "Can't build the zone named %s", pData->VectMesh[i].Node->GetName()); MessageBox (pData->eproc->ip->GetMAXHWnd(), message, "NeL Painter", MB_OK|MB_ICONEXCLAMATION); } } // Check zones #ifdef NL_DEBUG TheLand->Landscape.checkBinds(); #endif // NL_DEBUG // Refine les zones TheLand->Landscape.setRefineMode (true); // Go. //======== CEvent3dMouseListener mouseListener; MouseListener listener (pData->eproc->ip, CNELU::Camera, &viewport, pData->pobj, pData->eproc, &TheLand->Landscape, &CNELU::AsyncListener, &mouseListener, pData->VectMesh, pData->T); // Mouse listener CNELU::EventServer.addListener (EventMouseMoveId, &listener); CNELU::EventServer.addListener (EventMouseDownId, &listener); CNELU::EventServer.addListener (EventMouseUpId, &listener); CNELU::EventServer.addListener (EventMouseDblClkId, &listener); CNELU::EventServer.addListener (EventDestroyWindowId, &listener); CNELU::EventServer.addListener (EventKeyDownId, &listener); // Camera position // Mouse listener mouseListener.setMatrix (CNELU::Camera->getMatrix()); mouseListener.setFrustrum (CNELU::Camera->getFrustum()); mouseListener.setViewport (viewport); mouseListener.setHotSpot (pData->center); mouseListener.setMouseMode (CEvent3dMouseListener::edit3d); mouseListener.addToServer(CNELU::EventServer); // *** Flush all selected tileset... if (pData->pobj->preloadTiles) { // For all the tileset selected for (sint tss=0; tss<(sint)tileSetSelector.getTileCount (); tss++) { // Get the tileset index sint ts=tileSetSelector.getTileSet (tss); // Get the tileset pointer CTileSet *tileSet=bank.getTileSet (ts); nlassert (tileSet); // Flush all its 128x128 tiles sint tl; for (tl=0; tlgetNumTile128(); tl++) TheLand->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile128(tl), 1); // Flush all its 256x256 tiles for (tl=0; tlgetNumTile256(); tl++) TheLand->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile256(tl), 1); // Flush all its transisitons tiles for (tl=0; tlLandscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTransition(tl)->getTile (), 1); } } // Setup lights CPaintLight lights; lights.build (*pData->eproc->ip); lights.setup (TheLand->Landscape, *CNELU::Scene); // MAIN LOOP do { // Pump events CNELU::EventServer.pump(); // Call the proc mainproc(*CNELU::Scene, CNELU::AsyncListener, mouseListener, *TheLand, *CNELU::Scene->getDriver(), pData, listener.PaintColor); } while (!CNELU::AsyncListener.isKeyPushed(KeyESCAPE)&&listener.WindowActive); // Release the emitter from the server mouseListener.removeFromServer (CNELU::EventServer); CNELU::Scene->getDriver()->release (); // Mouse listener CNELU::EventServer.removeListener (EventMouseMoveId, &listener); CNELU::EventServer.removeListener (EventMouseDownId, &listener); CNELU::EventServer.removeListener (EventMouseUpId, &listener); CNELU::EventServer.removeListener (EventMouseDblClkId, &listener); CNELU::EventServer.removeListener (EventKeyDownId, &listener); CNELU::EventServer.removeListener (EventDestroyWindowId, &listener); // End. //======== CNELU::release(); } catch (EDru& druExcept) { MessageBox (NULL, druExcept.what(), "NeL driver utility", MB_OK|MB_ICONEXCLAMATION); } delete pData; } catch (Exception& e) { MessageBox (NULL, e.what(), "NeL Painter", MB_OK|MB_ICONEXCLAMATION); } return 0; } /*-------------------------------------------------------------------*/ void EPM_PaintCMode::ExitMode() { pobj->channelModified=EDITPAT_CHANNELS; if (pobj->hOpsPanel) { ICustButton *but = GetICustButton(GetDlgItem(pobj->hOpsPanel, IDC_PAINT)); but->SetCheck(FALSE); ReleaseICustButton(but); // Build paint tile infos.. nlassert (eproc.ip); } } /*-------------------------------------------------------------------*/ class NelPatchHitData : public HitData { public: NelPatchHitData (int index, int type, int mesh) { Index=index; Type=type; Mesh=mesh; } int Index; int Type; int Mesh; }; BOOL EPM_PaintMouseProc::HitATile(ViewExp *vpt, IPoint2 *p, int *tile, int *mesh, TimeValue t, std::vector& vectMesh, CVector &hit, CVector &topVector) { // Get a world ray with the mouse 2d point Ray ray; vpt->MapScreenToWorldRay((float)p->x, (float)p->y, ray); // Select tree's node with the world ray quadTreeSelect.clearSelection (); quadTreeSelect.selectRay (CVector (ray.p.x, ray.p.y, ray.p.z), CVector (ray.dir.x, ray.dir.y, ray.dir.z)); // Get selected nodes.. CQuadTree::CIterator it=quadTreeSelect.begin(); while (it!=quadTreeSelect.end()) { // Check if the ray intersect the tile.. if ((*it)->intersect (ray, vectMesh, t, hit, topVector)) { *tile = (*it)->tile; *mesh = (*it)->Mesh; return TRUE; } it++; } return FALSE; } BOOL EPM_PaintMouseProc::HitATile(const CViewport& viewport, const CCamera& camera, float x, float y, int *tile, int *mesh, TimeValue t, std::vector& vectMesh, NLMISC::CVector& hit, NLMISC::CVector &topVector) { // Get a world ray with the mouse 2d point CVector pos, dir; viewport.getRayWithPoint (x, y, pos, dir, camera.getMatrix(), camera.getFrustum()); // Select tree's node with the world ray quadTreeSelect.clearSelection (); quadTreeSelect.selectRay (pos, dir); // Get selected nodes.. CQuadTree::CIterator it=quadTreeSelect.begin(); BOOL bRet=FALSE; float fMin=FLT_MAX; while (it!=quadTreeSelect.end()) { // Check if the ray intersect the tile.. Ray ray; ray.p.x=pos.x; ray.p.y=pos.y; ray.p.z=pos.z; ray.dir.x=dir.x; ray.dir.y=dir.y; ray.dir.z=dir.z; CVector hit2; if ((*it)->intersect (ray, vectMesh, t, hit2, topVector)) { float newDist=(hit2-camera.getMatrix().getPos()).norm(); if (newDisttile; *mesh = (*it)->Mesh; fMin=newDist; hit=hit2; bRet=TRUE; } } it++; } return bRet; } /*-------------------------------------------------------------------*/ bool CheckTri (const Point3& pos0, const Point3& pos1, const Point3& pos2, const Ray& ray, CVector& hit) { // Vectors in Nel format CVector v0 (pos0.x, pos0.y, pos0.z); CVector v1 (pos1.x, pos1.y, pos1.z); CVector v2 (pos2.x, pos2.y, pos2.z); CVector pos (ray.p.x, ray.p.y, ray.p.z); CVector dir (ray.dir.x, ray.dir.y, ray.dir.z); CVector center=v0+v1+v2; center/=3.f; dir.normalize (); // A second point on the ray hit=pos+dir; // Plane of the tri CPlane plane; plane.make (v0, v1, v2); // Normale CVector normal=plane.getNormal(); // Devant ? if ((plane*pos)<0.f) return false; // Derrière ? if ((dir*(center-pos))<0.f) return false; // Point on the plane hit=plane.intersect (pos, pos+dir); //(D*p/(D-d))*dir+pos; // Check the point... bool positive=(((v0-hit)^(v1-hit))*normal>0.f); if ((((v1-hit)^(v2-hit))*normal>0.f)!=positive) return false; return ((((v2-hit)^(v0-hit))*normal>0.f)==positive); } /*-------------------------------------------------------------------*/ bool EPM_PaintTile::intersect (const Ray& ray, std::vector& vectMesh, TimeValue t, CVector& hit, CVector& topVector) { // Pointer on the patch mesh PatchMesh *patchPtr=vectMesh[Mesh].PMesh; RPatchMesh *rpatch=vectMesh[Mesh].RMesh; INode *node=vectMesh[Mesh].Node; // Nb tile in this patch int nU=1<getUIPatch (patch).NbTilesU; int nV=1<getUIPatch (patch).NbTilesV; // 4 corners Point3 pos[4]; pos[0]=patchPtr->patches[patch].interp (patchPtr, (float)u/(float)(nU), (float)v/(float)(nV)); pos[0]=pos[0]*(node->GetObjectTM (t)); pos[1]=patchPtr->patches[patch].interp (patchPtr, (float)u/(float)(nU), (float)(v+1)/(float)(nV)); pos[1]=pos[1]*(node->GetObjectTM (t)); pos[2]=patchPtr->patches[patch].interp (patchPtr, (float)(u+1)/(float)(nU), (float)(v+1)/(float)(nV)); pos[2]=pos[2]*(node->GetObjectTM (t)); pos[3]=patchPtr->patches[patch].interp (patchPtr, (float)(u+1)/(float)(nU), (float)v/(float)(nV)); pos[3]=pos[3]*(node->GetObjectTM (t)); // Symmetry ? if (vectMesh[Mesh].Symmetry) { Point3 tmp = pos[0]; pos[0] = pos[3]; pos[3] = tmp; tmp = pos[1]; pos[1] = pos[2]; pos[2] = tmp; } // Get the normal Point3 up = (pos[1]-pos[0]) ^ (pos[2]-pos[0]); topVector.x = up.x; topVector.y = up.y; topVector.z = up.z; topVector.normalize (); // Check first tri if (CheckTri (pos[0], pos[1], pos[3], ray, hit)) return true; // Check second tri if (CheckTri (pos[1], pos[2], pos[3], ray, hit)) return true; // No intersection return false; } /*-------------------------------------------------------------------*/ int EPM_PaintMouseProc::proc( HWND hwnd, int msg, int point, int flags, IPoint2 m) { ViewExp *vpt = ip->GetViewport(hwnd); int res = TRUE; static PatchMesh *shape1 = NULL; static int poly1, tile1, tile2, mesh1, mesh2, seg1; static bool pressed=false; static IPoint2 anchor, lastPoint; switch (msg) { case MOUSE_PROPCLICK: ip->SetStdCommandMode(CID_OBJMOVE); break; case MOUSE_POINT: if (point) PaintPatchMod::paintMode->DoPaint (); break; case MOUSE_MOVE: break; case MOUSE_FREEMOVE: break; case MOUSE_ABORT: ip->SetStdCommandMode(CID_OBJMOVE); break; } if (vpt) ip->ReleaseViewport(vpt); return res; } /*-------------------------------------------------------------------*/