/* * Descent 3 * Copyright (C) 2024 Parallax Software * * 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 . */ // ProceduralDialog.cpp : implementation file // #include "stdafx.h" #include "editor.h" #include "ProceduralDialog.h" #include "bitmap.h" #include "gametexture.h" #include "manage.h" #include "texpage.h" #include "pserror.h" #include "mono.h" #include "vclip.h" #include "macros.h" #include "texture.h" #include "EditLineDialog.h" #include "ddio.h" #include "game.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define MAX_SLIDER_RANGE 255 ///////////////////////////////////////////////////////////////////////////// // CProceduralDialog dialog CProceduralDialog::CProceduralDialog(CWnd* pParent /*=NULL*/) : CDialog(CProceduralDialog::IDD, pParent) { //{{AFX_DATA_INIT(CProceduralDialog) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } void CProceduralDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CProceduralDialog) DDX_Control(pDX, IDC_FP_HEAT_SLIDER, m_heat_slider); DDX_Control(pDX, IDC_FREQ_SLIDER, m_freq_slider); DDX_Control(pDX, IDC_SIZE_SLIDER, m_size_slider); DDX_Control(pDX, IDC_SPEED_SLIDER, m_speed_slider); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CProceduralDialog, CDialog) //{{AFX_MSG_MAP(CProceduralDialog) ON_WM_TIMER() ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_BN_CLICKED(IDC_CLEAR_PROCEDURALS, OnClearProcedurals) ON_CBN_SELENDOK(IDC_PROCEDURAL_PULLDOWN, OnSelendokProceduralPulldown) ON_BN_CLICKED(IDC_PROCEDURAL_UPDATE, OnProceduralUpdate) ON_BN_CLICKED(IDC_IMPORT_PALETTE, OnImportPalette) ON_BN_CLICKED(IDC_DEFAULT_PALETTE, OnDefaultPalette) ON_BN_CLICKED(IDC_COPY_PROCEDURAL, OnCopyProcedural) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CProceduralDialog message handlers static static_proc_element save_elements[8000]; BOOL CProceduralDialog::OnInitDialog() { CDialog::OnInitDialog(); CWnd::SetTimer(1,33,NULL); // 30 frames per second int save_num=0; int n=D3EditState.texdlg_texture; // Make a temp buffer for our static elements if (GameTextures[n].procedural->num_static_elements>0) { save_num=GameTextures[n].procedural->num_static_elements; memcpy (save_elements,GameTextures[n].procedural->static_proc_elements,save_num*sizeof(static_proc_element)); } FreeStaticProceduralsForTexture (n); Sleep(2000); AllocateStaticProceduralsForTexture (n,8000); memcpy (GameTextures[n].procedural->static_proc_elements,save_elements,save_num*sizeof(static_proc_element)); GameTextures[n].procedural->num_static_elements=save_num; m_button_down=0; m_proc_type=0; m_size=255; m_saturate=0; m_speed=0; m_frequency=0; m_cur_color=255; // Allocate bitmap memory CWnd *texwnd; RECT rect; texwnd = GetDlgItem(IDC_PALETTE_VIEW); texwnd->GetWindowRect(&rect); ScreenToClient(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; m_palette_bitmap=bm_AllocBitmap (w,h,((w*h)/3)*2); ASSERT (m_palette_bitmap>=0); m_PaletteSurf.create(bm_w(m_palette_bitmap,0), bm_h(m_palette_bitmap,0), bm_bpp(m_palette_bitmap)); texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); ScreenToClient(&rect); w=rect.right-rect.left; h=rect.bottom-rect.top; m_window_bitmap=bm_AllocBitmap (w,h,((w*h)/3)*2); ASSERT (m_window_bitmap>=0); m_TextureSurf.create(bm_w(m_window_bitmap,0), bm_h(m_window_bitmap,0), bm_bpp(m_window_bitmap)); // Set default slider values m_freq_slider.SetRange(0, MAX_SLIDER_RANGE); m_freq_slider.SetPos(255-m_frequency); m_speed_slider.SetRange(0, MAX_SLIDER_RANGE); m_speed_slider.SetPos(m_speed); m_size_slider.SetRange(1, MAX_SLIDER_RANGE); m_size_slider.SetPos(m_size); m_heat_slider.SetRange(0, MAX_SLIDER_RANGE); m_heat_slider.SetPos(GameTextures[n].procedural->heat); UpdateDialog(); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CProceduralDialog::PrintValue (int whichbox,uint8_t val) { CEdit *ebox; char str[255]; ebox=(CEdit *) GetDlgItem (whichbox); sprintf (str,"%d",val); ebox->SetWindowText (str); } void CProceduralDialog::UpdateDialog () { int n=D3EditState.texdlg_texture; // Update procedural box SendDlgItemMessage( IDC_PROCEDURAL_PULLDOWN, CB_RESETCONTENT,0,0); for (int i=0;stricmp ("End",ProcNames[i])!=0;i++) { SendDlgItemMessage( IDC_PROCEDURAL_PULLDOWN, CB_ADDSTRING,0,(LPARAM) (LPCTSTR) ProcNames[i]); } SendDlgItemMessage( IDC_PROCEDURAL_PULLDOWN, CB_SELECTSTRING,0,(LPARAM) (LPCTSTR) ProcNames[m_proc_type]); // Update palette box CWnd *texwnd; RECT rect; texwnd = GetDlgItem(IDC_PALETTE_VIEW); texwnd->GetWindowRect(&rect); ScreenToClient(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; Desktop_surf->attach_to_window((unsigned)m_hWnd); ushort *data=bm_data(m_palette_bitmap,0); for (i=0;ipalette[(int)cur_color]; } } m_PaletteSurf.load(m_palette_bitmap); int x = rect.left + ((rect.right-rect.left)/2) - m_PaletteSurf.width()/2; int y = rect.top + ((rect.bottom-rect.top)/2) - m_PaletteSurf.height()/2; Desktop_surf->blt(x, y, &m_PaletteSurf); // fill in the combo box for copy procedural data CComboBox *combo; combo = (CComboBox *)GetDlgItem(IDC_PROC_COPY_LIST); combo->ResetContent(); for(i=0;iAddString(GameTextures[i].name); } combo->SetCurSel(0); } void CProceduralDialog::UpdateProcView () { int n=D3EditState.texdlg_texture; FrameCount++; CWnd *texwnd; RECT rect; texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); ScreenToClient(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; Desktop_surf->attach_to_window((unsigned)m_hWnd); int bm_handle=GetTextureBitmap (n,0,1); if (GameBitmaps[bm_handle].flags & BF_MIPMAPPED) GameBitmaps[m_window_bitmap].flags|=BF_MIPMAPPED; bm_ScaleBitmapToBitmap (m_window_bitmap,bm_handle); m_TextureSurf.load(m_window_bitmap); int x = rect.left + ((rect.right-rect.left)/2) - m_TextureSurf.width()/2; int y = rect.top + ((rect.bottom-rect.top)/2) - m_TextureSurf.height()/2; Desktop_surf->blt(x, y, &m_TextureSurf); // Do slider values m_frequency = 255-m_freq_slider.GetPos(); m_speed = m_speed_slider.GetPos(); m_size = m_size_slider.GetPos(); GameTextures[n].procedural->heat=m_heat_slider.GetPos(); PrintValue (IDC_SPEED_TEXT,m_speed); PrintValue (IDC_SIZE_TEXT2,m_size); PrintValue (IDC_FREQUENCY_TEXT,255-m_frequency); PrintValue (IDC_HEAT_TEXT,GameTextures[n].procedural->heat); } void CProceduralDialog::OnTimer(UINT nIDEvent) { UpdateProcView (); CDialog::OnTimer(nIDEvent); } BOOL CProceduralDialog::DestroyWindow() { CWnd::KillTimer (1); int n=D3EditState.texdlg_texture; int save_num=0; // Restore our static proc elements save_num=GameTextures[n].procedural->num_static_elements; if (save_num>0) memcpy (save_elements,GameTextures[n].procedural->static_proc_elements,save_num*sizeof(static_proc_element)); FreeStaticProceduralsForTexture (n); Sleep(2000); if (save_num>0) { AllocateStaticProceduralsForTexture (n,save_num); memcpy (GameTextures[n].procedural->static_proc_elements,save_elements,save_num*sizeof(static_proc_element)); GameTextures[n].procedural->num_static_elements=save_num; } else GameTextures[n].flags &=~(TF_PROCEDURAL|TF_WATER_PROCEDURAL); bm_FreeBitmap (m_palette_bitmap); m_PaletteSurf.free(); bm_FreeBitmap (m_window_bitmap); m_TextureSurf.free(); return CDialog::DestroyWindow(); } void CProceduralDialog::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CPoint tpoint=point; CWnd *texwnd; RECT rect; int n=D3EditState.texdlg_texture; texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; MapWindowPoints(texwnd,&tpoint,1); if (tpoint.x<0) return; if (tpoint.y<0) return; if (tpoint.x>=w) return; if (tpoint.y>=h) return; int bm_handle=GameTextures[n].procedural->procedural_bitmap; int bw=bm_w(bm_handle,0); int bh=bm_h(bm_handle,0); int dest_x=((float)tpoint.x*(float)((float)bw/(float)w)); int dest_y=((float)tpoint.y*(float)((float)bh/(float)h)); if (m_proc_type==PROC_LINE_LIGHTNING) { m_save_x=dest_x; m_save_y=dest_y; } else if (m_proc_type!=PROC_NONE) { CreateElement (tpoint.x,tpoint.y); } m_button_down=1; CDialog::OnLButtonDown(nFlags, point); } // Creates a procedural element at x,y static_proc_element *CProceduralDialog::CreateElement(int x,int y) { int n=D3EditState.texdlg_texture; proc_struct *procedural=GameTextures[n].procedural; int index; if (GameTextures[n].procedural->num_static_elements>=MAX_PROC_ELEMENTS) return NULL; else index=GameTextures[n].procedural->num_static_elements; static_proc_element *proc_elem=&procedural->static_proc_elements[index]; CWnd *texwnd; RECT rect; texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; int bm_handle=GameTextures[n].procedural->procedural_bitmap; int bw=bm_w(bm_handle,0); int bh=bm_h(bm_handle,0); int dest_x=((float)x*(float)((float)bw/(float)w)); int dest_y=((float)y*(float)((float)bh/(float)h)); proc_elem->type=m_proc_type; proc_elem->x1=dest_x; proc_elem->y1=dest_y; proc_elem->color=m_cur_color; proc_elem->size=m_size; proc_elem->speed=m_speed; proc_elem->frequency=m_frequency; procedural->num_static_elements++; return &procedural->static_proc_elements[index]; } void CProceduralDialog::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CPoint tpoint=point; CWnd *texwnd; RECT rect; int n=D3EditState.texdlg_texture; if (m_button_down==0) return; texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; MapWindowPoints(texwnd,&tpoint,1); if (tpoint.x<0) return; if (tpoint.y<0) return; if (tpoint.x>=w) return; if (tpoint.y>=h) return; if (m_proc_type!=PROC_NONE && m_proc_type!=PROC_LINE_LIGHTNING) { CreateElement (tpoint.x,tpoint.y); } CDialog::OnMouseMove(nFlags, point); } void CProceduralDialog::OnLButtonUp(UINT nFlags, CPoint point) { int n=D3EditState.texdlg_texture; CPoint tpoint=point; if (m_button_down==1 && m_proc_type==PROC_LINE_LIGHTNING) { CWnd *texwnd; RECT rect; texwnd = GetDlgItem(IDC_PROCEDURAL_VIEW); texwnd->GetWindowRect(&rect); int w=rect.right-rect.left; int h=rect.bottom-rect.top; int bm_handle=GameTextures[n].procedural->procedural_bitmap; int bw=bm_w(bm_handle,0); int bh=bm_h(bm_handle,0); int dest_x=((float)tpoint.x*(float)((float)bw/(float)w)); int dest_y=((float)tpoint.y*(float)((float)bh/(float)h)); static_proc_element *proc_elem=CreateElement (0,0); if (proc_elem!=NULL) { proc_elem->x1=m_save_x; proc_elem->y1=m_save_y; proc_elem->x2=dest_x; proc_elem->y2=dest_y; } } m_button_down=0; CDialog::OnLButtonUp(nFlags, point); } void CProceduralDialog::OnClearProcedurals() { ClearAllProceduralsFromTexture (D3EditState.texdlg_texture); } void CProceduralDialog::OnSelendokProceduralPulldown() { int cur=SendDlgItemMessage( IDC_PROCEDURAL_PULLDOWN, CB_GETCURSEL,0,0); m_proc_type=cur; UpdateDialog(); } void CProceduralDialog::OnProceduralUpdate() { UpdateDialog(); } void CProceduralDialog::HarvestValue(int whichbox,uint8_t *val) { CEdit *ebox; char name[20]; int num=0; ebox=(CEdit *) GetDlgItem (whichbox); ebox->GetWindowText (name,20); num=atoi (name); if (num<0) num=0; if (num>255) num=255; *val=num; UpdateDialog(); } void CProceduralDialog::OnImportPalette() { CFILE *infile; char filename[255]; uint8_t r[256],g[256],b[256]; int n=D3EditState.texdlg_texture; CString filter = "PCX files (*.pcx)|*.pcx||"; if (!OpenFileDialog(this, (LPCSTR)filter, filename, Current_bitmap_dir, sizeof(Current_bitmap_dir))) return; if ((infile=cfopen (filename,"rb"))==NULL) { OutrageMessageBox ("Couldn't open that PCX file!"); return; } cfseek (infile,-768,SEEK_END); for (int i=0;i<256;i++) { r[i]=cf_ReadByte (infile); g[i]=cf_ReadByte (infile); b[i]=cf_ReadByte (infile); } cfclose (infile); GeneratePaletteForProcedural (r,g,b,GameTextures[n].procedural->palette); UpdateDialog(); } void CProceduralDialog::OnDefaultPalette() { int n=D3EditState.texdlg_texture; memcpy (GameTextures[n].procedural->palette,DefaultProcPalette,256*2); UpdateDialog(); } void CProceduralDialog::OnCopyProcedural() { // get the name of the texture CComboBox *combo; CString str; int src_id; combo = (CComboBox *)GetDlgItem(IDC_PROC_COPY_LIST); combo->GetLBText(combo->GetCurSel(),str); src_id = FindTextureName(str.GetBuffer(0)); ASSERT(src_id!=-1); bool do_pal =false; char buffer[512]; sprintf(buffer,"Are you sure you want to replace the procedural elements of %s with %s?",GameTextures[D3EditState.texdlg_texture].name,GameTextures[src_id].name); if(MessageBox(buffer,"Confirm",MB_YESNO)!=IDYES) return; if(MessageBox("Would you like to replace the palette also?","Palette",MB_YESNO)==IDYES) do_pal = true; mngs_texture_page src_page,dest_page; mng_AssignTextureToTexPage(src_id,&src_page); mng_AssignTextureToTexPage(D3EditState.texdlg_texture,&dest_page); ClearAllProceduralsFromTexture(D3EditState.texdlg_texture); FreeProceduralForTexture(D3EditState.texdlg_texture); int i; if(src_page.tex_struct.flags&TF_WATER_PROCEDURAL) dest_page.tex_struct.flags |= TF_WATER_PROCEDURAL; else dest_page.tex_struct.flags &= ~TF_WATER_PROCEDURAL; if(do_pal) { for(i=0;i<255;i++) { dest_page.proc_palette[i] = src_page.proc_palette[i]; } } dest_page.proc_heat = src_page.proc_heat; dest_page.proc_light = src_page.proc_light; dest_page.proc_thickness = src_page.proc_thickness; dest_page.proc_evaluation_time = src_page.proc_evaluation_time; dest_page.osc_time = src_page.osc_time; dest_page.osc_value = src_page.osc_value; dest_page.num_proc_elements = src_page.num_proc_elements; for(i=0;inum_static_elements=dest_page.num_proc_elements; GameTextures[D3EditState.texdlg_texture].procedural->heat=dest_page.proc_heat; GameTextures[D3EditState.texdlg_texture].procedural->light=dest_page.proc_light; GameTextures[D3EditState.texdlg_texture].procedural->thickness=dest_page.proc_thickness; GameTextures[D3EditState.texdlg_texture].procedural->evaluation_time=dest_page.proc_evaluation_time; GameTextures[D3EditState.texdlg_texture].procedural->osc_time=dest_page.osc_time; GameTextures[D3EditState.texdlg_texture].procedural->osc_value=dest_page.osc_value; // Last 2 colors are the same dest_page.proc_palette[255]=dest_page.proc_palette[254]; memcpy (GameTextures[D3EditState.texdlg_texture].procedural->palette,dest_page.proc_palette,256*2); for (i=0;istatic_proc_elements[i]; proc->type=dest_page.proc_type[i]; proc->frequency=dest_page.proc_frequency[i]; proc->speed=dest_page.proc_speed[i]; proc->size=dest_page.proc_size[i]; proc->x1=dest_page.proc_x1[i]; proc->y1=dest_page.proc_y1[i]; proc->x2=dest_page.proc_x2[i]; proc->y2=dest_page.proc_y2[i]; } UpdateDialog(); }