openmw-tes3coop/components/sdlutil/sdlcursormanager.cpp
scrawl e8662bea31 Change the way that image origin is converted to OpenGL's lower-left convention
Flip the texture coordinates instead of flipping textures.

This simplifies the TextureManager (no need to worry if the caller wants flipping or not), should make it easier to generalize & multithread it.
2016-02-05 21:03:11 +01:00

246 lines
7.4 KiB
C++

#include "sdlcursormanager.hpp"
#include <cassert>
#include <stdexcept>
#include <iostream>
#include <SDL_mouse.h>
#include <SDL_endian.h>
#include <osg/GraphicsContext>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osg/TexMat>
#include <osgViewer/GraphicsWindow>
#include "imagetosurface.hpp"
#ifdef OSG_LIBRARY_STATIC
// Sets the default windowing system interface according to the OS.
// Necessary for OpenSceneGraph to do some things, like decompression.
USE_GRAPHICSWINDOW()
#endif
namespace
{
class MyGraphicsContext {
public:
MyGraphicsContext(int w, int h)
{
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->x = 0;
traits->y = 0;
traits->width = w;
traits->height = h;
traits->red = 8;
traits->green = 8;
traits->blue = 8;
traits->alpha = 8;
traits->windowDecoration = false;
traits->doubleBuffer = false;
traits->sharedContext = 0;
traits->pbuffer = true;
osg::GraphicsContext::ScreenIdentifier si;
si.readDISPLAY();
if (si.displayNum<0) si.displayNum = 0;
traits->displayNum = si.displayNum;
traits->screenNum = si.screenNum;
traits->hostName = si.hostName;
_gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if (!_gc)
{
std::cerr << "Failed to create pbuffer, failing back to normal graphics window." << std::endl;
traits->pbuffer = false;
_gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if (!_gc)
throw std::runtime_error("Failed to create graphics context for image decompression");
}
if (_gc.valid())
{
_gc->realize();
_gc->makeCurrent();
}
}
osg::ref_ptr<osg::GraphicsContext> getContext()
{
return _gc;
}
bool valid() const { return _gc.valid() && _gc->isRealized(); }
private:
osg::ref_ptr<osg::GraphicsContext> _gc;
};
osg::ref_ptr<osg::Image> decompress (osg::ref_ptr<osg::Image> source, float rotDegrees)
{
int width = source->s();
int height = source->t();
MyGraphicsContext context(width, height);
osg::ref_ptr<osg::State> state = context.getContext()->getState();
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setImage(source);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);
texture->setBorderColor(osg::Vec4f(0.f, 0.f, 0.f, 0.f));
osg::ref_ptr<osg::TexMat> texmat = new osg::TexMat;
osg::Matrix texRot (osg::Matrix::identity());
float theta ( osg::DegreesToRadians(-rotDegrees) );
float cosTheta = std::cos(theta);
float sinTheta = std::sin(theta);
texRot(0,0) = cosTheta;
texRot(1,0) = -sinTheta;
texRot(0,1) = sinTheta;
texRot(1,1) = cosTheta;
// Offset center of rotation to center of texture
texRot(3,0) = 0.5f + ( (-0.5f * cosTheta) - (-0.5f * sinTheta) );
texRot(3,1) = 0.5f + ( (-0.5f * sinTheta) + (-0.5f * cosTheta) );
texmat->setMatrix(texRot);
state->applyTextureAttribute(0, texmat);
osg::ref_ptr<osg::RefMatrix> identity (new osg::RefMatrix(osg::Matrix::identity()));
state->applyModelViewMatrix(identity);
state->applyProjectionMatrix(identity);
state->applyMode(GL_TEXTURE_2D, true);
state->applyTextureAttribute(0, texture);
osg::ref_ptr<osg::Image> resultImage = new osg::Image;
resultImage->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
osg::RenderInfo renderInfo;
renderInfo.setState(state);
glViewport(0, 0, width, height);
osg::ref_ptr<osg::Geometry> geom;
#if defined(__APPLE__)
// Extra flip needed on Intel graphics OS X systems due to a driver bug
std::string vendorString = (const char*)glGetString(GL_VENDOR);
if (vendorString.find("Intel") != std::string::npos)
geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,1,0), osg::Vec3(2,0,0), osg::Vec3(0,-2,0));
else
#endif
geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0));
geom->drawImplementation(renderInfo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, resultImage->data());
geom->releaseGLObjects();
source->releaseGLObjects();
texture->releaseGLObjects();
return resultImage;
}
}
namespace SDLUtil
{
SDLCursorManager::SDLCursorManager() :
mEnabled(false),
mInitialized(false)
{
}
SDLCursorManager::~SDLCursorManager()
{
CursorMap::const_iterator curs_iter = mCursorMap.begin();
while(curs_iter != mCursorMap.end())
{
SDL_FreeCursor(curs_iter->second);
++curs_iter;
}
mCursorMap.clear();
}
void SDLCursorManager::setEnabled(bool enabled)
{
if(mInitialized && enabled == mEnabled)
return;
mInitialized = true;
mEnabled = enabled;
//turn on hardware cursors
if(enabled)
{
_setGUICursor(mCurrentCursor);
}
//turn off hardware cursors
else
{
SDL_ShowCursor(SDL_FALSE);
}
}
void SDLCursorManager::cursorChanged(const std::string& name)
{
mCurrentCursor = name;
CursorMap::const_iterator curs_iter = mCursorMap.find(name);
if(curs_iter != mCursorMap.end())
{
//we have this cursor
_setGUICursor(name);
}
}
void SDLCursorManager::_setGUICursor(const std::string &name)
{
SDL_SetCursor(mCursorMap.find(name)->second);
}
void SDLCursorManager::createCursor(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y)
{
_createCursorFromResource(name, rotDegrees, image, size_x, size_y, hotspot_x, hotspot_y);
}
void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y)
{
osg::ref_ptr<osg::Image> decompressed;
if (mCursorMap.find(name) != mCursorMap.end())
return;
try {
decompressed = decompress(image, static_cast<float>(rotDegrees));
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
std::cerr <<"Using default cursor."<<std::endl;
return;
}
SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, true);
//set the cursor and store it for later
SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y);
mCursorMap.insert(CursorMap::value_type(std::string(name), curs));
//clean up
SDL_FreeSurface(surf);
_setGUICursor(name);
}
}