Imported Upstream version 0.26.0

This commit is contained in:
Bret Curtis 2013-10-17 16:37:22 +02:00
commit 9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions

20
extern/oics/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,20 @@
set(OICS_LIBRARY "oics")
# Sources
set(OICS_SOURCE_FILES
ICSChannel.cpp
ICSControl.cpp
ICSInputControlSystem.cpp
ICSInputControlSystem_keyboard.cpp
ICSInputControlSystem_mouse.cpp
ICSInputControlSystem_joystick.cpp
tinyxml.cpp
tinyxmlparser.cpp
tinyxmlerror.cpp
tinystr.cpp
)
add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES})
link_directories(${CMAKE_CURRENT_BINARY_DIR})

258
extern/oics/ICSChannel.cpp vendored Normal file
View file

@ -0,0 +1,258 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
#define B1(t) (t*t)
#define B2(t) (2*t*(1-t))
#define B3(t) ((1-t)*(1-t))
namespace ICS
{
Channel::Channel(int number, float initialValue
, float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep)
: mNumber(number)
, mValue(initialValue)
, mSymmetricAt(symmetricAt)
, mBezierStep(bezierStep)
{
mBezierMidPoint.x = bezierMidPointX;
mBezierMidPoint.y = bezierMidPointY;
setBezierFunction(bezierMidPointY, bezierMidPointX, symmetricAt, bezierStep);
}
float Channel::getValue()
{
if(mValue == 0 || mValue == 1)
{
return mValue;
}
BezierFunction::iterator it = mBezierFunction.begin();
//size_t size_minus_1 = mBezierFunction.size() - 1;
BezierFunction::iterator last = mBezierFunction.end();
last--;
for ( ; it != last ; )
{
BezierPoint left = (*it);
BezierPoint right = (*(++it));
if( (left.x <= mValue) && (right.x > mValue) )
{
float val = left.y - (left.x - mValue) * (left.y - right.y) / (left.x - right.x);
return std::max<float>(0.0,std::min<float>(1.0, val));
}
}
return -1;
}
void Channel::setValue(float value)
{
float previousValue = this->getValue();
mValue = value;
if(previousValue != value)
{
notifyListeners(previousValue);
}
}
void Channel::notifyListeners(float previousValue)
{
std::list<ChannelListener*>::iterator pos = mListeners.begin();
while (pos != mListeners.end())
{
((ChannelListener* )(*pos))->channelChanged((Channel*)this, this->getValue(), previousValue);
++pos;
}
}
void Channel::addControl(Control* control, Channel::ChannelDirection dir, float percentage)
{
ControlChannelBinderItem ccBinderItem;
ccBinderItem.control = control;
ccBinderItem.direction = dir;
ccBinderItem.percentage = percentage;
mAttachedControls.push_back(ccBinderItem);
}
Channel::ControlChannelBinderItem Channel::getAttachedControlBinding(Control* control)
{
for(std::vector<ControlChannelBinderItem>::iterator it = mAttachedControls.begin() ;
it != mAttachedControls.end() ; it++)
{
if((*it).control == control)
{
return (*it);
}
}
ControlChannelBinderItem nullBinderItem;
nullBinderItem.control = NULL;
nullBinderItem.direction = Channel/*::ChannelDirection*/::DIRECT;
nullBinderItem.percentage = 0;
return nullBinderItem;
}
void Channel::update()
{
if(this->getControlsCount() == 1)
{
ControlChannelBinderItem ccBinderItem = mAttachedControls.back();
float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue();
if(ccBinderItem.direction == ICS::Channel::DIRECT)
{
this->setValue(ccBinderItem.control->getInitialValue() + (ccBinderItem.percentage * diff));
}
else
{
this->setValue(ccBinderItem.control->getInitialValue() - (ccBinderItem.percentage * diff));
}
}
else
{
float val = 0;
std::vector<ControlChannelBinderItem>::const_iterator it;
for(it=mAttachedControls.begin(); it!=mAttachedControls.end(); ++it)
{
ControlChannelBinderItem ccBinderItem = (*it);
float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue();
if(ccBinderItem.direction == ICS::Channel::DIRECT)
{
val += (ccBinderItem.percentage * diff);
}
else
{
val -= (ccBinderItem.percentage * diff);
}
}
if(mAttachedControls.size() > 0)
{
this->setValue(mAttachedControls.begin()->control->getInitialValue() + val);
}
}
}
void Channel::setBezierFunction(float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep)
{
mBezierMidPoint.x = bezierMidPointX;
mBezierMidPoint.y = bezierMidPointY;
mBezierStep = bezierStep;
mSymmetricAt = symmetricAt;
mBezierFunction.clear();
BezierPoint start;
start.x = 0;
start.y = 0;
BezierPoint end;
end.x = 1;
end.y = 1;
mBezierFunction.push_front(end);
FilterInterval interval;
interval.startX = start.x;
interval.startY = start.y;
interval.midX = mBezierMidPoint.x;
interval.midY = mBezierMidPoint.y;
interval.endX = end.x;
interval.endY = end.y;
interval.step = bezierStep;
mIntervals.push_back(interval);
if(!(mBezierMidPoint.x == 0.5 && mBezierMidPoint.y == 0.5))
{
float t = mBezierStep;
while(t < 1)
{
BezierPoint p;
p.x = start.x * B1(t) + mBezierMidPoint.x * B2(t) + end.x * B3(t);
p.y = start.y * B1(t) + mBezierMidPoint.y * B2(t) + end.y * B3(t);
mBezierFunction.push_front(p);
t += mBezierStep;
}
}
mBezierFunction.push_front(start);
}
void Channel::addBezierInterval(float startX, float startY, float midX, float midY
, float endX, float endY, float step)
{
FilterInterval interval;
interval.startX = startX;
interval.startY = startY;
interval.midX = midX;
interval.midY = midY;
interval.endX = endX;
interval.endY = endY;
interval.step = step;
mIntervals.push_back(interval);
float t = 0;
while(t <= 1)
{
BezierPoint p;
p.x = startX * B1(t) + midX * B2(t) + endX * B3(t);
p.y = startY * B1(t) + midY * B2(t) + endY * B3(t);
BezierFunction::iterator it = mBezierFunction.begin();
while( it != mBezierFunction.end() )
{
BezierPoint left = (*it);
BezierPoint right;
++it;
if( it != mBezierFunction.end() )
{
right = (*it);
}
else
{
right.x = endX;
right.y = endY;
}
if(p.x > left.x && p.x < right.x)
{
mBezierFunction.insert(it, p);
break;
}
}
t += 1.0f / ((endX-startX)/step);
}
}
}

122
extern/oics/ICSChannel.h vendored Normal file
View file

@ -0,0 +1,122 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#ifndef _Channel_H_
#define _Channel_H_
#include "ICSPrerequisites.h"
#include "ICSChannelListener.h"
namespace ICS
{
struct FilterInterval{
//std::string type; //! @todo uncomment when more types implemented
float startX;
float startY;
float midX;
float midY;
float endX;
float endY;
float step;
};
typedef std::list<FilterInterval> IntervalList;
class DllExport Channel
{
public:
enum ChannelDirection
{
INVERSE = -1, DIRECT = 1
};
typedef struct {
ChannelDirection direction;
float percentage;
Control* control;
} ControlChannelBinderItem;
Channel(int number, float initialValue = 0.5
, float bezierMidPointY = 0.5, float bezierMidPointX = 0.5
, float symmetricAt = 0, float bezierStep = 0.2); //! @todo implement symetry
~Channel(){};
void setValue(float value);
float getValue();
inline int getNumber(){ return mNumber; };
void addControl(Control* control, Channel::ChannelDirection dir, float percentage);
inline size_t getControlsCount(){ return mAttachedControls.size(); };
std::vector<ControlChannelBinderItem> getAttachedControls(){ return mAttachedControls; };
ControlChannelBinderItem getAttachedControlBinding(Control* control);
void addListener(ChannelListener* ob){ mListeners.push_back(ob); };
void removeListener(ChannelListener* ob){ mListeners.remove(ob); };
void update();
void setBezierFunction(float bezierMidPointY, float bezierMidPointX = 0.5
, float symmetricAt = 0, float bezierStep = 0.2);
void addBezierInterval(float startX, float startY, float midX, float midY
, float endX, float endY, float step = 0.1);
IntervalList& getIntervals(){ return mIntervals; };
protected:
int mNumber;
float mValue;
struct BezierPoint{
float x;
float y;
bool operator < (const BezierPoint& other){ return x < other.x; }
};
typedef std::list<BezierPoint> BezierFunction;
BezierPoint mBezierMidPoint;
BezierFunction mBezierFunction;
float mSymmetricAt;
float mBezierStep;
IntervalList mIntervals;
std::vector<ControlChannelBinderItem> mAttachedControls;
std::list<ChannelListener* > mListeners;
void notifyListeners(float previousValue);
};
}
#endif

46
extern/oics/ICSChannelListener.h vendored Normal file
View file

@ -0,0 +1,46 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#ifndef _ChannelListener_H_
#define _ChannelListener_H_
#include "ICSPrerequisites.h"
#include "ICSChannel.h"
namespace ICS
{
class DllExport ChannelListener
{
public:
virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0;
};
}
#endif

161
extern/oics/ICSControl.cpp vendored Normal file
View file

@ -0,0 +1,161 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
#include "ICSControl.h"
namespace ICS
{
Control::Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue
, float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable)
: mName(name)
, mValue(initialValue)
, mInitialValue(initialValue)
, mStepSize(stepSize)
, mStepsPerSeconds(stepsPerSeconds)
, mAutoReverseToInitialValue(autoReverseToInitialValue)
, mIgnoreAutoReverse(false)
, mAutoChangeDirectionOnLimitsAfterStop(autoChangeDirectionOnLimitsAfterStop)
, mAxisBindable(axisBindable)
, currentChangingDirection(STOP)
{
}
Control::~Control()
{
mAttachedChannels.clear();
}
void Control::setValue(float value)
{
float previousValue = mValue;
mValue = std::max<float>(0.0,std::min<float>(1.0,value));
if(mValue != previousValue)
{
updateChannels();
notifyListeners(previousValue);
}
}
void Control::attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage)
{
mAttachedChannels.push_back(channel);
channel->addControl(this, direction, percentage);
}
void Control::updateChannels()
{
std::list<Channel*>::iterator pos = mAttachedChannels.begin();
while (pos != mAttachedChannels.end())
{
((Channel* )(*pos))->update();
++pos;
}
}
void Control::notifyListeners(float previousValue)
{
std::list<ControlListener*>::iterator pos = mListeners.begin();
while (pos != mListeners.end())
{
((ControlListener* )(*pos))->controlChanged((Control*)this, this->getValue(), previousValue);
++pos;
}
}
void Control::setChangingDirection(ControlChangingDirection direction)
{
currentChangingDirection = direction;
mPendingActions.push_back(direction);
}
void Control::update(float timeSinceLastFrame)
{
if(mPendingActions.size() > 0)
{
size_t timedActionsCount = 0;
std::list<Control::ControlChangingDirection>::iterator cached_end = mPendingActions.end();
for(std::list<Control::ControlChangingDirection>::iterator it = mPendingActions.begin() ;
it != cached_end ; it++)
{
if( (*it) != Control::STOP )
{
timedActionsCount++;
}
}
float timeSinceLastFramePart = timeSinceLastFrame / std::max<size_t>(1, timedActionsCount);
for(std::list<Control::ControlChangingDirection>::iterator it = mPendingActions.begin() ;
it != cached_end ; it++)
{
if( (*it) != Control::STOP )
{
this->setValue(mValue +
(((int)(*it)) * mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)));
}
else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue )
{
if(mValue > mInitialValue)
{
this->setValue( std::max<float>( mInitialValue,
mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))));
}
else if(mValue < mInitialValue)
{
this->setValue( std::min<float>( mInitialValue,
mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))));
}
}
}
mPendingActions.clear();
}
else if( currentChangingDirection != Control::STOP )
{
this->setValue(mValue +
(((int)currentChangingDirection) * mStepSize * mStepsPerSeconds * (timeSinceLastFrame)));
}
else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue )
{
if(mValue > mInitialValue)
{
this->setValue( std::max<float>( mInitialValue,
mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFrame))));
}
else if(mValue < mInitialValue)
{
this->setValue( std::min<float>( mInitialValue,
mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFrame))));
}
}
}
}

107
extern/oics/ICSControl.h vendored Normal file
View file

@ -0,0 +1,107 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#ifndef _Control_H_
#define _Control_H_
#include "ICSPrerequisites.h"
#include "ICSChannel.h"
#include "ICSControlListener.h"
namespace ICS
{
class DllExport Control
{
public:
enum ControlChangingDirection
{
DECREASE = -1, STOP = 0, INCREASE = 1
};
Control(const std::string& name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true);
~Control();
void setChangingDirection(ControlChangingDirection direction);
inline ControlChangingDirection getChangingDirection(){ return currentChangingDirection; };
void setValue(float value);
inline float getValue(){ return mValue; };
inline float getInitialValue(){ return mInitialValue; };
void attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage = 1.0);
std::list<Channel*> getAttachedChannels(){ return mAttachedChannels; };
inline float getStepSize(){ return mStepSize; };
inline float getStepsPerSeconds(){ return mStepsPerSeconds; };
inline void setIgnoreAutoReverse(bool value){ mIgnoreAutoReverse = value; }; // mouse disable autoreverse
inline bool isAutoReverseIgnored(){ return mIgnoreAutoReverse; };
inline bool getAutoReverse(){ return mAutoReverseToInitialValue; };
inline bool getAutoChangeDirectionOnLimitsAfterStop(){ return mAutoChangeDirectionOnLimitsAfterStop; };
inline std::string getName(){ return mName; };
inline bool isAxisBindable(){ return mAxisBindable; };
inline void setAxisBindable(bool value){ mAxisBindable = value; };
inline void addListener(ControlListener* ob){ mListeners.push_back(ob); };
inline void removeListener(ControlListener* ob){ mListeners.remove(ob); };
void update(float timeSinceLastFrame);
protected:
float mValue;
float mInitialValue;
std::string mName;
float mStepSize;
float mStepsPerSeconds;
bool mAutoReverseToInitialValue;
bool mIgnoreAutoReverse;
bool mAutoChangeDirectionOnLimitsAfterStop;
bool mAxisBindable;
Control::ControlChangingDirection currentChangingDirection;
std::list<Channel*> mAttachedChannels;
std::list<ControlListener*> mListeners;
std::list<Control::ControlChangingDirection> mPendingActions;
protected:
void updateChannels();
void notifyListeners(float previousValue);
};
}
#endif

46
extern/oics/ICSControlListener.h vendored Normal file
View file

@ -0,0 +1,46 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#ifndef _ControlListener_H_
#define _ControlListener_H_
#include "ICSPrerequisites.h"
#include "ICSControl.h"
namespace ICS
{
class DllExport ControlListener
{
public:
virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0;
};
}
#endif

933
extern/oics/ICSInputControlSystem.cpp vendored Normal file
View file

@ -0,0 +1,933 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
namespace ICS
{
InputControlSystem::InputControlSystem(std::string file, bool active
, DetectingBindingListener* detectingBindingListener
, InputControlSystemLog* log, size_t channelCount)
: mFileName(file)
, mDetectingBindingListener(detectingBindingListener)
, mDetectingBindingControl(NULL)
, mLog(log)
, mXmouseAxisBinded(false), mYmouseAxisBinded(false)
{
ICS_LOG(" - Creating InputControlSystem - ");
this->mActive = active;
this->fillSDLKeysMap();
ICS_LOG("Channel count = " + ToString<size_t>(channelCount) );
for(size_t i=0;i<channelCount;i++)
{
mChannels.push_back(new Channel((int)i));
}
if(file != "")
{
TiXmlDocument* xmlDoc;
TiXmlElement* xmlRoot;
ICS_LOG("Loading file \""+file+"\"");
xmlDoc = new TiXmlDocument(file.c_str());
xmlDoc->LoadFile();
if(xmlDoc->Error())
{
std::ostringstream message;
message << "TinyXml reported an error reading \""+ file + "\". Row " <<
(int)xmlDoc->ErrorRow() << ", Col " << (int)xmlDoc->ErrorCol() << ": " <<
xmlDoc->ErrorDesc() ;
ICS_LOG(message.str());
delete xmlDoc;
return;
}
xmlRoot = xmlDoc->RootElement();
if(std::string(xmlRoot->Value()) != "Controller") {
ICS_LOG("Error: Invalid Controller file. Missing <Controller> element.");
delete xmlDoc;
return;
}
TiXmlElement* xmlControl = xmlRoot->FirstChildElement("Control");
size_t controlChannelCount = 0;
while(xmlControl)
{
TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel");
while(xmlChannel)
{
controlChannelCount = std::max(channelCount, FromString<size_t>(xmlChannel->Attribute("number")));
xmlChannel = xmlChannel->NextSiblingElement("Channel");
}
xmlControl = xmlControl->NextSiblingElement("Control");
}
if(controlChannelCount > channelCount)
{
size_t dif = controlChannelCount - channelCount;
ICS_LOG("Warning: default channel count exceeded. Adding " + ToString<size_t>(dif) + " channels" );
for(size_t i = channelCount ; i < controlChannelCount ; i++)
{
mChannels.push_back(new Channel((int)i));
}
}
ICS_LOG("Applying filters to channels");
//<ChannelFilter number="0">
// <interval type="bezier" startX="0.0" startY="0.0" midX="0.25" midY="0.5" endX="0.5" endY="0.5" step="0.1" />
// <interval type="bezier" startX="0.5" startY="0.5" midX="0.75" midY="0.5" endX="1.0" endY="1.0" step="0.1" />
//</ChannelFilter>
TiXmlElement* xmlChannelFilter = xmlRoot->FirstChildElement("ChannelFilter");
while(xmlChannelFilter)
{
int ch = FromString<int>(xmlChannelFilter->Attribute("number"));
TiXmlElement* xmlInterval = xmlChannelFilter->FirstChildElement("Interval");
while(xmlInterval)
{
std::string type = xmlInterval->Attribute("type");
if(type == "bezier")
{
float step = 0.1;
float startX = FromString<float>(xmlInterval->Attribute("startX"));
float startY = FromString<float>(xmlInterval->Attribute("startY"));
float midX = FromString<float>(xmlInterval->Attribute("midX"));
float midY = FromString<float>(xmlInterval->Attribute("midY"));
float endX = FromString<float>(xmlInterval->Attribute("endX"));
float endY = FromString<float>(xmlInterval->Attribute("endY"));
step = FromString<float>(xmlInterval->Attribute("step"));
ICS_LOG("Applying Bezier filter to channel [number="
+ ToString<int>(ch) + ", startX="
+ ToString<float>(startX) + ", startY="
+ ToString<float>(startY) + ", midX="
+ ToString<float>(midX) + ", midY="
+ ToString<float>(midY) + ", endX="
+ ToString<float>(endX) + ", endY="
+ ToString<float>(endY) + ", step="
+ ToString<float>(step) + "]");
mChannels.at(ch)->addBezierInterval(startX, startY, midX, midY, endX, endY, step);
}
xmlInterval = xmlInterval->NextSiblingElement("Interval");
}
xmlChannelFilter = xmlChannelFilter->NextSiblingElement("ChannelFilter");
}
xmlControl = xmlRoot->FirstChildElement("Control");
while(xmlControl)
{
bool axisBindable = true;
if(xmlControl->Attribute("axisBindable"))
{
axisBindable = (std::string( xmlControl->Attribute("axisBindable") ) == "true");
}
ICS_LOG("Adding Control [name="
+ std::string( xmlControl->Attribute("name") ) + ", autoChangeDirectionOnLimitsAfterStop="
+ std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) + ", autoReverseToInitialValue="
+ std::string( xmlControl->Attribute("autoReverseToInitialValue") ) + ", initialValue="
+ std::string( xmlControl->Attribute("initialValue") ) + ", stepSize="
+ std::string( xmlControl->Attribute("stepSize") ) + ", stepsPerSeconds="
+ std::string( xmlControl->Attribute("stepsPerSeconds") ) + ", axisBindable="
+ std::string( (axisBindable)? "true" : "false" ) + "]");
float _stepSize = 0;
if(xmlControl->Attribute("stepSize"))
{
std::string value(xmlControl->Attribute("stepSize"));
if(value == "MAX")
{
_stepSize = ICS_MAX;
}
else
{
_stepSize = FromString<float>(value.c_str());
}
}
else
{
ICS_LOG("Warning: no stepSize value found. Default value is 0.");
}
float _stepsPerSeconds = 0;
if(xmlControl->Attribute("stepsPerSeconds"))
{
std::string value(xmlControl->Attribute("stepsPerSeconds"));
if(value == "MAX")
{
_stepsPerSeconds = ICS_MAX;
}
else
{
_stepsPerSeconds = FromString<float>(value.c_str());
}
}
else
{
ICS_LOG("Warning: no stepSize value found. Default value is 0.");
}
addControl( new Control(xmlControl->Attribute("name")
, std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) == "true"
, std::string( xmlControl->Attribute("autoReverseToInitialValue") ) == "true"
, FromString<float>(xmlControl->Attribute("initialValue"))
, _stepSize
, _stepsPerSeconds
, axisBindable) );
loadKeyBinders(xmlControl);
loadMouseAxisBinders(xmlControl);
loadMouseButtonBinders(xmlControl);
loadJoystickAxisBinders(xmlControl);
loadJoystickButtonBinders(xmlControl);
loadJoystickPOVBinders(xmlControl);
loadJoystickSliderBinders(xmlControl);
// Attach controls to channels
TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel");
while(xmlChannel)
{
ICS_LOG("\tAttaching control to channel [number="
+ std::string( xmlChannel->Attribute("number") ) + ", direction="
+ std::string( xmlChannel->Attribute("direction") ) + "]");
float percentage = 1;
if(xmlChannel->Attribute("percentage"))
{
if(StringIsNumber<float>(xmlChannel->Attribute("percentage")))
{
float val = FromString<float>(xmlChannel->Attribute("percentage"));
if(val > 1 || val < 0)
{
ICS_LOG("ERROR: attaching percentage value range is [0,1]");
}
else
{
percentage = val;
}
}
else
{
ICS_LOG("ERROR: attaching percentage value range is [0,1]");
}
}
int chNumber = FromString<int>(xmlChannel->Attribute("number"));
if(std::string(xmlChannel->Attribute("direction")) == "DIRECT")
{
mControls.back()->attachChannel(mChannels[ chNumber ],Channel::DIRECT, percentage);
}
else if(std::string(xmlChannel->Attribute("direction")) == "INVERSE")
{
mControls.back()->attachChannel(mChannels[ chNumber ],Channel::INVERSE, percentage);
}
xmlChannel = xmlChannel->NextSiblingElement("Channel");
}
xmlControl = xmlControl->NextSiblingElement("Control");
}
std::vector<Channel *>::const_iterator o;
for(o = mChannels.begin(); o != mChannels.end(); ++o)
{
(*o)->update();
}
delete xmlDoc;
}
ICS_LOG(" - InputControlSystem Created - ");
}
InputControlSystem::~InputControlSystem()
{
ICS_LOG(" - Deleting InputControlSystem (" + mFileName + ") - ");
mJoystickIDList.clear();
std::vector<Channel *>::const_iterator o;
for(o = mChannels.begin(); o != mChannels.end(); ++o)
{
delete (*o);
}
mChannels.clear();
std::vector<Control *>::const_iterator o2;
for(o2 = mControls.begin(); o2 != mControls.end(); ++o2)
{
delete (*o2);
}
mControls.clear();
mControlsKeyBinderMap.clear();
mControlsMouseButtonBinderMap.clear();
mControlsJoystickButtonBinderMap.clear();
mKeys.clear();
mKeyCodes.clear();
ICS_LOG(" - InputControlSystem deleted - ");
}
std::string InputControlSystem::getBaseFileName()
{
size_t found = mFileName.find_last_of("/\\");
std::string file = mFileName.substr(found+1);
return file.substr(0, file.find_last_of("."));
}
bool InputControlSystem::save(std::string fileName)
{
if(fileName != "")
{
mFileName = fileName;
}
TiXmlDocument doc( mFileName.c_str() );
TiXmlDeclaration dec;
dec.Parse( "<?xml version='1.0' encoding='utf-8'?>", 0, TIXML_ENCODING_UNKNOWN );
doc.InsertEndChild(dec);
TiXmlElement Controller( "Controller" );
for(std::vector<Channel*>::const_iterator o = mChannels.begin() ; o != mChannels.end(); o++)
{
ICS::IntervalList intervals = (*o)->getIntervals();
if(intervals.size() > 1) // all channels have a default linear filter
{
TiXmlElement ChannelFilter( "ChannelFilter" );
ChannelFilter.SetAttribute("number", ToString<int>((*o)->getNumber()).c_str());
ICS::IntervalList::const_iterator interval = intervals.begin();
while( interval != intervals.end() )
{
// if not default linear filter
if(!( interval->step == 0.2f
&& interval->startX == 0.0f
&& interval->startY == 0.0f
&& interval->midX == 0.5f
&& interval->midY == 0.5f
&& interval->endX == 1.0f
&& interval->endY == 1.0f ))
{
TiXmlElement XMLInterval( "Interval" );
XMLInterval.SetAttribute("type", "bezier");
XMLInterval.SetAttribute("step", ToString<float>(interval->step).c_str());
XMLInterval.SetAttribute("startX", ToString<float>(interval->startX).c_str());
XMLInterval.SetAttribute("startY", ToString<float>(interval->startY).c_str());
XMLInterval.SetAttribute("midX", ToString<float>(interval->midX).c_str());
XMLInterval.SetAttribute("midY", ToString<float>(interval->midY).c_str());
XMLInterval.SetAttribute("endX", ToString<float>(interval->endX).c_str());
XMLInterval.SetAttribute("endY", ToString<float>(interval->endY).c_str());
ChannelFilter.InsertEndChild(XMLInterval);
}
interval++;
}
Controller.InsertEndChild(ChannelFilter);
}
}
for(std::vector<Control*>::const_iterator o = mControls.begin() ; o != mControls.end(); o++)
{
TiXmlElement control( "Control" );
control.SetAttribute( "name", (*o)->getName().c_str() );
if((*o)->getAutoChangeDirectionOnLimitsAfterStop())
{
control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "true" );
}
else
{
control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "false" );
}
if((*o)->getAutoReverse())
{
control.SetAttribute( "autoReverseToInitialValue", "true" );
}
else
{
control.SetAttribute( "autoReverseToInitialValue", "false" );
}
control.SetAttribute( "initialValue", ToString<float>((*o)->getInitialValue()).c_str() );
if((*o)->getStepSize() == ICS_MAX)
{
control.SetAttribute( "stepSize", "MAX" );
}
else
{
control.SetAttribute( "stepSize", ToString<float>((*o)->getStepSize()).c_str() );
}
if((*o)->getStepsPerSeconds() == ICS_MAX)
{
control.SetAttribute( "stepsPerSeconds", "MAX" );
}
else
{
control.SetAttribute( "stepsPerSeconds", ToString<float>((*o)->getStepsPerSeconds()).c_str() );
}
if(!(*o)->isAxisBindable())
{
control.SetAttribute( "axisBindable", "false" );
}
if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) != SDLK_UNKNOWN)
{
TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
keyBinder.SetAttribute( "direction", "INCREASE" );
control.InsertEndChild(keyBinder);
}
if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) != SDLK_UNKNOWN)
{
TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
keyBinder.SetAttribute( "direction", "DECREASE" );
control.InsertEndChild(keyBinder);
}
if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)
!= InputControlSystem/*::NamedAxis*/::UNASSIGNED)
{
TiXmlElement binder( "MouseBinder" );
InputControlSystem::NamedAxis axis =
getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE);
if(axis == InputControlSystem/*::NamedAxis*/::X)
{
binder.SetAttribute( "axis", "X" );
}
else if(axis == InputControlSystem/*::NamedAxis*/::Y)
{
binder.SetAttribute( "axis", "Y" );
}
else if(axis == InputControlSystem/*::NamedAxis*/::Z)
{
binder.SetAttribute( "axis", "Z" );
}
binder.SetAttribute( "direction", "INCREASE" );
control.InsertEndChild(binder);
}
if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)
!= InputControlSystem/*::NamedAxis*/::UNASSIGNED)
{
TiXmlElement binder( "MouseBinder" );
InputControlSystem::NamedAxis axis =
getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE);
if(axis == InputControlSystem/*::NamedAxis*/::X)
{
binder.SetAttribute( "axis", "X" );
}
else if(axis == InputControlSystem/*::NamedAxis*/::Y)
{
binder.SetAttribute( "axis", "Y" );
}
else if(axis == InputControlSystem/*::NamedAxis*/::Z)
{
binder.SetAttribute( "axis", "Z" );
}
binder.SetAttribute( "direction", "DECREASE" );
control.InsertEndChild(binder);
}
if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)
!= ICS_MAX_DEVICE_BUTTONS)
{
TiXmlElement binder( "MouseButtonBinder" );
unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE);
if(button == SDL_BUTTON_LEFT)
{
binder.SetAttribute( "button", "LEFT" );
}
else if(button == SDL_BUTTON_MIDDLE)
{
binder.SetAttribute( "button", "MIDDLE" );
}
else if(button == SDL_BUTTON_RIGHT)
{
binder.SetAttribute( "button", "RIGHT" );
}
else
{
binder.SetAttribute( "button", ToString<unsigned int>(button).c_str() );
}
binder.SetAttribute( "direction", "INCREASE" );
control.InsertEndChild(binder);
}
if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)
!= ICS_MAX_DEVICE_BUTTONS)
{
TiXmlElement binder( "MouseButtonBinder" );
unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE);
if(button == SDL_BUTTON_LEFT)
{
binder.SetAttribute( "button", "LEFT" );
}
else if(button == SDL_BUTTON_MIDDLE)
{
binder.SetAttribute( "button", "MIDDLE" );
}
else if(button == SDL_BUTTON_RIGHT)
{
binder.SetAttribute( "button", "RIGHT" );
}
else
{
binder.SetAttribute( "button", ToString<unsigned int>(button).c_str() );
}
binder.SetAttribute( "direction", "DECREASE" );
control.InsertEndChild(binder);
}
JoystickIDList::const_iterator it = mJoystickIDList.begin();
while(it != mJoystickIDList.end())
{
int deviceId = *it;
if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)
!= /*NamedAxis::*/UNASSIGNED)
{
TiXmlElement binder( "JoystickAxisBinder" );
binder.SetAttribute( "axis", ToString<int>(
getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
binder.SetAttribute( "direction", "INCREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)
!= /*NamedAxis::*/UNASSIGNED)
{
TiXmlElement binder( "JoystickAxisBinder" );
binder.SetAttribute( "axis", ToString<int>(
getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
binder.SetAttribute( "direction", "DECREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)
!= ICS_MAX_DEVICE_BUTTONS)
{
TiXmlElement binder( "JoystickButtonBinder" );
binder.SetAttribute( "button", ToString<unsigned int>(
getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
binder.SetAttribute( "direction", "INCREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)
!= ICS_MAX_DEVICE_BUTTONS)
{
TiXmlElement binder( "JoystickButtonBinder" );
binder.SetAttribute( "button", ToString<unsigned int>(
getJoystickButtonBinding(*o, *it, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
binder.SetAttribute( "direction", "DECREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE).index >= 0)
{
TiXmlElement binder( "JoystickPOVBinder" );
POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE);
binder.SetAttribute( "pov", ToString<int>(POVPair.index).c_str() );
binder.SetAttribute( "direction", "INCREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
if(POVPair.axis == ICS::InputControlSystem::EastWest)
{
binder.SetAttribute( "axis", "EastWest" );
}
else
{
binder.SetAttribute( "axis", "NorthSouth" );
}
control.InsertEndChild(binder);
}
if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE).index >= 0)
{
TiXmlElement binder( "JoystickPOVBinder" );
POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE);
binder.SetAttribute( "pov", ToString<int>(POVPair.index).c_str() );
binder.SetAttribute( "direction", "DECREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
if(POVPair.axis == ICS::InputControlSystem::EastWest)
{
binder.SetAttribute( "axis", "EastWest" );
}
else
{
binder.SetAttribute( "axis", "NorthSouth" );
}
control.InsertEndChild(binder);
}
if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)
!= /*NamedAxis::*/UNASSIGNED)
{
TiXmlElement binder( "JoystickSliderBinder" );
binder.SetAttribute( "slider", ToString<int>(
getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
binder.SetAttribute( "direction", "INCREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)
!= /*NamedAxis::*/UNASSIGNED)
{
TiXmlElement binder( "JoystickSliderBinder" );
binder.SetAttribute( "slider", ToString<int>(
getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
binder.SetAttribute( "direction", "DECREASE" );
binder.SetAttribute( "deviceId", ToString<int>(deviceId).c_str() );
control.InsertEndChild(binder);
}
it++;
}
std::list<Channel*> channels = (*o)->getAttachedChannels();
for(std::list<Channel*>::iterator it = channels.begin() ;
it != channels.end() ; it++)
{
TiXmlElement binder( "Channel" );
binder.SetAttribute( "number", ToString<int>((*it)->getNumber()).c_str() );
Channel::ChannelDirection direction = (*it)->getAttachedControlBinding(*o).direction;
if(direction == Channel/*::ChannelDirection*/::DIRECT)
{
binder.SetAttribute( "direction", "DIRECT" );
}
else
{
binder.SetAttribute( "direction", "INVERSE" );
}
float percentage = (*it)->getAttachedControlBinding(*o).percentage;
binder.SetAttribute( "percentage", ToString<float>(percentage).c_str() );
control.InsertEndChild(binder);
}
Controller.InsertEndChild(control);
}
doc.InsertEndChild(Controller);
return doc.SaveFile();
}
void InputControlSystem::update(float lTimeSinceLastFrame)
{
if(mActive)
{
std::vector<Control *>::const_iterator it;
for(it=mControls.begin(); it!=mControls.end(); ++it)
{
(*it)->update(lTimeSinceLastFrame);
}
}
//! @todo Future versions should consider channel exponentials and mixtures, so
// after updating Controls, Channels should be updated according to their values
}
float InputControlSystem::getChannelValue(int i)
{
return std::max<float>(0.0,std::min<float>(1.0,mChannels[i]->getValue()));
}
float InputControlSystem::getControlValue(int i)
{
return mControls[i]->getValue();
}
void InputControlSystem::addJoystick(int deviceId)
{
ICS_LOG("Adding joystick (device id: " + ToString<int>(deviceId) + ")");
for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++)
{
if(mControlsJoystickAxisBinderMap[deviceId].find(j) == mControlsJoystickAxisBinderMap[deviceId].end())
{
ControlAxisBinderItem controlJoystickBinderItem;
controlJoystickBinderItem.direction = Control::STOP;
controlJoystickBinderItem.control = NULL;
mControlsJoystickAxisBinderMap[deviceId][j] = controlJoystickBinderItem;
}
}
mJoystickIDList.push_back(deviceId);
}
Control* InputControlSystem::findControl(std::string name)
{
if(mActive)
{
std::vector<Control *>::const_iterator it;
for(it = mControls.begin(); it != mControls.end(); ++it)
{
if( ((Control*)(*it))->getName() == name)
{
return (Control*)(*it);
}
}
}
return NULL;
}
void InputControlSystem::enableDetectingBindingState(Control* control
, Control::ControlChangingDirection direction)
{
mDetectingBindingControl = control;
mDetectingBindingDirection = direction;
mMouseAxisBindingInitialValues[0] = ICS_MOUSE_AXIS_BINDING_NULL_VALUE;
}
void InputControlSystem::cancelDetectingBindingState()
{
mDetectingBindingControl = NULL;
}
void InputControlSystem::fillSDLKeysMap()
{
mKeys["UNASSIGNED"]= SDLK_UNKNOWN;
mKeys["ESCAPE"]= SDLK_ESCAPE;
mKeys["1"]= SDLK_1;
mKeys["2"]= SDLK_2;
mKeys["3"]= SDLK_3;
mKeys["4"]= SDLK_4;
mKeys["5"]= SDLK_5;
mKeys["6"]= SDLK_6;
mKeys["7"]= SDLK_7;
mKeys["8"]= SDLK_8;
mKeys["9"]= SDLK_9;
mKeys["0"]= SDLK_0;
mKeys["MINUS"]= SDLK_MINUS;
mKeys["EQUALS"]= SDLK_EQUALS;
mKeys["BACK"]= SDLK_BACKSPACE;
mKeys["TAB"]= SDLK_TAB;
mKeys["Q"]= SDLK_q;
mKeys["W"]= SDLK_w;
mKeys["E"]= SDLK_e;
mKeys["R"]= SDLK_r;
mKeys["T"]= SDLK_t;
mKeys["Y"]= SDLK_y;
mKeys["U"]= SDLK_u;
mKeys["I"]= SDLK_i;
mKeys["O"]= SDLK_o;
mKeys["P"]= SDLK_p;
mKeys["LBRACKET"]= SDLK_LEFTBRACKET;
mKeys["RBRACKET"]= SDLK_RIGHTBRACKET;
mKeys["RETURN"]= SDLK_RETURN;
mKeys["LCONTROL"]= SDLK_LCTRL;
mKeys["A"]= SDLK_a;
mKeys["S"]= SDLK_s;
mKeys["D"]= SDLK_d;
mKeys["F"]= SDLK_f;
mKeys["G"]= SDLK_g;
mKeys["H"]= SDLK_h;
mKeys["J"]= SDLK_j;
mKeys["K"]= SDLK_k;
mKeys["L"]= SDLK_l;
mKeys["SEMICOLON"]= SDLK_SEMICOLON;
mKeys["APOSTROPHE"]= SDLK_QUOTE;
mKeys["GRAVE"]= SDLK_BACKQUOTE;
mKeys["LSHIFT"]= SDLK_LSHIFT;
mKeys["BACKSLASH"]= SDLK_BACKSLASH;
mKeys["Z"]= SDLK_z;
mKeys["X"]= SDLK_x;
mKeys["C"]= SDLK_c;
mKeys["V"]= SDLK_v;
mKeys["B"]= SDLK_b;
mKeys["N"]= SDLK_n;
mKeys["M"]= SDLK_m;
mKeys["COMMA"]= SDLK_COMMA;
mKeys["PERIOD"]= SDLK_PERIOD;
mKeys["SLASH"]= SDLK_SLASH;
mKeys["RSHIFT"]= SDLK_RSHIFT;
mKeys["MULTIPLY"]= SDLK_ASTERISK;
mKeys["LMENU"]= SDLK_LALT;
mKeys["SPACE"]= SDLK_SPACE;
mKeys["CAPITAL"]= SDLK_CAPSLOCK;
mKeys["F1"]= SDLK_F1;
mKeys["F2"]= SDLK_F2;
mKeys["F3"]= SDLK_F3;
mKeys["F4"]= SDLK_F4;
mKeys["F5"]= SDLK_F5;
mKeys["F6"]= SDLK_F6;
mKeys["F7"]= SDLK_F7;
mKeys["F8"]= SDLK_F8;
mKeys["F9"]= SDLK_F9;
mKeys["F10"]= SDLK_F10;
mKeys["F11"]= SDLK_F11;
mKeys["F12"]= SDLK_F12;
mKeys["NUMLOCK"]= SDLK_NUMLOCKCLEAR;
mKeys["SCROLL"]= SDLK_SCROLLLOCK;
mKeys["NUMPAD7"]= SDLK_KP_7;
mKeys["NUMPAD8"]= SDLK_KP_8;
mKeys["NUMPAD9"]= SDLK_KP_9;
mKeys["SUBTRACT"]= SDLK_KP_MINUS;
mKeys["NUMPAD4"]= SDLK_KP_4;
mKeys["NUMPAD5"]= SDLK_KP_5;
mKeys["NUMPAD6"]= SDLK_KP_6;
mKeys["ADD"]= SDLK_KP_PLUS;
mKeys["NUMPAD1"]= SDLK_KP_1;
mKeys["NUMPAD2"]= SDLK_KP_2;
mKeys["NUMPAD3"]= SDLK_KP_3;
mKeys["NUMPAD0"]= SDLK_KP_0;
mKeys["DECIMAL"]= SDLK_KP_DECIMAL;
mKeys["RCONTROL"]= SDLK_RCTRL;
mKeys["DIVIDE"]= SDLK_SLASH;
mKeys["SYSRQ"]= SDLK_SYSREQ;
mKeys["PRNTSCRN"] = SDLK_PRINTSCREEN;
mKeys["RMENU"]= SDLK_RALT;
mKeys["PAUSE"]= SDLK_PAUSE;
mKeys["HOME"]= SDLK_HOME;
mKeys["UP"]= SDLK_UP;
mKeys["PGUP"]= SDLK_PAGEUP;
mKeys["LEFT"]= SDLK_LEFT;
mKeys["RIGHT"]= SDLK_RIGHT;
mKeys["END"]= SDLK_END;
mKeys["DOWN"]= SDLK_DOWN;
mKeys["PGDOWN"]= SDLK_PAGEDOWN;
mKeys["INSERT"]= SDLK_INSERT;
mKeys["DELETE"]= SDLK_DELETE;
mKeys["NUMPADENTER"]= SDLK_KP_ENTER;
for(std::map<std::string, SDL_Keycode>::iterator it = mKeys.begin()
; it != mKeys.end() ; it++)
{
mKeyCodes[ it->second ] = it->first;
}
}
std::string InputControlSystem::keyCodeToString(SDL_Keycode key)
{
return mKeyCodes[key];
}
SDL_Keycode InputControlSystem::stringToKeyCode(std::string key)
{
return mKeys[key];
}
void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height)
{
mClientWidth = width;
mClientHeight = height;
}
}

264
extern/oics/ICSInputControlSystem.h vendored Normal file
View file

@ -0,0 +1,264 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#ifndef _InputControlSystem_H_
#define _InputControlSystem_H_
#include "ICSPrerequisites.h"
#include "ICSControl.h"
#include "ICSChannel.h"
#include "../sdl4ogre/events.h"
#define ICS_LOG(text) if(mLog) mLog->logMessage( ("ICS: " + std::string(text)).c_str() );
#define ICS_MAX_JOYSTICK_AXIS 16
#define ICS_MOUSE_BINDING_MARGIN 30
#define ICS_JOYSTICK_AXIS_BINDING_MARGIN 10000
#define ICS_JOYSTICK_SLIDER_BINDING_MARGIN 10000
#define ICS_MOUSE_AXIS_BINDING_NULL_VALUE std::numeric_limits<int>::max()
namespace ICS
{
class DllExport InputControlSystemLog
{
public:
virtual void logMessage(const char* text) = 0;
};
class DllExport InputControlSystem :
public SFO::MouseListener,
public SFO::KeyListener,
public SFO::JoyListener
{
public:
enum NamedAxis { X = -1, Y = -2, Z = -3, UNASSIGNED = -4 };
enum POVAxis { NorthSouth = 0, EastWest = 1 };
typedef NamedAxis MouseAxis; // MouseAxis is deprecated. It will be removed in future versions
typedef std::list<int> JoystickIDList;
typedef struct
{
int index;
POVAxis axis;
} POVBindingPair;
InputControlSystem(std::string file = "", bool active = true
, DetectingBindingListener* detectingBindingListener = NULL
, InputControlSystemLog* log = NULL, size_t channelCount = 16);
~InputControlSystem();
std::string getFileName(){ return mFileName; };
std::string getBaseFileName();
void setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; };
DetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; };
// in seconds
void update(float timeSinceLastFrame);
inline Channel* getChannel(int i){ return mChannels[i]; };
float getChannelValue(int i);
inline int getChannelCount(){ return (int)mChannels.size(); };
inline Control* getControl(int i){ return mControls[i]; };
float getControlValue(int i);
inline int getControlCount(){ return (int)mControls.size(); };
inline void addControl(Control* control){ mControls.push_back(control); };
Control* findControl(std::string name);
inline void activate(){ this->mActive = true; };
inline void deactivate(){ this->mActive = false; };
void addJoystick(int deviceId);
JoystickIDList& getJoystickIdList(){ return mJoystickIDList; };
// MouseListener
bool mouseMoved(const SFO::MouseMotionEvent &evt);
bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8);
bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8);
// KeyListener
bool keyPressed(const SDL_KeyboardEvent &evt);
bool keyReleased(const SDL_KeyboardEvent &evt);
// JoyStickListener
bool buttonPressed(const SDL_JoyButtonEvent &evt, int button);
bool buttonReleased(const SDL_JoyButtonEvent &evt, int button);
bool axisMoved(const SDL_JoyAxisEvent &evt, int axis);
bool povMoved(const SDL_JoyHatEvent &evt, int index);
//TODO: does this have an SDL equivalent?
//bool sliderMoved(const OIS::JoyStickEvent &evt, int index);
void addKeyBinding(Control* control, SDL_Keycode key, Control::ControlChangingDirection direction);
void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction);
void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction);
void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction);
void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction);
void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction);
void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction);
void removeKeyBinding(SDL_Keycode key);
void removeMouseAxisBinding(NamedAxis axis);
void removeMouseButtonBinding(unsigned int button);
void removeJoystickAxisBinding(int deviceId, int axis);
void removeJoystickButtonBinding(int deviceId, unsigned int button);
void removeJoystickPOVBinding(int deviceId, int index, POVAxis axis);
void removeJoystickSliderBinding(int deviceId, int index);
SDL_Keycode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction);
NamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction);
unsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction);
int getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction);
unsigned int getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction);
POVBindingPair getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction);
int getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction);
std::string keyCodeToString(SDL_Keycode key);
SDL_Keycode stringToKeyCode(std::string key);
void enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction);
void cancelDetectingBindingState();
bool save(std::string fileName = "");
void adjustMouseRegion (Uint16 width, Uint16 height);
protected:
void loadKeyBinders(TiXmlElement* xmlControlNode);
void loadMouseAxisBinders(TiXmlElement* xmlControlNode);
void loadMouseButtonBinders(TiXmlElement* xmlControlNode);
void loadJoystickAxisBinders(TiXmlElement* xmlControlNode);
void loadJoystickButtonBinders(TiXmlElement* xmlControlNode);
void loadJoystickPOVBinders(TiXmlElement* xmlControlNode);
void loadJoystickSliderBinders(TiXmlElement* xmlControlNode);
void addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction);
void removeMouseAxisBinding_(int axis);
protected:
typedef struct {
Control::ControlChangingDirection direction;
Control* control;
} ControlKeyBinderItem;
typedef ControlKeyBinderItem ControlAxisBinderItem;
typedef ControlKeyBinderItem ControlButtonBinderItem;
typedef ControlKeyBinderItem ControlPOVBinderItem;
typedef ControlKeyBinderItem ControlSliderBinderItem;
typedef struct {
Control* control;
Control::ControlChangingDirection direction;
} PendingActionItem;
std::list<PendingActionItem> mPendingActions;
std::string mFileName;
typedef std::map<SDL_Keycode, ControlKeyBinderItem> ControlsKeyBinderMapType; // <KeyCode, [direction, control]>
typedef std::map<int, ControlAxisBinderItem> ControlsAxisBinderMapType; // <axis, [direction, control]>
typedef std::map<int, ControlButtonBinderItem> ControlsButtonBinderMapType; // <button, [direction, control]>
typedef std::map<int, ControlPOVBinderItem> ControlsPOVBinderMapType; // <index, [direction, control]>
typedef std::map<int, ControlSliderBinderItem> ControlsSliderBinderMapType; // <index, [direction, control]>
typedef std::map<int, ControlsAxisBinderMapType> JoystickAxisBinderMapType; // <joystick_id, <axis, [direction, control]> >
typedef std::map<int, ControlsButtonBinderMapType> JoystickButtonBinderMapType; // <joystick_id, <button, [direction, control]> >
typedef std::map<int, std::map<int, ControlsPOVBinderMapType> > JoystickPOVBinderMapType; // <joystick_id, <index, <axis, [direction, control]> > >
typedef std::map<int, ControlsSliderBinderMapType> JoystickSliderBinderMapType; // <joystick_id, <index, [direction, control]> >
ControlsAxisBinderMapType mControlsMouseAxisBinderMap; // <axis, [direction, control]>
ControlsButtonBinderMapType mControlsMouseButtonBinderMap; // <int, [direction, control]>
JoystickAxisBinderMapType mControlsJoystickAxisBinderMap; // <joystick_id, <axis, [direction, control]> >
JoystickButtonBinderMapType mControlsJoystickButtonBinderMap; // <joystick_id, <button, [direction, control]> >
JoystickPOVBinderMapType mControlsJoystickPOVBinderMap; // <joystick_id, <index, <axis, [direction, control]> > >
JoystickSliderBinderMapType mControlsJoystickSliderBinderMap; // <joystick_id, <index, [direction, control]> >
std::vector<Control *> mControls;
std::vector<Channel *> mChannels;
ControlsKeyBinderMapType mControlsKeyBinderMap;
std::map<std::string, SDL_Keycode> mKeys;
std::map<SDL_Keycode, std::string> mKeyCodes;
bool mActive;
InputControlSystemLog* mLog;
DetectingBindingListener* mDetectingBindingListener;
Control* mDetectingBindingControl;
Control::ControlChangingDirection mDetectingBindingDirection;
bool mXmouseAxisBinded;
bool mYmouseAxisBinded;
JoystickIDList mJoystickIDList;
int mMouseAxisBindingInitialValues[3];
private:
void fillSDLKeysMap();
Uint16 mClientWidth;
Uint16 mClientHeight;
};
class DllExport DetectingBindingListener
{
public:
virtual void keyBindingDetected(InputControlSystem* ICS, Control* control
, SDL_Keycode key, Control::ControlChangingDirection direction);
virtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control
, InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction);
virtual void mouseButtonBindingDetected(InputControlSystem* ICS, Control* control
, unsigned int button, Control::ControlChangingDirection direction);
virtual void joystickAxisBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int axis, Control::ControlChangingDirection direction);
virtual void joystickButtonBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, unsigned int button, Control::ControlChangingDirection direction);
virtual void joystickPOVBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction);
virtual void joystickSliderBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int slider, Control::ControlChangingDirection direction);
};
static const float ICS_MAX = std::numeric_limits<float>::max();
}
#endif

View file

@ -0,0 +1,666 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
#define SDL_JOY_AXIS_MIN -32768
#define SDL_JOY_AXIS_MAX 32767
namespace ICS
{
// load xml
void InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement("JoystickAxisBinder");
while(xmlJoystickBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlJoystickBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlJoystickBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
addJoystickAxisBinding(mControls.back(), FromString<int>(xmlJoystickBinder->Attribute("deviceId"))
, FromString<int>(xmlJoystickBinder->Attribute("axis")), dir);
xmlJoystickBinder = xmlJoystickBinder->NextSiblingElement("JoystickAxisBinder");
}
}
void InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement("JoystickButtonBinder");
while(xmlJoystickButtonBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
addJoystickButtonBinding(mControls.back(), FromString<int>(xmlJoystickButtonBinder->Attribute("deviceId"))
, FromString<int>(xmlJoystickButtonBinder->Attribute("button")), dir);
xmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement("JoystickButtonBinder");
}
}
void InputControlSystem::loadJoystickPOVBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlJoystickPOVBinder = xmlControlNode->FirstChildElement("JoystickPOVBinder");
while(xmlJoystickPOVBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
InputControlSystem::POVAxis axis = /*POVAxis::*/NorthSouth;
if(std::string(xmlJoystickPOVBinder->Attribute("axis")) == "EastWest")
{
axis = /*POVAxis::*/EastWest;
}
addJoystickPOVBinding(mControls.back(), FromString<int>(xmlJoystickPOVBinder->Attribute("deviceId"))
, FromString<int>(xmlJoystickPOVBinder->Attribute("pov")), axis, dir);
xmlJoystickPOVBinder = xmlJoystickPOVBinder->NextSiblingElement("JoystickPOVBinder");
}
}
void InputControlSystem::loadJoystickSliderBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlJoystickSliderBinder = xmlControlNode->FirstChildElement("JoystickSliderBinder");
while(xmlJoystickSliderBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
addJoystickSliderBinding(mControls.back(), FromString<int>(xmlJoystickSliderBinder->Attribute("deviceId"))
, FromString<int>(xmlJoystickSliderBinder->Attribute("slider")), dir);
xmlJoystickSliderBinder = xmlJoystickSliderBinder->NextSiblingElement("JoystickSliderBinder");
}
}
// add bindings
void InputControlSystem::addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding AxisBinder [deviceid="
+ ToString<int>(deviceId) + ", axis="
+ ToString<int>(axis) + ", direction="
+ ToString<int>(direction) + "]");
ControlAxisBinderItem controlAxisBinderItem;
controlAxisBinderItem.control = control;
controlAxisBinderItem.direction = direction;
mControlsJoystickAxisBinderMap[ deviceId ][ axis ] = controlAxisBinderItem;
}
void InputControlSystem::addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding JoystickButtonBinder [deviceId="
+ ToString<int>(deviceId) + ", button="
+ ToString<int>(button) + ", direction="
+ ToString<int>(direction) + "]");
ControlButtonBinderItem controlJoystickButtonBinderItem;
controlJoystickButtonBinderItem.direction = direction;
controlJoystickButtonBinderItem.control = control;
mControlsJoystickButtonBinderMap[ deviceId ][ button ] = controlJoystickButtonBinderItem;
}
void InputControlSystem::addJoystickPOVBinding(Control* control, int deviceId, int index, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding JoystickPOVBinder [deviceId="
+ ToString<int>(deviceId) + ", pov="
+ ToString<int>(index) + ", axis="
+ ToString<int>(axis) + ", direction="
+ ToString<int>(direction) + "]");
ControlPOVBinderItem ControlPOVBinderItem;
ControlPOVBinderItem.direction = direction;
ControlPOVBinderItem.control = control;
mControlsJoystickPOVBinderMap[ deviceId ][ index ][ axis ] = ControlPOVBinderItem;
}
void InputControlSystem::addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding JoystickSliderBinder [deviceId="
+ ToString<int>(deviceId) + ", direction="
+ ToString<int>(index) + ", direction="
+ ToString<int>(direction) + "]");
ControlSliderBinderItem ControlSliderBinderItem;
ControlSliderBinderItem.direction = direction;
ControlSliderBinderItem.control = control;
mControlsJoystickSliderBinderMap[ deviceId ][ index ] = ControlSliderBinderItem;
}
// get bindings
int InputControlSystem::getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction)
{
if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end())
{
ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].begin();
while(it != mControlsJoystickAxisBinderMap[deviceId].end())
{
if(it->first >= 0 && it->second.control == control && it->second.direction == direction)
{
return it->first;
}
it++;
}
}
return /*NamedAxis::*/UNASSIGNED;
}
unsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction)
{
if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end())
{
ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].begin();
while(it != mControlsJoystickButtonBinderMap[deviceId].end())
{
if(it->second.control == control && it->second.direction == direction)
{
return it->first;
}
it++;
}
}
return ICS_MAX_DEVICE_BUTTONS;
}
InputControlSystem::POVBindingPair InputControlSystem::getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction)
{
POVBindingPair result;
result.index = -1;
if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end())
{
//ControlsAxisBinderMapType::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin();
std::map<int, ControlsPOVBinderMapType>::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin();
while(it != mControlsJoystickPOVBinderMap[deviceId].end())
{
ControlsPOVBinderMapType::const_iterator it2 = it->second.begin();
while(it2 != it->second.end())
{
if(it2->second.control == control && it2->second.direction == direction)
{
result.index = it->first;
result.axis = (POVAxis)it2->first;
return result;
}
it2++;
}
it++;
}
}
return result;
}
int InputControlSystem::getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction)
{
if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end())
{
ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].begin();
while(it != mControlsJoystickSliderBinderMap[deviceId].end())
{
if(it->second.control == control && it->second.direction == direction)
{
return it->first;
}
it++;
}
}
return /*NamedAxis::*/UNASSIGNED;
}
// remove bindings
void InputControlSystem::removeJoystickAxisBinding(int deviceId, int axis)
{
if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end())
{
ControlsButtonBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].find(axis);
if(it != mControlsJoystickAxisBinderMap[deviceId].end())
{
mControlsJoystickAxisBinderMap[deviceId].erase(it);
}
}
}
void InputControlSystem::removeJoystickButtonBinding(int deviceId, unsigned int button)
{
if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end())
{
ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].find(button);
if(it != mControlsJoystickButtonBinderMap[deviceId].end())
{
mControlsJoystickButtonBinderMap[deviceId].erase(it);
}
}
}
void InputControlSystem::removeJoystickPOVBinding(int deviceId, int index, POVAxis axis)
{
if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end())
{
std::map<int, ControlsPOVBinderMapType>::iterator it = mControlsJoystickPOVBinderMap[deviceId].find(index);
if(it != mControlsJoystickPOVBinderMap[deviceId].end())
{
if(it->second.find(axis) != it->second.end())
{
mControlsJoystickPOVBinderMap[deviceId].find(index)->second.erase( it->second.find(axis) );
}
}
}
}
void InputControlSystem::removeJoystickSliderBinding(int deviceId, int index)
{
if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end())
{
ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].find(index);
if(it != mControlsJoystickSliderBinderMap[deviceId].end())
{
mControlsJoystickSliderBinderMap[deviceId].erase(it);
}
}
}
// joyStick listeners
bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
if(mControlsJoystickButtonBinderMap.find(evt.which) != mControlsJoystickButtonBinderMap.end())
{
ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.which].find(button);
if(it != mControlsJoystickButtonBinderMap[evt.which].end())
{
it->second.control->setIgnoreAutoReverse(false);
if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
{
it->second.control->setChangingDirection(it->second.direction);
}
else
{
if(it->second.control->getValue() == 1)
{
it->second.control->setChangingDirection(Control::DECREASE);
}
else if(it->second.control->getValue() == 0)
{
it->second.control->setChangingDirection(Control::INCREASE);
}
}
}
}
}
else if(mDetectingBindingListener)
{
mDetectingBindingListener->joystickButtonBindingDetected(this,
mDetectingBindingControl, evt.which, button, mDetectingBindingDirection);
}
}
return true;
}
bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button)
{
if(mActive)
{
if(mControlsJoystickButtonBinderMap.find(evt.which) != mControlsJoystickButtonBinderMap.end())
{
ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.which].find(button);
if(it != mControlsJoystickButtonBinderMap[evt.which].end())
{
it->second.control->setChangingDirection(Control::STOP);
}
}
}
return true;
}
bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
if(mControlsJoystickAxisBinderMap.find(evt.which) != mControlsJoystickAxisBinderMap.end())
{
ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index
Control* ctrl = joystickBinderItem.control;
if(ctrl)
{
ctrl->setIgnoreAutoReverse(true);
float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MAX;
float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN);
if(joystickBinderItem.direction == Control::INCREASE)
{
ctrl->setValue( valDisplaced / axisRange );
}
else if(joystickBinderItem.direction == Control::DECREASE)
{
ctrl->setValue( 1 - ( valDisplaced / axisRange ) );
}
}
}
}
else if(mDetectingBindingListener)
{
//ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index
//Control* ctrl = joystickBinderItem.control;
//if(ctrl && ctrl->isAxisBindable())
if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable())
{
if( abs( evt.value ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN)
{
mDetectingBindingListener->joystickAxisBindingDetected(this,
mDetectingBindingControl, evt.which, axis, mDetectingBindingDirection);
}
}
}
}
return true;
}
//Here be dragons, apparently
bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
if(mControlsJoystickPOVBinderMap.find(evt.which) != mControlsJoystickPOVBinderMap.end())
{
std::map<int, ControlsPOVBinderMapType>::const_iterator i = mControlsJoystickPOVBinderMap[ evt.which ].find(index);
if(i != mControlsJoystickPOVBinderMap[ evt.which ].end())
{
if(evt.value != SDL_HAT_LEFT
&& evt.value != SDL_HAT_RIGHT
&& evt.value != SDL_HAT_CENTERED)
{
ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth );
if(it != i->second.end())
{
it->second.control->setIgnoreAutoReverse(false);
if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
{
if(evt.value == SDL_HAT_UP
|| evt.value == SDL_HAT_LEFTUP
|| evt.value == SDL_HAT_RIGHTUP)
{
it->second.control->setChangingDirection(it->second.direction);
}
else
{
it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction));
}
}
else
{
if(it->second.control->getValue() == 1)
{
it->second.control->setChangingDirection(Control::DECREASE);
}
else if(it->second.control->getValue() == 0)
{
it->second.control->setChangingDirection(Control::INCREASE);
}
}
}
}
if(evt.value != SDL_HAT_UP
&& evt.value != SDL_HAT_DOWN
&& evt.value != SDL_HAT_CENTERED)
{
ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/EastWest );
if(it != i->second.end())
{
it->second.control->setIgnoreAutoReverse(false);
if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
{
if(evt.value == SDL_HAT_RIGHT
|| evt.value == SDL_HAT_RIGHTUP
|| evt.value == SDL_HAT_RIGHTDOWN)
{
it->second.control->setChangingDirection(it->second.direction);
}
else
{
it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction));
}
}
else
{
if(it->second.control->getValue() == 1)
{
it->second.control->setChangingDirection(Control::DECREASE);
}
else if(it->second.control->getValue() == 0)
{
it->second.control->setChangingDirection(Control::INCREASE);
}
}
}
}
if(evt.value == SDL_HAT_CENTERED)
{
ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth );
if(it != i->second.end())
{
it->second.control->setChangingDirection(Control::STOP);
}
it = i->second.find( /*POVAxis::*/EastWest );
if(it != i->second.end())
{
it->second.control->setChangingDirection(Control::STOP);
}
}
}
}
}
else if(mDetectingBindingListener)
{
if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable())
{
if(evt.value == SDL_HAT_LEFT
|| evt.value == SDL_HAT_RIGHT
|| evt.value == SDL_HAT_UP
|| evt.value == SDL_HAT_DOWN)
{
POVAxis povAxis = NorthSouth;
if(evt.value == SDL_HAT_LEFT
|| evt.value == SDL_HAT_RIGHT)
{
povAxis = EastWest;
}
mDetectingBindingListener->joystickPOVBindingDetected(this,
mDetectingBindingControl, evt.which, index, povAxis, mDetectingBindingDirection);
}
}
}
}
return true;
}
//TODO: does this have an SDL equivalent?
/*
bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
if(mControlsJoystickSliderBinderMap.find(evt.device->getID()) != mControlsJoystickSliderBinderMap.end())
{
ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ];
Control* ctrl = joystickBinderItem.control;
if(ctrl)
{
ctrl->setIgnoreAutoReverse(true);
if(joystickBinderItem.direction == Control::INCREASE)
{
float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS;
float valDisplaced = (float)( evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS);
ctrl->setValue( valDisplaced / axisRange );
}
else if(joystickBinderItem.direction == Control::DECREASE)
{
float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS;
float valDisplaced = (float)(evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS);
ctrl->setValue( 1 - ( valDisplaced / axisRange ) );
}
}
}
}
else if(mDetectingBindingListener)
{
if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable())
{
if( abs( evt.state.mSliders[index].abX ) > ICS_JOYSTICK_SLIDER_BINDING_MARGIN)
{
mDetectingBindingListener->joystickSliderBindingDetected(this,
mDetectingBindingControl, evt.device->getID(), index, mDetectingBindingDirection);
}
}
}
}
return true;
}
*/
// joystick auto bindings
void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int axis, Control::ControlChangingDirection direction)
{
// if the joystick axis is used by another control, remove it
ICS->removeJoystickAxisBinding(deviceId, axis);
// if the control has an axis assigned, remove it
int oldAxis = ICS->getJoystickAxisBinding(control, deviceId, direction);
if(oldAxis != InputControlSystem::UNASSIGNED)
{
ICS->removeJoystickAxisBinding(deviceId, oldAxis);
}
ICS->addJoystickAxisBinding(control, deviceId, axis, direction);
ICS->cancelDetectingBindingState();
}
void DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, unsigned int button, Control::ControlChangingDirection direction)
{
// if the joystick button is used by another control, remove it
ICS->removeJoystickButtonBinding(deviceId, button);
// if the control has a joystick button assigned, remove it
unsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceId, direction);
if(oldButton != ICS_MAX_DEVICE_BUTTONS)
{
ICS->removeJoystickButtonBinding(deviceId, oldButton);
}
ICS->addJoystickButtonBinding(control, deviceId, button, direction);
ICS->cancelDetectingBindingState();
}
void DetectingBindingListener::joystickPOVBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction)
{
// if the joystick slider is used by another control, remove it
ICS->removeJoystickPOVBinding(deviceId, pov, axis);
// if the control has a joystick button assigned, remove it
ICS::InputControlSystem::POVBindingPair oldPOV = ICS->getJoystickPOVBinding(control, deviceId, direction);
if(oldPOV.index >= 0 && oldPOV.axis == axis)
{
ICS->removeJoystickPOVBinding(deviceId, oldPOV.index, oldPOV.axis);
}
ICS->addJoystickPOVBinding(control, deviceId, pov, axis, direction);
ICS->cancelDetectingBindingState();
}
void DetectingBindingListener::joystickSliderBindingDetected(InputControlSystem* ICS, Control* control
, int deviceId, int slider, Control::ControlChangingDirection direction)
{
// if the joystick slider is used by another control, remove it
ICS->removeJoystickSliderBinding(deviceId, slider);
// if the control has a joystick slider assigned, remove it
int oldSlider = ICS->getJoystickSliderBinding(control, deviceId, direction);
if(oldSlider != InputControlSystem::/*NamedAxis::*/UNASSIGNED)
{
ICS->removeJoystickSliderBinding(deviceId, oldSlider);
}
ICS->addJoystickSliderBinding(control, deviceId, slider, direction);
ICS->cancelDetectingBindingState();
}
}

View file

@ -0,0 +1,156 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
namespace ICS
{
void InputControlSystem::loadKeyBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlKeyBinder = xmlControlNode->FirstChildElement("KeyBinder");
while(xmlKeyBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlKeyBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlKeyBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir);
xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder");
}
}
void InputControlSystem::addKeyBinding(Control* control, SDL_Keycode key, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding KeyBinder [key="
+ keyCodeToString(key) + ", direction="
+ ToString<int>(direction) + "]");
ControlKeyBinderItem controlKeyBinderItem;
controlKeyBinderItem.control = control;
controlKeyBinderItem.direction = direction;
mControlsKeyBinderMap[ key ] = controlKeyBinderItem;
}
void InputControlSystem::removeKeyBinding(SDL_Keycode key)
{
ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key);
if(it != mControlsKeyBinderMap.end())
{
mControlsKeyBinderMap.erase(it);
}
}
SDL_Keycode InputControlSystem::getKeyBinding(Control* control
, ICS::Control::ControlChangingDirection direction)
{
ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin();
while(it != mControlsKeyBinderMap.end())
{
if(it->second.control == control && it->second.direction == direction)
{
return it->first;
}
it++;
}
return SDLK_UNKNOWN;
}
bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.sym);
if(it != mControlsKeyBinderMap.end())
{
it->second.control->setIgnoreAutoReverse(false);
if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
{
it->second.control->setChangingDirection(it->second.direction);
}
else
{
if(it->second.control->getValue() == 1)
{
it->second.control->setChangingDirection(Control::DECREASE);
}
else if(it->second.control->getValue() == 0)
{
it->second.control->setChangingDirection(Control::INCREASE);
}
}
}
}
else if(mDetectingBindingListener)
{
mDetectingBindingListener->keyBindingDetected(this,
mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection);
}
}
return true;
}
bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt)
{
if(mActive)
{
ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.keysym.sym);
if(it != mControlsKeyBinderMap.end())
{
it->second.control->setChangingDirection(Control::STOP);
}
}
return true;
}
void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control
, SDL_Keycode key, Control::ControlChangingDirection direction)
{
// if the key is used by another control, remove it
ICS->removeKeyBinding(key);
// if the control has a key assigned, remove it
SDL_Keycode oldKey = ICS->getKeyBinding(control, direction);
if(oldKey != SDLK_UNKNOWN)
{
ICS->removeKeyBinding(oldKey);
}
ICS->addKeyBinding(control, key, direction);
ICS->cancelDetectingBindingState();
}
}

View file

@ -0,0 +1,397 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSInputControlSystem.h"
namespace ICS
{
// load xml
void InputControlSystem::loadMouseAxisBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlMouseBinder = xmlControlNode->FirstChildElement("MouseBinder");
while(xmlMouseBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlMouseBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlMouseBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
NamedAxis axis = /*NamedAxis::*/ X;
if((*xmlMouseBinder->Attribute("axis")) == 'Y')
{
axis = /*NamedAxis::*/ Y;
}
else if((*xmlMouseBinder->Attribute("axis")) == 'Z')
{
axis = /*NamedAxis::*/ Z;
}
addMouseAxisBinding(mControls.back(), axis, dir);
xmlMouseBinder = xmlMouseBinder->NextSiblingElement("MouseBinder");
}
}
void InputControlSystem::loadMouseButtonBinders(TiXmlElement* xmlControlNode)
{
TiXmlElement* xmlMouseButtonBinder = xmlControlNode->FirstChildElement("MouseButtonBinder");
while(xmlMouseButtonBinder)
{
Control::ControlChangingDirection dir = Control::STOP;
if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "INCREASE")
{
dir = Control::INCREASE;
}
else if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "DECREASE")
{
dir = Control::DECREASE;
}
int button = 0;
if(std::string(xmlMouseButtonBinder->Attribute("button")) == "LEFT")
{
button = SDL_BUTTON_LEFT;
}
else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "RIGHT")
{
button = SDL_BUTTON_RIGHT;
}
else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "MIDDLE")
{
button = SDL_BUTTON_MIDDLE;
}
else
{
button = FromString<int>(xmlMouseButtonBinder->Attribute("button"));
}
addMouseButtonBinding(mControls.back(), button, dir);
xmlMouseButtonBinder = xmlMouseButtonBinder->NextSiblingElement("MouseButtonBinder");
}
}
// add bindings
void InputControlSystem::addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction)
{
if(axis == /*NamedAxis::*/X)
{
mXmouseAxisBinded = true;
}
else if(axis == /*NamedAxis::*/Y)
{
mYmouseAxisBinded = true;
}
addMouseAxisBinding_(control, axis, direction);
}
/*protected*/ void InputControlSystem::addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding AxisBinder [axis="
+ ToString<int>(axis) + ", direction="
+ ToString<int>(direction) + "]");
ControlAxisBinderItem controlAxisBinderItem;
controlAxisBinderItem.control = control;
controlAxisBinderItem.direction = direction;
mControlsMouseAxisBinderMap[ axis ] = controlAxisBinderItem;
}
void InputControlSystem::addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction)
{
ICS_LOG("\tAdding MouseButtonBinder [button="
+ ToString<int>(button) + ", direction="
+ ToString<int>(direction) + "]");
ControlButtonBinderItem controlMouseButtonBinderItem;
controlMouseButtonBinderItem.direction = direction;
controlMouseButtonBinderItem.control = control;
mControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem;
}
// get bindings
InputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction)
{
ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin();
while(it != mControlsMouseAxisBinderMap.end())
{
if(it->first < 0 && it->second.control == control && it->second.direction == direction)
{
return (InputControlSystem::NamedAxis)(it->first);
}
it++;
}
return /*NamedAxis::*/UNASSIGNED;
}
//int InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction)
//{
// ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin();
// while(it != mControlsMouseAxisBinderMap.end())
// {
// if(it->first >= 0 && it->second.control == control && it->second.direction == direction)
// {
// return it->first;
// }
// it++;
// }
// return /*NamedAxis::*/UNASSIGNED;
//}
unsigned int InputControlSystem::getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction)
{
ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.begin();
while(it != mControlsMouseButtonBinderMap.end())
{
if(it->second.control == control && it->second.direction == direction)
{
return it->first;
}
it++;
}
return ICS_MAX_DEVICE_BUTTONS;
}
// remove bindings
void InputControlSystem::removeMouseAxisBinding(NamedAxis axis)
{
if(axis == /*NamedAxis::*/X)
{
mXmouseAxisBinded = false;
}
else if(axis == /*NamedAxis::*/Y)
{
mYmouseAxisBinded = false;
}
removeMouseAxisBinding_(axis);
}
/*protected*/ void InputControlSystem::removeMouseAxisBinding_(int axis)
{
ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.find(axis);
if(it != mControlsMouseAxisBinderMap.end())
{
mControlsMouseAxisBinderMap.erase(it);
}
}
void InputControlSystem::removeMouseButtonBinding(unsigned int button)
{
ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.find(button);
if(it != mControlsMouseButtonBinderMap.end())
{
mControlsMouseButtonBinderMap.erase(it);
}
}
// mouse Listeners
bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
if(mXmouseAxisBinded && evt.xrel)
{
ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ];
Control* ctrl = mouseBinderItem.control;
ctrl->setIgnoreAutoReverse(true);
if(mouseBinderItem.direction == Control::INCREASE)
{
ctrl->setValue( float( (evt.x) / float(mClientWidth) ) );
}
else if(mouseBinderItem.direction == Control::DECREASE)
{
ctrl->setValue( 1 - float( evt.x / float(mClientWidth) ) );
}
}
if(mYmouseAxisBinded && evt.yrel)
{
ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ];
Control* ctrl = mouseBinderItem.control;
ctrl->setIgnoreAutoReverse(true);
if(mouseBinderItem.direction == Control::INCREASE)
{
ctrl->setValue( float( (evt.y) / float(mClientHeight) ) );
}
else if(mouseBinderItem.direction == Control::DECREASE)
{
ctrl->setValue( 1 - float( evt.y / float(mClientHeight) ) );
}
}
//! @todo Whats the range of the Z axis?
/*if(evt.state.Z.rel)
{
ControlAxisBinderItem mouseBinderItem = mControlsAxisBinderMap[ NamedAxis::Z ];
Control* ctrl = mouseBinderItem.control;
ctrl->setIgnoreAutoReverse(true);
if(mouseBinderItem.direction == Control::INCREASE)
{
ctrl->setValue( float( (evt.state.Z.abs) / float(evt.state.¿width?) ) );
}
else if(mouseBinderItem.direction == Control::DECREASE)
{
ctrl->setValue( float( (1 - evt.state.Z.abs) / float(evt.state.¿width?) ) );
}
}*/
}
else if(mDetectingBindingListener)
{
if(mDetectingBindingControl->isAxisBindable())
{
if(mMouseAxisBindingInitialValues[0] == ICS_MOUSE_AXIS_BINDING_NULL_VALUE)
{
mMouseAxisBindingInitialValues[0] = 0;
mMouseAxisBindingInitialValues[1] = 0;
mMouseAxisBindingInitialValues[2] = 0;
}
mMouseAxisBindingInitialValues[0] += evt.xrel;
mMouseAxisBindingInitialValues[1] += evt.yrel;
mMouseAxisBindingInitialValues[2] += evt.zrel;
if( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN )
{
mDetectingBindingListener->mouseAxisBindingDetected(this,
mDetectingBindingControl, X, mDetectingBindingDirection);
}
else if( abs(mMouseAxisBindingInitialValues[1]) > ICS_MOUSE_BINDING_MARGIN )
{
mDetectingBindingListener->mouseAxisBindingDetected(this,
mDetectingBindingControl, Y, mDetectingBindingDirection);
}
else if( abs(mMouseAxisBindingInitialValues[2]) > ICS_MOUSE_BINDING_MARGIN )
{
mDetectingBindingListener->mouseAxisBindingDetected(this,
mDetectingBindingControl, Z, mDetectingBindingDirection);
}
}
}
}
return true;
}
bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn)
{
if(mActive)
{
if(!mDetectingBindingControl)
{
ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn);
if(it != mControlsMouseButtonBinderMap.end())
{
it->second.control->setIgnoreAutoReverse(false);
if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
{
it->second.control->setChangingDirection(it->second.direction);
}
else
{
if(it->second.control->getValue() == 1)
{
it->second.control->setChangingDirection(Control::DECREASE);
}
else if(it->second.control->getValue() == 0)
{
it->second.control->setChangingDirection(Control::INCREASE);
}
}
}
}
else if(mDetectingBindingListener)
{
mDetectingBindingListener->mouseButtonBindingDetected(this,
mDetectingBindingControl, btn, mDetectingBindingDirection);
}
}
return true;
}
bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn)
{
if(mActive)
{
ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn);
if(it != mControlsMouseButtonBinderMap.end())
{
it->second.control->setChangingDirection(Control::STOP);
}
}
return true;
}
// mouse auto bindings
void DetectingBindingListener::mouseAxisBindingDetected(InputControlSystem* ICS, Control* control
, InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction)
{
// if the mouse axis is used by another control, remove it
ICS->removeMouseAxisBinding(axis);
// if the control has an axis assigned, remove it
InputControlSystem::NamedAxis oldAxis = ICS->getMouseAxisBinding(control, direction);
if(oldAxis != InputControlSystem::UNASSIGNED)
{
ICS->removeMouseAxisBinding(oldAxis);
}
ICS->addMouseAxisBinding(control, axis, direction);
ICS->cancelDetectingBindingState();
}
void DetectingBindingListener::mouseButtonBindingDetected(InputControlSystem* ICS, Control* control
, unsigned int button, Control::ControlChangingDirection direction)
{
// if the mouse button is used by another control, remove it
ICS->removeMouseButtonBinding(button);
// if the control has a mouse button assigned, remove it
unsigned int oldButton = ICS->getMouseButtonBinding(control, direction);
if(oldButton != ICS_MAX_DEVICE_BUTTONS)
{
ICS->removeMouseButtonBinding(oldButton);
}
ICS->addMouseButtonBinding(control, button, direction);
ICS->cancelDetectingBindingState();
}
}

27
extern/oics/ICSPrerequisites.cpp vendored Normal file
View file

@ -0,0 +1,27 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
#include "ICSPrerequisites.h"

109
extern/oics/ICSPrerequisites.h vendored Normal file
View file

@ -0,0 +1,109 @@
/* -------------------------------------------------------
Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------- */
//! @todo add mouse wheel support
#ifndef _InputControlSystem_Prerequisites_H_
#define _InputControlSystem_Prerequisites_H_
/// Include external headers
#include <sstream>
#include <fstream>
#include <vector>
#include <map>
#include <list>
#include <limits>
#include "tinyxml.h"
#include "SDL_keyboard.h"
#include "SDL_mouse.h"
#include "SDL_joystick.h"
#include "SDL_events.h"
/// Define the dll export qualifier if compiling for Windows
/*
#ifdef ICS_PLATFORM_WIN32
#ifdef ICS_LIB
#define DllExport __declspec (dllexport)
#else
#define DllExport __declspec (dllimport)
#endif
#else
#define DllExport
#endif
*/
#define DllExport
// Define some macros
#define ICS_DEPRECATED __declspec(deprecated("Deprecated. It will be removed in future versions."))
/// Version defines
#define ICS_VERSION_MAJOR 0
#define ICS_VERSION_MINOR 4
#define ICS_VERSION_PATCH 0
#define ICS_MAX_DEVICE_BUTTONS 30
namespace ICS
{
template <typename T>
bool StringIsNumber ( const std::string &Text )
{
std::stringstream ss(Text);
T result;
return ss >> result ? true : false;
}
// from http://www.cplusplus.com/forum/articles/9645/
template <typename T>
std::string ToString ( T value )
{
std::stringstream ss;
ss << value;
return ss.str();
}
// from http://www.cplusplus.com/forum/articles/9645/
template <typename T>
T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a
{ //character array as argument
std::stringstream ss(Text);
T result;
return ss >> result ? result : 0;
}
class InputControlSystem;
class Channel;
class ChannelListener;
class Control;
class ControlListener;
class DetectingBindingListener;
class InputControlSystemLog;
}
#endif

116
extern/oics/tinystr.cpp vendored Normal file
View file

@ -0,0 +1,116 @@
/*
www.sourceforge.net/projects/tinyxml
Original file by Yves Berquin.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/*
* THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005.
*/
#ifndef TIXML_USE_STL
#include "tinystr.h"
// Error value for find primitive
const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
// Null rep.
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
void TiXmlString::reserve (size_type cap)
{
if (cap > capacity())
{
TiXmlString tmp;
tmp.init(length(), cap);
memcpy(tmp.start(), data(), length());
swap(tmp);
}
}
TiXmlString& TiXmlString::assign(const char* str, size_type len)
{
size_type cap = capacity();
if (len > cap || cap > 3*(len + 8))
{
TiXmlString tmp;
tmp.init(len);
memcpy(tmp.start(), str, len);
swap(tmp);
}
else
{
memmove(start(), str, len);
set_size(len);
}
return *this;
}
TiXmlString& TiXmlString::append(const char* str, size_type len)
{
size_type newsize = length() + len;
if (newsize > capacity())
{
reserve (newsize + capacity());
}
memmove(finish(), str, len);
set_size(newsize);
return *this;
}
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
{
TiXmlString tmp;
tmp.reserve(a.length() + b.length());
tmp += a;
tmp += b;
return tmp;
}
TiXmlString operator + (const TiXmlString & a, const char* b)
{
TiXmlString tmp;
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
tmp.reserve(a.length() + b_len);
tmp += a;
tmp.append(b, b_len);
return tmp;
}
TiXmlString operator + (const char* a, const TiXmlString & b)
{
TiXmlString tmp;
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
tmp.reserve(a_len + b.length());
tmp.append(a, a_len);
tmp += b;
return tmp;
}
#endif // TIXML_USE_STL

319
extern/oics/tinystr.h vendored Normal file
View file

@ -0,0 +1,319 @@
/*
www.sourceforge.net/projects/tinyxml
Original file by Yves Berquin.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/*
* THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
*
* - completely rewritten. compact, clean, and fast implementation.
* - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
* - fixed reserve() to work as per specification.
* - fixed buggy compares operator==(), operator<(), and operator>()
* - fixed operator+=() to take a const ref argument, following spec.
* - added "copy" constructor with length, and most compare operators.
* - added swap(), clear(), size(), capacity(), operator+().
*/
#ifndef TIXML_USE_STL
#ifndef TIXML_STRING_INCLUDED
#define TIXML_STRING_INCLUDED
#include <assert.h>
#include <string.h>
/* The support for explicit isn't that universal, and it isn't really
required - it is used to check that the TiXmlString class isn't incorrectly
used. Be nice to old compilers and macro it here:
*/
#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
// Microsoft visual studio, version 6 and higher.
#define TIXML_EXPLICIT explicit
#elif defined(__GNUC__) && (__GNUC__ >= 3 )
// GCC version 3 and higher.s
#define TIXML_EXPLICIT explicit
#else
#define TIXML_EXPLICIT
#endif
/*
TiXmlString is an emulation of a subset of the std::string template.
Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
Only the member functions relevant to the TinyXML project have been implemented.
The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
a string and there's no more room, we allocate a buffer twice as big as we need.
*/
class TiXmlString
{
public :
// The size type used
typedef size_t size_type;
// Error value for find primitive
static const size_type npos; // = -1;
// TiXmlString empty constructor
TiXmlString () : rep_(&nullrep_)
{
}
// TiXmlString copy constructor
TiXmlString ( const TiXmlString & copy) : rep_(0)
{
init(copy.length());
memcpy(start(), copy.data(), length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
{
init( static_cast<size_type>( strlen(copy) ));
memcpy(start(), copy, length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
{
init(len);
memcpy(start(), str, len);
}
// TiXmlString destructor
~TiXmlString ()
{
quit();
}
// = operator
TiXmlString& operator = (const char * copy)
{
return assign( copy, (size_type)strlen(copy));
}
// = operator
TiXmlString& operator = (const TiXmlString & copy)
{
return assign(copy.start(), copy.length());
}
// += operator. Maps to append
TiXmlString& operator += (const char * suffix)
{
return append(suffix, static_cast<size_type>( strlen(suffix) ));
}
// += operator. Maps to append
TiXmlString& operator += (char single)
{
return append(&single, 1);
}
// += operator. Maps to append
TiXmlString& operator += (const TiXmlString & suffix)
{
return append(suffix.data(), suffix.length());
}
// Convert a TiXmlString into a null-terminated char *
const char * c_str () const { return rep_->str; }
// Convert a TiXmlString into a char * (need not be null terminated).
const char * data () const { return rep_->str; }
// Return the length of a TiXmlString
size_type length () const { return rep_->size; }
// Alias for length()
size_type size () const { return rep_->size; }
// Checks if a TiXmlString is empty
bool empty () const { return rep_->size == 0; }
// Return capacity of string
size_type capacity () const { return rep_->capacity; }
// single char extraction
const char& at (size_type index) const
{
assert( index < length() );
return rep_->str[ index ];
}
// [] operator
char& operator [] (size_type index) const
{
assert( index < length() );
return rep_->str[ index ];
}
// find a char in a string. Return TiXmlString::npos if not found
size_type find (char lookup) const
{
return find(lookup, 0);
}
// find a char in a string from an offset. Return TiXmlString::npos if not found
size_type find (char tofind, size_type offset) const
{
if (offset >= length()) return npos;
for (const char* p = c_str() + offset; *p != '\0'; ++p)
{
if (*p == tofind) return static_cast< size_type >( p - c_str() );
}
return npos;
}
void clear ()
{
//Lee:
//The original was just too strange, though correct:
// TiXmlString().swap(*this);
//Instead use the quit & re-init:
quit();
init(0,0);
}
/* Function to reserve a big amount of data when we know we'll need it. Be aware that this
function DOES NOT clear the content of the TiXmlString if any exists.
*/
void reserve (size_type cap);
TiXmlString& assign (const char* str, size_type len);
TiXmlString& append (const char* str, size_type len);
void swap (TiXmlString& other)
{
Rep* r = rep_;
rep_ = other.rep_;
other.rep_ = r;
}
private:
void init(size_type sz) { init(sz, sz); }
void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
char* start() const { return rep_->str; }
char* finish() const { return rep_->str + rep_->size; }
struct Rep
{
size_type size, capacity;
char str[1];
};
void init(size_type sz, size_type cap)
{
if (cap)
{
// Lee: the original form:
// rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
// doesn't work in some cases of new being overloaded. Switching
// to the normal allocation, although use an 'int' for systems
// that are overly picky about structure alignment.
const size_type bytesNeeded = sizeof(Rep) + cap;
const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
rep_->str[ rep_->size = sz ] = '\0';
rep_->capacity = cap;
}
else
{
rep_ = &nullrep_;
}
}
void quit()
{
if (rep_ != &nullrep_)
{
// The rep_ is really an array of ints. (see the allocator, above).
// Cast it back before delete, so the compiler won't incorrectly call destructors.
delete [] ( reinterpret_cast<int*>( rep_ ) );
}
}
Rep * rep_;
static Rep nullrep_;
} ;
inline bool operator == (const TiXmlString & a, const TiXmlString & b)
{
return ( a.length() == b.length() ) // optimization on some platforms
&& ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
}
inline bool operator < (const TiXmlString & a, const TiXmlString & b)
{
return strcmp(a.c_str(), b.c_str()) < 0;
}
inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
TiXmlString operator + (const TiXmlString & a, const char* b);
TiXmlString operator + (const char* a, const TiXmlString & b);
/*
TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
Only the operators that we need for TinyXML have been developped.
*/
class TiXmlOutStream : public TiXmlString
{
public :
// TiXmlOutStream << operator.
TiXmlOutStream & operator << (const TiXmlString & in)
{
*this += in;
return *this;
}
// TiXmlOutStream << operator.
TiXmlOutStream & operator << (const char * in)
{
*this += in;
return *this;
}
} ;
#endif // TIXML_STRING_INCLUDED
#endif // TIXML_USE_STL

1888
extern/oics/tinyxml.cpp vendored Normal file

File diff suppressed because it is too large Load diff

1802
extern/oics/tinyxml.h vendored Normal file

File diff suppressed because it is too large Load diff

53
extern/oics/tinyxmlerror.cpp vendored Normal file
View file

@ -0,0 +1,53 @@
/*
www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinyxml.h"
// The goal of the seperate error file is to make the first
// step towards localization. tinyxml (currently) only supports
// english error messages, but the could now be translated.
//
// It also cleans up the code a bit.
//
const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
{
"No error",
"Error",
"Failed to open file",
"Memory allocation failed.",
"Error parsing Element.",
"Failed to read Element name",
"Error reading Element value.",
"Error reading Attributes.",
"Error: empty tag.",
"Error reading end tag.",
"Error parsing Unknown.",
"Error parsing Comment.",
"Error parsing Declaration.",
"Error document empty.",
"Error null (0) or unexpected EOF found in input stream.",
"Error parsing CDATA.",
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
};

1638
extern/oics/tinyxmlparser.cpp vendored Normal file

File diff suppressed because it is too large Load diff

25
extern/sdl4ogre/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,25 @@
set(SDL4OGRE_LIBRARY "sdl4ogre")
# Sources
set(SDL4OGRE_SOURCE_FILES
sdlinputwrapper.cpp
sdlcursormanager.cpp
sdlwindowhelper.cpp
)
if (APPLE)
set(SDL4OGRE_SOURCE_FILES ${SDL4OGRE_SOURCE_FILES} osx_utils.mm)
endif ()
set(SDL4OGRE_HEADER_FILES
OISCompat.h
cursormanager.hpp
)
add_library(${SDL4OGRE_LIBRARY} STATIC ${SDL4OGRE_SOURCE_FILES} ${SDL4OGRE_HEADER_FILES})
link_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${SDL4OGRE_LIBRARY} ${SDL2_LIBRARY})

159
extern/sdl4ogre/OISCompat.h vendored Normal file
View file

@ -0,0 +1,159 @@
#ifndef _OIS_SDL_COMPAT_H
#define _OIS_SDL_COMPAT_H
#include <SDL_events.h>
#include <SDL_types.h>
namespace OIS
{
//! Keyboard scan codes
enum KeyCode
{
KC_UNASSIGNED = 0x00,
KC_ESCAPE = 0x01,
KC_1 = 0x02,
KC_2 = 0x03,
KC_3 = 0x04,
KC_4 = 0x05,
KC_5 = 0x06,
KC_6 = 0x07,
KC_7 = 0x08,
KC_8 = 0x09,
KC_9 = 0x0A,
KC_0 = 0x0B,
KC_MINUS = 0x0C, // - on main keyboard
KC_EQUALS = 0x0D,
KC_BACK = 0x0E, // backspace
KC_TAB = 0x0F,
KC_Q = 0x10,
KC_W = 0x11,
KC_E = 0x12,
KC_R = 0x13,
KC_T = 0x14,
KC_Y = 0x15,
KC_U = 0x16,
KC_I = 0x17,
KC_O = 0x18,
KC_P = 0x19,
KC_LBRACKET = 0x1A,
KC_RBRACKET = 0x1B,
KC_RETURN = 0x1C, // Enter on main keyboard
KC_LCONTROL = 0x1D,
KC_A = 0x1E,
KC_S = 0x1F,
KC_D = 0x20,
KC_F = 0x21,
KC_G = 0x22,
KC_H = 0x23,
KC_J = 0x24,
KC_K = 0x25,
KC_L = 0x26,
KC_SEMICOLON = 0x27,
KC_APOSTROPHE = 0x28,
KC_GRAVE = 0x29, // accent
KC_LSHIFT = 0x2A,
KC_BACKSLASH = 0x2B,
KC_Z = 0x2C,
KC_X = 0x2D,
KC_C = 0x2E,
KC_V = 0x2F,
KC_B = 0x30,
KC_N = 0x31,
KC_M = 0x32,
KC_COMMA = 0x33,
KC_PERIOD = 0x34, // . on main keyboard
KC_SLASH = 0x35, // / on main keyboard
KC_RSHIFT = 0x36,
KC_MULTIPLY = 0x37, // * on numeric keypad
KC_LMENU = 0x38, // left Alt
KC_SPACE = 0x39,
KC_CAPITAL = 0x3A,
KC_F1 = 0x3B,
KC_F2 = 0x3C,
KC_F3 = 0x3D,
KC_F4 = 0x3E,
KC_F5 = 0x3F,
KC_F6 = 0x40,
KC_F7 = 0x41,
KC_F8 = 0x42,
KC_F9 = 0x43,
KC_F10 = 0x44,
KC_NUMLOCK = 0x45,
KC_SCROLL = 0x46, // Scroll Lock
KC_NUMPAD7 = 0x47,
KC_NUMPAD8 = 0x48,
KC_NUMPAD9 = 0x49,
KC_SUBTRACT = 0x4A, // - on numeric keypad
KC_NUMPAD4 = 0x4B,
KC_NUMPAD5 = 0x4C,
KC_NUMPAD6 = 0x4D,
KC_ADD = 0x4E, // + on numeric keypad
KC_NUMPAD1 = 0x4F,
KC_NUMPAD2 = 0x50,
KC_NUMPAD3 = 0x51,
KC_NUMPAD0 = 0x52,
KC_DECIMAL = 0x53, // . on numeric keypad
KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards
KC_F11 = 0x57,
KC_F12 = 0x58,
KC_F13 = 0x64, // (NEC PC98)
KC_F14 = 0x65, // (NEC PC98)
KC_F15 = 0x66, // (NEC PC98)
KC_KANA = 0x70, // (Japanese keyboard)
KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards
KC_CONVERT = 0x79, // (Japanese keyboard)
KC_NOCONVERT = 0x7B, // (Japanese keyboard)
KC_YEN = 0x7D, // (Japanese keyboard)
KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards
KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98)
KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard)
KC_AT = 0x91, // (NEC PC98)
KC_COLON = 0x92, // (NEC PC98)
KC_UNDERLINE = 0x93, // (NEC PC98)
KC_KANJI = 0x94, // (Japanese keyboard)
KC_STOP = 0x95, // (NEC PC98)
KC_AX = 0x96, // (Japan AX)
KC_UNLABELED = 0x97, // (J3100)
KC_NEXTTRACK = 0x99, // Next Track
KC_NUMPADENTER = 0x9C, // Enter on numeric keypad
KC_RCONTROL = 0x9D,
KC_MUTE = 0xA0, // Mute
KC_CALCULATOR = 0xA1, // Calculator
KC_PLAYPAUSE = 0xA2, // Play / Pause
KC_MEDIASTOP = 0xA4, // Media Stop
KC_VOLUMEDOWN = 0xAE, // Volume -
KC_VOLUMEUP = 0xB0, // Volume +
KC_WEBHOME = 0xB2, // Web home
KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98)
KC_DIVIDE = 0xB5, // / on numeric keypad
KC_SYSRQ = 0xB7,
KC_RMENU = 0xB8, // right Alt
KC_PAUSE = 0xC5, // Pause
KC_HOME = 0xC7, // Home on arrow keypad
KC_UP = 0xC8, // UpArrow on arrow keypad
KC_PGUP = 0xC9, // PgUp on arrow keypad
KC_LEFT = 0xCB, // LeftArrow on arrow keypad
KC_RIGHT = 0xCD, // RightArrow on arrow keypad
KC_END = 0xCF, // End on arrow keypad
KC_DOWN = 0xD0, // DownArrow on arrow keypad
KC_PGDOWN = 0xD1, // PgDn on arrow keypad
KC_INSERT = 0xD2, // Insert on arrow keypad
KC_DELETE = 0xD3, // Delete on arrow keypad
KC_LWIN = 0xDB, // Left Windows key
KC_RWIN = 0xDC, // Right Windows key
KC_APPS = 0xDD, // AppMenu key
KC_POWER = 0xDE, // System Power
KC_SLEEP = 0xDF, // System Sleep
KC_WAKE = 0xE3, // System Wake
KC_WEBSEARCH = 0xE5, // Web Search
KC_WEBFAVORITES= 0xE6, // Web Favorites
KC_WEBREFRESH = 0xE7, // Web Refresh
KC_WEBSTOP = 0xE8, // Web Stop
KC_WEBFORWARD = 0xE9, // Web Forward
KC_WEBBACK = 0xEA, // Web Back
KC_MYCOMPUTER = 0xEB, // My Computer
KC_MAIL = 0xEC, // Mail
KC_MEDIASELECT = 0xED // Media Select
};
}
#endif

33
extern/sdl4ogre/cursormanager.hpp vendored Normal file
View file

@ -0,0 +1,33 @@
#ifndef _SDL4OGRE_CURSOR_MANAGER_H
#define _SDL4OGRE_CURSOR_MANAGER_H
#include <SDL_types.h>
#include <string>
#include <OgreTexture.h>
#include <OgrePrerequisites.h>
namespace SFO
{
class CursorManager
{
public:
virtual ~CursorManager(){}
/// \brief Tell the manager that the cursor has changed, giving the
/// name of the cursor we changed to ("arrow", "ibeam", etc)
/// \return Whether the manager is interested in more information about the cursor
virtual bool cursorChanged(const std::string &name) = 0;
/// \brief Follow up a cursorChanged() call with enough info to create an cursor.
virtual void receiveCursorInfo(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) = 0;
/// \brief Tell the manager when the cursor visibility changed
virtual void cursorVisibilityChange(bool visible) = 0;
/// \brief sets whether to actively manage cursors or not
virtual void setEnabled(bool enabled) = 0;
};
}
#endif

78
extern/sdl4ogre/events.h vendored Normal file
View file

@ -0,0 +1,78 @@
#ifndef _SFO_EVENTS_H
#define _SFO_EVENTS_H
#include <SDL.h>
////////////
// Events //
////////////
namespace SFO {
/** Extended mouse event struct where we treat the wheel like an axis, like everyone expects */
struct MouseMotionEvent : SDL_MouseMotionEvent {
Sint32 zrel;
Sint32 z;
};
///////////////
// Listeners //
///////////////
class MouseListener
{
public:
virtual ~MouseListener() {}
virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0;
virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
};
class KeyListener
{
public:
virtual ~KeyListener() {}
virtual void textInput (const SDL_TextInputEvent& arg) {}
virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0;
virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0;
};
class JoyListener
{
public:
virtual ~JoyListener() {}
/** @remarks Joystick button down event */
virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0;
/** @remarks Joystick button up event */
virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0;
/** @remarks Joystick axis moved event */
virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0;
//-- Not so common control events, so are not required --//
//! Joystick Event, and povID
virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;}
};
class WindowListener
{
public:
virtual ~WindowListener() {}
/** @remarks The window's visibility changed */
virtual void windowVisibilityChange( bool visible ) {};
/** @remarks The window got / lost input focus */
virtual void windowFocusChange( bool have_focus ) {}
virtual void windowResized (int x, int y) {}
};
}
#endif

12
extern/sdl4ogre/osx_utils.h vendored Normal file
View file

@ -0,0 +1,12 @@
#ifndef SDL4OGRE_OSX_UTILS_H
#define SDL4OGRE_OSX_UTILS_H
#include <SDL_syswm.h>
namespace SFO {
extern unsigned long WindowContentViewHandle(SDL_SysWMinfo &info);
}
#endif // SDL4OGRE_OSX_UTILS_H

15
extern/sdl4ogre/osx_utils.mm vendored Normal file
View file

@ -0,0 +1,15 @@
#include "osx_utils.h"
#import <AppKit/NSWindow.h>
namespace SFO {
unsigned long WindowContentViewHandle(SDL_SysWMinfo &info)
{
NSWindow *window = info.info.cocoa.window;
NSView *view = [window contentView];
return (unsigned long)view;
}
}

177
extern/sdl4ogre/sdlcursormanager.cpp vendored Normal file
View file

@ -0,0 +1,177 @@
#include "sdlcursormanager.hpp"
#include <OgreHardwarePixelBuffer.h>
#include <OgreRoot.h>
#include <openengine/ogre/imagerotate.hpp>
namespace SFO
{
SDLCursorManager::SDLCursorManager() :
mEnabled(false),
mCursorVisible(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);
}
}
bool SDLCursorManager::cursorChanged(const std::string &name)
{
mCurrentCursor = name;
CursorMap::const_iterator curs_iter = mCursorMap.find(name);
//we have this cursor
if(curs_iter != mCursorMap.end())
{
_setGUICursor(name);
return false;
}
else
{
//they should get back to us with more info
return true;
}
}
void SDLCursorManager::_setGUICursor(const std::string &name)
{
if(mEnabled && mCursorVisible)
{
SDL_SetCursor(mCursorMap.find(name)->second);
_setCursorVisible(mCursorVisible);
}
}
void SDLCursorManager::_setCursorVisible(bool visible)
{
if(!mEnabled)
return;
SDL_ShowCursor(visible ? SDL_TRUE : SDL_FALSE);
}
void SDLCursorManager::cursorVisibilityChange(bool visible)
{
mCursorVisible = visible;
_setGUICursor(mCurrentCursor);
_setCursorVisible(visible);
}
void SDLCursorManager::receiveCursorInfo(const std::string& name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y)
{
_createCursorFromResource(name, rotDegrees, tex, size_x, size_y, hotspot_x, hotspot_y);
}
/// \brief creates an SDL cursor from an Ogre texture
void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y)
{
if (mCursorMap.find(name) != mCursorMap.end())
return;
std::string tempName = tex->getName() + "_rotated";
// we use a render target to uncompress the DDS texture
// just blitting doesn't seem to work on D3D9
OEngine::Render::ImageRotate::rotate(tex->getName(), tempName, -rotDegrees);
Ogre::TexturePtr resultTexture = Ogre::TextureManager::getSingleton().getByName(tempName);
// now blit to memory
Ogre::Image destImage;
resultTexture->convertToImage(destImage);
SDL_Surface* surf = SDL_CreateRGBSurface(0,size_x,size_y,32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);
//copy the Ogre texture to an SDL surface
for(size_t x = 0; x < size_x; ++x)
{
for(size_t y = 0; y < size_y; ++y)
{
Ogre::ColourValue clr = destImage.getColourAt(x, y, 0);
//set the pixel on the SDL surface to the same value as the Ogre texture's
_putPixel(surf, x, y, SDL_MapRGBA(surf->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255));
}
}
//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);
Ogre::TextureManager::getSingleton().remove(tempName);
_setGUICursor(name);
}
void SDLCursorManager::_putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
}

41
extern/sdl4ogre/sdlcursormanager.hpp vendored Normal file
View file

@ -0,0 +1,41 @@
#ifndef _SDL4OGRE_CURSORMANAGER_H
#define _SDL4OGRE_CURSORMANAGER_H
#include <SDL.h>
#include "cursormanager.hpp"
#include <map>
namespace SFO
{
class SDLCursorManager :
public CursorManager
{
public:
SDLCursorManager();
virtual ~SDLCursorManager();
virtual void setEnabled(bool enabled);
virtual bool cursorChanged(const std::string &name);
virtual void receiveCursorInfo(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);
virtual void cursorVisibilityChange(bool visible);
private:
void _createCursorFromResource(const std::string &name, int rotDegrees, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);
void _putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel);
void _setGUICursor(const std::string& name);
void _setCursorVisible(bool visible);
typedef std::map<std::string, SDL_Cursor*> CursorMap;
CursorMap mCursorMap;
std::string mCurrentCursor;
bool mEnabled;
bool mInitialized;
bool mCursorVisible;
};
}
#endif

418
extern/sdl4ogre/sdlinputwrapper.cpp vendored Normal file
View file

@ -0,0 +1,418 @@
#include "sdlinputwrapper.hpp"
#include <SDL_syswm.h>
#include <OgrePlatform.h>
#include <OgreRoot.h>
namespace SFO
{
/// \brief General purpose wrapper for OGRE applications around SDL's event
/// queue, mostly used for handling input-related events.
InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) :
mSDLWindow(window),
mOgreWindow(ogreWindow),
mWarpCompensate(false),
mMouseRelative(false),
mGrabPointer(false),
mWrapPointer(false),
mMouseZ(0),
mMouseY(0),
mMouseX(0),
mMouseInWindow(true),
mJoyListener(NULL),
mKeyboardListener(NULL),
mMouseListener(NULL),
mWindowListener(NULL)
{
_setupOISKeys();
}
InputWrapper::~InputWrapper()
{
if(mSDLWindow != NULL)
SDL_DestroyWindow(mSDLWindow);
mSDLWindow = NULL;
}
void InputWrapper::capture(bool windowEventsOnly)
{
SDL_PumpEvents();
SDL_Event evt;
if (windowEventsOnly)
{
// During loading, just handle window events, and keep others for later
while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT))
handleWindowEvent(evt);
return;
}
while(SDL_PollEvent(&evt))
{
switch(evt.type)
{
case SDL_MOUSEMOTION:
//ignore this if it happened due to a warp
if(!_handleWarpMotion(evt.motion))
{
mMouseListener->mouseMoved(_packageMouseMotion(evt));
//try to keep the mouse inside the window
_wrapMousePointer(evt.motion);
}
break;
case SDL_MOUSEWHEEL:
mMouseListener->mouseMoved(_packageMouseMotion(evt));
break;
case SDL_MOUSEBUTTONDOWN:
mMouseListener->mousePressed(evt.button, evt.button.button);
break;
case SDL_MOUSEBUTTONUP:
mMouseListener->mouseReleased(evt.button, evt.button.button);
break;
case SDL_KEYDOWN:
if (!evt.key.repeat)
mKeyboardListener->keyPressed(evt.key);
break;
case SDL_KEYUP:
if (!evt.key.repeat)
mKeyboardListener->keyReleased(evt.key);
break;
case SDL_TEXTINPUT:
mKeyboardListener->textInput(evt.text);
break;
case SDL_JOYAXISMOTION:
if (mJoyListener)
mJoyListener->axisMoved(evt.jaxis, evt.jaxis.axis);
break;
case SDL_JOYBUTTONDOWN:
if (mJoyListener)
mJoyListener->buttonPressed(evt.jbutton, evt.jbutton.button);
break;
case SDL_JOYBUTTONUP:
if (mJoyListener)
mJoyListener->buttonReleased(evt.jbutton, evt.jbutton.button);
break;
case SDL_JOYDEVICEADDED:
//SDL_JoystickOpen(evt.jdevice.which);
//std::cout << "Detected a new joystick: " << SDL_JoystickNameForIndex(evt.jdevice.which) << std::endl;
break;
case SDL_JOYDEVICEREMOVED:
//std::cout << "A joystick has been removed" << std::endl;
break;
case SDL_WINDOWEVENT:
handleWindowEvent(evt);
break;
case SDL_QUIT:
Ogre::Root::getSingleton().queueEndRendering();
break;
default:
std::cerr << "Unhandled SDL event of type " << evt.type << std::endl;
break;
}
}
}
void InputWrapper::handleWindowEvent(const SDL_Event& evt)
{
switch (evt.window.event) {
case SDL_WINDOWEVENT_ENTER:
mMouseInWindow = true;
break;
case SDL_WINDOWEVENT_LEAVE:
mMouseInWindow = false;
SDL_SetWindowGrab(mSDLWindow, SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
int w,h;
SDL_GetWindowSize(mSDLWindow, &w, &h);
// TODO: Fix Ogre to handle this more consistently
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
mOgreWindow->resize(w, h);
#else
mOgreWindow->windowMovedOrResized();
#endif
if (mWindowListener)
mWindowListener->windowResized(w, h);
break;
case SDL_WINDOWEVENT_RESIZED:
// TODO: Fix Ogre to handle this more consistently
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
mOgreWindow->resize(evt.window.data1, evt.window.data2);
#else
mOgreWindow->windowMovedOrResized();
#endif
if (mWindowListener)
mWindowListener->windowResized(evt.window.data1, evt.window.data2);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (mWindowListener)
mWindowListener->windowFocusChange(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (mWindowListener)
mWindowListener->windowFocusChange(false);
break;
case SDL_WINDOWEVENT_CLOSE:
break;
case SDL_WINDOWEVENT_SHOWN:
mOgreWindow->setVisible(true);
if (mWindowListener)
mWindowListener->windowVisibilityChange(true);
break;
case SDL_WINDOWEVENT_HIDDEN:
mOgreWindow->setVisible(false);
if (mWindowListener)
mWindowListener->windowVisibilityChange(false);
break;
}
}
bool InputWrapper::isModifierHeld(SDL_Keymod mod)
{
return SDL_GetModState() & mod;
}
bool InputWrapper::isKeyDown(SDL_Scancode key)
{
return SDL_GetKeyboardState(NULL)[key];
}
/// \brief Moves the mouse to the specified point within the viewport
void InputWrapper::warpMouse(int x, int y)
{
SDL_WarpMouseInWindow(mSDLWindow, x, y);
mWarpCompensate = true;
mWarpX = x;
mWarpY = y;
}
/// \brief Locks the pointer to the window
void InputWrapper::setGrabPointer(bool grab)
{
mGrabPointer = grab && mMouseInWindow;
SDL_SetWindowGrab(mSDLWindow, grab && mMouseInWindow ? SDL_TRUE : SDL_FALSE);
}
/// \brief Set the mouse to relative positioning. Doesn't move the cursor
/// and disables mouse acceleration.
void InputWrapper::setMouseRelative(bool relative)
{
if(mMouseRelative == relative && mMouseInWindow)
return;
mMouseRelative = relative && mMouseInWindow;
mWrapPointer = false;
//eep, wrap the pointer manually if the input driver doesn't support
//relative positioning natively
int success = SDL_SetRelativeMouseMode(relative && mMouseInWindow ? SDL_TRUE : SDL_FALSE);
if(relative && mMouseInWindow && success != 0)
mWrapPointer = true;
//now remove all mouse events using the old setting from the queue
SDL_PumpEvents();
SDL_FlushEvent(SDL_MOUSEMOTION);
}
/// \brief Internal method for ignoring relative motions as a side effect
/// of warpMouse()
bool InputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt)
{
if(!mWarpCompensate)
return false;
//this was a warp event, signal the caller to eat it.
if(evt.x == mWarpX && evt.y == mWarpY)
{
mWarpCompensate = false;
return true;
}
return false;
}
/// \brief Wrap the mouse to the viewport
void InputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt)
{
//don't wrap if we don't want relative movements, support relative
//movements natively, or aren't grabbing anyways
if(!mMouseRelative || !mWrapPointer || !mGrabPointer)
return;
int width = 0;
int height = 0;
SDL_GetWindowSize(mSDLWindow, &width, &height);
const int FUDGE_FACTOR_X = width;
const int FUDGE_FACTOR_Y = height;
//warp the mouse if it's about to go outside the window
if(evt.x - FUDGE_FACTOR_X < 0 || evt.x + FUDGE_FACTOR_X > width
|| evt.y - FUDGE_FACTOR_Y < 0 || evt.y + FUDGE_FACTOR_Y > height)
{
warpMouse(width / 2, height / 2);
}
}
/// \brief Package mouse and mousewheel motions into a single event
MouseMotionEvent InputWrapper::_packageMouseMotion(const SDL_Event &evt)
{
MouseMotionEvent pack_evt;
pack_evt.x = mMouseX;
pack_evt.xrel = 0;
pack_evt.y = mMouseY;
pack_evt.yrel = 0;
pack_evt.z = mMouseZ;
pack_evt.zrel = 0;
if(evt.type == SDL_MOUSEMOTION)
{
pack_evt.x = mMouseX = evt.motion.x;
pack_evt.y = mMouseY = evt.motion.y;
pack_evt.xrel = evt.motion.xrel;
pack_evt.yrel = evt.motion.yrel;
}
else if(evt.type == SDL_MOUSEWHEEL)
{
mMouseZ += pack_evt.zrel = (evt.wheel.y * 120);
pack_evt.z = mMouseZ;
}
else
{
throw new std::runtime_error("Tried to package non-motion event!");
}
return pack_evt;
}
OIS::KeyCode InputWrapper::sdl2OISKeyCode(SDL_Keycode code)
{
OIS::KeyCode kc = OIS::KC_UNASSIGNED;
KeyMap::const_iterator ois_equiv = mKeyMap.find(code);
if(ois_equiv != mKeyMap.end())
kc = ois_equiv->second;
return kc;
}
void InputWrapper::_setupOISKeys()
{
//lifted from OIS's SDLKeyboard.cpp
//TODO: Consider switching to scancodes so we
//can properly support international keyboards
//look at SDL_GetKeyFromScancode and SDL_GetKeyName
mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED));
mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) );
mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) );
mKeyMap.insert( KeyMap::value_type(SDLK_2, OIS::KC_2) );
mKeyMap.insert( KeyMap::value_type(SDLK_3, OIS::KC_3) );
mKeyMap.insert( KeyMap::value_type(SDLK_4, OIS::KC_4) );
mKeyMap.insert( KeyMap::value_type(SDLK_5, OIS::KC_5) );
mKeyMap.insert( KeyMap::value_type(SDLK_6, OIS::KC_6) );
mKeyMap.insert( KeyMap::value_type(SDLK_7, OIS::KC_7) );
mKeyMap.insert( KeyMap::value_type(SDLK_8, OIS::KC_8) );
mKeyMap.insert( KeyMap::value_type(SDLK_9, OIS::KC_9) );
mKeyMap.insert( KeyMap::value_type(SDLK_0, OIS::KC_0) );
mKeyMap.insert( KeyMap::value_type(SDLK_MINUS, OIS::KC_MINUS) );
mKeyMap.insert( KeyMap::value_type(SDLK_EQUALS, OIS::KC_EQUALS) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKSPACE, OIS::KC_BACK) );
mKeyMap.insert( KeyMap::value_type(SDLK_TAB, OIS::KC_TAB) );
mKeyMap.insert( KeyMap::value_type(SDLK_q, OIS::KC_Q) );
mKeyMap.insert( KeyMap::value_type(SDLK_w, OIS::KC_W) );
mKeyMap.insert( KeyMap::value_type(SDLK_e, OIS::KC_E) );
mKeyMap.insert( KeyMap::value_type(SDLK_r, OIS::KC_R) );
mKeyMap.insert( KeyMap::value_type(SDLK_t, OIS::KC_T) );
mKeyMap.insert( KeyMap::value_type(SDLK_y, OIS::KC_Y) );
mKeyMap.insert( KeyMap::value_type(SDLK_u, OIS::KC_U) );
mKeyMap.insert( KeyMap::value_type(SDLK_i, OIS::KC_I) );
mKeyMap.insert( KeyMap::value_type(SDLK_o, OIS::KC_O) );
mKeyMap.insert( KeyMap::value_type(SDLK_p, OIS::KC_P) );
mKeyMap.insert( KeyMap::value_type(SDLK_RETURN, OIS::KC_RETURN) );
mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LCONTROL));
mKeyMap.insert( KeyMap::value_type(SDLK_a, OIS::KC_A) );
mKeyMap.insert( KeyMap::value_type(SDLK_s, OIS::KC_S) );
mKeyMap.insert( KeyMap::value_type(SDLK_d, OIS::KC_D) );
mKeyMap.insert( KeyMap::value_type(SDLK_f, OIS::KC_F) );
mKeyMap.insert( KeyMap::value_type(SDLK_g, OIS::KC_G) );
mKeyMap.insert( KeyMap::value_type(SDLK_h, OIS::KC_H) );
mKeyMap.insert( KeyMap::value_type(SDLK_j, OIS::KC_J) );
mKeyMap.insert( KeyMap::value_type(SDLK_k, OIS::KC_K) );
mKeyMap.insert( KeyMap::value_type(SDLK_l, OIS::KC_L) );
mKeyMap.insert( KeyMap::value_type(SDLK_SEMICOLON, OIS::KC_SEMICOLON) );
mKeyMap.insert( KeyMap::value_type(SDLK_COLON, OIS::KC_COLON) );
mKeyMap.insert( KeyMap::value_type(SDLK_QUOTE, OIS::KC_APOSTROPHE) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKQUOTE, OIS::KC_GRAVE) );
mKeyMap.insert( KeyMap::value_type(SDLK_LSHIFT, OIS::KC_LSHIFT) );
mKeyMap.insert( KeyMap::value_type(SDLK_BACKSLASH, OIS::KC_BACKSLASH) );
mKeyMap.insert( KeyMap::value_type(SDLK_SLASH, OIS::KC_SLASH) );
mKeyMap.insert( KeyMap::value_type(SDLK_z, OIS::KC_Z) );
mKeyMap.insert( KeyMap::value_type(SDLK_x, OIS::KC_X) );
mKeyMap.insert( KeyMap::value_type(SDLK_c, OIS::KC_C) );
mKeyMap.insert( KeyMap::value_type(SDLK_v, OIS::KC_V) );
mKeyMap.insert( KeyMap::value_type(SDLK_b, OIS::KC_B) );
mKeyMap.insert( KeyMap::value_type(SDLK_n, OIS::KC_N) );
mKeyMap.insert( KeyMap::value_type(SDLK_m, OIS::KC_M) );
mKeyMap.insert( KeyMap::value_type(SDLK_COMMA, OIS::KC_COMMA) );
mKeyMap.insert( KeyMap::value_type(SDLK_PERIOD, OIS::KC_PERIOD));
mKeyMap.insert( KeyMap::value_type(SDLK_RSHIFT, OIS::KC_RSHIFT));
mKeyMap.insert( KeyMap::value_type(SDLK_KP_MULTIPLY, OIS::KC_MULTIPLY) );
mKeyMap.insert( KeyMap::value_type(SDLK_LALT, OIS::KC_LMENU) );
mKeyMap.insert( KeyMap::value_type(SDLK_SPACE, OIS::KC_SPACE));
mKeyMap.insert( KeyMap::value_type(SDLK_CAPSLOCK, OIS::KC_CAPITAL) );
mKeyMap.insert( KeyMap::value_type(SDLK_F1, OIS::KC_F1) );
mKeyMap.insert( KeyMap::value_type(SDLK_F2, OIS::KC_F2) );
mKeyMap.insert( KeyMap::value_type(SDLK_F3, OIS::KC_F3) );
mKeyMap.insert( KeyMap::value_type(SDLK_F4, OIS::KC_F4) );
mKeyMap.insert( KeyMap::value_type(SDLK_F5, OIS::KC_F5) );
mKeyMap.insert( KeyMap::value_type(SDLK_F6, OIS::KC_F6) );
mKeyMap.insert( KeyMap::value_type(SDLK_F7, OIS::KC_F7) );
mKeyMap.insert( KeyMap::value_type(SDLK_F8, OIS::KC_F8) );
mKeyMap.insert( KeyMap::value_type(SDLK_F9, OIS::KC_F9) );
mKeyMap.insert( KeyMap::value_type(SDLK_F10, OIS::KC_F10) );
mKeyMap.insert( KeyMap::value_type(SDLK_NUMLOCKCLEAR, OIS::KC_NUMLOCK) );
mKeyMap.insert( KeyMap::value_type(SDLK_SCROLLLOCK, OIS::KC_SCROLL));
mKeyMap.insert( KeyMap::value_type(SDLK_KP_7, OIS::KC_NUMPAD7) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_8, OIS::KC_NUMPAD8) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_9, OIS::KC_NUMPAD9) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_MINUS, OIS::KC_SUBTRACT) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_4, OIS::KC_NUMPAD4) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_5, OIS::KC_NUMPAD5) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_6, OIS::KC_NUMPAD6) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_PLUS, OIS::KC_ADD) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_1, OIS::KC_NUMPAD1) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_2, OIS::KC_NUMPAD2) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_3, OIS::KC_NUMPAD3) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_0, OIS::KC_NUMPAD0) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_PERIOD, OIS::KC_DECIMAL) );
mKeyMap.insert( KeyMap::value_type(SDLK_F11, OIS::KC_F11) );
mKeyMap.insert( KeyMap::value_type(SDLK_F12, OIS::KC_F12) );
mKeyMap.insert( KeyMap::value_type(SDLK_F13, OIS::KC_F13) );
mKeyMap.insert( KeyMap::value_type(SDLK_F14, OIS::KC_F14) );
mKeyMap.insert( KeyMap::value_type(SDLK_F15, OIS::KC_F15) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_EQUALS, OIS::KC_NUMPADEQUALS) );
mKeyMap.insert( KeyMap::value_type(SDLK_KP_DIVIDE, OIS::KC_DIVIDE) );
mKeyMap.insert( KeyMap::value_type(SDLK_SYSREQ, OIS::KC_SYSRQ) );
mKeyMap.insert( KeyMap::value_type(SDLK_RALT, OIS::KC_RMENU) );
mKeyMap.insert( KeyMap::value_type(SDLK_HOME, OIS::KC_HOME) );
mKeyMap.insert( KeyMap::value_type(SDLK_UP, OIS::KC_UP) );
mKeyMap.insert( KeyMap::value_type(SDLK_PAGEUP, OIS::KC_PGUP) );
mKeyMap.insert( KeyMap::value_type(SDLK_LEFT, OIS::KC_LEFT) );
mKeyMap.insert( KeyMap::value_type(SDLK_RIGHT, OIS::KC_RIGHT) );
mKeyMap.insert( KeyMap::value_type(SDLK_END, OIS::KC_END) );
mKeyMap.insert( KeyMap::value_type(SDLK_DOWN, OIS::KC_DOWN) );
mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) );
mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) );
mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) );
}
}

76
extern/sdl4ogre/sdlinputwrapper.hpp vendored Normal file
View file

@ -0,0 +1,76 @@
#ifndef _SDL4OGRE_SDLINPUTWRAPPER_H
#define _SDL4OGRE_SDLINPUTWRAPPER_H
#include <SDL_events.h>
#include <OgreRenderWindow.h>
#include <boost/unordered_map.hpp>
#include "OISCompat.h"
#include "events.h"
namespace SFO
{
class InputWrapper
{
public:
InputWrapper(SDL_Window *window, Ogre::RenderWindow* ogreWindow);
~InputWrapper();
void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; }
void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; }
void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; }
void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; }
void capture(bool windowEventsOnly);
bool isModifierHeld(SDL_Keymod mod);
bool isKeyDown(SDL_Scancode key);
void setMouseRelative(bool relative);
bool getMouseRelative() { return mMouseRelative; }
void setGrabPointer(bool grab);
OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code);
void warpMouse(int x, int y);
private:
void handleWindowEvent(const SDL_Event& evt);
bool _handleWarpMotion(const SDL_MouseMotionEvent& evt);
void _wrapMousePointer(const SDL_MouseMotionEvent &evt);
MouseMotionEvent _packageMouseMotion(const SDL_Event& evt);
void _setupOISKeys();
SFO::MouseListener* mMouseListener;
SFO::KeyListener* mKeyboardListener;
SFO::WindowListener* mWindowListener;
SFO::JoyListener* mJoyListener;
typedef boost::unordered_map<SDL_Keycode, OIS::KeyCode> KeyMap;
KeyMap mKeyMap;
Uint16 mWarpX;
Uint16 mWarpY;
bool mWarpCompensate;
bool mMouseRelative;
bool mWrapPointer;
bool mGrabPointer;
Sint32 mMouseZ;
Sint32 mMouseX;
Sint32 mMouseY;
bool mMouseInWindow;
SDL_Window* mSDLWindow;
Ogre::RenderWindow* mOgreWindow;
};
}
#endif

119
extern/sdl4ogre/sdlwindowhelper.cpp vendored Normal file
View file

@ -0,0 +1,119 @@
#include "sdlwindowhelper.hpp"
#include <OgreStringConverter.h>
#include <OgreRoot.h>
#include <SDL_syswm.h>
#include <SDL_endian.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include "osx_utils.h"
#endif
namespace SFO
{
SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h,
const std::string& title, bool fullscreen, Ogre::NameValuePairList params)
: mSDLWindow(window)
{
//get the native whnd
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if (SDL_GetWindowWMInfo(mSDLWindow, &wmInfo) == SDL_FALSE)
throw std::runtime_error("Couldn't get WM Info!");
Ogre::String winHandle;
switch (wmInfo.subsystem)
{
#ifdef WIN32
case SDL_SYSWM_WINDOWS:
// Windows code
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window);
break;
#elif __MACOSX__
case SDL_SYSWM_COCOA:
//required to make OGRE play nice with our window
params.insert(std::make_pair("macAPI", "cocoa"));
params.insert(std::make_pair("macAPICocoaUseNSView", "true"));
winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo));
break;
#else
case SDL_SYSWM_X11:
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window);
break;
#endif
default:
throw std::runtime_error("Unexpected WM!");
break;
}
/// \todo externalWindowHandle is deprecated according to the source code. Figure out a way to get parentWindowHandle
/// to work properly. On Linux/X11 it causes an occasional GLXBadDrawable error.
params.insert(std::make_pair("externalWindowHandle", winHandle));
mWindow = Ogre::Root::getSingleton().createRenderWindow(title, w, h, fullscreen, &params);
}
void SDLWindowHelper::setWindowIcon(const std::string &name)
{
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
if (texture.isNull())
{
std::stringstream error;
error << "Window icon not found: " << name;
throw std::runtime_error(error.str());
}
Ogre::Image image;
texture->convertToImage(image);
SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);
//copy the Ogre texture to an SDL surface
for(size_t x = 0; x < texture->getWidth(); ++x)
{
for(size_t y = 0; y < texture->getHeight(); ++y)
{
Ogre::ColourValue clr = image.getColourAt(x, y, 0);
//set the pixel on the SDL surface to the same value as the Ogre texture's
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255);
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
}
SDL_SetWindowIcon(mSDLWindow, surface);
SDL_FreeSurface(surface);
}
}

31
extern/sdl4ogre/sdlwindowhelper.hpp vendored Normal file
View file

@ -0,0 +1,31 @@
#ifndef SDL4OGRE_SDLWINDOWHELPER_H
#define SDL4OGRE_SDLWINDOWHELPER_H
#include <OgreRenderWindow.h>
namespace Ogre
{
class RenderWindow;
}
struct SDL_Window;
namespace SFO
{
/// @brief Creates an Ogre window from an SDL window and allows setting an Ogre texture as window icon
class SDLWindowHelper
{
public:
SDLWindowHelper (SDL_Window* window, int w, int h, const std::string& title, bool fullscreen, Ogre::NameValuePairList params);
void setWindowIcon(const std::string& name);
Ogre::RenderWindow* getWindow() { return mWindow; }
private:
Ogre::RenderWindow* mWindow;
SDL_Window* mSDLWindow;
};
}
#endif

54
extern/shiny/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 2.8)
# This is NOT intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project.
# Make sure to link against Ogre and boost::filesystem.
option(SHINY_BUILD_OGRE_PLATFORM "build the Ogre platform" ON)
set(SHINY_LIBRARY "shiny")
set(SHINY_OGREPLATFORM_LIBRARY "shiny.OgrePlatform")
# Sources
set(SOURCE_FILES
Main/Factory.cpp
Main/MaterialInstance.cpp
Main/MaterialInstancePass.cpp
Main/MaterialInstanceTextureUnit.cpp
Main/Platform.cpp
Main/Preprocessor.cpp
Main/PropertyBase.cpp
Main/ScriptLoader.cpp
Main/ShaderInstance.cpp
Main/ShaderSet.cpp
)
set(OGRE_PLATFORM_SOURCE_FILES
Platforms/Ogre/OgreGpuProgram.cpp
Platforms/Ogre/OgreMaterial.cpp
Platforms/Ogre/OgreMaterialSerializer.cpp
Platforms/Ogre/OgrePass.cpp
Platforms/Ogre/OgrePlatform.cpp
Platforms/Ogre/OgreTextureUnitState.cpp
)
file(GLOB OGRE_PLATFORM_SOURCE_FILES Platforms/Ogre/*.cpp)
add_library(${SHINY_LIBRARY} STATIC ${SOURCE_FILES})
set(SHINY_LIBRARIES ${SHINY_LIBRARY})
if (SHINY_BUILD_OGRE_PLATFORM)
add_library(${SHINY_OGREPLATFORM_LIBRARY} STATIC ${OGRE_PLATFORM_SOURCE_FILES})
set(SHINY_LIBRARIES ${SHINY_LIBRARIES} ${SHINY_OGREPLATFORM_LIBRARY})
endif()
set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE)
if (DEFINED SHINY_BUILD_MATERIAL_EDITOR)
add_subdirectory(Editor)
set(SHINY_BUILD_EDITOR_FLAG ${SHINY_BUILD_EDITOR_FLAG} PARENT_SCOPE)
endif()
link_directories(${CMAKE_CURRENT_BINARY_DIR})
set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE)

32
extern/shiny/Docs/Configurations.dox vendored Normal file
View file

@ -0,0 +1,32 @@
/*!
\page configurations Configurations
A common task in shader development is to provide a different set of simpler shaders for all your materials. Some examples:
- When rendering cubic or planar reflection maps in real-time, you will want to disable shadows.
- For an in-game minimap render target, you don't want to have fog.
For this task, the library provides a \a Configuration concept.
A Configuration is a set of properties that can override global settings, as long as this Configuration is active.
Here's an example. Say you have a global setting with the name 'shadows' that controls if your materials receive shadows.
Now, lets create a configuration for our reflection render targets that disables shadows for all materials. Paste the following in a new file with the extension '.configuration':
\code
configuration reflection_targets
{
shadows false
}
\endcode
\note You may also create configurations using sh::Factory::createConfiguration.
The active Configuration is controlled by the active material scheme in Ogre. So, in order to use the configuration "reflection_targets" for your reflection renders, simply call
\code
viewport->setMaterialScheme ("reflection_targets");
\endcode
on the Ogre viewport of your reflection render!
*/

1826
extern/shiny/Docs/Doxyfile vendored Normal file

File diff suppressed because it is too large Load diff

65
extern/shiny/Docs/GettingStarted.dox vendored Normal file
View file

@ -0,0 +1,65 @@
/*!
\page getting-started Getting started
\section download Download the source
\code
git clone git@github.com:scrawl/shiny.git
\endcode
\section building Build the source
The source files you want to build are:
- Main/*.cpp
- Preprocessor/*.cpp (unless you are using the system install of boost::wave, more below)
- Platforms/Ogre/*.cpp
You can either build the sources as a static library, or simply add the sources to the source tree of your project.
If you use CMake, you might find the included CMakeLists.txt useful. It builds static libraries with the names "shiny" and "shiny.OgrePlatform".
\note The CMakeLists.txt is not intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project.
Make sure to link against OGRE and the boost filesystem library.
If your boost version is older than 1.49, you must set the SHINY_USE_WAVE_SYSTEM_INSTALL variable and additionally link against the boost wave library.
\code
set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE")
\endcode
\section code Basic initialisation code
Add the following code to your application:
\code{cpp}
#include <shiny/Main/Factory.hpp>
#include <shiny/Platforms/OgrePlatform/OgrePlatform.hpp>
....
sh::OgrePlatform* platform = new sh::OgrePlatform(
"General", // OGRE Resource group to use for creating materials.
myApplication.getDataPath() + "/" + "materials" // Path to look for materials and shaders. NOTE: This does NOT use the Ogre resource system, so you have to specify an absolute path.
);
sh::Factory* factory = new sh::Factory(platform);
// Set a language. Valid options: CG, HLSL, GLSL
factory->setCurrentLanguage(sh::Language_GLSL);
factory->loadAllFiles();
....
your application runs here
....
// don't forget to delete on exit
delete factory;
\endcode
That's it! Now you can start defining materials. Refer to the page \ref defining-materials-shaders .
*/

49
extern/shiny/Docs/Lod.dox vendored Normal file
View file

@ -0,0 +1,49 @@
/*!
\page lod Material LOD
\section howitworks How it works
When Ogre requests a technique for a specific LOD index, the Factory selects the appropriate LOD configuration which then temporarily overrides the global settings in the shaders. We can use this to disable shader features one by one at a lower LOD, resulting in simpler and faster techniques for distant objects.
\section howtouseit How to use it
- Create a file with the extension '.lod'. There you can specify shader features to disable at a specific LOD level. Higher LOD index refers to a lower LOD. Example contents:
\code
lod_configuration 1
{
specular_mapping false
}
lod_configuration 2
{
specular_mapping false
normal_mapping false
}
lod_configuration 3
{
terrain_composite_map true
specular_mapping false
normal_mapping false
}
\endcode
\note You can also add LOD configurations by calling \a sh::Factory::registerLodConfiguration.
\note Do not use an index of 0. LOD 0 refers to the highest LOD, and you will never want to disable features at the highest LOD level.
- In your materials, specify the distances at which a lower LOD kicks in. Note that the actual distance might also be affected by the viewport and current entity LOD bias. In this example, the first LOD level (lod index 1) would normally be applied at a distance of 100 units, the next after 300, and the last after 1000 units.
\code
material sample_material
{
lod_values 100 300 1000
... your passes, texture units etc ...
}
\endcode
*/

283
extern/shiny/Docs/Macros.dox vendored Normal file
View file

@ -0,0 +1,283 @@
/*!
\page macros Shader Macros
\tableofcontents
\section Shader Language
These macros are automatically defined, depending on the shader language that has been set by the application using sh::Factory::setCurrentLanguage.
- SH_GLSL
- SH_HLSL
- SH_CG
<B>Example:</B>
\code
#if SH_GLSL == 1
// glsl porting code
#endif
#if SH_CG == 1 || SH_HLSL == 1
// cg / hlsl porting code (similiar syntax)
#endif
\endcode
\note It is encouraged to use the shipped porting header (extra/core.h) by #include-ing it in your shaders. If you do that, you should not have to use the above macros directly.
\section vertex-fragment Vertex / fragment shader
These macros are automatically defined, depending on the type of shader that is currently being compiled.
- SH_VERTEX_SHADER
- SH_FRAGMENT_SHADER
If you use the same source file for both vertex and fragment shader, then it is advised to use these macros for blending out the unused source. This will reduce your compile time.
\section passthrough Vertex -> Fragment passthrough
In shader development, a common task is to pass variables from the vertex to the fragment shader. This is no problem if you have a deterministic shader source (i.e. no #ifdefs).
However, as soon as you begin to have lots of permutations of the same shader source, a problem arises. All current GPUs have a limit of 8 vertex to fragment passthroughs (with 4 components each, for example a float4).
A common optimization is to put several individual float values together in a float4 (so-called "Packing"). But if your shader has lots of permutations and the passthrough elements you actually need are not known beforehand, it can be very tedious to pack manually. With the following macros, packing can become easier.
\subsection shAllocatePassthrough shAllocatePassthrough
<B>Usage:</B> \@shAllocatePassthrough(num_components, name)
<B>Example:</B>
\code
#if FRAGMENT_NEED_DEPTH
@shAllocatePassthrough(1, depth)
#endif
\endcode
This is the first thing you should do before using any of the macros below.
\subsection shPassthroughVertexOutputs shPassthroughVertexOutputs
<B>Usage:</B> \@shPassthroughVertexOutputs
Use this in the inputs/outputs section of your vertex shader, in order to declare all the outputs that are needed for packing the variables that you want passed to the fragment.
\subsection shPassthroughFragmentInputs shPassthroughFragmentInputs
<B>Usage:</B> \@shPassthroughFragmentInputs
Use this in the inputs/outputs section of your fragment shader, in order to declare all the inputs that are needed for receiving the variables that you want passed to the fragment.
\subsection shPassthroughAssign shPassthroughAssign
<B>Usage:</B> \@shPassthroughAssign(name, value)
Use this in the vertex shader for assigning a value to the variable you want passed to the fragment.
<B>Example:</B>
\code
#if FRAGMENT_NEED_DEPTH
@shPassthroughAssign(depth, shOutputPosition.z);
#endif
\endcode
\subsection shPassthroughReceive shPassthroughReceive
<B>Usage:</B> \@shPassthroughReceive(name)
Use this in the fragment shader to receive the passed value.
<B>Example:</B>
\code
#if FRAGMENT_NEED_DEPTH
float depth = @shPassthroughReceive(depth);
#endif
\endcode
\section texUnits Texture units
\subsection shUseSampler shUseSampler
<B>Usage:</B> \@shUseSampler(samplerName)
Requests the texture unit with name \a samplerName to be available for use in this pass.
Why is this necessary? If you have a derived material that does not use all of the texture units that its parent defines (for example, if an optional asset such as a normal map is not available), there would be no way to know which texture units are actually needed and which can be skipped in the creation process (those that are never referenced in the shader).
\section properties Property retrieval / binding
\subsection shPropertyHasValue shPropertyHasValue
<B>Usage:</B> \@shPropertyHasValue(property)
Gets replaced by 1 if the property's value is not empty, or 0 if it is empty.
Useful for checking whether an optional texture is present or not.
<B>Example:</B>
\code
#if @shPropertyHasValue(specularMap)
// specular mapping code
#endif
#if @shPropertyHasValue(normalMap)
// normal mapping code
#endif
\endcode
\subsection shUniformProperty shUniformProperty
<B>Usage:</B> \@shUniformProperty<4f|3f|2f|1f|int> (uniformName, property)
Binds the value of \a property (from the shader_properties of the pass this shader belongs to) to the uniform with name \a uniformName.
The following variants are available, depending on the type of your uniform variable:
- \@shUniformProperty4f
- \@shUniformProperty3f
- \@shUniformProperty2f
- \@shUniformProperty1f
- \@shUniformPropertyInt
<B>Example:</B> \@shUniformProperty1f (uFresnelScale, fresnelScale)
\subsection shPropertyBool shPropertyBool
Retrieve a boolean property of the pass that this shader belongs to, gets replaced with either 0 or 1.
<B>Usage:</B> \@shPropertyBool(propertyName)
<B>Example:</B>
\code
#if @shPropertyBool(has_vertex_colors)
...
#endif
\endcode
\subsection shPropertyString shPropertyString
Retrieve a string property of the pass that this shader belongs to
<B>Usage:</B> \@shPropertyString(propertyName)
\subsection shPropertyEqual shPropertyEqual
Check if the value of a property equals a specific value, gets replaced with either 0 or 1. This is useful because the preprocessor cannot compare strings, only numbers.
<B>Usage:</B> \@shPropertyEqual(propertyName, value)
<B>Example:</B>
\code
#if @shPropertyEqual(lighting_mode, phong)
...
#endif
\endcode
\section globalSettings Global settings
\subsection shGlobalSettingBool shGlobalSettingBool
Retrieves the boolean value of a specific global setting, gets replaced with either 0 or 1. The value can be set using sh::Factory::setGlobalSetting.
<B>Usage:</B> \@shGlobalSettingBool(settingName)
\subsection shGlobalSettingEqual shGlobalSettingEqual
Check if the value of a global setting equals a specific value, gets replaced with either 0 or 1. This is useful because the preprocessor cannot compare strings, only numbers.
<B>Usage:</B> \@shGlobalSettingEqual(settingName, value)
\subsection shGlobalSettingString shGlobalSettingString
Gets replaced with the current value of a given global setting. The value can be set using sh::Factory::setGlobalSetting.
<B>Usage:</B> \@shGlobalSettingString(settingName)
\section sharedParams Shared parameters
\subsection shSharedParameter shSharedParameter
Allows you to bind a custom value to a uniform parameter.
<B>Usage</B>: \@shSharedParameter(sharedParameterName)
<B>Example</B>: \@shSharedParameter(pssmSplitPoints) - now the uniform parameter 'pssmSplitPoints' can be altered in all shaders that use it by executing sh::Factory::setSharedParameter("pssmSplitPoints", value)
\note You may use the same shared parameter in as many shaders as you want. But don't forget to add the \@shSharedParameter macro to every shader that uses this shared parameter.
\section autoconstants Auto constants
\subsection shAutoConstant shAutoConstant
<B>Usage:</B> \@shAutoConstant(uniformName, autoConstantName, [extraData])
<B>Example</B>: \@shAutoConstant(uModelViewMatrix, worldviewproj_matrix)
<B>Example</B>: \@shAutoConstant(uLightPosition4, light_position, 4)
Binds auto constant with name \a autoConstantName to the uniform \a uniformName. Optionally, you may specify extra data (for example the light index), as required by some auto constants.
The auto constant names are the same as Ogre's. Read the section "3.1.9 Using Vertex/Geometry/Fragment Programs in a Pass" of the Ogre manual for a list of all auto constant names.
\section misc Misc
\subsection shForeach shForeach
<B>Usage:</B> \@shForeach(n)
Repeats the content of this foreach block \a n times. The end of the block is marked via \@shEndForeach, and the current iteration number can be retrieved via \@shIterator.
\note Nested foreach blocks are currently \a not supported.
\note For technical reasons, you can only use constant numbers, properties (\@shPropertyString) or global settings (\@shGlobalSettingString) as \a n parameter.
<B>Example:</B>
\code
@shForeach(3)
this is iteration number @shIterator
@shEndForeach
Gets replaced with:
this is iteration number 0
this is iteration number 1
this is iteration number 2
\endcode
Optionally, you can pass a constant offset to \@shIterator. Example:
\code
@shForeach(3)
this is iteration number @shIterator(7)
@shEndForeach
Gets replaced with:
this is iteration number 7
this is iteration number 8
this is iteration number 9
\endcode
\subsection shCounter shCounter
Gets replaced after the preprocessing step with the number that equals the n-th occurence of counters of the same ID.
<B>Usage:</B> \@shCounter(ID)
<B>Example</B>:
\code
@shCounter(0)
@shCounter(0)
@shCounter(1)
@shCounter(0)
\endcode
Gets replaced with:
\code
0
1
0
2
\endcode
*/

13
extern/shiny/Docs/Mainpage.dox vendored Normal file
View file

@ -0,0 +1,13 @@
/*!
\mainpage
- \ref getting-started
- \ref defining-materials-shaders
- \ref macros
- \ref configurations
- \ref lod
- sh::Factory - the main interface class
*/

131
extern/shiny/Docs/Materials.dox vendored Normal file
View file

@ -0,0 +1,131 @@
/*!
\page defining-materials-shaders Defining materials and shaders
\section first-material Your first material
Create a file called "myFirstMaterial.mat" and place it in the path you have used in your initialisation code (see \ref getting-started). Paste the following:
\code
material my_first_material
{
diffuse 1.0 1.0 1.0 1.0
specular 0.4 0.4 0.4 32
ambient 1.0 1.0 1.0
emissive 0.0 0.0 0.0
diffuseMap black.png
pass
{
diffuse $diffuse
specular $specular
ambient $ambient
emissive $emissive
texture_unit diffuseMap
{
texture $diffuseMap
create_in_ffp true // use this texture unit for fixed function pipeline
}
}
}
material material1
{
parent my_first_material
diffuseMap texture1.png
}
material material2
{
parent my_first_material
diffuseMap texture2.png
}
\endcode
\section first-shader The first shader
Change the 'pass' section to include some shaders:
\code
pass
{
vertex_program my_first_shader_vertex
fragment_program my_first_shader_fragment
...
}
\endcode
\note This does \a not refer to a single shader with a fixed source code, but in fact will automatically create a new \a instance of this shader (if necessary), which can have its own uniform variables, compile-time macros and much more!
Next, we're going to define our shaders. Paste this in a new file called 'myfirstshader.shaderset'
\code
shader_set my_first_shader_vertex
{
source example.shader
type vertex
profiles_cg vs_2_0 arbvp1
profiles_hlsl vs_2_0
}
shader_set my_first_shader_fragment
{
source example.shader
type fragment
profiles_cg ps_2_x ps_2_0 ps arbfp1
profiles_hlsl ps_2_0
}
\endcode
Some notes:
- There is no entry_point property because the entry point is always \a main.
- Both profiles_cg and profiles_hlsl are a list of shader profiles. The first profile that is supported is automatically picked. GLSL does not have shader profiles.
Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'.
Make sure to also copy the 'core.h' file to the same location. It is included in shiny's source tree under 'Extra/'.
Important: a newline at the end of the file is <b>required</b>. Many editors do this automatically or can be configured to do so. If there is no newline at the end of the file, and the last line is '#endif', you will get the rather cryptic error message " ill formed preprocessor directive: #endif" from boost::wave.
\code
#include "core.h"
#ifdef SH_VERTEX_SHADER
SH_BEGIN_PROGRAM
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
shVertexInput(float2, uv0)
shOutput(float2, UV)
SH_START_PROGRAM
{
shOutputPosition = shMatrixMult(wvp, shInputPosition);
UV = uv0;
}
#else
SH_BEGIN_PROGRAM
// NOTE: It is important that the sampler name here (diffuseMap) matches
// the name of the texture unit in the material. This is necessary because the system
// skips texture units that are never "referenced" in the shader. This can be the case
// when your base material has optional assets (for example a normal map) that are not
// used by some derived materials.
shSampler2D(diffuseMap)
shInput(float2, UV)
SH_START_PROGRAM
{
shOutputColour(0) = shSample(diffuseMap, UV);
}
#endif
\endcode
There you have it! This shader will compile in several languages thanks to the porting defines in "core.h". If you need more defines, feel free to add them and don't forget to send them to me!
For a full list of macros available when writing your shaders, refer to the page \ref macros
In the future, some more in-depth shader examples might follow.
*/

195
extern/shiny/Editor/Actions.cpp vendored Normal file
View file

@ -0,0 +1,195 @@
#include "Actions.hpp"
#include "../Main/Factory.hpp"
namespace sh
{
void ActionDeleteMaterial::execute()
{
sh::Factory::getInstance().destroyMaterialInstance(mName);
}
void ActionCloneMaterial::execute()
{
sh::MaterialInstance* sourceMaterial = sh::Factory::getInstance().getMaterialInstance(mSourceName);
std::string sourceMaterialParent = static_cast<sh::MaterialInstance*>(sourceMaterial->getParent())->getName();
sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance(
mDestName, sourceMaterialParent);
sourceMaterial->copyAll(material, sourceMaterial, false);
material->setSourceFile(sourceMaterial->getSourceFile());
}
void ActionSaveAll::execute()
{
sh::Factory::getInstance().saveAll();
}
void ActionChangeGlobalSetting::execute()
{
sh::Factory::getInstance().setGlobalSetting(mName, mNewValue);
}
void ActionCreateConfiguration::execute()
{
sh::Configuration newConfiguration;
sh::Factory::getInstance().createConfiguration(mName);
}
void ActionDeleteConfiguration::execute()
{
sh::Factory::getInstance().destroyConfiguration(mName);
}
void ActionChangeConfiguration::execute()
{
sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName);
c->setProperty(mKey, sh::makeProperty(new sh::StringValue(mValue)));
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeleteConfigurationProperty::execute()
{
sh::Configuration* c = sh::Factory::getInstance().getConfiguration(mName);
c->deleteProperty(mKey);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionSetMaterialProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
m->setProperty(mKey, sh::makeProperty(mValue));
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeleteMaterialProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
m->deleteProperty(mKey);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionCreatePass::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
m->createPass();
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeletePass::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
m->deletePass(mPassIndex);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionSetPassProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
m->getPasses()->at(mPassIndex).setProperty (mKey, sh::makeProperty(mValue));
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeletePassProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
m->getPasses()->at(mPassIndex).deleteProperty(mKey);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionSetShaderProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
m->getPasses()->at(mPassIndex).mShaderProperties.setProperty (mKey, sh::makeProperty(mValue));
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeleteShaderProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
m->getPasses()->at(mPassIndex).mShaderProperties.deleteProperty (mKey);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionSetTextureProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex);
m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).setProperty(mKey, sh::makeProperty(mValue));
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeleteTextureProperty::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex);
m->getPasses()->at(mPassIndex).mTexUnits.at(mTextureIndex).deleteProperty(mKey);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionCreateTextureUnit::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
m->getPasses()->at(mPassIndex).createTextureUnit(mTexUnitName);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionDeleteTextureUnit::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex);
m->getPasses()->at(mPassIndex).mTexUnits.erase(m->getPasses()->at(mPassIndex).mTexUnits.begin() + mTextureIndex);
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionMoveTextureUnit::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex);
if (!mMoveUp)
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex+1);
std::vector<MaterialInstanceTextureUnit> textures = m->getPasses()->at(mPassIndex).mTexUnits;
if (mMoveUp)
std::swap(textures[mTextureIndex-1], textures[mTextureIndex]);
else
std::swap(textures[mTextureIndex+1], textures[mTextureIndex]);
m->getPasses()->at(mPassIndex).mTexUnits = textures;
sh::Factory::getInstance().notifyConfigurationChanged();
}
void ActionChangeTextureUnitName::execute()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
assert (m->getPasses()->size() > mPassIndex);
assert (m->getPasses()->at(mPassIndex).mTexUnits.size() > mTextureIndex);
m->getPasses()->at(mPassIndex).mTexUnits[mTextureIndex].setName(mTexUnitName);
sh::Factory::getInstance().notifyConfigurationChanged();
}
}

307
extern/shiny/Editor/Actions.hpp vendored Normal file
View file

@ -0,0 +1,307 @@
#ifndef SH_ACTIONS_H
#define SH_ACTIONS_H
#include <string>
namespace sh
{
class Action
{
public:
virtual void execute() = 0;
virtual ~Action() {}
};
class ActionDeleteMaterial : public Action
{
public:
ActionDeleteMaterial(const std::string& name)
: mName(name) {}
virtual void execute();
private:
std::string mName;
};
class ActionCloneMaterial : public Action
{
public:
ActionCloneMaterial(const std::string& sourceName, const std::string& destName)
: mSourceName(sourceName), mDestName(destName) {}
virtual void execute();
private:
std::string mSourceName;
std::string mDestName;
};
class ActionSaveAll : public Action
{
public:
virtual void execute();
};
class ActionChangeGlobalSetting : public Action
{
public:
ActionChangeGlobalSetting(const std::string& name, const std::string& newValue)
: mName(name), mNewValue(newValue) {}
virtual void execute();
private:
std::string mName;
std::string mNewValue;
};
// configuration
class ActionCreateConfiguration : public Action
{
public:
ActionCreateConfiguration(const std::string& name)
: mName(name) {}
virtual void execute();
private:
std::string mName;
};
class ActionDeleteConfiguration : public Action
{
public:
ActionDeleteConfiguration(const std::string& name)
: mName(name) {}
virtual void execute();
private:
std::string mName;
};
class ActionChangeConfiguration : public Action
{
public:
ActionChangeConfiguration (const std::string& name, const std::string& key, const std::string& value)
: mName(name), mKey(key), mValue(value) {}
virtual void execute();
private:
std::string mName;
std::string mKey;
std::string mValue;
};
class ActionDeleteConfigurationProperty : public Action
{
public:
ActionDeleteConfigurationProperty (const std::string& name, const std::string& key)
: mName(name), mKey(key) {}
virtual void execute();
private:
std::string mName;
std::string mKey;
};
// material
class ActionSetMaterialProperty : public Action
{
public:
ActionSetMaterialProperty (const std::string& name, const std::string& key, const std::string& value)
: mName(name), mKey(key), mValue(value) {}
virtual void execute();
private:
std::string mName;
std::string mKey;
std::string mValue;
};
class ActionDeleteMaterialProperty : public Action
{
public:
ActionDeleteMaterialProperty (const std::string& name, const std::string& key)
: mName(name), mKey(key) {}
virtual void execute();
private:
std::string mName;
std::string mKey;
};
// pass
class ActionCreatePass : public Action
{
public:
ActionCreatePass (const std::string& name)
: mName(name) {}
virtual void execute();
private:
std::string mName;
};
class ActionDeletePass : public Action
{
public:
ActionDeletePass (const std::string& name, int passIndex)
: mName(name), mPassIndex(passIndex) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
};
class ActionSetPassProperty : public Action
{
public:
ActionSetPassProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value)
: mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
std::string mKey;
std::string mValue;
};
class ActionDeletePassProperty : public Action
{
public:
ActionDeletePassProperty (const std::string& name, int passIndex, const std::string& key)
: mName(name), mPassIndex(passIndex), mKey(key) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
std::string mKey;
};
// shader
class ActionSetShaderProperty : public Action
{
public:
ActionSetShaderProperty (const std::string& name, int passIndex, const std::string& key, const std::string& value)
: mName(name), mPassIndex(passIndex), mKey(key), mValue(value) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
std::string mKey;
std::string mValue;
};
class ActionDeleteShaderProperty : public Action
{
public:
ActionDeleteShaderProperty (const std::string& name, int passIndex, const std::string& key)
: mName(name), mPassIndex(passIndex), mKey(key) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
std::string mKey;
};
// texture unit
class ActionChangeTextureUnitName : public Action
{
public:
ActionChangeTextureUnitName (const std::string& name, int passIndex, int textureIndex, const std::string& texUnitName)
: mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mTexUnitName(texUnitName) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
int mTextureIndex;
std::string mTexUnitName;
};
class ActionCreateTextureUnit : public Action
{
public:
ActionCreateTextureUnit (const std::string& name, int passIndex, const std::string& texUnitName)
: mName(name), mPassIndex(passIndex), mTexUnitName(texUnitName) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
std::string mTexUnitName;
};
class ActionDeleteTextureUnit : public Action
{
public:
ActionDeleteTextureUnit (const std::string& name, int passIndex, int textureIndex)
: mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
int mTextureIndex;
};
class ActionMoveTextureUnit : public Action
{
public:
ActionMoveTextureUnit (const std::string& name, int passIndex, int textureIndex, bool moveUp)
: mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mMoveUp(moveUp) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
int mTextureIndex;
bool mMoveUp;
};
class ActionSetTextureProperty : public Action
{
public:
ActionSetTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key, const std::string& value)
: mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key), mValue(value) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
int mTextureIndex;
std::string mKey;
std::string mValue;
};
class ActionDeleteTextureProperty : public Action
{
public:
ActionDeleteTextureProperty (const std::string& name, int passIndex, int textureIndex, const std::string& key)
: mName(name), mPassIndex(passIndex), mTextureIndex(textureIndex), mKey(key) {}
virtual void execute();
private:
std::string mName;
int mPassIndex;
int mTextureIndex;
std::string mKey;
};
}
#endif

View file

@ -0,0 +1,31 @@
#include "AddPropertyDialog.hpp"
#include "ui_addpropertydialog.h"
AddPropertyDialog::AddPropertyDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::AddPropertyDialog)
, mType(0)
{
ui->setupUi(this);
connect(ui->buttonBox, SIGNAL(accepted()),
this, SLOT(accepted()));
connect(ui->buttonBox, SIGNAL(rejected()),
this, SLOT(rejected()));
}
void AddPropertyDialog::accepted()
{
mName = ui->lineEdit->text();
mType = ui->comboBox->currentIndex();
}
void AddPropertyDialog::rejected()
{
mName = "";
}
AddPropertyDialog::~AddPropertyDialog()
{
delete ui;
}

22
extern/shiny/Editor/AddPropertyDialog.h vendored Normal file
View file

@ -0,0 +1,22 @@
#ifndef ADDPROPERTYDIALOG_H
#define ADDPROPERTYDIALOG_H
#include <QDialog>
namespace Ui {
class AddPropertyDialog;
}
class AddPropertyDialog : public QDialog
{
Q_OBJECT
public:
explicit AddPropertyDialog(QWidget *parent = 0);
~AddPropertyDialog();
private:
Ui::AddPropertyDialog *ui;
};
#endif // ADDPROPERTYDIALOG_H

View file

@ -0,0 +1,29 @@
#ifndef ADDPROPERTYDIALOG_H
#define ADDPROPERTYDIALOG_H
#include <QDialog>
namespace Ui {
class AddPropertyDialog;
}
class AddPropertyDialog : public QDialog
{
Q_OBJECT
public:
explicit AddPropertyDialog(QWidget *parent = 0);
~AddPropertyDialog();
int mType;
QString mName;
public slots:
void accepted();
void rejected();
private:
Ui::AddPropertyDialog *ui;
};
#endif // ADDPROPERTYDIALOG_H

61
extern/shiny/Editor/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,61 @@
set(SHINY_EDITOR_LIBRARY "shiny.Editor")
find_package(Qt4)
if (QT_FOUND)
add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=1)
set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=1 PARENT_SCOPE)
set(QT_USE_QTGUI 1)
# Headers that must be preprocessed
set(SHINY_EDITOR_HEADER_MOC
MainWindow.hpp
NewMaterialDialog.hpp
AddPropertyDialog.hpp
PropertySortModel.hpp
)
set(SHINY_EDITOR_UI
mainwindow.ui
newmaterialdialog.ui
addpropertydialog.ui
)
QT4_WRAP_CPP(MOC_SRCS ${SHINY_EDITOR_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${SHINY_EDITOR_UI})
set(SOURCE_FILES
NewMaterialDialog.cpp
AddPropertyDialog.cpp
ColoredTabWidget.hpp
MainWindow.cpp
Editor.cpp
Actions.cpp
Query.cpp
PropertySortModel.cpp
${SHINY_EDITOR_UI} # Just to have them in the IDE's file explorer
)
include(${QT_USE_FILE})
set (CMAKE_INCLUDE_CURRENT_DIR "true")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(${SHINY_EDITOR_LIBRARY} STATIC ${SOURCE_FILES} ${MOC_SRCS} ${UI_HDRS})
set(SHINY_LIBRARIES ${SHINY_LIBRARIES}
${SHINY_EDITOR_LIBRARY}
${QT_LIBRARIES}
)
set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE)
else (QT_FOUND)
add_definitions(-DSHINY_BUILD_MATERIAL_EDITOR=0)
set (SHINY_BUILD_EDITOR_FLAG -DSHINY_BUILD_MATERIAL_EDITOR=0 PARENT_SCOPE)
message(STATUS "QT4 was not found. You will not be able to use the material editor.")
endif(QT_FOUND)

View file

@ -0,0 +1,24 @@
#ifndef SHINY_EDITOR_COLOREDTABWIDGET_H
#define SHINY_EDITOR_COLOREDTABWIDGET_H
#include <QTabWidget>
namespace sh
{
/// Makes tabBar() public to allow changing tab title colors.
class ColoredTabWidget : public QTabWidget
{
public:
ColoredTabWidget(QWidget* parent = 0)
: QTabWidget(parent) {}
QTabBar* tabBar()
{
return QTabWidget::tabBar();
}
};
}
#endif

117
extern/shiny/Editor/Editor.cpp vendored Normal file
View file

@ -0,0 +1,117 @@
#include "Editor.hpp"
#include <QApplication>
#include <QTimer>
#include <boost/thread.hpp>
#include "../Main/Factory.hpp"
#include "MainWindow.hpp"
namespace sh
{
Editor::Editor()
: mMainWindow(NULL)
, mApplication(NULL)
, mInitialized(false)
, mThread(NULL)
{
}
Editor::~Editor()
{
if (mMainWindow)
mMainWindow->mRequestExit = true;
if (mThread)
mThread->join();
delete mThread;
}
void Editor::show()
{
if (!mInitialized)
{
mInitialized = true;
mThread = new boost::thread(boost::bind(&Editor::runThread, this));
}
else
{
if (mMainWindow)
mMainWindow->mRequestShowWindow = true;
}
}
void Editor::runThread()
{
int argc = 0;
char** argv = NULL;
mApplication = new QApplication(argc, argv);
mApplication->setQuitOnLastWindowClosed(false);
mMainWindow = new MainWindow();
mMainWindow->mSync = &mSync;
mMainWindow->show();
mApplication->exec();
delete mApplication;
}
void Editor::update()
{
sh::Factory::getInstance().doMonitorShaderFiles();
if (!mMainWindow)
return;
{
boost::mutex::scoped_lock lock(mSync.mActionMutex);
// execute pending actions
while (mMainWindow->mActionQueue.size())
{
Action* action = mMainWindow->mActionQueue.front();
action->execute();
delete action;
mMainWindow->mActionQueue.pop();
}
}
{
boost::mutex::scoped_lock lock(mSync.mQueryMutex);
// execute pending queries
for (std::vector<Query*>::iterator it = mMainWindow->mQueries.begin(); it != mMainWindow->mQueries.end(); ++it)
{
Query* query = *it;
if (!query->mDone)
query->execute();
}
}
boost::mutex::scoped_lock lock2(mSync.mUpdateMutex);
// update the list of materials
mMainWindow->mState.mMaterialList.clear();
sh::Factory::getInstance().listMaterials(mMainWindow->mState.mMaterialList);
// update global settings
mMainWindow->mState.mGlobalSettingsMap.clear();
sh::Factory::getInstance().listGlobalSettings(mMainWindow->mState.mGlobalSettingsMap);
// update configuration list
mMainWindow->mState.mConfigurationList.clear();
sh::Factory::getInstance().listConfigurationNames(mMainWindow->mState.mConfigurationList);
// update shader list
mMainWindow->mState.mShaderSets.clear();
sh::Factory::getInstance().listShaderSets(mMainWindow->mState.mShaderSets);
mMainWindow->mState.mErrors += sh::Factory::getInstance().getErrorLog();
}
}

73
extern/shiny/Editor/Editor.hpp vendored Normal file
View file

@ -0,0 +1,73 @@
#ifndef SH_EDITOR_H
#define SH_EDITOR_H
#if SHINY_BUILD_MATERIAL_EDITOR
class QApplication;
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>
namespace boost
{
class thread;
}
namespace sh
{
class MainWindow;
struct SynchronizationState
{
boost::mutex mUpdateMutex;
boost::mutex mActionMutex;
boost::mutex mQueryMutex;
};
class Editor
{
public:
Editor();
~Editor();
void show();
void update();
private:
bool mInitialized;
MainWindow* mMainWindow;
QApplication* mApplication;
SynchronizationState mSync;
boost::thread* mThread;
void runThread();
void processShowWindow();
};
}
#else
// Dummy implementation, so that the user's code does not have to be polluted with #ifdefs
namespace sh
{
class Editor
{
public:
Editor() {}
~Editor() {}
void show() {}
void update() {}
};
}
#endif
#endif

952
extern/shiny/Editor/MainWindow.cpp vendored Normal file
View file

@ -0,0 +1,952 @@
#include "MainWindow.hpp"
#include "ui_mainwindow.h"
#include <iostream>
#include <QCloseEvent>
#include <QTimer>
#include <QInputDialog>
#include <QMessageBox>
#include "Editor.hpp"
#include "ColoredTabWidget.hpp"
#include "AddPropertyDialog.hpp"
sh::MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, mRequestShowWindow(false)
, mRequestExit(false)
, mIgnoreGlobalSettingChange(false)
, mIgnoreConfigurationChange(false)
, mIgnoreMaterialChange(false)
, mIgnoreMaterialPropertyChange(false)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(onIdle()));
timer->start(50);
QList<int> sizes;
sizes << 250;
sizes << 550;
ui->splitter->setSizes(sizes);
mMaterialModel = new QStringListModel(this);
mMaterialProxyModel = new QSortFilterProxyModel(this);
mMaterialProxyModel->setSourceModel(mMaterialModel);
mMaterialProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mMaterialProxyModel->setDynamicSortFilter(true);
mMaterialProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
ui->materialList->setModel(mMaterialProxyModel);
ui->materialList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->materialList->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->materialList->setAlternatingRowColors(true);
connect(ui->materialList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(onMaterialSelectionChanged(QModelIndex,QModelIndex)));
mMaterialPropertyModel = new QStandardItemModel(0, 2, this);
mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
connect(mMaterialPropertyModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(onMaterialPropertyChanged(QStandardItem*)));
mMaterialSortModel = new PropertySortModel(this);
mMaterialSortModel->setSourceModel(mMaterialPropertyModel);
mMaterialSortModel->setDynamicSortFilter(true);
mMaterialSortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
ui->materialView->setModel(mMaterialSortModel);
ui->materialView->setContextMenuPolicy(Qt::CustomContextMenu);
ui->materialView->setAlternatingRowColors(true);
ui->materialView->setSortingEnabled(true);
connect(ui->materialView, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(onContextMenuRequested(QPoint)));
mGlobalSettingsModel = new QStandardItemModel(0, 2, this);
mGlobalSettingsModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
mGlobalSettingsModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
connect(mGlobalSettingsModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(onGlobalSettingChanged(QStandardItem*)));
ui->globalSettingsView->setModel(mGlobalSettingsModel);
ui->globalSettingsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
ui->globalSettingsView->verticalHeader()->hide();
ui->globalSettingsView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
ui->globalSettingsView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->configurationList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->configurationList->setEditTriggers(QAbstractItemView::NoEditTriggers);
connect(ui->configurationList, SIGNAL(currentTextChanged(QString)),
this, SLOT(onConfigurationSelectionChanged(QString)));
mConfigurationModel = new QStandardItemModel(0, 2, this);
mConfigurationModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
mConfigurationModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
connect(mConfigurationModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(onConfigurationChanged(QStandardItem*)));
ui->configurationView->setModel(mConfigurationModel);
ui->configurationView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
ui->configurationView->verticalHeader()->hide();
ui->configurationView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
ui->configurationView->setSelectionMode(QAbstractItemView::SingleSelection);
}
sh::MainWindow::~MainWindow()
{
delete ui;
}
void sh::MainWindow::closeEvent(QCloseEvent *event)
{
this->hide();
event->ignore();
}
void sh::MainWindow::onIdle()
{
if (mRequestShowWindow)
{
mRequestShowWindow = false;
show();
}
if (mRequestExit)
{
QApplication::exit();
return;
}
boost::mutex::scoped_lock lock(mSync->mUpdateMutex);
mIgnoreMaterialChange = true;
QString selected;
QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
if (selectedIndex.isValid())
selected = mMaterialModel->data(selectedIndex, Qt::DisplayRole).toString();
QStringList list;
for (std::vector<std::string>::const_iterator it = mState.mMaterialList.begin(); it != mState.mMaterialList.end(); ++it)
{
list.push_back(QString::fromStdString(*it));
}
if (mMaterialModel->stringList() != list)
{
mMaterialModel->setStringList(list);
// quick hack to keep our selection when the model has changed
if (!selected.isEmpty())
for (int i=0; i<mMaterialModel->rowCount(); ++i)
{
const QModelIndex& index = mMaterialModel->index(i,0);
if (mMaterialModel->data(index, Qt::DisplayRole).toString() == selected)
{
ui->materialList->setCurrentIndex(index);
break;
}
}
}
mIgnoreMaterialChange = false;
mIgnoreGlobalSettingChange = true;
for (std::map<std::string, std::string>::const_iterator it = mState.mGlobalSettingsMap.begin();
it != mState.mGlobalSettingsMap.end(); ++it)
{
QList<QStandardItem *> list = mGlobalSettingsModel->findItems(QString::fromStdString(it->first));
if (!list.empty()) // item was already there
{
// if it changed, set the value column
if (mGlobalSettingsModel->data(mGlobalSettingsModel->index(list.front()->row(), 1)).toString()
!= QString::fromStdString(it->second))
{
mGlobalSettingsModel->setItem(list.front()->row(), 1, new QStandardItem(QString::fromStdString(it->second)));
}
}
else // item wasn't there; insert new row
{
QList<QStandardItem*> toAdd;
QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
toAdd.push_back(name);
toAdd.push_back(value);
mGlobalSettingsModel->appendRow(toAdd);
}
}
mIgnoreGlobalSettingChange = false;
mIgnoreConfigurationChange = true;
QList<QListWidgetItem*> selected_ = ui->configurationList->selectedItems();
QString selectedStr;
if (selected_.size())
selectedStr = selected_.front()->text();
ui->configurationList->clear();
for (std::vector<std::string>::const_iterator it = mState.mConfigurationList.begin(); it != mState.mConfigurationList.end(); ++it)
ui->configurationList->addItem(QString::fromStdString(*it));
if (!selectedStr.isEmpty())
for (int i=0; i<ui->configurationList->count(); ++i)
{
if (ui->configurationList->item(i)->text() == selectedStr)
{
ui->configurationList->setCurrentItem(ui->configurationList->item(i), QItemSelectionModel::ClearAndSelect);
}
}
mIgnoreConfigurationChange = false;
if (!mState.mErrors.empty())
{
ui->errorLog->append(QString::fromStdString(mState.mErrors));
mState.mErrors = "";
QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::Link);
ui->tabWidget->tabBar()->setTabTextColor(3, color);
}
// process query results
boost::mutex::scoped_lock lock2(mSync->mQueryMutex);
for (std::vector<Query*>::iterator it = mQueries.begin(); it != mQueries.end();)
{
if ((*it)->mDone)
{
if (typeid(**it) == typeid(ConfigurationQuery))
buildConfigurationModel(static_cast<ConfigurationQuery*>(*it));
else if (typeid(**it) == typeid(MaterialQuery))
buildMaterialModel(static_cast<MaterialQuery*>(*it));
else if (typeid(**it) == typeid(MaterialPropertyQuery))
{
MaterialPropertyQuery* q = static_cast<MaterialPropertyQuery*>(*it);
mIgnoreMaterialPropertyChange = true;
if (getSelectedMaterial().toStdString() == q->mName)
{
for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
{
if (mMaterialPropertyModel->item(i,0)->text() == QString::fromStdString(q->mPropertyName))
{
mMaterialPropertyModel->item(i,1)->setText(QString::fromStdString(q->mValue));
if (mMaterialPropertyModel->item(i,1)->isCheckable())
mMaterialPropertyModel->item(i,1)->setCheckState ((q->mValue == "true")
? Qt::Checked : Qt::Unchecked);
}
}
}
mIgnoreMaterialPropertyChange = false;
}
delete *it;
it = mQueries.erase(it);
}
else
++it;
}
}
void sh::MainWindow::onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous)
{
if (mIgnoreMaterialChange)
return;
QString name = getSelectedMaterial();
if (!name.isEmpty())
requestQuery(new sh::MaterialQuery(name.toStdString()));
}
QString sh::MainWindow::getSelectedMaterial()
{
QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
if (!selectedIndex.isValid())
return QString("");
return mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();
}
void sh::MainWindow::onConfigurationSelectionChanged (const QString& current)
{
if (mIgnoreConfigurationChange)
return;
requestQuery(new sh::ConfigurationQuery(current.toStdString()));
}
void sh::MainWindow::onGlobalSettingChanged(QStandardItem *item)
{
if (mIgnoreGlobalSettingChange)
return; // we are only interested in changes by the user, not by the backend.
std::string name = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 0)).toString().toStdString();
std::string value = mGlobalSettingsModel->data(mGlobalSettingsModel->index(item->row(), 1)).toString().toStdString();
queueAction(new sh::ActionChangeGlobalSetting(name, value));
}
void sh::MainWindow::onConfigurationChanged (QStandardItem* item)
{
QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
if (items.size())
{
std::string name = items.front()->text().toStdString();
std::string key = mConfigurationModel->data(mConfigurationModel->index(item->row(), 0)).toString().toStdString();
std::string value = mConfigurationModel->data(mConfigurationModel->index(item->row(), 1)).toString().toStdString();
queueAction(new sh::ActionChangeConfiguration(name, key, value));
requestQuery(new sh::ConfigurationQuery(name));
}
}
void sh::MainWindow::on_lineEdit_textEdited(const QString &arg1)
{
mMaterialProxyModel->setFilterFixedString(arg1);
}
void sh::MainWindow::on_actionSave_triggered()
{
queueAction (new sh::ActionSaveAll());
}
void sh::MainWindow::on_actionNewMaterial_triggered()
{
}
void sh::MainWindow::on_actionDeleteMaterial_triggered()
{
QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();
queueAction (new sh::ActionDeleteMaterial(name.toStdString()));
}
void sh::MainWindow::queueAction(Action* action)
{
boost::mutex::scoped_lock lock(mSync->mActionMutex);
mActionQueue.push(action);
}
void sh::MainWindow::requestQuery(Query *query)
{
boost::mutex::scoped_lock lock(mSync->mActionMutex);
mQueries.push_back(query);
}
void sh::MainWindow::on_actionQuit_triggered()
{
hide();
}
void sh::MainWindow::on_actionNewConfiguration_triggered()
{
QInputDialog dialog(this);
QString text = QInputDialog::getText(this, tr("New Configuration"),
tr("Configuration name:"));
if (!text.isEmpty())
{
queueAction(new ActionCreateConfiguration(text.toStdString()));
}
}
void sh::MainWindow::on_actionDeleteConfiguration_triggered()
{
QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
if (items.size())
queueAction(new ActionDeleteConfiguration(items.front()->text().toStdString()));
}
void sh::MainWindow::on_actionDeleteConfigurationProperty_triggered()
{
QList<QListWidgetItem*> items = ui->configurationList->selectedItems();
if (items.empty())
return;
std::string configurationName = items.front()->text().toStdString();
QModelIndex current = ui->configurationView->currentIndex();
if (!current.isValid())
return;
std::string propertyName = mConfigurationModel->data(mConfigurationModel->index(current.row(), 0)).toString().toStdString();
queueAction(new sh::ActionDeleteConfigurationProperty(configurationName, propertyName));
requestQuery(new sh::ConfigurationQuery(configurationName));
}
void sh::MainWindow::on_actionCloneMaterial_triggered()
{
QModelIndex selectedIndex = ui->materialList->selectionModel()->currentIndex();
QString name = mMaterialProxyModel->data(selectedIndex, Qt::DisplayRole).toString();
if (name.isEmpty())
return;
QInputDialog dialog(this);
QString text = QInputDialog::getText(this, tr("Clone material"),
tr("Name:"));
if (!text.isEmpty())
{
queueAction(new ActionCloneMaterial(name.toStdString(), text.toStdString()));
}
}
void sh::MainWindow::onContextMenuRequested(const QPoint &point)
{
QPoint globalPos = ui->materialView->viewport()->mapToGlobal(point);
QMenu menu;
QList <QAction*> actions;
actions.push_back(ui->actionNewProperty);
actions.push_back(ui->actionDeleteProperty);
actions.push_back(ui->actionCreatePass);
actions.push_back(ui->actionCreateTextureUnit);
menu.addActions(actions);
menu.exec(globalPos);
}
void sh::MainWindow::getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass, bool* isInTextureUnit)
{
if (passIndex)
{
*passIndex = 0;
if (isInPass)
*isInPass = false;
QModelIndex passModelIndex = index;
// go up until we find the pass item.
while (getPropertyKey(passModelIndex) != "pass" && passModelIndex.isValid())
passModelIndex = passModelIndex.parent();
if (passModelIndex.isValid())
{
if (passModelIndex.column() != 0)
passModelIndex = passModelIndex.parent().child(passModelIndex.row(), 0);
for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
{
if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass"))
{
if (mMaterialPropertyModel->index(i, 0) == passModelIndex)
{
if (isInPass)
*isInPass = true;
break;
}
++(*passIndex);
}
}
}
}
if (textureIndex)
{
*textureIndex = 0;
if (isInTextureUnit)
*isInTextureUnit = false;
QModelIndex texModelIndex = index;
// go up until we find the texture_unit item.
while (getPropertyKey(texModelIndex) != "texture_unit" && texModelIndex.isValid())
texModelIndex = texModelIndex.parent();
if (texModelIndex.isValid())
{
if (texModelIndex.column() != 0)
texModelIndex = texModelIndex.parent().child(texModelIndex.row(), 0);
for (int i=0; i<mMaterialPropertyModel->rowCount(texModelIndex.parent()); ++i)
{
if (texModelIndex.parent().child(i, 0).data().toString() == QString("texture_unit"))
{
if (texModelIndex.parent().child(i, 0) == texModelIndex)
{
if (isInTextureUnit)
*isInTextureUnit = true;
break;
}
++(*textureIndex);
}
}
}
}
}
std::string sh::MainWindow::getPropertyKey(QModelIndex index)
{
if (!index.parent().isValid())
return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 0)).toString().toStdString();
else
return index.parent().child(index.row(), 0).data().toString().toStdString();
}
std::string sh::MainWindow::getPropertyValue(QModelIndex index)
{
if (!index.parent().isValid())
return mMaterialPropertyModel->data(mMaterialPropertyModel->index(index.row(), 1)).toString().toStdString();
else
return index.parent().child(index.row(), 1).data().toString().toStdString();
}
void sh::MainWindow::onMaterialPropertyChanged(QStandardItem *item)
{
if (mIgnoreMaterialPropertyChange)
return;
QString material = getSelectedMaterial();
if (material.isEmpty())
return;
// handle checkboxes being checked/unchecked
std::string value = getPropertyValue(item->index());
if (item->data(Qt::UserRole).toInt() == MaterialProperty::Boolean)
{
if (item->checkState() == Qt::Checked && value != "true")
value = "true";
else if (item->checkState() == Qt::Unchecked && value == "true")
value = "false";
item->setText(QString::fromStdString(value));
}
// handle inherited properties being changed, i.e. overridden by the current (derived) material
if (item->data(Qt::UserRole+1).toInt() == MaterialProperty::Inherited_Unchanged)
{
QColor normalColor = ui->materialView->palette().color(QPalette::Normal, QPalette::WindowText);
mIgnoreMaterialPropertyChange = true;
mMaterialPropertyModel->item(item->index().row(), 0)
->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1);
mMaterialPropertyModel->item(item->index().row(), 0)
->setData(normalColor, Qt::ForegroundRole);
mMaterialPropertyModel->item(item->index().row(), 1)
->setData(QVariant(MaterialProperty::Inherited_Changed), Qt::UserRole+1);
mMaterialPropertyModel->item(item->index().row(), 1)
->setData(normalColor, Qt::ForegroundRole);
mIgnoreMaterialPropertyChange = false;
ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(item->index()));
}
if (!item->index().parent().isValid())
{
// top level material property
queueAction(new ActionSetMaterialProperty(
material.toStdString(), getPropertyKey(item->index()), value));
}
else if (getPropertyKey(item->index()) == "texture_unit")
{
// texture unit name changed
int passIndex, textureIndex;
getContext(item->index(), &passIndex, &textureIndex);
std::cout << "passIndex " << passIndex << " " << textureIndex << std::endl;
queueAction(new ActionChangeTextureUnitName(
material.toStdString(), passIndex, textureIndex, value));
}
else if (item->index().parent().data().toString() == "pass")
{
// pass property
int passIndex;
getContext(item->index(), &passIndex, NULL);
/// \todo if shaders are changed, check that the material provides all properties needed by the shader
queueAction(new ActionSetPassProperty(
material.toStdString(), passIndex, getPropertyKey(item->index()), value));
}
else if (item->index().parent().data().toString() == "shader_properties")
{
// shader property
int passIndex;
getContext(item->index(), &passIndex, NULL);
queueAction(new ActionSetShaderProperty(
material.toStdString(), passIndex, getPropertyKey(item->index()), value));
}
else if (item->index().parent().data().toString() == "texture_unit")
{
// texture property
int passIndex, textureIndex;
getContext(item->index(), &passIndex, &textureIndex);
queueAction(new ActionSetTextureProperty(
material.toStdString(), passIndex, textureIndex, getPropertyKey(item->index()), value));
}
}
void sh::MainWindow::buildMaterialModel(MaterialQuery *data)
{
mMaterialPropertyModel->clear();
mMaterialPropertyModel->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
mMaterialPropertyModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Value")));
for (std::map<std::string, MaterialProperty>::const_iterator it = data->mProperties.begin();
it != data->mProperties.end(); ++it)
{
addProperty(mMaterialPropertyModel->invisibleRootItem(), it->first, it->second);
}
for (std::vector<PassInfo>::iterator it = data->mPasses.begin();
it != data->mPasses.end(); ++it)
{
QStandardItem* passItem = new QStandardItem (QString("pass"));
passItem->setFlags(passItem->flags() &= ~Qt::ItemIsEditable);
passItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);
if (it->mShaderProperties.size())
{
QStandardItem* shaderPropertiesItem = new QStandardItem (QString("shader_properties"));
shaderPropertiesItem->setFlags(shaderPropertiesItem->flags() &= ~Qt::ItemIsEditable);
shaderPropertiesItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);
for (std::map<std::string, MaterialProperty>::iterator pit = it->mShaderProperties.begin();
pit != it->mShaderProperties.end(); ++pit)
{
addProperty(shaderPropertiesItem, pit->first, pit->second);
}
passItem->appendRow(shaderPropertiesItem);
}
for (std::map<std::string, MaterialProperty>::iterator pit = it->mProperties.begin();
pit != it->mProperties.end(); ++pit)
{
addProperty(passItem, pit->first, pit->second);
}
for (std::vector<TextureUnitInfo>::iterator tIt = it->mTextureUnits.begin();
tIt != it->mTextureUnits.end(); ++tIt)
{
QStandardItem* unitItem = new QStandardItem (QString("texture_unit"));
unitItem->setFlags(unitItem->flags() &= ~Qt::ItemIsEditable);
unitItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);
QStandardItem* nameItem = new QStandardItem (QString::fromStdString(tIt->mName));
nameItem->setData(QVariant(static_cast<int>(MaterialProperty::Object)), Qt::UserRole);
QList<QStandardItem*> texUnit;
texUnit << unitItem << nameItem;
for (std::map<std::string, MaterialProperty>::iterator pit = tIt->mProperties.begin();
pit != tIt->mProperties.end(); ++pit)
{
addProperty(unitItem, pit->first, pit->second);
}
passItem->appendRow(texUnit);
}
QList<QStandardItem*> toAdd;
toAdd << passItem;
toAdd << new QStandardItem(QString(""));
mMaterialPropertyModel->appendRow(toAdd);
}
ui->materialView->expandAll();
ui->materialView->resizeColumnToContents(0);
ui->materialView->resizeColumnToContents(1);
}
void sh::MainWindow::addProperty(QStandardItem *parent, const std::string &key, MaterialProperty value, bool scrollTo)
{
QList<QStandardItem*> toAdd;
QStandardItem* keyItem = new QStandardItem(QString::fromStdString(key));
keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable);
keyItem->setData(QVariant(value.mType), Qt::UserRole);
keyItem->setData(QVariant(value.mSource), Qt::UserRole+1);
toAdd.push_back(keyItem);
QStandardItem* valueItem = NULL;
if (value.mSource != MaterialProperty::None)
{
valueItem = new QStandardItem(QString::fromStdString(value.mValue));
valueItem->setData(QVariant(value.mType), Qt::UserRole);
valueItem->setData(QVariant(value.mSource), Qt::UserRole+1);
toAdd.push_back(valueItem);
}
if (value.mSource == MaterialProperty::Inherited_Unchanged)
{
QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText);
keyItem->setData(color, Qt::ForegroundRole);
if (valueItem)
valueItem->setData(color, Qt::ForegroundRole);
}
if (value.mType == MaterialProperty::Boolean && valueItem)
{
valueItem->setCheckable(true);
valueItem->setCheckState((value.mValue == "true") ? Qt::Checked : Qt::Unchecked);
}
parent->appendRow(toAdd);
if (scrollTo)
ui->materialView->scrollTo(mMaterialSortModel->mapFromSource(keyItem->index()));
}
void sh::MainWindow::buildConfigurationModel(ConfigurationQuery *data)
{
while (mConfigurationModel->rowCount())
mConfigurationModel->removeRow(0);
for (std::map<std::string, std::string>::iterator it = data->mProperties.begin();
it != data->mProperties.end(); ++it)
{
QList<QStandardItem*> toAdd;
QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
toAdd.push_back(name);
toAdd.push_back(value);
mConfigurationModel->appendRow(toAdd);
}
// add items that are in global settings, but not in this configuration (with a "inactive" color)
for (std::map<std::string, std::string>::const_iterator it = mState.mGlobalSettingsMap.begin();
it != mState.mGlobalSettingsMap.end(); ++it)
{
if (data->mProperties.find(it->first) == data->mProperties.end())
{
QColor color = ui->configurationView->palette().color(QPalette::Disabled, QPalette::WindowText);
QList<QStandardItem*> toAdd;
QStandardItem* name = new QStandardItem(QString::fromStdString(it->first));
name->setFlags(name->flags() &= ~Qt::ItemIsEditable);
name->setData(color, Qt::ForegroundRole);
QStandardItem* value = new QStandardItem(QString::fromStdString(it->second));
value->setData(color, Qt::ForegroundRole);
toAdd.push_back(name);
toAdd.push_back(value);
mConfigurationModel->appendRow(toAdd);
}
}
}
void sh::MainWindow::on_actionCreatePass_triggered()
{
QString material = getSelectedMaterial();
if (!material.isEmpty())
{
addProperty(mMaterialPropertyModel->invisibleRootItem(),
"pass", MaterialProperty("", MaterialProperty::Object, MaterialProperty::None), true);
queueAction (new ActionCreatePass(material.toStdString()));
}
}
void sh::MainWindow::on_actionDeleteProperty_triggered()
{
QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
QString material = getSelectedMaterial();
if (material.isEmpty())
return;
mIgnoreMaterialPropertyChange = true;
if (getPropertyKey(selectedIndex) == "pass")
{
// delete whole pass
int passIndex;
getContext(selectedIndex, &passIndex, NULL);
if (passIndex == 0)
{
QMessageBox msgBox;
msgBox.setText("The first pass can not be deleted.");
msgBox.exec();
}
else
{
queueAction(new ActionDeletePass(material.toStdString(), passIndex));
mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
}
}
else if (getPropertyKey(selectedIndex) == "texture_unit")
{
// delete whole texture unit
int passIndex, textureIndex;
getContext(selectedIndex, &passIndex, &textureIndex);
queueAction(new ActionDeleteTextureUnit(material.toStdString(), passIndex, textureIndex));
mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
}
else if (!selectedIndex.parent().isValid())
{
// top level material property
MaterialProperty::Source source = static_cast<MaterialProperty::Source>(
mMaterialPropertyModel->itemFromIndex(selectedIndex)->data(Qt::UserRole+1).toInt());
if (source == MaterialProperty::Inherited_Unchanged)
{
QMessageBox msgBox;
msgBox.setText("Inherited properties can not be deleted.");
msgBox.exec();
}
else
{
queueAction(new ActionDeleteMaterialProperty(
material.toStdString(), getPropertyKey(selectedIndex)));
std::cout << "source is " << source << std::endl;
if (source == MaterialProperty::Inherited_Changed)
{
QColor inactiveColor = ui->materialView->palette().color(QPalette::Disabled, QPalette::WindowText);
mMaterialPropertyModel->item(selectedIndex.row(), 0)
->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1);
mMaterialPropertyModel->item(selectedIndex.row(), 0)
->setData(inactiveColor, Qt::ForegroundRole);
mMaterialPropertyModel->item(selectedIndex.row(), 1)
->setData(QVariant(MaterialProperty::Inherited_Unchanged), Qt::UserRole+1);
mMaterialPropertyModel->item(selectedIndex.row(), 1)
->setData(inactiveColor, Qt::ForegroundRole);
// make sure to update the property's value
requestQuery(new sh::MaterialPropertyQuery(material.toStdString(), getPropertyKey(selectedIndex)));
}
else
mMaterialPropertyModel->removeRow(selectedIndex.row());
}
}
else if (selectedIndex.parent().data().toString() == "pass")
{
// pass property
int passIndex;
getContext(selectedIndex, &passIndex, NULL);
queueAction(new ActionDeletePassProperty(
material.toStdString(), passIndex, getPropertyKey(selectedIndex)));
mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
}
else if (selectedIndex.parent().data().toString() == "shader_properties")
{
// shader property
int passIndex;
getContext(selectedIndex, &passIndex, NULL);
queueAction(new ActionDeleteShaderProperty(
material.toStdString(), passIndex, getPropertyKey(selectedIndex)));
mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
}
else if (selectedIndex.parent().data().toString() == "texture_unit")
{
// texture property
int passIndex, textureIndex;
getContext(selectedIndex, &passIndex, &textureIndex);
queueAction(new ActionDeleteTextureProperty(
material.toStdString(), passIndex, textureIndex, getPropertyKey(selectedIndex)));
mMaterialPropertyModel->removeRow(selectedIndex.row(), selectedIndex.parent());
}
mIgnoreMaterialPropertyChange = false;
}
void sh::MainWindow::on_actionNewProperty_triggered()
{
QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
QString material = getSelectedMaterial();
if (material.isEmpty())
return;
AddPropertyDialog* dialog = new AddPropertyDialog(this);
dialog->exec();
QString propertyName = dialog->mName;
QString defaultValue = "";
/// \todo check if this property name exists already
if (!propertyName.isEmpty())
{
int passIndex, textureIndex;
bool isInPass, isInTextureUnit;
getContext(selectedIndex, &passIndex, &textureIndex, &isInPass, &isInTextureUnit);
QList<QStandardItem*> items;
QStandardItem* keyItem = new QStandardItem(propertyName);
keyItem->setFlags(keyItem->flags() &= ~Qt::ItemIsEditable);
items << keyItem;
items << new QStandardItem(defaultValue);
// figure out which item the new property should be a child of
QModelIndex parentIndex = selectedIndex;
if (selectedIndex.data(Qt::UserRole) != MaterialProperty::Object)
parentIndex = selectedIndex.parent();
QStandardItem* parentItem;
if (!parentIndex.isValid())
parentItem = mMaterialPropertyModel->invisibleRootItem();
else
parentItem = mMaterialPropertyModel->itemFromIndex(parentIndex);
if (isInTextureUnit)
{
queueAction(new ActionSetTextureProperty(
material.toStdString(), passIndex, textureIndex, propertyName.toStdString(), defaultValue.toStdString()));
}
else if (isInPass)
{
if (selectedIndex.parent().child(selectedIndex.row(),0).data().toString() == "shader_properties"
|| selectedIndex.parent().data().toString() == "shader_properties")
{
queueAction(new ActionSetShaderProperty(
material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString()));
}
else
{
queueAction(new ActionSetPassProperty(
material.toStdString(), passIndex, propertyName.toStdString(), defaultValue.toStdString()));
}
}
else
{
queueAction(new ActionSetMaterialProperty(
material.toStdString(), propertyName.toStdString(), defaultValue.toStdString()));
}
addProperty(parentItem, propertyName.toStdString(),
MaterialProperty (defaultValue.toStdString(), MaterialProperty::Misc, MaterialProperty::Normal), true);
/// \todo scroll to newly added property
}
}
void sh::MainWindow::on_actionCreateTextureUnit_triggered()
{
QString material = getSelectedMaterial();
if (material.isEmpty())
return;
QInputDialog dialog(this);
QString text = QInputDialog::getText(this, tr("New texture unit"),
tr("Texture unit name (for referencing in shaders):"));
if (!text.isEmpty())
{
QModelIndex selectedIndex = mMaterialSortModel->mapToSource(ui->materialView->selectionModel()->currentIndex());
int passIndex;
getContext(selectedIndex, &passIndex, NULL);
queueAction(new ActionCreateTextureUnit(material.toStdString(), passIndex, text.toStdString()));
// add to model
int index = 0;
for (int i=0; i<mMaterialPropertyModel->rowCount(); ++i)
{
if (mMaterialPropertyModel->data(mMaterialPropertyModel->index(i, 0)).toString() == QString("pass"))
{
if (index == passIndex)
{
addProperty(mMaterialPropertyModel->itemFromIndex(mMaterialPropertyModel->index(i, 0)),
"texture_unit", MaterialProperty(text.toStdString(), MaterialProperty::Object), true);
break;
}
++index;
}
}
}
}
void sh::MainWindow::on_clearButton_clicked()
{
ui->errorLog->clear();
}
void sh::MainWindow::on_tabWidget_currentChanged(int index)
{
QColor color = ui->tabWidget->palette().color(QPalette::Normal, QPalette::WindowText);
if (index == 3)
ui->tabWidget->tabBar()->setTabTextColor(3, color);
}

139
extern/shiny/Editor/MainWindow.hpp vendored Normal file
View file

@ -0,0 +1,139 @@
#ifndef SHINY_EDITOR_MAINWINDOW_HPP
#define SHINY_EDITOR_MAINWINDOW_HPP
#include <QMainWindow>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QStringListModel>
#include <queue>
#include "Actions.hpp"
#include "Query.hpp"
#include "PropertySortModel.hpp"
namespace Ui {
class MainWindow;
}
namespace sh
{
struct SynchronizationState;
/**
* @brief A snapshot of the material system's state. Lock the mUpdateMutex before accessing.
*/
struct MaterialSystemState
{
std::vector<std::string> mMaterialList;
std::map<std::string, std::string> mGlobalSettingsMap;
std::vector<std::string> mConfigurationList;
std::vector<std::string> mMaterialFiles;
std::vector<std::string> mConfigurationFiles;
std::vector<std::string> mShaderSets;
std::string mErrors;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// really should be an std::atomic
volatile bool mRequestShowWindow;
// dito
volatile bool mRequestExit;
SynchronizationState* mSync;
/// \todo Is there a better way to ignore manual model changes?
bool mIgnoreGlobalSettingChange;
bool mIgnoreConfigurationChange;
bool mIgnoreMaterialChange;
bool mIgnoreMaterialPropertyChange;
std::queue<Action*> mActionQueue;
std::vector<Query*> mQueries;
MaterialSystemState mState;
private:
Ui::MainWindow *ui;
// material tab
QStringListModel* mMaterialModel;
QSortFilterProxyModel* mMaterialProxyModel;
QStandardItemModel* mMaterialPropertyModel;
PropertySortModel* mMaterialSortModel;
// global settings tab
QStandardItemModel* mGlobalSettingsModel;
// configuration tab
QStandardItemModel* mConfigurationModel;
void queueAction(Action* action);
void requestQuery(Query* query);
void buildMaterialModel (MaterialQuery* data);
void buildConfigurationModel (ConfigurationQuery* data);
QString getSelectedMaterial();
/// get the context of an index in the material property model
void getContext(QModelIndex index, int* passIndex, int* textureIndex, bool* isInPass=NULL, bool* isInTextureUnit=NULL);
std::string getPropertyKey(QModelIndex index);
std::string getPropertyValue(QModelIndex index);
void addProperty (QStandardItem* parent, const std::string& key, MaterialProperty value, bool scrollTo=false);
protected:
void closeEvent(QCloseEvent *event);
public slots:
void onIdle();
void onMaterialSelectionChanged (const QModelIndex & current, const QModelIndex & previous);
void onConfigurationSelectionChanged (const QString& current);
void onGlobalSettingChanged (QStandardItem* item);
void onConfigurationChanged (QStandardItem* item);
void onMaterialPropertyChanged (QStandardItem* item);
void onContextMenuRequested(const QPoint& point);
private slots:
void on_lineEdit_textEdited(const QString &arg1);
void on_actionSave_triggered();
void on_actionNewMaterial_triggered();
void on_actionDeleteMaterial_triggered();
void on_actionQuit_triggered();
void on_actionNewConfiguration_triggered();
void on_actionDeleteConfiguration_triggered();
void on_actionDeleteConfigurationProperty_triggered();
void on_actionCloneMaterial_triggered();
void on_actionCreatePass_triggered();
void on_actionDeleteProperty_triggered();
void on_actionNewProperty_triggered();
void on_actionCreateTextureUnit_triggered();
void on_clearButton_clicked();
void on_tabWidget_currentChanged(int index);
};
}
#endif // MAINWINDOW_HPP

View file

@ -0,0 +1,14 @@
#include "NewMaterialDialog.hpp"
#include "ui_newmaterialdialog.h"
NewMaterialDialog::NewMaterialDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NewMaterialDialog)
{
ui->setupUi(this);
}
NewMaterialDialog::~NewMaterialDialog()
{
delete ui;
}

View file

@ -0,0 +1,22 @@
#ifndef NEWMATERIALDIALOG_HPP
#define NEWMATERIALDIALOG_HPP
#include <QDialog>
namespace Ui {
class NewMaterialDialog;
}
class NewMaterialDialog : public QDialog
{
Q_OBJECT
public:
explicit NewMaterialDialog(QWidget *parent = 0);
~NewMaterialDialog();
private:
Ui::NewMaterialDialog *ui;
};
#endif // NEWMATERIALDIALOG_HPP

View file

@ -0,0 +1,35 @@
#include "PropertySortModel.hpp"
#include "Query.hpp"
#include <iostream>
sh::PropertySortModel::PropertySortModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
bool sh::PropertySortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if (left.data(Qt::UserRole+1).toInt() != 0 && right.data(Qt::UserRole+1).toInt() != 0)
{
int sourceL = left.data(Qt::UserRole+1).toInt();
int sourceR = right.data(Qt::UserRole+1).toInt();
if (sourceL > sourceR)
return true;
else if (sourceR > sourceL)
return false;
}
int typeL = left.data(Qt::UserRole).toInt();
int typeR = right.data(Qt::UserRole).toInt();
if (typeL > typeR)
return true;
else if (typeR > typeL)
return false;
QString nameL = left.data().toString();
QString nameR = right.data().toString();
return nameL > nameR;
}

View file

@ -0,0 +1,21 @@
#ifndef SHINY_EDITOR_PROPERTYSORTMODEL_H
#define SHINY_EDITOR_PROPERTYSORTMODEL_H
#include <QSortFilterProxyModel>
namespace sh
{
class PropertySortModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
PropertySortModel(QObject* parent);
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
};
}
#endif

134
extern/shiny/Editor/Query.cpp vendored Normal file
View file

@ -0,0 +1,134 @@
#include "Query.hpp"
#include "../Main/Factory.hpp"
namespace sh
{
void Query::execute()
{
executeImpl();
mDone = true;
}
ConfigurationQuery::ConfigurationQuery(const std::string &name)
: mName(name)
{
}
void ConfigurationQuery::executeImpl()
{
sh::Factory::getInstance().listConfigurationSettings(mName, mProperties);
}
void MaterialQuery::executeImpl()
{
sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(mName);
if (instance->getParent())
mParent = static_cast<sh::MaterialInstance*>(instance->getParent())->getName();
// add the inherited properties
sh::PropertySetGet* parent = instance;
std::vector<std::string> inheritedPropertiesVector;
while (parent->getParent())
{
parent = parent->getParent();
const sh::PropertyMap& parentProperties = parent->listProperties();
for (PropertyMap::const_iterator it = parentProperties.begin(); it != parentProperties.end(); ++it)
{
MaterialProperty::Source source = MaterialProperty::Inherited_Unchanged;
MaterialProperty::Type type = getType(it->first, parent->getProperty(it->first));
mProperties[it->first] = MaterialProperty (
retrieveValue<sh::StringValue>(parent->getProperty(it->first), NULL).get(),
type, source);
inheritedPropertiesVector.push_back(it->first);
}
}
// add our properties
const sh::PropertyMap& ourProperties = instance->listProperties();
for (PropertyMap::const_iterator it = ourProperties.begin(); it != ourProperties.end(); ++it)
{
MaterialProperty::Source source =
(std::find(inheritedPropertiesVector.begin(), inheritedPropertiesVector.end(), it->first)
!= inheritedPropertiesVector.end()) ?
MaterialProperty::Inherited_Changed : MaterialProperty::Normal;
MaterialProperty::Type type = getType(it->first, instance->getProperty(it->first));
mProperties[it->first] = MaterialProperty (
retrieveValue<sh::StringValue>(instance->getProperty(it->first), NULL).get(),
type, source);
}
std::vector<MaterialInstancePass>* passes = instance->getPasses();
for (std::vector<MaterialInstancePass>::iterator it = passes->begin(); it != passes->end(); ++it)
{
mPasses.push_back(PassInfo());
const sh::PropertyMap& passProperties = it->listProperties();
for (PropertyMap::const_iterator pit = passProperties.begin(); pit != passProperties.end(); ++pit)
{
PropertyValuePtr property = it->getProperty(pit->first);
MaterialProperty::Type type = getType(pit->first, property);
if (typeid(*property).name() == typeid(sh::LinkedValue).name())
mPasses.back().mProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type);
else
mPasses.back().mProperties[pit->first] = MaterialProperty(
retrieveValue<sh::StringValue>(property, NULL).get(), type);
}
const sh::PropertyMap& shaderProperties = it->mShaderProperties.listProperties();
for (PropertyMap::const_iterator pit = shaderProperties.begin(); pit != shaderProperties.end(); ++pit)
{
PropertyValuePtr property = it->mShaderProperties.getProperty(pit->first);
MaterialProperty::Type type = getType(pit->first, property);
if (typeid(*property).name() == typeid(sh::LinkedValue).name())
mPasses.back().mShaderProperties[pit->first] = MaterialProperty("$" + property->_getStringValue(), type);
else
mPasses.back().mShaderProperties[pit->first] = MaterialProperty(
retrieveValue<sh::StringValue>(property, NULL).get(), type);
}
std::vector<MaterialInstanceTextureUnit>* texUnits = &it->mTexUnits;
for (std::vector<MaterialInstanceTextureUnit>::iterator tIt = texUnits->begin(); tIt != texUnits->end(); ++tIt)
{
mPasses.back().mTextureUnits.push_back(TextureUnitInfo());
mPasses.back().mTextureUnits.back().mName = tIt->getName();
const sh::PropertyMap& unitProperties = tIt->listProperties();
for (PropertyMap::const_iterator pit = unitProperties.begin(); pit != unitProperties.end(); ++pit)
{
PropertyValuePtr property = tIt->getProperty(pit->first);
MaterialProperty::Type type = getType(pit->first, property);
if (typeid(*property).name() == typeid(sh::LinkedValue).name())
mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty(
"$" + property->_getStringValue(), MaterialProperty::Linked);
else
mPasses.back().mTextureUnits.back().mProperties[pit->first] = MaterialProperty(
retrieveValue<sh::StringValue>(property, NULL).get(), type);
}
}
}
}
MaterialProperty::Type MaterialQuery::getType(const std::string &key, PropertyValuePtr value)
{
if (typeid(*value).name() == typeid(sh::LinkedValue).name())
return MaterialProperty::Linked;
if (key == "vertex_program" || key == "fragment_program")
return MaterialProperty::Shader;
std::string valueStr = retrieveValue<sh::StringValue>(value, NULL).get();
if (valueStr == "false" || valueStr == "true")
return MaterialProperty::Boolean;
}
void MaterialPropertyQuery::executeImpl()
{
sh::MaterialInstance* m = sh::Factory::getInstance().getMaterialInstance(mName);
mValue = retrieveValue<sh::StringValue>(m->getProperty(mPropertyName), m).get();
}
}

121
extern/shiny/Editor/Query.hpp vendored Normal file
View file

@ -0,0 +1,121 @@
#ifndef SH_QUERY_H
#define SH_QUERY_H
#include <string>
#include <map>
#include <vector>
#include "../Main/PropertyBase.hpp"
namespace sh
{
class Query
{
public:
Query()
: mDone(false) {}
virtual ~Query() {}
void execute();
bool mDone;
protected:
virtual void executeImpl() = 0;
};
class ConfigurationQuery : public Query
{
public:
ConfigurationQuery(const std::string& name);
std::map<std::string, std::string> mProperties;
protected:
std::string mName;
virtual void executeImpl();
};
struct MaterialProperty
{
enum Type
{
Texture,
Color,
Boolean,
Shader,
Misc,
Linked,
Object // child object, i.e. pass, texture unit, shader properties
};
enum Source
{
Normal,
Inherited_Changed,
Inherited_Unchanged,
None // there is no property source (e.g. a pass, which does not have a name)
};
MaterialProperty() {}
MaterialProperty (const std::string& value, Type type, Source source=Normal)
: mValue(value), mType(type), mSource(source) {}
std::string mValue;
Type mType;
Source mSource;
};
struct TextureUnitInfo
{
std::string mName;
std::map<std::string, MaterialProperty> mProperties;
};
struct PassInfo
{
std::map<std::string, MaterialProperty> mShaderProperties;
std::map<std::string, MaterialProperty> mProperties;
std::vector<TextureUnitInfo> mTextureUnits;
};
class MaterialQuery : public Query
{
public:
MaterialQuery(const std::string& name)
: mName(name) {}
std::string mParent;
std::vector<PassInfo> mPasses;
std::map<std::string, MaterialProperty> mProperties;
protected:
std::string mName;
virtual void executeImpl();
MaterialProperty::Type getType (const std::string& key, PropertyValuePtr value);
};
class MaterialPropertyQuery : public Query
{
public:
MaterialPropertyQuery(const std::string& name, const std::string& propertyName)
: mName(name), mPropertyName(propertyName)
{
}
std::string mValue;
std::string mName;
std::string mPropertyName;
protected:
virtual void executeImpl();
};
}
#endif

118
extern/shiny/Editor/addpropertydialog.ui vendored Normal file
View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddPropertyDialog</class>
<widget class="QDialog" name="AddPropertyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>257</width>
<height>133</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Property name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Editing widget</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBox">
<item>
<property name="text">
<string>Checkbox</string>
</property>
</item>
<item>
<property name="text">
<string>Shader</string>
</property>
</item>
<item>
<property name="text">
<string>Color</string>
</property>
</item>
<item>
<property name="text">
<string>Texture</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AddPropertyDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AddPropertyDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

420
extern/shiny/Editor/mainwindow.ui vendored Normal file
View file

@ -0,0 +1,420 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>512</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="verticalLayout">
<item>
<widget class="sh::ColoredTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<property name="accessibleName">
<string/>
</property>
<attribute name="title">
<string>Materials</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="materialList">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolBar" name="toolBar1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<addaction name="actionNewMaterial"/>
<addaction name="actionCloneMaterial"/>
<addaction name="actionDeleteMaterial"/>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget2">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QTreeView" name="materialView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolBar" name="toolBar4">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<addaction name="actionNewProperty"/>
<addaction name="actionDeleteProperty"/>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab2">
<attribute name="title">
<string>Global settings</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QTableView" name="globalSettingsView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Configurations</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QListWidget" name="configurationList"/>
</item>
<item>
<widget class="QToolBar" name="toolBar2">
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<addaction name="actionNewConfiguration"/>
<addaction name="actionDeleteConfiguration"/>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTableView" name="configurationView"/>
</item>
<item>
<widget class="QToolBar" name="toolBar2_2">
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<addaction name="actionDeleteConfigurationProperty"/>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Errors</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QTextEdit" name="errorLog">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear</string>
</property>
<property name="icon">
<iconset theme="edit-clear"/>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox"/>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>25</height>
</rect>
</property>
<property name="defaultUp">
<bool>false</bool>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSave"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuMaterial">
<property name="title">
<string>Material</string>
</property>
<addaction name="actionNewMaterial"/>
<addaction name="actionCloneMaterial"/>
<addaction name="actionDeleteMaterial"/>
<addaction name="actionChange_parent"/>
</widget>
<widget class="QMenu" name="menuHistory">
<property name="title">
<string>History</string>
</property>
</widget>
<addaction name="menuFile"/>
<addaction name="menuMaterial"/>
<addaction name="menuHistory"/>
</widget>
<action name="actionQuit">
<property name="icon">
<iconset theme="application-exit">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Quit</string>
</property>
</action>
<action name="actionSave">
<property name="icon">
<iconset theme="document-save">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Save</string>
</property>
<property name="toolTip">
<string>Save all</string>
</property>
</action>
<action name="actionDeleteMaterial">
<property name="icon">
<iconset theme="edit-delete">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected material</string>
</property>
</action>
<action name="actionChange_parent">
<property name="text">
<string>Change parent...</string>
</property>
</action>
<action name="actionNewMaterial">
<property name="icon">
<iconset theme="document-new">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>New</string>
</property>
<property name="toolTip">
<string>Create a new material</string>
</property>
</action>
<action name="actionCloneMaterial">
<property name="icon">
<iconset theme="edit-copy">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Clone</string>
</property>
<property name="toolTip">
<string>Clone selected material</string>
</property>
</action>
<action name="actionDeleteConfiguration">
<property name="icon">
<iconset theme="edit-delete">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected configuration</string>
</property>
<property name="shortcut">
<string>Del</string>
</property>
</action>
<action name="actionNewConfiguration">
<property name="icon">
<iconset theme="document-new">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>New</string>
</property>
<property name="toolTip">
<string>Create a new configuration</string>
</property>
</action>
<action name="actionDeleteConfigurationProperty">
<property name="icon">
<iconset theme="edit-delete">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete property</string>
</property>
</action>
<action name="actionDeleteProperty">
<property name="icon">
<iconset theme="remove">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete item</string>
</property>
</action>
<action name="actionNewProperty">
<property name="icon">
<iconset theme="add">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>New property</string>
</property>
</action>
<action name="actionCreatePass">
<property name="icon">
<iconset theme="edit-add">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Create pass</string>
</property>
</action>
<action name="actionCreateTextureUnit">
<property name="icon">
<iconset theme="edit-add">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Create texture unit</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>sh::ColoredTabWidget</class>
<extends>QTabWidget</extends>
<header>ColoredTabWidget.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewMaterialDialog</class>
<widget class="QDialog" name="NewMaterialDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>385</width>
<height>198</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Parent material</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_2"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>File</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBox"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NewMaterialDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NewMaterialDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

168
extern/shiny/Extra/core.h vendored Normal file
View file

@ -0,0 +1,168 @@
#if SH_HLSL == 1 || SH_CG == 1
#define shTexture2D sampler2D
#define shSample(tex, coord) tex2D(tex, coord)
#define shCubicSample(tex, coord) texCUBE(tex, coord)
#define shLerp(a, b, t) lerp(a, b, t)
#define shSaturate(a) saturate(a)
#define shSampler2D(name) , uniform sampler2D name : register(s@shCounter(0)) @shUseSampler(name)
#define shSamplerCube(name) , uniform samplerCUBE name : register(s@shCounter(0)) @shUseSampler(name)
#define shMatrixMult(m, v) mul(m, v)
#define shUniform(type, name) , uniform type name
#define shTangentInput(type) , in type tangent : TANGENT
#define shVertexInput(type, name) , in type name : TEXCOORD@shCounter(1)
#define shInput(type, name) , in type name : TEXCOORD@shCounter(1)
#define shOutput(type, name) , out type name : TEXCOORD@shCounter(2)
#define shNormalInput(type) , in type normal : NORMAL
#define shColourInput(type) , in type colour : COLOR
#ifdef SH_VERTEX_SHADER
#define shOutputPosition oPosition
#define shInputPosition iPosition
#define SH_BEGIN_PROGRAM \
void main( \
float4 iPosition : POSITION \
, out float4 oPosition : POSITION
#define SH_START_PROGRAM \
) \
#endif
#ifdef SH_FRAGMENT_SHADER
#define shOutputColour(num) oColor##num
#define shDeclareMrtOutput(num) , out float4 oColor##num : COLOR##num
#define SH_BEGIN_PROGRAM \
void main( \
out float4 oColor0 : COLOR
#define SH_START_PROGRAM \
) \
#endif
#endif
#if SH_GLSL == 1
@version 120
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define shTexture2D sampler2D
#define shSample(tex, coord) texture2D(tex, coord)
#define shCubicSample(tex, coord) textureCube(tex, coord)
#define shLerp(a, b, t) mix(a, b, t)
#define shSaturate(a) clamp(a, 0.0, 1.0)
#define shUniform(type, name) uniform type name;
#define shSampler2D(name) uniform sampler2D name; @shUseSampler(name)
#define shSamplerCube(name) uniform samplerCube name; @shUseSampler(name)
#define shMatrixMult(m, v) (m * v)
#define shOutputPosition gl_Position
#define float4x4 mat4
#define float3x3 mat3
// GLSL 1.3
#if 0
// automatically recognized by ogre when the input name equals this
#define shInputPosition vertex
#define shOutputColour(num) oColor##num
#define shTangentInput(type) in type tangent;
#define shVertexInput(type, name) in type name;
#define shInput(type, name) in type name;
#define shOutput(type, name) out type name;
// automatically recognized by ogre when the input name equals this
#define shNormalInput(type) in type normal;
#define shColourInput(type) in type colour;
#ifdef SH_VERTEX_SHADER
#define SH_BEGIN_PROGRAM \
in float4 vertex;
#define SH_START_PROGRAM \
void main(void)
#endif
#ifdef SH_FRAGMENT_SHADER
#define shDeclareMrtOutput(num) out vec4 oColor##num;
#define SH_BEGIN_PROGRAM \
out float4 oColor0;
#define SH_START_PROGRAM \
void main(void)
#endif
#endif
// GLSL 1.2
#if 1
// automatically recognized by ogre when the input name equals this
#define shInputPosition vertex
#define shOutputColour(num) gl_FragData[num]
#define shTangentInput(type) attribute type tangent;
#define shVertexInput(type, name) attribute type name;
#define shInput(type, name) varying type name;
#define shOutput(type, name) varying type name;
// automatically recognized by ogre when the input name equals this
#define shNormalInput(type) attribute type normal;
#define shColourInput(type) attribute type colour;
#ifdef SH_VERTEX_SHADER
#define SH_BEGIN_PROGRAM \
attribute vec4 vertex;
#define SH_START_PROGRAM \
void main(void)
#endif
#ifdef SH_FRAGMENT_SHADER
#define shDeclareMrtOutput(num)
#define SH_BEGIN_PROGRAM
#define SH_START_PROGRAM \
void main(void)
#endif
#endif
#endif

9
extern/shiny/License.txt vendored Normal file
View file

@ -0,0 +1,9 @@
Copyright (c) 2012 <scrawl@baseoftrash.de>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

803
extern/shiny/Main/Factory.cpp vendored Normal file
View file

@ -0,0 +1,803 @@
#include "Factory.hpp"
#include <stdexcept>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include "Platform.hpp"
#include "ScriptLoader.hpp"
#include "ShaderSet.hpp"
#include "MaterialInstanceTextureUnit.hpp"
namespace sh
{
Factory* Factory::sThis = 0;
const std::string Factory::mBinaryCacheName = "binaryCache";
Factory& Factory::getInstance()
{
assert (sThis);
return *sThis;
}
Factory* Factory::getInstancePtr()
{
return sThis;
}
Factory::Factory (Platform* platform)
: mPlatform(platform)
, mShadersEnabled(true)
, mShaderDebugOutputEnabled(false)
, mCurrentLanguage(Language_None)
, mListener(NULL)
, mCurrentConfiguration(NULL)
, mCurrentLodConfiguration(NULL)
, mReadMicrocodeCache(false)
, mWriteMicrocodeCache(false)
, mReadSourceCache(false)
, mWriteSourceCache(false)
{
assert (!sThis);
sThis = this;
mPlatform->setFactory(this);
}
void Factory::loadAllFiles()
{
assert(mCurrentLanguage != Language_None);
if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt"))
{
std::ifstream file;
file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str());
std::string line;
while (getline(file, line))
{
std::string sourceFile = line;
if (!getline(file, line))
assert(0);
int modified = boost::lexical_cast<int>(line);
mShadersLastModified[sourceFile] = modified;
}
}
// load configurations
{
ScriptLoader shaderSetLoader(".configuration");
ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath());
std::map <std::string, ScriptNode*> nodes = shaderSetLoader.getAllConfigScripts();
for (std::map <std::string, ScriptNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it)
{
if (!(it->second->getName() == "configuration"))
{
std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .configuration" << std::endl;
break;
}
Configuration newConfiguration;
newConfiguration.setParent(&mGlobalSettings);
newConfiguration.setSourceFile (it->second->mFileName);
std::vector<ScriptNode*> props = it->second->getChildren();
for (std::vector<ScriptNode*>::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt)
{
std::string name = (*propIt)->getName();
std::string val = (*propIt)->getValue();
newConfiguration.setProperty (name, makeProperty(val));
}
mConfigurations[it->first] = newConfiguration;
}
}
// load lod configurations
{
ScriptLoader lodLoader(".lod");
ScriptLoader::loadAllFiles (&lodLoader, mPlatform->getBasePath());
std::map <std::string, ScriptNode*> nodes = lodLoader.getAllConfigScripts();
for (std::map <std::string, ScriptNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it)
{
if (!(it->second->getName() == "lod_configuration"))
{
std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .lod" << std::endl;
break;
}
if (it->first == "0")
{
throw std::runtime_error("lod level 0 (max lod) can't have a configuration");
}
PropertySetGet newLod;
std::vector<ScriptNode*> props = it->second->getChildren();
for (std::vector<ScriptNode*>::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt)
{
std::string name = (*propIt)->getName();
std::string val = (*propIt)->getValue();
newLod.setProperty (name, makeProperty(val));
}
mLodConfigurations[boost::lexical_cast<int>(it->first)] = newLod;
}
}
// load shader sets
bool removeBinaryCache = reloadShaders();
// load materials
{
ScriptLoader materialLoader(".mat");
ScriptLoader::loadAllFiles (&materialLoader, mPlatform->getBasePath());
std::map <std::string, ScriptNode*> nodes = materialLoader.getAllConfigScripts();
for (std::map <std::string, ScriptNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it)
{
if (!(it->second->getName() == "material"))
{
std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .mat" << std::endl;
break;
}
MaterialInstance newInstance(it->first, this);
newInstance.create(mPlatform);
if (!mShadersEnabled)
newInstance.setShadersEnabled (false);
newInstance.setSourceFile (it->second->mFileName);
std::vector<ScriptNode*> props = it->second->getChildren();
for (std::vector<ScriptNode*>::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt)
{
std::string name = (*propIt)->getName();
std::string val = (*propIt)->getValue();
if (name == "pass")
{
MaterialInstancePass* newPass = newInstance.createPass();
std::vector<ScriptNode*> props2 = (*propIt)->getChildren();
for (std::vector<ScriptNode*>::const_iterator propIt2 = props2.begin(); propIt2 != props2.end(); ++propIt2)
{
std::string name2 = (*propIt2)->getName();
std::string val2 = (*propIt2)->getValue();
if (name2 == "shader_properties")
{
std::vector<ScriptNode*> shaderProps = (*propIt2)->getChildren();
for (std::vector<ScriptNode*>::const_iterator shaderPropIt = shaderProps.begin(); shaderPropIt != shaderProps.end(); ++shaderPropIt)
{
std::string val = (*shaderPropIt)->getValue();
newPass->mShaderProperties.setProperty((*shaderPropIt)->getName(), makeProperty(val));
}
}
else if (name2 == "texture_unit")
{
MaterialInstanceTextureUnit* newTex = newPass->createTextureUnit(val2);
std::vector<ScriptNode*> texProps = (*propIt2)->getChildren();
for (std::vector<ScriptNode*>::const_iterator texPropIt = texProps.begin(); texPropIt != texProps.end(); ++texPropIt)
{
std::string val = (*texPropIt)->getValue();
newTex->setProperty((*texPropIt)->getName(), makeProperty(val));
}
}
else
newPass->setProperty((*propIt2)->getName(), makeProperty(val2));
}
}
else if (name == "parent")
newInstance.setParentInstance(val);
else
newInstance.setProperty((*propIt)->getName(), makeProperty(val));
}
if (newInstance.hasProperty("create_configuration"))
{
std::string config = retrieveValue<StringValue>(newInstance.getProperty("create_configuration"), NULL).get();
newInstance.createForConfiguration (config, 0);
}
mMaterials.insert (std::make_pair(it->first, newInstance));
}
// now that all materials are loaded, replace the parent names with the actual pointers to parent
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
std::string parent = it->second.getParentInstance();
if (parent != "")
{
if (mMaterials.find (it->second.getParentInstance()) == mMaterials.end())
throw std::runtime_error ("Unable to find parent for material instance \"" + it->first + "\"");
it->second.setParent(&mMaterials.find(parent)->second);
}
}
}
if (mPlatform->supportsShaderSerialization () && mReadMicrocodeCache && !removeBinaryCache)
{
std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName;
if (boost::filesystem::exists(file))
{
mPlatform->deserializeShaders (file);
}
}
}
Factory::~Factory ()
{
mShaderSets.clear();
if (mPlatform->supportsShaderSerialization () && mWriteMicrocodeCache)
{
std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName;
mPlatform->serializeShaders (file);
}
if (mReadSourceCache)
{
// save the last modified time of shader sources (as of when they were loaded)
std::ofstream file;
file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str());
for (LastModifiedMap::const_iterator it = mShadersLastModifiedNew.begin(); it != mShadersLastModifiedNew.end(); ++it)
{
file << it->first << "\n" << it->second << std::endl;
}
file.close();
}
delete mPlatform;
sThis = 0;
}
MaterialInstance* Factory::searchInstance (const std::string& name)
{
if (mMaterials.find(name) != mMaterials.end())
return &mMaterials.find(name)->second;
return NULL;
}
MaterialInstance* Factory::findInstance (const std::string& name)
{
assert (mMaterials.find(name) != mMaterials.end());
return &mMaterials.find(name)->second;
}
MaterialInstance* Factory::requestMaterial (const std::string& name, const std::string& configuration, unsigned short lodIndex)
{
MaterialInstance* m = searchInstance (name);
if (configuration != "Default" && mConfigurations.find(configuration) == mConfigurations.end())
return NULL;
if (m)
{
// make sure all lod techniques below (higher lod) exist
int i = lodIndex;
while (i>0)
{
--i;
if (m->createForConfiguration (configuration, i))
{
if (mListener)
mListener->materialCreated (m, configuration, i);
}
else
return NULL;
}
if (m->createForConfiguration (configuration, lodIndex))
{
if (mListener)
mListener->materialCreated (m, configuration, lodIndex);
}
else
return NULL;
}
return m;
}
MaterialInstance* Factory::createMaterialInstance (const std::string& name, const std::string& parentInstance)
{
if (parentInstance != "" && mMaterials.find(parentInstance) == mMaterials.end())
throw std::runtime_error ("trying to clone material that does not exist");
MaterialInstance newInstance(name, this);
if (!mShadersEnabled)
newInstance.setShadersEnabled(false);
if (parentInstance != "")
newInstance.setParent (&mMaterials.find(parentInstance)->second);
newInstance.create(mPlatform);
mMaterials.insert (std::make_pair(name, newInstance));
return &mMaterials.find(name)->second;
}
void Factory::destroyMaterialInstance (const std::string& name)
{
if (mMaterials.find(name) != mMaterials.end())
mMaterials.erase(name);
}
void Factory::setShadersEnabled (bool enabled)
{
mShadersEnabled = enabled;
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
it->second.setShadersEnabled(enabled);
}
}
void Factory::setGlobalSetting (const std::string& name, const std::string& value)
{
bool changed = true;
if (mGlobalSettings.hasProperty(name))
changed = (retrieveValue<StringValue>(mGlobalSettings.getProperty(name), NULL).get() != value);
mGlobalSettings.setProperty (name, makeProperty<StringValue>(new StringValue(value)));
if (changed)
{
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
it->second.destroyAll();
}
}
}
void Factory::setSharedParameter (const std::string& name, PropertyValuePtr value)
{
mPlatform->setSharedParameter(name, value);
}
ShaderSet* Factory::getShaderSet (const std::string& name)
{
if (mShaderSets.find(name) == mShaderSets.end())
{
std::stringstream msg;
msg << "Shader '" << name << "' not found";
throw std::runtime_error(msg.str());
}
return &mShaderSets.find(name)->second;
}
Platform* Factory::getPlatform ()
{
return mPlatform;
}
Language Factory::getCurrentLanguage ()
{
return mCurrentLanguage;
}
void Factory::setCurrentLanguage (Language lang)
{
bool changed = (mCurrentLanguage != lang);
mCurrentLanguage = lang;
if (changed)
{
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
it->second.destroyAll();
}
}
}
void Factory::notifyConfigurationChanged()
{
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
it->second.destroyAll();
}
}
MaterialInstance* Factory::getMaterialInstance (const std::string& name)
{
return findInstance(name);
}
void Factory::setTextureAlias (const std::string& alias, const std::string& realName)
{
mTextureAliases[alias] = realName;
// update the already existing texture units
for (std::map<TextureUnitState*, std::string>::iterator it = mTextureAliasInstances.begin(); it != mTextureAliasInstances.end(); ++it)
{
if (it->second == alias)
{
it->first->setTextureName(realName);
}
}
}
std::string Factory::retrieveTextureAlias (const std::string& name)
{
if (mTextureAliases.find(name) != mTextureAliases.end())
return mTextureAliases[name];
else
return "";
}
Configuration* Factory::getConfiguration (const std::string& name)
{
return &mConfigurations[name];
}
void Factory::createConfiguration (const std::string& name)
{
mConfigurations[name].setParent (&mGlobalSettings);
}
void Factory::destroyConfiguration(const std::string &name)
{
mConfigurations.erase(name);
}
void Factory::registerLodConfiguration (int index, PropertySetGet configuration)
{
mLodConfigurations[index] = configuration;
}
void Factory::setMaterialListener (MaterialListener* listener)
{
mListener = listener;
}
void Factory::addTextureAliasInstance (const std::string& name, TextureUnitState* t)
{
mTextureAliasInstances[t] = name;
}
void Factory::removeTextureAliasInstances (TextureUnitState* t)
{
mTextureAliasInstances.erase(t);
}
void Factory::setActiveConfiguration (const std::string& configuration)
{
if (configuration == "Default")
mCurrentConfiguration = 0;
else
{
assert (mConfigurations.find(configuration) != mConfigurations.end());
mCurrentConfiguration = &mConfigurations[configuration];
}
}
void Factory::setActiveLodLevel (int level)
{
if (level == 0)
mCurrentLodConfiguration = 0;
else
{
assert (mLodConfigurations.find(level) != mLodConfigurations.end());
mCurrentLodConfiguration = &mLodConfigurations[level];
}
}
void Factory::setShaderDebugOutputEnabled (bool enabled)
{
mShaderDebugOutputEnabled = enabled;
}
PropertySetGet* Factory::getCurrentGlobalSettings()
{
PropertySetGet* p = &mGlobalSettings;
// current global settings are affected by active configuration & active lod configuration
if (mCurrentConfiguration)
{
p = mCurrentConfiguration;
}
if (mCurrentLodConfiguration)
{
mCurrentLodConfiguration->setParent(p);
p = mCurrentLodConfiguration;
}
return p;
}
void Factory::saveAll ()
{
std::map<std::string, std::ofstream*> files;
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
if (it->second.getSourceFile().empty())
continue;
if (files.find(it->second.getSourceFile()) == files.end())
{
/// \todo check if this is actually the same file, since there can be different paths to the same file
std::ofstream* stream = new std::ofstream();
stream->open (it->second.getSourceFile().c_str());
files[it->second.getSourceFile()] = stream;
}
it->second.save (*files[it->second.getSourceFile()]);
}
for (std::map<std::string, std::ofstream*>::iterator it = files.begin(); it != files.end(); ++it)
{
delete it->second;
}
files.clear();
for (ConfigurationMap::iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it)
{
if (it->second.getSourceFile().empty())
continue;
if (files.find(it->second.getSourceFile()) == files.end())
{
/// \todo check if this is actually the same file, since there can be different paths to the same file
std::ofstream* stream = new std::ofstream();
stream->open (it->second.getSourceFile().c_str());
files[it->second.getSourceFile()] = stream;
}
it->second.save (it->first, *files[it->second.getSourceFile()]);
}
for (std::map<std::string, std::ofstream*>::iterator it = files.begin(); it != files.end(); ++it)
{
delete it->second;
}
}
void Factory::listMaterials(std::vector<std::string> &out)
{
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
out.push_back(it->first);
}
}
void Factory::listGlobalSettings(std::map<std::string, std::string> &out)
{
const PropertyMap& properties = mGlobalSettings.listProperties();
for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it)
{
out[it->first] = retrieveValue<StringValue>(mGlobalSettings.getProperty(it->first), NULL).get();
}
}
void Factory::listConfigurationSettings(const std::string& name, std::map<std::string, std::string> &out)
{
const PropertyMap& properties = mConfigurations[name].listProperties();
for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it)
{
out[it->first] = retrieveValue<StringValue>(mConfigurations[name].getProperty(it->first), NULL).get();
}
}
void Factory::listConfigurationNames(std::vector<std::string> &out)
{
for (ConfigurationMap::const_iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it)
{
out.push_back(it->first);
}
}
void Factory::listShaderSets(std::vector<std::string> &out)
{
for (ShaderSetMap::const_iterator it = mShaderSets.begin(); it != mShaderSets.end(); ++it)
{
out.push_back(it->first);
}
}
void Factory::_ensureMaterial(const std::string& name, const std::string& configuration)
{
MaterialInstance* m = searchInstance (name);
assert(m);
m->createForConfiguration (configuration, 0);
}
bool Factory::removeCache(const std::string& pattern)
{
bool ret = false;
if ( boost::filesystem::exists(mPlatform->getCacheFolder())
&& boost::filesystem::is_directory(mPlatform->getCacheFolder()))
{
boost::filesystem::directory_iterator end_iter;
for( boost::filesystem::directory_iterator dir_iter(mPlatform->getCacheFolder()) ; dir_iter != end_iter ; ++dir_iter)
{
if (boost::filesystem::is_regular_file(dir_iter->status()) )
{
boost::filesystem::path file = dir_iter->path();
std::string pathname = file.filename().string();
// get first part of filename, e.g. main_fragment_546457654 -> main_fragment
// there is probably a better method for this...
std::vector<std::string> tokens;
boost::split(tokens, pathname, boost::is_any_of("_"));
tokens.erase(--tokens.end());
std::string shaderName;
for (std::vector<std::string>::const_iterator vector_iter = tokens.begin(); vector_iter != tokens.end();)
{
shaderName += *(vector_iter++);
if (vector_iter != tokens.end())
shaderName += "_";
}
if (shaderName == pattern)
{
boost::filesystem::remove(file);
ret = true;
std::cout << "Removing outdated shader: " << file << std::endl;
}
}
}
}
return ret;
}
bool Factory::reloadShaders()
{
mShaderSets.clear();
notifyConfigurationChanged();
bool removeBinaryCache = false;
ScriptLoader shaderSetLoader(".shaderset");
ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath());
std::map <std::string, ScriptNode*> nodes = shaderSetLoader.getAllConfigScripts();
for (std::map <std::string, ScriptNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it)
{
if (!(it->second->getName() == "shader_set"))
{
std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .shaderset" << std::endl;
break;
}
if (!it->second->findChild("profiles_cg"))
throw std::runtime_error ("missing \"profiles_cg\" field for \"" + it->first + "\"");
if (!it->second->findChild("profiles_hlsl"))
throw std::runtime_error ("missing \"profiles_hlsl\" field for \"" + it->first + "\"");
if (!it->second->findChild("source"))
throw std::runtime_error ("missing \"source\" field for \"" + it->first + "\"");
if (!it->second->findChild("type"))
throw std::runtime_error ("missing \"type\" field for \"" + it->first + "\"");
std::vector<std::string> profiles_cg;
boost::split (profiles_cg, it->second->findChild("profiles_cg")->getValue(), boost::is_any_of(" "));
std::string cg_profile;
for (std::vector<std::string>::iterator it2 = profiles_cg.begin(); it2 != profiles_cg.end(); ++it2)
{
if (mPlatform->isProfileSupported(*it2))
{
cg_profile = *it2;
break;
}
}
std::vector<std::string> profiles_hlsl;
boost::split (profiles_hlsl, it->second->findChild("profiles_hlsl")->getValue(), boost::is_any_of(" "));
std::string hlsl_profile;
for (std::vector<std::string>::iterator it2 = profiles_hlsl.begin(); it2 != profiles_hlsl.end(); ++it2)
{
if (mPlatform->isProfileSupported(*it2))
{
hlsl_profile = *it2;
break;
}
}
std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue();
std::string sourceRelative = it->second->findChild("source")->getValue();
ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile,
sourceAbsolute,
mPlatform->getBasePath(),
it->first,
&mGlobalSettings);
int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute));
mShadersLastModifiedNew[sourceRelative] = lastModified;
if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end())
{
if (mShadersLastModified[sourceRelative] != lastModified)
{
// delete any outdated shaders based on this shader set
if (removeCache (it->first))
removeBinaryCache = true;
}
}
else
{
// if we get here, this is either the first run or a new shader file was added
// in both cases we can safely delete
if (removeCache (it->first))
removeBinaryCache = true;
}
mShaderSets.insert(std::make_pair(it->first, newSet));
}
// new is now current
mShadersLastModified = mShadersLastModifiedNew;
return removeBinaryCache;
}
void Factory::doMonitorShaderFiles()
{
bool reload=false;
ScriptLoader shaderSetLoader(".shaderset");
ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath());
std::map <std::string, ScriptNode*> nodes = shaderSetLoader.getAllConfigScripts();
for (std::map <std::string, ScriptNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it)
{
std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue();
std::string sourceRelative = it->second->findChild("source")->getValue();
int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute));
if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end())
{
if (mShadersLastModified[sourceRelative] != lastModified)
{
reload=true;
break;
}
}
}
if (reload)
reloadShaders();
}
void Factory::logError(const std::string &msg)
{
mErrorLog << msg << '\n';
}
std::string Factory::getErrorLog()
{
std::string errors = mErrorLog.str();
mErrorLog.str("");
return errors;
}
void Factory::unloadUnreferencedMaterials()
{
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
if (it->second.getMaterial()->isUnreferenced())
it->second.destroyAll();
}
}
void Configuration::save(const std::string& name, std::ofstream &stream)
{
stream << "configuration " << name << '\n';
stream << "{\n";
PropertySetGet::save(stream, "\t");
stream << "}\n";
}
}

271
extern/shiny/Main/Factory.hpp vendored Normal file
View file

@ -0,0 +1,271 @@
#ifndef SH_FACTORY_H
#define SH_FACTORY_H
#include <map>
#include <string>
#include <sstream>
#include "MaterialInstance.hpp"
#include "ShaderSet.hpp"
#include "Language.hpp"
namespace sh
{
class Platform;
class Configuration : public PropertySetGet
{
public:
void setSourceFile (const std::string& file) { mSourceFile = file ; }
std::string getSourceFile () { return mSourceFile; }
void save(const std::string& name, std::ofstream &stream);
private:
std::string mSourceFile;
};
typedef std::map<std::string, MaterialInstance> MaterialMap;
typedef std::map<std::string, ShaderSet> ShaderSetMap;
typedef std::map<std::string, Configuration> ConfigurationMap;
typedef std::map<int, PropertySetGet> LodConfigurationMap;
typedef std::map<std::string, int> LastModifiedMap;
typedef std::map<std::string, std::string> TextureAliasMap;
/**
* @brief
* Allows you to be notified when a certain material was just created. Useful for changing material properties that you can't
* do in a .mat script (for example a series of animated textures) \n
* When receiving the event, you can get the platform material by calling m->getMaterial()
* and casting that to the platform specific material (e.g. for Ogre, sh::OgreMaterial)
*/
class MaterialListener
{
public:
virtual void materialCreated (MaterialInstance* m, const std::string& configuration, unsigned short lodIndex) = 0;
};
/**
* @brief
* The main interface class
*/
class Factory
{
public:
Factory(Platform* platform);
///< @note Ownership of \a platform is transferred to this class, so you don't have to delete it.
~Factory();
/**
* Create a MaterialInstance, optionally copying all properties from \a parentInstance
* @param name name of the new instance
* @param name of the parent (optional)
* @return newly created instance
*/
MaterialInstance* createMaterialInstance (const std::string& name, const std::string& parentInstance = "");
/// @note It is safe to call this if the instance does not exist
void destroyMaterialInstance (const std::string& name);
/// Use this to enable or disable shaders on-the-fly
void setShadersEnabled (bool enabled);
/// write generated shaders to current directory, useful for debugging
void setShaderDebugOutputEnabled (bool enabled);
/// Use this to manage user settings. \n
/// Global settings can be retrieved in shaders through a macro. \n
/// When a global setting is changed, the shaders that depend on them are recompiled automatically.
void setGlobalSetting (const std::string& name, const std::string& value);
/// Adjusts the given shared parameter. \n
/// Internally, this will change all uniform parameters of this name marked with the macro \@shSharedParameter \n
/// @param name of the shared parameter
/// @param value of the parameter, use sh::makeProperty to construct this value
void setSharedParameter (const std::string& name, PropertyValuePtr value);
Language getCurrentLanguage ();
/// Switch between different shader languages (cg, glsl, hlsl)
void setCurrentLanguage (Language lang);
/// Get a MaterialInstance by name
MaterialInstance* getMaterialInstance (const std::string& name);
/// Create a configuration, which can then be altered by using Factory::getConfiguration
void createConfiguration (const std::string& name);
/// Register a lod configuration, which can then be used by setting up lod distance values for the material \n
/// 0 refers to highest lod, so use 1 or higher as index parameter
void registerLodConfiguration (int index, PropertySetGet configuration);
/// Set an alias name for a texture, the real name can then be retrieved with the "texture_alias"
/// property in a texture unit - this is useful if you don't know the name of your texture beforehand. \n
/// Example: \n
/// - In the material definition: texture_alias ReflectionMap \n
/// - At runtime: factory->setTextureAlias("ReflectionMap", "rtt_654654"); \n
/// You can call factory->setTextureAlias as many times as you want, and if the material was already created, its texture will be updated!
void setTextureAlias (const std::string& alias, const std::string& realName);
/// Retrieve the real texture name for a texture alias (the real name is set by the user)
std::string retrieveTextureAlias (const std::string& name);
/// Attach a listener for material created events
void setMaterialListener (MaterialListener* listener);
/// Call this after you have set up basic stuff, like the shader language.
void loadAllFiles ();
/// Controls writing of generated shader source code to the cache folder, so that the
/// (rather expensive) preprocessing step can be skipped on the next run. See Factory::setReadSourceCache \n
/// \note The default is off (no cache writing)
void setWriteSourceCache(bool write) { mWriteSourceCache = write; }
/// Controls reading of generated shader sources from the cache folder
/// \note The default is off (no cache reading)
/// \note Even if microcode caching is enabled, generating (or caching) the source is still required due to the macros.
void setReadSourceCache(bool read) { mReadSourceCache = read; }
/// Controls writing the microcode of the generated shaders to the cache folder. Microcode is machine independent
/// and loads very fast compared to regular compilation. Note that the availability of this feature depends on the \a Platform.
/// \note The default is off (no cache writing)
void setWriteMicrocodeCache(bool write) { mWriteMicrocodeCache = write; }
/// Controls reading of shader microcode from the cache folder. Microcode is machine independent
/// and loads very fast compared to regular compilation. Note that the availability of this feature depends on the \a Platform.
/// \note The default is off (no cache reading)
void setReadMicrocodeCache(bool read) { mReadMicrocodeCache = read; }
/// Lists all materials currently registered with the factory. Whether they are
/// loaded or not does not matter.
void listMaterials (std::vector<std::string>& out);
/// Lists current name & value of all global settings.
void listGlobalSettings (std::map<std::string, std::string>& out);
/// Lists configuration names.
void listConfigurationNames (std::vector<std::string>& out);
/// Lists current name & value of settings for a given configuration.
void listConfigurationSettings (const std::string& name, std::map<std::string, std::string>& out);
/// Lists shader sets.
void listShaderSets (std::vector<std::string>& out);
/// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache
/// through the Ogre API. Luckily, this is already fixed in Ogre 1.9.
bool reloadShaders();
/// Calls reloadShaders() if shader files have been modified since the last reload.
/// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache
/// through the Ogre API. Luckily, this is already fixed in Ogre 1.9.
void doMonitorShaderFiles();
/// Unloads all materials that are currently not referenced. This will not unload the textures themselves,
/// but it will let go of the SharedPtr's to the textures, so that you may unload them if you so desire. \n
/// A good time to call this would be after a new level has been loaded, but just calling it occasionally after a period
/// of time should work just fine too.
void unloadUnreferencedMaterials();
void destroyConfiguration (const std::string& name);
void notifyConfigurationChanged();
/// Saves all materials and configurations, by default to the file they were loaded from.
/// If you wish to save them elsewhere, use setSourceFile first.
void saveAll ();
/// Returns the error log as a string, then clears it.
/// Note: Errors are also written to the standard error output, or thrown if they are fatal.
std::string getErrorLog ();
static Factory& getInstance();
///< Return instance of this class.
static Factory* getInstancePtr();
/// Make sure a material technique is loaded.\n
/// You will probably never have to use this.
void _ensureMaterial(const std::string& name, const std::string& configuration);
Configuration* getConfiguration (const std::string& name);
private:
MaterialInstance* requestMaterial (const std::string& name, const std::string& configuration, unsigned short lodIndex);
ShaderSet* getShaderSet (const std::string& name);
Platform* getPlatform ();
PropertySetGet* getCurrentGlobalSettings();
void addTextureAliasInstance (const std::string& name, TextureUnitState* t);
void removeTextureAliasInstances (TextureUnitState* t);
std::string getCacheFolder () { return mPlatform->getCacheFolder (); }
bool getReadSourceCache() { return mReadSourceCache; }
bool getWriteSourceCache() { return mReadSourceCache; }
public:
bool getWriteMicrocodeCache() { return mWriteMicrocodeCache; } // Fixme
private:
void setActiveConfiguration (const std::string& configuration);
void setActiveLodLevel (int level);
bool getShaderDebugOutputEnabled() { return mShaderDebugOutputEnabled; }
std::map<TextureUnitState*, std::string> mTextureAliasInstances;
void logError (const std::string& msg);
friend class Platform;
friend class MaterialInstance;
friend class ShaderInstance;
friend class ShaderSet;
friend class TextureUnitState;
private:
static Factory* sThis;
bool mShadersEnabled;
bool mShaderDebugOutputEnabled;
bool mReadMicrocodeCache;
bool mWriteMicrocodeCache;
bool mReadSourceCache;
bool mWriteSourceCache;
std::stringstream mErrorLog;
MaterialMap mMaterials;
ShaderSetMap mShaderSets;
ConfigurationMap mConfigurations;
LodConfigurationMap mLodConfigurations;
LastModifiedMap mShadersLastModified;
LastModifiedMap mShadersLastModifiedNew;
PropertySetGet mGlobalSettings;
PropertySetGet* mCurrentConfiguration;
PropertySetGet* mCurrentLodConfiguration;
TextureAliasMap mTextureAliases;
Language mCurrentLanguage;
MaterialListener* mListener;
Platform* mPlatform;
MaterialInstance* findInstance (const std::string& name);
MaterialInstance* searchInstance (const std::string& name);
/// @return was anything removed?
bool removeCache (const std::string& pattern);
static const std::string mBinaryCacheName;
};
}
#endif

17
extern/shiny/Main/Language.hpp vendored Normal file
View file

@ -0,0 +1,17 @@
#ifndef SH_LANGUAGE_H
#define SH_LANGUAGE_H
namespace sh
{
enum Language
{
Language_CG,
Language_HLSL,
Language_GLSL,
Language_GLSLES,
Language_Count,
Language_None
};
}
#endif

260
extern/shiny/Main/MaterialInstance.cpp vendored Normal file
View file

@ -0,0 +1,260 @@
#include "MaterialInstance.hpp"
#include <stdexcept>
#include <iostream>
#include "Factory.hpp"
#include "ShaderSet.hpp"
namespace sh
{
MaterialInstance::MaterialInstance (const std::string& name, Factory* f)
: mName(name)
, mShadersEnabled(true)
, mFactory(f)
, mListener(NULL)
, mFailedToCreate(false)
{
}
MaterialInstance::~MaterialInstance ()
{
}
void MaterialInstance::setParentInstance (const std::string& name)
{
mParentInstance = name;
}
std::string MaterialInstance::getParentInstance ()
{
return mParentInstance;
}
void MaterialInstance::create (Platform* platform)
{
mMaterial = platform->createMaterial(mName);
if (hasProperty ("shadow_caster_material"))
mMaterial->setShadowCasterMaterial (retrieveValue<StringValue>(getProperty("shadow_caster_material"), NULL).get());
if (hasProperty ("lod_values"))
mMaterial->setLodLevels (retrieveValue<StringValue>(getProperty("lod_values"), NULL).get());
}
void MaterialInstance::destroyAll ()
{
if (hasProperty("create_configuration"))
return;
mMaterial->removeAll();
mTexUnits.clear();
mFailedToCreate = false;
}
void MaterialInstance::setProperty (const std::string& name, PropertyValuePtr value)
{
PropertySetGet::setProperty (name, value);
destroyAll(); // trigger updates
}
bool MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex)
{
if (mFailedToCreate)
return false;
try{
mMaterial->ensureLoaded();
bool res = mMaterial->createConfiguration(configuration, lodIndex);
if (!res)
return false; // listener was false positive
if (mListener)
mListener->requestedConfiguration (this, configuration);
mFactory->setActiveConfiguration (configuration);
mFactory->setActiveLodLevel (lodIndex);
bool allowFixedFunction = true;
if (!mShadersEnabled && hasProperty("allow_fixed_function"))
{
allowFixedFunction = retrieveValue<BooleanValue>(getProperty("allow_fixed_function"), NULL).get();
}
bool useShaders = mShadersEnabled || !allowFixedFunction;
// get passes of the top-most parent
PassVector* passes = getParentPasses();
if (passes->empty())
throw std::runtime_error ("material \"" + mName + "\" does not have any passes");
for (PassVector::iterator it = passes->begin(); it != passes->end(); ++it)
{
boost::shared_ptr<Pass> pass = mMaterial->createPass (configuration, lodIndex);
it->copyAll (pass.get(), this);
// texture samplers used in the shaders
std::vector<std::string> usedTextureSamplersVertex;
std::vector<std::string> usedTextureSamplersFragment;
PropertySetGet* context = this;
// create or retrieve shaders
bool hasVertex = it->hasProperty("vertex_program")
&& !retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get().empty();
bool hasFragment = it->hasProperty("fragment_program")
&& !retrieveValue<StringValue>(it->getProperty("fragment_program"), context).get().empty();
if (useShaders)
{
it->setContext(context);
it->mShaderProperties.setContext(context);
if (hasVertex)
{
ShaderSet* vertex = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get());
ShaderInstance* v = vertex->getInstance(&it->mShaderProperties);
if (v)
{
pass->assignProgram (GPT_Vertex, v->getName());
v->setUniformParameters (pass, &it->mShaderProperties);
std::vector<std::string> sharedParams = v->getSharedParameters ();
for (std::vector<std::string>::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2)
{
pass->addSharedParameter (GPT_Vertex, *it2);
}
std::vector<std::string> vector = v->getUsedSamplers ();
usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end());
}
}
if (hasFragment)
{
ShaderSet* fragment = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("fragment_program"), context).get());
ShaderInstance* f = fragment->getInstance(&it->mShaderProperties);
if (f)
{
pass->assignProgram (GPT_Fragment, f->getName());
f->setUniformParameters (pass, &it->mShaderProperties);
std::vector<std::string> sharedParams = f->getSharedParameters ();
for (std::vector<std::string>::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2)
{
pass->addSharedParameter (GPT_Fragment, *it2);
}
std::vector<std::string> vector = f->getUsedSamplers ();
usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end());
}
}
}
// create texture units
std::vector<MaterialInstanceTextureUnit>* texUnits = &it->mTexUnits;
int i=0;
for (std::vector<MaterialInstanceTextureUnit>::iterator texIt = texUnits->begin(); texIt != texUnits->end(); ++texIt )
{
// only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled
bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end();
bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end();
if ( (foundVertex || foundFragment)
|| (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue<BooleanValue>(texIt->getProperty("create_in_ffp"), this).get()))
{
boost::shared_ptr<TextureUnitState> texUnit = pass->createTextureUnitState (texIt->getName());
texIt->copyAll (texUnit.get(), context);
mTexUnits.push_back(texUnit);
// set texture unit indices (required by GLSL)
if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL)
{
pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i);
++i;
}
}
}
}
if (mListener)
mListener->createdConfiguration (this, configuration);
return true;
} catch (std::runtime_error& e)
{
destroyAll();
mFailedToCreate = true;
std::stringstream msg;
msg << "Error while creating material " << mName << ": " << e.what();
std::cerr << msg.str() << std::endl;
mFactory->logError(msg.str());
return false;
}
}
Material* MaterialInstance::getMaterial ()
{
return mMaterial.get();
}
MaterialInstancePass* MaterialInstance::createPass ()
{
mPasses.push_back (MaterialInstancePass());
mPasses.back().setContext(this);
return &mPasses.back();
}
void MaterialInstance::deletePass(unsigned int index)
{
assert(mPasses.size() > index);
mPasses.erase(mPasses.begin()+index);
}
PassVector* MaterialInstance::getParentPasses()
{
if (mParent)
return static_cast<MaterialInstance*>(mParent)->getParentPasses();
else
return &mPasses;
}
PassVector* MaterialInstance::getPasses()
{
return &mPasses;
}
void MaterialInstance::setShadersEnabled (bool enabled)
{
if (enabled == mShadersEnabled)
return;
mShadersEnabled = enabled;
// trigger updates
if (mMaterial.get())
destroyAll();
}
void MaterialInstance::save (std::ofstream& stream)
{
stream << "material " << mName << "\n"
<< "{\n";
if (mParent)
{
stream << "\t" << "parent " << static_cast<MaterialInstance*>(mParent)->getName() << "\n";
}
const PropertyMap& properties = listProperties ();
for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it)
{
stream << "\t" << it->first << " " << retrieveValue<StringValue>(getProperty(it->first), NULL).get() << "\n";
}
for (PassVector::iterator it = mPasses.begin(); it != mPasses.end(); ++it)
{
stream << "\tpass" << '\n';
stream << "\t{" << '\n';
it->save(stream);
stream << "\t}" << '\n';
}
stream << "}\n";
}
}

109
extern/shiny/Main/MaterialInstance.hpp vendored Normal file
View file

@ -0,0 +1,109 @@
#ifndef SH_MATERIALINSTANCE_H
#define SH_MATERIALINSTANCE_H
#include <vector>
#include <fstream>
#include "PropertyBase.hpp"
#include "Platform.hpp"
#include "MaterialInstancePass.hpp"
namespace sh
{
class Factory;
typedef std::vector<MaterialInstancePass> PassVector;
/**
* @brief
* Allows you to be notified when a certain configuration for a material was just about to be created. \n
* Useful for adjusting some properties prior to the material being created (Or you could also re-create
* the whole material from scratch, i.e. use this as a method to create this material entirely in code)
*/
class MaterialInstanceListener
{
public:
virtual void requestedConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called before creating
virtual void createdConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called after creating
virtual ~MaterialInstanceListener(){}
};
/**
* @brief
* A specific material instance, which has all required properties set
* (for example the diffuse & normal map, ambient/diffuse/specular values). \n
* Depending on these properties, the system will automatically select a shader permutation
* that suits these and create the backend materials / passes (provided by the \a Platform class).
*/
class MaterialInstance : public PropertySetGet
{
public:
MaterialInstance (const std::string& name, Factory* f);
virtual ~MaterialInstance ();
PassVector* getParentPasses(); ///< gets the passes of the top-most parent
PassVector* getPasses(); ///< get our passes (for derived materials, none)
MaterialInstancePass* createPass ();
void deletePass (unsigned int index);
/// @attention Because the backend material passes are created on demand, the returned material here might not contain anything yet!
/// The only place where you should use this method, is for the MaterialInstance given by the MaterialListener::materialCreated event!
Material* getMaterial();
/// attach a \a MaterialInstanceListener to this specific material (as opposed to \a MaterialListener, which listens to all materials)
void setListener (MaterialInstanceListener* l) { mListener = l; }
std::string getName() { return mName; }
virtual void setProperty (const std::string& name, PropertyValuePtr value);
void setSourceFile(const std::string& sourceFile) { mSourceFile = sourceFile; }
std::string getSourceFile() { return mSourceFile; }
///< get the name of the file this material was read from, or empty if it was created dynamically by code
private:
void setParentInstance (const std::string& name);
std::string getParentInstance ();
void create (Platform* platform);
bool createForConfiguration (const std::string& configuration, unsigned short lodIndex);
void destroyAll ();
void setShadersEnabled (bool enabled);
void save (std::ofstream& stream);
bool mFailedToCreate;
friend class Factory;
private:
std::string mParentInstance;
///< this is only used during the file-loading phase. an instance could be loaded before its parent is loaded,
/// so initially only the parent's name is written to this member.
/// once all instances are loaded, the actual mParent pointer (from PropertySetGet class) can be set
std::vector< boost::shared_ptr<TextureUnitState> > mTexUnits;
MaterialInstanceListener* mListener;
PassVector mPasses;
std::string mName;
std::string mSourceFile;
boost::shared_ptr<Material> mMaterial;
bool mShadersEnabled;
Factory* mFactory;
};
}
#endif

View file

@ -0,0 +1,35 @@
#include "MaterialInstancePass.hpp"
#include <fstream>
namespace sh
{
MaterialInstanceTextureUnit* MaterialInstancePass::createTextureUnit (const std::string& name)
{
mTexUnits.push_back(MaterialInstanceTextureUnit(name));
return &mTexUnits.back();
}
void MaterialInstancePass::save(std::ofstream &stream)
{
if (mShaderProperties.listProperties().size())
{
stream << "\t\t" << "shader_properties" << '\n';
stream << "\t\t{\n";
mShaderProperties.save(stream, "\t\t\t");
stream << "\t\t}\n";
}
PropertySetGet::save(stream, "\t\t");
for (std::vector <MaterialInstanceTextureUnit>::iterator it = mTexUnits.begin();
it != mTexUnits.end(); ++it)
{
stream << "\t\ttexture_unit " << it->getName() << '\n';
stream << "\t\t{\n";
it->save(stream, "\t\t\t");
stream << "\t\t}\n";
}
}
}

View file

@ -0,0 +1,29 @@
#ifndef SH_MATERIALINSTANCEPASS_H
#define SH_MATERIALINSTANCEPASS_H
#include <vector>
#include "PropertyBase.hpp"
#include "MaterialInstanceTextureUnit.hpp"
namespace sh
{
/**
* @brief
* Holds properties of a single texture unit in a \a MaterialInstancePass. \n
* No inheritance here for now.
*/
class MaterialInstancePass : public PropertySetGet
{
public:
MaterialInstanceTextureUnit* createTextureUnit (const std::string& name);
void save (std::ofstream& stream);
PropertySetGet mShaderProperties;
std::vector <MaterialInstanceTextureUnit> mTexUnits;
};
}
#endif

View file

@ -0,0 +1,14 @@
#include "MaterialInstanceTextureUnit.hpp"
namespace sh
{
MaterialInstanceTextureUnit::MaterialInstanceTextureUnit (const std::string& name)
: mName(name)
{
}
std::string MaterialInstanceTextureUnit::getName() const
{
return mName;
}
}

View file

@ -0,0 +1,27 @@
#ifndef SH_MATERIALINSTANCETEXTUREUNIT_H
#define SH_MATERIALINSTANCETEXTUREUNIT_H
#include "PropertyBase.hpp"
namespace sh
{
/**
* @brief
* A single texture unit state that belongs to a \a MaterialInstancePass \n
* this is not the real "backend" \a TextureUnitState (provided by \a Platform),
* it is merely a placeholder for properties. \n
* @note The backend \a TextureUnitState will only be created if this texture unit is
* actually used (i.e. referenced in the shader, or marked with property create_in_ffp = true).
*/
class MaterialInstanceTextureUnit : public PropertySetGet
{
public:
MaterialInstanceTextureUnit (const std::string& name);
std::string getName() const;
void setName (const std::string& name) { mName = name; }
private:
std::string mName;
};
}
#endif

89
extern/shiny/Main/Platform.cpp vendored Normal file
View file

@ -0,0 +1,89 @@
#include "Platform.hpp"
#include <stdexcept>
#include "Factory.hpp"
namespace sh
{
Platform::Platform (const std::string& basePath)
: mBasePath(basePath)
, mCacheFolder("./")
, mFactory(NULL)
{
}
Platform::~Platform ()
{
}
void Platform::setFactory (Factory* factory)
{
mFactory = factory;
}
std::string Platform::getBasePath ()
{
return mBasePath;
}
bool Platform::supportsMaterialQueuedListener ()
{
return false;
}
bool Platform::supportsShaderSerialization ()
{
return false;
}
MaterialInstance* Platform::fireMaterialRequested (const std::string& name, const std::string& configuration, unsigned short lodIndex)
{
return mFactory->requestMaterial (name, configuration, lodIndex);
}
void Platform::serializeShaders (const std::string& file)
{
throw std::runtime_error ("Shader serialization not supported by this platform");
}
void Platform::deserializeShaders (const std::string& file)
{
throw std::runtime_error ("Shader serialization not supported by this platform");
}
void Platform::setCacheFolder (const std::string& folder)
{
mCacheFolder = folder;
}
std::string Platform::getCacheFolder() const
{
return mCacheFolder;
}
// ------------------------------------------------------------------------------
bool TextureUnitState::setPropertyOverride (const std::string& name, PropertyValuePtr& value, PropertySetGet *context)
{
if (name == "texture_alias")
{
std::string aliasName = retrieveValue<StringValue>(value, context).get();
Factory::getInstance().addTextureAliasInstance (aliasName, this);
setTextureName (Factory::getInstance().retrieveTextureAlias (aliasName));
return true;
}
else
return false;
}
TextureUnitState::~TextureUnitState()
{
Factory* f = Factory::getInstancePtr ();
if (f)
f->removeTextureAliasInstances (this);
}
}

146
extern/shiny/Main/Platform.hpp vendored Normal file
View file

@ -0,0 +1,146 @@
#ifndef SH_PLATFORM_H
#define SH_PLATFORM_H
#include <string>
#include <boost/shared_ptr.hpp>
#include "Language.hpp"
#include "PropertyBase.hpp"
namespace sh
{
class Factory;
class MaterialInstance;
enum GpuProgramType
{
GPT_Vertex,
GPT_Fragment
// GPT_Geometry
};
// These classes are supposed to be filled by the platform implementation
class GpuProgram
{
public:
virtual ~GpuProgram() {}
virtual bool getSupported () = 0; ///< @return true if the compilation was successful
/// @param name name of the uniform in the shader
/// @param autoConstantName name of the auto constant (for example world_viewproj_matrix)
/// @param extraInfo if any extra info is needed (e.g. light index), put it here
virtual void setAutoConstant (const std::string& name, const std::string& autoConstantName, const std::string& extraInfo = "") = 0;
};
class TextureUnitState : public PropertySet
{
public:
virtual ~TextureUnitState();
virtual void setTextureName (const std::string& textureName) = 0;
protected:
virtual bool setPropertyOverride (const std::string& name, PropertyValuePtr& value, PropertySetGet *context);
};
class Pass : public PropertySet
{
public:
virtual boost::shared_ptr<TextureUnitState> createTextureUnitState (const std::string& name) = 0;
virtual void assignProgram (GpuProgramType type, const std::string& name) = 0;
/// @param type gpu program type
/// @param name name of the uniform in the shader
/// @param vt type of value, e.g. vector4
/// @param value value to set
/// @param context used for retrieving linked values
virtual void setGpuConstant (int type, const std::string& name, ValueType vt, PropertyValuePtr value, PropertySetGet* context) = 0;
virtual void setTextureUnitIndex (int programType, const std::string& name, int index) = 0;
virtual void addSharedParameter (int type, const std::string& name) = 0;
};
class Material : public PropertySet
{
public:
virtual boost::shared_ptr<Pass> createPass (const std::string& configuration, unsigned short lodIndex) = 0;
virtual bool createConfiguration (const std::string& name, unsigned short lodIndex) = 0; ///< @return false if already exists
virtual void removeAll () = 0; ///< remove all configurations
virtual bool isUnreferenced() = 0;
virtual void ensureLoaded() = 0;
virtual void setLodLevels (const std::string& lodLevels) = 0;
virtual void setShadowCasterMaterial (const std::string& name) = 0;
};
class Platform
{
public:
Platform (const std::string& basePath);
virtual ~Platform ();
/// set the folder to use for shader caching
void setCacheFolder (const std::string& folder);
private:
virtual boost::shared_ptr<Material> createMaterial (const std::string& name) = 0;
virtual boost::shared_ptr<GpuProgram> createGpuProgram (
GpuProgramType type,
const std::string& compileArguments,
const std::string& name, const std::string& profile,
const std::string& source, Language lang) = 0;
virtual void destroyGpuProgram (const std::string& name) = 0;
virtual void setSharedParameter (const std::string& name, PropertyValuePtr value) = 0;
virtual bool isProfileSupported (const std::string& profile) = 0;
virtual void serializeShaders (const std::string& file);
virtual void deserializeShaders (const std::string& file);
std::string getCacheFolder () const;
friend class Factory;
friend class MaterialInstance;
friend class ShaderInstance;
friend class ShaderSet;
protected:
/**
* this will be \a true if the platform supports serialization (writing shader microcode
* to disk) and deserialization (create gpu program from saved microcode)
*/
virtual bool supportsShaderSerialization ();
/**
* this will be \a true if the platform supports a listener that notifies the system
* whenever a material is requested for rendering. if this is supported, shaders can be
* compiled on-demand when needed (and not earlier)
* @todo the Factory is not designed yet to handle the case where this method returns false
*/
virtual bool supportsMaterialQueuedListener ();
/**
* fire event: material requested for rendering
* @param name material name
* @param configuration requested configuration
*/
MaterialInstance* fireMaterialRequested (const std::string& name, const std::string& configuration, unsigned short lodIndex);
std::string mCacheFolder;
Factory* mFactory;
private:
void setFactory (Factory* factory);
std::string mBasePath;
std::string getBasePath();
};
}
#endif

99
extern/shiny/Main/Preprocessor.cpp vendored Normal file
View file

@ -0,0 +1,99 @@
#include "Preprocessor.hpp"
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
namespace sh
{
std::string Preprocessor::preprocess (std::string source, const std::string& includePath, std::vector<std::string> definitions, const std::string& name)
{
std::stringstream returnString;
// current file position is saved for exception handling
boost::wave::util::file_position_type current_position;
try
{
// This token type is one of the central types used throughout the library.
// It is a template parameter to some of the public classes and instances
// of this type are returned from the iterators.
typedef boost::wave::cpplexer::lex_token<> token_type;
// The template boost::wave::cpplexer::lex_iterator<> is the lexer type to
// to use as the token source for the preprocessing engine. It is
// parametrized with the token type.
typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;
// This is the resulting context type. The first template parameter should
// match the iterator type used during construction of the context
// instance (see below). It is the type of the underlying input stream.
typedef boost::wave::context<std::string::iterator, lex_iterator_type
, boost::wave::iteration_context_policies::load_file_to_string,
emit_custom_line_directives_hooks>
context_type;
// The preprocessor iterator shouldn't be constructed directly. It is
// generated through a wave::context<> object. This wave:context<> object
// is additionally used to initialize and define different parameters of
// the actual preprocessing.
//
// The preprocessing of the input stream is done on the fly behind the
// scenes during iteration over the range of context_type::iterator_type
// instances.
context_type ctx (source.begin(), source.end(), name.c_str());
ctx.add_include_path(includePath.c_str());
for (std::vector<std::string>::iterator it = definitions.begin(); it != definitions.end(); ++it)
{
ctx.add_macro_definition(*it);
}
// Get the preprocessor iterators and use them to generate the token
// sequence.
context_type::iterator_type first = ctx.begin();
context_type::iterator_type last = ctx.end();
// The input stream is preprocessed for you while iterating over the range
// [first, last). The dereferenced iterator returns tokens holding
// information about the preprocessed input stream, such as token type,
// token value, and position.
while (first != last)
{
current_position = (*first).get_position();
returnString << (*first).get_value();
++first;
}
}
catch (boost::wave::cpp_exception const& e)
{
// some preprocessing error
std::stringstream error;
error
<< e.file_name() << "(" << e.line_no() << "): "
<< e.description();
throw std::runtime_error(error.str());
}
catch (std::exception const& e)
{
// use last recognized token to retrieve the error position
std::stringstream error;
error
<< current_position.get_file()
<< "(" << current_position.get_line() << "): "
<< "exception caught: " << e.what();
throw std::runtime_error(error.str());
}
catch (...)
{
// use last recognized token to retrieve the error position
std::stringstream error;
error
<< current_position.get_file()
<< "(" << current_position.get_line() << "): "
<< "unexpected exception caught.";
throw std::runtime_error(error.str());
}
return returnString.str();
}
}

69
extern/shiny/Main/Preprocessor.hpp vendored Normal file
View file

@ -0,0 +1,69 @@
#ifndef SH_PREPROCESSOR_H
#define SH_PREPROCESSOR_H
#include <string>
#include <vector>
#include <cstdio>
#include <ostream>
#include <string>
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/wave/cpp_throw.hpp>
#include <boost/wave/cpp_exceptions.hpp>
#include <boost/wave/token_ids.hpp>
#include <boost/wave/util/macro_helpers.hpp>
#include <boost/wave/preprocessing_hooks.hpp>
namespace sh
{
/**
* @brief A simple interface for the boost::wave preprocessor
*/
class Preprocessor
{
public:
/**
* @brief Run a shader source string through the preprocessor
* @param source source string
* @param includePath path to search for includes (that are included with #include)
* @param definitions macros to predefine (vector of strings of the format MACRO=value, or just MACRO to define it as 1)
* @param name name to use for error messages
* @return processed string
*/
static std::string preprocess (std::string source, const std::string& includePath, std::vector<std::string> definitions, const std::string& name);
};
class emit_custom_line_directives_hooks
: public boost::wave::context_policies::default_preprocessing_hooks
{
public:
template <typename ContextT, typename ContainerT>
bool
emit_line_directive(ContextT const& ctx, ContainerT &pending,
typename ContextT::token_type const& act_token)
{
// emit a #line directive showing the relative filename instead
typename ContextT::position_type pos = act_token.get_position();
unsigned int column = 1;
typedef typename ContextT::token_type result_type;
// no line directives for now
pos.set_column(column);
pending.push_back(result_type(boost::wave::T_GENERATEDNEWLINE, "\n", pos));
return true;
}
};
}
#endif

302
extern/shiny/Main/PropertyBase.cpp vendored Normal file
View file

@ -0,0 +1,302 @@
#include "PropertyBase.hpp"
#include <vector>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <fstream>
namespace sh
{
IntValue::IntValue(int in)
: mValue(in)
{
}
IntValue::IntValue(const std::string& in)
{
mValue = boost::lexical_cast<int>(in);
}
std::string IntValue::serialize()
{
return boost::lexical_cast<std::string>(mValue);
}
// ------------------------------------------------------------------------------
BooleanValue::BooleanValue (bool in)
: mValue(in)
{
}
BooleanValue::BooleanValue (const std::string& in)
{
if (in == "true")
mValue = true;
else if (in == "false")
mValue = false;
else
{
std::stringstream msg;
msg << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue";
throw std::runtime_error(msg.str());
}
}
std::string BooleanValue::serialize ()
{
if (mValue)
return "true";
else
return "false";
}
// ------------------------------------------------------------------------------
StringValue::StringValue (const std::string& in)
{
mStringValue = in;
}
std::string StringValue::serialize()
{
return mStringValue;
}
// ------------------------------------------------------------------------------
LinkedValue::LinkedValue (const std::string& in)
{
mStringValue = in;
mStringValue.erase(0, 1);
}
std::string LinkedValue::serialize()
{
throw std::runtime_error ("can't directly get a linked value");
}
std::string LinkedValue::get(PropertySetGet* context) const
{
PropertyValuePtr p = context->getProperty(mStringValue);
return retrieveValue<StringValue>(p, NULL).get();
}
// ------------------------------------------------------------------------------
FloatValue::FloatValue (float in)
{
mValue = in;
}
FloatValue::FloatValue (const std::string& in)
{
mValue = boost::lexical_cast<float>(in);
}
std::string FloatValue::serialize ()
{
return boost::lexical_cast<std::string>(mValue);
}
// ------------------------------------------------------------------------------
Vector2::Vector2 (float x, float y)
: mX(x)
, mY(y)
{
}
Vector2::Vector2 (const std::string& in)
{
std::vector<std::string> tokens;
boost::split(tokens, in, boost::is_any_of(" "));
assert ((tokens.size() == 2) && "Invalid Vector2 conversion");
mX = boost::lexical_cast<float> (tokens[0]);
mY = boost::lexical_cast<float> (tokens[1]);
}
std::string Vector2::serialize ()
{
return boost::lexical_cast<std::string>(mX) + " "
+ boost::lexical_cast<std::string>(mY);
}
// ------------------------------------------------------------------------------
Vector3::Vector3 (float x, float y, float z)
: mX(x)
, mY(y)
, mZ(z)
{
}
Vector3::Vector3 (const std::string& in)
{
std::vector<std::string> tokens;
boost::split(tokens, in, boost::is_any_of(" "));
assert ((tokens.size() == 3) && "Invalid Vector3 conversion");
mX = boost::lexical_cast<float> (tokens[0]);
mY = boost::lexical_cast<float> (tokens[1]);
mZ = boost::lexical_cast<float> (tokens[2]);
}
std::string Vector3::serialize ()
{
return boost::lexical_cast<std::string>(mX) + " "
+ boost::lexical_cast<std::string>(mY) + " "
+ boost::lexical_cast<std::string>(mZ);
}
// ------------------------------------------------------------------------------
Vector4::Vector4 (float x, float y, float z, float w)
: mX(x)
, mY(y)
, mZ(z)
, mW(w)
{
}
Vector4::Vector4 (const std::string& in)
{
std::vector<std::string> tokens;
boost::split(tokens, in, boost::is_any_of(" "));
assert ((tokens.size() == 4) && "Invalid Vector4 conversion");
mX = boost::lexical_cast<float> (tokens[0]);
mY = boost::lexical_cast<float> (tokens[1]);
mZ = boost::lexical_cast<float> (tokens[2]);
mW = boost::lexical_cast<float> (tokens[3]);
}
std::string Vector4::serialize ()
{
return boost::lexical_cast<std::string>(mX) + " "
+ boost::lexical_cast<std::string>(mY) + " "
+ boost::lexical_cast<std::string>(mZ) + " "
+ boost::lexical_cast<std::string>(mW);
}
// ------------------------------------------------------------------------------
void PropertySet::setProperty (const std::string& name, PropertyValuePtr &value, PropertySetGet* context)
{
if (!setPropertyOverride (name, value, context))
{
std::stringstream msg;
msg << "sh::PropertySet: Warning: No match for property with name '" << name << "'";
throw std::runtime_error(msg.str());
}
}
bool PropertySet::setPropertyOverride (const std::string& name, PropertyValuePtr &value, PropertySetGet* context)
{
// if we got here, none of the sub-classes were able to make use of the property
return false;
}
// ------------------------------------------------------------------------------
PropertySetGet::PropertySetGet (PropertySetGet* parent)
: mParent(parent)
, mContext(NULL)
{
}
PropertySetGet::PropertySetGet ()
: mParent(NULL)
, mContext(NULL)
{
}
void PropertySetGet::setParent (PropertySetGet* parent)
{
mParent = parent;
}
void PropertySetGet::setContext (PropertySetGet* context)
{
mContext = context;
}
PropertySetGet* PropertySetGet::getContext()
{
return mContext;
}
void PropertySetGet::setProperty (const std::string& name, PropertyValuePtr value)
{
mProperties [name] = value;
}
void PropertySetGet::deleteProperty(const std::string &name)
{
mProperties.erase(name);
}
PropertyValuePtr& PropertySetGet::getProperty (const std::string& name)
{
bool found = (mProperties.find(name) != mProperties.end());
if (!found)
{
if (!mParent)
throw std::runtime_error ("Trying to retrieve property \"" + name + "\" that does not exist");
else
return mParent->getProperty (name);
}
else
return mProperties[name];
}
bool PropertySetGet::hasProperty (const std::string& name) const
{
bool found = (mProperties.find(name) != mProperties.end());
if (!found)
{
if (!mParent)
return false;
else
return mParent->hasProperty (name);
}
else
return true;
}
void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context, bool copyParent)
{
if (mParent && copyParent)
mParent->copyAll (target, context);
for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it)
{
target->setProperty(it->first, it->second, context);
}
}
void PropertySetGet::copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent)
{
if (mParent && copyParent)
mParent->copyAll (target, context);
for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it)
{
std::string val = retrieveValue<StringValue>(it->second, this).get();
target->setProperty(it->first, sh::makeProperty(new sh::StringValue(val)));
}
}
void PropertySetGet::save(std::ofstream &stream, const std::string& indentation)
{
for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it)
{
if (typeid( *(it->second) ) == typeid(LinkedValue))
stream << indentation << it->first << " " << "$" + static_cast<LinkedValue*>(&*(it->second))->_getStringValue() << '\n';
else
stream << indentation << it->first << " " << retrieveValue<StringValue>(it->second, this).get() << '\n';
}
}
}

244
extern/shiny/Main/PropertyBase.hpp vendored Normal file
View file

@ -0,0 +1,244 @@
#ifndef SH_PROPERTYBASE_H
#define SH_PROPERTYBASE_H
#include <string>
#include <map>
#include <boost/shared_ptr.hpp>
namespace sh
{
class StringValue;
class PropertySetGet;
class LinkedValue;
enum ValueType
{
VT_String,
VT_Int,
VT_Float,
VT_Vector2,
VT_Vector3,
VT_Vector4
};
class PropertyValue
{
public:
PropertyValue() {}
virtual ~PropertyValue() {}
std::string _getStringValue() { return mStringValue; }
virtual std::string serialize() = 0;
protected:
std::string mStringValue; ///< this will possibly not contain anything in the specialised classes
};
typedef boost::shared_ptr<PropertyValue> PropertyValuePtr;
class StringValue : public PropertyValue
{
public:
StringValue (const std::string& in);
std::string get() const { return mStringValue; }
virtual std::string serialize();
};
/**
* @brief Used for retrieving a named property from a context
*/
class LinkedValue : public PropertyValue
{
public:
LinkedValue (const std::string& in);
std::string get(PropertySetGet* context) const;
virtual std::string serialize();
};
class FloatValue : public PropertyValue
{
public:
FloatValue (float in);
FloatValue (const std::string& in);
float get() const { return mValue; }
virtual std::string serialize();
private:
float mValue;
};
class IntValue : public PropertyValue
{
public:
IntValue (int in);
IntValue (const std::string& in);
int get() const { return mValue; }
virtual std::string serialize();
private:
int mValue;
};
class BooleanValue : public PropertyValue
{
public:
BooleanValue (bool in);
BooleanValue (const std::string& in);
bool get() const { return mValue; }
virtual std::string serialize();
private:
bool mValue;
};
class Vector2 : public PropertyValue
{
public:
Vector2 (float x, float y);
Vector2 (const std::string& in);
float mX, mY;
virtual std::string serialize();
};
class Vector3 : public PropertyValue
{
public:
Vector3 (float x, float y, float z);
Vector3 (const std::string& in);
float mX, mY, mZ;
virtual std::string serialize();
};
class Vector4 : public PropertyValue
{
public:
Vector4 (float x, float y, float z, float w);
Vector4 (const std::string& in);
float mX, mY, mZ, mW;
virtual std::string serialize();
};
/// \brief base class that allows setting properties with any kind of value-type
class PropertySet
{
public:
virtual ~PropertySet() {}
void setProperty (const std::string& name, PropertyValuePtr& value, PropertySetGet* context);
protected:
virtual bool setPropertyOverride (const std::string& name, PropertyValuePtr& value, PropertySetGet* context);
///< @return \a true if the specified property was found, or false otherwise
};
typedef std::map<std::string, PropertyValuePtr> PropertyMap;
/// \brief base class that allows setting properties with any kind of value-type and retrieving them
class PropertySetGet
{
public:
PropertySetGet (PropertySetGet* parent);
PropertySetGet ();
virtual ~PropertySetGet() {}
void save (std::ofstream& stream, const std::string& indentation);
void copyAll (PropertySet* target, PropertySetGet* context, bool copyParent=true);
///< call setProperty for each property/value pair stored in \a this
void copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent=true);
///< call setProperty for each property/value pair stored in \a this
void setParent (PropertySetGet* parent);
PropertySetGet* getParent () { return mParent; }
void setContext (PropertySetGet* context);
PropertySetGet* getContext();
virtual void setProperty (const std::string& name, PropertyValuePtr value);
PropertyValuePtr& getProperty (const std::string& name);
void deleteProperty (const std::string& name);
const PropertyMap& listProperties() { return mProperties; }
bool hasProperty (const std::string& name) const;
private:
PropertyMap mProperties;
protected:
PropertySetGet* mParent;
///< the parent can provide properties as well (when they are retrieved via getProperty) \n
/// multiple levels of inheritance are also supported \n
/// children can override properties of their parents
PropertySetGet* mContext;
///< used to retrieve linked property values
};
template <typename T>
static T retrieveValue (boost::shared_ptr<PropertyValue>& value, PropertySetGet* context)
{
if (typeid(*value).name() == typeid(LinkedValue).name())
{
std::string v = static_cast<LinkedValue*>(value.get())->get(context);
PropertyValuePtr newVal = PropertyValuePtr (new StringValue(v));
return retrieveValue<T>(newVal, NULL);
}
if (typeid(T).name() == typeid(*value).name())
{
// requested type is the same as source type, only have to cast it
return *static_cast<T*>(value.get());
}
if ((typeid(T).name() == typeid(StringValue).name())
&& typeid(*value).name() != typeid(StringValue).name())
{
// if string type is requested and value is not string, use serialize method to convert to string
T* ptr = new T (value->serialize()); // note that T is always StringValue here, but we can't use it here
value = boost::shared_ptr<PropertyValue> (static_cast<PropertyValue*>(ptr));
return *ptr;
}
{
// remaining case: deserialization from string by passing the string to constructor of class T
T* ptr = new T(value->_getStringValue());
PropertyValuePtr newVal (static_cast<PropertyValue*>(ptr));
value = newVal;
return *ptr;
}
}
///<
/// @brief alternate version that supports linked values (use of $variables in parent material)
/// @note \a value is changed in-place to the converted object
/// @return converted object \n
/// Create a property from a string
inline PropertyValuePtr makeProperty (const std::string& prop)
{
if (prop.size() > 1 && prop[0] == '$')
return PropertyValuePtr (static_cast<PropertyValue*>(new LinkedValue(prop)));
else
return PropertyValuePtr (static_cast<PropertyValue*> (new StringValue(prop)));
}
template <typename T>
/// Create a property of any type
/// Example: sh::makeProperty (new sh::Vector4(1, 1, 1, 1))
inline PropertyValuePtr makeProperty (T* p)
{
return PropertyValuePtr ( static_cast<PropertyValue*>(p) );
}
}
#endif

414
extern/shiny/Main/ScriptLoader.cpp vendored Normal file
View file

@ -0,0 +1,414 @@
#include "ScriptLoader.hpp"
#include <vector>
#include <map>
#include <exception>
#include <fstream>
#include <boost/filesystem.hpp>
namespace sh
{
void ScriptLoader::loadAllFiles(ScriptLoader* c, const std::string& path)
{
for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir )
{
boost::filesystem::path p(*dir);
if(p.extension() == c->mFileEnding)
{
c->mCurrentFileName = (*dir).path().string();
std::ifstream in((*dir).path().string().c_str(), std::ios::binary);
c->parseScript(in);
}
}
}
ScriptLoader::ScriptLoader(const std::string& fileEnding)
: mLoadOrder(0)
, mToken(TOKEN_NewLine)
, mLastToken(TOKEN_NewLine)
{
mFileEnding = fileEnding;
}
ScriptLoader::~ScriptLoader()
{
clearScriptList();
}
void ScriptLoader::clearScriptList()
{
std::map <std::string, ScriptNode *>::iterator i;
for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i)
{
delete i->second;
}
m_scriptList.clear();
}
ScriptNode *ScriptLoader::getConfigScript(const std::string &name)
{
std::map <std::string, ScriptNode*>::iterator i;
std::string key = name;
i = m_scriptList.find(key);
//If found..
if (i != m_scriptList.end())
{
return i->second;
}
else
{
return NULL;
}
}
std::map <std::string, ScriptNode*> ScriptLoader::getAllConfigScripts ()
{
return m_scriptList;
}
void ScriptLoader::parseScript(std::ifstream &stream)
{
//Get first token
_nextToken(stream);
if (mToken == TOKEN_EOF)
{
stream.close();
return;
}
//Parse the script
_parseNodes(stream, 0);
stream.close();
}
void ScriptLoader::_nextToken(std::ifstream &stream)
{
//EOF token
if (!stream.good())
{
mToken = TOKEN_EOF;
return;
}
//(Get next character)
int ch = stream.get();
while ((ch == ' ' || ch == 9) && !stream.eof())
{ //Skip leading spaces / tabs
ch = stream.get();
}
if (!stream.good())
{
mToken = TOKEN_EOF;
return;
}
//Newline token
if (ch == '\r' || ch == '\n')
{
do
{
ch = stream.get();
} while ((ch == '\r' || ch == '\n') && !stream.eof());
stream.unget();
mToken = TOKEN_NewLine;
return;
}
//Open brace token
else if (ch == '{')
{
mToken = TOKEN_OpenBrace;
return;
}
//Close brace token
else if (ch == '}')
{
mToken = TOKEN_CloseBrace;
return;
}
//Text token
if (ch < 32 || ch > 122) //Verify valid char
{
throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()");
}
mTokenValue = "";
mToken = TOKEN_Text;
do
{
//Skip comments
if (ch == '/')
{
int ch2 = stream.peek();
//C++ style comment (//)
if (ch2 == '/')
{
stream.get();
do
{
ch = stream.get();
} while (ch != '\r' && ch != '\n' && !stream.eof());
mToken = TOKEN_NewLine;
return;
}
}
//Add valid char to tokVal
mTokenValue += (char)ch;
//Next char
ch = stream.get();
} while (ch > 32 && ch <= 122 && !stream.eof());
stream.unget();
return;
}
void ScriptLoader::_skipNewLines(std::ifstream &stream)
{
while (mToken == TOKEN_NewLine)
{
_nextToken(stream);
}
}
void ScriptLoader::_parseNodes(std::ifstream &stream, ScriptNode *parent)
{
typedef std::pair<std::string, ScriptNode*> ScriptItem;
while (true)
{
switch (mToken)
{
//Node
case TOKEN_Text:
{
//Add the new node
ScriptNode *newNode;
if (parent)
{
newNode = parent->addChild(mTokenValue);
}
else
{
newNode = new ScriptNode(0, mTokenValue);
}
//Get values
_nextToken(stream);
std::string valueStr;
int i=0;
while (mToken == TOKEN_Text)
{
if (i == 0)
valueStr += mTokenValue;
else
valueStr += " " + mTokenValue;
_nextToken(stream);
++i;
}
newNode->setValue(valueStr);
//Add root nodes to scriptList
if (!parent)
{
std::string key;
if (newNode->getValue() == "")
throw std::runtime_error("Root node must have a name (\"" + newNode->getName() + "\")");
key = newNode->getValue();
m_scriptList.insert(ScriptItem(key, newNode));
}
_skipNewLines(stream);
//Add any sub-nodes
if (mToken == TOKEN_OpenBrace)
{
//Parse nodes
_nextToken(stream);
_parseNodes(stream, newNode);
//Check for matching closing brace
if (mToken != TOKEN_CloseBrace)
{
throw std::runtime_error("Parse Error: Expecting closing brace");
}
_nextToken(stream);
_skipNewLines(stream);
}
newNode->mFileName = mCurrentFileName;
break;
}
//Out of place brace
case TOKEN_OpenBrace:
throw std::runtime_error("Parse Error: Opening brace out of plane");
break;
//Return if end of nodes have been reached
case TOKEN_CloseBrace:
return;
//Return if reached end of file
case TOKEN_EOF:
return;
case TOKEN_NewLine:
_nextToken(stream);
break;
}
};
}
ScriptNode::ScriptNode(ScriptNode *parent, const std::string &name)
{
mName = name;
mParent = parent;
mRemoveSelf = true; //For proper destruction
mLastChildFound = -1;
//Add self to parent's child list (unless this is the root node being created)
if (parent != NULL)
{
mParent->mChildren.push_back(this);
mIter = --(mParent->mChildren.end());
}
}
ScriptNode::~ScriptNode()
{
//Delete all children
std::vector<ScriptNode*>::iterator i;
for (i = mChildren.begin(); i != mChildren.end(); ++i)
{
ScriptNode *node = *i;
node->mRemoveSelf = false;
delete node;
}
mChildren.clear();
//Remove self from parent's child list
if (mRemoveSelf && mParent != NULL)
{
mParent->mChildren.erase(mIter);
}
}
ScriptNode *ScriptNode::addChild(const std::string &name, bool replaceExisting)
{
if (replaceExisting)
{
ScriptNode *node = findChild(name, false);
if (node)
{
return node;
}
}
return new ScriptNode(this, name);
}
ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive)
{
int indx;
int childCount = (int)mChildren.size();
if (mLastChildFound != -1)
{
//If possible, try checking the nodes neighboring the last successful search
//(often nodes searched for in sequence, so this will boost search speeds).
int prevC = mLastChildFound-1;
if (prevC < 0)
prevC = 0;
else if (prevC >= childCount)
prevC = childCount-1;
int nextC = mLastChildFound+1;
if (nextC < 0)
nextC = 0;
else if (nextC >= childCount)
nextC = childCount-1;
for (indx = prevC; indx <= nextC; ++indx)
{
ScriptNode *node = mChildren[indx];
if (node->mName == name)
{
mLastChildFound = indx;
return node;
}
}
//If not found that way, search for the node from start to finish, avoiding the
//already searched area above.
for (indx = nextC + 1; indx < childCount; ++indx)
{
ScriptNode *node = mChildren[indx];
if (node->mName == name) {
mLastChildFound = indx;
return node;
}
}
for (indx = 0; indx < prevC; ++indx)
{
ScriptNode *node = mChildren[indx];
if (node->mName == name) {
mLastChildFound = indx;
return node;
}
}
}
else
{
//Search for the node from start to finish
for (indx = 0; indx < childCount; ++indx){
ScriptNode *node = mChildren[indx];
if (node->mName == name) {
mLastChildFound = indx;
return node;
}
}
}
//If not found, search child nodes (if recursive == true)
if (recursive)
{
for (indx = 0; indx < childCount; ++indx)
{
mChildren[indx]->findChild(name, recursive);
}
}
//Not found anywhere
return NULL;
}
void ScriptNode::setParent(ScriptNode *newParent)
{
//Remove self from current parent
mParent->mChildren.erase(mIter);
//Set new parent
mParent = newParent;
//Add self to new parent
mParent->mChildren.push_back(this);
mIter = --(mParent->mChildren.end());
}
}

134
extern/shiny/Main/ScriptLoader.hpp vendored Normal file
View file

@ -0,0 +1,134 @@
#ifndef SH_CONFIG_LOADER_H__
#define SH_CONFIG_LOADER_H__
#include <map>
#include <vector>
#include <cassert>
#include <string>
namespace sh
{
class ScriptNode;
/**
* @brief The base class of loaders that read Ogre style script files to get configuration and settings.
* Heavily inspired by: http://www.ogre3d.org/tikiwiki/All-purpose+script+parser
* ( "Non-ogre version")
*/
class ScriptLoader
{
public:
static void loadAllFiles(ScriptLoader* c, const std::string& path);
ScriptLoader(const std::string& fileEnding);
virtual ~ScriptLoader();
std::string mFileEnding;
// For a line like
// entity animals/dog
// {
// ...
// }
// The type is "entity" and the name is "animals/dog"
// Or if animal/dog was not there then name is ""
ScriptNode *getConfigScript (const std::string &name);
std::map <std::string, ScriptNode*> getAllConfigScripts ();
void parseScript(std::ifstream &stream);
std::string mCurrentFileName;
protected:
float mLoadOrder;
// like "*.object"
std::map <std::string, ScriptNode*> m_scriptList;
enum Token
{
TOKEN_Text,
TOKEN_NewLine,
TOKEN_OpenBrace,
TOKEN_CloseBrace,
TOKEN_EOF
};
Token mToken, mLastToken;
std::string mTokenValue;
void _parseNodes(std::ifstream &stream, ScriptNode *parent);
void _nextToken(std::ifstream &stream);
void _skipNewLines(std::ifstream &stream);
void clearScriptList();
};
class ScriptNode
{
public:
ScriptNode(ScriptNode *parent, const std::string &name = "untitled");
~ScriptNode();
inline void setName(const std::string &name)
{
this->mName = name;
}
inline std::string &getName()
{
return mName;
}
inline void setValue(const std::string &value)
{
mValue = value;
}
inline std::string &getValue()
{
return mValue;
}
ScriptNode *addChild(const std::string &name = "untitled", bool replaceExisting = false);
ScriptNode *findChild(const std::string &name, bool recursive = false);
inline std::vector<ScriptNode*> &getChildren()
{
return mChildren;
}
inline ScriptNode *getChild(unsigned int index = 0)
{
assert(index < mChildren.size());
return mChildren[index];
}
void setParent(ScriptNode *newParent);
inline ScriptNode *getParent()
{
return mParent;
}
std::string mFileName;
private:
std::string mName;
std::string mValue;
std::vector<ScriptNode*> mChildren;
ScriptNode *mParent;
int mLastChildFound; //The last child node's index found with a call to findChild()
std::vector<ScriptNode*>::iterator mIter;
bool mRemoveSelf;
};
}
#endif

698
extern/shiny/Main/ShaderInstance.cpp vendored Normal file
View file

@ -0,0 +1,698 @@
#include "ShaderInstance.hpp"
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include "Preprocessor.hpp"
#include "Factory.hpp"
#include "ShaderSet.hpp"
namespace
{
std::string convertLang (sh::Language lang)
{
if (lang == sh::Language_CG)
return "SH_CG";
else if (lang == sh::Language_HLSL)
return "SH_HLSL";
else if (lang == sh::Language_GLSL)
return "SH_GLSL";
else if (lang == sh::Language_GLSLES)
return "SH_GLSLES";
throw std::runtime_error("invalid language");
}
char getComponent(int num)
{
if (num == 0)
return 'x';
else if (num == 1)
return 'y';
else if (num == 2)
return 'z';
else if (num == 3)
return 'w';
else
throw std::runtime_error("invalid component");
}
std::string getFloat(sh::Language lang, int num_components)
{
if (lang == sh::Language_CG || lang == sh::Language_HLSL)
return (num_components == 1) ? "float" : "float" + boost::lexical_cast<std::string>(num_components);
else
return (num_components == 1) ? "float" : "vec" + boost::lexical_cast<std::string>(num_components);
}
bool isCmd (const std::string& source, size_t pos, const std::string& cmd)
{
return (source.size() >= pos + cmd.size() && source.substr(pos, cmd.size()) == cmd);
}
void writeDebugFile (const std::string& content, const std::string& filename)
{
boost::filesystem::path full_path(boost::filesystem::current_path());
std::ofstream of ((full_path / filename ).string().c_str() , std::ios_base::out);
of.write(content.c_str(), content.size());
of.close();
}
}
namespace sh
{
std::string Passthrough::expand_assign(std::string toAssign)
{
std::string res;
int i = 0;
int current_passthrough = passthrough_number;
int current_component_left = component_start;
int current_component_right = 0;
int components_left = num_components;
int components_at_once;
while (i < num_components)
{
if (components_left + current_component_left <= 4)
components_at_once = components_left;
else
components_at_once = 4 - current_component_left;
std::string componentStr = ".";
for (int j = 0; j < components_at_once; ++j)
componentStr += getComponent(j + current_component_left);
std::string componentStr2 = ".";
for (int j = 0; j < components_at_once; ++j)
componentStr2 += getComponent(j + current_component_right);
if (num_components == 1)
{
componentStr2 = "";
}
res += "passthrough" + boost::lexical_cast<std::string>(current_passthrough) + componentStr + " = " + toAssign + componentStr2;
current_component_left += components_at_once;
current_component_right += components_at_once;
components_left -= components_at_once;
i += components_at_once;
if (components_left == 0)
{
// finished
return res;
}
else
{
// add semicolon to every instruction but the last
res += "; ";
}
if (current_component_left == 4)
{
current_passthrough++;
current_component_left = 0;
}
}
throw std::runtime_error("expand_assign error"); // this should never happen, but gets us rid of the "control reaches end of non-void function" warning
}
std::string Passthrough::expand_receive()
{
std::string res;
res += getFloat(lang, num_components) + "(";
int i = 0;
int current_passthrough = passthrough_number;
int current_component = component_start;
int components_left = num_components;
while (i < num_components)
{
int components_at_once = std::min(components_left, 4 - current_component);
std::string componentStr;
for (int j = 0; j < components_at_once; ++j)
componentStr += getComponent(j + current_component);
res += "passthrough" + boost::lexical_cast<std::string>(current_passthrough) + "." + componentStr;
current_component += components_at_once;
components_left -= components_at_once;
i += components_at_once;
if (components_left == 0)
{
// finished
return res + ")";
;
}
else
{
// add comma to every variable but the last
res += ", ";
}
if (current_component == 4)
{
current_passthrough++;
current_component = 0;
}
}
throw std::runtime_error("expand_receive error"); // this should never happen, but gets us rid of the "control reaches end of non-void function" warning
}
// ------------------------------------------------------------------------------
void ShaderInstance::parse (std::string& source, PropertySetGet* properties)
{
size_t pos = 0;
while (true)
{
pos = source.find("@", pos);
if (pos == std::string::npos)
break;
if (isCmd(source, pos, "@shProperty"))
{
std::vector<std::string> args = extractMacroArguments (pos, source);
size_t start = source.find("(", pos);
size_t end = source.find(")", pos);
std::string cmd = source.substr(pos+1, start-(pos+1));
std::string replaceValue;
if (cmd == "shPropertyBool")
{
std::string propertyName = args[0];
PropertyValuePtr value = properties->getProperty(propertyName);
bool val = retrieveValue<BooleanValue>(value, properties->getContext()).get();
replaceValue = val ? "1" : "0";
}
else if (cmd == "shPropertyString")
{
std::string propertyName = args[0];
PropertyValuePtr value = properties->getProperty(propertyName);
replaceValue = retrieveValue<StringValue>(value, properties->getContext()).get();
}
else if (cmd == "shPropertyEqual")
{
std::string propertyName = args[0];
std::string comparedAgainst = args[1];
std::string value = retrieveValue<StringValue>(properties->getProperty(propertyName), properties->getContext()).get();
replaceValue = (value == comparedAgainst) ? "1" : "0";
}
else if (isCmd(source, pos, "@shPropertyHasValue"))
{
assert(args.size() == 1);
std::string propertyName = args[0];
PropertyValuePtr value = properties->getProperty(propertyName);
std::string val = retrieveValue<StringValue>(value, properties->getContext()).get();
replaceValue = (val.empty() ? "0" : "1");
}
else
throw std::runtime_error ("unknown command \"" + cmd + "\"");
source.replace(pos, (end+1)-pos, replaceValue);
}
else if (isCmd(source, pos, "@shGlobalSetting"))
{
std::vector<std::string> args = extractMacroArguments (pos, source);
std::string cmd = source.substr(pos+1, source.find("(", pos)-(pos+1));
std::string replaceValue;
if (cmd == "shGlobalSettingBool")
{
std::string settingName = args[0];
std::string value = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
replaceValue = (value == "true" || value == "1") ? "1" : "0";
}
else if (cmd == "shGlobalSettingEqual")
{
std::string settingName = args[0];
std::string comparedAgainst = args[1];
std::string value = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
replaceValue = (value == comparedAgainst) ? "1" : "0";
}
else if (cmd == "shGlobalSettingString")
{
std::string settingName = args[0];
replaceValue = retrieveValue<StringValue>(mParent->getCurrentGlobalSettings()->getProperty(settingName), NULL).get();
}
else
throw std::runtime_error ("unknown command \"" + cmd + "\"");
source.replace(pos, (source.find(")", pos)+1)-pos, replaceValue);
}
else if (isCmd(source, pos, "@shForeach"))
{
assert(source.find("@shEndForeach", pos) != std::string::npos);
size_t block_end = source.find("@shEndForeach", pos);
// get the argument for parsing
size_t start = source.find("(", pos);
size_t end = start;
int brace_depth = 1;
while (brace_depth > 0)
{
++end;
if (source[end] == '(')
++brace_depth;
else if (source[end] == ')')
--brace_depth;
}
std::string arg = source.substr(start+1, end-(start+1));
parse(arg, properties);
int num = boost::lexical_cast<int>(arg);
// get the content of the inner block
std::string content = source.substr(end+1, block_end - (end+1));
// replace both outer and inner block with content of inner block num times
std::string replaceStr;
for (int i=0; i<num; ++i)
{
// replace @shIterator with the current iteration
std::string addStr = content;
while (true)
{
size_t pos2 = addStr.find("@shIterator");
if (pos2 == std::string::npos)
break;
// optional offset parameter.
size_t openBracePos = pos2 + std::string("@shIterator").length();
if (addStr[openBracePos] == '(')
{
// get the argument for parsing
size_t _start = openBracePos;
size_t _end = _start;
int _brace_depth = 1;
while (_brace_depth > 0)
{
++_end;
if (addStr[_end] == '(')
++_brace_depth;
else if (addStr[_end] == ')')
--_brace_depth;
}
std::string arg = addStr.substr(_start+1, _end-(_start+1));
parse(arg, properties);
int offset = boost::lexical_cast<int> (arg);
addStr.replace(pos2, (_end+1)-pos2, boost::lexical_cast<std::string>(i+offset));
}
else
{
addStr.replace(pos2, std::string("@shIterator").length(), boost::lexical_cast<std::string>(i));
}
}
replaceStr += addStr;
}
source.replace(pos, (block_end+std::string("@shEndForeach").length())-pos, replaceStr);
}
else if (source.size() > pos+1)
++pos; // skip
}
}
ShaderInstance::ShaderInstance (ShaderSet* parent, const std::string& name, PropertySetGet* properties)
: mName(name)
, mParent(parent)
, mSupported(true)
, mCurrentPassthrough(0)
, mCurrentComponent(0)
{
std::string source = mParent->getSource();
int type = mParent->getType();
std::string basePath = mParent->getBasePath();
size_t pos;
bool readCache = Factory::getInstance ().getReadSourceCache () && boost::filesystem::exists(
Factory::getInstance ().getCacheFolder () + "/" + mName);
bool writeCache = Factory::getInstance ().getWriteSourceCache ();
if (readCache)
{
std::ifstream ifs( std::string(Factory::getInstance ().getCacheFolder () + "/" + mName).c_str() );
std::stringstream ss;
ss << ifs.rdbuf();
source = ss.str();
}
else
{
std::vector<std::string> definitions;
if (mParent->getType() == GPT_Vertex)
definitions.push_back("SH_VERTEX_SHADER");
else
definitions.push_back("SH_FRAGMENT_SHADER");
definitions.push_back(convertLang(Factory::getInstance().getCurrentLanguage()));
parse(source, properties);
if (Factory::getInstance ().getShaderDebugOutputEnabled ())
writeDebugFile(source, name + ".pre");
// why do we need our own preprocessor? there are several custom commands available in the shader files
// (for example for binding uniforms to properties or auto constants) - more below. it is important that these
// commands are _only executed if the specific code path actually "survives" the compilation.
// thus, we run the code through a preprocessor first to remove the parts that are unused because of
// unmet #if conditions (or other preprocessor directives).
source = Preprocessor::preprocess(source, basePath, definitions, name);
// parse counter
std::map<int, int> counters;
while (true)
{
pos = source.find("@shCounter");
if (pos == std::string::npos)
break;
size_t end = source.find(")", pos);
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size());
int index = boost::lexical_cast<int>(args[0]);
if (counters.find(index) == counters.end())
counters[index] = 0;
source.replace(pos, (end+1)-pos, boost::lexical_cast<std::string>(counters[index]++));
}
// parse passthrough declarations
while (true)
{
pos = source.find("@shAllocatePassthrough");
if (pos == std::string::npos)
break;
if (mCurrentPassthrough > 7)
throw std::runtime_error ("too many passthrough's requested (max 8)");
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size() == 2);
size_t end = source.find(")", pos);
Passthrough passthrough;
passthrough.num_components = boost::lexical_cast<int>(args[0]);
assert (passthrough.num_components != 0);
std::string passthroughName = args[1];
passthrough.lang = Factory::getInstance().getCurrentLanguage ();
passthrough.component_start = mCurrentComponent;
passthrough.passthrough_number = mCurrentPassthrough;
mPassthroughMap[passthroughName] = passthrough;
mCurrentComponent += passthrough.num_components;
if (mCurrentComponent > 3)
{
mCurrentComponent -= 4;
++mCurrentPassthrough;
}
source.erase(pos, (end+1)-pos);
}
// passthrough assign
while (true)
{
pos = source.find("@shPassthroughAssign");
if (pos == std::string::npos)
break;
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size() == 2);
size_t end = source.find(")", pos);
std::string passthroughName = args[0];
std::string assignTo = args[1];
assert(mPassthroughMap.find(passthroughName) != mPassthroughMap.end());
Passthrough& p = mPassthroughMap[passthroughName];
source.replace(pos, (end+1)-pos, p.expand_assign(assignTo));
}
// passthrough receive
while (true)
{
pos = source.find("@shPassthroughReceive");
if (pos == std::string::npos)
break;
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size() == 1);
size_t end = source.find(")", pos);
std::string passthroughName = args[0];
assert(mPassthroughMap.find(passthroughName) != mPassthroughMap.end());
Passthrough& p = mPassthroughMap[passthroughName];
source.replace(pos, (end+1)-pos, p.expand_receive());
}
// passthrough vertex outputs
while (true)
{
pos = source.find("@shPassthroughVertexOutputs");
if (pos == std::string::npos)
break;
std::string result;
for (int i = 0; i < mCurrentPassthrough+1; ++i)
{
// not using newlines here, otherwise the line numbers reported by compiler would be messed up..
if (Factory::getInstance().getCurrentLanguage () == Language_CG || Factory::getInstance().getCurrentLanguage () == Language_HLSL)
result += ", out float4 passthrough" + boost::lexical_cast<std::string>(i) + " : TEXCOORD" + boost::lexical_cast<std::string>(i);
/*
else
result += "out vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
*/
else
result += "varying vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
}
source.replace(pos, std::string("@shPassthroughVertexOutputs").length(), result);
}
// passthrough fragment inputs
while (true)
{
pos = source.find("@shPassthroughFragmentInputs");
if (pos == std::string::npos)
break;
std::string result;
for (int i = 0; i < mCurrentPassthrough+1; ++i)
{
// not using newlines here, otherwise the line numbers reported by compiler would be messed up..
if (Factory::getInstance().getCurrentLanguage () == Language_CG || Factory::getInstance().getCurrentLanguage () == Language_HLSL)
result += ", in float4 passthrough" + boost::lexical_cast<std::string>(i) + " : TEXCOORD" + boost::lexical_cast<std::string>(i);
/*
else
result += "in vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
*/
else
result += "varying vec4 passthrough" + boost::lexical_cast<std::string>(i) + "; ";
}
source.replace(pos, std::string("@shPassthroughFragmentInputs").length(), result);
}
}
// save to cache _here_ - we want to preserve some macros
if (writeCache && !readCache)
{
std::ofstream of (std::string(Factory::getInstance ().getCacheFolder () + "/" + mName).c_str(), std::ios_base::out);
of.write(source.c_str(), source.size());
of.close();
}
// parse shared parameters
while (true)
{
pos = source.find("@shSharedParameter");
if (pos == std::string::npos)
break;
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size());
size_t end = source.find(")", pos);
mSharedParameters.push_back(args[0]);
source.erase(pos, (end+1)-pos);
}
// parse auto constants
typedef std::map< std::string, std::pair<std::string, std::string> > AutoConstantMap;
AutoConstantMap autoConstants;
while (true)
{
pos = source.find("@shAutoConstant");
if (pos == std::string::npos)
break;
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size() >= 2);
size_t end = source.find(")", pos);
std::string autoConstantName, uniformName;
std::string extraData;
uniformName = args[0];
autoConstantName = args[1];
if (args.size() > 2)
extraData = args[2];
autoConstants[uniformName] = std::make_pair(autoConstantName, extraData);
source.erase(pos, (end+1)-pos);
}
// parse uniform properties
while (true)
{
pos = source.find("@shUniformProperty");
if (pos == std::string::npos)
break;
std::vector<std::string> args = extractMacroArguments (pos, source);
assert(args.size() == 2);
size_t start = source.find("(", pos);
size_t end = source.find(")", pos);
std::string cmd = source.substr(pos, start-pos);
ValueType vt;
if (cmd == "@shUniformProperty4f")
vt = VT_Vector4;
else if (cmd == "@shUniformProperty3f")
vt = VT_Vector3;
else if (cmd == "@shUniformProperty2f")
vt = VT_Vector2;
else if (cmd == "@shUniformProperty1f")
vt = VT_Float;
else if (cmd == "@shUniformPropertyInt")
vt = VT_Int;
else
throw std::runtime_error ("unsupported command \"" + cmd + "\"");
std::string propertyName, uniformName;
uniformName = args[0];
propertyName = args[1];
mUniformProperties[uniformName] = std::make_pair(propertyName, vt);
source.erase(pos, (end+1)-pos);
}
// parse texture samplers used
while (true)
{
pos = source.find("@shUseSampler");
if (pos == std::string::npos)
break;
size_t end = source.find(")", pos);
mUsedSamplers.push_back(extractMacroArguments (pos, source)[0]);
source.erase(pos, (end+1)-pos);
}
// convert any left-over @'s to #
boost::algorithm::replace_all(source, "@", "#");
Platform* platform = Factory::getInstance().getPlatform();
std::string profile;
if (Factory::getInstance ().getCurrentLanguage () == Language_CG)
profile = mParent->getCgProfile ();
else if (Factory::getInstance ().getCurrentLanguage () == Language_HLSL)
profile = mParent->getHlslProfile ();
if (type == GPT_Vertex)
mProgram = boost::shared_ptr<GpuProgram>(platform->createGpuProgram(GPT_Vertex, "", mName, profile, source, Factory::getInstance().getCurrentLanguage()));
else if (type == GPT_Fragment)
mProgram = boost::shared_ptr<GpuProgram>(platform->createGpuProgram(GPT_Fragment, "", mName, profile, source, Factory::getInstance().getCurrentLanguage()));
if (Factory::getInstance ().getShaderDebugOutputEnabled ())
writeDebugFile(source, name);
if (!mProgram->getSupported())
{
std::cerr << " Full source code below: \n" << source << std::endl;
mSupported = false;
return;
}
// set auto constants
for (AutoConstantMap::iterator it = autoConstants.begin(); it != autoConstants.end(); ++it)
{
mProgram->setAutoConstant(it->first, it->second.first, it->second.second);
}
}
std::string ShaderInstance::getName ()
{
return mName;
}
bool ShaderInstance::getSupported () const
{
return mSupported;
}
std::vector<std::string> ShaderInstance::getUsedSamplers()
{
return mUsedSamplers;
}
void ShaderInstance::setUniformParameters (boost::shared_ptr<Pass> pass, PropertySetGet* properties)
{
for (UniformMap::iterator it = mUniformProperties.begin(); it != mUniformProperties.end(); ++it)
{
pass->setGpuConstant(mParent->getType(), it->first, it->second.second, properties->getProperty(it->second.first), properties->getContext());
}
}
std::vector<std::string> ShaderInstance::extractMacroArguments (size_t pos, const std::string& source)
{
size_t start = source.find("(", pos);
size_t end = source.find(")", pos);
std::string args = source.substr(start+1, end-(start+1));
std::vector<std::string> results;
boost::algorithm::split(results, args, boost::is_any_of(","));
std::for_each(results.begin(), results.end(),
boost::bind(&boost::trim<std::string>,
_1, std::locale() ));
return results;
}
}

71
extern/shiny/Main/ShaderInstance.hpp vendored Normal file
View file

@ -0,0 +1,71 @@
#ifndef SH_SHADERINSTANCE_H
#define SH_SHADERINSTANCE_H
#include <vector>
#include "Platform.hpp"
namespace sh
{
class ShaderSet;
typedef std::map< std::string, std::pair<std::string, ValueType > > UniformMap;
struct Passthrough
{
Language lang; ///< language to generate for
int num_components; ///< e.g. 4 for a float4
int passthrough_number;
int component_start; ///< 0 = x
std::string expand_assign(std::string assignTo);
std::string expand_receive();
};
typedef std::map<std::string, Passthrough> PassthroughMap;
/**
* @brief A specific instance of a \a ShaderSet with a deterministic shader source
*/
class ShaderInstance
{
public:
ShaderInstance (ShaderSet* parent, const std::string& name, PropertySetGet* properties);
std::string getName();
bool getSupported () const;
std::vector<std::string> getUsedSamplers();
std::vector<std::string> getSharedParameters() { return mSharedParameters; }
void setUniformParameters (boost::shared_ptr<Pass> pass, PropertySetGet* properties);
private:
boost::shared_ptr<GpuProgram> mProgram;
std::string mName;
ShaderSet* mParent;
bool mSupported; ///< shader compilation was sucessful?
std::vector<std::string> mUsedSamplers;
///< names of the texture samplers that are used by this shader
std::vector<std::string> mSharedParameters;
UniformMap mUniformProperties;
///< uniforms that this depends on, and their property names / value-types
/// @note this lists shared uniform parameters as well
int mCurrentPassthrough; ///< 0 - x
int mCurrentComponent; ///< 0:x, 1:y, 2:z, 3:w
PassthroughMap mPassthroughMap;
std::vector<std::string> extractMacroArguments (size_t pos, const std::string& source); ///< take a macro invocation and return vector of arguments
void parse (std::string& source, PropertySetGet* properties);
};
}
#endif

195
extern/shiny/Main/ShaderSet.cpp vendored Normal file
View file

@ -0,0 +1,195 @@
#include "ShaderSet.hpp"
#include <fstream>
#include <sstream>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include "Factory.hpp"
namespace sh
{
ShaderSet::ShaderSet (const std::string& type, const std::string& cgProfile, const std::string& hlslProfile, const std::string& sourceFile, const std::string& basePath,
const std::string& name, PropertySetGet* globalSettingsPtr)
: mBasePath(basePath)
, mName(name)
, mCgProfile(cgProfile)
, mHlslProfile(hlslProfile)
{
if (type == "vertex")
mType = GPT_Vertex;
else // if (type == "fragment")
mType = GPT_Fragment;
std::ifstream stream(sourceFile.c_str(), std::ifstream::in);
std::stringstream buffer;
boost::filesystem::path p (sourceFile);
p = p.branch_path();
mBasePath = p.string();
buffer << stream.rdbuf();
stream.close();
mSource = buffer.str();
parse();
}
ShaderSet::~ShaderSet()
{
for (ShaderInstanceMap::iterator it = mInstances.begin(); it != mInstances.end(); ++it)
{
sh::Factory::getInstance().getPlatform()->destroyGpuProgram(it->second.getName());
}
}
void ShaderSet::parse()
{
std::string currentToken;
bool tokenIsRecognized = false;
bool isInBraces = false;
for (std::string::const_iterator it = mSource.begin(); it != mSource.end(); ++it)
{
char c = *it;
if (((c == ' ') && !isInBraces) || (c == '\n') ||
( ((c == '(') || (c == ')'))
&& !tokenIsRecognized))
{
if (tokenIsRecognized)
{
if (boost::starts_with(currentToken, "@shGlobalSetting"))
{
assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
size_t start = currentToken.find('(')+1;
mGlobalSettings.push_back(currentToken.substr(start, currentToken.find(')')-start));
}
else if (boost::starts_with(currentToken, "@shPropertyHasValue"))
{
assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
size_t start = currentToken.find('(')+1;
mPropertiesToExist.push_back(currentToken.substr(start, currentToken.find(')')-start));
}
else if (boost::starts_with(currentToken, "@shPropertyEqual"))
{
assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos)
&& (currentToken.find(',') != std::string::npos));
size_t start = currentToken.find('(')+1;
size_t end = currentToken.find(',');
mProperties.push_back(currentToken.substr(start, end-start));
}
else if (boost::starts_with(currentToken, "@shProperty"))
{
assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
size_t start = currentToken.find('(')+1;
std::string propertyName = currentToken.substr(start, currentToken.find(')')-start);
// if the property name is constructed dynamically (e.g. through an iterator) then there is nothing we can do
if (propertyName.find("@") == std::string::npos)
mProperties.push_back(propertyName);
}
}
currentToken = "";
}
else
{
if (currentToken == "")
{
if (c == '@')
tokenIsRecognized = true;
else
tokenIsRecognized = false;
}
else
{
if (c == '@')
{
// ouch, there are nested macros
// ( for example @shForeach(@shPropertyString(foobar)) )
currentToken = "";
}
}
if (c == '(' && tokenIsRecognized)
isInBraces = true;
else if (c == ')' && tokenIsRecognized)
isInBraces = false;
currentToken += c;
}
}
}
ShaderInstance* ShaderSet::getInstance (PropertySetGet* properties)
{
size_t h = buildHash (properties);
if (std::find(mFailedToCompile.begin(), mFailedToCompile.end(), h) != mFailedToCompile.end())
return NULL;
if (mInstances.find(h) == mInstances.end())
{
ShaderInstance newInstance(this, mName + "_" + boost::lexical_cast<std::string>(h), properties);
if (!newInstance.getSupported())
{
mFailedToCompile.push_back(h);
return NULL;
}
mInstances.insert(std::make_pair(h, newInstance));
}
return &mInstances.find(h)->second;
}
size_t ShaderSet::buildHash (PropertySetGet* properties)
{
size_t seed = 0;
PropertySetGet* currentGlobalSettings = getCurrentGlobalSettings ();
for (std::vector<std::string>::iterator it = mProperties.begin(); it != mProperties.end(); ++it)
{
std::string v = retrieveValue<StringValue>(properties->getProperty(*it), properties->getContext()).get();
boost::hash_combine(seed, v);
}
for (std::vector <std::string>::iterator it = mGlobalSettings.begin(); it != mGlobalSettings.end(); ++it)
{
boost::hash_combine(seed, retrieveValue<StringValue>(currentGlobalSettings->getProperty(*it), NULL).get());
}
for (std::vector<std::string>::iterator it = mPropertiesToExist.begin(); it != mPropertiesToExist.end(); ++it)
{
std::string v = retrieveValue<StringValue>(properties->getProperty(*it), properties->getContext()).get();
boost::hash_combine(seed, static_cast<bool>(v != ""));
}
boost::hash_combine(seed, static_cast<int>(Factory::getInstance().getCurrentLanguage()));
return seed;
}
PropertySetGet* ShaderSet::getCurrentGlobalSettings() const
{
return Factory::getInstance ().getCurrentGlobalSettings ();
}
std::string ShaderSet::getBasePath() const
{
return mBasePath;
}
std::string ShaderSet::getSource() const
{
return mSource;
}
std::string ShaderSet::getCgProfile() const
{
return mCgProfile;
}
std::string ShaderSet::getHlslProfile() const
{
return mHlslProfile;
}
int ShaderSet::getType() const
{
return mType;
}
}

69
extern/shiny/Main/ShaderSet.hpp vendored Normal file
View file

@ -0,0 +1,69 @@
#ifndef SH_SHADERSET_H
#define SH_SHADERSET_H
#include <string>
#include <vector>
#include <map>
#include "ShaderInstance.hpp"
namespace sh
{
class PropertySetGet;
typedef std::map<size_t, ShaderInstance> ShaderInstanceMap;
/**
* @brief Contains possible shader permutations of a single uber-shader (represented by one source file)
*/
class ShaderSet
{
public:
ShaderSet (const std::string& type, const std::string& cgProfile, const std::string& hlslProfile, const std::string& sourceFile, const std::string& basePath,
const std::string& name, PropertySetGet* globalSettingsPtr);
~ShaderSet();
/// Retrieve a shader instance for the given properties. \n
/// If a \a ShaderInstance with the same properties exists already, simply returns this instance. \n
/// Otherwise, creates a new \a ShaderInstance (i.e. compiles a new shader). \n
/// Might also return NULL if the shader failed to compile. \n
/// @note Only the properties that actually affect the shader source are taken into consideration here,
/// so it does not matter if you pass any extra properties that the shader does not care about.
ShaderInstance* getInstance (PropertySetGet* properties);
private:
PropertySetGet* getCurrentGlobalSettings() const;
std::string getBasePath() const;
std::string getSource() const;
std::string getCgProfile() const;
std::string getHlslProfile() const;
int getType() const;
friend class ShaderInstance;
private:
GpuProgramType mType;
std::string mSource;
std::string mBasePath;
std::string mCgProfile;
std::string mHlslProfile;
std::string mName;
std::vector <size_t> mFailedToCompile;
std::vector <std::string> mGlobalSettings; ///< names of the global settings that affect the shader source
std::vector <std::string> mProperties; ///< names of the per-material properties that affect the shader source
std::vector <std::string> mPropertiesToExist;
///< same as mProperties, however in this case, it is only relevant if the property is empty or not
/// (we don't care about the value)
ShaderInstanceMap mInstances; ///< maps permutation ID (generated from the properties) to \a ShaderInstance
void parse(); ///< find out which properties and global settings affect the shader source
size_t buildHash (PropertySetGet* properties);
};
}
#endif

View file

@ -0,0 +1,69 @@
#include <stdexcept>
#include "OgreGpuProgram.hpp"
#include <boost/lexical_cast.hpp>
#include <OgreHighLevelGpuProgramManager.h>
#include <OgreGpuProgramManager.h>
#include <OgreVector4.h>
namespace sh
{
OgreGpuProgram::OgreGpuProgram(
GpuProgramType type,
const std::string& compileArguments,
const std::string& name, const std::string& profile,
const std::string& source, const std::string& lang,
const std::string& resourceGroup)
: GpuProgram()
{
Ogre::HighLevelGpuProgramManager& mgr = Ogre::HighLevelGpuProgramManager::getSingleton();
assert (mgr.getByName(name).isNull() && "Vertex program already exists");
Ogre::GpuProgramType t;
if (type == GPT_Vertex)
t = Ogre::GPT_VERTEX_PROGRAM;
else
t = Ogre::GPT_FRAGMENT_PROGRAM;
mProgram = mgr.createProgram(name, resourceGroup, lang, t);
if (lang != "glsl" && lang != "glsles")
mProgram->setParameter("entry_point", "main");
if (lang == "hlsl")
mProgram->setParameter("target", profile);
else if (lang == "cg")
mProgram->setParameter("profiles", profile);
mProgram->setSource(source);
mProgram->load();
if (mProgram.isNull() || !mProgram->isSupported())
std::cerr << "Failed to compile shader \"" << name << "\". Consider the OGRE log for more information." << std::endl;
}
bool OgreGpuProgram::getSupported()
{
return (!mProgram.isNull() && mProgram->isSupported());
}
void OgreGpuProgram::setAutoConstant (const std::string& name, const std::string& autoConstantName, const std::string& extraInfo)
{
assert (!mProgram.isNull() && mProgram->isSupported());
const Ogre::GpuProgramParameters::AutoConstantDefinition* d = Ogre::GpuProgramParameters::getAutoConstantDefinition(autoConstantName);
if (!d)
throw std::runtime_error ("can't find auto constant with name \"" + autoConstantName + "\"");
Ogre::GpuProgramParameters::AutoConstantType t = d->acType;
// this simplifies debugging for CG a lot.
mProgram->getDefaultParameters()->setIgnoreMissingParams(true);
if (d->dataType == Ogre::GpuProgramParameters::ACDT_NONE)
mProgram->getDefaultParameters()->setNamedAutoConstant (name, t, 0);
else if (d->dataType == Ogre::GpuProgramParameters::ACDT_INT)
mProgram->getDefaultParameters()->setNamedAutoConstant (name, t, extraInfo == "" ? 0 : boost::lexical_cast<int>(extraInfo));
else if (d->dataType == Ogre::GpuProgramParameters::ACDT_REAL)
mProgram->getDefaultParameters()->setNamedAutoConstantReal (name, t, extraInfo == "" ? 0.f : boost::lexical_cast<float>(extraInfo));
}
}

View file

@ -0,0 +1,31 @@
#ifndef SH_OGREGPUPROGRAM_H
#define SH_OGREGPUPROGRAM_H
#include <string>
#include <OgreHighLevelGpuProgram.h>
#include "../../Main/Platform.hpp"
namespace sh
{
class OgreGpuProgram : public GpuProgram
{
public:
OgreGpuProgram (
GpuProgramType type,
const std::string& compileArguments,
const std::string& name, const std::string& profile,
const std::string& source, const std::string& lang,
const std::string& resourceGroup);
virtual bool getSupported();
virtual void setAutoConstant (const std::string& name, const std::string& autoConstantName, const std::string& extraInfo = "");
private:
Ogre::HighLevelGpuProgramPtr mProgram;
};
}
#endif

View file

@ -0,0 +1,115 @@
#include "OgreMaterial.hpp"
#include <OgreMaterialManager.h>
#include <OgreTechnique.h>
#include <stdexcept>
#include "OgrePass.hpp"
#include "OgreMaterialSerializer.hpp"
#include "OgrePlatform.hpp"
namespace sh
{
static const std::string sDefaultTechniqueName = "SH_DefaultTechnique";
OgreMaterial::OgreMaterial (const std::string& name, const std::string& resourceGroup)
: Material()
{
mName = name;
assert (Ogre::MaterialManager::getSingleton().getByName(name).isNull() && "Material already exists");
mMaterial = Ogre::MaterialManager::getSingleton().create (name, resourceGroup);
mMaterial->removeAllTechniques();
mMaterial->createTechnique()->setSchemeName (sDefaultTechniqueName);
mMaterial->compile();
}
void OgreMaterial::ensureLoaded()
{
if (mMaterial.isNull())
mMaterial = Ogre::MaterialManager::getSingleton().getByName(mName);
}
bool OgreMaterial::isUnreferenced()
{
// Resource system internals hold 3 shared pointers, we hold one, so usecount of 4 means unused
return (!mMaterial.isNull() && mMaterial.useCount() <= Ogre::ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS+1);
}
OgreMaterial::~OgreMaterial()
{
if (!mMaterial.isNull())
Ogre::MaterialManager::getSingleton().remove(mMaterial->getName());
}
boost::shared_ptr<Pass> OgreMaterial::createPass (const std::string& configuration, unsigned short lodIndex)
{
return boost::shared_ptr<Pass> (new OgrePass (this, configuration, lodIndex));
}
void OgreMaterial::removeAll ()
{
if (mMaterial.isNull())
return;
mMaterial->removeAllTechniques();
mMaterial->createTechnique()->setSchemeName (sDefaultTechniqueName);
mMaterial->compile();
}
void OgreMaterial::setLodLevels (const std::string& lodLevels)
{
OgreMaterialSerializer& s = OgrePlatform::getSerializer();
s.setMaterialProperty ("lod_values", lodLevels, mMaterial);
}
bool OgreMaterial::createConfiguration (const std::string& name, unsigned short lodIndex)
{
for (int i=0; i<mMaterial->getNumTechniques(); ++i)
{
if (mMaterial->getTechnique(i)->getSchemeName() == name && mMaterial->getTechnique(i)->getLodIndex() == lodIndex)
return false;
}
Ogre::Technique* t = mMaterial->createTechnique();
t->setSchemeName (name);
t->setLodIndex (lodIndex);
if (mShadowCasterMaterial != "")
t->setShadowCasterMaterial(mShadowCasterMaterial);
mMaterial->compile();
return true;
}
Ogre::MaterialPtr OgreMaterial::getOgreMaterial ()
{
return mMaterial;
}
Ogre::Technique* OgreMaterial::getOgreTechniqueForConfiguration (const std::string& configurationName, unsigned short lodIndex)
{
for (int i=0; i<mMaterial->getNumTechniques(); ++i)
{
if (mMaterial->getTechnique(i)->getSchemeName() == configurationName && mMaterial->getTechnique(i)->getLodIndex() == lodIndex)
{
return mMaterial->getTechnique(i);
}
}
// Prepare and throw error message
std::stringstream message;
message << "Could not find configurationName '" << configurationName
<< "' and lodIndex " << lodIndex;
throw std::runtime_error(message.str());
}
void OgreMaterial::setShadowCasterMaterial (const std::string& name)
{
mShadowCasterMaterial = name;
for (int i=0; i<mMaterial->getNumTechniques(); ++i)
{
mMaterial->getTechnique(i)->setShadowCasterMaterial(mShadowCasterMaterial);
}
}
}

View file

@ -0,0 +1,42 @@
#ifndef SH_OGREMATERIAL_H
#define SH_OGREMATERIAL_H
#include <string>
#include <OgreMaterial.h>
#include "../../Main/Platform.hpp"
namespace sh
{
class OgreMaterial : public Material
{
public:
OgreMaterial (const std::string& name, const std::string& resourceGroup);
virtual ~OgreMaterial();
virtual boost::shared_ptr<Pass> createPass (const std::string& configuration, unsigned short lodIndex);
virtual bool createConfiguration (const std::string& name, unsigned short lodIndex);
virtual bool isUnreferenced();
virtual void ensureLoaded();
virtual void removeAll ();
Ogre::MaterialPtr getOgreMaterial();
virtual void setLodLevels (const std::string& lodLevels);
Ogre::Technique* getOgreTechniqueForConfiguration (const std::string& configurationName, unsigned short lodIndex = 0);
virtual void setShadowCasterMaterial (const std::string& name);
private:
Ogre::MaterialPtr mMaterial;
std::string mName;
std::string mShadowCasterMaterial;
};
}
#endif

View file

@ -0,0 +1,85 @@
#include "OgreMaterialSerializer.hpp"
#include <OgrePass.h>
#include <OgreStringConverter.h>
namespace sh
{
void OgreMaterialSerializer::reset()
{
mScriptContext.section = Ogre::MSS_NONE;
mScriptContext.material.setNull();
mScriptContext.technique = 0;
mScriptContext.pass = 0;
mScriptContext.textureUnit = 0;
mScriptContext.program.setNull();
mScriptContext.lineNo = 0;
mScriptContext.filename.clear();
mScriptContext.techLev = -1;
mScriptContext.passLev = -1;
mScriptContext.stateLev = -1;
}
bool OgreMaterialSerializer::setPassProperty (const std::string& param, std::string value, Ogre::Pass* pass)
{
// workaround https://ogre3d.atlassian.net/browse/OGRE-158
if (param == "transparent_sorting" && value == "force")
{
pass->setTransparentSortingForced(true);
return true;
}
reset();
mScriptContext.section = Ogre::MSS_PASS;
mScriptContext.pass = pass;
if (mPassAttribParsers.find (param) == mPassAttribParsers.end())
return false;
else
{
mPassAttribParsers.find(param)->second(value, mScriptContext);
return true;
}
}
bool OgreMaterialSerializer::setTextureUnitProperty (const std::string& param, std::string value, Ogre::TextureUnitState* t)
{
// quick access to automip setting, without having to use 'texture' which doesn't like spaces in filenames
if (param == "num_mipmaps")
{
t->setNumMipmaps(Ogre::StringConverter::parseInt(value));
return true;
}
reset();
mScriptContext.section = Ogre::MSS_TEXTUREUNIT;
mScriptContext.textureUnit = t;
if (mTextureUnitAttribParsers.find (param) == mTextureUnitAttribParsers.end())
return false;
else
{
mTextureUnitAttribParsers.find(param)->second(value, mScriptContext);
return true;
}
}
bool OgreMaterialSerializer::setMaterialProperty (const std::string& param, std::string value, Ogre::MaterialPtr m)
{
reset();
mScriptContext.section = Ogre::MSS_MATERIAL;
mScriptContext.material = m;
if (mMaterialAttribParsers.find (param) == mMaterialAttribParsers.end())
return false;
else
{
mMaterialAttribParsers.find(param)->second(value, mScriptContext);
return true;
}
}
}

View file

@ -0,0 +1,29 @@
#ifndef SH_OGREMATERIALSERIALIZER_H
#define SH_OGREMATERIALSERIALIZER_H
#include <OgreMaterialSerializer.h>
namespace Ogre
{
class Pass;
}
namespace sh
{
/**
* @brief This class allows me to let Ogre handle the pass & texture unit properties
*/
class OgreMaterialSerializer : public Ogre::MaterialSerializer
{
public:
bool setPassProperty (const std::string& param, std::string value, Ogre::Pass* pass);
bool setTextureUnitProperty (const std::string& param, std::string value, Ogre::TextureUnitState* t);
bool setMaterialProperty (const std::string& param, std::string value, Ogre::MaterialPtr m);
private:
void reset();
};
}
#endif

131
extern/shiny/Platforms/Ogre/OgrePass.cpp vendored Normal file
View file

@ -0,0 +1,131 @@
#include <stdexcept>
#include "OgrePass.hpp"
#include <OgrePass.h>
#include <OgreTechnique.h>
#include "OgreTextureUnitState.hpp"
#include "OgreGpuProgram.hpp"
#include "OgreMaterial.hpp"
#include "OgreMaterialSerializer.hpp"
#include "OgrePlatform.hpp"
namespace sh
{
OgrePass::OgrePass (OgreMaterial* parent, const std::string& configuration, unsigned short lodIndex)
: Pass()
{
Ogre::Technique* t = parent->getOgreTechniqueForConfiguration(configuration, lodIndex);
mPass = t->createPass();
}
boost::shared_ptr<TextureUnitState> OgrePass::createTextureUnitState (const std::string& name)
{
return boost::shared_ptr<TextureUnitState> (new OgreTextureUnitState (this, name));
}
void OgrePass::assignProgram (GpuProgramType type, const std::string& name)
{
if (type == GPT_Vertex)
mPass->setVertexProgram (name);
else if (type == GPT_Fragment)
mPass->setFragmentProgram (name);
else
throw std::runtime_error("unsupported GpuProgramType");
}
Ogre::Pass* OgrePass::getOgrePass ()
{
return mPass;
}
bool OgrePass::setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context)
{
if (((typeid(*value) == typeid(StringValue)) || typeid(*value) == typeid(LinkedValue))
&& retrieveValue<StringValue>(value, context).get() == "default")
return true;
if (name == "vertex_program")
return true; // handled already
else if (name == "fragment_program")
return true; // handled already
else
{
OgreMaterialSerializer& s = OgrePlatform::getSerializer();
return s.setPassProperty (name, retrieveValue<StringValue>(value, context).get(), mPass);
}
}
void OgrePass::setGpuConstant (int type, const std::string& name, ValueType vt, PropertyValuePtr value, PropertySetGet* context)
{
Ogre::GpuProgramParametersSharedPtr params;
if (type == GPT_Vertex)
{
if (!mPass->hasVertexProgram ())
return;
params = mPass->getVertexProgramParameters();
}
else if (type == GPT_Fragment)
{
if (!mPass->hasFragmentProgram ())
return;
params = mPass->getFragmentProgramParameters();
}
if (vt == VT_Float)
params->setNamedConstant (name, retrieveValue<FloatValue>(value, context).get());
else if (vt == VT_Int)
params->setNamedConstant (name, retrieveValue<IntValue>(value, context).get());
else if (vt == VT_Vector4)
{
Vector4 v = retrieveValue<Vector4>(value, context);
params->setNamedConstant (name, Ogre::Vector4(v.mX, v.mY, v.mZ, v.mW));
}
else if (vt == VT_Vector3)
{
Vector3 v = retrieveValue<Vector3>(value, context);
params->setNamedConstant (name, Ogre::Vector4(v.mX, v.mY, v.mZ, 1.0));
}
else if (vt == VT_Vector2)
{
Vector2 v = retrieveValue<Vector2>(value, context);
params->setNamedConstant (name, Ogre::Vector4(v.mX, v.mY, 1.0, 1.0));
}
else
throw std::runtime_error ("unsupported constant type");
}
void OgrePass::addSharedParameter (int type, const std::string& name)
{
Ogre::GpuProgramParametersSharedPtr params;
if (type == GPT_Vertex)
params = mPass->getVertexProgramParameters();
else if (type == GPT_Fragment)
params = mPass->getFragmentProgramParameters();
try
{
params->addSharedParameters (name);
}
catch (Ogre::Exception& e)
{
std::stringstream msg;
msg << "Could not create a shared parameter instance for '"
<< name << "'. Make sure this shared parameter has a value set (via Factory::setSharedParameter)!";
throw std::runtime_error(msg.str());
}
}
void OgrePass::setTextureUnitIndex (int programType, const std::string& name, int index)
{
Ogre::GpuProgramParametersSharedPtr params;
if (programType == GPT_Vertex)
params = mPass->getVertexProgramParameters();
else if (programType == GPT_Fragment)
params = mPass->getFragmentProgramParameters();
params->setNamedConstant(name, index);
}
}

View file

@ -0,0 +1,35 @@
#ifndef SH_OGREPASS_H
#define SH_OGREPASS_H
#include <OgrePass.h>
#include "../../Main/Platform.hpp"
namespace sh
{
class OgreMaterial;
class OgrePass : public Pass
{
public:
OgrePass (OgreMaterial* parent, const std::string& configuration, unsigned short lodIndex);
virtual boost::shared_ptr<TextureUnitState> createTextureUnitState (const std::string& name);
virtual void assignProgram (GpuProgramType type, const std::string& name);
Ogre::Pass* getOgrePass();
virtual void setGpuConstant (int type, const std::string& name, ValueType vt, PropertyValuePtr value, PropertySetGet* context);
virtual void addSharedParameter (int type, const std::string& name);
virtual void setTextureUnitIndex (int programType, const std::string& name, int index);
private:
Ogre::Pass* mPass;
protected:
virtual bool setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context);
};
}
#endif

View file

@ -0,0 +1,183 @@
#include <stdexcept>
#include "OgrePlatform.hpp"
#include <OgreDataStream.h>
#include <OgreGpuProgramManager.h>
#include <OgreHighLevelGpuProgramManager.h>
#include <OgreRoot.h>
#include "OgreMaterial.hpp"
#include "OgreGpuProgram.hpp"
#include "OgreMaterialSerializer.hpp"
#include "../../Main/MaterialInstance.hpp"
#include "../../Main/Factory.hpp"
namespace
{
std::string convertLang (sh::Language lang)
{
if (lang == sh::Language_CG)
return "cg";
else if (lang == sh::Language_HLSL)
return "hlsl";
else if (lang == sh::Language_GLSL)
return "glsl";
else if (lang == sh::Language_GLSLES)
return "glsles";
throw std::runtime_error ("invalid language, valid are: cg, hlsl, glsl, glsles");
}
}
namespace sh
{
OgreMaterialSerializer* OgrePlatform::sSerializer = 0;
OgrePlatform::OgrePlatform(const std::string& resourceGroupName, const std::string& basePath)
: Platform(basePath)
, mResourceGroup(resourceGroupName)
{
Ogre::MaterialManager::getSingleton().addListener(this);
if (supportsShaderSerialization())
Ogre::GpuProgramManager::getSingletonPtr()->setSaveMicrocodesToCache(true);
sSerializer = new OgreMaterialSerializer();
}
OgreMaterialSerializer& OgrePlatform::getSerializer()
{
assert(sSerializer);
return *sSerializer;
}
OgrePlatform::~OgrePlatform ()
{
delete sSerializer;
}
bool OgrePlatform::isProfileSupported (const std::string& profile)
{
return Ogre::GpuProgramManager::getSingleton().isSyntaxSupported(profile);
}
bool OgrePlatform::supportsShaderSerialization ()
{
// Not very reliable in OpenGL mode (requires extension), and somehow doesn't work on linux even if the extension is present
return Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") == std::string::npos;
}
bool OgrePlatform::supportsMaterialQueuedListener ()
{
return true;
}
boost::shared_ptr<Material> OgrePlatform::createMaterial (const std::string& name)
{
OgreMaterial* material = new OgreMaterial(name, mResourceGroup);
return boost::shared_ptr<Material> (material);
}
void OgrePlatform::destroyGpuProgram(const std::string &name)
{
Ogre::HighLevelGpuProgramManager::getSingleton().remove(name);
}
boost::shared_ptr<GpuProgram> OgrePlatform::createGpuProgram (
GpuProgramType type,
const std::string& compileArguments,
const std::string& name, const std::string& profile,
const std::string& source, Language lang)
{
OgreGpuProgram* prog = new OgreGpuProgram (type, compileArguments, name, profile, source, convertLang(lang), mResourceGroup);
return boost::shared_ptr<GpuProgram> (static_cast<GpuProgram*>(prog));
}
Ogre::Technique* OgrePlatform::handleSchemeNotFound (
unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial,
unsigned short lodIndex, const Ogre::Renderable *rend)
{
MaterialInstance* m = fireMaterialRequested(originalMaterial->getName(), schemeName, lodIndex);
if (m)
{
OgreMaterial* _m = static_cast<OgreMaterial*>(m->getMaterial());
return _m->getOgreTechniqueForConfiguration (schemeName, lodIndex);
}
else
return 0; // material does not belong to us
}
void OgrePlatform::serializeShaders (const std::string& file)
{
std::fstream output;
output.open(file.c_str(), std::ios::out | std::ios::binary);
Ogre::DataStreamPtr shaderCache (OGRE_NEW Ogre::FileStreamDataStream(file, &output, false));
Ogre::GpuProgramManager::getSingleton().saveMicrocodeCache(shaderCache);
}
void OgrePlatform::deserializeShaders (const std::string& file)
{
std::ifstream inp;
inp.open(file.c_str(), std::ios::in | std::ios::binary);
Ogre::DataStreamPtr shaderCache(OGRE_NEW Ogre::FileStreamDataStream(file, &inp, false));
Ogre::GpuProgramManager::getSingleton().loadMicrocodeCache(shaderCache);
}
void OgrePlatform::setSharedParameter (const std::string& name, PropertyValuePtr value)
{
Ogre::GpuSharedParametersPtr params;
if (mSharedParameters.find(name) == mSharedParameters.end())
{
params = Ogre::GpuProgramManager::getSingleton().createSharedParameters(name);
Ogre::GpuConstantType type;
if (typeid(*value) == typeid(Vector4))
type = Ogre::GCT_FLOAT4;
else if (typeid(*value) == typeid(Vector3))
type = Ogre::GCT_FLOAT3;
else if (typeid(*value) == typeid(Vector2))
type = Ogre::GCT_FLOAT2;
else if (typeid(*value) == typeid(FloatValue))
type = Ogre::GCT_FLOAT1;
else if (typeid(*value) == typeid(IntValue))
type = Ogre::GCT_INT1;
else
assert(0);
params->addConstantDefinition(name, type);
mSharedParameters[name] = params;
}
else
params = mSharedParameters.find(name)->second;
Ogre::Vector4 v (1.0, 1.0, 1.0, 1.0);
if (typeid(*value) == typeid(Vector4))
{
Vector4 vec = retrieveValue<Vector4>(value, NULL);
v.x = vec.mX;
v.y = vec.mY;
v.z = vec.mZ;
v.w = vec.mW;
}
else if (typeid(*value) == typeid(Vector3))
{
Vector3 vec = retrieveValue<Vector3>(value, NULL);
v.x = vec.mX;
v.y = vec.mY;
v.z = vec.mZ;
}
else if (typeid(*value) == typeid(Vector2))
{
Vector2 vec = retrieveValue<Vector2>(value, NULL);
v.x = vec.mX;
v.y = vec.mY;
}
else if (typeid(*value) == typeid(FloatValue))
v.x = retrieveValue<FloatValue>(value, NULL).get();
else if (typeid(*value) == typeid(IntValue))
v.x = static_cast<float>(retrieveValue<IntValue>(value, NULL).get());
else
throw std::runtime_error ("unsupported property type for shared parameter \"" + name + "\"");
params->setNamedConstant(name, v);
}
}

View file

@ -0,0 +1,74 @@
#ifndef SH_OGREPLATFORM_H
#define SH_OGREPLATFORM_H
/**
* @addtogroup Platforms
* @{
*/
/**
* @addtogroup Ogre
* A set of classes to interact with Ogre's material system
* @{
*/
#include "../../Main/Platform.hpp"
#include <OgreMaterialManager.h>
#include <OgreGpuProgramParams.h>
namespace sh
{
class OgreMaterialSerializer;
class OgrePlatform : public Platform, public Ogre::MaterialManager::Listener
{
public:
OgrePlatform (const std::string& resourceGroupName, const std::string& basePath);
virtual ~OgrePlatform ();
virtual Ogre::Technique* handleSchemeNotFound (
unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial,
unsigned short lodIndex, const Ogre::Renderable *rend);
static OgreMaterialSerializer& getSerializer();
private:
virtual bool isProfileSupported (const std::string& profile);
virtual void serializeShaders (const std::string& file);
virtual void deserializeShaders (const std::string& file);
virtual boost::shared_ptr<Material> createMaterial (const std::string& name);
virtual boost::shared_ptr<GpuProgram> createGpuProgram (
GpuProgramType type,
const std::string& compileArguments,
const std::string& name, const std::string& profile,
const std::string& source, Language lang);
virtual void destroyGpuProgram (const std::string& name);
virtual void setSharedParameter (const std::string& name, PropertyValuePtr value);
friend class ShaderInstance;
friend class Factory;
protected:
virtual bool supportsShaderSerialization ();
virtual bool supportsMaterialQueuedListener ();
std::string mResourceGroup;
static OgreMaterialSerializer* sSerializer;
std::map <std::string, Ogre::GpuSharedParametersPtr> mSharedParameters;
};
}
/**
* @}
* @}
*/
#endif

View file

@ -0,0 +1,41 @@
#include "OgreTextureUnitState.hpp"
#include "OgrePass.hpp"
#include "OgrePlatform.hpp"
#include "OgreMaterialSerializer.hpp"
namespace sh
{
OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent, const std::string& name)
: TextureUnitState()
{
mTextureUnitState = parent->getOgrePass()->createTextureUnitState("");
mTextureUnitState->setName(name);
}
bool OgreTextureUnitState::setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context)
{
OgreMaterialSerializer& s = OgrePlatform::getSerializer();
if (name == "texture_alias")
{
// texture alias in this library refers to something else than in ogre
// delegate up
return TextureUnitState::setPropertyOverride (name, value, context);
}
else if (name == "direct_texture")
{
setTextureName (retrieveValue<StringValue>(value, context).get());
return true;
}
else if (name == "create_in_ffp")
return true; // handled elsewhere
return s.setTextureUnitProperty (name, retrieveValue<StringValue>(value, context).get(), mTextureUnitState);
}
void OgreTextureUnitState::setTextureName (const std::string& textureName)
{
mTextureUnitState->setTextureName(textureName);
}
}

View file

@ -0,0 +1,27 @@
#ifndef SH_OGRETEXTUREUNITSTATE_H
#define SH_OGRETEXTUREUNITSTATE_H
#include <OgreTextureUnitState.h>
#include "../../Main/Platform.hpp"
namespace sh
{
class OgrePass;
class OgreTextureUnitState : public TextureUnitState
{
public:
OgreTextureUnitState (OgrePass* parent, const std::string& name);
virtual void setTextureName (const std::string& textureName);
private:
Ogre::TextureUnitState* mTextureUnitState;
protected:
virtual bool setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context);
};
}
#endif

33
extern/shiny/Readme.txt vendored Normal file
View file

@ -0,0 +1,33 @@
shiny - a shader and material management library for OGRE
FEATURES
- High-level layer on top of OGRE's material system. It allows you to generate multiple techniques for all your materials from a set of high-level per-material properties.
- Several available Macros in shader source files. Just a few examples of the possibilities: binding OGRE auto constants, binding uniforms to material properties, foreach loops (repeat shader source a given number of times), retrieving per-material properties in an #if condition, automatic packing for vertex to fragment passthroughs. These macros allow you to generate even very complex shaders (for example the Ogre::Terrain shader) without assembling them in C++ code.
- Integrated preprocessor (no, I didn't reinvent the wheel, I used boost::wave which turned out to be an excellent choice) that allows me to blend out macros that shouldn't be in use because e.g. the shader permutation doesn't need this specific feature.
- User settings integration. They can be set by a C++ interface and retrieved through a macro in shader files.
- Automatic handling of shader permutations, i.e. shaders are shared between materials in a smart way.
- An optional "meta-language" (well, actually it's just a small header with some conditional defines) that you may use to compile the same shader source for different target languages. If you don't like it, you can still code in GLSL / CG etc separately. You can also switch between the languages at runtime.
- On-demand material and shader creation. It uses Ogre's material listener to compile the shaders as soon as they are needed for rendering, and not earlier.
- Shader changes are fully dynamic and real-time. Changing a user setting will recompile all shaders affected by this setting when they are next needed.
- Serialization system that extends Ogre's material script system, it uses Ogre's script parser, but also adds some additional properties that are not available in Ogre's material system.
- A concept called "Configuration" allowing you to create a different set of your shaders, doing the same thing except for some minor differences: the properties that are overridden by the active configuration. Possible uses for this are using simpler shaders (no shadows, no fog etc) when rendering for example realtime reflections or a minimap. You can easily switch between configurations by changing the active Ogre material scheme (for example on a viewport level).
- Fixed function support. You can globally enable or disable shaders at any time, and for texture units you can specify if they're only needed for the shader-based path (e.g. normal maps) or if they should also be created in the fixed function path.
LICENSE
see License.txt
AUTHOR
scrawl <scrawl@baseoftrash.de>