#ifndef __EDITPATCH_H__ #define __EDITPATCH_H__ #include "resource.h" #include #include #if MAX_VERSION_MAJOR >= 14 # include #else # include #endif #include "namesel.h" #include "nsclip.h" #include "sbmtlapi.h" #include "../nel_patch_lib/rpo.h" #include "nel/3d/tile_bank.h" #include "nel/3d/quad_tree.h" #include "nel/misc/rgba.h" #include #include "../nel_patch_lib/vertex_neighborhood.h" // For MAX_RELEASE #include namespace NL3D { class CCamera; class CViewport; class CLandscape; } using namespace NL3D; using namespace NLMISC; #define COLOR_BRUSH_STEP 10 #define COLOR_BRUSH_MIN (2.f) #define COLOR_BRUSH_MAX (32.f) #define Alert(x) MessageBox(GetActiveWindow(),x,_T("Alert"),MB_OK); #define EDITPAT_CHANNELS (PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_DISPLAY|PART_TOPO|TEXMAP_CHANNEL) #define CID_EP_PAINT CID_EP_BEVEL+5 // These are values for selLevel. #define EP_OBJECT 0 #define EP_VERTEX 1 #define EP_EDGE 2 #define EP_PATCH 3 #define EP_TILE 4 // Named selection set levels: #define EP_NS_VERTEX 0 #define EP_NS_EDGE 1 #define EP_NS_PATCH 2 // Conversion from selLevel to named selection level: static int namedSetLevel[] = { EP_NS_VERTEX, EP_NS_VERTEX, EP_NS_EDGE, EP_NS_PATCH }; static int namedClipLevel[] = { CLIP_P_VERT, CLIP_P_VERT, CLIP_P_EDGE, CLIP_P_PATCH }; #define MAX_MATID 0xffff #define UNDEFINED 0xffffffff class PaintPatchMod; class CNelPatchChanger; #define BRUSH_COUNT 3 inline int getScriptAppDataPatchMesh (Animatable *node, uint32 id, int def) { // Get the chunk AppDataChunk *ap=node->GetAppDataChunk (MAXSCRIPT_UTILITY_CLASS_ID, UTILITY_CLASS_ID, id); // Not found ? return default if (ap==NULL) return def; // String to int int value; if (sscanf ((const char*)ap->data, "%d", &value)==1) return value; else return def; } struct EPM_Mesh { EPM_Mesh (PatchMesh *pmesh, RPatchMesh *rmesh, class PaintPatchData *patchData, INode* node, ModContext *mod, int mcListIndex, bool original) { PMesh=pmesh; RMesh=rmesh; PatchData=patchData; Node=node; Mod=mod; McListIndex=mcListIndex; Original = original; Rotate = getScriptAppDataPatchMesh (node, NEL3D_APPDATA_ZONE_ROTATE, 0); Symmetry = getScriptAppDataPatchMesh (node, NEL3D_APPDATA_ZONE_SYMMETRY, 0) != 0; } PatchMesh *PMesh; RPatchMesh *RMesh; INode *Node; ModContext *Mod; uint Rotate; bool Symmetry; bool Original; int McListIndex; class PaintPatchData *PatchData; }; struct EPM_PaintTile { EPM_PaintTile () { Mesh=-1; frozen = false; locked = 0; } EPM_PaintTile* get2Voisin (int i) { if (voisins[i]) { return voisins[i]->voisins[(i+rotate[i])&3]; } return NULL; } int get2VoisinRotate (int i) { if (voisins[i]) { return rotate[i]+voisins[i]->rotate[(i+rotate[i])&3]; } return NULL; } EPM_PaintTile* getRight256 (int rot, int& _rotate) { _rotate=rotate[(2+rot)&3]; return voisins[(2+rot)&3]; } EPM_PaintTile* getBottom256 (int rot, int& _rotate) { _rotate=rotate[(1+rot)&3]; return voisins[(1+rot)&3]; } EPM_PaintTile* getRightBottom256 (int rot, int& _rotate) { int rightRot; EPM_PaintTile* right=getRight256 (rot, rightRot); if (right) return right->getBottom256 ((rot-rightRot)&3, _rotate); else return NULL; } EPM_PaintTile* getBottomRight256 (int rot, int& _rotate) { int bottomRot; EPM_PaintTile* bottom=getBottom256 (rot, bottomRot); if (bottom) return bottom->getRight256 ((rot-bottomRot)&3, _rotate); else return NULL; } bool validFor256 (int rot) { int _rotate; if (!getRight256 (rot, _rotate)) return false; if (!getBottom256 (rot, _rotate)) return false; if (!getRightBottom256 (rot, _rotate)) return false; if (getRightBottom256 (rot, _rotate)!=getBottomRight256 (rot, _rotate)) return false; return true; } void set256 (int rotate) { } bool intersect (const Ray& ray, std::vector& vectMesh, TimeValue t, NLMISC::CVector& hit, CVector& topVector); sint32 patch; sint32 tile; sint16 Mesh; uint8 u; uint8 v; bool frozen; uint8 locked; EPM_PaintTile* voisins[4]; uint8 rotate[4]; CVector Center; float Radius; }; /*-------------------------------------------------------------------*/ class tileSetIndex { public: int TileSet; int Rotate; bool operator< (const tileSetIndex& other) const { if (TileSetother.TileSet) return false; int delta=(other.Rotate-Rotate)&3; if (delta==1) return true; if (delta==3) return false; if (delta==0) return false; nlassert (0); // no! return false; } bool operator!= (const tileSetIndex& other) const { return ((TileSet!=other.TileSet)||(Rotate!=other.Rotate)); } bool operator== (const tileSetIndex& other) const { return ((TileSet==other.TileSet)&&(Rotate==other.Rotate)); } }; /*-------------------------------------------------------------------*/ class CBackupValue { public: // Ctor CBackupValue () {} CBackupValue (const tileDesc& desc, uint mesh, uint tile) : Desc (desc), Mesh (mesh), Tile (tile) {} // Public data tileDesc Desc; uint Mesh; uint Tile; }; /*-------------------------------------------------------------------*/ class EPM_PaintMouseProc : public MouseCallBack { friend class EPM_PaintCMode; friend class MouseListener; friend DWORD WINAPI myThread (LPVOID vData); friend class CTileUndo; friend class CPaintColor; friend class CFillPatch; private: PaintPatchMod *pobj; IObjParam *ip; IPoint2 om; //PatchMesh *pMesh; //sint32 Mesh; //std::map metaMeshIndex; std::vector > metaTile; std::vector bitArray; int Rotation; int TileIndex; CQuadTree quadTreeSelect; protected: // Paint algorithm void SetTile (int mesh, int tile, const tileDesc& desc, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg, std::vector* backupStack, bool undo=true, bool updateDisplace=false); bool isLockedEx (PaintPatchMod *pobj, EPM_PaintTile* pTile, std::vector& vectMesh, CLandscape* land); inline static bool isLocked (PaintPatchMod *pobj, EPM_PaintTile* pTile, uint8 mask = 0xff); static bool isLocked256 (PaintPatchMod *pobj, EPM_PaintTile* pTile); // Get a tile void GetTile (int mesh, int tile, tileDesc& desc, std::vector& vectMesh, CLandscape* land); bool PutATile ( EPM_PaintTile* pTile, int tileSet, int curRotation, const NL3D::CTileBank& bank, bool selectCycle, std::set& visited, std::vector& vectMesh, NL3D::CLandscape* land, CNelPatchChanger& nelPatchChg, bool _256); bool ClearATile ( EPM_PaintTile* pTile, std::vector& vectMesh, NL3D::CLandscape* land, CNelPatchChanger& nelPatchChg, bool _256, bool _force128=false); void PutADisplacetile ( EPM_PaintTile* pTile, const CTileBank& bank, std::vector& vectMesh, CLandscape* land, CNelPatchChanger& nelPatchChg); BOOL PutDisplace (int tile, int mesh, const CTileBank& bank, std::vector& vectMesh, CLandscape* land, int recurs, std::set& alreadyRecursed, CNelPatchChanger& nelPatchChg); int selectTile (uint tileSet, bool selectCycle, bool _256, uint group, const CTileBank& bank); bool GetBorderDesc (EPM_PaintTile* tile, tileSetIndex *pVoisinCorner, NL3D::CTileSet::TFlagBorder pBorder[4][3], tileDesc *pVoisinIndex, const NL3D::CTileBank& bank, std::vector& vectMesh, CNelPatchChanger& nelPatchChg, CLandscape *land); const NL3D::CTileSetTransition* FindTransition (int nTileSet, int nRotate, const NL3D::CTileSet::TFlagBorder *border, const NL3D::CTileBank& bank); bool PropagateBorder (EPM_PaintTile* tile, int curRotation, int curTileSet, std::set& visited, const NL3D::CTileBank& bank, std::vector& vectMesh, NL3D::CLandscape* land, CNelPatchChanger& nelPatchChg, std::vector& backupStack, bool recurseNoDiff=true); // Calc rotate path uint8 CalcRotPath (EPM_PaintTile* from, EPM_PaintTile* to, int depth, int rotate, int& deltaX, int& deltaY, int& cost); // Just put a tile BOOL PutTile (int tile, int mesh, bool first, const NL3D::CTileBank& bank, int tileSet, std::vector& vectMesh, NL3D::CLandscape* land, int recurs, std::set& alreadyRecursed, CNelPatchChanger& nelPatchChg, bool _256); void RecursTile (EPM_PaintTile* pTile, const CTileBank& bank, int tileSet, std::vector& vectMesh, CLandscape* land, int recurs, std::set& alreadyRecursed, bool first, int rotation, CNelPatchChanger& nelPatchChg, bool _256); BOOL HitATile(ViewExp *vpt, IPoint2 *p, int *tile, int *mesh, TimeValue t, std::vector& vectMesh, NLMISC::CVector &hit, NLMISC::CVector &topVector); BOOL HitATile(const CViewport& viewport, const CCamera& camera, float x, float y, int *tile, int *mesh, TimeValue t, std::vector& vectMesh, NLMISC::CVector& hit, NLMISC::CVector &topVector); BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); } int getLayer (EPM_PaintTile* tile, int border, int tileSet, int rotate, std::vector& vectMesh, NL3D::CLandscape *land); public: EPM_PaintMouseProc(PaintPatchMod* spl, IObjParam *i) { pobj=spl; ip=i; TileIndex=0; // Init plane matrix XY NLMISC::CMatrix tmp; NLMISC::CVector I(1,0,0); NLMISC::CVector J(0,0,-1); NLMISC::CVector K(0,1,0); tmp.identity(); tmp.setRot(I,J,K, true); quadTreeSelect.changeBase (tmp); } int proc( HWND hwnd, int msg, int point, int flags, IPoint2 m ); }; /*-------------------------------------------------------------------*/ class EPM_PaintCMode : public CommandMode { private: ChangeFGObject fgProc; EPM_PaintMouseProc eproc; PaintPatchMod* pobj; public: EPM_PaintCMode(PaintPatchMod* spl, IObjParam *i) : fgProc((ReferenceTarget*)spl), eproc(spl,i) { pobj=spl; } int Class() { return MODIFY_COMMAND; } int ID() { return CID_EP_PAINT; } MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &eproc; } ChangeForegroundCallback *ChangeFGProc() { return &fgProc; } BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; } void EnterMode(); void ExitMode(); void DoPaint (); // void SetType(int type) { this->type = type; eproc.SetType(type); } }; /*-------------------------------------------------------------------*/ struct EPM_PaintPatch { enum TBorder {left=0, bottom, right, top, count}; EPM_PaintPatch () { Mesh=-1; } EPM_PaintPatch (sint32 mesh, sint32 p) { Mesh=mesh; patch=p; } sint32 Mesh; sint32 patch; }; /*-------------------------------------------------------------------*/ class PaintPatchMod : public Modifier { friend class EPTempData; friend class PaintPatchData; friend class PatchRestore; public: static HWND hOpsPanel; static IObjParam *ip; static BOOL rsOps; // rollup states (FALSE = rolled up) static EPM_PaintCMode *paintMode; static bool ShowCurrentState; static uint CurrentState; static int CurrentTileSet; static int brushSize; static int ColorBushSize; static int tileSize; static int TileGroup; // Active group of tiles. 0: no gourp, 1-4: group 0 to 3 static int DisplaceTile; // Active displace tile static int DisplaceTileSet; // Active tileset in displace static uint TileFillRotation; // Rotation used in fill static bool TileTrick; // Trick static int tileSetSet; static int channelModified; static bool additiveTile; static bool automaticLighting; static bool lockBorders; RefResult NotifyRefChanged( Interval changeInt,RefTargetHandle hTarget, PartID& partID, RefMessage message ) { return REF_SUCCEED; } bool includeMeshes; bool preloadTiles; // Remembered info PatchMesh *rememberedPatch; // NULL if using all selected patches int rememberedIndex; int rememberedData; PaintPatchMod(); ~PaintPatchMod(); Interval LocalValidity(TimeValue t); ChannelMask ChannelsUsed() { return EDITPAT_CHANNELS; } ChannelMask ChannelsChanged() { return channelModified; } void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node); void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc); Class_ID InputType() { return RYKOLPATCHOBJ_CLASS_ID; } int CompMatrix(TimeValue t, ModContext& mc, Matrix3& tm, Interval& valid); // From Animatable void DeleteThis() { delete this; } void GetClassName(TSTR& s) { s= TSTR(_T("NeLPatchPaintMod")); } Class_ID ClassID() { return Class_ID(0xc49560f, 0x3c3d68e7); } void* GetInterface(ULONG id); // From BaseObject int Display(TimeValue t, INode* inode, ViewExp *vpt, int flagst, ModContext *mc); void GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box, ModContext *mc); BOOL DependOnTopology(ModContext &mc); BOOL SupportsNamedSubSels() {return FALSE;} void ClearPatchDataFlag(ModContextList& mcList,DWORD f); void DeletePatchDataTempData(); void CreatePatchDataTempData(); int NumRefs() { return 0; } RefTargetHandle GetReference(int i) { return NULL; } void SetReference(int i, RefTargetHandle rtarg) {} void ChangeRememberedPatch(int type); int RememberPatchThere(HWND hWnd, IPoint2 m); void SetRememberedPatchType(int type); // IO IOResult Save(ISave *isave); IOResult Load(ILoad *iload); IOResult SaveLocalData(ISave *isave, LocalModData *ld); IOResult LoadLocalData(ILoad *iload, LocalModData **pld); IOResult LoadNamedSelChunk(ILoad *iload,int level); CreateMouseCallBack* GetCreateMouseCallBack() { return NULL; } void BeginEditParams( IObjParam *ip, ULONG flags, Animatable *prev ); void EndEditParams( IObjParam *ip, ULONG flags, Animatable *next ); RefTargetHandle Clone(RemapDir& remap = DefaultRemapDir()); TCHAR *GetObjectName() { return "NeL Patch Painter"; } void RescaleWorldUnits(float f); DWORD GetSelLevel(); void SetSelLevel(DWORD level); void LocalDataChanged(); void SetOpsDlgEnables(); }; class EditPatchClassDesc:public ClassDesc { public: int IsPublic() { return 1; } void * Create(BOOL loading = FALSE ) { return new PaintPatchMod; } const TCHAR * ClassName() { return "NeL Painter"; } SClass_ID SuperClassID() { return OSM_CLASS_ID; } Class_ID ClassID() { return Class_ID(0xc49560f, 0x3c3d68e7); } const TCHAR* Category() { return "NeL Tools";} void ResetClassParams(BOOL fileReset); }; class PatchRestore; class PModRecord { public: virtual BOOL Redo(PatchMesh *patch,RPatchMesh *rpatch,int reRecord)=0; virtual IOResult Load(ILoad *iload)=0; }; typedef PModRecord* PPModRecord; typedef Tab ModRecordTab; /*-------------------------------------------------------------------*/ // Here are the types of modification records we use! #define PATCHCHANGERECORD_CHUNK 0x2070 class PatchChangeRecord : public PModRecord { public: PatchMesh oldPatch; // How the patch mesh looked before the change RPatchMesh roldPatch; // How the patch looked before the delete int index; int type; BOOL Redo(PatchMesh *patch,RPatchMesh *rpatch,int reRecord); IOResult Load(ILoad *iload); }; typedef Tab MtlIDTab; /*-------------------------------------------------------------------*/ // PaintPatchData flags #define EPD_BEENDONE (1<<0) #define EPD_UPDATING_CACHE (1<<1) #define EPD_HASDATA (1<<2) #define EMD_HELD (1<<3) // equivalent to A_HELD // This is the data that each mod app will have. class PaintPatchData : public LocalModData { public: BOOL handleFlag; int handleVert; // Stuff we need to have for the patch's mesh conversion -- These are // Here because they're kind of a global change -- not undoable. bool includeMeshes; bool preloadTiles; DWORD flags; // This records the changes to the incoming object. ModRecordTab changes; // While an object is being edited, this exists. EPTempData *tempData; // The final edited patch PatchMesh finalPatch; RPatchMesh rfinalPatch; PaintPatchData(PaintPatchMod *mod); PaintPatchData(PaintPatchData& emc); // Applies modifications to a patchObject void Apply(TimeValue t,RPO *patchOb,int selLevel); // Invalidates any caches affected by the change. void Invalidate(PartID part,BOOL meshValid=TRUE); // If this is the first edit, then the delta arrays will be allocated void BeginEdit(TimeValue t); LocalModData *Clone() { return new PaintPatchData(*this); } void SetFlag(DWORD f,BOOL on) { if ( on ) { flags|=f; } else { flags&=~f; } } DWORD GetFlag(DWORD f) { return flags&f; } EPTempData *TempData(PaintPatchMod *mod); // Change recording functions: void ClearHandleFlag() { handleFlag = FALSE; } void SetHandleFlag(int vert) { handleVert = vert; handleFlag = TRUE; } BOOL DoingHandles() { return handleFlag; } //void ApplyHandlesAndZero(PatchMesh &patch) { vdelta.ApplyHandlesAndZero(patch, handleVert); } void RescaleWorldUnits(float f); // MAXr3: New recording system void RecordTopologyTags(PatchMesh *patch); void UpdateChanges(PatchMesh *patch, RPatchMesh *rpatch, BOOL checkTopology=TRUE); // IO IOResult Save(ISave *isave); IOResult Load(ILoad *iload); }; // My generic restore class class PatchRestore : public RestoreObj { public: bool RPatchModified; PatchMesh oldPatch, newPatch; RPatchMesh *roldPatch, *rnewPatch; // How the patch looked before the delete BOOL gotRedo; TimeValue t; PaintPatchData *epd; PaintPatchMod *mod; TSTR where; PatchRestore (PaintPatchData* pd, PaintPatchMod* mod, PatchMesh *patch, RPatchMesh *rpatch, TCHAR *id=_T("")); virtual ~PatchRestore (); void Restore(int isUndo); void Redo(); int Size() { return 1; } void EndHold() {mod->ClearAFlag(A_HELD);} TSTR Description() { TSTR string; string.printf(_T("Generic patch restore [%s]"),where); return string; } }; /*-------------------------------------------------------------------*/ class EPTempData { private: PatchMesh *patch; RPatchMesh *rpatch; Interval patchValid; PaintPatchMod *mod; PaintPatchData *patchData; public: ~EPTempData(); EPTempData(PaintPatchMod *m,PaintPatchData *md); void Invalidate(PartID part,BOOL meshValid=TRUE); PatchMesh *GetPatch(TimeValue t, RPatchMesh *&rPatch); BOOL PatchCached(TimeValue t); void UpdateCache(RPO *patchOb); PaintPatchMod *GetMod() { return mod; } }; inline bool EPM_PaintMouseProc::isLocked (PaintPatchMod *pobj, EPM_PaintTile* pTile, uint8 mask) { // Lock border ? if (pobj->lockBorders) { // Return the lock state return (pTile->locked & mask) != 0; } // Not locked return false; } #endif // __EDITPATCH_H__