Move source/Irrlicht/ to src/
This commit is contained in:
100
src/Android/CAndroidAssetFileArchive.cpp
Normal file
100
src/Android/CAndroidAssetFileArchive.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (C) 2002-2011 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAndroidAssetReader.h"
|
||||
|
||||
#include "CReadFile.h"
|
||||
#include "coreutil.h"
|
||||
#include "CAndroidAssetFileArchive.h"
|
||||
#include "CIrrDeviceAndroid.h"
|
||||
#include "os.h" // for logging (just keep it in even when not needed right now as it's used all the time)
|
||||
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_activity.h>
|
||||
#include <android/log.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
CAndroidAssetFileArchive::CAndroidAssetFileArchive(AAssetManager *assetManager, bool ignoreCase, bool ignorePaths) :
|
||||
CFileList("/asset", ignoreCase, ignorePaths), AssetManager(assetManager)
|
||||
{
|
||||
}
|
||||
|
||||
CAndroidAssetFileArchive::~CAndroidAssetFileArchive()
|
||||
{
|
||||
}
|
||||
|
||||
//! get the archive type
|
||||
E_FILE_ARCHIVE_TYPE CAndroidAssetFileArchive::getType() const
|
||||
{
|
||||
return EFAT_ANDROID_ASSET;
|
||||
}
|
||||
|
||||
const IFileList *CAndroidAssetFileArchive::getFileList() const
|
||||
{
|
||||
// The assert_manager can not read directory names, so
|
||||
// getFileList returns only files in folders which have been added.
|
||||
return this;
|
||||
}
|
||||
|
||||
//! opens a file by file name
|
||||
IReadFile *CAndroidAssetFileArchive::createAndOpenFile(const io::path &filename)
|
||||
{
|
||||
CAndroidAssetReader *reader = new CAndroidAssetReader(AssetManager, filename);
|
||||
|
||||
if (reader->isOpen())
|
||||
return reader;
|
||||
|
||||
reader->drop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//! opens a file by index
|
||||
IReadFile *CAndroidAssetFileArchive::createAndOpenFile(u32 index)
|
||||
{
|
||||
const io::path &filename(getFullFileName(index));
|
||||
if (filename.empty())
|
||||
return 0;
|
||||
|
||||
return createAndOpenFile(filename);
|
||||
}
|
||||
|
||||
void CAndroidAssetFileArchive::addDirectoryToFileList(const io::path &dirname_)
|
||||
{
|
||||
io::path dirname(dirname_);
|
||||
fschar_t lastChar = dirname.lastChar();
|
||||
if (lastChar == '/' || lastChar == '\\')
|
||||
dirname.erase(dirname.size() - 1);
|
||||
|
||||
// os::Printer::log("addDirectoryToFileList:", dirname.c_str(), ELL_DEBUG);
|
||||
if (findFile(dirname, true) >= 0)
|
||||
return; // was already added
|
||||
|
||||
AAssetDir *dir = AAssetManager_openDir(AssetManager, core::stringc(dirname).c_str());
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
// add directory itself
|
||||
addItem(dirname, 0, 0, /*isDir*/ true, getFileCount());
|
||||
|
||||
// add all files in folder.
|
||||
// Note: AAssetDir_getNextFileName does not return directory names (neither does any other NDK function)
|
||||
while (const char *filename = AAssetDir_getNextFileName(dir)) {
|
||||
core::stringc full_filename = dirname == "" ? filename
|
||||
: dirname + "/" + filename;
|
||||
|
||||
// We can't get the size without opening the file - so for performance
|
||||
// reasons we set the file size to 0.
|
||||
// TODO: Does this really cost so much performance that it's worth losing this information? Dirs are usually just added once at startup...
|
||||
addItem(full_filename, /*offet*/ 0, /*size*/ 0, /*isDir*/ false, getFileCount());
|
||||
// os::Printer::log("addItem:", full_filename.c_str(), ELL_DEBUG);
|
||||
}
|
||||
AAssetDir_close(dir);
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
58
src/Android/CAndroidAssetFileArchive.h
Normal file
58
src/Android/CAndroidAssetFileArchive.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2012 Joerg Henrichs
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IReadFile.h"
|
||||
#include "IFileArchive.h"
|
||||
#include "CFileList.h"
|
||||
|
||||
#include <android/native_activity.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/*!
|
||||
Android asset file system written August 2012 by J.Henrichs (later reworked by others).
|
||||
*/
|
||||
class CAndroidAssetFileArchive : public virtual IFileArchive,
|
||||
virtual CFileList
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CAndroidAssetFileArchive(AAssetManager *assetManager, bool ignoreCase, bool ignorePaths);
|
||||
|
||||
//! destructor
|
||||
virtual ~CAndroidAssetFileArchive();
|
||||
|
||||
//! opens a file by file name
|
||||
virtual IReadFile *createAndOpenFile(const io::path &filename);
|
||||
|
||||
//! opens a file by index
|
||||
virtual IReadFile *createAndOpenFile(u32 index);
|
||||
|
||||
//! returns the list of files
|
||||
virtual const IFileList *getFileList() const;
|
||||
|
||||
//! get the archive type
|
||||
virtual E_FILE_ARCHIVE_TYPE getType() const;
|
||||
|
||||
//! Add a directory to read files from. Since the Android
|
||||
//! API does not return names of directories, they need to
|
||||
//! be added manually.
|
||||
virtual void addDirectoryToFileList(const io::path &filename);
|
||||
|
||||
//! return the name (id) of the file Archive
|
||||
const io::path &getArchiveName() const override { return Path; }
|
||||
|
||||
protected:
|
||||
//! Android's asset manager
|
||||
AAssetManager *AssetManager;
|
||||
|
||||
}; // CAndroidAssetFileArchive
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
65
src/Android/CAndroidAssetReader.cpp
Normal file
65
src/Android/CAndroidAssetReader.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2002-2011 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAndroidAssetReader.h"
|
||||
|
||||
#include "CReadFile.h"
|
||||
#include "coreutil.h"
|
||||
#include "CAndroidAssetReader.h"
|
||||
#include "CIrrDeviceAndroid.h"
|
||||
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
CAndroidAssetReader::CAndroidAssetReader(AAssetManager *assetManager, const io::path &filename) :
|
||||
AssetManager(assetManager), Filename(filename)
|
||||
{
|
||||
Asset = AAssetManager_open(AssetManager,
|
||||
core::stringc(filename).c_str(),
|
||||
AASSET_MODE_RANDOM);
|
||||
}
|
||||
|
||||
CAndroidAssetReader::~CAndroidAssetReader()
|
||||
{
|
||||
if (Asset)
|
||||
AAsset_close(Asset);
|
||||
}
|
||||
|
||||
size_t CAndroidAssetReader::read(void *buffer, size_t sizeToRead)
|
||||
{
|
||||
int readBytes = AAsset_read(Asset, buffer, sizeToRead);
|
||||
if (readBytes >= 0)
|
||||
return size_t(readBytes);
|
||||
return 0; // direct fd access is not possible (for example, if the asset is compressed).
|
||||
}
|
||||
|
||||
bool CAndroidAssetReader::seek(long finalPos, bool relativeMovement)
|
||||
{
|
||||
off_t status = AAsset_seek(Asset, finalPos, relativeMovement ? SEEK_CUR : SEEK_SET);
|
||||
|
||||
return status + 1;
|
||||
}
|
||||
|
||||
long CAndroidAssetReader::getSize() const
|
||||
{
|
||||
return AAsset_getLength(Asset);
|
||||
}
|
||||
|
||||
long CAndroidAssetReader::getPos() const
|
||||
{
|
||||
return AAsset_getLength(Asset) - AAsset_getRemainingLength(Asset);
|
||||
}
|
||||
|
||||
const io::path &CAndroidAssetReader::getFileName() const
|
||||
{
|
||||
return Filename;
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
64
src/Android/CAndroidAssetReader.h
Normal file
64
src/Android/CAndroidAssetReader.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2012 Joerg Henrichs
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IReadFile.h"
|
||||
|
||||
struct AAssetManager;
|
||||
struct AAsset;
|
||||
struct ANativeActivity;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
class CAndroidAssetReader : public virtual IReadFile
|
||||
{
|
||||
public:
|
||||
CAndroidAssetReader(AAssetManager *assetManager, const io::path &filename);
|
||||
|
||||
virtual ~CAndroidAssetReader();
|
||||
|
||||
//! Reads an amount of bytes from the file.
|
||||
/** \param buffer Pointer to buffer where read bytes are written to.
|
||||
\param sizeToRead Amount of bytes to read from the file.
|
||||
\return How many bytes were read. */
|
||||
virtual size_t read(void *buffer, size_t sizeToRead);
|
||||
|
||||
//! Changes position in file
|
||||
/** \param finalPos Destination position in the file.
|
||||
\param relativeMovement If set to true, the position in the file is
|
||||
changed relative to current position. Otherwise the position is changed
|
||||
from beginning of file.
|
||||
\return True if successful, otherwise false. */
|
||||
virtual bool seek(long finalPos, bool relativeMovement = false);
|
||||
|
||||
//! Get size of file.
|
||||
/** \return Size of the file in bytes. */
|
||||
virtual long getSize() const;
|
||||
|
||||
//! Get the current position in the file.
|
||||
/** \return Current position in the file in bytes. */
|
||||
virtual long getPos() const;
|
||||
|
||||
//! Get name of file.
|
||||
/** \return File name as zero terminated character string. */
|
||||
virtual const io::path &getFileName() const;
|
||||
|
||||
/** Return true if the file could be opened. */
|
||||
bool isOpen() const { return Asset != NULL; }
|
||||
|
||||
private:
|
||||
//! Android's asset manager
|
||||
AAssetManager *AssetManager;
|
||||
|
||||
// An asset, i.e. file
|
||||
AAsset *Asset;
|
||||
path Filename;
|
||||
};
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
828
src/Android/CIrrDeviceAndroid.cpp
Normal file
828
src/Android/CIrrDeviceAndroid.cpp
Normal file
@@ -0,0 +1,828 @@
|
||||
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
||||
// Copyright (C) 2007-2011 Christian Stehno
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CIrrDeviceAndroid.h"
|
||||
|
||||
#include "os.h"
|
||||
#include "CFileSystem.h"
|
||||
#include "CAndroidAssetReader.h"
|
||||
#include "CAndroidAssetFileArchive.h"
|
||||
#include "CKeyEventWrapper.h"
|
||||
#include "CEGLManager.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "CEGLManager.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
IVideoDriver *createOGLES1Driver(const SIrrlichtCreationParameters ¶ms,
|
||||
io::IFileSystem *io, video::IContextManager *contextManager);
|
||||
|
||||
IVideoDriver *createOGLES2Driver(const SIrrlichtCreationParameters ¶ms,
|
||||
io::IFileSystem *io, video::IContextManager *contextManager);
|
||||
}
|
||||
}
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
CIrrDeviceAndroid::CIrrDeviceAndroid(const SIrrlichtCreationParameters ¶m) :
|
||||
CIrrDeviceStub(param), Accelerometer(0), Gyroscope(0), Initialized(false),
|
||||
Stopped(true), Paused(true), Focused(false), JNIEnvAttachedToVM(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CIrrDeviceAndroid");
|
||||
#endif
|
||||
|
||||
// Get the interface to the native Android activity.
|
||||
Android = (android_app *)(param.PrivateData);
|
||||
|
||||
// Set the private data so we can use it in any static callbacks.
|
||||
Android->userData = this;
|
||||
|
||||
// Set the default command handler. This is a callback function that the Android
|
||||
// OS invokes to send the native activity messages.
|
||||
Android->onAppCmd = handleAndroidCommand;
|
||||
|
||||
createKeyMap();
|
||||
|
||||
// Create a sensor manager to receive touch screen events from the java activity.
|
||||
SensorManager = ASensorManager_getInstance();
|
||||
SensorEventQueue = ASensorManager_createEventQueue(SensorManager, Android->looper, LOOPER_ID_USER, 0, 0);
|
||||
Android->onInputEvent = handleInput;
|
||||
|
||||
// Create EGL manager.
|
||||
ContextManager = new video::CEGLManager();
|
||||
|
||||
os::Printer::log("Waiting for Android activity window to be created.", ELL_DEBUG);
|
||||
|
||||
do {
|
||||
s32 Events = 0;
|
||||
android_poll_source *Source = 0;
|
||||
|
||||
while ((ALooper_pollAll((!Initialized || isWindowActive()) ? 0 : -1, 0, &Events, (void **)&Source)) >= 0) {
|
||||
if (Source)
|
||||
Source->process(Android, Source);
|
||||
}
|
||||
} while (!Initialized);
|
||||
}
|
||||
|
||||
CIrrDeviceAndroid::~CIrrDeviceAndroid()
|
||||
{
|
||||
if (GUIEnvironment) {
|
||||
GUIEnvironment->drop();
|
||||
GUIEnvironment = 0;
|
||||
}
|
||||
|
||||
if (SceneManager) {
|
||||
SceneManager->drop();
|
||||
SceneManager = 0;
|
||||
}
|
||||
|
||||
if (VideoDriver) {
|
||||
VideoDriver->drop();
|
||||
VideoDriver = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::run()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
os::Timer::tick();
|
||||
|
||||
s32 id;
|
||||
s32 Events = 0;
|
||||
android_poll_source *Source = 0;
|
||||
|
||||
while ((id = ALooper_pollAll(0, 0, &Events, (void **)&Source)) >= 0) {
|
||||
if (Source)
|
||||
Source->process(Android, Source);
|
||||
|
||||
// if a sensor has data, we'll process it now.
|
||||
if (id == LOOPER_ID_USER) {
|
||||
ASensorEvent sensorEvent;
|
||||
while (ASensorEventQueue_getEvents(SensorEventQueue, &sensorEvent, 1) > 0) {
|
||||
switch (sensorEvent.type) {
|
||||
case ASENSOR_TYPE_ACCELEROMETER:
|
||||
SEvent accEvent;
|
||||
accEvent.EventType = EET_ACCELEROMETER_EVENT;
|
||||
accEvent.AccelerometerEvent.X = sensorEvent.acceleration.x;
|
||||
accEvent.AccelerometerEvent.Y = sensorEvent.acceleration.y;
|
||||
accEvent.AccelerometerEvent.Z = sensorEvent.acceleration.z;
|
||||
|
||||
postEventFromUser(accEvent);
|
||||
break;
|
||||
|
||||
case ASENSOR_TYPE_GYROSCOPE:
|
||||
SEvent gyroEvent;
|
||||
gyroEvent.EventType = EET_GYROSCOPE_EVENT;
|
||||
gyroEvent.GyroscopeEvent.X = sensorEvent.vector.x;
|
||||
gyroEvent.GyroscopeEvent.Y = sensorEvent.vector.y;
|
||||
gyroEvent.GyroscopeEvent.Z = sensorEvent.vector.z;
|
||||
|
||||
postEventFromUser(gyroEvent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Initialized)
|
||||
break;
|
||||
}
|
||||
|
||||
return Initialized;
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::yield()
|
||||
{
|
||||
struct timespec ts = {0, 1};
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::sleep(u32 timeMs, bool pauseTimer)
|
||||
{
|
||||
const bool wasStopped = Timer ? Timer->isStopped() : true;
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_t)(timeMs / 1000);
|
||||
ts.tv_nsec = (long)(timeMs % 1000) * 1000000;
|
||||
|
||||
if (pauseTimer && !wasStopped)
|
||||
Timer->stop();
|
||||
|
||||
nanosleep(&ts, NULL);
|
||||
|
||||
if (pauseTimer && !wasStopped)
|
||||
Timer->start();
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::setWindowCaption(const wchar_t *text)
|
||||
{
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isWindowActive() const
|
||||
{
|
||||
return (Focused && !Paused && !Stopped);
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isWindowFocused() const
|
||||
{
|
||||
return Focused;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isWindowMinimized() const
|
||||
{
|
||||
return !Focused;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isWindowVisible() const
|
||||
{
|
||||
return !Stopped;
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::closeDevice()
|
||||
{
|
||||
ANativeActivity_finish(Android->activity);
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::setResizable(bool resize)
|
||||
{
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::minimizeWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::maximizeWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::restoreWindow()
|
||||
{
|
||||
}
|
||||
|
||||
core::position2di CIrrDeviceAndroid::getWindowPosition()
|
||||
{
|
||||
return core::position2di(0, 0);
|
||||
}
|
||||
|
||||
E_DEVICE_TYPE CIrrDeviceAndroid::getType() const
|
||||
{
|
||||
return EIDT_ANDROID;
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::handleAndroidCommand(android_app *app, int32_t cmd)
|
||||
{
|
||||
CIrrDeviceAndroid *device = (CIrrDeviceAndroid *)app->userData;
|
||||
|
||||
SEvent event;
|
||||
event.EventType = EET_SYSTEM_EVENT;
|
||||
event.SystemEvent.EventType = ESET_ANDROID_CMD;
|
||||
event.SystemEvent.AndroidCmd.Cmd = cmd;
|
||||
if (device->postEventFromUser(event))
|
||||
return;
|
||||
|
||||
switch (cmd) {
|
||||
case APP_CMD_INPUT_CHANGED:
|
||||
os::Printer::log("Android command APP_CMD_INPUT_CHANGED", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_WINDOW_RESIZED:
|
||||
os::Printer::log("Android command APP_CMD_WINDOW_RESIZED", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_WINDOW_REDRAW_NEEDED:
|
||||
os::Printer::log("Android command APP_CMD_WINDOW_REDRAW_NEEDED", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_SAVE_STATE:
|
||||
os::Printer::log("Android command APP_CMD_SAVE_STATE", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_CONTENT_RECT_CHANGED:
|
||||
os::Printer::log("Android command APP_CMD_CONTENT_RECT_CHANGED", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_CONFIG_CHANGED:
|
||||
os::Printer::log("Android command APP_CMD_CONFIG_CHANGED", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_LOW_MEMORY:
|
||||
os::Printer::log("Android command APP_CMD_LOW_MEMORY", ELL_DEBUG);
|
||||
break;
|
||||
case APP_CMD_START:
|
||||
os::Printer::log("Android command APP_CMD_START", ELL_DEBUG);
|
||||
device->Stopped = false;
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
os::Printer::log("Android command APP_CMD_INIT_WINDOW", ELL_DEBUG);
|
||||
device->getExposedVideoData().OGLESAndroid.Window = app->window;
|
||||
|
||||
if (device->CreationParams.WindowSize.Width == 0 || device->CreationParams.WindowSize.Height == 0) {
|
||||
device->CreationParams.WindowSize.Width = ANativeWindow_getWidth(app->window);
|
||||
device->CreationParams.WindowSize.Height = ANativeWindow_getHeight(app->window);
|
||||
}
|
||||
|
||||
device->getContextManager()->initialize(device->CreationParams, device->ExposedVideoData);
|
||||
device->getContextManager()->generateSurface();
|
||||
device->getContextManager()->generateContext();
|
||||
device->getContextManager()->activateContext(device->getContextManager()->getContext());
|
||||
|
||||
if (!device->Initialized) {
|
||||
io::CAndroidAssetFileArchive *assets = new io::CAndroidAssetFileArchive(device->Android->activity->assetManager, false, false);
|
||||
assets->addDirectoryToFileList("");
|
||||
device->FileSystem->addFileArchive(assets);
|
||||
assets->drop();
|
||||
|
||||
device->createDriver();
|
||||
|
||||
if (device->VideoDriver)
|
||||
device->createGUIAndScene();
|
||||
}
|
||||
device->Initialized = true;
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
os::Printer::log("Android command APP_CMD_TERM_WINDOW", ELL_DEBUG);
|
||||
device->getContextManager()->destroySurface();
|
||||
break;
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
os::Printer::log("Android command APP_CMD_GAINED_FOCUS", ELL_DEBUG);
|
||||
device->Focused = true;
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
os::Printer::log("Android command APP_CMD_LOST_FOCUS", ELL_DEBUG);
|
||||
device->Focused = false;
|
||||
break;
|
||||
case APP_CMD_DESTROY:
|
||||
os::Printer::log("Android command APP_CMD_DESTROY", ELL_DEBUG);
|
||||
if (device->JNIEnvAttachedToVM) {
|
||||
device->JNIEnvAttachedToVM = 0;
|
||||
device->Android->activity->vm->DetachCurrentThread();
|
||||
}
|
||||
device->Initialized = false;
|
||||
break;
|
||||
case APP_CMD_PAUSE:
|
||||
os::Printer::log("Android command APP_CMD_PAUSE", ELL_DEBUG);
|
||||
device->Paused = true;
|
||||
break;
|
||||
case APP_CMD_STOP:
|
||||
os::Printer::log("Android command APP_CMD_STOP", ELL_DEBUG);
|
||||
device->Stopped = true;
|
||||
break;
|
||||
case APP_CMD_RESUME:
|
||||
os::Printer::log("Android command APP_CMD_RESUME", ELL_DEBUG);
|
||||
device->Paused = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s32 CIrrDeviceAndroid::handleInput(android_app *app, AInputEvent *androidEvent)
|
||||
{
|
||||
CIrrDeviceAndroid *device = (CIrrDeviceAndroid *)app->userData;
|
||||
s32 status = 0;
|
||||
|
||||
switch (AInputEvent_getType(androidEvent)) {
|
||||
case AINPUT_EVENT_TYPE_MOTION: {
|
||||
SEvent event;
|
||||
event.EventType = EET_TOUCH_INPUT_EVENT;
|
||||
|
||||
s32 eventAction = AMotionEvent_getAction(androidEvent);
|
||||
s32 eventType = eventAction & AMOTION_EVENT_ACTION_MASK;
|
||||
|
||||
#if 0
|
||||
// Useful for debugging. We might have to pass some of those infos on at some point.
|
||||
// but preferably device independent (so iphone can use same irrlicht flags).
|
||||
int32_t flags = AMotionEvent_getFlags(androidEvent);
|
||||
os::Printer::log("flags: ", core::stringc(flags).c_str(), ELL_DEBUG);
|
||||
int32_t metaState = AMotionEvent_getMetaState(androidEvent);
|
||||
os::Printer::log("metaState: ", core::stringc(metaState).c_str(), ELL_DEBUG);
|
||||
int32_t edgeFlags = AMotionEvent_getEdgeFlags(androidEvent);
|
||||
os::Printer::log("edgeFlags: ", core::stringc(flags).c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
bool touchReceived = true;
|
||||
|
||||
switch (eventType) {
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN:
|
||||
event.TouchInput.Event = ETIE_PRESSED_DOWN;
|
||||
break;
|
||||
case AMOTION_EVENT_ACTION_MOVE:
|
||||
event.TouchInput.Event = ETIE_MOVED;
|
||||
break;
|
||||
case AMOTION_EVENT_ACTION_UP:
|
||||
case AMOTION_EVENT_ACTION_POINTER_UP:
|
||||
case AMOTION_EVENT_ACTION_CANCEL:
|
||||
event.TouchInput.Event = ETIE_LEFT_UP;
|
||||
break;
|
||||
default:
|
||||
touchReceived = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (touchReceived) {
|
||||
// Process all touches for move action.
|
||||
if (event.TouchInput.Event == ETIE_MOVED) {
|
||||
s32 pointerCount = AMotionEvent_getPointerCount(androidEvent);
|
||||
|
||||
for (s32 i = 0; i < pointerCount; ++i) {
|
||||
event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i);
|
||||
event.TouchInput.X = AMotionEvent_getX(androidEvent, i);
|
||||
event.TouchInput.Y = AMotionEvent_getY(androidEvent, i);
|
||||
event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);
|
||||
|
||||
device->postEventFromUser(event);
|
||||
}
|
||||
} else // Process one touch for other actions.
|
||||
{
|
||||
s32 pointerIndex = (eventAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
||||
|
||||
event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex);
|
||||
event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex);
|
||||
event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex);
|
||||
event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);
|
||||
|
||||
device->postEventFromUser(event);
|
||||
}
|
||||
|
||||
status = 1;
|
||||
}
|
||||
} break;
|
||||
case AINPUT_EVENT_TYPE_KEY: {
|
||||
SEvent event;
|
||||
event.EventType = EET_KEY_INPUT_EVENT;
|
||||
|
||||
int32_t keyCode = AKeyEvent_getKeyCode(androidEvent);
|
||||
// os::Printer::log("keyCode: ", core::stringc(keyCode).c_str(), ELL_DEBUG);
|
||||
|
||||
int32_t keyAction = AKeyEvent_getAction(androidEvent);
|
||||
int32_t keyMetaState = AKeyEvent_getMetaState(androidEvent);
|
||||
|
||||
if (keyCode >= 0 && (u32)keyCode < device->KeyMap.size())
|
||||
event.KeyInput.Key = device->KeyMap[keyCode];
|
||||
else
|
||||
event.KeyInput.Key = KEY_UNKNOWN;
|
||||
event.KeyInput.SystemKeyCode = (u32)keyCode;
|
||||
if (keyAction == AKEY_EVENT_ACTION_DOWN)
|
||||
event.KeyInput.PressedDown = true;
|
||||
else if (keyAction == AKEY_EVENT_ACTION_UP)
|
||||
event.KeyInput.PressedDown = false;
|
||||
else if (keyAction == AKEY_EVENT_ACTION_MULTIPLE) {
|
||||
// TODO: Multiple duplicate key events have occurred in a row,
|
||||
// or a complex string is being delivered. The repeat_count
|
||||
// property of the key event contains the number of times the
|
||||
// given key code should be executed.
|
||||
// I guess this might necessary for more complicated i18n key input,
|
||||
// but don't see yet how to handle this correctly.
|
||||
}
|
||||
|
||||
/* no use for meta keys so far.
|
||||
if ( keyMetaState & AMETA_ALT_ON
|
||||
|| keyMetaState & AMETA_ALT_LEFT_ON
|
||||
|| keyMetaState & AMETA_ALT_RIGHT_ON )
|
||||
;
|
||||
// what is a sym?
|
||||
if ( keyMetaState & AMETA_SYM_ON )
|
||||
;
|
||||
*/
|
||||
if (keyMetaState & AMETA_SHIFT_ON || keyMetaState & AMETA_SHIFT_LEFT_ON || keyMetaState & AMETA_SHIFT_RIGHT_ON)
|
||||
event.KeyInput.Shift = true;
|
||||
else
|
||||
event.KeyInput.Shift = false;
|
||||
event.KeyInput.Control = false;
|
||||
|
||||
// Having memory allocations + going through JNI for each key-press is pretty bad (slow).
|
||||
// So we do it only for those keys which are likely text-characters and avoid it for all other keys.
|
||||
// So it's fast for keys like game controller input and special keys. And text keys are typically
|
||||
// only used or entering text and not for gaming on Android, so speed likely doesn't matter there too much.
|
||||
if (event.KeyInput.Key > 0) {
|
||||
// TODO:
|
||||
// Not sure why we have to attach a JNIEnv here, but it won't work when doing that in the constructor or
|
||||
// trying to use the activity->env. My best guess is that the event-handling happens in an own thread.
|
||||
// It means JNIEnvAttachedToVM will never get detached as I don't know a safe way where to do that
|
||||
// (we could attach & detach each time, but that would probably be slow)
|
||||
// Also - it has to be each time as it get's invalid when the application mode changes.
|
||||
if (device->Initialized && device->Android && device->Android->activity && device->Android->activity->vm) {
|
||||
JavaVMAttachArgs attachArgs;
|
||||
attachArgs.version = JNI_VERSION_1_6;
|
||||
attachArgs.name = 0;
|
||||
attachArgs.group = NULL;
|
||||
|
||||
// Not a big problem calling it each time - it's a no-op when the thread already is attached.
|
||||
// And we have to do that as someone else can have detached the thread in the meantime.
|
||||
jint result = device->Android->activity->vm->AttachCurrentThread(&device->JNIEnvAttachedToVM, &attachArgs);
|
||||
if (result == JNI_ERR) {
|
||||
os::Printer::log("AttachCurrentThread for the JNI environment failed.", ELL_WARNING);
|
||||
device->JNIEnvAttachedToVM = 0;
|
||||
}
|
||||
|
||||
if (device->JNIEnvAttachedToVM) {
|
||||
jni::CKeyEventWrapper *keyEventWrapper = new jni::CKeyEventWrapper(device->JNIEnvAttachedToVM, keyAction, keyCode);
|
||||
event.KeyInput.Char = keyEventWrapper->getUnicodeChar(keyMetaState);
|
||||
delete keyEventWrapper;
|
||||
}
|
||||
}
|
||||
if (event.KeyInput.Key == KEY_BACK) {
|
||||
event.KeyInput.Char = 0x08; // same key-code as on other operating systems. Otherwise we have to handle too much system specific stuff in the editbox.
|
||||
}
|
||||
// os::Printer::log("char-code: ", core::stringc((int)event.KeyInput.Char).c_str(), ELL_DEBUG);
|
||||
|
||||
} else {
|
||||
// os::Printer::log("keyCode: ", core::stringc(keyCode).c_str(), ELL_DEBUG);
|
||||
event.KeyInput.Char = 0;
|
||||
}
|
||||
|
||||
status = device->postEventFromUser(event);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::createDriver()
|
||||
{
|
||||
switch (CreationParams.DriverType) {
|
||||
case video::EDT_OGLES1:
|
||||
#ifdef _IRR_COMPILE_WITH_OGLES1_
|
||||
VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);
|
||||
#else
|
||||
os::Printer::log("No OpenGL ES 1.0 support compiled in.", ELL_ERROR);
|
||||
#endif
|
||||
break;
|
||||
case video::EDT_OGLES2:
|
||||
#ifdef _IRR_COMPILE_WITH_OGLES2_
|
||||
VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);
|
||||
#else
|
||||
os::Printer::log("No OpenGL ES 2.0 support compiled in.", ELL_ERROR);
|
||||
#endif
|
||||
break;
|
||||
case video::EDT_NULL:
|
||||
VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
|
||||
break;
|
||||
case video::EDT_OPENGL:
|
||||
os::Printer::log("This driver is not available in Android. Try OpenGL ES 1.0 or ES 2.0.", ELL_ERROR);
|
||||
break;
|
||||
default:
|
||||
os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
video::SExposedVideoData &CIrrDeviceAndroid::getExposedVideoData()
|
||||
{
|
||||
return ExposedVideoData;
|
||||
}
|
||||
|
||||
void CIrrDeviceAndroid::createKeyMap()
|
||||
{
|
||||
KeyMap.set_used(223);
|
||||
|
||||
KeyMap[0] = KEY_UNKNOWN; // AKEYCODE_UNKNOWN
|
||||
KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT
|
||||
KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT
|
||||
KeyMap[3] = KEY_HOME; // AKEYCODE_HOME
|
||||
KeyMap[4] = KEY_CANCEL; // AKEYCODE_BACK
|
||||
KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL
|
||||
KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL
|
||||
KeyMap[7] = KEY_KEY_0; // AKEYCODE_0
|
||||
KeyMap[8] = KEY_KEY_1; // AKEYCODE_1
|
||||
KeyMap[9] = KEY_KEY_2; // AKEYCODE_2
|
||||
KeyMap[10] = KEY_KEY_3; // AKEYCODE_3
|
||||
KeyMap[11] = KEY_KEY_4; // AKEYCODE_4
|
||||
KeyMap[12] = KEY_KEY_5; // AKEYCODE_5
|
||||
KeyMap[13] = KEY_KEY_6; // AKEYCODE_6
|
||||
KeyMap[14] = KEY_KEY_7; // AKEYCODE_7
|
||||
KeyMap[15] = KEY_KEY_8; // AKEYCODE_8
|
||||
KeyMap[16] = KEY_KEY_9; // AKEYCODE_9
|
||||
KeyMap[17] = KEY_UNKNOWN; // AKEYCODE_STAR
|
||||
KeyMap[18] = KEY_UNKNOWN; // AKEYCODE_POUND
|
||||
KeyMap[19] = KEY_UP; // AKEYCODE_DPAD_UP
|
||||
KeyMap[20] = KEY_DOWN; // AKEYCODE_DPAD_DOWN
|
||||
KeyMap[21] = KEY_LEFT; // AKEYCODE_DPAD_LEFT
|
||||
KeyMap[22] = KEY_RIGHT; // AKEYCODE_DPAD_RIGHT
|
||||
KeyMap[23] = KEY_SELECT; // AKEYCODE_DPAD_CENTER
|
||||
KeyMap[24] = KEY_VOLUME_DOWN; // AKEYCODE_VOLUME_UP
|
||||
KeyMap[25] = KEY_VOLUME_UP; // AKEYCODE_VOLUME_DOWN
|
||||
KeyMap[26] = KEY_UNKNOWN; // AKEYCODE_POWER
|
||||
KeyMap[27] = KEY_UNKNOWN; // AKEYCODE_CAMERA
|
||||
KeyMap[28] = KEY_CLEAR; // AKEYCODE_CLEAR
|
||||
KeyMap[29] = KEY_KEY_A; // AKEYCODE_A
|
||||
KeyMap[30] = KEY_KEY_B; // AKEYCODE_B
|
||||
KeyMap[31] = KEY_KEY_C; // AKEYCODE_C
|
||||
KeyMap[32] = KEY_KEY_D; // AKEYCODE_D
|
||||
KeyMap[33] = KEY_KEY_E; // AKEYCODE_E
|
||||
KeyMap[34] = KEY_KEY_F; // AKEYCODE_F
|
||||
KeyMap[35] = KEY_KEY_G; // AKEYCODE_G
|
||||
KeyMap[36] = KEY_KEY_H; // AKEYCODE_H
|
||||
KeyMap[37] = KEY_KEY_I; // AKEYCODE_I
|
||||
KeyMap[38] = KEY_KEY_J; // AKEYCODE_J
|
||||
KeyMap[39] = KEY_KEY_K; // AKEYCODE_K
|
||||
KeyMap[40] = KEY_KEY_L; // AKEYCODE_L
|
||||
KeyMap[41] = KEY_KEY_M; // AKEYCODE_M
|
||||
KeyMap[42] = KEY_KEY_N; // AKEYCODE_N
|
||||
KeyMap[43] = KEY_KEY_O; // AKEYCODE_O
|
||||
KeyMap[44] = KEY_KEY_P; // AKEYCODE_P
|
||||
KeyMap[45] = KEY_KEY_Q; // AKEYCODE_Q
|
||||
KeyMap[46] = KEY_KEY_R; // AKEYCODE_R
|
||||
KeyMap[47] = KEY_KEY_S; // AKEYCODE_S
|
||||
KeyMap[48] = KEY_KEY_T; // AKEYCODE_T
|
||||
KeyMap[49] = KEY_KEY_U; // AKEYCODE_U
|
||||
KeyMap[50] = KEY_KEY_V; // AKEYCODE_V
|
||||
KeyMap[51] = KEY_KEY_W; // AKEYCODE_W
|
||||
KeyMap[52] = KEY_KEY_X; // AKEYCODE_X
|
||||
KeyMap[53] = KEY_KEY_Y; // AKEYCODE_Y
|
||||
KeyMap[54] = KEY_KEY_Z; // AKEYCODE_Z
|
||||
KeyMap[55] = KEY_COMMA; // AKEYCODE_COMMA
|
||||
KeyMap[56] = KEY_PERIOD; // AKEYCODE_PERIOD
|
||||
KeyMap[57] = KEY_MENU; // AKEYCODE_ALT_LEFT
|
||||
KeyMap[58] = KEY_MENU; // AKEYCODE_ALT_RIGHT
|
||||
KeyMap[59] = KEY_LSHIFT; // AKEYCODE_SHIFT_LEFT
|
||||
KeyMap[60] = KEY_RSHIFT; // AKEYCODE_SHIFT_RIGHT
|
||||
KeyMap[61] = KEY_TAB; // AKEYCODE_TAB
|
||||
KeyMap[62] = KEY_SPACE; // AKEYCODE_SPACE
|
||||
KeyMap[63] = KEY_UNKNOWN; // AKEYCODE_SYM
|
||||
KeyMap[64] = KEY_UNKNOWN; // AKEYCODE_EXPLORER
|
||||
KeyMap[65] = KEY_UNKNOWN; // AKEYCODE_ENVELOPE
|
||||
KeyMap[66] = KEY_RETURN; // AKEYCODE_ENTER
|
||||
KeyMap[67] = KEY_BACK; // AKEYCODE_DEL
|
||||
KeyMap[68] = KEY_OEM_3; // AKEYCODE_GRAVE
|
||||
KeyMap[69] = KEY_MINUS; // AKEYCODE_MINUS
|
||||
KeyMap[70] = KEY_UNKNOWN; // AKEYCODE_EQUALS
|
||||
KeyMap[71] = KEY_UNKNOWN; // AKEYCODE_LEFT_BRACKET
|
||||
KeyMap[72] = KEY_UNKNOWN; // AKEYCODE_RIGHT_BRACKET
|
||||
KeyMap[73] = KEY_UNKNOWN; // AKEYCODE_BACKSLASH
|
||||
KeyMap[74] = KEY_UNKNOWN; // AKEYCODE_SEMICOLON
|
||||
KeyMap[75] = KEY_UNKNOWN; // AKEYCODE_APOSTROPHE
|
||||
KeyMap[76] = KEY_UNKNOWN; // AKEYCODE_SLASH
|
||||
KeyMap[77] = KEY_UNKNOWN; // AKEYCODE_AT
|
||||
KeyMap[78] = KEY_UNKNOWN; // AKEYCODE_NUM
|
||||
KeyMap[79] = KEY_UNKNOWN; // AKEYCODE_HEADSETHOOK
|
||||
KeyMap[80] = KEY_UNKNOWN; // AKEYCODE_FOCUS (*Camera* focus)
|
||||
KeyMap[81] = KEY_PLUS; // AKEYCODE_PLUS
|
||||
KeyMap[82] = KEY_MENU; // AKEYCODE_MENU
|
||||
KeyMap[83] = KEY_UNKNOWN; // AKEYCODE_NOTIFICATION
|
||||
KeyMap[84] = KEY_UNKNOWN; // AKEYCODE_SEARCH
|
||||
KeyMap[85] = KEY_MEDIA_PLAY_PAUSE; // AKEYCODE_MEDIA_PLAY_PAUSE
|
||||
KeyMap[86] = KEY_MEDIA_STOP; // AKEYCODE_MEDIA_STOP
|
||||
KeyMap[87] = KEY_MEDIA_NEXT_TRACK; // AKEYCODE_MEDIA_NEXT
|
||||
KeyMap[88] = KEY_MEDIA_PREV_TRACK; // AKEYCODE_MEDIA_PREVIOUS
|
||||
KeyMap[89] = KEY_UNKNOWN; // AKEYCODE_MEDIA_REWIND
|
||||
KeyMap[90] = KEY_UNKNOWN; // AKEYCODE_MEDIA_FAST_FORWARD
|
||||
KeyMap[91] = KEY_VOLUME_MUTE; // AKEYCODE_MUTE
|
||||
KeyMap[92] = KEY_PRIOR; // AKEYCODE_PAGE_UP
|
||||
KeyMap[93] = KEY_NEXT; // AKEYCODE_PAGE_DOWN
|
||||
KeyMap[94] = KEY_UNKNOWN; // AKEYCODE_PICTSYMBOLS
|
||||
KeyMap[95] = KEY_UNKNOWN; // AKEYCODE_SWITCH_CHARSET
|
||||
|
||||
// following look like controller inputs
|
||||
KeyMap[96] = KEY_UNKNOWN; // AKEYCODE_BUTTON_A
|
||||
KeyMap[97] = KEY_UNKNOWN; // AKEYCODE_BUTTON_B
|
||||
KeyMap[98] = KEY_UNKNOWN; // AKEYCODE_BUTTON_C
|
||||
KeyMap[99] = KEY_UNKNOWN; // AKEYCODE_BUTTON_X
|
||||
KeyMap[100] = KEY_UNKNOWN; // AKEYCODE_BUTTON_Y
|
||||
KeyMap[101] = KEY_UNKNOWN; // AKEYCODE_BUTTON_Z
|
||||
KeyMap[102] = KEY_UNKNOWN; // AKEYCODE_BUTTON_L1
|
||||
KeyMap[103] = KEY_UNKNOWN; // AKEYCODE_BUTTON_R1
|
||||
KeyMap[104] = KEY_UNKNOWN; // AKEYCODE_BUTTON_L2
|
||||
KeyMap[105] = KEY_UNKNOWN; // AKEYCODE_BUTTON_R2
|
||||
KeyMap[106] = KEY_UNKNOWN; // AKEYCODE_BUTTON_THUMBL
|
||||
KeyMap[107] = KEY_UNKNOWN; // AKEYCODE_BUTTON_THUMBR
|
||||
KeyMap[108] = KEY_UNKNOWN; // AKEYCODE_BUTTON_START
|
||||
KeyMap[109] = KEY_UNKNOWN; // AKEYCODE_BUTTON_SELECT
|
||||
KeyMap[110] = KEY_UNKNOWN; // AKEYCODE_BUTTON_MODE
|
||||
|
||||
KeyMap[111] = KEY_ESCAPE; // AKEYCODE_ESCAPE
|
||||
KeyMap[112] = KEY_DELETE; // AKEYCODE_FORWARD_DEL
|
||||
KeyMap[113] = KEY_CONTROL; // AKEYCODE_CTRL_LEFT
|
||||
KeyMap[114] = KEY_CONTROL; // AKEYCODE_CTRL_RIGHT
|
||||
KeyMap[115] = KEY_CAPITAL; // AKEYCODE_CAPS_LOCK
|
||||
KeyMap[116] = KEY_SCROLL; // AKEYCODE_SCROLL_LOCK
|
||||
KeyMap[117] = KEY_UNKNOWN; // AKEYCODE_META_LEFT
|
||||
KeyMap[118] = KEY_UNKNOWN; // AKEYCODE_META_RIGHT
|
||||
KeyMap[119] = KEY_UNKNOWN; // AKEYCODE_FUNCTION
|
||||
KeyMap[120] = KEY_SNAPSHOT; // AKEYCODE_SYSRQ
|
||||
KeyMap[121] = KEY_PAUSE; // AKEYCODE_BREAK
|
||||
KeyMap[122] = KEY_HOME; // AKEYCODE_MOVE_HOME
|
||||
KeyMap[123] = KEY_END; // AKEYCODE_MOVE_END
|
||||
KeyMap[124] = KEY_INSERT; // AKEYCODE_INSERT
|
||||
KeyMap[125] = KEY_UNKNOWN; // AKEYCODE_FORWARD
|
||||
KeyMap[126] = KEY_PLAY; // AKEYCODE_MEDIA_PLAY
|
||||
KeyMap[127] = KEY_MEDIA_PLAY_PAUSE; // AKEYCODE_MEDIA_PAUSE
|
||||
KeyMap[128] = KEY_UNKNOWN; // AKEYCODE_MEDIA_CLOSE
|
||||
KeyMap[129] = KEY_UNKNOWN; // AKEYCODE_MEDIA_EJECT
|
||||
KeyMap[130] = KEY_UNKNOWN; // AKEYCODE_MEDIA_RECORD
|
||||
KeyMap[131] = KEY_F1; // AKEYCODE_F1
|
||||
KeyMap[132] = KEY_F2; // AKEYCODE_F2
|
||||
KeyMap[133] = KEY_F3; // AKEYCODE_F3
|
||||
KeyMap[134] = KEY_F4; // AKEYCODE_F4
|
||||
KeyMap[135] = KEY_F5; // AKEYCODE_F5
|
||||
KeyMap[136] = KEY_F6; // AKEYCODE_F6
|
||||
KeyMap[137] = KEY_F7; // AKEYCODE_F7
|
||||
KeyMap[138] = KEY_F8; // AKEYCODE_F8
|
||||
KeyMap[139] = KEY_F9; // AKEYCODE_F9
|
||||
KeyMap[140] = KEY_F10; // AKEYCODE_F10
|
||||
KeyMap[141] = KEY_F11; // AKEYCODE_F11
|
||||
KeyMap[142] = KEY_F12; // AKEYCODE_F12
|
||||
KeyMap[143] = KEY_NUMLOCK; // AKEYCODE_NUM_LOCK
|
||||
KeyMap[144] = KEY_NUMPAD0; // AKEYCODE_NUMPAD_0
|
||||
KeyMap[145] = KEY_NUMPAD1; // AKEYCODE_NUMPAD_1
|
||||
KeyMap[146] = KEY_NUMPAD2; // AKEYCODE_NUMPAD_2
|
||||
KeyMap[147] = KEY_NUMPAD3; // AKEYCODE_NUMPAD_3
|
||||
KeyMap[148] = KEY_NUMPAD4; // AKEYCODE_NUMPAD_4
|
||||
KeyMap[149] = KEY_NUMPAD5; // AKEYCODE_NUMPAD_5
|
||||
KeyMap[150] = KEY_NUMPAD6; // AKEYCODE_NUMPAD_6
|
||||
KeyMap[151] = KEY_NUMPAD7; // AKEYCODE_NUMPAD_7
|
||||
KeyMap[152] = KEY_NUMPAD8; // AKEYCODE_NUMPAD_8
|
||||
KeyMap[153] = KEY_NUMPAD9; // AKEYCODE_NUMPAD_9
|
||||
KeyMap[154] = KEY_DIVIDE; // AKEYCODE_NUMPAD_DIVIDE
|
||||
KeyMap[155] = KEY_MULTIPLY; // AKEYCODE_NUMPAD_MULTIPLY
|
||||
KeyMap[156] = KEY_SUBTRACT; // AKEYCODE_NUMPAD_SUBTRACT
|
||||
KeyMap[157] = KEY_ADD; // AKEYCODE_NUMPAD_ADD
|
||||
KeyMap[158] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_DOT
|
||||
KeyMap[159] = KEY_COMMA; // AKEYCODE_NUMPAD_COMMA
|
||||
KeyMap[160] = KEY_RETURN; // AKEYCODE_NUMPAD_ENTER
|
||||
KeyMap[161] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_EQUALS
|
||||
KeyMap[162] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_LEFT_PAREN
|
||||
KeyMap[163] = KEY_UNKNOWN; // AKEYCODE_NUMPAD_RIGHT_PAREN
|
||||
KeyMap[164] = KEY_VOLUME_MUTE; // AKEYCODE_VOLUME_MUTE
|
||||
KeyMap[165] = KEY_UNKNOWN; // AKEYCODE_INFO
|
||||
KeyMap[166] = KEY_UNKNOWN; // AKEYCODE_CHANNEL_UP
|
||||
KeyMap[167] = KEY_UNKNOWN; // AKEYCODE_CHANNEL_DOWN
|
||||
KeyMap[168] = KEY_ZOOM; // AKEYCODE_ZOOM_IN
|
||||
KeyMap[169] = KEY_UNKNOWN; // AKEYCODE_ZOOM_OUT
|
||||
KeyMap[170] = KEY_UNKNOWN; // AKEYCODE_TV
|
||||
KeyMap[171] = KEY_UNKNOWN; // AKEYCODE_WINDOW
|
||||
KeyMap[172] = KEY_UNKNOWN; // AKEYCODE_GUIDE
|
||||
KeyMap[173] = KEY_UNKNOWN; // AKEYCODE_DVR
|
||||
KeyMap[174] = KEY_UNKNOWN; // AKEYCODE_BOOKMARK
|
||||
KeyMap[175] = KEY_UNKNOWN; // AKEYCODE_CAPTIONS
|
||||
KeyMap[176] = KEY_UNKNOWN; // AKEYCODE_SETTINGS
|
||||
KeyMap[177] = KEY_UNKNOWN; // AKEYCODE_TV_POWER
|
||||
KeyMap[178] = KEY_UNKNOWN; // AKEYCODE_TV_INPUT
|
||||
KeyMap[179] = KEY_UNKNOWN; // AKEYCODE_STB_POWER
|
||||
KeyMap[180] = KEY_UNKNOWN; // AKEYCODE_STB_INPUT
|
||||
KeyMap[181] = KEY_UNKNOWN; // AKEYCODE_AVR_POWER
|
||||
KeyMap[182] = KEY_UNKNOWN; // AKEYCODE_AVR_INPUT
|
||||
KeyMap[183] = KEY_UNKNOWN; // AKEYCODE_PROG_RED
|
||||
KeyMap[184] = KEY_UNKNOWN; // AKEYCODE_PROG_GREEN
|
||||
KeyMap[185] = KEY_UNKNOWN; // AKEYCODE_PROG_YELLOW
|
||||
KeyMap[186] = KEY_UNKNOWN; // AKEYCODE_PROG_BLUE
|
||||
KeyMap[187] = KEY_UNKNOWN; // AKEYCODE_APP_SWITCH
|
||||
KeyMap[188] = KEY_UNKNOWN; // AKEYCODE_BUTTON_1
|
||||
KeyMap[189] = KEY_UNKNOWN; // AKEYCODE_BUTTON_2
|
||||
KeyMap[190] = KEY_UNKNOWN; // AKEYCODE_BUTTON_3
|
||||
KeyMap[191] = KEY_UNKNOWN; // AKEYCODE_BUTTON_4
|
||||
KeyMap[192] = KEY_UNKNOWN; // AKEYCODE_BUTTON_5
|
||||
KeyMap[193] = KEY_UNKNOWN; // AKEYCODE_BUTTON_6
|
||||
KeyMap[194] = KEY_UNKNOWN; // AKEYCODE_BUTTON_7
|
||||
KeyMap[195] = KEY_UNKNOWN; // AKEYCODE_BUTTON_8
|
||||
KeyMap[196] = KEY_UNKNOWN; // AKEYCODE_BUTTON_9
|
||||
KeyMap[197] = KEY_UNKNOWN; // AKEYCODE_BUTTON_10
|
||||
KeyMap[198] = KEY_UNKNOWN; // AKEYCODE_BUTTON_11
|
||||
KeyMap[199] = KEY_UNKNOWN; // AKEYCODE_BUTTON_12
|
||||
KeyMap[200] = KEY_UNKNOWN; // AKEYCODE_BUTTON_13
|
||||
KeyMap[201] = KEY_UNKNOWN; // AKEYCODE_BUTTON_14
|
||||
KeyMap[202] = KEY_UNKNOWN; // AKEYCODE_BUTTON_15
|
||||
KeyMap[203] = KEY_UNKNOWN; // AKEYCODE_BUTTON_16
|
||||
KeyMap[204] = KEY_UNKNOWN; // AKEYCODE_LANGUAGE_SWITCH
|
||||
KeyMap[205] = KEY_UNKNOWN; // AKEYCODE_MANNER_MODE
|
||||
KeyMap[206] = KEY_UNKNOWN; // AKEYCODE_3D_MODE
|
||||
KeyMap[207] = KEY_UNKNOWN; // AKEYCODE_CONTACTS
|
||||
KeyMap[208] = KEY_UNKNOWN; // AKEYCODE_CALENDAR
|
||||
KeyMap[209] = KEY_UNKNOWN; // AKEYCODE_MUSIC
|
||||
KeyMap[210] = KEY_UNKNOWN; // AKEYCODE_CALCULATOR
|
||||
KeyMap[211] = KEY_UNKNOWN; // AKEYCODE_ZENKAKU_HANKAKU
|
||||
KeyMap[212] = KEY_UNKNOWN; // AKEYCODE_EISU
|
||||
KeyMap[213] = KEY_UNKNOWN; // AKEYCODE_MUHENKAN
|
||||
KeyMap[214] = KEY_UNKNOWN; // AKEYCODE_HENKAN
|
||||
KeyMap[215] = KEY_UNKNOWN; // AKEYCODE_KATAKANA_HIRAGANA
|
||||
KeyMap[216] = KEY_UNKNOWN; // AKEYCODE_YEN
|
||||
KeyMap[217] = KEY_UNKNOWN; // AKEYCODE_RO
|
||||
KeyMap[218] = KEY_UNKNOWN; // AKEYCODE_KANA
|
||||
KeyMap[219] = KEY_UNKNOWN; // AKEYCODE_ASSIST
|
||||
KeyMap[220] = KEY_UNKNOWN; // AKEYCODE_BRIGHTNESS_DOWN
|
||||
KeyMap[221] = KEY_UNKNOWN; // AKEYCODE_BRIGHTNESS_UP ,
|
||||
KeyMap[222] = KEY_UNKNOWN; // AKEYCODE_MEDIA_AUDIO_TRACK
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::activateAccelerometer(float updateInterval)
|
||||
{
|
||||
if (!isAccelerometerAvailable())
|
||||
return false;
|
||||
|
||||
ASensorEventQueue_enableSensor(SensorEventQueue, Accelerometer);
|
||||
ASensorEventQueue_setEventRate(SensorEventQueue, Accelerometer, (int32_t)(updateInterval * 1000.f * 1000.f)); // in microseconds
|
||||
|
||||
os::Printer::log("Activated accelerometer", ELL_DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::deactivateAccelerometer()
|
||||
{
|
||||
if (Accelerometer) {
|
||||
ASensorEventQueue_disableSensor(SensorEventQueue, Accelerometer);
|
||||
Accelerometer = 0;
|
||||
os::Printer::log("Deactivated accelerometer", ELL_DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isAccelerometerActive()
|
||||
{
|
||||
return (Accelerometer != 0);
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isAccelerometerAvailable()
|
||||
{
|
||||
if (!Accelerometer)
|
||||
Accelerometer = ASensorManager_getDefaultSensor(SensorManager, ASENSOR_TYPE_ACCELEROMETER);
|
||||
|
||||
return (Accelerometer != 0);
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::activateGyroscope(float updateInterval)
|
||||
{
|
||||
if (!isGyroscopeAvailable())
|
||||
return false;
|
||||
|
||||
ASensorEventQueue_enableSensor(SensorEventQueue, Gyroscope);
|
||||
ASensorEventQueue_setEventRate(SensorEventQueue, Gyroscope, (int32_t)(updateInterval * 1000.f * 1000.f)); // in microseconds
|
||||
|
||||
os::Printer::log("Activated gyroscope", ELL_DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::deactivateGyroscope()
|
||||
{
|
||||
if (Gyroscope) {
|
||||
ASensorEventQueue_disableSensor(SensorEventQueue, Gyroscope);
|
||||
Gyroscope = 0;
|
||||
os::Printer::log("Deactivated gyroscope", ELL_DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isGyroscopeActive()
|
||||
{
|
||||
return (Gyroscope != 0);
|
||||
}
|
||||
|
||||
bool CIrrDeviceAndroid::isGyroscopeAvailable()
|
||||
{
|
||||
if (!Gyroscope)
|
||||
Gyroscope = ASensorManager_getDefaultSensor(SensorManager, ASENSOR_TYPE_GYROSCOPE);
|
||||
|
||||
return (Gyroscope != 0);
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
98
src/Android/CIrrDeviceAndroid.h
Normal file
98
src/Android/CIrrDeviceAndroid.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright (C) 2002-2011 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "ICursorControl.h"
|
||||
|
||||
#include <android/sensor.h>
|
||||
#include <android_native_app_glue.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
class CIrrDeviceAndroid : public CIrrDeviceStub
|
||||
{
|
||||
public:
|
||||
CIrrDeviceAndroid(const SIrrlichtCreationParameters ¶m);
|
||||
|
||||
virtual ~CIrrDeviceAndroid();
|
||||
|
||||
virtual bool run();
|
||||
|
||||
virtual void yield();
|
||||
|
||||
virtual void sleep(u32 timeMs, bool pauseTimer = false);
|
||||
|
||||
virtual void setWindowCaption(const wchar_t *text);
|
||||
|
||||
virtual bool isWindowActive() const;
|
||||
|
||||
virtual bool isWindowFocused() const;
|
||||
|
||||
virtual bool isWindowMinimized() const;
|
||||
|
||||
virtual bool isWindowVisible() const;
|
||||
|
||||
virtual void closeDevice();
|
||||
|
||||
virtual void setResizable(bool resize = false);
|
||||
|
||||
virtual void minimizeWindow();
|
||||
|
||||
virtual void maximizeWindow();
|
||||
|
||||
virtual void restoreWindow();
|
||||
|
||||
virtual core::position2di getWindowPosition();
|
||||
|
||||
virtual E_DEVICE_TYPE getType() const;
|
||||
|
||||
virtual bool activateAccelerometer(float updateInterval);
|
||||
|
||||
virtual bool deactivateAccelerometer();
|
||||
|
||||
virtual bool isAccelerometerActive();
|
||||
|
||||
virtual bool isAccelerometerAvailable();
|
||||
|
||||
virtual bool activateGyroscope(float updateInterval);
|
||||
|
||||
virtual bool deactivateGyroscope();
|
||||
|
||||
virtual bool isGyroscopeActive();
|
||||
|
||||
virtual bool isGyroscopeAvailable();
|
||||
|
||||
private:
|
||||
static void handleAndroidCommand(android_app *app, int32_t cmd);
|
||||
|
||||
static s32 handleInput(android_app *app, AInputEvent *event);
|
||||
|
||||
void createDriver();
|
||||
|
||||
void createKeyMap();
|
||||
|
||||
video::SExposedVideoData &getExposedVideoData();
|
||||
|
||||
android_app *Android;
|
||||
ASensorManager *SensorManager;
|
||||
ASensorEventQueue *SensorEventQueue;
|
||||
const ASensor *Accelerometer;
|
||||
const ASensor *Gyroscope;
|
||||
|
||||
bool Initialized;
|
||||
bool Stopped;
|
||||
bool Paused;
|
||||
bool Focused;
|
||||
|
||||
JNIEnv *JNIEnvAttachedToVM;
|
||||
|
||||
video::SExposedVideoData ExposedVideoData;
|
||||
|
||||
core::array<EKEY_CODE> KeyMap;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
52
src/Android/CKeyEventWrapper.cpp
Normal file
52
src/Android/CKeyEventWrapper.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CKeyEventWrapper.h"
|
||||
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace jni
|
||||
{
|
||||
|
||||
jclass CKeyEventWrapper::Class_KeyEvent = 0;
|
||||
jmethodID CKeyEventWrapper::Method_constructor = 0;
|
||||
jmethodID CKeyEventWrapper::Method_getUnicodeChar = 0;
|
||||
|
||||
CKeyEventWrapper::CKeyEventWrapper(JNIEnv *jniEnv, int action, int code) :
|
||||
JniEnv(jniEnv), JniKeyEvent(0)
|
||||
{
|
||||
if (JniEnv) {
|
||||
if (!Class_KeyEvent) {
|
||||
// Find java classes & functions on first call
|
||||
os::Printer::log("CKeyEventWrapper first initialize", ELL_DEBUG);
|
||||
jclass localClass = JniEnv->FindClass("android/view/KeyEvent");
|
||||
if (localClass) {
|
||||
Class_KeyEvent = reinterpret_cast<jclass>(JniEnv->NewGlobalRef(localClass));
|
||||
}
|
||||
|
||||
Method_constructor = JniEnv->GetMethodID(Class_KeyEvent, "<init>", "(II)V");
|
||||
Method_getUnicodeChar = JniEnv->GetMethodID(Class_KeyEvent, "getUnicodeChar", "(I)I");
|
||||
}
|
||||
|
||||
if (Class_KeyEvent && Method_constructor) {
|
||||
JniKeyEvent = JniEnv->NewObject(Class_KeyEvent, Method_constructor, action, code);
|
||||
} else {
|
||||
os::Printer::log("CKeyEventWrapper didn't find JNI classes/methods", ELL_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CKeyEventWrapper::~CKeyEventWrapper()
|
||||
{
|
||||
JniEnv->DeleteLocalRef(JniKeyEvent);
|
||||
}
|
||||
|
||||
int CKeyEventWrapper::getUnicodeChar(int metaState)
|
||||
{
|
||||
return JniEnv->CallIntMethod(JniKeyEvent, Method_getUnicodeChar, metaState);
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace irr
|
||||
35
src/Android/CKeyEventWrapper.h
Normal file
35
src/Android/CKeyEventWrapper.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
struct android_app;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace jni
|
||||
{
|
||||
|
||||
//! Minimal JNI wrapper class around android.view.KeyEvent
|
||||
//! NOTE: Only functions we actually use in the engine are wrapped
|
||||
//! This is currently not written to support multithreading - meaning threads are not attached/detached to the Java VM (to be discussed)
|
||||
class CKeyEventWrapper
|
||||
{
|
||||
public:
|
||||
CKeyEventWrapper(JNIEnv *jniEnv, int action, int code);
|
||||
~CKeyEventWrapper();
|
||||
|
||||
int getUnicodeChar(int metaState);
|
||||
|
||||
private:
|
||||
static jclass Class_KeyEvent;
|
||||
static jmethodID Method_getUnicodeChar;
|
||||
static jmethodID Method_constructor;
|
||||
JNIEnv *JniEnv;
|
||||
jobject JniKeyEvent; // this object in java
|
||||
};
|
||||
|
||||
} // namespace jni
|
||||
} // namespace irr
|
||||
135
src/BuiltInFont.h
Normal file
135
src/BuiltInFont.h
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
// header file generated by xxd
|
||||
// for the source bitmap, see builtInFont.png
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
const u8 BuiltInFontData[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
|
||||
0x04, 0x03, 0x00, 0x00, 0x00, 0x31, 0x10, 0x7c, 0xf8, 0x00, 0x00, 0x00,
|
||||
0x0f, 0x50, 0x4c, 0x54, 0x45, 0x18, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xc8, 0xc8, 0xae,
|
||||
0x00, 0x00, 0x05, 0x11, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x50,
|
||||
0x06, 0xf0, 0x62, 0x06, 0xb6, 0xed, 0xf4, 0x3a, 0x0c, 0xff, 0xf1, 0xba,
|
||||
0x00, 0xb9, 0x41, 0xde, 0x06, 0xd9, 0x7f, 0xba, 0x07, 0x98, 0x10, 0x6a,
|
||||
0x1f, 0xd2, 0xe2, 0x9f, 0xa0, 0xf8, 0x74, 0xaa, 0x65, 0xc9, 0x32, 0x23,
|
||||
0x39, 0xc4, 0xc1, 0xe9, 0xc5, 0x83, 0x0b, 0xd6, 0x58, 0x7d, 0xf4, 0xe9,
|
||||
0xec, 0x6a, 0xac, 0x6b, 0xd5, 0xc7, 0x9e, 0xfc, 0x17, 0x62, 0x09, 0x09,
|
||||
0x4c, 0x60, 0x59, 0x0b, 0x99, 0x0a, 0x6b, 0xde, 0xc1, 0x60, 0x62, 0x82,
|
||||
0xd2, 0xc0, 0x00, 0x90, 0x58, 0x21, 0xc2, 0xc6, 0x02, 0xef, 0x00, 0x16,
|
||||
0xc1, 0x2a, 0x2e, 0x46, 0x13, 0xb5, 0x0a, 0x00, 0x48, 0xc5, 0xef, 0x62,
|
||||
0x63, 0xde, 0x00, 0xd4, 0x28, 0xea, 0xc7, 0x77, 0x8a, 0x82, 0x07, 0xa0,
|
||||
0xcb, 0xe0, 0x0c, 0x80, 0x42, 0x41, 0x0b, 0x20, 0x83, 0xb0, 0xc1, 0xc2,
|
||||
0x8d, 0x22, 0x46, 0x12, 0x13, 0x1c, 0xec, 0x5b, 0x05, 0x03, 0xd0, 0x9d,
|
||||
0xc9, 0x00, 0xbc, 0x68, 0x21, 0x10, 0xa4, 0x01, 0xe8, 0x8a, 0x68, 0xb1,
|
||||
0x24, 0x73, 0x88, 0x30, 0x2d, 0xc8, 0x98, 0x11, 0x66, 0x7e, 0x02, 0x7c,
|
||||
0x26, 0x16, 0x6f, 0xc8, 0xf3, 0x0b, 0x9e, 0xd0, 0xf1, 0xb9, 0xfc, 0x3e,
|
||||
0x9d, 0x3d, 0x79, 0xd2, 0x61, 0xf4, 0xeb, 0x4c, 0x7e, 0x2c, 0x6e, 0x0e,
|
||||
0x41, 0x29, 0xab, 0x37, 0x53, 0xed, 0xf2, 0x46, 0xe3, 0x59, 0xfd, 0x53,
|
||||
0x09, 0x28, 0x80, 0x51, 0x8c, 0xb2, 0x4c, 0x42, 0xca, 0xdc, 0x32, 0xb4,
|
||||
0x26, 0xc8, 0xb2, 0x9a, 0x0f, 0xee, 0x2e, 0x8c, 0x29, 0x1d, 0xeb, 0xd4,
|
||||
0x54, 0x3d, 0x00, 0x62, 0x03, 0x70, 0x93, 0xfb, 0x01, 0xdd, 0xa6, 0x52,
|
||||
0xc2, 0xb8, 0x81, 0x98, 0x3a, 0x4a, 0x01, 0xca, 0x57, 0xdb, 0x92, 0xf0,
|
||||
0x37, 0xe3, 0x0a, 0x26, 0x15, 0x0d, 0x0d, 0x80, 0x59, 0x2a, 0x8b, 0x1f,
|
||||
0x01, 0xa6, 0x02, 0x50, 0x3f, 0xcd, 0x3f, 0xb6, 0xe0, 0xb6, 0x40, 0xbd,
|
||||
0x60, 0x8a, 0x47, 0xb3, 0x66, 0xe6, 0xf3, 0x10, 0xa7, 0x7e, 0x8b, 0x9e,
|
||||
0x65, 0x18, 0x4f, 0x01, 0xb1, 0x89, 0xc2, 0x7f, 0x2f, 0xe5, 0xe6, 0x10,
|
||||
0xb4, 0xac, 0xde, 0x4d, 0xb5, 0xcb, 0xdf, 0x34, 0xde, 0x92, 0x1e, 0x1b,
|
||||
0x73, 0x0f, 0xe7, 0xa0, 0x73, 0x9f, 0x26, 0x94, 0xcb, 0x75, 0x3e, 0x11,
|
||||
0xdd, 0xfd, 0x40, 0x4f, 0x9b, 0x90, 0x7c, 0x7f, 0x85, 0x63, 0x45, 0x42,
|
||||
0x30, 0x41, 0x1b, 0x71, 0x67, 0x8a, 0x98, 0x3d, 0x6b, 0x60, 0x07, 0x90,
|
||||
0xc0, 0x86, 0x59, 0xb1, 0xa9, 0xbc, 0x06, 0x28, 0x11, 0xc4, 0x6c, 0x5c,
|
||||
0x2e, 0x02, 0x3c, 0x8c, 0xa9, 0xc8, 0xc4, 0x1d, 0xdb, 0x82, 0x24, 0x12,
|
||||
0xd0, 0x4b, 0x80, 0x2d, 0x3e, 0x52, 0x56, 0x96, 0xa7, 0x4d, 0xf0, 0xd1,
|
||||
0x42, 0x37, 0xaa, 0x7f, 0xf5, 0x0e, 0x00, 0xdd, 0x01, 0x48, 0xf7, 0x06,
|
||||
0x0c, 0xf1, 0x00, 0x4c, 0xe1, 0xc9, 0x09, 0x40, 0xf6, 0x16, 0xfe, 0x4d,
|
||||
0x32, 0x7f, 0x31, 0x7f, 0x2f, 0x43, 0xca, 0xd1, 0x3a, 0x27, 0x9d, 0xfb,
|
||||
0x34, 0xa1, 0x5c, 0xae, 0xd3, 0x7b, 0xc5, 0x30, 0xf3, 0x5a, 0xce, 0xa3,
|
||||
0x0e, 0xa3, 0xcd, 0x68, 0x62, 0xc7, 0x46, 0xba, 0xc2, 0x9f, 0x88, 0x00,
|
||||
0x3c, 0xef, 0x5d, 0xfe, 0x49, 0xe6, 0xf5, 0x69, 0x60, 0x1b, 0x7e, 0x02,
|
||||
0xf0, 0x6b, 0x00, 0x97, 0xbd, 0x04, 0x85, 0xac, 0x98, 0xb0, 0x62, 0x42,
|
||||
0x83, 0xc4, 0x32, 0x21, 0x78, 0xad, 0xb9, 0xa3, 0x9a, 0x5c, 0x68, 0x09,
|
||||
0x17, 0x4d, 0x58, 0xc6, 0x52, 0x9d, 0xd1, 0x96, 0x35, 0x00, 0x6b, 0x3a,
|
||||
0xdc, 0x2d, 0x00, 0x3b, 0x80, 0x5e, 0x00, 0x34, 0xdd, 0x18, 0x0d, 0x16,
|
||||
0x5b, 0x05, 0x04, 0xa3, 0xdc, 0x01, 0x98, 0x16, 0xe6, 0x92, 0x56, 0x80,
|
||||
0xa5, 0x1c, 0x2d, 0x54, 0xf4, 0xe2, 0xac, 0x8e, 0x84, 0x5b, 0x7c, 0x00,
|
||||
0x46, 0xe4, 0x37, 0x00, 0xcc, 0x4d, 0x9e, 0xc3, 0xcc, 0x67, 0x2f, 0x15,
|
||||
0x75, 0x46, 0xcb, 0xdc, 0x67, 0x4d, 0xc7, 0x46, 0xd6, 0x4a, 0x2f, 0x18,
|
||||
0x6f, 0x6b, 0xb9, 0xff, 0x57, 0x4c, 0x16, 0x7e, 0x4b, 0xa9, 0x0c, 0x33,
|
||||
0x3e, 0x01, 0x30, 0x42, 0xc3, 0xaf, 0xcf, 0x2a, 0x08, 0xa2, 0x8c, 0xfe,
|
||||
0xac, 0x02, 0x41, 0xdc, 0x3e, 0xf8, 0x14, 0x40, 0x00, 0x9f, 0x56, 0x30,
|
||||
0x34, 0x07, 0x63, 0xde, 0x95, 0x73, 0xdb, 0xa7, 0x00, 0xf7, 0xbb, 0xf2,
|
||||
0xdb, 0x3a, 0xdc, 0x5f, 0xf3, 0xbe, 0x61, 0xfb, 0xf6, 0x5d, 0x5a, 0xa7,
|
||||
0x84, 0x5d, 0xbc, 0xdd, 0x7f, 0x00, 0x36, 0x75, 0xcc, 0x25, 0x7e, 0x90,
|
||||
0xe4, 0x8d, 0x76, 0x2c, 0xa1, 0x6c, 0xad, 0xcd, 0x09, 0xb3, 0x11, 0x07,
|
||||
0xd0, 0x18, 0x70, 0x43, 0x9d, 0xa9, 0x11, 0xcb, 0xf7, 0x53, 0x2e, 0x36,
|
||||
0xa0, 0x13, 0xc0, 0xcc, 0x25, 0xb3, 0x00, 0x06, 0x6c, 0x33, 0x32, 0x4e,
|
||||
0x13, 0x91, 0x86, 0x30, 0x54, 0x04, 0xe3, 0x22, 0x1d, 0x15, 0xd8, 0x8a,
|
||||
0x35, 0x15, 0x28, 0x34, 0x67, 0x66, 0x04, 0x1b, 0xec, 0x62, 0x18, 0x77,
|
||||
0x73, 0x26, 0x59, 0x83, 0x3d, 0xfd, 0x0d, 0x00, 0x01, 0xc6, 0x91, 0x8c,
|
||||
0x53, 0x43, 0x52, 0x90, 0x0a, 0x36, 0x23, 0x5b, 0x05, 0x36, 0xd9, 0x32,
|
||||
0x7a, 0x02, 0x35, 0x73, 0x0e, 0xbf, 0x4a, 0x8b, 0x7c, 0x99, 0x96, 0x77,
|
||||
0xee, 0x95, 0x32, 0x3f, 0xca, 0x93, 0x67, 0xdf, 0xbe, 0xd5, 0x27, 0x25,
|
||||
0xe9, 0x90, 0xfc, 0xfc, 0x01, 0xd8, 0xd5, 0x9a, 0x8e, 0x0f, 0x7c, 0x61,
|
||||
0x1e, 0xd2, 0x25, 0xf1, 0x10, 0x17, 0x94, 0xd3, 0xe6, 0xb1, 0x02, 0xff,
|
||||
0xc3, 0x8f, 0x4b, 0x97, 0x2f, 0x5d, 0xd0, 0x44, 0x66, 0x5b, 0x6b, 0x9c,
|
||||
0x1b, 0x29, 0x8c, 0x99, 0x05, 0x37, 0x80, 0x86, 0x53, 0x34, 0xe0, 0xd9,
|
||||
0x76, 0x12, 0x63, 0x8c, 0x62, 0x82, 0xe2, 0xd1, 0x01, 0x25, 0x41, 0x68,
|
||||
0xee, 0xd7, 0xca, 0x0f, 0x00, 0xe9, 0x47, 0xc2, 0xa6, 0x9e, 0x47, 0xd6,
|
||||
0x32, 0x8d, 0x41, 0x99, 0xa5, 0xdd, 0x90, 0x58, 0x1c, 0x15, 0x34, 0xd9,
|
||||
0x0a, 0x96, 0x7e, 0x05, 0x18, 0x53, 0x7d, 0x59, 0x01, 0x39, 0x01, 0x94,
|
||||
0x90, 0xf8, 0x30, 0xbf, 0xb6, 0x80, 0x6e, 0x00, 0xe8, 0x1e, 0x57, 0x4c,
|
||||
0x5e, 0xb5, 0xf0, 0xf2, 0x10, 0x7f, 0x27, 0xe0, 0xe7, 0x12, 0xde, 0x90,
|
||||
0xa1, 0xe7, 0x98, 0x65, 0x4f, 0x7d, 0xf6, 0x69, 0xc6, 0x69, 0x86, 0xc3,
|
||||
0x7d, 0xd1, 0x9a, 0x8b, 0x12, 0x5a, 0x2a, 0x5d, 0xa9, 0x3e, 0x8e, 0x61,
|
||||
0xa6, 0x77, 0xf2, 0x0e, 0x9d, 0x26, 0x26, 0x2a, 0x65, 0x98, 0xda, 0x98,
|
||||
0x26, 0x5f, 0x4b, 0xb7, 0xe3, 0x54, 0xfc, 0xe2, 0x2b, 0x99, 0x44, 0x63,
|
||||
0xab, 0x03, 0x95, 0xf3, 0x6b, 0x04, 0xac, 0x1b, 0x00, 0x36, 0x59, 0xe3,
|
||||
0x6f, 0x32, 0x7b, 0x84, 0x7d, 0x07, 0x10, 0x58, 0x94, 0x15, 0xc7, 0xd0,
|
||||
0x09, 0x47, 0x05, 0xfe, 0x11, 0xc0, 0x02, 0x73, 0x0e, 0xee, 0xa0, 0xa3,
|
||||
0x82, 0xf0, 0x02, 0x20, 0x73, 0x91, 0x3e, 0x86, 0x0c, 0xd4, 0x71, 0x88,
|
||||
0xc9, 0x38, 0x63, 0xe6, 0x95, 0x72, 0x6b, 0x80, 0x64, 0xa6, 0x7c, 0x24,
|
||||
0x7f, 0xcf, 0xf5, 0x72, 0xb2, 0xe3, 0xdd, 0x54, 0xf9, 0x3a, 0x87, 0x7d,
|
||||
0x5a, 0x4e, 0x5e, 0xf4, 0x59, 0xce, 0x5d, 0xef, 0x64, 0x9e, 0x2d, 0x70,
|
||||
0xfe, 0x23, 0xac, 0xae, 0x8c, 0x41, 0x55, 0x53, 0xa9, 0x77, 0xfe, 0xaa,
|
||||
0x50, 0x92, 0x00, 0xf3, 0x03, 0x47, 0x92, 0x00, 0xf6, 0xc3, 0xcf, 0x37,
|
||||
0x2e, 0x65, 0x8a, 0x8a, 0x82, 0xad, 0x01, 0x38, 0x44, 0xfe, 0xe7, 0x2f,
|
||||
0x40, 0x9a, 0x99, 0x37, 0x32, 0x62, 0xc2, 0x5d, 0x8d, 0x72, 0x52, 0xc9,
|
||||
0x0c, 0x0b, 0xa7, 0x85, 0x01, 0xe8, 0x12, 0xa7, 0x06, 0xdf, 0x00, 0x74,
|
||||
0x00, 0x80, 0x19, 0x00, 0x17, 0xed, 0x50, 0x4c, 0xf4, 0x0b, 0x00, 0x41,
|
||||
0x67, 0x05, 0xf7, 0x16, 0x20, 0x07, 0x99, 0x91, 0x11, 0x03, 0xf0, 0xbe,
|
||||
0x08, 0x51, 0xfd, 0x10, 0xc0, 0xed, 0x7a, 0x78, 0x40, 0x39, 0xf9, 0xec,
|
||||
0x53, 0xe7, 0xd4, 0x57, 0x64, 0xee, 0x16, 0x28, 0x95, 0xd9, 0xff, 0x4f,
|
||||
0x3c, 0x7f, 0x8f, 0x99, 0xcc, 0x7c, 0x72, 0xb8, 0x46, 0x47, 0x92, 0xf0,
|
||||
0xb9, 0x14, 0x40, 0xbe, 0x73, 0xd0, 0x2e, 0xdb, 0x5e, 0x81, 0x6b, 0x3d,
|
||||
0xe7, 0xa1, 0x84, 0x8c, 0xd3, 0x1c, 0x2f, 0x3f, 0xc4, 0x13, 0x40, 0x96,
|
||||
0x43, 0x20, 0xee, 0x32, 0x62, 0x0a, 0x40, 0x4d, 0x44, 0x41, 0x49, 0x88,
|
||||
0x63, 0x2d, 0x0f, 0x96, 0x4d, 0x48, 0x19, 0x53, 0x18, 0x17, 0x7f, 0x00,
|
||||
0x66, 0x4e, 0x62, 0x12, 0x2b, 0x4e, 0xe4, 0x34, 0xcd, 0x98, 0x44, 0x96,
|
||||
0x21, 0x64, 0x3a, 0x98, 0xae, 0x8d, 0x32, 0x15, 0x28, 0x09, 0x0a, 0xad,
|
||||
0x60, 0xcd, 0xc3, 0xb4, 0x30, 0x15, 0x30, 0x08, 0x61, 0x00, 0x20, 0xe3,
|
||||
0x35, 0xd2, 0x33, 0x88, 0xd9, 0xe5, 0x7e, 0x06, 0xb2, 0x06, 0xa0, 0x47,
|
||||
0x94, 0xec, 0x20, 0xfe, 0x91, 0xa7, 0x73, 0x06, 0xf9, 0x3e, 0x83, 0xf7,
|
||||
0xf8, 0xd0, 0x0e, 0xda, 0x4d, 0x65, 0xff, 0x3f, 0xf1, 0xfc, 0x7d, 0xcd,
|
||||
0x64, 0xe6, 0x93, 0x33, 0xf3, 0x21, 0xf2, 0x76, 0x53, 0x78, 0xfc, 0x7f,
|
||||
0x94, 0xa4, 0x46, 0x17, 0x17, 0x06, 0xca, 0x80, 0xa3, 0x88, 0x03, 0xa5,
|
||||
0x06, 0x88, 0x30, 0xb0, 0xb8, 0x90, 0x69, 0x88, 0x00, 0xd8, 0x00, 0x17,
|
||||
0x07, 0x17, 0x41, 0xf2, 0x3c, 0xc2, 0x08, 0x8a, 0x55, 0x47, 0x41, 0x41,
|
||||
0x07, 0x20, 0x16, 0x21, 0xdb, 0x05, 0x14, 0x19, 0xc0, 0x30, 0x28, 0x0c,
|
||||
0x70, 0x71, 0x01, 0x06, 0x22, 0x10, 0x90, 0x3d, 0x47, 0x82, 0x48, 0xa6,
|
||||
0x23, 0x13, 0x8c, 0x02, 0x00, 0xe7, 0xfb, 0x84, 0x1b, 0xf9, 0x8e, 0xc0,
|
||||
0x23, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
|
||||
0x82
|
||||
};
|
||||
|
||||
const u32 BuiltInFontDataSize = sizeof(BuiltInFontData);
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
755
src/CAnimatedMeshSceneNode.cpp
Normal file
755
src/CAnimatedMeshSceneNode.cpp
Normal file
@@ -0,0 +1,755 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAnimatedMeshSceneNode.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "S3DVertex.h"
|
||||
#include "os.h"
|
||||
#include "CSkinnedMesh.h"
|
||||
#include "IDummyTransformationSceneNode.h"
|
||||
#include "IBoneSceneNode.h"
|
||||
#include "IMaterialRenderer.h"
|
||||
#include "IMesh.h"
|
||||
#include "IMeshCache.h"
|
||||
#include "IAnimatedMesh.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "quaternion.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh,
|
||||
ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position,
|
||||
const core::vector3df &rotation,
|
||||
const core::vector3df &scale) :
|
||||
IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale),
|
||||
Mesh(0),
|
||||
StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),
|
||||
CurrentFrameNr(0.f), LastTimeMs(0),
|
||||
TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),
|
||||
JointMode(EJUOR_NONE), JointsUsed(false),
|
||||
Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),
|
||||
LoopCallBack(0), PassCount(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CAnimatedMeshSceneNode");
|
||||
#endif
|
||||
|
||||
setMesh(mesh);
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()
|
||||
{
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->drop();
|
||||
if (Mesh)
|
||||
Mesh->drop();
|
||||
}
|
||||
|
||||
//! Sets the current frame. From now on the animation is played from this frame.
|
||||
void CAnimatedMeshSceneNode::setCurrentFrame(f32 frame)
|
||||
{
|
||||
// if you pass an out of range value, we just clamp it
|
||||
CurrentFrameNr = core::clamp(frame, (f32)StartFrame, (f32)EndFrame);
|
||||
|
||||
beginTransition(); // transit to this frame if enabled
|
||||
}
|
||||
|
||||
//! Returns the currently displayed frame number.
|
||||
f32 CAnimatedMeshSceneNode::getFrameNr() const
|
||||
{
|
||||
return CurrentFrameNr;
|
||||
}
|
||||
|
||||
//! Get CurrentFrameNr and update transiting settings
|
||||
void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
|
||||
{
|
||||
if (Transiting != 0.f) {
|
||||
TransitingBlend += (f32)(timeMs)*Transiting;
|
||||
if (TransitingBlend > 1.f) {
|
||||
Transiting = 0.f;
|
||||
TransitingBlend = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
if (StartFrame == EndFrame) {
|
||||
CurrentFrameNr = (f32)StartFrame; // Support for non animated meshes
|
||||
} else if (Looping) {
|
||||
// play animation looped
|
||||
CurrentFrameNr += timeMs * FramesPerSecond;
|
||||
|
||||
// We have no interpolation between EndFrame and StartFrame,
|
||||
// the last frame must be identical to first one with our current solution.
|
||||
if (FramesPerSecond > 0.f) { // forwards...
|
||||
if (CurrentFrameNr > EndFrame)
|
||||
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, (f32)(EndFrame - StartFrame));
|
||||
} else // backwards...
|
||||
{
|
||||
if (CurrentFrameNr < StartFrame)
|
||||
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, (f32)(EndFrame - StartFrame));
|
||||
}
|
||||
} else {
|
||||
// play animation non looped
|
||||
|
||||
CurrentFrameNr += timeMs * FramesPerSecond;
|
||||
if (FramesPerSecond > 0.f) { // forwards...
|
||||
if (CurrentFrameNr > (f32)EndFrame) {
|
||||
CurrentFrameNr = (f32)EndFrame;
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->OnAnimationEnd(this);
|
||||
}
|
||||
} else // backwards...
|
||||
{
|
||||
if (CurrentFrameNr < (f32)StartFrame) {
|
||||
CurrentFrameNr = (f32)StartFrame;
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->OnAnimationEnd(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAnimatedMeshSceneNode::OnRegisterSceneNode()
|
||||
{
|
||||
if (IsVisible && Mesh) {
|
||||
// because this node supports rendering of mixed mode meshes consisting of
|
||||
// transparent and solid material at the same time, we need to go through all
|
||||
// materials, check of what type they are and register this node for the right
|
||||
// render pass according to that.
|
||||
|
||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||
|
||||
PassCount = 0;
|
||||
int transparentCount = 0;
|
||||
int solidCount = 0;
|
||||
|
||||
// count transparent and solid materials in this scene node
|
||||
const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();
|
||||
for (u32 i = 0; i < numMaterials; ++i) {
|
||||
const video::SMaterial &material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];
|
||||
|
||||
if (driver->needsTransparentRenderPass(material))
|
||||
++transparentCount;
|
||||
else
|
||||
++solidCount;
|
||||
|
||||
if (solidCount && transparentCount)
|
||||
break;
|
||||
}
|
||||
|
||||
// register according to material types counted
|
||||
|
||||
if (solidCount)
|
||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
|
||||
|
||||
if (transparentCount)
|
||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
|
||||
|
||||
ISceneNode::OnRegisterSceneNode();
|
||||
}
|
||||
}
|
||||
|
||||
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
|
||||
{
|
||||
if (Mesh->getMeshType() != EAMT_SKINNED) {
|
||||
s32 frameNr = (s32)getFrameNr();
|
||||
s32 frameBlend = (s32)(core::fract(getFrameNr()) * 1000.f);
|
||||
return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame);
|
||||
} else {
|
||||
// As multiple scene nodes may be sharing the same skinned mesh, we have to
|
||||
// re-animate it every frame to ensure that this node gets the mesh that it needs.
|
||||
|
||||
CSkinnedMesh *skinnedMesh = static_cast<CSkinnedMesh *>(Mesh);
|
||||
|
||||
if (JointMode == EJUOR_CONTROL) // write to mesh
|
||||
skinnedMesh->transferJointsToMesh(JointChildSceneNodes);
|
||||
else
|
||||
skinnedMesh->animateMesh(getFrameNr(), 1.0f);
|
||||
|
||||
// Update the skinned mesh for the current joint transforms.
|
||||
skinnedMesh->skinMesh();
|
||||
|
||||
if (JointMode == EJUOR_READ) { // read from mesh
|
||||
skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
|
||||
|
||||
//---slow---
|
||||
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n)
|
||||
if (JointChildSceneNodes[n]->getParent() == this) {
|
||||
JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option
|
||||
}
|
||||
}
|
||||
|
||||
if (JointMode == EJUOR_CONTROL) {
|
||||
// For meshes other than EJUOR_CONTROL, this is done by calling animateMesh()
|
||||
skinnedMesh->updateBoundingBox();
|
||||
}
|
||||
|
||||
return skinnedMesh;
|
||||
}
|
||||
}
|
||||
|
||||
//! OnAnimate() is called just before rendering the whole scene.
|
||||
void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
|
||||
{
|
||||
if (LastTimeMs == 0) { // first frame
|
||||
LastTimeMs = timeMs;
|
||||
}
|
||||
|
||||
// set CurrentFrameNr
|
||||
buildFrameNr(timeMs - LastTimeMs);
|
||||
LastTimeMs = timeMs;
|
||||
|
||||
IAnimatedMeshSceneNode::OnAnimate(timeMs);
|
||||
}
|
||||
|
||||
//! renders the node.
|
||||
void CAnimatedMeshSceneNode::render()
|
||||
{
|
||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||
|
||||
if (!Mesh || !driver)
|
||||
return;
|
||||
|
||||
const bool isTransparentPass =
|
||||
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
|
||||
|
||||
++PassCount;
|
||||
|
||||
scene::IMesh *m = getMeshForCurrentFrame();
|
||||
|
||||
if (m) {
|
||||
Box = m->getBoundingBox();
|
||||
} else {
|
||||
#ifdef _DEBUG
|
||||
os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
||||
|
||||
for (u32 i = 0; i < m->getMeshBufferCount(); ++i) {
|
||||
const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
|
||||
|
||||
// only render transparent buffer if this is the transparent render pass
|
||||
// and solid only in solid pass
|
||||
if (transparent == isTransparentPass) {
|
||||
scene::IMeshBuffer *mb = m->getMeshBuffer(i);
|
||||
const video::SMaterial &material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];
|
||||
if (RenderFromIdentity)
|
||||
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
||||
else if (Mesh->getMeshType() == EAMT_SKINNED)
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer *)mb)->Transformation);
|
||||
|
||||
driver->setMaterial(material);
|
||||
driver->drawMeshBuffer(mb);
|
||||
}
|
||||
}
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
||||
|
||||
// for debug purposes only:
|
||||
if (DebugDataVisible && PassCount == 1) {
|
||||
video::SMaterial debug_mat;
|
||||
debug_mat.Lighting = false;
|
||||
debug_mat.AntiAliasing = 0;
|
||||
driver->setMaterial(debug_mat);
|
||||
// show normals
|
||||
if (DebugDataVisible & scene::EDS_NORMALS) {
|
||||
const f32 debugNormalLength = 1.f;
|
||||
const video::SColor debugNormalColor = video::SColor(255, 34, 221, 221);
|
||||
const u32 count = m->getMeshBufferCount();
|
||||
|
||||
// draw normals
|
||||
for (u32 g = 0; g < count; ++g) {
|
||||
scene::IMeshBuffer *mb = m->getMeshBuffer(g);
|
||||
if (RenderFromIdentity)
|
||||
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
||||
else if (Mesh->getMeshType() == EAMT_SKINNED)
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer *)mb)->Transformation);
|
||||
|
||||
driver->drawMeshBufferNormals(mb, debugNormalLength, debugNormalColor);
|
||||
}
|
||||
}
|
||||
|
||||
debug_mat.ZBuffer = video::ECFN_DISABLED;
|
||||
debug_mat.Lighting = false;
|
||||
driver->setMaterial(debug_mat);
|
||||
|
||||
if (DebugDataVisible & scene::EDS_BBOX)
|
||||
driver->draw3DBox(Box, video::SColor(255, 255, 255, 255));
|
||||
|
||||
// show bounding box
|
||||
if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) {
|
||||
for (u32 g = 0; g < m->getMeshBufferCount(); ++g) {
|
||||
const IMeshBuffer *mb = m->getMeshBuffer(g);
|
||||
|
||||
if (Mesh->getMeshType() == EAMT_SKINNED)
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer *)mb)->Transformation);
|
||||
driver->draw3DBox(mb->getBoundingBox(), video::SColor(255, 190, 128, 128));
|
||||
}
|
||||
}
|
||||
|
||||
// show skeleton
|
||||
if (DebugDataVisible & scene::EDS_SKELETON) {
|
||||
if (Mesh->getMeshType() == EAMT_SKINNED) {
|
||||
// draw skeleton
|
||||
|
||||
for (u32 g = 0; g < ((ISkinnedMesh *)Mesh)->getAllJoints().size(); ++g) {
|
||||
ISkinnedMesh::SJoint *joint = ((ISkinnedMesh *)Mesh)->getAllJoints()[g];
|
||||
|
||||
for (u32 n = 0; n < joint->Children.size(); ++n) {
|
||||
driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
|
||||
joint->Children[n]->GlobalAnimatedMatrix.getTranslation(),
|
||||
video::SColor(255, 51, 66, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// show mesh
|
||||
if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY) {
|
||||
debug_mat.Lighting = false;
|
||||
debug_mat.Wireframe = true;
|
||||
debug_mat.ZBuffer = video::ECFN_DISABLED;
|
||||
driver->setMaterial(debug_mat);
|
||||
|
||||
for (u32 g = 0; g < m->getMeshBufferCount(); ++g) {
|
||||
const IMeshBuffer *mb = m->getMeshBuffer(g);
|
||||
if (RenderFromIdentity)
|
||||
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
||||
else if (Mesh->getMeshType() == EAMT_SKINNED)
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation * ((SSkinMeshBuffer *)mb)->Transformation);
|
||||
driver->drawMeshBuffer(mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns the current start frame number.
|
||||
s32 CAnimatedMeshSceneNode::getStartFrame() const
|
||||
{
|
||||
return StartFrame;
|
||||
}
|
||||
|
||||
//! Returns the current start frame number.
|
||||
s32 CAnimatedMeshSceneNode::getEndFrame() const
|
||||
{
|
||||
return EndFrame;
|
||||
}
|
||||
|
||||
//! sets the frames between the animation is looped.
|
||||
//! the default is 0 - MaximalFrameCount of the mesh.
|
||||
bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end)
|
||||
{
|
||||
const s32 maxFrameCount = Mesh->getFrameCount() - 1;
|
||||
if (end < begin) {
|
||||
StartFrame = core::s32_clamp(end, 0, maxFrameCount);
|
||||
EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);
|
||||
} else {
|
||||
StartFrame = core::s32_clamp(begin, 0, maxFrameCount);
|
||||
EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);
|
||||
}
|
||||
if (FramesPerSecond < 0)
|
||||
setCurrentFrame((f32)EndFrame);
|
||||
else
|
||||
setCurrentFrame((f32)StartFrame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! sets the speed with witch the animation is played
|
||||
void CAnimatedMeshSceneNode::setAnimationSpeed(f32 framesPerSecond)
|
||||
{
|
||||
FramesPerSecond = framesPerSecond * 0.001f;
|
||||
}
|
||||
|
||||
f32 CAnimatedMeshSceneNode::getAnimationSpeed() const
|
||||
{
|
||||
return FramesPerSecond * 1000.f;
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CAnimatedMeshSceneNode::getBoundingBox() const
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! returns the material based on the zero based index i.
|
||||
video::SMaterial &CAnimatedMeshSceneNode::getMaterial(u32 i)
|
||||
{
|
||||
if (i >= Materials.size())
|
||||
return ISceneNode::getMaterial(i);
|
||||
|
||||
return Materials[i];
|
||||
}
|
||||
|
||||
//! returns amount of materials used by this scene node.
|
||||
u32 CAnimatedMeshSceneNode::getMaterialCount() const
|
||||
{
|
||||
return Materials.size();
|
||||
}
|
||||
|
||||
//! Returns a pointer to a child node, which has the same transformation as
|
||||
//! the corresponding joint, if the mesh in this scene node is a skinned mesh.
|
||||
IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(const c8 *jointName)
|
||||
{
|
||||
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) {
|
||||
os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
checkJoints();
|
||||
|
||||
ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh;
|
||||
|
||||
const std::optional<u32> number = skinnedMesh->getJointNumber(jointName);
|
||||
|
||||
if (!number.has_value()) {
|
||||
os::Printer::log("Joint with specified name not found in skinned mesh", jointName, ELL_DEBUG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (JointChildSceneNodes.size() <= *number) {
|
||||
os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return JointChildSceneNodes[*number];
|
||||
}
|
||||
|
||||
//! Returns a pointer to a child node, which has the same transformation as
|
||||
//! the corresponding joint, if the mesh in this scene node is a skinned mesh.
|
||||
IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(u32 jointID)
|
||||
{
|
||||
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) {
|
||||
os::Printer::log("No mesh, or mesh not of skinned mesh type", ELL_WARNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
checkJoints();
|
||||
|
||||
if (JointChildSceneNodes.size() <= jointID) {
|
||||
os::Printer::log("Joint not loaded into node", ELL_WARNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return JointChildSceneNodes[jointID];
|
||||
}
|
||||
|
||||
//! Gets joint count.
|
||||
u32 CAnimatedMeshSceneNode::getJointCount() const
|
||||
{
|
||||
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
|
||||
return 0;
|
||||
|
||||
ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh;
|
||||
|
||||
return skinnedMesh->getJointCount();
|
||||
}
|
||||
|
||||
//! Removes a child from this scene node.
|
||||
//! Implemented here, to be able to remove the shadow properly, if there is one,
|
||||
//! or to remove attached childs.
|
||||
bool CAnimatedMeshSceneNode::removeChild(ISceneNode *child)
|
||||
{
|
||||
if (ISceneNode::removeChild(child)) {
|
||||
if (JointsUsed) { // stop weird bugs caused while changing parents as the joints are being created
|
||||
for (u32 i = 0; i < JointChildSceneNodes.size(); ++i) {
|
||||
if (JointChildSceneNodes[i] == child) {
|
||||
JointChildSceneNodes[i] = 0; // remove link to child
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Sets looping mode which is on by default. If set to false,
|
||||
//! animations will not be looped.
|
||||
void CAnimatedMeshSceneNode::setLoopMode(bool playAnimationLooped)
|
||||
{
|
||||
Looping = playAnimationLooped;
|
||||
}
|
||||
|
||||
//! returns the current loop mode
|
||||
bool CAnimatedMeshSceneNode::getLoopMode() const
|
||||
{
|
||||
return Looping;
|
||||
}
|
||||
|
||||
//! Sets a callback interface which will be called if an animation
|
||||
//! playback has ended. Set this to 0 to disable the callback again.
|
||||
void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack *callback)
|
||||
{
|
||||
if (callback == LoopCallBack)
|
||||
return;
|
||||
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->drop();
|
||||
|
||||
LoopCallBack = callback;
|
||||
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->grab();
|
||||
}
|
||||
|
||||
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
|
||||
void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly)
|
||||
{
|
||||
ReadOnlyMaterials = readonly;
|
||||
}
|
||||
|
||||
//! Returns if the scene node should not copy the materials of the mesh but use them in a read only style
|
||||
bool CAnimatedMeshSceneNode::isReadOnlyMaterials() const
|
||||
{
|
||||
return ReadOnlyMaterials;
|
||||
}
|
||||
|
||||
//! Sets a new mesh
|
||||
void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
|
||||
{
|
||||
if (!mesh)
|
||||
return; // won't set null mesh
|
||||
|
||||
if (Mesh != mesh) {
|
||||
if (Mesh)
|
||||
Mesh->drop();
|
||||
|
||||
Mesh = mesh;
|
||||
|
||||
// grab the mesh (it's non-null!)
|
||||
Mesh->grab();
|
||||
}
|
||||
|
||||
// get materials and bounding box
|
||||
Box = Mesh->getBoundingBox();
|
||||
|
||||
IMesh *m = Mesh->getMesh(0, 0);
|
||||
if (m) {
|
||||
Materials.clear();
|
||||
Materials.reallocate(m->getMeshBufferCount());
|
||||
|
||||
for (u32 i = 0; i < m->getMeshBufferCount(); ++i) {
|
||||
IMeshBuffer *mb = m->getMeshBuffer(i);
|
||||
if (mb)
|
||||
Materials.push_back(mb->getMaterial());
|
||||
else
|
||||
Materials.push_back(video::SMaterial());
|
||||
}
|
||||
}
|
||||
|
||||
// clean up joint nodes
|
||||
if (JointsUsed) {
|
||||
JointsUsed = false;
|
||||
checkJoints();
|
||||
}
|
||||
|
||||
// get start and begin time
|
||||
setAnimationSpeed(Mesh->getAnimationSpeed()); // NOTE: This had been commented out (but not removed!) in r3526. Which caused meshloader-values for speed to be ignored unless users specified explicitly. Missing a test-case where this could go wrong so I put the code back in.
|
||||
setFrameLoop(0, Mesh->getFrameCount() - 1);
|
||||
}
|
||||
|
||||
//! updates the absolute position based on the relative and the parents position
|
||||
void CAnimatedMeshSceneNode::updateAbsolutePosition()
|
||||
{
|
||||
IAnimatedMeshSceneNode::updateAbsolutePosition();
|
||||
}
|
||||
|
||||
//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)
|
||||
void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode)
|
||||
{
|
||||
checkJoints();
|
||||
JointMode = mode;
|
||||
}
|
||||
|
||||
//! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2)
|
||||
//! you must call animateJoints(), or the mesh will not animate
|
||||
void CAnimatedMeshSceneNode::setTransitionTime(f32 time)
|
||||
{
|
||||
const u32 ttime = (u32)core::floor32(time * 1000.0f);
|
||||
if (TransitionTime == ttime)
|
||||
return;
|
||||
TransitionTime = ttime;
|
||||
if (ttime != 0)
|
||||
setJointMode(EJUOR_CONTROL);
|
||||
else
|
||||
setJointMode(EJUOR_NONE);
|
||||
}
|
||||
|
||||
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
||||
void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
|
||||
{
|
||||
RenderFromIdentity = enable;
|
||||
}
|
||||
|
||||
//! updates the joint positions of this mesh
|
||||
void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
|
||||
{
|
||||
if (Mesh && Mesh->getMeshType() == EAMT_SKINNED) {
|
||||
checkJoints();
|
||||
const f32 frame = getFrameNr(); // old?
|
||||
|
||||
CSkinnedMesh *skinnedMesh = static_cast<CSkinnedMesh *>(Mesh);
|
||||
|
||||
skinnedMesh->transferOnlyJointsHintsToMesh(JointChildSceneNodes);
|
||||
skinnedMesh->animateMesh(frame, 1.0f);
|
||||
skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
|
||||
|
||||
//-----------------------------------------
|
||||
// Transition
|
||||
//-----------------------------------------
|
||||
|
||||
if (Transiting != 0.f) {
|
||||
// Init additional matrices
|
||||
if (PretransitingSave.size() < JointChildSceneNodes.size()) {
|
||||
for (u32 n = PretransitingSave.size(); n < JointChildSceneNodes.size(); ++n)
|
||||
PretransitingSave.push_back(core::matrix4());
|
||||
}
|
||||
|
||||
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) {
|
||||
//------Position------
|
||||
|
||||
JointChildSceneNodes[n]->setPosition(
|
||||
core::lerp(
|
||||
PretransitingSave[n].getTranslation(),
|
||||
JointChildSceneNodes[n]->getPosition(),
|
||||
TransitingBlend));
|
||||
|
||||
//------Rotation------
|
||||
|
||||
// Code is slow, needs to be fixed up
|
||||
|
||||
const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees() * core::DEGTORAD);
|
||||
const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation() * core::DEGTORAD);
|
||||
|
||||
core::quaternion QRotation;
|
||||
QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);
|
||||
|
||||
core::vector3df tmpVector;
|
||||
QRotation.toEuler(tmpVector);
|
||||
tmpVector *= core::RADTODEG; // convert from radians back to degrees
|
||||
JointChildSceneNodes[n]->setRotation(tmpVector);
|
||||
|
||||
//------Scale------
|
||||
|
||||
// JointChildSceneNodes[n]->setScale(
|
||||
// core::lerp(
|
||||
// PretransitingSave[n].getScale(),
|
||||
// JointChildSceneNodes[n]->getScale(),
|
||||
// TransitingBlend));
|
||||
}
|
||||
}
|
||||
|
||||
if (CalculateAbsolutePositions) {
|
||||
//---slow---
|
||||
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) {
|
||||
if (JointChildSceneNodes[n]->getParent() == this) {
|
||||
JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
void CAnimatedMeshSceneNode::checkJoints()
|
||||
{
|
||||
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
|
||||
return;
|
||||
|
||||
if (!JointsUsed) {
|
||||
for (u32 i = 0; i < JointChildSceneNodes.size(); ++i)
|
||||
removeChild(JointChildSceneNodes[i]);
|
||||
JointChildSceneNodes.clear();
|
||||
|
||||
// Create joints for SkinnedMesh
|
||||
((CSkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
|
||||
((CSkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
|
||||
|
||||
JointsUsed = true;
|
||||
JointMode = EJUOR_READ;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
void CAnimatedMeshSceneNode::beginTransition()
|
||||
{
|
||||
if (!JointsUsed)
|
||||
return;
|
||||
|
||||
if (TransitionTime != 0) {
|
||||
// Check the array is big enough
|
||||
if (PretransitingSave.size() < JointChildSceneNodes.size()) {
|
||||
for (u32 n = PretransitingSave.size(); n < JointChildSceneNodes.size(); ++n)
|
||||
PretransitingSave.push_back(core::matrix4());
|
||||
}
|
||||
|
||||
// Copy the position of joints
|
||||
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n)
|
||||
PretransitingSave[n] = JointChildSceneNodes[n]->getRelativeTransformation();
|
||||
|
||||
Transiting = core::reciprocal((f32)TransitionTime);
|
||||
}
|
||||
TransitingBlend = 0.f;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||
{
|
||||
if (!newParent)
|
||||
newParent = Parent;
|
||||
if (!newManager)
|
||||
newManager = SceneManager;
|
||||
|
||||
CAnimatedMeshSceneNode *newNode =
|
||||
new CAnimatedMeshSceneNode(Mesh, NULL, newManager, ID, RelativeTranslation,
|
||||
RelativeRotation, RelativeScale);
|
||||
|
||||
if (newParent) {
|
||||
newNode->setParent(newParent); // not in constructor because virtual overload for updateAbsolutePosition won't be called
|
||||
newNode->drop();
|
||||
}
|
||||
|
||||
newNode->cloneMembers(this, newManager);
|
||||
|
||||
newNode->Materials = Materials;
|
||||
newNode->Box = Box;
|
||||
newNode->Mesh = Mesh;
|
||||
newNode->StartFrame = StartFrame;
|
||||
newNode->EndFrame = EndFrame;
|
||||
newNode->FramesPerSecond = FramesPerSecond;
|
||||
newNode->CurrentFrameNr = CurrentFrameNr;
|
||||
newNode->JointMode = JointMode;
|
||||
newNode->JointsUsed = JointsUsed;
|
||||
newNode->TransitionTime = TransitionTime;
|
||||
newNode->Transiting = Transiting;
|
||||
newNode->TransitingBlend = TransitingBlend;
|
||||
newNode->Looping = Looping;
|
||||
newNode->ReadOnlyMaterials = ReadOnlyMaterials;
|
||||
newNode->LoopCallBack = LoopCallBack;
|
||||
if (newNode->LoopCallBack)
|
||||
newNode->LoopCallBack->grab();
|
||||
newNode->PassCount = PassCount;
|
||||
newNode->JointChildSceneNodes = JointChildSceneNodes;
|
||||
newNode->PretransitingSave = PretransitingSave;
|
||||
newNode->RenderFromIdentity = RenderFromIdentity;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
177
src/CAnimatedMeshSceneNode.h
Normal file
177
src/CAnimatedMeshSceneNode.h
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IAnimatedMeshSceneNode.h"
|
||||
#include "IAnimatedMesh.h"
|
||||
|
||||
#include "matrix4.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
class IDummyTransformationSceneNode;
|
||||
|
||||
class CAnimatedMeshSceneNode : public IAnimatedMeshSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CAnimatedMeshSceneNode(IAnimatedMesh *mesh, ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position = core::vector3df(0, 0, 0),
|
||||
const core::vector3df &rotation = core::vector3df(0, 0, 0),
|
||||
const core::vector3df &scale = core::vector3df(1.0f, 1.0f, 1.0f));
|
||||
|
||||
//! destructor
|
||||
virtual ~CAnimatedMeshSceneNode();
|
||||
|
||||
//! sets the current frame. from now on the animation is played from this frame.
|
||||
void setCurrentFrame(f32 frame) override;
|
||||
|
||||
//! frame
|
||||
void OnRegisterSceneNode() override;
|
||||
|
||||
//! OnAnimate() is called just before rendering the whole scene.
|
||||
void OnAnimate(u32 timeMs) override;
|
||||
|
||||
//! renders the node.
|
||||
void render() override;
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
//! sets the frames between the animation is looped.
|
||||
//! the default is 0 - MaximalFrameCount of the mesh.
|
||||
//! NOTE: setMesh will also change this value and set it to the full range of animations of the mesh
|
||||
bool setFrameLoop(s32 begin, s32 end) override;
|
||||
|
||||
//! Sets looping mode which is on by default. If set to false,
|
||||
//! animations will not be looped.
|
||||
void setLoopMode(bool playAnimationLooped) override;
|
||||
|
||||
//! returns the current loop mode
|
||||
bool getLoopMode() const override;
|
||||
|
||||
//! Sets a callback interface which will be called if an animation
|
||||
//! playback has ended. Set this to 0 to disable the callback again.
|
||||
void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) override;
|
||||
|
||||
//! sets the speed with which the animation is played
|
||||
//! NOTE: setMesh will also change this value and set it to the default speed of the mesh
|
||||
void setAnimationSpeed(f32 framesPerSecond) override;
|
||||
|
||||
//! gets the speed with which the animation is played
|
||||
f32 getAnimationSpeed() const override;
|
||||
|
||||
//! returns the material based on the zero based index i. To get the amount
|
||||
//! of materials used by this scene node, use getMaterialCount().
|
||||
//! This function is needed for inserting the node into the scene hierarchy on a
|
||||
//! optimal position for minimizing renderstate changes, but can also be used
|
||||
//! to directly modify the material of a scene node.
|
||||
video::SMaterial &getMaterial(u32 i) override;
|
||||
|
||||
//! returns amount of materials used by this scene node.
|
||||
u32 getMaterialCount() const override;
|
||||
|
||||
//! Returns a pointer to a child node, which has the same transformation as
|
||||
//! the corresponding joint, if the mesh in this scene node is a skinned mesh.
|
||||
IBoneSceneNode *getJointNode(const c8 *jointName) override;
|
||||
|
||||
//! same as getJointNode(const c8* jointName), but based on id
|
||||
IBoneSceneNode *getJointNode(u32 jointID) override;
|
||||
|
||||
//! Gets joint count.
|
||||
u32 getJointCount() const override;
|
||||
|
||||
//! Removes a child from this scene node.
|
||||
//! Implemented here, to be able to remove the shadow properly, if there is one,
|
||||
//! or to remove attached child.
|
||||
bool removeChild(ISceneNode *child) override;
|
||||
|
||||
//! Returns the current displayed frame number.
|
||||
f32 getFrameNr() const override;
|
||||
//! Returns the current start frame number.
|
||||
s32 getStartFrame() const override;
|
||||
//! Returns the current end frame number.
|
||||
s32 getEndFrame() const override;
|
||||
|
||||
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
|
||||
/* In this way it is possible to change the materials a mesh causing all mesh scene nodes
|
||||
referencing this mesh to change too. */
|
||||
void setReadOnlyMaterials(bool readonly) override;
|
||||
|
||||
//! Returns if the scene node should not copy the materials of the mesh but use them in a read only style
|
||||
bool isReadOnlyMaterials() const override;
|
||||
|
||||
//! Sets a new mesh
|
||||
void setMesh(IAnimatedMesh *mesh) override;
|
||||
|
||||
//! Returns the current mesh
|
||||
IAnimatedMesh *getMesh(void) override { return Mesh; }
|
||||
|
||||
//! Returns type of the scene node
|
||||
ESCENE_NODE_TYPE getType() const override { return ESNT_ANIMATED_MESH; }
|
||||
|
||||
//! updates the absolute position based on the relative and the parents position
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)
|
||||
void setJointMode(E_JOINT_UPDATE_ON_RENDER mode) override;
|
||||
|
||||
//! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2)
|
||||
//! you must call animateJoints(), or the mesh will not animate
|
||||
void setTransitionTime(f32 Time) override;
|
||||
|
||||
//! updates the joint positions of this mesh
|
||||
void animateJoints(bool CalculateAbsolutePositions = true) override;
|
||||
|
||||
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
||||
void setRenderFromIdentity(bool On) override;
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
/** \param newParent An optional new parent.
|
||||
\param newManager An optional new scene manager.
|
||||
\return The newly created clone of this node. */
|
||||
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
|
||||
|
||||
private:
|
||||
//! Get a static mesh for the current frame of this animated mesh
|
||||
IMesh *getMeshForCurrentFrame();
|
||||
|
||||
void buildFrameNr(u32 timeMs);
|
||||
void checkJoints();
|
||||
void beginTransition();
|
||||
|
||||
core::array<video::SMaterial> Materials;
|
||||
core::aabbox3d<f32> Box;
|
||||
IAnimatedMesh *Mesh;
|
||||
|
||||
s32 StartFrame;
|
||||
s32 EndFrame;
|
||||
f32 FramesPerSecond;
|
||||
f32 CurrentFrameNr;
|
||||
|
||||
u32 LastTimeMs;
|
||||
u32 TransitionTime; // Transition time in millisecs
|
||||
f32 Transiting; // is mesh transiting (plus cache of TransitionTime)
|
||||
f32 TransitingBlend; // 0-1, calculated on buildFrameNr
|
||||
|
||||
// 0-unused, 1-get joints only, 2-set joints only, 3-move and set
|
||||
E_JOINT_UPDATE_ON_RENDER JointMode;
|
||||
bool JointsUsed;
|
||||
|
||||
bool Looping;
|
||||
bool ReadOnlyMaterials;
|
||||
bool RenderFromIdentity;
|
||||
|
||||
IAnimationEndCallBack *LoopCallBack;
|
||||
s32 PassCount;
|
||||
|
||||
core::array<IBoneSceneNode *> JointChildSceneNodes;
|
||||
core::array<core::matrix4> PretransitingSave;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
144
src/CAttributeImpl.h
Normal file
144
src/CAttributeImpl.h
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAttributes.h"
|
||||
#include "fast_atof.h"
|
||||
#include "ITexture.h"
|
||||
#include "IVideoDriver.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/*
|
||||
Basic types, check documentation in IAttribute.h to see how they generally work.
|
||||
*/
|
||||
|
||||
// Attribute implemented for boolean values
|
||||
class CBoolAttribute : public IAttribute
|
||||
{
|
||||
public:
|
||||
CBoolAttribute(const char *name, bool value)
|
||||
{
|
||||
Name = name;
|
||||
setBool(value);
|
||||
}
|
||||
|
||||
s32 getInt() const override
|
||||
{
|
||||
return BoolValue ? 1 : 0;
|
||||
}
|
||||
|
||||
f32 getFloat() const override
|
||||
{
|
||||
return BoolValue ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
bool getBool() const override
|
||||
{
|
||||
return BoolValue;
|
||||
}
|
||||
|
||||
void setInt(s32 intValue) override
|
||||
{
|
||||
BoolValue = (intValue != 0);
|
||||
}
|
||||
|
||||
void setFloat(f32 floatValue) override
|
||||
{
|
||||
BoolValue = (floatValue != 0);
|
||||
}
|
||||
|
||||
void setBool(bool boolValue) override
|
||||
{
|
||||
BoolValue = boolValue;
|
||||
}
|
||||
|
||||
E_ATTRIBUTE_TYPE getType() const override
|
||||
{
|
||||
return EAT_BOOL;
|
||||
}
|
||||
|
||||
bool BoolValue;
|
||||
};
|
||||
|
||||
// Attribute implemented for integers
|
||||
class CIntAttribute : public IAttribute
|
||||
{
|
||||
public:
|
||||
CIntAttribute(const char *name, s32 value)
|
||||
{
|
||||
Name = name;
|
||||
setInt(value);
|
||||
}
|
||||
|
||||
s32 getInt() const override
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
f32 getFloat() const override
|
||||
{
|
||||
return (f32)Value;
|
||||
}
|
||||
|
||||
void setInt(s32 intValue) override
|
||||
{
|
||||
Value = intValue;
|
||||
}
|
||||
|
||||
void setFloat(f32 floatValue) override
|
||||
{
|
||||
Value = (s32)floatValue;
|
||||
};
|
||||
|
||||
E_ATTRIBUTE_TYPE getType() const override
|
||||
{
|
||||
return EAT_INT;
|
||||
}
|
||||
|
||||
s32 Value;
|
||||
};
|
||||
|
||||
// Attribute implemented for floats
|
||||
class CFloatAttribute : public IAttribute
|
||||
{
|
||||
public:
|
||||
CFloatAttribute(const char *name, f32 value)
|
||||
{
|
||||
Name = name;
|
||||
setFloat(value);
|
||||
}
|
||||
|
||||
s32 getInt() const override
|
||||
{
|
||||
return (s32)Value;
|
||||
}
|
||||
|
||||
f32 getFloat() const override
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
void setInt(s32 intValue) override
|
||||
{
|
||||
Value = (f32)intValue;
|
||||
}
|
||||
|
||||
void setFloat(f32 floatValue) override
|
||||
{
|
||||
Value = floatValue;
|
||||
}
|
||||
|
||||
E_ATTRIBUTE_TYPE getType() const override
|
||||
{
|
||||
return EAT_FLOAT;
|
||||
}
|
||||
|
||||
f32 Value;
|
||||
};
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
249
src/CAttributes.cpp
Normal file
249
src/CAttributes.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAttributes.h"
|
||||
#include "CAttributeImpl.h"
|
||||
#include "ITexture.h"
|
||||
#include "IVideoDriver.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
CAttributes::CAttributes(video::IVideoDriver *driver) :
|
||||
Driver(driver)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CAttributes");
|
||||
#endif
|
||||
|
||||
if (Driver)
|
||||
Driver->grab();
|
||||
}
|
||||
|
||||
CAttributes::~CAttributes()
|
||||
{
|
||||
clear();
|
||||
|
||||
if (Driver)
|
||||
Driver->drop();
|
||||
}
|
||||
|
||||
//! Removes all attributes
|
||||
void CAttributes::clear()
|
||||
{
|
||||
for (u32 i = 0; i < Attributes.size(); ++i)
|
||||
Attributes[i]->drop();
|
||||
|
||||
Attributes.clear();
|
||||
}
|
||||
|
||||
//! Returns attribute index from name, -1 if not found
|
||||
s32 CAttributes::findAttribute(const c8 *attributeName) const
|
||||
{
|
||||
for (u32 i = 0; i < Attributes.size(); ++i)
|
||||
if (Attributes[i]->Name == attributeName)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
IAttribute *CAttributes::getAttributeP(const c8 *attributeName) const
|
||||
{
|
||||
for (u32 i = 0; i < Attributes.size(); ++i)
|
||||
if (Attributes[i]->Name == attributeName)
|
||||
return Attributes[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Sets a attribute as boolean value
|
||||
void CAttributes::setAttribute(const c8 *attributeName, bool value)
|
||||
{
|
||||
IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
att->setBool(value);
|
||||
else {
|
||||
Attributes.push_back(new CBoolAttribute(attributeName, value));
|
||||
}
|
||||
}
|
||||
|
||||
//! Gets a attribute as boolean value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \return Returns value of the attribute previously set by setAttribute() as bool
|
||||
//! or 0 if attribute is not set.
|
||||
bool CAttributes::getAttributeAsBool(const c8 *attributeName, bool defaultNotFound) const
|
||||
{
|
||||
const IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
return att->getBool();
|
||||
else
|
||||
return defaultNotFound;
|
||||
}
|
||||
|
||||
//! Sets a attribute as integer value
|
||||
void CAttributes::setAttribute(const c8 *attributeName, s32 value)
|
||||
{
|
||||
IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
att->setInt(value);
|
||||
else {
|
||||
Attributes.push_back(new CIntAttribute(attributeName, value));
|
||||
}
|
||||
}
|
||||
|
||||
//! Gets a attribute as integer value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \return Returns value of the attribute previously set by setAttribute() as integer
|
||||
//! or 0 if attribute is not set.
|
||||
s32 CAttributes::getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotFound) const
|
||||
{
|
||||
const IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
return att->getInt();
|
||||
else
|
||||
return defaultNotFound;
|
||||
}
|
||||
|
||||
//! Sets a attribute as float value
|
||||
void CAttributes::setAttribute(const c8 *attributeName, f32 value)
|
||||
{
|
||||
IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
att->setFloat(value);
|
||||
else
|
||||
Attributes.push_back(new CFloatAttribute(attributeName, value));
|
||||
}
|
||||
|
||||
//! Gets a attribute as integer value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \return Returns value of the attribute previously set by setAttribute() as float value
|
||||
//! or 0 if attribute is not set.
|
||||
f32 CAttributes::getAttributeAsFloat(const c8 *attributeName, irr::f32 defaultNotFound) const
|
||||
{
|
||||
const IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
return att->getFloat();
|
||||
|
||||
return defaultNotFound;
|
||||
}
|
||||
|
||||
//! Returns amount of string attributes set in this scene manager.
|
||||
u32 CAttributes::getAttributeCount() const
|
||||
{
|
||||
return Attributes.size();
|
||||
}
|
||||
|
||||
//! Returns string attribute name by index.
|
||||
//! \param index: Index value, must be between 0 and getStringAttributeCount()-1.
|
||||
const c8 *CAttributes::getAttributeName(s32 index) const
|
||||
{
|
||||
if ((u32)index >= Attributes.size())
|
||||
return 0;
|
||||
|
||||
return Attributes[index]->Name.c_str();
|
||||
}
|
||||
|
||||
//! Returns the type of an attribute
|
||||
E_ATTRIBUTE_TYPE CAttributes::getAttributeType(const c8 *attributeName) const
|
||||
{
|
||||
E_ATTRIBUTE_TYPE ret = EAT_UNKNOWN;
|
||||
|
||||
const IAttribute *att = getAttributeP(attributeName);
|
||||
if (att)
|
||||
ret = att->getType();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Returns attribute type by index.
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
E_ATTRIBUTE_TYPE CAttributes::getAttributeType(s32 index) const
|
||||
{
|
||||
if ((u32)index >= Attributes.size())
|
||||
return EAT_UNKNOWN;
|
||||
|
||||
return Attributes[index]->getType();
|
||||
}
|
||||
|
||||
//! Gets an attribute as integer value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
s32 CAttributes::getAttributeAsInt(s32 index) const
|
||||
{
|
||||
if ((u32)index < Attributes.size())
|
||||
return Attributes[index]->getInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Gets an attribute as float value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
f32 CAttributes::getAttributeAsFloat(s32 index) const
|
||||
{
|
||||
if ((u32)index < Attributes.size())
|
||||
return Attributes[index]->getFloat();
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
//! Gets an attribute as boolean value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
bool CAttributes::getAttributeAsBool(s32 index) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ((u32)index < Attributes.size())
|
||||
ret = Attributes[index]->getBool();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Adds an attribute as integer
|
||||
void CAttributes::addInt(const c8 *attributeName, s32 value)
|
||||
{
|
||||
Attributes.push_back(new CIntAttribute(attributeName, value));
|
||||
}
|
||||
|
||||
//! Adds an attribute as float
|
||||
void CAttributes::addFloat(const c8 *attributeName, f32 value)
|
||||
{
|
||||
Attributes.push_back(new CFloatAttribute(attributeName, value));
|
||||
}
|
||||
|
||||
//! Adds an attribute as bool
|
||||
void CAttributes::addBool(const c8 *attributeName, bool value)
|
||||
{
|
||||
Attributes.push_back(new CBoolAttribute(attributeName, value));
|
||||
}
|
||||
|
||||
//! Returns if an attribute with a name exists
|
||||
bool CAttributes::existsAttribute(const c8 *attributeName) const
|
||||
{
|
||||
return getAttributeP(attributeName) != 0;
|
||||
}
|
||||
|
||||
//! Sets an attribute as boolean value
|
||||
void CAttributes::setAttribute(s32 index, bool value)
|
||||
{
|
||||
if ((u32)index < Attributes.size())
|
||||
Attributes[index]->setBool(value);
|
||||
}
|
||||
|
||||
//! Sets an attribute as integer value
|
||||
void CAttributes::setAttribute(s32 index, s32 value)
|
||||
{
|
||||
if ((u32)index < Attributes.size())
|
||||
Attributes[index]->setInt(value);
|
||||
}
|
||||
|
||||
//! Sets a attribute as float value
|
||||
void CAttributes::setAttribute(s32 index, f32 value)
|
||||
{
|
||||
if ((u32)index < Attributes.size())
|
||||
Attributes[index]->setFloat(value);
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
133
src/CAttributes.h
Normal file
133
src/CAttributes.h
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IAttributes.h"
|
||||
#include "IAttribute.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
class ITexture;
|
||||
class IVideoDriver;
|
||||
}
|
||||
namespace io
|
||||
{
|
||||
|
||||
//! Implementation of the IAttributes interface
|
||||
class CAttributes : public IAttributes
|
||||
{
|
||||
public:
|
||||
CAttributes(video::IVideoDriver *driver = 0);
|
||||
~CAttributes();
|
||||
|
||||
//! Returns amount of attributes in this collection of attributes.
|
||||
u32 getAttributeCount() const override;
|
||||
|
||||
//! Returns attribute name by index.
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
const c8 *getAttributeName(s32 index) const override;
|
||||
|
||||
//! Returns the type of an attribute
|
||||
//! \param attributeName: Name for the attribute
|
||||
E_ATTRIBUTE_TYPE getAttributeType(const c8 *attributeName) const override;
|
||||
|
||||
//! Returns attribute type by index.
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
E_ATTRIBUTE_TYPE getAttributeType(s32 index) const override;
|
||||
|
||||
//! Returns if an attribute with a name exists
|
||||
bool existsAttribute(const c8 *attributeName) const override;
|
||||
|
||||
//! Returns attribute index from name, -1 if not found
|
||||
s32 findAttribute(const c8 *attributeName) const override;
|
||||
|
||||
//! Removes all attributes
|
||||
void clear() override;
|
||||
|
||||
/*
|
||||
|
||||
Integer Attribute
|
||||
|
||||
*/
|
||||
|
||||
//! Adds an attribute as integer
|
||||
void addInt(const c8 *attributeName, s32 value) override;
|
||||
|
||||
//! Sets an attribute as integer value
|
||||
void setAttribute(const c8 *attributeName, s32 value) override;
|
||||
|
||||
//! Gets an attribute as integer value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \param defaultNotFound Value returned when attributeName was not found
|
||||
//! \return Returns value of the attribute previously set by setAttribute()
|
||||
s32 getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotFound = 0) const override;
|
||||
|
||||
//! Gets an attribute as integer value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
s32 getAttributeAsInt(s32 index) const override;
|
||||
|
||||
//! Sets an attribute as integer value
|
||||
void setAttribute(s32 index, s32 value) override;
|
||||
|
||||
/*
|
||||
|
||||
Float Attribute
|
||||
|
||||
*/
|
||||
|
||||
//! Adds an attribute as float
|
||||
void addFloat(const c8 *attributeName, f32 value) override;
|
||||
|
||||
//! Sets a attribute as float value
|
||||
void setAttribute(const c8 *attributeName, f32 value) override;
|
||||
|
||||
//! Gets an attribute as float value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \param defaultNotFound Value returned when attributeName was not found
|
||||
//! \return Returns value of the attribute previously set by setAttribute()
|
||||
f32 getAttributeAsFloat(const c8 *attributeName, irr::f32 defaultNotFound = 0.f) const override;
|
||||
|
||||
//! Gets an attribute as float value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
f32 getAttributeAsFloat(s32 index) const override;
|
||||
|
||||
//! Sets an attribute as float value
|
||||
void setAttribute(s32 index, f32 value) override;
|
||||
|
||||
/*
|
||||
Bool Attribute
|
||||
*/
|
||||
|
||||
//! Adds an attribute as bool
|
||||
void addBool(const c8 *attributeName, bool value) override;
|
||||
|
||||
//! Sets an attribute as boolean value
|
||||
void setAttribute(const c8 *attributeName, bool value) override;
|
||||
|
||||
//! Gets an attribute as boolean value
|
||||
//! \param attributeName: Name of the attribute to get.
|
||||
//! \param defaultNotFound Value returned when attributeName was not found
|
||||
//! \return Returns value of the attribute previously set by setAttribute()
|
||||
bool getAttributeAsBool(const c8 *attributeName, bool defaultNotFound = false) const override;
|
||||
|
||||
//! Gets an attribute as boolean value
|
||||
//! \param index: Index value, must be between 0 and getAttributeCount()-1.
|
||||
bool getAttributeAsBool(s32 index) const override;
|
||||
|
||||
//! Sets an attribute as boolean value
|
||||
void setAttribute(s32 index, bool value) override;
|
||||
|
||||
protected:
|
||||
core::array<IAttribute *> Attributes;
|
||||
|
||||
IAttribute *getAttributeP(const c8 *attributeName) const;
|
||||
|
||||
video::IVideoDriver *Driver;
|
||||
};
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
949
src/CB3DMeshFileLoader.cpp
Normal file
949
src/CB3DMeshFileLoader.cpp
Normal file
@@ -0,0 +1,949 @@
|
||||
// Copyright (C) 2006-2012 Luke Hoschke
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
// B3D Mesh loader
|
||||
// File format designed by Mark Sibly for the Blitz3D engine and has been
|
||||
// declared public domain
|
||||
|
||||
#include "CB3DMeshFileLoader.h"
|
||||
|
||||
#include "IVideoDriver.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "os.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define _B3D_READER_DEBUG
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! Constructor
|
||||
CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager *smgr) :
|
||||
AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false),
|
||||
HasVertexColors(false), ShowWarning(true)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CB3DMeshFileLoader");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".bsp")
|
||||
bool CB3DMeshFileLoader::isALoadableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "b3d");
|
||||
}
|
||||
|
||||
//! creates/loads an animated mesh from the file.
|
||||
//! \return Pointer to the created mesh. Returns 0 if loading failed.
|
||||
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
|
||||
//! See IReferenceCounted::drop() for more information.
|
||||
IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
B3DFile = file;
|
||||
AnimatedMesh = new scene::CSkinnedMesh();
|
||||
ShowWarning = true; // If true a warning is issued if too many textures are used
|
||||
VerticesStart = 0;
|
||||
|
||||
if (load()) {
|
||||
AnimatedMesh->finalize();
|
||||
} else {
|
||||
AnimatedMesh->drop();
|
||||
AnimatedMesh = 0;
|
||||
}
|
||||
|
||||
return AnimatedMesh;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::load()
|
||||
{
|
||||
B3dStack.clear();
|
||||
|
||||
NormalsInFile = false;
|
||||
HasVertexColors = false;
|
||||
|
||||
//------ Get header ------
|
||||
|
||||
SB3dChunkHeader header;
|
||||
B3DFile->read(&header, sizeof(header));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.size = os::Byteswap::byteswap(header.size);
|
||||
#endif
|
||||
|
||||
if (strncmp(header.name, "BB3D", 4) != 0) {
|
||||
os::Printer::log("File is not a b3d file. Loading failed (No header found)", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add main chunk...
|
||||
B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
|
||||
|
||||
// Get file version, but ignore it, as it's not important with b3d files...
|
||||
s32 fileVersion;
|
||||
B3DFile->read(&fileVersion, sizeof(fileVersion));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
fileVersion = os::Byteswap::byteswap(fileVersion);
|
||||
#endif
|
||||
|
||||
//------ Read main chunk ------
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) {
|
||||
B3DFile->read(&header, sizeof(header));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.size = os::Byteswap::byteswap(header.size);
|
||||
#endif
|
||||
B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
|
||||
|
||||
if (strncmp(B3dStack.getLast().name, "TEXS", 4) == 0) {
|
||||
if (!readChunkTEXS())
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "BRUS", 4) == 0) {
|
||||
if (!readChunkBRUS())
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) {
|
||||
if (!readChunkNODE((CSkinnedMesh::SJoint *)0))
|
||||
return false;
|
||||
} else {
|
||||
os::Printer::log("Unknown chunk found in mesh base - skipping");
|
||||
if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
|
||||
return false;
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
B3dStack.clear();
|
||||
|
||||
BaseVertices.clear();
|
||||
AnimatedVertices_VertexID.clear();
|
||||
AnimatedVertices_BufferID.clear();
|
||||
|
||||
Materials.clear();
|
||||
Textures.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);
|
||||
joint->Name = readString();
|
||||
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkNODE";
|
||||
os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
f32 position[3], scale[3], rotation[4];
|
||||
|
||||
readFloats(position, 3);
|
||||
readFloats(scale, 3);
|
||||
readFloats(rotation, 4);
|
||||
|
||||
joint->Animatedposition = core::vector3df(position[0], position[1], position[2]);
|
||||
joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]);
|
||||
joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]);
|
||||
|
||||
// Build LocalMatrix:
|
||||
|
||||
core::matrix4 positionMatrix;
|
||||
positionMatrix.setTranslation(joint->Animatedposition);
|
||||
core::matrix4 scaleMatrix;
|
||||
scaleMatrix.setScale(joint->Animatedscale);
|
||||
core::matrix4 rotationMatrix;
|
||||
joint->Animatedrotation.getMatrix_transposed(rotationMatrix);
|
||||
|
||||
joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;
|
||||
|
||||
if (inJoint)
|
||||
joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix;
|
||||
else
|
||||
joint->GlobalMatrix = joint->LocalMatrix;
|
||||
|
||||
while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
SB3dChunkHeader header;
|
||||
B3DFile->read(&header, sizeof(header));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.size = os::Byteswap::byteswap(header.size);
|
||||
#endif
|
||||
|
||||
B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
|
||||
|
||||
if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) {
|
||||
if (!readChunkNODE(joint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "MESH", 4) == 0) {
|
||||
VerticesStart = BaseVertices.size();
|
||||
if (!readChunkMESH(joint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "BONE", 4) == 0) {
|
||||
if (!readChunkBONE(joint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "KEYS", 4) == 0) {
|
||||
if (!readChunkKEYS(joint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "ANIM", 4) == 0) {
|
||||
if (!readChunkANIM())
|
||||
return false;
|
||||
} else {
|
||||
os::Printer::log("Unknown chunk found in node chunk - skipping");
|
||||
if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
|
||||
return false;
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkMESH";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
s32 brushID;
|
||||
B3DFile->read(&brushID, sizeof(brushID));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
brushID = os::Byteswap::byteswap(brushID);
|
||||
#endif
|
||||
|
||||
NormalsInFile = false;
|
||||
HasVertexColors = false;
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
SB3dChunkHeader header;
|
||||
B3DFile->read(&header, sizeof(header));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.size = os::Byteswap::byteswap(header.size);
|
||||
#endif
|
||||
|
||||
B3dStack.push_back(SB3dChunk(header, B3DFile->getPos() - 8));
|
||||
|
||||
if (strncmp(B3dStack.getLast().name, "VRTS", 4) == 0) {
|
||||
if (!readChunkVRTS(inJoint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "TRIS", 4) == 0) {
|
||||
scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh->addMeshBuffer();
|
||||
|
||||
if (brushID == -1) { /* ok */
|
||||
} else if (brushID < 0 || (u32)brushID >= Materials.size()) {
|
||||
os::Printer::log("Illegal brush ID found", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
meshBuffer->Material = Materials[brushID].Material;
|
||||
}
|
||||
|
||||
if (readChunkTRIS(meshBuffer, AnimatedMesh->getMeshBuffers().size() - 1, VerticesStart) == false)
|
||||
return false;
|
||||
|
||||
if (!NormalsInFile) {
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < (s32)meshBuffer->Indices.size(); i += 3) {
|
||||
core::plane3df p(meshBuffer->getVertex(meshBuffer->Indices[i + 0])->Pos,
|
||||
meshBuffer->getVertex(meshBuffer->Indices[i + 1])->Pos,
|
||||
meshBuffer->getVertex(meshBuffer->Indices[i + 2])->Pos);
|
||||
|
||||
meshBuffer->getVertex(meshBuffer->Indices[i + 0])->Normal += p.Normal;
|
||||
meshBuffer->getVertex(meshBuffer->Indices[i + 1])->Normal += p.Normal;
|
||||
meshBuffer->getVertex(meshBuffer->Indices[i + 2])->Normal += p.Normal;
|
||||
}
|
||||
|
||||
for (i = 0; i < (s32)meshBuffer->getVertexCount(); ++i) {
|
||||
meshBuffer->getVertex(i)->Normal.normalize();
|
||||
BaseVertices[VerticesStart + i].Normal = meshBuffer->getVertex(i)->Normal;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
os::Printer::log("Unknown chunk found in mesh - skipping");
|
||||
if (!B3DFile->seek(B3dStack.getLast().startposition + B3dStack.getLast().length))
|
||||
return false;
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
VRTS:
|
||||
int flags ;1=normal values present, 2=rgba values present
|
||||
int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8
|
||||
but we only support 3
|
||||
int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4
|
||||
{
|
||||
float x,y,z ;always present
|
||||
float nx,ny,nz ;vertex normal: present if (flags&1)
|
||||
float red,green,blue,alpha ;vertex color: present if (flags&2)
|
||||
float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
|
||||
}
|
||||
*/
|
||||
bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "ChunkVRTS";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
const s32 max_tex_coords = 3;
|
||||
s32 flags, tex_coord_sets, tex_coord_set_size;
|
||||
|
||||
B3DFile->read(&flags, sizeof(flags));
|
||||
B3DFile->read(&tex_coord_sets, sizeof(tex_coord_sets));
|
||||
B3DFile->read(&tex_coord_set_size, sizeof(tex_coord_set_size));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
flags = os::Byteswap::byteswap(flags);
|
||||
tex_coord_sets = os::Byteswap::byteswap(tex_coord_sets);
|
||||
tex_coord_set_size = os::Byteswap::byteswap(tex_coord_set_size);
|
||||
#endif
|
||||
|
||||
if (tex_coord_sets < 0 || tex_coord_set_size < 0 ||
|
||||
tex_coord_sets >= max_tex_coords || tex_coord_set_size >= 4) // Something is wrong
|
||||
{
|
||||
os::Printer::log("tex_coord_sets or tex_coord_set_size too big", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
//------ Allocate Memory, for speed -----------//
|
||||
|
||||
s32 numberOfReads = 3;
|
||||
|
||||
if (flags & 1) {
|
||||
NormalsInFile = true;
|
||||
numberOfReads += 3;
|
||||
}
|
||||
if (flags & 2) {
|
||||
numberOfReads += 4;
|
||||
HasVertexColors = true;
|
||||
}
|
||||
|
||||
numberOfReads += tex_coord_sets * tex_coord_set_size;
|
||||
|
||||
const s32 memoryNeeded = (B3dStack.getLast().length / sizeof(f32)) / numberOfReads;
|
||||
|
||||
BaseVertices.reallocate(memoryNeeded + BaseVertices.size() + 1);
|
||||
AnimatedVertices_VertexID.reallocate(memoryNeeded + AnimatedVertices_VertexID.size() + 1);
|
||||
|
||||
//--------------------------------------------//
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
f32 position[3];
|
||||
f32 normal[3] = {0.f, 0.f, 0.f};
|
||||
f32 color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
f32 tex_coords[max_tex_coords][4];
|
||||
|
||||
readFloats(position, 3);
|
||||
|
||||
if (flags & 1)
|
||||
readFloats(normal, 3);
|
||||
if (flags & 2)
|
||||
readFloats(color, 4);
|
||||
|
||||
for (s32 i = 0; i < tex_coord_sets; ++i)
|
||||
readFloats(tex_coords[i], tex_coord_set_size);
|
||||
|
||||
f32 tu = 0.0f, tv = 0.0f;
|
||||
if (tex_coord_sets >= 1 && tex_coord_set_size >= 2) {
|
||||
tu = tex_coords[0][0];
|
||||
tv = tex_coords[0][1];
|
||||
}
|
||||
|
||||
f32 tu2 = 0.0f, tv2 = 0.0f;
|
||||
if (tex_coord_sets > 1 && tex_coord_set_size > 1) {
|
||||
tu2 = tex_coords[1][0];
|
||||
tv2 = tex_coords[1][1];
|
||||
}
|
||||
|
||||
// Create Vertex...
|
||||
video::S3DVertex2TCoords Vertex(position[0], position[1], position[2],
|
||||
normal[0], normal[1], normal[2],
|
||||
video::SColorf(color[0], color[1], color[2], color[3]).toSColor(),
|
||||
tu, tv, tu2, tv2);
|
||||
|
||||
// Transform the Vertex position by nested node...
|
||||
inJoint->GlobalMatrix.transformVect(Vertex.Pos);
|
||||
inJoint->GlobalMatrix.rotateVect(Vertex.Normal);
|
||||
|
||||
// Add it...
|
||||
BaseVertices.push_back(Vertex);
|
||||
|
||||
AnimatedVertices_VertexID.push_back(-1);
|
||||
AnimatedVertices_BufferID.push_back(-1);
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 meshBufferID, s32 vertices_Start)
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "ChunkTRIS";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
bool showVertexWarning = false;
|
||||
|
||||
s32 triangle_brush_id; // Note: Irrlicht can't have different brushes for each triangle (using a workaround)
|
||||
B3DFile->read(&triangle_brush_id, sizeof(triangle_brush_id));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
triangle_brush_id = os::Byteswap::byteswap(triangle_brush_id);
|
||||
#endif
|
||||
|
||||
SB3dMaterial *B3dMaterial;
|
||||
|
||||
if (triangle_brush_id == -1)
|
||||
B3dMaterial = 0;
|
||||
else if (triangle_brush_id < 0 || (u32)triangle_brush_id >= Materials.size()) {
|
||||
os::Printer::log("Illegal material index found", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
B3dMaterial = &Materials[triangle_brush_id];
|
||||
meshBuffer->Material = B3dMaterial->Material;
|
||||
}
|
||||
|
||||
const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32);
|
||||
meshBuffer->Indices.reallocate(memoryNeeded + meshBuffer->Indices.size() + 1);
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
s32 vertex_id[3];
|
||||
|
||||
B3DFile->read(vertex_id, 3 * sizeof(s32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
vertex_id[0] = os::Byteswap::byteswap(vertex_id[0]);
|
||||
vertex_id[1] = os::Byteswap::byteswap(vertex_id[1]);
|
||||
vertex_id[2] = os::Byteswap::byteswap(vertex_id[2]);
|
||||
#endif
|
||||
|
||||
// Make Ids global:
|
||||
vertex_id[0] += vertices_Start;
|
||||
vertex_id[1] += vertices_Start;
|
||||
vertex_id[2] += vertices_Start;
|
||||
|
||||
for (s32 i = 0; i < 3; ++i) {
|
||||
if ((u32)vertex_id[i] >= AnimatedVertices_VertexID.size()) {
|
||||
os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AnimatedVertices_VertexID[vertex_id[i]] != -1) {
|
||||
if (AnimatedVertices_BufferID[vertex_id[i]] != (s32)meshBufferID) { // If this vertex is linked in a different meshbuffer
|
||||
AnimatedVertices_VertexID[vertex_id[i]] = -1;
|
||||
AnimatedVertices_BufferID[vertex_id[i]] = -1;
|
||||
showVertexWarning = true;
|
||||
}
|
||||
}
|
||||
if (AnimatedVertices_VertexID[vertex_id[i]] == -1) { // If this vertex is not in the meshbuffer
|
||||
// Check for lightmapping:
|
||||
if (BaseVertices[vertex_id[i]].TCoords2 != core::vector2df(0.f, 0.f))
|
||||
meshBuffer->convertTo2TCoords(); // Will only affect the meshbuffer the first time this is called
|
||||
|
||||
// Add the vertex to the meshbuffer:
|
||||
if (meshBuffer->VertexType == video::EVT_STANDARD)
|
||||
meshBuffer->Vertices_Standard.push_back(BaseVertices[vertex_id[i]]);
|
||||
else
|
||||
meshBuffer->Vertices_2TCoords.push_back(BaseVertices[vertex_id[i]]);
|
||||
|
||||
// create vertex id to meshbuffer index link:
|
||||
AnimatedVertices_VertexID[vertex_id[i]] = meshBuffer->getVertexCount() - 1;
|
||||
AnimatedVertices_BufferID[vertex_id[i]] = meshBufferID;
|
||||
|
||||
if (B3dMaterial) {
|
||||
// Apply Material/Color/etc...
|
||||
video::S3DVertex *Vertex = meshBuffer->getVertex(meshBuffer->getVertexCount() - 1);
|
||||
|
||||
if (!HasVertexColors)
|
||||
Vertex->Color = B3dMaterial->Material.DiffuseColor;
|
||||
else if (Vertex->Color.getAlpha() == 255)
|
||||
Vertex->Color.setAlpha((s32)(B3dMaterial->alpha * 255.0f));
|
||||
|
||||
// Use texture's scale
|
||||
if (B3dMaterial->Textures[0]) {
|
||||
Vertex->TCoords.X *= B3dMaterial->Textures[0]->Xscale;
|
||||
Vertex->TCoords.Y *= B3dMaterial->Textures[0]->Yscale;
|
||||
}
|
||||
/*
|
||||
if (B3dMaterial->Textures[1])
|
||||
{
|
||||
Vertex->TCoords2.X *=B3dMaterial->Textures[1]->Xscale;
|
||||
Vertex->TCoords2.Y *=B3dMaterial->Textures[1]->Yscale;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[0]]);
|
||||
meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[1]]);
|
||||
meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[2]]);
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
if (showVertexWarning)
|
||||
os::Printer::log("B3dMeshLoader: Warning, different meshbuffers linking to the same vertex, this will cause problems with animated meshes");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkBONE";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
if (B3dStack.getLast().length > 8) {
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
u32 globalVertexID;
|
||||
f32 strength;
|
||||
B3DFile->read(&globalVertexID, sizeof(globalVertexID));
|
||||
B3DFile->read(&strength, sizeof(strength));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
globalVertexID = os::Byteswap::byteswap(globalVertexID);
|
||||
strength = os::Byteswap::byteswap(strength);
|
||||
#endif
|
||||
globalVertexID += VerticesStart;
|
||||
|
||||
if (globalVertexID >= AnimatedVertices_VertexID.size()) {
|
||||
os::Printer::log("Illegal vertex index found", B3DFile->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AnimatedVertices_VertexID[globalVertexID] == -1) {
|
||||
os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
|
||||
} else if (strength > 0) {
|
||||
CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint);
|
||||
weight->strength = strength;
|
||||
// Find the meshbuffer and Vertex index from the Global Vertex ID:
|
||||
weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
|
||||
weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
// Only print first, that's just too much output otherwise
|
||||
if (!inJoint || (inJoint->PositionKeys.empty() && inJoint->ScaleKeys.empty() && inJoint->RotationKeys.empty())) {
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkKEYS";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
}
|
||||
#endif
|
||||
|
||||
s32 flags;
|
||||
B3DFile->read(&flags, sizeof(flags));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
flags = os::Byteswap::byteswap(flags);
|
||||
#endif
|
||||
|
||||
CSkinnedMesh::SPositionKey *oldPosKey = 0;
|
||||
core::vector3df oldPos[2];
|
||||
CSkinnedMesh::SScaleKey *oldScaleKey = 0;
|
||||
core::vector3df oldScale[2];
|
||||
CSkinnedMesh::SRotationKey *oldRotKey = 0;
|
||||
core::quaternion oldRot[2];
|
||||
bool isFirst[3] = {true, true, true};
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
s32 frame;
|
||||
|
||||
B3DFile->read(&frame, sizeof(frame));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
frame = os::Byteswap::byteswap(frame);
|
||||
#endif
|
||||
|
||||
// Add key frames, frames in Irrlicht are zero-based
|
||||
f32 data[4];
|
||||
if (flags & 1) {
|
||||
readFloats(data, 3);
|
||||
if ((oldPosKey != 0) && (oldPos[0] == oldPos[1])) {
|
||||
const core::vector3df pos(data[0], data[1], data[2]);
|
||||
if (oldPos[1] == pos)
|
||||
oldPosKey->frame = (f32)frame - 1;
|
||||
else {
|
||||
oldPos[0] = oldPos[1];
|
||||
oldPosKey = AnimatedMesh->addPositionKey(inJoint);
|
||||
oldPosKey->frame = (f32)frame - 1;
|
||||
oldPos[1].set(oldPosKey->position.set(pos));
|
||||
}
|
||||
} else if (oldPosKey == 0 && isFirst[0]) {
|
||||
oldPosKey = AnimatedMesh->addPositionKey(inJoint);
|
||||
oldPosKey->frame = (f32)frame - 1;
|
||||
oldPos[0].set(oldPosKey->position.set(data[0], data[1], data[2]));
|
||||
oldPosKey = 0;
|
||||
isFirst[0] = false;
|
||||
} else {
|
||||
if (oldPosKey != 0)
|
||||
oldPos[0] = oldPos[1];
|
||||
oldPosKey = AnimatedMesh->addPositionKey(inJoint);
|
||||
oldPosKey->frame = (f32)frame - 1;
|
||||
oldPos[1].set(oldPosKey->position.set(data[0], data[1], data[2]));
|
||||
}
|
||||
}
|
||||
if (flags & 2) {
|
||||
readFloats(data, 3);
|
||||
if ((oldScaleKey != 0) && (oldScale[0] == oldScale[1])) {
|
||||
const core::vector3df scale(data[0], data[1], data[2]);
|
||||
if (oldScale[1] == scale)
|
||||
oldScaleKey->frame = (f32)frame - 1;
|
||||
else {
|
||||
oldScale[0] = oldScale[1];
|
||||
oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
|
||||
oldScaleKey->frame = (f32)frame - 1;
|
||||
oldScale[1].set(oldScaleKey->scale.set(scale));
|
||||
}
|
||||
} else if (oldScaleKey == 0 && isFirst[1]) {
|
||||
oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
|
||||
oldScaleKey->frame = (f32)frame - 1;
|
||||
oldScale[0].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
|
||||
oldScaleKey = 0;
|
||||
isFirst[1] = false;
|
||||
} else {
|
||||
if (oldScaleKey != 0)
|
||||
oldScale[0] = oldScale[1];
|
||||
oldScaleKey = AnimatedMesh->addScaleKey(inJoint);
|
||||
oldScaleKey->frame = (f32)frame - 1;
|
||||
oldScale[1].set(oldScaleKey->scale.set(data[0], data[1], data[2]));
|
||||
}
|
||||
}
|
||||
if (flags & 4) {
|
||||
readFloats(data, 4);
|
||||
if ((oldRotKey != 0) && (oldRot[0] == oldRot[1])) {
|
||||
// meant to be in this order since b3d stores W first
|
||||
const core::quaternion rot(data[1], data[2], data[3], data[0]);
|
||||
if (oldRot[1] == rot)
|
||||
oldRotKey->frame = (f32)frame - 1;
|
||||
else {
|
||||
oldRot[0] = oldRot[1];
|
||||
oldRotKey = AnimatedMesh->addRotationKey(inJoint);
|
||||
oldRotKey->frame = (f32)frame - 1;
|
||||
oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
|
||||
oldRot[1].normalize();
|
||||
}
|
||||
} else if (oldRotKey == 0 && isFirst[2]) {
|
||||
oldRotKey = AnimatedMesh->addRotationKey(inJoint);
|
||||
oldRotKey->frame = (f32)frame - 1;
|
||||
// meant to be in this order since b3d stores W first
|
||||
oldRot[0].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
|
||||
oldRot[0].normalize();
|
||||
oldRotKey = 0;
|
||||
isFirst[2] = false;
|
||||
} else {
|
||||
if (oldRotKey != 0)
|
||||
oldRot[0] = oldRot[1];
|
||||
oldRotKey = AnimatedMesh->addRotationKey(inJoint);
|
||||
oldRotKey->frame = (f32)frame - 1;
|
||||
// meant to be in this order since b3d stores W first
|
||||
oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0]));
|
||||
oldRot[1].normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkANIM()
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkANIM";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
s32 animFlags; // not stored\used
|
||||
s32 animFrames; // not stored\used
|
||||
f32 animFPS; // not stored\used
|
||||
|
||||
B3DFile->read(&animFlags, sizeof(s32));
|
||||
B3DFile->read(&animFrames, sizeof(s32));
|
||||
readFloats(&animFPS, 1);
|
||||
if (animFPS > 0.f)
|
||||
AnimatedMesh->setAnimationSpeed(animFPS);
|
||||
os::Printer::log("FPS", io::path((double)animFPS), ELL_DEBUG);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
animFlags = os::Byteswap::byteswap(animFlags);
|
||||
animFrames = os::Byteswap::byteswap(animFrames);
|
||||
#endif
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkTEXS()
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkTEXS";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
Textures.push_back(SB3dTexture());
|
||||
SB3dTexture &B3dTexture = Textures.getLast();
|
||||
|
||||
B3dTexture.TextureName = readString();
|
||||
std::replace(B3dTexture.TextureName.begin(), B3dTexture.TextureName.end(), '\\', '/');
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
os::Printer::log("read Texture", B3dTexture.TextureName.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
B3DFile->read(&B3dTexture.Flags, sizeof(s32));
|
||||
B3DFile->read(&B3dTexture.Blend, sizeof(s32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
B3dTexture.Flags = os::Byteswap::byteswap(B3dTexture.Flags);
|
||||
B3dTexture.Blend = os::Byteswap::byteswap(B3dTexture.Blend);
|
||||
#endif
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
os::Printer::log("Flags", core::stringc(B3dTexture.Flags).c_str(), ELL_DEBUG);
|
||||
os::Printer::log("Blend", core::stringc(B3dTexture.Blend).c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
readFloats(&B3dTexture.Xpos, 1);
|
||||
readFloats(&B3dTexture.Ypos, 1);
|
||||
readFloats(&B3dTexture.Xscale, 1);
|
||||
readFloats(&B3dTexture.Yscale, 1);
|
||||
readFloats(&B3dTexture.Angle, 1);
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkBRUS()
|
||||
{
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
core::stringc logStr;
|
||||
for (u32 i = 1; i < B3dStack.size(); ++i)
|
||||
logStr += "-";
|
||||
logStr += "read ChunkBRUS";
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
u32 n_texs;
|
||||
B3DFile->read(&n_texs, sizeof(u32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
n_texs = os::Byteswap::byteswap(n_texs);
|
||||
#endif
|
||||
|
||||
// number of texture ids read for Irrlicht
|
||||
const u32 num_textures = core::min_(n_texs, video::MATERIAL_MAX_TEXTURES);
|
||||
// number of bytes to skip (for ignored texture ids)
|
||||
const u32 n_texs_offset = (num_textures < n_texs) ? (n_texs - num_textures) : 0;
|
||||
|
||||
while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
// This is what blitz basic calls a brush, like a Irrlicht Material
|
||||
|
||||
auto name = readString();
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
os::Printer::log("read Material", name.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
Materials.push_back(SB3dMaterial());
|
||||
SB3dMaterial &B3dMaterial = Materials.getLast();
|
||||
|
||||
readFloats(&B3dMaterial.red, 1);
|
||||
readFloats(&B3dMaterial.green, 1);
|
||||
readFloats(&B3dMaterial.blue, 1);
|
||||
readFloats(&B3dMaterial.alpha, 1);
|
||||
readFloats(&B3dMaterial.shininess, 1);
|
||||
|
||||
B3DFile->read(&B3dMaterial.blend, sizeof(B3dMaterial.blend));
|
||||
B3DFile->read(&B3dMaterial.fx, sizeof(B3dMaterial.fx));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
B3dMaterial.blend = os::Byteswap::byteswap(B3dMaterial.blend);
|
||||
B3dMaterial.fx = os::Byteswap::byteswap(B3dMaterial.fx);
|
||||
#endif
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
os::Printer::log("Blend", core::stringc(B3dMaterial.blend).c_str(), ELL_DEBUG);
|
||||
os::Printer::log("FX", core::stringc(B3dMaterial.fx).c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
u32 i;
|
||||
for (i = 0; i < num_textures; ++i) {
|
||||
s32 texture_id = -1;
|
||||
B3DFile->read(&texture_id, sizeof(s32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
texture_id = os::Byteswap::byteswap(texture_id);
|
||||
#endif
|
||||
//--- Get pointers to the texture, based on the IDs ---
|
||||
if ((u32)texture_id < Textures.size()) {
|
||||
B3dMaterial.Textures[i] = &Textures[texture_id];
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
os::Printer::log("Layer", core::stringc(i).c_str(), ELL_DEBUG);
|
||||
os::Printer::log("using texture", Textures[texture_id].TextureName.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
} else
|
||||
B3dMaterial.Textures[i] = 0;
|
||||
}
|
||||
// skip other texture ids
|
||||
for (i = 0; i < n_texs_offset; ++i) {
|
||||
s32 texture_id = -1;
|
||||
B3DFile->read(&texture_id, sizeof(s32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
texture_id = os::Byteswap::byteswap(texture_id);
|
||||
#endif
|
||||
if (ShowWarning && (texture_id != -1) && (n_texs > video::MATERIAL_MAX_TEXTURES)) {
|
||||
os::Printer::log("Too many textures used in one material", B3DFile->getFileName(), ELL_WARNING);
|
||||
ShowWarning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixes problems when the lightmap is on the first texture:
|
||||
if (B3dMaterial.Textures[0] != 0) {
|
||||
if (B3dMaterial.Textures[0]->Flags & 65536) { // 65536 = secondary UV
|
||||
SB3dTexture *TmpTexture;
|
||||
TmpTexture = B3dMaterial.Textures[1];
|
||||
B3dMaterial.Textures[1] = B3dMaterial.Textures[0];
|
||||
B3dMaterial.Textures[0] = TmpTexture;
|
||||
}
|
||||
}
|
||||
|
||||
// If a preceeding texture slot is empty move the others down:
|
||||
for (i = num_textures; i > 0; --i) {
|
||||
for (u32 j = i - 1; j < num_textures - 1; ++j) {
|
||||
if (B3dMaterial.Textures[j + 1] != 0 && B3dMaterial.Textures[j] == 0) {
|
||||
B3dMaterial.Textures[j] = B3dMaterial.Textures[j + 1];
|
||||
B3dMaterial.Textures[j + 1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------ Convert blitz flags/blend to irrlicht -------
|
||||
|
||||
// Two textures:
|
||||
if (B3dMaterial.Textures[1]) {
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
||||
B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
|
||||
} else if (B3dMaterial.Textures[0]) { // One texture:
|
||||
// Flags & 0x1 is usual SOLID, 0x8 is mipmap (handled before)
|
||||
if (B3dMaterial.Textures[0]->Flags & 0x2) { // (Alpha mapped)
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
|
||||
} else if (B3dMaterial.Textures[0]->Flags & 0x4) //(Masked)
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // TODO: create color key texture
|
||||
else if (B3dMaterial.alpha == 1.f)
|
||||
B3dMaterial.Material.MaterialType = video::EMT_SOLID;
|
||||
else {
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
||||
B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
|
||||
}
|
||||
} else // No texture:
|
||||
{
|
||||
if (B3dMaterial.alpha == 1.f)
|
||||
B3dMaterial.Material.MaterialType = video::EMT_SOLID;
|
||||
else {
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
||||
B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
B3dMaterial.Material.DiffuseColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor();
|
||||
B3dMaterial.Material.ColorMaterial = video::ECM_NONE;
|
||||
|
||||
//------ Material fx ------
|
||||
|
||||
if (B3dMaterial.fx & 1) { // full-bright
|
||||
B3dMaterial.Material.AmbientColor = video::SColor(255, 255, 255, 255);
|
||||
B3dMaterial.Material.Lighting = false;
|
||||
} else
|
||||
B3dMaterial.Material.AmbientColor = B3dMaterial.Material.DiffuseColor;
|
||||
|
||||
if (B3dMaterial.fx & 2) // use vertex colors instead of brush color
|
||||
B3dMaterial.Material.ColorMaterial = video::ECM_DIFFUSE_AND_AMBIENT;
|
||||
|
||||
if (B3dMaterial.fx & 4) // flatshaded
|
||||
B3dMaterial.Material.GouraudShading = false;
|
||||
|
||||
if (B3dMaterial.fx & 16) // disable backface culling
|
||||
B3dMaterial.Material.BackfaceCulling = false;
|
||||
|
||||
if (B3dMaterial.fx & 32) { // force vertex alpha-blending
|
||||
B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
||||
B3dMaterial.Material.ZWriteEnable = video::EZW_OFF;
|
||||
}
|
||||
|
||||
B3dMaterial.Material.Shininess = B3dMaterial.shininess;
|
||||
}
|
||||
|
||||
B3dStack.erase(B3dStack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CB3DMeshFileLoader::readString()
|
||||
{
|
||||
std::string newstring = "";
|
||||
while (true) {
|
||||
c8 character;
|
||||
if (B3DFile->read(&character, sizeof(character)) == 0)
|
||||
break; // eof
|
||||
if (character == 0)
|
||||
break;
|
||||
newstring.push_back(character);
|
||||
}
|
||||
return newstring;
|
||||
}
|
||||
|
||||
void CB3DMeshFileLoader::readFloats(f32 *vec, u32 count)
|
||||
{
|
||||
B3DFile->read(vec, count * sizeof(f32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
for (u32 n = 0; n < count; ++n)
|
||||
vec[n] = os::Byteswap::byteswap(vec[n]);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
79
src/CB3DMeshFileLoader.h
Normal file
79
src/CB3DMeshFileLoader.h
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2006-2012 Luke Hoschke
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
// B3D Mesh loader
|
||||
// File format designed by Mark Sibly for the Blitz3D engine and has been
|
||||
// declared public domain
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IMeshLoader.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "CSkinnedMesh.h"
|
||||
#include "SB3DStructs.h"
|
||||
#include "IReadFile.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! Meshloader for B3D format
|
||||
class CB3DMeshFileLoader : public IMeshLoader
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CB3DMeshFileLoader(scene::ISceneManager *smgr);
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".bsp")
|
||||
bool isALoadableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! creates/loads an animated mesh from the file.
|
||||
//! \return Pointer to the created mesh. Returns 0 if loading failed.
|
||||
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
|
||||
//! See IReferenceCounted::drop() for more information.
|
||||
IAnimatedMesh *createMesh(io::IReadFile *file) override;
|
||||
|
||||
private:
|
||||
bool load();
|
||||
bool readChunkNODE(CSkinnedMesh::SJoint *InJoint);
|
||||
bool readChunkMESH(CSkinnedMesh::SJoint *InJoint);
|
||||
bool readChunkVRTS(CSkinnedMesh::SJoint *InJoint);
|
||||
bool readChunkTRIS(scene::SSkinMeshBuffer *MeshBuffer, u32 MeshBufferID, s32 Vertices_Start);
|
||||
bool readChunkBONE(CSkinnedMesh::SJoint *InJoint);
|
||||
bool readChunkKEYS(CSkinnedMesh::SJoint *InJoint);
|
||||
bool readChunkANIM();
|
||||
bool readChunkTEXS();
|
||||
bool readChunkBRUS();
|
||||
|
||||
std::string readString();
|
||||
void readFloats(f32 *vec, u32 count);
|
||||
|
||||
core::array<SB3dChunk> B3dStack;
|
||||
|
||||
core::array<SB3dMaterial> Materials;
|
||||
core::array<SB3dTexture> Textures;
|
||||
|
||||
core::array<s32> AnimatedVertices_VertexID;
|
||||
|
||||
core::array<s32> AnimatedVertices_BufferID;
|
||||
|
||||
core::array<video::S3DVertex2TCoords> BaseVertices;
|
||||
|
||||
CSkinnedMesh *AnimatedMesh;
|
||||
io::IReadFile *B3DFile;
|
||||
|
||||
// B3Ds have Vertex ID's local within the mesh I don't want this
|
||||
// Variable needs to be class member due to recursion in calls
|
||||
u32 VerticesStart;
|
||||
|
||||
bool NormalsInFile;
|
||||
bool HasVertexColors;
|
||||
bool ShowWarning;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
266
src/CBillboardSceneNode.cpp
Normal file
266
src/CBillboardSceneNode.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CBillboardSceneNode.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "ICameraSceneNode.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position, const core::dimension2d<f32> &size,
|
||||
video::SColor colorTop, video::SColor colorBottom) :
|
||||
IBillboardSceneNode(parent, mgr, id, position),
|
||||
Buffer(new SMeshBuffer())
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CBillboardSceneNode");
|
||||
#endif
|
||||
|
||||
setSize(size);
|
||||
|
||||
Buffer->Vertices.set_used(4);
|
||||
Buffer->Indices.set_used(6);
|
||||
|
||||
Buffer->Indices[0] = 0;
|
||||
Buffer->Indices[1] = 2;
|
||||
Buffer->Indices[2] = 1;
|
||||
Buffer->Indices[3] = 0;
|
||||
Buffer->Indices[4] = 3;
|
||||
Buffer->Indices[5] = 2;
|
||||
|
||||
Buffer->Vertices[0].TCoords.set(1.0f, 1.0f);
|
||||
Buffer->Vertices[0].Color = colorBottom;
|
||||
|
||||
Buffer->Vertices[1].TCoords.set(1.0f, 0.0f);
|
||||
Buffer->Vertices[1].Color = colorTop;
|
||||
|
||||
Buffer->Vertices[2].TCoords.set(0.0f, 0.0f);
|
||||
Buffer->Vertices[2].Color = colorTop;
|
||||
|
||||
Buffer->Vertices[3].TCoords.set(0.0f, 1.0f);
|
||||
Buffer->Vertices[3].Color = colorBottom;
|
||||
}
|
||||
|
||||
CBillboardSceneNode::~CBillboardSceneNode()
|
||||
{
|
||||
Buffer->drop();
|
||||
}
|
||||
|
||||
//! pre render event
|
||||
void CBillboardSceneNode::OnRegisterSceneNode()
|
||||
{
|
||||
if (IsVisible)
|
||||
SceneManager->registerNodeForRendering(this);
|
||||
|
||||
ISceneNode::OnRegisterSceneNode();
|
||||
}
|
||||
|
||||
//! render
|
||||
void CBillboardSceneNode::render()
|
||||
{
|
||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||
ICameraSceneNode *camera = SceneManager->getActiveCamera();
|
||||
|
||||
if (!camera || !driver)
|
||||
return;
|
||||
|
||||
// make billboard look to camera
|
||||
updateMesh(camera);
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
||||
driver->setMaterial(Buffer->Material);
|
||||
driver->drawMeshBuffer(Buffer);
|
||||
|
||||
if (DebugDataVisible & scene::EDS_BBOX) {
|
||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
||||
video::SMaterial m;
|
||||
m.Lighting = false;
|
||||
driver->setMaterial(m);
|
||||
driver->draw3DBox(BBoxSafe, video::SColor(0, 208, 195, 152));
|
||||
}
|
||||
}
|
||||
|
||||
void CBillboardSceneNode::updateMesh(const irr::scene::ICameraSceneNode *camera)
|
||||
{
|
||||
// billboard looks toward camera
|
||||
core::vector3df pos = getAbsolutePosition();
|
||||
|
||||
core::vector3df campos = camera->getAbsolutePosition();
|
||||
core::vector3df target = camera->getTarget();
|
||||
core::vector3df up = camera->getUpVector();
|
||||
core::vector3df view = target - campos;
|
||||
view.normalize();
|
||||
|
||||
core::vector3df horizontal = up.crossProduct(view);
|
||||
if (horizontal.getLength() == 0) {
|
||||
horizontal.set(up.Y, up.X, up.Z);
|
||||
}
|
||||
horizontal.normalize();
|
||||
core::vector3df topHorizontal = horizontal * 0.5f * TopEdgeWidth;
|
||||
horizontal *= 0.5f * Size.Width;
|
||||
|
||||
// pointing down!
|
||||
core::vector3df vertical = horizontal.crossProduct(view);
|
||||
vertical.normalize();
|
||||
vertical *= 0.5f * Size.Height;
|
||||
|
||||
view *= -1.0f;
|
||||
|
||||
core::array<video::S3DVertex> &vertices = Buffer->Vertices;
|
||||
|
||||
for (s32 i = 0; i < 4; ++i)
|
||||
vertices[i].Normal = view;
|
||||
|
||||
/* Vertices are:
|
||||
2--1
|
||||
|\ |
|
||||
| \|
|
||||
3--0
|
||||
*/
|
||||
vertices[0].Pos = pos + horizontal + vertical;
|
||||
vertices[1].Pos = pos + topHorizontal - vertical;
|
||||
vertices[2].Pos = pos - topHorizontal - vertical;
|
||||
vertices[3].Pos = pos - horizontal + vertical;
|
||||
|
||||
Buffer->setDirty(EBT_VERTEX);
|
||||
Buffer->recalculateBoundingBox();
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CBillboardSceneNode::getBoundingBox() const
|
||||
{
|
||||
// Really wrong when scaled (as the node does not scale it's vertices - maybe it should?)
|
||||
return BBoxSafe;
|
||||
}
|
||||
|
||||
const core::aabbox3d<f32> &CBillboardSceneNode::getTransformedBillboardBoundingBox(const irr::scene::ICameraSceneNode *camera)
|
||||
{
|
||||
updateMesh(camera);
|
||||
return Buffer->BoundingBox;
|
||||
}
|
||||
|
||||
void CBillboardSceneNode::setSize(const core::dimension2d<f32> &size)
|
||||
{
|
||||
Size = size;
|
||||
|
||||
if (core::equals(Size.Width, 0.0f))
|
||||
Size.Width = 1.0f;
|
||||
TopEdgeWidth = Size.Width;
|
||||
|
||||
if (core::equals(Size.Height, 0.0f))
|
||||
Size.Height = 1.0f;
|
||||
|
||||
const f32 extent = 0.5f * sqrtf(Size.Width * Size.Width + Size.Height * Size.Height);
|
||||
BBoxSafe.MinEdge.set(-extent, -extent, -extent);
|
||||
BBoxSafe.MaxEdge.set(extent, extent, extent);
|
||||
}
|
||||
|
||||
void CBillboardSceneNode::setSize(f32 height, f32 bottomEdgeWidth, f32 topEdgeWidth)
|
||||
{
|
||||
Size.set(bottomEdgeWidth, height);
|
||||
TopEdgeWidth = topEdgeWidth;
|
||||
|
||||
if (core::equals(Size.Height, 0.0f))
|
||||
Size.Height = 1.0f;
|
||||
|
||||
if (core::equals(Size.Width, 0.f) && core::equals(TopEdgeWidth, 0.f)) {
|
||||
Size.Width = 1.0f;
|
||||
TopEdgeWidth = 1.0f;
|
||||
}
|
||||
|
||||
const f32 extent = 0.5f * sqrtf(Size.Width * Size.Width + Size.Height * Size.Height);
|
||||
BBoxSafe.MinEdge.set(-extent, -extent, -extent);
|
||||
BBoxSafe.MaxEdge.set(extent, extent, extent);
|
||||
}
|
||||
|
||||
video::SMaterial &CBillboardSceneNode::getMaterial(u32 i)
|
||||
{
|
||||
return Buffer->Material;
|
||||
}
|
||||
|
||||
//! returns amount of materials used by this scene node.
|
||||
u32 CBillboardSceneNode::getMaterialCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//! gets the size of the billboard
|
||||
const core::dimension2d<f32> &CBillboardSceneNode::getSize() const
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
|
||||
//! Gets the widths of the top and bottom edges of the billboard.
|
||||
void CBillboardSceneNode::getSize(f32 &height, f32 &bottomEdgeWidth,
|
||||
f32 &topEdgeWidth) const
|
||||
{
|
||||
height = Size.Height;
|
||||
bottomEdgeWidth = Size.Width;
|
||||
topEdgeWidth = TopEdgeWidth;
|
||||
}
|
||||
|
||||
//! Set the color of all vertices of the billboard
|
||||
//! \param overallColor: the color to set
|
||||
void CBillboardSceneNode::setColor(const video::SColor &overallColor)
|
||||
{
|
||||
for (u32 vertex = 0; vertex < 4; ++vertex)
|
||||
Buffer->Vertices[vertex].Color = overallColor;
|
||||
}
|
||||
|
||||
//! Set the color of the top and bottom vertices of the billboard
|
||||
//! \param topColor: the color to set the top vertices
|
||||
//! \param bottomColor: the color to set the bottom vertices
|
||||
void CBillboardSceneNode::setColor(const video::SColor &topColor,
|
||||
const video::SColor &bottomColor)
|
||||
{
|
||||
Buffer->Vertices[0].Color = bottomColor;
|
||||
Buffer->Vertices[1].Color = topColor;
|
||||
Buffer->Vertices[2].Color = topColor;
|
||||
Buffer->Vertices[3].Color = bottomColor;
|
||||
}
|
||||
|
||||
//! Gets the color of the top and bottom vertices of the billboard
|
||||
//! \param[out] topColor: stores the color of the top vertices
|
||||
//! \param[out] bottomColor: stores the color of the bottom vertices
|
||||
void CBillboardSceneNode::getColor(video::SColor &topColor,
|
||||
video::SColor &bottomColor) const
|
||||
{
|
||||
bottomColor = Buffer->Vertices[0].Color;
|
||||
topColor = Buffer->Vertices[1].Color;
|
||||
}
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *CBillboardSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||
{
|
||||
if (!newParent)
|
||||
newParent = Parent;
|
||||
if (!newManager)
|
||||
newManager = SceneManager;
|
||||
|
||||
CBillboardSceneNode *nb = new CBillboardSceneNode(newParent,
|
||||
newManager, ID, RelativeTranslation, Size);
|
||||
|
||||
nb->cloneMembers(this, newManager);
|
||||
nb->Buffer->Material = Buffer->Material;
|
||||
nb->Size = Size;
|
||||
nb->TopEdgeWidth = this->TopEdgeWidth;
|
||||
|
||||
video::SColor topColor, bottomColor;
|
||||
getColor(topColor, bottomColor);
|
||||
nb->setColor(topColor, bottomColor);
|
||||
|
||||
if (newParent)
|
||||
nb->drop();
|
||||
return nb;
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
113
src/CBillboardSceneNode.h
Normal file
113
src/CBillboardSceneNode.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IBillboardSceneNode.h"
|
||||
#include "SMeshBuffer.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! Scene node which is a billboard. A billboard is like a 3d sprite: A 2d element,
|
||||
//! which always looks to the camera.
|
||||
class CBillboardSceneNode : virtual public IBillboardSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position, const core::dimension2d<f32> &size,
|
||||
video::SColor colorTop = video::SColor(0xFFFFFFFF),
|
||||
video::SColor colorBottom = video::SColor(0xFFFFFFFF));
|
||||
|
||||
virtual ~CBillboardSceneNode();
|
||||
|
||||
//! pre render event
|
||||
void OnRegisterSceneNode() override;
|
||||
|
||||
//! render
|
||||
void render() override;
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
//! sets the size of the billboard
|
||||
void setSize(const core::dimension2d<f32> &size) override;
|
||||
|
||||
//! Sets the widths of the top and bottom edges of the billboard independently.
|
||||
void setSize(f32 height, f32 bottomEdgeWidth, f32 topEdgeWidth) override;
|
||||
|
||||
//! gets the size of the billboard
|
||||
const core::dimension2d<f32> &getSize() const override;
|
||||
|
||||
//! Gets the widths of the top and bottom edges of the billboard.
|
||||
void getSize(f32 &height, f32 &bottomEdgeWidth, f32 &topEdgeWidth) const override;
|
||||
|
||||
video::SMaterial &getMaterial(u32 i) override;
|
||||
|
||||
//! returns amount of materials used by this scene node.
|
||||
u32 getMaterialCount() const override;
|
||||
|
||||
//! Set the color of all vertices of the billboard
|
||||
//! \param overallColor: the color to set
|
||||
void setColor(const video::SColor &overallColor) override;
|
||||
|
||||
//! Set the color of the top and bottom vertices of the billboard
|
||||
//! \param topColor: the color to set the top vertices
|
||||
//! \param bottomColor: the color to set the bottom vertices
|
||||
virtual void setColor(const video::SColor &topColor,
|
||||
const video::SColor &bottomColor) override;
|
||||
|
||||
//! Gets the color of the top and bottom vertices of the billboard
|
||||
//! \param[out] topColor: stores the color of the top vertices
|
||||
//! \param[out] bottomColor: stores the color of the bottom vertices
|
||||
virtual void getColor(video::SColor &topColor,
|
||||
video::SColor &bottomColor) const override;
|
||||
|
||||
//! Get the real boundingbox used by the billboard (which depends on the active camera)
|
||||
const core::aabbox3d<f32> &getTransformedBillboardBoundingBox(const irr::scene::ICameraSceneNode *camera) override;
|
||||
|
||||
//! Get the amount of mesh buffers.
|
||||
u32 getMeshBufferCount() const override
|
||||
{
|
||||
return Buffer ? 1 : 0;
|
||||
}
|
||||
|
||||
//! Get pointer to the mesh buffer.
|
||||
IMeshBuffer *getMeshBuffer(u32 nr) const override
|
||||
{
|
||||
if (nr == 0)
|
||||
return Buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Returns type of the scene node
|
||||
ESCENE_NODE_TYPE getType() const override { return ESNT_BILLBOARD; }
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
|
||||
|
||||
protected:
|
||||
void updateMesh(const irr::scene::ICameraSceneNode *camera);
|
||||
|
||||
private:
|
||||
//! Size.Width is the bottom edge width
|
||||
core::dimension2d<f32> Size;
|
||||
f32 TopEdgeWidth;
|
||||
|
||||
//! BoundingBox which is large enough to contain the billboard independent of the camera
|
||||
// TODO: BUG - still can be wrong with scaling < 1. Billboards should calculate relative coordinates for their mesh
|
||||
// and then use the node-scaling. But needs some work...
|
||||
/** Note that we can't use the real boundingbox for culling because at that point
|
||||
the camera which is used to calculate the billboard is not yet updated. So we only
|
||||
know the real boundingbox after rendering - which is too late for culling. */
|
||||
core::aabbox3d<f32> BBoxSafe;
|
||||
|
||||
scene::SMeshBuffer *Buffer;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
942
src/CBlit.h
Normal file
942
src/CBlit.h
Normal file
@@ -0,0 +1,942 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SoftwareDriver2_helper.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
//! f18 - fixpoint 14.18 limit to 16k Textures
|
||||
#define CBLIT_USE_FIXPOINT18
|
||||
|
||||
#if defined(CBLIT_USE_FIXPOINT18)
|
||||
typedef int f18;
|
||||
#define f18_one 262144
|
||||
#define f18_zero 0
|
||||
#define f32_to_f18(x) ((f18)floorf(((x) * 262144.f) + 0.f))
|
||||
#define f32_to_f32(x) (x)
|
||||
#define f18_floor(x) ((x) >> 18)
|
||||
#define f18_round(x) ((x + 131.072) >> 18)
|
||||
#else
|
||||
typedef float f18;
|
||||
#define f18_one 1.f
|
||||
#define f18_zero_dot_five 0.5f
|
||||
#define f18_zero 0.f
|
||||
#define f32_to_f18(x) (x)
|
||||
#define f32_to_f32(x) (x)
|
||||
#define f18_floor(x) ((int)(x))
|
||||
#define f18_round(x) ((int)(x + 0.5f))
|
||||
#endif
|
||||
|
||||
struct SBlitJob
|
||||
{
|
||||
AbsRectangle Dest;
|
||||
AbsRectangle Source;
|
||||
|
||||
u32 argb;
|
||||
|
||||
const void *src;
|
||||
void *dst;
|
||||
|
||||
u32 width; // draw size
|
||||
u32 height;
|
||||
|
||||
u32 srcPixelMul; // pixel byte size
|
||||
u32 dstPixelMul;
|
||||
|
||||
int srcPitch; // scanline byte size. allow negative for mirror
|
||||
u32 dstPitch;
|
||||
|
||||
bool stretch;
|
||||
f32 x_stretch;
|
||||
f32 y_stretch;
|
||||
};
|
||||
|
||||
// Bitfields Cohen Sutherland
|
||||
enum eClipCode
|
||||
{
|
||||
CLIPCODE_EMPTY = 0,
|
||||
CLIPCODE_BOTTOM = 1,
|
||||
CLIPCODE_TOP = 2,
|
||||
CLIPCODE_LEFT = 4,
|
||||
CLIPCODE_RIGHT = 8
|
||||
};
|
||||
|
||||
inline u32 GetClipCode(const AbsRectangle &r, const core::position2d<s32> &p)
|
||||
{
|
||||
u32 code = CLIPCODE_EMPTY;
|
||||
|
||||
if (p.X < r.x0)
|
||||
code = CLIPCODE_LEFT;
|
||||
else if (p.X > r.x1)
|
||||
code = CLIPCODE_RIGHT;
|
||||
|
||||
if (p.Y < r.y0)
|
||||
code |= CLIPCODE_TOP;
|
||||
else if (p.Y > r.y1)
|
||||
code |= CLIPCODE_BOTTOM;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
inline void GetClip(AbsRectangle &clipping, video::IImage *t)
|
||||
{
|
||||
clipping.x0 = 0;
|
||||
clipping.y0 = 0;
|
||||
clipping.x1 = t->getDimension().Width - 1;
|
||||
clipping.y1 = t->getDimension().Height - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
return alpha in [0;256] Granularity from 32-Bit ARGB
|
||||
add highbit alpha ( alpha > 127 ? + 1 )
|
||||
*/
|
||||
static inline u32 extractAlpha(const u32 c)
|
||||
{
|
||||
return (c >> 24) + (c >> 31);
|
||||
}
|
||||
|
||||
/*
|
||||
return alpha in [0;255] Granularity and 32-Bit ARGB
|
||||
add highbit alpha ( alpha > 127 ? + 1 )
|
||||
*/
|
||||
static inline u32 packAlpha(const u32 c)
|
||||
{
|
||||
return (c > 127 ? c - 1 : c) << 24;
|
||||
}
|
||||
|
||||
/*!
|
||||
Scale Color by (1/value)
|
||||
value 0 - 256 ( alpha )
|
||||
*/
|
||||
inline u32 PixelLerp32(const u32 source, const u32 value)
|
||||
{
|
||||
u32 srcRB = source & 0x00FF00FF;
|
||||
u32 srcXG = (source & 0xFF00FF00) >> 8;
|
||||
|
||||
srcRB *= value;
|
||||
srcXG *= value;
|
||||
|
||||
srcRB >>= 8;
|
||||
// srcXG >>= 8;
|
||||
|
||||
srcXG &= 0xFF00FF00;
|
||||
srcRB &= 0x00FF00FF;
|
||||
|
||||
return srcRB | srcXG;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureCopy_x_to_x(const SBlitJob *job)
|
||||
{
|
||||
if (job->stretch) {
|
||||
const f18 wscale = f32_to_f18(job->x_stretch);
|
||||
const f18 hscale = f32_to_f18(job->y_stretch);
|
||||
|
||||
f18 src_y = f18_zero;
|
||||
|
||||
if (job->srcPixelMul == 4) {
|
||||
const u32 *src = (u32 *)(job->src);
|
||||
u32 *dst = (u32 *)(job->dst);
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
dst[dx] = src[f18_floor(src_x)];
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
} else if (job->srcPixelMul == 2) {
|
||||
const u16 *src = (u16 *)(job->src);
|
||||
u16 *dst = (u16 *)(job->dst);
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
dst[dx] = src[f18_floor(src_x)];
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const size_t widthPitch = job->width * job->dstPixelMul;
|
||||
const void *src = (void *)job->src;
|
||||
void *dst = (void *)job->dst;
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy) {
|
||||
memcpy(dst, src, widthPitch);
|
||||
|
||||
src = (void *)((u8 *)(src) + job->srcPitch);
|
||||
dst = (void *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureCopy_32_to_16(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u32 *src = static_cast<const u32 *>(job->src);
|
||||
u16 *dst = static_cast<u16 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = (u32)(dx * wscale);
|
||||
// 16 bit Blitter depends on pre-multiplied color
|
||||
const u32 s = PixelLerp32(src[src_x] | 0xFF000000, extractAlpha(src[src_x]));
|
||||
dst[dx] = video::A8R8G8B8toA1R5G5B5(s);
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
// 16 bit Blitter depends on pre-multiplied color
|
||||
const u32 s = PixelLerp32(src[dx] | 0xFF000000, extractAlpha(src[dx]));
|
||||
dst[dx] = video::A8R8G8B8toA1R5G5B5(s);
|
||||
}
|
||||
|
||||
src = (u32 *)((u8 *)(src) + job->srcPitch);
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureCopy_24_to_16(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u8 *src = static_cast<const u8 *>(job->src);
|
||||
u16 *dst = static_cast<u16 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch * 3.f;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u8 *)(job->src) + job->srcPitch * src_y;
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u8 *src_x = src + (u32)(dx * wscale);
|
||||
dst[dx] = video::RGBA16(src_x[0], src_x[1], src_x[2]);
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
const u8 *s = src;
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
dst[dx] = video::RGBA16(s[0], s[1], s[2]);
|
||||
s += 3;
|
||||
}
|
||||
|
||||
src = src + job->srcPitch;
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureCopy_16_to_32(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u16 *src = static_cast<const u16 *>(job->src);
|
||||
u32 *dst = static_cast<u32 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = (u32)(dx * wscale);
|
||||
dst[dx] = video::A1R5G5B5toA8R8G8B8(src[src_x]);
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
dst[dx] = video::A1R5G5B5toA8R8G8B8(src[dx]);
|
||||
}
|
||||
|
||||
src = (u16 *)((u8 *)(src) + job->srcPitch);
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void executeBlit_TextureCopy_16_to_24(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u16 *src = static_cast<const u16 *>(job->src);
|
||||
u8 *dst = static_cast<u8 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = (u32)(dx * wscale);
|
||||
u32 color = video::A1R5G5B5toA8R8G8B8(src[src_x]);
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
*writeTo++ = (color >> 16) & 0xFF;
|
||||
*writeTo++ = (color >> 8) & 0xFF;
|
||||
*writeTo++ = color & 0xFF;
|
||||
}
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
u32 color = video::A1R5G5B5toA8R8G8B8(src[dx]);
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
*writeTo++ = (color >> 16) & 0xFF;
|
||||
*writeTo++ = (color >> 8) & 0xFF;
|
||||
*writeTo++ = color & 0xFF;
|
||||
}
|
||||
|
||||
src = (u16 *)((u8 *)(src) + job->srcPitch);
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureCopy_24_to_32(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u8 *src = static_cast<const u8 *>(job->src);
|
||||
u32 *dst = static_cast<u32 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch * 3.f;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (const u8 *)job->src + (job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u8 *s = src + (u32)(dx * wscale);
|
||||
dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2];
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy < job->height; ++dy) {
|
||||
const u8 *s = src;
|
||||
|
||||
for (u32 dx = 0; dx < job->width; ++dx) {
|
||||
dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2];
|
||||
s += 3;
|
||||
}
|
||||
|
||||
src = src + job->srcPitch;
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void executeBlit_TextureCopy_32_to_24(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u32 *src = static_cast<const u32 *>(job->src);
|
||||
u8 *dst = static_cast<u8 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = src[(u32)(dx * wscale)];
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
*writeTo++ = (src_x >> 16) & 0xFF;
|
||||
*writeTo++ = (src_x >> 8) & 0xFF;
|
||||
*writeTo++ = src_x & 0xFF;
|
||||
}
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
*writeTo++ = (src[dx] >> 16) & 0xFF;
|
||||
*writeTo++ = (src[dx] >> 8) & 0xFF;
|
||||
*writeTo++ = src[dx] & 0xFF;
|
||||
}
|
||||
|
||||
src = (u32 *)((u8 *)(src) + job->srcPitch);
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureBlend_16_to_16(const SBlitJob *job)
|
||||
{
|
||||
const f18 wscale = f32_to_f18(job->x_stretch);
|
||||
const f18 hscale = f32_to_f18(job->y_stretch);
|
||||
|
||||
f18 src_y = f18_zero;
|
||||
u16 *dst = (u16 *)job->dst;
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
const u16 *src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
dst[dx] = PixelBlend16(dst[dx], src[f18_floor(src_x)]);
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureBlend_32_to_32(const SBlitJob *job)
|
||||
{
|
||||
const f18 wscale = f32_to_f18(job->x_stretch);
|
||||
const f18 hscale = f32_to_f18(job->y_stretch);
|
||||
|
||||
f18 src_y = f18_zero;
|
||||
u32 *dst = (u32 *)job->dst;
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
const u32 *src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
dst[dx] = PixelBlend32(dst[dx], src[f18_floor(src_x)]);
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureBlendColor_16_to_16(const SBlitJob *job)
|
||||
{
|
||||
const u16 blend = video::A8R8G8B8toA1R5G5B5(job->argb);
|
||||
|
||||
const f18 wscale = f32_to_f18(job->x_stretch);
|
||||
const f18 hscale = f32_to_f18(job->y_stretch);
|
||||
|
||||
f18 src_y = f18_zero;
|
||||
u16 *dst = (u16 *)job->dst;
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
const u16 *src = (u16 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
u16 c0 = src[f18_floor(src_x)];
|
||||
if (0 == (c0 & 0x8000))
|
||||
continue;
|
||||
|
||||
dst[dx] = PixelMul16_2(c0, blend);
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_TextureBlendColor_32_to_32(const SBlitJob *job)
|
||||
{
|
||||
const f18 wscale = f32_to_f18(job->x_stretch);
|
||||
const f18 hscale = f32_to_f18(job->y_stretch);
|
||||
|
||||
u32 *dst = (u32 *)job->dst;
|
||||
f18 src_y = f18_zero;
|
||||
for (u32 dy = 0; dy < job->height; ++dy, src_y += hscale) {
|
||||
const u32 *src = (u32 *)((u8 *)(job->src) + job->srcPitch * f18_floor(src_y));
|
||||
|
||||
f18 src_x = f18_zero;
|
||||
for (u32 dx = 0; dx < job->width; ++dx, src_x += wscale) {
|
||||
dst[dx] = PixelBlend32(dst[dx], PixelMul32_2(src[f18_floor(src_x)], job->argb));
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_Color_16_to_16(const SBlitJob *job)
|
||||
{
|
||||
const u16 c = video::A8R8G8B8toA1R5G5B5(job->argb);
|
||||
u16 *dst = (u16 *)job->dst;
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy) {
|
||||
memset16(dst, c, job->srcPitch);
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_Color_32_to_32(const SBlitJob *job)
|
||||
{
|
||||
u32 *dst = (u32 *)job->dst;
|
||||
|
||||
for (u32 dy = 0; dy < job->height; ++dy) {
|
||||
memset32(dst, job->argb, job->srcPitch);
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_ColorAlpha_16_to_16(const SBlitJob *job)
|
||||
{
|
||||
u16 *dst = (u16 *)job->dst;
|
||||
|
||||
const u16 alpha = extractAlpha(job->argb) >> 3;
|
||||
if (0 == alpha)
|
||||
return;
|
||||
const u32 src = video::A8R8G8B8toA1R5G5B5(job->argb);
|
||||
|
||||
for (u32 dy = 0; dy != job->height; ++dy) {
|
||||
for (u32 dx = 0; dx != job->width; ++dx) {
|
||||
dst[dx] = PixelBlend16(dst[dx], src, alpha);
|
||||
}
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static void executeBlit_ColorAlpha_32_to_32(const SBlitJob *job)
|
||||
{
|
||||
const u32 alpha = extractAlpha(job->argb);
|
||||
if (0 == alpha)
|
||||
return;
|
||||
|
||||
u32 *dst = (u32 *)job->dst;
|
||||
for (u32 dy = 0; dy < job->height; ++dy) {
|
||||
for (u32 dx = 0; dx < job->width; ++dx) {
|
||||
dst[dx] = PixelBlend32(dst[dx], job->argb, alpha);
|
||||
}
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Pixel =>
|
||||
color = sourceAlpha > 0 ? source, else dest
|
||||
alpha = max(destAlpha, sourceAlpha)
|
||||
*/
|
||||
inline u16 PixelCombine16(const u16 c2, const u16 c1)
|
||||
{
|
||||
if (video::getAlpha(c1) > 0)
|
||||
return c1;
|
||||
else
|
||||
return c2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Combine alpha channels (increases alpha / reduces transparency)
|
||||
*/
|
||||
static void executeBlit_TextureCombineColor_16_to_16(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width * 2;
|
||||
const u32 h = job->height * 2;
|
||||
u16 *src = (u16 *)job->src;
|
||||
u16 *dst = (u16 *)job->dst;
|
||||
|
||||
const u16 jobColor = video::A8R8G8B8toA1R5G5B5(job->argb);
|
||||
|
||||
/*
|
||||
Stretch not supported.
|
||||
*/
|
||||
for (u32 dy = 0; dy != h; dy++) {
|
||||
for (u32 dx = 0; dx != w; dx++) {
|
||||
const u16 src_x = src[dx];
|
||||
const u16 dst_x = dst[dx];
|
||||
dst[dx] = PixelCombine16(dst_x, PixelMul16_2(src_x, jobColor));
|
||||
}
|
||||
src = (u16 *)((u8 *)(src) + job->srcPitch);
|
||||
dst = (u16 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Combine alpha channels (increases alpha / reduces transparency)
|
||||
*/
|
||||
static void executeBlit_TextureCombineColor_16_to_24(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u16 *src = static_cast<const u16 *>(job->src);
|
||||
u8 *dst = static_cast<u8 *>(job->dst);
|
||||
|
||||
const u16 jobColor = video::A8R8G8B8toA1R5G5B5(job->argb);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u16 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = (u32)(dx * wscale);
|
||||
u32 color = PixelMul16_2(video::A1R5G5B5toA8R8G8B8(src[src_x]), jobColor);
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
if (video::getAlpha(src[src_x]) > 0) { // only overlay if source has visible alpha (alpha == 1)
|
||||
*writeTo++ = (color >> 16) & 0xFF;
|
||||
*writeTo++ = (color >> 8) & 0xFF;
|
||||
*writeTo++ = color & 0xFF;
|
||||
}
|
||||
}
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
u32 color = PixelMul16_2(video::A1R5G5B5toA8R8G8B8(src[dx]), jobColor);
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
if (video::getAlpha(src[dx]) > 0) { // only overlay if source has visible alpha (alpha == 1)
|
||||
*writeTo++ = (color >> 16) & 0xFF;
|
||||
*writeTo++ = (color >> 8) & 0xFF;
|
||||
*writeTo++ = color & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
src = (u16 *)((u8 *)(src) + job->srcPitch);
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Pixel =>
|
||||
color = dest * ( 1 - SourceAlpha ) + source * SourceAlpha,
|
||||
alpha = destAlpha * ( 1 - SourceAlpha ) + sourceAlpha
|
||||
|
||||
where "1" means "full scale" (255)
|
||||
*/
|
||||
inline u32 PixelCombine32(const u32 c2, const u32 c1)
|
||||
{
|
||||
// alpha test
|
||||
u32 alpha = c1 & 0xFF000000;
|
||||
|
||||
if (0 == alpha)
|
||||
return c2;
|
||||
if (0xFF000000 == alpha) {
|
||||
return c1;
|
||||
}
|
||||
|
||||
alpha >>= 24;
|
||||
|
||||
// add highbit alpha, if ( alpha > 127 ) alpha += 1;
|
||||
// stretches [0;255] to [0;256] to avoid division by 255. use division 256 == shr 8
|
||||
alpha += (alpha >> 7);
|
||||
|
||||
u32 srcRB = c1 & 0x00FF00FF;
|
||||
u32 srcXG = c1 & 0x0000FF00;
|
||||
|
||||
u32 dstRB = c2 & 0x00FF00FF;
|
||||
u32 dstXG = c2 & 0x0000FF00;
|
||||
|
||||
u32 rb = srcRB - dstRB;
|
||||
u32 xg = srcXG - dstXG;
|
||||
|
||||
rb *= alpha;
|
||||
xg *= alpha;
|
||||
rb >>= 8;
|
||||
xg >>= 8;
|
||||
|
||||
rb += dstRB;
|
||||
xg += dstXG;
|
||||
|
||||
rb &= 0x00FF00FF;
|
||||
xg &= 0x0000FF00;
|
||||
|
||||
u32 sa = c1 >> 24;
|
||||
u32 da = c2 >> 24;
|
||||
u32 blendAlpha_fix8 = (sa * 256 + da * (256 - alpha)) >> 8;
|
||||
return blendAlpha_fix8 << 24 | rb | xg;
|
||||
}
|
||||
|
||||
/*!
|
||||
Combine alpha channels (increases alpha / reduces transparency)
|
||||
Destination alpha is treated as full 255
|
||||
*/
|
||||
static void executeBlit_TextureCombineColor_32_to_24(const SBlitJob *job)
|
||||
{
|
||||
const u32 w = job->width;
|
||||
const u32 h = job->height;
|
||||
const u32 *src = static_cast<const u32 *>(job->src);
|
||||
u8 *dst = static_cast<u8 *>(job->dst);
|
||||
|
||||
if (job->stretch) {
|
||||
const float wscale = job->x_stretch;
|
||||
const float hscale = job->y_stretch;
|
||||
|
||||
for (u32 dy = 0; dy < h; ++dy) {
|
||||
const u32 src_y = (u32)(dy * hscale);
|
||||
src = (u32 *)((u8 *)(job->src) + job->srcPitch * src_y);
|
||||
|
||||
for (u32 dx = 0; dx < w; ++dx) {
|
||||
const u32 src_x = src[(u32)(dx * wscale)];
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2];
|
||||
const u32 combo = PixelCombine32(dst_x, PixelMul32_2(src_x, job->argb));
|
||||
*writeTo++ = (combo >> 16) & 0xFF;
|
||||
*writeTo++ = (combo >> 8) & 0xFF;
|
||||
*writeTo++ = combo & 0xFF;
|
||||
}
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
} else {
|
||||
for (u32 dy = 0; dy != h; ++dy) {
|
||||
for (u32 dx = 0; dx != w; ++dx) {
|
||||
u8 *writeTo = &dst[dx * 3];
|
||||
const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2];
|
||||
const u32 combo = PixelCombine32(dst_x, PixelMul32_2(src[dx], job->argb));
|
||||
*writeTo++ = (combo >> 16) & 0xFF;
|
||||
*writeTo++ = (combo >> 8) & 0xFF;
|
||||
*writeTo++ = combo & 0xFF;
|
||||
}
|
||||
|
||||
src = (u32 *)((u8 *)(src) + job->srcPitch);
|
||||
dst += job->dstPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Combine alpha channels (increases alpha / reduces transparency)
|
||||
*/
|
||||
static void executeBlit_TextureCombineColor_32_to_32(const SBlitJob *job)
|
||||
{
|
||||
u32 *src = (u32 *)job->src;
|
||||
u32 *dst = (u32 *)job->dst;
|
||||
|
||||
for (u32 dy = 0; dy != job->height; ++dy) {
|
||||
for (u32 dx = 0; dx != job->width; ++dx) {
|
||||
dst[dx] = PixelCombine32(dst[dx], PixelMul32_2(src[dx], job->argb));
|
||||
}
|
||||
src = (u32 *)((u8 *)(src) + job->srcPitch);
|
||||
dst = (u32 *)((u8 *)(dst) + job->dstPitch);
|
||||
}
|
||||
}
|
||||
|
||||
// Blitter Operation
|
||||
enum eBlitter
|
||||
{
|
||||
BLITTER_INVALID = 0,
|
||||
BLITTER_COLOR,
|
||||
BLITTER_COLOR_ALPHA,
|
||||
BLITTER_TEXTURE,
|
||||
BLITTER_TEXTURE_ALPHA_BLEND,
|
||||
BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
|
||||
BLITTER_TEXTURE_COMBINE_ALPHA,
|
||||
};
|
||||
|
||||
typedef void (*tExecuteBlit)(const SBlitJob *job);
|
||||
|
||||
/*!
|
||||
*/
|
||||
struct blitterTable
|
||||
{
|
||||
eBlitter operation;
|
||||
s32 destFormat;
|
||||
s32 sourceFormat;
|
||||
tExecuteBlit func;
|
||||
};
|
||||
|
||||
static const blitterTable blitTable[] = {
|
||||
{BLITTER_TEXTURE, -2, -2, executeBlit_TextureCopy_x_to_x},
|
||||
{BLITTER_TEXTURE, video::ECF_A1R5G5B5, video::ECF_A8R8G8B8, executeBlit_TextureCopy_32_to_16},
|
||||
{BLITTER_TEXTURE, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16},
|
||||
{BLITTER_TEXTURE, video::ECF_A8R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCopy_16_to_32},
|
||||
{BLITTER_TEXTURE, video::ECF_A8R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_32},
|
||||
{BLITTER_TEXTURE, video::ECF_R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCopy_16_to_24},
|
||||
{BLITTER_TEXTURE, video::ECF_R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCopy_32_to_24},
|
||||
{BLITTER_TEXTURE_ALPHA_BLEND, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureBlend_16_to_16},
|
||||
{BLITTER_TEXTURE_ALPHA_BLEND, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureBlend_32_to_32},
|
||||
{BLITTER_TEXTURE_ALPHA_COLOR_BLEND, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureBlendColor_16_to_16},
|
||||
{BLITTER_TEXTURE_ALPHA_COLOR_BLEND, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureBlendColor_32_to_32},
|
||||
{BLITTER_COLOR, video::ECF_A1R5G5B5, -1, executeBlit_Color_16_to_16},
|
||||
{BLITTER_COLOR, video::ECF_A8R8G8B8, -1, executeBlit_Color_32_to_32},
|
||||
{BLITTER_COLOR_ALPHA, video::ECF_A1R5G5B5, -1, executeBlit_ColorAlpha_16_to_16},
|
||||
{BLITTER_COLOR_ALPHA, video::ECF_A8R8G8B8, -1, executeBlit_ColorAlpha_32_to_32},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_32},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_32},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_24},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_x_to_x},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_16},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16},
|
||||
{BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_24},
|
||||
{BLITTER_INVALID, -1, -1, 0},
|
||||
};
|
||||
|
||||
static inline tExecuteBlit getBlitter2(eBlitter operation, const video::IImage *dest, const video::IImage *source)
|
||||
{
|
||||
video::ECOLOR_FORMAT sourceFormat = (video::ECOLOR_FORMAT)(source ? source->getColorFormat() : -1);
|
||||
video::ECOLOR_FORMAT destFormat = (video::ECOLOR_FORMAT)(dest ? dest->getColorFormat() : -1);
|
||||
|
||||
const blitterTable *b = blitTable;
|
||||
|
||||
while (b->operation != BLITTER_INVALID) {
|
||||
if (b->operation == operation) {
|
||||
if ((b->destFormat == -1 || b->destFormat == destFormat) &&
|
||||
(b->sourceFormat == -1 || b->sourceFormat == sourceFormat))
|
||||
return b->func;
|
||||
else if (b->destFormat == -2 && (sourceFormat == destFormat))
|
||||
return b->func;
|
||||
}
|
||||
b += 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// bounce clipping to texture
|
||||
inline void setClip(AbsRectangle &out, const core::rect<s32> *clip,
|
||||
const video::IImage *tex, s32 passnative, const core::dimension2d<u32> *tex_org)
|
||||
{
|
||||
if (0 == tex) {
|
||||
if (clip && passnative) {
|
||||
out.x0 = clip->UpperLeftCorner.X;
|
||||
out.x1 = clip->LowerRightCorner.X;
|
||||
out.y0 = clip->UpperLeftCorner.Y;
|
||||
out.y1 = clip->LowerRightCorner.Y;
|
||||
} else {
|
||||
out.x0 = 0;
|
||||
out.x1 = 0;
|
||||
out.y0 = 0;
|
||||
out.y1 = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const s32 w = tex->getDimension().Width;
|
||||
const s32 h = tex->getDimension().Height;
|
||||
|
||||
// driver could have changed texture size.
|
||||
if (clip && tex_org && ((u32)w != tex_org->Width || (u32)h != tex_org->Height)) {
|
||||
out.x0 = core::s32_clamp((clip->UpperLeftCorner.X * w) / tex_org->Width, 0, w - 1);
|
||||
out.x1 = core::s32_clamp((clip->LowerRightCorner.X * w) / tex_org->Width, out.x0, w);
|
||||
out.y0 = core::s32_clamp((clip->UpperLeftCorner.Y * h) / tex_org->Height, 0, h - 1);
|
||||
out.y1 = core::s32_clamp((clip->LowerRightCorner.Y * h) / tex_org->Height, out.y0, h);
|
||||
} else if (clip) {
|
||||
// y-1 to prevent starting on illegal memory (not ideal!).
|
||||
out.x0 = core::s32_clamp(clip->UpperLeftCorner.X, 0, w - 1);
|
||||
out.x1 = core::s32_clamp(clip->LowerRightCorner.X, passnative ? 0 : out.x0, w);
|
||||
out.y0 = core::s32_clamp(clip->UpperLeftCorner.Y, 0, h - 1);
|
||||
out.y1 = core::s32_clamp(clip->LowerRightCorner.Y, passnative ? 0 : out.y0, h);
|
||||
} else {
|
||||
out.x0 = 0;
|
||||
out.y0 = 0;
|
||||
out.x1 = w;
|
||||
out.y1 = h;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
a generic 2D Blitter
|
||||
*/
|
||||
static s32 Blit(eBlitter operation,
|
||||
video::IImage *dest,
|
||||
const core::rect<s32> *destClipping,
|
||||
const core::position2d<s32> *destPos,
|
||||
video::IImage *const source,
|
||||
const core::rect<s32> *sourceClipping,
|
||||
u32 argb)
|
||||
{
|
||||
tExecuteBlit blitter = getBlitter2(operation, dest, source);
|
||||
if (0 == blitter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clipping
|
||||
AbsRectangle sourceClip;
|
||||
AbsRectangle destClip;
|
||||
AbsRectangle v;
|
||||
|
||||
SBlitJob job;
|
||||
|
||||
setClip(sourceClip, sourceClipping, source, 1, 0);
|
||||
setClip(destClip, destClipping, dest, 0, 0);
|
||||
|
||||
v.x0 = destPos ? destPos->X : 0;
|
||||
v.y0 = destPos ? destPos->Y : 0;
|
||||
v.x1 = v.x0 + (sourceClip.x1 - sourceClip.x0);
|
||||
v.y1 = v.y0 + (sourceClip.y1 - sourceClip.y0);
|
||||
|
||||
if (!intersect(job.Dest, destClip, v))
|
||||
return 0;
|
||||
|
||||
job.width = job.Dest.x1 - job.Dest.x0;
|
||||
job.height = job.Dest.y1 - job.Dest.y0;
|
||||
|
||||
job.Source.x0 = sourceClip.x0 + (job.Dest.x0 - v.x0);
|
||||
job.Source.x1 = job.Source.x0 + job.width;
|
||||
job.Source.y0 = sourceClip.y0 + (job.Dest.y0 - v.y0);
|
||||
job.Source.y1 = job.Source.y0 + job.height;
|
||||
|
||||
job.argb = argb;
|
||||
|
||||
job.stretch = false;
|
||||
job.x_stretch = 1.f;
|
||||
job.y_stretch = 1.f;
|
||||
|
||||
if (source) {
|
||||
job.srcPitch = source->getPitch();
|
||||
job.srcPixelMul = source->getBytesPerPixel();
|
||||
job.src = (void *)((u8 *)source->getData() + (job.Source.y0 * job.srcPitch) + (job.Source.x0 * job.srcPixelMul));
|
||||
} else {
|
||||
// use srcPitch for color operation on dest
|
||||
job.srcPitch = job.width * dest->getBytesPerPixel();
|
||||
}
|
||||
|
||||
job.dstPitch = dest->getPitch();
|
||||
job.dstPixelMul = dest->getBytesPerPixel();
|
||||
job.dst = (void *)((u8 *)dest->getData() + (job.Dest.y0 * job.dstPitch) + (job.Dest.x0 * job.dstPixelMul));
|
||||
|
||||
blitter(&job);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
89
src/CBoneSceneNode.cpp
Normal file
89
src/CBoneSceneNode.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CBoneSceneNode.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CBoneSceneNode::CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
u32 boneIndex, const std::optional<std::string> &boneName) :
|
||||
IBoneSceneNode(parent, mgr, id),
|
||||
BoneIndex(boneIndex),
|
||||
AnimationMode(EBAM_AUTOMATIC), SkinningSpace(EBSS_LOCAL)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CBoneSceneNode");
|
||||
#endif
|
||||
setName(boneName);
|
||||
}
|
||||
|
||||
//! Returns the index of the bone
|
||||
u32 CBoneSceneNode::getBoneIndex() const
|
||||
{
|
||||
return BoneIndex;
|
||||
}
|
||||
|
||||
//! Sets the animation mode of the bone. Returns true if successful.
|
||||
bool CBoneSceneNode::setAnimationMode(E_BONE_ANIMATION_MODE mode)
|
||||
{
|
||||
AnimationMode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Gets the current animation mode of the bone
|
||||
E_BONE_ANIMATION_MODE CBoneSceneNode::getAnimationMode() const
|
||||
{
|
||||
return AnimationMode;
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CBoneSceneNode::getBoundingBox() const
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
/*
|
||||
//! Returns the relative transformation of the scene node.
|
||||
core::matrix4 CBoneSceneNode::getRelativeTransformation() const
|
||||
{
|
||||
return core::matrix4(); // RelativeTransformation;
|
||||
}
|
||||
*/
|
||||
|
||||
void CBoneSceneNode::OnAnimate(u32 timeMs)
|
||||
{
|
||||
if (IsVisible) {
|
||||
// update absolute position
|
||||
// updateAbsolutePosition();
|
||||
|
||||
// perform the post render process on all children
|
||||
ISceneNodeList::iterator it = Children.begin();
|
||||
for (; it != Children.end(); ++it)
|
||||
(*it)->OnAnimate(timeMs);
|
||||
}
|
||||
}
|
||||
|
||||
void CBoneSceneNode::helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node)
|
||||
{
|
||||
Node->updateAbsolutePosition();
|
||||
|
||||
ISceneNodeList::const_iterator it = Node->getChildren().begin();
|
||||
for (; it != Node->getChildren().end(); ++it) {
|
||||
helper_updateAbsolutePositionOfAllChildren((*it));
|
||||
}
|
||||
}
|
||||
|
||||
void CBoneSceneNode::updateAbsolutePositionOfAllChildren()
|
||||
{
|
||||
helper_updateAbsolutePositionOfAllChildren(this);
|
||||
}
|
||||
|
||||
} // namespace scene
|
||||
} // namespace irr
|
||||
70
src/CBoneSceneNode.h
Normal file
70
src/CBoneSceneNode.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
// Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes
|
||||
|
||||
#include "IBoneSceneNode.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class CBoneSceneNode : public IBoneSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||
s32 id = -1, u32 boneIndex = 0,
|
||||
const std::optional<std::string> &boneName = std::nullopt);
|
||||
|
||||
//! Returns the index of the bone
|
||||
u32 getBoneIndex() const override;
|
||||
|
||||
//! Sets the animation mode of the bone. Returns true if successful.
|
||||
bool setAnimationMode(E_BONE_ANIMATION_MODE mode) override;
|
||||
|
||||
//! Gets the current animation mode of the bone
|
||||
E_BONE_ANIMATION_MODE getAnimationMode() const override;
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
/*
|
||||
//! Returns the relative transformation of the scene node.
|
||||
//core::matrix4 getRelativeTransformation() const override;
|
||||
*/
|
||||
|
||||
void OnAnimate(u32 timeMs) override;
|
||||
|
||||
void updateAbsolutePositionOfAllChildren() override;
|
||||
|
||||
//! How the relative transformation of the bone is used
|
||||
void setSkinningSpace(E_BONE_SKINNING_SPACE space) override
|
||||
{
|
||||
SkinningSpace = space;
|
||||
}
|
||||
|
||||
E_BONE_SKINNING_SPACE getSkinningSpace() const override
|
||||
{
|
||||
return SkinningSpace;
|
||||
}
|
||||
|
||||
private:
|
||||
void helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node);
|
||||
|
||||
u32 BoneIndex;
|
||||
|
||||
core::aabbox3d<f32> Box;
|
||||
|
||||
E_BONE_ANIMATION_MODE AnimationMode;
|
||||
E_BONE_SKINNING_SPACE SkinningSpace;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
314
src/CCameraSceneNode.cpp
Normal file
314
src/CCameraSceneNode.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CCameraSceneNode.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CCameraSceneNode::CCameraSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position, const core::vector3df &lookat) :
|
||||
ICameraSceneNode(parent, mgr, id, position),
|
||||
BoundingBox(core::vector3df(0, 0, 0)), // Camera has no size. Still not sure if FLT_MAX might be the better variant
|
||||
Target(lookat), UpVector(0.0f, 1.0f, 0.0f), ZNear(1.0f), ZFar(3000.0f),
|
||||
InputReceiverEnabled(true), TargetAndRotationAreBound(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CCameraSceneNode");
|
||||
#endif
|
||||
|
||||
// set default projection
|
||||
Fovy = core::PI / 2.5f; // Field of view, in radians.
|
||||
Aspect = 4.0f / 3.0f; // Aspect ratio.
|
||||
|
||||
const video::IVideoDriver *const d = mgr ? mgr->getVideoDriver() : 0;
|
||||
if (d) {
|
||||
if (d->getCurrentRenderTargetSize().Height)
|
||||
Aspect = (f32)d->getCurrentRenderTargetSize().Width /
|
||||
(f32)d->getCurrentRenderTargetSize().Height;
|
||||
}
|
||||
|
||||
ViewArea.setFarNearDistance(ZFar - ZNear);
|
||||
recalculateProjectionMatrix();
|
||||
recalculateViewArea();
|
||||
}
|
||||
|
||||
//! Disables or enables the camera to get key or mouse inputs.
|
||||
void CCameraSceneNode::setInputReceiverEnabled(bool enabled)
|
||||
{
|
||||
InputReceiverEnabled = enabled;
|
||||
}
|
||||
|
||||
//! Returns if the input receiver of the camera is currently enabled.
|
||||
bool CCameraSceneNode::isInputReceiverEnabled() const
|
||||
{
|
||||
return InputReceiverEnabled;
|
||||
}
|
||||
|
||||
//! Sets the projection matrix of the camera.
|
||||
/** The core::matrix4 class has some methods
|
||||
to build a projection matrix. e.g: core::matrix4::buildProjectionMatrixPerspectiveFovLH
|
||||
\param projection: The new projection matrix of the camera. */
|
||||
void CCameraSceneNode::setProjectionMatrix(const core::matrix4 &projection, bool isOrthogonal)
|
||||
{
|
||||
IsOrthogonal = isOrthogonal;
|
||||
ViewArea.getTransform(video::ETS_PROJECTION) = projection;
|
||||
}
|
||||
|
||||
//! Gets the current projection matrix of the camera
|
||||
//! \return Returns the current projection matrix of the camera.
|
||||
const core::matrix4 &CCameraSceneNode::getProjectionMatrix() const
|
||||
{
|
||||
return ViewArea.getTransform(video::ETS_PROJECTION);
|
||||
}
|
||||
|
||||
//! Gets the current view matrix of the camera
|
||||
//! \return Returns the current view matrix of the camera.
|
||||
const core::matrix4 &CCameraSceneNode::getViewMatrix() const
|
||||
{
|
||||
return ViewArea.getTransform(video::ETS_VIEW);
|
||||
}
|
||||
|
||||
//! Sets a custom view matrix affector. The matrix passed here, will be
|
||||
//! multiplied with the view matrix when it gets updated.
|
||||
//! This allows for custom camera setups like, for example, a reflection camera.
|
||||
/** \param affector: The affector matrix. */
|
||||
void CCameraSceneNode::setViewMatrixAffector(const core::matrix4 &affector)
|
||||
{
|
||||
Affector = affector;
|
||||
}
|
||||
|
||||
//! Gets the custom view matrix affector.
|
||||
const core::matrix4 &CCameraSceneNode::getViewMatrixAffector() const
|
||||
{
|
||||
return Affector;
|
||||
}
|
||||
|
||||
//! It is possible to send mouse and key events to the camera. Most cameras
|
||||
//! may ignore this input, but camera scene nodes which are created for
|
||||
//! example with scene::ISceneManager::addMayaCameraSceneNode or
|
||||
//! scene::ISceneManager::addFPSCameraSceneNode, may want to get this input
|
||||
//! for changing their position, look at target or whatever.
|
||||
bool CCameraSceneNode::OnEvent(const SEvent &event)
|
||||
{
|
||||
// animators have been deleted; nothing happens here now!
|
||||
return false;
|
||||
}
|
||||
|
||||
//! sets the look at target of the camera
|
||||
//! \param pos: Look at target of the camera.
|
||||
void CCameraSceneNode::setTarget(const core::vector3df &pos)
|
||||
{
|
||||
Target = pos;
|
||||
|
||||
if (TargetAndRotationAreBound) {
|
||||
const core::vector3df toTarget = Target - getAbsolutePosition();
|
||||
ISceneNode::setRotation(toTarget.getHorizontalAngle());
|
||||
}
|
||||
}
|
||||
|
||||
//! Sets the rotation of the node.
|
||||
/** This only modifies the relative rotation of the node.
|
||||
If the camera's target and rotation are bound ( @see bindTargetAndRotation() )
|
||||
then calling this will also change the camera's target to match the rotation.
|
||||
\param rotation New rotation of the node in degrees. */
|
||||
void CCameraSceneNode::setRotation(const core::vector3df &rotation)
|
||||
{
|
||||
if (TargetAndRotationAreBound)
|
||||
Target = getAbsolutePosition() + rotation.rotationToDirection();
|
||||
|
||||
ISceneNode::setRotation(rotation);
|
||||
}
|
||||
|
||||
//! Gets the current look at target of the camera
|
||||
//! \return Returns the current look at target of the camera
|
||||
const core::vector3df &CCameraSceneNode::getTarget() const
|
||||
{
|
||||
return Target;
|
||||
}
|
||||
|
||||
//! sets the up vector of the camera
|
||||
//! \param pos: New upvector of the camera.
|
||||
void CCameraSceneNode::setUpVector(const core::vector3df &pos)
|
||||
{
|
||||
UpVector = pos;
|
||||
}
|
||||
|
||||
//! Gets the up vector of the camera.
|
||||
//! \return Returns the up vector of the camera.
|
||||
const core::vector3df &CCameraSceneNode::getUpVector() const
|
||||
{
|
||||
return UpVector;
|
||||
}
|
||||
|
||||
f32 CCameraSceneNode::getNearValue() const
|
||||
{
|
||||
return ZNear;
|
||||
}
|
||||
|
||||
f32 CCameraSceneNode::getFarValue() const
|
||||
{
|
||||
return ZFar;
|
||||
}
|
||||
|
||||
f32 CCameraSceneNode::getAspectRatio() const
|
||||
{
|
||||
return Aspect;
|
||||
}
|
||||
|
||||
f32 CCameraSceneNode::getFOV() const
|
||||
{
|
||||
return Fovy;
|
||||
}
|
||||
|
||||
void CCameraSceneNode::setNearValue(f32 f)
|
||||
{
|
||||
ZNear = f;
|
||||
recalculateProjectionMatrix();
|
||||
ViewArea.setFarNearDistance(ZFar - ZNear);
|
||||
}
|
||||
|
||||
void CCameraSceneNode::setFarValue(f32 f)
|
||||
{
|
||||
ZFar = f;
|
||||
recalculateProjectionMatrix();
|
||||
ViewArea.setFarNearDistance(ZFar - ZNear);
|
||||
}
|
||||
|
||||
void CCameraSceneNode::setAspectRatio(f32 f)
|
||||
{
|
||||
Aspect = f;
|
||||
recalculateProjectionMatrix();
|
||||
}
|
||||
|
||||
void CCameraSceneNode::setFOV(f32 f)
|
||||
{
|
||||
Fovy = f;
|
||||
recalculateProjectionMatrix();
|
||||
}
|
||||
|
||||
void CCameraSceneNode::recalculateProjectionMatrix()
|
||||
{
|
||||
ViewArea.getTransform(video::ETS_PROJECTION).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar, false);
|
||||
IsOrthogonal = false;
|
||||
}
|
||||
|
||||
//! prerender
|
||||
void CCameraSceneNode::OnRegisterSceneNode()
|
||||
{
|
||||
if (SceneManager->getActiveCamera() == this)
|
||||
SceneManager->registerNodeForRendering(this, ESNRP_CAMERA);
|
||||
|
||||
ISceneNode::OnRegisterSceneNode();
|
||||
}
|
||||
|
||||
//! render
|
||||
void CCameraSceneNode::render()
|
||||
{
|
||||
updateMatrices();
|
||||
|
||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||
if (driver) {
|
||||
driver->setTransform(video::ETS_PROJECTION, ViewArea.getTransform(video::ETS_PROJECTION));
|
||||
driver->setTransform(video::ETS_VIEW, ViewArea.getTransform(video::ETS_VIEW));
|
||||
}
|
||||
}
|
||||
|
||||
//! update
|
||||
void CCameraSceneNode::updateMatrices()
|
||||
{
|
||||
core::vector3df pos = getAbsolutePosition();
|
||||
core::vector3df tgtv = Target - pos;
|
||||
tgtv.normalize();
|
||||
|
||||
// if upvector and vector to the target are the same, we have a
|
||||
// problem. so solve this problem:
|
||||
core::vector3df up = UpVector;
|
||||
up.normalize();
|
||||
|
||||
f32 dp = tgtv.dotProduct(up);
|
||||
|
||||
if (core::equals(core::abs_<f32>(dp), 1.f)) {
|
||||
up.X += 0.5f;
|
||||
}
|
||||
|
||||
ViewArea.getTransform(video::ETS_VIEW).buildCameraLookAtMatrixLH(pos, Target, up);
|
||||
ViewArea.getTransform(video::ETS_VIEW) *= Affector;
|
||||
recalculateViewArea();
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CCameraSceneNode::getBoundingBox() const
|
||||
{
|
||||
return BoundingBox;
|
||||
}
|
||||
|
||||
//! returns the view frustum.
|
||||
const SViewFrustum *CCameraSceneNode::getViewFrustum() const
|
||||
{
|
||||
return &ViewArea;
|
||||
}
|
||||
|
||||
void CCameraSceneNode::recalculateViewArea()
|
||||
{
|
||||
ViewArea.cameraPosition = getAbsolutePosition();
|
||||
|
||||
core::matrix4 m(core::matrix4::EM4CONST_NOTHING);
|
||||
m.setbyproduct_nocheck(ViewArea.getTransform(video::ETS_PROJECTION),
|
||||
ViewArea.getTransform(video::ETS_VIEW));
|
||||
ViewArea.setFrom(m, false);
|
||||
}
|
||||
|
||||
//! Set the binding between the camera's rotation adn target.
|
||||
void CCameraSceneNode::bindTargetAndRotation(bool bound)
|
||||
{
|
||||
TargetAndRotationAreBound = bound;
|
||||
}
|
||||
|
||||
//! Gets the binding between the camera's rotation and target.
|
||||
bool CCameraSceneNode::getTargetAndRotationBinding(void) const
|
||||
{
|
||||
return TargetAndRotationAreBound;
|
||||
}
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *CCameraSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||
{
|
||||
ICameraSceneNode::clone(newParent, newManager);
|
||||
|
||||
if (!newParent)
|
||||
newParent = Parent;
|
||||
if (!newManager)
|
||||
newManager = SceneManager;
|
||||
|
||||
CCameraSceneNode *nb = new CCameraSceneNode(newParent,
|
||||
newManager, ID, RelativeTranslation, Target);
|
||||
|
||||
nb->ISceneNode::cloneMembers(this, newManager);
|
||||
nb->ICameraSceneNode::cloneMembers(this);
|
||||
|
||||
nb->Target = Target;
|
||||
nb->UpVector = UpVector;
|
||||
nb->Fovy = Fovy;
|
||||
nb->Aspect = Aspect;
|
||||
nb->ZNear = ZNear;
|
||||
nb->ZFar = ZFar;
|
||||
nb->ViewArea = ViewArea;
|
||||
nb->Affector = Affector;
|
||||
nb->InputReceiverEnabled = InputReceiverEnabled;
|
||||
nb->TargetAndRotationAreBound = TargetAndRotationAreBound;
|
||||
|
||||
if (newParent)
|
||||
nb->drop();
|
||||
return nb;
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
} // end namespace
|
||||
165
src/CCameraSceneNode.h
Normal file
165
src/CCameraSceneNode.h
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ICameraSceneNode.h"
|
||||
#include "SViewFrustum.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class CCameraSceneNode : public ICameraSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CCameraSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
||||
const core::vector3df &position = core::vector3df(0, 0, 0),
|
||||
const core::vector3df &lookat = core::vector3df(0, 0, 100));
|
||||
|
||||
//! Sets the projection matrix of the camera.
|
||||
/** The core::matrix4 class has some methods
|
||||
to build a projection matrix. e.g: core::matrix4::buildProjectionMatrixPerspectiveFovLH.
|
||||
Note that the matrix will only stay as set by this method until one of
|
||||
the following Methods are called: setNearValue, setFarValue, setAspectRatio, setFOV.
|
||||
\param projection The new projection matrix of the camera.
|
||||
\param isOrthogonal Set this to true if the matrix is an orthogonal one (e.g.
|
||||
from matrix4::buildProjectionMatrixOrthoLH(). */
|
||||
void setProjectionMatrix(const core::matrix4 &projection, bool isOrthogonal = false) override;
|
||||
|
||||
//! Gets the current projection matrix of the camera
|
||||
//! \return Returns the current projection matrix of the camera.
|
||||
const core::matrix4 &getProjectionMatrix() const override;
|
||||
|
||||
//! Gets the current view matrix of the camera
|
||||
//! \return Returns the current view matrix of the camera.
|
||||
const core::matrix4 &getViewMatrix() const override;
|
||||
|
||||
//! Sets a custom view matrix affector.
|
||||
/** \param affector: The affector matrix. */
|
||||
void setViewMatrixAffector(const core::matrix4 &affector) override;
|
||||
|
||||
//! Gets the custom view matrix affector.
|
||||
const core::matrix4 &getViewMatrixAffector() const override;
|
||||
|
||||
//! It is possible to send mouse and key events to the camera. Most cameras
|
||||
//! may ignore this input, but camera scene nodes which are created for
|
||||
//! example with scene::ISceneManager::addMayaCameraSceneNode or
|
||||
//! scene::ISceneManager::addMeshViewerCameraSceneNode, may want to get this input
|
||||
//! for changing their position, look at target or whatever.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! Sets the look at target of the camera
|
||||
/** If the camera's target and rotation are bound ( @see bindTargetAndRotation() )
|
||||
then calling this will also change the camera's scene node rotation to match the target.
|
||||
\param pos: Look at target of the camera. */
|
||||
void setTarget(const core::vector3df &pos) override;
|
||||
|
||||
//! Sets the rotation of the node.
|
||||
/** This only modifies the relative rotation of the node.
|
||||
If the camera's target and rotation are bound ( @see bindTargetAndRotation() )
|
||||
then calling this will also change the camera's target to match the rotation.
|
||||
\param rotation New rotation of the node in degrees. */
|
||||
void setRotation(const core::vector3df &rotation) override;
|
||||
|
||||
//! Gets the current look at target of the camera
|
||||
/** \return The current look at target of the camera */
|
||||
const core::vector3df &getTarget() const override;
|
||||
|
||||
//! Sets the up vector of the camera.
|
||||
//! \param pos: New upvector of the camera.
|
||||
void setUpVector(const core::vector3df &pos) override;
|
||||
|
||||
//! Gets the up vector of the camera.
|
||||
//! \return Returns the up vector of the camera.
|
||||
const core::vector3df &getUpVector() const override;
|
||||
|
||||
//! Gets distance from the camera to the near plane.
|
||||
//! \return Value of the near plane of the camera.
|
||||
f32 getNearValue() const override;
|
||||
|
||||
//! Gets the distance from the camera to the far plane.
|
||||
//! \return Value of the far plane of the camera.
|
||||
f32 getFarValue() const override;
|
||||
|
||||
//! Get the aspect ratio of the camera.
|
||||
//! \return The aspect ratio of the camera.
|
||||
f32 getAspectRatio() const override;
|
||||
|
||||
//! Gets the field of view of the camera.
|
||||
//! \return Field of view of the camera
|
||||
f32 getFOV() const override;
|
||||
|
||||
//! Sets the value of the near clipping plane. (default: 1.0f)
|
||||
void setNearValue(f32 zn) override;
|
||||
|
||||
//! Sets the value of the far clipping plane (default: 2000.0f)
|
||||
void setFarValue(f32 zf) override;
|
||||
|
||||
//! Sets the aspect ratio (default: 4.0f / 3.0f)
|
||||
void setAspectRatio(f32 aspect) override;
|
||||
|
||||
//! Sets the field of view (Default: PI / 3.5f)
|
||||
void setFOV(f32 fovy) override;
|
||||
|
||||
//! PreRender event
|
||||
void OnRegisterSceneNode() override;
|
||||
|
||||
//! Render
|
||||
void render() override;
|
||||
|
||||
//! Update
|
||||
void updateMatrices() override;
|
||||
|
||||
//! Returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
//! Returns the view area.
|
||||
const SViewFrustum *getViewFrustum() const override;
|
||||
|
||||
//! Disables or enables the camera to get key or mouse inputs.
|
||||
//! If this is set to true, the camera will respond to key inputs
|
||||
//! otherwise not.
|
||||
void setInputReceiverEnabled(bool enabled) override;
|
||||
|
||||
//! Returns if the input receiver of the camera is currently enabled.
|
||||
bool isInputReceiverEnabled() const override;
|
||||
|
||||
//! Returns type of the scene node
|
||||
ESCENE_NODE_TYPE getType() const override { return ESNT_CAMERA; }
|
||||
|
||||
//! Binds the camera scene node's rotation to its target position and vice versa, or unbinds them.
|
||||
void bindTargetAndRotation(bool bound) override;
|
||||
|
||||
//! Queries if the camera scene node's rotation and its target position are bound together.
|
||||
bool getTargetAndRotationBinding(void) const override;
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
|
||||
|
||||
protected:
|
||||
void recalculateProjectionMatrix();
|
||||
void recalculateViewArea();
|
||||
|
||||
core::aabbox3d<f32> BoundingBox;
|
||||
|
||||
core::vector3df Target;
|
||||
core::vector3df UpVector;
|
||||
|
||||
f32 Fovy; // Field of view, in radians.
|
||||
f32 Aspect; // Aspect ratio.
|
||||
f32 ZNear; // value of the near view-plane.
|
||||
f32 ZFar; // Z-value of the far view-plane.
|
||||
|
||||
SViewFrustum ViewArea;
|
||||
core::matrix4 Affector;
|
||||
|
||||
bool InputReceiverEnabled;
|
||||
bool TargetAndRotationAreBound;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
} // end namespace
|
||||
760
src/CColorConverter.cpp
Normal file
760
src/CColorConverter.cpp
Normal file
@@ -0,0 +1,760 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CColorConverter.h"
|
||||
#include "SColor.h"
|
||||
#include "os.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! converts a monochrome bitmap to A1R5G5B5 data
|
||||
void CColorConverter::convert1BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
if (flip)
|
||||
out += width * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
s32 shift = 7;
|
||||
if (flip)
|
||||
out -= width;
|
||||
|
||||
for (s32 x = 0; x < width; ++x) {
|
||||
out[x] = *in >> shift & 0x01 ? (s16)0xffff : (s16)0x8000;
|
||||
|
||||
if ((--shift) < 0) { // 8 pixel done
|
||||
shift = 7;
|
||||
++in;
|
||||
}
|
||||
}
|
||||
|
||||
if (shift != 7) // width did not fill last byte
|
||||
++in;
|
||||
|
||||
if (!flip)
|
||||
out += width;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! converts a 4 bit palettized image to A1R5G5B5
|
||||
void CColorConverter::convert4BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, const s32 *palette, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out || !palette)
|
||||
return;
|
||||
|
||||
if (flip)
|
||||
out += width * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
s32 shift = 4;
|
||||
if (flip)
|
||||
out -= width;
|
||||
|
||||
for (s32 x = 0; x < width; ++x) {
|
||||
out[x] = X8R8G8B8toA1R5G5B5(palette[(u8)((*in >> shift) & 0xf)]);
|
||||
|
||||
if (shift == 0) {
|
||||
shift = 4;
|
||||
++in;
|
||||
} else
|
||||
shift = 0;
|
||||
}
|
||||
|
||||
if (shift == 0) // odd width
|
||||
++in;
|
||||
|
||||
if (!flip)
|
||||
out += width;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! converts a 8 bit palettized image into A1R5G5B5
|
||||
void CColorConverter::convert8BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, const s32 *palette, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out || !palette)
|
||||
return;
|
||||
|
||||
if (flip)
|
||||
out += width * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
if (flip)
|
||||
out -= width; // one line back
|
||||
for (s32 x = 0; x < width; ++x) {
|
||||
out[x] = X8R8G8B8toA1R5G5B5(palette[(u8)(*in)]);
|
||||
++in;
|
||||
}
|
||||
if (!flip)
|
||||
out += width;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! converts a 8 bit palettized or non palettized image (A8) into R8G8B8
|
||||
void CColorConverter::convert8BitTo24Bit(const u8 *in, u8 *out, s32 width, s32 height, const u8 *palette, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
const s32 lineWidth = 3 * width;
|
||||
if (flip)
|
||||
out += lineWidth * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
if (flip)
|
||||
out -= lineWidth; // one line back
|
||||
for (s32 x = 0; x < lineWidth; x += 3) {
|
||||
if (palette) {
|
||||
#ifdef __BIG_ENDIAN__
|
||||
out[x + 0] = palette[(in[0] << 2) + 0];
|
||||
out[x + 1] = palette[(in[0] << 2) + 1];
|
||||
out[x + 2] = palette[(in[0] << 2) + 2];
|
||||
#else
|
||||
out[x + 0] = palette[(in[0] << 2) + 2];
|
||||
out[x + 1] = palette[(in[0] << 2) + 1];
|
||||
out[x + 2] = palette[(in[0] << 2) + 0];
|
||||
#endif
|
||||
} else {
|
||||
out[x + 0] = in[0];
|
||||
out[x + 1] = in[0];
|
||||
out[x + 2] = in[0];
|
||||
}
|
||||
++in;
|
||||
}
|
||||
if (!flip)
|
||||
out += lineWidth;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! converts a 8 bit palettized or non palettized image (A8) into R8G8B8
|
||||
void CColorConverter::convert8BitTo32Bit(const u8 *in, u8 *out, s32 width, s32 height, const u8 *palette, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
const u32 lineWidth = 4 * width;
|
||||
if (flip)
|
||||
out += lineWidth * height;
|
||||
|
||||
u32 x;
|
||||
u32 c;
|
||||
for (u32 y = 0; y < (u32)height; ++y) {
|
||||
if (flip)
|
||||
out -= lineWidth; // one line back
|
||||
|
||||
if (palette) {
|
||||
for (x = 0; x < (u32)width; x += 1) {
|
||||
c = in[x];
|
||||
((u32 *)out)[x] = ((u32 *)palette)[c];
|
||||
}
|
||||
} else {
|
||||
for (x = 0; x < (u32)width; x += 1) {
|
||||
c = in[x];
|
||||
#ifdef __BIG_ENDIAN__
|
||||
((u32 *)out)[x] = c << 24 | c << 16 | c << 8 | 0x000000FF;
|
||||
#else
|
||||
((u32 *)out)[x] = 0xFF000000 | c << 16 | c << 8 | c;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!flip)
|
||||
out += lineWidth;
|
||||
in += width + linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! converts 16bit data to 16bit data
|
||||
void CColorConverter::convert16BitTo16Bit(const s16 *in, s16 *out, s32 width, s32 height, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
if (flip)
|
||||
out += width * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
if (flip)
|
||||
out -= width;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
for (s32 x = 0; x < width; ++x)
|
||||
out[x] = os::Byteswap::byteswap(in[x]);
|
||||
#else
|
||||
memcpy(out, in, width * sizeof(s16));
|
||||
#endif
|
||||
if (!flip)
|
||||
out += width;
|
||||
in += width;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! copies R8G8B8 24bit data to 24bit data
|
||||
void CColorConverter::convert24BitTo24Bit(const u8 *in, u8 *out, s32 width, s32 height, s32 linepad, bool flip, bool bgr)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
const s32 lineWidth = 3 * width;
|
||||
if (flip)
|
||||
out += lineWidth * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
if (flip)
|
||||
out -= lineWidth;
|
||||
if (bgr) {
|
||||
for (s32 x = 0; x < lineWidth; x += 3) {
|
||||
out[x + 0] = in[x + 2];
|
||||
out[x + 1] = in[x + 1];
|
||||
out[x + 2] = in[x + 0];
|
||||
}
|
||||
} else {
|
||||
memcpy(out, in, lineWidth);
|
||||
}
|
||||
if (!flip)
|
||||
out += lineWidth;
|
||||
in += lineWidth;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
//! Resizes the surface to a new size and converts it at the same time
|
||||
//! to an A8R8G8B8 format, returning the pointer to the new buffer.
|
||||
void CColorConverter::convert16bitToA8R8G8B8andResize(const s16 *in, s32 *out, s32 newWidth, s32 newHeight, s32 currentWidth, s32 currentHeight)
|
||||
{
|
||||
if (!newWidth || !newHeight)
|
||||
return;
|
||||
|
||||
// note: this is very very slow. (i didn't want to write a fast version.
|
||||
// but hopefully, nobody wants to convert surfaces every frame.
|
||||
|
||||
f32 sourceXStep = (f32)currentWidth / (f32)newWidth;
|
||||
f32 sourceYStep = (f32)currentHeight / (f32)newHeight;
|
||||
f32 sy;
|
||||
s32 t;
|
||||
|
||||
for (s32 x = 0; x < newWidth; ++x) {
|
||||
sy = 0.0f;
|
||||
|
||||
for (s32 y = 0; y < newHeight; ++y) {
|
||||
t = in[(s32)(((s32)sy) * currentWidth + x * sourceXStep)];
|
||||
t = (((t >> 15) & 0x1) << 31) | (((t >> 10) & 0x1F) << 19) |
|
||||
(((t >> 5) & 0x1F) << 11) | (t & 0x1F) << 3;
|
||||
out[(s32)(y * newWidth + x)] = t;
|
||||
|
||||
sy += sourceYStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! copies X8R8G8B8 32 bit data
|
||||
void CColorConverter::convert32BitTo32Bit(const s32 *in, s32 *out, s32 width, s32 height, s32 linepad, bool flip)
|
||||
{
|
||||
if (!in || !out)
|
||||
return;
|
||||
|
||||
if (flip)
|
||||
out += width * height;
|
||||
|
||||
for (s32 y = 0; y < height; ++y) {
|
||||
if (flip)
|
||||
out -= width;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
for (s32 x = 0; x < width; ++x)
|
||||
out[x] = os::Byteswap::byteswap(in[x]);
|
||||
#else
|
||||
memcpy(out, in, width * sizeof(s32));
|
||||
#endif
|
||||
if (!flip)
|
||||
out += width;
|
||||
in += width;
|
||||
in += linepad;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toR8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[2] = (*sB & 0x7c00) >> 7;
|
||||
dB[1] = (*sB & 0x03e0) >> 2;
|
||||
dB[0] = (*sB & 0x1f) << 3;
|
||||
|
||||
sB += 1;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toB8G8R8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[0] = (*sB & 0x7c00) >> 7;
|
||||
dB[1] = (*sB & 0x03e0) >> 2;
|
||||
dB[2] = (*sB & 0x1f) << 3;
|
||||
|
||||
sB += 1;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toR5G5B5A1(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
const u16 *sB = (const u16 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
*dB = (*sB << 1) | (*sB >> 15);
|
||||
++sB;
|
||||
++dB;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x)
|
||||
*dB++ = A1R5G5B5toA8R8G8B8(*sB++);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toA1R5G5B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
memcpy(dP, sP, sN * 2);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A1R5G5B5toR5G6B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x)
|
||||
*dB++ = A1R5G5B5toR5G6B5(*sB++);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toR8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
// sB[3] is alpha
|
||||
dB[0] = sB[2];
|
||||
dB[1] = sB[1];
|
||||
dB[2] = sB[0];
|
||||
|
||||
sB += 4;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
// sB[3] is alpha
|
||||
dB[0] = sB[0];
|
||||
dB[1] = sB[1];
|
||||
dB[2] = sB[2];
|
||||
|
||||
sB += 4;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
memcpy(dP, sP, sN * 4);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u32 *sB = (u32 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x)
|
||||
*dB++ = A8R8G8B8toA1R5G5B5(*sB++);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toA1B5G5R5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
s32 r = sB[0] >> 3;
|
||||
s32 g = sB[1] >> 3;
|
||||
s32 b = sB[2] >> 3;
|
||||
s32 a = sB[3] >> 3;
|
||||
|
||||
dB[0] = (a << 15) | (r << 10) | (g << 5) | (b);
|
||||
|
||||
sB += 4;
|
||||
dB += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toR5G6B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
s32 r = sB[2] >> 3;
|
||||
s32 g = sB[1] >> 2;
|
||||
s32 b = sB[0] >> 3;
|
||||
|
||||
dB[0] = (r << 11) | (g << 5) | (b);
|
||||
|
||||
sB += 4;
|
||||
dB += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toR3G3B2(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
u8 r = sB[2] & 0xe0;
|
||||
u8 g = (sB[1] & 0xe0) >> 3;
|
||||
u8 b = (sB[0] & 0xc0) >> 6;
|
||||
|
||||
dB[0] = (r | g | b);
|
||||
|
||||
sB += 4;
|
||||
dB += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R8G8B8toR8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
memcpy(dP, sP, sN * 3);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R8G8B8toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
*dB = 0xff000000 | (sB[0] << 16) | (sB[1] << 8) | sB[2];
|
||||
|
||||
sB += 3;
|
||||
++dB;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
s32 r = sB[0] >> 3;
|
||||
s32 g = sB[1] >> 3;
|
||||
s32 b = sB[2] >> 3;
|
||||
|
||||
dB[0] = (0x8000) | (r << 10) | (g << 5) | (b);
|
||||
|
||||
sB += 3;
|
||||
dB += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_B8G8R8toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
*dB = 0xff000000 | (sB[2] << 16) | (sB[1] << 8) | sB[0];
|
||||
|
||||
sB += 3;
|
||||
++dB;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toR8G8B8A8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
const u32 *sB = (const u32 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
*dB++ = (*sB << 8) | (*sB >> 24);
|
||||
++sB;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_A8R8G8B8toA8B8G8R8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
const u32 *sB = (const u32 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
*dB++ = (*sB & 0xff00ff00) | ((*sB & 0x00ff0000) >> 16) | ((*sB & 0x000000ff) << 16);
|
||||
++sB;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_B8G8R8A8toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[0] = sB[3];
|
||||
dB[1] = sB[2];
|
||||
dB[2] = sB[1];
|
||||
dB[3] = sB[0];
|
||||
|
||||
sB += 4;
|
||||
dB += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[2] = sB[0];
|
||||
dB[1] = sB[1];
|
||||
dB[0] = sB[2];
|
||||
|
||||
sB += 3;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R8G8B8toR5G6B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u8 *sB = (u8 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
s32 r = sB[0] >> 3;
|
||||
s32 g = sB[1] >> 2;
|
||||
s32 b = sB[2] >> 3;
|
||||
|
||||
dB[0] = (r << 11) | (g << 5) | (b);
|
||||
|
||||
sB += 3;
|
||||
dB += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R5G6B5toR5G6B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
memcpy(dP, sP, sN * 2);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R5G6B5toR8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[0] = (*sB & 0xf800) >> 8;
|
||||
dB[1] = (*sB & 0x07e0) >> 3;
|
||||
dB[2] = (*sB & 0x001f) << 3;
|
||||
|
||||
sB += 1;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R5G6B5toB8G8R8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u8 *dB = (u8 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x) {
|
||||
dB[2] = (*sB & 0xf800) >> 8;
|
||||
dB[1] = (*sB & 0x07e0) >> 3;
|
||||
dB[0] = (*sB & 0x001f) << 3;
|
||||
|
||||
sB += 1;
|
||||
dB += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R5G6B5toA8R8G8B8(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u32 *dB = (u32 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x)
|
||||
*dB++ = R5G6B5toA8R8G8B8(*sB++);
|
||||
}
|
||||
|
||||
void CColorConverter::convert_R5G6B5toA1R5G5B5(const void *sP, s32 sN, void *dP)
|
||||
{
|
||||
u16 *sB = (u16 *)sP;
|
||||
u16 *dB = (u16 *)dP;
|
||||
|
||||
for (s32 x = 0; x < sN; ++x)
|
||||
*dB++ = R5G6B5toA1R5G5B5(*sB++);
|
||||
}
|
||||
|
||||
bool CColorConverter::canConvertFormat(ECOLOR_FORMAT sourceFormat, ECOLOR_FORMAT destFormat)
|
||||
{
|
||||
switch (sourceFormat) {
|
||||
case ECF_A1R5G5B5:
|
||||
switch (destFormat) {
|
||||
case ECF_A1R5G5B5:
|
||||
case ECF_R5G6B5:
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_R8G8B8:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
switch (destFormat) {
|
||||
case ECF_A1R5G5B5:
|
||||
case ECF_R5G6B5:
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_R8G8B8:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
switch (destFormat) {
|
||||
case ECF_A1R5G5B5:
|
||||
case ECF_R5G6B5:
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_R8G8B8:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
switch (destFormat) {
|
||||
case ECF_A1R5G5B5:
|
||||
case ECF_R5G6B5:
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_R8G8B8:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CColorConverter::convert_viaFormat(const void *sP, ECOLOR_FORMAT sF, s32 sN,
|
||||
void *dP, ECOLOR_FORMAT dF)
|
||||
{
|
||||
// please also update can_convert_viaFormat when adding new conversions
|
||||
switch (sF) {
|
||||
case ECF_A1R5G5B5:
|
||||
switch (dF) {
|
||||
case ECF_A1R5G5B5:
|
||||
convert_A1R5G5B5toA1R5G5B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
convert_A1R5G5B5toR5G6B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
convert_A1R5G5B5toA8R8G8B8(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
convert_A1R5G5B5toR8G8B8(sP, sN, dP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
switch (dF) {
|
||||
case ECF_A1R5G5B5:
|
||||
convert_R5G6B5toA1R5G5B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
convert_R5G6B5toR5G6B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
convert_R5G6B5toA8R8G8B8(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
convert_R5G6B5toR8G8B8(sP, sN, dP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
switch (dF) {
|
||||
case ECF_A1R5G5B5:
|
||||
convert_A8R8G8B8toA1R5G5B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
convert_A8R8G8B8toR5G6B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
convert_A8R8G8B8toA8R8G8B8(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
convert_A8R8G8B8toR8G8B8(sP, sN, dP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
switch (dF) {
|
||||
case ECF_A1R5G5B5:
|
||||
convert_R8G8B8toA1R5G5B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
convert_R8G8B8toR5G6B5(sP, sN, dP);
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
convert_R8G8B8toA8R8G8B8(sP, sN, dP);
|
||||
break;
|
||||
case ECF_R8G8B8:
|
||||
convert_R8G8B8toR8G8B8(sP, sN, dP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
94
src/CColorConverter.h
Normal file
94
src/CColorConverter.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrTypes.h"
|
||||
#include "IImage.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class CColorConverter
|
||||
{
|
||||
public:
|
||||
//! converts a monochrome bitmap to A1R5G5B5
|
||||
static void convert1BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! converts a 4 bit palettized image to A1R5G5B5
|
||||
static void convert4BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, const s32 *palette, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! converts a 8 bit palettized image to A1R5G5B5
|
||||
static void convert8BitTo16Bit(const u8 *in, s16 *out, s32 width, s32 height, const s32 *palette, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! converts a 8 bit palettized or non palettized image (A8) into R8G8B8
|
||||
static void convert8BitTo24Bit(const u8 *in, u8 *out, s32 width, s32 height, const u8 *palette, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! converts a 8 bit palettized or non palettized image (A8) into A8R8G8B8
|
||||
static void convert8BitTo32Bit(const u8 *in, u8 *out, s32 width, s32 height, const u8 *palette, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! converts R8G8B8 16 bit data to A1R5G5B5 data
|
||||
static void convert16BitTo16Bit(const s16 *in, s16 *out, s32 width, s32 height, s32 linepad = 0, bool flip = false);
|
||||
|
||||
//! copies R8G8B8 24 bit data to 24 data, and flips and
|
||||
//! mirrors the image during the process.
|
||||
static void convert24BitTo24Bit(const u8 *in, u8 *out, s32 width, s32 height, s32 linepad = 0, bool flip = false, bool bgr = false);
|
||||
|
||||
//! Resizes the surface to a new size and converts it at the same time
|
||||
//! to an A8R8G8B8 format, returning the pointer to the new buffer.
|
||||
static void convert16bitToA8R8G8B8andResize(const s16 *in, s32 *out, s32 newWidth, s32 newHeight, s32 currentWidth, s32 currentHeight);
|
||||
|
||||
//! copies X8R8G8B8 32 bit data, and flips and
|
||||
//! mirrors the image during the process.
|
||||
static void convert32BitTo32Bit(const s32 *in, s32 *out, s32 width, s32 height, s32 linepad, bool flip = false);
|
||||
|
||||
//! Functions for converting one image format to another efficiently
|
||||
//! and hopefully correctly.
|
||||
//! Note that the format descriptions refer to the ECOLOR_FORMAT's and not to memory layout.
|
||||
//! Reasons for that go back to DX9 naming which tended to flip 32-bit and 16-bit byte orders so ARGB usually means BGRA.
|
||||
//!
|
||||
//! \param sP pointer to source pixel data
|
||||
//! \param sN number of source pixels to copy
|
||||
//! \param dP pointer to destination data buffer. must be big enough
|
||||
//! to hold sN pixels in the output format.
|
||||
static void convert_A1R5G5B5toR8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A1R5G5B5toB8G8R8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A1R5G5B5toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A1R5G5B5toA1R5G5B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A1R5G5B5toR5G5B5A1(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A1R5G5B5toR5G6B5(const void *sP, s32 sN, void *dP);
|
||||
|
||||
static void convert_A8R8G8B8toR8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toA1B5G5R5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toR5G6B5(const void *sP, s32 sN, void *dP);
|
||||
|
||||
static void convert_A8R8G8B8toR3G3B2(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R8G8B8toR8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R8G8B8toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R8G8B8toR5G6B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_B8G8R8toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_B8G8R8A8toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toR8G8B8A8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_A8R8G8B8toA8B8G8R8(const void *sP, s32 sN, void *dP);
|
||||
|
||||
static void convert_R5G6B5toR5G6B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R5G6B5toR8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R5G6B5toB8G8R8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R5G6B5toA8R8G8B8(const void *sP, s32 sN, void *dP);
|
||||
static void convert_R5G6B5toA1R5G5B5(const void *sP, s32 sN, void *dP);
|
||||
static void convert_viaFormat(const void *sP, ECOLOR_FORMAT sF, s32 sN,
|
||||
void *dP, ECOLOR_FORMAT dF);
|
||||
// Check if convert_viaFormat is usable
|
||||
static bool canConvertFormat(ECOLOR_FORMAT sourceFormat, ECOLOR_FORMAT destFormat);
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
102
src/CDummyTransformationSceneNode.cpp
Normal file
102
src/CDummyTransformationSceneNode.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CDummyTransformationSceneNode.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CDummyTransformationSceneNode::CDummyTransformationSceneNode(
|
||||
ISceneNode *parent, ISceneManager *mgr, s32 id) :
|
||||
IDummyTransformationSceneNode(parent, mgr, id)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CDummyTransformationSceneNode");
|
||||
#endif
|
||||
|
||||
setAutomaticCulling(scene::EAC_OFF);
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CDummyTransformationSceneNode::getBoundingBox() const
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! Returns a reference to the current relative transformation matrix.
|
||||
//! This is the matrix, this scene node uses instead of scale, translation
|
||||
//! and rotation.
|
||||
core::matrix4 &CDummyTransformationSceneNode::getRelativeTransformationMatrix()
|
||||
{
|
||||
return RelativeTransformationMatrix;
|
||||
}
|
||||
|
||||
//! Returns the relative transformation of the scene node.
|
||||
core::matrix4 CDummyTransformationSceneNode::getRelativeTransformation() const
|
||||
{
|
||||
return RelativeTransformationMatrix;
|
||||
}
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *CDummyTransformationSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||
{
|
||||
if (!newParent)
|
||||
newParent = Parent;
|
||||
if (!newManager)
|
||||
newManager = SceneManager;
|
||||
|
||||
CDummyTransformationSceneNode *nb = new CDummyTransformationSceneNode(newParent,
|
||||
newManager, ID);
|
||||
|
||||
nb->cloneMembers(this, newManager);
|
||||
nb->RelativeTransformationMatrix = RelativeTransformationMatrix;
|
||||
nb->Box = Box;
|
||||
|
||||
if (newParent)
|
||||
nb->drop();
|
||||
return nb;
|
||||
}
|
||||
|
||||
const core::vector3df &CDummyTransformationSceneNode::getScale() const
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::getScale() does not contain the relative transformation.", ELL_DEBUG);
|
||||
return RelativeScale;
|
||||
}
|
||||
|
||||
void CDummyTransformationSceneNode::setScale(const core::vector3df &scale)
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::setScale() does not affect the relative transformation.", ELL_DEBUG);
|
||||
RelativeScale = scale;
|
||||
}
|
||||
|
||||
const core::vector3df &CDummyTransformationSceneNode::getRotation() const
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::getRotation() does not contain the relative transformation.", ELL_DEBUG);
|
||||
return RelativeRotation;
|
||||
}
|
||||
|
||||
void CDummyTransformationSceneNode::setRotation(const core::vector3df &rotation)
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::setRotation() does not affect the relative transformation.", ELL_DEBUG);
|
||||
RelativeRotation = rotation;
|
||||
}
|
||||
|
||||
const core::vector3df &CDummyTransformationSceneNode::getPosition() const
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::getPosition() does not contain the relative transformation.", ELL_DEBUG);
|
||||
return RelativeTranslation;
|
||||
}
|
||||
|
||||
void CDummyTransformationSceneNode::setPosition(const core::vector3df &newpos)
|
||||
{
|
||||
os::Printer::log("CDummyTransformationSceneNode::setPosition() does not affect the relative transformation.", ELL_DEBUG);
|
||||
RelativeTranslation = newpos;
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
55
src/CDummyTransformationSceneNode.h
Normal file
55
src/CDummyTransformationSceneNode.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IDummyTransformationSceneNode.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class CDummyTransformationSceneNode : public IDummyTransformationSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CDummyTransformationSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id);
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
//! Returns a reference to the current relative transformation matrix.
|
||||
//! This is the matrix, this scene node uses instead of scale, translation
|
||||
//! and rotation.
|
||||
core::matrix4 &getRelativeTransformationMatrix() override;
|
||||
|
||||
//! Returns the relative transformation of the scene node.
|
||||
core::matrix4 getRelativeTransformation() const override;
|
||||
|
||||
//! does nothing.
|
||||
void render() override {}
|
||||
|
||||
//! Returns type of the scene node
|
||||
ESCENE_NODE_TYPE getType() const override { return ESNT_DUMMY_TRANSFORMATION; }
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
|
||||
|
||||
private:
|
||||
// TODO: We can add least add some warnings to find troubles faster until we have
|
||||
// fixed bug id 2318691.
|
||||
const core::vector3df &getScale() const override;
|
||||
void setScale(const core::vector3df &scale) override;
|
||||
const core::vector3df &getRotation() const override;
|
||||
void setRotation(const core::vector3df &rotation) override;
|
||||
const core::vector3df &getPosition() const override;
|
||||
void setPosition(const core::vector3df &newpos) override;
|
||||
|
||||
core::matrix4 RelativeTransformationMatrix;
|
||||
core::aabbox3d<f32> Box;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
611
src/CEGLManager.cpp
Normal file
611
src/CEGLManager.cpp
Normal file
@@ -0,0 +1,611 @@
|
||||
// Copyright (C) 2013 Patryk Nadrowski
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
||||
|
||||
#include "CEGLManager.h"
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_EGL_MANAGER_
|
||||
|
||||
#include "irrString.h"
|
||||
#include "irrArray.h"
|
||||
#include "os.h"
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
|
||||
#include <android/native_activity.h>
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
CEGLManager::CEGLManager() :
|
||||
IContextManager(), EglWindow(0), EglDisplay(EGL_NO_DISPLAY),
|
||||
EglSurface(EGL_NO_SURFACE), EglContext(EGL_NO_CONTEXT), EglConfig(0), MajorVersion(0), MinorVersion(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CEGLManager");
|
||||
#endif
|
||||
}
|
||||
|
||||
CEGLManager::~CEGLManager()
|
||||
{
|
||||
destroyContext();
|
||||
destroySurface();
|
||||
terminate();
|
||||
}
|
||||
|
||||
bool CEGLManager::initialize(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &data)
|
||||
{
|
||||
// store new data
|
||||
Params = params;
|
||||
Data = data;
|
||||
|
||||
if (EglWindow != 0 && EglDisplay != EGL_NO_DISPLAY)
|
||||
return true;
|
||||
|
||||
// Window is depend on platform.
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
EglWindow = (NativeWindowType)Data.OpenGLWin32.HWnd;
|
||||
Data.OpenGLWin32.HDc = GetDC((HWND)EglWindow);
|
||||
EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLWin32.HDc);
|
||||
#elif defined(_IRR_EMSCRIPTEN_PLATFORM_)
|
||||
EglWindow = 0;
|
||||
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
|
||||
EglWindow = (NativeWindowType)Data.OpenGLLinux.X11Window;
|
||||
EglDisplay = eglGetDisplay((NativeDisplayType)Data.OpenGLLinux.X11Display);
|
||||
#elif defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
|
||||
EglWindow = (ANativeWindow *)Data.OGLESAndroid.Window;
|
||||
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
#elif defined(_IRR_COMPILE_WITH_FB_DEVICE_)
|
||||
EglWindow = (NativeWindowType)Data.OpenGLFB.Window;
|
||||
EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
#endif
|
||||
|
||||
// We must check if EGL display is valid.
|
||||
if (EglDisplay == EGL_NO_DISPLAY) {
|
||||
os::Printer::log("Could not get EGL display.");
|
||||
terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize EGL here.
|
||||
if (!eglInitialize(EglDisplay, &MajorVersion, &MinorVersion)) {
|
||||
os::Printer::log("Could not initialize EGL display.");
|
||||
|
||||
EglDisplay = EGL_NO_DISPLAY;
|
||||
terminate();
|
||||
return false;
|
||||
} else
|
||||
os::Printer::log("EGL version", core::stringc(MajorVersion + (MinorVersion * 0.1f)).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEGLManager::terminate()
|
||||
{
|
||||
if (EglWindow == 0 && EglDisplay == EGL_NO_DISPLAY)
|
||||
return;
|
||||
|
||||
if (EglDisplay != EGL_NO_DISPLAY) {
|
||||
// We should unbind current EGL context before terminate EGL.
|
||||
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
eglTerminate(EglDisplay);
|
||||
EglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
|
||||
if (Data.OpenGLWin32.HDc) {
|
||||
ReleaseDC((HWND)EglWindow, (HDC)Data.OpenGLWin32.HDc);
|
||||
Data.OpenGLWin32.HDc = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MajorVersion = 0;
|
||||
MinorVersion = 0;
|
||||
}
|
||||
|
||||
bool CEGLManager::generateSurface()
|
||||
{
|
||||
if (EglDisplay == EGL_NO_DISPLAY)
|
||||
return false;
|
||||
|
||||
if (EglSurface != EGL_NO_SURFACE)
|
||||
return true;
|
||||
|
||||
// We should assign new WindowID on platforms, where WindowID may change at runtime,
|
||||
// at this time only Android support this feature.
|
||||
// this needs an update method instead!
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
|
||||
EglWindow = (ANativeWindow *)Data.OGLESAndroid.Window;
|
||||
#endif
|
||||
|
||||
#if defined(_IRR_EMSCRIPTEN_PLATFORM_)
|
||||
// eglChooseConfig is currently only implemented as stub in emscripten (version 1.37.22 at point of writing)
|
||||
// But the other solution would also be fine as it also only generates a single context so there is not much to choose from.
|
||||
EglConfig = chooseConfig(ECS_IRR_CHOOSE);
|
||||
#else
|
||||
EglConfig = chooseConfig(ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS);
|
||||
#endif
|
||||
|
||||
if (EglConfig == 0) {
|
||||
os::Printer::log("Could not get config for EGL display.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
|
||||
EGLint Format = 0;
|
||||
eglGetConfigAttrib(EglDisplay, EglConfig, EGL_NATIVE_VISUAL_ID, &Format);
|
||||
|
||||
ANativeWindow_setBuffersGeometry(EglWindow, 0, 0, Format);
|
||||
#endif
|
||||
|
||||
// Now we are able to create EGL surface.
|
||||
EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, EglWindow, 0);
|
||||
|
||||
if (EGL_NO_SURFACE == EglSurface)
|
||||
EglSurface = eglCreateWindowSurface(EglDisplay, EglConfig, 0, 0);
|
||||
|
||||
if (EGL_NO_SURFACE == EglSurface)
|
||||
os::Printer::log("Could not create EGL surface.");
|
||||
|
||||
#ifdef EGL_VERSION_1_2
|
||||
if (MinorVersion > 1)
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
#endif
|
||||
|
||||
if (Params.Vsync)
|
||||
eglSwapInterval(EglDisplay, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EGLConfig CEGLManager::chooseConfig(EConfigStyle confStyle)
|
||||
{
|
||||
EGLConfig configResult = 0;
|
||||
|
||||
// Find proper OpenGL BIT.
|
||||
EGLint eglOpenGLBIT = 0;
|
||||
switch (Params.DriverType) {
|
||||
case EDT_OGLES1:
|
||||
eglOpenGLBIT = EGL_OPENGL_ES_BIT;
|
||||
break;
|
||||
case EDT_OGLES2:
|
||||
case EDT_WEBGL1:
|
||||
eglOpenGLBIT = EGL_OPENGL_ES2_BIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (confStyle == ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS) {
|
||||
EGLint Attribs[] = {
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, Params.WithAlphaChannel ? 1 : 0,
|
||||
EGL_BUFFER_SIZE, Params.Bits,
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_DEPTH_SIZE, Params.ZBufferBits,
|
||||
EGL_STENCIL_SIZE, Params.Stencilbuffer,
|
||||
EGL_SAMPLE_BUFFERS, Params.AntiAlias ? 1 : 0,
|
||||
EGL_SAMPLES, Params.AntiAlias,
|
||||
#ifdef EGL_VERSION_1_3
|
||||
EGL_RENDERABLE_TYPE, eglOpenGLBIT,
|
||||
#endif
|
||||
EGL_NONE, 0,
|
||||
};
|
||||
|
||||
EGLint numConfigs = 0;
|
||||
u32 steps = 5;
|
||||
|
||||
// Choose the best EGL config.
|
||||
// TODO: We should also have a confStyle ECS_EGL_CHOOSE_CLOSEST
|
||||
// which doesn't take first result of eglChooseConfigs,
|
||||
// but the closest to requested parameters. eglChooseConfigs
|
||||
// can return more than 1 result and first one might have
|
||||
// "better" values than requested (more bits per pixel etc).
|
||||
// So this returns the config which can do most, not the
|
||||
// config which is closest to the requested parameters.
|
||||
//
|
||||
while (!eglChooseConfig(EglDisplay, Attribs, &configResult, 1, &numConfigs) || !numConfigs) {
|
||||
switch (steps) {
|
||||
case 5: // samples
|
||||
if (Attribs[19] > 2) // Params.AntiAlias
|
||||
--Attribs[19];
|
||||
else {
|
||||
Attribs[17] = 0; // Params.Stencilbuffer
|
||||
Attribs[19] = 0; // Params.AntiAlias
|
||||
--steps;
|
||||
}
|
||||
break;
|
||||
case 4: // alpha
|
||||
if (Attribs[7]) { // Params.WithAlphaChannel
|
||||
Attribs[7] = 0;
|
||||
|
||||
if (Params.AntiAlias) {
|
||||
Attribs[17] = 1;
|
||||
Attribs[19] = Params.AntiAlias;
|
||||
steps = 5;
|
||||
}
|
||||
} else
|
||||
--steps;
|
||||
break;
|
||||
case 3: // stencil
|
||||
if (Attribs[15]) { // Params.Stencilbuffer
|
||||
Attribs[15] = 0;
|
||||
|
||||
if (Params.AntiAlias) {
|
||||
Attribs[17] = 1;
|
||||
Attribs[19] = Params.AntiAlias;
|
||||
steps = 5;
|
||||
}
|
||||
} else
|
||||
--steps;
|
||||
break;
|
||||
case 2: // depth size
|
||||
if (Attribs[13] > 16) { // Params.ZBufferBits
|
||||
Attribs[13] -= 8;
|
||||
} else
|
||||
--steps;
|
||||
break;
|
||||
case 1: // buffer size
|
||||
if (Attribs[9] > 16) { // Params.Bits
|
||||
Attribs[9] -= 8;
|
||||
} else
|
||||
--steps;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Params.AntiAlias && !Attribs[17])
|
||||
os::Printer::log("No multisampling.");
|
||||
|
||||
if (Params.WithAlphaChannel && !Attribs[7])
|
||||
os::Printer::log("No alpha.");
|
||||
|
||||
if (Params.Stencilbuffer && !Attribs[15])
|
||||
os::Printer::log("No stencil buffer.");
|
||||
|
||||
if (Params.ZBufferBits > Attribs[13])
|
||||
os::Printer::log("No full depth buffer.");
|
||||
|
||||
if (Params.Bits > Attribs[9])
|
||||
os::Printer::log("No full color buffer.");
|
||||
} else if (confStyle == ECS_IRR_CHOOSE) {
|
||||
// find number of available configs
|
||||
EGLint numConfigs;
|
||||
if (eglGetConfigs(EglDisplay, NULL, 0, &numConfigs) == EGL_FALSE) {
|
||||
testEGLError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (numConfigs <= 0)
|
||||
return 0;
|
||||
|
||||
// Get all available configs.
|
||||
EGLConfig *configs = new EGLConfig[numConfigs];
|
||||
if (eglGetConfigs(EglDisplay, configs, numConfigs, &numConfigs) == EGL_FALSE) {
|
||||
testEGLError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the best one.
|
||||
core::array<SConfigRating> ratings((u32)numConfigs);
|
||||
for (u32 i = 0; i < (u32)numConfigs; ++i) {
|
||||
SConfigRating r;
|
||||
r.config = configs[i];
|
||||
r.rating = rateConfig(r.config, eglOpenGLBIT);
|
||||
|
||||
if (r.rating >= 0)
|
||||
ratings.push_back(r);
|
||||
}
|
||||
|
||||
if (ratings.size() > 0) {
|
||||
ratings.sort();
|
||||
configResult = ratings[0].config;
|
||||
|
||||
if (ratings[0].rating != 0) {
|
||||
// This is just to print some log info (it also rates again while doing that, but rating is cheap enough, so that doesn't matter here).
|
||||
rateConfig(ratings[0].config, eglOpenGLBIT, true);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] configs;
|
||||
}
|
||||
|
||||
return configResult;
|
||||
}
|
||||
|
||||
irr::s32 CEGLManager::rateConfig(EGLConfig config, EGLint eglOpenGLBIT, bool log)
|
||||
{
|
||||
// some values must be there or we ignore the config
|
||||
#ifdef EGL_VERSION_1_3
|
||||
EGLint attribRenderableType = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_RENDERABLE_TYPE, &attribRenderableType);
|
||||
if (attribRenderableType != eglOpenGLBIT) {
|
||||
if (log)
|
||||
os::Printer::log("EGL_RENDERABLE_TYPE != eglOpenGLBIT");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
EGLint attribSurfaceType = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_SURFACE_TYPE, &attribSurfaceType);
|
||||
if (attribSurfaceType != EGL_WINDOW_BIT) {
|
||||
if (log)
|
||||
os::Printer::log("EGL_SURFACE_TYPE!= EGL_WINDOW_BIT");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generally we give a really bad rating if attributes are worse than requested
|
||||
// We give a slight worse rating if attributes are not exact as requested
|
||||
// And we use some priorities which might make sense (but not really fine-tuned,
|
||||
// so if you think other priorities would be better don't worry about changing the values.
|
||||
int rating = 0;
|
||||
|
||||
EGLint attribBufferSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_BUFFER_SIZE, &attribBufferSize);
|
||||
if (attribBufferSize < Params.Bits) {
|
||||
if (log)
|
||||
os::Printer::log("No full color buffer.");
|
||||
rating += 100;
|
||||
}
|
||||
if (attribBufferSize > Params.Bits) {
|
||||
if (log)
|
||||
os::Printer::log("Larger color buffer.", ELL_DEBUG);
|
||||
++rating;
|
||||
}
|
||||
|
||||
EGLint attribRedSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_RED_SIZE, &attribRedSize);
|
||||
if (attribRedSize < 5 && Params.Bits >= 4)
|
||||
rating += 100;
|
||||
else if (attribRedSize < 8 && Params.Bits >= 24)
|
||||
rating += 10;
|
||||
else if (attribRedSize >= 8 && Params.Bits < 24)
|
||||
rating++;
|
||||
EGLint attribGreenSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_GREEN_SIZE, &attribGreenSize);
|
||||
if (attribGreenSize < 5 && Params.Bits >= 4)
|
||||
rating += 100;
|
||||
else if (attribGreenSize < 8 && Params.Bits >= 24)
|
||||
rating += 10;
|
||||
else if (attribGreenSize >= 8 && Params.Bits < 24)
|
||||
rating++;
|
||||
EGLint attribBlueSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_BLUE_SIZE, &attribBlueSize);
|
||||
if (attribBlueSize < 5 && Params.Bits >= 4)
|
||||
rating += 100;
|
||||
else if (attribBlueSize < 8 && Params.Bits >= 24)
|
||||
rating += 10;
|
||||
else if (attribBlueSize >= 8 && Params.Bits < 24)
|
||||
rating++;
|
||||
|
||||
EGLint attribAlphaSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_ALPHA_SIZE, &attribAlphaSize);
|
||||
if (Params.WithAlphaChannel && attribAlphaSize == 0) {
|
||||
if (log)
|
||||
os::Printer::log("No alpha.");
|
||||
rating += 10;
|
||||
} else if (!Params.WithAlphaChannel && attribAlphaSize > 0) {
|
||||
if (log)
|
||||
os::Printer::log("Got alpha (unrequested).", ELL_DEBUG);
|
||||
rating++;
|
||||
}
|
||||
|
||||
EGLint attribStencilSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_STENCIL_SIZE, &attribStencilSize);
|
||||
if (Params.Stencilbuffer && attribStencilSize == 0) {
|
||||
if (log)
|
||||
os::Printer::log("No stencil buffer.");
|
||||
rating += 10;
|
||||
} else if (!Params.Stencilbuffer && attribStencilSize > 0) {
|
||||
if (log)
|
||||
os::Printer::log("Got a stencil buffer (unrequested).", ELL_DEBUG);
|
||||
rating++;
|
||||
}
|
||||
|
||||
EGLint attribDepthSize = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_DEPTH_SIZE, &attribDepthSize);
|
||||
if (attribDepthSize < Params.ZBufferBits) {
|
||||
if (log) {
|
||||
if (attribDepthSize > 0)
|
||||
os::Printer::log("No full depth buffer.");
|
||||
else
|
||||
os::Printer::log("No depth buffer.");
|
||||
}
|
||||
rating += 50;
|
||||
} else if (attribDepthSize != Params.ZBufferBits) {
|
||||
if (log) {
|
||||
if (Params.ZBufferBits == 0)
|
||||
os::Printer::log("Got a depth buffer (unrequested).", ELL_DEBUG);
|
||||
else
|
||||
os::Printer::log("Got a larger depth buffer.", ELL_DEBUG);
|
||||
}
|
||||
rating++;
|
||||
}
|
||||
|
||||
EGLint attribSampleBuffers = 0, attribSamples = 0;
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_SAMPLE_BUFFERS, &attribSampleBuffers);
|
||||
eglGetConfigAttrib(EglDisplay, config, EGL_SAMPLES, &attribSamples);
|
||||
if (Params.AntiAlias && attribSampleBuffers == 0) {
|
||||
if (log)
|
||||
os::Printer::log("No multisampling.");
|
||||
rating += 20;
|
||||
} else if (Params.AntiAlias && attribSampleBuffers && attribSamples < Params.AntiAlias) {
|
||||
if (log)
|
||||
os::Printer::log("Multisampling with less samples than requested.", ELL_DEBUG);
|
||||
rating += 10;
|
||||
} else if (Params.AntiAlias && attribSampleBuffers && attribSamples > Params.AntiAlias) {
|
||||
if (log)
|
||||
os::Printer::log("Multisampling with more samples than requested.", ELL_DEBUG);
|
||||
rating += 5;
|
||||
} else if (!Params.AntiAlias && attribSampleBuffers > 0) {
|
||||
if (log)
|
||||
os::Printer::log("Got multisampling (unrequested).", ELL_DEBUG);
|
||||
rating += 3;
|
||||
}
|
||||
|
||||
return rating;
|
||||
}
|
||||
|
||||
void CEGLManager::destroySurface()
|
||||
{
|
||||
if (EglSurface == EGL_NO_SURFACE)
|
||||
return;
|
||||
|
||||
// We should unbind current EGL context before destroy EGL surface.
|
||||
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
eglDestroySurface(EglDisplay, EglSurface);
|
||||
EglSurface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
bool CEGLManager::generateContext()
|
||||
{
|
||||
if (EglDisplay == EGL_NO_DISPLAY || EglSurface == EGL_NO_SURFACE)
|
||||
return false;
|
||||
|
||||
if (EglContext != EGL_NO_CONTEXT)
|
||||
return true;
|
||||
|
||||
EGLint OpenGLESVersion = 0;
|
||||
|
||||
switch (Params.DriverType) {
|
||||
case EDT_OGLES1:
|
||||
OpenGLESVersion = 1;
|
||||
break;
|
||||
case EDT_OGLES2:
|
||||
case EDT_WEBGL1:
|
||||
OpenGLESVersion = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EGLint ContextAttrib[] = {
|
||||
#ifdef EGL_VERSION_1_3
|
||||
EGL_CONTEXT_CLIENT_VERSION, OpenGLESVersion,
|
||||
#endif
|
||||
EGL_NONE, 0,
|
||||
};
|
||||
|
||||
EglContext = eglCreateContext(EglDisplay, EglConfig, EGL_NO_CONTEXT, ContextAttrib);
|
||||
|
||||
if (testEGLError()) {
|
||||
os::Printer::log("Could not create EGL context.", ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
os::Printer::log("EGL context created with OpenGLESVersion: ", core::stringc((int)OpenGLESVersion), ELL_DEBUG);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEGLManager::destroyContext()
|
||||
{
|
||||
if (EglContext == EGL_NO_CONTEXT)
|
||||
return;
|
||||
|
||||
// We must unbind current EGL context before destroy it.
|
||||
eglMakeCurrent(EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(EglDisplay, EglContext);
|
||||
|
||||
EglContext = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
bool CEGLManager::activateContext(const SExposedVideoData &videoData, bool restorePrimaryOnZero)
|
||||
{
|
||||
eglMakeCurrent(EglDisplay, EglSurface, EglSurface, EglContext);
|
||||
|
||||
if (testEGLError()) {
|
||||
os::Printer::log("Could not make EGL context current.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const SExposedVideoData &CEGLManager::getContext() const
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
void *CEGLManager::getProcAddress(const std::string &procName)
|
||||
{
|
||||
return (void *)eglGetProcAddress(procName.c_str());
|
||||
}
|
||||
|
||||
bool CEGLManager::swapBuffers()
|
||||
{
|
||||
return (eglSwapBuffers(EglDisplay, EglSurface) == EGL_TRUE);
|
||||
}
|
||||
|
||||
bool CEGLManager::testEGLError()
|
||||
{
|
||||
#if defined(EGL_VERSION_1_0) && defined(_DEBUG)
|
||||
EGLint status = eglGetError();
|
||||
|
||||
switch (status) {
|
||||
case EGL_SUCCESS:
|
||||
return false;
|
||||
case EGL_NOT_INITIALIZED:
|
||||
os::Printer::log("Not Initialized", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_ACCESS:
|
||||
os::Printer::log("Bad Access", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_ALLOC:
|
||||
os::Printer::log("Bad Alloc", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_ATTRIBUTE:
|
||||
os::Printer::log("Bad Attribute", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_CONTEXT:
|
||||
os::Printer::log("Bad Context", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_CONFIG:
|
||||
os::Printer::log("Bad Config", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_CURRENT_SURFACE:
|
||||
os::Printer::log("Bad Current Surface", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_DISPLAY:
|
||||
os::Printer::log("Bad Display", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_SURFACE:
|
||||
os::Printer::log("Bad Surface", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_MATCH:
|
||||
os::Printer::log("Bad Match", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_PARAMETER:
|
||||
os::Printer::log("Bad Parameter", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_NATIVE_PIXMAP:
|
||||
os::Printer::log("Bad Native Pixmap", ELL_ERROR);
|
||||
break;
|
||||
case EGL_BAD_NATIVE_WINDOW:
|
||||
os::Printer::log("Bad Native Window", ELL_ERROR);
|
||||
break;
|
||||
case EGL_CONTEXT_LOST:
|
||||
os::Printer::log("Context Lost", ELL_ERROR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
114
src/CEGLManager.h
Normal file
114
src/CEGLManager.h
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2013 Patryk Nadrowski
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_EGL_MANAGER_
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include "SIrrCreationParameters.h"
|
||||
#include "SExposedVideoData.h"
|
||||
#include "IContextManager.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
// EGL manager.
|
||||
class CEGLManager : public IContextManager
|
||||
{
|
||||
public:
|
||||
//! Constructor.
|
||||
CEGLManager();
|
||||
|
||||
//! Destructor.
|
||||
virtual ~CEGLManager();
|
||||
|
||||
// Initialize EGL.
|
||||
/* This method initialize EGLand create EGL display, anyway surface and context
|
||||
aren't create. */
|
||||
bool initialize(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &data) override;
|
||||
|
||||
// Terminate EGL.
|
||||
/* Terminate EGL context. This method break both existed surface and context. */
|
||||
void terminate() override;
|
||||
|
||||
// Create EGL surface.
|
||||
/* This method create EGL surface. On some platforms eg. Android, we must
|
||||
recreate surface on each resume, because WindowID may change, so existed
|
||||
surface may not be valid. If EGL context already exist, this method
|
||||
automatically activates it. */
|
||||
bool generateSurface() override;
|
||||
|
||||
// Destroy EGL surface.
|
||||
/* This method destroy EGL. On some platforms eg. Android, we should call
|
||||
this method on each pause, because after resume this surface may not be valid.
|
||||
Hovewer this method doesn'r break EGL context. */
|
||||
void destroySurface() override;
|
||||
|
||||
// Create EGL context.
|
||||
/* This method create and activate EGL context. */
|
||||
bool generateContext() override;
|
||||
|
||||
// Destroy EGL context.
|
||||
/* This method destroy EGL context. */
|
||||
void destroyContext() override;
|
||||
|
||||
const SExposedVideoData &getContext() const override;
|
||||
|
||||
bool activateContext(const SExposedVideoData &videoData, bool restorePrimaryOnZero) override;
|
||||
|
||||
// Get procedure address.
|
||||
void *getProcAddress(const std::string &procName) override;
|
||||
|
||||
// Swap buffers.
|
||||
bool swapBuffers() override;
|
||||
|
||||
protected:
|
||||
enum EConfigStyle
|
||||
{
|
||||
//! Get first result of eglChooseConfigs and if that fails try again by requesting simpler attributes
|
||||
ECS_EGL_CHOOSE_FIRST_LOWER_EXPECTATIONS,
|
||||
|
||||
//! We select our own best fit and avoid using eglChooseConfigs
|
||||
ECS_IRR_CHOOSE,
|
||||
};
|
||||
|
||||
EGLConfig chooseConfig(EConfigStyle confStyle);
|
||||
|
||||
//! Check how close this config is to the parameters we requested
|
||||
//! returns 0 is perfect, larger values are worse and < 0 is unusable.
|
||||
irr::s32 rateConfig(EGLConfig config, EGLint eglOpenGLBIT, bool log = false);
|
||||
|
||||
// Helper to sort EGLConfig's. (because we got no std::pair....)
|
||||
struct SConfigRating
|
||||
{
|
||||
EGLConfig config;
|
||||
irr::s32 rating;
|
||||
bool operator<(const SConfigRating &other) const
|
||||
{
|
||||
return rating < other.rating;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool testEGLError();
|
||||
|
||||
NativeWindowType EglWindow;
|
||||
EGLDisplay EglDisplay;
|
||||
EGLSurface EglSurface;
|
||||
EGLContext EglContext;
|
||||
|
||||
EGLConfig EglConfig;
|
||||
|
||||
SIrrlichtCreationParameters Params;
|
||||
SExposedVideoData Data;
|
||||
|
||||
EGLint MajorVersion;
|
||||
EGLint MinorVersion;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
65
src/CEmptySceneNode.cpp
Normal file
65
src/CEmptySceneNode.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CEmptySceneNode.h"
|
||||
#include "ISceneManager.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CEmptySceneNode::CEmptySceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id) :
|
||||
ISceneNode(parent, mgr, id)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CEmptySceneNode");
|
||||
#endif
|
||||
|
||||
setAutomaticCulling(scene::EAC_OFF);
|
||||
}
|
||||
|
||||
//! pre render event
|
||||
void CEmptySceneNode::OnRegisterSceneNode()
|
||||
{
|
||||
if (IsVisible)
|
||||
SceneManager->registerNodeForRendering(this);
|
||||
|
||||
ISceneNode::OnRegisterSceneNode();
|
||||
}
|
||||
|
||||
//! render
|
||||
void CEmptySceneNode::render()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &CEmptySceneNode::getBoundingBox() const
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *CEmptySceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||
{
|
||||
if (!newParent)
|
||||
newParent = Parent;
|
||||
if (!newManager)
|
||||
newManager = SceneManager;
|
||||
|
||||
CEmptySceneNode *nb = new CEmptySceneNode(newParent,
|
||||
newManager, ID);
|
||||
|
||||
nb->cloneMembers(this, newManager);
|
||||
nb->Box = Box;
|
||||
|
||||
if (newParent)
|
||||
nb->drop();
|
||||
return nb;
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
40
src/CEmptySceneNode.h
Normal file
40
src/CEmptySceneNode.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISceneNode.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class CEmptySceneNode : public ISceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CEmptySceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id);
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override;
|
||||
|
||||
//! This method is called just before the rendering process of the whole scene.
|
||||
void OnRegisterSceneNode() override;
|
||||
|
||||
//! does nothing.
|
||||
void render() override;
|
||||
|
||||
//! Returns type of the scene node
|
||||
ESCENE_NODE_TYPE getType() const override { return ESNT_EMPTY; }
|
||||
|
||||
//! Creates a clone of this scene node and its children.
|
||||
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
|
||||
|
||||
private:
|
||||
core::aabbox3d<f32> Box;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
||||
66
src/CFPSCounter.cpp
Normal file
66
src/CFPSCounter.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CFPSCounter.h"
|
||||
#include "irrMath.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
CFPSCounter::CFPSCounter() :
|
||||
FPS(60), Primitive(0), StartTime(0), FramesCounted(0),
|
||||
PrimitivesCounted(0), PrimitiveAverage(0), PrimitiveTotal(0)
|
||||
{
|
||||
}
|
||||
|
||||
//! returns current fps
|
||||
s32 CFPSCounter::getFPS() const
|
||||
{
|
||||
return FPS;
|
||||
}
|
||||
|
||||
//! returns current primitive count
|
||||
u32 CFPSCounter::getPrimitive() const
|
||||
{
|
||||
return Primitive;
|
||||
}
|
||||
|
||||
//! returns average primitive count of last period
|
||||
u32 CFPSCounter::getPrimitiveAverage() const
|
||||
{
|
||||
return PrimitiveAverage;
|
||||
}
|
||||
|
||||
//! returns accumulated primitive count since start
|
||||
u32 CFPSCounter::getPrimitiveTotal() const
|
||||
{
|
||||
return PrimitiveTotal;
|
||||
}
|
||||
|
||||
//! to be called every frame
|
||||
void CFPSCounter::registerFrame(u32 now, u32 primitivesDrawn)
|
||||
{
|
||||
++FramesCounted;
|
||||
PrimitiveTotal += primitivesDrawn;
|
||||
PrimitivesCounted += primitivesDrawn;
|
||||
Primitive = primitivesDrawn;
|
||||
|
||||
const u32 milliseconds = now - StartTime;
|
||||
|
||||
if (milliseconds >= 1500) {
|
||||
const f32 invMilli = core::reciprocal((f32)milliseconds);
|
||||
|
||||
FPS = core::ceil32((1000 * FramesCounted) * invMilli);
|
||||
PrimitiveAverage = core::ceil32((1000 * PrimitivesCounted) * invMilli);
|
||||
|
||||
FramesCounted = 0;
|
||||
PrimitivesCounted = 0;
|
||||
StartTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
46
src/CFPSCounter.h
Normal file
46
src/CFPSCounter.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrTypes.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class CFPSCounter
|
||||
{
|
||||
public:
|
||||
CFPSCounter();
|
||||
|
||||
//! returns current fps
|
||||
s32 getFPS() const;
|
||||
|
||||
//! returns primitive count
|
||||
u32 getPrimitive() const;
|
||||
|
||||
//! returns average primitive count of last period
|
||||
u32 getPrimitiveAverage() const;
|
||||
|
||||
//! returns accumulated primitive count since start
|
||||
u32 getPrimitiveTotal() const;
|
||||
|
||||
//! to be called every frame
|
||||
void registerFrame(u32 now, u32 primitive);
|
||||
|
||||
private:
|
||||
s32 FPS;
|
||||
u32 Primitive;
|
||||
u32 StartTime;
|
||||
|
||||
u32 FramesCounted;
|
||||
u32 PrimitivesCounted;
|
||||
u32 PrimitiveAverage;
|
||||
u32 PrimitiveTotal;
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
155
src/CFileList.cpp
Normal file
155
src/CFileList.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CFileList.h"
|
||||
#include "irrArray.h"
|
||||
#include "coreutil.h"
|
||||
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
static const io::path emptyFileListEntry;
|
||||
|
||||
CFileList::CFileList(const io::path &path, bool ignoreCase, bool ignorePaths) :
|
||||
IgnorePaths(ignorePaths), IgnoreCase(ignoreCase), Path(path)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CFileList");
|
||||
#endif
|
||||
|
||||
Path.replace('\\', '/');
|
||||
}
|
||||
|
||||
CFileList::~CFileList()
|
||||
{
|
||||
Files.clear();
|
||||
}
|
||||
|
||||
u32 CFileList::getFileCount() const
|
||||
{
|
||||
return Files.size();
|
||||
}
|
||||
|
||||
void CFileList::sort()
|
||||
{
|
||||
Files.sort();
|
||||
}
|
||||
|
||||
const io::path &CFileList::getFileName(u32 index) const
|
||||
{
|
||||
if (index >= Files.size())
|
||||
return emptyFileListEntry;
|
||||
|
||||
return Files[index].Name;
|
||||
}
|
||||
|
||||
//! Gets the full name of a file in the list, path included, based on an index.
|
||||
const io::path &CFileList::getFullFileName(u32 index) const
|
||||
{
|
||||
if (index >= Files.size())
|
||||
return emptyFileListEntry;
|
||||
|
||||
return Files[index].FullName;
|
||||
}
|
||||
|
||||
//! adds a file or folder
|
||||
u32 CFileList::addItem(const io::path &fullPath, u32 offset, u32 size, bool isDirectory, u32 id)
|
||||
{
|
||||
SFileListEntry entry;
|
||||
entry.ID = id ? id : Files.size();
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
entry.Name = fullPath;
|
||||
entry.Name.replace('\\', '/');
|
||||
entry.IsDirectory = isDirectory;
|
||||
|
||||
// remove trailing slash
|
||||
if (entry.Name.lastChar() == '/') {
|
||||
entry.IsDirectory = true;
|
||||
entry.Name[entry.Name.size() - 1] = 0;
|
||||
entry.Name.validate();
|
||||
}
|
||||
|
||||
if (IgnoreCase)
|
||||
entry.Name.make_lower();
|
||||
|
||||
entry.FullName = entry.Name;
|
||||
|
||||
core::deletePathFromFilename(entry.Name);
|
||||
|
||||
if (IgnorePaths)
|
||||
entry.FullName = entry.Name;
|
||||
|
||||
// os::Printer::log(Path.c_str(), entry.FullName);
|
||||
|
||||
Files.push_back(entry);
|
||||
|
||||
return Files.size() - 1;
|
||||
}
|
||||
|
||||
//! Returns the ID of a file in the file list, based on an index.
|
||||
u32 CFileList::getID(u32 index) const
|
||||
{
|
||||
return index < Files.size() ? Files[index].ID : 0;
|
||||
}
|
||||
|
||||
bool CFileList::isDirectory(u32 index) const
|
||||
{
|
||||
bool ret = false;
|
||||
if (index < Files.size())
|
||||
ret = Files[index].IsDirectory;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Returns the size of a file
|
||||
u32 CFileList::getFileSize(u32 index) const
|
||||
{
|
||||
return index < Files.size() ? Files[index].Size : 0;
|
||||
}
|
||||
|
||||
u32 CFileList::getFileOffset(u32 index) const
|
||||
{
|
||||
return index < Files.size() ? Files[index].Offset : 0;
|
||||
}
|
||||
|
||||
//! Searches for a file or folder within the list, returns the index
|
||||
s32 CFileList::findFile(const io::path &filename, bool isDirectory = false) const
|
||||
{
|
||||
SFileListEntry entry;
|
||||
// we only need FullName to be set for the search
|
||||
entry.FullName = filename;
|
||||
entry.IsDirectory = isDirectory;
|
||||
|
||||
// exchange
|
||||
entry.FullName.replace('\\', '/');
|
||||
|
||||
// remove trailing slash
|
||||
if (entry.FullName.lastChar() == '/') {
|
||||
entry.IsDirectory = true;
|
||||
entry.FullName[entry.FullName.size() - 1] = 0;
|
||||
entry.FullName.validate();
|
||||
}
|
||||
|
||||
if (IgnoreCase)
|
||||
entry.FullName.make_lower();
|
||||
|
||||
if (IgnorePaths)
|
||||
core::deletePathFromFilename(entry.FullName);
|
||||
|
||||
return Files.binary_search(entry);
|
||||
}
|
||||
|
||||
//! Returns the base path of the file list
|
||||
const io::path &CFileList::getPath() const
|
||||
{
|
||||
return Path;
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace io
|
||||
128
src/CFileList.h
Normal file
128
src/CFileList.h
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IFileList.h"
|
||||
#include "irrString.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
//! An entry in a list of files, can be a folder or a file.
|
||||
struct SFileListEntry
|
||||
{
|
||||
//! The name of the file
|
||||
/** If this is a file or folder in the virtual filesystem and the archive
|
||||
was created with the ignoreCase flag then the file name will be lower case. */
|
||||
io::path Name;
|
||||
|
||||
//! The name of the file including the path
|
||||
/** If this is a file or folder in the virtual filesystem and the archive was
|
||||
created with the ignoreDirs flag then it will be the same as Name. */
|
||||
io::path FullName;
|
||||
|
||||
//! The size of the file in bytes
|
||||
u32 Size;
|
||||
|
||||
//! The ID of the file in an archive
|
||||
/** This is used to link the FileList entry to extra info held about this
|
||||
file in an archive, which can hold things like data offset and CRC. */
|
||||
u32 ID;
|
||||
|
||||
//! FileOffset inside an archive
|
||||
u32 Offset;
|
||||
|
||||
//! True if this is a folder, false if not.
|
||||
bool IsDirectory;
|
||||
|
||||
//! The == operator is provided so that CFileList can slowly search the list!
|
||||
bool operator==(const struct SFileListEntry &other) const
|
||||
{
|
||||
if (IsDirectory != other.IsDirectory)
|
||||
return false;
|
||||
|
||||
return FullName.equals_ignore_case(other.FullName);
|
||||
}
|
||||
|
||||
//! The < operator is provided so that CFileList can sort and quickly search the list.
|
||||
bool operator<(const struct SFileListEntry &other) const
|
||||
{
|
||||
if (IsDirectory != other.IsDirectory)
|
||||
return IsDirectory;
|
||||
|
||||
return FullName.lower_ignore_case(other.FullName);
|
||||
}
|
||||
};
|
||||
|
||||
//! Implementation of a file list
|
||||
class CFileList : public IFileList
|
||||
{
|
||||
public:
|
||||
// CFileList methods
|
||||
|
||||
//! Constructor
|
||||
/** \param path The path of this file archive */
|
||||
CFileList(const io::path &path, bool ignoreCase, bool ignorePaths);
|
||||
|
||||
//! Destructor
|
||||
virtual ~CFileList();
|
||||
|
||||
//! Add as a file or folder to the list
|
||||
/** \param fullPath The file name including path, up to the root of the file list.
|
||||
\param isDirectory True if this is a directory rather than a file.
|
||||
\param offset The offset where the file is stored in an archive
|
||||
\param size The size of the file in bytes.
|
||||
\param id The ID of the file in the archive which owns it */
|
||||
u32 addItem(const io::path &fullPath, u32 offset, u32 size, bool isDirectory, u32 id = 0) override;
|
||||
|
||||
//! Sorts the file list. You should call this after adding any items to the file list
|
||||
void sort() override;
|
||||
|
||||
//! Returns the amount of files in the filelist.
|
||||
u32 getFileCount() const override;
|
||||
|
||||
//! Gets the name of a file in the list, based on an index.
|
||||
const io::path &getFileName(u32 index) const override;
|
||||
|
||||
//! Gets the full name of a file in the list, path included, based on an index.
|
||||
const io::path &getFullFileName(u32 index) const override;
|
||||
|
||||
//! Returns the ID of a file in the file list, based on an index.
|
||||
u32 getID(u32 index) const override;
|
||||
|
||||
//! Returns true if the file is a directory
|
||||
bool isDirectory(u32 index) const override;
|
||||
|
||||
//! Returns the size of a file
|
||||
u32 getFileSize(u32 index) const override;
|
||||
|
||||
//! Returns the offset of a file
|
||||
u32 getFileOffset(u32 index) const override;
|
||||
|
||||
//! Searches for a file or folder within the list, returns the index
|
||||
s32 findFile(const io::path &filename, bool isFolder) const override;
|
||||
|
||||
//! Returns the base path of the file list
|
||||
const io::path &getPath() const override;
|
||||
|
||||
protected:
|
||||
//! Ignore paths when adding or searching for files
|
||||
bool IgnorePaths;
|
||||
|
||||
//! Ignore case when adding or searching for files
|
||||
bool IgnoreCase;
|
||||
|
||||
//! Path to the file list
|
||||
io::path Path;
|
||||
|
||||
//! List of files
|
||||
core::array<SFileListEntry> Files;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace io
|
||||
762
src/CFileSystem.cpp
Normal file
762
src/CFileSystem.cpp
Normal file
@@ -0,0 +1,762 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CFileSystem.h"
|
||||
#include "IReadFile.h"
|
||||
#include "IWriteFile.h"
|
||||
#include "CZipReader.h"
|
||||
#include "CFileList.h"
|
||||
#include "stdio.h"
|
||||
#include "os.h"
|
||||
#include "CReadFile.h"
|
||||
#include "CMemoryFile.h"
|
||||
#include "CLimitReadFile.h"
|
||||
#include "CWriteFile.h"
|
||||
#include <list>
|
||||
|
||||
#if defined(__STRICT_ANSI__)
|
||||
#error Compiling with __STRICT_ANSI__ not supported. g++ does set this when compiling with -std=c++11 or -std=c++0x. Use instead -std=gnu++11 or -std=gnu++0x. Or use -U__STRICT_ANSI__ to disable strict ansi.
|
||||
#endif
|
||||
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
#include <direct.h> // for _chdir
|
||||
#include <io.h> // for _access
|
||||
#include <tchar.h>
|
||||
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_))
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_IRR_EMSCRIPTEN_PLATFORM_)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CFileSystem::CFileSystem()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CFileSystem");
|
||||
#endif
|
||||
|
||||
setFileListSystem(FILESYSTEM_NATIVE);
|
||||
//! reset current working directory
|
||||
getWorkingDirectory();
|
||||
|
||||
ArchiveLoader.push_back(new CArchiveLoaderZIP(this));
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CFileSystem::~CFileSystem()
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < FileArchives.size(); ++i) {
|
||||
FileArchives[i]->drop();
|
||||
}
|
||||
|
||||
for (i = 0; i < ArchiveLoader.size(); ++i) {
|
||||
ArchiveLoader[i]->drop();
|
||||
}
|
||||
}
|
||||
|
||||
//! opens a file for read access
|
||||
IReadFile *CFileSystem::createAndOpenFile(const io::path &filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
return 0;
|
||||
|
||||
IReadFile *file = 0;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < FileArchives.size(); ++i) {
|
||||
file = FileArchives[i]->createAndOpenFile(filename);
|
||||
if (file)
|
||||
return file;
|
||||
}
|
||||
|
||||
// Create the file using an absolute path so that it matches
|
||||
// the scheme used by CNullDriver::getTexture().
|
||||
return CReadFile::createReadFile(getAbsolutePath(filename));
|
||||
}
|
||||
|
||||
//! Creates an IReadFile interface for treating memory like a file.
|
||||
IReadFile *CFileSystem::createMemoryReadFile(const void *memory, s32 len,
|
||||
const io::path &fileName, bool deleteMemoryWhenDropped)
|
||||
{
|
||||
if (!memory)
|
||||
return 0;
|
||||
else
|
||||
return new CMemoryReadFile(memory, len, fileName, deleteMemoryWhenDropped);
|
||||
}
|
||||
|
||||
//! Creates an IReadFile interface for reading files inside files
|
||||
IReadFile *CFileSystem::createLimitReadFile(const io::path &fileName,
|
||||
IReadFile *alreadyOpenedFile, long pos, long areaSize)
|
||||
{
|
||||
if (!alreadyOpenedFile)
|
||||
return 0;
|
||||
else
|
||||
return new CLimitReadFile(alreadyOpenedFile, pos, areaSize, fileName);
|
||||
}
|
||||
|
||||
//! Creates an IReadFile interface for treating memory like a file.
|
||||
IWriteFile *CFileSystem::createMemoryWriteFile(void *memory, s32 len,
|
||||
const io::path &fileName, bool deleteMemoryWhenDropped)
|
||||
{
|
||||
if (!memory)
|
||||
return 0;
|
||||
else
|
||||
return new CMemoryWriteFile(memory, len, fileName, deleteMemoryWhenDropped);
|
||||
}
|
||||
|
||||
//! Opens a file for write access.
|
||||
IWriteFile *CFileSystem::createAndWriteFile(const io::path &filename, bool append)
|
||||
{
|
||||
return CWriteFile::createWriteFile(filename, append);
|
||||
}
|
||||
|
||||
//! Adds an external archive loader to the engine.
|
||||
void CFileSystem::addArchiveLoader(IArchiveLoader *loader)
|
||||
{
|
||||
if (!loader)
|
||||
return;
|
||||
|
||||
loader->grab();
|
||||
ArchiveLoader.push_back(loader);
|
||||
}
|
||||
|
||||
//! Returns the total number of archive loaders added.
|
||||
u32 CFileSystem::getArchiveLoaderCount() const
|
||||
{
|
||||
return ArchiveLoader.size();
|
||||
}
|
||||
|
||||
//! Gets the archive loader by index.
|
||||
IArchiveLoader *CFileSystem::getArchiveLoader(u32 index) const
|
||||
{
|
||||
if (index < ArchiveLoader.size())
|
||||
return ArchiveLoader[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! move the hirarchy of the filesystem. moves sourceIndex relative up or down
|
||||
bool CFileSystem::moveFileArchive(u32 sourceIndex, s32 relative)
|
||||
{
|
||||
bool r = false;
|
||||
const s32 dest = (s32)sourceIndex + relative;
|
||||
const s32 dir = relative < 0 ? -1 : 1;
|
||||
const s32 sourceEnd = ((s32)FileArchives.size()) - 1;
|
||||
IFileArchive *t;
|
||||
|
||||
for (s32 s = (s32)sourceIndex; s != dest; s += dir) {
|
||||
if (s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd)
|
||||
continue;
|
||||
|
||||
t = FileArchives[s + dir];
|
||||
FileArchives[s + dir] = FileArchives[s];
|
||||
FileArchives[s] = t;
|
||||
r = true;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//! Adds an archive to the file system.
|
||||
bool CFileSystem::addFileArchive(const io::path &filename, bool ignoreCase,
|
||||
bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType,
|
||||
const core::stringc &password,
|
||||
IFileArchive **retArchive)
|
||||
{
|
||||
IFileArchive *archive = 0;
|
||||
bool ret = false;
|
||||
|
||||
// see if archive is already added
|
||||
|
||||
s32 i;
|
||||
|
||||
// do we know what type it should be?
|
||||
if (archiveType == EFAT_UNKNOWN) {
|
||||
// try to load archive based on file name
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(filename)) {
|
||||
archive = ArchiveLoader[i]->createArchive(filename, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try to load archive based on content
|
||||
if (!archive) {
|
||||
io::IReadFile *file = createAndOpenFile(filename);
|
||||
if (file) {
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
file->seek(0);
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(file)) {
|
||||
file->seek(0);
|
||||
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
file->drop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to open archive based on archive loader type
|
||||
|
||||
io::IReadFile *file = 0;
|
||||
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) {
|
||||
// attempt to open file
|
||||
if (!file)
|
||||
file = createAndOpenFile(filename);
|
||||
|
||||
// is the file open?
|
||||
if (file) {
|
||||
// attempt to open archive
|
||||
file->seek(0);
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(file)) {
|
||||
file->seek(0);
|
||||
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// couldn't open file
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if open, close the file
|
||||
if (file)
|
||||
file->drop();
|
||||
}
|
||||
|
||||
if (archive) {
|
||||
FileArchives.push_back(archive);
|
||||
if (password.size())
|
||||
archive->Password = password;
|
||||
if (retArchive)
|
||||
*retArchive = archive;
|
||||
ret = true;
|
||||
} else {
|
||||
os::Printer::log("Could not create archive for", filename, ELL_ERROR);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CFileSystem::addFileArchive(IReadFile *file, bool ignoreCase,
|
||||
bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType,
|
||||
const core::stringc &password, IFileArchive **retArchive)
|
||||
{
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
if (file) {
|
||||
IFileArchive *archive = 0;
|
||||
s32 i;
|
||||
|
||||
if (archiveType == EFAT_UNKNOWN) {
|
||||
// try to load archive based on file name
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(file->getFileName())) {
|
||||
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try to load archive based on content
|
||||
if (!archive) {
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
file->seek(0);
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(file)) {
|
||||
file->seek(0);
|
||||
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to open archive based on archive loader type
|
||||
for (i = ArchiveLoader.size() - 1; i >= 0; --i) {
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) {
|
||||
// attempt to open archive
|
||||
file->seek(0);
|
||||
if (ArchiveLoader[i]->isALoadableFileFormat(file)) {
|
||||
file->seek(0);
|
||||
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
|
||||
if (archive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (archive) {
|
||||
FileArchives.push_back(archive);
|
||||
if (password.size())
|
||||
archive->Password = password;
|
||||
if (retArchive)
|
||||
*retArchive = archive;
|
||||
return true;
|
||||
} else {
|
||||
os::Printer::log("Could not create archive for", file->getFileName(), ELL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Adds an archive to the file system.
|
||||
bool CFileSystem::addFileArchive(IFileArchive *archive)
|
||||
{
|
||||
if (archive) {
|
||||
for (u32 i = 0; i < FileArchives.size(); ++i) {
|
||||
if (archive == FileArchives[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FileArchives.push_back(archive);
|
||||
archive->grab();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! removes an archive from the file system.
|
||||
bool CFileSystem::removeFileArchive(u32 index)
|
||||
{
|
||||
bool ret = false;
|
||||
if (index < FileArchives.size()) {
|
||||
FileArchives[index]->drop();
|
||||
FileArchives.erase(index);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! removes an archive from the file system.
|
||||
bool CFileSystem::removeFileArchive(const io::path &filename)
|
||||
{
|
||||
const path absPath = getAbsolutePath(filename);
|
||||
for (u32 i = 0; i < FileArchives.size(); ++i) {
|
||||
if (absPath == FileArchives[i]->getFileList()->getPath())
|
||||
return removeFileArchive(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Removes an archive from the file system.
|
||||
bool CFileSystem::removeFileArchive(const IFileArchive *archive)
|
||||
{
|
||||
for (u32 i = 0; i < FileArchives.size(); ++i) {
|
||||
if (archive == FileArchives[i]) {
|
||||
return removeFileArchive(i);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! gets an archive
|
||||
u32 CFileSystem::getFileArchiveCount() const
|
||||
{
|
||||
return FileArchives.size();
|
||||
}
|
||||
|
||||
IFileArchive *CFileSystem::getFileArchive(u32 index)
|
||||
{
|
||||
return index < getFileArchiveCount() ? FileArchives[index] : 0;
|
||||
}
|
||||
|
||||
//! Returns the string of the current working directory
|
||||
const io::path &CFileSystem::getWorkingDirectory()
|
||||
{
|
||||
EFileSystemType type = FileSystemType;
|
||||
|
||||
if (type != FILESYSTEM_NATIVE) {
|
||||
type = FILESYSTEM_VIRTUAL;
|
||||
} else {
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
fschar_t tmp[_MAX_PATH];
|
||||
_getcwd(tmp, _MAX_PATH);
|
||||
WorkingDirectory[FILESYSTEM_NATIVE] = tmp;
|
||||
WorkingDirectory[FILESYSTEM_NATIVE].replace('\\', '/');
|
||||
#endif
|
||||
|
||||
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
|
||||
|
||||
// getting the CWD is rather complex as we do not know the size
|
||||
// so try it until the call was successful
|
||||
// Note that neither the first nor the second parameter may be 0 according to POSIX
|
||||
|
||||
u32 pathSize = 256;
|
||||
char *tmpPath = new char[pathSize];
|
||||
while ((pathSize < (1 << 16)) && !(getcwd(tmpPath, pathSize))) {
|
||||
delete[] tmpPath;
|
||||
pathSize *= 2;
|
||||
tmpPath = new char[pathSize];
|
||||
}
|
||||
if (tmpPath) {
|
||||
WorkingDirectory[FILESYSTEM_NATIVE] = tmpPath;
|
||||
delete[] tmpPath;
|
||||
}
|
||||
#endif
|
||||
|
||||
WorkingDirectory[type].validate();
|
||||
}
|
||||
|
||||
return WorkingDirectory[type];
|
||||
}
|
||||
|
||||
//! Changes the current Working Directory to the given string.
|
||||
bool CFileSystem::changeWorkingDirectoryTo(const io::path &newDirectory)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (FileSystemType != FILESYSTEM_NATIVE) {
|
||||
WorkingDirectory[FILESYSTEM_VIRTUAL] = newDirectory;
|
||||
// is this empty string constant really intended?
|
||||
flattenFilename(WorkingDirectory[FILESYSTEM_VIRTUAL], _IRR_TEXT(""));
|
||||
success = true;
|
||||
} else {
|
||||
WorkingDirectory[FILESYSTEM_NATIVE] = newDirectory;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
success = (_chdir(newDirectory.c_str()) == 0);
|
||||
#else
|
||||
success = (chdir(newDirectory.c_str()) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
io::path CFileSystem::getAbsolutePath(const io::path &filename) const
|
||||
{
|
||||
if (filename.empty())
|
||||
return filename;
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
fschar_t *p = 0;
|
||||
fschar_t fpath[_MAX_PATH];
|
||||
p = _fullpath(fpath, filename.c_str(), _MAX_PATH);
|
||||
core::stringc tmp(p);
|
||||
tmp.replace('\\', '/');
|
||||
return tmp;
|
||||
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
|
||||
c8 *p = 0;
|
||||
c8 fpath[4096];
|
||||
fpath[0] = 0;
|
||||
p = realpath(filename.c_str(), fpath);
|
||||
if (!p) {
|
||||
// content in fpath is unclear at this point
|
||||
if (!fpath[0]) { // seems like fpath wasn't altered, use our best guess
|
||||
io::path tmp(filename);
|
||||
return flattenFilename(tmp);
|
||||
} else
|
||||
return io::path(fpath);
|
||||
}
|
||||
if (filename[filename.size() - 1] == '/')
|
||||
return io::path(p) + _IRR_TEXT("/");
|
||||
else
|
||||
return io::path(p);
|
||||
#else
|
||||
return io::path(filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns the directory part of a filename, i.e. all until the first
|
||||
//! slash or backslash, excluding it. If no directory path is prefixed, a '.'
|
||||
//! is returned.
|
||||
io::path CFileSystem::getFileDir(const io::path &filename) const
|
||||
{
|
||||
// find last forward or backslash
|
||||
s32 lastSlash = filename.findLast('/');
|
||||
const s32 lastBackSlash = filename.findLast('\\');
|
||||
lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
|
||||
|
||||
if ((u32)lastSlash < filename.size())
|
||||
return filename.subString(0, lastSlash);
|
||||
else
|
||||
return _IRR_TEXT(".");
|
||||
}
|
||||
|
||||
//! returns the base part of a filename, i.e. all except for the directory
|
||||
//! part. If no directory path is prefixed, the full name is returned.
|
||||
io::path CFileSystem::getFileBasename(const io::path &filename, bool keepExtension) const
|
||||
{
|
||||
// find last forward or backslash
|
||||
s32 lastSlash = filename.findLast('/');
|
||||
const s32 lastBackSlash = filename.findLast('\\');
|
||||
lastSlash = core::max_(lastSlash, lastBackSlash);
|
||||
|
||||
// get number of chars after last dot
|
||||
s32 end = 0;
|
||||
if (!keepExtension) {
|
||||
// take care to search only after last slash to check only for
|
||||
// dots in the filename
|
||||
end = filename.findLast('.');
|
||||
if (end == -1 || end < lastSlash)
|
||||
end = 0;
|
||||
else
|
||||
end = filename.size() - end;
|
||||
}
|
||||
|
||||
if ((u32)lastSlash < filename.size())
|
||||
return filename.subString(lastSlash + 1, filename.size() - lastSlash - 1 - end);
|
||||
else if (end != 0)
|
||||
return filename.subString(0, filename.size() - end);
|
||||
else
|
||||
return filename;
|
||||
}
|
||||
|
||||
//! flatten a path and file name for example: "/you/me/../." becomes "/you"
|
||||
io::path &CFileSystem::flattenFilename(io::path &directory, const io::path &root) const
|
||||
{
|
||||
directory.replace('\\', '/');
|
||||
if (directory.lastChar() != '/')
|
||||
directory.append('/');
|
||||
|
||||
io::path dir;
|
||||
io::path subdir;
|
||||
|
||||
s32 lastpos = 0;
|
||||
s32 pos = 0;
|
||||
bool lastWasRealDir = false;
|
||||
|
||||
while ((pos = directory.findNext('/', lastpos)) >= 0) {
|
||||
subdir = directory.subString(lastpos, pos - lastpos + 1);
|
||||
|
||||
if (subdir == _IRR_TEXT("../")) {
|
||||
if (lastWasRealDir) {
|
||||
deletePathFromPath(dir, 2);
|
||||
lastWasRealDir = (dir.size() != 0);
|
||||
} else {
|
||||
dir.append(subdir);
|
||||
lastWasRealDir = false;
|
||||
}
|
||||
} else if (subdir == _IRR_TEXT("/")) {
|
||||
dir = root;
|
||||
} else if (subdir != _IRR_TEXT("./")) {
|
||||
dir.append(subdir);
|
||||
lastWasRealDir = true;
|
||||
}
|
||||
|
||||
lastpos = pos + 1;
|
||||
}
|
||||
directory = dir;
|
||||
return directory;
|
||||
}
|
||||
|
||||
//! Get the relative filename, relative to the given directory
|
||||
path CFileSystem::getRelativeFilename(const path &filename, const path &directory) const
|
||||
{
|
||||
if (filename.empty() || directory.empty())
|
||||
return filename;
|
||||
|
||||
io::path path1, file, ext;
|
||||
core::splitFilename(getAbsolutePath(filename), &path1, &file, &ext);
|
||||
io::path path2(getAbsolutePath(directory));
|
||||
std::list<io::path> list1, list2;
|
||||
path1.split(list1, _IRR_TEXT("/\\"), 2);
|
||||
path2.split(list2, _IRR_TEXT("/\\"), 2);
|
||||
std::list<io::path>::const_iterator it1, it2;
|
||||
it1 = list1.begin();
|
||||
it2 = list2.begin();
|
||||
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
fschar_t partition1 = 0, partition2 = 0;
|
||||
io::path prefix1, prefix2;
|
||||
if (it1 != list1.end())
|
||||
prefix1 = *it1;
|
||||
if (it2 != list2.end())
|
||||
prefix2 = *it2;
|
||||
if (prefix1.size() > 1 && prefix1[1] == _IRR_TEXT(':'))
|
||||
partition1 = core::locale_lower(prefix1[0]);
|
||||
if (prefix2.size() > 1 && prefix2[1] == _IRR_TEXT(':'))
|
||||
partition2 = core::locale_lower(prefix2[0]);
|
||||
|
||||
// must have the same prefix or we can't resolve it to a relative filename
|
||||
if (partition1 != partition2) {
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; it1 != list1.end() && it2 != list2.end()
|
||||
#if defined(_IRR_WINDOWS_API_)
|
||||
&& (io::path(*it1).make_lower() == io::path(*it2).make_lower())
|
||||
#else
|
||||
&& (*it1 == *it2)
|
||||
#endif
|
||||
;) {
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
path1 = _IRR_TEXT("");
|
||||
for (; it2 != list2.end(); ++it2)
|
||||
path1 += _IRR_TEXT("../");
|
||||
while (it1 != list1.end()) {
|
||||
path1 += *it1++;
|
||||
path1 += _IRR_TEXT('/');
|
||||
}
|
||||
path1 += file;
|
||||
if (ext.size()) {
|
||||
path1 += _IRR_TEXT('.');
|
||||
path1 += ext;
|
||||
}
|
||||
return path1;
|
||||
}
|
||||
|
||||
//! Sets the current file systen type
|
||||
EFileSystemType CFileSystem::setFileListSystem(EFileSystemType listType)
|
||||
{
|
||||
EFileSystemType current = FileSystemType;
|
||||
FileSystemType = listType;
|
||||
return current;
|
||||
}
|
||||
|
||||
//! Creates a list of files and directories in the current working directory
|
||||
IFileList *CFileSystem::createFileList()
|
||||
{
|
||||
CFileList *r = 0;
|
||||
io::path Path = getWorkingDirectory();
|
||||
Path.replace('\\', '/');
|
||||
if (!Path.empty() && Path.lastChar() != '/')
|
||||
Path.append('/');
|
||||
|
||||
//! Construct from native filesystem
|
||||
if (FileSystemType == FILESYSTEM_NATIVE) {
|
||||
// --------------------------------------------
|
||||
//! Windows version
|
||||
#ifdef _IRR_WINDOWS_API_
|
||||
|
||||
r = new CFileList(Path, true, false);
|
||||
|
||||
// intptr_t is optional but supported by MinGW since 2007 or earlier.
|
||||
intptr_t hFile;
|
||||
struct _tfinddata_t c_file;
|
||||
if ((hFile = _tfindfirst(_T("*"), &c_file)) != (intptr_t)(-1L)) {
|
||||
do {
|
||||
r->addItem(Path + c_file.name, 0, c_file.size, (_A_SUBDIR & c_file.attrib) != 0, 0);
|
||||
} while (_tfindnext(hFile, &c_file) == 0);
|
||||
|
||||
_findclose(hFile);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// --------------------------------------------
|
||||
//! Linux version
|
||||
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
|
||||
|
||||
r = new CFileList(Path, false, false);
|
||||
|
||||
r->addItem(Path + _IRR_TEXT(".."), 0, 0, true, 0);
|
||||
|
||||
//! We use the POSIX compliant methods instead of scandir
|
||||
DIR *dirHandle = opendir(Path.c_str());
|
||||
if (dirHandle) {
|
||||
struct dirent *dirEntry;
|
||||
while ((dirEntry = readdir(dirHandle))) {
|
||||
u32 size = 0;
|
||||
bool isDirectory = false;
|
||||
|
||||
if ((strcmp(dirEntry->d_name, ".") == 0) ||
|
||||
(strcmp(dirEntry->d_name, "..") == 0)) {
|
||||
continue;
|
||||
}
|
||||
struct stat buf;
|
||||
if (stat(dirEntry->d_name, &buf) == 0) {
|
||||
size = buf.st_size;
|
||||
isDirectory = S_ISDIR(buf.st_mode);
|
||||
}
|
||||
#if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__) && !defined(__HAIKU__)
|
||||
// only available on some systems
|
||||
else {
|
||||
isDirectory = dirEntry->d_type == DT_DIR;
|
||||
}
|
||||
#endif
|
||||
|
||||
r->addItem(Path + dirEntry->d_name, 0, size, isDirectory, 0);
|
||||
}
|
||||
closedir(dirHandle);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
//! create file list for the virtual filesystem
|
||||
r = new CFileList(Path, false, false);
|
||||
|
||||
//! add relative navigation
|
||||
SFileListEntry e2;
|
||||
SFileListEntry e3;
|
||||
|
||||
//! PWD
|
||||
r->addItem(Path + _IRR_TEXT("."), 0, 0, true, 0);
|
||||
|
||||
//! parent
|
||||
r->addItem(Path + _IRR_TEXT(".."), 0, 0, true, 0);
|
||||
|
||||
//! merge archives
|
||||
for (u32 i = 0; i < FileArchives.size(); ++i) {
|
||||
const IFileList *merge = FileArchives[i]->getFileList();
|
||||
|
||||
for (u32 j = 0; j < merge->getFileCount(); ++j) {
|
||||
if (core::isInSameDirectory(Path, merge->getFullFileName(j)) == 0) {
|
||||
r->addItem(merge->getFullFileName(j), merge->getFileOffset(j), merge->getFileSize(j), merge->isDirectory(j), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r)
|
||||
r->sort();
|
||||
return r;
|
||||
}
|
||||
|
||||
//! Creates an empty filelist
|
||||
IFileList *CFileSystem::createEmptyFileList(const io::path &path, bool ignoreCase, bool ignorePaths)
|
||||
{
|
||||
return new CFileList(path, ignoreCase, ignorePaths);
|
||||
}
|
||||
|
||||
//! determines if a file exists and would be able to be opened.
|
||||
bool CFileSystem::existFile(const io::path &filename) const
|
||||
{
|
||||
for (u32 i = 0; i < FileArchives.size(); ++i)
|
||||
if (FileArchives[i]->getFileList()->findFile(filename) != -1)
|
||||
return true;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
return (_access(filename.c_str(), 0) != -1);
|
||||
#elif defined(F_OK)
|
||||
return (access(filename.c_str(), F_OK) != -1);
|
||||
#else
|
||||
return (access(filename.c_str(), 0) != -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
//! creates a filesystem which is able to open files from the ordinary file system,
|
||||
//! and out of zipfiles, which are able to be added to the filesystem.
|
||||
IFileSystem *createFileSystem()
|
||||
{
|
||||
return new CFileSystem();
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace io
|
||||
138
src/CFileSystem.h
Normal file
138
src/CFileSystem.h
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IFileSystem.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
class CZipReader;
|
||||
|
||||
/*!
|
||||
FileSystem which uses normal files and one zipfile
|
||||
*/
|
||||
class CFileSystem : public IFileSystem
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CFileSystem();
|
||||
|
||||
//! destructor
|
||||
virtual ~CFileSystem();
|
||||
|
||||
//! opens a file for read access
|
||||
IReadFile *createAndOpenFile(const io::path &filename) override;
|
||||
|
||||
//! Creates an IReadFile interface for accessing memory like a file.
|
||||
IReadFile *createMemoryReadFile(const void *memory, s32 len, const io::path &fileName, bool deleteMemoryWhenDropped = false) override;
|
||||
|
||||
//! Creates an IReadFile interface for accessing files inside files
|
||||
IReadFile *createLimitReadFile(const io::path &fileName, IReadFile *alreadyOpenedFile, long pos, long areaSize) override;
|
||||
|
||||
//! Creates an IWriteFile interface for accessing memory like a file.
|
||||
IWriteFile *createMemoryWriteFile(void *memory, s32 len, const io::path &fileName, bool deleteMemoryWhenDropped = false) override;
|
||||
|
||||
//! Opens a file for write access.
|
||||
IWriteFile *createAndWriteFile(const io::path &filename, bool append = false) override;
|
||||
|
||||
//! Adds an archive to the file system.
|
||||
virtual bool addFileArchive(const io::path &filename,
|
||||
bool ignoreCase = true, bool ignorePaths = true,
|
||||
E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN,
|
||||
const core::stringc &password = "",
|
||||
IFileArchive **retArchive = 0) override;
|
||||
|
||||
//! Adds an archive to the file system.
|
||||
virtual bool addFileArchive(IReadFile *file, bool ignoreCase = true,
|
||||
bool ignorePaths = true,
|
||||
E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN,
|
||||
const core::stringc &password = "",
|
||||
IFileArchive **retArchive = 0) override;
|
||||
|
||||
//! Adds an archive to the file system.
|
||||
bool addFileArchive(IFileArchive *archive) override;
|
||||
|
||||
//! move the hirarchy of the filesystem. moves sourceIndex relative up or down
|
||||
bool moveFileArchive(u32 sourceIndex, s32 relative) override;
|
||||
|
||||
//! Adds an external archive loader to the engine.
|
||||
void addArchiveLoader(IArchiveLoader *loader) override;
|
||||
|
||||
//! Returns the total number of archive loaders added.
|
||||
u32 getArchiveLoaderCount() const override;
|
||||
|
||||
//! Gets the archive loader by index.
|
||||
IArchiveLoader *getArchiveLoader(u32 index) const override;
|
||||
|
||||
//! gets the file archive count
|
||||
u32 getFileArchiveCount() const override;
|
||||
|
||||
//! gets an archive
|
||||
IFileArchive *getFileArchive(u32 index) override;
|
||||
|
||||
//! removes an archive from the file system.
|
||||
bool removeFileArchive(u32 index) override;
|
||||
|
||||
//! removes an archive from the file system.
|
||||
bool removeFileArchive(const io::path &filename) override;
|
||||
|
||||
//! Removes an archive from the file system.
|
||||
bool removeFileArchive(const IFileArchive *archive) override;
|
||||
|
||||
//! Returns the string of the current working directory
|
||||
const io::path &getWorkingDirectory() override;
|
||||
|
||||
//! Changes the current Working Directory to the string given.
|
||||
//! The string is operating system dependent. Under Windows it will look
|
||||
//! like this: "drive:\directory\sudirectory\"
|
||||
bool changeWorkingDirectoryTo(const io::path &newDirectory) override;
|
||||
|
||||
//! Converts a relative path to an absolute (unique) path, resolving symbolic links
|
||||
io::path getAbsolutePath(const io::path &filename) const override;
|
||||
|
||||
//! Returns the directory a file is located in.
|
||||
/** \param filename: The file to get the directory from */
|
||||
io::path getFileDir(const io::path &filename) const override;
|
||||
|
||||
//! Returns the base part of a filename, i.e. the name without the directory
|
||||
//! part. If no directory is prefixed, the full name is returned.
|
||||
/** \param filename: The file to get the basename from */
|
||||
io::path getFileBasename(const io::path &filename, bool keepExtension = true) const override;
|
||||
|
||||
//! flatten a path and file name for example: "/you/me/../." becomes "/you"
|
||||
io::path &flattenFilename(io::path &directory, const io::path &root = "/") const override;
|
||||
|
||||
//! Get the relative filename, relative to the given directory
|
||||
path getRelativeFilename(const path &filename, const path &directory) const override;
|
||||
|
||||
EFileSystemType setFileListSystem(EFileSystemType listType) override;
|
||||
|
||||
//! Creates a list of files and directories in the current working directory
|
||||
//! and returns it.
|
||||
IFileList *createFileList() override;
|
||||
|
||||
//! Creates an empty filelist
|
||||
IFileList *createEmptyFileList(const io::path &path, bool ignoreCase, bool ignorePaths) override;
|
||||
|
||||
//! determines if a file exists and would be able to be opened.
|
||||
bool existFile(const io::path &filename) const override;
|
||||
|
||||
private:
|
||||
//! Currently used FileSystemType
|
||||
EFileSystemType FileSystemType;
|
||||
//! WorkingDirectory for Native and Virtual filesystems
|
||||
io::path WorkingDirectory[2];
|
||||
//! currently attached ArchiveLoaders
|
||||
core::array<IArchiveLoader *> ArchiveLoader;
|
||||
//! currently attached Archives
|
||||
core::array<IFileArchive *> FileArchives;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace io
|
||||
401
src/CGLXManager.cpp
Normal file
401
src/CGLXManager.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
// Copyright (C) 2013 Christian Stehno
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
||||
|
||||
#include "CGLXManager.h"
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_GLX_MANAGER_
|
||||
|
||||
#include "os.h"
|
||||
|
||||
#define GL_GLEXT_LEGACY 1
|
||||
#define GLX_GLXEXT_LEGACY 1
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GL/glxext.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
CGLXManager::CGLXManager(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &videodata, int screennr) :
|
||||
Params(params), PrimaryContext(videodata), VisualInfo(0), glxFBConfig(0), GlxWin(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGLXManager");
|
||||
#endif
|
||||
|
||||
CurrentContext.OpenGLLinux.X11Display = PrimaryContext.OpenGLLinux.X11Display;
|
||||
|
||||
int major, minor;
|
||||
Display *display = (Display *)PrimaryContext.OpenGLLinux.X11Display;
|
||||
const bool isAvailableGLX = glXQueryExtension(display, &major, &minor);
|
||||
|
||||
if (isAvailableGLX && glXQueryVersion(display, &major, &minor)) {
|
||||
#if defined(GLX_VERSION_1_3)
|
||||
typedef GLXFBConfig *(*PFNGLXCHOOSEFBCONFIGPROC)(Display *dpy, int screen, const int *attrib_list, int *nelements);
|
||||
|
||||
PFNGLXCHOOSEFBCONFIGPROC glxChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddress(reinterpret_cast<const GLubyte *>("glXChooseFBConfig"));
|
||||
if (major == 1 && minor > 2 && glxChooseFBConfig) {
|
||||
os::Printer::log("GLX >= 1.3", ELL_DEBUG);
|
||||
// attribute array for the draw buffer
|
||||
int visualAttrBuffer[] = {
|
||||
GLX_RENDER_TYPE,
|
||||
GLX_RGBA_BIT,
|
||||
GLX_RED_SIZE,
|
||||
4,
|
||||
GLX_GREEN_SIZE,
|
||||
4,
|
||||
GLX_BLUE_SIZE,
|
||||
4,
|
||||
GLX_ALPHA_SIZE,
|
||||
Params.WithAlphaChannel ? 1 : 0,
|
||||
GLX_DEPTH_SIZE,
|
||||
Params.ZBufferBits, // 10,11
|
||||
GLX_DOUBLEBUFFER,
|
||||
Params.Doublebuffer ? True : False,
|
||||
GLX_STENCIL_SIZE,
|
||||
Params.Stencilbuffer ? 1 : 0,
|
||||
#if defined(GLX_VERSION_1_4) && defined(GLX_SAMPLE_BUFFERS) // we need to check the extension string!
|
||||
GLX_SAMPLE_BUFFERS,
|
||||
1,
|
||||
GLX_SAMPLES,
|
||||
Params.AntiAlias, // 18,19
|
||||
#elif defined(GLX_ARB_multisample)
|
||||
GLX_SAMPLE_BUFFERS_ARB,
|
||||
1,
|
||||
GLX_SAMPLES_ARB,
|
||||
Params.AntiAlias, // 18,19
|
||||
#elif defined(GLX_SGIS_multisample)
|
||||
GLX_SAMPLE_BUFFERS_SGIS,
|
||||
1,
|
||||
GLX_SAMPLES_SGIS,
|
||||
Params.AntiAlias, // 18,19
|
||||
#endif
|
||||
GLX_STEREO,
|
||||
Params.Stereobuffer ? True : False,
|
||||
None,
|
||||
};
|
||||
|
||||
GLXFBConfig *configList = 0;
|
||||
int nitems = 0;
|
||||
if (Params.AntiAlias < 2) {
|
||||
visualAttrBuffer[17] = 0;
|
||||
visualAttrBuffer[19] = 0;
|
||||
}
|
||||
// first round with unchanged values
|
||||
{
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (!configList && Params.AntiAlias) {
|
||||
while (!configList && (visualAttrBuffer[19] > 1)) {
|
||||
visualAttrBuffer[19] -= 1;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
}
|
||||
if (!configList) {
|
||||
visualAttrBuffer[17] = 0;
|
||||
visualAttrBuffer[19] = 0;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (configList) {
|
||||
os::Printer::log("No FSAA available.", ELL_WARNING);
|
||||
Params.AntiAlias = 0;
|
||||
} else {
|
||||
// reenable multisampling
|
||||
visualAttrBuffer[17] = 1;
|
||||
visualAttrBuffer[19] = Params.AntiAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Next try with flipped stencil buffer value
|
||||
// If the first round was with stencil flag it's now without
|
||||
// Other way round also makes sense because some configs
|
||||
// only have depth buffer combined with stencil buffer
|
||||
if (!configList) {
|
||||
if (Params.Stencilbuffer)
|
||||
os::Printer::log("No stencilbuffer available, disabling stencil shadows.", ELL_WARNING);
|
||||
Params.Stencilbuffer = !Params.Stencilbuffer;
|
||||
visualAttrBuffer[15] = Params.Stencilbuffer ? 1 : 0;
|
||||
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (!configList && Params.AntiAlias) {
|
||||
while (!configList && (visualAttrBuffer[19] > 1)) {
|
||||
visualAttrBuffer[19] -= 1;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
}
|
||||
if (!configList) {
|
||||
visualAttrBuffer[17] = 0;
|
||||
visualAttrBuffer[19] = 0;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (configList) {
|
||||
os::Printer::log("No FSAA available.", ELL_WARNING);
|
||||
Params.AntiAlias = 0;
|
||||
} else {
|
||||
// reenable multisampling
|
||||
visualAttrBuffer[17] = 1;
|
||||
visualAttrBuffer[19] = Params.AntiAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Next try without double buffer
|
||||
if (!configList && Params.Doublebuffer) {
|
||||
os::Printer::log("No doublebuffering available.", ELL_WARNING);
|
||||
Params.Doublebuffer = false;
|
||||
visualAttrBuffer[13] = GLX_DONT_CARE;
|
||||
Params.Stencilbuffer = false;
|
||||
visualAttrBuffer[15] = 0;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (!configList && Params.AntiAlias) {
|
||||
while (!configList && (visualAttrBuffer[19] > 1)) {
|
||||
visualAttrBuffer[19] -= 1;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
}
|
||||
if (!configList) {
|
||||
visualAttrBuffer[17] = 0;
|
||||
visualAttrBuffer[19] = 0;
|
||||
configList = glxChooseFBConfig(display, screennr, visualAttrBuffer, &nitems);
|
||||
if (configList) {
|
||||
os::Printer::log("No FSAA available.", ELL_WARNING);
|
||||
Params.AntiAlias = 0;
|
||||
} else {
|
||||
// reenable multisampling
|
||||
visualAttrBuffer[17] = 1;
|
||||
visualAttrBuffer[19] = Params.AntiAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (configList) {
|
||||
glxFBConfig = configList[0];
|
||||
XFree(configList);
|
||||
typedef XVisualInfo *(*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display *dpy, GLXFBConfig config);
|
||||
PFNGLXGETVISUALFROMFBCONFIGPROC glxGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)glXGetProcAddress(reinterpret_cast<const GLubyte *>("glXGetVisualFromFBConfig"));
|
||||
if (glxGetVisualFromFBConfig)
|
||||
VisualInfo = glxGetVisualFromFBConfig(display, (GLXFBConfig)glxFBConfig);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// attribute array for the draw buffer
|
||||
int visualAttrBuffer[] = {
|
||||
GLX_RGBA, GLX_USE_GL,
|
||||
GLX_RED_SIZE, 4,
|
||||
GLX_GREEN_SIZE, 4,
|
||||
GLX_BLUE_SIZE, 4,
|
||||
GLX_ALPHA_SIZE, Params.WithAlphaChannel ? 1 : 0,
|
||||
GLX_DEPTH_SIZE, Params.ZBufferBits,
|
||||
GLX_STENCIL_SIZE, Params.Stencilbuffer ? 1 : 0, // 12,13
|
||||
// The following attributes have no flags, but are
|
||||
// either present or not. As a no-op we use
|
||||
// GLX_USE_GL, which is silently ignored by glXChooseVisual
|
||||
Params.Doublebuffer ? GLX_DOUBLEBUFFER : GLX_USE_GL, // 14
|
||||
Params.Stereobuffer ? GLX_STEREO : GLX_USE_GL, // 15
|
||||
None,
|
||||
};
|
||||
|
||||
VisualInfo = glXChooseVisual(display, screennr, visualAttrBuffer);
|
||||
if (!VisualInfo) {
|
||||
if (Params.Stencilbuffer)
|
||||
os::Printer::log("No stencilbuffer available, disabling.", ELL_WARNING);
|
||||
Params.Stencilbuffer = !Params.Stencilbuffer;
|
||||
visualAttrBuffer[13] = Params.Stencilbuffer ? 1 : 0;
|
||||
|
||||
VisualInfo = glXChooseVisual(display, screennr, visualAttrBuffer);
|
||||
if (!VisualInfo && Params.Doublebuffer) {
|
||||
os::Printer::log("No doublebuffering available.", ELL_WARNING);
|
||||
Params.Doublebuffer = false;
|
||||
visualAttrBuffer[14] = GLX_USE_GL;
|
||||
VisualInfo = glXChooseVisual(display, screennr, visualAttrBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
os::Printer::log("No GLX support available. OpenGL driver will not work.", ELL_WARNING);
|
||||
}
|
||||
|
||||
CGLXManager::~CGLXManager()
|
||||
{
|
||||
}
|
||||
|
||||
bool CGLXManager::initialize(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &videodata)
|
||||
{
|
||||
// store params
|
||||
Params = params;
|
||||
|
||||
// set display
|
||||
CurrentContext.OpenGLLinux.X11Display = videodata.OpenGLLinux.X11Display;
|
||||
|
||||
// now get new window
|
||||
CurrentContext.OpenGLLinux.X11Window = videodata.OpenGLLinux.X11Window;
|
||||
if (!PrimaryContext.OpenGLLinux.X11Window) {
|
||||
PrimaryContext.OpenGLLinux.X11Window = CurrentContext.OpenGLLinux.X11Window;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGLXManager::terminate()
|
||||
{
|
||||
memset((void *)&CurrentContext, 0, sizeof(CurrentContext));
|
||||
}
|
||||
|
||||
bool CGLXManager::generateSurface()
|
||||
{
|
||||
if (glxFBConfig) {
|
||||
GlxWin = glXCreateWindow((Display *)CurrentContext.OpenGLLinux.X11Display, (GLXFBConfig)glxFBConfig, CurrentContext.OpenGLLinux.X11Window, NULL);
|
||||
if (!GlxWin) {
|
||||
os::Printer::log("Could not create GLX window.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentContext.OpenGLLinux.GLXWindow = GlxWin;
|
||||
} else {
|
||||
CurrentContext.OpenGLLinux.GLXWindow = CurrentContext.OpenGLLinux.X11Window;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGLXManager::destroySurface()
|
||||
{
|
||||
if (GlxWin)
|
||||
glXDestroyWindow((Display *)CurrentContext.OpenGLLinux.X11Display, GlxWin);
|
||||
}
|
||||
|
||||
#if defined(GLX_ARB_create_context)
|
||||
static int IrrIgnoreError(Display *display, XErrorEvent *event)
|
||||
{
|
||||
char msg[256];
|
||||
XGetErrorText(display, event->error_code, msg, 256);
|
||||
os::Printer::log("Ignoring an X error", msg, ELL_DEBUG);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CGLXManager::generateContext()
|
||||
{
|
||||
GLXContext context = 0;
|
||||
|
||||
if (glxFBConfig) {
|
||||
if (GlxWin) {
|
||||
#if defined(GLX_ARB_create_context)
|
||||
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC glxCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress(reinterpret_cast<const GLubyte *>("glXCreateContextAttribsARB"));
|
||||
|
||||
if (glxCreateContextAttribsARB) {
|
||||
os::Printer::log("GLX with GLX_ARB_create_context", ELL_DEBUG);
|
||||
int contextAttrBuffer[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
|
||||
// GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
None};
|
||||
XErrorHandler old = XSetErrorHandler(IrrIgnoreError);
|
||||
context = glxCreateContextAttribsARB((Display *)CurrentContext.OpenGLLinux.X11Display, (GLXFBConfig)glxFBConfig, NULL, True, contextAttrBuffer);
|
||||
XSetErrorHandler(old);
|
||||
// transparently fall back to legacy call
|
||||
}
|
||||
if (!context)
|
||||
#endif
|
||||
{
|
||||
// create glx context
|
||||
context = glXCreateNewContext((Display *)CurrentContext.OpenGLLinux.X11Display, (GLXFBConfig)glxFBConfig, GLX_RGBA_TYPE, NULL, True);
|
||||
if (!context) {
|
||||
os::Printer::log("Could not create GLX rendering context.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
os::Printer::log("GLX window was not properly created.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
context = glXCreateContext((Display *)CurrentContext.OpenGLLinux.X11Display, VisualInfo, NULL, True);
|
||||
if (!context) {
|
||||
os::Printer::log("Could not create GLX rendering context.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CurrentContext.OpenGLLinux.X11Context = context;
|
||||
return true;
|
||||
}
|
||||
|
||||
const SExposedVideoData &CGLXManager::getContext() const
|
||||
{
|
||||
return CurrentContext;
|
||||
}
|
||||
|
||||
bool CGLXManager::activateContext(const SExposedVideoData &videoData, bool restorePrimaryOnZero)
|
||||
{
|
||||
// TODO: handle restorePrimaryOnZero
|
||||
|
||||
if (videoData.OpenGLLinux.X11Window) {
|
||||
if (videoData.OpenGLLinux.X11Display && videoData.OpenGLLinux.X11Context) {
|
||||
if (!glXMakeCurrent((Display *)videoData.OpenGLLinux.X11Display, videoData.OpenGLLinux.GLXWindow, (GLXContext)videoData.OpenGLLinux.X11Context)) {
|
||||
os::Printer::log("Context activation failed.");
|
||||
return false;
|
||||
} else {
|
||||
CurrentContext.OpenGLLinux.GLXWindow = videoData.OpenGLLinux.GLXWindow;
|
||||
CurrentContext.OpenGLLinux.X11Window = videoData.OpenGLLinux.X11Window;
|
||||
CurrentContext.OpenGLLinux.X11Display = videoData.OpenGLLinux.X11Display;
|
||||
}
|
||||
} else {
|
||||
// in case we only got a window ID, try with the existing values for display and context
|
||||
if (!glXMakeCurrent((Display *)PrimaryContext.OpenGLLinux.X11Display, videoData.OpenGLLinux.GLXWindow, (GLXContext)PrimaryContext.OpenGLLinux.X11Context)) {
|
||||
os::Printer::log("Context activation failed.");
|
||||
return false;
|
||||
} else {
|
||||
CurrentContext.OpenGLLinux.GLXWindow = videoData.OpenGLLinux.GLXWindow;
|
||||
CurrentContext.OpenGLLinux.X11Window = videoData.OpenGLLinux.X11Window;
|
||||
CurrentContext.OpenGLLinux.X11Display = PrimaryContext.OpenGLLinux.X11Display;
|
||||
}
|
||||
}
|
||||
} else if (!restorePrimaryOnZero && !videoData.OpenGLLinux.X11Window && !videoData.OpenGLLinux.X11Display) {
|
||||
if (!glXMakeCurrent((Display *)PrimaryContext.OpenGLLinux.X11Display, None, NULL)) {
|
||||
os::Printer::log("Render Context reset failed.");
|
||||
return false;
|
||||
}
|
||||
CurrentContext.OpenGLLinux.X11Window = 0;
|
||||
CurrentContext.OpenGLLinux.X11Display = 0;
|
||||
}
|
||||
// set back to main context
|
||||
else if (CurrentContext.OpenGLLinux.X11Display != PrimaryContext.OpenGLLinux.X11Display) {
|
||||
if (!glXMakeCurrent((Display *)PrimaryContext.OpenGLLinux.X11Display, PrimaryContext.OpenGLLinux.X11Window, (GLXContext)PrimaryContext.OpenGLLinux.X11Context)) {
|
||||
os::Printer::log("Context activation failed.");
|
||||
return false;
|
||||
} else {
|
||||
CurrentContext = PrimaryContext;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGLXManager::destroyContext()
|
||||
{
|
||||
if (CurrentContext.OpenGLLinux.X11Context) {
|
||||
if (GlxWin) {
|
||||
if (!glXMakeContextCurrent((Display *)CurrentContext.OpenGLLinux.X11Display, None, None, NULL))
|
||||
os::Printer::log("Could not release glx context.", ELL_WARNING);
|
||||
} else {
|
||||
if (!glXMakeCurrent((Display *)CurrentContext.OpenGLLinux.X11Display, None, NULL))
|
||||
os::Printer::log("Could not release glx context.", ELL_WARNING);
|
||||
}
|
||||
glXDestroyContext((Display *)CurrentContext.OpenGLLinux.X11Display, (GLXContext)CurrentContext.OpenGLLinux.X11Context);
|
||||
}
|
||||
}
|
||||
|
||||
void *CGLXManager::getProcAddress(const std::string &procName)
|
||||
{
|
||||
return (void *)glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.c_str()));
|
||||
}
|
||||
|
||||
bool CGLXManager::swapBuffers()
|
||||
{
|
||||
glXSwapBuffers((Display *)CurrentContext.OpenGLLinux.X11Display, CurrentContext.OpenGLLinux.GLXWindow);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
76
src/CGLXManager.h
Normal file
76
src/CGLXManager.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2013 Christian Stehno
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_GLX_MANAGER_
|
||||
|
||||
#include "SIrrCreationParameters.h"
|
||||
#include "SExposedVideoData.h"
|
||||
#include "IContextManager.h"
|
||||
#include "SColor.h"
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
// we can't include glx.h here, because gl.h has incompatible types with ogl es headers and it
|
||||
// cause redefinition errors, thats why we use ugly trick with void* types and casts.
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
// GLX manager.
|
||||
class CGLXManager : public IContextManager
|
||||
{
|
||||
public:
|
||||
//! Constructor.
|
||||
CGLXManager(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &videodata, int screennr);
|
||||
|
||||
//! Destructor
|
||||
~CGLXManager();
|
||||
|
||||
// Initialize
|
||||
bool initialize(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &data) override;
|
||||
|
||||
// Terminate
|
||||
void terminate() override;
|
||||
|
||||
// Create surface.
|
||||
bool generateSurface() override;
|
||||
|
||||
// Destroy surface.
|
||||
void destroySurface() override;
|
||||
|
||||
// Create context.
|
||||
bool generateContext() override;
|
||||
|
||||
// Destroy context.
|
||||
void destroyContext() override;
|
||||
|
||||
//! Get current context
|
||||
const SExposedVideoData &getContext() const override;
|
||||
|
||||
//! Change render context, disable old and activate new defined by videoData
|
||||
bool activateContext(const SExposedVideoData &videoData, bool restorePrimaryOnZero) override;
|
||||
|
||||
// Get procedure address.
|
||||
void *getProcAddress(const std::string &procName) override;
|
||||
|
||||
// Swap buffers.
|
||||
bool swapBuffers() override;
|
||||
|
||||
XVisualInfo *getVisual() const { return VisualInfo; } // return XVisualInfo
|
||||
|
||||
private:
|
||||
SIrrlichtCreationParameters Params;
|
||||
SExposedVideoData PrimaryContext;
|
||||
SExposedVideoData CurrentContext;
|
||||
XVisualInfo *VisualInfo;
|
||||
void *glxFBConfig; // GLXFBConfig
|
||||
XID GlxWin; // GLXWindow
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
500
src/CGUIButton.cpp
Normal file
500
src/CGUIButton.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIButton.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIButton::CGUIButton(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle, bool noclip) :
|
||||
IGUIButton(environment, parent, id, rectangle),
|
||||
SpriteBank(0), OverrideFont(0),
|
||||
OverrideColorEnabled(false), OverrideColor(video::SColor(101, 255, 255, 255)),
|
||||
ClickTime(0), HoverTime(0), FocusTime(0),
|
||||
ClickShiftState(false), ClickControlState(false),
|
||||
IsPushButton(false), Pressed(false),
|
||||
UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIButton");
|
||||
#endif
|
||||
setNotClipped(noclip);
|
||||
|
||||
// This element can be tabbed.
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIButton::~CGUIButton()
|
||||
{
|
||||
if (OverrideFont)
|
||||
OverrideFont->drop();
|
||||
|
||||
if (SpriteBank)
|
||||
SpriteBank->drop();
|
||||
}
|
||||
|
||||
//! Sets if the images should be scaled to fit the button
|
||||
void CGUIButton::setScaleImage(bool scaleImage)
|
||||
{
|
||||
ScaleImage = scaleImage;
|
||||
}
|
||||
|
||||
//! Returns whether the button scale the used images
|
||||
bool CGUIButton::isScalingImage() const
|
||||
{
|
||||
return ScaleImage;
|
||||
}
|
||||
|
||||
//! Sets if the button should use the skin to draw its border
|
||||
void CGUIButton::setDrawBorder(bool border)
|
||||
{
|
||||
DrawBorder = border;
|
||||
}
|
||||
|
||||
void CGUIButton::setSpriteBank(IGUISpriteBank *sprites)
|
||||
{
|
||||
if (sprites)
|
||||
sprites->grab();
|
||||
|
||||
if (SpriteBank)
|
||||
SpriteBank->drop();
|
||||
|
||||
SpriteBank = sprites;
|
||||
}
|
||||
|
||||
void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
|
||||
{
|
||||
ButtonSprites[(u32)state].Index = index;
|
||||
ButtonSprites[(u32)state].Color = color;
|
||||
ButtonSprites[(u32)state].Loop = loop;
|
||||
ButtonSprites[(u32)state].Scale = scale;
|
||||
}
|
||||
|
||||
//! Get the sprite-index for the given state or -1 when no sprite is set
|
||||
s32 CGUIButton::getSpriteIndex(EGUI_BUTTON_STATE state) const
|
||||
{
|
||||
return ButtonSprites[(u32)state].Index;
|
||||
}
|
||||
|
||||
//! Get the sprite color for the given state. Color is only used when a sprite is set.
|
||||
video::SColor CGUIButton::getSpriteColor(EGUI_BUTTON_STATE state) const
|
||||
{
|
||||
return ButtonSprites[(u32)state].Color;
|
||||
}
|
||||
|
||||
//! Returns if the sprite in the given state does loop
|
||||
bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
|
||||
{
|
||||
return ButtonSprites[(u32)state].Loop;
|
||||
}
|
||||
|
||||
//! Returns if the sprite in the given state is scaled
|
||||
bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
|
||||
{
|
||||
return ButtonSprites[(u32)state].Scale;
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUIButton::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (!isEnabled())
|
||||
return IGUIElement::OnEvent(event);
|
||||
|
||||
switch (event.EventType) {
|
||||
case EET_KEY_INPUT_EVENT:
|
||||
if (event.KeyInput.PressedDown &&
|
||||
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) {
|
||||
if (!IsPushButton)
|
||||
setPressed(true);
|
||||
else
|
||||
setPressed(!Pressed);
|
||||
|
||||
return true;
|
||||
}
|
||||
if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE) {
|
||||
setPressed(false);
|
||||
return true;
|
||||
} else if (!event.KeyInput.PressedDown && Pressed &&
|
||||
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) {
|
||||
|
||||
if (!IsPushButton)
|
||||
setPressed(false);
|
||||
|
||||
if (Parent) {
|
||||
ClickShiftState = event.KeyInput.Shift;
|
||||
ClickControlState = event.KeyInput.Control;
|
||||
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case EET_GUI_EVENT:
|
||||
if (event.GUIEvent.Caller == this) {
|
||||
if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
|
||||
if (!IsPushButton)
|
||||
setPressed(false);
|
||||
FocusTime = os::Timer::getTime();
|
||||
} else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED) {
|
||||
FocusTime = os::Timer::getTime();
|
||||
} else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
|
||||
HoverTime = os::Timer::getTime();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
if (!IsPushButton)
|
||||
setPressed(true);
|
||||
|
||||
return true;
|
||||
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
||||
bool wasPressed = Pressed;
|
||||
|
||||
if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
|
||||
if (!IsPushButton)
|
||||
setPressed(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsPushButton)
|
||||
setPressed(false);
|
||||
else {
|
||||
setPressed(!Pressed);
|
||||
}
|
||||
|
||||
if ((!IsPushButton && wasPressed && Parent) ||
|
||||
(IsPushButton && wasPressed != Pressed)) {
|
||||
ClickShiftState = event.MouseInput.Shift;
|
||||
ClickControlState = event.MouseInput.Control;
|
||||
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIButton::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
|
||||
if (DrawBorder) {
|
||||
if (!Pressed) {
|
||||
skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);
|
||||
} else {
|
||||
skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);
|
||||
}
|
||||
}
|
||||
|
||||
const core::position2di buttonCenter(AbsoluteRect.getCenter());
|
||||
|
||||
EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);
|
||||
if (ButtonImages[(u32)imageState].Texture) {
|
||||
core::position2d<s32> pos(buttonCenter);
|
||||
core::rect<s32> sourceRect(ButtonImages[(u32)imageState].SourceRect);
|
||||
if (sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0)
|
||||
sourceRect = core::rect<s32>(core::position2di(0, 0), ButtonImages[(u32)imageState].Texture->getOriginalSize());
|
||||
|
||||
pos.X -= sourceRect.getWidth() / 2;
|
||||
pos.Y -= sourceRect.getHeight() / 2;
|
||||
|
||||
if (Pressed) {
|
||||
// Create a pressed-down effect by moving the image when it looks identical to the unpressed state image
|
||||
EGUI_BUTTON_IMAGE_STATE unpressedState = getImageState(false);
|
||||
if (unpressedState == imageState || ButtonImages[(u32)imageState] == ButtonImages[(u32)unpressedState]) {
|
||||
pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X);
|
||||
pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y);
|
||||
}
|
||||
}
|
||||
|
||||
driver->draw2DImage(ButtonImages[(u32)imageState].Texture,
|
||||
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
|
||||
sourceRect, &AbsoluteClippingRect,
|
||||
0, UseAlphaChannel);
|
||||
}
|
||||
|
||||
if (SpriteBank) {
|
||||
core::position2di pos(buttonCenter);
|
||||
if (Pressed) {
|
||||
pos.X += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X);
|
||||
pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y);
|
||||
}
|
||||
|
||||
if (isEnabled()) {
|
||||
// pressed / unpressed animation
|
||||
EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP;
|
||||
drawSprite(state, ClickTime, pos);
|
||||
|
||||
// focused / unfocused animation
|
||||
state = Environment->hasFocus(this) ? EGBS_BUTTON_FOCUSED : EGBS_BUTTON_NOT_FOCUSED;
|
||||
drawSprite(state, FocusTime, pos);
|
||||
|
||||
// mouse over / off animation
|
||||
state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
|
||||
drawSprite(state, HoverTime, pos);
|
||||
} else {
|
||||
// draw disabled
|
||||
drawSprite(EGBS_BUTTON_DISABLED, 0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (Text.size()) {
|
||||
IGUIFont *font = getActiveFont();
|
||||
|
||||
core::rect<s32> rect = AbsoluteRect;
|
||||
if (Pressed) {
|
||||
rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);
|
||||
rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);
|
||||
}
|
||||
|
||||
if (font)
|
||||
font->draw(Text.c_str(), rect,
|
||||
getActiveColor(),
|
||||
true, true, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di ¢er)
|
||||
{
|
||||
u32 stateIdx = (u32)state;
|
||||
|
||||
if (ButtonSprites[stateIdx].Index != -1) {
|
||||
if (ButtonSprites[stateIdx].Scale) {
|
||||
const video::SColor colors[] = {ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color};
|
||||
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect,
|
||||
&AbsoluteClippingRect, colors,
|
||||
os::Timer::getTime() - startTime, ButtonSprites[stateIdx].Loop);
|
||||
} else {
|
||||
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
|
||||
&AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(),
|
||||
ButtonSprites[stateIdx].Loop, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const
|
||||
{
|
||||
// figure state we should have
|
||||
EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;
|
||||
bool focused = Environment->hasFocus(this);
|
||||
bool mouseOver = static_cast<const IGUIElement *>(Environment->getHovered()) == this; // (static cast for Borland)
|
||||
if (isEnabled()) {
|
||||
if (pressed) {
|
||||
if (focused && mouseOver)
|
||||
state = EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER;
|
||||
else if (focused)
|
||||
state = EGBIS_IMAGE_DOWN_FOCUSED;
|
||||
else if (mouseOver)
|
||||
state = EGBIS_IMAGE_DOWN_MOUSEOVER;
|
||||
else
|
||||
state = EGBIS_IMAGE_DOWN;
|
||||
} else // !pressed
|
||||
{
|
||||
if (focused && mouseOver)
|
||||
state = EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER;
|
||||
else if (focused)
|
||||
state = EGBIS_IMAGE_UP_FOCUSED;
|
||||
else if (mouseOver)
|
||||
state = EGBIS_IMAGE_UP_MOUSEOVER;
|
||||
else
|
||||
state = EGBIS_IMAGE_UP;
|
||||
}
|
||||
}
|
||||
|
||||
// find a compatible state that has images
|
||||
while (state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture) {
|
||||
switch (state) {
|
||||
case EGBIS_IMAGE_UP_FOCUSED:
|
||||
state = EGBIS_IMAGE_UP_MOUSEOVER;
|
||||
break;
|
||||
case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER:
|
||||
state = EGBIS_IMAGE_UP_FOCUSED;
|
||||
break;
|
||||
case EGBIS_IMAGE_DOWN_MOUSEOVER:
|
||||
state = EGBIS_IMAGE_DOWN;
|
||||
break;
|
||||
case EGBIS_IMAGE_DOWN_FOCUSED:
|
||||
state = EGBIS_IMAGE_DOWN_MOUSEOVER;
|
||||
break;
|
||||
case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER:
|
||||
state = EGBIS_IMAGE_DOWN_FOCUSED;
|
||||
break;
|
||||
case EGBIS_IMAGE_DISABLED:
|
||||
if (pressed)
|
||||
state = EGBIS_IMAGE_DOWN;
|
||||
else
|
||||
state = EGBIS_IMAGE_UP;
|
||||
break;
|
||||
default:
|
||||
state = EGBIS_IMAGE_UP;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
//! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
|
||||
void CGUIButton::setOverrideFont(IGUIFont *font)
|
||||
{
|
||||
if (OverrideFont == font)
|
||||
return;
|
||||
|
||||
if (OverrideFont)
|
||||
OverrideFont->drop();
|
||||
|
||||
OverrideFont = font;
|
||||
|
||||
if (OverrideFont)
|
||||
OverrideFont->grab();
|
||||
}
|
||||
|
||||
//! Gets the override font (if any)
|
||||
IGUIFont *CGUIButton::getOverrideFont() const
|
||||
{
|
||||
return OverrideFont;
|
||||
}
|
||||
|
||||
//! Get the font which is used right now for drawing
|
||||
IGUIFont *CGUIButton::getActiveFont() const
|
||||
{
|
||||
if (OverrideFont)
|
||||
return OverrideFont;
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin)
|
||||
return skin->getFont(EGDF_BUTTON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Sets another color for the text.
|
||||
void CGUIButton::setOverrideColor(video::SColor color)
|
||||
{
|
||||
OverrideColor = color;
|
||||
OverrideColorEnabled = true;
|
||||
}
|
||||
|
||||
video::SColor CGUIButton::getOverrideColor() const
|
||||
{
|
||||
return OverrideColor;
|
||||
}
|
||||
|
||||
irr::video::SColor CGUIButton::getActiveColor() const
|
||||
{
|
||||
if (OverrideColorEnabled)
|
||||
return OverrideColor;
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin)
|
||||
return OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT);
|
||||
return OverrideColor;
|
||||
}
|
||||
|
||||
void CGUIButton::enableOverrideColor(bool enable)
|
||||
{
|
||||
OverrideColorEnabled = enable;
|
||||
}
|
||||
|
||||
bool CGUIButton::isOverrideColorEnabled() const
|
||||
{
|
||||
return OverrideColorEnabled;
|
||||
}
|
||||
|
||||
void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture *image, const core::rect<s32> &sourceRect)
|
||||
{
|
||||
if (state >= EGBIS_COUNT)
|
||||
return;
|
||||
|
||||
if (image)
|
||||
image->grab();
|
||||
|
||||
u32 stateIdx = (u32)state;
|
||||
if (ButtonImages[stateIdx].Texture)
|
||||
ButtonImages[stateIdx].Texture->drop();
|
||||
|
||||
ButtonImages[stateIdx].Texture = image;
|
||||
ButtonImages[stateIdx].SourceRect = sourceRect;
|
||||
}
|
||||
|
||||
//! Sets if the button should behave like a push button. Which means it
|
||||
//! can be in two states: Normal or Pressed. With a click on the button,
|
||||
//! the user can change the state of the button.
|
||||
void CGUIButton::setIsPushButton(bool isPushButton)
|
||||
{
|
||||
IsPushButton = isPushButton;
|
||||
}
|
||||
|
||||
//! Returns if the button is currently pressed
|
||||
bool CGUIButton::isPressed() const
|
||||
{
|
||||
return Pressed;
|
||||
}
|
||||
|
||||
//! Sets the pressed state of the button if this is a pushbutton
|
||||
void CGUIButton::setPressed(bool pressed)
|
||||
{
|
||||
if (Pressed != pressed) {
|
||||
ClickTime = os::Timer::getTime();
|
||||
Pressed = pressed;
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns whether the button is a push button
|
||||
bool CGUIButton::isPushButton() const
|
||||
{
|
||||
return IsPushButton;
|
||||
}
|
||||
|
||||
//! Sets if the alpha channel should be used for drawing images on the button (default is false)
|
||||
void CGUIButton::setUseAlphaChannel(bool useAlphaChannel)
|
||||
{
|
||||
UseAlphaChannel = useAlphaChannel;
|
||||
}
|
||||
|
||||
//! Returns if the alpha channel should be used for drawing images on the button
|
||||
bool CGUIButton::isAlphaChannelUsed() const
|
||||
{
|
||||
return UseAlphaChannel;
|
||||
}
|
||||
|
||||
bool CGUIButton::isDrawingBorder() const
|
||||
{
|
||||
return DrawBorder;
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
241
src/CGUIButton.h
Normal file
241
src/CGUIButton.h
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIButton.h"
|
||||
#include "IGUISpriteBank.h"
|
||||
#include "ITexture.h"
|
||||
#include "SColor.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIButton : public IGUIButton
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIButton(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle, bool noclip = false);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIButton();
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
|
||||
void setOverrideFont(IGUIFont *font = 0) override;
|
||||
|
||||
//! Gets the override font (if any)
|
||||
IGUIFont *getOverrideFont() const override;
|
||||
|
||||
//! Get the font which is used right now for drawing
|
||||
IGUIFont *getActiveFont() const override;
|
||||
|
||||
//! Sets another color for the button text.
|
||||
void setOverrideColor(video::SColor color) override;
|
||||
|
||||
//! Gets the override color
|
||||
video::SColor getOverrideColor(void) const override;
|
||||
|
||||
//! Gets the currently used text color
|
||||
video::SColor getActiveColor() const override;
|
||||
|
||||
//! Sets if the button text should use the override color or the color in the gui skin.
|
||||
void enableOverrideColor(bool enable) override;
|
||||
|
||||
//! Checks if an override color is enabled
|
||||
bool isOverrideColorEnabled(void) const override;
|
||||
|
||||
//! Sets an image which should be displayed on the button when it is in the given state.
|
||||
void setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture *image = 0, const core::rect<s32> &sourceRect = core::rect<s32>(0, 0, 0, 0)) override;
|
||||
|
||||
//! Sets an image which should be displayed on the button when it is in normal state.
|
||||
void setImage(video::ITexture *image = 0) override
|
||||
{
|
||||
setImage(EGBIS_IMAGE_UP, image);
|
||||
}
|
||||
|
||||
//! Sets an image which should be displayed on the button when it is in normal state.
|
||||
void setImage(video::ITexture *image, const core::rect<s32> &pos) override
|
||||
{
|
||||
setImage(EGBIS_IMAGE_UP, image, pos);
|
||||
}
|
||||
|
||||
//! Sets an image which should be displayed on the button when it is in pressed state.
|
||||
void setPressedImage(video::ITexture *image = 0) override
|
||||
{
|
||||
setImage(EGBIS_IMAGE_DOWN, image);
|
||||
}
|
||||
|
||||
//! Sets an image which should be displayed on the button when it is in pressed state.
|
||||
void setPressedImage(video::ITexture *image, const core::rect<s32> &pos) override
|
||||
{
|
||||
setImage(EGBIS_IMAGE_DOWN, image, pos);
|
||||
}
|
||||
|
||||
//! Sets the sprite bank used by the button
|
||||
void setSpriteBank(IGUISpriteBank *bank = 0) override;
|
||||
|
||||
//! Sets the animated sprite for a specific button state
|
||||
/** \param index: Number of the sprite within the sprite bank, use -1 for no sprite
|
||||
\param state: State of the button to set the sprite for
|
||||
\param index: The sprite number from the current sprite bank
|
||||
\param color: The color of the sprite
|
||||
*/
|
||||
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index,
|
||||
video::SColor color = video::SColor(255, 255, 255, 255),
|
||||
bool loop = false, bool scale = false) override;
|
||||
|
||||
//! Get the sprite-index for the given state or -1 when no sprite is set
|
||||
s32 getSpriteIndex(EGUI_BUTTON_STATE state) const override;
|
||||
|
||||
//! Get the sprite color for the given state. Color is only used when a sprite is set.
|
||||
video::SColor getSpriteColor(EGUI_BUTTON_STATE state) const override;
|
||||
|
||||
//! Returns if the sprite in the given state does loop
|
||||
bool getSpriteLoop(EGUI_BUTTON_STATE state) const override;
|
||||
|
||||
//! Returns if the sprite in the given state is scaled
|
||||
bool getSpriteScale(EGUI_BUTTON_STATE state) const override;
|
||||
|
||||
//! Sets if the button should behave like a push button. Which means it
|
||||
//! can be in two states: Normal or Pressed. With a click on the button,
|
||||
//! the user can change the state of the button.
|
||||
void setIsPushButton(bool isPushButton = true) override;
|
||||
|
||||
//! Checks whether the button is a push button
|
||||
bool isPushButton() const override;
|
||||
|
||||
//! Sets the pressed state of the button if this is a pushbutton
|
||||
void setPressed(bool pressed = true) override;
|
||||
|
||||
//! Returns if the button is currently pressed
|
||||
bool isPressed() const override;
|
||||
|
||||
//! Sets if the button should use the skin to draw its border
|
||||
void setDrawBorder(bool border = true) override;
|
||||
|
||||
//! Checks if the button face and border are being drawn
|
||||
bool isDrawingBorder() const override;
|
||||
|
||||
//! Sets if the alpha channel should be used for drawing images on the button (default is false)
|
||||
void setUseAlphaChannel(bool useAlphaChannel = true) override;
|
||||
|
||||
//! Checks if the alpha channel should be used for drawing images on the button
|
||||
bool isAlphaChannelUsed() const override;
|
||||
|
||||
//! Sets if the button should scale the button images to fit
|
||||
void setScaleImage(bool scaleImage = true) override;
|
||||
|
||||
//! Checks whether the button scales the used images
|
||||
bool isScalingImage() const override;
|
||||
|
||||
//! Get if the shift key was pressed in last EGET_BUTTON_CLICKED event
|
||||
bool getClickShiftState() const override
|
||||
{
|
||||
return ClickShiftState;
|
||||
}
|
||||
|
||||
//! Get if the control key was pressed in last EGET_BUTTON_CLICKED event
|
||||
bool getClickControlState() const override
|
||||
{
|
||||
return ClickControlState;
|
||||
}
|
||||
|
||||
protected:
|
||||
void drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di ¢er);
|
||||
EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const;
|
||||
|
||||
private:
|
||||
struct ButtonSprite
|
||||
{
|
||||
ButtonSprite() :
|
||||
Index(-1), Loop(false), Scale(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const ButtonSprite &other) const
|
||||
{
|
||||
return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale;
|
||||
}
|
||||
|
||||
s32 Index;
|
||||
video::SColor Color;
|
||||
bool Loop;
|
||||
bool Scale;
|
||||
};
|
||||
|
||||
ButtonSprite ButtonSprites[EGBS_COUNT];
|
||||
IGUISpriteBank *SpriteBank;
|
||||
|
||||
struct ButtonImage
|
||||
{
|
||||
ButtonImage() :
|
||||
Texture(0), SourceRect(core::rect<s32>(0, 0, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
ButtonImage(const ButtonImage &other) :
|
||||
Texture(0), SourceRect(core::rect<s32>(0, 0, 0, 0))
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
~ButtonImage()
|
||||
{
|
||||
if (Texture)
|
||||
Texture->drop();
|
||||
}
|
||||
|
||||
ButtonImage &operator=(const ButtonImage &other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if (other.Texture)
|
||||
other.Texture->grab();
|
||||
if (Texture)
|
||||
Texture->drop();
|
||||
Texture = other.Texture;
|
||||
SourceRect = other.SourceRect;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ButtonImage &other) const
|
||||
{
|
||||
return Texture == other.Texture && SourceRect == other.SourceRect;
|
||||
}
|
||||
|
||||
video::ITexture *Texture;
|
||||
core::rect<s32> SourceRect;
|
||||
};
|
||||
|
||||
ButtonImage ButtonImages[EGBIS_COUNT];
|
||||
|
||||
IGUIFont *OverrideFont;
|
||||
|
||||
bool OverrideColorEnabled;
|
||||
video::SColor OverrideColor;
|
||||
|
||||
u32 ClickTime, HoverTime, FocusTime;
|
||||
|
||||
bool ClickShiftState;
|
||||
bool ClickControlState;
|
||||
|
||||
bool IsPushButton;
|
||||
bool Pressed;
|
||||
bool UseAlphaChannel;
|
||||
bool DrawBorder;
|
||||
bool ScaleImage;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
198
src/CGUICheckBox.cpp
Normal file
198
src/CGUICheckBox.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUICheckBox.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUICheckBox::CGUICheckBox(bool checked, IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect<s32> rectangle) :
|
||||
IGUICheckBox(environment, parent, id, rectangle), CheckTime(0), Pressed(false), Checked(checked), Border(false), Background(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUICheckBox");
|
||||
#endif
|
||||
|
||||
// this element can be tabbed into
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUICheckBox::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
switch (event.EventType) {
|
||||
case EET_KEY_INPUT_EVENT:
|
||||
if (event.KeyInput.PressedDown &&
|
||||
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) {
|
||||
Pressed = true;
|
||||
return true;
|
||||
} else if (Pressed && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE) {
|
||||
Pressed = false;
|
||||
return true;
|
||||
} else if (!event.KeyInput.PressedDown && Pressed &&
|
||||
(event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) {
|
||||
Pressed = false;
|
||||
if (Parent) {
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
Checked = !Checked;
|
||||
newEvent.GUIEvent.EventType = EGET_CHECKBOX_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case EET_GUI_EVENT:
|
||||
if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
|
||||
if (event.GUIEvent.Caller == this)
|
||||
Pressed = false;
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
Pressed = true;
|
||||
CheckTime = os::Timer::getTime();
|
||||
return true;
|
||||
} else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
||||
bool wasPressed = Pressed;
|
||||
Pressed = false;
|
||||
|
||||
if (wasPressed && Parent) {
|
||||
if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
|
||||
Pressed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
Checked = !Checked;
|
||||
newEvent.GUIEvent.EventType = EGET_CHECKBOX_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUICheckBox::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin) {
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
// draw background
|
||||
if (Background) {
|
||||
video::SColor bgColor = skin->getColor(gui::EGDC_3D_FACE);
|
||||
driver->draw2DRectangle(bgColor, frameRect, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
// draw the border
|
||||
if (Border) {
|
||||
skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
|
||||
frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
frameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
}
|
||||
|
||||
const s32 height = skin->getSize(EGDS_CHECK_BOX_WIDTH);
|
||||
|
||||
// the rectangle around the "checked" area.
|
||||
core::rect<s32> checkRect(frameRect.UpperLeftCorner.X,
|
||||
((frameRect.getHeight() - height) / 2) + frameRect.UpperLeftCorner.Y,
|
||||
0, 0);
|
||||
|
||||
checkRect.LowerRightCorner.X = checkRect.UpperLeftCorner.X + height;
|
||||
checkRect.LowerRightCorner.Y = checkRect.UpperLeftCorner.Y + height;
|
||||
|
||||
EGUI_DEFAULT_COLOR col = EGDC_GRAY_EDITABLE;
|
||||
if (isEnabled())
|
||||
col = Pressed ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE;
|
||||
skin->draw3DSunkenPane(this, skin->getColor(col),
|
||||
false, true, checkRect, &AbsoluteClippingRect);
|
||||
|
||||
// the checked icon
|
||||
if (Checked) {
|
||||
skin->drawIcon(this, EGDI_CHECK_BOX_CHECKED, checkRect.getCenter(),
|
||||
CheckTime, os::Timer::getTime(), false, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
// associated text
|
||||
if (Text.size()) {
|
||||
checkRect = frameRect;
|
||||
checkRect.UpperLeftCorner.X += height + 5;
|
||||
|
||||
IGUIFont *font = skin->getFont();
|
||||
if (font) {
|
||||
font->draw(Text.c_str(), checkRect,
|
||||
skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), false, true, &AbsoluteClippingRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! set if box is checked
|
||||
void CGUICheckBox::setChecked(bool checked)
|
||||
{
|
||||
Checked = checked;
|
||||
}
|
||||
|
||||
//! returns if box is checked
|
||||
bool CGUICheckBox::isChecked() const
|
||||
{
|
||||
return Checked;
|
||||
}
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void CGUICheckBox::setDrawBackground(bool draw)
|
||||
{
|
||||
Background = draw;
|
||||
}
|
||||
|
||||
//! Checks if background drawing is enabled
|
||||
bool CGUICheckBox::isDrawBackgroundEnabled() const
|
||||
{
|
||||
return Background;
|
||||
}
|
||||
|
||||
//! Sets whether to draw the border
|
||||
void CGUICheckBox::setDrawBorder(bool draw)
|
||||
{
|
||||
Border = draw;
|
||||
}
|
||||
|
||||
//! Checks if border drawing is enabled
|
||||
bool CGUICheckBox::isDrawBorderEnabled() const
|
||||
{
|
||||
return Border;
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
55
src/CGUICheckBox.h
Normal file
55
src/CGUICheckBox.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUICheckBox.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUICheckBox : public IGUICheckBox
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUICheckBox(bool checked, IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect<s32> rectangle);
|
||||
|
||||
//! set if box is checked
|
||||
void setChecked(bool checked) override;
|
||||
|
||||
//! returns if box is checked
|
||||
bool isChecked() const override;
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void setDrawBackground(bool draw) override;
|
||||
|
||||
//! Checks if background drawing is enabled
|
||||
/** \return true if background drawing is enabled, false otherwise */
|
||||
bool isDrawBackgroundEnabled() const override;
|
||||
|
||||
//! Sets whether to draw the border
|
||||
void setDrawBorder(bool draw) override;
|
||||
|
||||
//! Checks if border drawing is enabled
|
||||
/** \return true if border drawing is enabled, false otherwise */
|
||||
bool isDrawBorderEnabled() const override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
u32 CheckTime;
|
||||
bool Pressed;
|
||||
bool Checked;
|
||||
bool Border;
|
||||
bool Background;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
463
src/CGUIComboBox.cpp
Normal file
463
src/CGUIComboBox.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIComboBox.h"
|
||||
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IGUIButton.h"
|
||||
#include "CGUIListBox.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIComboBox::CGUIComboBox(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle) :
|
||||
IGUIComboBox(environment, parent, id, rectangle),
|
||||
ListButton(nullptr), SelectedText(nullptr), ListBox(nullptr), LastFocus(nullptr),
|
||||
Selected(-1), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), MaxSelectionRows(5), HasFocus(false),
|
||||
ActiveFont(nullptr)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIComboBox");
|
||||
#endif
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
ListButton = Environment->addButton(core::recti(0, 0, 1, 1), this, -1, L"");
|
||||
if (skin && skin->getSpriteBank()) {
|
||||
ListButton->setSpriteBank(skin->getSpriteBank());
|
||||
ListButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(EGDC_WINDOW_SYMBOL));
|
||||
ListButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(EGDC_WINDOW_SYMBOL));
|
||||
}
|
||||
ListButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
ListButton->setSubElement(true);
|
||||
ListButton->setTabStop(false);
|
||||
|
||||
SelectedText = Environment->addStaticText(L"", core::recti(0, 0, 1, 1), false, false, this, -1, false);
|
||||
SelectedText->setSubElement(true);
|
||||
SelectedText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
SelectedText->setTextAlignment(EGUIA_UPPERLEFT, EGUIA_CENTER);
|
||||
if (skin)
|
||||
SelectedText->setOverrideColor(skin->getColor(EGDC_BUTTON_TEXT));
|
||||
SelectedText->enableOverrideColor(true);
|
||||
|
||||
updateListButtonWidth(skin ? skin->getSize(EGDS_SCROLLBAR_SIZE) : 15);
|
||||
|
||||
// this element can be tabbed to
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
}
|
||||
|
||||
void CGUIComboBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
|
||||
{
|
||||
HAlign = horizontal;
|
||||
VAlign = vertical;
|
||||
SelectedText->setTextAlignment(horizontal, vertical);
|
||||
}
|
||||
|
||||
//! Set the maximal number of rows for the selection listbox
|
||||
void CGUIComboBox::setMaxSelectionRows(u32 max)
|
||||
{
|
||||
MaxSelectionRows = max;
|
||||
|
||||
// force recalculation of open listbox
|
||||
if (ListBox) {
|
||||
openCloseMenu();
|
||||
openCloseMenu();
|
||||
}
|
||||
}
|
||||
|
||||
//! Get the maximal number of rows for the selection listbox
|
||||
u32 CGUIComboBox::getMaxSelectionRows() const
|
||||
{
|
||||
return MaxSelectionRows;
|
||||
}
|
||||
|
||||
//! Returns amount of items in box
|
||||
u32 CGUIComboBox::getItemCount() const
|
||||
{
|
||||
return Items.size();
|
||||
}
|
||||
|
||||
//! returns string of an item. the idx may be a value from 0 to itemCount-1
|
||||
const wchar_t *CGUIComboBox::getItem(u32 idx) const
|
||||
{
|
||||
if (idx >= Items.size())
|
||||
return 0;
|
||||
|
||||
return Items[idx].Name.c_str();
|
||||
}
|
||||
|
||||
//! returns string of an item. the idx may be a value from 0 to itemCount-1
|
||||
u32 CGUIComboBox::getItemData(u32 idx) const
|
||||
{
|
||||
if (idx >= Items.size())
|
||||
return 0;
|
||||
|
||||
return Items[idx].Data;
|
||||
}
|
||||
|
||||
//! Returns index based on item data
|
||||
s32 CGUIComboBox::getIndexForItemData(u32 data) const
|
||||
{
|
||||
for (u32 i = 0; i < Items.size(); ++i) {
|
||||
if (Items[i].Data == data)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! Removes an item from the combo box.
|
||||
void CGUIComboBox::removeItem(u32 idx)
|
||||
{
|
||||
if (idx >= Items.size())
|
||||
return;
|
||||
|
||||
if (Selected == (s32)idx)
|
||||
setSelected(-1);
|
||||
|
||||
Items.erase(idx);
|
||||
}
|
||||
|
||||
//! Returns caption of this element.
|
||||
const wchar_t *CGUIComboBox::getText() const
|
||||
{
|
||||
return getItem(Selected);
|
||||
}
|
||||
|
||||
//! adds an item and returns the index of it
|
||||
u32 CGUIComboBox::addItem(const wchar_t *text, u32 data)
|
||||
{
|
||||
Items.push_back(SComboData(text, data));
|
||||
|
||||
if (Selected == -1)
|
||||
setSelected(0);
|
||||
|
||||
return Items.size() - 1;
|
||||
}
|
||||
|
||||
//! deletes all items in the combo box
|
||||
void CGUIComboBox::clear()
|
||||
{
|
||||
Items.clear();
|
||||
setSelected(-1);
|
||||
}
|
||||
|
||||
//! returns id of selected item. returns -1 if no item is selected.
|
||||
s32 CGUIComboBox::getSelected() const
|
||||
{
|
||||
return Selected;
|
||||
}
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void CGUIComboBox::setSelected(s32 idx)
|
||||
{
|
||||
if (idx < -1 || idx >= (s32)Items.size())
|
||||
return;
|
||||
|
||||
Selected = idx;
|
||||
if (Selected == -1)
|
||||
SelectedText->setText(L"");
|
||||
else
|
||||
SelectedText->setText(Items[Selected].Name.c_str());
|
||||
}
|
||||
|
||||
//! Sets the selected item and emits a change event.
|
||||
/** Set this to -1 if no item should be selected */
|
||||
void CGUIComboBox::setAndSendSelected(s32 idx)
|
||||
{
|
||||
setSelected(idx);
|
||||
sendSelectionChangedEvent();
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUIComboBox::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
switch (event.EventType) {
|
||||
|
||||
case EET_KEY_INPUT_EVENT:
|
||||
if (ListBox && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE) {
|
||||
// hide list box
|
||||
openCloseMenu();
|
||||
return true;
|
||||
} else if (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE) {
|
||||
if (!event.KeyInput.PressedDown) {
|
||||
openCloseMenu();
|
||||
}
|
||||
|
||||
ListButton->setPressed(ListBox == nullptr);
|
||||
|
||||
return true;
|
||||
} else if (event.KeyInput.PressedDown) {
|
||||
s32 oldSelected = Selected;
|
||||
bool absorb = true;
|
||||
switch (event.KeyInput.Key) {
|
||||
case KEY_DOWN:
|
||||
setSelected(Selected + 1);
|
||||
break;
|
||||
case KEY_UP:
|
||||
setSelected(Selected - 1);
|
||||
break;
|
||||
case KEY_HOME:
|
||||
case KEY_PRIOR:
|
||||
setSelected(0);
|
||||
break;
|
||||
case KEY_END:
|
||||
case KEY_NEXT:
|
||||
setSelected((s32)Items.size() - 1);
|
||||
break;
|
||||
default:
|
||||
absorb = false;
|
||||
}
|
||||
|
||||
if (Selected < 0)
|
||||
setSelected(0);
|
||||
|
||||
if (Selected >= (s32)Items.size())
|
||||
setSelected((s32)Items.size() - 1);
|
||||
|
||||
if (Selected != oldSelected) {
|
||||
sendSelectionChangedEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (absorb)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case EET_GUI_EVENT:
|
||||
|
||||
switch (event.GUIEvent.EventType) {
|
||||
case EGET_ELEMENT_FOCUS_LOST:
|
||||
if (ListBox &&
|
||||
(Environment->hasFocus(ListBox) || ListBox->isMyChild(event.GUIEvent.Caller)) &&
|
||||
event.GUIEvent.Element != this &&
|
||||
!isMyChild(event.GUIEvent.Element) &&
|
||||
!ListBox->isMyChild(event.GUIEvent.Element)) {
|
||||
openCloseMenu();
|
||||
}
|
||||
break;
|
||||
case EGET_BUTTON_CLICKED:
|
||||
if (event.GUIEvent.Caller == ListButton) {
|
||||
openCloseMenu();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case EGET_LISTBOX_SELECTED_AGAIN:
|
||||
case EGET_LISTBOX_CHANGED:
|
||||
if (event.GUIEvent.Caller == ListBox) {
|
||||
setSelected(ListBox->getSelected());
|
||||
if (Selected < 0 || Selected >= (s32)Items.size())
|
||||
setSelected(-1);
|
||||
openCloseMenu();
|
||||
|
||||
sendSelectionChangedEvent();
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
|
||||
switch (event.MouseInput.Event) {
|
||||
case EMIE_LMOUSE_PRESSED_DOWN: {
|
||||
core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
|
||||
|
||||
// send to list box
|
||||
if (ListBox && ListBox->isPointInside(p) && ListBox->OnEvent(event))
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
case EMIE_LMOUSE_LEFT_UP: {
|
||||
core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
|
||||
|
||||
// send to list box
|
||||
if (!(ListBox &&
|
||||
ListBox->getAbsolutePosition().isPointInside(p) &&
|
||||
ListBox->OnEvent(event))) {
|
||||
openCloseMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case EMIE_MOUSE_WHEEL: {
|
||||
// Try scrolling parent first
|
||||
if (IGUIElement::OnEvent(event))
|
||||
return true;
|
||||
|
||||
s32 oldSelected = Selected;
|
||||
setSelected(Selected + ((event.MouseInput.Wheel < 0) ? 1 : -1));
|
||||
|
||||
if (Selected < 0)
|
||||
setSelected(0);
|
||||
|
||||
if (Selected >= (s32)Items.size())
|
||||
setSelected((s32)Items.size() - 1);
|
||||
|
||||
if (Selected != oldSelected) {
|
||||
sendSelectionChangedEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
void CGUIComboBox::sendSelectionChangedEvent()
|
||||
{
|
||||
if (Parent) {
|
||||
SEvent event;
|
||||
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = nullptr;
|
||||
event.GUIEvent.EventType = EGET_COMBO_BOX_CHANGED;
|
||||
Parent->OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIComboBox::updateListButtonWidth(s32 width)
|
||||
{
|
||||
if (ListButton->getRelativePosition().getWidth() != width) {
|
||||
core::rect<s32> r;
|
||||
r.UpperLeftCorner.X = RelativeRect.getWidth() - width - 2;
|
||||
r.LowerRightCorner.X = RelativeRect.getWidth() - 2;
|
||||
r.UpperLeftCorner.Y = 2;
|
||||
r.LowerRightCorner.Y = RelativeRect.getHeight() - 2;
|
||||
ListButton->setRelativePosition(r);
|
||||
|
||||
r.UpperLeftCorner.X = 2;
|
||||
r.UpperLeftCorner.Y = 2;
|
||||
r.LowerRightCorner.X = RelativeRect.getWidth() - (width + 2);
|
||||
r.LowerRightCorner.Y = RelativeRect.getHeight() - 2;
|
||||
SelectedText->setRelativePosition(r);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIComboBox::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
updateListButtonWidth(skin->getSize(EGDS_SCROLLBAR_SIZE));
|
||||
|
||||
// font changed while the listbox is open?
|
||||
if (ActiveFont != skin->getFont() && ListBox) {
|
||||
// close and re-open to use new font-size
|
||||
openCloseMenu();
|
||||
openCloseMenu();
|
||||
}
|
||||
|
||||
IGUIElement *currentFocus = Environment->getFocus();
|
||||
if (currentFocus != LastFocus) {
|
||||
HasFocus = currentFocus == this || isMyChild(currentFocus);
|
||||
LastFocus = currentFocus;
|
||||
}
|
||||
|
||||
// set colors each time as skin-colors can be changed
|
||||
SelectedText->setBackgroundColor(skin->getColor(EGDC_HIGH_LIGHT));
|
||||
if (isEnabled()) {
|
||||
SelectedText->setDrawBackground(HasFocus);
|
||||
SelectedText->setOverrideColor(skin->getColor(HasFocus ? EGDC_HIGH_LIGHT_TEXT : EGDC_BUTTON_TEXT));
|
||||
} else {
|
||||
SelectedText->setDrawBackground(false);
|
||||
SelectedText->setOverrideColor(skin->getColor(EGDC_GRAY_TEXT));
|
||||
}
|
||||
ListButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL));
|
||||
ListButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL));
|
||||
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
// draw the border
|
||||
|
||||
skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT),
|
||||
true, true, frameRect, &AbsoluteClippingRect);
|
||||
|
||||
// draw children
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
void CGUIComboBox::openCloseMenu()
|
||||
{
|
||||
if (ListBox) {
|
||||
// close list box
|
||||
Environment->setFocus(this);
|
||||
ListBox->remove();
|
||||
ListBox = nullptr;
|
||||
} else {
|
||||
if (Parent) {
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = nullptr;
|
||||
event.GUIEvent.EventType = EGET_LISTBOX_OPENED;
|
||||
|
||||
// Allow to prevent the listbox from opening.
|
||||
if (Parent->OnEvent(event))
|
||||
return;
|
||||
|
||||
Parent->bringToFront(this);
|
||||
}
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
u32 h = Items.size();
|
||||
|
||||
if (h > getMaxSelectionRows())
|
||||
h = getMaxSelectionRows();
|
||||
if (h == 0)
|
||||
h = 1;
|
||||
|
||||
ActiveFont = skin->getFont();
|
||||
if (ActiveFont)
|
||||
h *= (ActiveFont->getDimension(L"A").Height + 4);
|
||||
|
||||
// open list box
|
||||
core::rect<s32> r(0, AbsoluteRect.getHeight(),
|
||||
AbsoluteRect.getWidth(), AbsoluteRect.getHeight() + h);
|
||||
|
||||
ListBox = new CGUIListBox(Environment, this, -1, r, false, true, true);
|
||||
ListBox->setSubElement(true);
|
||||
ListBox->setNotClipped(true);
|
||||
ListBox->drop();
|
||||
|
||||
// ensure that list box is always completely visible
|
||||
if (ListBox->getAbsolutePosition().LowerRightCorner.Y > Environment->getRootGUIElement()->getAbsolutePosition().getHeight())
|
||||
ListBox->setRelativePosition(core::rect<s32>(0, -ListBox->getAbsolutePosition().getHeight(), AbsoluteRect.getWidth(), 0));
|
||||
|
||||
for (s32 i = 0; i < (s32)Items.size(); ++i)
|
||||
ListBox->addItem(Items[i].Name.c_str());
|
||||
|
||||
ListBox->setSelected(Selected);
|
||||
|
||||
// set focus
|
||||
Environment->setFocus(ListBox);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
104
src/CGUIComboBox.h
Normal file
104
src/CGUIComboBox.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIComboBox.h"
|
||||
#include "IGUIStaticText.h"
|
||||
#include "irrString.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class IGUIButton;
|
||||
class IGUIListBox;
|
||||
|
||||
//! Single line edit box for editing simple text.
|
||||
class CGUIComboBox : public IGUIComboBox
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIComboBox(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle);
|
||||
|
||||
//! Returns amount of items in box
|
||||
u32 getItemCount() const override;
|
||||
|
||||
//! returns string of an item. the idx may be a value from 0 to itemCount-1
|
||||
const wchar_t *getItem(u32 idx) const override;
|
||||
|
||||
//! Returns item data of an item. the idx may be a value from 0 to itemCount-1
|
||||
u32 getItemData(u32 idx) const override;
|
||||
|
||||
//! Returns index based on item data
|
||||
s32 getIndexForItemData(u32 data) const override;
|
||||
|
||||
//! adds an item and returns the index of it
|
||||
u32 addItem(const wchar_t *text, u32 data) override;
|
||||
|
||||
//! Removes an item from the combo box.
|
||||
void removeItem(u32 id) override;
|
||||
|
||||
//! deletes all items in the combo box
|
||||
void clear() override;
|
||||
|
||||
//! returns the text of the currently selected item
|
||||
const wchar_t *getText() const override;
|
||||
|
||||
//! returns id of selected item. returns -1 if no item is selected.
|
||||
s32 getSelected() const override;
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void setSelected(s32 idx) override;
|
||||
|
||||
//! Sets the selected item and emits a change event.
|
||||
/** Set this to -1 if no item should be selected */
|
||||
void setAndSendSelected(s32 idx) override;
|
||||
|
||||
//! sets the text alignment of the text part
|
||||
void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) override;
|
||||
|
||||
//! Set the maximal number of rows for the selection listbox
|
||||
void setMaxSelectionRows(u32 max) override;
|
||||
|
||||
//! Get the maximal number of rows for the selection listbox
|
||||
u32 getMaxSelectionRows() const override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
private:
|
||||
void openCloseMenu();
|
||||
void sendSelectionChangedEvent();
|
||||
void updateListButtonWidth(s32 width);
|
||||
|
||||
IGUIButton *ListButton;
|
||||
IGUIStaticText *SelectedText;
|
||||
IGUIListBox *ListBox;
|
||||
IGUIElement *LastFocus;
|
||||
|
||||
struct SComboData
|
||||
{
|
||||
SComboData(const wchar_t *text, u32 data) :
|
||||
Name(text), Data(data) {}
|
||||
|
||||
core::stringw Name;
|
||||
u32 Data;
|
||||
};
|
||||
core::array<SComboData> Items;
|
||||
|
||||
s32 Selected;
|
||||
EGUI_ALIGNMENT HAlign, VAlign;
|
||||
u32 MaxSelectionRows;
|
||||
bool HasFocus;
|
||||
IGUIFont *ActiveFont;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
1495
src/CGUIEditBox.cpp
Normal file
1495
src/CGUIEditBox.cpp
Normal file
File diff suppressed because it is too large
Load Diff
196
src/CGUIEditBox.h
Normal file
196
src/CGUIEditBox.h
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIEditBox.h"
|
||||
#include "irrArray.h"
|
||||
#include "IOSOperator.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class CGUIEditBox : public IGUIEditBox
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIEditBox(const wchar_t *text, bool border, IGUIEnvironment *environment,
|
||||
IGUIElement *parent, s32 id, const core::rect<s32> &rectangle);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIEditBox();
|
||||
|
||||
//! Sets another skin independent font.
|
||||
void setOverrideFont(IGUIFont *font = 0) override;
|
||||
|
||||
//! Gets the override font (if any)
|
||||
/** \return The override font (may be 0) */
|
||||
IGUIFont *getOverrideFont() const override;
|
||||
|
||||
//! Get the font which is used right now for drawing
|
||||
/** Currently this is the override font when one is set and the
|
||||
font of the active skin otherwise */
|
||||
IGUIFont *getActiveFont() const override;
|
||||
|
||||
//! Sets another color for the text.
|
||||
void setOverrideColor(video::SColor color) override;
|
||||
|
||||
//! Gets the override color
|
||||
video::SColor getOverrideColor() const override;
|
||||
|
||||
//! Sets if the text should use the override color or the
|
||||
//! color in the gui skin.
|
||||
void enableOverrideColor(bool enable) override;
|
||||
|
||||
//! Checks if an override color is enabled
|
||||
/** \return true if the override color is enabled, false otherwise */
|
||||
bool isOverrideColorEnabled(void) const override;
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void setDrawBackground(bool draw) override;
|
||||
|
||||
//! Checks if background drawing is enabled
|
||||
bool isDrawBackgroundEnabled() const override;
|
||||
|
||||
//! Turns the border on or off
|
||||
void setDrawBorder(bool border) override;
|
||||
|
||||
//! Checks if border drawing is enabled
|
||||
bool isDrawBorderEnabled() const override;
|
||||
|
||||
//! Enables or disables word wrap for using the edit box as multiline text editor.
|
||||
void setWordWrap(bool enable) override;
|
||||
|
||||
//! Checks if word wrap is enabled
|
||||
//! \return true if word wrap is enabled, false otherwise
|
||||
bool isWordWrapEnabled() const override;
|
||||
|
||||
//! Enables or disables newlines.
|
||||
/** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
|
||||
instead a newline character will be inserted. */
|
||||
void setMultiLine(bool enable) override;
|
||||
|
||||
//! Checks if multi line editing is enabled
|
||||
//! \return true if mult-line is enabled, false otherwise
|
||||
bool isMultiLineEnabled() const override;
|
||||
|
||||
//! Enables or disables automatic scrolling with cursor position
|
||||
//! \param enable: If set to true, the text will move around with the cursor position
|
||||
void setAutoScroll(bool enable) override;
|
||||
|
||||
//! Checks to see if automatic scrolling is enabled
|
||||
//! \return true if automatic scrolling is enabled, false if not
|
||||
bool isAutoScrollEnabled() const override;
|
||||
|
||||
//! Gets the size area of the text in the edit box
|
||||
//! \return Returns the size in pixels of the text
|
||||
core::dimension2du getTextDimension() override;
|
||||
|
||||
//! Sets text justification
|
||||
void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! Sets the new caption of this element.
|
||||
void setText(const wchar_t *text) override;
|
||||
|
||||
//! Sets the maximum amount of characters which may be entered in the box.
|
||||
//! \param max: Maximum amount of characters. If 0, the character amount is
|
||||
//! infinity.
|
||||
void setMax(u32 max) override;
|
||||
|
||||
//! Returns maximum amount of characters, previously set by setMax();
|
||||
u32 getMax() const override;
|
||||
|
||||
//! Set the character used for the cursor.
|
||||
/** By default it's "_" */
|
||||
void setCursorChar(const wchar_t cursorChar) override;
|
||||
|
||||
//! Get the character used for the cursor.
|
||||
wchar_t getCursorChar() const override;
|
||||
|
||||
//! Set the blinktime for the cursor. 2x blinktime is one full cycle.
|
||||
//** \param timeMs Blinktime in milliseconds. When set to 0 the cursor is constantly on without blinking */
|
||||
void setCursorBlinkTime(irr::u32 timeMs) override;
|
||||
|
||||
//! Get the cursor blinktime
|
||||
irr::u32 getCursorBlinkTime() const override;
|
||||
|
||||
//! Sets whether the edit box is a password box. Setting this to true will
|
||||
/** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
|
||||
\param passwordBox: true to enable password, false to disable
|
||||
\param passwordChar: the character that is displayed instead of letters */
|
||||
void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*') override;
|
||||
|
||||
//! Returns true if the edit box is currently a password box.
|
||||
bool isPasswordBox() const override;
|
||||
|
||||
//! Updates the absolute position, splits text if required
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
//! Returns whether the element takes input from the IME
|
||||
bool acceptsIME() override;
|
||||
|
||||
protected:
|
||||
//! Breaks the single text line.
|
||||
void breakText();
|
||||
//! sets the area of the given line
|
||||
void setTextRect(s32 line);
|
||||
//! returns the line number that the cursor is on
|
||||
s32 getLineFromPos(s32 pos);
|
||||
//! adds a letter to the edit box
|
||||
void inputChar(wchar_t c);
|
||||
//! adds a string to the edit box
|
||||
void inputString(const core::stringw &str);
|
||||
//! calculates the current scroll position
|
||||
void calculateScrollPos();
|
||||
//! calculated the FrameRect
|
||||
void calculateFrameRect();
|
||||
//! send some gui event to parent
|
||||
void sendGuiEvent(EGUI_EVENT_TYPE type);
|
||||
//! set text markers
|
||||
void setTextMarkers(s32 begin, s32 end);
|
||||
//! delete current selection or next char
|
||||
bool keyDelete();
|
||||
|
||||
bool processKey(const SEvent &event);
|
||||
bool processMouse(const SEvent &event);
|
||||
s32 getCursorPos(s32 x, s32 y);
|
||||
|
||||
bool OverwriteMode;
|
||||
bool MouseMarking;
|
||||
bool Border;
|
||||
bool Background;
|
||||
bool OverrideColorEnabled;
|
||||
s32 MarkBegin;
|
||||
s32 MarkEnd;
|
||||
|
||||
video::SColor OverrideColor;
|
||||
gui::IGUIFont *OverrideFont, *LastBreakFont;
|
||||
IOSOperator *Operator;
|
||||
|
||||
u32 BlinkStartTime;
|
||||
irr::u32 CursorBlinkTime;
|
||||
core::stringw CursorChar; // IGUIFont::draw needs stringw instead of wchar_t
|
||||
s32 CursorPos;
|
||||
s32 HScrollPos, VScrollPos; // scroll position in characters
|
||||
u32 Max;
|
||||
|
||||
bool WordWrap, MultiLine, AutoScroll, PasswordBox;
|
||||
wchar_t PasswordChar;
|
||||
EGUI_ALIGNMENT HAlign, VAlign;
|
||||
|
||||
core::array<core::stringw> BrokenText;
|
||||
core::array<s32> BrokenTextPositions;
|
||||
|
||||
core::rect<s32> CurrentTextRect, FrameRect; // temporary values
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
999
src/CGUIEnvironment.cpp
Normal file
999
src/CGUIEnvironment.cpp
Normal file
@@ -0,0 +1,999 @@
|
||||
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIEnvironment.h"
|
||||
|
||||
#include "IVideoDriver.h"
|
||||
|
||||
#include "CGUISkin.h"
|
||||
#include "CGUIButton.h"
|
||||
#include "CGUIScrollBar.h"
|
||||
#include "CGUIFont.h"
|
||||
#include "CGUISpriteBank.h"
|
||||
#include "CGUIImage.h"
|
||||
#include "CGUICheckBox.h"
|
||||
#include "CGUIListBox.h"
|
||||
#include "CGUIImageList.h"
|
||||
#include "CGUIFileOpenDialog.h"
|
||||
#include "CGUIStaticText.h"
|
||||
#include "CGUIEditBox.h"
|
||||
#include "CGUITabControl.h"
|
||||
#include "CGUIComboBox.h"
|
||||
|
||||
#include "IWriteFile.h"
|
||||
#ifdef IRR_ENABLE_BUILTIN_FONT
|
||||
#include "BuiltInFont.h"
|
||||
#endif
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
const io::path CGUIEnvironment::DefaultFontName = "#DefaultFont";
|
||||
|
||||
//! constructor
|
||||
CGUIEnvironment::CGUIEnvironment(io::IFileSystem *fs, video::IVideoDriver *driver, IOSOperator *op) :
|
||||
IGUIElement(EGUIET_ROOT, 0, 0, 0, core::rect<s32>(driver ? core::dimension2d<s32>(driver->getScreenSize()) : core::dimension2d<s32>(0, 0))),
|
||||
Driver(driver), Hovered(0), HoveredNoSubelement(0), Focus(0), LastHoveredMousePos(0, 0), CurrentSkin(0),
|
||||
FileSystem(fs), UserReceiver(0), Operator(op), FocusFlags(EFF_SET_ON_LMOUSE_DOWN | EFF_SET_ON_TAB)
|
||||
{
|
||||
if (Driver)
|
||||
Driver->grab();
|
||||
|
||||
if (FileSystem)
|
||||
FileSystem->grab();
|
||||
|
||||
if (Operator)
|
||||
Operator->grab();
|
||||
|
||||
#ifdef _DEBUG
|
||||
IGUIEnvironment::setDebugName("CGUIEnvironment");
|
||||
#endif
|
||||
|
||||
loadBuiltInFont();
|
||||
|
||||
IGUISkin *skin = createSkin(gui::EGST_WINDOWS_METALLIC);
|
||||
setSkin(skin);
|
||||
skin->drop();
|
||||
|
||||
// set tooltip default
|
||||
ToolTip.LastTime = 0;
|
||||
ToolTip.EnterTime = 0;
|
||||
ToolTip.LaunchTime = 1000;
|
||||
ToolTip.RelaunchTime = 500;
|
||||
ToolTip.Element = 0;
|
||||
|
||||
// environment is root tab group
|
||||
Environment = this;
|
||||
setTabGroup(true);
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIEnvironment::~CGUIEnvironment()
|
||||
{
|
||||
clearDeletionQueue();
|
||||
|
||||
if (HoveredNoSubelement && HoveredNoSubelement != this) {
|
||||
HoveredNoSubelement->drop();
|
||||
HoveredNoSubelement = 0;
|
||||
}
|
||||
|
||||
if (Hovered && Hovered != this) {
|
||||
Hovered->drop();
|
||||
Hovered = 0;
|
||||
}
|
||||
|
||||
if (Focus) {
|
||||
Focus->drop();
|
||||
Focus = 0;
|
||||
}
|
||||
|
||||
if (ToolTip.Element) {
|
||||
ToolTip.Element->drop();
|
||||
ToolTip.Element = 0;
|
||||
}
|
||||
|
||||
// drop skin
|
||||
if (CurrentSkin) {
|
||||
CurrentSkin->drop();
|
||||
CurrentSkin = 0;
|
||||
}
|
||||
|
||||
u32 i;
|
||||
|
||||
// delete all sprite banks
|
||||
for (i = 0; i < Banks.size(); ++i)
|
||||
if (Banks[i].Bank)
|
||||
Banks[i].Bank->drop();
|
||||
|
||||
// delete all fonts
|
||||
for (i = 0; i < Fonts.size(); ++i)
|
||||
Fonts[i].Font->drop();
|
||||
|
||||
if (Operator) {
|
||||
Operator->drop();
|
||||
Operator = 0;
|
||||
}
|
||||
|
||||
if (FileSystem) {
|
||||
FileSystem->drop();
|
||||
FileSystem = 0;
|
||||
}
|
||||
|
||||
if (Driver) {
|
||||
Driver->drop();
|
||||
Driver = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIEnvironment::loadBuiltInFont()
|
||||
{
|
||||
#ifdef IRR_ENABLE_BUILTIN_FONT
|
||||
io::IReadFile *file = FileSystem->createMemoryReadFile(BuiltInFontData,
|
||||
BuiltInFontDataSize, DefaultFontName, false);
|
||||
|
||||
CGUIFont *font = new CGUIFont(this, DefaultFontName);
|
||||
if (!font->load(file)) {
|
||||
os::Printer::log("Error: Could not load built-in Font.", ELL_ERROR);
|
||||
font->drop();
|
||||
file->drop();
|
||||
return;
|
||||
}
|
||||
|
||||
SFont f;
|
||||
f.NamedPath.setPath(DefaultFontName);
|
||||
f.Font = font;
|
||||
Fonts.push_back(f);
|
||||
|
||||
file->drop();
|
||||
#endif
|
||||
}
|
||||
|
||||
//! draws all gui elements
|
||||
void CGUIEnvironment::drawAll(bool useScreenSize)
|
||||
{
|
||||
if (useScreenSize && Driver) {
|
||||
core::dimension2d<s32> dim(Driver->getScreenSize());
|
||||
if (AbsoluteRect.LowerRightCorner.X != dim.Width ||
|
||||
AbsoluteRect.UpperLeftCorner.X != 0 ||
|
||||
AbsoluteRect.LowerRightCorner.Y != dim.Height ||
|
||||
AbsoluteRect.UpperLeftCorner.Y != 0) {
|
||||
setRelativePosition(core::recti(0, 0, dim.Width, dim.Height));
|
||||
}
|
||||
}
|
||||
|
||||
// make sure tooltip is always on top
|
||||
if (ToolTip.Element)
|
||||
bringToFront(ToolTip.Element);
|
||||
|
||||
draw();
|
||||
OnPostRender(os::Timer::getTime());
|
||||
|
||||
clearDeletionQueue();
|
||||
}
|
||||
|
||||
//! sets the focus to an element
|
||||
bool CGUIEnvironment::setFocus(IGUIElement *element)
|
||||
{
|
||||
if (Focus == element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// GUI Environment should just reset the focus to 0
|
||||
if (element == this)
|
||||
element = 0;
|
||||
|
||||
// stop element from being deleted
|
||||
if (element)
|
||||
element->grab();
|
||||
|
||||
// focus may change or be removed in this call
|
||||
IGUIElement *currentFocus = 0;
|
||||
if (Focus) {
|
||||
currentFocus = Focus;
|
||||
currentFocus->grab();
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = Focus;
|
||||
e.GUIEvent.Element = element;
|
||||
e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
|
||||
if (Focus->OnEvent(e)) {
|
||||
if (element)
|
||||
element->drop();
|
||||
currentFocus->drop();
|
||||
return false;
|
||||
}
|
||||
currentFocus->drop();
|
||||
currentFocus = 0;
|
||||
}
|
||||
|
||||
if (element) {
|
||||
currentFocus = Focus;
|
||||
if (currentFocus)
|
||||
currentFocus->grab();
|
||||
|
||||
// send focused event
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = element;
|
||||
e.GUIEvent.Element = Focus;
|
||||
e.GUIEvent.EventType = EGET_ELEMENT_FOCUSED;
|
||||
if (element->OnEvent(e)) {
|
||||
if (element)
|
||||
element->drop();
|
||||
if (currentFocus)
|
||||
currentFocus->drop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFocus)
|
||||
currentFocus->drop();
|
||||
|
||||
if (Focus)
|
||||
Focus->drop();
|
||||
|
||||
// element is the new focus so it doesn't have to be dropped
|
||||
Focus = element;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! returns the element with the focus
|
||||
IGUIElement *CGUIEnvironment::getFocus() const
|
||||
{
|
||||
return Focus;
|
||||
}
|
||||
|
||||
//! returns the element last known to be under the mouse cursor
|
||||
IGUIElement *CGUIEnvironment::getHovered() const
|
||||
{
|
||||
return Hovered;
|
||||
}
|
||||
|
||||
//! removes the focus from an element
|
||||
bool CGUIEnvironment::removeFocus(IGUIElement *element)
|
||||
{
|
||||
if (Focus && Focus == element) {
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = Focus;
|
||||
e.GUIEvent.Element = 0;
|
||||
e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
|
||||
if (Focus->OnEvent(e)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (Focus) {
|
||||
Focus->drop();
|
||||
Focus = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Returns whether the element has focus
|
||||
bool CGUIEnvironment::hasFocus(const IGUIElement *element, bool checkSubElements) const
|
||||
{
|
||||
if (element == Focus)
|
||||
return true;
|
||||
|
||||
if (!checkSubElements || !element)
|
||||
return false;
|
||||
|
||||
IGUIElement *f = Focus;
|
||||
while (f && f->isSubElement()) {
|
||||
f = f->getParent();
|
||||
if (f == element)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! returns the current video driver
|
||||
video::IVideoDriver *CGUIEnvironment::getVideoDriver() const
|
||||
{
|
||||
return Driver;
|
||||
}
|
||||
|
||||
//! returns the current file system
|
||||
io::IFileSystem *CGUIEnvironment::getFileSystem() const
|
||||
{
|
||||
return FileSystem;
|
||||
}
|
||||
|
||||
//! returns a pointer to the OS operator
|
||||
IOSOperator *CGUIEnvironment::getOSOperator() const
|
||||
{
|
||||
return Operator;
|
||||
}
|
||||
|
||||
//! clear all GUI elements
|
||||
void CGUIEnvironment::clear()
|
||||
{
|
||||
// Remove the focus
|
||||
if (Focus) {
|
||||
Focus->drop();
|
||||
Focus = 0;
|
||||
}
|
||||
|
||||
if (Hovered && Hovered != this) {
|
||||
Hovered->drop();
|
||||
Hovered = 0;
|
||||
}
|
||||
if (HoveredNoSubelement && HoveredNoSubelement != this) {
|
||||
HoveredNoSubelement->drop();
|
||||
HoveredNoSubelement = 0;
|
||||
}
|
||||
|
||||
getRootGUIElement()->removeAllChildren();
|
||||
}
|
||||
|
||||
//! called by ui if an event happened.
|
||||
bool CGUIEnvironment::OnEvent(const SEvent &event)
|
||||
{
|
||||
|
||||
bool ret = false;
|
||||
if (UserReceiver && (event.EventType != EET_MOUSE_INPUT_EVENT) && (event.EventType != EET_KEY_INPUT_EVENT) && (event.EventType != EET_GUI_EVENT || event.GUIEvent.Caller != this)) {
|
||||
ret = UserReceiver->OnEvent(event);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
void CGUIEnvironment::OnPostRender(u32 time)
|
||||
{
|
||||
// launch tooltip
|
||||
if (ToolTip.Element == 0 &&
|
||||
HoveredNoSubelement && HoveredNoSubelement != this &&
|
||||
(time - ToolTip.EnterTime >= ToolTip.LaunchTime || (time - ToolTip.LastTime >= ToolTip.RelaunchTime && time - ToolTip.LastTime < ToolTip.LaunchTime)) &&
|
||||
HoveredNoSubelement->getToolTipText().size() &&
|
||||
getSkin() &&
|
||||
getSkin()->getFont(EGDF_TOOLTIP)) {
|
||||
core::rect<s32> pos;
|
||||
|
||||
pos.UpperLeftCorner = LastHoveredMousePos;
|
||||
core::dimension2du dim = getSkin()->getFont(EGDF_TOOLTIP)->getDimension(HoveredNoSubelement->getToolTipText().c_str());
|
||||
dim.Width += getSkin()->getSize(EGDS_TEXT_DISTANCE_X) * 2;
|
||||
dim.Height += getSkin()->getSize(EGDS_TEXT_DISTANCE_Y) * 2;
|
||||
|
||||
pos.UpperLeftCorner.Y -= dim.Height + 1;
|
||||
pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + dim.Height - 1;
|
||||
pos.LowerRightCorner.X = pos.UpperLeftCorner.X + dim.Width;
|
||||
|
||||
pos.constrainTo(getAbsolutePosition());
|
||||
|
||||
ToolTip.Element = addStaticText(HoveredNoSubelement->getToolTipText().c_str(), pos, true, true, this, -1, true);
|
||||
ToolTip.Element->setOverrideColor(getSkin()->getColor(EGDC_TOOLTIP));
|
||||
ToolTip.Element->setBackgroundColor(getSkin()->getColor(EGDC_TOOLTIP_BACKGROUND));
|
||||
ToolTip.Element->setOverrideFont(getSkin()->getFont(EGDF_TOOLTIP));
|
||||
ToolTip.Element->setSubElement(true);
|
||||
ToolTip.Element->grab();
|
||||
|
||||
s32 textHeight = ToolTip.Element->getTextHeight();
|
||||
pos = ToolTip.Element->getRelativePosition();
|
||||
pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + textHeight;
|
||||
ToolTip.Element->setRelativePosition(pos);
|
||||
}
|
||||
|
||||
if (ToolTip.Element && ToolTip.Element->isVisible()) { // (isVisible() check only because we might use visibility for ToolTip one day)
|
||||
ToolTip.LastTime = time;
|
||||
|
||||
// got invisible or removed in the meantime?
|
||||
if (!HoveredNoSubelement ||
|
||||
!HoveredNoSubelement->isVisible() ||
|
||||
!HoveredNoSubelement->getParent()) // got invisible or removed in the meantime?
|
||||
{
|
||||
ToolTip.Element->remove();
|
||||
ToolTip.Element->drop();
|
||||
ToolTip.Element = 0;
|
||||
}
|
||||
}
|
||||
|
||||
IGUIElement::OnPostRender(time);
|
||||
}
|
||||
|
||||
void CGUIEnvironment::addToDeletionQueue(IGUIElement *element)
|
||||
{
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
element->grab();
|
||||
DeletionQueue.push_back(element);
|
||||
}
|
||||
|
||||
void CGUIEnvironment::clearDeletionQueue()
|
||||
{
|
||||
if (DeletionQueue.empty())
|
||||
return;
|
||||
|
||||
for (u32 i = 0; i < DeletionQueue.size(); ++i) {
|
||||
DeletionQueue[i]->remove();
|
||||
DeletionQueue[i]->drop();
|
||||
}
|
||||
|
||||
DeletionQueue.clear();
|
||||
}
|
||||
|
||||
//
|
||||
void CGUIEnvironment::updateHoveredElement(core::position2d<s32> mousePos)
|
||||
{
|
||||
IGUIElement *lastHovered = Hovered;
|
||||
IGUIElement *lastHoveredNoSubelement = HoveredNoSubelement;
|
||||
LastHoveredMousePos = mousePos;
|
||||
|
||||
Hovered = getElementFromPoint(mousePos);
|
||||
|
||||
if (ToolTip.Element && Hovered == ToolTip.Element) {
|
||||
// When the mouse is over the ToolTip we remove that so it will be re-created at a new position.
|
||||
// Note that ToolTip.EnterTime does not get changed here, so it will be re-created at once.
|
||||
ToolTip.Element->remove();
|
||||
ToolTip.Element->drop();
|
||||
ToolTip.Element = 0;
|
||||
|
||||
// Get the real Hovered
|
||||
Hovered = getElementFromPoint(mousePos);
|
||||
}
|
||||
|
||||
// for tooltips we want the element itself and not some of it's subelements
|
||||
HoveredNoSubelement = Hovered;
|
||||
while (HoveredNoSubelement && HoveredNoSubelement->isSubElement()) {
|
||||
HoveredNoSubelement = HoveredNoSubelement->getParent();
|
||||
}
|
||||
|
||||
if (Hovered && Hovered != this)
|
||||
Hovered->grab();
|
||||
if (HoveredNoSubelement && HoveredNoSubelement != this)
|
||||
HoveredNoSubelement->grab();
|
||||
|
||||
if (Hovered != lastHovered) {
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
|
||||
if (lastHovered) {
|
||||
event.GUIEvent.Caller = lastHovered;
|
||||
event.GUIEvent.Element = 0;
|
||||
event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
|
||||
lastHovered->OnEvent(event);
|
||||
}
|
||||
|
||||
if (Hovered) {
|
||||
event.GUIEvent.Caller = Hovered;
|
||||
event.GUIEvent.Element = Hovered;
|
||||
event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
|
||||
Hovered->OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastHoveredNoSubelement != HoveredNoSubelement) {
|
||||
if (ToolTip.Element) {
|
||||
ToolTip.Element->remove();
|
||||
ToolTip.Element->drop();
|
||||
ToolTip.Element = 0;
|
||||
}
|
||||
|
||||
if (HoveredNoSubelement) {
|
||||
u32 now = os::Timer::getTime();
|
||||
ToolTip.EnterTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastHovered && lastHovered != this)
|
||||
lastHovered->drop();
|
||||
if (lastHoveredNoSubelement && lastHoveredNoSubelement != this)
|
||||
lastHoveredNoSubelement->drop();
|
||||
}
|
||||
|
||||
//! This sets a new event receiver for gui events. Usually you do not have to
|
||||
//! use this method, it is used by the internal engine.
|
||||
void CGUIEnvironment::setUserEventReceiver(IEventReceiver *evr)
|
||||
{
|
||||
UserReceiver = evr;
|
||||
}
|
||||
|
||||
//! posts an input event to the environment
|
||||
bool CGUIEnvironment::postEventFromUser(const SEvent &event)
|
||||
{
|
||||
switch (event.EventType) {
|
||||
case EET_GUI_EVENT: {
|
||||
// hey, why is the user sending gui events..?
|
||||
}
|
||||
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
|
||||
updateHoveredElement(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
|
||||
|
||||
if (Hovered != Focus) {
|
||||
IGUIElement *focusCandidate = Hovered;
|
||||
|
||||
// Only allow enabled elements to be focused (unless EFF_CAN_FOCUS_DISABLED is set)
|
||||
if (Hovered && !Hovered->isEnabled() && !(FocusFlags & EFF_CAN_FOCUS_DISABLED))
|
||||
focusCandidate = NULL; // we still remove focus from the active element
|
||||
|
||||
// Please don't merge this into a single if clause, it's easier to debug the way it is
|
||||
if (FocusFlags & EFF_SET_ON_LMOUSE_DOWN &&
|
||||
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
setFocus(focusCandidate);
|
||||
} else if (FocusFlags & EFF_SET_ON_RMOUSE_DOWN &&
|
||||
event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
|
||||
setFocus(focusCandidate);
|
||||
} else if (FocusFlags & EFF_SET_ON_MOUSE_OVER &&
|
||||
event.MouseInput.Event == EMIE_MOUSE_MOVED) {
|
||||
setFocus(focusCandidate);
|
||||
}
|
||||
}
|
||||
|
||||
// sending input to focus
|
||||
if (Focus && Focus->OnEvent(event))
|
||||
return true;
|
||||
|
||||
// focus could have died in last call
|
||||
if (!Focus && Hovered) {
|
||||
return Hovered->OnEvent(event);
|
||||
}
|
||||
|
||||
break;
|
||||
case EET_KEY_INPUT_EVENT: {
|
||||
if (Focus && Focus->OnEvent(event))
|
||||
return true;
|
||||
|
||||
// For keys we handle the event before changing focus to give elements the chance for catching the TAB
|
||||
// Send focus changing event
|
||||
// CAREFUL when changing - there's an identical check in CGUIModalScreen::OnEvent
|
||||
if (FocusFlags & EFF_SET_ON_TAB &&
|
||||
event.KeyInput.PressedDown &&
|
||||
event.KeyInput.Key == KEY_TAB) {
|
||||
IGUIElement *next = getNextElement(event.KeyInput.Shift, event.KeyInput.Control);
|
||||
if (next && next != Focus) {
|
||||
if (setFocus(next))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case EET_STRING_INPUT_EVENT:
|
||||
if (Focus && Focus->OnEvent(event))
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // end switch
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! returns the current gui skin
|
||||
IGUISkin *CGUIEnvironment::getSkin() const
|
||||
{
|
||||
return CurrentSkin;
|
||||
}
|
||||
|
||||
//! Sets a new GUI Skin
|
||||
void CGUIEnvironment::setSkin(IGUISkin *skin)
|
||||
{
|
||||
if (CurrentSkin == skin)
|
||||
return;
|
||||
|
||||
if (CurrentSkin)
|
||||
CurrentSkin->drop();
|
||||
|
||||
CurrentSkin = skin;
|
||||
|
||||
if (CurrentSkin)
|
||||
CurrentSkin->grab();
|
||||
}
|
||||
|
||||
//! Creates a new GUI Skin based on a template.
|
||||
/** \return Returns a pointer to the created skin.
|
||||
If you no longer need the skin, you should call IGUISkin::drop().
|
||||
See IReferenceCounted::drop() for more information. */
|
||||
IGUISkin *CGUIEnvironment::createSkin(EGUI_SKIN_TYPE type)
|
||||
{
|
||||
IGUISkin *skin = new CGUISkin(type, Driver);
|
||||
|
||||
IGUIFont *builtinfont = getBuiltInFont();
|
||||
IGUIFontBitmap *bitfont = 0;
|
||||
if (builtinfont && builtinfont->getType() == EGFT_BITMAP)
|
||||
bitfont = (IGUIFontBitmap *)builtinfont;
|
||||
|
||||
IGUISpriteBank *bank = 0;
|
||||
skin->setFont(builtinfont);
|
||||
|
||||
if (bitfont)
|
||||
bank = bitfont->getSpriteBank();
|
||||
|
||||
skin->setSpriteBank(bank);
|
||||
|
||||
return skin;
|
||||
}
|
||||
|
||||
//! adds a button. The returned pointer must not be dropped.
|
||||
IGUIButton *CGUIEnvironment::addButton(const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, const wchar_t *text, const wchar_t *tooltiptext)
|
||||
{
|
||||
IGUIButton *button = new CGUIButton(this, parent ? parent : this, id, rectangle);
|
||||
if (text)
|
||||
button->setText(text);
|
||||
|
||||
if (tooltiptext)
|
||||
button->setToolTipText(tooltiptext);
|
||||
|
||||
button->drop();
|
||||
return button;
|
||||
}
|
||||
|
||||
//! adds a scrollbar. The returned pointer must not be dropped.
|
||||
IGUIScrollBar *CGUIEnvironment::addScrollBar(bool horizontal, const core::rect<s32> &rectangle, IGUIElement *parent, s32 id)
|
||||
{
|
||||
IGUIScrollBar *bar = new CGUIScrollBar(horizontal, this, parent ? parent : this, id, rectangle);
|
||||
bar->drop();
|
||||
return bar;
|
||||
}
|
||||
|
||||
//! Adds an image element.
|
||||
IGUIImage *CGUIEnvironment::addImage(video::ITexture *image, core::position2d<s32> pos,
|
||||
bool useAlphaChannel, IGUIElement *parent, s32 id, const wchar_t *text)
|
||||
{
|
||||
core::dimension2d<s32> sz(0, 0);
|
||||
if (image)
|
||||
sz = core::dimension2d<s32>(image->getOriginalSize());
|
||||
|
||||
IGUIImage *img = new CGUIImage(this, parent ? parent : this,
|
||||
id, core::rect<s32>(pos, sz));
|
||||
|
||||
if (text)
|
||||
img->setText(text);
|
||||
|
||||
if (useAlphaChannel)
|
||||
img->setUseAlphaChannel(true);
|
||||
|
||||
if (image)
|
||||
img->setImage(image);
|
||||
|
||||
img->drop();
|
||||
return img;
|
||||
}
|
||||
|
||||
//! adds an image. The returned pointer must not be dropped.
|
||||
IGUIImage *CGUIEnvironment::addImage(const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, const wchar_t *text, bool useAlphaChannel)
|
||||
{
|
||||
IGUIImage *img = new CGUIImage(this, parent ? parent : this,
|
||||
id, rectangle);
|
||||
|
||||
if (text)
|
||||
img->setText(text);
|
||||
|
||||
if (useAlphaChannel)
|
||||
img->setUseAlphaChannel(true);
|
||||
|
||||
img->drop();
|
||||
return img;
|
||||
}
|
||||
|
||||
//! adds a checkbox
|
||||
IGUICheckBox *CGUIEnvironment::addCheckBox(bool checked, const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, const wchar_t *text)
|
||||
{
|
||||
IGUICheckBox *b = new CGUICheckBox(checked, this,
|
||||
parent ? parent : this, id, rectangle);
|
||||
|
||||
if (text)
|
||||
b->setText(text);
|
||||
|
||||
b->drop();
|
||||
return b;
|
||||
}
|
||||
|
||||
//! adds a list box
|
||||
IGUIListBox *CGUIEnvironment::addListBox(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent, s32 id, bool drawBackground)
|
||||
{
|
||||
IGUIListBox *b = new CGUIListBox(this, parent ? parent : this, id, rectangle,
|
||||
true, drawBackground, false);
|
||||
|
||||
if (CurrentSkin && CurrentSkin->getSpriteBank()) {
|
||||
b->setSpriteBank(CurrentSkin->getSpriteBank());
|
||||
} else if (getBuiltInFont() && getBuiltInFont()->getType() == EGFT_BITMAP) {
|
||||
b->setSpriteBank(((IGUIFontBitmap *)getBuiltInFont())->getSpriteBank());
|
||||
}
|
||||
|
||||
b->drop();
|
||||
return b;
|
||||
}
|
||||
|
||||
//! adds a file open dialog. The returned pointer must not be dropped.
|
||||
IGUIFileOpenDialog *CGUIEnvironment::addFileOpenDialog(const wchar_t *title,
|
||||
bool modal, IGUIElement *parent, s32 id,
|
||||
bool restoreCWD, io::path::char_type *startDir)
|
||||
{
|
||||
parent = parent ? parent : this;
|
||||
|
||||
if (modal)
|
||||
return nullptr;
|
||||
|
||||
IGUIFileOpenDialog *d = new CGUIFileOpenDialog(title, this, parent, id,
|
||||
restoreCWD, startDir);
|
||||
d->drop();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//! adds a static text. The returned pointer must not be dropped.
|
||||
IGUIStaticText *CGUIEnvironment::addStaticText(const wchar_t *text,
|
||||
const core::rect<s32> &rectangle,
|
||||
bool border, bool wordWrap,
|
||||
IGUIElement *parent, s32 id, bool background)
|
||||
{
|
||||
IGUIStaticText *d = new CGUIStaticText(text, border, this,
|
||||
parent ? parent : this, id, rectangle, background);
|
||||
|
||||
d->setWordWrap(wordWrap);
|
||||
d->drop();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//! Adds an edit box. The returned pointer must not be dropped.
|
||||
IGUIEditBox *CGUIEnvironment::addEditBox(const wchar_t *text,
|
||||
const core::rect<s32> &rectangle, bool border,
|
||||
IGUIElement *parent, s32 id)
|
||||
{
|
||||
IGUIEditBox *d = new CGUIEditBox(text, border, this,
|
||||
parent ? parent : this, id, rectangle);
|
||||
|
||||
d->drop();
|
||||
return d;
|
||||
}
|
||||
|
||||
//! Adds a tab control to the environment.
|
||||
IGUITabControl *CGUIEnvironment::addTabControl(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent, bool fillbackground, bool border, s32 id)
|
||||
{
|
||||
IGUITabControl *t = new CGUITabControl(this, parent ? parent : this,
|
||||
rectangle, fillbackground, border, id);
|
||||
t->drop();
|
||||
return t;
|
||||
}
|
||||
|
||||
//! Adds tab to the environment.
|
||||
IGUITab *CGUIEnvironment::addTab(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent, s32 id)
|
||||
{
|
||||
IGUITab *t = new CGUITab(this, parent ? parent : this,
|
||||
rectangle, id);
|
||||
t->drop();
|
||||
return t;
|
||||
}
|
||||
|
||||
//! Adds a combo box to the environment.
|
||||
IGUIComboBox *CGUIEnvironment::addComboBox(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent, s32 id)
|
||||
{
|
||||
IGUIComboBox *t = new CGUIComboBox(this, parent ? parent : this,
|
||||
id, rectangle);
|
||||
t->drop();
|
||||
return t;
|
||||
}
|
||||
|
||||
//! returns the font
|
||||
IGUIFont *CGUIEnvironment::getFont(const io::path &filename)
|
||||
{
|
||||
// search existing font
|
||||
|
||||
SFont f;
|
||||
f.NamedPath.setPath(filename);
|
||||
|
||||
s32 index = Fonts.binary_search(f);
|
||||
if (index != -1)
|
||||
return Fonts[index].Font;
|
||||
|
||||
// font doesn't exist, attempt to load it
|
||||
|
||||
// does the file exist?
|
||||
|
||||
if (!FileSystem->existFile(filename)) {
|
||||
os::Printer::log("Could not load font because the file does not exist", f.NamedPath.getPath(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
IGUIFont *ifont = 0;
|
||||
#if 0
|
||||
{
|
||||
CGUIFont* font = new CGUIFont(this, filename);
|
||||
ifont = (IGUIFont*)font;
|
||||
|
||||
// load the font
|
||||
io::path directory;
|
||||
core::splitFilename(filename, &directory);
|
||||
if (!font->load(xml, directory))
|
||||
{
|
||||
font->drop();
|
||||
font = 0;
|
||||
ifont = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ifont) {
|
||||
|
||||
CGUIFont *font = new CGUIFont(this, f.NamedPath.getPath());
|
||||
ifont = (IGUIFont *)font;
|
||||
if (!font->load(f.NamedPath.getPath())) {
|
||||
font->drop();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// add to fonts.
|
||||
|
||||
f.Font = ifont;
|
||||
Fonts.push_back(f);
|
||||
|
||||
return ifont;
|
||||
}
|
||||
|
||||
//! add an externally loaded font
|
||||
IGUIFont *CGUIEnvironment::addFont(const io::path &name, IGUIFont *font)
|
||||
{
|
||||
if (font) {
|
||||
SFont f;
|
||||
f.NamedPath.setPath(name);
|
||||
s32 index = Fonts.binary_search(f);
|
||||
if (index != -1)
|
||||
return Fonts[index].Font;
|
||||
f.Font = font;
|
||||
Fonts.push_back(f);
|
||||
font->grab();
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
//! remove loaded font
|
||||
void CGUIEnvironment::removeFont(IGUIFont *font)
|
||||
{
|
||||
if (!font)
|
||||
return;
|
||||
for (u32 i = 0; i < Fonts.size(); ++i) {
|
||||
if (Fonts[i].Font == font) {
|
||||
Fonts[i].Font->drop();
|
||||
Fonts.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! returns default font
|
||||
IGUIFont *CGUIEnvironment::getBuiltInFont() const
|
||||
{
|
||||
if (Fonts.empty())
|
||||
return 0;
|
||||
|
||||
return Fonts[0].Font;
|
||||
}
|
||||
|
||||
IGUISpriteBank *CGUIEnvironment::getSpriteBank(const io::path &filename)
|
||||
{
|
||||
// search for the file name
|
||||
|
||||
SSpriteBank b;
|
||||
b.NamedPath.setPath(filename);
|
||||
|
||||
s32 index = Banks.binary_search(b);
|
||||
if (index != -1)
|
||||
return Banks[index].Bank;
|
||||
|
||||
// we don't have this sprite bank, we should load it
|
||||
if (!FileSystem->existFile(b.NamedPath.getPath())) {
|
||||
if (filename != DefaultFontName) {
|
||||
os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_DEBUG);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: load it!
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IGUISpriteBank *CGUIEnvironment::addEmptySpriteBank(const io::path &name)
|
||||
{
|
||||
// no duplicate names allowed
|
||||
|
||||
SSpriteBank b;
|
||||
b.NamedPath.setPath(name);
|
||||
|
||||
const s32 index = Banks.binary_search(b);
|
||||
if (index != -1)
|
||||
return 0;
|
||||
|
||||
// create a new sprite bank
|
||||
|
||||
b.Bank = new CGUISpriteBank(this);
|
||||
Banks.push_back(b);
|
||||
|
||||
return b.Bank;
|
||||
}
|
||||
|
||||
//! Creates the image list from the given texture.
|
||||
IGUIImageList *CGUIEnvironment::createImageList(video::ITexture *texture,
|
||||
core::dimension2d<s32> imageSize, bool useAlphaChannel)
|
||||
{
|
||||
CGUIImageList *imageList = new CGUIImageList(Driver);
|
||||
if (!imageList->createImageList(texture, imageSize, useAlphaChannel)) {
|
||||
imageList->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return imageList;
|
||||
}
|
||||
|
||||
//! Returns the root gui element.
|
||||
IGUIElement *CGUIEnvironment::getRootGUIElement()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
//! Returns the next element in the tab group starting at the focused element
|
||||
IGUIElement *CGUIEnvironment::getNextElement(bool reverse, bool group)
|
||||
{
|
||||
// start the search at the root of the current tab group
|
||||
IGUIElement *startPos = Focus ? Focus->getTabGroup() : 0;
|
||||
s32 startOrder = -1;
|
||||
|
||||
// if we're searching for a group
|
||||
if (group && startPos) {
|
||||
startOrder = startPos->getTabOrder();
|
||||
} else if (!group && Focus && !Focus->isTabGroup()) {
|
||||
startOrder = Focus->getTabOrder();
|
||||
if (startOrder == -1) {
|
||||
// this element is not part of the tab cycle,
|
||||
// but its parent might be...
|
||||
IGUIElement *el = Focus;
|
||||
while (el && el->getParent() && startOrder == -1) {
|
||||
el = el->getParent();
|
||||
startOrder = el->getTabOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (group || !startPos)
|
||||
startPos = this; // start at the root
|
||||
|
||||
// find the element
|
||||
IGUIElement *closest = 0;
|
||||
IGUIElement *first = 0;
|
||||
startPos->getNextElement(startOrder, reverse, group, first, closest, false, (FocusFlags & EFF_CAN_FOCUS_DISABLED) != 0);
|
||||
|
||||
if (closest)
|
||||
return closest; // we found an element
|
||||
else if (first)
|
||||
return first; // go to the end or the start
|
||||
else if (group)
|
||||
return this; // no group found? root group
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CGUIEnvironment::setFocusBehavior(u32 flags)
|
||||
{
|
||||
FocusFlags = flags;
|
||||
}
|
||||
|
||||
u32 CGUIEnvironment::getFocusBehavior() const
|
||||
{
|
||||
return FocusFlags;
|
||||
}
|
||||
|
||||
//! creates an GUI Environment
|
||||
IGUIEnvironment *createGUIEnvironment(io::IFileSystem *fs,
|
||||
video::IVideoDriver *Driver,
|
||||
IOSOperator *op)
|
||||
{
|
||||
return new CGUIEnvironment(fs, Driver, op);
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
226
src/CGUIEnvironment.h
Normal file
226
src/CGUIEnvironment.h
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIElement.h"
|
||||
#include "irrArray.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "IOSOperator.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIEnvironment : public IGUIEnvironment, public IGUIElement
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIEnvironment(io::IFileSystem *fs, video::IVideoDriver *driver, IOSOperator *op);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIEnvironment();
|
||||
|
||||
//! draws all gui elements
|
||||
void drawAll(bool useScreenSize) override;
|
||||
|
||||
//! returns the current video driver
|
||||
video::IVideoDriver *getVideoDriver() const override;
|
||||
|
||||
//! returns pointer to the filesystem
|
||||
io::IFileSystem *getFileSystem() const override;
|
||||
|
||||
//! returns a pointer to the OS operator
|
||||
IOSOperator *getOSOperator() const override;
|
||||
|
||||
//! posts an input event to the environment
|
||||
bool postEventFromUser(const SEvent &event) override;
|
||||
|
||||
//! This sets a new event receiver for gui events. Usually you do not have to
|
||||
//! use this method, it is used by the internal engine.
|
||||
void setUserEventReceiver(IEventReceiver *evr) override;
|
||||
|
||||
//! removes all elements from the environment
|
||||
void clear() override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! returns the current gui skin
|
||||
IGUISkin *getSkin() const override;
|
||||
|
||||
//! Sets a new GUI Skin
|
||||
void setSkin(IGUISkin *skin) override;
|
||||
|
||||
//! Creates a new GUI Skin based on a template.
|
||||
/** \return Returns a pointer to the created skin.
|
||||
If you no longer need the skin, you should call IGUISkin::drop().
|
||||
See IReferenceCounted::drop() for more information. */
|
||||
IGUISkin *createSkin(EGUI_SKIN_TYPE type) override;
|
||||
|
||||
//! Creates the image list from the given texture.
|
||||
virtual IGUIImageList *createImageList(video::ITexture *texture,
|
||||
core::dimension2d<s32> imageSize, bool useAlphaChannel) override;
|
||||
|
||||
//! returns the font
|
||||
IGUIFont *getFont(const io::path &filename) override;
|
||||
|
||||
//! add an externally loaded font
|
||||
IGUIFont *addFont(const io::path &name, IGUIFont *font) override;
|
||||
|
||||
//! remove loaded font
|
||||
void removeFont(IGUIFont *font) override;
|
||||
|
||||
//! returns default font
|
||||
IGUIFont *getBuiltInFont() const override;
|
||||
|
||||
//! returns the sprite bank
|
||||
IGUISpriteBank *getSpriteBank(const io::path &filename) override;
|
||||
|
||||
//! returns the sprite bank
|
||||
IGUISpriteBank *addEmptySpriteBank(const io::path &name) override;
|
||||
|
||||
//! adds an button. The returned pointer must not be dropped.
|
||||
IGUIButton *addButton(const core::rect<s32> &rectangle, IGUIElement *parent = 0, s32 id = -1, const wchar_t *text = 0, const wchar_t *tooltiptext = 0) override;
|
||||
|
||||
//! adds a scrollbar. The returned pointer must not be dropped.
|
||||
virtual IGUIScrollBar *addScrollBar(bool horizontal, const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1) override;
|
||||
|
||||
//! Adds an image element.
|
||||
virtual IGUIImage *addImage(video::ITexture *image, core::position2d<s32> pos,
|
||||
bool useAlphaChannel = true, IGUIElement *parent = 0, s32 id = -1, const wchar_t *text = 0) override;
|
||||
|
||||
//! adds an image. The returned pointer must not be dropped.
|
||||
virtual IGUIImage *addImage(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1, const wchar_t *text = 0, bool useAlphaChannel = true) override;
|
||||
|
||||
//! adds a checkbox
|
||||
virtual IGUICheckBox *addCheckBox(bool checked, const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1, const wchar_t *text = 0) override;
|
||||
|
||||
//! adds a list box
|
||||
virtual IGUIListBox *addListBox(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1, bool drawBackground = false) override;
|
||||
|
||||
//! Adds a file open dialog.
|
||||
virtual IGUIFileOpenDialog *addFileOpenDialog(const wchar_t *title = 0,
|
||||
bool modal = true, IGUIElement *parent = 0, s32 id = -1,
|
||||
bool restoreCWD = false, io::path::char_type *startDir = 0) override;
|
||||
|
||||
//! adds a static text. The returned pointer must not be dropped.
|
||||
virtual IGUIStaticText *addStaticText(const wchar_t *text, const core::rect<s32> &rectangle,
|
||||
bool border = false, bool wordWrap = true, IGUIElement *parent = 0, s32 id = -1, bool drawBackground = false) override;
|
||||
|
||||
//! Adds an edit box. The returned pointer must not be dropped.
|
||||
virtual IGUIEditBox *addEditBox(const wchar_t *text, const core::rect<s32> &rectangle,
|
||||
bool border = false, IGUIElement *parent = 0, s32 id = -1) override;
|
||||
|
||||
//! Adds a tab control to the environment.
|
||||
virtual IGUITabControl *addTabControl(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, bool fillbackground = false, bool border = true, s32 id = -1) override;
|
||||
|
||||
//! Adds tab to the environment.
|
||||
virtual IGUITab *addTab(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1) override;
|
||||
|
||||
//! Adds a combo box to the environment.
|
||||
virtual IGUIComboBox *addComboBox(const core::rect<s32> &rectangle,
|
||||
IGUIElement *parent = 0, s32 id = -1) override;
|
||||
|
||||
//! sets the focus to an element
|
||||
bool setFocus(IGUIElement *element) override;
|
||||
|
||||
//! removes the focus from an element
|
||||
bool removeFocus(IGUIElement *element) override;
|
||||
|
||||
//! Returns if the element has focus
|
||||
bool hasFocus(const IGUIElement *element, bool checkSubElements = false) const override;
|
||||
|
||||
//! Returns the element with the focus
|
||||
IGUIElement *getFocus() const override;
|
||||
|
||||
//! Returns the element last known to be under the mouse
|
||||
IGUIElement *getHovered() const override;
|
||||
|
||||
//! Returns the root gui element.
|
||||
IGUIElement *getRootGUIElement() override;
|
||||
|
||||
void OnPostRender(u32 time) override;
|
||||
|
||||
//! Find the next element which would be selected when pressing the tab-key
|
||||
IGUIElement *getNextElement(bool reverse = false, bool group = false) override;
|
||||
|
||||
//! Set the way the gui will handle focus changes
|
||||
void setFocusBehavior(u32 flags) override;
|
||||
|
||||
//! Get the way the gui does handle focus changes
|
||||
u32 getFocusBehavior() const override;
|
||||
|
||||
//! Adds a IGUIElement to deletion queue.
|
||||
void addToDeletionQueue(IGUIElement *element) override;
|
||||
|
||||
private:
|
||||
//! clears the deletion queue
|
||||
void clearDeletionQueue();
|
||||
|
||||
void updateHoveredElement(core::position2d<s32> mousePos);
|
||||
|
||||
void loadBuiltInFont();
|
||||
|
||||
struct SFont
|
||||
{
|
||||
io::SNamedPath NamedPath;
|
||||
IGUIFont *Font;
|
||||
|
||||
bool operator<(const SFont &other) const
|
||||
{
|
||||
return (NamedPath < other.NamedPath);
|
||||
}
|
||||
};
|
||||
|
||||
struct SSpriteBank
|
||||
{
|
||||
io::SNamedPath NamedPath;
|
||||
IGUISpriteBank *Bank;
|
||||
|
||||
bool operator<(const SSpriteBank &other) const
|
||||
{
|
||||
return (NamedPath < other.NamedPath);
|
||||
}
|
||||
};
|
||||
|
||||
struct SToolTip
|
||||
{
|
||||
IGUIStaticText *Element;
|
||||
u32 LastTime;
|
||||
u32 EnterTime;
|
||||
u32 LaunchTime;
|
||||
u32 RelaunchTime;
|
||||
};
|
||||
|
||||
SToolTip ToolTip;
|
||||
|
||||
core::array<SFont> Fonts;
|
||||
core::array<SSpriteBank> Banks;
|
||||
video::IVideoDriver *Driver;
|
||||
IGUIElement *Hovered;
|
||||
IGUIElement *HoveredNoSubelement; // subelements replaced by their parent, so you only have 'real' elements here
|
||||
IGUIElement *Focus;
|
||||
core::position2d<s32> LastHoveredMousePos;
|
||||
IGUISkin *CurrentSkin;
|
||||
io::IFileSystem *FileSystem;
|
||||
IEventReceiver *UserReceiver;
|
||||
IOSOperator *Operator;
|
||||
u32 FocusFlags;
|
||||
core::array<IGUIElement *> DeletionQueue;
|
||||
|
||||
static const io::path DefaultFontName;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
372
src/CGUIFileOpenDialog.cpp
Normal file
372
src/CGUIFileOpenDialog.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIFileOpenDialog.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUIButton.h"
|
||||
#include "IGUIStaticText.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IGUIFontBitmap.h"
|
||||
#include "IFileList.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
const s32 FOD_WIDTH = 350;
|
||||
const s32 FOD_HEIGHT = 250;
|
||||
|
||||
//! constructor
|
||||
CGUIFileOpenDialog::CGUIFileOpenDialog(const wchar_t *title,
|
||||
IGUIEnvironment *environment, IGUIElement *parent, s32 id,
|
||||
bool restoreCWD, io::path::char_type *startDir) :
|
||||
IGUIFileOpenDialog(environment, parent, id,
|
||||
core::rect<s32>((parent->getAbsolutePosition().getWidth() - FOD_WIDTH) / 2,
|
||||
(parent->getAbsolutePosition().getHeight() - FOD_HEIGHT) / 2,
|
||||
(parent->getAbsolutePosition().getWidth() - FOD_WIDTH) / 2 + FOD_WIDTH,
|
||||
(parent->getAbsolutePosition().getHeight() - FOD_HEIGHT) / 2 + FOD_HEIGHT)),
|
||||
FileNameText(0), FileList(0), Dragging(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
IGUIElement::setDebugName("CGUIFileOpenDialog");
|
||||
#endif
|
||||
|
||||
Text = title;
|
||||
|
||||
FileSystem = Environment ? Environment->getFileSystem() : 0;
|
||||
|
||||
if (FileSystem) {
|
||||
FileSystem->grab();
|
||||
|
||||
if (restoreCWD)
|
||||
RestoreDirectory = FileSystem->getWorkingDirectory();
|
||||
if (startDir) {
|
||||
StartDirectory = startDir;
|
||||
FileSystem->changeWorkingDirectoryTo(startDir);
|
||||
}
|
||||
} else
|
||||
return;
|
||||
|
||||
IGUISpriteBank *sprites = 0;
|
||||
video::SColor color(255, 255, 255, 255);
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin) {
|
||||
sprites = skin->getSpriteBank();
|
||||
color = skin->getColor(EGDC_WINDOW_SYMBOL);
|
||||
}
|
||||
|
||||
const s32 buttonw = skin ? skin->getSize(EGDS_WINDOW_BUTTON_WIDTH) : 2;
|
||||
const s32 posx = RelativeRect.getWidth() - buttonw - 4;
|
||||
|
||||
CloseButton = Environment->addButton(core::rect<s32>(posx, 3, posx + buttonw, 3 + buttonw), this, -1,
|
||||
L"", skin ? skin->getDefaultText(EGDT_WINDOW_CLOSE) : L"Close");
|
||||
CloseButton->setSubElement(true);
|
||||
CloseButton->setTabStop(false);
|
||||
if (sprites && skin) {
|
||||
CloseButton->setSpriteBank(sprites);
|
||||
CloseButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_WINDOW_CLOSE), color);
|
||||
CloseButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_WINDOW_CLOSE), color);
|
||||
}
|
||||
CloseButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
CloseButton->grab();
|
||||
|
||||
OKButton = Environment->addButton(
|
||||
core::rect<s32>(RelativeRect.getWidth() - 80, 30, RelativeRect.getWidth() - 10, 50),
|
||||
this, -1, skin ? skin->getDefaultText(EGDT_MSG_BOX_OK) : L"OK");
|
||||
OKButton->setSubElement(true);
|
||||
OKButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
OKButton->grab();
|
||||
|
||||
CancelButton = Environment->addButton(
|
||||
core::rect<s32>(RelativeRect.getWidth() - 80, 55, RelativeRect.getWidth() - 10, 75),
|
||||
this, -1, skin ? skin->getDefaultText(EGDT_MSG_BOX_CANCEL) : L"Cancel");
|
||||
CancelButton->setSubElement(true);
|
||||
CancelButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
CancelButton->grab();
|
||||
|
||||
FileBox = Environment->addListBox(core::rect<s32>(10, 55, RelativeRect.getWidth() - 90, 230), this, -1, true);
|
||||
FileBox->setSubElement(true);
|
||||
FileBox->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
FileBox->grab();
|
||||
|
||||
FileNameText = Environment->addEditBox(0, core::rect<s32>(10, 30, RelativeRect.getWidth() - 90, 50), true, this);
|
||||
FileNameText->setSubElement(true);
|
||||
FileNameText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
FileNameText->grab();
|
||||
|
||||
setTabGroup(true);
|
||||
|
||||
fillListBox();
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIFileOpenDialog::~CGUIFileOpenDialog()
|
||||
{
|
||||
if (CloseButton)
|
||||
CloseButton->drop();
|
||||
|
||||
if (OKButton)
|
||||
OKButton->drop();
|
||||
|
||||
if (CancelButton)
|
||||
CancelButton->drop();
|
||||
|
||||
if (FileBox)
|
||||
FileBox->drop();
|
||||
|
||||
if (FileNameText)
|
||||
FileNameText->drop();
|
||||
|
||||
if (FileSystem) {
|
||||
// revert to original CWD if path was set in constructor
|
||||
if (RestoreDirectory.size())
|
||||
FileSystem->changeWorkingDirectoryTo(RestoreDirectory);
|
||||
FileSystem->drop();
|
||||
}
|
||||
|
||||
if (FileList)
|
||||
FileList->drop();
|
||||
}
|
||||
|
||||
//! returns the filename of the selected file. Returns NULL, if no file was selected.
|
||||
const wchar_t *CGUIFileOpenDialog::getFileName() const
|
||||
{
|
||||
return FileNameW.c_str();
|
||||
}
|
||||
|
||||
const io::path &CGUIFileOpenDialog::getFileNameP() const
|
||||
{
|
||||
return FileName;
|
||||
}
|
||||
|
||||
//! Returns the directory of the selected file. Returns NULL, if no directory was selected.
|
||||
const io::path &CGUIFileOpenDialog::getDirectoryName() const
|
||||
{
|
||||
return FileDirectoryFlat;
|
||||
}
|
||||
|
||||
const wchar_t *CGUIFileOpenDialog::getDirectoryNameW() const
|
||||
{
|
||||
return FileDirectoryFlatW.c_str();
|
||||
}
|
||||
|
||||
void CGUIFileOpenDialog::setFileName(const irr::io::path &name)
|
||||
{
|
||||
FileName = name;
|
||||
pathToStringW(FileNameW, FileName);
|
||||
}
|
||||
|
||||
void CGUIFileOpenDialog::setDirectoryName(const irr::io::path &name)
|
||||
{
|
||||
FileDirectory = name;
|
||||
FileDirectoryFlat = name;
|
||||
FileSystem->flattenFilename(FileDirectoryFlat);
|
||||
pathToStringW(FileDirectoryFlatW, FileDirectoryFlat);
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUIFileOpenDialog::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
switch (event.EventType) {
|
||||
case EET_GUI_EVENT:
|
||||
switch (event.GUIEvent.EventType) {
|
||||
case EGET_ELEMENT_FOCUS_LOST:
|
||||
Dragging = false;
|
||||
break;
|
||||
case EGET_BUTTON_CLICKED:
|
||||
if (event.GUIEvent.Caller == CloseButton ||
|
||||
event.GUIEvent.Caller == CancelButton) {
|
||||
sendCancelEvent();
|
||||
remove();
|
||||
return true;
|
||||
} else if (event.GUIEvent.Caller == OKButton) {
|
||||
if (FileDirectory != L"") {
|
||||
sendSelectedEvent(EGET_DIRECTORY_SELECTED);
|
||||
}
|
||||
if (FileName != L"") {
|
||||
sendSelectedEvent(EGET_FILE_SELECTED);
|
||||
remove();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EGET_LISTBOX_CHANGED: {
|
||||
s32 selected = FileBox->getSelected();
|
||||
if (FileList && FileSystem) {
|
||||
if (FileList->isDirectory(selected)) {
|
||||
setFileName("");
|
||||
setDirectoryName(FileList->getFullFileName(selected));
|
||||
} else {
|
||||
setDirectoryName("");
|
||||
setFileName(FileList->getFullFileName(selected));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case EGET_LISTBOX_SELECTED_AGAIN: {
|
||||
const s32 selected = FileBox->getSelected();
|
||||
if (FileList && FileSystem) {
|
||||
if (FileList->isDirectory(selected)) {
|
||||
setDirectoryName(FileList->getFullFileName(selected));
|
||||
FileSystem->changeWorkingDirectoryTo(FileDirectory);
|
||||
fillListBox();
|
||||
setFileName("");
|
||||
} else {
|
||||
setFileName(FileList->getFullFileName(selected));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
case EGET_EDITBOX_ENTER:
|
||||
if (event.GUIEvent.Caller == FileNameText) {
|
||||
io::path dir(FileNameText->getText());
|
||||
if (FileSystem->changeWorkingDirectoryTo(dir)) {
|
||||
fillListBox();
|
||||
setFileName("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
switch (event.MouseInput.Event) {
|
||||
case EMIE_MOUSE_WHEEL:
|
||||
return FileBox->OnEvent(event);
|
||||
case EMIE_LMOUSE_PRESSED_DOWN:
|
||||
DragStart.X = event.MouseInput.X;
|
||||
DragStart.Y = event.MouseInput.Y;
|
||||
Dragging = true;
|
||||
return true;
|
||||
case EMIE_LMOUSE_LEFT_UP:
|
||||
Dragging = false;
|
||||
return true;
|
||||
case EMIE_MOUSE_MOVED:
|
||||
|
||||
if (!event.MouseInput.isLeftPressed())
|
||||
Dragging = false;
|
||||
|
||||
if (Dragging) {
|
||||
// gui window should not be dragged outside its parent
|
||||
if (Parent)
|
||||
if (event.MouseInput.X < Parent->getAbsolutePosition().UpperLeftCorner.X + 1 ||
|
||||
event.MouseInput.Y < Parent->getAbsolutePosition().UpperLeftCorner.Y + 1 ||
|
||||
event.MouseInput.X > Parent->getAbsolutePosition().LowerRightCorner.X - 1 ||
|
||||
event.MouseInput.Y > Parent->getAbsolutePosition().LowerRightCorner.Y - 1)
|
||||
|
||||
return true;
|
||||
|
||||
move(core::position2d<s32>(event.MouseInput.X - DragStart.X, event.MouseInput.Y - DragStart.Y));
|
||||
DragStart.X = event.MouseInput.X;
|
||||
DragStart.Y = event.MouseInput.Y;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIFileOpenDialog::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
core::rect<s32> rect = AbsoluteRect;
|
||||
|
||||
rect = skin->draw3DWindowBackground(this, true, skin->getColor(EGDC_ACTIVE_BORDER),
|
||||
rect, &AbsoluteClippingRect);
|
||||
|
||||
if (Text.size()) {
|
||||
rect.UpperLeftCorner.X += 2;
|
||||
rect.LowerRightCorner.X -= skin->getSize(EGDS_WINDOW_BUTTON_WIDTH) + 5;
|
||||
|
||||
IGUIFont *font = skin->getFont(EGDF_WINDOW);
|
||||
if (font)
|
||||
font->draw(Text.c_str(), rect,
|
||||
skin->getColor(EGDC_ACTIVE_CAPTION),
|
||||
false, true, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
void CGUIFileOpenDialog::pathToStringW(irr::core::stringw &result, const irr::io::path &p)
|
||||
{
|
||||
core::multibyteToWString(result, p);
|
||||
}
|
||||
|
||||
//! fills the listbox with files.
|
||||
void CGUIFileOpenDialog::fillListBox()
|
||||
{
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
if (!FileSystem || !FileBox || !skin)
|
||||
return;
|
||||
|
||||
if (FileList)
|
||||
FileList->drop();
|
||||
|
||||
FileBox->clear();
|
||||
|
||||
FileList = FileSystem->createFileList();
|
||||
core::stringw s;
|
||||
|
||||
if (FileList) {
|
||||
for (u32 i = 0; i < FileList->getFileCount(); ++i) {
|
||||
pathToStringW(s, FileList->getFileName(i));
|
||||
FileBox->addItem(s.c_str(), skin->getIcon(FileList->isDirectory(i) ? EGDI_DIRECTORY : EGDI_FILE));
|
||||
}
|
||||
}
|
||||
|
||||
if (FileNameText) {
|
||||
setDirectoryName(FileSystem->getWorkingDirectory());
|
||||
pathToStringW(s, FileDirectory);
|
||||
FileNameText->setText(s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//! sends the event that the file has been selected.
|
||||
void CGUIFileOpenDialog::sendSelectedEvent(EGUI_EVENT_TYPE type)
|
||||
{
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = 0;
|
||||
event.GUIEvent.EventType = type;
|
||||
Parent->OnEvent(event);
|
||||
}
|
||||
|
||||
//! sends the event that the file choose process has been cancelled
|
||||
void CGUIFileOpenDialog::sendCancelEvent()
|
||||
{
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = 0;
|
||||
event.GUIEvent.EventType = EGET_FILE_CHOOSE_DIALOG_CANCELLED;
|
||||
Parent->OnEvent(event);
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
84
src/CGUIFileOpenDialog.h
Normal file
84
src/CGUIFileOpenDialog.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIFileOpenDialog.h"
|
||||
#include "IGUIButton.h"
|
||||
#include "IGUIListBox.h"
|
||||
#include "IGUIEditBox.h"
|
||||
#include "IFileSystem.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIFileOpenDialog : public IGUIFileOpenDialog
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIFileOpenDialog(const wchar_t *title, IGUIEnvironment *environment,
|
||||
IGUIElement *parent, s32 id, bool restoreCWD = false,
|
||||
io::path::char_type *startDir = 0);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIFileOpenDialog();
|
||||
|
||||
//! returns the filename of the selected file. Returns NULL, if no file was selected.
|
||||
const wchar_t *getFileName() const override;
|
||||
|
||||
//! Returns the filename of the selected file. Is empty if no file was selected.
|
||||
const io::path &getFileNameP() const override;
|
||||
|
||||
//! Returns the directory of the selected file. Returns NULL, if no directory was selected.
|
||||
const io::path &getDirectoryName() const override;
|
||||
|
||||
//! Returns the directory of the selected file converted to wide characters. Returns NULL if no directory was selected.
|
||||
const wchar_t *getDirectoryNameW() const override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
protected:
|
||||
void setFileName(const irr::io::path &name);
|
||||
void setDirectoryName(const irr::io::path &name);
|
||||
|
||||
//! Ensure filenames are converted correct depending on wide-char settings
|
||||
void pathToStringW(irr::core::stringw &result, const irr::io::path &p);
|
||||
|
||||
//! fills the listbox with files.
|
||||
void fillListBox();
|
||||
|
||||
//! sends the event that the file has been selected.
|
||||
void sendSelectedEvent(EGUI_EVENT_TYPE type);
|
||||
|
||||
//! sends the event that the file choose process has been canceld
|
||||
void sendCancelEvent();
|
||||
|
||||
core::position2d<s32> DragStart;
|
||||
io::path FileName;
|
||||
core::stringw FileNameW;
|
||||
io::path FileDirectory;
|
||||
io::path FileDirectoryFlat;
|
||||
core::stringw FileDirectoryFlatW;
|
||||
io::path RestoreDirectory;
|
||||
io::path StartDirectory;
|
||||
|
||||
IGUIButton *CloseButton;
|
||||
IGUIButton *OKButton;
|
||||
IGUIButton *CancelButton;
|
||||
IGUIListBox *FileBox;
|
||||
IGUIEditBox *FileNameText;
|
||||
IGUIElement *EventParent;
|
||||
io::IFileSystem *FileSystem;
|
||||
io::IFileList *FileList;
|
||||
bool Dragging;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
548
src/CGUIFont.cpp
Normal file
548
src/CGUIFont.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIFont.h"
|
||||
|
||||
#include "os.h"
|
||||
#include "coreutil.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IReadFile.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUISpriteBank.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIFont::CGUIFont(IGUIEnvironment *env, const io::path &filename) :
|
||||
Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0),
|
||||
MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIFont");
|
||||
#endif
|
||||
|
||||
if (Environment) {
|
||||
// don't grab environment, to avoid circular references
|
||||
Driver = Environment->getVideoDriver();
|
||||
|
||||
SpriteBank = Environment->getSpriteBank(filename);
|
||||
if (!SpriteBank) // could be default-font which has no file
|
||||
SpriteBank = Environment->addEmptySpriteBank(filename);
|
||||
if (SpriteBank)
|
||||
SpriteBank->grab();
|
||||
}
|
||||
|
||||
if (Driver)
|
||||
Driver->grab();
|
||||
|
||||
setInvisibleCharacters(L" ");
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIFont::~CGUIFont()
|
||||
{
|
||||
if (Driver)
|
||||
Driver->drop();
|
||||
|
||||
if (SpriteBank) {
|
||||
SpriteBank->drop();
|
||||
// TODO: spritebank still exists in gui-environment and should be removed here when it's
|
||||
// reference-count is 1. Just can't do that from here at the moment.
|
||||
// But spritebank would not be able to drop textures anyway because those are in texture-cache
|
||||
// where they can't be removed unless materials start reference-couting 'em.
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//! loads a font file from xml
|
||||
bool CGUIFont::load(io::IXMLReader* xml, const io::path& directory)
|
||||
{
|
||||
if (!SpriteBank)
|
||||
return false;
|
||||
|
||||
SpriteBank->clear();
|
||||
|
||||
while (xml->read())
|
||||
{
|
||||
if (io::EXN_ELEMENT == xml->getNodeType())
|
||||
{
|
||||
if (core::stringw(L"Texture") == xml->getNodeName())
|
||||
{
|
||||
// add a texture
|
||||
core::stringc fn = xml->getAttributeValue(L"filename");
|
||||
u32 i = (u32)xml->getAttributeValueAsInt(L"index");
|
||||
core::stringw alpha = xml->getAttributeValue(L"hasAlpha");
|
||||
|
||||
while (i+1 > SpriteBank->getTextureCount())
|
||||
SpriteBank->addTexture(0);
|
||||
|
||||
bool flags[3];
|
||||
pushTextureCreationFlags(flags);
|
||||
|
||||
// load texture
|
||||
io::path textureFullName = core::mergeFilename(directory, fn);
|
||||
SpriteBank->setTexture(i, Driver->getTexture(textureFullName));
|
||||
|
||||
popTextureCreationFlags(flags);
|
||||
|
||||
// couldn't load texture, abort.
|
||||
if (!SpriteBank->getTexture(i))
|
||||
{
|
||||
os::Printer::log("Unable to load all textures in the font, aborting", ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// colorkey texture rather than alpha channel?
|
||||
if (alpha == core::stringw("false"))
|
||||
Driver->makeColorKeyTexture(SpriteBank->getTexture(i), core::position2di(0,0));
|
||||
}
|
||||
}
|
||||
else if (core::stringw(L"c") == xml->getNodeName())
|
||||
{
|
||||
// adding a character to this font
|
||||
SFontArea a;
|
||||
SGUISpriteFrame f;
|
||||
SGUISprite s;
|
||||
core::rect<s32> rectangle;
|
||||
|
||||
a.underhang = xml->getAttributeValueAsInt(L"u");
|
||||
a.overhang = xml->getAttributeValueAsInt(L"o");
|
||||
a.spriteno = SpriteBank->getSprites().size();
|
||||
s32 texno = xml->getAttributeValueAsInt(L"i");
|
||||
|
||||
// parse rectangle
|
||||
core::stringc rectstr = xml->getAttributeValue(L"r");
|
||||
wchar_t ch = xml->getAttributeValue(L"c")[0];
|
||||
|
||||
const c8 *c = rectstr.c_str();
|
||||
s32 val;
|
||||
val = 0;
|
||||
while (*c >= '0' && *c <= '9')
|
||||
{
|
||||
val *= 10;
|
||||
val += *c - '0';
|
||||
c++;
|
||||
}
|
||||
rectangle.UpperLeftCorner.X = val;
|
||||
while (*c == L' ' || *c == L',') c++;
|
||||
|
||||
val = 0;
|
||||
while (*c >= '0' && *c <= '9')
|
||||
{
|
||||
val *= 10;
|
||||
val += *c - '0';
|
||||
c++;
|
||||
}
|
||||
rectangle.UpperLeftCorner.Y = val;
|
||||
while (*c == L' ' || *c == L',') c++;
|
||||
|
||||
val = 0;
|
||||
while (*c >= '0' && *c <= '9')
|
||||
{
|
||||
val *= 10;
|
||||
val += *c - '0';
|
||||
c++;
|
||||
}
|
||||
rectangle.LowerRightCorner.X = val;
|
||||
while (*c == L' ' || *c == L',') c++;
|
||||
|
||||
val = 0;
|
||||
while (*c >= '0' && *c <= '9')
|
||||
{
|
||||
val *= 10;
|
||||
val += *c - '0';
|
||||
c++;
|
||||
}
|
||||
rectangle.LowerRightCorner.Y = val;
|
||||
|
||||
CharacterMap.emplace(ch, Areas.size());
|
||||
|
||||
// make frame
|
||||
f.rectNumber = SpriteBank->getPositions().size();
|
||||
f.textureNumber = texno;
|
||||
|
||||
// add frame to sprite
|
||||
s.Frames.push_back(f);
|
||||
s.frameTime = 0;
|
||||
|
||||
// add rectangle to sprite bank
|
||||
SpriteBank->getPositions().push_back(rectangle);
|
||||
a.width = rectangle.getWidth();
|
||||
|
||||
// add sprite to sprite bank
|
||||
SpriteBank->getSprites().push_back(s);
|
||||
|
||||
// add character to font
|
||||
Areas.push_back(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set bad character
|
||||
WrongCharacter = getAreaFromCharacter(L' ');
|
||||
|
||||
setMaxHeight();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CGUIFont::setMaxHeight()
|
||||
{
|
||||
if (!SpriteBank)
|
||||
return;
|
||||
|
||||
MaxHeight = 0;
|
||||
|
||||
core::array<core::rect<s32>> &p = SpriteBank->getPositions();
|
||||
|
||||
for (u32 i = 0; i < p.size(); ++i) {
|
||||
const s32 t = p[i].getHeight();
|
||||
if (t > MaxHeight)
|
||||
MaxHeight = t;
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIFont::pushTextureCreationFlags(bool (&flags)[3])
|
||||
{
|
||||
flags[0] = Driver->getTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2);
|
||||
flags[1] = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
|
||||
flags[2] = Driver->getTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY);
|
||||
|
||||
Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, true);
|
||||
Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
|
||||
Driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, true);
|
||||
}
|
||||
|
||||
void CGUIFont::popTextureCreationFlags(const bool (&flags)[3])
|
||||
{
|
||||
Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, flags[0]);
|
||||
Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flags[1]);
|
||||
Driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, flags[2]);
|
||||
}
|
||||
|
||||
//! loads a font file, native file needed, for texture parsing
|
||||
bool CGUIFont::load(io::IReadFile *file)
|
||||
{
|
||||
if (!Driver)
|
||||
return false;
|
||||
|
||||
return loadTexture(Driver->createImageFromFile(file),
|
||||
file->getFileName());
|
||||
}
|
||||
|
||||
//! loads a font file, native file needed, for texture parsing
|
||||
bool CGUIFont::load(const io::path &filename)
|
||||
{
|
||||
if (!Driver)
|
||||
return false;
|
||||
|
||||
return loadTexture(Driver->createImageFromFile(filename),
|
||||
filename);
|
||||
}
|
||||
|
||||
//! load & prepare font from ITexture
|
||||
bool CGUIFont::loadTexture(video::IImage *image, const io::path &name)
|
||||
{
|
||||
if (!image || !SpriteBank)
|
||||
return false;
|
||||
|
||||
s32 lowerRightPositions = 0;
|
||||
|
||||
video::IImage *tmpImage = image;
|
||||
bool deleteTmpImage = false;
|
||||
switch (image->getColorFormat()) {
|
||||
case video::ECF_R5G6B5:
|
||||
tmpImage = Driver->createImage(video::ECF_A1R5G5B5, image->getDimension());
|
||||
image->copyTo(tmpImage);
|
||||
deleteTmpImage = true;
|
||||
break;
|
||||
case video::ECF_A1R5G5B5:
|
||||
case video::ECF_A8R8G8B8:
|
||||
break;
|
||||
case video::ECF_R8G8B8:
|
||||
tmpImage = Driver->createImage(video::ECF_A8R8G8B8, image->getDimension());
|
||||
image->copyTo(tmpImage);
|
||||
deleteTmpImage = true;
|
||||
break;
|
||||
default:
|
||||
os::Printer::log("Unknown texture format provided for CGUIFont::loadTexture", ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
readPositions(tmpImage, lowerRightPositions);
|
||||
|
||||
WrongCharacter = getAreaFromCharacter(L' ');
|
||||
|
||||
// output warnings
|
||||
if (!lowerRightPositions || !SpriteBank->getSprites().size())
|
||||
os::Printer::log("Either no upper or lower corner pixels in the font file. If this font was made using the new font tool, please load the XML file instead. If not, the font may be corrupted.", ELL_ERROR);
|
||||
else if (lowerRightPositions != (s32)SpriteBank->getPositions().size())
|
||||
os::Printer::log("The amount of upper corner pixels and the lower corner pixels is not equal, font file may be corrupted.", ELL_ERROR);
|
||||
|
||||
bool ret = (!SpriteBank->getSprites().empty() && lowerRightPositions);
|
||||
|
||||
if (ret) {
|
||||
bool flags[3];
|
||||
pushTextureCreationFlags(flags);
|
||||
|
||||
SpriteBank->addTexture(Driver->addTexture(name, tmpImage));
|
||||
|
||||
popTextureCreationFlags(flags);
|
||||
}
|
||||
if (deleteTmpImage)
|
||||
tmpImage->drop();
|
||||
image->drop();
|
||||
|
||||
setMaxHeight();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGUIFont::readPositions(video::IImage *image, s32 &lowerRightPositions)
|
||||
{
|
||||
if (!SpriteBank)
|
||||
return;
|
||||
|
||||
const core::dimension2d<u32> size = image->getDimension();
|
||||
|
||||
video::SColor colorTopLeft = image->getPixel(0, 0);
|
||||
colorTopLeft.setAlpha(255);
|
||||
image->setPixel(0, 0, colorTopLeft);
|
||||
video::SColor colorLowerRight = image->getPixel(1, 0);
|
||||
video::SColor colorBackGround = image->getPixel(2, 0);
|
||||
video::SColor colorBackGroundTransparent = 0;
|
||||
|
||||
image->setPixel(1, 0, colorBackGround);
|
||||
|
||||
// start parsing
|
||||
|
||||
core::position2d<s32> pos(0, 0);
|
||||
for (pos.Y = 0; pos.Y < (s32)size.Height; ++pos.Y) {
|
||||
for (pos.X = 0; pos.X < (s32)size.Width; ++pos.X) {
|
||||
const video::SColor c = image->getPixel(pos.X, pos.Y);
|
||||
if (c == colorTopLeft) {
|
||||
image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
|
||||
SpriteBank->getPositions().push_back(core::rect<s32>(pos, pos));
|
||||
} else if (c == colorLowerRight) {
|
||||
// too many lower right points
|
||||
if (SpriteBank->getPositions().size() <= (u32)lowerRightPositions) {
|
||||
lowerRightPositions = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
|
||||
SpriteBank->getPositions()[lowerRightPositions].LowerRightCorner = pos;
|
||||
// add frame to sprite bank
|
||||
SGUISpriteFrame f;
|
||||
f.rectNumber = lowerRightPositions;
|
||||
f.textureNumber = 0;
|
||||
SGUISprite s;
|
||||
s.Frames.push_back(f);
|
||||
s.frameTime = 0;
|
||||
SpriteBank->getSprites().push_back(s);
|
||||
// add character to font
|
||||
SFontArea a;
|
||||
a.overhang = 0;
|
||||
a.underhang = 0;
|
||||
a.spriteno = lowerRightPositions;
|
||||
a.width = SpriteBank->getPositions()[lowerRightPositions].getWidth();
|
||||
Areas.push_back(a);
|
||||
// map letter to character
|
||||
wchar_t ch = (wchar_t)(lowerRightPositions + 32);
|
||||
CharacterMap[ch] = lowerRightPositions;
|
||||
|
||||
++lowerRightPositions;
|
||||
} else if (c == colorBackGround)
|
||||
image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on width )
|
||||
void CGUIFont::setKerningWidth(s32 kerning)
|
||||
{
|
||||
GlobalKerningWidth = kerning;
|
||||
}
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on width )
|
||||
s32 CGUIFont::getKerningWidth(const wchar_t *thisLetter, const wchar_t *previousLetter) const
|
||||
{
|
||||
s32 ret = GlobalKerningWidth;
|
||||
|
||||
if (thisLetter) {
|
||||
ret += Areas[getAreaFromCharacter(*thisLetter)].overhang;
|
||||
|
||||
if (previousLetter) {
|
||||
ret += Areas[getAreaFromCharacter(*previousLetter)].underhang;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on height )
|
||||
void CGUIFont::setKerningHeight(s32 kerning)
|
||||
{
|
||||
GlobalKerningHeight = kerning;
|
||||
}
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on height )
|
||||
s32 CGUIFont::getKerningHeight() const
|
||||
{
|
||||
return GlobalKerningHeight;
|
||||
}
|
||||
|
||||
//! returns the sprite number from a given character
|
||||
u32 CGUIFont::getSpriteNoFromChar(const wchar_t *c) const
|
||||
{
|
||||
return Areas[getAreaFromCharacter(*c)].spriteno;
|
||||
}
|
||||
|
||||
s32 CGUIFont::getAreaFromCharacter(const wchar_t c) const
|
||||
{
|
||||
auto n = CharacterMap.find(c);
|
||||
if (n != CharacterMap.end())
|
||||
return n->second;
|
||||
else
|
||||
return WrongCharacter;
|
||||
}
|
||||
|
||||
void CGUIFont::setInvisibleCharacters(const wchar_t *s)
|
||||
{
|
||||
Invisible = s;
|
||||
}
|
||||
|
||||
//! returns the dimension of text
|
||||
core::dimension2d<u32> CGUIFont::getDimension(const wchar_t *text) const
|
||||
{
|
||||
core::dimension2d<u32> dim(0, 0);
|
||||
core::dimension2d<u32> thisLine(0, MaxHeight);
|
||||
|
||||
for (const wchar_t *p = text; *p; ++p) {
|
||||
bool lineBreak = false;
|
||||
if (*p == L'\r') { // Mac or Windows breaks
|
||||
lineBreak = true;
|
||||
if (p[1] == L'\n') // Windows breaks
|
||||
++p;
|
||||
} else if (*p == L'\n') { // Unix breaks
|
||||
lineBreak = true;
|
||||
}
|
||||
if (lineBreak) {
|
||||
dim.Height += thisLine.Height;
|
||||
if (dim.Width < thisLine.Width)
|
||||
dim.Width = thisLine.Width;
|
||||
thisLine.Width = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const SFontArea &area = Areas[getAreaFromCharacter(*p)];
|
||||
|
||||
thisLine.Width += area.underhang;
|
||||
thisLine.Width += area.width + area.overhang + GlobalKerningWidth;
|
||||
}
|
||||
|
||||
dim.Height += thisLine.Height;
|
||||
if (dim.Width < thisLine.Width)
|
||||
dim.Width = thisLine.Width;
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
//! draws some text and clips it to the specified rectangle if wanted
|
||||
void CGUIFont::draw(const core::stringw &text, const core::rect<s32> &position,
|
||||
video::SColor color,
|
||||
bool hcenter, bool vcenter, const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver || !SpriteBank)
|
||||
return;
|
||||
|
||||
core::dimension2d<s32> textDimension; // NOTE: don't make this u32 or the >> later on can fail when the dimension width is < position width
|
||||
core::position2d<s32> offset = position.UpperLeftCorner;
|
||||
|
||||
if (hcenter || vcenter || clip)
|
||||
textDimension = getDimension(text.c_str());
|
||||
|
||||
if (hcenter)
|
||||
offset.X += (position.getWidth() - textDimension.Width) >> 1;
|
||||
|
||||
if (vcenter)
|
||||
offset.Y += (position.getHeight() - textDimension.Height) >> 1;
|
||||
|
||||
if (clip) {
|
||||
core::rect<s32> clippedRect(offset, textDimension);
|
||||
clippedRect.clipAgainst(*clip);
|
||||
if (!clippedRect.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
core::array<u32> indices(text.size());
|
||||
core::array<core::position2di> offsets(text.size());
|
||||
|
||||
for (u32 i = 0; i < text.size(); i++) {
|
||||
wchar_t c = text[i];
|
||||
|
||||
bool lineBreak = false;
|
||||
if (c == L'\r') { // Mac or Windows breaks
|
||||
lineBreak = true;
|
||||
if (text[i + 1] == L'\n') // Windows breaks
|
||||
c = text[++i];
|
||||
} else if (c == L'\n') { // Unix breaks
|
||||
lineBreak = true;
|
||||
}
|
||||
|
||||
if (lineBreak) {
|
||||
offset.Y += MaxHeight;
|
||||
offset.X = position.UpperLeftCorner.X;
|
||||
|
||||
if (hcenter) {
|
||||
offset.X += (position.getWidth() - textDimension.Width) >> 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SFontArea &area = Areas[getAreaFromCharacter(c)];
|
||||
|
||||
offset.X += area.underhang;
|
||||
if (Invisible.findFirst(c) < 0) {
|
||||
indices.push_back(area.spriteno);
|
||||
offsets.push_back(offset);
|
||||
}
|
||||
|
||||
offset.X += area.width + area.overhang + GlobalKerningWidth;
|
||||
}
|
||||
|
||||
SpriteBank->draw2DSpriteBatch(indices, offsets, clip, color);
|
||||
}
|
||||
|
||||
//! Calculates the index of the character in the text which is on a specific position.
|
||||
s32 CGUIFont::getCharacterFromPos(const wchar_t *text, s32 pixel_x) const
|
||||
{
|
||||
s32 x = 0;
|
||||
s32 idx = 0;
|
||||
|
||||
while (text[idx]) {
|
||||
const SFontArea &a = Areas[getAreaFromCharacter(text[idx])];
|
||||
|
||||
x += a.width + a.overhang + a.underhang + GlobalKerningWidth;
|
||||
|
||||
if (x >= pixel_x)
|
||||
return idx;
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
IGUISpriteBank *CGUIFont::getSpriteBank() const
|
||||
{
|
||||
return SpriteBank;
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
107
src/CGUIFont.h
Normal file
107
src/CGUIFont.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIFontBitmap.h"
|
||||
#include "irrString.h"
|
||||
#include "IReadFile.h"
|
||||
#include "irrArray.h"
|
||||
#include <map>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
namespace video
|
||||
{
|
||||
class IVideoDriver;
|
||||
class IImage;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class IGUIEnvironment;
|
||||
|
||||
class CGUIFont : public IGUIFontBitmap
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIFont(IGUIEnvironment *env, const io::path &filename);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIFont();
|
||||
|
||||
//! loads a font from a texture file
|
||||
bool load(const io::path &filename);
|
||||
|
||||
//! loads a font from a texture file
|
||||
bool load(io::IReadFile *file);
|
||||
|
||||
//! draws an text and clips it to the specified rectangle if wanted
|
||||
virtual void draw(const core::stringw &text, const core::rect<s32> &position,
|
||||
video::SColor color, bool hcenter = false,
|
||||
bool vcenter = false, const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! returns the dimension of a text
|
||||
core::dimension2d<u32> getDimension(const wchar_t *text) const override;
|
||||
|
||||
//! Calculates the index of the character in the text which is on a specific position.
|
||||
s32 getCharacterFromPos(const wchar_t *text, s32 pixel_x) const override;
|
||||
|
||||
//! Returns the type of this font
|
||||
EGUI_FONT_TYPE getType() const override { return EGFT_BITMAP; }
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on width )
|
||||
void setKerningWidth(s32 kerning) override;
|
||||
void setKerningHeight(s32 kerning) override;
|
||||
|
||||
//! set an Pixel Offset on Drawing ( scale position on width )
|
||||
s32 getKerningWidth(const wchar_t *thisLetter = 0, const wchar_t *previousLetter = 0) const override;
|
||||
s32 getKerningHeight() const override;
|
||||
|
||||
//! gets the sprite bank
|
||||
IGUISpriteBank *getSpriteBank() const override;
|
||||
|
||||
//! returns the sprite number from a given character
|
||||
u32 getSpriteNoFromChar(const wchar_t *c) const override;
|
||||
|
||||
void setInvisibleCharacters(const wchar_t *s) override;
|
||||
|
||||
private:
|
||||
struct SFontArea
|
||||
{
|
||||
SFontArea() :
|
||||
underhang(0), overhang(0), width(0), spriteno(0) {}
|
||||
s32 underhang;
|
||||
s32 overhang;
|
||||
s32 width;
|
||||
u32 spriteno;
|
||||
};
|
||||
|
||||
//! load & prepare font from ITexture
|
||||
bool loadTexture(video::IImage *image, const io::path &name);
|
||||
|
||||
void readPositions(video::IImage *texture, s32 &lowerRightPositions);
|
||||
|
||||
s32 getAreaFromCharacter(const wchar_t c) const;
|
||||
void setMaxHeight();
|
||||
|
||||
void pushTextureCreationFlags(bool (&flags)[3]);
|
||||
void popTextureCreationFlags(const bool (&flags)[3]);
|
||||
|
||||
core::array<SFontArea> Areas;
|
||||
std::map<wchar_t, s32> CharacterMap;
|
||||
video::IVideoDriver *Driver;
|
||||
IGUISpriteBank *SpriteBank;
|
||||
IGUIEnvironment *Environment;
|
||||
u32 WrongCharacter;
|
||||
s32 MaxHeight;
|
||||
s32 GlobalKerningWidth, GlobalKerningHeight;
|
||||
|
||||
core::stringw Invisible;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
164
src/CGUIImage.cpp
Normal file
164
src/CGUIImage.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIImage.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIImage::CGUIImage(IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect<s32> rectangle) :
|
||||
IGUIImage(environment, parent, id, rectangle), Texture(0), Color(255, 255, 255, 255),
|
||||
UseAlphaChannel(false), ScaleImage(false), DrawBounds(0.f, 0.f, 1.f, 1.f), DrawBackground(true)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIImage");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIImage::~CGUIImage()
|
||||
{
|
||||
if (Texture)
|
||||
Texture->drop();
|
||||
}
|
||||
|
||||
//! sets an image
|
||||
void CGUIImage::setImage(video::ITexture *image)
|
||||
{
|
||||
if (image == Texture)
|
||||
return;
|
||||
|
||||
if (Texture)
|
||||
Texture->drop();
|
||||
|
||||
Texture = image;
|
||||
|
||||
if (Texture)
|
||||
Texture->grab();
|
||||
}
|
||||
|
||||
//! Gets the image texture
|
||||
video::ITexture *CGUIImage::getImage() const
|
||||
{
|
||||
return Texture;
|
||||
}
|
||||
|
||||
//! sets the color of the image
|
||||
void CGUIImage::setColor(video::SColor color)
|
||||
{
|
||||
Color = color;
|
||||
}
|
||||
|
||||
//! Gets the color of the image
|
||||
video::SColor CGUIImage::getColor() const
|
||||
{
|
||||
return Color;
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIImage::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
|
||||
if (Texture) {
|
||||
core::rect<s32> sourceRect(SourceRect);
|
||||
if (sourceRect.getWidth() == 0 || sourceRect.getHeight() == 0) {
|
||||
sourceRect = core::rect<s32>(core::dimension2di(Texture->getOriginalSize()));
|
||||
}
|
||||
|
||||
if (ScaleImage) {
|
||||
const video::SColor Colors[] = {Color, Color, Color, Color};
|
||||
|
||||
core::rect<s32> clippingRect(AbsoluteClippingRect);
|
||||
checkBounds(clippingRect);
|
||||
|
||||
driver->draw2DImage(Texture, AbsoluteRect, sourceRect,
|
||||
&clippingRect, Colors, UseAlphaChannel);
|
||||
} else {
|
||||
core::rect<s32> clippingRect(AbsoluteRect.UpperLeftCorner, sourceRect.getSize());
|
||||
checkBounds(clippingRect);
|
||||
clippingRect.clipAgainst(AbsoluteClippingRect);
|
||||
|
||||
driver->draw2DImage(Texture, AbsoluteRect.UpperLeftCorner, sourceRect,
|
||||
&clippingRect, Color, UseAlphaChannel);
|
||||
}
|
||||
} else if (DrawBackground) {
|
||||
core::rect<s32> clippingRect(AbsoluteClippingRect);
|
||||
checkBounds(clippingRect);
|
||||
|
||||
skin->draw2DRectangle(this, skin->getColor(EGDC_3D_DARK_SHADOW), AbsoluteRect, &clippingRect);
|
||||
}
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! sets if the image should use its alpha channel to draw itself
|
||||
void CGUIImage::setUseAlphaChannel(bool use)
|
||||
{
|
||||
UseAlphaChannel = use;
|
||||
}
|
||||
|
||||
//! sets if the image should use its alpha channel to draw itself
|
||||
void CGUIImage::setScaleImage(bool scale)
|
||||
{
|
||||
ScaleImage = scale;
|
||||
}
|
||||
|
||||
//! Returns true if the image is scaled to fit, false if not
|
||||
bool CGUIImage::isImageScaled() const
|
||||
{
|
||||
return ScaleImage;
|
||||
}
|
||||
|
||||
//! Returns true if the image is using the alpha channel, false if not
|
||||
bool CGUIImage::isAlphaChannelUsed() const
|
||||
{
|
||||
return UseAlphaChannel;
|
||||
}
|
||||
|
||||
//! Sets the source rectangle of the image. By default the full image is used.
|
||||
void CGUIImage::setSourceRect(const core::rect<s32> &sourceRect)
|
||||
{
|
||||
SourceRect = sourceRect;
|
||||
}
|
||||
|
||||
//! Returns the customized source rectangle of the image to be used.
|
||||
core::rect<s32> CGUIImage::getSourceRect() const
|
||||
{
|
||||
return SourceRect;
|
||||
}
|
||||
|
||||
//! Restrict target drawing-area.
|
||||
void CGUIImage::setDrawBounds(const core::rect<f32> &drawBoundUVs)
|
||||
{
|
||||
DrawBounds = drawBoundUVs;
|
||||
DrawBounds.UpperLeftCorner.X = core::clamp(DrawBounds.UpperLeftCorner.X, 0.f, 1.f);
|
||||
DrawBounds.UpperLeftCorner.Y = core::clamp(DrawBounds.UpperLeftCorner.Y, 0.f, 1.f);
|
||||
DrawBounds.LowerRightCorner.X = core::clamp(DrawBounds.LowerRightCorner.X, 0.f, 1.f);
|
||||
DrawBounds.LowerRightCorner.X = core::clamp(DrawBounds.LowerRightCorner.X, 0.f, 1.f);
|
||||
if (DrawBounds.UpperLeftCorner.X > DrawBounds.LowerRightCorner.X)
|
||||
DrawBounds.UpperLeftCorner.X = DrawBounds.LowerRightCorner.X;
|
||||
if (DrawBounds.UpperLeftCorner.Y > DrawBounds.LowerRightCorner.Y)
|
||||
DrawBounds.UpperLeftCorner.Y = DrawBounds.LowerRightCorner.Y;
|
||||
}
|
||||
|
||||
//! Get target drawing-area restrictions.
|
||||
core::rect<f32> CGUIImage::getDrawBounds() const
|
||||
{
|
||||
return DrawBounds;
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
97
src/CGUIImage.h
Normal file
97
src/CGUIImage.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIImage.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIImage : public IGUIImage
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIImage(IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect<s32> rectangle);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIImage();
|
||||
|
||||
//! sets an image
|
||||
void setImage(video::ITexture *image) override;
|
||||
|
||||
//! Gets the image texture
|
||||
video::ITexture *getImage() const override;
|
||||
|
||||
//! sets the color of the image
|
||||
void setColor(video::SColor color) override;
|
||||
|
||||
//! sets if the image should scale to fit the element
|
||||
void setScaleImage(bool scale) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! sets if the image should use its alpha channel to draw itself
|
||||
void setUseAlphaChannel(bool use) override;
|
||||
|
||||
//! Gets the color of the image
|
||||
video::SColor getColor() const override;
|
||||
|
||||
//! Returns true if the image is scaled to fit, false if not
|
||||
bool isImageScaled() const override;
|
||||
|
||||
//! Returns true if the image is using the alpha channel, false if not
|
||||
bool isAlphaChannelUsed() const override;
|
||||
|
||||
//! Sets the source rectangle of the image. By default the full image is used.
|
||||
void setSourceRect(const core::rect<s32> &sourceRect) override;
|
||||
|
||||
//! Returns the customized source rectangle of the image to be used.
|
||||
core::rect<s32> getSourceRect() const override;
|
||||
|
||||
//! Restrict drawing-area.
|
||||
void setDrawBounds(const core::rect<f32> &drawBoundUVs) override;
|
||||
|
||||
//! Get drawing-area restrictions.
|
||||
core::rect<f32> getDrawBounds() const override;
|
||||
|
||||
//! Sets whether to draw a background color (EGDC_3D_DARK_SHADOW) when no texture is set
|
||||
void setDrawBackground(bool draw) override
|
||||
{
|
||||
DrawBackground = draw;
|
||||
}
|
||||
|
||||
//! Checks if a background is drawn when no texture is set
|
||||
bool isDrawBackgroundEnabled() const override
|
||||
{
|
||||
return DrawBackground;
|
||||
}
|
||||
|
||||
protected:
|
||||
void checkBounds(core::rect<s32> &rect)
|
||||
{
|
||||
f32 clipWidth = (f32)rect.getWidth();
|
||||
f32 clipHeight = (f32)rect.getHeight();
|
||||
|
||||
rect.UpperLeftCorner.X += core::round32(DrawBounds.UpperLeftCorner.X * clipWidth);
|
||||
rect.UpperLeftCorner.Y += core::round32(DrawBounds.UpperLeftCorner.Y * clipHeight);
|
||||
rect.LowerRightCorner.X -= core::round32((1.f - DrawBounds.LowerRightCorner.X) * clipWidth);
|
||||
rect.LowerRightCorner.Y -= core::round32((1.f - DrawBounds.LowerRightCorner.Y) * clipHeight);
|
||||
}
|
||||
|
||||
private:
|
||||
video::ITexture *Texture;
|
||||
video::SColor Color;
|
||||
bool UseAlphaChannel;
|
||||
bool ScaleImage;
|
||||
core::rect<s32> SourceRect;
|
||||
core::rect<f32> DrawBounds;
|
||||
bool DrawBackground;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
84
src/CGUIImageList.cpp
Normal file
84
src/CGUIImageList.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// written by Reinhard Ostermeier, reinhard@nospam.r-ostermeier.de
|
||||
// modified by Thomas Alten
|
||||
|
||||
#include "CGUIImageList.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIImageList::CGUIImageList(video::IVideoDriver *driver) :
|
||||
Driver(driver),
|
||||
Texture(0),
|
||||
ImageCount(0),
|
||||
ImageSize(0, 0),
|
||||
ImagesPerRow(0),
|
||||
UseAlphaChannel(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIImageList");
|
||||
#endif
|
||||
|
||||
if (Driver) {
|
||||
Driver->grab();
|
||||
}
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIImageList::~CGUIImageList()
|
||||
{
|
||||
if (Driver) {
|
||||
Driver->drop();
|
||||
}
|
||||
|
||||
if (Texture) {
|
||||
Texture->drop();
|
||||
}
|
||||
}
|
||||
|
||||
//! Creates the image list from texture.
|
||||
bool CGUIImageList::createImageList(video::ITexture *texture,
|
||||
core::dimension2d<s32> imageSize,
|
||||
bool useAlphaChannel)
|
||||
{
|
||||
if (!texture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Texture = texture;
|
||||
Texture->grab();
|
||||
|
||||
ImageSize = imageSize;
|
||||
|
||||
ImagesPerRow = Texture->getSize().Width / ImageSize.Width;
|
||||
ImageCount = ImagesPerRow * Texture->getSize().Height / ImageSize.Height;
|
||||
|
||||
UseAlphaChannel = useAlphaChannel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Draws an image and clips it to the specified rectangle if wanted
|
||||
void CGUIImageList::draw(s32 index, const core::position2d<s32> &destPos,
|
||||
const core::rect<s32> *clip /*= 0*/)
|
||||
{
|
||||
core::rect<s32> sourceRect;
|
||||
|
||||
if (!Driver || index < 0 || index >= ImageCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
sourceRect.UpperLeftCorner.X = (index % ImagesPerRow) * ImageSize.Width;
|
||||
sourceRect.UpperLeftCorner.Y = (index / ImagesPerRow) * ImageSize.Height;
|
||||
sourceRect.LowerRightCorner.X = sourceRect.UpperLeftCorner.X + ImageSize.Width;
|
||||
sourceRect.LowerRightCorner.Y = sourceRect.UpperLeftCorner.Y + ImageSize.Height;
|
||||
|
||||
Driver->draw2DImage(Texture, destPos, sourceRect, clip,
|
||||
video::SColor(255, 255, 255, 255), UseAlphaChannel);
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
65
src/CGUIImageList.h
Normal file
65
src/CGUIImageList.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// written by Reinhard Ostermeier, reinhard@nospam.r-ostermeier.de
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIImageList.h"
|
||||
#include "IVideoDriver.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIImageList : public IGUIImageList
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIImageList(video::IVideoDriver *Driver);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIImageList();
|
||||
|
||||
//! Creates the image list from texture.
|
||||
//! \param texture: The texture to use
|
||||
//! \param imageSize: Size of a single image
|
||||
//! \param useAlphaChannel: true if the alpha channel from the texture should be used
|
||||
//! \return
|
||||
//! true if the image list was created
|
||||
bool createImageList(video::ITexture *texture,
|
||||
core::dimension2d<s32> imageSize,
|
||||
bool useAlphaChannel);
|
||||
|
||||
//! Draws an image and clips it to the specified rectangle if wanted
|
||||
//! \param index: Index of the image
|
||||
//! \param destPos: Position of the image to draw
|
||||
//! \param clip: Optional pointer to a rectangle against which the text will be clipped.
|
||||
//! If the pointer is null, no clipping will be done.
|
||||
virtual void draw(s32 index, const core::position2d<s32> &destPos,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! Returns the count of Images in the list.
|
||||
//! \return Returns the count of Images in the list.
|
||||
s32 getImageCount() const override
|
||||
{
|
||||
return ImageCount;
|
||||
}
|
||||
|
||||
//! Returns the size of the images in the list.
|
||||
//! \return Returns the size of the images in the list.
|
||||
core::dimension2d<s32> getImageSize() const override
|
||||
{
|
||||
return ImageSize;
|
||||
}
|
||||
|
||||
private:
|
||||
video::IVideoDriver *Driver;
|
||||
video::ITexture *Texture;
|
||||
s32 ImageCount;
|
||||
core::dimension2d<s32> ImageSize;
|
||||
s32 ImagesPerRow;
|
||||
bool UseAlphaChannel;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
745
src/CGUIListBox.cpp
Normal file
745
src/CGUIListBox.cpp
Normal file
@@ -0,0 +1,745 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIListBox.h"
|
||||
|
||||
#include "CGUIListBox.h"
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IGUISpriteBank.h"
|
||||
#include "CGUIScrollBar.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIListBox::CGUIListBox(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle, bool clip,
|
||||
bool drawBack, bool moveOverSelect) :
|
||||
IGUIListBox(environment, parent, id, rectangle),
|
||||
Selected(-1),
|
||||
ItemHeight(0), ItemHeightOverride(0),
|
||||
TotalItemHeight(0), ItemsIconWidth(0), Font(0), IconBank(0),
|
||||
ScrollBar(0), selectTime(0), LastKeyTime(0), Selecting(false), DrawBack(drawBack),
|
||||
MoveOverSelect(moveOverSelect), AutoScroll(true), HighlightWhenNotFocused(true)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIListBox");
|
||||
#endif
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
ScrollBar = new CGUIScrollBar(false, Environment, this, -1,
|
||||
core::recti(0, 0, 1, 1),
|
||||
!clip);
|
||||
ScrollBar->setSubElement(true);
|
||||
ScrollBar->setTabStop(false);
|
||||
ScrollBar->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
ScrollBar->setVisible(false);
|
||||
ScrollBar->setPos(0);
|
||||
|
||||
updateScrollBarSize(skin->getSize(EGDS_SCROLLBAR_SIZE));
|
||||
|
||||
setNotClipped(!clip);
|
||||
|
||||
// this element can be tabbed to
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIListBox::~CGUIListBox()
|
||||
{
|
||||
if (ScrollBar)
|
||||
ScrollBar->drop();
|
||||
|
||||
if (Font)
|
||||
Font->drop();
|
||||
|
||||
if (IconBank)
|
||||
IconBank->drop();
|
||||
}
|
||||
|
||||
//! returns amount of list items
|
||||
u32 CGUIListBox::getItemCount() const
|
||||
{
|
||||
return Items.size();
|
||||
}
|
||||
|
||||
//! returns string of a list item. the may be a value from 0 to itemCount-1
|
||||
const wchar_t *CGUIListBox::getListItem(u32 id) const
|
||||
{
|
||||
if (id >= Items.size())
|
||||
return 0;
|
||||
|
||||
return Items[id].Text.c_str();
|
||||
}
|
||||
|
||||
//! Returns the icon of an item
|
||||
s32 CGUIListBox::getIcon(u32 id) const
|
||||
{
|
||||
if (id >= Items.size())
|
||||
return -1;
|
||||
|
||||
return Items[id].Icon;
|
||||
}
|
||||
|
||||
//! adds a list item, returns id of item
|
||||
u32 CGUIListBox::addItem(const wchar_t *text)
|
||||
{
|
||||
return addItem(text, -1);
|
||||
}
|
||||
|
||||
//! adds a list item, returns id of item
|
||||
void CGUIListBox::removeItem(u32 id)
|
||||
{
|
||||
if (id >= Items.size())
|
||||
return;
|
||||
|
||||
if ((u32)Selected == id) {
|
||||
Selected = -1;
|
||||
} else if ((u32)Selected > id) {
|
||||
Selected -= 1;
|
||||
selectTime = os::Timer::getTime();
|
||||
}
|
||||
|
||||
Items.erase(id);
|
||||
|
||||
recalculateItemHeight();
|
||||
}
|
||||
|
||||
s32 CGUIListBox::getItemAt(s32 xpos, s32 ypos) const
|
||||
{
|
||||
if (xpos < AbsoluteRect.UpperLeftCorner.X || xpos >= AbsoluteRect.LowerRightCorner.X || ypos < AbsoluteRect.UpperLeftCorner.Y || ypos >= AbsoluteRect.LowerRightCorner.Y)
|
||||
return -1;
|
||||
|
||||
if (ItemHeight == 0)
|
||||
return -1;
|
||||
|
||||
s32 item = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight;
|
||||
if (item < 0 || item >= (s32)Items.size())
|
||||
return -1;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
//! clears the list
|
||||
void CGUIListBox::clear()
|
||||
{
|
||||
Items.clear();
|
||||
ItemsIconWidth = 0;
|
||||
Selected = -1;
|
||||
|
||||
ScrollBar->setPos(0);
|
||||
|
||||
recalculateItemHeight();
|
||||
}
|
||||
|
||||
void CGUIListBox::recalculateItemHeight()
|
||||
{
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
if (Font != skin->getFont()) {
|
||||
if (Font)
|
||||
Font->drop();
|
||||
|
||||
Font = skin->getFont();
|
||||
if (0 == ItemHeightOverride)
|
||||
ItemHeight = 0;
|
||||
|
||||
if (Font) {
|
||||
if (0 == ItemHeightOverride)
|
||||
ItemHeight = Font->getDimension(L"A").Height + 4;
|
||||
|
||||
Font->grab();
|
||||
}
|
||||
}
|
||||
|
||||
TotalItemHeight = ItemHeight * Items.size();
|
||||
ScrollBar->setMax(core::max_(0, TotalItemHeight - AbsoluteRect.getHeight()));
|
||||
s32 minItemHeight = ItemHeight > 0 ? ItemHeight : 1;
|
||||
ScrollBar->setSmallStep(minItemHeight);
|
||||
ScrollBar->setLargeStep(2 * minItemHeight);
|
||||
|
||||
if (TotalItemHeight <= AbsoluteRect.getHeight())
|
||||
ScrollBar->setVisible(false);
|
||||
else
|
||||
ScrollBar->setVisible(true);
|
||||
}
|
||||
|
||||
//! returns id of selected item. returns -1 if no item is selected.
|
||||
s32 CGUIListBox::getSelected() const
|
||||
{
|
||||
return Selected;
|
||||
}
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void CGUIListBox::setSelected(s32 id)
|
||||
{
|
||||
if ((u32)id >= Items.size())
|
||||
Selected = -1;
|
||||
else
|
||||
Selected = id;
|
||||
|
||||
selectTime = os::Timer::getTime();
|
||||
|
||||
recalculateScrollPos();
|
||||
}
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void CGUIListBox::setSelected(const wchar_t *item)
|
||||
{
|
||||
s32 index = -1;
|
||||
|
||||
if (item) {
|
||||
for (index = 0; index < (s32)Items.size(); ++index) {
|
||||
if (Items[index].Text == item)
|
||||
break;
|
||||
}
|
||||
}
|
||||
setSelected(index);
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUIListBox::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
switch (event.EventType) {
|
||||
case EET_KEY_INPUT_EVENT:
|
||||
if (event.KeyInput.PressedDown &&
|
||||
(event.KeyInput.Key == KEY_DOWN ||
|
||||
event.KeyInput.Key == KEY_UP ||
|
||||
event.KeyInput.Key == KEY_HOME ||
|
||||
event.KeyInput.Key == KEY_END ||
|
||||
event.KeyInput.Key == KEY_NEXT ||
|
||||
event.KeyInput.Key == KEY_PRIOR)) {
|
||||
s32 oldSelected = Selected;
|
||||
switch (event.KeyInput.Key) {
|
||||
case KEY_DOWN:
|
||||
Selected += 1;
|
||||
break;
|
||||
case KEY_UP:
|
||||
Selected -= 1;
|
||||
break;
|
||||
case KEY_HOME:
|
||||
Selected = 0;
|
||||
break;
|
||||
case KEY_END:
|
||||
Selected = (s32)Items.size() - 1;
|
||||
break;
|
||||
case KEY_NEXT:
|
||||
Selected += AbsoluteRect.getHeight() / ItemHeight;
|
||||
break;
|
||||
case KEY_PRIOR:
|
||||
Selected -= AbsoluteRect.getHeight() / ItemHeight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (Selected < 0)
|
||||
Selected = 0;
|
||||
if (Selected >= (s32)Items.size())
|
||||
Selected = Items.size() - 1; // will set Selected to -1 for empty listboxes which is correct
|
||||
|
||||
recalculateScrollPos();
|
||||
|
||||
// post the news
|
||||
|
||||
if (oldSelected != Selected && Parent && !Selecting && !MoveOverSelect) {
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = this;
|
||||
e.GUIEvent.Element = 0;
|
||||
e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
|
||||
Parent->OnEvent(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (!event.KeyInput.PressedDown && (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) {
|
||||
if (Parent) {
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = this;
|
||||
e.GUIEvent.Element = 0;
|
||||
e.GUIEvent.EventType = EGET_LISTBOX_SELECTED_AGAIN;
|
||||
Parent->OnEvent(e);
|
||||
}
|
||||
return true;
|
||||
} else if (event.KeyInput.Key == KEY_TAB) {
|
||||
return false;
|
||||
} else if (event.KeyInput.PressedDown && event.KeyInput.Char) {
|
||||
// change selection based on text as it is typed.
|
||||
u32 now = os::Timer::getTime();
|
||||
|
||||
if (now - LastKeyTime < 500) {
|
||||
// add to key buffer if it isn't a key repeat
|
||||
if (!(KeyBuffer.size() == 1 && KeyBuffer[0] == event.KeyInput.Char)) {
|
||||
KeyBuffer += L" ";
|
||||
KeyBuffer[KeyBuffer.size() - 1] = event.KeyInput.Char;
|
||||
}
|
||||
} else {
|
||||
KeyBuffer = L" ";
|
||||
KeyBuffer[0] = event.KeyInput.Char;
|
||||
}
|
||||
LastKeyTime = now;
|
||||
|
||||
// find the selected item, starting at the current selection
|
||||
s32 start = Selected;
|
||||
// dont change selection if the key buffer matches the current item
|
||||
if (Selected > -1 && KeyBuffer.size() > 1) {
|
||||
if (Items[Selected].Text.size() >= KeyBuffer.size() &&
|
||||
KeyBuffer.equals_ignore_case(Items[Selected].Text.subString(0, KeyBuffer.size())))
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 current;
|
||||
for (current = start + 1; current < (s32)Items.size(); ++current) {
|
||||
if (Items[current].Text.size() >= KeyBuffer.size()) {
|
||||
if (KeyBuffer.equals_ignore_case(Items[current].Text.subString(0, KeyBuffer.size()))) {
|
||||
if (Parent && Selected != current && !Selecting && !MoveOverSelect) {
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = this;
|
||||
e.GUIEvent.Element = 0;
|
||||
e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
|
||||
Parent->OnEvent(e);
|
||||
}
|
||||
setSelected(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (current = 0; current <= start; ++current) {
|
||||
if (Items[current].Text.size() >= KeyBuffer.size()) {
|
||||
if (KeyBuffer.equals_ignore_case(Items[current].Text.subString(0, KeyBuffer.size()))) {
|
||||
if (Parent && Selected != current && !Selecting && !MoveOverSelect) {
|
||||
Selected = current;
|
||||
SEvent e;
|
||||
e.EventType = EET_GUI_EVENT;
|
||||
e.GUIEvent.Caller = this;
|
||||
e.GUIEvent.Element = 0;
|
||||
e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
|
||||
Parent->OnEvent(e);
|
||||
}
|
||||
setSelected(current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case EET_GUI_EVENT:
|
||||
switch (event.GUIEvent.EventType) {
|
||||
case gui::EGET_SCROLL_BAR_CHANGED:
|
||||
if (event.GUIEvent.Caller == ScrollBar)
|
||||
return true;
|
||||
break;
|
||||
case gui::EGET_ELEMENT_FOCUS_LOST: {
|
||||
if (event.GUIEvent.Caller == this)
|
||||
Selecting = false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EET_MOUSE_INPUT_EVENT: {
|
||||
core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
|
||||
|
||||
switch (event.MouseInput.Event) {
|
||||
case EMIE_MOUSE_WHEEL:
|
||||
ScrollBar->setPos(ScrollBar->getPos() + (event.MouseInput.Wheel < 0 ? -1 : 1) * -ItemHeight / 2);
|
||||
return true;
|
||||
|
||||
case EMIE_LMOUSE_PRESSED_DOWN: {
|
||||
Selecting = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case EMIE_LMOUSE_LEFT_UP: {
|
||||
Selecting = false;
|
||||
|
||||
if (isPointInside(p))
|
||||
selectNew(event.MouseInput.Y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case EMIE_MOUSE_MOVED:
|
||||
if (Selecting || MoveOverSelect) {
|
||||
if (isPointInside(p)) {
|
||||
selectNew(event.MouseInput.Y, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
void CGUIListBox::selectNew(s32 ypos, bool onlyHover)
|
||||
{
|
||||
u32 now = os::Timer::getTime();
|
||||
s32 oldSelected = Selected;
|
||||
|
||||
Selected = getItemAt(AbsoluteRect.UpperLeftCorner.X, ypos);
|
||||
if (Selected < 0 && !Items.empty())
|
||||
Selected = 0;
|
||||
|
||||
recalculateScrollPos();
|
||||
|
||||
gui::EGUI_EVENT_TYPE eventType = (Selected == oldSelected && now < selectTime + 500) ? EGET_LISTBOX_SELECTED_AGAIN : EGET_LISTBOX_CHANGED;
|
||||
selectTime = now;
|
||||
// post the news
|
||||
if (Parent && !onlyHover) {
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = 0;
|
||||
event.GUIEvent.EventType = eventType;
|
||||
Parent->OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
//! Update the position and size of the listbox, and update the scrollbar
|
||||
void CGUIListBox::updateAbsolutePosition()
|
||||
{
|
||||
IGUIElement::updateAbsolutePosition();
|
||||
|
||||
recalculateItemHeight();
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIListBox::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
recalculateItemHeight(); // if the font changed
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
updateScrollBarSize(skin->getSize(EGDS_SCROLLBAR_SIZE));
|
||||
|
||||
core::rect<s32> *clipRect = 0;
|
||||
|
||||
// draw background
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
// draw items
|
||||
|
||||
core::rect<s32> clientClip(AbsoluteRect);
|
||||
clientClip.UpperLeftCorner.Y += 1;
|
||||
clientClip.UpperLeftCorner.X += 1;
|
||||
if (ScrollBar->isVisible())
|
||||
clientClip.LowerRightCorner.X -= ScrollBar->getRelativePosition().getWidth();
|
||||
clientClip.LowerRightCorner.Y -= 1;
|
||||
clientClip.clipAgainst(AbsoluteClippingRect);
|
||||
|
||||
skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true,
|
||||
DrawBack, frameRect, &AbsoluteClippingRect);
|
||||
|
||||
if (clipRect)
|
||||
clientClip.clipAgainst(*clipRect);
|
||||
|
||||
frameRect = AbsoluteRect;
|
||||
frameRect.UpperLeftCorner.X += 1;
|
||||
if (ScrollBar->isVisible())
|
||||
frameRect.LowerRightCorner.X -= ScrollBar->getRelativePosition().getWidth();
|
||||
|
||||
frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight;
|
||||
|
||||
frameRect.UpperLeftCorner.Y -= ScrollBar->getPos();
|
||||
frameRect.LowerRightCorner.Y -= ScrollBar->getPos();
|
||||
|
||||
bool hl = (HighlightWhenNotFocused || Environment->hasFocus(this) || Environment->hasFocus(ScrollBar));
|
||||
|
||||
for (s32 i = 0; i < (s32)Items.size(); ++i) {
|
||||
if (frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y &&
|
||||
frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y) {
|
||||
if (i == Selected && hl)
|
||||
skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), frameRect, &clientClip);
|
||||
|
||||
core::rect<s32> textRect = frameRect;
|
||||
textRect.UpperLeftCorner.X += 3;
|
||||
|
||||
if (Font) {
|
||||
if (IconBank && (Items[i].Icon > -1)) {
|
||||
core::position2di iconPos = textRect.UpperLeftCorner;
|
||||
iconPos.Y += textRect.getHeight() / 2;
|
||||
iconPos.X += ItemsIconWidth / 2;
|
||||
|
||||
if (i == Selected && hl) {
|
||||
IconBank->draw2DSprite((u32)Items[i].Icon, iconPos, &clientClip,
|
||||
hasItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) ? getItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_ICON_HIGHLIGHT),
|
||||
selectTime, os::Timer::getTime(), false, true);
|
||||
} else {
|
||||
IconBank->draw2DSprite((u32)Items[i].Icon, iconPos, &clientClip,
|
||||
hasItemOverrideColor(i, EGUI_LBC_ICON) ? getItemOverrideColor(i, EGUI_LBC_ICON) : getItemDefaultColor(EGUI_LBC_ICON),
|
||||
0, (i == Selected) ? os::Timer::getTime() : 0, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
textRect.UpperLeftCorner.X += ItemsIconWidth + 3;
|
||||
|
||||
if (i == Selected && hl) {
|
||||
Font->draw(Items[i].Text.c_str(), textRect,
|
||||
hasItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) ? getItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_TEXT_HIGHLIGHT),
|
||||
false, true, &clientClip);
|
||||
} else {
|
||||
Font->draw(Items[i].Text.c_str(), textRect,
|
||||
hasItemOverrideColor(i, EGUI_LBC_TEXT) ? getItemOverrideColor(i, EGUI_LBC_TEXT) : getItemDefaultColor(EGUI_LBC_TEXT),
|
||||
false, true, &clientClip);
|
||||
}
|
||||
|
||||
textRect.UpperLeftCorner.X -= ItemsIconWidth + 3;
|
||||
}
|
||||
}
|
||||
|
||||
frameRect.UpperLeftCorner.Y += ItemHeight;
|
||||
frameRect.LowerRightCorner.Y += ItemHeight;
|
||||
}
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! adds an list item with an icon
|
||||
u32 CGUIListBox::addItem(const wchar_t *text, s32 icon)
|
||||
{
|
||||
ListItem i;
|
||||
i.Text = text;
|
||||
i.Icon = icon;
|
||||
|
||||
Items.push_back(i);
|
||||
recalculateItemHeight();
|
||||
recalculateItemWidth(icon);
|
||||
|
||||
return Items.size() - 1;
|
||||
}
|
||||
|
||||
void CGUIListBox::setSpriteBank(IGUISpriteBank *bank)
|
||||
{
|
||||
if (bank == IconBank)
|
||||
return;
|
||||
if (IconBank)
|
||||
IconBank->drop();
|
||||
|
||||
IconBank = bank;
|
||||
if (IconBank)
|
||||
IconBank->grab();
|
||||
}
|
||||
|
||||
void CGUIListBox::recalculateScrollPos()
|
||||
{
|
||||
if (!AutoScroll)
|
||||
return;
|
||||
|
||||
const s32 selPos = (getSelected() == -1 ? TotalItemHeight : getSelected() * ItemHeight) - ScrollBar->getPos();
|
||||
|
||||
if (selPos < 0) {
|
||||
ScrollBar->setPos(ScrollBar->getPos() + selPos);
|
||||
} else if (selPos > AbsoluteRect.getHeight() - ItemHeight) {
|
||||
ScrollBar->setPos(ScrollBar->getPos() + selPos - AbsoluteRect.getHeight() + ItemHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIListBox::updateScrollBarSize(s32 size)
|
||||
{
|
||||
if (size != ScrollBar->getRelativePosition().getWidth()) {
|
||||
core::recti r(RelativeRect.getWidth() - size, 0, RelativeRect.getWidth(), RelativeRect.getHeight());
|
||||
ScrollBar->setRelativePosition(r);
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIListBox::setAutoScrollEnabled(bool scroll)
|
||||
{
|
||||
AutoScroll = scroll;
|
||||
}
|
||||
|
||||
bool CGUIListBox::isAutoScrollEnabled() const
|
||||
{
|
||||
return AutoScroll;
|
||||
}
|
||||
|
||||
bool CGUIListBox::getSerializationLabels(EGUI_LISTBOX_COLOR colorType, core::stringc &useColorLabel, core::stringc &colorLabel) const
|
||||
{
|
||||
switch (colorType) {
|
||||
case EGUI_LBC_TEXT:
|
||||
useColorLabel = "UseColText";
|
||||
colorLabel = "ColText";
|
||||
break;
|
||||
case EGUI_LBC_TEXT_HIGHLIGHT:
|
||||
useColorLabel = "UseColTextHl";
|
||||
colorLabel = "ColTextHl";
|
||||
break;
|
||||
case EGUI_LBC_ICON:
|
||||
useColorLabel = "UseColIcon";
|
||||
colorLabel = "ColIcon";
|
||||
break;
|
||||
case EGUI_LBC_ICON_HIGHLIGHT:
|
||||
useColorLabel = "UseColIconHl";
|
||||
colorLabel = "ColIconHl";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGUIListBox::recalculateItemWidth(s32 icon)
|
||||
{
|
||||
if (IconBank && icon > -1 &&
|
||||
IconBank->getSprites().size() > (u32)icon &&
|
||||
IconBank->getSprites()[(u32)icon].Frames.size()) {
|
||||
u32 rno = IconBank->getSprites()[(u32)icon].Frames[0].rectNumber;
|
||||
if (IconBank->getPositions().size() > rno) {
|
||||
const s32 w = IconBank->getPositions()[rno].getWidth();
|
||||
if (w > ItemsIconWidth)
|
||||
ItemsIconWidth = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIListBox::setItem(u32 index, const wchar_t *text, s32 icon)
|
||||
{
|
||||
if (index >= Items.size())
|
||||
return;
|
||||
|
||||
Items[index].Text = text;
|
||||
Items[index].Icon = icon;
|
||||
|
||||
recalculateItemHeight();
|
||||
recalculateItemWidth(icon);
|
||||
}
|
||||
|
||||
//! Insert the item at the given index
|
||||
//! Return the index on success or -1 on failure.
|
||||
s32 CGUIListBox::insertItem(u32 index, const wchar_t *text, s32 icon)
|
||||
{
|
||||
ListItem i;
|
||||
i.Text = text;
|
||||
i.Icon = icon;
|
||||
|
||||
Items.insert(i, index);
|
||||
recalculateItemHeight();
|
||||
recalculateItemWidth(icon);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void CGUIListBox::swapItems(u32 index1, u32 index2)
|
||||
{
|
||||
if (index1 >= Items.size() || index2 >= Items.size())
|
||||
return;
|
||||
|
||||
ListItem dummmy = Items[index1];
|
||||
Items[index1] = Items[index2];
|
||||
Items[index2] = dummmy;
|
||||
}
|
||||
|
||||
void CGUIListBox::setItemOverrideColor(u32 index, video::SColor color)
|
||||
{
|
||||
for (u32 c = 0; c < EGUI_LBC_COUNT; ++c) {
|
||||
Items[index].OverrideColors[c].Use = true;
|
||||
Items[index].OverrideColors[c].Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIListBox::setItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType, video::SColor color)
|
||||
{
|
||||
if (index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT)
|
||||
return;
|
||||
|
||||
Items[index].OverrideColors[colorType].Use = true;
|
||||
Items[index].OverrideColors[colorType].Color = color;
|
||||
}
|
||||
|
||||
void CGUIListBox::clearItemOverrideColor(u32 index)
|
||||
{
|
||||
for (u32 c = 0; c < (u32)EGUI_LBC_COUNT; ++c) {
|
||||
Items[index].OverrideColors[c].Use = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CGUIListBox::clearItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType)
|
||||
{
|
||||
if (index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT)
|
||||
return;
|
||||
|
||||
Items[index].OverrideColors[colorType].Use = false;
|
||||
}
|
||||
|
||||
bool CGUIListBox::hasItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const
|
||||
{
|
||||
if (index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT)
|
||||
return false;
|
||||
|
||||
return Items[index].OverrideColors[colorType].Use;
|
||||
}
|
||||
|
||||
video::SColor CGUIListBox::getItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const
|
||||
{
|
||||
if ((u32)index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT)
|
||||
return video::SColor();
|
||||
|
||||
return Items[index].OverrideColors[colorType].Color;
|
||||
}
|
||||
|
||||
video::SColor CGUIListBox::getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const
|
||||
{
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return video::SColor();
|
||||
|
||||
switch (colorType) {
|
||||
case EGUI_LBC_TEXT:
|
||||
return skin->getColor(EGDC_BUTTON_TEXT);
|
||||
case EGUI_LBC_TEXT_HIGHLIGHT:
|
||||
return skin->getColor(EGDC_HIGH_LIGHT_TEXT);
|
||||
case EGUI_LBC_ICON:
|
||||
return skin->getColor(EGDC_ICON);
|
||||
case EGUI_LBC_ICON_HIGHLIGHT:
|
||||
return skin->getColor(EGDC_ICON_HIGH_LIGHT);
|
||||
default:
|
||||
return video::SColor();
|
||||
}
|
||||
}
|
||||
|
||||
//! set global itemHeight
|
||||
void CGUIListBox::setItemHeight(s32 height)
|
||||
{
|
||||
ItemHeight = height;
|
||||
ItemHeightOverride = 1;
|
||||
}
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void CGUIListBox::setDrawBackground(bool draw)
|
||||
{
|
||||
DrawBack = draw;
|
||||
}
|
||||
|
||||
//! Access the vertical scrollbar
|
||||
IGUIScrollBar *CGUIListBox::getVerticalScrollBar() const
|
||||
{
|
||||
return ScrollBar;
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
173
src/CGUIListBox.h
Normal file
173
src/CGUIListBox.h
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIListBox.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class IGUIFont;
|
||||
class IGUIScrollBar;
|
||||
|
||||
class CGUIListBox : public IGUIListBox
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIListBox(IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, core::rect<s32> rectangle, bool clip = true,
|
||||
bool drawBack = false, bool moveOverSelect = false);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIListBox();
|
||||
|
||||
//! returns amount of list items
|
||||
u32 getItemCount() const override;
|
||||
|
||||
//! returns string of a list item. the id may be a value from 0 to itemCount-1
|
||||
const wchar_t *getListItem(u32 id) const override;
|
||||
|
||||
//! adds an list item, returns id of item
|
||||
u32 addItem(const wchar_t *text) override;
|
||||
|
||||
//! clears the list
|
||||
void clear() override;
|
||||
|
||||
//! returns id of selected item. returns -1 if no item is selected.
|
||||
s32 getSelected() const override;
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void setSelected(s32 id) override;
|
||||
|
||||
//! sets the selected item. Set this to -1 if no item should be selected
|
||||
void setSelected(const wchar_t *item) override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! adds an list item with an icon
|
||||
//! \param text Text of list entry
|
||||
//! \param icon Sprite index of the Icon within the current sprite bank. Set it to -1 if you want no icon
|
||||
//! \return
|
||||
//! returns the id of the new created item
|
||||
u32 addItem(const wchar_t *text, s32 icon) override;
|
||||
|
||||
//! Returns the icon of an item
|
||||
s32 getIcon(u32 id) const override;
|
||||
|
||||
//! removes an item from the list
|
||||
void removeItem(u32 id) override;
|
||||
|
||||
//! get the the id of the item at the given absolute coordinates
|
||||
s32 getItemAt(s32 xpos, s32 ypos) const override;
|
||||
|
||||
//! Sets the sprite bank which should be used to draw list icons. This font is set to the sprite bank of
|
||||
//! the built-in-font by default. A sprite can be displayed in front of every list item.
|
||||
//! An icon is an index within the icon sprite bank. Several default icons are available in the
|
||||
//! skin through getIcon
|
||||
void setSpriteBank(IGUISpriteBank *bank) override;
|
||||
|
||||
//! set whether the listbox should scroll to newly selected items
|
||||
void setAutoScrollEnabled(bool scroll) override;
|
||||
|
||||
//! returns true if automatic scrolling is enabled, false if not.
|
||||
bool isAutoScrollEnabled() const override;
|
||||
|
||||
//! Update the position and size of the listbox, and update the scrollbar
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
//! set all item colors at given index to color
|
||||
void setItemOverrideColor(u32 index, video::SColor color) override;
|
||||
|
||||
//! set all item colors of specified type at given index to color
|
||||
void setItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType, video::SColor color) override;
|
||||
|
||||
//! clear all item colors at index
|
||||
void clearItemOverrideColor(u32 index) override;
|
||||
|
||||
//! clear item color at index for given colortype
|
||||
void clearItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) override;
|
||||
|
||||
//! has the item at index its color overwritten?
|
||||
bool hasItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const override;
|
||||
|
||||
//! return the overwrite color at given item index.
|
||||
video::SColor getItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const override;
|
||||
|
||||
//! return the default color which is used for the given colorType
|
||||
video::SColor getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const override;
|
||||
|
||||
//! set the item at the given index
|
||||
void setItem(u32 index, const wchar_t *text, s32 icon) override;
|
||||
|
||||
//! Insert the item at the given index
|
||||
//! Return the index on success or -1 on failure.
|
||||
s32 insertItem(u32 index, const wchar_t *text, s32 icon) override;
|
||||
|
||||
//! Swap the items at the given indices
|
||||
void swapItems(u32 index1, u32 index2) override;
|
||||
|
||||
//! set global itemHeight
|
||||
void setItemHeight(s32 height) override;
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void setDrawBackground(bool draw) override;
|
||||
|
||||
//! Access the vertical scrollbar
|
||||
IGUIScrollBar *getVerticalScrollBar() const override;
|
||||
|
||||
private:
|
||||
struct ListItem
|
||||
{
|
||||
core::stringw Text;
|
||||
s32 Icon = -1;
|
||||
|
||||
// A multicolor extension
|
||||
struct ListItemOverrideColor
|
||||
{
|
||||
bool Use = false;
|
||||
video::SColor Color;
|
||||
};
|
||||
ListItemOverrideColor OverrideColors[EGUI_LBC_COUNT]{};
|
||||
};
|
||||
|
||||
void recalculateItemHeight();
|
||||
void selectNew(s32 ypos, bool onlyHover = false);
|
||||
void recalculateScrollPos();
|
||||
void updateScrollBarSize(s32 size);
|
||||
|
||||
// extracted that function to avoid copy&paste code
|
||||
void recalculateItemWidth(s32 icon);
|
||||
|
||||
// get labels used for serialization
|
||||
bool getSerializationLabels(EGUI_LISTBOX_COLOR colorType, core::stringc &useColorLabel, core::stringc &colorLabel) const;
|
||||
|
||||
core::array<ListItem> Items;
|
||||
s32 Selected;
|
||||
s32 ItemHeight;
|
||||
s32 ItemHeightOverride;
|
||||
s32 TotalItemHeight;
|
||||
s32 ItemsIconWidth;
|
||||
gui::IGUIFont *Font;
|
||||
gui::IGUISpriteBank *IconBank;
|
||||
gui::IGUIScrollBar *ScrollBar;
|
||||
u32 selectTime;
|
||||
u32 LastKeyTime;
|
||||
core::stringw KeyBuffer;
|
||||
bool Selecting;
|
||||
bool DrawBack;
|
||||
bool MoveOverSelect;
|
||||
bool AutoScroll;
|
||||
bool HighlightWhenNotFocused;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
463
src/CGUIScrollBar.cpp
Normal file
463
src/CGUIScrollBar.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIScrollBar.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "CGUIButton.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IGUIFontBitmap.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIScrollBar::CGUIScrollBar(bool horizontal, IGUIEnvironment *environment,
|
||||
IGUIElement *parent, s32 id,
|
||||
core::rect<s32> rectangle, bool noclip) :
|
||||
IGUIScrollBar(environment, parent, id, rectangle),
|
||||
UpButton(0),
|
||||
DownButton(0), Dragging(false), Horizontal(horizontal),
|
||||
DraggedBySlider(false), TrayClick(false), Pos(0), DrawPos(0),
|
||||
DrawHeight(0), Min(0), Max(100), SmallStep(10), LargeStep(50), DesiredPos(0),
|
||||
LastChange(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIScrollBar");
|
||||
#endif
|
||||
|
||||
refreshControls();
|
||||
|
||||
setNotClipped(noclip);
|
||||
|
||||
// this element can be tabbed to
|
||||
setTabStop(true);
|
||||
setTabOrder(-1);
|
||||
|
||||
setPos(0);
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIScrollBar::~CGUIScrollBar()
|
||||
{
|
||||
if (UpButton)
|
||||
UpButton->drop();
|
||||
|
||||
if (DownButton)
|
||||
DownButton->drop();
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUIScrollBar::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
|
||||
switch (event.EventType) {
|
||||
case EET_KEY_INPUT_EVENT:
|
||||
if (event.KeyInput.PressedDown) {
|
||||
const s32 oldPos = Pos;
|
||||
bool absorb = true;
|
||||
switch (event.KeyInput.Key) {
|
||||
case KEY_LEFT:
|
||||
case KEY_UP:
|
||||
setPos(Pos - SmallStep);
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
case KEY_DOWN:
|
||||
setPos(Pos + SmallStep);
|
||||
break;
|
||||
case KEY_HOME:
|
||||
setPos(Min);
|
||||
break;
|
||||
case KEY_PRIOR:
|
||||
setPos(Pos - LargeStep);
|
||||
break;
|
||||
case KEY_END:
|
||||
setPos(Max);
|
||||
break;
|
||||
case KEY_NEXT:
|
||||
setPos(Pos + LargeStep);
|
||||
break;
|
||||
default:
|
||||
absorb = false;
|
||||
}
|
||||
|
||||
if (Pos != oldPos) {
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
if (absorb)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case EET_GUI_EVENT:
|
||||
if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) {
|
||||
if (event.GUIEvent.Caller == UpButton)
|
||||
setPos(Pos - SmallStep);
|
||||
else if (event.GUIEvent.Caller == DownButton)
|
||||
setPos(Pos + SmallStep);
|
||||
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
|
||||
return true;
|
||||
} else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
|
||||
if (event.GUIEvent.Caller == this)
|
||||
Dragging = false;
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT: {
|
||||
const core::position2di p(event.MouseInput.X, event.MouseInput.Y);
|
||||
bool isInside = isPointInside(p);
|
||||
switch (event.MouseInput.Event) {
|
||||
case EMIE_MOUSE_WHEEL:
|
||||
if (Environment->hasFocus(this)) {
|
||||
// thanks to a bug report by REAPER
|
||||
// thanks to tommi by tommi for another bugfix
|
||||
// everybody needs a little thanking. hallo niko!;-)
|
||||
setPos(getPos() +
|
||||
((event.MouseInput.Wheel < 0 ? -1 : 1) * SmallStep * (Horizontal ? 1 : -1)));
|
||||
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case EMIE_LMOUSE_PRESSED_DOWN: {
|
||||
if (isInside) {
|
||||
Dragging = true;
|
||||
DraggedBySlider = SliderRect.isPointInside(p);
|
||||
TrayClick = !DraggedBySlider;
|
||||
DesiredPos = getPosFromMousePos(p);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EMIE_LMOUSE_LEFT_UP:
|
||||
case EMIE_MOUSE_MOVED: {
|
||||
if (!event.MouseInput.isLeftPressed())
|
||||
Dragging = false;
|
||||
|
||||
if (!Dragging) {
|
||||
if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
|
||||
break;
|
||||
return isInside;
|
||||
}
|
||||
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
|
||||
Dragging = false;
|
||||
|
||||
const s32 newPos = getPosFromMousePos(p);
|
||||
const s32 oldPos = Pos;
|
||||
|
||||
if (!DraggedBySlider) {
|
||||
if (isInside) {
|
||||
DraggedBySlider = SliderRect.isPointInside(p);
|
||||
TrayClick = !DraggedBySlider;
|
||||
}
|
||||
|
||||
if (DraggedBySlider) {
|
||||
setPos(newPos);
|
||||
} else {
|
||||
TrayClick = false;
|
||||
if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
|
||||
return isInside;
|
||||
}
|
||||
}
|
||||
|
||||
if (DraggedBySlider) {
|
||||
setPos(newPos);
|
||||
} else {
|
||||
DesiredPos = newPos;
|
||||
}
|
||||
|
||||
if (Pos != oldPos && Parent) {
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
return isInside;
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
void CGUIScrollBar::OnPostRender(u32 timeMs)
|
||||
{
|
||||
if (Dragging && !DraggedBySlider && TrayClick && timeMs > LastChange + 200) {
|
||||
LastChange = timeMs;
|
||||
|
||||
const s32 oldPos = Pos;
|
||||
|
||||
if (DesiredPos >= Pos + LargeStep)
|
||||
setPos(Pos + LargeStep);
|
||||
else if (DesiredPos <= Pos - LargeStep)
|
||||
setPos(Pos - LargeStep);
|
||||
else if (DesiredPos >= Pos - LargeStep && DesiredPos <= Pos + LargeStep)
|
||||
setPos(DesiredPos);
|
||||
|
||||
if (Pos != oldPos && Parent) {
|
||||
SEvent newEvent;
|
||||
newEvent.EventType = EET_GUI_EVENT;
|
||||
newEvent.GUIEvent.Caller = this;
|
||||
newEvent.GUIEvent.Element = 0;
|
||||
newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
|
||||
Parent->OnEvent(newEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIScrollBar::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
|
||||
video::SColor iconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
|
||||
if (iconColor != CurrentIconColor) {
|
||||
refreshControls();
|
||||
}
|
||||
|
||||
SliderRect = AbsoluteRect;
|
||||
|
||||
// draws the background
|
||||
skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), SliderRect, &AbsoluteClippingRect);
|
||||
|
||||
if (core::isnotzero(range())) {
|
||||
// recalculate slider rectangle
|
||||
if (Horizontal) {
|
||||
SliderRect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X + DrawPos + RelativeRect.getHeight() - DrawHeight / 2;
|
||||
SliderRect.LowerRightCorner.X = SliderRect.UpperLeftCorner.X + DrawHeight;
|
||||
} else {
|
||||
SliderRect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y + DrawPos + RelativeRect.getWidth() - DrawHeight / 2;
|
||||
SliderRect.LowerRightCorner.Y = SliderRect.UpperLeftCorner.Y + DrawHeight;
|
||||
}
|
||||
|
||||
skin->draw3DButtonPaneStandard(this, SliderRect, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
// draw buttons
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
void CGUIScrollBar::updateAbsolutePosition()
|
||||
{
|
||||
IGUIElement::updateAbsolutePosition();
|
||||
// todo: properly resize
|
||||
refreshControls();
|
||||
setPos(Pos);
|
||||
}
|
||||
|
||||
//!
|
||||
s32 CGUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
|
||||
{
|
||||
f32 w, p;
|
||||
if (Horizontal) {
|
||||
w = RelativeRect.getWidth() - f32(RelativeRect.getHeight()) * 3.0f;
|
||||
p = pos.X - AbsoluteRect.UpperLeftCorner.X - RelativeRect.getHeight() * 1.5f;
|
||||
} else {
|
||||
w = RelativeRect.getHeight() - f32(RelativeRect.getWidth()) * 3.0f;
|
||||
p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - RelativeRect.getWidth() * 1.5f;
|
||||
}
|
||||
return (s32)(p / w * range()) + Min;
|
||||
}
|
||||
|
||||
//! sets the position of the scrollbar
|
||||
void CGUIScrollBar::setPos(s32 pos)
|
||||
{
|
||||
Pos = core::s32_clamp(pos, Min, Max);
|
||||
|
||||
if (core::isnotzero(range())) {
|
||||
if (Horizontal) {
|
||||
f32 f = (RelativeRect.getWidth() - ((f32)RelativeRect.getHeight() * 3.0f)) / range();
|
||||
DrawPos = (s32)(((Pos - Min) * f) + ((f32)RelativeRect.getHeight() * 0.5f));
|
||||
DrawHeight = RelativeRect.getHeight();
|
||||
} else {
|
||||
f32 f = (RelativeRect.getHeight() - ((f32)RelativeRect.getWidth() * 3.0f)) / range();
|
||||
|
||||
DrawPos = (s32)(((Pos - Min) * f) + ((f32)RelativeRect.getWidth() * 0.5f));
|
||||
DrawHeight = RelativeRect.getWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! gets the small step value
|
||||
s32 CGUIScrollBar::getSmallStep() const
|
||||
{
|
||||
return SmallStep;
|
||||
}
|
||||
|
||||
//! sets the small step value
|
||||
void CGUIScrollBar::setSmallStep(s32 step)
|
||||
{
|
||||
if (step > 0)
|
||||
SmallStep = step;
|
||||
else
|
||||
SmallStep = 10;
|
||||
}
|
||||
|
||||
//! gets the small step value
|
||||
s32 CGUIScrollBar::getLargeStep() const
|
||||
{
|
||||
return LargeStep;
|
||||
}
|
||||
|
||||
//! sets the small step value
|
||||
void CGUIScrollBar::setLargeStep(s32 step)
|
||||
{
|
||||
if (step > 0)
|
||||
LargeStep = step;
|
||||
else
|
||||
LargeStep = 50;
|
||||
}
|
||||
|
||||
//! gets the maximum value of the scrollbar.
|
||||
s32 CGUIScrollBar::getMax() const
|
||||
{
|
||||
return Max;
|
||||
}
|
||||
|
||||
//! sets the maximum value of the scrollbar.
|
||||
void CGUIScrollBar::setMax(s32 max)
|
||||
{
|
||||
Max = max;
|
||||
if (Min > Max)
|
||||
Min = Max;
|
||||
|
||||
bool enable = core::isnotzero(range());
|
||||
UpButton->setEnabled(enable);
|
||||
DownButton->setEnabled(enable);
|
||||
setPos(Pos);
|
||||
}
|
||||
|
||||
//! gets the minimum value of the scrollbar.
|
||||
s32 CGUIScrollBar::getMin() const
|
||||
{
|
||||
return Min;
|
||||
}
|
||||
|
||||
//! sets the minimum value of the scrollbar.
|
||||
void CGUIScrollBar::setMin(s32 min)
|
||||
{
|
||||
Min = min;
|
||||
if (Max < Min)
|
||||
Max = Min;
|
||||
|
||||
bool enable = core::isnotzero(range());
|
||||
UpButton->setEnabled(enable);
|
||||
DownButton->setEnabled(enable);
|
||||
setPos(Pos);
|
||||
}
|
||||
|
||||
//! gets the current position of the scrollbar
|
||||
s32 CGUIScrollBar::getPos() const
|
||||
{
|
||||
return Pos;
|
||||
}
|
||||
|
||||
//! refreshes the position and text on child buttons
|
||||
void CGUIScrollBar::refreshControls()
|
||||
{
|
||||
CurrentIconColor = video::SColor(255, 255, 255, 255);
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
IGUISpriteBank *sprites = 0;
|
||||
|
||||
if (skin) {
|
||||
sprites = skin->getSpriteBank();
|
||||
CurrentIconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
|
||||
}
|
||||
|
||||
if (Horizontal) {
|
||||
const s32 h = RelativeRect.getHeight();
|
||||
const s32 w = (h < RelativeRect.getWidth() / 2) ? h : RelativeRect.getWidth() / 2;
|
||||
if (!UpButton) {
|
||||
UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, 0, w, h), NoClip);
|
||||
UpButton->setSubElement(true);
|
||||
UpButton->setTabStop(false);
|
||||
}
|
||||
if (sprites) {
|
||||
UpButton->setSpriteBank(sprites);
|
||||
UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);
|
||||
UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);
|
||||
}
|
||||
UpButton->setRelativePosition(core::rect<s32>(0, 0, w, h));
|
||||
UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
if (!DownButton) {
|
||||
DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(RelativeRect.getWidth() - w, 0, RelativeRect.getWidth(), h), NoClip);
|
||||
DownButton->setSubElement(true);
|
||||
DownButton->setTabStop(false);
|
||||
}
|
||||
if (sprites) {
|
||||
DownButton->setSpriteBank(sprites);
|
||||
DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);
|
||||
DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);
|
||||
}
|
||||
DownButton->setRelativePosition(core::rect<s32>(RelativeRect.getWidth() - w, 0, RelativeRect.getWidth(), h));
|
||||
DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
} else {
|
||||
const s32 w = RelativeRect.getWidth();
|
||||
const s32 h = (w < RelativeRect.getHeight() / 2) ? w : RelativeRect.getHeight() / 2;
|
||||
if (!UpButton) {
|
||||
UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, 0, w, h), NoClip);
|
||||
UpButton->setSubElement(true);
|
||||
UpButton->setTabStop(false);
|
||||
}
|
||||
if (sprites) {
|
||||
UpButton->setSpriteBank(sprites);
|
||||
UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);
|
||||
UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);
|
||||
}
|
||||
UpButton->setRelativePosition(core::rect<s32>(0, 0, w, h));
|
||||
UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
if (!DownButton) {
|
||||
DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, RelativeRect.getHeight() - h, w, RelativeRect.getHeight()), NoClip);
|
||||
DownButton->setSubElement(true);
|
||||
DownButton->setTabStop(false);
|
||||
}
|
||||
if (sprites) {
|
||||
DownButton->setSpriteBank(sprites);
|
||||
DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);
|
||||
DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);
|
||||
}
|
||||
DownButton->setRelativePosition(core::rect<s32>(0, RelativeRect.getHeight() - h, w, RelativeRect.getHeight()));
|
||||
DownButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
95
src/CGUIScrollBar.h
Normal file
95
src/CGUIScrollBar.h
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIScrollBar.h"
|
||||
#include "IGUIButton.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUIScrollBar : public IGUIScrollBar
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIScrollBar(bool horizontal, IGUIEnvironment *environment,
|
||||
IGUIElement *parent, s32 id, core::rect<s32> rectangle,
|
||||
bool noclip = false);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIScrollBar();
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
void OnPostRender(u32 timeMs) override;
|
||||
|
||||
//! gets the maximum value of the scrollbar.
|
||||
s32 getMax() const override;
|
||||
|
||||
//! sets the maximum value of the scrollbar.
|
||||
void setMax(s32 max) override;
|
||||
|
||||
//! gets the minimum value of the scrollbar.
|
||||
s32 getMin() const override;
|
||||
|
||||
//! sets the minimum value of the scrollbar.
|
||||
void setMin(s32 min) override;
|
||||
|
||||
//! gets the small step value
|
||||
s32 getSmallStep() const override;
|
||||
|
||||
//! sets the small step value
|
||||
void setSmallStep(s32 step) override;
|
||||
|
||||
//! gets the large step value
|
||||
s32 getLargeStep() const override;
|
||||
|
||||
//! sets the large step value
|
||||
void setLargeStep(s32 step) override;
|
||||
|
||||
//! gets the current position of the scrollbar
|
||||
s32 getPos() const override;
|
||||
|
||||
//! sets the position of the scrollbar
|
||||
void setPos(s32 pos) override;
|
||||
|
||||
//! updates the rectangle
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
private:
|
||||
void refreshControls();
|
||||
s32 getPosFromMousePos(const core::position2di &p) const;
|
||||
|
||||
IGUIButton *UpButton;
|
||||
IGUIButton *DownButton;
|
||||
|
||||
core::rect<s32> SliderRect;
|
||||
|
||||
bool Dragging;
|
||||
bool Horizontal;
|
||||
bool DraggedBySlider;
|
||||
bool TrayClick;
|
||||
s32 Pos;
|
||||
s32 DrawPos;
|
||||
s32 DrawHeight;
|
||||
s32 Min;
|
||||
s32 Max;
|
||||
s32 SmallStep;
|
||||
s32 LargeStep;
|
||||
s32 DesiredPos;
|
||||
u32 LastChange;
|
||||
video::SColor CurrentIconColor;
|
||||
|
||||
f32 range() const { return (f32)(Max - Min); }
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
891
src/CGUISkin.cpp
Normal file
891
src/CGUISkin.cpp
Normal file
@@ -0,0 +1,891 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUISkin.h"
|
||||
|
||||
#include "IGUIFont.h"
|
||||
#include "IGUISpriteBank.h"
|
||||
#include "IGUIElement.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IAttributes.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver) :
|
||||
SpriteBank(0), Driver(driver), Type(type)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUISkin");
|
||||
#endif
|
||||
|
||||
if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) {
|
||||
Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101, 50, 50, 50);
|
||||
Colors[EGDC_3D_SHADOW] = video::SColor(101, 130, 130, 130);
|
||||
Colors[EGDC_3D_FACE] = video::SColor(101, 210, 210, 210);
|
||||
Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101, 255, 255, 255);
|
||||
Colors[EGDC_3D_LIGHT] = video::SColor(101, 210, 210, 210);
|
||||
Colors[EGDC_ACTIVE_BORDER] = video::SColor(101, 16, 14, 115);
|
||||
Colors[EGDC_ACTIVE_CAPTION] = video::SColor(255, 255, 255, 255);
|
||||
Colors[EGDC_APP_WORKSPACE] = video::SColor(101, 100, 100, 100);
|
||||
Colors[EGDC_BUTTON_TEXT] = video::SColor(240, 10, 10, 10);
|
||||
Colors[EGDC_GRAY_TEXT] = video::SColor(240, 130, 130, 130);
|
||||
Colors[EGDC_HIGH_LIGHT] = video::SColor(101, 8, 36, 107);
|
||||
Colors[EGDC_HIGH_LIGHT_TEXT] = video::SColor(240, 255, 255, 255);
|
||||
Colors[EGDC_INACTIVE_BORDER] = video::SColor(101, 165, 165, 165);
|
||||
Colors[EGDC_INACTIVE_CAPTION] = video::SColor(255, 30, 30, 30);
|
||||
Colors[EGDC_TOOLTIP] = video::SColor(200, 0, 0, 0);
|
||||
Colors[EGDC_TOOLTIP_BACKGROUND] = video::SColor(200, 255, 255, 225);
|
||||
Colors[EGDC_SCROLLBAR] = video::SColor(101, 230, 230, 230);
|
||||
Colors[EGDC_WINDOW] = video::SColor(101, 255, 255, 255);
|
||||
Colors[EGDC_WINDOW_SYMBOL] = video::SColor(200, 10, 10, 10);
|
||||
Colors[EGDC_ICON] = video::SColor(200, 255, 255, 255);
|
||||
Colors[EGDC_ICON_HIGH_LIGHT] = video::SColor(200, 8, 36, 107);
|
||||
Colors[EGDC_GRAY_WINDOW_SYMBOL] = video::SColor(240, 100, 100, 100);
|
||||
Colors[EGDC_EDITABLE] = video::SColor(255, 255, 255, 255);
|
||||
Colors[EGDC_GRAY_EDITABLE] = video::SColor(255, 120, 120, 120);
|
||||
Colors[EGDC_FOCUSED_EDITABLE] = video::SColor(255, 240, 240, 255);
|
||||
|
||||
Sizes[EGDS_SCROLLBAR_SIZE] = 14;
|
||||
Sizes[EGDS_MENU_HEIGHT] = 30;
|
||||
Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15;
|
||||
Sizes[EGDS_CHECK_BOX_WIDTH] = 18;
|
||||
Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500;
|
||||
Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200;
|
||||
Sizes[EGDS_BUTTON_WIDTH] = 80;
|
||||
Sizes[EGDS_BUTTON_HEIGHT] = 30;
|
||||
|
||||
Sizes[EGDS_TEXT_DISTANCE_X] = 2;
|
||||
Sizes[EGDS_TEXT_DISTANCE_Y] = 0;
|
||||
|
||||
Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 2;
|
||||
Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 0;
|
||||
} else {
|
||||
// 0x80a6a8af
|
||||
Colors[EGDC_3D_DARK_SHADOW] = 0x60767982;
|
||||
// Colors[EGDC_3D_FACE] = 0xc0c9ccd4; // tab background
|
||||
Colors[EGDC_3D_FACE] = 0xc0cbd2d9; // tab background
|
||||
Colors[EGDC_3D_SHADOW] = 0x50e4e8f1; // tab background, and left-top highlight
|
||||
Colors[EGDC_3D_HIGH_LIGHT] = 0x40c7ccdc;
|
||||
Colors[EGDC_3D_LIGHT] = 0x802e313a;
|
||||
Colors[EGDC_ACTIVE_BORDER] = 0x80404040; // window title
|
||||
Colors[EGDC_ACTIVE_CAPTION] = 0xffd0d0d0;
|
||||
Colors[EGDC_APP_WORKSPACE] = 0xc0646464; // unused
|
||||
Colors[EGDC_BUTTON_TEXT] = 0xd0161616;
|
||||
Colors[EGDC_GRAY_TEXT] = 0x3c141414;
|
||||
Colors[EGDC_HIGH_LIGHT] = 0x6c606060;
|
||||
Colors[EGDC_HIGH_LIGHT_TEXT] = 0xd0e0e0e0;
|
||||
Colors[EGDC_INACTIVE_BORDER] = 0xf0a5a5a5;
|
||||
Colors[EGDC_INACTIVE_CAPTION] = 0xffd2d2d2;
|
||||
Colors[EGDC_TOOLTIP] = 0xf00f2033;
|
||||
Colors[EGDC_TOOLTIP_BACKGROUND] = 0xc0cbd2d9;
|
||||
Colors[EGDC_SCROLLBAR] = 0xf0e0e0e0;
|
||||
Colors[EGDC_WINDOW] = 0xf0f0f0f0;
|
||||
Colors[EGDC_WINDOW_SYMBOL] = 0xd0161616;
|
||||
Colors[EGDC_ICON] = 0xd0161616;
|
||||
Colors[EGDC_ICON_HIGH_LIGHT] = 0xd0606060;
|
||||
Colors[EGDC_GRAY_WINDOW_SYMBOL] = 0x3c101010;
|
||||
Colors[EGDC_EDITABLE] = 0xf0ffffff;
|
||||
Colors[EGDC_GRAY_EDITABLE] = 0xf0cccccc;
|
||||
Colors[EGDC_FOCUSED_EDITABLE] = 0xf0fffff0;
|
||||
|
||||
Sizes[EGDS_SCROLLBAR_SIZE] = 14;
|
||||
Sizes[EGDS_MENU_HEIGHT] = 48;
|
||||
Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15;
|
||||
Sizes[EGDS_CHECK_BOX_WIDTH] = 18;
|
||||
Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500;
|
||||
Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200;
|
||||
Sizes[EGDS_BUTTON_WIDTH] = 80;
|
||||
Sizes[EGDS_BUTTON_HEIGHT] = 30;
|
||||
|
||||
Sizes[EGDS_TEXT_DISTANCE_X] = 3;
|
||||
Sizes[EGDS_TEXT_DISTANCE_Y] = 2;
|
||||
|
||||
Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 3;
|
||||
Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 2;
|
||||
}
|
||||
|
||||
Sizes[EGDS_MESSAGE_BOX_GAP_SPACE] = 15;
|
||||
Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH] = 0;
|
||||
Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_WIDTH] = 500;
|
||||
Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT] = 0;
|
||||
Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT] = 99999;
|
||||
|
||||
Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X] = 1;
|
||||
Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y] = 1;
|
||||
Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0;
|
||||
Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2;
|
||||
Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X] = 0;
|
||||
Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y] = 0;
|
||||
|
||||
Texts[EGDT_MSG_BOX_OK] = L"OK";
|
||||
Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel";
|
||||
Texts[EGDT_MSG_BOX_YES] = L"Yes";
|
||||
Texts[EGDT_MSG_BOX_NO] = L"No";
|
||||
Texts[EGDT_WINDOW_CLOSE] = L"Close";
|
||||
Texts[EGDT_WINDOW_RESTORE] = L"Restore";
|
||||
Texts[EGDT_WINDOW_MINIMIZE] = L"Minimize";
|
||||
Texts[EGDT_WINDOW_MAXIMIZE] = L"Maximize";
|
||||
|
||||
Icons[EGDI_WINDOW_MAXIMIZE] = 225;
|
||||
Icons[EGDI_WINDOW_RESTORE] = 226;
|
||||
Icons[EGDI_WINDOW_CLOSE] = 227;
|
||||
Icons[EGDI_WINDOW_MINIMIZE] = 228;
|
||||
Icons[EGDI_CURSOR_UP] = 229;
|
||||
Icons[EGDI_CURSOR_DOWN] = 230;
|
||||
Icons[EGDI_CURSOR_LEFT] = 231;
|
||||
Icons[EGDI_CURSOR_RIGHT] = 232;
|
||||
Icons[EGDI_MENU_MORE] = 232;
|
||||
Icons[EGDI_CHECK_BOX_CHECKED] = 233;
|
||||
Icons[EGDI_DROP_DOWN] = 234;
|
||||
Icons[EGDI_SMALL_CURSOR_UP] = 235;
|
||||
Icons[EGDI_SMALL_CURSOR_DOWN] = 236;
|
||||
Icons[EGDI_RADIO_BUTTON_CHECKED] = 237;
|
||||
Icons[EGDI_MORE_LEFT] = 238;
|
||||
Icons[EGDI_MORE_RIGHT] = 239;
|
||||
Icons[EGDI_MORE_UP] = 240;
|
||||
Icons[EGDI_MORE_DOWN] = 241;
|
||||
Icons[EGDI_WINDOW_RESIZE] = 242;
|
||||
Icons[EGDI_EXPAND] = 243;
|
||||
Icons[EGDI_COLLAPSE] = 244;
|
||||
|
||||
Icons[EGDI_FILE] = 245;
|
||||
Icons[EGDI_DIRECTORY] = 246;
|
||||
|
||||
for (u32 i = 0; i < EGDF_COUNT; ++i)
|
||||
Fonts[i] = 0;
|
||||
|
||||
UseGradient = (Type == EGST_WINDOWS_METALLIC) || (Type == EGST_BURNING_SKIN);
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUISkin::~CGUISkin()
|
||||
{
|
||||
for (u32 i = 0; i < EGDF_COUNT; ++i) {
|
||||
if (Fonts[i])
|
||||
Fonts[i]->drop();
|
||||
}
|
||||
|
||||
if (SpriteBank)
|
||||
SpriteBank->drop();
|
||||
}
|
||||
|
||||
//! returns default color
|
||||
video::SColor CGUISkin::getColor(EGUI_DEFAULT_COLOR color) const
|
||||
{
|
||||
if ((u32)color < EGDC_COUNT)
|
||||
return Colors[color];
|
||||
else
|
||||
return video::SColor();
|
||||
}
|
||||
|
||||
//! sets a default color
|
||||
void CGUISkin::setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor)
|
||||
{
|
||||
if ((u32)which < EGDC_COUNT)
|
||||
Colors[which] = newColor;
|
||||
}
|
||||
|
||||
//! returns size for the given size type
|
||||
s32 CGUISkin::getSize(EGUI_DEFAULT_SIZE size) const
|
||||
{
|
||||
if ((u32)size < EGDS_COUNT)
|
||||
return Sizes[size];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! sets a default size
|
||||
void CGUISkin::setSize(EGUI_DEFAULT_SIZE which, s32 size)
|
||||
{
|
||||
if ((u32)which < EGDS_COUNT)
|
||||
Sizes[which] = size;
|
||||
}
|
||||
|
||||
//! returns the default font
|
||||
IGUIFont *CGUISkin::getFont(EGUI_DEFAULT_FONT which) const
|
||||
{
|
||||
if (((u32)which < EGDF_COUNT) && Fonts[which])
|
||||
return Fonts[which];
|
||||
else
|
||||
return Fonts[EGDF_DEFAULT];
|
||||
}
|
||||
|
||||
//! sets a default font
|
||||
void CGUISkin::setFont(IGUIFont *font, EGUI_DEFAULT_FONT which)
|
||||
{
|
||||
if ((u32)which >= EGDF_COUNT)
|
||||
return;
|
||||
|
||||
if (font) {
|
||||
font->grab();
|
||||
if (Fonts[which])
|
||||
Fonts[which]->drop();
|
||||
|
||||
Fonts[which] = font;
|
||||
}
|
||||
}
|
||||
|
||||
//! gets the sprite bank stored
|
||||
IGUISpriteBank *CGUISkin::getSpriteBank() const
|
||||
{
|
||||
return SpriteBank;
|
||||
}
|
||||
|
||||
//! set a new sprite bank or remove one by passing 0
|
||||
void CGUISkin::setSpriteBank(IGUISpriteBank *bank)
|
||||
{
|
||||
if (bank)
|
||||
bank->grab();
|
||||
|
||||
if (SpriteBank)
|
||||
SpriteBank->drop();
|
||||
|
||||
SpriteBank = bank;
|
||||
}
|
||||
|
||||
//! Returns a default icon
|
||||
u32 CGUISkin::getIcon(EGUI_DEFAULT_ICON icon) const
|
||||
{
|
||||
if ((u32)icon < EGDI_COUNT)
|
||||
return Icons[icon];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Sets a default icon
|
||||
void CGUISkin::setIcon(EGUI_DEFAULT_ICON icon, u32 index)
|
||||
{
|
||||
if ((u32)icon < EGDI_COUNT)
|
||||
Icons[icon] = index;
|
||||
}
|
||||
|
||||
//! Returns a default text. For example for Message box button captions:
|
||||
//! "OK", "Cancel", "Yes", "No" and so on.
|
||||
const wchar_t *CGUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const
|
||||
{
|
||||
if ((u32)text < EGDT_COUNT)
|
||||
return Texts[text].c_str();
|
||||
else
|
||||
return Texts[0].c_str();
|
||||
}
|
||||
|
||||
//! Sets a default text. For example for Message box button captions:
|
||||
//! "OK", "Cancel", "Yes", "No" and so on.
|
||||
void CGUISkin::setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t *newText)
|
||||
{
|
||||
if ((u32)which < EGDT_COUNT)
|
||||
Texts[which] = newText;
|
||||
}
|
||||
|
||||
//! draws a standard 3d button pane
|
||||
/** Used for drawing for example buttons in normal state.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly. */
|
||||
void CGUISkin::draw3DButtonPaneStandard(IGUIElement *element,
|
||||
const core::rect<s32> &r,
|
||||
const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
|
||||
if (Type == EGST_BURNING_SKIN) {
|
||||
rect.UpperLeftCorner.X -= 1;
|
||||
rect.UpperLeftCorner.Y -= 1;
|
||||
rect.LowerRightCorner.X += 1;
|
||||
rect.LowerRightCorner.Y += 1;
|
||||
draw3DSunkenPane(element,
|
||||
getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.9f), false, true, rect, clip);
|
||||
return;
|
||||
}
|
||||
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
|
||||
if (!UseGradient) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip);
|
||||
} else {
|
||||
const video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
const video::SColor c2 = c1.getInterpolated(getColor(EGDC_3D_DARK_SHADOW), 0.4f);
|
||||
Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a pressed 3d button pane
|
||||
/** Used for drawing for example buttons in pressed state.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly. */
|
||||
void CGUISkin::draw3DButtonPanePressed(IGUIElement *element,
|
||||
const core::rect<s32> &r,
|
||||
const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
|
||||
if (!UseGradient) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip);
|
||||
} else {
|
||||
const video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
const video::SColor c2 = c1.getInterpolated(getColor(EGDC_3D_DARK_SHADOW), 0.4f);
|
||||
Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a sunken 3d pane
|
||||
/** Used for drawing the background of edit, combo or check boxes.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param bgcolor: Background color.
|
||||
\param flat: Specifies if the sunken pane should be flat or displayed as sunken
|
||||
deep into the ground.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::draw3DSunkenPane(IGUIElement *element, video::SColor bgcolor,
|
||||
bool flat, bool fillBackGround,
|
||||
const core::rect<s32> &r,
|
||||
const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
|
||||
if (fillBackGround)
|
||||
Driver->draw2DRectangle(bgcolor, rect, clip);
|
||||
|
||||
if (flat) {
|
||||
// draw flat sunken pane
|
||||
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // top
|
||||
|
||||
++rect.UpperLeftCorner.Y;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // left
|
||||
|
||||
rect = r;
|
||||
++rect.UpperLeftCorner.Y;
|
||||
rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // right
|
||||
|
||||
rect = r;
|
||||
++rect.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1;
|
||||
--rect.LowerRightCorner.X;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // bottom
|
||||
} else {
|
||||
// draw deep sunken pane
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // top
|
||||
++rect.UpperLeftCorner.X;
|
||||
++rect.UpperLeftCorner.Y;
|
||||
--rect.LowerRightCorner.X;
|
||||
++rect.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X = r.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y + 1;
|
||||
rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // left
|
||||
++rect.UpperLeftCorner.X;
|
||||
++rect.UpperLeftCorner.Y;
|
||||
++rect.LowerRightCorner.X;
|
||||
--rect.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect = r;
|
||||
rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1;
|
||||
++rect.UpperLeftCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // right
|
||||
--rect.UpperLeftCorner.X;
|
||||
++rect.UpperLeftCorner.Y;
|
||||
--rect.LowerRightCorner.X;
|
||||
--rect.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_LIGHT), rect, clip);
|
||||
|
||||
rect = r;
|
||||
++rect.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1;
|
||||
--rect.LowerRightCorner.X;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // bottom
|
||||
++rect.UpperLeftCorner.X;
|
||||
--rect.UpperLeftCorner.Y;
|
||||
--rect.LowerRightCorner.X;
|
||||
--rect.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_LIGHT), rect, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a window background
|
||||
// return where to draw title bar text.
|
||||
core::rect<s32> CGUISkin::draw3DWindowBackground(IGUIElement *element,
|
||||
bool drawTitleBar, video::SColor titleBarColor,
|
||||
const core::rect<s32> &r,
|
||||
const core::rect<s32> *clip,
|
||||
core::rect<s32> *checkClientArea)
|
||||
{
|
||||
if (!Driver) {
|
||||
if (checkClientArea) {
|
||||
*checkClientArea = r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
|
||||
// top border
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
}
|
||||
|
||||
// left border
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
}
|
||||
|
||||
// right border dark outer line
|
||||
rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1;
|
||||
rect.LowerRightCorner.X = r.LowerRightCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
}
|
||||
|
||||
// right border bright innner line
|
||||
rect.UpperLeftCorner.X -= 1;
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
}
|
||||
|
||||
// bottom border dark outer line
|
||||
rect.UpperLeftCorner.X = r.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = r.LowerRightCorner.X;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
}
|
||||
|
||||
// bottom border bright inner line
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.UpperLeftCorner.Y -= 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
if (!checkClientArea) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
}
|
||||
|
||||
// client area for background
|
||||
rect = r;
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
rect.LowerRightCorner.X -= 2;
|
||||
rect.LowerRightCorner.Y -= 2;
|
||||
if (checkClientArea) {
|
||||
*checkClientArea = rect;
|
||||
}
|
||||
|
||||
if (!checkClientArea) {
|
||||
if (!UseGradient) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip);
|
||||
} else if (Type == EGST_BURNING_SKIN) {
|
||||
const video::SColor c1 = getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.9f);
|
||||
const video::SColor c2 = getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.8f);
|
||||
|
||||
Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip);
|
||||
} else {
|
||||
const video::SColor c2 = getColor(EGDC_3D_SHADOW);
|
||||
const video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
Driver->draw2DRectangle(rect, c1, c1, c1, c2, clip);
|
||||
}
|
||||
}
|
||||
|
||||
// title bar
|
||||
rect = r;
|
||||
rect.UpperLeftCorner.X += 2;
|
||||
rect.UpperLeftCorner.Y += 2;
|
||||
rect.LowerRightCorner.X -= 2;
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + getSize(EGDS_WINDOW_BUTTON_WIDTH) + 2;
|
||||
|
||||
if (drawTitleBar) {
|
||||
if (checkClientArea) {
|
||||
(*checkClientArea).UpperLeftCorner.Y = rect.LowerRightCorner.Y;
|
||||
} else {
|
||||
// draw title bar
|
||||
// if (!UseGradient)
|
||||
// Driver->draw2DRectangle(titleBarColor, rect, clip);
|
||||
// else
|
||||
if (Type == EGST_BURNING_SKIN) {
|
||||
const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(), 255, 255, 255), 0.8f);
|
||||
Driver->draw2DRectangle(rect, titleBarColor, titleBarColor, c, c, clip);
|
||||
} else {
|
||||
const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(), 0, 0, 0), 0.2f);
|
||||
Driver->draw2DRectangle(rect, titleBarColor, c, titleBarColor, c, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
//! draws a standard 3d menu pane
|
||||
/** Used for drawing for menus and context menus.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::draw3DMenuPane(IGUIElement *element,
|
||||
const core::rect<s32> &r, const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
|
||||
if (Type == EGST_BURNING_SKIN) {
|
||||
rect.UpperLeftCorner.Y -= 3;
|
||||
draw3DButtonPaneStandard(element, rect, clip);
|
||||
return;
|
||||
}
|
||||
|
||||
// in this skin, this is exactly what non pressed buttons look like,
|
||||
// so we could simply call
|
||||
// draw3DButtonPaneStandard(element, rect, clip);
|
||||
// here.
|
||||
// but if the skin is transparent, this doesn't look that nice. So
|
||||
// We draw it a little bit better, with some more draw2DRectangle calls,
|
||||
// but there aren't that much menus visible anyway.
|
||||
|
||||
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1;
|
||||
rect.LowerRightCorner.X = r.LowerRightCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X -= 1;
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X = r.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = r.LowerRightCorner.X;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip);
|
||||
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.LowerRightCorner.X -= 1;
|
||||
rect.UpperLeftCorner.Y -= 1;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
|
||||
rect = r;
|
||||
rect.UpperLeftCorner.X += 1;
|
||||
rect.UpperLeftCorner.Y += 1;
|
||||
rect.LowerRightCorner.X -= 2;
|
||||
rect.LowerRightCorner.Y -= 2;
|
||||
|
||||
if (!UseGradient)
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip);
|
||||
else {
|
||||
const video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
const video::SColor c2 = getColor(EGDC_3D_SHADOW);
|
||||
Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a standard 3d tool bar
|
||||
/** Used for drawing for toolbars and menus.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::draw3DToolBar(IGUIElement *element,
|
||||
const core::rect<s32> &r,
|
||||
const core::rect<s32> *clip)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> rect = r;
|
||||
|
||||
rect.UpperLeftCorner.X = r.UpperLeftCorner.X;
|
||||
rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1;
|
||||
rect.LowerRightCorner.Y = r.LowerRightCorner.Y;
|
||||
rect.LowerRightCorner.X = r.LowerRightCorner.X;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip);
|
||||
|
||||
rect = r;
|
||||
rect.LowerRightCorner.Y -= 1;
|
||||
|
||||
if (!UseGradient) {
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip);
|
||||
} else if (Type == EGST_BURNING_SKIN) {
|
||||
const video::SColor c1 = 0xF0000000 | getColor(EGDC_3D_FACE).color;
|
||||
const video::SColor c2 = 0xF0000000 | getColor(EGDC_3D_SHADOW).color;
|
||||
|
||||
rect.LowerRightCorner.Y += 1;
|
||||
Driver->draw2DRectangle(rect, c1, c2, c1, c2, clip);
|
||||
} else {
|
||||
const video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
const video::SColor c2 = getColor(EGDC_3D_SHADOW);
|
||||
Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a tab button
|
||||
/** Used for drawing for tab buttons on top of tabs.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param active: Specifies if the tab is currently active.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::draw3DTabButton(IGUIElement *element, bool active,
|
||||
const core::rect<s32> &frameRect, const core::rect<s32> *clip, EGUI_ALIGNMENT alignment)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> tr = frameRect;
|
||||
|
||||
if (alignment == EGUIA_UPPERLEFT) {
|
||||
tr.LowerRightCorner.X -= 2;
|
||||
tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw left highlight
|
||||
tr = frameRect;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
tr.UpperLeftCorner.Y += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw grey background
|
||||
tr = frameRect;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.UpperLeftCorner.Y += 1;
|
||||
tr.LowerRightCorner.X -= 2;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip);
|
||||
|
||||
// draw right middle gray shadow
|
||||
tr.LowerRightCorner.X += 1;
|
||||
tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip);
|
||||
|
||||
tr.LowerRightCorner.X += 1;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.UpperLeftCorner.Y += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), tr, clip);
|
||||
} else {
|
||||
tr.LowerRightCorner.X -= 2;
|
||||
tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw left highlight
|
||||
tr = frameRect;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
tr.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw grey background
|
||||
tr = frameRect;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.UpperLeftCorner.Y -= 1;
|
||||
tr.LowerRightCorner.X -= 2;
|
||||
tr.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip);
|
||||
|
||||
// draw right middle gray shadow
|
||||
tr.LowerRightCorner.X += 1;
|
||||
tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1;
|
||||
// tr.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip);
|
||||
|
||||
tr.LowerRightCorner.X += 1;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.LowerRightCorner.Y -= 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), tr, clip);
|
||||
}
|
||||
}
|
||||
|
||||
//! draws a tab control body
|
||||
/** \param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param border: Specifies if the border should be drawn.
|
||||
\param background: Specifies if the background should be drawn.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::draw3DTabBody(IGUIElement *element, bool border, bool background,
|
||||
const core::rect<s32> &rect, const core::rect<s32> *clip, s32 tabHeight, EGUI_ALIGNMENT alignment)
|
||||
{
|
||||
if (!Driver)
|
||||
return;
|
||||
|
||||
core::rect<s32> tr = rect;
|
||||
|
||||
if (tabHeight == -1)
|
||||
tabHeight = getSize(gui::EGDS_BUTTON_HEIGHT);
|
||||
|
||||
// draw border.
|
||||
if (border) {
|
||||
if (alignment == EGUIA_UPPERLEFT) {
|
||||
// draw left hightlight
|
||||
tr.UpperLeftCorner.Y += tabHeight + 2;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw right shadow
|
||||
tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip);
|
||||
|
||||
// draw lower shadow
|
||||
tr = rect;
|
||||
tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip);
|
||||
} else {
|
||||
// draw left hightlight
|
||||
tr.LowerRightCorner.Y -= tabHeight + 2;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
|
||||
// draw right shadow
|
||||
tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1;
|
||||
tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip);
|
||||
|
||||
// draw lower shadow
|
||||
tr = rect;
|
||||
tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1;
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip);
|
||||
}
|
||||
}
|
||||
|
||||
if (background) {
|
||||
if (alignment == EGUIA_UPPERLEFT) {
|
||||
tr = rect;
|
||||
tr.UpperLeftCorner.Y += tabHeight + 2;
|
||||
tr.LowerRightCorner.X -= 1;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.LowerRightCorner.Y -= 1;
|
||||
} else {
|
||||
tr = rect;
|
||||
tr.UpperLeftCorner.X += 1;
|
||||
tr.UpperLeftCorner.Y -= 1;
|
||||
tr.LowerRightCorner.X -= 1;
|
||||
tr.LowerRightCorner.Y -= tabHeight + 2;
|
||||
// tr.UpperLeftCorner.X += 1;
|
||||
}
|
||||
|
||||
if (!UseGradient)
|
||||
Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip);
|
||||
else {
|
||||
video::SColor c1 = getColor(EGDC_3D_FACE);
|
||||
video::SColor c2 = getColor(EGDC_3D_SHADOW);
|
||||
Driver->draw2DRectangle(tr, c1, c1, c2, c2, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! draws an icon, usually from the skin's sprite bank
|
||||
/** \param parent: Pointer to the element which wishes to draw this icon.
|
||||
This parameter is usually not used by IGUISkin, but can be used for example
|
||||
by more complex implementations to find out how to draw the part exactly.
|
||||
\param icon: Specifies the icon to be drawn.
|
||||
\param position: The position to draw the icon
|
||||
\param starttime: The time at the start of the animation
|
||||
\param currenttime: The present time, used to calculate the frame number
|
||||
\param loop: Whether the animation should loop or not
|
||||
\param clip: Clip area. */
|
||||
void CGUISkin::drawIcon(IGUIElement *element, EGUI_DEFAULT_ICON icon,
|
||||
const core::position2di position,
|
||||
u32 starttime, u32 currenttime,
|
||||
bool loop, const core::rect<s32> *clip)
|
||||
{
|
||||
if (!SpriteBank)
|
||||
return;
|
||||
|
||||
bool gray = element && !element->isEnabled();
|
||||
SpriteBank->draw2DSprite(Icons[icon], position, clip,
|
||||
Colors[gray ? EGDC_GRAY_WINDOW_SYMBOL : EGDC_WINDOW_SYMBOL], starttime, currenttime, loop, true);
|
||||
}
|
||||
|
||||
EGUI_SKIN_TYPE CGUISkin::getType() const
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
|
||||
//! draws a 2d rectangle.
|
||||
void CGUISkin::draw2DRectangle(IGUIElement *element,
|
||||
const video::SColor &color, const core::rect<s32> &pos,
|
||||
const core::rect<s32> *clip)
|
||||
{
|
||||
Driver->draw2DRectangle(color, pos, clip);
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
226
src/CGUISkin.h
Normal file
226
src/CGUISkin.h
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
class IVideoDriver;
|
||||
}
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CGUISkin : public IGUISkin
|
||||
{
|
||||
public:
|
||||
CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUISkin();
|
||||
|
||||
//! returns default color
|
||||
video::SColor getColor(EGUI_DEFAULT_COLOR color) const override;
|
||||
|
||||
//! sets a default color
|
||||
void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) override;
|
||||
|
||||
//! returns size for the given size type
|
||||
s32 getSize(EGUI_DEFAULT_SIZE size) const override;
|
||||
|
||||
//! sets a default size
|
||||
void setSize(EGUI_DEFAULT_SIZE which, s32 size) override;
|
||||
|
||||
//! returns the default font
|
||||
IGUIFont *getFont(EGUI_DEFAULT_FONT which = EGDF_DEFAULT) const override;
|
||||
|
||||
//! sets a default font
|
||||
void setFont(IGUIFont *font, EGUI_DEFAULT_FONT which = EGDF_DEFAULT) override;
|
||||
|
||||
//! sets the sprite bank used for drawing icons
|
||||
void setSpriteBank(IGUISpriteBank *bank) override;
|
||||
|
||||
//! gets the sprite bank used for drawing icons
|
||||
IGUISpriteBank *getSpriteBank() const override;
|
||||
|
||||
//! Returns a default icon
|
||||
/** Returns the sprite index within the sprite bank */
|
||||
u32 getIcon(EGUI_DEFAULT_ICON icon) const override;
|
||||
|
||||
//! Sets a default icon
|
||||
/** Sets the sprite index used for drawing icons like arrows,
|
||||
close buttons and ticks in checkboxes
|
||||
\param icon: Enum specifying which icon to change
|
||||
\param index: The sprite index used to draw this icon */
|
||||
void setIcon(EGUI_DEFAULT_ICON icon, u32 index) override;
|
||||
|
||||
//! Returns a default text.
|
||||
/** For example for Message box button captions:
|
||||
"OK", "Cancel", "Yes", "No" and so on. */
|
||||
const wchar_t *getDefaultText(EGUI_DEFAULT_TEXT text) const override;
|
||||
|
||||
//! Sets a default text.
|
||||
/** For example for Message box button captions:
|
||||
"OK", "Cancel", "Yes", "No" and so on. */
|
||||
void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t *newText) override;
|
||||
|
||||
//! draws a standard 3d button pane
|
||||
/** Used for drawing for example buttons in normal state.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly. */
|
||||
virtual void draw3DButtonPaneStandard(IGUIElement *element,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a pressed 3d button pane
|
||||
/** Used for drawing for example buttons in pressed state.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly. */
|
||||
virtual void draw3DButtonPanePressed(IGUIElement *element,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a sunken 3d pane
|
||||
/** Used for drawing the background of edit, combo or check boxes.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param bgcolor: Background color.
|
||||
\param flat: Specifies if the sunken pane should be flat or displayed as sunken
|
||||
deep into the ground.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
virtual void draw3DSunkenPane(IGUIElement *element,
|
||||
video::SColor bgcolor, bool flat,
|
||||
bool fillBackGround,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a window background
|
||||
/** Used for drawing the background of dialogs and windows.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param titleBarColor: Title color.
|
||||
\param drawTitleBar: True to enable title drawing.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area.
|
||||
\param checkClientArea: When set to non-null the function will not draw anything,
|
||||
but will instead return the clientArea which can be used for drawing by the calling window.
|
||||
That is the area without borders and without titlebar.
|
||||
\return Returns rect where it would be good to draw title bar text. This will
|
||||
work even when checkClientArea is set to a non-null value.*/
|
||||
virtual core::rect<s32> draw3DWindowBackground(IGUIElement *element,
|
||||
bool drawTitleBar, video::SColor titleBarColor,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip,
|
||||
core::rect<s32> *checkClientArea) override;
|
||||
|
||||
//! draws a standard 3d menu pane
|
||||
/** Used for drawing for menus and context menus.
|
||||
It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
|
||||
EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
virtual void draw3DMenuPane(IGUIElement *element,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a standard 3d tool bar
|
||||
/** Used for drawing for toolbars and menus.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
virtual void draw3DToolBar(IGUIElement *element,
|
||||
const core::rect<s32> &rect,
|
||||
const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a tab button
|
||||
/** Used for drawing for tab buttons on top of tabs.
|
||||
\param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param active: Specifies if the tab is currently active.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
virtual void draw3DTabButton(IGUIElement *element, bool active,
|
||||
const core::rect<s32> &rect, const core::rect<s32> *clip = 0,
|
||||
EGUI_ALIGNMENT alignment = EGUIA_UPPERLEFT) override;
|
||||
|
||||
//! draws a tab control body
|
||||
/** \param element: Pointer to the element which wishes to draw this. This parameter
|
||||
is usually not used by ISkin, but can be used for example by more complex
|
||||
implementations to find out how to draw the part exactly.
|
||||
\param border: Specifies if the border should be drawn.
|
||||
\param background: Specifies if the background should be drawn.
|
||||
\param rect: Defining area where to draw.
|
||||
\param clip: Clip area. */
|
||||
virtual void draw3DTabBody(IGUIElement *element, bool border, bool background,
|
||||
const core::rect<s32> &rect, const core::rect<s32> *clip = 0, s32 tabHeight = -1,
|
||||
EGUI_ALIGNMENT alignment = EGUIA_UPPERLEFT) override;
|
||||
|
||||
//! draws an icon, usually from the skin's sprite bank
|
||||
/** \param element: Pointer to the element which wishes to draw this icon.
|
||||
This parameter is usually not used by IGUISkin, but can be used for example
|
||||
by more complex implementations to find out how to draw the part exactly.
|
||||
\param icon: Specifies the icon to be drawn.
|
||||
\param position: The position to draw the icon
|
||||
\param starttime: The time at the start of the animation
|
||||
\param currenttime: The present time, used to calculate the frame number
|
||||
\param loop: Whether the animation should loop or not
|
||||
\param clip: Clip area. */
|
||||
virtual void drawIcon(IGUIElement *element, EGUI_DEFAULT_ICON icon,
|
||||
const core::position2di position,
|
||||
u32 starttime = 0, u32 currenttime = 0,
|
||||
bool loop = false, const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! draws a 2d rectangle.
|
||||
/** \param element: Pointer to the element which wishes to draw this icon.
|
||||
This parameter is usually not used by IGUISkin, but can be used for example
|
||||
by more complex implementations to find out how to draw the part exactly.
|
||||
\param color: Color of the rectangle to draw. The alpha component specifies how
|
||||
transparent the rectangle will be.
|
||||
\param pos: Position of the rectangle.
|
||||
\param clip: Pointer to rectangle against which the rectangle will be clipped.
|
||||
If the pointer is null, no clipping will be performed. */
|
||||
virtual void draw2DRectangle(IGUIElement *element, const video::SColor &color,
|
||||
const core::rect<s32> &pos, const core::rect<s32> *clip = 0) override;
|
||||
|
||||
//! get the type of this skin
|
||||
EGUI_SKIN_TYPE getType() const override;
|
||||
|
||||
private:
|
||||
video::SColor Colors[EGDC_COUNT];
|
||||
s32 Sizes[EGDS_COUNT];
|
||||
u32 Icons[EGDI_COUNT];
|
||||
IGUIFont *Fonts[EGDF_COUNT];
|
||||
IGUISpriteBank *SpriteBank;
|
||||
core::stringw Texts[EGDT_COUNT];
|
||||
video::IVideoDriver *Driver;
|
||||
bool UseGradient;
|
||||
|
||||
EGUI_SKIN_TYPE Type;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
246
src/CGUISpriteBank.cpp
Normal file
246
src/CGUISpriteBank.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUISpriteBank.h"
|
||||
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "ITexture.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
CGUISpriteBank::CGUISpriteBank(IGUIEnvironment *env) :
|
||||
Environment(env), Driver(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUISpriteBank");
|
||||
#endif
|
||||
|
||||
if (Environment) {
|
||||
Driver = Environment->getVideoDriver();
|
||||
if (Driver)
|
||||
Driver->grab();
|
||||
}
|
||||
}
|
||||
|
||||
CGUISpriteBank::~CGUISpriteBank()
|
||||
{
|
||||
clear();
|
||||
|
||||
// drop video driver
|
||||
if (Driver)
|
||||
Driver->drop();
|
||||
}
|
||||
|
||||
core::array<core::rect<s32>> &CGUISpriteBank::getPositions()
|
||||
{
|
||||
return Rectangles;
|
||||
}
|
||||
|
||||
core::array<SGUISprite> &CGUISpriteBank::getSprites()
|
||||
{
|
||||
return Sprites;
|
||||
}
|
||||
|
||||
u32 CGUISpriteBank::getTextureCount() const
|
||||
{
|
||||
return Textures.size();
|
||||
}
|
||||
|
||||
video::ITexture *CGUISpriteBank::getTexture(u32 index) const
|
||||
{
|
||||
if (index < Textures.size())
|
||||
return Textures[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CGUISpriteBank::addTexture(video::ITexture *texture)
|
||||
{
|
||||
if (texture)
|
||||
texture->grab();
|
||||
|
||||
Textures.push_back(texture);
|
||||
}
|
||||
|
||||
void CGUISpriteBank::setTexture(u32 index, video::ITexture *texture)
|
||||
{
|
||||
while (index >= Textures.size())
|
||||
Textures.push_back(0);
|
||||
|
||||
if (texture)
|
||||
texture->grab();
|
||||
|
||||
if (Textures[index])
|
||||
Textures[index]->drop();
|
||||
|
||||
Textures[index] = texture;
|
||||
}
|
||||
|
||||
//! clear everything
|
||||
void CGUISpriteBank::clear()
|
||||
{
|
||||
// drop textures
|
||||
for (u32 i = 0; i < Textures.size(); ++i) {
|
||||
if (Textures[i])
|
||||
Textures[i]->drop();
|
||||
}
|
||||
Textures.clear();
|
||||
Sprites.clear();
|
||||
Rectangles.clear();
|
||||
}
|
||||
|
||||
//! Add the texture and use it for a single non-animated sprite.
|
||||
s32 CGUISpriteBank::addTextureAsSprite(video::ITexture *texture)
|
||||
{
|
||||
if (!texture)
|
||||
return -1;
|
||||
|
||||
addTexture(texture);
|
||||
u32 textureIndex = getTextureCount() - 1;
|
||||
|
||||
u32 rectangleIndex = Rectangles.size();
|
||||
Rectangles.push_back(core::rect<s32>(0, 0, texture->getOriginalSize().Width, texture->getOriginalSize().Height));
|
||||
|
||||
SGUISprite sprite;
|
||||
sprite.frameTime = 0;
|
||||
|
||||
SGUISpriteFrame frame;
|
||||
frame.textureNumber = textureIndex;
|
||||
frame.rectNumber = rectangleIndex;
|
||||
sprite.Frames.push_back(frame);
|
||||
|
||||
Sprites.push_back(sprite);
|
||||
|
||||
return Sprites.size() - 1;
|
||||
}
|
||||
|
||||
// get FrameNr for time. return true on exisiting frame
|
||||
inline bool CGUISpriteBank::getFrameNr(u32 &frame, u32 index, u32 time, bool loop) const
|
||||
{
|
||||
frame = 0;
|
||||
if (index >= Sprites.size())
|
||||
return false;
|
||||
|
||||
const SGUISprite &sprite = Sprites[index];
|
||||
const u32 frameSize = sprite.Frames.size();
|
||||
if (frameSize < 1)
|
||||
return false;
|
||||
|
||||
if (sprite.frameTime) {
|
||||
u32 f = (time / sprite.frameTime);
|
||||
if (loop)
|
||||
frame = f % frameSize;
|
||||
else
|
||||
frame = (f >= frameSize) ? frameSize - 1 : f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! draws a sprite in 2d with scale and color
|
||||
void CGUISpriteBank::draw2DSprite(u32 index, const core::position2di &pos,
|
||||
const core::rect<s32> *clip, const video::SColor &color,
|
||||
u32 starttime, u32 currenttime, bool loop, bool center)
|
||||
{
|
||||
u32 frame = 0;
|
||||
if (!getFrameNr(frame, index, currenttime - starttime, loop))
|
||||
return;
|
||||
|
||||
const video::ITexture *tex = getTexture(Sprites[index].Frames[frame].textureNumber);
|
||||
if (!tex)
|
||||
return;
|
||||
|
||||
const u32 rn = Sprites[index].Frames[frame].rectNumber;
|
||||
if (rn >= Rectangles.size())
|
||||
return;
|
||||
|
||||
const core::rect<s32> &r = Rectangles[rn];
|
||||
core::position2di p(pos);
|
||||
if (center) {
|
||||
p -= r.getSize() / 2;
|
||||
}
|
||||
Driver->draw2DImage(tex, p, r, clip, color, true);
|
||||
}
|
||||
|
||||
void CGUISpriteBank::draw2DSprite(u32 index, const core::rect<s32> &destRect,
|
||||
const core::rect<s32> *clip, const video::SColor *const colors,
|
||||
u32 timeTicks, bool loop)
|
||||
{
|
||||
u32 frame = 0;
|
||||
if (!getFrameNr(frame, index, timeTicks, loop))
|
||||
return;
|
||||
|
||||
const video::ITexture *tex = getTexture(Sprites[index].Frames[frame].textureNumber);
|
||||
if (!tex)
|
||||
return;
|
||||
|
||||
const u32 rn = Sprites[index].Frames[frame].rectNumber;
|
||||
if (rn >= Rectangles.size())
|
||||
return;
|
||||
|
||||
Driver->draw2DImage(tex, destRect, Rectangles[rn], clip, colors, true);
|
||||
}
|
||||
|
||||
void CGUISpriteBank::draw2DSpriteBatch(const core::array<u32> &indices,
|
||||
const core::array<core::position2di> &pos,
|
||||
const core::rect<s32> *clip,
|
||||
const video::SColor &color,
|
||||
u32 starttime, u32 currenttime,
|
||||
bool loop, bool center)
|
||||
{
|
||||
const irr::u32 drawCount = core::min_<u32>(indices.size(), pos.size());
|
||||
|
||||
if (!getTextureCount())
|
||||
return;
|
||||
core::array<SDrawBatch> drawBatches(getTextureCount());
|
||||
for (u32 i = 0; i < Textures.size(); ++i) {
|
||||
drawBatches.push_back(SDrawBatch());
|
||||
drawBatches[i].positions.reallocate(drawCount);
|
||||
drawBatches[i].sourceRects.reallocate(drawCount);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < drawCount; ++i) {
|
||||
const u32 index = indices[i];
|
||||
|
||||
// work out frame number
|
||||
u32 frame = 0;
|
||||
if (!getFrameNr(frame, index, currenttime - starttime, loop))
|
||||
return;
|
||||
|
||||
const u32 texNum = Sprites[index].Frames[frame].textureNumber;
|
||||
if (texNum >= drawBatches.size()) {
|
||||
continue;
|
||||
}
|
||||
SDrawBatch ¤tBatch = drawBatches[texNum];
|
||||
|
||||
const u32 rn = Sprites[index].Frames[frame].rectNumber;
|
||||
if (rn >= Rectangles.size())
|
||||
return;
|
||||
|
||||
const core::rect<s32> &r = Rectangles[rn];
|
||||
|
||||
if (center) {
|
||||
core::position2di p = pos[i];
|
||||
p -= r.getSize() / 2;
|
||||
|
||||
currentBatch.positions.push_back(p);
|
||||
currentBatch.sourceRects.push_back(r);
|
||||
} else {
|
||||
currentBatch.positions.push_back(pos[i]);
|
||||
currentBatch.sourceRects.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < drawBatches.size(); i++) {
|
||||
if (!drawBatches[i].positions.empty() && !drawBatches[i].sourceRects.empty())
|
||||
Driver->draw2DImageBatch(getTexture(i), drawBatches[i].positions,
|
||||
drawBatches[i].sourceRects, clip, color, true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace irr
|
||||
81
src/CGUISpriteBank.h
Normal file
81
src/CGUISpriteBank.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUISpriteBank.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
namespace video
|
||||
{
|
||||
class IVideoDriver;
|
||||
class ITexture;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class IGUIEnvironment;
|
||||
|
||||
//! Sprite bank interface.
|
||||
class CGUISpriteBank : public IGUISpriteBank
|
||||
{
|
||||
public:
|
||||
CGUISpriteBank(IGUIEnvironment *env);
|
||||
virtual ~CGUISpriteBank();
|
||||
|
||||
core::array<core::rect<s32>> &getPositions() override;
|
||||
core::array<SGUISprite> &getSprites() override;
|
||||
|
||||
u32 getTextureCount() const override;
|
||||
video::ITexture *getTexture(u32 index) const override;
|
||||
void addTexture(video::ITexture *texture) override;
|
||||
void setTexture(u32 index, video::ITexture *texture) override;
|
||||
|
||||
//! Add the texture and use it for a single non-animated sprite.
|
||||
s32 addTextureAsSprite(video::ITexture *texture) override;
|
||||
|
||||
//! clears sprites, rectangles and textures
|
||||
void clear() override;
|
||||
|
||||
//! Draws a sprite in 2d with position and color
|
||||
virtual void draw2DSprite(u32 index, const core::position2di &pos, const core::rect<s32> *clip = 0,
|
||||
const video::SColor &color = video::SColor(255, 255, 255, 255),
|
||||
u32 starttime = 0, u32 currenttime = 0, bool loop = true, bool center = false) override;
|
||||
|
||||
//! Draws a sprite in 2d with destination rectangle and colors
|
||||
virtual void draw2DSprite(u32 index, const core::rect<s32> &destRect,
|
||||
const core::rect<s32> *clip = 0,
|
||||
const video::SColor *const colors = 0,
|
||||
u32 timeTicks = 0,
|
||||
bool loop = true) override;
|
||||
|
||||
//! Draws a sprite batch in 2d using an array of positions and a color
|
||||
virtual void draw2DSpriteBatch(const core::array<u32> &indices, const core::array<core::position2di> &pos,
|
||||
const core::rect<s32> *clip = 0,
|
||||
const video::SColor &color = video::SColor(255, 255, 255, 255),
|
||||
u32 starttime = 0, u32 currenttime = 0,
|
||||
bool loop = true, bool center = false) override;
|
||||
|
||||
protected:
|
||||
bool getFrameNr(u32 &frameNr, u32 index, u32 time, bool loop) const;
|
||||
|
||||
struct SDrawBatch
|
||||
{
|
||||
core::array<core::position2di> positions;
|
||||
core::array<core::recti> sourceRects;
|
||||
u32 textureNumber;
|
||||
};
|
||||
|
||||
core::array<SGUISprite> Sprites;
|
||||
core::array<core::rect<s32>> Rectangles;
|
||||
core::array<video::ITexture *> Textures;
|
||||
IGUIEnvironment *Environment;
|
||||
video::IVideoDriver *Driver;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
511
src/CGUIStaticText.cpp
Normal file
511
src/CGUIStaticText.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUIStaticText.h"
|
||||
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "rect.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CGUIStaticText::CGUIStaticText(const wchar_t *text, bool border,
|
||||
IGUIEnvironment *environment, IGUIElement *parent,
|
||||
s32 id, const core::rect<s32> &rectangle,
|
||||
bool background) :
|
||||
IGUIStaticText(environment, parent, id, rectangle),
|
||||
HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
|
||||
Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
|
||||
RestrainTextInside(true), RightToLeft(false),
|
||||
OverrideColor(video::SColor(101, 255, 255, 255)), BGColor(video::SColor(101, 210, 210, 210)),
|
||||
OverrideFont(0), LastBreakFont(0)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIStaticText");
|
||||
#endif
|
||||
|
||||
Text = text;
|
||||
if (environment && environment->getSkin()) {
|
||||
BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUIStaticText::~CGUIStaticText()
|
||||
{
|
||||
if (OverrideFont)
|
||||
OverrideFont->drop();
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUIStaticText::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
// draw background
|
||||
|
||||
if (Background) {
|
||||
if (!OverrideBGColorEnabled) // skin-colors can change
|
||||
BGColor = skin->getColor(gui::EGDC_3D_FACE);
|
||||
|
||||
driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect);
|
||||
}
|
||||
|
||||
// draw the border
|
||||
|
||||
if (Border) {
|
||||
skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
|
||||
frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
}
|
||||
|
||||
// draw the text
|
||||
if (Text.size()) {
|
||||
IGUIFont *font = getActiveFont();
|
||||
|
||||
if (font) {
|
||||
if (!WordWrap) {
|
||||
if (VAlign == EGUIA_LOWERRIGHT) {
|
||||
frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
|
||||
font->getDimension(L"A").Height - font->getKerningHeight();
|
||||
}
|
||||
if (HAlign == EGUIA_LOWERRIGHT) {
|
||||
frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
|
||||
font->getDimension(Text.c_str()).Width;
|
||||
}
|
||||
|
||||
font->draw(Text.c_str(), frameRect,
|
||||
getActiveColor(),
|
||||
HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
|
||||
} else {
|
||||
if (font != LastBreakFont)
|
||||
breakText();
|
||||
|
||||
core::rect<s32> r = frameRect;
|
||||
s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
|
||||
s32 totalHeight = height * BrokenText.size();
|
||||
if (VAlign == EGUIA_CENTER) {
|
||||
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
|
||||
} else if (VAlign == EGUIA_LOWERRIGHT) {
|
||||
r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < BrokenText.size(); ++i) {
|
||||
if (HAlign == EGUIA_LOWERRIGHT) {
|
||||
r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
|
||||
font->getDimension(BrokenText[i].c_str()).Width;
|
||||
}
|
||||
|
||||
font->draw(BrokenText[i].c_str(), r,
|
||||
getActiveColor(),
|
||||
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
|
||||
|
||||
r.LowerRightCorner.Y += height;
|
||||
r.UpperLeftCorner.Y += height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! Sets another skin independent font.
|
||||
void CGUIStaticText::setOverrideFont(IGUIFont *font)
|
||||
{
|
||||
if (OverrideFont == font)
|
||||
return;
|
||||
|
||||
if (OverrideFont)
|
||||
OverrideFont->drop();
|
||||
|
||||
OverrideFont = font;
|
||||
|
||||
if (OverrideFont)
|
||||
OverrideFont->grab();
|
||||
|
||||
breakText();
|
||||
}
|
||||
|
||||
//! Gets the override font (if any)
|
||||
IGUIFont *CGUIStaticText::getOverrideFont() const
|
||||
{
|
||||
return OverrideFont;
|
||||
}
|
||||
|
||||
//! Get the font which is used right now for drawing
|
||||
IGUIFont *CGUIStaticText::getActiveFont() const
|
||||
{
|
||||
if (OverrideFont)
|
||||
return OverrideFont;
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin)
|
||||
return skin->getFont();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Sets another color for the text.
|
||||
void CGUIStaticText::setOverrideColor(video::SColor color)
|
||||
{
|
||||
OverrideColor = color;
|
||||
OverrideColorEnabled = true;
|
||||
}
|
||||
|
||||
//! Sets another color for the text.
|
||||
void CGUIStaticText::setBackgroundColor(video::SColor color)
|
||||
{
|
||||
BGColor = color;
|
||||
OverrideBGColorEnabled = true;
|
||||
Background = true;
|
||||
}
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void CGUIStaticText::setDrawBackground(bool draw)
|
||||
{
|
||||
Background = draw;
|
||||
}
|
||||
|
||||
//! Gets the background color
|
||||
video::SColor CGUIStaticText::getBackgroundColor() const
|
||||
{
|
||||
return BGColor;
|
||||
}
|
||||
|
||||
//! Checks if background drawing is enabled
|
||||
bool CGUIStaticText::isDrawBackgroundEnabled() const
|
||||
{
|
||||
return Background;
|
||||
}
|
||||
|
||||
//! Sets whether to draw the border
|
||||
void CGUIStaticText::setDrawBorder(bool draw)
|
||||
{
|
||||
Border = draw;
|
||||
}
|
||||
|
||||
//! Checks if border drawing is enabled
|
||||
bool CGUIStaticText::isDrawBorderEnabled() const
|
||||
{
|
||||
return Border;
|
||||
}
|
||||
|
||||
void CGUIStaticText::setTextRestrainedInside(bool restrainTextInside)
|
||||
{
|
||||
RestrainTextInside = restrainTextInside;
|
||||
}
|
||||
|
||||
bool CGUIStaticText::isTextRestrainedInside() const
|
||||
{
|
||||
return RestrainTextInside;
|
||||
}
|
||||
|
||||
void CGUIStaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
|
||||
{
|
||||
HAlign = horizontal;
|
||||
VAlign = vertical;
|
||||
}
|
||||
|
||||
video::SColor CGUIStaticText::getOverrideColor() const
|
||||
{
|
||||
return OverrideColor;
|
||||
}
|
||||
|
||||
irr::video::SColor CGUIStaticText::getActiveColor() const
|
||||
{
|
||||
if (OverrideColorEnabled)
|
||||
return OverrideColor;
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin)
|
||||
return OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT);
|
||||
return OverrideColor;
|
||||
}
|
||||
|
||||
//! Sets if the static text should use the override color or the
|
||||
//! color in the gui skin.
|
||||
void CGUIStaticText::enableOverrideColor(bool enable)
|
||||
{
|
||||
OverrideColorEnabled = enable;
|
||||
}
|
||||
|
||||
bool CGUIStaticText::isOverrideColorEnabled() const
|
||||
{
|
||||
return OverrideColorEnabled;
|
||||
}
|
||||
|
||||
//! Enables or disables word wrap for using the static text as
|
||||
//! multiline text control.
|
||||
void CGUIStaticText::setWordWrap(bool enable)
|
||||
{
|
||||
WordWrap = enable;
|
||||
breakText();
|
||||
}
|
||||
|
||||
bool CGUIStaticText::isWordWrapEnabled() const
|
||||
{
|
||||
return WordWrap;
|
||||
}
|
||||
|
||||
void CGUIStaticText::setRightToLeft(bool rtl)
|
||||
{
|
||||
if (RightToLeft != rtl) {
|
||||
RightToLeft = rtl;
|
||||
breakText();
|
||||
}
|
||||
}
|
||||
|
||||
bool CGUIStaticText::isRightToLeft() const
|
||||
{
|
||||
return RightToLeft;
|
||||
}
|
||||
|
||||
//! Breaks the single text line.
|
||||
void CGUIStaticText::breakText()
|
||||
{
|
||||
if (!WordWrap)
|
||||
return;
|
||||
|
||||
BrokenText.clear();
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
IGUIFont *font = getActiveFont();
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
LastBreakFont = font;
|
||||
|
||||
core::stringw line;
|
||||
core::stringw word;
|
||||
core::stringw whitespace;
|
||||
s32 size = Text.size();
|
||||
s32 length = 0;
|
||||
s32 elWidth = RelativeRect.getWidth();
|
||||
if (Border)
|
||||
elWidth -= 2 * skin->getSize(EGDS_TEXT_DISTANCE_X);
|
||||
wchar_t c;
|
||||
|
||||
// We have to deal with right-to-left and left-to-right differently
|
||||
// However, most parts of the following code is the same, it's just
|
||||
// some order and boundaries which change.
|
||||
if (!RightToLeft) {
|
||||
// regular (left-to-right)
|
||||
for (s32 i = 0; i < size; ++i) {
|
||||
c = Text[i];
|
||||
bool lineBreak = false;
|
||||
|
||||
if (c == L'\r') { // Mac or Windows breaks
|
||||
lineBreak = true;
|
||||
if (Text[i + 1] == L'\n') { // Windows breaks
|
||||
Text.erase(i + 1);
|
||||
--size;
|
||||
}
|
||||
c = '\0';
|
||||
} else if (c == L'\n') { // Unix breaks
|
||||
lineBreak = true;
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
bool isWhitespace = (c == L' ' || c == 0);
|
||||
if (!isWhitespace) {
|
||||
// part of a word
|
||||
word += c;
|
||||
}
|
||||
|
||||
if (isWhitespace || i == (size - 1)) {
|
||||
if (word.size()) {
|
||||
// here comes the next whitespace, look if
|
||||
// we must break the last word to the next line.
|
||||
const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
|
||||
const s32 wordlgth = font->getDimension(word.c_str()).Width;
|
||||
|
||||
if (wordlgth > elWidth) {
|
||||
// This word is too long to fit in the available space, look for
|
||||
// the Unicode Soft HYphen (SHY / 00AD) character for a place to
|
||||
// break the word at
|
||||
int where = word.findFirst(wchar_t(0x00AD));
|
||||
if (where != -1) {
|
||||
core::stringw first = word.subString(0, where);
|
||||
core::stringw second = word.subString(where, word.size() - where);
|
||||
BrokenText.push_back(line + first + L"-");
|
||||
const s32 secondLength = font->getDimension(second.c_str()).Width;
|
||||
|
||||
length = secondLength;
|
||||
line = second;
|
||||
} else {
|
||||
// No soft hyphen found, so there's nothing more we can do
|
||||
// break to next line
|
||||
if (length)
|
||||
BrokenText.push_back(line);
|
||||
length = wordlgth;
|
||||
line = word;
|
||||
}
|
||||
} else if (length && (length + wordlgth + whitelgth > elWidth)) {
|
||||
// break to next line
|
||||
BrokenText.push_back(line);
|
||||
length = wordlgth;
|
||||
line = word;
|
||||
} else {
|
||||
// add word to line
|
||||
line += whitespace;
|
||||
line += word;
|
||||
length += whitelgth + wordlgth;
|
||||
}
|
||||
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
}
|
||||
|
||||
if (isWhitespace) {
|
||||
whitespace += c;
|
||||
}
|
||||
|
||||
// compute line break
|
||||
if (lineBreak) {
|
||||
line += whitespace;
|
||||
line += word;
|
||||
BrokenText.push_back(line);
|
||||
line = L"";
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line += whitespace;
|
||||
line += word;
|
||||
BrokenText.push_back(line);
|
||||
} else {
|
||||
// right-to-left
|
||||
for (s32 i = size; i >= 0; --i) {
|
||||
c = Text[i];
|
||||
bool lineBreak = false;
|
||||
|
||||
if (c == L'\r') { // Mac or Windows breaks
|
||||
lineBreak = true;
|
||||
if ((i > 0) && Text[i - 1] == L'\n') { // Windows breaks
|
||||
Text.erase(i - 1);
|
||||
--size;
|
||||
}
|
||||
c = '\0';
|
||||
} else if (c == L'\n') { // Unix breaks
|
||||
lineBreak = true;
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
if (c == L' ' || c == 0 || i == 0) {
|
||||
if (word.size()) {
|
||||
// here comes the next whitespace, look if
|
||||
// we must break the last word to the next line.
|
||||
const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
|
||||
const s32 wordlgth = font->getDimension(word.c_str()).Width;
|
||||
|
||||
if (length && (length + wordlgth + whitelgth > elWidth)) {
|
||||
// break to next line
|
||||
BrokenText.push_back(line);
|
||||
length = wordlgth;
|
||||
line = word;
|
||||
} else {
|
||||
// add word to line
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
length += whitelgth + wordlgth;
|
||||
}
|
||||
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
}
|
||||
|
||||
if (c != 0)
|
||||
whitespace = core::stringw(&c, 1) + whitespace;
|
||||
|
||||
// compute line break
|
||||
if (lineBreak) {
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
BrokenText.push_back(line);
|
||||
line = L"";
|
||||
word = L"";
|
||||
whitespace = L"";
|
||||
length = 0;
|
||||
}
|
||||
} else {
|
||||
// yippee this is a word..
|
||||
word = core::stringw(&c, 1) + word;
|
||||
}
|
||||
}
|
||||
|
||||
line = whitespace + line;
|
||||
line = word + line;
|
||||
BrokenText.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
//! Sets the new caption of this element.
|
||||
void CGUIStaticText::setText(const wchar_t *text)
|
||||
{
|
||||
IGUIElement::setText(text);
|
||||
breakText();
|
||||
}
|
||||
|
||||
void CGUIStaticText::updateAbsolutePosition()
|
||||
{
|
||||
IGUIElement::updateAbsolutePosition();
|
||||
breakText();
|
||||
}
|
||||
|
||||
//! Returns the height of the text in pixels when it is drawn.
|
||||
s32 CGUIStaticText::getTextHeight() const
|
||||
{
|
||||
IGUIFont *font = getActiveFont();
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
if (WordWrap) {
|
||||
s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
|
||||
return height * BrokenText.size();
|
||||
} else {
|
||||
// TODO: Text can have multiple lines which are not in BrokenText
|
||||
// This is likely not correct. But as I have no time for further
|
||||
// investigation I just fix it for now by return the true height here.
|
||||
return font->getDimension(Text.c_str()).Height;
|
||||
}
|
||||
}
|
||||
|
||||
s32 CGUIStaticText::getTextWidth() const
|
||||
{
|
||||
IGUIFont *font = getActiveFont();
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
if (WordWrap) {
|
||||
s32 widest = 0;
|
||||
|
||||
for (u32 line = 0; line < BrokenText.size(); ++line) {
|
||||
s32 width = font->getDimension(BrokenText[line].c_str()).Width;
|
||||
|
||||
if (width > widest)
|
||||
widest = width;
|
||||
}
|
||||
|
||||
return widest;
|
||||
} else {
|
||||
return font->getDimension(Text.c_str()).Width;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
131
src/CGUIStaticText.h
Normal file
131
src/CGUIStaticText.h
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUIStaticText.h"
|
||||
#include "irrArray.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class CGUIStaticText : public IGUIStaticText
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUIStaticText(const wchar_t *text, bool border, IGUIEnvironment *environment,
|
||||
IGUIElement *parent, s32 id, const core::rect<s32> &rectangle,
|
||||
bool background = false);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUIStaticText();
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! Sets another skin independent font.
|
||||
void setOverrideFont(IGUIFont *font = 0) override;
|
||||
|
||||
//! Gets the override font (if any)
|
||||
IGUIFont *getOverrideFont() const override;
|
||||
|
||||
//! Get the font which is used right now for drawing
|
||||
IGUIFont *getActiveFont() const override;
|
||||
|
||||
//! Sets another color for the text.
|
||||
void setOverrideColor(video::SColor color) override;
|
||||
|
||||
//! Sets another color for the background.
|
||||
void setBackgroundColor(video::SColor color) override;
|
||||
|
||||
//! Sets whether to draw the background
|
||||
void setDrawBackground(bool draw) override;
|
||||
|
||||
//! Gets the background color
|
||||
video::SColor getBackgroundColor() const override;
|
||||
|
||||
//! Checks if background drawing is enabled
|
||||
bool isDrawBackgroundEnabled() const override;
|
||||
|
||||
//! Sets whether to draw the border
|
||||
void setDrawBorder(bool draw) override;
|
||||
|
||||
//! Checks if border drawing is enabled
|
||||
bool isDrawBorderEnabled() const override;
|
||||
|
||||
//! Sets alignment mode for text
|
||||
void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) override;
|
||||
|
||||
//! Gets the override color
|
||||
video::SColor getOverrideColor() const override;
|
||||
|
||||
//! Gets the currently used text color
|
||||
video::SColor getActiveColor() const override;
|
||||
|
||||
//! Sets if the static text should use the override color or the
|
||||
//! color in the gui skin.
|
||||
void enableOverrideColor(bool enable) override;
|
||||
|
||||
//! Checks if an override color is enabled
|
||||
bool isOverrideColorEnabled() const override;
|
||||
|
||||
//! Set whether the text in this label should be clipped if it goes outside bounds
|
||||
void setTextRestrainedInside(bool restrainedInside) override;
|
||||
|
||||
//! Checks if the text in this label should be clipped if it goes outside bounds
|
||||
bool isTextRestrainedInside() const override;
|
||||
|
||||
//! Enables or disables word wrap for using the static text as
|
||||
//! multiline text control.
|
||||
void setWordWrap(bool enable) override;
|
||||
|
||||
//! Checks if word wrap is enabled
|
||||
bool isWordWrapEnabled() const override;
|
||||
|
||||
//! Sets the new caption of this element.
|
||||
void setText(const wchar_t *text) override;
|
||||
|
||||
//! Returns the height of the text in pixels when it is drawn.
|
||||
s32 getTextHeight() const override;
|
||||
|
||||
//! Returns the width of the current text, in the current font
|
||||
s32 getTextWidth() const override;
|
||||
|
||||
//! Updates the absolute position, splits text if word wrap is enabled
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
//! Set whether the string should be interpreted as right-to-left (RTL) text
|
||||
/** \note This component does not implement the Unicode bidi standard, the
|
||||
text of the component should be already RTL if you call this. The
|
||||
main difference when RTL is enabled is that the linebreaks for multiline
|
||||
elements are performed starting from the end.
|
||||
*/
|
||||
void setRightToLeft(bool rtl) override;
|
||||
|
||||
//! Checks if the text should be interpreted as right-to-left text
|
||||
bool isRightToLeft() const override;
|
||||
|
||||
private:
|
||||
//! Breaks the single text line.
|
||||
void breakText();
|
||||
|
||||
EGUI_ALIGNMENT HAlign, VAlign;
|
||||
bool Border;
|
||||
bool OverrideColorEnabled;
|
||||
bool OverrideBGColorEnabled;
|
||||
bool WordWrap;
|
||||
bool Background;
|
||||
bool RestrainTextInside;
|
||||
bool RightToLeft;
|
||||
|
||||
video::SColor OverrideColor, BGColor;
|
||||
gui::IGUIFont *OverrideFont;
|
||||
gui::IGUIFont *LastBreakFont; // stored because: if skin changes, line break must be recalculated.
|
||||
|
||||
core::array<core::stringw> BrokenText;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
934
src/CGUITabControl.cpp
Normal file
934
src/CGUITabControl.cpp
Normal file
@@ -0,0 +1,934 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CGUITabControl.h"
|
||||
|
||||
#include "CGUIButton.h"
|
||||
#include "IGUISkin.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "rect.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Tab
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//! constructor
|
||||
CGUITab::CGUITab(IGUIEnvironment *environment,
|
||||
IGUIElement *parent, const core::rect<s32> &rectangle,
|
||||
s32 id) :
|
||||
IGUITab(environment, parent, id, rectangle),
|
||||
BackColor(0, 0, 0, 0), OverrideTextColorEnabled(false), TextColor(255, 0, 0, 0),
|
||||
DrawBackground(false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUITab");
|
||||
#endif
|
||||
|
||||
const IGUISkin *const skin = environment->getSkin();
|
||||
if (skin)
|
||||
TextColor = skin->getColor(EGDC_BUTTON_TEXT);
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUITab::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
|
||||
if (skin && DrawBackground)
|
||||
skin->draw2DRectangle(this, BackColor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! sets if the tab should draw its background
|
||||
void CGUITab::setDrawBackground(bool draw)
|
||||
{
|
||||
DrawBackground = draw;
|
||||
}
|
||||
|
||||
//! sets the color of the background, if it should be drawn.
|
||||
void CGUITab::setBackgroundColor(video::SColor c)
|
||||
{
|
||||
BackColor = c;
|
||||
}
|
||||
|
||||
//! sets the color of the text
|
||||
void CGUITab::setTextColor(video::SColor c)
|
||||
{
|
||||
OverrideTextColorEnabled = true;
|
||||
TextColor = c;
|
||||
}
|
||||
|
||||
video::SColor CGUITab::getTextColor() const
|
||||
{
|
||||
if (OverrideTextColorEnabled)
|
||||
return TextColor;
|
||||
else
|
||||
return Environment->getSkin()->getColor(EGDC_BUTTON_TEXT);
|
||||
}
|
||||
|
||||
//! returns true if the tab is drawing its background, false if not
|
||||
bool CGUITab::isDrawingBackground() const
|
||||
{
|
||||
return DrawBackground;
|
||||
}
|
||||
|
||||
//! returns the color of the background
|
||||
video::SColor CGUITab::getBackgroundColor() const
|
||||
{
|
||||
return BackColor;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Tabcontrol
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//! constructor
|
||||
CGUITabControl::CGUITabControl(IGUIEnvironment *environment,
|
||||
IGUIElement *parent, const core::rect<s32> &rectangle,
|
||||
bool fillbackground, bool border, s32 id) :
|
||||
IGUITabControl(environment, parent, id, rectangle),
|
||||
ActiveTabIndex(-1),
|
||||
Border(border), FillBackground(fillbackground), ScrollControl(false), TabHeight(0), VerticalAlignment(EGUIA_UPPERLEFT),
|
||||
UpButton(0), DownButton(0), TabMaxWidth(0), CurrentScrollTabIndex(0), TabExtraWidth(20)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUITabControl");
|
||||
#endif
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
IGUISpriteBank *sprites = 0;
|
||||
|
||||
TabHeight = 32;
|
||||
|
||||
if (skin) {
|
||||
sprites = skin->getSpriteBank();
|
||||
TabHeight = skin->getSize(gui::EGDS_BUTTON_HEIGHT) + 2;
|
||||
}
|
||||
|
||||
UpButton = Environment->addButton(core::rect<s32>(0, 0, 10, 10), this);
|
||||
|
||||
if (UpButton) {
|
||||
UpButton->setSpriteBank(sprites);
|
||||
UpButton->setVisible(false);
|
||||
UpButton->setSubElement(true);
|
||||
UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
UpButton->setOverrideFont(Environment->getBuiltInFont());
|
||||
UpButton->grab();
|
||||
}
|
||||
|
||||
DownButton = Environment->addButton(core::rect<s32>(0, 0, 10, 10), this);
|
||||
|
||||
if (DownButton) {
|
||||
DownButton->setSpriteBank(sprites);
|
||||
DownButton->setVisible(false);
|
||||
DownButton->setSubElement(true);
|
||||
DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
DownButton->setOverrideFont(Environment->getBuiltInFont());
|
||||
DownButton->grab();
|
||||
}
|
||||
|
||||
setTabVerticalAlignment(EGUIA_UPPERLEFT);
|
||||
refreshSprites();
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CGUITabControl::~CGUITabControl()
|
||||
{
|
||||
for (u32 i = 0; i < Tabs.size(); ++i) {
|
||||
if (Tabs[i])
|
||||
Tabs[i]->drop();
|
||||
}
|
||||
|
||||
if (UpButton)
|
||||
UpButton->drop();
|
||||
|
||||
if (DownButton)
|
||||
DownButton->drop();
|
||||
}
|
||||
|
||||
void CGUITabControl::refreshSprites()
|
||||
{
|
||||
video::SColor color(255, 255, 255, 255);
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (skin) {
|
||||
color = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
|
||||
|
||||
if (UpButton) {
|
||||
UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), color);
|
||||
UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), color);
|
||||
}
|
||||
|
||||
if (DownButton) {
|
||||
DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), color);
|
||||
DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Adds a tab
|
||||
IGUITab *CGUITabControl::addTab(const wchar_t *caption, s32 id)
|
||||
{
|
||||
CGUITab *tab = new CGUITab(Environment, this, calcTabPos(), id);
|
||||
|
||||
tab->setText(caption);
|
||||
tab->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
tab->setVisible(false);
|
||||
Tabs.push_back(tab); // no grab as new already creates a reference
|
||||
|
||||
if (ActiveTabIndex == -1) {
|
||||
ActiveTabIndex = Tabs.size() - 1;
|
||||
tab->setVisible(true);
|
||||
}
|
||||
|
||||
recalculateScrollBar();
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
//! adds a tab which has been created elsewhere
|
||||
s32 CGUITabControl::addTab(IGUITab *tab)
|
||||
{
|
||||
return insertTab(Tabs.size(), tab, false);
|
||||
}
|
||||
|
||||
//! Insert the tab at the given index
|
||||
IGUITab *CGUITabControl::insertTab(s32 idx, const wchar_t *caption, s32 id)
|
||||
{
|
||||
if (idx < 0 || idx > (s32)Tabs.size()) // idx == Tabs.size() is indeed OK here as core::array can handle that
|
||||
return NULL;
|
||||
|
||||
CGUITab *tab = new CGUITab(Environment, this, calcTabPos(), id);
|
||||
|
||||
tab->setText(caption);
|
||||
tab->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
|
||||
tab->setVisible(false);
|
||||
Tabs.insert(tab, (u32)idx);
|
||||
|
||||
if (ActiveTabIndex == -1) {
|
||||
ActiveTabIndex = (u32)idx;
|
||||
tab->setVisible(true);
|
||||
} else if (idx <= ActiveTabIndex) {
|
||||
++ActiveTabIndex;
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
}
|
||||
|
||||
recalculateScrollBar();
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
s32 CGUITabControl::insertTab(s32 idx, IGUITab *tab, bool serializationMode)
|
||||
{
|
||||
if (!tab)
|
||||
return -1;
|
||||
if (idx > (s32)Tabs.size() && !serializationMode) // idx == Tabs.size() is indeed OK here as core::array can handle that
|
||||
return -1;
|
||||
// Not allowing to add same tab twice as it would make things complicated (serialization or setting active visible)
|
||||
if (getTabIndex(tab) >= 0)
|
||||
return -1;
|
||||
|
||||
if (idx < 0)
|
||||
idx = (s32)Tabs.size();
|
||||
|
||||
if (tab->getParent() != this)
|
||||
this->addChildToEnd(tab);
|
||||
|
||||
tab->setVisible(false);
|
||||
|
||||
tab->grab();
|
||||
|
||||
if (serializationMode) {
|
||||
while (idx >= (s32)Tabs.size()) {
|
||||
Tabs.push_back(0);
|
||||
}
|
||||
Tabs[idx] = tab;
|
||||
|
||||
if (idx == ActiveTabIndex) { // in serialization that can happen for any index
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
tab->setVisible(true);
|
||||
}
|
||||
} else {
|
||||
Tabs.insert(tab, (u32)idx);
|
||||
|
||||
if (ActiveTabIndex == -1) {
|
||||
ActiveTabIndex = idx;
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
} else if (idx <= ActiveTabIndex) {
|
||||
++ActiveTabIndex;
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
recalculateScrollBar();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
//! Removes a child.
|
||||
void CGUITabControl::removeChild(IGUIElement *child)
|
||||
{
|
||||
s32 idx = getTabIndex(child);
|
||||
if (idx >= 0)
|
||||
removeTabButNotChild(idx);
|
||||
|
||||
// remove real element
|
||||
IGUIElement::removeChild(child);
|
||||
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
//! Removes a tab from the tabcontrol
|
||||
void CGUITabControl::removeTab(s32 idx)
|
||||
{
|
||||
if (idx < 0 || idx >= (s32)Tabs.size())
|
||||
return;
|
||||
|
||||
removeChild(Tabs[(u32)idx]);
|
||||
}
|
||||
|
||||
void CGUITabControl::removeTabButNotChild(s32 idx)
|
||||
{
|
||||
if (idx < 0 || idx >= (s32)Tabs.size())
|
||||
return;
|
||||
|
||||
Tabs[(u32)idx]->drop();
|
||||
Tabs.erase((u32)idx);
|
||||
|
||||
if (idx < ActiveTabIndex) {
|
||||
--ActiveTabIndex;
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
} else if (idx == ActiveTabIndex) {
|
||||
if ((u32)idx == Tabs.size())
|
||||
--ActiveTabIndex;
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//! Clears the tabcontrol removing all tabs
|
||||
void CGUITabControl::clear()
|
||||
{
|
||||
for (u32 i = 0; i < Tabs.size(); ++i) {
|
||||
if (Tabs[i]) {
|
||||
IGUIElement::removeChild(Tabs[i]);
|
||||
Tabs[i]->drop();
|
||||
}
|
||||
}
|
||||
Tabs.clear();
|
||||
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
//! Returns amount of tabs in the tabcontrol
|
||||
s32 CGUITabControl::getTabCount() const
|
||||
{
|
||||
return Tabs.size();
|
||||
}
|
||||
|
||||
//! Returns a tab based on zero based index
|
||||
IGUITab *CGUITabControl::getTab(s32 idx) const
|
||||
{
|
||||
if (idx < 0 || (u32)idx >= Tabs.size())
|
||||
return 0;
|
||||
|
||||
return Tabs[idx];
|
||||
}
|
||||
|
||||
//! called if an event happened.
|
||||
bool CGUITabControl::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (isEnabled()) {
|
||||
switch (event.EventType) {
|
||||
case EET_GUI_EVENT:
|
||||
switch (event.GUIEvent.EventType) {
|
||||
case EGET_BUTTON_CLICKED:
|
||||
if (event.GUIEvent.Caller == UpButton) {
|
||||
scrollLeft();
|
||||
return true;
|
||||
} else if (event.GUIEvent.Caller == DownButton) {
|
||||
scrollRight();
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
switch (event.MouseInput.Event) {
|
||||
// case EMIE_LMOUSE_PRESSED_DOWN:
|
||||
// // todo: dragging tabs around
|
||||
// return true;
|
||||
case EMIE_LMOUSE_LEFT_UP: {
|
||||
s32 idx = getTabAt(event.MouseInput.X, event.MouseInput.Y);
|
||||
if (idx >= 0) {
|
||||
setActiveTab(idx);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
void CGUITabControl::scrollLeft()
|
||||
{
|
||||
if (CurrentScrollTabIndex > 0)
|
||||
--CurrentScrollTabIndex;
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
void CGUITabControl::scrollRight()
|
||||
{
|
||||
if (CurrentScrollTabIndex < (s32)(Tabs.size()) - 1) {
|
||||
if (needScrollControl(CurrentScrollTabIndex, true))
|
||||
++CurrentScrollTabIndex;
|
||||
}
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
s32 CGUITabControl::calcTabWidth(IGUIFont *font, const wchar_t *text) const
|
||||
{
|
||||
if (!font)
|
||||
return 0;
|
||||
|
||||
s32 len = font->getDimension(text).Width + TabExtraWidth;
|
||||
if (TabMaxWidth > 0 && len > TabMaxWidth)
|
||||
len = TabMaxWidth;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl, s32 *pos_rightmost)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
startIndex = 0;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return false;
|
||||
|
||||
IGUIFont *font = skin->getFont();
|
||||
|
||||
if (Tabs.empty())
|
||||
return false;
|
||||
|
||||
if (!font)
|
||||
return false;
|
||||
|
||||
s32 pos = AbsoluteRect.UpperLeftCorner.X + 2;
|
||||
const s32 pos_right = withScrollControl ? UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 : AbsoluteRect.LowerRightCorner.X;
|
||||
|
||||
for (s32 i = startIndex; i < (s32)Tabs.size(); ++i) {
|
||||
// get Text
|
||||
const wchar_t *text = 0;
|
||||
if (Tabs[i]) {
|
||||
text = Tabs[i]->getText();
|
||||
|
||||
// get text length
|
||||
s32 len = calcTabWidth(font, text); // always without withScrollControl here or len would be shortened
|
||||
pos += len;
|
||||
}
|
||||
|
||||
if (pos > pos_right)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pos_rightmost)
|
||||
*pos_rightmost = pos;
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 CGUITabControl::calculateScrollIndexFromActive()
|
||||
{
|
||||
if (!ScrollControl || Tabs.empty())
|
||||
return 0;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return false;
|
||||
|
||||
IGUIFont *font = skin->getFont();
|
||||
if (!font)
|
||||
return false;
|
||||
|
||||
const s32 pos_left = AbsoluteRect.UpperLeftCorner.X + 2;
|
||||
const s32 pos_right = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2;
|
||||
|
||||
// Move from center to the left border left until it is reached
|
||||
s32 pos_cl = (pos_left + pos_right) / 2;
|
||||
s32 i = ActiveTabIndex;
|
||||
for (; i > 0; --i) {
|
||||
if (!Tabs[i])
|
||||
continue;
|
||||
|
||||
s32 len = calcTabWidth(font, Tabs[i]->getText());
|
||||
if (i == ActiveTabIndex)
|
||||
len /= 2;
|
||||
if (pos_cl - len < pos_left)
|
||||
break;
|
||||
|
||||
pos_cl -= len;
|
||||
}
|
||||
if (i == 0)
|
||||
return i;
|
||||
|
||||
// Is scrolling to right still possible?
|
||||
s32 pos_rr = 0;
|
||||
if (needScrollControl(i, true, &pos_rr))
|
||||
return i; // Yes? -> OK
|
||||
|
||||
// No? -> Decrease "i" more. Append tabs until scrolling becomes necessary
|
||||
for (--i; i > 0; --i) {
|
||||
if (!Tabs[i])
|
||||
continue;
|
||||
|
||||
pos_rr += calcTabWidth(font, Tabs[i]->getText());
|
||||
if (pos_rr > pos_right)
|
||||
break;
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
core::rect<s32> CGUITabControl::calcTabPos()
|
||||
{
|
||||
core::rect<s32> r;
|
||||
r.UpperLeftCorner.X = 0;
|
||||
r.LowerRightCorner.X = AbsoluteRect.getWidth();
|
||||
if (Border) {
|
||||
++r.UpperLeftCorner.X;
|
||||
--r.LowerRightCorner.X;
|
||||
}
|
||||
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
r.UpperLeftCorner.Y = TabHeight + 2;
|
||||
r.LowerRightCorner.Y = AbsoluteRect.getHeight() - 1;
|
||||
if (Border) {
|
||||
--r.LowerRightCorner.Y;
|
||||
}
|
||||
} else {
|
||||
r.UpperLeftCorner.Y = 0;
|
||||
r.LowerRightCorner.Y = AbsoluteRect.getHeight() - (TabHeight + 2);
|
||||
if (Border) {
|
||||
++r.UpperLeftCorner.Y;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//! draws the element and its children
|
||||
void CGUITabControl::draw()
|
||||
{
|
||||
if (!IsVisible)
|
||||
return;
|
||||
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
|
||||
IGUIFont *font = skin->getFont();
|
||||
video::IVideoDriver *driver = Environment->getVideoDriver();
|
||||
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
// some empty background as placeholder when there are no tabs
|
||||
if (Tabs.empty())
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), frameRect, &AbsoluteClippingRect);
|
||||
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
// tab button bar can be above or below the tabs
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
frameRect.UpperLeftCorner.Y += 2;
|
||||
frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + TabHeight;
|
||||
} else {
|
||||
frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - TabHeight - 1;
|
||||
frameRect.LowerRightCorner.Y -= 2;
|
||||
}
|
||||
|
||||
core::rect<s32> tr;
|
||||
s32 pos = frameRect.UpperLeftCorner.X + 2;
|
||||
|
||||
bool needLeftScroll = CurrentScrollTabIndex > 0;
|
||||
bool needRightScroll = false;
|
||||
|
||||
// left and right pos of the active tab
|
||||
s32 left = 0;
|
||||
s32 right = 0;
|
||||
|
||||
// const wchar_t* activetext = 0;
|
||||
IGUITab *activeTab = 0;
|
||||
|
||||
// Draw all tab-buttons except the active one
|
||||
for (u32 i = CurrentScrollTabIndex; i < Tabs.size() && !needRightScroll; ++i) {
|
||||
// get Text
|
||||
const wchar_t *text = 0;
|
||||
if (Tabs[i])
|
||||
text = Tabs[i]->getText();
|
||||
|
||||
// get text length
|
||||
s32 len = calcTabWidth(font, text);
|
||||
if (ScrollControl) {
|
||||
s32 space = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;
|
||||
if (space < len) {
|
||||
needRightScroll = true;
|
||||
len = space;
|
||||
}
|
||||
}
|
||||
|
||||
frameRect.LowerRightCorner.X += len;
|
||||
frameRect.UpperLeftCorner.X = pos;
|
||||
frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;
|
||||
|
||||
pos += len;
|
||||
|
||||
if ((s32)i == ActiveTabIndex) {
|
||||
// for active button just remember values
|
||||
left = frameRect.UpperLeftCorner.X;
|
||||
right = frameRect.LowerRightCorner.X;
|
||||
// activetext = text;
|
||||
activeTab = Tabs[i];
|
||||
} else {
|
||||
skin->draw3DTabButton(this, false, frameRect, &AbsoluteClippingRect, VerticalAlignment);
|
||||
|
||||
// draw text
|
||||
core::rect<s32> textClipRect(frameRect); // TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface
|
||||
textClipRect.clipAgainst(AbsoluteClippingRect);
|
||||
font->draw(text, frameRect, Tabs[i]->getTextColor(),
|
||||
true, true, &textClipRect);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw active tab button
|
||||
// Drawn later than other buttons because it draw over the buttons before/after it.
|
||||
if (left != 0 && right != 0 && activeTab != 0) {
|
||||
// draw upper highlight frame
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
frameRect.UpperLeftCorner.X = left - 2;
|
||||
frameRect.LowerRightCorner.X = right + 2;
|
||||
frameRect.UpperLeftCorner.Y -= 2;
|
||||
|
||||
skin->draw3DTabButton(this, true, frameRect, &AbsoluteClippingRect, VerticalAlignment);
|
||||
|
||||
// draw text
|
||||
core::rect<s32> textClipRect(frameRect); // TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface
|
||||
textClipRect.clipAgainst(AbsoluteClippingRect);
|
||||
font->draw(activeTab->getText(), frameRect, activeTab->getTextColor(),
|
||||
true, true, &textClipRect);
|
||||
|
||||
tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
|
||||
tr.LowerRightCorner.X = left - 1;
|
||||
tr.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - 1;
|
||||
tr.LowerRightCorner.Y = frameRect.LowerRightCorner.Y;
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
|
||||
|
||||
tr.UpperLeftCorner.X = right;
|
||||
tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
|
||||
} else {
|
||||
frameRect.UpperLeftCorner.X = left - 2;
|
||||
frameRect.LowerRightCorner.X = right + 2;
|
||||
frameRect.LowerRightCorner.Y += 2;
|
||||
|
||||
skin->draw3DTabButton(this, true, frameRect, &AbsoluteClippingRect, VerticalAlignment);
|
||||
|
||||
// draw text
|
||||
font->draw(activeTab->getText(), frameRect, activeTab->getTextColor(),
|
||||
true, true, &frameRect);
|
||||
|
||||
tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
|
||||
tr.LowerRightCorner.X = left - 1;
|
||||
tr.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - 1;
|
||||
tr.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y;
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
|
||||
|
||||
tr.UpperLeftCorner.X = right;
|
||||
tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
|
||||
}
|
||||
} else {
|
||||
// No active tab
|
||||
// Draw a line separating button bar from tab area
|
||||
tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
|
||||
tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
|
||||
tr.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - 1;
|
||||
tr.LowerRightCorner.Y = frameRect.LowerRightCorner.Y;
|
||||
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
|
||||
} else {
|
||||
tr.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - 1;
|
||||
tr.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y;
|
||||
driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
|
||||
}
|
||||
}
|
||||
|
||||
// drawing some border and background for the tab-area.
|
||||
skin->draw3DTabBody(this, Border, FillBackground, AbsoluteRect, &AbsoluteClippingRect, TabHeight, VerticalAlignment);
|
||||
|
||||
// enable scrollcontrols on need
|
||||
if (UpButton)
|
||||
UpButton->setEnabled(needLeftScroll);
|
||||
if (DownButton)
|
||||
DownButton->setEnabled(needRightScroll);
|
||||
refreshSprites();
|
||||
|
||||
IGUIElement::draw();
|
||||
}
|
||||
|
||||
//! Set the height of the tabs
|
||||
void CGUITabControl::setTabHeight(s32 height)
|
||||
{
|
||||
if (height < 0)
|
||||
height = 0;
|
||||
|
||||
TabHeight = height;
|
||||
|
||||
recalculateScrollButtonPlacement();
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
//! Get the height of the tabs
|
||||
s32 CGUITabControl::getTabHeight() const
|
||||
{
|
||||
return TabHeight;
|
||||
}
|
||||
|
||||
//! set the maximal width of a tab. Per default width is 0 which means "no width restriction".
|
||||
void CGUITabControl::setTabMaxWidth(s32 width)
|
||||
{
|
||||
TabMaxWidth = width;
|
||||
}
|
||||
|
||||
//! get the maximal width of a tab
|
||||
s32 CGUITabControl::getTabMaxWidth() const
|
||||
{
|
||||
return TabMaxWidth;
|
||||
}
|
||||
|
||||
//! Set the extra width added to tabs on each side of the text
|
||||
void CGUITabControl::setTabExtraWidth(s32 extraWidth)
|
||||
{
|
||||
if (extraWidth < 0)
|
||||
extraWidth = 0;
|
||||
|
||||
TabExtraWidth = extraWidth;
|
||||
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
//! Get the extra width added to tabs on each side of the text
|
||||
s32 CGUITabControl::getTabExtraWidth() const
|
||||
{
|
||||
return TabExtraWidth;
|
||||
}
|
||||
|
||||
void CGUITabControl::recalculateScrollBar()
|
||||
{
|
||||
// Down: to right, Up: to left
|
||||
if (!UpButton || !DownButton)
|
||||
return;
|
||||
|
||||
ScrollControl = needScrollControl() || CurrentScrollTabIndex > 0;
|
||||
|
||||
if (ScrollControl) {
|
||||
UpButton->setVisible(true);
|
||||
DownButton->setVisible(true);
|
||||
} else {
|
||||
UpButton->setVisible(false);
|
||||
DownButton->setVisible(false);
|
||||
}
|
||||
|
||||
bringToFront(UpButton);
|
||||
bringToFront(DownButton);
|
||||
}
|
||||
|
||||
//! Set the alignment of the tabs
|
||||
void CGUITabControl::setTabVerticalAlignment(EGUI_ALIGNMENT alignment)
|
||||
{
|
||||
VerticalAlignment = alignment;
|
||||
|
||||
recalculateScrollButtonPlacement();
|
||||
recalculateScrollBar();
|
||||
|
||||
core::rect<s32> r(calcTabPos());
|
||||
for (u32 i = 0; i < Tabs.size(); ++i) {
|
||||
Tabs[i]->setRelativePosition(r);
|
||||
}
|
||||
}
|
||||
|
||||
void CGUITabControl::recalculateScrollButtonPlacement()
|
||||
{
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
s32 ButtonSize = 16;
|
||||
s32 ButtonHeight = TabHeight - 2;
|
||||
if (ButtonHeight < 0)
|
||||
ButtonHeight = TabHeight;
|
||||
if (skin) {
|
||||
ButtonSize = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH);
|
||||
if (ButtonSize > TabHeight)
|
||||
ButtonSize = TabHeight;
|
||||
}
|
||||
|
||||
s32 ButtonX = RelativeRect.getWidth() - (s32)(2.5f * (f32)ButtonSize) - 1;
|
||||
s32 ButtonY = 0;
|
||||
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
ButtonY = 2 + (TabHeight / 2) - (ButtonHeight / 2);
|
||||
UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
|
||||
} else {
|
||||
ButtonY = RelativeRect.getHeight() - (TabHeight / 2) - (ButtonHeight / 2) - 2;
|
||||
UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
|
||||
DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
|
||||
}
|
||||
|
||||
UpButton->setRelativePosition(core::rect<s32>(ButtonX, ButtonY, ButtonX + ButtonSize, ButtonY + ButtonHeight));
|
||||
ButtonX += ButtonSize + 1;
|
||||
DownButton->setRelativePosition(core::rect<s32>(ButtonX, ButtonY, ButtonX + ButtonSize, ButtonY + ButtonHeight));
|
||||
}
|
||||
|
||||
//! Get the alignment of the tabs
|
||||
EGUI_ALIGNMENT CGUITabControl::getTabVerticalAlignment() const
|
||||
{
|
||||
return VerticalAlignment;
|
||||
}
|
||||
|
||||
s32 CGUITabControl::getTabAt(s32 xpos, s32 ypos) const
|
||||
{
|
||||
core::position2di p(xpos, ypos);
|
||||
IGUISkin *skin = Environment->getSkin();
|
||||
IGUIFont *font = skin->getFont();
|
||||
|
||||
core::rect<s32> frameRect(AbsoluteRect);
|
||||
|
||||
if (VerticalAlignment == EGUIA_UPPERLEFT) {
|
||||
frameRect.UpperLeftCorner.Y += 2;
|
||||
frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + TabHeight;
|
||||
} else {
|
||||
frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - TabHeight;
|
||||
}
|
||||
|
||||
s32 pos = frameRect.UpperLeftCorner.X + 2;
|
||||
|
||||
if (!frameRect.isPointInside(p))
|
||||
return -1;
|
||||
|
||||
bool abort = false;
|
||||
for (s32 i = CurrentScrollTabIndex; i < (s32)Tabs.size() && !abort; ++i) {
|
||||
// get Text
|
||||
const wchar_t *text = 0;
|
||||
if (Tabs[i])
|
||||
text = Tabs[i]->getText();
|
||||
|
||||
// get text length
|
||||
s32 len = calcTabWidth(font, text);
|
||||
if (ScrollControl) {
|
||||
// TODO: merge this with draw() ?
|
||||
s32 space = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;
|
||||
if (space < len) {
|
||||
abort = true;
|
||||
len = space;
|
||||
}
|
||||
}
|
||||
|
||||
frameRect.UpperLeftCorner.X = pos;
|
||||
frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;
|
||||
|
||||
pos += len;
|
||||
|
||||
if (frameRect.isPointInside(p)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! Returns which tab is currently active
|
||||
s32 CGUITabControl::getActiveTab() const
|
||||
{
|
||||
return ActiveTabIndex;
|
||||
}
|
||||
|
||||
//! Brings a tab to front.
|
||||
bool CGUITabControl::setActiveTab(s32 idx)
|
||||
{
|
||||
if ((u32)idx >= Tabs.size())
|
||||
return false;
|
||||
|
||||
bool changed = (ActiveTabIndex != idx);
|
||||
|
||||
ActiveTabIndex = idx;
|
||||
|
||||
setVisibleTab(ActiveTabIndex);
|
||||
|
||||
if (changed && Parent) {
|
||||
SEvent event;
|
||||
event.EventType = EET_GUI_EVENT;
|
||||
event.GUIEvent.Caller = this;
|
||||
event.GUIEvent.Element = 0;
|
||||
event.GUIEvent.EventType = EGET_TAB_CHANGED;
|
||||
Parent->OnEvent(event);
|
||||
}
|
||||
|
||||
if (ScrollControl) {
|
||||
CurrentScrollTabIndex = calculateScrollIndexFromActive();
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGUITabControl::setVisibleTab(s32 idx)
|
||||
{
|
||||
for (u32 i = 0; i < Tabs.size(); ++i)
|
||||
if (Tabs[i])
|
||||
Tabs[i]->setVisible((s32)i == idx);
|
||||
}
|
||||
|
||||
bool CGUITabControl::setActiveTab(IGUITab *tab)
|
||||
{
|
||||
return setActiveTab(getTabIndex(tab));
|
||||
}
|
||||
|
||||
s32 CGUITabControl::getTabIndex(const IGUIElement *tab) const
|
||||
{
|
||||
for (u32 i = 0; i < Tabs.size(); ++i)
|
||||
if (Tabs[i] == tab)
|
||||
return (s32)i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! Update the position of the element, decides scroll button status
|
||||
void CGUITabControl::updateAbsolutePosition()
|
||||
{
|
||||
IGUIElement::updateAbsolutePosition();
|
||||
recalculateScrollBar();
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace gui
|
||||
175
src/CGUITabControl.h
Normal file
175
src/CGUITabControl.h
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGUITabControl.h"
|
||||
#include "irrArray.h"
|
||||
#include "IGUISkin.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class CGUITabControl;
|
||||
class IGUIButton;
|
||||
|
||||
// A tab, onto which other gui elements could be added.
|
||||
class CGUITab : public IGUITab
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CGUITab(IGUIEnvironment *environment,
|
||||
IGUIElement *parent, const core::rect<s32> &rectangle,
|
||||
s32 id);
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! sets if the tab should draw its background
|
||||
void setDrawBackground(bool draw = true) override;
|
||||
|
||||
//! sets the color of the background, if it should be drawn.
|
||||
void setBackgroundColor(video::SColor c) override;
|
||||
|
||||
//! sets the color of the text
|
||||
void setTextColor(video::SColor c) override;
|
||||
|
||||
//! returns true if the tab is drawing its background, false if not
|
||||
bool isDrawingBackground() const override;
|
||||
|
||||
//! returns the color of the background
|
||||
video::SColor getBackgroundColor() const override;
|
||||
|
||||
video::SColor getTextColor() const override;
|
||||
|
||||
private:
|
||||
video::SColor BackColor;
|
||||
bool OverrideTextColorEnabled;
|
||||
video::SColor TextColor;
|
||||
bool DrawBackground;
|
||||
};
|
||||
|
||||
//! A standard tab control
|
||||
class CGUITabControl : public IGUITabControl
|
||||
{
|
||||
public:
|
||||
//! destructor
|
||||
CGUITabControl(IGUIEnvironment *environment,
|
||||
IGUIElement *parent, const core::rect<s32> &rectangle,
|
||||
bool fillbackground = true, bool border = true, s32 id = -1);
|
||||
|
||||
//! destructor
|
||||
virtual ~CGUITabControl();
|
||||
|
||||
//! Adds a tab
|
||||
IGUITab *addTab(const wchar_t *caption, s32 id = -1) override;
|
||||
|
||||
//! Adds an existing tab
|
||||
s32 addTab(IGUITab *tab) override;
|
||||
|
||||
//! Insert the tab at the given index
|
||||
IGUITab *insertTab(s32 idx, const wchar_t *caption, s32 id = -1) override;
|
||||
|
||||
//! Insert an existing tab
|
||||
/** Note that it will also add the tab as a child of this TabControl.
|
||||
\return Index of added tab (should be same as the one passed) or -1 for failure*/
|
||||
s32 insertTab(s32 idx, IGUITab *tab, bool serializationMode) override;
|
||||
|
||||
//! Removes a tab from the tabcontrol
|
||||
void removeTab(s32 idx) override;
|
||||
|
||||
//! Clears the tabcontrol removing all tabs
|
||||
void clear() override;
|
||||
|
||||
//! Returns amount of tabs in the tabcontrol
|
||||
s32 getTabCount() const override;
|
||||
|
||||
//! Returns a tab based on zero based index
|
||||
IGUITab *getTab(s32 idx) const override;
|
||||
|
||||
//! Brings a tab to front.
|
||||
bool setActiveTab(s32 idx) override;
|
||||
|
||||
//! Brings a tab to front.
|
||||
bool setActiveTab(IGUITab *tab) override;
|
||||
|
||||
//! For given given tab find it's zero-based index (or -1 for not found)
|
||||
s32 getTabIndex(const IGUIElement *tab) const override;
|
||||
|
||||
//! Returns which tab is currently active
|
||||
s32 getActiveTab() const override;
|
||||
|
||||
//! get the the id of the tab at the given absolute coordinates
|
||||
s32 getTabAt(s32 xpos, s32 ypos) const override;
|
||||
|
||||
//! called if an event happened.
|
||||
bool OnEvent(const SEvent &event) override;
|
||||
|
||||
//! draws the element and its children
|
||||
void draw() override;
|
||||
|
||||
//! Removes a child.
|
||||
void removeChild(IGUIElement *child) override;
|
||||
|
||||
//! Set the height of the tabs
|
||||
void setTabHeight(s32 height) override;
|
||||
|
||||
//! Get the height of the tabs
|
||||
s32 getTabHeight() const override;
|
||||
|
||||
//! set the maximal width of a tab. Per default width is 0 which means "no width restriction".
|
||||
void setTabMaxWidth(s32 width) override;
|
||||
|
||||
//! get the maximal width of a tab
|
||||
s32 getTabMaxWidth() const override;
|
||||
|
||||
//! Set the alignment of the tabs
|
||||
//! note: EGUIA_CENTER is not an option
|
||||
void setTabVerticalAlignment(gui::EGUI_ALIGNMENT alignment) override;
|
||||
|
||||
//! Get the alignment of the tabs
|
||||
gui::EGUI_ALIGNMENT getTabVerticalAlignment() const override;
|
||||
|
||||
//! Set the extra width added to tabs on each side of the text
|
||||
void setTabExtraWidth(s32 extraWidth) override;
|
||||
|
||||
//! Get the extra width added to tabs on each side of the text
|
||||
s32 getTabExtraWidth() const override;
|
||||
|
||||
//! Update the position of the element, decides scroll button status
|
||||
void updateAbsolutePosition() override;
|
||||
|
||||
private:
|
||||
void scrollLeft();
|
||||
void scrollRight();
|
||||
//! Indicates whether the tabs overflow in X direction
|
||||
bool needScrollControl(s32 startIndex = 0, bool withScrollControl = false, s32 *pos_rightmost = nullptr);
|
||||
//! Left index calculation based on the selected tab
|
||||
s32 calculateScrollIndexFromActive();
|
||||
s32 calcTabWidth(IGUIFont *font, const wchar_t *text) const;
|
||||
core::rect<s32> calcTabPos();
|
||||
void setVisibleTab(s32 idx);
|
||||
void removeTabButNotChild(s32 idx);
|
||||
|
||||
void recalculateScrollButtonPlacement();
|
||||
void recalculateScrollBar();
|
||||
void refreshSprites();
|
||||
|
||||
core::array<IGUITab *> Tabs;
|
||||
s32 ActiveTabIndex;
|
||||
bool Border;
|
||||
bool FillBackground;
|
||||
bool ScrollControl;
|
||||
s32 TabHeight;
|
||||
gui::EGUI_ALIGNMENT VerticalAlignment;
|
||||
IGUIButton *UpButton;
|
||||
IGUIButton *DownButton;
|
||||
s32 TabMaxWidth;
|
||||
s32 CurrentScrollTabIndex;
|
||||
s32 TabExtraWidth;
|
||||
};
|
||||
|
||||
} // end namespace gui
|
||||
} // end namespace irr
|
||||
373
src/CImage.cpp
Normal file
373
src/CImage.cpp
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImage.h"
|
||||
#include "irrString.h"
|
||||
#include "CColorConverter.h"
|
||||
#include "CBlit.h"
|
||||
#include "os.h"
|
||||
#include "SoftwareDriver2_helper.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! Constructor from raw data
|
||||
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32> &size, void *data,
|
||||
bool ownForeignMemory, bool deleteMemory) :
|
||||
IImage(format, size, deleteMemory)
|
||||
{
|
||||
if (ownForeignMemory) {
|
||||
Data = (u8 *)data;
|
||||
} else {
|
||||
const u32 dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height);
|
||||
const u32 allocSize = align_next(dataSize, 16);
|
||||
|
||||
// allocate as u32 to ensure enough alignment when casted
|
||||
Data = reinterpret_cast<u8 *>(new u32[allocSize / 4]);
|
||||
memcpy(Data, data, dataSize);
|
||||
DeleteMemory = true;
|
||||
}
|
||||
}
|
||||
|
||||
//! Constructor of empty image
|
||||
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32> &size) :
|
||||
IImage(format, size, true)
|
||||
{
|
||||
const u32 dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height);
|
||||
const u32 allocSize = align_next(dataSize, 16);
|
||||
|
||||
Data = reinterpret_cast<u8 *>(new u32[allocSize / 4]);
|
||||
DeleteMemory = true;
|
||||
}
|
||||
|
||||
//! sets a pixel
|
||||
void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend)
|
||||
{
|
||||
if (x >= Size.Width || y >= Size.Height)
|
||||
return;
|
||||
|
||||
switch (Format) {
|
||||
case ECF_A1R5G5B5: {
|
||||
u16 *dest = (u16 *)(Data + (y * Pitch) + (x << 1));
|
||||
*dest = video::A8R8G8B8toA1R5G5B5(color.color);
|
||||
} break;
|
||||
|
||||
case ECF_R5G6B5: {
|
||||
u16 *dest = (u16 *)(Data + (y * Pitch) + (x << 1));
|
||||
*dest = video::A8R8G8B8toR5G6B5(color.color);
|
||||
} break;
|
||||
|
||||
case ECF_R8G8B8: {
|
||||
u8 *dest = Data + (y * Pitch) + (x * 3);
|
||||
dest[0] = (u8)color.getRed();
|
||||
dest[1] = (u8)color.getGreen();
|
||||
dest[2] = (u8)color.getBlue();
|
||||
} break;
|
||||
|
||||
case ECF_A8R8G8B8: {
|
||||
u32 *dest = (u32 *)(Data + (y * Pitch) + (x << 2));
|
||||
*dest = blend ? PixelBlend32(*dest, color.color) : color.color;
|
||||
} break;
|
||||
|
||||
case ECF_UNKNOWN:
|
||||
os::Printer::log("IImage::setPixel unknown format.", ELL_WARNING);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! returns a pixel
|
||||
SColor CImage::getPixel(u32 x, u32 y) const
|
||||
{
|
||||
if (x >= Size.Width || y >= Size.Height)
|
||||
return SColor(0);
|
||||
|
||||
switch (Format) {
|
||||
case ECF_A1R5G5B5:
|
||||
return A1R5G5B5toA8R8G8B8(((u16 *)Data)[y * Size.Width + x]);
|
||||
case ECF_R5G6B5:
|
||||
return R5G6B5toA8R8G8B8(((u16 *)Data)[y * Size.Width + x]);
|
||||
case ECF_A8R8G8B8:
|
||||
return ((u32 *)Data)[y * Size.Width + x];
|
||||
case ECF_R8G8B8: {
|
||||
u8 *p = Data + (y * 3) * Size.Width + (x * 3);
|
||||
return SColor(255, p[0], p[1], p[2]);
|
||||
}
|
||||
|
||||
case ECF_UNKNOWN:
|
||||
os::Printer::log("IImage::getPixel unknown format.", ELL_WARNING);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SColor(0);
|
||||
}
|
||||
|
||||
//! copies this surface into another at given position
|
||||
void CImage::copyTo(IImage *target, const core::position2d<s32> &pos)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Blit(BLITTER_TEXTURE, target, 0, &pos, this, 0, 0) && target && pos.X == 0 && pos.Y == 0 &&
|
||||
CColorConverter::canConvertFormat(Format, target->getColorFormat())) {
|
||||
// No fast blitting, but copyToScaling uses other color conversions and might work
|
||||
irr::core::dimension2du dim(target->getDimension());
|
||||
copyToScaling(target->getData(), dim.Width, dim.Height, target->getColorFormat(), target->getPitch());
|
||||
}
|
||||
}
|
||||
|
||||
//! copies this surface partially into another at given position
|
||||
void CImage::copyTo(IImage *target, const core::position2d<s32> &pos, const core::rect<s32> &sourceRect, const core::rect<s32> *clipRect)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
Blit(BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0);
|
||||
}
|
||||
|
||||
//! copies this surface into another, using the alpha mask, a cliprect and a color to add with
|
||||
void CImage::copyToWithAlpha(IImage *target, const core::position2d<s32> &pos, const core::rect<s32> &sourceRect, const SColor &color, const core::rect<s32> *clipRect, bool combineAlpha)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyToWithAlpha method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
eBlitter op = combineAlpha ? BLITTER_TEXTURE_COMBINE_ALPHA : color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND
|
||||
: BLITTER_TEXTURE_ALPHA_COLOR_BLEND;
|
||||
Blit(op, target, clipRect, &pos, this, &sourceRect, color.color);
|
||||
}
|
||||
|
||||
//! copies this surface into another, if it has the exact same size and format.
|
||||
bool CImage::copyToNoScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch) const
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyToNoScaling method doesn't work with compressed images.", ELL_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!target || !width || !height || !Size.Width || !Size.Height)
|
||||
return false;
|
||||
|
||||
const u32 bpp = getBitsPerPixelFromFormat(format) / 8;
|
||||
if (0 == pitch)
|
||||
pitch = width * bpp;
|
||||
|
||||
if (!(Format == format && Size.Width == width && Size.Height == height))
|
||||
return false;
|
||||
|
||||
if (pitch == Pitch) {
|
||||
memcpy(target, Data, (size_t)height * pitch);
|
||||
} else {
|
||||
u8 *tgtpos = (u8 *)target;
|
||||
u8 *srcpos = Data;
|
||||
const u32 bwidth = width * bpp;
|
||||
const u32 rest = pitch - bwidth;
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
// copy scanline
|
||||
memcpy(tgtpos, srcpos, bwidth);
|
||||
// clear pitch
|
||||
memset(tgtpos + bwidth, 0, rest);
|
||||
tgtpos += pitch;
|
||||
srcpos += Pitch;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! copies this surface into another, scaling it to the target image size
|
||||
// note: this is very very slow.
|
||||
void CImage::copyToScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target || !width || !height || !Size.Width || !Size.Height)
|
||||
return;
|
||||
|
||||
const u32 bpp = getBitsPerPixelFromFormat(format) / 8;
|
||||
if (0 == pitch)
|
||||
pitch = width * bpp;
|
||||
|
||||
if (copyToNoScaling(target, width, height, format, pitch))
|
||||
return;
|
||||
|
||||
// NOTE: Scaling is coded to keep the border pixels intact.
|
||||
// Alternatively we could for example work with first pixel being taken at half step-size.
|
||||
// Then we have one more step here and it would be:
|
||||
// sourceXStep = (f32)(Size.Width-1) / (f32)(width);
|
||||
// And sx would start at 0.5f + sourceXStep / 2.f;
|
||||
// Similar for y.
|
||||
// As scaling is done without any antialiasing it doesn't matter too much which outermost pixels we use and keeping
|
||||
// border pixels intact is probably mostly better (with AA the other solution would be more correct).
|
||||
// This is however unnecessary (and unexpected) for scaling to integer multiples, so don't do it there.
|
||||
f32 sourceXStep, sourceYStep;
|
||||
f32 sourceXStart = 0.f, sourceYStart = 0.f;
|
||||
if (width % Size.Width == 0)
|
||||
sourceXStep = (f32)(Size.Width) / (f32)(width);
|
||||
else {
|
||||
sourceXStep = width > 1 ? (f32)(Size.Width - 1) / (f32)(width - 1) : 0.f;
|
||||
sourceXStart = 0.5f; // for rounding to nearest pixel
|
||||
}
|
||||
if (height % Size.Height == 0)
|
||||
sourceYStep = (f32)(Size.Height) / (f32)(height);
|
||||
else {
|
||||
sourceYStep = height > 1 ? (f32)(Size.Height - 1) / (f32)(height - 1) : 0.f;
|
||||
sourceYStart = 0.5f; // for rounding to nearest pixel
|
||||
}
|
||||
|
||||
s32 yval = 0, syval = 0;
|
||||
f32 sy = sourceYStart;
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
f32 sx = sourceXStart;
|
||||
for (u32 x = 0; x < width; ++x) {
|
||||
CColorConverter::convert_viaFormat(Data + syval + ((s32)sx) * BytesPerPixel, Format, 1, ((u8 *)target) + yval + (x * bpp), format);
|
||||
sx += sourceXStep;
|
||||
}
|
||||
sy += sourceYStep;
|
||||
syval = (s32)(sy)*Pitch;
|
||||
yval += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
//! copies this surface into another, scaling it to the target image size
|
||||
// note: this is very very slow.
|
||||
void CImage::copyToScaling(IImage *target)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
const core::dimension2d<u32> &targetSize = target->getDimension();
|
||||
|
||||
if (targetSize == Size) {
|
||||
copyTo(target);
|
||||
return;
|
||||
}
|
||||
|
||||
copyToScaling(target->getData(), targetSize.Width, targetSize.Height, target->getColorFormat());
|
||||
}
|
||||
|
||||
//! copies this surface into another, scaling it to fit it.
|
||||
void CImage::copyToScalingBoxFilter(IImage *target, s32 bias, bool blend)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::copyToScalingBoxFilter method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
const core::dimension2d<u32> destSize = target->getDimension();
|
||||
|
||||
const f32 sourceXStep = (f32)Size.Width / (f32)destSize.Width;
|
||||
const f32 sourceYStep = (f32)Size.Height / (f32)destSize.Height;
|
||||
|
||||
s32 fx = core::ceil32(sourceXStep);
|
||||
s32 fy = core::ceil32(sourceYStep);
|
||||
f32 sx;
|
||||
f32 sy;
|
||||
|
||||
sy = 0.f;
|
||||
for (u32 y = 0; y != destSize.Height; ++y) {
|
||||
sx = 0.f;
|
||||
for (u32 x = 0; x != destSize.Width; ++x) {
|
||||
target->setPixel(x, y,
|
||||
getPixelBox(core::floor32(sx), core::floor32(sy), fx, fy, bias), blend);
|
||||
sx += sourceXStep;
|
||||
}
|
||||
sy += sourceYStep;
|
||||
}
|
||||
}
|
||||
|
||||
//! fills the surface with given color
|
||||
void CImage::fill(const SColor &color)
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::fill method doesn't work with compressed images.", ELL_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 c;
|
||||
|
||||
switch (Format) {
|
||||
case ECF_A1R5G5B5:
|
||||
c = color.toA1R5G5B5();
|
||||
c |= c << 16;
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
c = video::A8R8G8B8toR5G6B5(color.color);
|
||||
c |= c << 16;
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
c = color.color;
|
||||
break;
|
||||
case ECF_R8G8B8: {
|
||||
u8 rgb[3];
|
||||
CColorConverter::convert_A8R8G8B8toR8G8B8(&color, 1, rgb);
|
||||
const u32 size = getImageDataSizeInBytes();
|
||||
for (u32 i = 0; i < size; i += 3) {
|
||||
memcpy(Data + i, rgb, 3);
|
||||
}
|
||||
return;
|
||||
} break;
|
||||
default:
|
||||
// TODO: Handle other formats
|
||||
return;
|
||||
}
|
||||
memset32(Data, c, getImageDataSizeInBytes());
|
||||
}
|
||||
|
||||
//! get a filtered pixel
|
||||
inline SColor CImage::getPixelBox(s32 x, s32 y, s32 fx, s32 fy, s32 bias) const
|
||||
{
|
||||
if (IImage::isCompressedFormat(Format)) {
|
||||
os::Printer::log("IImage::getPixelBox method doesn't work with compressed images.", ELL_WARNING);
|
||||
return SColor(0);
|
||||
}
|
||||
|
||||
SColor c;
|
||||
s32 a = 0, r = 0, g = 0, b = 0;
|
||||
|
||||
for (s32 dx = 0; dx != fx; ++dx) {
|
||||
for (s32 dy = 0; dy != fy; ++dy) {
|
||||
c = getPixel(core::s32_min(x + dx, Size.Width - 1),
|
||||
core::s32_min(y + dy, Size.Height - 1));
|
||||
|
||||
a += c.getAlpha();
|
||||
r += c.getRed();
|
||||
g += c.getGreen();
|
||||
b += c.getBlue();
|
||||
}
|
||||
}
|
||||
|
||||
s32 sdiv = s32_log2_s32(fx * fy);
|
||||
|
||||
a = core::s32_clamp((a >> sdiv) + bias, 0, 255);
|
||||
r = core::s32_clamp((r >> sdiv) + bias, 0, 255);
|
||||
g = core::s32_clamp((g >> sdiv) + bias, 0, 255);
|
||||
b = core::s32_clamp((b >> sdiv) + bias, 0, 255);
|
||||
|
||||
c.set(a, r, g, b);
|
||||
return c;
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
74
src/CImage.h
Normal file
74
src/CImage.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImage.h"
|
||||
#include "rect.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! check sanity of image dimensions to prevent issues later, for use by CImageLoaders
|
||||
inline bool checkImageDimensions(u32 width, u32 height)
|
||||
{
|
||||
// 4 * 23000 * 23000 is just under S32_MAX
|
||||
return width <= 23000 && height <= 23000;
|
||||
}
|
||||
|
||||
//! IImage implementation with a lot of special image operations for
|
||||
//! 16 bit A1R5G5B5/32 Bit A8R8G8B8 images, which are used by the SoftwareDevice.
|
||||
class CImage : public IImage
|
||||
{
|
||||
public:
|
||||
//! constructor from raw image data
|
||||
/** \param useForeignMemory: If true, the image will use the data pointer
|
||||
directly and own it from now on, which means it will also try to delete [] the
|
||||
data when the image will be destructed. If false, the memory will by copied. */
|
||||
CImage(ECOLOR_FORMAT format, const core::dimension2d<u32> &size, void *data,
|
||||
bool ownForeignMemory = true, bool deleteMemory = true);
|
||||
|
||||
//! constructor for empty image
|
||||
CImage(ECOLOR_FORMAT format, const core::dimension2d<u32> &size);
|
||||
|
||||
//! returns a pixel
|
||||
SColor getPixel(u32 x, u32 y) const override;
|
||||
|
||||
//! sets a pixel
|
||||
void setPixel(u32 x, u32 y, const SColor &color, bool blend = false) override;
|
||||
|
||||
//! copies this surface into another, if it has the exact same size and format.
|
||||
bool copyToNoScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch = 0) const override;
|
||||
|
||||
//! copies this surface into another, scaling it to fit.
|
||||
void copyToScaling(void *target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch = 0) override;
|
||||
|
||||
//! copies this surface into another, scaling it to fit.
|
||||
void copyToScaling(IImage *target) override;
|
||||
|
||||
//! copies this surface into another
|
||||
void copyTo(IImage *target, const core::position2d<s32> &pos = core::position2d<s32>(0, 0)) override;
|
||||
|
||||
//! copies this surface into another
|
||||
void copyTo(IImage *target, const core::position2d<s32> &pos, const core::rect<s32> &sourceRect, const core::rect<s32> *clipRect = 0) override;
|
||||
|
||||
//! copies this surface into another, using the alpha mask, an cliprect and a color to add with
|
||||
virtual void copyToWithAlpha(IImage *target, const core::position2d<s32> &pos,
|
||||
const core::rect<s32> &sourceRect, const SColor &color,
|
||||
const core::rect<s32> *clipRect = 0, bool combineAlpha = false) override;
|
||||
|
||||
//! copies this surface into another, scaling it to fit, applying a box filter
|
||||
void copyToScalingBoxFilter(IImage *target, s32 bias = 0, bool blend = false) override;
|
||||
|
||||
//! fills the surface with given color
|
||||
void fill(const SColor &color) override;
|
||||
|
||||
private:
|
||||
inline SColor getPixelBox(s32 x, s32 y, s32 fx, s32 fy, s32 bias) const;
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
402
src/CImageLoaderBMP.cpp
Normal file
402
src/CImageLoaderBMP.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageLoaderBMP.h"
|
||||
|
||||
#include "IReadFile.h"
|
||||
#include "SColor.h"
|
||||
#include "CColorConverter.h"
|
||||
#include "CImage.h"
|
||||
#include "os.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CImageLoaderBMP::CImageLoaderBMP()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CImageLoaderBMP");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool CImageLoaderBMP::isALoadableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "bmp");
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile *file) const
|
||||
{
|
||||
u16 headerID;
|
||||
file->read(&headerID, sizeof(u16));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
headerID = os::Byteswap::byteswap(headerID);
|
||||
#endif
|
||||
return headerID == 0x4d42;
|
||||
}
|
||||
|
||||
// UB-safe overflow check
|
||||
static inline bool overflowCheck(const void *base, size_t offset, const void *end)
|
||||
{
|
||||
auto baseI = reinterpret_cast<uintptr_t>(base),
|
||||
endI = reinterpret_cast<uintptr_t>(end);
|
||||
return baseI > endI || offset >= (endI - baseI);
|
||||
}
|
||||
// check whether &p[0] to &p[_off - 1] can be accessed
|
||||
#define CHECKP(_off) \
|
||||
if ((_off) < 0 || overflowCheck(p, _off, pEnd)) \
|
||||
goto exit
|
||||
// same for d
|
||||
#define CHECKD(_off) \
|
||||
if ((_off) < 0 || overflowCheck(d, _off, destEnd)) \
|
||||
goto exit
|
||||
|
||||
void CImageLoaderBMP::decompress8BitRLE(u8 *&bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
||||
{
|
||||
u8 *p = bmpData;
|
||||
const u8 *pEnd = bmpData + size;
|
||||
u8 *newBmp = new u8[(width + pitch) * height];
|
||||
u8 *d = newBmp;
|
||||
const u8 *destEnd = newBmp + (width + pitch) * height;
|
||||
s32 line = 0;
|
||||
|
||||
while (p < pEnd && d < destEnd) {
|
||||
if (*p == 0) {
|
||||
++p;
|
||||
CHECKP(1);
|
||||
|
||||
switch (*p) {
|
||||
case 0: // end of line
|
||||
++p;
|
||||
++line;
|
||||
d = newBmp + (line * (width + pitch));
|
||||
break;
|
||||
case 1: // end of bmp
|
||||
goto exit;
|
||||
case 2:
|
||||
++p;
|
||||
CHECKP(2);
|
||||
d += (u8)*p;
|
||||
++p; // delta
|
||||
d += ((u8)*p) * (width + pitch);
|
||||
++p;
|
||||
break;
|
||||
default: {
|
||||
// absolute mode
|
||||
s32 count = (u8)*p;
|
||||
++p;
|
||||
s32 readAdditional = ((2 - (count % 2)) % 2);
|
||||
|
||||
CHECKP(count);
|
||||
CHECKD(count);
|
||||
for (s32 i = 0; i < count; ++i) {
|
||||
*d = *p;
|
||||
++p;
|
||||
++d;
|
||||
}
|
||||
|
||||
CHECKP(readAdditional);
|
||||
for (s32 i = 0; i < readAdditional; ++i)
|
||||
++p;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s32 count = (u8)*p;
|
||||
++p;
|
||||
CHECKP(1);
|
||||
u8 color = *p;
|
||||
++p;
|
||||
CHECKD(count);
|
||||
for (s32 i = 0; i < count; ++i) {
|
||||
*d = color;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
delete[] bmpData;
|
||||
bmpData = newBmp;
|
||||
}
|
||||
|
||||
// how many bytes will be touched given the current state of decompress4BitRLE
|
||||
static inline u32 shiftedCount(s32 count, s32 shift)
|
||||
{
|
||||
_IRR_DEBUG_BREAK_IF(count < 0)
|
||||
u32 ret = count / 2;
|
||||
if (shift == 0 || count % 2 == 1)
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CImageLoaderBMP::decompress4BitRLE(u8 *&bmpData, s32 size, s32 width, s32 height, s32 pitch) const
|
||||
{
|
||||
const s32 lineWidth = (width + 1) / 2 + pitch;
|
||||
u8 *p = bmpData;
|
||||
const u8 *pEnd = bmpData + size;
|
||||
u8 *newBmp = new u8[lineWidth * height];
|
||||
u8 *d = newBmp;
|
||||
const u8 *destEnd = newBmp + lineWidth * height;
|
||||
s32 line = 0;
|
||||
s32 shift = 4;
|
||||
|
||||
while (p < pEnd && d < destEnd) {
|
||||
if (*p == 0) {
|
||||
++p;
|
||||
CHECKP(1);
|
||||
|
||||
switch (*p) {
|
||||
case 0: // end of line
|
||||
++p;
|
||||
++line;
|
||||
d = newBmp + (line * lineWidth);
|
||||
shift = 4;
|
||||
break;
|
||||
case 1: // end of bmp
|
||||
goto exit;
|
||||
case 2: {
|
||||
++p;
|
||||
CHECKP(2);
|
||||
s32 x = (u8)*p;
|
||||
++p;
|
||||
s32 y = (u8)*p;
|
||||
++p;
|
||||
d += x / 2 + y * lineWidth;
|
||||
shift = x % 2 == 0 ? 4 : 0;
|
||||
} break;
|
||||
default: {
|
||||
// absolute mode
|
||||
s32 count = (u8)*p;
|
||||
++p;
|
||||
s32 readAdditional = ((2 - ((count) % 2)) % 2);
|
||||
s32 readShift = 4;
|
||||
|
||||
CHECKP(shiftedCount(count, readShift));
|
||||
CHECKD(shiftedCount(count, shift));
|
||||
for (s32 i = 0; i < count; ++i) {
|
||||
s32 color = (((u8)*p) >> readShift) & 0x0f;
|
||||
readShift -= 4;
|
||||
if (readShift < 0) {
|
||||
++*p; // <- bug?
|
||||
readShift = 4;
|
||||
}
|
||||
|
||||
u8 mask = 0x0f << shift;
|
||||
*d = (*d & (~mask)) | ((color << shift) & mask);
|
||||
|
||||
shift -= 4;
|
||||
if (shift < 0) {
|
||||
shift = 4;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
|
||||
CHECKP(readAdditional);
|
||||
for (s32 i = 0; i < readAdditional; ++i)
|
||||
++p;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s32 count = (u8)*p;
|
||||
++p;
|
||||
CHECKP(1);
|
||||
s32 color1 = (u8)*p;
|
||||
color1 = color1 & 0x0f;
|
||||
s32 color2 = (u8)*p;
|
||||
color2 = (color2 >> 4) & 0x0f;
|
||||
++p;
|
||||
|
||||
CHECKD(shiftedCount(count, shift));
|
||||
for (s32 i = 0; i < count; ++i) {
|
||||
u8 mask = 0x0f << shift;
|
||||
u8 toSet = (shift == 0 ? color1 : color2) << shift;
|
||||
*d = (*d & (~mask)) | (toSet & mask);
|
||||
|
||||
shift -= 4;
|
||||
if (shift < 0) {
|
||||
shift = 4;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
delete[] bmpData;
|
||||
bmpData = newBmp;
|
||||
}
|
||||
|
||||
#undef CHECKOFF
|
||||
#undef CHECKP
|
||||
#undef CHECKD
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *CImageLoaderBMP::loadImage(io::IReadFile *file) const
|
||||
{
|
||||
SBMPHeader header;
|
||||
|
||||
file->read(&header, sizeof(header));
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.Id = os::Byteswap::byteswap(header.Id);
|
||||
header.FileSize = os::Byteswap::byteswap(header.FileSize);
|
||||
header.BitmapDataOffset = os::Byteswap::byteswap(header.BitmapDataOffset);
|
||||
header.BitmapHeaderSize = os::Byteswap::byteswap(header.BitmapHeaderSize);
|
||||
header.Width = os::Byteswap::byteswap(header.Width);
|
||||
header.Height = os::Byteswap::byteswap(header.Height);
|
||||
header.Planes = os::Byteswap::byteswap(header.Planes);
|
||||
header.BPP = os::Byteswap::byteswap(header.BPP);
|
||||
header.Compression = os::Byteswap::byteswap(header.Compression);
|
||||
header.BitmapDataSize = os::Byteswap::byteswap(header.BitmapDataSize);
|
||||
header.PixelPerMeterX = os::Byteswap::byteswap(header.PixelPerMeterX);
|
||||
header.PixelPerMeterY = os::Byteswap::byteswap(header.PixelPerMeterY);
|
||||
header.Colors = os::Byteswap::byteswap(header.Colors);
|
||||
header.ImportantColors = os::Byteswap::byteswap(header.ImportantColors);
|
||||
#endif
|
||||
|
||||
s32 pitch = 0;
|
||||
|
||||
//! return if the header is false
|
||||
|
||||
if (header.Id != 0x4d42)
|
||||
return 0;
|
||||
|
||||
if (header.Compression > 2) { // we'll only handle RLE-Compression
|
||||
os::Printer::log("Compression mode not supported.", ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height)) {
|
||||
os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// adjust bitmap data size to dword boundary
|
||||
header.BitmapDataSize += (4 - (header.BitmapDataSize % 4)) % 4;
|
||||
|
||||
// read palette
|
||||
|
||||
long pos = file->getPos();
|
||||
constexpr s32 paletteAllocSize = 256;
|
||||
s32 paletteSize = (header.BitmapDataOffset - pos) / 4;
|
||||
paletteSize = core::clamp(paletteSize, 0, paletteAllocSize);
|
||||
|
||||
s32 *paletteData = 0;
|
||||
if (paletteSize) {
|
||||
// always allocate an 8-bit palette to ensure enough space
|
||||
paletteData = new s32[paletteAllocSize];
|
||||
memset(paletteData, 0, paletteAllocSize * sizeof(s32));
|
||||
file->read(paletteData, paletteSize * sizeof(s32));
|
||||
#ifdef __BIG_ENDIAN__
|
||||
for (s32 i = 0; i < paletteSize; ++i)
|
||||
paletteData[i] = os::Byteswap::byteswap(paletteData[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// read image data
|
||||
|
||||
if (!header.BitmapDataSize) {
|
||||
// okay, lets guess the size
|
||||
// some tools simply don't set it
|
||||
header.BitmapDataSize = static_cast<u32>(file->getSize()) - header.BitmapDataOffset;
|
||||
}
|
||||
|
||||
file->seek(header.BitmapDataOffset);
|
||||
|
||||
s32 widthInBytes;
|
||||
{
|
||||
f32 t = (header.Width) * (header.BPP / 8.0f);
|
||||
widthInBytes = (s32)t;
|
||||
t -= widthInBytes;
|
||||
if (t != 0.0f)
|
||||
++widthInBytes;
|
||||
}
|
||||
|
||||
const s32 lineSize = widthInBytes + ((4 - (widthInBytes % 4))) % 4;
|
||||
pitch = lineSize - widthInBytes;
|
||||
|
||||
u8 *bmpData = new u8[header.BitmapDataSize];
|
||||
file->read(bmpData, header.BitmapDataSize);
|
||||
|
||||
// decompress data if needed
|
||||
switch (header.Compression) {
|
||||
case 1: // 8 bit rle
|
||||
decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
|
||||
header.BitmapDataSize = (header.Width + pitch) * header.Height;
|
||||
break;
|
||||
case 2: // 4 bit rle
|
||||
decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch);
|
||||
header.BitmapDataSize = ((header.Width + 1) / 2 + pitch) * header.Height;
|
||||
break;
|
||||
}
|
||||
|
||||
if (header.BitmapDataSize < lineSize * header.Height) {
|
||||
os::Printer::log("Bitmap data is cut off.", ELL_ERROR);
|
||||
|
||||
delete[] paletteData;
|
||||
delete[] bmpData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create surface
|
||||
core::dimension2d<u32> dim;
|
||||
dim.Width = header.Width;
|
||||
dim.Height = header.Height;
|
||||
|
||||
IImage *image = 0;
|
||||
switch (header.BPP) {
|
||||
case 1:
|
||||
image = new CImage(ECF_A1R5G5B5, dim);
|
||||
if (image)
|
||||
CColorConverter::convert1BitTo16Bit(bmpData, (s16 *)image->getData(), header.Width, header.Height, pitch, true);
|
||||
break;
|
||||
case 4:
|
||||
image = new CImage(ECF_A1R5G5B5, dim);
|
||||
if (image)
|
||||
CColorConverter::convert4BitTo16Bit(bmpData, (s16 *)image->getData(), header.Width, header.Height, paletteData, pitch, true);
|
||||
break;
|
||||
case 8:
|
||||
image = new CImage(ECF_A1R5G5B5, dim);
|
||||
if (image)
|
||||
CColorConverter::convert8BitTo16Bit(bmpData, (s16 *)image->getData(), header.Width, header.Height, paletteData, pitch, true);
|
||||
break;
|
||||
case 16:
|
||||
image = new CImage(ECF_A1R5G5B5, dim);
|
||||
if (image)
|
||||
CColorConverter::convert16BitTo16Bit((s16 *)bmpData, (s16 *)image->getData(), header.Width, header.Height, pitch, true);
|
||||
break;
|
||||
case 24:
|
||||
image = new CImage(ECF_R8G8B8, dim);
|
||||
if (image)
|
||||
CColorConverter::convert24BitTo24Bit(bmpData, (u8 *)image->getData(), header.Width, header.Height, pitch, true, true);
|
||||
break;
|
||||
case 32: // thx to Reinhard Ostermeier
|
||||
image = new CImage(ECF_A8R8G8B8, dim);
|
||||
if (image)
|
||||
CColorConverter::convert32BitTo32Bit((s32 *)bmpData, (s32 *)image->getData(), header.Width, header.Height, pitch, true);
|
||||
break;
|
||||
};
|
||||
|
||||
// clean up
|
||||
|
||||
delete[] paletteData;
|
||||
delete[] bmpData;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//! creates a loader which is able to load windows bitmaps
|
||||
IImageLoader *createImageLoaderBMP()
|
||||
{
|
||||
return new CImageLoaderBMP;
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
81
src/CImageLoaderBMP.h
Normal file
81
src/CImageLoaderBMP.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageLoader.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
// byte-align structures
|
||||
#include "irrpack.h"
|
||||
|
||||
struct SBMPHeader
|
||||
{
|
||||
u16 Id; // BM - Windows 3.1x, 95, NT, 98, 2000, ME, XP
|
||||
// BA - OS/2 Bitmap Array
|
||||
// CI - OS/2 Color Icon
|
||||
// CP - OS/2 Color Pointer
|
||||
// IC - OS/2 Icon
|
||||
// PT - OS/2 Pointer
|
||||
u32 FileSize;
|
||||
u32 Reserved;
|
||||
u32 BitmapDataOffset;
|
||||
u32 BitmapHeaderSize; // should be 28h for windows bitmaps or
|
||||
// 0Ch for OS/2 1.x or F0h for OS/2 2.x
|
||||
u32 Width;
|
||||
u32 Height;
|
||||
u16 Planes;
|
||||
u16 BPP; // 1: Monochrome bitmap
|
||||
// 4: 16 color bitmap
|
||||
// 8: 256 color bitmap
|
||||
// 16: 16bit (high color) bitmap
|
||||
// 24: 24bit (true color) bitmap
|
||||
// 32: 32bit (true color) bitmap
|
||||
|
||||
u32 Compression; // 0: none (Also identified by BI_RGB)
|
||||
// 1: RLE 8-bit / pixel (Also identified by BI_RLE4)
|
||||
// 2: RLE 4-bit / pixel (Also identified by BI_RLE8)
|
||||
// 3: Bitfields (Also identified by BI_BITFIELDS)
|
||||
|
||||
u32 BitmapDataSize; // Size of the bitmap data in bytes. This number must be rounded to the next 4 byte boundary.
|
||||
u32 PixelPerMeterX;
|
||||
u32 PixelPerMeterY;
|
||||
u32 Colors;
|
||||
u32 ImportantColors;
|
||||
} PACK_STRUCT;
|
||||
|
||||
// Default alignment
|
||||
#include "irrunpack.h"
|
||||
|
||||
/*!
|
||||
Surface Loader for Windows bitmaps
|
||||
*/
|
||||
class CImageLoaderBMP : public IImageLoader
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CImageLoaderBMP();
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool isALoadableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool isALoadableFileFormat(io::IReadFile *file) const override;
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *loadImage(io::IReadFile *file) const override;
|
||||
|
||||
private:
|
||||
void decompress8BitRLE(u8 *&BmpData, s32 size, s32 width, s32 height, s32 pitch) const;
|
||||
|
||||
void decompress4BitRLE(u8 *&BmpData, s32 size, s32 width, s32 height, s32 pitch) const;
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
263
src/CImageLoaderJPG.cpp
Normal file
263
src/CImageLoaderJPG.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageLoaderJPG.h"
|
||||
|
||||
#include "IReadFile.h"
|
||||
#include "CImage.h"
|
||||
#include "os.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! constructor
|
||||
CImageLoaderJPG::CImageLoaderJPG()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CImageLoaderJPG");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! destructor
|
||||
CImageLoaderJPG::~CImageLoaderJPG()
|
||||
{
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool CImageLoaderJPG::isALoadableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "jpg", "jpeg");
|
||||
}
|
||||
|
||||
// struct for handling jpeg errors
|
||||
struct irr_jpeg_error_mgr
|
||||
{
|
||||
// public jpeg error fields
|
||||
struct jpeg_error_mgr pub;
|
||||
|
||||
// for longjmp, to return to caller on a fatal error
|
||||
jmp_buf setjmp_buffer;
|
||||
|
||||
// for having access to the filename when printing the error messages
|
||||
core::stringc *filename;
|
||||
};
|
||||
|
||||
void CImageLoaderJPG::init_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
|
||||
boolean CImageLoaderJPG::fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
// DO NOTHING
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CImageLoaderJPG::skip_input_data(j_decompress_ptr cinfo, long count)
|
||||
{
|
||||
jpeg_source_mgr *src = cinfo->src;
|
||||
if (count > 0) {
|
||||
src->bytes_in_buffer -= count;
|
||||
src->next_input_byte += count;
|
||||
}
|
||||
}
|
||||
|
||||
void CImageLoaderJPG::term_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
|
||||
void CImageLoaderJPG::error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
// unfortunately we need to use a goto rather than throwing an exception
|
||||
// as gcc crashes under linux crashes when using throw from within
|
||||
// extern c code
|
||||
|
||||
// Always display the message
|
||||
(*cinfo->err->output_message)(cinfo);
|
||||
|
||||
// cinfo->err really points to a irr_error_mgr struct
|
||||
irr_jpeg_error_mgr *myerr = (irr_jpeg_error_mgr *)cinfo->err;
|
||||
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
void CImageLoaderJPG::output_message(j_common_ptr cinfo)
|
||||
{
|
||||
// display the error message.
|
||||
c8 temp1[JMSG_LENGTH_MAX];
|
||||
(*cinfo->err->format_message)(cinfo, temp1);
|
||||
core::stringc errMsg("JPEG FATAL ERROR in ");
|
||||
|
||||
irr_jpeg_error_mgr *myerr = (irr_jpeg_error_mgr *)cinfo->err;
|
||||
errMsg += *myerr->filename;
|
||||
os::Printer::log(errMsg.c_str(), temp1, ELL_ERROR);
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile *file) const
|
||||
{
|
||||
if (!(file && file->seek(0)))
|
||||
return false;
|
||||
unsigned char header[3];
|
||||
size_t headerLen = file->read(header, sizeof(header));
|
||||
return headerLen >= 3 && !memcmp(header, "\xFF\xD8\xFF", 3);
|
||||
}
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *CImageLoaderJPG::loadImage(io::IReadFile *file) const
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
core::stringc filename = file->getFileName();
|
||||
|
||||
u8 **rowPtr = 0;
|
||||
u8 *input = new u8[file->getSize()];
|
||||
file->read(input, file->getSize());
|
||||
|
||||
// allocate and initialize JPEG decompression object
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct irr_jpeg_error_mgr jerr;
|
||||
|
||||
// We have to set up the error handler first, in case the initialization
|
||||
// step fails. (Unlikely, but it could happen if you are out of memory.)
|
||||
// This routine fills in the contents of struct jerr, and returns jerr's
|
||||
// address which we place into the link field in cinfo.
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
cinfo.err->error_exit = error_exit;
|
||||
cinfo.err->output_message = output_message;
|
||||
jerr.filename = &filename;
|
||||
|
||||
// compatibility fudge:
|
||||
// we need to use setjmp/longjmp for error handling as gcc-linux
|
||||
// crashes when throwing within external c code
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
// If we get here, the JPEG code has signaled an error.
|
||||
// We need to clean up the JPEG object and return.
|
||||
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
delete[] input;
|
||||
delete[] rowPtr;
|
||||
|
||||
// return null pointer
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now we can initialize the JPEG decompression object.
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
// specify data source
|
||||
jpeg_source_mgr jsrc;
|
||||
|
||||
// Set up data pointer
|
||||
jsrc.bytes_in_buffer = file->getSize();
|
||||
jsrc.next_input_byte = (JOCTET *)input;
|
||||
cinfo.src = &jsrc;
|
||||
|
||||
jsrc.init_source = init_source;
|
||||
jsrc.fill_input_buffer = fill_input_buffer;
|
||||
jsrc.skip_input_data = skip_input_data;
|
||||
jsrc.resync_to_restart = jpeg_resync_to_restart;
|
||||
jsrc.term_source = term_source;
|
||||
|
||||
// Decodes JPG input from whatever source
|
||||
// Does everything AFTER jpeg_create_decompress
|
||||
// and BEFORE jpeg_destroy_decompress
|
||||
// Caller is responsible for arranging these + setting up cinfo
|
||||
|
||||
// read file parameters with jpeg_read_header()
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
bool useCMYK = false;
|
||||
if (cinfo.jpeg_color_space == JCS_CMYK) {
|
||||
cinfo.out_color_space = JCS_CMYK;
|
||||
cinfo.out_color_components = 4;
|
||||
useCMYK = true;
|
||||
} else {
|
||||
cinfo.out_color_space = JCS_RGB;
|
||||
cinfo.out_color_components = 3;
|
||||
}
|
||||
cinfo.output_gamma = 2.2;
|
||||
cinfo.do_fancy_upsampling = FALSE;
|
||||
|
||||
// reject unreasonable sizes
|
||||
if (!checkImageDimensions(cinfo.image_width, cinfo.image_height))
|
||||
longjmp(jerr.setjmp_buffer, 1);
|
||||
|
||||
// Start decompressor
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
// Get image data
|
||||
u32 rowspan = cinfo.image_width * cinfo.out_color_components;
|
||||
u32 width = cinfo.image_width;
|
||||
u32 height = cinfo.image_height;
|
||||
|
||||
// Allocate memory for buffer
|
||||
u8 *output = new u8[rowspan * height];
|
||||
|
||||
// Here we use the library's state variable cinfo.output_scanline as the
|
||||
// loop counter, so that we don't have to keep track ourselves.
|
||||
// Create array of row pointers for lib
|
||||
rowPtr = new u8 *[height];
|
||||
|
||||
for (u32 i = 0; i < height; i++)
|
||||
rowPtr[i] = &output[i * rowspan];
|
||||
|
||||
u32 rowsRead = 0;
|
||||
|
||||
while (cinfo.output_scanline < cinfo.output_height)
|
||||
rowsRead += jpeg_read_scanlines(&cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead);
|
||||
|
||||
delete[] rowPtr;
|
||||
// Finish decompression
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
// Release JPEG decompression object
|
||||
// This is an important step since it will release a good deal of memory.
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
// convert image
|
||||
IImage *image = 0;
|
||||
if (useCMYK) {
|
||||
image = new CImage(ECF_R8G8B8,
|
||||
core::dimension2d<u32>(width, height));
|
||||
const u32 size = 3 * width * height;
|
||||
u8 *data = (u8 *)image->getData();
|
||||
if (data) {
|
||||
for (u32 i = 0, j = 0; i < size; i += 3, j += 4) {
|
||||
// Also works without K, but has more contrast with K multiplied in
|
||||
// data[i+0] = output[j+2];
|
||||
// data[i+1] = output[j+1];
|
||||
// data[i+2] = output[j+0];
|
||||
data[i + 0] = (char)(output[j + 2] * (output[j + 3] / 255.f));
|
||||
data[i + 1] = (char)(output[j + 1] * (output[j + 3] / 255.f));
|
||||
data[i + 2] = (char)(output[j + 0] * (output[j + 3] / 255.f));
|
||||
}
|
||||
}
|
||||
delete[] output;
|
||||
} else
|
||||
image = new CImage(ECF_R8G8B8,
|
||||
core::dimension2d<u32>(width, height), output);
|
||||
|
||||
delete[] input;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//! creates a loader which is able to load jpeg images
|
||||
IImageLoader *createImageLoaderJPG()
|
||||
{
|
||||
return new CImageLoaderJPG();
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
87
src/CImageLoaderJPG.h
Normal file
87
src/CImageLoaderJPG.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageLoader.h"
|
||||
|
||||
#include <cstdio> // required for jpeglib.h
|
||||
#include <jpeglib.h> // use system lib
|
||||
#include <setjmp.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! Surface Loader for JPG images
|
||||
class CImageLoaderJPG : public IImageLoader
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CImageLoaderJPG();
|
||||
|
||||
//! destructor
|
||||
virtual ~CImageLoaderJPG();
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool isALoadableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool isALoadableFileFormat(io::IReadFile *file) const override;
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *loadImage(io::IReadFile *file) const override;
|
||||
|
||||
private:
|
||||
// several methods used via function pointers by jpeglib
|
||||
|
||||
/* Receives control for a fatal error. Information sufficient to
|
||||
generate the error message has been stored in cinfo->err; call
|
||||
output_message to display it. Control must NOT return to the caller;
|
||||
generally this routine will exit() or longjmp() somewhere.
|
||||
Typically you would override this routine to get rid of the exit()
|
||||
default behavior. Note that if you continue processing, you should
|
||||
clean up the JPEG object with jpeg_abort() or jpeg_destroy().
|
||||
*/
|
||||
static void error_exit(j_common_ptr cinfo);
|
||||
|
||||
/* output error messages via Irrlicht logger. */
|
||||
static void output_message(j_common_ptr cinfo);
|
||||
|
||||
/* Initialize source. This is called by jpeg_read_header() before any
|
||||
data is actually read. Unlike init_destination(), it may leave
|
||||
bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
|
||||
will occur immediately). */
|
||||
static void init_source(j_decompress_ptr cinfo);
|
||||
|
||||
/* This is called whenever bytes_in_buffer has reached zero and more
|
||||
data is wanted. In typical applications, it should read fresh data
|
||||
into the buffer (ignoring the current state of next_input_byte and
|
||||
bytes_in_buffer), reset the pointer & count to the start of the
|
||||
buffer, and return TRUE indicating that the buffer has been reloaded.
|
||||
It is not necessary to fill the buffer entirely, only to obtain at
|
||||
least one more byte. bytes_in_buffer MUST be set to a positive value
|
||||
if TRUE is returned. A FALSE return should only be used when I/O
|
||||
suspension is desired (this mode is discussed in the next section). */
|
||||
static boolean fill_input_buffer(j_decompress_ptr cinfo);
|
||||
|
||||
/* Skip num_bytes worth of data. The buffer pointer and count should
|
||||
be advanced over num_bytes input bytes, refilling the buffer as
|
||||
needed. This is used to skip over a potentially large amount of
|
||||
uninteresting data (such as an APPn marker). In some applications
|
||||
it may be possible to optimize away the reading of the skipped data,
|
||||
but it's not clear that being smart is worth much trouble; large
|
||||
skips are uncommon. bytes_in_buffer may be zero on return.
|
||||
A zero or negative skip count should be treated as a no-op. */
|
||||
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
|
||||
|
||||
/* Terminate source --- called by jpeg_finish_decompress() after all
|
||||
data has been read. Often a no-op. */
|
||||
static void term_source(j_decompress_ptr cinfo);
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
248
src/CImageLoaderPNG.cpp
Normal file
248
src/CImageLoaderPNG.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageLoaderPNG.h"
|
||||
|
||||
#include <png.h> // use system lib png
|
||||
|
||||
#include "CImage.h"
|
||||
#include "CReadFile.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
// PNG function for error handling
|
||||
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
// PNG function for warning handling
|
||||
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG warning", msg, ELL_WARNING);
|
||||
}
|
||||
|
||||
// PNG function for file reading
|
||||
void PNGAPI user_read_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
png_size_t check;
|
||||
|
||||
// changed by zola {
|
||||
io::IReadFile *file = (io::IReadFile *)png_get_io_ptr(png_ptr);
|
||||
check = (png_size_t)file->read((void *)data, length);
|
||||
// }
|
||||
|
||||
if (check != length)
|
||||
png_error(png_ptr, "Read Error");
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool CImageLoaderPng::isALoadableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "png");
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile *file) const
|
||||
{
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
png_byte buffer[8];
|
||||
// Read the first few bytes of the PNG file
|
||||
if (file->read(buffer, 8) != 8)
|
||||
return false;
|
||||
|
||||
// Check if it really is a PNG file
|
||||
return !png_sig_cmp(buffer, 0, 8);
|
||||
}
|
||||
|
||||
// load in the image data
|
||||
IImage *CImageLoaderPng::loadImage(io::IReadFile *file) const
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
// Used to point to image rows
|
||||
u8 **RowPointers = 0;
|
||||
|
||||
png_byte buffer[8];
|
||||
// Read the first few bytes of the PNG file
|
||||
if (file->read(buffer, 8) != 8) {
|
||||
os::Printer::log("LOAD PNG: can't read file (filesize < 8)", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if it really is a PNG file
|
||||
if (png_sig_cmp(buffer, 0, 8)) {
|
||||
os::Printer::log("LOAD PNG: not really a png (wrong signature)", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate the png read struct
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
|
||||
if (!png_ptr) {
|
||||
os::Printer::log("LOAD PNG: Internal PNG create read struct failure", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate the png info struct
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
os::Printer::log("LOAD PNG: Internal PNG create info struct failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for proper error handling
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
delete[] RowPointers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// changed by zola so we don't need to have public FILE pointers
|
||||
png_set_read_fn(png_ptr, file, user_read_data_fcn);
|
||||
|
||||
png_set_sig_bytes(png_ptr, 8); // Tell png that we read the signature
|
||||
|
||||
png_read_info(png_ptr, info_ptr); // Read the info section of the png file
|
||||
|
||||
u32 Width;
|
||||
u32 Height;
|
||||
s32 BitDepth;
|
||||
s32 ColorType;
|
||||
{
|
||||
// Use temporary variables to avoid passing cast pointers
|
||||
png_uint_32 w, h;
|
||||
// Extract info
|
||||
png_get_IHDR(png_ptr, info_ptr,
|
||||
&w, &h,
|
||||
&BitDepth, &ColorType, NULL, NULL, NULL);
|
||||
Width = w;
|
||||
Height = h;
|
||||
}
|
||||
|
||||
if (!checkImageDimensions(Width, Height))
|
||||
png_cpexcept_error(png_ptr, "Unreasonable size");
|
||||
|
||||
// Convert palette color to true color
|
||||
if (ColorType == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
|
||||
// Convert low bit colors to 8 bit colors
|
||||
if (BitDepth < 8) {
|
||||
if (ColorType == PNG_COLOR_TYPE_GRAY || ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||
else
|
||||
png_set_packing(png_ptr);
|
||||
}
|
||||
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
|
||||
// Convert high bit colors to 8 bit colors
|
||||
if (BitDepth == 16)
|
||||
png_set_strip_16(png_ptr);
|
||||
|
||||
// Convert gray color to true color
|
||||
if (ColorType == PNG_COLOR_TYPE_GRAY || ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
|
||||
int intent;
|
||||
const double screen_gamma = 2.2;
|
||||
|
||||
if (png_get_sRGB(png_ptr, info_ptr, &intent))
|
||||
png_set_gamma(png_ptr, screen_gamma, 0.45455);
|
||||
else {
|
||||
double image_gamma;
|
||||
if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
|
||||
png_set_gamma(png_ptr, screen_gamma, image_gamma);
|
||||
else
|
||||
png_set_gamma(png_ptr, screen_gamma, 0.45455);
|
||||
}
|
||||
|
||||
// Update the changes in between, as we need to get the new color type
|
||||
// for proper processing of the RGBA type
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
{
|
||||
// Use temporary variables to avoid passing cast pointers
|
||||
png_uint_32 w, h;
|
||||
// Extract info
|
||||
png_get_IHDR(png_ptr, info_ptr,
|
||||
&w, &h,
|
||||
&BitDepth, &ColorType, NULL, NULL, NULL);
|
||||
Width = w;
|
||||
Height = h;
|
||||
}
|
||||
|
||||
// Convert RGBA to BGRA
|
||||
if (ColorType == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
#ifdef __BIG_ENDIAN__
|
||||
png_set_swap_alpha(png_ptr);
|
||||
#else
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Create the image structure to be filled by png data
|
||||
video::IImage *image = 0;
|
||||
if (ColorType == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(Width, Height));
|
||||
else
|
||||
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
|
||||
if (!image) {
|
||||
os::Printer::log("LOAD PNG: Internal PNG create image struct failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create array of pointers to rows in image data
|
||||
RowPointers = new png_bytep[Height];
|
||||
if (!RowPointers) {
|
||||
os::Printer::log("LOAD PNG: Internal PNG create row pointers failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
delete image;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fill array of pointers to rows in image data
|
||||
unsigned char *data = (unsigned char *)image->getData();
|
||||
for (u32 i = 0; i < Height; ++i) {
|
||||
RowPointers[i] = data;
|
||||
data += image->getPitch();
|
||||
}
|
||||
|
||||
// for proper error handling
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
delete[] RowPointers;
|
||||
delete image;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read data using the library function that handles all transformations including interlacing
|
||||
png_read_image(png_ptr, RowPointers);
|
||||
|
||||
png_read_end(png_ptr, NULL);
|
||||
delete[] RowPointers;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0); // Clean up memory
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
IImageLoader *createImageLoaderPNG()
|
||||
{
|
||||
return new CImageLoaderPng();
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
} // end namespace video
|
||||
34
src/CImageLoaderPNG.h
Normal file
34
src/CImageLoaderPNG.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
// this file was created by rt (www.tomkorp.com), based on ttk's png-reader
|
||||
// i wanted to be able to read in PNG images with irrlicht :)
|
||||
// why? lossless compression with 8-bit alpha channel!
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageLoader.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! Surface Loader for PNG files
|
||||
class CImageLoaderPng : public IImageLoader
|
||||
{
|
||||
public:
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".png")
|
||||
bool isALoadableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool isALoadableFileFormat(io::IReadFile *file) const override;
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *loadImage(io::IReadFile *file) const override;
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
246
src/CImageLoaderTGA.cpp
Normal file
246
src/CImageLoaderTGA.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageLoaderTGA.h"
|
||||
|
||||
#include "IReadFile.h"
|
||||
#include "os.h"
|
||||
#include "CColorConverter.h"
|
||||
#include "CImage.h"
|
||||
#include "irrString.h"
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool CImageLoaderTGA::isALoadableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "tga");
|
||||
}
|
||||
|
||||
//! loads a compressed tga.
|
||||
u8 *CImageLoaderTGA::loadCompressedImage(io::IReadFile *file, const STGAHeader &header) const
|
||||
{
|
||||
// This was written and sent in by Jon Pry, thank you very much!
|
||||
// I only changed the formatting a little bit.
|
||||
|
||||
const u32 bytesPerPixel = header.PixelDepth / 8;
|
||||
const u32 imageSize = header.ImageHeight * header.ImageWidth * bytesPerPixel;
|
||||
u8 *data = new u8[imageSize];
|
||||
u32 currentByte = 0;
|
||||
|
||||
while (currentByte < imageSize) {
|
||||
u8 chunkheader = 0;
|
||||
file->read(&chunkheader, sizeof(u8)); // Read The Chunk's Header
|
||||
|
||||
if (chunkheader < 128) { // If The Chunk Is A 'RAW' Chunk
|
||||
chunkheader++; // Add 1 To The Value To Get Total Number Of Raw Pixels
|
||||
|
||||
const u32 bytesToRead = bytesPerPixel * chunkheader;
|
||||
if (currentByte + bytesToRead <= imageSize) {
|
||||
file->read(&data[currentByte], bytesToRead);
|
||||
currentByte += bytesToRead;
|
||||
} else {
|
||||
os::Printer::log("Compressed TGA file RAW chunk tries writing beyond buffer", file->getFileName(), ELL_WARNING);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// thnx to neojzs for some fixes with this code
|
||||
|
||||
// If It's An RLE Header
|
||||
chunkheader -= 127; // Subtract 127 To Get Rid Of The ID Bit
|
||||
|
||||
u32 dataOffset = currentByte;
|
||||
if (dataOffset + bytesPerPixel < imageSize) {
|
||||
file->read(&data[dataOffset], bytesPerPixel);
|
||||
currentByte += bytesPerPixel;
|
||||
} else {
|
||||
os::Printer::log("Compressed TGA file RLE headertries writing beyond buffer", file->getFileName(), ELL_WARNING);
|
||||
break;
|
||||
}
|
||||
|
||||
for (u32 counter = 1; counter < chunkheader; counter++) {
|
||||
if (currentByte + bytesPerPixel <= imageSize) {
|
||||
for (u32 elementCounter = 0; elementCounter < bytesPerPixel; elementCounter++) {
|
||||
data[currentByte + elementCounter] = data[dataOffset + elementCounter];
|
||||
}
|
||||
}
|
||||
|
||||
currentByte += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool CImageLoaderTGA::isALoadableFileFormat(io::IReadFile *file) const
|
||||
{
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
STGAFooter footer;
|
||||
memset(&footer, 0, sizeof(STGAFooter));
|
||||
file->seek(file->getSize() - sizeof(STGAFooter));
|
||||
file->read(&footer, sizeof(STGAFooter));
|
||||
return (!strcmp(footer.Signature, "TRUEVISION-XFILE.")); // very old tgas are refused.
|
||||
}
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *CImageLoaderTGA::loadImage(io::IReadFile *file) const
|
||||
{
|
||||
STGAHeader header;
|
||||
u32 *palette = 0;
|
||||
|
||||
file->read(&header, sizeof(STGAHeader));
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
header.ColorMapLength = os::Byteswap::byteswap(header.ColorMapLength);
|
||||
header.ImageWidth = os::Byteswap::byteswap(header.ImageWidth);
|
||||
header.ImageHeight = os::Byteswap::byteswap(header.ImageHeight);
|
||||
#endif
|
||||
|
||||
if (!checkImageDimensions(header.ImageWidth, header.ImageHeight)) {
|
||||
os::Printer::log("Image dimensions too large in file", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip image identification field
|
||||
if (header.IdLength)
|
||||
file->seek(header.IdLength, true);
|
||||
|
||||
if (header.ColorMapType) {
|
||||
// Create 32 bit palette
|
||||
// `core::max_()` is not used here because it takes its inputs as references. Since `header` is packed, use the macro `MAX()` instead:
|
||||
const irr::u16 paletteSize = MAX((u16)256u, header.ColorMapLength); // ColorMapLength can lie, but so far we only use palette for 8-bit, so ensure it has 256 entries
|
||||
palette = new u32[paletteSize];
|
||||
|
||||
if (paletteSize > header.ColorMapLength) {
|
||||
// To catch images using palette colors with invalid indices
|
||||
const irr::u32 errorCol = irr::video::SColor(255, 255, 0, 205).color; // bright magenta
|
||||
for (irr::u16 i = header.ColorMapLength; i < paletteSize; ++i)
|
||||
palette[i] = errorCol;
|
||||
}
|
||||
|
||||
// read color map
|
||||
u8 *colorMap = new u8[header.ColorMapEntrySize / 8 * header.ColorMapLength];
|
||||
file->read(colorMap, header.ColorMapEntrySize / 8 * header.ColorMapLength);
|
||||
|
||||
// convert to 32-bit palette
|
||||
switch (header.ColorMapEntrySize) {
|
||||
case 16:
|
||||
CColorConverter::convert_A1R5G5B5toA8R8G8B8(colorMap, header.ColorMapLength, palette);
|
||||
break;
|
||||
case 24:
|
||||
CColorConverter::convert_B8G8R8toA8R8G8B8(colorMap, header.ColorMapLength, palette);
|
||||
break;
|
||||
case 32:
|
||||
CColorConverter::convert_B8G8R8A8toA8R8G8B8(colorMap, header.ColorMapLength, palette);
|
||||
break;
|
||||
}
|
||||
delete[] colorMap;
|
||||
}
|
||||
|
||||
// read image
|
||||
|
||||
u8 *data = 0;
|
||||
|
||||
if (header.ImageType == 1 || // Uncompressed, color-mapped images.
|
||||
header.ImageType == 2 || // Uncompressed, RGB images
|
||||
header.ImageType == 3 // Uncompressed, black and white images
|
||||
) {
|
||||
const s32 imageSize = header.ImageHeight * header.ImageWidth * (header.PixelDepth / 8);
|
||||
data = new u8[imageSize];
|
||||
file->read(data, imageSize);
|
||||
} else if (header.ImageType == 10) {
|
||||
// Runlength encoded RGB images
|
||||
data = loadCompressedImage(file, header);
|
||||
} else {
|
||||
os::Printer::log("Unsupported TGA file type", file->getFileName(), ELL_ERROR);
|
||||
delete[] palette;
|
||||
return 0;
|
||||
}
|
||||
|
||||
IImage *image = 0;
|
||||
|
||||
switch (header.PixelDepth) {
|
||||
case 8: {
|
||||
if (header.ImageType == 3) { // grey image
|
||||
image = new CImage(ECF_R8G8B8,
|
||||
core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert8BitTo24Bit((u8 *)data,
|
||||
(u8 *)image->getData(),
|
||||
header.ImageWidth, header.ImageHeight,
|
||||
0, 0, (header.ImageDescriptor & 0x20) == 0);
|
||||
} else {
|
||||
switch (header.ColorMapEntrySize) {
|
||||
case 16:
|
||||
image = new CImage(ECF_A1R5G5B5, core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert8BitTo16Bit((u8 *)data,
|
||||
(s16 *)image->getData(),
|
||||
header.ImageWidth, header.ImageHeight,
|
||||
(s32 *)palette, 0,
|
||||
(header.ImageDescriptor & 0x20) == 0);
|
||||
break;
|
||||
// Note: 24 bit with palette would need a 24 bit palette, too lazy doing that now (textures will prefer 32-bit later anyway)
|
||||
default:
|
||||
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert8BitTo32Bit((u8 *)data,
|
||||
(u8 *)image->getData(),
|
||||
header.ImageWidth, header.ImageHeight,
|
||||
(u8 *)palette, 0,
|
||||
(header.ImageDescriptor & 0x20) == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 16:
|
||||
image = new CImage(ECF_A1R5G5B5,
|
||||
core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert16BitTo16Bit((s16 *)data,
|
||||
(s16 *)image->getData(), header.ImageWidth, header.ImageHeight, 0, (header.ImageDescriptor & 0x20) == 0);
|
||||
break;
|
||||
case 24:
|
||||
image = new CImage(ECF_R8G8B8,
|
||||
core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert24BitTo24Bit(
|
||||
(u8 *)data, (u8 *)image->getData(), header.ImageWidth, header.ImageHeight, 0, (header.ImageDescriptor & 0x20) == 0, true);
|
||||
break;
|
||||
case 32:
|
||||
image = new CImage(ECF_A8R8G8B8,
|
||||
core::dimension2d<u32>(header.ImageWidth, header.ImageHeight));
|
||||
if (image)
|
||||
CColorConverter::convert32BitTo32Bit((s32 *)data,
|
||||
(s32 *)image->getData(), header.ImageWidth, header.ImageHeight, 0, (header.ImageDescriptor & 0x20) == 0);
|
||||
break;
|
||||
default:
|
||||
os::Printer::log("Unsupported TGA format", file->getFileName(), ELL_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
delete[] palette;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//! creates a loader which is able to load tgas
|
||||
IImageLoader *createImageLoaderTGA()
|
||||
{
|
||||
return new CImageLoaderTGA();
|
||||
}
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
66
src/CImageLoaderTGA.h
Normal file
66
src/CImageLoaderTGA.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageLoader.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
// byte-align structures
|
||||
#include "irrpack.h"
|
||||
|
||||
// these structs are also used in the TGA writer
|
||||
struct STGAHeader
|
||||
{
|
||||
u8 IdLength;
|
||||
u8 ColorMapType;
|
||||
u8 ImageType;
|
||||
u8 FirstEntryIndex[2];
|
||||
u16 ColorMapLength;
|
||||
u8 ColorMapEntrySize;
|
||||
u8 XOrigin[2];
|
||||
u8 YOrigin[2];
|
||||
u16 ImageWidth;
|
||||
u16 ImageHeight;
|
||||
u8 PixelDepth;
|
||||
u8 ImageDescriptor;
|
||||
} PACK_STRUCT;
|
||||
|
||||
struct STGAFooter
|
||||
{
|
||||
u32 ExtensionOffset;
|
||||
u32 DeveloperOffset;
|
||||
c8 Signature[18];
|
||||
} PACK_STRUCT;
|
||||
|
||||
// Default alignment
|
||||
#include "irrunpack.h"
|
||||
|
||||
/*!
|
||||
Surface Loader for targa images
|
||||
*/
|
||||
class CImageLoaderTGA : public IImageLoader
|
||||
{
|
||||
public:
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".tga")
|
||||
bool isALoadableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
bool isALoadableFileFormat(io::IReadFile *file) const override;
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage *loadImage(io::IReadFile *file) const override;
|
||||
|
||||
private:
|
||||
//! loads a compressed tga. Was written and sent in by Jon Pry, thank you very much!
|
||||
u8 *loadCompressedImage(io::IReadFile *file, const STGAHeader &header) const;
|
||||
};
|
||||
|
||||
} // end namespace video
|
||||
} // end namespace irr
|
||||
195
src/CImageWriterJPG.cpp
Normal file
195
src/CImageWriterJPG.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageWriterJPG.h"
|
||||
|
||||
#include "CColorConverter.h"
|
||||
#include "IWriteFile.h"
|
||||
#include "CImage.h"
|
||||
#include "irrString.h"
|
||||
#include "os.h"
|
||||
|
||||
#include <cstdio> // required for jpeglib.h
|
||||
extern "C" {
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
}
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
// The writer uses a 4k buffer and flushes to disk each time it's filled
|
||||
#define OUTPUT_BUF_SIZE 4096
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
io::IWriteFile *file; /* target file */
|
||||
JOCTET buffer[OUTPUT_BUF_SIZE]; /* image buffer */
|
||||
} mem_destination_mgr;
|
||||
|
||||
typedef mem_destination_mgr *mem_dest_ptr;
|
||||
|
||||
// init
|
||||
static void jpeg_init_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
// flush to disk and reset buffer
|
||||
static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
|
||||
// for now just exit upon file error
|
||||
if (dest->file->write(dest->buffer, OUTPUT_BUF_SIZE) != OUTPUT_BUF_SIZE)
|
||||
ERREXIT(cinfo, JERR_FILE_WRITE);
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void jpeg_term_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
|
||||
const size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
// for now just exit upon file error
|
||||
if (dest->file->write(dest->buffer, datacount) != datacount)
|
||||
ERREXIT(cinfo, JERR_FILE_WRITE);
|
||||
}
|
||||
|
||||
// set up buffer data
|
||||
static void jpeg_file_dest(j_compress_ptr cinfo, io::IWriteFile *file)
|
||||
{
|
||||
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo,
|
||||
JPOOL_PERMANENT,
|
||||
sizeof(mem_destination_mgr));
|
||||
}
|
||||
|
||||
mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest; /* for casting */
|
||||
|
||||
/* Initialize method pointers */
|
||||
dest->pub.init_destination = jpeg_init_destination;
|
||||
dest->pub.empty_output_buffer = jpeg_empty_output_buffer;
|
||||
dest->pub.term_destination = jpeg_term_destination;
|
||||
|
||||
/* Initialize private member */
|
||||
dest->file = file;
|
||||
}
|
||||
|
||||
/* write_JPEG_memory: store JPEG compressed image into memory.
|
||||
*/
|
||||
static bool writeJPEGFile(io::IWriteFile *file, IImage *image, u32 quality)
|
||||
{
|
||||
void (*format)(const void *, s32, void *) = 0;
|
||||
switch (image->getColorFormat()) {
|
||||
case ECF_R8G8B8:
|
||||
format = CColorConverter::convert_R8G8B8toR8G8B8;
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
format = CColorConverter::convert_A8R8G8B8toR8G8B8;
|
||||
break;
|
||||
case ECF_A1R5G5B5:
|
||||
format = CColorConverter::convert_A1R5G5B5toB8G8R8;
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
format = CColorConverter::convert_R5G6B5toR8G8B8;
|
||||
break;
|
||||
default:
|
||||
os::Printer::log("writeJPEGFile does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING);
|
||||
break;
|
||||
}
|
||||
|
||||
// couldn't find a color converter
|
||||
if (0 == format)
|
||||
return false;
|
||||
|
||||
const core::dimension2du dim = image->getDimension();
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
jpeg_create_compress(&cinfo);
|
||||
jpeg_file_dest(&cinfo, file);
|
||||
cinfo.image_width = dim.Width;
|
||||
cinfo.image_height = dim.Height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(&cinfo);
|
||||
|
||||
if (0 == quality)
|
||||
quality = 75;
|
||||
|
||||
jpeg_set_quality(&cinfo, quality, TRUE);
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
u8 *dest = new u8[dim.Width * 3];
|
||||
|
||||
if (dest) {
|
||||
const u32 pitch = image->getPitch();
|
||||
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
|
||||
row_pointer[0] = dest;
|
||||
|
||||
u8 *src = (u8 *)image->getData();
|
||||
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
// convert next line
|
||||
format(src, dim.Width, dest);
|
||||
src += pitch;
|
||||
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||||
}
|
||||
|
||||
delete[] dest;
|
||||
|
||||
/* Step 6: Finish compression */
|
||||
jpeg_finish_compress(&cinfo);
|
||||
}
|
||||
|
||||
/* Step 7: Destroy */
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
return (dest != 0);
|
||||
}
|
||||
|
||||
} // namespace video
|
||||
} // namespace irr
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
IImageWriter *createImageWriterJPG()
|
||||
{
|
||||
return new CImageWriterJPG;
|
||||
}
|
||||
|
||||
CImageWriterJPG::CImageWriterJPG()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CImageWriterJPG");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CImageWriterJPG::isAWriteableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "jpg", "jpeg");
|
||||
}
|
||||
|
||||
bool CImageWriterJPG::writeImage(io::IWriteFile *file, IImage *image, u32 quality) const
|
||||
{
|
||||
return writeJPEGFile(file, image, quality);
|
||||
}
|
||||
|
||||
} // namespace video
|
||||
} // namespace irr
|
||||
28
src/CImageWriterJPG.h
Normal file
28
src/CImageWriterJPG.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class CImageWriterJPG : public IImageWriter
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CImageWriterJPG();
|
||||
|
||||
//! return true if this writer can write a file with the given extension
|
||||
bool isAWriteableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! write image to file
|
||||
bool writeImage(io::IWriteFile *file, IImage *image, u32 param) const override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
190
src/CImageWriterPNG.cpp
Normal file
190
src/CImageWriterPNG.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CImageWriterPNG.h"
|
||||
|
||||
#include "CImageLoaderPNG.h"
|
||||
#include "CColorConverter.h"
|
||||
#include "IWriteFile.h"
|
||||
#include "irrString.h"
|
||||
#include "os.h" // for logging
|
||||
|
||||
#include <png.h> // use system lib png
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
IImageWriter *createImageWriterPNG()
|
||||
{
|
||||
return new CImageWriterPNG;
|
||||
}
|
||||
|
||||
// PNG function for error handling
|
||||
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
// PNG function for warning handling
|
||||
static void png_cpexcept_warning(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG warning", msg, ELL_WARNING);
|
||||
}
|
||||
|
||||
// PNG function for file writing
|
||||
void PNGAPI user_write_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
png_size_t check;
|
||||
|
||||
io::IWriteFile *file = (io::IWriteFile *)png_get_io_ptr(png_ptr);
|
||||
check = (png_size_t)file->write((const void *)data, length);
|
||||
|
||||
if (check != length)
|
||||
png_error(png_ptr, "Write Error");
|
||||
}
|
||||
|
||||
CImageWriterPNG::CImageWriterPNG()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CImageWriterPNG");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CImageWriterPNG::isAWriteableFileExtension(const io::path &filename) const
|
||||
{
|
||||
return core::hasFileExtension(filename, "png");
|
||||
}
|
||||
|
||||
bool CImageWriterPNG::writeImage(io::IWriteFile *file, IImage *image, u32 param) const
|
||||
{
|
||||
if (!file || !image)
|
||||
return false;
|
||||
|
||||
// Allocate the png write struct
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warning);
|
||||
if (!png_ptr) {
|
||||
os::Printer::log("PNGWriter: Internal PNG create write struct failure", file->getFileName(), ELL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the png info struct
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
os::Printer::log("PNGWriter: Internal PNG create info struct failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_write_struct(&png_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// for proper error handling
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(png_ptr, file, user_write_data_fcn, NULL);
|
||||
|
||||
// Set info
|
||||
switch (image->getColorFormat()) {
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_A1R5G5B5:
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
image->getDimension().Width, image->getDimension().Height,
|
||||
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
break;
|
||||
default:
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
image->getDimension().Width, image->getDimension().Height,
|
||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
s32 lineWidth = image->getDimension().Width;
|
||||
switch (image->getColorFormat()) {
|
||||
case ECF_R8G8B8:
|
||||
case ECF_R5G6B5:
|
||||
lineWidth *= 3;
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
case ECF_A1R5G5B5:
|
||||
lineWidth *= 4;
|
||||
break;
|
||||
// TODO: Error handling in case of unsupported color format
|
||||
default:
|
||||
break;
|
||||
}
|
||||
u8 *tmpImage = new u8[image->getDimension().Height * lineWidth];
|
||||
if (!tmpImage) {
|
||||
os::Printer::log("PNGWriter: Internal PNG create image failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 *data = (u8 *)image->getData();
|
||||
switch (image->getColorFormat()) {
|
||||
case ECF_R8G8B8:
|
||||
CColorConverter::convert_R8G8B8toR8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage);
|
||||
break;
|
||||
case ECF_A8R8G8B8:
|
||||
CColorConverter::convert_A8R8G8B8toA8R8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage);
|
||||
break;
|
||||
case ECF_R5G6B5:
|
||||
CColorConverter::convert_R5G6B5toR8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage);
|
||||
break;
|
||||
case ECF_A1R5G5B5:
|
||||
CColorConverter::convert_A1R5G5B5toA8R8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage);
|
||||
break;
|
||||
// TODO: Error handling in case of unsupported color format
|
||||
default:
|
||||
os::Printer::log("CImageWriterPNG does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
delete[] tmpImage;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create array of pointers to rows in image data
|
||||
|
||||
// Used to point to image rows
|
||||
u8 **RowPointers = new png_bytep[image->getDimension().Height];
|
||||
if (!RowPointers) {
|
||||
os::Printer::log("PNGWriter: Internal PNG create row pointers failure", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
delete[] tmpImage;
|
||||
return false;
|
||||
}
|
||||
|
||||
data = tmpImage;
|
||||
// Fill array of pointers to rows in image data
|
||||
for (u32 i = 0; i < image->getDimension().Height; ++i) {
|
||||
RowPointers[i] = data;
|
||||
data += lineWidth;
|
||||
}
|
||||
// for proper error handling
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
delete[] RowPointers;
|
||||
delete[] tmpImage;
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, RowPointers);
|
||||
|
||||
if (image->getColorFormat() == ECF_A8R8G8B8 || image->getColorFormat() == ECF_A1R5G5B5)
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);
|
||||
else {
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
}
|
||||
|
||||
delete[] RowPointers;
|
||||
delete[] tmpImage;
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace video
|
||||
} // namespace irr
|
||||
28
src/CImageWriterPNG.h
Normal file
28
src/CImageWriterPNG.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IImageWriter.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video
|
||||
{
|
||||
|
||||
class CImageWriterPNG : public IImageWriter
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CImageWriterPNG();
|
||||
|
||||
//! return true if this writer can write a file with the given extension
|
||||
bool isAWriteableFileExtension(const io::path &filename) const override;
|
||||
|
||||
//! write image to file
|
||||
bool writeImage(io::IWriteFile *file, IImage *image, u32 param) const override;
|
||||
};
|
||||
|
||||
} // namespace video
|
||||
} // namespace irr
|
||||
2244
src/CIrrDeviceLinux.cpp
Normal file
2244
src/CIrrDeviceLinux.cpp
Normal file
File diff suppressed because it is too large
Load Diff
469
src/CIrrDeviceLinux.h
Normal file
469
src/CIrrDeviceLinux.h
Normal file
@@ -0,0 +1,469 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
|
||||
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "ICursorControl.h"
|
||||
#include "os.h"
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#ifdef _IRR_LINUX_X11_XINPUT2_
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define KeySym s32
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
class CIrrDeviceLinux : public CIrrDeviceStub
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CIrrDeviceLinux(const SIrrlichtCreationParameters ¶m);
|
||||
|
||||
//! destructor
|
||||
virtual ~CIrrDeviceLinux();
|
||||
|
||||
//! runs the device. Returns false if device wants to be deleted
|
||||
bool run() override;
|
||||
|
||||
//! Cause the device to temporarily pause execution and let other processes to run
|
||||
// This should bring down processor usage without major performance loss for Irrlicht
|
||||
void yield() override;
|
||||
|
||||
//! Pause execution and let other processes to run for a specified amount of time.
|
||||
void sleep(u32 timeMs, bool pauseTimer) override;
|
||||
|
||||
//! sets the caption of the window
|
||||
void setWindowCaption(const wchar_t *text) override;
|
||||
|
||||
//! Sets the window icon.
|
||||
bool setWindowIcon(const video::IImage *img) override;
|
||||
|
||||
//! returns if window is active. if not, nothing need to be drawn
|
||||
bool isWindowActive() const override;
|
||||
|
||||
//! returns if window has focus.
|
||||
bool isWindowFocused() const override;
|
||||
|
||||
//! returns if window is minimized.
|
||||
bool isWindowMinimized() const override;
|
||||
|
||||
//! returns last state from maximizeWindow() and restoreWindow()
|
||||
bool isWindowMaximized() const override;
|
||||
|
||||
//! returns color format of the window.
|
||||
video::ECOLOR_FORMAT getColorFormat() const override;
|
||||
|
||||
//! notifies the device that it should close itself
|
||||
void closeDevice() override;
|
||||
|
||||
//! Sets if the window should be resizable in windowed mode.
|
||||
void setResizable(bool resize = false) override;
|
||||
|
||||
//! Resize the render window.
|
||||
void setWindowSize(const irr::core::dimension2d<u32> &size) override;
|
||||
|
||||
//! Minimizes the window.
|
||||
void minimizeWindow() override;
|
||||
|
||||
//! Maximizes the window.
|
||||
void maximizeWindow() override;
|
||||
|
||||
//! Restores the window size.
|
||||
void restoreWindow() override;
|
||||
|
||||
//! Get the position of this window on screen
|
||||
core::position2di getWindowPosition() override;
|
||||
|
||||
//! Activate any joysticks, and generate events for them.
|
||||
bool activateJoysticks(core::array<SJoystickInfo> &joystickInfo) override;
|
||||
|
||||
//! gets text from the clipboard
|
||||
//! \return Returns 0 if no string is in there, otherwise utf-8 text.
|
||||
virtual const c8 *getTextFromClipboard() const;
|
||||
|
||||
//! gets text from the primary selection
|
||||
//! \return Returns 0 if no string is in there, otherwise utf-8 text.
|
||||
virtual const c8 *getTextFromPrimarySelection() const;
|
||||
|
||||
//! copies text to the clipboard
|
||||
//! This sets the clipboard selection and _not_ the primary selection.
|
||||
//! @param text The text in utf-8
|
||||
virtual void copyToClipboard(const c8 *text) const;
|
||||
|
||||
//! copies text to the primary selection
|
||||
//! This sets the primary selection which you have on X on the middle mouse button.
|
||||
//! @param text The text in utf-8
|
||||
virtual void copyToPrimarySelection(const c8 *text) const;
|
||||
|
||||
//! Remove all messages pending in the system message loop
|
||||
void clearSystemMessages() override;
|
||||
|
||||
//! Get the device type
|
||||
E_DEVICE_TYPE getType() const override
|
||||
{
|
||||
return EIDT_X11;
|
||||
}
|
||||
|
||||
//! Get the display density in dots per inch.
|
||||
float getDisplayDensity() const override;
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
// convert an Irrlicht texture to a X11 cursor
|
||||
Cursor TextureToCursor(irr::video::ITexture *tex, const core::rect<s32> &sourceRect, const core::position2d<s32> &hotspot);
|
||||
Cursor TextureToMonochromeCursor(irr::video::ITexture *tex, const core::rect<s32> &sourceRect, const core::position2d<s32> &hotspot);
|
||||
#ifdef _IRR_LINUX_XCURSOR_
|
||||
Cursor TextureToARGBCursor(irr::video::ITexture *tex, const core::rect<s32> &sourceRect, const core::position2d<s32> &hotspot);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
//! create the driver
|
||||
void createDriver();
|
||||
|
||||
bool createWindow();
|
||||
|
||||
void createKeyMap();
|
||||
|
||||
void pollJoysticks();
|
||||
|
||||
void initXAtoms();
|
||||
|
||||
void initXInput2();
|
||||
|
||||
bool switchToFullscreen();
|
||||
|
||||
void setupTopLevelXorgWindow();
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
bool createInputContext();
|
||||
void destroyInputContext();
|
||||
EKEY_CODE getKeyCode(XEvent &event);
|
||||
|
||||
const c8 *getTextFromSelection(Atom selection, core::stringc &text_buffer) const;
|
||||
bool becomeSelectionOwner(Atom selection) const;
|
||||
#endif
|
||||
|
||||
//! Implementation of the linux cursor control
|
||||
class CCursorControl : public gui::ICursorControl
|
||||
{
|
||||
public:
|
||||
CCursorControl(CIrrDeviceLinux *dev, bool null);
|
||||
|
||||
~CCursorControl();
|
||||
|
||||
//! Changes the visible state of the mouse cursor.
|
||||
void setVisible(bool visible) override
|
||||
{
|
||||
if (visible == IsVisible)
|
||||
return;
|
||||
IsVisible = visible;
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
if (!Null) {
|
||||
if (!IsVisible)
|
||||
XDefineCursor(Device->XDisplay, Device->XWindow, InvisCursor);
|
||||
else
|
||||
XUndefineCursor(Device->XDisplay, Device->XWindow);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//! Returns if the cursor is currently visible.
|
||||
bool isVisible() const override
|
||||
{
|
||||
return IsVisible;
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<f32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(f32 x, f32 y) override
|
||||
{
|
||||
setPosition((s32)(x * Device->Width), (s32)(y * Device->Height));
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<s32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(s32 x, s32 y) override
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
|
||||
if (!Null) {
|
||||
if (UseReferenceRect) {
|
||||
// NOTE: XIWarpPointer works when X11 has set a coordinate transformation matrix for the mouse unlike XWarpPointer
|
||||
// which runs into a bug mentioned here: https://gitlab.freedesktop.org/xorg/xserver/-/issues/600
|
||||
// So also workaround for Irrlicht bug #450
|
||||
#ifdef _IRR_LINUX_X11_XINPUT2_
|
||||
if (DeviceId != 0) {
|
||||
XIWarpPointer(Device->XDisplay,
|
||||
DeviceId,
|
||||
None,
|
||||
Device->XWindow, 0, 0,
|
||||
Device->Width,
|
||||
Device->Height,
|
||||
ReferenceRect.UpperLeftCorner.X + x,
|
||||
ReferenceRect.UpperLeftCorner.Y + y);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
XWarpPointer(Device->XDisplay,
|
||||
None,
|
||||
Device->XWindow, 0, 0,
|
||||
Device->Width,
|
||||
Device->Height,
|
||||
ReferenceRect.UpperLeftCorner.X + x,
|
||||
ReferenceRect.UpperLeftCorner.Y + y);
|
||||
}
|
||||
} else {
|
||||
#ifdef _IRR_LINUX_X11_XINPUT2_
|
||||
if (DeviceId != 0) {
|
||||
XIWarpPointer(Device->XDisplay,
|
||||
DeviceId,
|
||||
None,
|
||||
Device->XWindow, 0, 0,
|
||||
Device->Width,
|
||||
Device->Height, x, y);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
XWarpPointer(Device->XDisplay,
|
||||
None,
|
||||
Device->XWindow, 0, 0,
|
||||
Device->Width,
|
||||
Device->Height, x, y);
|
||||
}
|
||||
}
|
||||
XFlush(Device->XDisplay);
|
||||
}
|
||||
#endif
|
||||
CursorPos.X = x;
|
||||
CursorPos.Y = y;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
const core::position2d<s32> &getPosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateCursorPos();
|
||||
return CursorPos;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
core::position2d<f32> getRelativePosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateCursorPos();
|
||||
|
||||
if (!UseReferenceRect) {
|
||||
return core::position2d<f32>(CursorPos.X / (f32)Device->Width,
|
||||
CursorPos.Y / (f32)Device->Height);
|
||||
}
|
||||
|
||||
return core::position2d<f32>(CursorPos.X / (f32)ReferenceRect.getWidth(),
|
||||
CursorPos.Y / (f32)ReferenceRect.getHeight());
|
||||
}
|
||||
|
||||
void setReferenceRect(core::rect<s32> *rect = 0) override
|
||||
{
|
||||
if (rect) {
|
||||
ReferenceRect = *rect;
|
||||
UseReferenceRect = true;
|
||||
|
||||
// prevent division through zero and uneven sizes
|
||||
|
||||
if (!ReferenceRect.getHeight() || ReferenceRect.getHeight() % 2)
|
||||
ReferenceRect.LowerRightCorner.Y += 1;
|
||||
|
||||
if (!ReferenceRect.getWidth() || ReferenceRect.getWidth() % 2)
|
||||
ReferenceRect.LowerRightCorner.X += 1;
|
||||
} else
|
||||
UseReferenceRect = false;
|
||||
}
|
||||
|
||||
//! Sets the active cursor icon
|
||||
void setActiveIcon(gui::ECURSOR_ICON iconId) override;
|
||||
|
||||
//! Gets the currently active icon
|
||||
gui::ECURSOR_ICON getActiveIcon() const override
|
||||
{
|
||||
return ActiveIcon;
|
||||
}
|
||||
|
||||
//! Add a custom sprite as cursor icon.
|
||||
gui::ECURSOR_ICON addIcon(const gui::SCursorSprite &icon) override;
|
||||
|
||||
//! replace the given cursor icon.
|
||||
void changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite &icon) override;
|
||||
|
||||
//! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work.
|
||||
core::dimension2di getSupportedIconSize() const override;
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
//! Set platform specific behavior flags.
|
||||
void setPlatformBehavior(gui::ECURSOR_PLATFORM_BEHAVIOR behavior) override { PlatformBehavior = behavior; }
|
||||
|
||||
//! Return platform specific behavior.
|
||||
gui::ECURSOR_PLATFORM_BEHAVIOR getPlatformBehavior() const override { return PlatformBehavior; }
|
||||
|
||||
void update();
|
||||
void clearCursors();
|
||||
#endif
|
||||
private:
|
||||
void updateCursorPos()
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
if (Null)
|
||||
return;
|
||||
|
||||
if (PlatformBehavior & gui::ECPB_X11_CACHE_UPDATES && !os::Timer::isStopped()) {
|
||||
u32 now = os::Timer::getTime();
|
||||
if (now <= LastQuery)
|
||||
return;
|
||||
LastQuery = now;
|
||||
}
|
||||
|
||||
Window tmp;
|
||||
int itmp1, itmp2;
|
||||
unsigned int maskreturn;
|
||||
XQueryPointer(Device->XDisplay, Device->XWindow,
|
||||
&tmp, &tmp,
|
||||
&itmp1, &itmp2,
|
||||
&CursorPos.X, &CursorPos.Y, &maskreturn);
|
||||
#endif
|
||||
}
|
||||
|
||||
CIrrDeviceLinux *Device;
|
||||
core::position2d<s32> CursorPos;
|
||||
core::rect<s32> ReferenceRect;
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
gui::ECURSOR_PLATFORM_BEHAVIOR PlatformBehavior;
|
||||
u32 LastQuery;
|
||||
Cursor InvisCursor;
|
||||
|
||||
#ifdef _IRR_LINUX_X11_XINPUT2_
|
||||
int DeviceId;
|
||||
#endif
|
||||
|
||||
struct CursorFrameX11
|
||||
{
|
||||
CursorFrameX11() :
|
||||
IconHW(0) {}
|
||||
CursorFrameX11(Cursor icon) :
|
||||
IconHW(icon) {}
|
||||
|
||||
Cursor IconHW; // hardware cursor
|
||||
};
|
||||
|
||||
struct CursorX11
|
||||
{
|
||||
CursorX11() {}
|
||||
explicit CursorX11(Cursor iconHw, u32 frameTime = 0) :
|
||||
FrameTime(frameTime)
|
||||
{
|
||||
Frames.push_back(CursorFrameX11(iconHw));
|
||||
}
|
||||
core::array<CursorFrameX11> Frames;
|
||||
u32 FrameTime;
|
||||
};
|
||||
|
||||
core::array<CursorX11> Cursors;
|
||||
|
||||
void initCursors();
|
||||
#endif
|
||||
bool IsVisible;
|
||||
bool Null;
|
||||
bool UseReferenceRect;
|
||||
gui::ECURSOR_ICON ActiveIcon;
|
||||
u32 ActiveIconStartTime;
|
||||
};
|
||||
|
||||
friend class CCursorControl;
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_X11_
|
||||
friend class COpenGLDriver;
|
||||
|
||||
Display *XDisplay;
|
||||
XVisualInfo *VisualInfo;
|
||||
int Screennr;
|
||||
Window XWindow;
|
||||
XSetWindowAttributes WndAttributes;
|
||||
XSizeHints *StdHints;
|
||||
XIM XInputMethod;
|
||||
XIC XInputContext;
|
||||
bool HasNetWM;
|
||||
// text is utf-8
|
||||
mutable core::stringc Clipboard;
|
||||
mutable core::stringc PrimarySelection;
|
||||
#endif
|
||||
#if defined(_IRR_LINUX_X11_XINPUT2_)
|
||||
int currentTouchedCount;
|
||||
#endif
|
||||
u32 Width, Height;
|
||||
bool WindowHasFocus;
|
||||
bool WindowMinimized;
|
||||
bool WindowMaximized;
|
||||
bool ExternalWindow;
|
||||
int AutorepeatSupport;
|
||||
|
||||
struct SKeyMap
|
||||
{
|
||||
SKeyMap() {}
|
||||
SKeyMap(s32 x11, s32 win32) :
|
||||
X11Key(x11), Win32Key(win32)
|
||||
{
|
||||
}
|
||||
|
||||
KeySym X11Key;
|
||||
s32 Win32Key;
|
||||
|
||||
bool operator<(const SKeyMap &o) const
|
||||
{
|
||||
return X11Key < o.X11Key;
|
||||
}
|
||||
};
|
||||
|
||||
core::array<SKeyMap> KeyMap;
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
|
||||
struct JoystickInfo
|
||||
{
|
||||
int fd;
|
||||
int axes;
|
||||
int buttons;
|
||||
|
||||
SEvent persistentData;
|
||||
|
||||
JoystickInfo() :
|
||||
fd(-1), axes(0), buttons(0) {}
|
||||
};
|
||||
core::array<JoystickInfo> ActiveJoysticks;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
||||
#endif // _IRR_COMPILE_WITH_X11_DEVICE_
|
||||
238
src/CIrrDeviceOSX.h
Normal file
238
src/CIrrDeviceOSX.h
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright (C) 2005-2006 Etienne Petitjean
|
||||
// Copyright (C) 2007-2012 Christian Stehno
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in Irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_OSX_DEVICE_
|
||||
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "ICursorControl.h"
|
||||
|
||||
#import <AppKit/NSWindow.h>
|
||||
#import <AppKit/NSBitmapImageRep.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
class CIrrDeviceMacOSX;
|
||||
}
|
||||
|
||||
@interface CIrrDelegateOSX : NSObject <NSApplicationDelegate, NSWindowDelegate>
|
||||
|
||||
- (id)initWithDevice:(irr::CIrrDeviceMacOSX *)device;
|
||||
- (void)terminate:(id)sender;
|
||||
- (BOOL)isQuit;
|
||||
|
||||
@end
|
||||
|
||||
namespace irr
|
||||
{
|
||||
class CIrrDeviceMacOSX : public CIrrDeviceStub
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CIrrDeviceMacOSX(const SIrrlichtCreationParameters ¶ms);
|
||||
|
||||
//! destructor
|
||||
virtual ~CIrrDeviceMacOSX();
|
||||
|
||||
//! runs the device. Returns false if device wants to be deleted
|
||||
bool run() override;
|
||||
|
||||
//! Cause the device to temporarily pause execution and let other processes to run
|
||||
// This should bring down processor usage without major performance loss for Irrlicht
|
||||
void yield() override;
|
||||
|
||||
//! Pause execution and let other processes to run for a specified amount of time.
|
||||
void sleep(u32 timeMs, bool pauseTimer) override;
|
||||
|
||||
//! sets the caption of the window
|
||||
void setWindowCaption(const wchar_t *text) override;
|
||||
|
||||
//! returns if window is active. if not, nothing need to be drawn
|
||||
bool isWindowActive() const override;
|
||||
|
||||
//! Checks if the Irrlicht window has focus
|
||||
bool isWindowFocused() const override;
|
||||
|
||||
//! Checks if the Irrlicht window is minimized
|
||||
bool isWindowMinimized() const override;
|
||||
|
||||
//! notifies the device that it should close itself
|
||||
void closeDevice() override;
|
||||
|
||||
//! Sets if the window should be resizable in windowed mode.
|
||||
void setResizable(bool resize) override;
|
||||
|
||||
//! Returns true if the window is resizable, false if not
|
||||
virtual bool isResizable() const;
|
||||
|
||||
//! Minimizes the window if possible
|
||||
void minimizeWindow() override;
|
||||
|
||||
//! Maximizes the window if possible.
|
||||
void maximizeWindow() override;
|
||||
|
||||
//! Restore the window to normal size if possible.
|
||||
void restoreWindow() override;
|
||||
|
||||
//! Get the position of this window on screen
|
||||
core::position2di getWindowPosition() override;
|
||||
|
||||
//! Activate any joysticks, and generate events for them.
|
||||
bool activateJoysticks(core::array<SJoystickInfo> &joystickInfo) override;
|
||||
|
||||
//! Get the device type
|
||||
E_DEVICE_TYPE getType() const override
|
||||
{
|
||||
return EIDT_OSX;
|
||||
}
|
||||
|
||||
void setMouseLocation(int x, int y);
|
||||
void setResize(int width, int height);
|
||||
void setCursorVisible(bool visible);
|
||||
void setWindow(NSWindow *window);
|
||||
|
||||
private:
|
||||
//! create the driver
|
||||
void createDriver();
|
||||
|
||||
//! Implementation of the macos x cursor control
|
||||
class CCursorControl : public gui::ICursorControl
|
||||
{
|
||||
public:
|
||||
CCursorControl(const core::dimension2d<u32> &wsize, CIrrDeviceMacOSX *device) :
|
||||
WindowSize(wsize), InvWindowSize(0.0f, 0.0f), Device(device), IsVisible(true), UseReferenceRect(false)
|
||||
{
|
||||
CursorPos.X = CursorPos.Y = 0;
|
||||
if (WindowSize.Width != 0)
|
||||
InvWindowSize.Width = 1.0f / WindowSize.Width;
|
||||
if (WindowSize.Height != 0)
|
||||
InvWindowSize.Height = 1.0f / WindowSize.Height;
|
||||
}
|
||||
|
||||
//! Changes the visible state of the mouse cursor.
|
||||
void setVisible(bool visible) override
|
||||
{
|
||||
IsVisible = visible;
|
||||
Device->setCursorVisible(visible);
|
||||
}
|
||||
|
||||
//! Returns if the cursor is currently visible.
|
||||
bool isVisible() const override
|
||||
{
|
||||
return IsVisible;
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<f32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(f32 x, f32 y) override
|
||||
{
|
||||
setPosition((s32)(x * WindowSize.Width), (s32)(y * WindowSize.Height));
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<s32> &pos) override
|
||||
{
|
||||
if (CursorPos.X != pos.X || CursorPos.Y != pos.Y)
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(s32 x, s32 y) override
|
||||
{
|
||||
if (UseReferenceRect) {
|
||||
Device->setMouseLocation(ReferenceRect.UpperLeftCorner.X + x, ReferenceRect.UpperLeftCorner.Y + y);
|
||||
} else {
|
||||
Device->setMouseLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
const core::position2d<s32> &getPosition(bool updateCursor) override
|
||||
{
|
||||
return CursorPos;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
core::position2d<f32> getRelativePosition(bool updateCursor) override
|
||||
{
|
||||
if (!UseReferenceRect) {
|
||||
return core::position2d<f32>(CursorPos.X * InvWindowSize.Width,
|
||||
CursorPos.Y * InvWindowSize.Height);
|
||||
}
|
||||
|
||||
return core::position2d<f32>(CursorPos.X / (f32)ReferenceRect.getWidth(),
|
||||
CursorPos.Y / (f32)ReferenceRect.getHeight());
|
||||
}
|
||||
|
||||
//! Sets an absolute reference rect for calculating the cursor position.
|
||||
void setReferenceRect(core::rect<s32> *rect = 0) override
|
||||
{
|
||||
if (rect) {
|
||||
ReferenceRect = *rect;
|
||||
UseReferenceRect = true;
|
||||
|
||||
// prevent division through zero and uneven sizes
|
||||
|
||||
if (!ReferenceRect.getHeight() || ReferenceRect.getHeight() % 2)
|
||||
ReferenceRect.LowerRightCorner.Y += 1;
|
||||
|
||||
if (!ReferenceRect.getWidth() || ReferenceRect.getWidth() % 2)
|
||||
ReferenceRect.LowerRightCorner.X += 1;
|
||||
} else
|
||||
UseReferenceRect = false;
|
||||
}
|
||||
|
||||
//! Updates the internal cursor position
|
||||
void updateInternalCursorPosition(int x, int y)
|
||||
{
|
||||
CursorPos.X = x;
|
||||
CursorPos.Y = y;
|
||||
}
|
||||
|
||||
private:
|
||||
core::position2d<s32> CursorPos;
|
||||
core::dimension2d<s32> WindowSize;
|
||||
core::dimension2d<float> InvWindowSize;
|
||||
core::rect<s32> ReferenceRect;
|
||||
CIrrDeviceMacOSX *Device;
|
||||
bool IsVisible;
|
||||
bool UseReferenceRect;
|
||||
};
|
||||
|
||||
bool createWindow();
|
||||
void initKeycodes();
|
||||
void storeMouseLocation();
|
||||
void postMouseEvent(void *event, irr::SEvent &ievent);
|
||||
void postKeyEvent(void *event, irr::SEvent &ievent, bool pressed);
|
||||
void pollJoysticks();
|
||||
|
||||
NSWindow *Window;
|
||||
CGDirectDisplayID Display;
|
||||
std::map<int, int> KeyCodes;
|
||||
int DeviceWidth;
|
||||
int DeviceHeight;
|
||||
int ScreenWidth;
|
||||
int ScreenHeight;
|
||||
u32 MouseButtonStates;
|
||||
bool IsFullscreen;
|
||||
bool IsActive;
|
||||
bool IsShiftDown;
|
||||
bool IsControlDown;
|
||||
bool IsResizable;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
||||
#endif // _IRR_COMPILE_WITH_OSX_DEVICE_
|
||||
1470
src/CIrrDeviceOSX.mm
Normal file
1470
src/CIrrDeviceOSX.mm
Normal file
File diff suppressed because it is too large
Load Diff
1264
src/CIrrDeviceSDL.cpp
Normal file
1264
src/CIrrDeviceSDL.cpp
Normal file
File diff suppressed because it is too large
Load Diff
326
src/CIrrDeviceSDL.h
Normal file
326
src/CIrrDeviceSDL.h
Normal file
@@ -0,0 +1,326 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
// This device code is based on the original SDL device implementation
|
||||
// contributed by Shane Parker (sirshane).
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
|
||||
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "ICursorControl.h"
|
||||
|
||||
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
|
||||
#include <emscripten/html5.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
// DirectFB is removed in SDL3, thou distribution as Alpine currently ships SDL2
|
||||
// with enabled DirectFB, but requiring another fix at a top of SDL2.
|
||||
// We don't need DirectFB in Irrlicht/Minetest, so simply disable it here to prevent issues.
|
||||
#undef SDL_VIDEO_DRIVER_DIRECTFB
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
class CIrrDeviceSDL : public CIrrDeviceStub
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m);
|
||||
|
||||
//! destructor
|
||||
virtual ~CIrrDeviceSDL();
|
||||
|
||||
//! runs the device. Returns false if device wants to be deleted
|
||||
bool run() override;
|
||||
|
||||
//! pause execution temporarily
|
||||
void yield() override;
|
||||
|
||||
//! pause execution for a specified time
|
||||
void sleep(u32 timeMs, bool pauseTimer) override;
|
||||
|
||||
//! sets the caption of the window
|
||||
void setWindowCaption(const wchar_t *text) override;
|
||||
|
||||
//! Sets the window icon.
|
||||
bool setWindowIcon(const video::IImage *img) override;
|
||||
|
||||
//! returns if window is active. if not, nothing need to be drawn
|
||||
bool isWindowActive() const override;
|
||||
|
||||
//! returns if window has focus.
|
||||
bool isWindowFocused() const override;
|
||||
|
||||
//! returns if window is minimized.
|
||||
bool isWindowMinimized() const override;
|
||||
|
||||
//! returns color format of the window.
|
||||
video::ECOLOR_FORMAT getColorFormat() const override;
|
||||
|
||||
//! notifies the device that it should close itself
|
||||
void closeDevice() override;
|
||||
|
||||
//! Sets if the window should be resizable in windowed mode.
|
||||
void setResizable(bool resize = false) override;
|
||||
|
||||
//! Minimizes the window.
|
||||
void minimizeWindow() override;
|
||||
|
||||
//! Maximizes the window.
|
||||
void maximizeWindow() override;
|
||||
|
||||
//! Restores the window size.
|
||||
void restoreWindow() override;
|
||||
|
||||
//! Checks if the window is maximized.
|
||||
bool isWindowMaximized() const override;
|
||||
|
||||
//! Checks if the Irrlicht window is running in fullscreen mode
|
||||
/** \return True if window is fullscreen. */
|
||||
bool isFullscreen() const override;
|
||||
|
||||
//! Get the position of this window on screen
|
||||
core::position2di getWindowPosition() override;
|
||||
|
||||
//! Activate any joysticks, and generate events for them.
|
||||
bool activateJoysticks(core::array<SJoystickInfo> &joystickInfo) override;
|
||||
|
||||
//! Get the device type
|
||||
E_DEVICE_TYPE getType() const override
|
||||
{
|
||||
return EIDT_SDL;
|
||||
}
|
||||
|
||||
//! Get the display density in dots per inch.
|
||||
float getDisplayDensity() const override;
|
||||
|
||||
void SwapWindow();
|
||||
|
||||
//! Implementation of the linux cursor control
|
||||
class CCursorControl : public gui::ICursorControl
|
||||
{
|
||||
public:
|
||||
CCursorControl(CIrrDeviceSDL *dev) :
|
||||
Device(dev), IsVisible(true)
|
||||
{
|
||||
initCursors();
|
||||
}
|
||||
|
||||
//! Changes the visible state of the mouse cursor.
|
||||
void setVisible(bool visible) override
|
||||
{
|
||||
IsVisible = visible;
|
||||
if (visible)
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
else {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns if the cursor is currently visible.
|
||||
bool isVisible() const override
|
||||
{
|
||||
return IsVisible;
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<f32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(f32 x, f32 y) override
|
||||
{
|
||||
setPosition((s32)(x * Device->Width), (s32)(y * Device->Height));
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<s32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(s32 x, s32 y) override
|
||||
{
|
||||
SDL_WarpMouseInWindow(Device->Window, x, y);
|
||||
|
||||
if (SDL_GetRelativeMouseMode()) {
|
||||
// There won't be an event for this warp (details on libsdl-org/SDL/issues/6034)
|
||||
Device->MouseX = x;
|
||||
Device->MouseY = y;
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
const core::position2d<s32> &getPosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateCursorPos();
|
||||
return CursorPos;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
core::position2d<f32> getRelativePosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateCursorPos();
|
||||
return core::position2d<f32>(CursorPos.X / (f32)Device->Width,
|
||||
CursorPos.Y / (f32)Device->Height);
|
||||
}
|
||||
|
||||
void setReferenceRect(core::rect<s32> *rect = 0) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setRelativeMode(bool relative) _IRR_OVERRIDE_
|
||||
{
|
||||
// Only change it when necessary, as it flushes mouse motion when enabled
|
||||
if (relative != SDL_GetRelativeMouseMode()) {
|
||||
if (relative)
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
else
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void setActiveIcon(gui::ECURSOR_ICON iconId) override
|
||||
{
|
||||
ActiveIcon = iconId;
|
||||
if (iconId > Cursors.size() || !Cursors[iconId]) {
|
||||
iconId = gui::ECI_NORMAL;
|
||||
if (iconId > Cursors.size() || !Cursors[iconId])
|
||||
return;
|
||||
}
|
||||
SDL_SetCursor(Cursors[iconId].get());
|
||||
}
|
||||
|
||||
gui::ECURSOR_ICON getActiveIcon() const override
|
||||
{
|
||||
return ActiveIcon;
|
||||
}
|
||||
|
||||
private:
|
||||
void updateCursorPos()
|
||||
{
|
||||
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
|
||||
EmscriptenPointerlockChangeEvent pointerlockStatus; // let's hope that test is not expensive ...
|
||||
if (emscripten_get_pointerlock_status(&pointerlockStatus) == EMSCRIPTEN_RESULT_SUCCESS) {
|
||||
if (pointerlockStatus.isActive) {
|
||||
CursorPos.X += Device->MouseXRel;
|
||||
CursorPos.Y += Device->MouseYRel;
|
||||
Device->MouseXRel = 0;
|
||||
Device->MouseYRel = 0;
|
||||
} else {
|
||||
CursorPos.X = Device->MouseX;
|
||||
CursorPos.Y = Device->MouseY;
|
||||
}
|
||||
}
|
||||
#else
|
||||
CursorPos.X = Device->MouseX;
|
||||
CursorPos.Y = Device->MouseY;
|
||||
|
||||
if (CursorPos.X < 0)
|
||||
CursorPos.X = 0;
|
||||
if (CursorPos.X > (s32)Device->Width)
|
||||
CursorPos.X = Device->Width;
|
||||
if (CursorPos.Y < 0)
|
||||
CursorPos.Y = 0;
|
||||
if (CursorPos.Y > (s32)Device->Height)
|
||||
CursorPos.Y = Device->Height;
|
||||
#endif
|
||||
}
|
||||
|
||||
void initCursors();
|
||||
|
||||
CIrrDeviceSDL *Device;
|
||||
core::position2d<s32> CursorPos;
|
||||
bool IsVisible;
|
||||
|
||||
struct CursorDeleter
|
||||
{
|
||||
void operator()(SDL_Cursor *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
SDL_FreeCursor(ptr);
|
||||
}
|
||||
};
|
||||
std::vector<std::unique_ptr<SDL_Cursor, CursorDeleter>> Cursors;
|
||||
gui::ECURSOR_ICON ActiveIcon = gui::ECURSOR_ICON::ECI_NORMAL;
|
||||
};
|
||||
|
||||
private:
|
||||
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
|
||||
static EM_BOOL MouseUpDownCallback(int eventType, const EmscriptenMouseEvent *event, void *userData);
|
||||
static EM_BOOL MouseEnterCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
|
||||
static EM_BOOL MouseLeaveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
|
||||
|
||||
#endif
|
||||
// Check if a key is a known special character with no side effects on text boxes.
|
||||
static bool keyIsKnownSpecial(EKEY_CODE key);
|
||||
|
||||
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
|
||||
static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
|
||||
|
||||
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
|
||||
void resetReceiveTextInputEvents();
|
||||
|
||||
//! create the driver
|
||||
void createDriver();
|
||||
|
||||
bool createWindow();
|
||||
|
||||
void createKeyMap();
|
||||
|
||||
void logAttributes();
|
||||
SDL_GLContext Context;
|
||||
SDL_Window *Window;
|
||||
int SDL_Flags;
|
||||
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
|
||||
core::array<SDL_Joystick *> Joysticks;
|
||||
#endif
|
||||
|
||||
s32 MouseX, MouseY;
|
||||
s32 MouseXRel, MouseYRel;
|
||||
u32 MouseButtonStates;
|
||||
|
||||
u32 Width, Height;
|
||||
|
||||
bool Resizable;
|
||||
|
||||
core::rect<s32> lastElemPos;
|
||||
|
||||
struct SKeyMap
|
||||
{
|
||||
SKeyMap() {}
|
||||
SKeyMap(s32 x11, s32 win32) :
|
||||
SDLKey(x11), Win32Key(win32)
|
||||
{
|
||||
}
|
||||
|
||||
s32 SDLKey;
|
||||
s32 Win32Key;
|
||||
|
||||
bool operator<(const SKeyMap &o) const
|
||||
{
|
||||
return SDLKey < o.SDLKey;
|
||||
}
|
||||
};
|
||||
|
||||
core::array<SKeyMap> KeyMap;
|
||||
SDL_SysWMinfo Info;
|
||||
|
||||
s32 CurrentTouchCount;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
||||
#endif // _IRR_COMPILE_WITH_SDL_DEVICE_
|
||||
376
src/CIrrDeviceStub.cpp
Normal file
376
src/CIrrDeviceStub.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "IEventReceiver.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "IGUIElement.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "os.h"
|
||||
#include "CTimer.h"
|
||||
#include "CLogger.h"
|
||||
#include "irrString.h"
|
||||
#include "IrrCompileConfig.h" // for IRRLICHT_SDK_VERSION
|
||||
|
||||
namespace irr
|
||||
{
|
||||
//! constructor
|
||||
CIrrDeviceStub::CIrrDeviceStub(const SIrrlichtCreationParameters ¶ms) :
|
||||
IrrlichtDevice(), VideoDriver(0), GUIEnvironment(0), SceneManager(0),
|
||||
Timer(0), CursorControl(0), UserReceiver(params.EventReceiver),
|
||||
Logger(0), Operator(0), FileSystem(0),
|
||||
InputReceivingSceneManager(0), ContextManager(0),
|
||||
CreationParams(params), Close(false)
|
||||
{
|
||||
Timer = new CTimer();
|
||||
if (os::Printer::Logger) {
|
||||
os::Printer::Logger->grab();
|
||||
Logger = (CLogger *)os::Printer::Logger;
|
||||
Logger->setReceiver(UserReceiver);
|
||||
} else {
|
||||
Logger = new CLogger(UserReceiver);
|
||||
os::Printer::Logger = Logger;
|
||||
}
|
||||
Logger->setLogLevel(CreationParams.LoggingLevel);
|
||||
|
||||
os::Printer::Logger = Logger;
|
||||
|
||||
FileSystem = io::createFileSystem();
|
||||
|
||||
core::stringc s = "Irrlicht Engine version ";
|
||||
s.append(getVersion());
|
||||
os::Printer::log(s.c_str(), ELL_INFORMATION);
|
||||
|
||||
checkVersion(params.SDK_version_do_not_use);
|
||||
}
|
||||
|
||||
CIrrDeviceStub::~CIrrDeviceStub()
|
||||
{
|
||||
if (GUIEnvironment)
|
||||
GUIEnvironment->drop();
|
||||
|
||||
if (SceneManager)
|
||||
SceneManager->drop();
|
||||
|
||||
if (VideoDriver)
|
||||
VideoDriver->drop();
|
||||
|
||||
if (ContextManager)
|
||||
ContextManager->drop();
|
||||
|
||||
if (FileSystem)
|
||||
FileSystem->drop();
|
||||
|
||||
if (InputReceivingSceneManager)
|
||||
InputReceivingSceneManager->drop();
|
||||
|
||||
if (CursorControl)
|
||||
CursorControl->drop();
|
||||
|
||||
if (Operator)
|
||||
Operator->drop();
|
||||
|
||||
CursorControl = 0;
|
||||
|
||||
if (Timer)
|
||||
Timer->drop();
|
||||
|
||||
if (Logger->drop())
|
||||
os::Printer::Logger = 0;
|
||||
}
|
||||
|
||||
void CIrrDeviceStub::createGUIAndScene()
|
||||
{
|
||||
// create gui environment
|
||||
GUIEnvironment = gui::createGUIEnvironment(FileSystem, VideoDriver, Operator);
|
||||
|
||||
// create Scene manager
|
||||
SceneManager = scene::createSceneManager(VideoDriver, CursorControl);
|
||||
|
||||
setEventReceiver(UserReceiver);
|
||||
}
|
||||
|
||||
//! returns the video driver
|
||||
video::IVideoDriver *CIrrDeviceStub::getVideoDriver()
|
||||
{
|
||||
return VideoDriver;
|
||||
}
|
||||
|
||||
//! return file system
|
||||
io::IFileSystem *CIrrDeviceStub::getFileSystem()
|
||||
{
|
||||
return FileSystem;
|
||||
}
|
||||
|
||||
//! returns the gui environment
|
||||
gui::IGUIEnvironment *CIrrDeviceStub::getGUIEnvironment()
|
||||
{
|
||||
return GUIEnvironment;
|
||||
}
|
||||
|
||||
//! returns the scene manager
|
||||
scene::ISceneManager *CIrrDeviceStub::getSceneManager()
|
||||
{
|
||||
return SceneManager;
|
||||
}
|
||||
|
||||
//! \return Returns a pointer to the ITimer object. With it the
|
||||
//! current Time can be received.
|
||||
ITimer *CIrrDeviceStub::getTimer()
|
||||
{
|
||||
return Timer;
|
||||
}
|
||||
|
||||
//! Sets the window icon.
|
||||
bool CIrrDeviceStub::setWindowIcon(const video::IImage *img)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Returns the version of the engine.
|
||||
const char *CIrrDeviceStub::getVersion() const
|
||||
{
|
||||
return IRRLICHT_SDK_VERSION;
|
||||
}
|
||||
|
||||
//! \return Returns a pointer to the mouse cursor control interface.
|
||||
gui::ICursorControl *CIrrDeviceStub::getCursorControl()
|
||||
{
|
||||
return CursorControl;
|
||||
}
|
||||
|
||||
//! return the context manager
|
||||
video::IContextManager *CIrrDeviceStub::getContextManager()
|
||||
{
|
||||
return ContextManager;
|
||||
}
|
||||
|
||||
//! checks version of sdk and prints warning if there might be a problem
|
||||
bool CIrrDeviceStub::checkVersion(const char *version)
|
||||
{
|
||||
if (strcmp(getVersion(), version)) {
|
||||
core::stringc w;
|
||||
w = "Warning: The library version of the Irrlicht Engine (";
|
||||
w += getVersion();
|
||||
w += ") does not match the version the application was compiled with (";
|
||||
w += version;
|
||||
w += "). This may cause problems.";
|
||||
os::Printer::log(w.c_str(), ELL_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Compares to the last call of this function to return double and triple clicks.
|
||||
u32 CIrrDeviceStub::checkSuccessiveClicks(s32 mouseX, s32 mouseY, EMOUSE_INPUT_EVENT inputEvent)
|
||||
{
|
||||
const s32 MAX_MOUSEMOVE = 3;
|
||||
|
||||
irr::u32 clickTime = getTimer()->getRealTime();
|
||||
|
||||
if ((clickTime - MouseMultiClicks.LastClickTime) < MouseMultiClicks.DoubleClickTime && core::abs_(MouseMultiClicks.LastClick.X - mouseX) <= MAX_MOUSEMOVE && core::abs_(MouseMultiClicks.LastClick.Y - mouseY) <= MAX_MOUSEMOVE && MouseMultiClicks.CountSuccessiveClicks < 3 && MouseMultiClicks.LastMouseInputEvent == inputEvent) {
|
||||
++MouseMultiClicks.CountSuccessiveClicks;
|
||||
} else {
|
||||
MouseMultiClicks.CountSuccessiveClicks = 1;
|
||||
}
|
||||
|
||||
MouseMultiClicks.LastMouseInputEvent = inputEvent;
|
||||
MouseMultiClicks.LastClickTime = clickTime;
|
||||
MouseMultiClicks.LastClick.X = mouseX;
|
||||
MouseMultiClicks.LastClick.Y = mouseY;
|
||||
|
||||
return MouseMultiClicks.CountSuccessiveClicks;
|
||||
}
|
||||
|
||||
//! send the event to the right receiver
|
||||
bool CIrrDeviceStub::postEventFromUser(const SEvent &event)
|
||||
{
|
||||
bool absorbed = false;
|
||||
|
||||
if (UserReceiver)
|
||||
absorbed = UserReceiver->OnEvent(event);
|
||||
|
||||
if (!absorbed && GUIEnvironment)
|
||||
absorbed = GUIEnvironment->postEventFromUser(event);
|
||||
|
||||
scene::ISceneManager *inputReceiver = InputReceivingSceneManager;
|
||||
if (!inputReceiver)
|
||||
inputReceiver = SceneManager;
|
||||
|
||||
if (!absorbed && inputReceiver)
|
||||
absorbed = inputReceiver->postEventFromUser(event);
|
||||
|
||||
return absorbed;
|
||||
}
|
||||
|
||||
//! Sets a new event receiver to receive events
|
||||
void CIrrDeviceStub::setEventReceiver(IEventReceiver *receiver)
|
||||
{
|
||||
UserReceiver = receiver;
|
||||
Logger->setReceiver(receiver);
|
||||
if (GUIEnvironment)
|
||||
GUIEnvironment->setUserEventReceiver(receiver);
|
||||
}
|
||||
|
||||
//! Returns poinhter to the current event receiver. Returns 0 if there is none.
|
||||
IEventReceiver *CIrrDeviceStub::getEventReceiver()
|
||||
{
|
||||
return UserReceiver;
|
||||
}
|
||||
|
||||
//! \return Returns a pointer to the logger.
|
||||
ILogger *CIrrDeviceStub::getLogger()
|
||||
{
|
||||
return Logger;
|
||||
}
|
||||
|
||||
//! Returns the operation system opertator object.
|
||||
IOSOperator *CIrrDeviceStub::getOSOperator()
|
||||
{
|
||||
return Operator;
|
||||
}
|
||||
|
||||
//! Sets the input receiving scene manager.
|
||||
void CIrrDeviceStub::setInputReceivingSceneManager(scene::ISceneManager *sceneManager)
|
||||
{
|
||||
if (sceneManager)
|
||||
sceneManager->grab();
|
||||
if (InputReceivingSceneManager)
|
||||
InputReceivingSceneManager->drop();
|
||||
|
||||
InputReceivingSceneManager = sceneManager;
|
||||
}
|
||||
|
||||
//! Checks if the window is maximized.
|
||||
bool CIrrDeviceStub::isWindowMaximized() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Checks if the window is running in fullscreen mode
|
||||
bool CIrrDeviceStub::isFullscreen() const
|
||||
{
|
||||
return CreationParams.Fullscreen;
|
||||
}
|
||||
|
||||
//! returns color format
|
||||
video::ECOLOR_FORMAT CIrrDeviceStub::getColorFormat() const
|
||||
{
|
||||
return video::ECF_R5G6B5;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::activateJoysticks(core::array<SJoystickInfo> &joystickInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::activateAccelerometer(float updateInterval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::deactivateAccelerometer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isAccelerometerActive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isAccelerometerAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::activateGyroscope(float updateInterval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::deactivateGyroscope()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isGyroscopeActive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isGyroscopeAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::activateDeviceMotion(float updateInterval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::deactivateDeviceMotion()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isDeviceMotionActive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! No-op in this implementation
|
||||
bool CIrrDeviceStub::isDeviceMotionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Set the maximal elapsed time between 2 clicks to generate doubleclicks for the mouse. It also affects tripleclick behavior.
|
||||
void CIrrDeviceStub::setDoubleClickTime(u32 timeMs)
|
||||
{
|
||||
MouseMultiClicks.DoubleClickTime = timeMs;
|
||||
}
|
||||
|
||||
//! Get the maximal elapsed time between 2 clicks to generate double- and tripleclicks for the mouse.
|
||||
u32 CIrrDeviceStub::getDoubleClickTime() const
|
||||
{
|
||||
return MouseMultiClicks.DoubleClickTime;
|
||||
}
|
||||
|
||||
//! Remove all messages pending in the system message loop
|
||||
void CIrrDeviceStub::clearSystemMessages()
|
||||
{
|
||||
}
|
||||
|
||||
//! Get the display density in dots per inch.
|
||||
float CIrrDeviceStub::getDisplayDensity() const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
//! Checks whether the input device should take input from the IME
|
||||
bool CIrrDeviceStub::acceptsIME()
|
||||
{
|
||||
if (!GUIEnvironment)
|
||||
return false;
|
||||
gui::IGUIElement *elem = GUIEnvironment->getFocus();
|
||||
return elem && elem->acceptsIME();
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
204
src/CIrrDeviceStub.h
Normal file
204
src/CIrrDeviceStub.h
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "SIrrCreationParameters.h"
|
||||
#include "IContextManager.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
// lots of prototypes:
|
||||
class ILogger;
|
||||
class CLogger;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class IGUIEnvironment;
|
||||
IGUIEnvironment *createGUIEnvironment(io::IFileSystem *fs,
|
||||
video::IVideoDriver *Driver, IOSOperator *op);
|
||||
}
|
||||
|
||||
namespace scene
|
||||
{
|
||||
ISceneManager *createSceneManager(video::IVideoDriver *driver, gui::ICursorControl *cc);
|
||||
}
|
||||
|
||||
namespace io
|
||||
{
|
||||
IFileSystem *createFileSystem();
|
||||
}
|
||||
|
||||
namespace video
|
||||
{
|
||||
IVideoDriver *createNullDriver(io::IFileSystem *io, const core::dimension2d<u32> &screenSize);
|
||||
}
|
||||
|
||||
//! Stub for an Irrlicht Device implementation
|
||||
class CIrrDeviceStub : public IrrlichtDevice
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
CIrrDeviceStub(const SIrrlichtCreationParameters ¶m);
|
||||
|
||||
//! destructor
|
||||
virtual ~CIrrDeviceStub();
|
||||
|
||||
//! returns the video driver
|
||||
video::IVideoDriver *getVideoDriver() override;
|
||||
|
||||
//! return file system
|
||||
io::IFileSystem *getFileSystem() override;
|
||||
|
||||
//! returns the gui environment
|
||||
gui::IGUIEnvironment *getGUIEnvironment() override;
|
||||
|
||||
//! returns the scene manager
|
||||
scene::ISceneManager *getSceneManager() override;
|
||||
|
||||
//! \return Returns a pointer to the mouse cursor control interface.
|
||||
gui::ICursorControl *getCursorControl() override;
|
||||
|
||||
//! return the context manager
|
||||
video::IContextManager *getContextManager() override;
|
||||
|
||||
//! Returns a pointer to the ITimer object. With it the current Time can be received.
|
||||
ITimer *getTimer() override;
|
||||
|
||||
//! Sets the window icon.
|
||||
bool setWindowIcon(const video::IImage *img) override;
|
||||
|
||||
//! Returns the version of the engine.
|
||||
const char *getVersion() const override;
|
||||
|
||||
//! send the event to the right receiver
|
||||
bool postEventFromUser(const SEvent &event) override;
|
||||
|
||||
//! Sets a new event receiver to receive events
|
||||
void setEventReceiver(IEventReceiver *receiver) override;
|
||||
|
||||
//! Returns pointer to the current event receiver. Returns 0 if there is none.
|
||||
IEventReceiver *getEventReceiver() override;
|
||||
|
||||
//! Sets the input receiving scene manager.
|
||||
/** If set to null, the main scene manager (returned by GetSceneManager()) will receive the input */
|
||||
void setInputReceivingSceneManager(scene::ISceneManager *sceneManager) override;
|
||||
|
||||
//! Returns a pointer to the logger.
|
||||
ILogger *getLogger() override;
|
||||
|
||||
//! Returns the operation system opertator object.
|
||||
IOSOperator *getOSOperator() override;
|
||||
|
||||
//! Checks if the window is maximized.
|
||||
bool isWindowMaximized() const override;
|
||||
|
||||
//! Checks if the window is running in fullscreen mode.
|
||||
bool isFullscreen() const override;
|
||||
|
||||
//! get color format of the current window
|
||||
video::ECOLOR_FORMAT getColorFormat() const override;
|
||||
|
||||
//! Activate any joysticks, and generate events for them.
|
||||
bool activateJoysticks(core::array<SJoystickInfo> &joystickInfo) override;
|
||||
|
||||
//! Activate accelerometer.
|
||||
bool activateAccelerometer(float updateInterval = 0.016666f) override;
|
||||
|
||||
//! Deactivate accelerometer.
|
||||
bool deactivateAccelerometer() override;
|
||||
|
||||
//! Is accelerometer active.
|
||||
bool isAccelerometerActive() override;
|
||||
|
||||
//! Is accelerometer available.
|
||||
bool isAccelerometerAvailable() override;
|
||||
|
||||
//! Activate gyroscope.
|
||||
bool activateGyroscope(float updateInterval = 0.016666f) override;
|
||||
|
||||
//! Deactivate gyroscope.
|
||||
bool deactivateGyroscope() override;
|
||||
|
||||
//! Is gyroscope active.
|
||||
bool isGyroscopeActive() override;
|
||||
|
||||
//! Is gyroscope available.
|
||||
bool isGyroscopeAvailable() override;
|
||||
|
||||
//! Activate device motion.
|
||||
bool activateDeviceMotion(float updateInterval = 0.016666f) override;
|
||||
|
||||
//! Deactivate device motion.
|
||||
bool deactivateDeviceMotion() override;
|
||||
|
||||
//! Is device motion active.
|
||||
bool isDeviceMotionActive() override;
|
||||
|
||||
//! Is device motion available.
|
||||
bool isDeviceMotionAvailable() override;
|
||||
|
||||
//! Set the maximal elapsed time between 2 clicks to generate doubleclicks for the mouse. It also affects tripleclick behavior.
|
||||
//! When set to 0 no double- and tripleclicks will be generated.
|
||||
void setDoubleClickTime(u32 timeMs) override;
|
||||
|
||||
//! Get the maximal elapsed time between 2 clicks to generate double- and tripleclicks for the mouse.
|
||||
u32 getDoubleClickTime() const override;
|
||||
|
||||
//! Remove all messages pending in the system message loop
|
||||
void clearSystemMessages() override;
|
||||
|
||||
//! Get the display density in dots per inch.
|
||||
float getDisplayDensity() const override;
|
||||
|
||||
//! Resize the render window.
|
||||
void setWindowSize(const irr::core::dimension2d<u32> &size) override {}
|
||||
|
||||
protected:
|
||||
void createGUIAndScene();
|
||||
|
||||
//! checks version of SDK and prints warning if there might be a problem
|
||||
bool checkVersion(const char *version);
|
||||
|
||||
//! Compares to the last call of this function to return double and triple clicks.
|
||||
/** Needed for win32 device event handling
|
||||
\return Returns only 1,2 or 3. A 4th click will start with 1 again.
|
||||
*/
|
||||
virtual u32 checkSuccessiveClicks(s32 mouseX, s32 mouseY, EMOUSE_INPUT_EVENT inputEvent);
|
||||
|
||||
//! Checks whether the input device should take input from the IME
|
||||
bool acceptsIME();
|
||||
|
||||
video::IVideoDriver *VideoDriver;
|
||||
gui::IGUIEnvironment *GUIEnvironment;
|
||||
scene::ISceneManager *SceneManager;
|
||||
ITimer *Timer;
|
||||
gui::ICursorControl *CursorControl;
|
||||
IEventReceiver *UserReceiver;
|
||||
CLogger *Logger;
|
||||
IOSOperator *Operator;
|
||||
io::IFileSystem *FileSystem;
|
||||
scene::ISceneManager *InputReceivingSceneManager;
|
||||
|
||||
struct SMouseMultiClicks
|
||||
{
|
||||
SMouseMultiClicks() :
|
||||
DoubleClickTime(500), CountSuccessiveClicks(0), LastClickTime(0), LastMouseInputEvent(EMIE_COUNT)
|
||||
{
|
||||
}
|
||||
|
||||
u32 DoubleClickTime;
|
||||
u32 CountSuccessiveClicks;
|
||||
u32 LastClickTime;
|
||||
core::position2di LastClick;
|
||||
EMOUSE_INPUT_EVENT LastMouseInputEvent;
|
||||
};
|
||||
SMouseMultiClicks MouseMultiClicks;
|
||||
video::IContextManager *ContextManager;
|
||||
SIrrlichtCreationParameters CreationParams;
|
||||
bool Close;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
1474
src/CIrrDeviceWin32.cpp
Normal file
1474
src/CIrrDeviceWin32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
405
src/CIrrDeviceWin32.h
Normal file
405
src/CIrrDeviceWin32.h
Normal file
@@ -0,0 +1,405 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
|
||||
#include "CIrrDeviceStub.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h> // For JOYCAPS
|
||||
#include <windowsx.h>
|
||||
#if !defined(GET_X_LPARAM)
|
||||
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
|
||||
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
|
||||
#endif
|
||||
|
||||
namespace irr
|
||||
{
|
||||
struct SJoystickWin32Control;
|
||||
|
||||
class CIrrDeviceWin32 : public CIrrDeviceStub
|
||||
{
|
||||
friend struct SJoystickWin32Control;
|
||||
|
||||
public:
|
||||
//! constructor
|
||||
CIrrDeviceWin32(const SIrrlichtCreationParameters ¶ms);
|
||||
|
||||
//! destructor
|
||||
virtual ~CIrrDeviceWin32();
|
||||
|
||||
//! runs the device. Returns false if device wants to be deleted
|
||||
bool run() override;
|
||||
|
||||
//! Cause the device to temporarily pause execution and let other processes to run
|
||||
// This should bring down processor usage without major performance loss for Irrlicht
|
||||
void yield() override;
|
||||
|
||||
//! Pause execution and let other processes to run for a specified amount of time.
|
||||
void sleep(u32 timeMs, bool pauseTimer) override;
|
||||
|
||||
//! sets the caption of the window
|
||||
void setWindowCaption(const wchar_t *text) override;
|
||||
|
||||
//! Sets the window icon.
|
||||
bool setWindowIcon(const video::IImage *img) override;
|
||||
|
||||
//! returns if window is active. if not, nothing need to be drawn
|
||||
bool isWindowActive() const override;
|
||||
|
||||
//! returns if window has focus
|
||||
bool isWindowFocused() const override;
|
||||
|
||||
//! returns if window is minimized
|
||||
bool isWindowMinimized() const override;
|
||||
|
||||
//! returns last state from maximizeWindow() and restoreWindow()
|
||||
bool isWindowMaximized() const override;
|
||||
|
||||
//! notifies the device that it should close itself
|
||||
void closeDevice() override;
|
||||
|
||||
//! Notifies the device, that it has been resized
|
||||
/** Must be publis as it is called from free function (event handler) */
|
||||
void OnResized();
|
||||
|
||||
//! Sets if the window should be resizable in windowed mode.
|
||||
void setResizable(bool resize = false) override;
|
||||
|
||||
//! Resize the render window.
|
||||
void setWindowSize(const irr::core::dimension2d<u32> &size) override;
|
||||
|
||||
//! Minimizes the window.
|
||||
void minimizeWindow() override;
|
||||
|
||||
//! Maximizes the window.
|
||||
void maximizeWindow() override;
|
||||
|
||||
//! Restores the window size.
|
||||
void restoreWindow() override;
|
||||
|
||||
//! Get the position of the window on screen
|
||||
core::position2di getWindowPosition() override;
|
||||
|
||||
//! Activate any joysticks, and generate events for them.
|
||||
bool activateJoysticks(core::array<SJoystickInfo> &joystickInfo) override;
|
||||
|
||||
//! Remove all messages pending in the system message loop
|
||||
void clearSystemMessages() override;
|
||||
|
||||
//! Get the device type
|
||||
E_DEVICE_TYPE getType() const override
|
||||
{
|
||||
return EIDT_WIN32;
|
||||
}
|
||||
|
||||
//! Get the display density in dots per inch.
|
||||
float getDisplayDensity() const override;
|
||||
|
||||
//! Compares to the last call of this function to return double and triple clicks.
|
||||
//! \return Returns only 1,2 or 3. A 4th click will start with 1 again.
|
||||
u32 checkSuccessiveClicks(s32 mouseX, s32 mouseY, EMOUSE_INPUT_EVENT inputEvent) override
|
||||
{
|
||||
// we just have to make it public
|
||||
return CIrrDeviceStub::checkSuccessiveClicks(mouseX, mouseY, inputEvent);
|
||||
}
|
||||
|
||||
//! Switch to fullscreen
|
||||
bool switchToFullScreen();
|
||||
|
||||
// convert an Irrlicht texture to a windows cursor
|
||||
HCURSOR TextureToCursor(HWND hwnd, irr::video::ITexture *tex, const core::rect<s32> &sourceRect, const core::position2d<s32> &hotspot);
|
||||
|
||||
//! Implementation of the win32 cursor control
|
||||
class CCursorControl : public gui::ICursorControl
|
||||
{
|
||||
public:
|
||||
CCursorControl(CIrrDeviceWin32 *device, const core::dimension2d<u32> &wsize, HWND hwnd, bool fullscreen);
|
||||
~CCursorControl();
|
||||
|
||||
//! Changes the visible state of the mouse cursor.
|
||||
void setVisible(bool visible) override
|
||||
{
|
||||
CURSORINFO info;
|
||||
info.cbSize = sizeof(CURSORINFO);
|
||||
BOOL gotCursorInfo = GetCursorInfo(&info);
|
||||
while (gotCursorInfo) {
|
||||
#ifdef CURSOR_SUPPRESSED
|
||||
// Since Windows 8 the cursor can be suppressed by a touch interface
|
||||
if (visible && info.flags == CURSOR_SUPPRESSED) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if ((visible && info.flags == CURSOR_SHOWING) || // visible
|
||||
(!visible && info.flags == 0)) // hidden
|
||||
{
|
||||
break;
|
||||
}
|
||||
// this only increases an internal
|
||||
// display counter in windows, so it
|
||||
// might have to be called some more
|
||||
const int showResult = ShowCursor(visible);
|
||||
// if result has correct sign we can
|
||||
// stop here as well
|
||||
if ((!visible && showResult < 0) ||
|
||||
(visible && showResult >= 0))
|
||||
break;
|
||||
// yes, it really must be set each time
|
||||
info.cbSize = sizeof(CURSORINFO);
|
||||
gotCursorInfo = GetCursorInfo(&info);
|
||||
|
||||
#ifdef CURSOR_SUPPRESSED
|
||||
// Not sure if a cursor which we tried to hide still can be suppressed.
|
||||
// I have no touch-display for testing this and MSDN doesn't describe it.
|
||||
// But adding this check shouldn't hurt and might prevent an endless loop.
|
||||
if (!visible && info.flags == CURSOR_SUPPRESSED) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
IsVisible = visible;
|
||||
}
|
||||
|
||||
//! Returns if the cursor is currently visible.
|
||||
bool isVisible() const override
|
||||
{
|
||||
return IsVisible;
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<f32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(f32 x, f32 y) override
|
||||
{
|
||||
if (!UseReferenceRect)
|
||||
setPosition(core::round32(x * WindowSize.Width), core::round32(y * WindowSize.Height));
|
||||
else
|
||||
setPosition(core::round32(x * ReferenceRect.getWidth()), core::round32(y * ReferenceRect.getHeight()));
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(const core::position2d<s32> &pos) override
|
||||
{
|
||||
setPosition(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
//! Sets the new position of the cursor.
|
||||
void setPosition(s32 x, s32 y) override
|
||||
{
|
||||
if (UseReferenceRect) {
|
||||
SetCursorPos(ReferenceRect.UpperLeftCorner.X + x,
|
||||
ReferenceRect.UpperLeftCorner.Y + y);
|
||||
} else {
|
||||
RECT rect;
|
||||
if (GetWindowRect(HWnd, &rect))
|
||||
SetCursorPos(x + rect.left + BorderX, y + rect.top + BorderY);
|
||||
}
|
||||
|
||||
CursorPos.X = x;
|
||||
CursorPos.Y = y;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
const core::position2d<s32> &getPosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateInternalCursorPosition();
|
||||
return CursorPos;
|
||||
}
|
||||
|
||||
//! Returns the current position of the mouse cursor.
|
||||
core::position2d<f32> getRelativePosition(bool updateCursor) override
|
||||
{
|
||||
if (updateCursor)
|
||||
updateInternalCursorPosition();
|
||||
|
||||
if (!UseReferenceRect) {
|
||||
return core::position2d<f32>(CursorPos.X * InvWindowSize.Width,
|
||||
CursorPos.Y * InvWindowSize.Height);
|
||||
}
|
||||
|
||||
return core::position2d<f32>(CursorPos.X / (f32)ReferenceRect.getWidth(),
|
||||
CursorPos.Y / (f32)ReferenceRect.getHeight());
|
||||
}
|
||||
|
||||
//! Sets an absolute reference rect for calculating the cursor position.
|
||||
void setReferenceRect(core::rect<s32> *rect = 0) override
|
||||
{
|
||||
if (rect) {
|
||||
ReferenceRect = *rect;
|
||||
UseReferenceRect = true;
|
||||
|
||||
// prevent division through zero and uneven sizes
|
||||
|
||||
if (!ReferenceRect.getHeight() || ReferenceRect.getHeight() % 2)
|
||||
ReferenceRect.LowerRightCorner.Y += 1;
|
||||
|
||||
if (!ReferenceRect.getWidth() || ReferenceRect.getWidth() % 2)
|
||||
ReferenceRect.LowerRightCorner.X += 1;
|
||||
} else
|
||||
UseReferenceRect = false;
|
||||
}
|
||||
|
||||
/** Used to notify the cursor that the window was resized. */
|
||||
void OnResize(const core::dimension2d<u32> &size)
|
||||
{
|
||||
WindowSize = size;
|
||||
if (size.Width != 0)
|
||||
InvWindowSize.Width = 1.0f / size.Width;
|
||||
else
|
||||
InvWindowSize.Width = 0.f;
|
||||
|
||||
if (size.Height != 0)
|
||||
InvWindowSize.Height = 1.0f / size.Height;
|
||||
else
|
||||
InvWindowSize.Height = 0.f;
|
||||
}
|
||||
|
||||
/** Used to notify the cursor that the window resizable settings changed. */
|
||||
void updateBorderSize(bool fullscreen, bool resizable)
|
||||
{
|
||||
if (!fullscreen) {
|
||||
s32 paddingBorder = 0;
|
||||
#ifdef SM_CXPADDEDBORDER
|
||||
paddingBorder = GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||
#endif
|
||||
|
||||
if (resizable) {
|
||||
BorderX = GetSystemMetrics(SM_CXSIZEFRAME) + paddingBorder;
|
||||
BorderY = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME) + paddingBorder;
|
||||
} else {
|
||||
BorderX = GetSystemMetrics(SM_CXDLGFRAME) + paddingBorder;
|
||||
BorderY = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME) + paddingBorder;
|
||||
}
|
||||
} else {
|
||||
BorderX = BorderY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//! Sets the active cursor icon
|
||||
void setActiveIcon(gui::ECURSOR_ICON iconId) override;
|
||||
|
||||
//! Gets the currently active icon
|
||||
gui::ECURSOR_ICON getActiveIcon() const override
|
||||
{
|
||||
return ActiveIcon;
|
||||
}
|
||||
|
||||
//! Add a custom sprite as cursor icon.
|
||||
gui::ECURSOR_ICON addIcon(const gui::SCursorSprite &icon) override;
|
||||
|
||||
//! replace the given cursor icon.
|
||||
void changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite &icon) override;
|
||||
|
||||
//! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work.
|
||||
core::dimension2di getSupportedIconSize() const override;
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
//! Updates the internal cursor position
|
||||
void updateInternalCursorPosition()
|
||||
{
|
||||
POINT p;
|
||||
if (!GetCursorPos(&p)) {
|
||||
DWORD xy = GetMessagePos();
|
||||
p.x = GET_X_LPARAM(xy);
|
||||
p.y = GET_Y_LPARAM(xy);
|
||||
}
|
||||
|
||||
if (UseReferenceRect) {
|
||||
CursorPos.X = p.x - ReferenceRect.UpperLeftCorner.X;
|
||||
CursorPos.Y = p.y - ReferenceRect.UpperLeftCorner.Y;
|
||||
} else {
|
||||
RECT rect;
|
||||
if (GetWindowRect(HWnd, &rect)) {
|
||||
CursorPos.X = p.x - rect.left - BorderX;
|
||||
CursorPos.Y = p.y - rect.top - BorderY;
|
||||
} else {
|
||||
// window seems not to be existent, so set cursor to
|
||||
// a negative value
|
||||
CursorPos.X = -1;
|
||||
CursorPos.Y = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CIrrDeviceWin32 *Device;
|
||||
core::position2d<s32> CursorPos;
|
||||
core::dimension2d<u32> WindowSize;
|
||||
core::dimension2d<f32> InvWindowSize;
|
||||
HWND HWnd;
|
||||
|
||||
s32 BorderX, BorderY;
|
||||
core::rect<s32> ReferenceRect;
|
||||
bool UseReferenceRect;
|
||||
bool IsVisible;
|
||||
|
||||
struct CursorFrameW32
|
||||
{
|
||||
CursorFrameW32() :
|
||||
IconHW(0) {}
|
||||
CursorFrameW32(HCURSOR icon) :
|
||||
IconHW(icon) {}
|
||||
|
||||
HCURSOR IconHW; // hardware cursor
|
||||
};
|
||||
|
||||
struct CursorW32
|
||||
{
|
||||
CursorW32() {}
|
||||
explicit CursorW32(HCURSOR iconHw, u32 frameTime = 0) :
|
||||
FrameTime(frameTime)
|
||||
{
|
||||
Frames.push_back(CursorFrameW32(iconHw));
|
||||
}
|
||||
core::array<CursorFrameW32> Frames;
|
||||
u32 FrameTime;
|
||||
};
|
||||
|
||||
core::array<CursorW32> Cursors;
|
||||
gui::ECURSOR_ICON ActiveIcon;
|
||||
u32 ActiveIconStartTime;
|
||||
|
||||
void initCursors();
|
||||
};
|
||||
|
||||
//! returns the win32 cursor control
|
||||
CCursorControl *getWin32CursorControl();
|
||||
|
||||
private:
|
||||
//! create the driver
|
||||
void createDriver();
|
||||
|
||||
//! Process system events
|
||||
void handleSystemMessages();
|
||||
|
||||
void getWindowsVersion(core::stringc &version);
|
||||
|
||||
void resizeIfNecessary();
|
||||
|
||||
DWORD getWindowStyle(bool fullscreen, bool resizable) const;
|
||||
|
||||
HWND HWnd;
|
||||
|
||||
bool Resized;
|
||||
bool ExternalWindow;
|
||||
CCursorControl *Win32CursorControl;
|
||||
|
||||
SJoystickWin32Control *JoyControl;
|
||||
|
||||
bool WindowMaximized;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
||||
#endif // _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
||||
115
src/CLimitReadFile.cpp
Normal file
115
src/CLimitReadFile.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CLimitReadFile.h"
|
||||
#include "irrMath.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
CLimitReadFile::CLimitReadFile(IReadFile *alreadyOpenedFile, long pos,
|
||||
long areaSize, const io::path &name) :
|
||||
Filename(name),
|
||||
AreaStart(0), AreaEnd(0), Pos(0),
|
||||
File(alreadyOpenedFile)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CLimitReadFile");
|
||||
#endif
|
||||
|
||||
if (File) {
|
||||
File->grab();
|
||||
AreaStart = pos;
|
||||
AreaEnd = AreaStart + areaSize;
|
||||
}
|
||||
}
|
||||
|
||||
CLimitReadFile::~CLimitReadFile()
|
||||
{
|
||||
if (File)
|
||||
File->drop();
|
||||
}
|
||||
|
||||
//! returns how much was read
|
||||
size_t CLimitReadFile::read(void *buffer, size_t sizeToRead)
|
||||
{
|
||||
if (0 == File)
|
||||
return 0;
|
||||
|
||||
#if 1
|
||||
long r = AreaStart + Pos;
|
||||
long toRead = core::min_(AreaEnd, r + (long)sizeToRead) - core::max_(AreaStart, r);
|
||||
if (toRead < 0)
|
||||
return 0;
|
||||
File->seek(r);
|
||||
r = (long)File->read(buffer, toRead);
|
||||
Pos += r;
|
||||
return r;
|
||||
#else
|
||||
const long pos = File->getPos();
|
||||
|
||||
if (pos >= AreaEnd)
|
||||
return 0;
|
||||
|
||||
if (pos + (long)sizeToRead >= AreaEnd)
|
||||
sizeToRead = AreaEnd - pos;
|
||||
|
||||
return File->read(buffer, sizeToRead);
|
||||
#endif
|
||||
}
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
bool CLimitReadFile::seek(long finalPos, bool relativeMovement)
|
||||
{
|
||||
#if 1
|
||||
Pos = core::s32_clamp(finalPos + (relativeMovement ? Pos : 0), 0, AreaEnd - AreaStart);
|
||||
return true;
|
||||
#else
|
||||
const long pos = File->getPos();
|
||||
|
||||
if (relativeMovement) {
|
||||
if (pos + finalPos > AreaEnd)
|
||||
finalPos = AreaEnd - pos;
|
||||
} else {
|
||||
finalPos += AreaStart;
|
||||
if (finalPos > AreaEnd)
|
||||
return false;
|
||||
}
|
||||
|
||||
return File->seek(finalPos, relativeMovement);
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns size of file
|
||||
long CLimitReadFile::getSize() const
|
||||
{
|
||||
return AreaEnd - AreaStart;
|
||||
}
|
||||
|
||||
//! returns where in the file we are.
|
||||
long CLimitReadFile::getPos() const
|
||||
{
|
||||
#if 1
|
||||
return Pos;
|
||||
#else
|
||||
return File->getPos() - AreaStart;
|
||||
#endif
|
||||
}
|
||||
|
||||
//! returns name of file
|
||||
const io::path &CLimitReadFile::getFileName() const
|
||||
{
|
||||
return Filename;
|
||||
}
|
||||
|
||||
IReadFile *createLimitReadFile(const io::path &fileName, IReadFile *alreadyOpenedFile, long pos, long areaSize)
|
||||
{
|
||||
return new CLimitReadFile(alreadyOpenedFile, pos, areaSize, fileName);
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
62
src/CLimitReadFile.h
Normal file
62
src/CLimitReadFile.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IReadFile.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
class CUnicodeConverter;
|
||||
|
||||
namespace io
|
||||
{
|
||||
|
||||
/*! this is a read file, which is limited to some boundaries,
|
||||
so that it may only start from a certain file position
|
||||
and may only read until a certain file position.
|
||||
This can be useful, for example for reading uncompressed files
|
||||
in an archive (zip, tar).
|
||||
!*/
|
||||
class CLimitReadFile : public IReadFile
|
||||
{
|
||||
public:
|
||||
CLimitReadFile(IReadFile *alreadyOpenedFile, long pos, long areaSize, const io::path &name);
|
||||
|
||||
virtual ~CLimitReadFile();
|
||||
|
||||
//! returns how much was read
|
||||
size_t read(void *buffer, size_t sizeToRead) override;
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
//! if relativeMovement==true, the pos is changed relative to current pos,
|
||||
//! otherwise from begin of file
|
||||
bool seek(long finalPos, bool relativeMovement = false) override;
|
||||
|
||||
//! returns size of file
|
||||
long getSize() const override;
|
||||
|
||||
//! returns where in the file we are.
|
||||
long getPos() const override;
|
||||
|
||||
//! returns name of file
|
||||
const io::path &getFileName() const override;
|
||||
|
||||
//! Get the type of the class implementing this interface
|
||||
EREAD_FILE_TYPE getType() const override
|
||||
{
|
||||
return ERFT_LIMIT_READ_FILE;
|
||||
}
|
||||
|
||||
private:
|
||||
io::path Filename;
|
||||
long AreaStart;
|
||||
long AreaEnd;
|
||||
long Pos;
|
||||
IReadFile *File;
|
||||
};
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
66
src/CLogger.cpp
Normal file
66
src/CLogger.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CLogger.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
CLogger::CLogger(IEventReceiver *r) :
|
||||
LogLevel(ELL_INFORMATION), Receiver(r)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CLogger");
|
||||
#endif
|
||||
}
|
||||
|
||||
//! Returns the current set log level.
|
||||
ELOG_LEVEL CLogger::getLogLevel() const
|
||||
{
|
||||
return LogLevel;
|
||||
}
|
||||
|
||||
//! Sets a new log level.
|
||||
void CLogger::setLogLevel(ELOG_LEVEL ll)
|
||||
{
|
||||
LogLevel = ll;
|
||||
}
|
||||
|
||||
//! Prints out a text into the log
|
||||
void CLogger::log(const c8 *text, ELOG_LEVEL ll)
|
||||
{
|
||||
if (ll < LogLevel)
|
||||
return;
|
||||
|
||||
if (Receiver) {
|
||||
SEvent event;
|
||||
event.EventType = EET_LOG_TEXT_EVENT;
|
||||
event.LogEvent.Text = text;
|
||||
event.LogEvent.Level = ll;
|
||||
if (Receiver->OnEvent(event))
|
||||
return;
|
||||
}
|
||||
|
||||
os::Printer::print(text);
|
||||
}
|
||||
|
||||
//! Prints out a text into the log
|
||||
void CLogger::log(const c8 *text, const c8 *hint, ELOG_LEVEL ll)
|
||||
{
|
||||
if (ll < LogLevel)
|
||||
return;
|
||||
|
||||
core::stringc s = text;
|
||||
s += ": ";
|
||||
s += hint;
|
||||
log(s.c_str(), ll);
|
||||
}
|
||||
|
||||
//! Sets a new event receiver
|
||||
void CLogger::setReceiver(IEventReceiver *r)
|
||||
{
|
||||
Receiver = r;
|
||||
}
|
||||
|
||||
} // end namespace irr
|
||||
41
src/CLogger.h
Normal file
41
src/CLogger.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ILogger.h"
|
||||
#include "os.h"
|
||||
#include "irrString.h"
|
||||
#include "IEventReceiver.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
//! Class for logging messages, warnings and errors to stdout
|
||||
class CLogger : public ILogger
|
||||
{
|
||||
public:
|
||||
CLogger(IEventReceiver *r);
|
||||
|
||||
//! Returns the current set log level.
|
||||
ELOG_LEVEL getLogLevel() const override;
|
||||
|
||||
//! Sets a new log level. void setLogLevel(ELOG_LEVEL ll) override;
|
||||
void setLogLevel(ELOG_LEVEL ll) override;
|
||||
|
||||
//! Prints out a text into the log
|
||||
void log(const c8 *text, ELOG_LEVEL ll = ELL_INFORMATION) override;
|
||||
|
||||
//! Prints out a text into the log
|
||||
void log(const c8 *text, const c8 *hint, ELOG_LEVEL ll = ELL_INFORMATION) override;
|
||||
|
||||
//! Sets a new event receiver
|
||||
void setReceiver(IEventReceiver *r);
|
||||
|
||||
private:
|
||||
ELOG_LEVEL LogLevel;
|
||||
IEventReceiver *Receiver;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
561
src/CMakeLists.txt
Normal file
561
src/CMakeLists.txt
Normal file
@@ -0,0 +1,561 @@
|
||||
if(NOT ANDROID AND NOT APPLE)
|
||||
set(DEFAULT_SDL2 ON)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library" TRUE)
|
||||
option(USE_SDL2 "Use the SDL2 backend" ${DEFAULT_SDL2})
|
||||
|
||||
# Compiler flags
|
||||
|
||||
add_definitions(-DIRRLICHT_EXPORTS)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if(WIN32)
|
||||
set(API_IMPORT "__declspec(dllimport)")
|
||||
set(API_EXPORT "__declspec(dllexport)")
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(API_EXPORT "__attribute__ ((visibility(\"default\")))") # only necessary if default visibility is set to hidden
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-D_IRR_STATIC_LIB_)
|
||||
endif()
|
||||
add_definitions("-DIRRLICHT_API=${API_EXPORT}")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-D_DEBUG)
|
||||
endif()
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
|
||||
add_compile_options(-Wall -pipe -fno-exceptions)
|
||||
|
||||
# Enable SSE for floating point math on 32-bit x86 by default
|
||||
# reasoning see minetest issue #11810 and https://gcc.gnu.org/wiki/FloatingPointMath
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("#ifndef __i686__\n#error\n#endif\nint main(){}" IS_I686)
|
||||
if(IS_I686)
|
||||
message(STATUS "Detected Intel x86: using SSE instead of x87 FPU")
|
||||
add_compile_options(-mfpmath=sse -msse)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(MSVC)
|
||||
string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " msvcrt.lib") # ???? fuck off
|
||||
|
||||
add_compile_options(/GR- /Zl)
|
||||
|
||||
# Enable SSE for floating point math on 32-bit x86 by default
|
||||
# reasoning see minetest issue #11810 and https://gcc.gnu.org/wiki/FloatingPointMath
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
add_compile_options(/arch:SSE)
|
||||
endif()
|
||||
|
||||
add_compile_options(/D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||
endif()
|
||||
|
||||
# Sanity-check version
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${PROJECT_SOURCE_DIR}/include)
|
||||
unset(REVISION_SANITY_CHECK CACHE)
|
||||
check_cxx_source_compiles("#include <IrrCompileConfig.h>\n\
|
||||
#if IRRLICHT_VERSION_MT_REVISION != ${IRRLICHTMT_REVISION}\n\
|
||||
#error\n\
|
||||
#endif\n\
|
||||
int main() {}" REVISION_SANITY_CHECK)
|
||||
if(NOT REVISION_SANITY_CHECK)
|
||||
message(FATAL_ERROR "IrrlichtMt revision number mismatches between CMake and headers.")
|
||||
endif()
|
||||
|
||||
# Platform-independent configuration (hard-coded currently)
|
||||
add_definitions(
|
||||
-DIRR_ENABLE_BUILTIN_FONT
|
||||
)
|
||||
|
||||
# Platform-specific configuration
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
set(SOLARIS TRUE)
|
||||
endif()
|
||||
|
||||
# Device
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_IRR_WINDOWS_ -D_IRR_WINDOWS_API_)
|
||||
set(DEVICE "WINDOWS")
|
||||
elseif(APPLE)
|
||||
add_definitions(-D_IRR_OSX_PLATFORM_)
|
||||
set(DEVICE "OSX")
|
||||
elseif(ANDROID)
|
||||
add_definitions(-D_IRR_ANDROID_PLATFORM_)
|
||||
if(USE_SDL2)
|
||||
message(FATAL_ERROR "SDL2 device is not (yet) supported on Android")
|
||||
endif()
|
||||
set(DEVICE "ANDROID")
|
||||
elseif(EMSCRIPTEN)
|
||||
add_definitions(-D_IRR_EMSCRIPTEN_PLATFORM_ -D_IRR_COMPILE_WITH_EGL_MANAGER_)
|
||||
set(LINUX_PLATFORM TRUE)
|
||||
set(DEVICE "SDL")
|
||||
elseif(SOLARIS)
|
||||
add_definitions(-D_IRR_SOLARIS_PLATFORM_ -D_IRR_POSIX_API_)
|
||||
set(DEVICE "X11")
|
||||
else()
|
||||
add_definitions(-D_IRR_POSIX_API_)
|
||||
set(LINUX_PLATFORM TRUE)
|
||||
set(DEVICE "X11")
|
||||
endif()
|
||||
|
||||
if(LINUX_PLATFORM)
|
||||
add_definitions(-D_IRR_LINUX_PLATFORM_)
|
||||
endif()
|
||||
|
||||
if(USE_SDL2)
|
||||
set(DEVICE "SDL")
|
||||
elseif(DEVICE STREQUAL "SDL")
|
||||
message(FATAL_ERROR "SDL was used but not enabled?!")
|
||||
endif()
|
||||
|
||||
add_definitions("-D_IRR_COMPILE_WITH_${DEVICE}_DEVICE_")
|
||||
|
||||
# X11
|
||||
|
||||
if(DEVICE STREQUAL "X11")
|
||||
option(USE_X11 "Use X11" TRUE)
|
||||
else()
|
||||
set(USE_X11 FALSE)
|
||||
endif()
|
||||
|
||||
if(LINUX_PLATFORM AND USE_X11)
|
||||
option(USE_XINPUT2 "Use XInput2" TRUE)
|
||||
option(USE_XCURSOR "Use XCursor" FALSE)
|
||||
else()
|
||||
set(USE_XINPUT2 FALSE)
|
||||
set(USE_XCURSOR FALSE)
|
||||
endif()
|
||||
|
||||
# Joystick
|
||||
|
||||
if(NOT (BSD OR SOLARIS OR EMSCRIPTEN))
|
||||
add_definitions(-D_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
|
||||
endif()
|
||||
|
||||
# OpenGL
|
||||
|
||||
if(USE_SDL2)
|
||||
option(ENABLE_OPENGL3 "Enable OpenGL 3+" TRUE)
|
||||
else()
|
||||
set(ENABLE_OPENGL3 FALSE)
|
||||
endif()
|
||||
|
||||
if(ANDROID OR EMSCRIPTEN)
|
||||
set(ENABLE_OPENGL FALSE)
|
||||
else()
|
||||
option(ENABLE_OPENGL "Enable OpenGL" TRUE)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN OR APPLE)
|
||||
set(ENABLE_GLES1 FALSE)
|
||||
else()
|
||||
if(ANDROID)
|
||||
set(DEFAULT_GLES1 TRUE)
|
||||
endif()
|
||||
option(ENABLE_GLES1 "Enable OpenGL ES" ${DEFAULT_GLES1})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(ENABLE_GLES2 FALSE)
|
||||
set(ENABLE_WEBGL1 FALSE)
|
||||
else()
|
||||
if(ANDROID OR EMSCRIPTEN)
|
||||
set(DEFAULT_GLES2 TRUE)
|
||||
endif()
|
||||
if(EMSCRIPTEN)
|
||||
set(DEFAULT_WEBGL1 TRUE)
|
||||
endif()
|
||||
option(ENABLE_GLES2 "Enable OpenGL ES 2+" ${DEFAULT_GLES2})
|
||||
#option(ENABLE_WEBGL1 "Enable WebGL (requires GLES2)" ${DEFAULT_WEBGL1})
|
||||
set(ENABLE_WEBGL1 FALSE) # not working currently
|
||||
if(ENABLE_WEBGL1)
|
||||
set(ENABLE_GLES2 TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
add_definitions(-D_IRR_COMPILE_WITH_OPENGL_)
|
||||
set(OPENGL_DIRECT_LINK TRUE) # driver relies on this
|
||||
if(DEVICE STREQUAL "WINDOWS")
|
||||
add_definitions(-D_IRR_COMPILE_WITH_WGL_MANAGER_)
|
||||
elseif(DEVICE STREQUAL "X11")
|
||||
add_definitions(-D_IRR_COMPILE_WITH_GLX_MANAGER_)
|
||||
elseif(DEVICE STREQUAL "OSX")
|
||||
add_definitions(-D_IRR_COMPILE_WITH_NSOGL_MANAGER_)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_OPENGL3)
|
||||
if (NOT USE_SDL2)
|
||||
message(FATAL_ERROR "OpenGL 3 driver requires SDL2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_GLES1)
|
||||
if (USE_SDL2)
|
||||
message(FATAL_ERROR "OpenGL ES 1 is not supported with SDL2")
|
||||
endif()
|
||||
add_definitions(-D_IRR_COMPILE_WITH_OGLES1_)
|
||||
set(OPENGLES_DIRECT_LINK TRUE)
|
||||
if(DEVICE MATCHES "^(WINDOWS|X11|ANDROID)$")
|
||||
add_definitions(-D_IRR_COMPILE_WITH_EGL_MANAGER_)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_GLES2)
|
||||
add_definitions(-D_IRR_COMPILE_WITH_OGLES2_)
|
||||
if(DEVICE MATCHES "^(WINDOWS|X11|ANDROID)$" OR EMSCRIPTEN)
|
||||
add_definitions(-D_IRR_COMPILE_WITH_EGL_MANAGER_)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_WEBGL1)
|
||||
add_definitions(-D_IRR_COMPILE_WITH_WEBGL1_)
|
||||
endif()
|
||||
|
||||
# Misc
|
||||
|
||||
include(TestBigEndian)
|
||||
TEST_BIG_ENDIAN(BIG_ENDIAN)
|
||||
if(BIG_ENDIAN)
|
||||
add_definitions(-D__BIG_ENDIAN__)
|
||||
endif()
|
||||
|
||||
# Configuration report
|
||||
|
||||
message(STATUS "Device: ${DEVICE}")
|
||||
message(STATUS "OpenGL: ${ENABLE_OPENGL}")
|
||||
message(STATUS "OpenGL 3: ${ENABLE_OPENGL3}")
|
||||
message(STATUS "OpenGL ES: ${ENABLE_GLES1}")
|
||||
if (ENABLE_GLES2)
|
||||
message(STATUS "OpenGL ES 2: ON (unified)")
|
||||
else()
|
||||
message(STATUS "OpenGL ES 2: OFF")
|
||||
endif()
|
||||
#message(STATUS "WebGL: ${ENABLE_WEBGL1}")
|
||||
|
||||
# Required libs
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(JPEG REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
|
||||
if(ENABLE_GLES1)
|
||||
# only tested on Android, probably works on Linux (is this needed anywhere else?)
|
||||
find_library(OPENGLES_LIBRARY NAMES GLESv1_CM REQUIRED)
|
||||
find_library(EGL_LIBRARY NAMES EGL REQUIRED)
|
||||
|
||||
message(STATUS "Found OpenGLES: ${OPENGLES_LIBRARY}")
|
||||
endif()
|
||||
if(ENABLE_GLES2)
|
||||
find_package(OpenGLES2 REQUIRED)
|
||||
endif()
|
||||
if(ENABLE_OPENGL OR ENABLE_OPENGL3)
|
||||
find_package(OpenGL REQUIRED)
|
||||
endif()
|
||||
if(USE_SDL2)
|
||||
find_package(SDL2 2.0.10 CONFIG REQUIRED)
|
||||
message(STATUS "Found SDL2: ${SDL2_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# More special config
|
||||
|
||||
if(ENABLE_OPENGL AND DEVICE STREQUAL "SDL")
|
||||
# The legacy GL driver requires some symbols from GL 4.5 to compile,
|
||||
# which SDL only provides since 2.26.0 (Nov 2022).
|
||||
# We have a fallback in case this isn't satisfied so test for it.
|
||||
set(CMAKE_REQUIRED_INCLUDES ${SDL2_INCLUDE_DIRS})
|
||||
unset(CHECK_GL_VERSION_4_5 CACHE)
|
||||
check_cxx_source_compiles("#include <SDL_opengl.h>\n\
|
||||
#ifndef GL_VERSION_4_5\n\
|
||||
#error\n\
|
||||
#endif\n\
|
||||
int main() {}" CHECK_GL_VERSION_4_5)
|
||||
if(CHECK_GL_VERSION_4_5)
|
||||
add_definitions(-DIRR_PREFER_SDL_GL_HEADER)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Platform-specific libs
|
||||
|
||||
if(ANDROID)
|
||||
enable_language(C)
|
||||
elseif(APPLE)
|
||||
find_library(COCOA_LIB Cocoa REQUIRED)
|
||||
find_library(IOKIT_LIB IOKit REQUIRED)
|
||||
|
||||
add_definitions(-DGL_SILENCE_DEPRECATION)
|
||||
elseif(NOT USE_SDL2)
|
||||
# Unix probably
|
||||
find_package(X11 REQUIRED)
|
||||
if(USE_XINPUT2 AND NOT X11_Xi_FOUND)
|
||||
message(FATAL_ERROR "XInput not found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(link_includes
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
|
||||
"${ZLIB_INCLUDE_DIR}"
|
||||
"${JPEG_INCLUDE_DIR}"
|
||||
"${PNG_INCLUDE_DIR}"
|
||||
"$<$<BOOL:${USE_SDL2}>:${SDL2_INCLUDE_DIRS}>"
|
||||
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${OPENGLES2_INCLUDE_DIR}
|
||||
${EGL_INCLUDE_DIR}
|
||||
|
||||
"$<$<PLATFORM_ID:Android>:${ANDROID_NDK}/sources/android/native_app_glue>"
|
||||
"$<$<BOOL:${USE_X11}>:${X11_INCLUDE_DIR}>"
|
||||
)
|
||||
|
||||
# Source files
|
||||
|
||||
set(IRRMESHLOADER
|
||||
CB3DMeshFileLoader.cpp
|
||||
COBJMeshFileLoader.cpp
|
||||
CXMeshFileLoader.cpp
|
||||
)
|
||||
|
||||
add_library(IRRMESHOBJ OBJECT
|
||||
CSkinnedMesh.cpp
|
||||
CBoneSceneNode.cpp
|
||||
CMeshSceneNode.cpp
|
||||
CAnimatedMeshSceneNode.cpp
|
||||
${IRRMESHLOADER}
|
||||
)
|
||||
|
||||
add_library(IRROBJ OBJECT
|
||||
CBillboardSceneNode.cpp
|
||||
CCameraSceneNode.cpp
|
||||
CDummyTransformationSceneNode.cpp
|
||||
CEmptySceneNode.cpp
|
||||
CMeshManipulator.cpp
|
||||
CSceneCollisionManager.cpp
|
||||
CSceneManager.cpp
|
||||
CMeshCache.cpp
|
||||
)
|
||||
|
||||
set(IRRDRVROBJ
|
||||
CNullDriver.cpp
|
||||
CGLXManager.cpp
|
||||
CWGLManager.cpp
|
||||
CEGLManager.cpp
|
||||
CSDLManager.cpp
|
||||
mt_opengl_loader.cpp
|
||||
)
|
||||
|
||||
# the two legacy drivers
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
set(IRRDRVROBJ
|
||||
${IRRDRVROBJ}
|
||||
COpenGLCacheHandler.cpp
|
||||
COpenGLDriver.cpp
|
||||
COpenGLSLMaterialRenderer.cpp
|
||||
COpenGLExtensionHandler.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_GLES1)
|
||||
set(IRRDRVROBJ
|
||||
${IRRDRVROBJ}
|
||||
COGLESDriver.cpp
|
||||
COGLESExtensionHandler.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# the unified drivers
|
||||
|
||||
if(ENABLE_OPENGL3 OR ENABLE_GLES2)
|
||||
set(IRRDRVROBJ
|
||||
${IRRDRVROBJ}
|
||||
OpenGL/Driver.cpp
|
||||
OpenGL/ExtensionHandler.cpp
|
||||
OpenGL/FixedPipelineRenderer.cpp
|
||||
OpenGL/MaterialRenderer.cpp
|
||||
OpenGL/Renderer2D.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_OPENGL3)
|
||||
set(IRRDRVROBJ
|
||||
${IRRDRVROBJ}
|
||||
OpenGL3/Driver.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_GLES2)
|
||||
set(IRRDRVROBJ
|
||||
${IRRDRVROBJ}
|
||||
OpenGLES2/Driver.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(IRRIMAGEOBJ
|
||||
CColorConverter.cpp
|
||||
CImage.cpp
|
||||
CImageLoaderBMP.cpp
|
||||
CImageLoaderJPG.cpp
|
||||
CImageLoaderPNG.cpp
|
||||
CImageLoaderTGA.cpp
|
||||
CImageWriterJPG.cpp
|
||||
CImageWriterPNG.cpp
|
||||
)
|
||||
|
||||
add_library(IRRVIDEOOBJ OBJECT
|
||||
CFPSCounter.cpp
|
||||
${IRRDRVROBJ}
|
||||
${IRRIMAGEOBJ}
|
||||
)
|
||||
|
||||
add_library(IRRIOOBJ OBJECT
|
||||
CFileList.cpp
|
||||
CFileSystem.cpp
|
||||
CLimitReadFile.cpp
|
||||
CMemoryFile.cpp
|
||||
CReadFile.cpp
|
||||
CWriteFile.cpp
|
||||
CZipReader.cpp
|
||||
CAttributes.cpp
|
||||
)
|
||||
|
||||
add_library(IRROTHEROBJ OBJECT
|
||||
CIrrDeviceSDL.cpp
|
||||
CIrrDeviceLinux.cpp
|
||||
CIrrDeviceStub.cpp
|
||||
CIrrDeviceWin32.cpp
|
||||
CLogger.cpp
|
||||
COSOperator.cpp
|
||||
Irrlicht.cpp
|
||||
os.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_OPENGL3)
|
||||
target_compile_definitions(IRROTHEROBJ PRIVATE ENABLE_OPENGL3)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(IRROTHEROBJ PRIVATE
|
||||
Android/CIrrDeviceAndroid.cpp
|
||||
Android/CAndroidAssetReader.cpp
|
||||
Android/CAndroidAssetFileArchive.cpp
|
||||
Android/CKeyEventWrapper.cpp
|
||||
)
|
||||
elseif(APPLE)
|
||||
# Build all IRROTHEROBJ sources as objc++, including the .cpp's
|
||||
set_target_properties(IRROTHEROBJ PROPERTIES COMPILE_OPTIONS "-xobjective-c++")
|
||||
target_sources(IRROTHEROBJ PRIVATE
|
||||
CIrrDeviceOSX.mm
|
||||
CNSOGLManager.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_X11)
|
||||
target_compile_definitions(IRROTHEROBJ PRIVATE _IRR_COMPILE_WITH_X11_)
|
||||
endif()
|
||||
|
||||
if(USE_XINPUT2)
|
||||
target_compile_definitions(IRROTHEROBJ PRIVATE _IRR_LINUX_X11_XINPUT2_)
|
||||
endif()
|
||||
|
||||
if(USE_XCURSOR)
|
||||
target_compile_definitions(IRROTHEROBJ PRIVATE _IRR_LINUX_XCURSOR_)
|
||||
endif()
|
||||
|
||||
add_library(IRRGUIOBJ OBJECT
|
||||
CGUIButton.cpp
|
||||
CGUICheckBox.cpp
|
||||
CGUIComboBox.cpp
|
||||
CGUIEditBox.cpp
|
||||
CGUIEnvironment.cpp
|
||||
CGUIFileOpenDialog.cpp
|
||||
CGUIFont.cpp
|
||||
CGUIImage.cpp
|
||||
CGUIListBox.cpp
|
||||
CGUIScrollBar.cpp
|
||||
CGUISkin.cpp
|
||||
CGUIStaticText.cpp
|
||||
CGUITabControl.cpp
|
||||
CGUISpriteBank.cpp
|
||||
CGUIImageList.cpp
|
||||
)
|
||||
|
||||
# Library
|
||||
|
||||
add_library(IrrlichtMt)
|
||||
foreach(object_lib
|
||||
IRRMESHOBJ IRROBJ IRRVIDEOOBJ
|
||||
IRRIOOBJ IRROTHEROBJ IRRGUIOBJ)
|
||||
# Set include directories for object library compilation
|
||||
target_include_directories(${object_lib} PRIVATE ${link_includes})
|
||||
# Add objects from object library to main library
|
||||
target_sources(IrrlichtMt PRIVATE $<TARGET_OBJECTS:${object_lib}>)
|
||||
endforeach()
|
||||
|
||||
# Alias target provides add_submodule compatibility
|
||||
add_library(IrrlichtMt::IrrlichtMt ALIAS IrrlichtMt)
|
||||
|
||||
target_include_directories(IrrlichtMt
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/irrlichtmt>"
|
||||
PRIVATE
|
||||
${link_includes}
|
||||
)
|
||||
|
||||
# this needs to be here and not in a variable (like link_includes) due to issues
|
||||
# with the generator expressions on at least CMake 3.22, but not 3.28 or later
|
||||
target_link_libraries(IrrlichtMt PRIVATE
|
||||
${ZLIB_LIBRARY}
|
||||
${JPEG_LIBRARY}
|
||||
${PNG_LIBRARY}
|
||||
"$<$<BOOL:${USE_SDL2}>:SDL2::SDL2>"
|
||||
|
||||
"$<$<BOOL:${OPENGL_DIRECT_LINK}>:${OPENGL_LIBRARIES}>"
|
||||
"$<$<BOOL:${OPENGLES_DIRECT_LINK}>:${OPENGLES_LIBRARY}>"
|
||||
${EGL_LIBRARY}
|
||||
|
||||
"$<$<PLATFORM_ID:Android>:-landroid -llog>"
|
||||
${COCOA_LIB}
|
||||
${IOKIT_LIB}
|
||||
"$<$<PLATFORM_ID:Windows>:gdi32>"
|
||||
"$<$<PLATFORM_ID:Windows>:winmm>"
|
||||
"$<$<BOOL:${USE_X11}>:${X11_X11_LIB}>"
|
||||
"$<$<BOOL:${USE_X11}>:${X11_Xi_LIB}>"
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(IrrlichtMt INTERFACE _IRR_WINDOWS_API_) # used in _IRR_DEBUG_BREAK_IF definition in a public header
|
||||
endif()
|
||||
target_compile_definitions(IrrlichtMt INTERFACE "IRRLICHT_API=${API_IMPORT}")
|
||||
if(APPLE OR ANDROID OR EMSCRIPTEN)
|
||||
target_compile_definitions(IrrlichtMt PUBLIC IRR_MOBILE_PATHS)
|
||||
endif()
|
||||
|
||||
set_target_properties(IrrlichtMt PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(IrrlichtMt PROPERTIES PREFIX "") # for DLL name
|
||||
endif()
|
||||
|
||||
# Installation of library
|
||||
install(TARGETS IrrlichtMt
|
||||
EXPORT IrrlichtMt-export
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
165
src/CMemoryFile.cpp
Normal file
165
src/CMemoryFile.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CMemoryFile.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
CMemoryReadFile::CMemoryReadFile(const void *memory, long len, const io::path &fileName, bool d) :
|
||||
Buffer(memory), Len(len), Pos(0), Filename(fileName), deleteMemoryWhenDropped(d)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CMemoryReadFile");
|
||||
#endif
|
||||
}
|
||||
|
||||
CMemoryReadFile::~CMemoryReadFile()
|
||||
{
|
||||
if (deleteMemoryWhenDropped)
|
||||
delete[] (c8 *)Buffer;
|
||||
}
|
||||
|
||||
//! returns how much was read
|
||||
size_t CMemoryReadFile::read(void *buffer, size_t sizeToRead)
|
||||
{
|
||||
long amount = static_cast<long>(sizeToRead);
|
||||
if (Pos + amount > Len)
|
||||
amount -= Pos + amount - Len;
|
||||
|
||||
if (amount <= 0)
|
||||
return 0;
|
||||
|
||||
c8 *p = (c8 *)Buffer;
|
||||
memcpy(buffer, p + Pos, amount);
|
||||
|
||||
Pos += amount;
|
||||
|
||||
return static_cast<size_t>(amount);
|
||||
}
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
//! if relativeMovement==true, the pos is changed relative to current pos,
|
||||
//! otherwise from begin of file
|
||||
bool CMemoryReadFile::seek(long finalPos, bool relativeMovement)
|
||||
{
|
||||
if (relativeMovement) {
|
||||
if (Pos + finalPos < 0 || Pos + finalPos > Len)
|
||||
return false;
|
||||
|
||||
Pos += finalPos;
|
||||
} else {
|
||||
if (finalPos < 0 || finalPos > Len)
|
||||
return false;
|
||||
|
||||
Pos = finalPos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! returns size of file
|
||||
long CMemoryReadFile::getSize() const
|
||||
{
|
||||
return Len;
|
||||
}
|
||||
|
||||
//! returns where in the file we are.
|
||||
long CMemoryReadFile::getPos() const
|
||||
{
|
||||
return Pos;
|
||||
}
|
||||
|
||||
//! returns name of file
|
||||
const io::path &CMemoryReadFile::getFileName() const
|
||||
{
|
||||
return Filename;
|
||||
}
|
||||
|
||||
CMemoryWriteFile::CMemoryWriteFile(void *memory, long len, const io::path &fileName, bool d) :
|
||||
Buffer(memory), Len(len), Pos(0), Filename(fileName), deleteMemoryWhenDropped(d)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CMemoryWriteFile");
|
||||
#endif
|
||||
}
|
||||
|
||||
CMemoryWriteFile::~CMemoryWriteFile()
|
||||
{
|
||||
if (deleteMemoryWhenDropped)
|
||||
delete[] (c8 *)Buffer;
|
||||
}
|
||||
|
||||
//! returns how much was written
|
||||
size_t CMemoryWriteFile::write(const void *buffer, size_t sizeToWrite)
|
||||
{
|
||||
long amount = (long)sizeToWrite;
|
||||
if (Pos + amount > Len)
|
||||
amount -= Pos + amount - Len;
|
||||
|
||||
if (amount <= 0)
|
||||
return 0;
|
||||
|
||||
c8 *p = (c8 *)Buffer;
|
||||
memcpy(p + Pos, buffer, amount);
|
||||
|
||||
Pos += amount;
|
||||
|
||||
return (size_t)amount;
|
||||
}
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
//! if relativeMovement==true, the pos is changed relative to current pos,
|
||||
//! otherwise from begin of file
|
||||
bool CMemoryWriteFile::seek(long finalPos, bool relativeMovement)
|
||||
{
|
||||
if (relativeMovement) {
|
||||
if (Pos + finalPos < 0 || Pos + finalPos > Len)
|
||||
return false;
|
||||
|
||||
Pos += finalPos;
|
||||
} else {
|
||||
if (finalPos < 0 || finalPos > Len)
|
||||
return false;
|
||||
|
||||
Pos = finalPos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! returns where in the file we are.
|
||||
long CMemoryWriteFile::getPos() const
|
||||
{
|
||||
return Pos;
|
||||
}
|
||||
|
||||
//! returns name of file
|
||||
const io::path &CMemoryWriteFile::getFileName() const
|
||||
{
|
||||
return Filename;
|
||||
}
|
||||
|
||||
bool CMemoryWriteFile::flush()
|
||||
{
|
||||
return true; // no buffering, so nothing to do
|
||||
}
|
||||
|
||||
IReadFile *createMemoryReadFile(const void *memory, long size, const io::path &fileName, bool deleteMemoryWhenDropped)
|
||||
{
|
||||
CMemoryReadFile *file = new CMemoryReadFile(memory, size, fileName, deleteMemoryWhenDropped);
|
||||
return file;
|
||||
}
|
||||
|
||||
IWriteFile *createMemoryWriteFile(void *memory, long size, const io::path &fileName, bool deleteMemoryWhenDropped)
|
||||
{
|
||||
CMemoryWriteFile *file = new CMemoryWriteFile(memory, size, fileName, deleteMemoryWhenDropped);
|
||||
return file;
|
||||
}
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
99
src/CMemoryFile.h
Normal file
99
src/CMemoryFile.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||
// This file is part of the "Irrlicht Engine".
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IMemoryReadFile.h"
|
||||
#include "IWriteFile.h"
|
||||
#include "irrString.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
||||
namespace io
|
||||
{
|
||||
|
||||
/*!
|
||||
Class for reading from memory.
|
||||
*/
|
||||
class CMemoryReadFile : public IMemoryReadFile
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CMemoryReadFile(const void *memory, long len, const io::path &fileName, bool deleteMemoryWhenDropped);
|
||||
|
||||
//! Destructor
|
||||
virtual ~CMemoryReadFile();
|
||||
|
||||
//! returns how much was read
|
||||
size_t read(void *buffer, size_t sizeToRead) override;
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
bool seek(long finalPos, bool relativeMovement = false) override;
|
||||
|
||||
//! returns size of file
|
||||
long getSize() const override;
|
||||
|
||||
//! returns where in the file we are.
|
||||
long getPos() const override;
|
||||
|
||||
//! returns name of file
|
||||
const io::path &getFileName() const override;
|
||||
|
||||
//! Get the type of the class implementing this interface
|
||||
EREAD_FILE_TYPE getType() const override
|
||||
{
|
||||
return ERFT_MEMORY_READ_FILE;
|
||||
}
|
||||
|
||||
//! Get direct access to internal buffer
|
||||
const void *getBuffer() const override
|
||||
{
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
const void *Buffer;
|
||||
long Len;
|
||||
long Pos;
|
||||
io::path Filename;
|
||||
bool deleteMemoryWhenDropped;
|
||||
};
|
||||
|
||||
/*!
|
||||
Class for writing to memory.
|
||||
*/
|
||||
class CMemoryWriteFile : public IWriteFile
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CMemoryWriteFile(void *memory, long len, const io::path &fileName, bool deleteMemoryWhenDropped);
|
||||
|
||||
//! Destructor
|
||||
virtual ~CMemoryWriteFile();
|
||||
|
||||
//! returns how much was written
|
||||
size_t write(const void *buffer, size_t sizeToWrite) override;
|
||||
|
||||
//! changes position in file, returns true if successful
|
||||
bool seek(long finalPos, bool relativeMovement = false) override;
|
||||
|
||||
//! returns where in the file we are.
|
||||
long getPos() const override;
|
||||
|
||||
//! returns name of file
|
||||
const io::path &getFileName() const override;
|
||||
|
||||
bool flush() override;
|
||||
|
||||
private:
|
||||
void *Buffer;
|
||||
long Len;
|
||||
long Pos;
|
||||
io::path Filename;
|
||||
bool deleteMemoryWhenDropped;
|
||||
};
|
||||
|
||||
} // end namespace io
|
||||
} // end namespace irr
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user