/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "TerrainTextureExport.h" #include "Terrain/TerrainTexGen.h" #include "WaitProgress.h" #include "CryEditDoc.h" #include "ResizeResolutionDialog.h" #include "Viewport.h" #include "Terrain/TerrainManager.h" #include "TerrainLighting.h" #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) #include #endif namespace { static int TERRAIN_PREVIEW_RESOLUTION = 256; } ///////////////////////////////////////////////////////////////////////////// // Utility method static void PreviewToTile(uint32& outX, uint32& outY, uint32 x, uint32 y) { uint32 dwTileCountY = GetIEditor()->GetTerrainManager()->GetRGBLayer()->GetTileCountY(); // rotate 90 deg outX = dwTileCountY - 1 - y; outY = x; } ///////////////////////////////////////////////////////////////////////////// // TerrainTextureWidget class TerrainTextureWidget : public QWidget { public: TerrainTextureWidget(QWidget* parent = nullptr) : QWidget(parent) , m_bSelectMode(false) { setFixedSize(TERRAIN_PREVIEW_RESOLUTION, TERRAIN_PREVIEW_RESOLUTION); m_pTexGen = new CTerrainTexGen; m_pTexGen->Init(TERRAIN_PREVIEW_RESOLUTION, true); } ~TerrainTextureWidget() { delete m_pTexGen; } QRect GetSelection() const{ return m_selection; } protected: void paintEvent(QPaintEvent* event) { CRGBLayer* pRGBLayer = GetIEditor()->GetTerrainManager()->GetRGBLayer(); int dwTileCountX = pRGBLayer->GetTileCountX(); int dwTileCountY = pRGBLayer->GetTileCountY(); if (dwTileCountX == 0 || dwTileCountY == 0) { return; } QPainter p(this); // Generate a preview if we don't have one if (m_lightMap.isNull()) { Generate(); } // Draw the preview p.drawImage(1, 1, m_lightMap); // Draw the tiles p.setPen(qRgb(127, 127, 127)); for (uint32 x = 0; x < dwTileCountX; x++) { for (uint32 y = 0; y < dwTileCountY; y++) { QRect rc = { int(x) * TERRAIN_PREVIEW_RESOLUTION / dwTileCountX, int(y) * TERRAIN_PREVIEW_RESOLUTION / dwTileCountY, TERRAIN_PREVIEW_RESOLUTION / dwTileCountX, TERRAIN_PREVIEW_RESOLUTION / dwTileCountY }; uint32 dwTileX; uint32 dwTileY; PreviewToTile(dwTileX, dwTileY, x, y); uint32 dwLocalSize = pRGBLayer->GetTileResolution(dwTileX, dwTileY); p.setPen(qRgb(SATURATEB(dwLocalSize / 8), 0, 0)); if (!m_selection.isEmpty() && m_selection.left() <= x && m_selection.right() >= x && m_selection.top() <= y && m_selection.bottom() >= y) { rc.adjust(4, 4, -4, -4); p.fillRect(rc, qRgb(128, 128, 128)); } QString str; if (dwLocalSize == 1024) { str = QLatin1String("1k"); } else if (dwLocalSize == 2048) { str = QLatin1String("2k"); } else if (dwLocalSize == 4096) { str = QLatin1String("4k"); } else { str = QString::number(dwLocalSize); } p.drawText(rc, Qt::AlignCenter, str); } } p.setPen(Qt::green); qreal dpos = 0; qreal dstep = (qreal) (TERRAIN_PREVIEW_RESOLUTION - 1) / (qreal)dwTileCountX; for (float x = 0; x <= dwTileCountX; x++, dpos += dstep) { p.drawLine(dpos, 0, dpos, TERRAIN_PREVIEW_RESOLUTION); } dpos = 0; dstep = (qreal)(TERRAIN_PREVIEW_RESOLUTION - 1) / (qreal)dwTileCountY; for (float y = 0; y <= dwTileCountY; y++, dpos += dstep) { p.drawLine(0, dpos, TERRAIN_PREVIEW_RESOLUTION, dpos); } if (!m_selection.isNull()) { QRect rc; rc.setLeft(m_selection.left() * TERRAIN_PREVIEW_RESOLUTION / dwTileCountX + 1); rc.setTop(m_selection.top() * TERRAIN_PREVIEW_RESOLUTION / dwTileCountY + 1); rc.setRight((m_selection.right() + 1) * TERRAIN_PREVIEW_RESOLUTION / dwTileCountX - 2); rc.setBottom((m_selection.bottom() + 1) * TERRAIN_PREVIEW_RESOLUTION / dwTileCountY - 2); p.setPen(qRgb(255, 64, 64)); p.drawRect(rc); } } void mousePressEvent(QMouseEvent* event) { QWidget::mousePressEvent(event); if (event->button() != Qt::LeftButton) { return; } CRGBLayer* pRGBLayer = GetIEditor()->GetTerrainManager()->GetRGBLayer(); uint32 dwTileCountX = pRGBLayer->GetTileCountX(); uint32 dwTileCountY = pRGBLayer->GetTileCountY(); if (dwTileCountX == 0 || dwTileCountY == 0) { return; } QPoint point = event->pos(); int x = point.x() * dwTileCountX / TERRAIN_PREVIEW_RESOLUTION; int y = point.y() * dwTileCountY / TERRAIN_PREVIEW_RESOLUTION; if (x >= 0 && y >= 0 && x < dwTileCountX && y < dwTileCountY) { if (m_selection.left() == x && m_selection.top() == y && m_selection.right() == x && m_selection.bottom() == y) { m_selection = QRect(); } else { m_selection.setRect(x, y, 1, 1); m_bSelectMode = true; } } update(); } void mouseReleaseEvent(QMouseEvent* event) { if (m_bSelectMode) { m_bSelectMode = false; } QWidget::mouseReleaseEvent(event); } void mouseMoveEvent(QMouseEvent* event) { QWidget::mouseMoveEvent(event); if (!m_bSelectMode) { return; } CRGBLayer* pRGBLayer = GetIEditor()->GetTerrainManager()->GetRGBLayer(); uint32 dwTileCountX = pRGBLayer->GetTileCountX(); uint32 dwTileCountY = pRGBLayer->GetTileCountY(); QPoint point = event->pos(); int x = point.x() * dwTileCountX / TERRAIN_PREVIEW_RESOLUTION; int y = point.y() * dwTileCountY / TERRAIN_PREVIEW_RESOLUTION; if (x >= 0 && y >= 0 && x >= m_selection.left() && y >= m_selection.top() && x < dwTileCountX && y < dwTileCountY && (x != m_selection.right() || y != m_selection.bottom())) { m_selection.setRight(x); m_selection.setBottom(y); update(); } } private: void Generate() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); LightingSettings* ls = GetIEditor()->GetDocument()->GetLighting(); // Calculate the lighting. m_terrain.Allocate(TERRAIN_PREVIEW_RESOLUTION, TERRAIN_PREVIEW_RESOLUTION); m_pTexGen->InvalidateLighting(); m_pTexGen->GenerateSurfaceTexture(ETTG_LIGHTING | ETTG_QUIET | ETTG_BAKELIGHTING | ETTG_NOTEXTURE | ETTG_SHOW_WATER, m_terrain); // put terrain into m_lightMap. This does NOT copy the bits from m_terrain, so m_terrain must stay around m_lightMap = QImage((uchar*)m_terrain.GetData(), m_terrain.GetWidth(), m_terrain.GetHeight(), QImage::Format_RGBX8888); QApplication::restoreOverrideCursor(); } private: CTerrainTexGen* m_pTexGen; CImageEx m_terrain; QImage m_lightMap; QRect m_selection; bool m_bSelectMode; }; ///////////////////////////////////////////////////////////////////////////// // CTerrainTextureExport dialog //CDC CTerrainTextureExport::m_dcLightmap; CTerrainTextureExport::CTerrainTextureExport(QWidget* pParent /*=NULL*/) : QDialog(pParent) , ui(new Ui::CTerrainTextureExport) { ui->setupUi(this); setWindowFlags(windowFlags() & ~(Qt::WindowContextHelpButtonHint | Qt::WindowSystemMenuHint)); m_terrainTexture = new TerrainTextureWidget(ui->terrainTextureFrame); setFixedSize(sizeHint()); connect(ui->exportButton, &QPushButton::clicked, this, &CTerrainTextureExport::OnExport); connect(ui->importButton, &QPushButton::clicked, this, &CTerrainTextureExport::OnImport); connect(ui->CHANGE, &QPushButton::clicked, this, &CTerrainTextureExport::OnChangeResolutionBtn); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &QDialog::accept); ui->FILE->setText(gSettings.terrainTextureExport); UpdateTotalResolution(); } CTerrainTextureExport::~CTerrainTextureExport() { } ////////////////////////////////////////////////////////////////////////// void CTerrainTextureExport::ImportExport(bool bIsImport, bool bIsClipboard) { if (!bIsClipboard) { //if(!strlen(gSettings.terrainTextureExport)) if (!gSettings.BrowseTerrainTexture(!bIsImport)) { return; } ui->FILE->setText(gSettings.terrainTextureExport); if (gSettings.terrainTextureExport.isEmpty()) { QMessageBox::warning(this, tr("Terrain Texture"), tr("Error: Need to specify file name.")); return; } } CHeightmap* pHeightmap = GetIEditor()->GetHeightmap(); CRGBLayer* pRGBLayer = GetIEditor()->GetTerrainManager()->GetRGBLayer(); uint32 dwTileCountX = pRGBLayer->GetTileCountX(); uint32 dwTileCountY = pRGBLayer->GetTileCountY(); uint32 left; uint32 top; uint32 right; uint32 bottom; QRect sel = m_terrainTexture->GetSelection(); PreviewToTile(right, top, sel.left(), sel.top()); PreviewToTile(left, bottom, sel.right(), sel.bottom()); AZStd::string filename; if (bIsClipboard == false) { filename = gSettings.terrainTextureExport.toUtf8().data(); } if (bIsImport) { pRGBLayer->TryImportTileRect(filename, left, top, right + 1, bottom + 1); } else { pRGBLayer->ExportTileRect(filename, left, top, right + 1, bottom + 1); } if (bIsImport) { QRect rc( QPoint(left* pHeightmap->GetWidth() / dwTileCountX, top * pHeightmap->GetHeight() / dwTileCountY), QPoint((right + 1) * pHeightmap->GetWidth() / dwTileCountX, (bottom + 1) * pHeightmap->GetHeight() / dwTileCountY) - QPoint(1, 1) ); pHeightmap->UpdateLayerTexture(rc); } } ////////////////////////////////////////////////////////////////////////// void CTerrainTextureExport::OnExport() { if (m_terrainTexture->GetSelection().isNull()) { return; } CHeightmap* pHeightmap = GetIEditor()->GetHeightmap(); if (!pHeightmap->IsAllocated()) { return; } ImportExport(false /*bIsImport*/); } ////////////////////////////////////////////////////////////////////////// void CTerrainTextureExport::OnImport() { if (m_terrainTexture->GetSelection().isNull()) { return; } CHeightmap* pHeightmap = GetIEditor()->GetHeightmap(); if (!pHeightmap->IsAllocated()) { return; } ImportExport(true /*bIsImport*/); GetIEditor()->SetModifiedFlag(); GetIEditor()->SetModifiedModule(eModifiedTerrain); } ////////////////////////////////////////////////////////////////////////// void CTerrainTextureExport::OnChangeResolutionBtn() { if (m_terrainTexture->GetSelection().isNull()) { QMessageBox::warning(this, tr("Terrain Texture"), tr("Error: You have to select a sector.")); return; } uint32 dwTileX; uint32 dwTileY; QRect sel = m_terrainTexture->GetSelection(); PreviewToTile(dwTileX, dwTileY, sel.left(), sel.top()); CHeightmap* pHeightmap = GetIEditor()->GetHeightmap(); CResizeResolutionDialog dlg; uint32 dwCurSize = pHeightmap->GetRGBLayer()->GetTileResolution(dwTileX, dwTileY); dlg.SetSize(dwCurSize); if (dlg.exec() != QDialog::Accepted) { return; } uint32 dwNewSize = dlg.GetSize(); //TODO: make translatable CWaitProgress tileResolutionProgress("Changing tile resolution..."); tileResolutionProgress.Start(); // Updating the progress bar every sector can add a few seconds, update it once per tile int totalTiles = (sel.bottom() - sel.top() + 1) * (sel.right() - sel.left() + 1); int tilesProcessed = 0; for (uint32 y = sel.top(); y <= sel.bottom(); ++y) { for (uint32 x = sel.left(); x <= sel.right(); ++x) { PreviewToTile(dwTileX, dwTileY, x, y); uint32 dwOldSize = pHeightmap->GetRGBLayer()->GetTileResolution(dwTileX, dwTileY); if (dwOldSize == dwNewSize || dwNewSize < 64 || dwNewSize > 2048) { continue; } GetIEditor()->GetTerrainManager()->GetHeightmap()->GetRGBLayer()->ChangeTileResolution(dwTileX, dwTileY, dwNewSize); GetIEditor()->GetDocument()->SetModifiedFlag(TRUE); GetIEditor()->SetModifiedModule(eModifiedTerrain); // update terrain preview image in the dialog m_terrainTexture->update(); // update 3D Engine display pHeightmap->RefreshTextureTile(dwTileX, dwTileY); // update progress bar tileResolutionProgress.Step(++tilesProcessed * 100 / totalTiles); } } tileResolutionProgress.Stop(); UpdateTotalResolution(); GetIEditor()->GetActiveView()->Update(); } ////////////////////////////////////////////////////////////////////////// void CTerrainTextureExport::UpdateTotalResolution() { CRGBLayer* pRGBLayer = GetIEditor()->GetTerrainManager()->GetRGBLayer(); uint32 minTexExtend = pRGBLayer->CalcMinRequiredTextureExtend(); ui->INFO_TEXT->setText(QString("Total (%1x%1)").arg(minTexExtend)); } #include