Texture browser

--HG--
branch : feature-material-editor
hg/feature/material-editor
kaetemi 9 years ago
parent 47bc7e439a
commit 93e8e11cf7

@ -43,6 +43,7 @@
#include "../shared_widgets/error_list.h" #include "../shared_widgets/error_list.h"
#include "graphics_viewport.h" #include "graphics_viewport.h"
#include "graphics_config.h" #include "graphics_config.h"
#include "texture_browser.h"
using namespace std; using namespace std;
using namespace NLMISC; using namespace NLMISC;
@ -103,6 +104,12 @@ CMainWindow::CMainWindow(QWidget *parent, Qt::WindowFlags flags)
} }
} }
} }
QDockWidget *dock = new QDockWidget(this);
dock->setFloating(true);
CTextureBrowser *browser = new CTextureBrowser(dock);
browser->resize(800, 800);
dock->setWidget(browser);
} }
CMainWindow::~CMainWindow() CMainWindow::~CMainWindow()

@ -0,0 +1,138 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2016 Winch Gate Property Limited
// Author: Jan Boon <jan.boon@kaetemi.be>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <nel/misc/types_nl.h>
#include "texture_browser.h"
// STL includes
#include <functional>
// Qt includes
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QPixmap>
#include <QListWidget>
#include <QFileInfo>
// NeL includes
// #include <nel/misc/debug.h>
#include <nel/misc/common.h>
#include <nel/misc/bitmap.h>
#include <nel/misc/file.h>
#include <nel/pipeline/project_config.h>
// Project includes
#include "../shared_widgets/event_loop.h"
// See also: studio/.../gui_editor/texture_chooser.cpp
// UTILITY ->
QPixmap qPixmapFromCBitmap(NLMISC::CBitmap &bitmap, bool alpha)
{
QImage img(bitmap.getWidth(), bitmap.getHeight(), alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
NLMISC::CObjectVector<uint8> &pixels = bitmap.getPixels();
uint height = bitmap.getHeight();
uint stride = bitmap.getWidth() * sizeof(NLMISC::CRGBA);
for (uint y = 0; y < height; ++y)
{
// memcpy(img.scanLine(y), &pixels[y * stride], stride);
// Convert from ABGR to ARGB
uint8 *dst = img.scanLine(y);
uint8 *src = &pixels[y * stride];
for (uint x = 0; x < stride; x += 4)
{
dst[x] = src[x + 2];
dst[x + 1] = src[x + 1];
dst[x + 2] = src[x];
dst[x + 3] = src[x + 3];
}
}
return QPixmap::fromImage(img);
}
// <- UTILITY
CTextureBrowser::CTextureBrowser(QWidget *parent) : QListWidget(parent)
{
qRegisterMetaType<CStdFunctionVoid>("CStdFunctionVoid");
m_Thread = new CEventLoop();
m_Thread->run();
setViewMode(QListWidget::IconMode);
setIconSize(QSize(200, 200));
setResizeMode(QListWidget::Adjust);
setDirectory("W:/database/stuff/fyros/agents/_textures/actors/");
}
CTextureBrowser::~CTextureBrowser()
{
m_Thread->clear();
delete m_Thread;
}
void CTextureBrowser::setDirectory(const QString &dir)
{
// Remove any pending stuff
m_Thread->clear();
// Sync up, clear, and start processing
m_Thread->immediate([this, dir]() -> void {
invokeStdFunction([this, dir]() -> void {
// m_listeWidget->addItem(new QListWidgetItem(QIcon("../earth.jpg"), "Earth"));
clear();
std::vector<std::string> files;
NLMISC::CPath::getPathContent(dir.toUtf8().data(), false, false, true, files);
for (size_t i = 0; i < files.size(); ++i)
{
std::string &file = files[i];
m_Thread->immediate([this, file]() -> void {
std::string ext = NLMISC::toLower(NLMISC::CFile::getExtension(file));
if (ext == "dds" || ext == "tga" || ext == "png" || ext == "jpg" || ext == "jpeg")
{
NLMISC::CIFile f;
if (f.open(file))
{
NLMISC::CBitmap bitmap;
bitmap.load(f);
bitmap.resample(128, 128);
QPixmap pixmap = qPixmapFromCBitmap(bitmap, false);
QString fileName = QFileInfo(QString::fromUtf8(file.c_str())).fileName();
invokeStdFunction([this, pixmap, fileName]() -> void {
addItem(new QListWidgetItem(QIcon(pixmap), fileName));
});
}
}
});
}
});
});
}
void CTextureBrowser::invokeStdFunction(CStdFunctionVoid f)
{
QMetaObject::invokeMethod(this, "callStdFunction", Qt::QueuedConnection, Q_ARG(CStdFunctionVoid, f));
}
void CTextureBrowser::callStdFunction(CStdFunctionVoid f)
{
f();
}
/* end of file */

@ -0,0 +1,70 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2016 Winch Gate Property Limited
// Author: Jan Boon <jan.boon@kaetemi.be>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NL_TEXTURE_BROWSER_H
#define NL_TEXTURE_BROWSER_H
#include <nel/misc/types_nl.h>
// STL includes
#include <functional>
// Qt includes
#include <QListWidget>
// NeL includes
// ...
class CEventLoop;
typedef std::function<void()> CStdFunctionVoid;
/**
* CTextureBrowser
* \brief CTextureBrowser
* \date 2016-02-18 14:06GMT
* \author Jan Boon <jan.boon@kaetemi.be>
*/
class CTextureBrowser : public QListWidget
{
Q_OBJECT
public:
CTextureBrowser(QWidget *parent = NULL);
virtual ~CTextureBrowser();
private:
void setDirectory(const QString &dir);
// STD INVOKE ->
public:
void invokeStdFunction(CStdFunctionVoid f);
private slots:
void callStdFunction(CStdFunctionVoid f);
// <- STD INVOKE
private:
CEventLoop *m_Thread;
private:
CTextureBrowser(const CTextureBrowser &);
CTextureBrowser &operator=(const CTextureBrowser &);
}; /* class CTextureBrowser */
#endif /* #ifndef NL_TEXTURE_BROWSER_H */
/* end of file */

@ -0,0 +1,281 @@
/*
Copyright (C) 2016 by authors
Author: Jan Boon <jan.boon@kaetemi.be>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Source: https://github.com/kaetemi/threadutil/blob/master/threadutil/eventloop.h
#ifndef NL_EVENT_LOOP_H
#define NL_EVENT_LOOP_H
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>
#include <set>
class CEventLoop
{
public:
CEventLoop() : m_Running(false), m_Handle(0)
{
}
~CEventLoop()
{
stop();
clear();
}
void run()
{
stop();
m_Running = true;
m_Thread = std::move(std::thread(&CEventLoop::loop, this));
}
void runSync()
{
stop();
m_Running = true;
loop();
}
void stop() // thread-safe
{
m_Running = false;
poke();
if (m_Thread.joinable())
m_Thread.join();
}
void clear() // thread-safe
{
std::unique_lock<std::mutex> lock(m_QueueLock);
std::unique_lock<std::mutex> tlock(m_QueueTimeoutLock);
m_Immediate = std::move(std::queue<std::function<void()>>());
m_Timeout = std::move(std::priority_queue<timeout_func>());
}
void clear(int handle) // thread-safe, relatively slow, not recommended nor reliable to do this for timeouts, only reliable for intervals
{
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
std::priority_queue<timeout_func> timeout;
while (m_Timeout.size())
{
if (m_Timeout.top().handle != handle)
timeout.push(m_Timeout.top());
m_Timeout.pop();
}
m_Timeout = std::move(timeout);
}
void join() // thread-safe
{
std::mutex syncLock;
std::condition_variable syncCond;
std::unique_lock<std::mutex> lock(syncLock);
immediate([this, &syncCond]() -> void {
syncCond.notify_one();
});
m_PokeCond.wait(lock);
}
public:
void immediate(std::function<void()> f) // thread-safe
{
std::unique_lock<std::mutex> lock(m_QueueLock);
m_Immediate.push(f);
poke();
}
template<class rep, class period>
int timeout(std::function<void()> f, const std::chrono::duration<rep, period>& delta) // thread-safe
{
timeout_func tf;
tf.f = f;
tf.time = std::chrono::steady_clock::now() + delta;
tf.interval = std::chrono::nanoseconds::zero();
tf.handle = ++m_Handle;
; {
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
m_Timeout.push(tf);
poke();
}
return tf.handle;
}
template<class rep, class period>
int interval(std::function<void()> f, const std::chrono::duration<rep, period>& interval) // thread-safe
{
timeout_func tf;
tf.f = f;
tf.time = std::chrono::steady_clock::now() + interval;
tf.interval = interval;
tf.handle = ++m_Handle;
; {
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
m_Timeout.push(tf);
poke();
}
return tf.handle;
}
int timed(std::function<void()> f, const std::chrono::steady_clock::time_point &point) // thread-safe
{
timeout_func tf;
tf.f = f;
tf.time = point;
tf.interval = std::chrono::steady_clock::duration::zero();
tf.handle = ++m_Handle;
; {
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
m_Timeout.push(tf);
poke();
}
return tf.handle;
}
public:
void thread(std::function<void()> f, std::function<void()> callback)
{
std::thread t([this, f, callback]() -> void {
f();
immediate(callback);
});
t.detach();
}
private:
void loop()
{
while (m_Running)
{
m_Poked = false;
for (;;)
{
m_QueueLock.lock();
if (!m_Immediate.size())
{
m_QueueLock.unlock();
break;
}
std::function<void()> f = m_Immediate.front();
m_Immediate.pop();
m_QueueLock.unlock();
f();
}
bool poked = false;
for (;;)
{
m_QueueTimeoutLock.lock();
if (!m_Timeout.size())
{
m_QueueTimeoutLock.unlock();
break;
}
const timeout_func &tfr = m_Timeout.top();
if (tfr.time > std::chrono::steady_clock::now()) // wait
{
m_QueueTimeoutLock.unlock();
; {
std::unique_lock<std::mutex> lock(m_PokeLock);
if (!m_Poked)
m_PokeCond.wait_until(lock, tfr.time);
}
poked = true;
break;
}
timeout_func tf = tfr;
m_Timeout.pop();
m_QueueTimeoutLock.unlock();
tf.f(); // call
if (tf.interval > std::chrono::nanoseconds::zero()) // repeat
{
tf.time += tf.interval;
; {
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
m_Timeout.push(tf);
poke();
}
}
}
if (!poked)
{
std::unique_lock<std::mutex> lock(m_PokeLock);
if (!m_Poked)
m_PokeCond.wait(lock);
}
}
}
void poke() // private
{
std::unique_lock<std::mutex> lock(m_PokeLock);
m_PokeCond.notify_one();
m_Poked = true;
}
private:
struct timeout_func
{
std::function<void()> f;
std::chrono::steady_clock::time_point time;
std::chrono::steady_clock::duration interval;
int handle;
bool operator <(const timeout_func &o) const
{
return time > o.time;
}
};
private:
volatile bool m_Running;
volatile bool m_Poked;
std::thread m_Thread;
std::mutex m_PokeLock;
std::condition_variable m_PokeCond;
std::mutex m_QueueLock;
std::queue<std::function<void()>> m_Immediate;
std::mutex m_QueueTimeoutLock;
std::priority_queue<timeout_func> m_Timeout;
int m_Handle;
};
#endif /* NL_EVENT_LOOP_H */
/* end of file */
Loading…
Cancel
Save