Merge pull request #1548 from drummyfish/screenshot360

360° screenshots
0.6.3
Bret Curtis 7 years ago committed by GitHub
commit ccfc07e7e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -31,6 +31,7 @@
Bug #4453: Quick keys behaviour is invalid for equipment
Bug #4454: AI opens doors too slow
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
Feature #4222: 360° screenshots
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4444: Per-group KF-animation files support

@ -499,7 +499,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
else
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, keybinderUser, keybinderUserExists, gameControllerdb, mGrab);
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, gameControllerdb, mGrab);
mEnvironment.setInputManager (input);
std::string myguiResources = (mResDir / "mygui").string();
@ -656,8 +656,11 @@ void OMW::Engine::go()
settingspath = loadSettings (settings);
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General")));
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General"));
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
mViewer->addEventHandler(mScreenCaptureHandler);
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));

@ -7,7 +7,7 @@
#include <components/settings/settings.hpp>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include "mwbase/environment.hpp"
@ -82,6 +82,7 @@ namespace OMW
boost::filesystem::path mResDir;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
std::string mCellName;
std::vector<std::string> mContentFiles;
bool mSkipMenu;

@ -450,6 +450,7 @@ namespace MWBase
/// \todo this does not belong here
virtual void screenshot (osg::Image* image, int w, int h) = 0;
virtual bool screenshot360 (osg::Image* image, std::string settingStr) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise

@ -153,7 +153,7 @@ namespace MWGui
virtual osg::BoundingSphere computeBound(const osg::Node&) const { return osg::BoundingSphere(); }
};
void LoadingScreen::loadingOn()
void LoadingScreen::loadingOn(bool visible)
{
mLoadingOnTime = mTimer.time_m();
// Early-out if already on
@ -169,7 +169,10 @@ namespace MWGui
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
mShowWallpaper = (MWBase::Environment::get().getStateManager()->getState()
mVisible = visible;
mLoadingBox->setVisible(mVisible);
mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
setVisible(true);
@ -180,10 +183,15 @@ namespace MWGui
}
MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);
if (!mVisible)
draw();
}
void LoadingScreen::loadingOff()
{
mLoadingBox->setVisible(true); // restore
if (mLastRenderTime < mLoadingOnTime)
{
// the loading was so fast that we didn't show loading screen at all
@ -306,7 +314,7 @@ namespace MWGui
void LoadingScreen::draw()
{
if (!needToDrawLoadingScreen())
if (mVisible && !needToDrawLoadingScreen())
return;
if (mShowWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)

@ -35,7 +35,7 @@ namespace MWGui
/// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
virtual void setLabel (const std::string& label, bool important);
virtual void loadingOn();
virtual void loadingOn(bool visible=true);
virtual void loadingOff();
virtual void setProgressRange (size_t range);
virtual void setProgress (size_t value);
@ -63,6 +63,8 @@ namespace MWGui
bool mImportantLabel;
bool mVisible;
size_t mProgress;
bool mShowWallpaper;

@ -36,12 +36,14 @@ namespace MWInput
SDL_Window* window,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists,
const std::string& controllerBindingsFile, bool grab)
: mWindow(window)
, mWindowVisible(true)
, mViewer(viewer)
, mScreenCaptureHandler(screenCaptureHandler)
, mScreenCaptureOperation(screenCaptureOperation)
, mJoystickLastUsed(false)
, mPlayer(NULL)
, mInputManager(NULL)
@ -1033,8 +1035,34 @@ namespace MWInput
void InputManager::screenshot()
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);
bool regularScreenshot = true;
bool screenshotTaken = false;
std::string settingStr;
settingStr = Settings::Manager::getString("screenshot type","Video");
regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0;
if (regularScreenshot)
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);
screenshotTaken = true;
}
else
{
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(),settingStr))
{
(*mScreenCaptureOperation) (*(screenshot.get()),0);
// FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
screenshotTaken = true;
}
}
if (screenshotTaken)
MWBase::Environment::get().getWindowManager()->messageBox("Screenshot saved");
}
void InputManager::toggleInventory()

@ -4,6 +4,7 @@
#include "../mwgui/mode.hpp"
#include <osg/ref_ptr>
#include <osgViewer/ViewerEventHandlers>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
@ -14,7 +15,6 @@
#include "../mwbase/inputmanager.hpp"
namespace MWWorld
{
class Player;
@ -74,6 +74,7 @@ namespace MWInput
SDL_Window* window,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists,
const std::string& controllerBindingsFile, bool grab);
@ -158,6 +159,7 @@ namespace MWInput
bool mWindowVisible;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
bool mJoystickLastUsed;
MWWorld::Player* mPlayer;

@ -12,16 +12,21 @@
#include <osg/Group>
#include <osg/UserDataContainer>
#include <osg/ComputeBoundsVisitor>
#include <osg/ShapeDrawable>
#include <osg/TextureCubeMap>
#include <osgUtil/LineSegmentIntersector>
#include <osgUtil/IncrementalCompileOperation>
#include <osg/ImageUtils>
#include <osgViewer/Viewer>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/keyframemanager.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/settings/settings.hpp>
@ -39,7 +44,12 @@
#include <components/esm/loadcell.hpp>
#include <components/fallback/fallback.hpp>
#include <boost/algorithm/string.hpp>
#include "../mwworld/cellstore.hpp"
#include "../mwgui/loadingscreen.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "sky.hpp"
#include "effectmanager.hpp"
@ -212,7 +222,7 @@ namespace MWRender
mSceneRoot = sceneRoot;
sceneRoot->setStartLight(1);
mRootNode->addChild(sceneRoot);
mRootNode->addChild(mSceneRoot);
mPathgrid.reset(new Pathgrid(mRootNode));
@ -668,7 +678,203 @@ namespace MWRender
mutable bool mDone;
};
void RenderingManager::screenshot(osg::Image *image, int w, int h)
bool RenderingManager::screenshot360(osg::Image* image, std::string settingStr)
{
int screenshotW = mViewer->getCamera()->getViewport()->width();
int screenshotH = mViewer->getCamera()->getViewport()->height();
int screenshotMapping = 0;
int cubeSize = screenshotMapping == 2 ?
screenshotW: // planet mapping needs higher resolution
screenshotW / 2;
std::vector<std::string> settingArgs;
boost::algorithm::split(settingArgs,settingStr,boost::is_any_of(" "));
if (settingArgs.size() > 0)
{
std::string typeStrings[4] = {"spherical","cylindrical","planet","cubemap"};
bool found = false;
for (int i = 0; i < 4; ++i)
if (settingArgs[0].compare(typeStrings[i]) == 0)
{
screenshotMapping = i;
found = true;
break;
}
if (!found)
{
std::cerr << "Wrong screenshot type: " << settingArgs[0] << "." << std::endl;
return false;
}
}
if (settingArgs.size() > 1)
screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str()));
if (settingArgs.size() > 2)
screenshotH = std::min(10000,std::atoi(settingArgs[2].c_str()));
if (settingArgs.size() > 3)
cubeSize = std::min(5000,std::atoi(settingArgs[3].c_str()));
if (mCamera->isVanityOrPreviewModeEnabled())
{
std::cerr << "Spherical screenshots are not allowed in preview mode." << std::endl;
return false;
}
bool rawCubemap = screenshotMapping == 3;
if (rawCubemap)
screenshotW = cubeSize * 6; // the image will consist of 6 cube sides in a row
else if (screenshotMapping == 2)
screenshotH = screenshotW; // use square resolution for planet mapping
std::vector<osg::ref_ptr<osg::Image>> images;
for (int i = 0; i < 6; ++i)
images.push_back(new osg::Image);
osg::Vec3 directions[6] = {
rawCubemap ? osg::Vec3(1,0,0) : osg::Vec3(0,0,1),
osg::Vec3(0,0,-1),
osg::Vec3(-1,0,0),
rawCubemap ? osg::Vec3(0,0,1) : osg::Vec3(1,0,0),
osg::Vec3(0,1,0),
osg::Vec3(0,-1,0)};
double rotations[] = {
-osg::PI / 2.0,
osg::PI / 2.0,
osg::PI,
0,
osg::PI / 2.0,
osg::PI / 2.0};
double fovBackup = mFieldOfView;
mFieldOfView = 90.0; // each cubemap side sees 90 degrees
int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
if (mCamera->isFirstPerson())
mPlayerAnimation->getObjectRoot()->setNodeMask(0);
for (int i = 0; i < 6; ++i) // for each cubemap side
{
osg::Matrixd transform = osg::Matrixd::rotate(osg::Vec3(0,0,-1),directions[i]);
if (!rawCubemap)
transform *= osg::Matrixd::rotate(rotations[i],osg::Vec3(0,0,-1));
osg::Image *sideImage = images[i].get();
screenshot(sideImage,cubeSize,cubeSize,transform);
if (!rawCubemap)
sideImage->flipHorizontal();
}
mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);
mFieldOfView = fovBackup;
if (rawCubemap) // for raw cubemap don't run on GPU, just merge the images
{
image->allocateImage(cubeSize * 6,cubeSize,images[0]->r(),images[0]->getPixelFormat(),images[0]->getDataType());
for (int i = 0; i < 6; ++i)
osg::copyImage(images[i].get(),0,0,0,images[i]->s(),images[i]->t(),images[i]->r(),image,i * cubeSize,0,0);
return true;
}
// run on GPU now:
osg::ref_ptr<osg::TextureCubeMap> cubeTexture (new osg::TextureCubeMap);
cubeTexture->setResizeNonPowerOfTwoHint(false);
cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST);
cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);
cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
for (int i = 0; i < 6; ++i)
cubeTexture->setImage(i,images[i].get());
osg::ref_ptr<osg::Camera> screenshotCamera (new osg::Camera);
osg::ref_ptr<osg::ShapeDrawable> quad (new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0),2.0)));
std::map<std::string, std::string> defineMap;
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
osg::ref_ptr<osg::Shader> fragmentShader (shaderMgr.getShader("s360_fragment.glsl",defineMap,osg::Shader::FRAGMENT));
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("s360_vertex.glsl", defineMap, osg::Shader::VERTEX));
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
osg::ref_ptr<osg::Program> program (new osg::Program);
program->addShader(fragmentShader);
program->addShader(vertexShader);
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform("cubeMap",0));
stateset->addUniform(new osg::Uniform("mapping",screenshotMapping));
stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON);
quad->setStateSet(stateset);
quad->setUpdateCallback(NULL);
screenshotCamera->addChild(quad);
mRootNode->addChild(screenshotCamera);
renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH);
screenshotCamera->removeChildren(0,screenshotCamera->getNumChildren());
mRootNode->removeChild(screenshotCamera);
return true;
}
void RenderingManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h)
{
camera->setNodeMask(Mask_RenderToTexture);
camera->attach(osg::Camera::COLOR_BUFFER, image);
camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT);
camera->setViewport(0, 0, w, h);
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
texture->setInternalFormat(GL_RGB);
texture->setTextureSize(w,h);
texture->setResizeNonPowerOfTwoHint(false);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
camera->attach(osg::Camera::COLOR_BUFFER,texture);
image->setDataType(GL_UNSIGNED_BYTE);
image->setPixelFormat(texture->getInternalFormat());
// The draw needs to complete before we can copy back our image.
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback);
camera->setFinalDrawCallback(callback);
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false);
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
callback->waitTillDone();
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff();
// now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
}
void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform)
{
osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);
rttCamera->setNodeMask(Mask_RenderToTexture);
@ -677,7 +883,8 @@ namespace MWRender
rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance);
rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix());
rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform);
rttCamera->setViewport(0, 0, w, h);
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
@ -693,25 +900,17 @@ namespace MWRender
rttCamera->setUpdateCallback(new NoTraverseCallback);
rttCamera->addChild(mSceneRoot);
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
mRootNode->addChild(rttCamera);
rttCamera->addChild(mWater->getReflectionCamera());
rttCamera->addChild(mWater->getRefractionCamera());
// The draw needs to complete before we can copy back our image.
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback);
rttCamera->setFinalDrawCallback(callback);
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
// at the time this function is called we are in the middle of a frame,
// so out of order calls are necessary to get a correct frameNumber for the next frame.
// refer to the advance() and frame() order in Engine::go()
mViewer->eventTraversal();
mViewer->updateTraversal();
mViewer->renderingTraversals();
mRootNode->addChild(rttCamera);
callback->waitTillDone();
rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
renderCameraToImage(rttCamera.get(),image,w,h);
rttCamera->removeChildren(0, rttCamera->getNumChildren());
mRootNode->removeChild(rttCamera);

@ -3,6 +3,7 @@
#include <osg/ref_ptr>
#include <osg/Light>
#include <osg/Camera>
#include <components/settings/settings.hpp>
@ -125,7 +126,8 @@ namespace MWRender
void setWaterHeight(float level);
/// Take a screenshot of w*h onto the given image, not including the GUI.
void screenshot(osg::Image* image, int w, int h);
void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd());
bool screenshot360(osg::Image* image, std::string settingStr);
struct RayResult
{
@ -213,6 +215,8 @@ namespace MWRender
void reportStats() const;
void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h);
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;

@ -470,6 +470,16 @@ void Water::updateWaterMaterial()
updateVisible();
}
osg::Camera *Water::getReflectionCamera()
{
return mReflection;
}
osg::Camera *Water::getRefractionCamera()
{
return mRefraction;
}
void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
{
osg::ref_ptr<osg::StateSet> stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water);

@ -7,6 +7,7 @@
#include <osg/ref_ptr>
#include <osg/Vec3f>
#include <osg/Uniform>
#include <osg/Camera>
#include <components/settings/settings.hpp>
@ -112,6 +113,9 @@ namespace MWRender
void update(float dt);
osg::Camera *getReflectionCamera();
osg::Camera *getRefractionCamera();
void processChangedSettings(const Settings::CategorySettingVector& settings);
osg::Uniform *getRainIntensityUniform();

@ -2301,6 +2301,11 @@ namespace MWWorld
mRendering->screenshot(image, w, h);
}
bool World::screenshot360(osg::Image* image, std::string settingStr)
{
return mRendering->screenshot360(image,settingStr);
}
void World::activateDoor(const MWWorld::Ptr& door)
{
int state = door.getClass().getDoorState(door);

@ -561,6 +561,7 @@ namespace MWWorld
/// \todo this does not belong here
void screenshot (osg::Image* image, int w, int h) override;
bool screenshot360 (osg::Image* image, std::string settingStr) override;
/// Find center of exterior cell above land surface
/// \return false if exterior with given name not exists, true otherwise

@ -20,7 +20,7 @@ namespace Loading
/// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically.
/// @note It is best to use the ScopedLoad object instead of using loadingOn()/loadingOff() directly,
/// so that the loading is exception safe.
virtual void loadingOn() {}
virtual void loadingOn(bool visible=true) {}
virtual void loadingOff() {}
/// Set the total range of progress (e.g. the number of objects to load).

@ -402,6 +402,10 @@ contrast = 1.0
# Video gamma setting. (>0.0). No effect in Linux.
gamma = 1.0
# Type of screenshot to take (regular, cylindrical, spherical or planet), optionally followed by
# screenshot width, height and cubemap resolution in pixels. (e.g. spherical 1600 1000 1200)
screenshot type = regular
[Water]
# Enable water shader with reflections and optionally refraction.

@ -16,6 +16,8 @@ set(SHADER_FILES
terrain_fragment.glsl
lighting.glsl
parallax.glsl
s360_fragment.glsl
s360_vertex.glsl
)
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")

@ -0,0 +1,52 @@
#version 120
varying vec2 uv;
uniform samplerCube cubeMap;
uniform int mapping;
#define PI 3.1415926535
vec3 sphericalCoords(vec2 coords)
{
coords.x = -1 * coords.x * 2 * PI;
coords.y = (coords.y - 0.5) * PI;
vec3 result = vec3(0.0,cos(coords.y),sin(coords.y));
result = vec3(cos(coords.x) * result.y,sin(coords.x) * result.y,result.z);
return result;
}
vec3 cylindricalCoords(vec2 coords)
{
return normalize(vec3(cos(-1 * coords.x * 2 * PI),sin(-1 * coords.x * 2 * PI),coords.y * 2.0 - 1.0));
}
vec3 planetCoords(vec2 coords)
{
vec2 fromCenter = coords - vec2(0.5,0.5);
float magnitude = length(fromCenter);
fromCenter = normalize(fromCenter);
float dotProduct = dot(fromCenter,vec2(0.0,1.0));
coords.x = coords.x > 0.5 ? 0.5 - (dotProduct + 1.0) / 4.0 : 0.5 + (dotProduct + 1.0) / 4.0;
coords.y = max(0.0,1.0 - pow(magnitude / 0.5,0.5));
return sphericalCoords(coords);
}
void main(void)
{
vec3 c;
if (mapping == 0)
c = sphericalCoords(uv);
else if (mapping == 1)
c = cylindricalCoords(uv);
else
c = planetCoords(uv);
gl_FragData[0] = textureCube(cubeMap,c);
}

@ -0,0 +1,9 @@
#version 120
varying vec2 uv;
void main(void)
{
gl_Position = gl_Vertex;
uv = (gl_Vertex.xy * vec2(1.0,-1.0) + vec2(1.0)) / 2;
}
Loading…
Cancel
Save