4596 lines
126 KiB
C++
4596 lines
126 KiB
C++
#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<CZoneSymmetrisation> 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; t<num; t++)
|
||
{
|
||
// TileSet number
|
||
int tileSet=tileSetSelector.getTileSet (t);
|
||
|
||
// 128 tile in this bank ?
|
||
if (bank.getTileSet (tileSet)->getNumTile128()>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; t<CTileSet::CountDisplace; t++)
|
||
{
|
||
// Get the texture
|
||
ITexture *texture=bankCont->TileSet[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<EPM_Mesh> &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<EPM_Mesh>& 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<EPM_Mesh>& 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<<vectMesh[mesh].RMesh->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; l<desc.getNumLayer (); l++)
|
||
{
|
||
// Tile and rot
|
||
uint tile = desc.getLayer (l).Tile;
|
||
uint tileRotation = desc.getLayer (l).Rotate;
|
||
|
||
// XRef
|
||
int tileSet;
|
||
int number;
|
||
CTileBank::TTileType type;
|
||
bank.getTileXRef (tile, tileSet, number, type);
|
||
|
||
// Get rot and symmetry for this tile
|
||
uint tileRotate = rotate;
|
||
bool tileSymmetry = symmetry;
|
||
|
||
// Get the state of the
|
||
bool goofy = false;
|
||
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, 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<EPM_Mesh>& 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<EPM_Mesh>& vectMesh, CLandscape* land,
|
||
CNelPatchChanger& nelPatchChg, std::vector<CBackupValue>* 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<vectMesh.size(); i++)
|
||
{
|
||
// Same mesh ?
|
||
if (vectMesh[i].RMesh == vectMesh[mesh].RMesh)
|
||
{
|
||
int patch=tile/NUM_TILE_SEL;
|
||
int ttile=tile%NUM_TILE_SEL;
|
||
int v=ttile/MAX_TILE_IN_PATCH;
|
||
int u=ttile%MAX_TILE_IN_PATCH;
|
||
|
||
// Get the patch
|
||
std::vector<CTileElement>& copyZone = *nelPatchChg.getTileArray (i, patch);
|
||
RPatchMesh *pMesh=vectMesh[i].RMesh;
|
||
int OrderS=(1<<pMesh->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<EPM_Mesh>& 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<uint>& 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<uint>& 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<EPM_Mesh>& 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<EPM_Mesh>& 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<EPM_PaintTile*>& visited, std::vector<EPM_Mesh>& 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<CBackupValue> 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<tileSetIndex> 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]<setIndex[l]);
|
||
|
||
// Fill the edge
|
||
for (uint e=0; e<4; e++)
|
||
{
|
||
// Two filled ?
|
||
if (bFilled[e] && bFilled[(e+1)&3])
|
||
border[e]=CTileSet::_1111;
|
||
else if ( (!bFilled[e]) && (!bFilled[(e+1)&3]) )
|
||
border[e]=CTileSet::_0000;
|
||
else
|
||
{
|
||
// Found
|
||
bool found = false;
|
||
|
||
// Get neighbor edge
|
||
if (pTile->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<EPM_Mesh>& 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)&&(tile<bank.getTileCount ()))
|
||
{
|
||
bank.getTileXRef (tile, tileSet, number, type);
|
||
|
||
// Pointer on the tileset
|
||
const CTileSet *pTileSet=bank.getTileSet (tileSet);
|
||
|
||
// Set the new displacement map
|
||
desc.setDisplace (pobj->DisplaceTile);
|
||
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<EPM_Mesh>& 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<nLayer; nL++)
|
||
{
|
||
// GetTileIndex
|
||
tileIndex index=backup.getLayer (nL);
|
||
pIndexx[nL]=index;
|
||
|
||
// Get layer info from base
|
||
int tileSet;
|
||
int number;
|
||
CTileBank::TTileType type;
|
||
|
||
if ((int)index.Tile>=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<EPM_Mesh>& vectMesh, CLandscape *land)
|
||
{
|
||
int nLayer=-1;
|
||
tileDesc desc;
|
||
GetTile (tile->voisins[border]->Mesh, tile->voisins[border]->tile, desc, vectMesh, land);
|
||
for (int o=0; o<desc.getNumLayer(); o++)
|
||
{
|
||
tileIndex index=desc.getLayer(o);
|
||
index.Rotate-=tile->rotate[border];
|
||
index.Rotate&=3;
|
||
|
||
CTileBank::TTileType type;
|
||
int TileSet, number;
|
||
|
||
if ((int)index.Tile<bank.getTileCount())
|
||
{
|
||
bank.getTileXRef (index.Tile, TileSet, number, type);
|
||
if ((TileSet==tileSet)&&(index.Rotate==rotate))
|
||
nLayer=o;
|
||
}
|
||
}
|
||
|
||
return nLayer;
|
||
}
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
|
||
bool EPM_PaintMouseProc::PropagateBorder (EPM_PaintTile* tile, int curRotation, int curTileSet, std::set<EPM_PaintTile*>& visited,
|
||
const CTileBank& bank, std::vector<EPM_Mesh>& vectMesh,
|
||
CLandscape* land, CNelPatchChanger& nelPatchChg, std::vector<CBackupValue>& 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]<pVoisinCorner[(edge+1)&3])
|
||
{
|
||
nCorner[v]=pVoisinCorner[(edge+1)&3];
|
||
extraOrdinarySmallEdge[v]=(v-1)&3;
|
||
}
|
||
else
|
||
extraOrdinarySmallEdge[v]=v;
|
||
}
|
||
else
|
||
{
|
||
// Bad corner
|
||
return false;
|
||
}
|
||
extraOrdinary[v]++;
|
||
bDiff=true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (nCorner[v]!=pVoisinCorner[(edge+1)&3])
|
||
{
|
||
nCorner[v]=pVoisinCorner[(edge+1)&3];
|
||
bDiff=true;
|
||
bModified[v]=true;
|
||
}
|
||
bTouched[v]=true;
|
||
}
|
||
int nNextCorner=(v+1)&3;
|
||
if (bTouched[nNextCorner])
|
||
{
|
||
if (nCorner[nNextCorner]!=pVoisinCorner[edge])
|
||
{
|
||
// Check if it is a valid corner
|
||
int delta=(pVoisinCorner[edge].Rotate-nCorner[nNextCorner].Rotate)&3;
|
||
if (delta != 2)
|
||
{
|
||
if (nCorner[nNextCorner]<pVoisinCorner[edge])
|
||
{
|
||
nCorner[nNextCorner]=pVoisinCorner[edge];
|
||
extraOrdinarySmallEdge[nNextCorner]=nNextCorner;
|
||
}
|
||
else
|
||
extraOrdinarySmallEdge[nNextCorner]=v;
|
||
}
|
||
else
|
||
{
|
||
// Bad corner
|
||
return false;
|
||
}
|
||
extraOrdinary[nNextCorner]++;
|
||
bDiff=true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (nCorner[nNextCorner]!=pVoisinCorner[edge])
|
||
{
|
||
nCorner[nNextCorner]=pVoisinCorner[edge];
|
||
bDiff=true;
|
||
bModified[nNextCorner]=true;
|
||
}
|
||
bTouched[nNextCorner]=true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Frozen ?
|
||
bool _isLocked = isLocked (pobj, tile);
|
||
|
||
// Force to visite tile in the same 256 in 256 mode..
|
||
if (_256 && !tile->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<tileSetIndex> setIndex;
|
||
std::vector<tileSetIndex> 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<tileSetIndex>::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<<f))
|
||
{
|
||
// Try to get a tile from this group
|
||
nTile=selectTile (ite->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 (*ite<nCorner[c])
|
||
{
|
||
// no,
|
||
wanted=CTileSet::_1000;
|
||
invWanted=CTileSet::_1110;
|
||
}
|
||
else
|
||
{
|
||
// yes,
|
||
wanted=CTileSet::_1110;
|
||
invWanted=CTileSet::_1000;
|
||
}
|
||
border[c]=wanted;
|
||
|
||
// If voisin already visited, force his transition to 3/4
|
||
if (tile->voisins[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 (*ite<nCorner[c])
|
||
{
|
||
// no,
|
||
wanted=CTileSet::_0001;
|
||
invWanted=CTileSet::_0111;
|
||
}
|
||
else
|
||
{
|
||
// yes,
|
||
wanted=CTileSet::_0111;
|
||
invWanted=CTileSet::_0001;
|
||
}
|
||
border[c]=wanted;
|
||
|
||
// If voisin already visited, force his transition to 3/4
|
||
if (tile->voisins[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 (myCost<cost)
|
||
{
|
||
cost=myCost;
|
||
deltaX=myDeltaX;
|
||
deltaY=myDeltaY;
|
||
best=i;
|
||
ret=myRet;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (ret!=0xff)
|
||
return (from->rotate[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<EPM_Mesh>& vectMesh, CLandscape* land,
|
||
int recurs, std::set<EPM_PaintTile*>& alreadyRecursed, bool first, int rotation,
|
||
CNelPatchChanger& nelPatchChg, bool _256)
|
||
{
|
||
if (alreadyRecursed.find (pTile)==alreadyRecursed.end())
|
||
{
|
||
alreadyRecursed.insert (pTile);
|
||
std::set<EPM_PaintTile*> 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<EPM_Mesh>& vectMesh, CLandscape* land,
|
||
int recurs, std::set<EPM_PaintTile*>& 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<EPM_Mesh>& vectMesh, CLandscape* land,
|
||
int recurs, std::set<EPM_PaintTile*>& 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<desc.getNumLayer(); i++)
|
||
{
|
||
if ((sint)desc.getLayer (i).Tile==tile)
|
||
{
|
||
EPM_PaintMouseProc::Rotation=desc.getLayer (i).Rotate;
|
||
break;
|
||
}
|
||
}
|
||
if (i==desc.getNumLayer())
|
||
EPM_PaintMouseProc::Rotation=desc.getLayer (0).Rotate;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
EPM_PaintTile* pOldTile=&metaTile[MeshOld][tileOld];
|
||
int deltaX;
|
||
int deltaY;
|
||
int cost;
|
||
uint8 deltaRot=CalcRotPath (pOldTile, pTile, DEPTH_SEARCH_MAX, 0, deltaX, deltaY, cost);
|
||
|
||
if (deltaRot!=0xff)
|
||
{
|
||
EPM_PaintMouseProc::Rotation+=deltaRot;
|
||
EPM_PaintMouseProc::Rotation&=3;
|
||
}
|
||
else
|
||
lost=true;
|
||
}
|
||
|
||
if (!lost)
|
||
{
|
||
{
|
||
std::set<EPM_PaintTile*> 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<EPM_Mesh>& vectMesh) : VectMesh (vectMesh) {}
|
||
EPM_PaintMouseProc *eproc;
|
||
PaintPatchMod *pobj;
|
||
CVector center;
|
||
TimeValue T;
|
||
std::vector<EPM_Mesh>& 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; i<landscape.Landscape.TileBank.getTileCount(); i++)
|
||
{
|
||
landscape.Landscape.TileBank.getTile(i)->setFileName (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; i<landscape.Landscape.TileBank.getTileCount(); i++)
|
||
{
|
||
landscape.Landscape.releaseTiles(i, 1);
|
||
}
|
||
}
|
||
|
||
// Touch all patches
|
||
for (uint zone=0; zone<pData->VectMesh.size(); zone++)
|
||
{
|
||
// Get the zone
|
||
CZone *pZone=landscape.Landscape.getZone (zone);
|
||
if (pZone)
|
||
{
|
||
// For each patch
|
||
uint numPatch=pZone->getNumPatchs();
|
||
for (uint patch=0; patch<numPatch; patch++)
|
||
{
|
||
// Invalidate this patch
|
||
pZone->changePatchTextureAndColor (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; zone<pData->VectMesh.size(); zone++)
|
||
{
|
||
// Get the zone
|
||
CZone *pZone=landscape.Landscape.getZone (zone);
|
||
nlassert (pZone);
|
||
|
||
// For each patch
|
||
uint numPatch=pZone->getNumPatchs();
|
||
for (uint patch=0; patch<numPatch; patch++)
|
||
{
|
||
// Invalidate this patch
|
||
pZone->changePatchTextureAndColor (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<EPM_Mesh>& _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<EPM_Mesh>& 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<EPM_Mesh>& 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<EPM_PaintTile*> 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<EPM_PaintTile*> 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)&&(tile<CTileSet::CountDisplace))
|
||
{
|
||
_Pobj->DisplaceTile=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<fBestDist)
|
||
{
|
||
bestColor=color;
|
||
fBestDist=fDist;
|
||
}
|
||
}
|
||
|
||
// Set the color
|
||
color1=nelToMax (bestColor);
|
||
}
|
||
}
|
||
|
||
/*-------------------------------------------------------------------*/
|
||
// Pick a tile
|
||
CZoneSymmetrisation::TState MouseListener::getState (int mesh, int tile, const CVector& hit, std::vector<EPM_Mesh>& 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<<vectMesh[mesh].RMesh->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<EPM_Mesh> 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; p<patch->numPatches; p++)
|
||
{
|
||
int nU=1<<rpatch->getUIPatch (p).NbTilesU;
|
||
int nV=1<<rpatch->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 (fMaxX<pos.x)
|
||
fMaxX=pos.x;
|
||
if (fMinX>pos.x)
|
||
fMinX=pos.x;
|
||
if (fMaxY<pos.y)
|
||
fMaxY=pos.y;
|
||
if (fMinY>pos.y)
|
||
fMinY=pos.y;
|
||
if (fMaxZ<pos.z)
|
||
fMaxZ=pos.z;
|
||
if (fMinZ>pos.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; p<patch->numPatches; p++)
|
||
{
|
||
int nU=1<<rpatch->getUIPatch (p).NbTilesU;
|
||
int nV=1<<rpatch->getUIPatch (p).NbTilesV;
|
||
for (int u=0; u<nU; u++)
|
||
for (int v=0; v<nV; v++)
|
||
{
|
||
EPM_PaintTile *pTile=&eproc.metaTile[i][p*NUM_TILE_SEL+v*MAX_TILE_IN_PATCH+u];
|
||
pTile->Mesh=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 (fMaxX<pos[i].x)
|
||
fMaxX=pos[i].x;
|
||
if (fMinX>pos[i].x)
|
||
fMinX=pos[i].x;
|
||
if (fMaxY<pos[i].y)
|
||
fMaxY=pos[i].y;
|
||
if (fMinY>pos[i].y)
|
||
fMinY=pos[i].y;
|
||
if (fMaxZ<pos[i].z)
|
||
fMaxZ=pos[i].z;
|
||
if (fMinZ>pos[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; p<patch->numPatches; 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; ee<patch2->numEdges; 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 ()<WELD_THRESOLD)&&((vA2-vB1).Length ()<WELD_THRESOLD))
|
||
{
|
||
// The same!!
|
||
patchVoisin.patch=pp;
|
||
patchVoisin.Mesh=ii;
|
||
edgeVoisin=edge;
|
||
nMeshIndex=ii;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (patchVoisin.patch!=-1)
|
||
{
|
||
std::string first = vectMesh[i].Node->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; end<nTile; end++)
|
||
{
|
||
// tile dest
|
||
pTile1->voisins[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; p<patch->numPatches; p++)
|
||
{
|
||
int nU=1<<rpatch->getUIPatch (p).NbTilesU;
|
||
int nV=1<<rpatch->getUIPatch (p).NbTilesV;
|
||
for (int u=0; u<nU; u++)
|
||
for (int v=0; v<nV; v++)
|
||
{
|
||
EPM_PaintTile *pTile=&eproc.metaTile[i][p*NUM_TILE_SEL+v*MAX_TILE_IN_PATCH+u];
|
||
|
||
// frozen ?
|
||
pTile->frozen = 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<<neighbor;
|
||
}
|
||
else
|
||
pTile->locked |= 1<<neighbor;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
std::string sName=GetBankPathName ();
|
||
if (sName!="")
|
||
{
|
||
CIFile file;
|
||
if (file.open (sName))
|
||
{
|
||
try
|
||
{
|
||
bank.clear();
|
||
bank.serial (file);
|
||
bank.computeXRef ();
|
||
}
|
||
catch (EStream& stream)
|
||
{
|
||
MessageBox (NULL, stream.what(), "Error", MB_OK|MB_ICONEXCLAMATION);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Enter painter mode
|
||
enterPainter (bank);
|
||
|
||
// Select the tilesets to use
|
||
tileSetSelector.setSelection (GetBankTileSetSet (), bank);
|
||
|
||
// Create a paint thread..
|
||
//DWORD id;
|
||
callThread *pData=new callThread (vectMesh);
|
||
pData->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<CLight> vectLight;
|
||
export_.getLights (vectLight, pData->T);
|
||
|
||
// Insert each lights
|
||
for (uint light=0; light<vectLight.size(); light++)
|
||
CNELU::Driver->setLight (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<CZone*> 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; tl<tileSet->getNumTile128(); tl++)
|
||
TheLand->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile128(tl), 1);
|
||
|
||
// Flush all its 256x256 tiles
|
||
for (tl=0; tl<tileSet->getNumTile256(); tl++)
|
||
TheLand->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile256(tl), 1);
|
||
|
||
// Flush all its transisitons tiles
|
||
for (tl=0; tl<CTileSet::count; tl++)
|
||
TheLand->Landscape.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<EPM_Mesh>& 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<EPM_PaintTile*>::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<EPM_Mesh>& 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<EPM_PaintTile*>::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 (newDist<fMin)
|
||
{
|
||
*tile = (*it)->tile;
|
||
*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<72>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<EPM_Mesh>& 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<<rpatch->getUIPatch (patch).NbTilesU;
|
||
int nV=1<<rpatch->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;
|
||
}
|
||
|
||
/*-------------------------------------------------------------------*/
|