You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
11 KiB
390 lines
11 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "HeadSpin.h" |
|
#include "plTextureSearch.h" |
|
#include "resource.h" |
|
#include "hsUtils.h" |
|
|
|
#define PB2Export __declspec( dllexport ) // Because I don't feel like including all the paramblock crap |
|
#include "pbbitmap.h" |
|
#include "bmmlib.h" |
|
#include "IMtlEdit.h" |
|
|
|
#include "plMtlCollector.h" |
|
#include "plMaxAccelerators.h" |
|
#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" |
|
#include "../../AssetMan/PublicInterface/MaxAssInterface.h" |
|
|
|
// Not a class member so we don't have to make everyone who uses this know about AssetMan |
|
static jvUniqueId gAssetID; |
|
|
|
plTextureSearch::plTextureSearch() : fDlg(NULL) |
|
{ |
|
gAssetID.SetEmpty(); |
|
memset(fFileName, 0, sizeof(fFileName)); |
|
} |
|
|
|
plTextureSearch& plTextureSearch::Instance() |
|
{ |
|
static plTextureSearch theInstance; |
|
return theInstance; |
|
} |
|
|
|
void plTextureSearch::Toggle() |
|
{ |
|
if (!fDlg) |
|
{ |
|
fDlg = CreateDialog(hInstance, |
|
MAKEINTRESOURCE(IDD_FIND_TEXTURE), |
|
GetCOREInterface()->GetMAXHWnd(), |
|
ForwardDlgProc); |
|
|
|
HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); |
|
LVCOLUMN lvc; |
|
lvc.mask = LVCF_TEXT; |
|
lvc.pszText = "Material"; |
|
ListView_InsertColumn(hList, 0, &lvc); |
|
|
|
lvc.pszText = "Layer"; |
|
ListView_InsertColumn(hList, 1, &lvc); |
|
|
|
lvc.pszText = "Texture"; |
|
ListView_InsertColumn(hList, 2, &lvc); |
|
|
|
IUpdateTextures(kUpdateLoadList); |
|
|
|
GetCOREInterface()->RegisterDlgWnd(fDlg); |
|
ShowWindow(fDlg, SW_SHOW); |
|
} |
|
else |
|
{ |
|
DestroyWindow(fDlg); |
|
fDlg = NULL; |
|
} |
|
} |
|
|
|
|
|
BOOL plTextureSearch::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
return Instance().DlgProc(hDlg, msg, wParam, lParam); |
|
} |
|
|
|
BOOL plTextureSearch::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_ACTIVATE: |
|
if (LOWORD(wParam) == WA_INACTIVE) |
|
plMaxAccelerators::Enable(); |
|
else |
|
plMaxAccelerators::Disable(); |
|
return TRUE; |
|
|
|
case WM_COMMAND: |
|
if (HIWORD(wParam) == BN_CLICKED) |
|
{ |
|
int id = LOWORD(wParam); |
|
if (id == IDCANCEL) |
|
{ |
|
Toggle(); |
|
return TRUE; |
|
} |
|
else if (id == IDC_UPDATE_BUTTON) |
|
{ |
|
IUpdateTextures(kUpdateLoadList); |
|
return TRUE; |
|
} |
|
else if (id == IDC_REPLACE_ALL_BUTTON) |
|
{ |
|
if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes) |
|
{ |
|
IUpdateTextures(kUpdateReplace); |
|
} |
|
return TRUE; |
|
} |
|
else if (id == IDC_SET_ALL_BUTTON) |
|
{ |
|
if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes) |
|
{ |
|
IUpdateTextures(kUpdateSetSize); |
|
} |
|
return TRUE; |
|
} |
|
else if (id == IDC_REPLACE_BUTTON) |
|
{ |
|
IPickReplaceTexture(); |
|
return TRUE; |
|
} |
|
} |
|
else if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_FIND_EDIT) |
|
{ |
|
bool findText = (SendDlgItemMessage(hDlg, IDC_FIND_EDIT, EM_LINELENGTH, 0, 0) > 0); |
|
bool replace = (fFileName[0] != '\0'); |
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_BUTTON), findText); |
|
EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_ALL_BUTTON), findText && replace); |
|
|
|
return TRUE; |
|
} |
|
break; |
|
|
|
case WM_NOTIFY: |
|
{ |
|
NMHDR* nmhdr = (NMHDR*)lParam; |
|
// User double-clicked a material in the texture list |
|
if (nmhdr->idFrom == IDC_TEXTURE_LIST && nmhdr->code == NM_DBLCLK) |
|
{ |
|
NMITEMACTIVATE* itema = (NMITEMACTIVATE*)lParam; |
|
if (itema->iItem != -1) |
|
{ |
|
// Get the material the user clicked on |
|
HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); |
|
LV_ITEM item; |
|
item.iItem = itema->iItem; |
|
item.mask = LVIF_PARAM; |
|
ListView_GetItem(hList, &item); |
|
|
|
Mtl* mtl = (Mtl*)item.lParam; |
|
|
|
// Make sure the material is still in the scene (paranoid check) |
|
MtlSet mtls; |
|
plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl); |
|
if (mtls.find(mtl) != mtls.end()) |
|
{ |
|
// Put the material in the current slot of the material editor |
|
IMtlEditInterface* mtlInterface = GetMtlEditInterface(); |
|
int slot = mtlInterface->GetActiveMtlSlot(); |
|
mtlInterface->PutMtlToMtlEditor(mtl, slot); |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
static int FloorPow2(int value) |
|
{ |
|
int v; |
|
for (v = 1; v <= value; v <<= 1); |
|
return v >> 1; |
|
} |
|
|
|
void plTextureSearch::IUpdateTextures(plTextureSearch::Update update) |
|
{ |
|
MtlSet mtls; |
|
plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl); |
|
|
|
char searchStr[256]; |
|
GetDlgItemText(fDlg, IDC_FIND_EDIT, searchStr, sizeof(searchStr)); |
|
strlwr(searchStr); |
|
|
|
HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); |
|
ListView_DeleteAllItems(hList); |
|
|
|
int sizeX = -1, sizeY = -1; |
|
|
|
HWND hCombo = GetDlgItem(fDlg, IDC_SIZE_COMBO); |
|
|
|
// If we're updating the size, get whatever the user selected |
|
if (update == kUpdateSetSize) |
|
{ |
|
int sel = ComboBox_GetCurSel(hCombo); |
|
UInt32 data = ComboBox_GetItemData(hCombo, sel); |
|
sizeX = LOWORD(data); |
|
sizeY = HIWORD(data); |
|
} |
|
|
|
MtlSet::iterator it = mtls.begin(); |
|
for (; it != mtls.end(); it++) |
|
{ |
|
Mtl *mtl = (*it); |
|
|
|
LayerSet layers; |
|
plMtlCollector::GetMtlLayers(mtl, layers); |
|
|
|
LayerSet::iterator layerIt = layers.begin(); |
|
for (; layerIt != layers.end(); layerIt++) |
|
{ |
|
plPlasmaMAXLayer *layer = (*layerIt); |
|
|
|
int numBitmaps = layer->GetNumBitmaps(); |
|
|
|
for (int i = 0; i < numBitmaps; i++) |
|
{ |
|
PBBitmap *pbbm = layer->GetPBBitmap(i); |
|
if (pbbm) |
|
{ |
|
const char *name = pbbm->bi.Filename(); |
|
if (name && *name != '\0') |
|
{ |
|
char buf[256]; |
|
strncpy(buf, name, sizeof(buf)); |
|
strlwr(buf); |
|
|
|
// If we don't have a search string, or we do and it was |
|
// found in the texture name, add the texture to the list. |
|
if (searchStr[0] == '\0' || strstr(buf, searchStr)) |
|
{ |
|
if (update == kUpdateLoadList) |
|
{ |
|
LVITEM item = {0}; |
|
item.mask = LVIF_TEXT | LVIF_PARAM; |
|
item.pszText = mtl->GetName(); |
|
item.lParam = (LPARAM)mtl; // A little dangerous, since the user could delete this |
|
int idx = ListView_InsertItem(hList, &item); |
|
|
|
ListView_SetItemText(hList, idx, 1, layer->GetName()); |
|
ListView_SetItemText(hList, idx, 2, (char*)name); |
|
|
|
// If size is uninitialized or the same as the last, keep size |
|
if ((sizeX == -1 && sizeY == -1) || (sizeX == pbbm->bi.Width() && sizeY == pbbm->bi.Height())) |
|
{ |
|
sizeX = pbbm->bi.Width(); |
|
sizeY = pbbm->bi.Height(); |
|
} |
|
// Otherwise clear it |
|
else |
|
{ |
|
sizeX = sizeY = 0; |
|
} |
|
} |
|
else if (update == kUpdateReplace) |
|
{ |
|
layer->SetBitmapAssetId(gAssetID, i); |
|
|
|
BitmapInfo info; |
|
info.SetName(fFileName); |
|
layer->SetBitmap(&info, i); |
|
} |
|
else if (update == kUpdateSetSize) |
|
{ |
|
layer->SetExportSize(sizeX, sizeY); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (update == kUpdateLoadList) |
|
{ |
|
HWND hButton = GetDlgItem(fDlg, IDC_SET_ALL_BUTTON); |
|
ComboBox_ResetContent(hCombo); |
|
|
|
// If all bitmaps are the same size, enable resizing |
|
if (sizeX != -1 && sizeX != 0) |
|
{ |
|
sizeX = FloorPow2(sizeX); |
|
sizeY = FloorPow2(sizeY); |
|
|
|
char buf[256]; |
|
|
|
while (sizeX >= 4 && sizeY >= 4) |
|
{ |
|
sprintf(buf, "%d x %d", sizeX, sizeY); |
|
int idx = ComboBox_AddString(hCombo, buf); |
|
ComboBox_SetItemData(hCombo, idx, MAKELPARAM(sizeX, sizeY)); |
|
|
|
sizeX >>= 1; |
|
sizeY >>= 1; |
|
} |
|
|
|
ComboBox_SetCurSel(hCombo, 0); |
|
|
|
EnableWindow(hCombo, TRUE); |
|
EnableWindow(hButton, TRUE); |
|
} |
|
else |
|
{ |
|
EnableWindow(hCombo, FALSE); |
|
EnableWindow(hButton, FALSE); |
|
} |
|
} |
|
|
|
int autoSizeType = LVSCW_AUTOSIZE; |
|
if (ListView_GetItemCount(hList) == 0) |
|
autoSizeType = LVSCW_AUTOSIZE_USEHEADER; |
|
|
|
ListView_SetColumnWidth(hList, 0, autoSizeType); |
|
ListView_SetColumnWidth(hList, 1, autoSizeType); |
|
ListView_SetColumnWidth(hList, 2, autoSizeType); |
|
} |
|
|
|
void plTextureSearch::IPickReplaceTexture() |
|
{ |
|
fFileName[0] = '\0'; |
|
|
|
// if we have the assetman plug-in, then try to use it, unless shift is held down |
|
MaxAssInterface* maxAssInterface = GetMaxAssInterface(); |
|
if (maxAssInterface && !(GetKeyState(VK_SHIFT) & 0x8000)) |
|
{ |
|
maxAssInterface->OpenBitmapDlg(gAssetID, fFileName, sizeof(fFileName)); |
|
} |
|
else |
|
{ |
|
gAssetID.SetEmpty(); |
|
BitmapInfo bi; |
|
TheManager->SelectFileInput(&bi, GetCOREInterface()->GetMAXHWnd(), _T("Select Bitmap Image File")); |
|
strcpy(fFileName, bi.Filename()); |
|
} |
|
|
|
if (fFileName[0] == '\0') |
|
{ |
|
SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, "(none)"); |
|
EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), FALSE); |
|
} |
|
else |
|
{ |
|
char fname[_MAX_FNAME+_MAX_EXT], ext[_MAX_EXT]; |
|
_splitpath(fFileName, NULL, NULL, fname, ext); |
|
strcat(fname, ext); |
|
|
|
SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, fname); |
|
EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), TRUE); |
|
} |
|
}
|
|
|