1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-25 06:35:30 +00:00

Merge remote-tracking branch 'zini/master' into animations

Conflicts:
	apps/openmw/mwmechanics/character.cpp
This commit is contained in:
Chris Robinson 2013-05-06 19:17:39 -07:00
commit 90e29c83c0
91 changed files with 7184 additions and 911 deletions

View File

@ -25,13 +25,14 @@ add_openmw_dir (mwinput
add_openmw_dir (mwgui
textinput widgets race class birth review windowmanagerimp console dialogue
dialoguehistory windowbase statswindow messagebox journalwindow charactercreation
windowbase statswindow messagebox journalwindow charactercreation
mapwindow windowpinnablebase tooltips scrollwindow bookwindow list
formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
merchantrepair repair soulgemdialog companionwindow
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch
)
add_openmw_dir (mwdialogue

View File

@ -41,7 +41,7 @@ namespace MWBase
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0;
virtual void questionAnswered (const std::string& answer) = 0;
virtual void questionAnswered (int answer) = 0;
virtual bool checkServiceRefused () = 0;

View File

@ -201,6 +201,8 @@ namespace MWBase
///< Hides dialog and schedules dialog to be deleted.
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>()) = 0;
virtual void staticMessageBox(const std::string& message) = 0;
virtual void removeStaticMessageBox() = 0;
virtual void enterPressed () = 0;
virtual int readPressedButton() = 0;

View File

@ -161,7 +161,7 @@ namespace MWDialogue
parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
mLastDialogue = *info;
@ -263,6 +263,7 @@ namespace MWDialogue
parseText (info->mResponse);
std::string title;
if (dialogue.mType==ESM::Dialogue::Persuasion)
{
std::string modifiedTopic = "s" + topic;
@ -272,13 +273,13 @@ namespace MWDialogue
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
win->addTitle (gmsts.find (modifiedTopic)->getString());
title = gmsts.find (modifiedTopic)->getString();
}
else
win->addTitle (topic);
title = topic;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId);
executeScript (info->mResultScript);
@ -289,9 +290,7 @@ namespace MWDialogue
else
{
// no response found, print a fallback text
win->addTitle (topic);
win->addText ("");
win->addResponse ("", topic);
}
}
@ -424,53 +423,42 @@ namespace MWDialogue
mTemporaryDispositionChange = 0;
}
void DialogueManager::questionAnswered (const std::string& answer)
void DialogueManager::questionAnswered (int answer)
{
mChoice = answer;
if (mChoiceMap.find(answer) != mChoiceMap.end())
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
{
mChoice = mChoiceMap[answer];
Filter filter (mActor, mChoice, mTalkedTo);
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
{
Filter filter (mActor, mChoice, mTalkedTo);
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
{
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
{
std::string text = info->mResponse;
parseText (text);
std::string text = info->mResponse;
parseText (text);
mChoiceMap.clear();
mChoice = -1;
mIsInChoice = false;
mChoice = -1;
mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
executeScript (info->mResultScript);
mLastDialogue = *info;
}
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
executeScript (info->mResultScript);
mLastDialogue = *info;
}
}
updateTopics();
}
}
void DialogueManager::printError (const std::string& error)
{
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->addText(error);
updateTopics();
}
void DialogueManager::askQuestion (const std::string& question, int choice)
{
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->askQuestion(question);
mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice;
win->addChoice(question, choice);
mIsInChoice = true;
}
@ -551,10 +539,10 @@ namespace MWDialogue
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
win->addTitle (gmsts.find ("sServiceRefusal")->getString());
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext),
gmsts.find ("sServiceRefusal")->getString());
executeScript (info->mResultScript);
return true;
@ -565,9 +553,7 @@ namespace MWDialogue
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{
std::vector<HyperTextToken> result;
MyGUI::UString utext(text);
size_t pos_begin, pos_end, iteration_pos = 0;
for(;;)
{

View File

@ -30,7 +30,6 @@ namespace MWDialogue
bool mTalkedTo;
int mChoice;
std::map<std::string, int> mChoiceMap;
std::string mLastTopic;
ESM::DialInfo mLastDialogue;
bool mIsInChoice;
@ -46,8 +45,6 @@ namespace MWDialogue
bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
void executeScript (const std::string& script);
void printError (const std::string& error);
void executeTopic (const std::string& topic, bool randomResponse=false);
public:
@ -72,7 +69,7 @@ namespace MWDialogue
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword);
virtual void goodbyeSelected();
virtual void questionAnswered (const std::string& answer);
virtual void questionAnswered (int answer);
virtual void persuade (int type);
virtual int getTemporaryDispositionChange () const;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
#ifndef MWGUI_BOOKPAGE_HPP
#define MWGUI_BOOKPAGE_HPP
#include "MyGUI_Colour.h"
#include "MyGUI_Widget.h"
#include <functional>
#include <platform/stdint.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
namespace MWGui
{
/// A formatted and paginated document to be used with
/// the book page widget.
struct TypesetBook
{
typedef boost::shared_ptr <TypesetBook> Ptr;
typedef intptr_t InteractiveId;
/// Returns the number of pages in the document.
virtual size_t pageCount () const = 0;
/// Return the area covered by the document. The first
/// integer is the maximum with of any line. This is not
/// the largest coordinate of the right edge of any line,
/// it is the largest distance from the left edge to the
/// right edge. The second integer is the height of all
/// text combined prior to pagination.
virtual std::pair <unsigned int, unsigned int> getSize () const = 0;
};
/// A factory class for creating a typeset book instance.
struct BookTypesetter
{
typedef boost::shared_ptr <BookTypesetter> Ptr;
typedef TypesetBook::InteractiveId InteractiveId;
typedef MyGUI::Colour Colour;
typedef uint8_t const * Utf8Point;
typedef std::pair <Utf8Point, Utf8Point> Utf8Span;
enum Alignment {
AlignLeft = -1,
AlignCenter = 0,
AlignRight = +1
};
/// Styles are used to control the character level formatting
/// of text added to a typeset book. Their lifetime is equal
/// to the lifetime of the book-typesetter instance that created
/// them.
struct Style;
/// A factory function for creating the default implementation of a book typesetter
static Ptr create (int pageWidth, int pageHeight);
/// Create a simple text style consisting of a font and a text color.
virtual Style* createStyle (char const * Font, Colour Colour) = 0;
/// Create a hyper-link style with a user-defined identifier based on an
/// existing style. The unique flag forces a new instance of this style
/// to be created even if an existing instance is present.
virtual Style* createHotStyle (Style * BaseStyle, Colour NormalColour, Colour HoverColour, Colour ActiveColour, InteractiveId Id, bool Unique = true) = 0;
/// Insert a line break into the document. Newline characters in the input
/// text have the same affect. The margin parameter adds additional space
/// before the next line of text.
virtual void lineBreak (float margin = 0) = 0;
/// Insert a section break into the document. This causes a new section
/// to begin when additional text is inserted. Pagination attempts to keep
/// sections together on a single page. The margin parameter adds additional space
/// before the next line of text.
virtual void sectionBreak (float margin = 0) = 0;
/// Changes the alignment for the current section of text.
virtual void setSectionAlignment (Alignment sectionAlignment) = 0;
// Layout a block of text with the specified style into the document.
virtual void write (Style * Style, Utf8Span Text) = 0;
/// Adds a content block to the document without laying it out. An
/// identifier is returned that can be used to refer to it. If select
/// is true, the block is activated to be references by future writes.
virtual intptr_t addContent (Utf8Span Text, bool Select = true) = 0;
/// Select a previously created content block for future writes.
virtual void selectContent (intptr_t contentHandle) = 0;
/// Layout a span of the selected content block into the document
/// using the specified style.
virtual void write (Style * Style, size_t Begin, size_t End) = 0;
/// Finalize the document layout, and return a pointer to it.
virtual TypesetBook::Ptr complete () = 0;
};
/// An interface to the BookPage widget.
class BookPage : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(BookPage)
public:
typedef TypesetBook::InteractiveId InteractiveId;
typedef boost::function <void (InteractiveId)> ClickCallback;
/// Make the widget display the specified page from the specified book.
virtual void showPage (TypesetBook::Ptr Book, size_t Page) = 0;
/// Set the callback for a clicking a hyper-link in the document.
virtual void adviseLinkClicked (ClickCallback callback) = 0;
/// Clear the hyper-link click callback.
virtual void unadviseLinkClicked () = 0;
/// Register the widget and associated sub-widget with MyGUI. Should be
/// called once near the beginning of the program.
static void registerMyGUIComponents ();
};
}
#endif // MWGUI_BOOKPAGE_HPP

View File

@ -76,7 +76,7 @@ namespace MWGui
parent = mRightPage;
MyGUI::Widget* pageWidget = parent->createWidgetReal<MyGUI::Widget>("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast<std::string>(i));
parser.parse(*it, pageWidget, mLeftPage->getSize().width);
parser.parsePage(*it, pageWidget, mLeftPage->getSize().width);
mPages.push_back(pageWidget);
++i;
}
@ -157,6 +157,21 @@ namespace MWGui
}
++i;
}
//If it is the last page, hide the button "Next Page"
if ( (mCurrentPage+1)*2 == mPages.size()
|| (mCurrentPage+1)*2 == mPages.size() + 1)
{
mNextPageButton->setVisible(false);
} else {
mNextPageButton->setVisible(true);
}
//If it is the fist page, hide the button "Prev Page"
if (mCurrentPage == 0) {
mPrevPageButton->setVisible(false);
} else {
mPrevPageButton->setVisible(true);
}
}
}

View File

@ -11,36 +11,24 @@
#include "../mwdialogue/dialoguemanagerimp.hpp"
#include "dialoguehistory.hpp"
#include "widgets.hpp"
#include "list.hpp"
#include "tradewindow.hpp"
#include "spellbuyingwindow.hpp"
#include "inventorywindow.hpp"
#include "travelwindow.hpp"
#include "bookpage.hpp"
/**
*Copied from the internet.
*/
namespace
{
std::string lower_string(const std::string& str)
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
std::string lowerCase = Misc::StringUtils::lowerCase (str);
typedef MWGui::BookTypesetter::Utf8Point point;
return lowerCase;
}
point begin = reinterpret_cast <point> (text);
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
{
return lower_string(str).find(lower_string(substr),pos);
}
bool sortByLength (const std::string& left, const std::string& right)
{
return left.size() > right.size();
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
}
@ -116,11 +104,144 @@ namespace MWGui
// --------------------------------------------------------------------------------------------------
Response::Response(const std::string &text, const std::string &title)
: mTitle(title)
{
mText = text;
}
void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const
{
BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f));
typesetter->sectionBreak(9);
if (mTitle != "")
typesetter->write(title, to_utf8_span(mTitle.c_str()));
typesetter->sectionBreak(9);
typedef std::pair<size_t, size_t> Range;
std::map<Range, intptr_t> hyperLinks;
// We need this copy for when @# hyperlinks are replaced
std::string text = mText;
size_t pos_begin, pos_end;
for(;;)
{
pos_begin = text.find('@');
if (pos_begin != std::string::npos)
pos_end = text.find('#', pos_begin);
if (pos_begin != std::string::npos && pos_end != std::string::npos)
{
std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1);
const char specialPseudoAsteriskCharacter = 127;
std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*');
std::string topicName = MWBase::Environment::get().getWindowManager()->
getTranslationDataStorage().topicStandardForm(link);
std::string displayName = link;
while (displayName[displayName.size()-1] == '*')
displayName.erase(displayName.size()-1, 1);
text.replace(pos_begin, pos_end+1-pos_begin, displayName);
if (topicLinks.find(Misc::StringUtils::lowerCase(topicName)) != topicLinks.end())
hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[Misc::StringUtils::lowerCase(topicName)]);
}
else
break;
}
typesetter->addContent(to_utf8_span(text.c_str()));
if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
{
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
size_t formatted = 0; // points to the first character that is not laid out yet
for (std::map<Range, intptr_t>::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it)
{
intptr_t topicId = it->second;
const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f);
const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f);
const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f);
BookTypesetter::Style* hotStyle = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId);
if (formatted < it->first.first)
typesetter->write(style, formatted, it->first.first);
typesetter->write(hotStyle, it->first.first, it->first.second);
formatted = it->first.second;
}
if (formatted < text.size())
typesetter->write(style, formatted, text.size());
}
else
{
std::string::const_iterator i = text.begin ();
KeywordSearchT::Match match;
while (i != text.end () && keywordSearch->search (i, text.end (), match))
{
if (i != match.mBeg)
addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ());
addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ());
i = match.mEnd;
}
if (i != text.end ())
addTopicLink (typesetter, 0, i - text.begin (), text.size ());
}
}
void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const
{
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f);
const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f);
const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f);
if (topicId)
style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId);
typesetter->write (style, begin, end);
}
Message::Message(const std::string& text)
{
mText = text;
}
void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const
{
BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f));
typesetter->sectionBreak(9);
typesetter->write(title, to_utf8_span(mText.c_str()));
}
// --------------------------------------------------------------------------------------------------
void Choice::activated()
{
MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId);
}
void Topic::activated()
{
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId));
}
void Goodbye::activated()
{
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
}
// --------------------------------------------------------------------------------------------------
DialogueWindow::DialogueWindow()
: WindowBase("openmw_dialogue_window.layout")
, mPersuasionDialog()
, mEnabled(false)
, mServices(0)
, mGoodbye(false)
{
// Centre dialog
center();
@ -129,15 +250,6 @@ namespace MWGui
//History view
getWidget(mHistory, "History");
mHistory->setOverflowToTheLeft(true);
mHistory->setMaxTextLength(1000000);
MyGUI::Widget* eventbox;
//An EditBox cannot receive mouse click events, so we use an
//invisible widget on top of the editbox to receive them
getWidget(eventbox, "EventBox");
eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
//Topics list
getWidget(mTopicsList, "TopicsList");
@ -149,68 +261,30 @@ namespace MWGui
getWidget(mDispositionBar, "Disposition");
getWidget(mDispositionText,"DispositionText");
getWidget(mScrollBar, "VScroll");
mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved);
mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1);
mHistory->adviseLinkClicked(callback);
static_cast<MyGUI::Window*>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
}
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
{
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
if(t == NULL)
return;
const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
size_t cursorPosition = t->getCursorPosition(lastPressed);
MyGUI::UString color = mHistory->getColorAtPos(cursorPosition);
if (!mEnabled && color == "#572D21")
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
if (!mEnabled)
return;
if(color != "#B29154")
{
MyGUI::UString key = mHistory->getColorTextAt(cursorPosition);
if(color == "#686EBA")
{
std::map<size_t, HyperLink>::iterator i = mHyperLinks.upper_bound(cursorPosition);
if( !mHyperLinks.empty() )
{
--i;
if( i->first + i->second.mLength > cursorPosition)
{
MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue);
}
}
else
{
// the link was colored, but it is not in mHyperLinks.
// It means that those liunks are not marked with @# and found
// by topic name search
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key));
}
}
if(color == "#572D21")
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
}
}
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
{
mTopicsList->adjustSize();
updateHistory();
}
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
{
if (mHistory->getVScrollPosition() - _rel*0.3 < 0)
mHistory->setVScrollPosition(0);
else
mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3);
if (!mScrollBar->getVisible())
return;
mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1),
std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));
onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());
}
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
@ -231,7 +305,7 @@ namespace MWGui
}
if (id >= separatorPos)
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic));
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic));
else
{
const MWWorld::Store<ESM::GameSetting> &gmst =
@ -289,20 +363,32 @@ namespace MWGui
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName)
{
mGoodbye = false;
mEnabled = true;
mPtr = actor;
mTopicsList->setEnabled(true);
setTitle(npcName);
mTopicsList->clear();
mHyperLinks.clear();
mHistory->setCaption("");
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it);
mHistoryContents.clear();
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it)
delete (*it);
mLinks.clear();
updateOptions();
}
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
{
mTopicsList->clear();
for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it)
delete it->second;
mTopicLinks.clear();
mKeywordSearch.clear();
bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty()
&& mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion");
@ -346,161 +432,138 @@ namespace MWGui
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
{
mTopicsList->addItem(*it);
Topic* t = new Topic(*it);
mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t;
mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t));
}
mTopicsList->adjustSize();
updateHistory();
}
void DialogueWindow::removeKeyword(std::string keyWord)
void DialogueWindow::updateHistory(bool scrollbar)
{
if(mTopicsList->hasItem(keyWord))
if (!scrollbar && mScrollBar->getVisible())
{
mTopicsList->removeItem(keyWord);
mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0));
mScrollBar->setVisible(false);
}
if (scrollbar && !mScrollBar->getVisible())
{
mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0));
mScrollBar->setVisible(true);
}
BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits<int>().max());
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
(*it)->write(typesetter, &mKeywordSearch, mTopicLinks);
BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White);
// choices
const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f);
const MyGUI::Colour linkNormal (150/255.f, 50/255.f, 30/255.f);
const MyGUI::Colour linkActive (243/255.f, 237/255.f, 221/255.f);
for (std::map<std::string, int>::iterator it = mChoices.begin(); it != mChoices.end(); ++it)
{
Choice* link = new Choice(it->second);
mLinks.push_back(link);
typesetter->lineBreak();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
TypesetBook::InteractiveId(link));
typesetter->write(questionStyle, to_utf8_span(it->first.c_str()));
}
if (mGoodbye)
{
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
TypesetBook::InteractiveId(mLinks.back()));
typesetter->lineBreak();
typesetter->write(questionStyle, to_utf8_span(goodbye.c_str()));
}
TypesetBook::Ptr book = typesetter->complete();
mHistory->showPage(book, 0);
size_t viewHeight = mHistory->getParent()->getHeight();
if (!scrollbar && book->getSize().second > viewHeight)
updateHistory(true);
else if (scrollbar)
{
mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second));
size_t range = book->getSize().second - viewHeight;
mScrollBar->setScrollRange(range);
mScrollBar->setScrollPosition(range-1);
mScrollBar->setTrackSize(viewHeight / static_cast<float>(book->getSize().second) * mScrollBar->getLineSize());
onScrollbarMoved(mScrollBar, range-1);
}
else
{
// no scrollbar
onScrollbarMoved(mScrollBar, 0);
}
mTopicsList->adjustSize();
}
void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2)
void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link)
{
size_t pos = 0;
while((pos = find_str_ci(str,keyword, pos)) != std::string::npos)
reinterpret_cast<Link*>(link)->activated();
}
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
{
mHistory->setPosition(0,-pos);
}
void DialogueWindow::addResponse(const std::string &text, const std::string &title)
{
// This is called from the dialogue manager, so text is
// case-smashed - thus we have to retrieve the correct case
// of the title through the topic list.
std::string realTitle = title;
if (realTitle != "")
{
// do not add color if this portion of text is already colored.
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{
MyGUI::TextIterator iterator (str);
MyGUI::UString colour;
while(iterator.moveNext())
std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::lowerCase(item) == title)
{
size_t iteratorPos = iterator.getPosition();
iterator.getTagColour(colour);
if (iteratorPos == pos)
break;
realTitle = item;
break;
}
if (colour == color1)
return;
}
str.insert(pos,color1);
pos += color1.length();
pos += keyword.length();
str.insert(pos,color2);
pos+= color2.length();
}
}
std::string DialogueWindow::parseText(const std::string& text)
{
bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored)
std::vector<std::string> topics;
bool hasSeparator = false;
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
{
if (mTopicsList->getItemNameAt(i) == "")
hasSeparator = true;
}
for(unsigned int i = 0;i<mTopicsList->getItemCount();i++)
{
std::string keyWord = mTopicsList->getItemNameAt(i);
if (separatorReached || !hasSeparator)
topics.push_back(keyWord);
else if (keyWord == "")
separatorReached = true;
}
// sort by length to make sure longer topics are replaced first
std::sort(topics.begin(), topics.end(), sortByLength);
std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
size_t historySize = 0;
if(mHistory->getClient()->getSubWidgetText() != NULL)
{
historySize = mHistory->getOnlyText().size();
}
std::string result;
size_t hypertextPos = 0;
for (size_t i = 0; i < hypertext.size(); ++i)
{
if (hypertext[i].mLink)
{
size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText);
std::string standardForm = hypertext[i].mText;
for(; asterisk_count > 0; --asterisk_count)
standardForm.append("*");
standardForm =
MWBase::Environment::get().getWindowManager()->
getTranslationDataStorage().topicStandardForm(standardForm);
if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() )
{
result.append("#686EBA").append(hypertext[i].mText).append("#B29154");
mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length();
mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm);
}
else
result += hypertext[i].mText;
}
else
{
if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() )
{
for(std::vector<std::string>::const_iterator it = topics.begin(); it != topics.end(); ++it)
{
addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154");
}
}
result += hypertext[i].mText;
}
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
}
return result;
}
void DialogueWindow::addText(std::string text)
{
mHistory->addDialogText("#B29154"+parseText(text)+"#B29154");
mHistoryContents.push_back(new Response(text, realTitle));
updateHistory();
}
void DialogueWindow::addMessageBox(const std::string& text)
{
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
mHistoryContents.push_back(new Message(text));
updateHistory();
}
void DialogueWindow::addTitle(std::string text)
void DialogueWindow::addChoice(const std::string& choice, int id)
{
// This is called from the dialogue manager, so text is
// case-smashed - thus we have to retrieve the correct case
// of the text through the topic list.
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{
std::string item = mTopicsList->getItemNameAt(i);
if (lower_string(item) == text)
text = item;
}
mHistory->addDialogHeading(text);
mChoices[choice] = id;
updateHistory();
}
void DialogueWindow::askQuestion(std::string question)
void DialogueWindow::clearChoices()
{
mHistory->addDialogText("#572D21"+question+"#B29154"+" ");
mChoices.clear();
updateHistory();
}
void DialogueWindow::updateOptions()
{
//Clear the list of topics
mTopicsList->clear();
mHyperLinks.clear();
mHistory->eraseText(0, mHistory->getTextLength());
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
{
@ -513,9 +576,11 @@ namespace MWGui
void DialogueWindow::goodbye()
{
mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString());
mLinks.push_back(new Goodbye());
mGoodbye = true;
mTopicsList->setEnabled(false);
mEnabled = false;
updateHistory();
}
void DialogueWindow::onReferenceUnavailable()

View File

@ -4,6 +4,10 @@
#include "windowbase.hpp"
#include "referenceinterface.hpp"
#include "bookpage.hpp"
#include "keywordsearch.hpp"
namespace MWGui
{
class WindowManager;
@ -21,7 +25,8 @@ namespace MWGui
namespace MWGui
{
class DialogueHistory;
class DialogueHistoryViewModel;
class BookPage;
class PersuasionDialog : public WindowModal
{
@ -44,6 +49,55 @@ namespace MWGui
void onPersuade (MyGUI::Widget* sender);
};
struct Link
{
virtual ~Link() {}
virtual void activated () = 0;
};
struct Topic : Link
{
Topic(const std::string& id) : mTopicId(id) {}
std::string mTopicId;
virtual void activated ();
};
struct Choice : Link
{
Choice(int id) : mChoiceId(id) {}
int mChoiceId;
virtual void activated ();
};
struct Goodbye : Link
{
virtual void activated ();
};
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
struct DialogueText
{
virtual ~DialogueText() {}
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const = 0;
std::string mText;
};
struct Response : DialogueText
{
Response(const std::string& text, const std::string& title = "");
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const;
void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const;
std::string mTitle;
};
struct Message : DialogueText
{
Message(const std::string& text);
virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const;
};
class DialogueWindow: public WindowBase, public ReferenceInterface
{
public:
@ -52,19 +106,18 @@ namespace MWGui
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Dialog finished, OK button clicked.\n
signature : void method()\n
*/
EventHandle_Void eventBye;
void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName);
void stopDialogue();
void setKeywords(std::list<std::string> keyWord);
void removeKeyword(std::string keyWord);
void addText(std::string text);
void addResponse (const std::string& text, const std::string& title="");
void addMessageBox(const std::string& text);
void addTitle(std::string text);
void askQuestion(std::string question);
void addChoice(const std::string& choice, int id);
void clearChoices();
void goodbye();
void onFrame();
@ -85,37 +138,39 @@ namespace MWGui
protected:
void onSelectTopic(const std::string& topic, int id);
void onByeClicked(MyGUI::Widget* _sender);
void onHistoryClicked(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onWindowResize(MyGUI::Window* _sender);
virtual void onReferenceUnavailable();
void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
struct HyperLink
{
size_t mLength;
std::string mTrueValue;
};
void updateHistory(bool scrollbar=false);
virtual void onReferenceUnavailable();
private:
void updateOptions();
/**
*Helper function that add topic keyword in blue in a text.
*/
std::string parseText(const std::string& text);
int mServices;
bool mEnabled;
DialogueHistory* mHistory;
bool mGoodbye;
std::vector<DialogueText*> mHistoryContents;
std::map<std::string, int> mChoices;
std::vector<Link*> mLinks;
std::map<std::string, Link*> mTopicLinks;
KeywordSearchT mKeywordSearch;
BookPage* mHistory;
Widgets::MWList* mTopicsList;
MyGUI::ScrollBar* mScrollBar;
MyGUI::ProgressPtr mDispositionBar;
MyGUI::EditBox* mDispositionText;
PersuasionDialog mPersuasionDialog;
std::map<size_t, HyperLink> mHyperLinks;
};
}
#endif

View File

@ -1,78 +0,0 @@
#include "dialoguehistory.hpp"
#include "../mwbase/windowmanager.hpp"
#include "widgets.hpp"
#include "../mwworld/esmstore.hpp"
#include <iostream>
#include <iterator>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
namespace MWGui
{
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
{
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
MyGUI::TextIterator iterator(getCaption());
while(iterator.moveNext())
{
size_t pos = iterator.getPosition();
iterator.getTagColour(colour);
if (pos < _pos)
continue;
else if (pos == _pos)
break;
}
return colour;
}
MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos)
{
bool breakOnNext = false;
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
MyGUI::UString colour2 = colour;
MyGUI::TextIterator iterator(getCaption());
MyGUI::TextIterator col_start = iterator;
while(iterator.moveNext())
{
size_t pos = iterator.getPosition();
iterator.getTagColour(colour);
if(colour != colour2)
{
if(breakOnNext)
{
return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition());
}
col_start = iterator;
colour2 = colour;
}
if (pos < _pos)
continue;
else if (pos == _pos)
{
breakOnNext = true;
}
}
return "";
}
void DialogueHistory::addDialogHeading(const MyGUI::UString& parText)
{
MyGUI::UString head("\n#D8C09A");
head.append(parText);
head.append("#B29154\n");
addText(head);
}
void DialogueHistory::addDialogText(const MyGUI::UString& parText)
{
addText(parText);
addText("\n");
}
}

View File

@ -1,20 +0,0 @@
#ifndef MWGUI_DIALOGE_HISTORY_H
#define MWGUI_DIALOGE_HISTORY_H
#include <openengine/gui/layout.hpp>
namespace MWGui
{
class DialogueHistory : public MyGUI::EditBox
{
MYGUI_RTTI_DERIVED( DialogueHistory )
public:
Widget* getClient() { return mClient; }
MyGUI::UString getColorAtPos(size_t _pos);
MyGUI::UString getColorTextAt(size_t _pos);
void addDialogHeading(const MyGUI::UString& parText);
void addDialogText(const MyGUI::UString& parText);
};
}
#endif

View File

@ -6,7 +6,10 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <OgreUTFString.h>
namespace
@ -74,6 +77,12 @@ namespace
Ogre::UTFString string(s);
return string.getChar(0);
}
bool is_not_empty(const std::string s) {
std::string temp = s;
boost::algorithm::trim(temp);
return !temp.empty();
}
}
namespace MWGui
@ -88,6 +97,7 @@ namespace MWGui
utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext);
boost::algorithm::replace_all(utf8Text, "\n", "");
boost::algorithm::replace_all(utf8Text, "\r", "");
boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
@ -106,6 +116,14 @@ namespace MWGui
size_t currentWordStart = 0;
size_t index = 0;
{
std::string texToTrim = text.asUTF8();
boost::algorithm::trim( texToTrim );
text = UTFString(texToTrim);
}
while (currentHeight <= height - spacing && index < text.size())
{
const UTFString::unicode_char ch = text.getChar(index);
@ -176,8 +194,10 @@ namespace MWGui
result.push_back(text.substr(0, pageEnd).asUTF8());
text.erase(0, pageEnd);
}
return result;
std::vector<std::string> nonEmptyPages;
boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages));
return nonEmptyPages;
}
float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const
@ -193,7 +213,30 @@ namespace MWGui
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
}
MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width)
MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width)
{
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext);
mParent = parent;
mWidth = width;
mHeight = 0;
assert(mParent);
while (mParent->getChildCount())
{
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
}
// remove trailing "
if (text[text.size()-1] == '\"')
text.erase(text.size()-1);
parseSubText(text);
return MyGUI::IntSize(mWidth, mHeight);
}
MyGUI::IntSize BookTextParser::parseScroll(std::string text, MyGUI::Widget* parent, const int width)
{
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext);
@ -209,12 +252,10 @@ namespace MWGui
}
boost::algorithm::replace_all(text, "\n", "");
boost::algorithm::replace_all(text, "\r", "");
boost::algorithm::replace_all(text, "<BR>", "\n");
boost::algorithm::replace_all(text, "<P>", "\n\n");
// remove leading newlines
// while (text[0] == '\n')
// text.erase(0);
boost::algorithm::trim_left(text);
// remove trailing "
if (text[text.size()-1] == '\"')
@ -223,6 +264,7 @@ namespace MWGui
parseSubText(text);
return MyGUI::IntSize(mWidth, mHeight);
}
void BookTextParser::parseImage(std::string tag, bool createWidget)
{
@ -309,7 +351,7 @@ namespace MWGui
parseImage(tag);
if (boost::algorithm::starts_with(tag, "FONT"))
parseFont(tag);
if (boost::algorithm::starts_with(tag, "DOV"))
if (boost::algorithm::starts_with(tag, "DIV"))
parseDiv(tag);
text.erase(0, tagEnd + 1);

View File

@ -32,7 +32,16 @@ namespace MWGui
* @param maximum width
* @return size of the created widgets
*/
MyGUI::IntSize parse(std::string text, MyGUI::Widget* parent, const int width);
MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width);
/**
* Parse markup as MyGUI widgets
* @param markup to parse
* @param parent for the created widgets
* @param maximum width
* @return size of the created widgets
*/
MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width);
/**
* Split the specified text into pieces that fit in the area specified by width and height parameters

View File

@ -0,0 +1,326 @@
#include "journalbooks.hpp"
namespace
{
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
typedef MWGui::BookTypesetter::Utf8Point point;
point begin = reinterpret_cast <point> (text);
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
struct AddContent
{
MWGui::BookTypesetter::Ptr mTypesetter;
MWGui::BookTypesetter::Style* mBodyStyle;
AddContent (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :
mTypesetter (typesetter), mBodyStyle (body_style)
{
}
};
struct AddSpan : AddContent
{
AddSpan (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :
AddContent (typesetter, body_style)
{
}
void operator () (intptr_t topicId, size_t begin, size_t end)
{
MWGui::BookTypesetter::Style* style = mBodyStyle;
if (topicId)
style = mTypesetter->createHotStyle (mBodyStyle, linkNormal, linkHot, linkActive, topicId);
mTypesetter->write (style, begin, end);
}
};
struct AddEntry
{
MWGui::BookTypesetter::Ptr mTypesetter;
MWGui::BookTypesetter::Style* mBodyStyle;
AddEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) :
mTypesetter (typesetter), mBodyStyle (body_style)
{
}
void operator () (MWGui::JournalViewModel::Entry const & entry)
{
mTypesetter->addContent (entry.body ());
entry.visitSpans (AddSpan (mTypesetter, mBodyStyle));
}
};
struct AddJournalEntry : AddEntry
{
bool mAddHeader;
MWGui::BookTypesetter::Style* mHeaderStyle;
AddJournalEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style,
MWGui::BookTypesetter::Style* header_style, bool add_header) :
AddEntry (typesetter, body_style),
mHeaderStyle (header_style),
mAddHeader (add_header)
{
}
void operator () (MWGui::JournalViewModel::JournalEntry const & entry)
{
if (mAddHeader)
{
mTypesetter->write (mHeaderStyle, entry.timestamp ());
mTypesetter->lineBreak ();
}
AddEntry::operator () (entry);
mTypesetter->sectionBreak (10);
}
};
struct AddTopicEntry : AddEntry
{
intptr_t mContentId;
MWGui::BookTypesetter::Style* mHeaderStyle;
AddTopicEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style,
MWGui::BookTypesetter::Style* header_style, intptr_t contentId) :
AddEntry (typesetter, body_style), mHeaderStyle (header_style), mContentId (contentId)
{
}
void operator () (MWGui::JournalViewModel::TopicEntry const & entry)
{
mTypesetter->write (mBodyStyle, entry.source ());
mTypesetter->write (mBodyStyle, 0, 3);// begin
AddEntry::operator() (entry);
mTypesetter->selectContent (mContentId);
mTypesetter->write (mBodyStyle, 2, 3);// end quote
mTypesetter->sectionBreak (10);
}
};
struct AddTopicName : AddContent
{
AddTopicName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :
AddContent (typesetter, style)
{
}
void operator () (MWGui::JournalViewModel::Utf8Span topicName)
{
mTypesetter->write (mBodyStyle, topicName);
mTypesetter->sectionBreak (10);
}
};
struct AddQuestName : AddContent
{
AddQuestName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :
AddContent (typesetter, style)
{
}
void operator () (MWGui::JournalViewModel::Utf8Span topicName)
{
mTypesetter->write (mBodyStyle, topicName);
mTypesetter->sectionBreak (10);
}
};
struct AddTopicLink : AddContent
{
AddTopicLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :
AddContent (typesetter, style)
{
}
void operator () (MWGui::JournalViewModel::TopicId topicId, MWGui::JournalViewModel::Utf8Span name)
{
MWGui::BookTypesetter::Style* link = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, topicId);
mTypesetter->write (link, name);
mTypesetter->lineBreak ();
}
};
struct AddQuestLink : AddContent
{
AddQuestLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) :
AddContent (typesetter, style)
{
}
void operator () (MWGui::JournalViewModel::QuestId id, MWGui::JournalViewModel::Utf8Span name)
{
MWGui::BookTypesetter::Style* style = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, id);
mTypesetter->write (style, name);
mTypesetter->lineBreak ();
}
};
}
namespace MWGui
{
typedef TypesetBook::Ptr book;
JournalBooks::JournalBooks (JournalViewModel::Ptr model) :
mModel (model)
{
}
book JournalBooks::createEmptyJournalBook ()
{
BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
typesetter->write (header, to_utf8_span ("You have no journal entries!"));
typesetter->lineBreak ();
typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest."));
BookTypesetter::Style* big = typesetter->createStyle ("EB Garamond 24", MyGUI::Colour::Black);
BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue);
typesetter->sectionBreak (20);
typesetter->write (body, to_utf8_span (
"The layout engine doesn't currently support aligning fonts to "
"their baseline within a single line so the following text looks "
"funny. In order to properly implement it, a stupidly simple "
"change is needed in MyGUI to report the where the baseline is for "
"a particular font"
));
typesetter->sectionBreak (20);
typesetter->write (big, to_utf8_span ("big text g"));
typesetter->write (body, to_utf8_span (" проверяем только в дебаге"));
typesetter->write (body, to_utf8_span (" normal g"));
typesetter->write (big, to_utf8_span (" done g"));
typesetter->sectionBreak (20);
typesetter->write (test, to_utf8_span (
"int main (int argc,\n"
" char ** argv)\n"
"{\n"
" print (\"hello world!\\n\");\n"
" return 0;\n"
"}\n"
));
return typesetter->complete ();
}
book JournalBooks::createJournalBook ()
{
BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true));
return typesetter->complete ();
}
book JournalBooks::createTopicBook (uintptr_t topicId)
{
BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
mModel->visitTopicName (topicId, AddTopicName (typesetter, header));
intptr_t contentId = typesetter->addContent (to_utf8_span (", \""));
mModel->visitTopicEntries (topicId, AddTopicEntry (typesetter, body, header, contentId));
return typesetter->complete ();
}
book JournalBooks::createQuestBook (uintptr_t questId)
{
BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
mModel->visitQuestName (questId, AddQuestName (typesetter, header));
mModel->visitJournalEntries (questId, AddJournalEntry (typesetter, body, header, false));
return typesetter->complete ();
}
book JournalBooks::createTopicIndexBook ()
{
BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250);
typesetter->setSectionAlignment (BookTypesetter::AlignCenter);
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
for (int i = 0; i < 26; ++i)
{
char ch = 'A' + i;
char buffer [32];
sprintf (buffer, "( %c )", ch);
BookTypesetter::Style* style = typesetter->createHotStyle (body, MyGUI::Colour::Black, linkHot, linkActive, ch);
if (i == 13)
typesetter->sectionBreak ();
typesetter->write (style, to_utf8_span (buffer));
typesetter->lineBreak ();
}
return typesetter->complete ();
}
book JournalBooks::createTopicIndexBook (char character)
{
BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF);
BookTypesetter::Style* style = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
mModel->visitTopicNamesStartingWith (character, AddTopicLink (typesetter, style));
return typesetter->complete ();
}
book JournalBooks::createQuestIndexBook (bool activeOnly)
{
BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF);
BookTypesetter::Style* base = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black);
mModel->visitQuestNames (activeOnly, AddQuestLink (typesetter, base));
return typesetter->complete ();
}
BookTypesetter::Ptr JournalBooks::createTypesetter ()
{
//TODO: determine page size from layout...
return BookTypesetter::create (240, 300);
}
}

View File

@ -0,0 +1,29 @@
#ifndef MWGUI_JOURNALBOOKS_HPP
#define MWGUI_JOURNALBOOKS_HPP
#include "bookpage.hpp"
#include "journalviewmodel.hpp"
namespace MWGui
{
struct JournalBooks
{
typedef TypesetBook::Ptr Book;
JournalViewModel::Ptr mModel;
JournalBooks (JournalViewModel::Ptr model);
Book createEmptyJournalBook ();
Book createJournalBook ();
Book createTopicBook (uintptr_t topicId);
Book createQuestBook (uintptr_t questId);
Book createTopicIndexBook ();
Book createTopicIndexBook (char character);
Book createQuestIndexBook (bool showAll);
private:
BookTypesetter::Ptr createTypesetter ();
};
}
#endif // MWGUI_JOURNALBOOKS_HPP

View File

@ -0,0 +1,389 @@
#include "journalviewmodel.hpp"
#include <map>
#include <sstream>
#include <boost/make_shared.hpp>
#include <MyGUI_LanguageManager.h>
#include <components/misc/utf8stream.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwdialogue/journalentry.hpp"
#include "keywordsearch.hpp"
namespace MWGui {
struct JournalViewModelImpl;
static void injectMonthName (std::ostream & os, int month);
struct JournalViewModelImpl : JournalViewModel
{
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
mutable bool mKeywordSearchLoaded;
mutable KeywordSearchT mKeywordSearch;
std::locale mLocale;
JournalViewModelImpl ()
{
mKeywordSearchLoaded = false;
}
virtual ~JournalViewModelImpl ()
{
}
/// \todo replace this nasty BS
static Utf8Span toUtf8Span (std::string const & str)
{
if (str.size () == 0)
return Utf8Span (Utf8Point (NULL), Utf8Point (NULL));
Utf8Point point = reinterpret_cast <Utf8Point> (str.c_str ());
return Utf8Span (point, point + str.size ());
}
void load ()
{
}
void unload ()
{
mKeywordSearch.clear ();
mKeywordSearchLoaded = false;
}
void ensureKeyWordSearchLoaded () const
{
if (!mKeywordSearchLoaded)
{
MWBase::Journal * journal = MWBase::Environment::get().getJournal();
for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i)
mKeywordSearch.seed (i->first, intptr_t (&i->second));
mKeywordSearchLoaded = true;
}
}
wchar_t tolower (wchar_t ch) const { return std::tolower (ch, mLocale); }
bool isEmpty () const
{
MWBase::Journal * journal = MWBase::Environment::get().getJournal();
return journal->begin () == journal->end ();
}
template <typename t_iterator, typename Interface>
struct BaseEntry : Interface
{
typedef t_iterator iterator_t;
iterator_t itr;
JournalViewModelImpl const * mModel;
BaseEntry (JournalViewModelImpl const * model, iterator_t itr) :
mModel (model), itr (itr), loaded (false)
{}
virtual ~BaseEntry () {}
mutable bool loaded;
mutable std::string utf8text;
typedef std::pair<size_t, size_t> Range;
// hyperlinks in @link# notation
mutable std::map<Range, intptr_t> mHyperLinks;
virtual std::string getText () const = 0;
void ensureLoaded () const
{
if (!loaded)
{
mModel->ensureKeyWordSearchLoaded ();
utf8text = getText ();
size_t pos_begin, pos_end;
for(;;)
{
pos_begin = utf8text.find('@');
if (pos_begin != std::string::npos)
pos_end = utf8text.find('#', pos_begin);
if (pos_begin != std::string::npos && pos_end != std::string::npos)
{
std::string link = utf8text.substr(pos_begin + 1, pos_end - pos_begin - 1);
const char specialPseudoAsteriskCharacter = 127;
std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*');
std::string topicName = MWBase::Environment::get().getWindowManager()->
getTranslationDataStorage().topicStandardForm(link);
std::string displayName = link;
while (displayName[displayName.size()-1] == '*')
displayName.erase(displayName.size()-1, 1);
utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName);
intptr_t value;
if (mModel->mKeywordSearch.containsKeyword(topicName, value))
mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value;
}
else
break;
}
loaded = true;
}
}
Utf8Span body () const
{
ensureLoaded ();
return toUtf8Span (utf8text);
}
void visitSpans (boost::function < void (TopicId, size_t, size_t)> visitor) const
{
ensureLoaded ();
mModel->ensureKeyWordSearchLoaded ();
if (mHyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
{
size_t formatted = 0; // points to the first character that is not laid out yet
for (std::map<Range, intptr_t>::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); ++it)
{
intptr_t topicId = it->second;
if (formatted < it->first.first)
visitor (0, formatted, it->first.first);
visitor (topicId, it->first.first, it->first.second);
formatted = it->first.second;
}
if (formatted < utf8text.size())
visitor (0, formatted, utf8text.size());
}
else
{
std::string::const_iterator i = utf8text.begin ();
KeywordSearchT::Match match;
while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match))
{
if (i != match.mBeg)
visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ());
visitor (match.mValue, match.mBeg - utf8text.begin (), match.mEnd - utf8text.begin ());
i = match.mEnd;
}
if (i != utf8text.end ())
visitor (0, i - utf8text.begin (), utf8text.size ());
}
}
};
void visitQuestNames (bool active_only, boost::function <void (QuestId, Utf8Span)> visitor) const
{
MWBase::Journal * journal = MWBase::Environment::get ().getJournal ();
for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i)
{
if (active_only && i->second.isFinished ())
continue;
/// \todo quest.getName() is broken? returns empty string
//const MWDialogue::Quest& quest = i->second;
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (i->first));
}
}
void visitQuestName (QuestId questId, boost::function <void (Utf8Span)> visitor) const
{
MWDialogue::Quest const * quest = reinterpret_cast <MWDialogue::Quest const *> (questId);
std::string name = quest->getName ();
visitor (toUtf8Span (name));
}
template <typename iterator_t>
struct JournalEntryImpl : BaseEntry <iterator_t, JournalEntry>
{
using BaseEntry <iterator_t, JournalEntry>::itr;
mutable std::string timestamp_buffer;
JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) :
BaseEntry <iterator_t, JournalEntry> (model, itr)
{}
std::string getText () const
{
return itr->getText(MWBase::Environment::get().getWorld()->getStore());
}
Utf8Span timestamp () const
{
if (timestamp_buffer.empty ())
{
std::ostringstream os;
os << itr->mDayOfMonth << ' ';
injectMonthName (os, itr->mMonth);
const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
os << " (" << dayStr << " " << (itr->mDay + 1) << ')';
timestamp_buffer = os.str ();
}
return toUtf8Span (timestamp_buffer);
}
};
void visitJournalEntries (QuestId questId, boost::function <void (JournalEntry const &)> visitor) const
{
MWBase::Journal * journal = MWBase::Environment::get().getJournal();
if (questId != 0)
{
MWDialogue::Quest const * quest = reinterpret_cast <MWDialogue::Quest const *> (questId);
for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i)
{
for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j)
{
if (i->mInfoId == *j)
visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));
}
}
}
else
{
for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i)
visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));
}
}
void visitTopics (boost::function <void (TopicId, Utf8Span)> visitor) const
{
throw std::runtime_error ("not implemented");
}
void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const
{
MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(topic.getName())->mId;
visitor (toUtf8Span (name));
}
void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const
{
MWBase::Journal * journal = MWBase::Environment::get().getJournal();
for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)
{
if (i->first [0] != std::tolower (character, mLocale))
continue;
// This is to get the correct case for the topic
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(i->first)->mId;
visitor (TopicId (&i->second), toUtf8Span (name));
}
}
struct TopicEntryImpl : BaseEntry <MWDialogue::Topic::TEntryIter, TopicEntry>
{
MWDialogue::Topic const & mTopic;
mutable std::string source_buffer;
TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) :
BaseEntry (model, itr), mTopic (topic)
{}
std::string getText () const
{
/// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor
return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore());
}
Utf8Span source () const
{
if (source_buffer.empty ())
source_buffer = "someone";
return toUtf8Span (source_buffer);
}
};
void visitTopicEntries (TopicId topicId, boost::function <void (TopicEntry const &)> visitor) const
{
typedef MWDialogue::Topic::TEntryIter iterator_t;
MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);
for (iterator_t i = topic.begin (); i != topic.end (); ++i)
visitor (TopicEntryImpl (this, topic, i));
}
};
static void injectMonthName (std::ostream & os, int month)
{
MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance();
if (month == 0)
os << lm.replaceTags ("#{sMonthMorningstar}");
else if (month == 1)
os << lm.replaceTags ("#{sMonthSunsdawn}");
else if (month == 2)
os << lm.replaceTags ("#{sMonthFirstseed}");
else if (month == 3)
os << lm.replaceTags ("#{sMonthRainshand}");
else if (month == 4)
os << lm.replaceTags ("#{sMonthSecondseed}");
else if (month == 5)
os << lm.replaceTags ("#{sMonthMidyear}");
else if (month == 6)
os << lm.replaceTags ("#{sMonthSunsheight}");
else if (month == 7)
os << lm.replaceTags ("#{sMonthLastseed}");
else if (month == 8)
os << lm.replaceTags ("#{sMonthHeartfire}");
else if (month == 9)
os << lm.replaceTags ("#{sMonthFrostfall}");
else if (month == 10)
os << lm.replaceTags ("#{sMonthSunsdusk}");
else if (month == 11)
os << lm.replaceTags ("#{sMonthEveningstar}");
else
os << month;
}
JournalViewModel::Ptr JournalViewModel::create ()
{
return boost::make_shared <JournalViewModelImpl> ();
}
}

View File

@ -0,0 +1,93 @@
#ifndef MWGUI_JOURNALVIEWMODEL_HPP
#define MWGUI_JOURNALVIEWMODEL_HPP
#include <string>
#include <memory>
#include <functional>
#include <platform/stdint.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
namespace MWGui
{
/// View-Model for the journal GUI
///
/// This interface defines an abstract data model suited
/// specifically to the needs of the journal GUI. It isolates
/// the journal GUI from the implementation details of the
/// game data store.
struct JournalViewModel
{
typedef boost::shared_ptr <JournalViewModel> Ptr;
typedef intptr_t QuestId;
typedef intptr_t TopicId;
typedef uint8_t const * Utf8Point;
typedef std::pair <Utf8Point, Utf8Point> Utf8Span;
/// The base interface for both journal entries and topics.
struct Entry
{
/// returns the body text for the journal entry
///
/// This function returns a borrowed reference to the body of the
/// journal entry. The returned reference becomes invalid when the
/// entry is destroyed.
virtual Utf8Span body () const = 0;
/// Visits each subset of text in the body, delivering the beginning
/// and end of the span relative to the body, and a valid topic ID if
/// the span represents a keyword, or zero if not.
virtual void visitSpans (boost::function <void (TopicId, size_t, size_t)> visitor) const = 0;
};
/// An interface to topic data.
struct TopicEntry : Entry
{
/// Returns a pre-formatted span of UTF8 encoded text representing
/// the name of the NPC this portion of dialog was heard from.
virtual Utf8Span source () const = 0;
};
/// An interface to journal data.
struct JournalEntry : Entry
{
/// Returns a pre-formatted span of UTF8 encoded text representing
/// the in-game date this entry was added to the journal.
virtual Utf8Span timestamp () const = 0;
};
/// called prior to journal opening
virtual void load () = 0;
/// called prior to journal closing
virtual void unload () = 0;
/// returns true if their are no journal entries to display
virtual bool isEmpty () const = 0;
/// provides access to the name of the quest with the specified identifier
virtual void visitQuestName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const = 0;
/// walks the active and optionally completed, quests providing the quest id and name
virtual void visitQuestNames (bool active_only, boost::function <void (QuestId, Utf8Span)> visitor) const = 0;
/// walks over the journal entries related to the specified quest identified by its id
virtual void visitJournalEntries (QuestId questId, boost::function <void (JournalEntry const &)> visitor) const = 0;
/// provides the name of the topic specified by its id
virtual void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const = 0;
/// walks over the topics whose names start with the specified character providing the topics id and name
virtual void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const = 0;
/// walks over the topic entries for the topic specified by its identifier
virtual void visitTopicEntries (TopicId topicId, boost::function <void (TopicEntry const &)> visitor) const = 0;
// create an instance of the default journal view model implementation
static Ptr create ();
};
}
#endif // MWGUI_JOURNALVIEWMODEL_HPP

View File

@ -1,192 +1,433 @@
#include "journalwindow.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "list.hpp"
#include <sstream>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include "boost/lexical_cast.hpp"
#include "bookpage.hpp"
#include "windowbase.hpp"
#include "imagebutton.hpp"
#include "journalviewmodel.hpp"
#include "journalbooks.hpp"
namespace
{
struct book
static char const OptionsOverlay [] = "OptionsOverlay";
static char const OptionsBTN [] = "OptionsBTN";
static char const PrevPageBTN [] = "PrevPageBTN";
static char const NextPageBTN [] = "NextPageBTN";
static char const CloseBTN [] = "CloseBTN";
static char const JournalBTN [] = "JournalBTN";
static char const TopicsBTN [] = "TopicsBTN";
static char const QuestsBTN [] = "QuestsBTN";
static char const CancelBTN [] = "CancelBTN";
static char const ShowAllBTN [] = "ShowAllBTN";
static char const ShowActiveBTN [] = "ShowActiveBTN";
static char const PageOneNum [] = "PageOneNum";
static char const PageTwoNum [] = "PageTwoNum";
static char const TopicsList [] = "TopicsList";
static char const TopicsPage [] = "TopicsPage";
static char const QuestsList [] = "QuestsList";
static char const QuestsPage [] = "QuestsPage";
static char const LeftBookPage [] = "LeftBookPage";
static char const RightBookPage [] = "RightBookPage";
static char const LeftTopicIndex [] = "LeftTopicIndex";
static char const RightTopicIndex [] = "RightTopicIndex";
struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow
{
int endLine;
std::list<std::string> pages;
struct DisplayState
{
unsigned int mPage;
Book mBook;
};
typedef std::stack <DisplayState> DisplayStateStack;
DisplayStateStack mStates;
Book mTopicIndexBook;
bool mQuestMode;
bool mAllQuests;
template <typename T>
T * getWidget (char const * name)
{
T * widget;
WindowBase::getWidget (widget, name);
return widget;
}
template <typename value_type>
void setText (char const * name, value_type const & value)
{
getWidget <MyGUI::TextBox> (name) ->
setCaption (boost::lexical_cast <std::string> (value));
}
void setVisible (char const * name, bool visible)
{
getWidget <MyGUI::Widget> (name) ->
setVisible (visible);
}
void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender))
{
getWidget <MWGui::ImageButton> (name) ->
eventMouseButtonClick += newDelegate(this, Handler);
}
MWGui::BookPage* getPage (char const * name)
{
return getWidget <MWGui::BookPage> (name);
}
JournalWindowImpl (MWGui::JournalViewModel::Ptr Model)
: WindowBase("openmw_journal.layout"), JournalBooks (Model)
{
mMainWidget->setVisible(false);
center();
adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions );
adviseButtonClick (PrevPageBTN, &JournalWindowImpl::notifyPrevPage );
adviseButtonClick (NextPageBTN, &JournalWindowImpl::notifyNextPage );
adviseButtonClick (CloseBTN, &JournalWindowImpl::notifyClose );
adviseButtonClick (JournalBTN, &JournalWindowImpl::notifyJournal );
adviseButtonClick (TopicsBTN, &JournalWindowImpl::notifyTopics );
adviseButtonClick (QuestsBTN, &JournalWindowImpl::notifyQuests );
adviseButtonClick (CancelBTN, &JournalWindowImpl::notifyCancel );
adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll );
adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive);
{
MWGui::BookPage::ClickCallback callback;
callback = boost::bind (&JournalWindowImpl::notifyTopicClicked, this, _1);
getPage (TopicsPage)->adviseLinkClicked (callback);
getPage (LeftBookPage)->adviseLinkClicked (callback);
getPage (RightBookPage)->adviseLinkClicked (callback);
}
{
MWGui::BookPage::ClickCallback callback;
callback = boost::bind (&JournalWindowImpl::notifyIndexLinkClicked, this, _1);
getPage (LeftTopicIndex)->adviseLinkClicked (callback);
getPage (RightTopicIndex)->adviseLinkClicked (callback);
}
{
MWGui::BookPage::ClickCallback callback;
callback = boost::bind (&JournalWindowImpl::notifyQuestClicked, this, _1);
getPage (QuestsPage)->adviseLinkClicked (callback);
}
mQuestMode = false;
mAllQuests = false;
}
void open()
{
mModel->load ();
setBookMode ();
/// \todo Wiping the whole book layout each time the journal is opened is probably too costly for a large journal (eg 300+ pages).
/// There should be a way to keep the existing layout and append new entries to the end of it.
/// However, that still leaves the problem of having to add links to previously unknown, but now known topics, so
/// we maybe need to find another way to speed things up.
Book journalBook;
if (mModel->isEmpty ())
journalBook = createEmptyJournalBook ();
else
journalBook = createJournalBook ();
pushBook (journalBook, 0);
// fast forward to the last page
if (!mStates.empty ())
{
unsigned int & page = mStates.top ().mPage;
page = mStates.top().mBook->pageCount()-1;
if (page%2)
--page;
}
updateShowingPages();
}
void close()
{
mModel->unload ();
getPage (LeftBookPage)->showPage (Book (), 0);
getPage (RightBookPage)->showPage (Book (), 0);
while (!mStates.empty ())
mStates.pop ();
mTopicIndexBook.reset ();
}
void setVisible (bool newValue)
{
WindowBase::setVisible (newValue);
}
void setBookMode ()
{
setVisible (OptionsBTN, true);
setVisible (OptionsOverlay, false);
updateShowingPages ();
updateCloseJournalButton ();
}
void setOptionsMode ()
{
setVisible (OptionsBTN, false);
setVisible (OptionsOverlay, true);
setVisible (PrevPageBTN, false);
setVisible (NextPageBTN, false);
setVisible (CloseBTN, false);
setVisible (JournalBTN, false);
setVisible (TopicsList, false);
setVisible (QuestsList, mQuestMode);
setVisible (LeftTopicIndex, !mQuestMode);
setVisible (RightTopicIndex, !mQuestMode);
setVisible (ShowAllBTN, mQuestMode && !mAllQuests);
setVisible (ShowActiveBTN, mQuestMode && mAllQuests);
//TODO: figure out how to make "options" page overlay book page
// correctly, so that text may show underneath
getPage (RightBookPage)->showPage (Book (), 0);
}
void pushBook (Book book, unsigned int page)
{
DisplayState bs;
bs.mPage = page;
bs.mBook = book;
mStates.push (bs);
updateShowingPages ();
updateCloseJournalButton ();
}
void replaceBook (Book book, unsigned int page)
{
assert (!mStates.empty ());
mStates.top ().mBook = book;
mStates.top ().mPage = page;
updateShowingPages ();
}
void popBook ()
{
mStates.pop ();
updateShowingPages ();
updateCloseJournalButton ();
}
void updateCloseJournalButton ()
{
setVisible (CloseBTN, mStates.size () < 2);
setVisible (JournalBTN, mStates.size () >= 2);
}
void updateShowingPages ()
{
Book book;
unsigned int page;
unsigned int relPages;
if (!mStates.empty ())
{
book = mStates.top ().mBook;
page = mStates.top ().mPage;
relPages = book->pageCount () - page;
}
else
{
page = 0;
relPages = 0;
}
setVisible (PrevPageBTN, page > 0);
setVisible (NextPageBTN, relPages > 2);
setVisible (PageOneNum, relPages > 0);
setVisible (PageTwoNum, relPages > 1);
getPage (LeftBookPage)->showPage ((relPages > 0) ? book : Book (), page+0);
getPage (RightBookPage)->showPage ((relPages > 0) ? book : Book (), page+1);
setText (PageOneNum, page + 1);
setText (PageTwoNum, page + 2);
}
void notifyTopicClicked (intptr_t linkId)
{
Book topicBook = createTopicBook (linkId);
if (mStates.size () > 1)
replaceBook (topicBook, 0);
else
pushBook (topicBook, 0);
setVisible (OptionsOverlay, false);
setVisible (OptionsBTN, true);
setVisible (JournalBTN, true);
}
void notifyQuestClicked (intptr_t questId)
{
Book book = createQuestBook (questId);
if (mStates.size () > 1)
replaceBook (book, 0);
else
pushBook (book, 0);
setVisible (OptionsOverlay, false);
setVisible (OptionsBTN, true);
setVisible (JournalBTN, true);
}
void notifyOptions(MyGUI::Widget* _sender)
{
setOptionsMode ();
if (!mTopicIndexBook)
mTopicIndexBook = createTopicIndexBook ();
getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0);
getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1);
}
void notifyJournal(MyGUI::Widget* _sender)
{
assert (mStates.size () > 1);
popBook ();
}
void showList (char const * listId, char const * pageId, Book book)
{
std::pair <int, int> size = book->getSize ();
getPage (pageId)->showPage (book, 0);
getWidget <MyGUI::ScrollView> (listId)->setCanvasSize (size.first, size.second);
}
void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character)
{
setVisible (LeftTopicIndex, false);
setVisible (RightTopicIndex, false);
setVisible (TopicsList, true);
showList (TopicsList, TopicsPage, createTopicIndexBook ((char)character));
}
void notifyTopics(MyGUI::Widget* _sender)
{
mQuestMode = false;
setVisible (LeftTopicIndex, true);
setVisible (RightTopicIndex, true);
setVisible (TopicsList, false);
setVisible (QuestsList, false);
setVisible (ShowAllBTN, false);
setVisible (ShowActiveBTN, false);
}
void notifyQuests(MyGUI::Widget* _sender)
{
mQuestMode = true;
setVisible (LeftTopicIndex, false);
setVisible (RightTopicIndex, false);
setVisible (TopicsList, false);
setVisible (QuestsList, true);
setVisible (ShowAllBTN, !mAllQuests);
setVisible (ShowActiveBTN, mAllQuests);
showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests));
}
void notifyShowAll(MyGUI::Widget* _sender)
{
mAllQuests = true;
setVisible (ShowAllBTN, !mAllQuests);
setVisible (ShowActiveBTN, mAllQuests);
showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests));
}
void notifyShowActive(MyGUI::Widget* _sender)
{
mAllQuests = false;
setVisible (ShowAllBTN, !mAllQuests);
setVisible (ShowActiveBTN, mAllQuests);
showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests));
}
void notifyCancel(MyGUI::Widget* _sender)
{
setBookMode ();
}
void notifyClose(MyGUI::Widget* _sender)
{
MWBase::Environment::get().getWindowManager ()->popGuiMode ();
}
void notifyNextPage(MyGUI::Widget* _sender)
{
if (!mStates.empty ())
{
unsigned int & page = mStates.top ().mPage;
Book book = mStates.top ().mBook;
if (page < book->pageCount () - 2)
{
page += 2;
updateShowingPages ();
}
}
}
void notifyPrevPage(MyGUI::Widget* _sender)
{
if (!mStates.empty ())
{
unsigned int & page = mStates.top ().mPage;
if(page > 0)
{
page -= 2;
updateShowingPages ();
}
}
}
};
}
book formatText(std::string text,book mBook,int maxLine, int lineSize)
// glue the implementation to the interface
MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model)
{
//stringList.push_back("");
int cLineSize = 0;
int cLine = mBook.endLine +1;
std::string cString;
if(mBook.pages.empty())
{
cString = "";
cLine = 0;
}
else
{
cString = mBook.pages.back() + std::string("\n");
mBook.pages.pop_back();
}
//std::string::iterator wordBegin = text.begin();
//std::string::iterator wordEnd;
std::string cText = text;
while(cText.length() != 0)
{
size_t firstSpace = cText.find_first_of(' ');
if(firstSpace == std::string::npos)
{
cString = cString + cText;
mBook.pages.push_back(cString);
//TODO:finnish this
break;
}
if(static_cast<int> (firstSpace) + cLineSize <= lineSize)
{
cLineSize = firstSpace + cLineSize;
cString = cString + cText.substr(0,firstSpace +1);
}
else
{
cLineSize = firstSpace;
if(cLine +1 <= maxLine)
{
cLine = cLine + 1;
cString = cString + std::string("\n") + cText.substr(0,firstSpace +1);
}
else
{
cLine = 0;
mBook.pages.push_back(cString);
cString = cText.substr(0,firstSpace +1);
}
}
//std::cout << cText << "\n";
//std::cout << cText.length();
cText = cText.substr(firstSpace +1,cText.length() - firstSpace -1);
}
mBook.endLine = cLine;
return mBook;
//std::string
}
MWGui::JournalWindow::JournalWindow ()
: WindowBase("openmw_journal.layout")
, mPageNumber(0)
{
mMainWidget->setVisible(false);
//setCoord(0,0,498, 342);
center();
getWidget(mLeftTextWidget, "LeftText");
getWidget(mRightTextWidget, "RightText");
getWidget(mPrevBtn, "PrevPageBTN");
mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage);
getWidget(mNextBtn, "NextPageBTN");
mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage);
//MyGUI::ItemBox* list = new MyGUI::ItemBox();
//list->addItem("qaq","aqzazaz");
//mScrollerWidget->addChildItem(list);
//mScrollerWidget->addItem("dserzt",MyGUI::UString("fedgdfg"));
//mEditWidget->addText("ljblsxdvdsfvgedfvdfvdkjfghldfjgn sdv,nhsxl;vvn lklksbvlksb lbsdflkbdSLKJGBLskdhbvl<kbvlqksbgkqsjhdvb");
//mEditWidget->show();
//mEditWidget->setEditStatic(true);
mLeftTextWidget->addText("left texxxt ");
mLeftTextWidget->setEditReadOnly(true);
mRightTextWidget->setEditReadOnly(true);
mRightTextWidget->setEditStatic(true);
mLeftTextWidget->setEditStatic(true);
mRightTextWidget->addText("Right texxt ");
//std::list<std::string> list = formatText("OpenMW rgh dsfg sqef srg ZT uzql n ZLIEHRF LQSJH GLOIjf qjfmj hslkdgn jlkdjhg qlr isgli shli uhs fiuh qksf cg ksjnf lkqsnbf ksbf sbfkl zbf kuyzflkj sbgdfkj zlfh ozhjfmo hzmfh lizuf rty qzt ezy tkyEZT RYYJ DG fgh is an open-source implementation of the game engine found in the game Morrowind. This is a dumb test text msodjbg smojg smoig fiiinnn");
//std::list<std::string> list = formatText();
//displayLeftText(list.front());
}
void MWGui::JournalWindow::open()
{
mPageNumber = 0;
if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end())
{
book journal;
journal.endLine = 0;
for(std::deque<MWDialogue::StampedJournalEntry>::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it)
{
std::string a = it->getText(MWBase::Environment::get().getWorld()->getStore());
journal = formatText(a,journal,10,17);
journal.endLine = journal.endLine +1;
journal.pages.back() = journal.pages.back() + std::string("\n");
}
//std::string a = MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore());
//std::list<std::string> journal = formatText(a,10,20,1);
bool left = true;
for(std::list<std::string>::iterator it = journal.pages.begin(); it != journal.pages.end();++it)
{
if(left)
{
mLeftPages.push_back(*it);
}
else
{
mRightPages.push_back(*it);
}
left = !left;
}
if(!left) mRightPages.push_back("");
mPageNumber = mLeftPages.size()-1;
displayLeftText(mLeftPages[mPageNumber]);
displayRightText(mRightPages[mPageNumber]);
}
else
{
//std::cout << MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore());
}
}
void MWGui::JournalWindow::displayLeftText(std::string text)
{
mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength());
mLeftTextWidget->addText(text);
}
void MWGui::JournalWindow::displayRightText(std::string text)
{
mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength());
mRightTextWidget->addText(text);
}
void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender)
{
if(mPageNumber < int(mLeftPages.size())-1)
{
std::string nextSound = "book page2";
MWBase::Environment::get().getSoundManager()->playSound (nextSound, 1.0, 1.0);
mPageNumber = mPageNumber + 1;
displayLeftText(mLeftPages[mPageNumber]);
displayRightText(mRightPages[mPageNumber]);
}
}
void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender)
{
if(mPageNumber > 0)
{
std::string prevSound = "book page";
MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0);
mPageNumber = mPageNumber - 1;
displayLeftText(mLeftPages[mPageNumber]);
displayRightText(mRightPages[mPageNumber]);
}
return new JournalWindowImpl (Model);
}

View File

@ -1,39 +1,26 @@
#ifndef MWGUI_JOURNAL_H
#define MWGUI_JOURNAL_H
#include <string>
#include <memory>
#include <boost/shared_ptr.hpp>
#include "windowbase.hpp"
#include "imagebutton.hpp"
namespace MWBase { class WindowManager; }
namespace MWGui
{
class JournalWindow : public WindowBase
struct JournalViewModel;
struct JournalWindow
{
public:
JournalWindow();
virtual void open();
/// construct a new instance of the one JournalWindow implementation
static JournalWindow * create (boost::shared_ptr <JournalViewModel> Model);
private:
void displayLeftText(std::string text);
void displayRightText(std::string text);
/// destroy this instance of the JournalWindow implementation
virtual ~JournalWindow () {};
/**
*Called when next/prev button is used.
*/
void notifyNextPage(MyGUI::Widget* _sender);
void notifyPrevPage(MyGUI::Widget* _sender);
MyGUI::EditBox* mLeftTextWidget;
MyGUI::EditBox* mRightTextWidget;
MWGui::ImageButton* mPrevBtn;
MWGui::ImageButton* mNextBtn;
std::vector<std::string> mLeftPages;
std::vector<std::string> mRightPages;
int mPageNumber; //store the number of the current left page
/// show/hide the journal window
virtual void setVisible (bool newValue) = 0;
};
}
#endif

View File

View File

@ -0,0 +1,199 @@
#ifndef MWGUI_KEYWORDSEARCH_H
#define MWGUI_KEYWORDSEARCH_H
#include <map>
#include <locale>
#include <stdexcept>
#include <vector>
#include <algorithm> // std::reverse
#include <components/misc/stringops.hpp>
namespace MWGui
{
template <typename string_t, typename value_t>
class KeywordSearch
{
public:
typedef typename string_t::const_iterator Point;
struct Match
{
Point mBeg;
Point mEnd;
value_t mValue;
};
void seed (string_t keyword, value_t value)
{
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot);
}
void clear ()
{
mRoot.mChildren.clear ();
mRoot.mKeyword.clear ();
}
bool containsKeyword (string_t keyword, value_t& value)
{
typename Entry::childen_t::iterator current;
typename Entry::childen_t::iterator next;
current = mRoot.mChildren.find (std::tolower (*keyword.begin(), mLocale));
if (current == mRoot.mChildren.end())
return false;
else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword))
{
value = current->second.mValue;
return true;
}
for (Point i = ++keyword.begin(); i != keyword.end(); ++i)
{
next = current->second.mChildren.find(std::tolower (*i, mLocale));
if (next == current->second.mChildren.end())
return false;
if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword))
{
value = next->second.mValue;
return true;
}
current = next;
}
return false;
}
bool search (Point beg, Point end, Match & match)
{
for (Point i = beg; i != end; ++i)
{
// check first character
typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale));
// no match, on to next character
if (candidate == mRoot.mChildren.end ())
continue;
// see how far the match goes
Point j = i;
// some keywords might be longer variations of other keywords, so we definitely need a list of candidates
// the first element in the pair is length of the match, i.e. depth from the first character on
std::vector< typename std::pair<int, typename Entry::childen_t::iterator> > candidates;
while ((j + 1) != end)
{
typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale));
if (next == candidate->second.mChildren.end ())
{
if (candidate->second.mKeyword.size() > 0)
candidates.push_back(std::make_pair((j-i), candidate));
break;
}
candidate = next;
if (candidate->second.mKeyword.size() > 0)
candidates.push_back(std::make_pair((j-i), candidate));
}
if (!candidates.size())
continue; // didn't match enough to disambiguate, on to next character
// shorter candidates will be added to the vector first. however, we want to check against longer candidates first
std::reverse(candidates.begin(), candidates.end());
for (typename std::vector< std::pair<int, typename Entry::childen_t::iterator> >::iterator it = candidates.begin();
it != candidates.end(); ++it)
{
candidate = it->second;
// try to match the rest of the keyword
Point k = i + it->first;
typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (k - i);
while (k != end && t != candidate->second.mKeyword.end ())
{
if (std::tolower (*k, mLocale) != std::tolower (*t, mLocale))
break;
++k, ++t;
}
// didn't match full keyword, try the next candidate
if (t != candidate->second.mKeyword.end ())
continue;
// we did it, report the good news
match.mValue = candidate->second.mValue;
match.mBeg = i;
match.mEnd = k;
return true;
}
}
// no match in range, report the bad news
return false;
}
private:
struct Entry
{
typedef std::map <wchar_t, Entry> childen_t;
string_t mKeyword;
value_t mValue;
childen_t mChildren;
};
void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry)
{
int ch = tolower (keyword.at (depth), mLocale);
typename Entry::childen_t::iterator j = entry.mChildren.find (ch);
if (j == entry.mChildren.end ())
{
entry.mChildren [ch].mValue = /*std::move*/ (value);
entry.mChildren [ch].mKeyword = /*std::move*/ (keyword);
}
else
{
if (j->second.mKeyword.size () > 0)
{
if (keyword == j->second.mKeyword)
throw std::runtime_error ("duplicate keyword inserted");
value_t pushValue = /*std::move*/ (j->second.mValue);
string_t pushKeyword = /*std::move*/ (j->second.mKeyword);
if (depth >= pushKeyword.size ())
throw std::runtime_error ("unexpected");
if (depth+1 < pushKeyword.size())
{
seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second);
j->second.mKeyword.clear ();
}
}
if (depth+1 == keyword.size())
j->second.mKeyword = value;
else // depth+1 < keyword.size()
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second);
}
}
Entry mRoot;
std::locale mLocale;
};
}
#endif

View File

@ -22,6 +22,7 @@ namespace MWGui
, mLastRenderTime(0.f)
, mLastWallpaperChangeTime(0.f)
, mFirstLoad(true)
, mTotalRefsLoading(0)
{
getWidget(mLoadingText, "LoadingText");
getWidget(mProgressBar, "ProgressBar");

View File

@ -13,6 +13,7 @@ namespace MWGui
// defines
mMessageBoxSpeed = 0.1;
mInterMessageBoxe = NULL;
mStaticMessageBox = NULL;
}
void MessageBoxManager::onFrame (float frameDuration)
@ -68,11 +69,14 @@ namespace MWGui
}
}
void MessageBoxManager::createMessageBox (const std::string& message)
void MessageBoxManager::createMessageBox (const std::string& message, bool stat)
{
MessageBox *box = new MessageBox(*this, message);
removeMessageBox(message.length()*mMessageBoxSpeed, box);
if(stat)
mStaticMessageBox = box;
else
removeMessageBox(message.length()*mMessageBoxSpeed, box);
mMessageBoxes.push_back(box);
std::vector<MessageBox*>::iterator it;
@ -90,6 +94,12 @@ namespace MWGui
}
}
void MessageBoxManager::removeStaticMessageBox ()
{
removeMessageBox(mStaticMessageBox);
mStaticMessageBox = NULL;
}
bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons)
{
if(mInterMessageBoxe != NULL) {

View File

@ -31,7 +31,8 @@ namespace MWGui
public:
MessageBoxManager ();
void onFrame (float frameDuration);
void createMessageBox (const std::string& message);
void createMessageBox (const std::string& message, bool stat = false);
void removeStaticMessageBox ();
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
bool isInteractiveMessageBox ();
@ -52,6 +53,7 @@ namespace MWGui
private:
std::vector<MessageBox*> mMessageBoxes;
InteractiveMessageBox* mInterMessageBoxe;
MessageBox* mStaticMessageBox;
std::vector<MessageBoxManagerTimer> mTimers;
float mMessageBoxSpeed;
};

View File

@ -39,7 +39,7 @@ namespace MWGui
MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>();
BookTextParser parser;
MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390);
MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390);
if (size.height > mTextView->getSize().height)
mTextView->setCanvasSize(MyGUI::IntSize(410, size.height));

View File

@ -551,6 +551,8 @@ namespace MWGui
while (mControlsBox->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0));
MWBase::Environment::get().getWindowManager ()->removeStaticMessageBox();
std::vector<int> actions = MWBase::Environment::get().getInputManager()->getActionSorting ();
const int h = 18;
@ -585,7 +587,7 @@ namespace MWGui
static_cast<MyGUI::Button*>(_sender)->setCaptionWithReplacing("#{sNone}");
MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}");
MWBase::Environment::get().getWindowManager ()->staticMessageBox ("#{sControlsMenu3}");
MWBase::Environment::get().getWindowManager ()->disallowMouse();
MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId);

View File

@ -7,9 +7,9 @@
#include "console.hpp"
#include "journalwindow.hpp"
#include "journalviewmodel.hpp"
#include "charactercreation.hpp"
#include "dialogue.hpp"
#include "dialoguehistory.hpp"
#include "statswindow.hpp"
#include "messagebox.hpp"
#include "tooltips.hpp"
@ -38,6 +38,7 @@
#include "soulgemdialog.hpp"
#include "companionwindow.hpp"
#include "inventorywindow.hpp"
#include "bookpage.hpp"
namespace MWGui
{
@ -106,7 +107,6 @@ namespace MWGui
mGui = mGuiManager->getGui();
//Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<DialogueHistory>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpell>("Widget");
@ -122,6 +122,7 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ImageButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ExposedWindow>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWScrollView>("Widget");
BookPage::registerMyGUIComponents ();
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::ResourceManager::getInstance().load("core.xml");
@ -145,7 +146,7 @@ namespace MWGui
mMap = new MapWindow(cacheDir);
mStatsWindow = new StatsWindow();
mConsole = new Console(w,h, consoleOnlyScripts);
mJournal = new JournalWindow();
mJournal = JournalWindow::create(JournalViewModel::create ());
mMessageBoxManager = new MessageBoxManager();
mInventoryWindow = new InventoryWindow(mDragAndDrop);
mTradeWindow = new TradeWindow();
@ -600,6 +601,16 @@ namespace MWGui
}
}
void WindowManager::staticMessageBox(const std::string& message)
{
mMessageBoxManager->createMessageBox(message, true);
}
void WindowManager::removeStaticMessageBox()
{
mMessageBoxManager->removeStaticMessageBox();
}
void WindowManager::enterPressed ()
{
mMessageBoxManager->enterPressed();

View File

@ -192,6 +192,8 @@ namespace MWGui
virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted.
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>());
virtual void staticMessageBox(const std::string& message);
virtual void removeStaticMessageBox();
virtual void enterPressed ();
virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)

View File

@ -905,6 +905,10 @@ namespace MWInput
void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control
, OIS::KeyCode key, ICS::Control::ControlChangingDirection direction)
{
//Disallow binding escape key, and unassigned keys
if(key==OIS::KC_ESCAPE || key==OIS::KC_UNASSIGNED)
return
clearAllBindings(control);
ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction);
MWBase::Environment::get().getWindowManager ()->notifyInputActionBound ();

View File

@ -48,7 +48,7 @@ namespace MWMechanics
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 2000))
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
@ -58,7 +58,7 @@ namespace MWMechanics
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 2000))
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;

View File

@ -152,6 +152,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mMovingAnim(false)
, mSecondsOfRunning(0)
, mSecondsOfSwimming(0)
, mLooping(false)
{
if(!mAnimation)
return;

View File

@ -14,6 +14,7 @@
#include <OgreCompositionPass.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreControllerManager.h>
#include <OgreMeshManager.h>
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
@ -120,13 +121,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 );
//ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::TextureManager::getSingleton().setMemoryBudget(126*1024*1024);
Ogre::MeshManager::getSingleton().setMemoryBudget(64*1024*1024);
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
// causes light flicker in opengl when moving..
//mRendering.getScene()->setCameraRelativeRendering(true);
// disable unsupported effects
if (!Settings::Manager::getBool("shaders", "Objects"))
Settings::Manager::setBool("enabled", "Shadows", false);
@ -236,6 +235,7 @@ void RenderingManager::toggleWater()
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
{
sh::Factory::getInstance().unloadUnreferencedMaterials();
mObjects.buildStaticGeometry (*store);
mDebugging->cellAdded(store);
if (store->mCell->isExterior())
@ -306,7 +306,6 @@ void RenderingManager::update (float duration, bool paused)
int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
mRendering.getFader()->setFactor(1.f-(blind / 100.f));
setAmbientMode();
// player position

View File

@ -49,6 +49,7 @@ namespace MWRender
TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc)
: Ogre::TerrainMaterialGenerator::Profile(parent, name, desc)
, mGlobalColourMap(false)
, mMaterial(0)
{
}

View File

@ -41,7 +41,9 @@ namespace MWRender {
{
public:
Reflection(Ogre::SceneManager* sceneManager)
: mSceneMgr(sceneManager) {}
: mSceneMgr(sceneManager)
, mIsUnderwater(false)
{}
virtual ~Reflection() {}
virtual void setHeight (float height) {}

View File

@ -292,19 +292,25 @@ namespace MWScript
std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first;
std::map<std::string, int> ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.begin();
const MWWorld::ESMStore &store = world->getStore();
const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
if(it->second < 0 || it->second > 9)
return "";
std::map<std::string, int> ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks();
if(it->second <= 8) // If player is at max rank, there is no next rank
return faction->mRanks[it->second + 1];
if (ranks.size())
{
std::map<std::string, int>::const_iterator it = ranks.begin();
if(it->second < -1 || it->second > 9)
return "";
if(it->second <= 8) // If player is at max rank, there is no next rank
return faction->mRanks[it->second + 1];
else
return faction->mRanks[it->second];
}
else
return faction->mRanks[it->second];
return faction->mRanks[0];
}
int InterpreterContext::getPCBounty() const

View File

@ -141,7 +141,7 @@ namespace Compiler
if (mState==SetMemberVarState)
{
mMemberName = Misc::StringUtils::lowerCase (name);
mMemberName = name;
char type = getContext().getMemberType (mMemberName, mName);
if (type!=' ')

View File

@ -23,7 +23,6 @@ namespace Interpreter{
}
std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){
unsigned int start = 0;
std::ostringstream retval;
for(unsigned int i = 0; i < text.length(); i++){

View File

@ -0,0 +1,116 @@
#ifndef MISC_UTF8ITER_HPP
#define MISC_UTF8ITER_HPP
#include <boost/tuple/tuple.hpp>
class Utf8Stream
{
public:
typedef uint32_t UnicodeChar;
typedef unsigned char const * Point;
//static const unicode_char sBadChar = 0xFFFFFFFF; gcc can't handle this
static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); }
Utf8Stream (Point begin, Point end) :
cur (begin), nxt (begin), end (end)
{
}
Utf8Stream (std::pair <Point, Point> range) :
cur (range.first), nxt (range.first), end (range.second)
{
}
bool eof () const
{
return cur == end;
}
Point current () const
{
return cur;
}
UnicodeChar peek ()
{
if (cur == nxt)
next ();
return val;
}
UnicodeChar consume ()
{
if (cur == nxt)
next ();
cur = nxt;
return val;
}
static std::pair <UnicodeChar, Point> decode (Point cur, Point end)
{
if ((*cur & 0x80) == 0)
{
UnicodeChar chr = *cur++;
return std::make_pair (chr, cur);
}
int octets;
UnicodeChar chr;
boost::tie (octets, chr) = octet_count (*cur++);
if (octets > 5)
return std::make_pair (sBadChar(), cur);
Point eoc = cur + octets;
if (eoc > end)
return std::make_pair (sBadChar(), cur);
while (cur != eoc)
{
if ((*cur & 0xC0) != 0x80) // check continuation mark
return std::make_pair (sBadChar(), cur);;
chr = (chr << 6) | UnicodeChar ((*cur++) & 0x3F);
}
return std::make_pair (chr, cur);
}
private:
static std::pair <int, UnicodeChar> octet_count (unsigned char octet)
{
int octets;
unsigned char mark = 0xC0;
unsigned char mask = 0xE0;
for (octets = 1; octets <= 5; ++octets)
{
if ((octet & mask) == mark)
break;
mark = (mark >> 1) | 0x80;
mask = (mask >> 1) | 0x80;
}
return std::make_pair (octets, octet & ~mask);
}
void next ()
{
boost::tie (val, nxt) = decode (nxt, end);
}
Point cur;
Point nxt;
Point end;
UnicodeChar val;
};
#endif

View File

@ -52,6 +52,7 @@ Paul McElroy (Greendogo)
Pieter van der Kloet (pvdk)
Radu-Marius Popovici (rpopovici)
Roman Melnik (Kromgart)
Roman Proskuryakov (humbug)
Sandy Carter (bwrsandman)
Sebastian Wick (swick)
Sergey Shambir

View File

@ -706,9 +706,9 @@ void TiXmlElement::SetDoubleAttribute( const char * name, double val )
{
char buf[256];
#if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%f", val );
TIXML_SNPRINTF( buf, sizeof(buf), "%f", val );
#else
sprintf( buf, "%f", val );
sprintf( buf, "%f", val );
#endif
SetAttribute( name, buf );
}
@ -1266,9 +1266,9 @@ void TiXmlAttribute::SetDoubleValue( double _value )
{
char buf [256];
#if defined(TIXML_SNPRINTF)
TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value);
TIXML_SNPRINTF( buf, sizeof(buf), "%f", _value);
#else
sprintf (buf, "%lf", _value);
sprintf (buf, "%f", _value);
#endif
SetValue (buf);
}

View File

@ -9,8 +9,6 @@ set(SHINY_LIBRARY "shiny")
set(SHINY_OGREPLATFORM_LIBRARY "shiny.OgrePlatform")
# Sources
file(GLOB SOURCE_FILES Main/*.cpp )
set(SOURCE_FILES
Main/Factory.cpp
Main/MaterialInstance.cpp
@ -57,12 +55,20 @@ 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_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE)
set(SHINY_OGREPLATFORM_LIBRARY ${SHINY_OGREPLATFORM_LIBRARY} PARENT_SCOPE)
set(SHINY_LIBRARIES ${SHINY_LIBRARIES} PARENT_SCOPE)

View File

@ -21,7 +21,7 @@
}
\endcode
\note You may also create configurations using sh::Factory::registerConfiguration.
\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

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

950
extern/shiny/Editor/MainWindow.cpp vendored Normal file
View File

@ -0,0 +1,950 @@
#include "MainWindow.hpp"
#include "ui_mainwindow.h"
#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>

View File

@ -51,8 +51,6 @@ namespace sh
{
assert(mCurrentLanguage != Language_None);
bool removeBinaryCache = false;
if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt"))
{
std::ifstream file;
@ -86,8 +84,9 @@ namespace sh
break;
}
PropertySetGet newConfiguration;
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)
@ -137,82 +136,7 @@ namespace sh
}
// load shader sets
{
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));
}
}
bool removeBinaryCache = reloadShaders();
// load materials
{
@ -315,6 +239,8 @@ namespace sh
Factory::~Factory ()
{
mShaderSets.clear();
if (mPlatform->supportsShaderSerialization () && mWriteMicrocodeCache)
{
std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName;
@ -367,15 +293,16 @@ namespace sh
while (i>0)
{
--i;
m->createForConfiguration (configuration, i);
if (mListener)
if (m->createForConfiguration (configuration, i) && mListener)
mListener->materialCreated (m, configuration, i);
else
return NULL;
}
m->createForConfiguration (configuration, lodIndex);
if (mListener)
if (m->createForConfiguration (configuration, lodIndex) && mListener)
mListener->materialCreated (m, configuration, lodIndex);
else
return NULL;
}
return m;
}
@ -439,6 +366,12 @@ namespace sh
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;
}
@ -466,6 +399,14 @@ namespace sh
}
}
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);
@ -493,17 +434,21 @@ namespace sh
return "";
}
PropertySetGet* Factory::getConfiguration (const std::string& name)
Configuration* Factory::getConfiguration (const std::string& name)
{
return &mConfigurations[name];
}
void Factory::registerConfiguration (const std::string& name, PropertySetGet configuration)
void Factory::createConfiguration (const std::string& name)
{
mConfigurations[name] = configuration;
mConfigurations[name].setParent (&mGlobalSettings);
}
void Factory::destroyConfiguration(const std::string &name)
{
mConfigurations.erase(name);
}
void Factory::registerLodConfiguration (int index, PropertySetGet configuration)
{
mLodConfigurations[index] = configuration;
@ -571,17 +516,93 @@ namespace sh
return p;
}
void Factory::saveMaterials (const std::string& filename)
void Factory::saveAll ()
{
std::ofstream file;
file.open (filename.c_str ());
std::map<std::string, std::ofstream*> files;
for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it)
{
it->second.save(file);
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()]);
}
file.close();
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)
@ -630,4 +651,146 @@ namespace sh
}
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;
mShadersLastModified[sourceRelative] = lastModified;
}
}
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;
mShadersLastModified[sourceRelative] = lastModified;
}
mShaderSets.insert(std::make_pair(it->first, newSet));
}
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";
}
}

View File

@ -3,6 +3,7 @@
#include <map>
#include <string>
#include <sstream>
#include "MaterialInstance.hpp"
#include "ShaderSet.hpp"
@ -12,9 +13,21 @@ 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, PropertySetGet> ConfigurationMap;
typedef std::map<std::string, Configuration> ConfigurationMap;
typedef std::map<int, PropertySetGet> LodConfigurationMap;
typedef std::map<std::string, int> LastModifiedMap;
@ -81,8 +94,8 @@ namespace sh
/// Get a MaterialInstance by name
MaterialInstance* getMaterialInstance (const std::string& name);
/// Register a configuration, which can then be used by switching the active material scheme
void registerConfiguration (const std::string& name, PropertySetGet configuration);
/// 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
@ -125,8 +138,48 @@ namespace sh
/// \note The default is off (no cache reading)
void setReadMicrocodeCache(bool read) { mReadMicrocodeCache = read; }
/// Saves all the materials that were initially loaded from the file with this name
void saveMaterials (const std::string& filename);
/// 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.
@ -137,11 +190,13 @@ namespace sh
/// 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);
PropertySetGet* getConfiguration (const std::string& name);
Platform* getPlatform ();
PropertySetGet* getCurrentGlobalSettings();
@ -163,6 +218,8 @@ namespace sh
std::map<TextureUnitState*, std::string> mTextureAliasInstances;
void logError (const std::string& msg);
friend class Platform;
friend class MaterialInstance;
friend class ShaderInstance;
@ -179,6 +236,7 @@ namespace sh
bool mWriteMicrocodeCache;
bool mReadSourceCache;
bool mWriteSourceCache;
std::stringstream mErrorLog;
MaterialMap mMaterials;
ShaderSetMap mShaderSets;

View File

@ -1,6 +1,7 @@
#include "MaterialInstance.hpp"
#include <stdexcept>
#include <iostream>
#include "Factory.hpp"
#include "ShaderSet.hpp"
@ -12,6 +13,7 @@ namespace sh
, mShadersEnabled(true)
, mFactory(f)
, mListener(NULL)
, mFailedToCreate(false)
{
}
@ -46,6 +48,7 @@ namespace sh
return;
mMaterial->removeAll();
mTexUnits.clear();
mFailedToCreate = false;
}
void MaterialInstance::setProperty (const std::string& name, PropertyValuePtr value)
@ -54,118 +57,136 @@ namespace sh
destroyAll(); // trigger updates
}
void MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex)
bool MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex)
{
bool res = mMaterial->createConfiguration(configuration, lodIndex);
if (!res)
return; // listener was false positive
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);
if (mListener)
mListener->requestedConfiguration (this, configuration);
mFactory->setActiveConfiguration (configuration);
mFactory->setActiveLodLevel (lodIndex);
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 = getPasses();
if (passes.size() == 0)
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");
bool hasFragment = it->hasProperty("fragment_program");
if (useShaders)
bool allowFixedFunction = true;
if (!mShadersEnabled && hasProperty("allow_fixed_function"))
{
it->setContext(context);
it->mShaderProperties.setContext(context);
if (hasVertex)
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)
{
ShaderSet* vertex = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get());
ShaderInstance* v = vertex->getInstance(&it->mShaderProperties);
if (v)
it->setContext(context);
it->mShaderProperties.setContext(context);
if (hasVertex)
{
pass->assignProgram (GPT_Vertex, v->getName());
v->setUniformParameters (pass, &it->mShaderProperties);
std::vector<std::string> sharedParams = v->getSharedParameters ();
for (std::vector<std::string>::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it)
ShaderSet* vertex = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("vertex_program"), context).get());
ShaderInstance* v = vertex->getInstance(&it->mShaderProperties);
if (v)
{
pass->addSharedParameter (GPT_Vertex, *it);
}
pass->assignProgram (GPT_Vertex, v->getName());
v->setUniformParameters (pass, &it->mShaderProperties);
std::vector<std::string> vector = v->getUsedSamplers ();
usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end());
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());
}
}
}
if (hasFragment)
// create texture units
std::vector<MaterialInstanceTextureUnit>* texUnits = &it->mTexUnits;
int i=0;
for (std::vector<MaterialInstanceTextureUnit>::iterator texIt = texUnits->begin(); texIt != texUnits->end(); ++texIt )
{
ShaderSet* fragment = mFactory->getShaderSet(retrieveValue<StringValue>(it->getProperty("fragment_program"), context).get());
ShaderInstance* f = fragment->getInstance(&it->mShaderProperties);
if (f)
// 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()))
{
pass->assignProgram (GPT_Fragment, f->getName());
f->setUniformParameters (pass, &it->mShaderProperties);
boost::shared_ptr<TextureUnitState> texUnit = pass->createTextureUnitState (texIt->getName());
texIt->copyAll (texUnit.get(), context);
std::vector<std::string> sharedParams = f->getSharedParameters ();
for (std::vector<std::string>::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it)
mTexUnits.push_back(texUnit);
// set texture unit indices (required by GLSL)
if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL)
{
pass->addSharedParameter (GPT_Fragment, *it);
}
pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i);
std::vector<std::string> vector = f->getUsedSamplers ();
usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end());
++i;
}
}
}
}
// create texture units
std::vector<MaterialInstanceTextureUnit> texUnits = it->getTexUnits();
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->copyAll (texUnit.get(), context);
if (mListener)
mListener->createdConfiguration (this, configuration);
return true;
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;
}
}
}
} 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;
}
if (mListener)
mListener->createdConfiguration (this, configuration);
}
Material* MaterialInstance::getMaterial ()
@ -180,12 +201,23 @@ namespace sh
return &mPasses.back();
}
PassVector MaterialInstance::getPasses()
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)->getPasses();
return static_cast<MaterialInstance*>(mParent)->getParentPasses();
else
return mPasses;
return &mPasses;
}
PassVector* MaterialInstance::getPasses()
{
return &mPasses;
}
void MaterialInstance::setShadersEnabled (bool enabled)
@ -206,7 +238,7 @@ namespace sh
if (mParent)
{
stream << "\t" << static_cast<MaterialInstance*>(mParent)->getName() << "\n";
stream << "\t" << "parent " << static_cast<MaterialInstance*>(mParent)->getName() << "\n";
}
const PropertyMap& properties = listProperties ();
@ -215,6 +247,14 @@ namespace sh
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";
}
}

View File

@ -41,8 +41,12 @@ namespace sh
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 ();
PassVector getPasses(); ///< gets the passes of the top-most parent
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!
@ -55,25 +59,25 @@ namespace sh
virtual void setProperty (const std::string& name, PropertyValuePtr value);
private:
void setParentInstance (const std::string& name);
std::string getParentInstance ();
void create (Platform* platform);
void createForConfiguration (const std::string& configuration, unsigned short lodIndex);
void destroyAll ();
void setShadersEnabled (bool enabled);
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);
///< this will only save the properties, not the passes and texture units, and as such
/// is only intended to be used for derived materials
bool mFailedToCreate;
friend class Factory;

View File

@ -1,5 +1,7 @@
#include "MaterialInstancePass.hpp"
#include <fstream>
namespace sh
{
@ -9,8 +11,25 @@ namespace sh
return &mTexUnits.back();
}
std::vector <MaterialInstanceTextureUnit> MaterialInstancePass::getTexUnits ()
void MaterialInstancePass::save(std::ofstream &stream)
{
return mTexUnits;
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

@ -18,10 +18,10 @@ namespace sh
public:
MaterialInstanceTextureUnit* createTextureUnit (const std::string& name);
void save (std::ofstream& stream);
PropertySetGet mShaderProperties;
std::vector <MaterialInstanceTextureUnit> getTexUnits ();
private:
std::vector <MaterialInstanceTextureUnit> mTexUnits;
};
}

View File

@ -18,6 +18,7 @@ namespace sh
public:
MaterialInstanceTextureUnit (const std::string& name);
std::string getName() const;
void setName (const std::string& name) { mName = name; }
private:
std::string mName;
};

View File

@ -9,7 +9,7 @@ namespace sh
Platform::Platform (const std::string& basePath)
: mBasePath(basePath)
, mCacheFolder("./")
, mShaderCachingEnabled(false)
, mFactory(NULL)
{
}
@ -57,11 +57,6 @@ namespace sh
mCacheFolder = folder;
}
void Platform::setShaderCachingEnabled (bool enabled)
{
mShaderCachingEnabled = enabled;
}
std::string Platform::getCacheFolder() const
{
return mCacheFolder;

View File

@ -24,6 +24,7 @@ namespace sh
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
@ -35,8 +36,7 @@ namespace sh
class TextureUnitState : public PropertySet
{
public:
virtual ~TextureUnitState();
virtual ~TextureUnitState();
virtual void setTextureName (const std::string& textureName) = 0;
protected:
@ -46,7 +46,7 @@ namespace sh
class Pass : public PropertySet
{
public:
virtual boost::shared_ptr<TextureUnitState> createTextureUnitState () = 0;
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
@ -68,6 +68,9 @@ namespace sh
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;
@ -79,8 +82,6 @@ namespace sh
Platform (const std::string& basePath);
virtual ~Platform ();
void setShaderCachingEnabled (bool enabled);
/// set the folder to use for shader caching
void setCacheFolder (const std::string& folder);
@ -93,6 +94,8 @@ namespace sh
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;
@ -105,6 +108,7 @@ namespace sh
friend class Factory;
friend class MaterialInstance;
friend class ShaderInstance;
friend class ShaderSet;
protected:
/**
@ -131,9 +135,6 @@ namespace sh
std::string mCacheFolder;
Factory* mFactory;
protected:
bool mShaderCachingEnabled;
private:
void setFactory (Factory* factory);

View File

@ -6,6 +6,8 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <fstream>
namespace sh
{
@ -39,8 +41,9 @@ namespace sh
mValue = false;
else
{
std::cerr << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue" << std::endl;
mValue = false;
std::stringstream msg;
msg << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue";
throw std::runtime_error(msg.str());
}
}
@ -183,12 +186,16 @@ namespace sh
void PropertySet::setProperty (const std::string& name, PropertyValuePtr &value, PropertySetGet* context)
{
if (!setPropertyOverride (name, value, context))
std::cerr << "sh::PropertySet: Warning: No match for property with name '" << name << "'" << std::endl;
{
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 was able to make use of the property
// if we got here, none of the sub-classes were able to make use of the property
return false;
}
@ -226,6 +233,11 @@ namespace sh
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());
@ -241,7 +253,7 @@ namespace sh
return mProperties[name];
}
bool PropertySetGet::hasProperty (const std::string& name)
bool PropertySetGet::hasProperty (const std::string& name) const
{
bool found = (mProperties.find(name) != mProperties.end());
@ -256,13 +268,35 @@ namespace sh
return true;
}
void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context)
void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context, bool copyParent)
{
if (mParent)
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';
}
}
}

View File

@ -133,6 +133,7 @@ namespace sh
class PropertySet
{
public:
virtual ~PropertySet() {}
void setProperty (const std::string& name, PropertyValuePtr& value, PropertySetGet* context);
protected:
@ -151,18 +152,26 @@ namespace sh
virtual ~PropertySetGet() {}
void copyAll (PropertySet* target, PropertySetGet* context); ///< call setProperty for each property/value pair stored in \a this
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);
bool hasProperty (const std::string& name) const;
private:
PropertyMap mProperties;
@ -225,7 +234,7 @@ namespace sh
template <typename T>
/// Create a property of any type
/// Example: sh::makeProperty\<sh::Vector4\> (new sh::Vector4(1, 1, 1, 1))
/// Example: sh::makeProperty (new sh::Vector4(1, 1, 1, 1))
inline PropertyValuePtr makeProperty (T* p)
{
return PropertyValuePtr ( static_cast<PropertyValue*>(p) );

View File

@ -24,6 +24,10 @@ namespace sh
}
ScriptLoader::ScriptLoader(const std::string& fileEnding)
: mLoadOrder(0)
, mToken(TOKEN_NewLine)
, mLastToken(TOKEN_NewLine)
{
mFileEnding = fileEnding;
}
@ -36,7 +40,7 @@ namespace sh
void ScriptLoader::clearScriptList()
{
std::map <std::string, ScriptNode *>::iterator i;
for (i = m_scriptList.begin(); i != m_scriptList.end(); i++)
for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i)
{
delete i->second;
}
@ -293,7 +297,7 @@ namespace sh
{
//Delete all children
std::vector<ScriptNode*>::iterator i;
for (i = mChildren.begin(); i != mChildren.end(); i++)
for (i = mChildren.begin(); i != mChildren.end(); ++i)
{
ScriptNode *node = *i;
node->mRemoveSelf = false;
@ -323,15 +327,24 @@ namespace sh
ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive)
{
int indx, prevC, nextC;
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).
prevC = mLastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1;
nextC = mLastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1;
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];

View File

@ -37,6 +37,14 @@ namespace sh
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;

View File

@ -21,6 +21,7 @@ namespace sh
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

View File

@ -15,6 +15,7 @@ namespace sh
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();
@ -22,9 +23,22 @@ namespace sh
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()
{
Ogre::MaterialManager::getSingleton().remove(mMaterial->getName());
if (!mMaterial.isNull())
Ogre::MaterialManager::getSingleton().remove(mMaterial->getName());
}
boost::shared_ptr<Pass> OgreMaterial::createPass (const std::string& configuration, unsigned short lodIndex)
@ -34,6 +48,8 @@ namespace sh
void OgreMaterial::removeAll ()
{
if (mMaterial.isNull())
return;
mMaterial->removeAllTechniques();
mMaterial->createTechnique()->setSchemeName (sDefaultTechniqueName);
mMaterial->compile();

View File

@ -18,6 +18,9 @@ namespace sh
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();
@ -30,6 +33,7 @@ namespace sh
private:
Ogre::MaterialPtr mMaterial;
std::string mName;
std::string mShadowCasterMaterial;
};

View File

@ -20,9 +20,9 @@ namespace sh
mPass = t->createPass();
}
boost::shared_ptr<TextureUnitState> OgrePass::createTextureUnitState ()
boost::shared_ptr<TextureUnitState> OgrePass::createTextureUnitState (const std::string& name)
{
return boost::shared_ptr<TextureUnitState> (new OgreTextureUnitState (this));
return boost::shared_ptr<TextureUnitState> (new OgreTextureUnitState (this, name));
}
void OgrePass::assignProgram (GpuProgramType type, const std::string& name)
@ -105,7 +105,17 @@ namespace sh
else if (type == GPT_Fragment)
params = mPass->getFragmentProgramParameters();
params->addSharedParameters (name);
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)

View File

@ -14,7 +14,7 @@ namespace sh
public:
OgrePass (OgreMaterial* parent, const std::string& configuration, unsigned short lodIndex);
virtual boost::shared_ptr<TextureUnitState> createTextureUnitState ();
virtual boost::shared_ptr<TextureUnitState> createTextureUnitState (const std::string& name);
virtual void assignProgram (GpuProgramType type, const std::string& name);
Ogre::Pass* getOgrePass();

View File

@ -4,6 +4,7 @@
#include <OgreDataStream.h>
#include <OgreGpuProgramManager.h>
#include <OgreHighLevelGpuProgramManager.h>
#include <OgreRoot.h>
#include "OgreMaterial.hpp"
@ -76,6 +77,11 @@ namespace sh
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,
@ -122,6 +128,7 @@ namespace sh
if (mSharedParameters.find(name) == mSharedParameters.end())
{
params = Ogre::GpuProgramManager::getSingleton().createSharedParameters(name);
Ogre::GpuConstantType type;
if (typeid(*value) == typeid(Vector4))
type = Ogre::GCT_FLOAT4;

View File

@ -47,6 +47,8 @@ namespace sh
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;

View File

@ -6,10 +6,11 @@
namespace sh
{
OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent)
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)

View File

@ -12,7 +12,7 @@ namespace sh
class OgreTextureUnitState : public TextureUnitState
{
public:
OgreTextureUnitState (OgrePass* parent);
OgreTextureUnitState (OgrePass* parent, const std::string& name);
virtual void setTextureName (const std::string& textureName);

View File

@ -415,7 +415,7 @@
float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel);
#endif
#if SHADOWS
#if SHADOWS || SHADOWS_PSSM
shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0));
#else
shOutputColour(0) *= lightResult;

View File

@ -5,14 +5,13 @@
<Widget type="Widget" skin="MW_Box" position="8 8 415 381" align="Stretch" name = "Client"/>
<!-- The Dialogue history -->
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="13 13 405 371" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<!-- box for receiving mouse events -->
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH"/>
<Widget type="Widget" position="13 13 391 371" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
<Widget type="BookPage" skin="MW_BookPage" position="0 0 391 371" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
</Widget>
</Widget>
<Widget type="ScrollBar" skin="MW_VScroll" position="404 13 14 371" align="ALIGN_RIGHT ALIGN_VSTRETCH" name="VScroll">
<Property key="Visible" value="false"/>
</Widget>
<!-- The disposition bar-->

View File

@ -23,6 +23,28 @@
</Codes>
</Resource>
<Resource type="ResourceTrueTypeFont" name="EB Garamond 24">
<Property key="Source" value="EBGaramond-Regular.ttf"/>
<Property key="Size" value="24"/>
<Property key="Resolution" value="72"/>
<Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="33 126"/>
<Code range="160"/> <!-- Non-breaking space -->
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/>
<Code range="2026"/> <!-- Ellipsis -->
<Code range="8470"/>
<Code range="8211"/> <!-- Minus -->
<Code range="8216 8217"/> <!-- Single quotes -->
<Code range="8220 8221"/> <!-- Right and Left Double Quotation mark -->
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Codes>
</Resource>
<Resource type="ResourceTrueTypeFont" name="Daedric">
<!--<Property key="Source" value="Oblivion/Oblivion Worn.ttf"/>-->
<Property key="Source" value="Oblivion/Oblivion.ttf"/>

View File

@ -2,24 +2,111 @@
<MyGUI type="Layout">
<Widget type="Window" skin="" layer="Windows" align="Left|Top" position="00 200 512 256" name="_Main">
<Widget type="Window" skin="" layer="Windows" align="Left|Top" position="0 0 565 390" name="_Main">
<!-- pages -->
<Widget type="ImageBox" skin="ImageBox" position="0 0 565 390" align="Top|Right" name="JImage">
<Property key="ImageTexture" value="textures\tx_menubook.dds"/>
<Property key="ImageCoord" value="50 0 412 256"/>
<Widget type="ImageBox" skin="ImageBox" position_real="0 0 1 1" align="Top|Right" name="JImage">
<Property key="ImageTexture" value="textures\tx_menubook.dds"/>
<!-- buttons -->
<Widget type="ImageButton" skin="ImageBox" position="40 350 64 32" name="OptionsBTN">
<Property key="ImageHighlighted" value="textures\tx_menubook_options_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_options_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_options_pressed.dds"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="150 350 32 16" name="PageOneNum">
<Property key="TextColour" value="0 0 0"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="220 350 48 32" name="PrevPageBTN">
<Property key="ImageCoord" value="0 0 48 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_prev_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_prev_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_prev_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="300 350 48 32" name="NextPageBTN">
<Property key="ImageCoord" value="0 0 48 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_next_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_next_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_next_pressed.dds"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="410 350 32 16" name="PageTwoNum">
<Property key="TextColour" value="0 0 0"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="475 350 48 32" name="CloseBTN">
<Property key="ImageCoord" value="0 0 48 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_close_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_close_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_close_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="460 350 64 32" name="JournalBTN">
<Property key="ImageCoord" value="0 0 64 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_journal_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_journal_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_journal_pressed.dds"/>
</Widget>
<!-- text pages -->
<Widget type="BookPage" skin="MW_BookPage" position="30 22 240 300" name="LeftBookPage"/>
<Widget type="BookPage" skin="MW_BookPage" position="300 22 240 300" name="RightBookPage"/>
<!-- options overlay -->
<Widget type="ImageButton" skin="ImageBox" position="370 220 128 32" name="NextPageBTN">
<Property key="ImageHighlighted" value="textures\tx_menubook_next_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_next_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_next_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="80 220 128 32" name="PrevPageBTN">
<Property key="ImageHighlighted" value="textures\tx_menubook_prev_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_prev_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_prev_pressed.dds"/>
</Widget>
<Widget type="EditBox" skin="MW_BookPage" position_real="0.15 0.1 0.3 0.75" name = "LeftText"/>
<Widget type="EditBox" skin="MW_BookPage" position_real="0.55 0.1 0.3 0.75" name = "RightText"/>
</Widget>
</Widget>
<!-- "options" -->
<Widget type="ImageBox" skin="ImageBox" position="300 0 224 350" name="OptionsOverlay">
<Property key="ImageTexture" value="textures\tx_menubook_bookmark.dds"/>
<Property key="ImageCoord" value="0 0 164 256"/>
<Widget type="BookPage" skin="MW_BookPage" position="20 15 92 250" name="LeftTopicIndex"/>
<Widget type="BookPage" skin="MW_BookPage" position="112 15 92 250" name="RightTopicIndex"/>
<Widget type="ScrollView" skin="MW_ScrollView" position="20 15 184 245" name="TopicsList" align="Right VStretch">
<Property key="CanvasAlign" value="Left Top"/>
<Widget type="BookPage" skin="MW_BookPage" position="0 0 30000 30000" name="TopicsPage"/>
</Widget>
<Widget type="ScrollView" skin="MW_ScrollView" position="20 35 184 225" name="QuestsList" align="Right VStretch">
<Property key="CanvasAlign" value="Left Top"/>
<Widget type="BookPage" skin="MW_BookPage" position="0 0 30000 30000" name="QuestsPage"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="62 15 100 20" Align="Top|Left" name="ShowActiveBTN">
<Property key="ImageCoord" value="0 0 100 20"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_quests_active_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_quests_active_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_quests_active_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="76 15 72 20" Align="Top|Left" name="ShowAllBTN">
<Property key="ImageCoord" value="0 0 72 20"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_quests_all_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_quests_all_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_quests_all_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="40 265 56 32" Align="Top|Left" name="TopicsBTN">
<Property key="ImageCoord" value="0 0 56 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_topics_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_topics_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_topics_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="130 265 56 32" Align="Top|Left" name="QuestsBTN">
<Property key="ImageCoord" value="0 0 56 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_quests_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_quests_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_quests_pressed.dds"/>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="85 290 56 32" Align="Top|Left" name="CancelBTN">
<Property key="ImageCoord" value="0 0 56 32"/>
<Property key="ImageHighlighted" value="textures\tx_menubook_cancel_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_cancel_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_cancel_pressed.dds"/>
</Widget>
</Widget>
</Widget>
</MyGUI>

View File

@ -10,8 +10,6 @@
</Skin>
<Skin name="MW_BookPage" size="0 0 50 50">
<Property key="WordWrap" value = "true" />
<Child type="TextBox" skin="MW_BookClient" offset="0 0 35 10" align = "ALIGN_STRETCH" name = "Client"/>
<!--Child type="ScrollBar" skin="VScroll" offset = "35 0 15 50" align = "Right VStretch" name = "VScroll"/-->
<BasisSkin type="PageDisplay"/>
</Skin>
</MyGUI>

View File

@ -16,7 +16,7 @@
<Property key="Caption" value="#{sTransparency_Menu}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 28 352 18" align="Left Top" name="MenuTransparencySlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 52 352 18" align="Left Top">
<Property key="Caption" value="#{sFull}"/>
@ -31,7 +31,7 @@
<Property key="Caption" value="#{sMenu_Help_Delay}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 102 352 18" align="Left Top" name="ToolTipDelaySlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 126 352 18" align="Left Top">
<Property key="Caption" value="#{sFast}"/>
@ -65,35 +65,35 @@
<Property key="Caption" value="#{sMaster}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 28 352 18" align="Left Top" name="MasterVolume">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="4 54 352 18" align="Left Top">
<Property key="Caption" value="#{sVoice}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 78 352 18" align="Left Top" name="VoiceVolume">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="4 104 352 18" align="Left Top">
<Property key="Caption" value="#{sEffects}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 128 352 18" align="Left Top" name="EffectsVolume">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="4 154 352 18" align="Left Top">
<Property key="Caption" value="#{sFootsteps}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 178 352 18" align="Left Top" name="FootstepsVolume">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="NormalText" position="4 204 352 18" align="Left Top">
<Property key="Caption" value="#{sMusic}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 228 352 18" align="Left Top" name="MusicVolume">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
</Widget>
<Widget type="TabItem" skin="" position="4 28 360 312">
@ -118,7 +118,7 @@
<Property key="Caption" value="UI cursor sensitivity"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 252 160 18" align="Left Top" name="UISensitivitySlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 276 160 18" align="Left Top">
<Property key="Caption" value="#{sLow}"/>
@ -134,7 +134,7 @@
<Property key="Caption" value="Camera sensitivity"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="180 252 160 18" align="Left Top" name="CameraSensitivitySlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="180 276 160 18" align="Left Top">
<Property key="Caption" value="#{sLow}"/>
@ -199,7 +199,7 @@
<Property key="Caption" value="Field of View"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 222 329 18" align="Left Top" name="FOVSlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 246 329 18" align="Left Top">
<Property key="Caption" value="#{sLow}"/>
@ -224,7 +224,7 @@
<Property key="Caption" value="Anisotropy"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 28 150 18" align="Left Top" name="AnisotropySlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
</Widget>
@ -232,7 +232,7 @@
<Property key="Caption" value="#{sRender_Distance}"/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="4 154 322 18" align="Left Top" name="ViewDistanceSlider">
<Property key="Range" value="1000000"/>
<Property key="Range" value="100"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 178 332 18" align="Left Top">
<Property key="Caption" value="#{sNear}"/>