remove ssynth source and download it using cmake

This commit is contained in:
alemuntoni 2022-11-04 09:36:10 +01:00
parent 523fae4fb1
commit 65012cd020
96 changed files with 14 additions and 9771 deletions

View File

@ -2,110 +2,20 @@
# Copyright 2019, 2020, Visual Computing Lab, ISTI - Italian National Research Council
# SPDX-License-Identifier: BSL-1.0
option(ALLOW_BUNDLED_SSYNTH "Allow use of bundled structure-synth source" ON)
set(SSYNTH_DIR ${CMAKE_CURRENT_LIST_DIR}/structuresynth-1.5)
option(MESHLAB_ALLOW_DOWNLOAD_SOURCE_STRUCTURE_SYNTH "Allow use of bundled structure-synth source" ON)
if(ALLOW_BUNDLED_SSYNTH AND EXISTS "${SSYNTH_DIR}/ssynth/StructureSynth/Model/Action.h")
message(STATUS "- structure-synth - using bundled source")
# Can't use a system version because using StructureSynth as a library is not common
add_library(
external-ssynth STATIC
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Action.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/AmbiguousRule.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Builder.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/ColorPool.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/CustomRule.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/ExecutionStack.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/PrimitiveClass.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/PrimitiveRule.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RandomStreams.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rule.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RuleRef.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RuleSet.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/State.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Transformation.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/TransformationLoop.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/EisenParser.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/Preprocessor.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/Tokenizer.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Exceptions/Exception.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Box.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Dot.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/EngineWidget.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Grid.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Line.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Mesh.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Object3D.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Sphere.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Triangle.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Logging/ListWidgetLogger.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Logging/Logging.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Matrix4.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Random.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Vector3.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/ColorUtils.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/MiniParser.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/Persistence.h"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/Version.h"
"${SSYNTH_DIR}/ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/Renderer.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Action.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/AmbiguousRule.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Builder.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/ColorPool.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/CustomRule.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/ExecutionStack.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/PrimitiveClass.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/PrimitiveRule.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RandomStreams.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rule.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RuleRef.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/RuleSet.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/State.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Transformation.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/TransformationLoop.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/EisenParser.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/Preprocessor.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Parser/Tokenizer.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Box.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Dot.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Grid.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Line.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Mesh.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Object3D.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Triangle.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Logging/Logging.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Matrix4.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Random.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Math/Vector3.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/ColorUtils.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/MiniParser.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/Persistence.cpp"
"${SSYNTH_DIR}/ssynth/SyntopiaCore/Misc/Version.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/Renderer.cpp"
"${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp")
if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_STRUCTURE_SYNTH)
set(SSYNTH_VER 1.5.1)
set(SSYNTH_DIR ${MESHLAB_EXTERNAL_DOWNLOAD_DIR}/StructureSynth-${SSYNTH_VER})
# These sources were disabled in the .pro file: "${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp"
# "${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp"
# "${SSYNTH_DIR}/ssynth/SyntopiaCore/GLEngine/Sphere.cpp"
# "${SSYNTH_DIR}/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp"
target_include_directories(external-ssynth SYSTEM PUBLIC "${SSYNTH_DIR}/ssynth")
target_link_libraries(external-ssynth PRIVATE Qt5::Core Qt5::Xml Qt5::OpenGL OpenGL::GLU)
set_property(TARGET external-ssynth PROPERTY FOLDER External)
set_property(TARGET external-ssynth PROPERTY AUTOMOC TRUE)
target_link_libraries(external-ssynth PRIVATE external-disable-warnings)
if (NOT EXISTS "${SSYNTH_DIR}/StructureSynth/Model/Action.h")
set(SSYNTH_LINK "https://github.com/alemuntoni/StructureSynth/archive/refs/tags/${SSYNTH_VER}.zip")
download_and_unzip(${SSYNTH_LINK} ${MESHLAB_EXTERNAL_DOWNLOAD_DIR} "Structure Synth")
endif()
message(STATUS "- structure-synth - using downloaded source")
add_subdirectory(${SSYNTH_DIR})
add_library(external-ssynth INTERFACE)
target_link_libraries(external-ssynth INTERFACE structure-synth)
endif()

View File

@ -1,104 +0,0 @@
#include "Action.h"
#include "ExecutionStack.h"
#include "Builder.h"
#include "../../SyntopiaCore/Logging/Logging.h"
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Model {
void Action::apply(Builder* b, const Rule* callingRule, int ruleDepth) const {
bool rememberPreviousMatrix = true; // at some point we might make this optional -> only needed for grid meshes...
if (set != 0) {
b->setCommand(set->key, set->value);
return;
}
State s = b->getState();
QList<int> counters;
for (int i = 0; i < loops.size(); i++) counters.append(1);
if (counters.size() == 0) {
if (callingRule) {
s.maxDepths[callingRule] = ruleDepth;
}
b->getNextStack().append(RuleState(rule->rule(), s));
return;
}
bool done = false;
while (!done) {
// create state
State s0 = s;
if (rememberPreviousMatrix) {
// Copy the old matrix...
s0.setPreviousState(s.matrix, s.hsv, s.alpha);
}
for (int i = 0; i < counters.size(); i++) {
for (int j = 0; j < counters[i]; j++) {
s0 = loops[i].transformation.apply(s0, b->getColorPool());
}
}
if (callingRule) {
s0.maxDepths[callingRule] = ruleDepth;
}
b->getNextStack().append(RuleState(rule->rule(), s0));
// increase lowest counter...
counters[0]++;
for (int i = 0; i < counters.size(); i++) {
if (counters[i] > loops[i].repetitions) {
if (i == counters.size()-1) {
done = true;
} else {
counters[i] = 1;
counters[i+1]++;
}
}
}
}
}
Action::Action(QString key, QString value) {
set = new SetAction();
set->key = key;
set->value = value;
rule = 0;
}
Action::~Action() {
// TODO: Handle leaks (Actions are treated as value types, and hence rule,set ptrs are duped)
//delete(rule);
//delete(set);
}
void Action::addTransformationLoop(TransformationLoop tl) {
loops.append(tl);
}
void Action::setRule(QString ruleName) {
rule = new RuleRef(ruleName);
set = 0;
}
Action::Action(Transformation t, QString ruleName) {
loops.append(TransformationLoop(1, t));
rule = new RuleRef(ruleName);
set = 0;
}
Action::Action(QString ruleName) {
rule = new RuleRef(ruleName);
set = 0;
}
}
}

View File

@ -1,44 +0,0 @@
#pragma once
#include "RuleRef.h"
#include "TransformationLoop.h"
namespace StructureSynth {
namespace Model {
struct SetAction {
QString key;
QString value;
};
/// An actions is a number of loops, that is applied to a rule.
///
/// Rules with only one transformation, e.g.:
/// { x 1 } R1
/// are represented as a loop transformation with one repetitions.
class Action {
public:
Action(Transformation t, QString ruleName);
Action(QString ruleName);
Action(QString key, QString value);
Action() { rule = 0; set = 0; }
void addTransformationLoop(TransformationLoop tl);
void setRule(QString rule);
~Action();
/// If 'callingRule' != 0 the new states generated will be set with
/// a depth equal to 'ruleDepth'
void apply(Builder* b, const Rule* callingRule, int ruleDepth) const;
RuleRef* getRuleRef() const { return rule; }
private:
QList<TransformationLoop> loops;
RuleRef* rule; // The rule that will be called after all transformations.
SetAction* set;
};
}
}

View File

@ -1,52 +0,0 @@
#include "AmbiguousRule.h"
#include "Builder.h"
#include "RandomStreams.h"
#include "../../SyntopiaCore/Logging/Logging.h"
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Model {
QList<RuleRef*> AmbiguousRule::getRuleRefs() const {
QList<RuleRef*> list;
for (int i = 0; i < rules.size(); i++) {
for (int j = 0; j < rules[i]->getRuleRefs().size(); j++) {
list.append(rules[i]->getRuleRefs()[j]);
}
}
return list;
}
void AmbiguousRule::apply(Builder* builder) const {
// Calc sum of weigths
double totalWeight = 0;
for (int i = 0; i < rules.size(); i++) {
totalWeight += rules[i]->getWeight();
}
double random = totalWeight*RandomStreams::Geometry()->getDouble();
// Choose a random rule according to weights
double accWeight = 0;
for (int i = 0; i < rules.size(); i++) {
accWeight += rules[i]->getWeight();
if (random <= accWeight) {
rules[i]->apply(builder);
return;
}
}
rules[rules.size()-1]->apply(builder);
WARNING("Assertion failed: in AmbiguousRule::apply");
};
}
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "Rule.h"
#include "CustomRule.h"
namespace StructureSynth {
namespace Model {
/// If several definitions for the same rule exists,
/// an Ambiguous Rule is created which contains the multiple definitions.
///
/// When the rule is executed, a random rule is chosen from the multiple definitions,
/// taking their weights into account.
class AmbiguousRule : public Rule {
public:
AmbiguousRule(QString name) : Rule(name) {};
virtual void apply(Builder* builder) const;
/// Returns a list over rules that this rule references.
virtual QList<RuleRef*> getRuleRefs() const;
QList<CustomRule*> getRules() { return rules; };
void appendRule(CustomRule* r) { rules.append(r); }
virtual void setMaxDepth(int maxDepth) { for (int i = 0; i < rules.size(); i++) rules[i]->setMaxDepth(maxDepth); }
private:
QList<CustomRule*> rules;
};
}
}

View File

@ -1,400 +0,0 @@
#include "Builder.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Misc/ColorUtils.h"
#include "../../SyntopiaCore/Misc/MiniParser.h"
#include "../../SyntopiaCore/Math/Vector3.h"
#include "RandomStreams.h"
#include <QProgressDialog>
#include <QLinkedList>
#include <QApplication>
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Misc;
namespace StructureSynth {
namespace Model {
Builder::Builder(Rendering::Renderer* renderTarget, RuleSet* ruleSet, bool verbose) : renderTarget(renderTarget), ruleSet(ruleSet), verbose(verbose) {
maxGenerations = 1000;
maxObjects = 100000;
objects = 0;
minDim = 0;
maxDim = 0;
newSeed = 0;
hasSeedChanged = false;
syncRandom = false;
initialSeed = 0;
colorPool = new ColorPool("RandomHue");
userCancelled = false;
};
void Builder::recurseDepthFirst(QProgressDialog& progressDialog, int& maxTerminated, int& minTerminated, int& generationCounter) {
int lastValue = 0;
if (maxGenerations > 0) {
ruleSet->setRulesMaxDepth(maxGenerations);
}
QLinkedList<RuleState> ruleStates;
ruleStates.append(stack[0]);
while (ruleStates.size() != 0 && objects < maxObjects) {
/*
QStringList l;
foreach (RuleState r, ruleStates) {
QString name = r.rule->getName();
int d = r.state.maxDepths[r.rule];
l.append(QString("%1(%2)").arg(name).arg(d));
}
INFO(l.join(" "));
*/
double p = 0;
if (maxObjects>0) { p = objects/(double)maxObjects; }
double progress = p;
if (maxObjects<=0) { progress = (generationCounter%9)/9.0; }
if (lastValue != (int)(progress*100.0)) {
progressDialog.setValue((int)(progress*100.0));
progressDialog.setLabelText(
QString("Building objects...\r\n\r\nGeneration: %1\r\nObjects: %2\r\nPending rules: %3")
.arg(generationCounter).arg(objects).arg(stack.size()));
//qApp->processEvents();
if (progressDialog.wasCanceled()) { userCancelled = true; break; }
}
lastValue = (int)(progress*100.0);
generationCounter++; // Notice this does not make sense for depth first search.
// Now iterate though all RuleState's on stack and create next generation.
nextStack.clear();
currentState = &ruleStates.first().state;
if (currentState->seed != 0) {
RandomStreams::SetSeed(currentState->seed);
currentState->seed = RandomStreams::Geometry()->getInt();
}
state = ruleStates.first().state;
// Check the dimensions against the min and max limits.
if (maxDim != 0 || minDim != 0) {
Vector3f s = state.matrix * Vector3f(1,1,1) - state.matrix * Vector3f(0,0,0);
double l = s.length();
if (maxDim && l > maxDim) { maxTerminated++; continue; }
if (minDim && l < minDim) { minTerminated++; continue; }
}
ruleStates.first().rule->apply(this);
ruleStates.removeFirst();
QLinkedList<RuleState>::iterator it = ruleStates.begin();
foreach (RuleState r, nextStack) {
ruleStates.insert(it, r);
};
}
}
void Builder::recurseBreadthFirst(QProgressDialog& progressDialog, int& maxTerminated, int& minTerminated, int& generationCounter) {
int syncSeed = 0;
if (syncRandom) {
syncSeed = RandomStreams::Geometry()->getInt();
}
int lastValue = 0;
while (stack.size() != 0 && generationCounter < maxGenerations && objects < maxObjects && stack.size() < maxObjects) {
syncSeed = RandomStreams::Geometry()->getInt();
double p = 0;
if (maxObjects>0) { p = objects/(double)maxObjects; }
double p2 = 0;
if (maxGenerations>0) { p2 = generationCounter/(double)maxGenerations; }
double progress = p;
if (p2 > p) progress = p2;
if (maxObjects<=0 && maxGenerations<=0) { progress = (generationCounter%9)/9.0; }
if (lastValue != (int)(progress*100.0)) {
progressDialog.setValue((int)(progress*100.0));
progressDialog.setLabelText(
QString("Building objects...\r\n\r\nGeneration: %1\r\nObjects: %2\r\nPending rules: %3")
.arg(generationCounter).arg(objects).arg(stack.size()));
//qApp->processEvents();
}
lastValue = (int)(progress*100.0);
if (progressDialog.wasCanceled()) {
userCancelled = true;
break;
}
generationCounter++;
// Now iterate though all RuleState's on stack and create next generation.
//INFO(QString("Executing generation %1 with %2 individuals").arg(generationCounter).arg(stack.size()));
nextStack.clear();
for (int i = 0; i < stack.size(); i++) {
// INFO("Executing: " + stack[i].rule->getName());
currentState = &stack[i].state;
if (currentState->seed != 0) {
RandomStreams::SetSeed(currentState->seed);
currentState->seed = RandomStreams::Geometry()->getInt();
}
state = stack[i].state;
// if we are synchronizing random numbers every state must get the same rands
if (syncRandom) { RandomStreams::SetSeed(syncSeed); }
// Check the dimensions against the min and max limits.
if (maxDim != 0 || minDim != 0) {
Vector3f s = state.matrix * Vector3f(1,1,1) - state.matrix * Vector3f(0,0,0);
double l = s.length();
if (maxDim && l > maxDim) { maxTerminated++; continue; }
if (minDim && l < minDim) { minTerminated++; continue; }
}
stack[i].rule->apply(this);
}
stack = nextStack;
}
}
void Builder::build() {
objects = 0;
if (verbose) INFO("Starting builder...");
/// Push first generation state
stack.append(RuleState(ruleSet->getStartRule(), State()));
int generationCounter = 0;
QProgressDialog progressDialog("Building objects...", "Cancel", 0, 100, 0);
progressDialog.setWindowModality(Qt::WindowModal);
if (verbose) {
progressDialog.setMinimumDuration(0);
progressDialog.show();
} else {
progressDialog.setMinimumDuration(4000);
}
progressDialog.setValue(0);
int maxTerminated = 0;
int minTerminated = 0;
if (ruleSet->recurseDepthFirst()) {
recurseDepthFirst(progressDialog, maxTerminated, minTerminated, generationCounter);
} else {
recurseBreadthFirst(progressDialog, maxTerminated, minTerminated, generationCounter);
}
progressDialog.setValue(100);
progressDialog.hide();
if (verbose) {
if (progressDialog.wasCanceled()) {
userCancelled = true;
INFO("User terminated.");
}
if (objects >= maxObjects) {
INFO(QString("Terminated because maximum number of objects reached (%1).").arg(maxObjects));
INFO(QString("Use 'Set MaxObjects' command to increase this number."));
}
if (stack.size() >= objects) {
INFO(QString("Terminated because the number of pending rules reached (%1).").arg(maxObjects));
INFO(QString("Use 'Set MaxObjects' command to run for longer time."));
}
if (generationCounter == maxGenerations) {
INFO(QString("Terminated because maximum number of generations reached (%1).").arg(maxGenerations));
INFO(QString("Use 'Set Maxdepth' command to increase this number."));
}
if (maxTerminated != 0) {
INFO(QString("Terminated %1 branches, because the dimension was greater than max size (%2)").arg(maxTerminated).arg(maxDim));
}
if (minTerminated != 0) {
INFO(QString("Terminated %1 branches, because the dimension was less than min size (%2)").arg(minTerminated).arg(minDim));
}
//INFO("Done building...");
}
}
void Builder::setCommand(QString command, QString param) {
if (command.toLower().startsWith("raytracer::")) {
QString c = command.toLower().remove("raytracer::");
if (c.contains("::")) {
QStringList l = c.split("::");
QString classID = l[0];
QString prop = l[1];
param.remove("["); param.remove("]");
if (!ruleSet->existsPrimitiveClass(classID)) {
WARNING("Trying to set property for unused class: " + classID);
} else {
PrimitiveClass* pc = ruleSet->getPrimitiveClass(classID);
if (prop == "reflection") {
MiniParser(param, ',').getDouble(pc->reflection);
} else if (prop == "phong") {
MiniParser(param, ',').getDouble(pc->ambient).getDouble(pc->diffuse).getDouble(pc->specular);
INFO(QString("Lightning for %1 set to: ambient: %1, diffuse: %2, specular: %3")
.arg(classID).arg(pc->ambient).arg(pc->diffuse).arg(pc->specular));
} else {
raytracerCommands.append(GLEngine::Command(c,param));
}
}
} else {
PrimitiveClass* pc = ruleSet->getDefaultClass();
QString prop = c;
param.remove("["); param.remove("]");
if (prop == "reflection") {
MiniParser(param, ',').getDouble(pc->reflection);
} else if (prop == "phong") {
MiniParser(param, ',').getDouble(pc->ambient).getDouble(pc->diffuse).getDouble(pc->specular);
INFO(QString("Default lightning set to: ambient: %1, diffuse: %2, specular: %3").arg(pc->ambient).arg(pc->diffuse).arg(pc->specular));
} else {
raytracerCommands.append(GLEngine::Command(c,param));
}
}
} else if (command == "maxdepth") {
bool succes;
int i = param.toInt(&succes);
if (!succes) throw Exception(QString("Command 'maxdepth' expected integer parameter. Found: %1").arg(param));
maxGenerations = i;
if (ruleSet->recurseDepthFirst()) {
if (maxGenerations > 0) {
ruleSet->setRulesMaxDepth(maxGenerations);
}
}
} else if (command == "colorpool") {
delete colorPool;
colorPool = 0; // Important - prevents crash if ColorPool constructor throws exception
colorPool = new ColorPool(param); // will throw exception for invalid pools.
} else if (command == "recursion") {
} else if (command == "rng") {
if (param.toLower() == "old") {
RandomStreams::UseOldRandomGenerators(true);
WARNING("Using the old random number generators is an obsolete option.");
} else if (param.toLower() == "new") {
RandomStreams::UseOldRandomGenerators(false);
} else {
throw Exception("Command 'set rng' expects either 'old' or 'new' as argument.");
}
} else if (command == "syncrandom") {
if (param.toLower() == "true") {
syncRandom = true;
} else if (param.toLower() == "false") {
syncRandom = false;
} else {
throw Exception(QString("Command 'syncrandom' expected either 'true' or 'false'. Found: %1").arg(param));
}
} else if (command == "maxsize") {
bool succes;
float f = param.toFloat(&succes);
if (!succes) throw Exception(QString("Command 'maxsize' expected floating-point parameter. Found: %1").arg(param));
maxDim = f;
} else if (command == "minsize") {
bool succes;
float f = param.toFloat(&succes);
if (!succes) throw Exception(QString("Command 'minsize' expected floating-point parameter. Found: %1").arg(param));
minDim = f;
} else if (command == "maxobjects") {
//INFO(QString("Setting 'maxgenerations' to %1").arg(param));
bool succes;
int i = param.toInt(&succes);
if (!succes) throw Exception(QString("Command 'maxobjects' expected integer parameter. Found: %1").arg(param));
maxObjects = i;
} else if (command == "seed") {
if (param.toLower() == "initial") {
if (initialSeed == 0) {
initialSeed = RandomStreams::Geometry()->getInt();
}
currentState->seed = initialSeed;
state.seed = initialSeed;
} else {
bool succes;
int i = param.toInt(&succes);
if (!succes) throw Exception(QString("Command 'seed' expected integer parameter or 'initial'. Found: %1").arg(param));
RandomStreams::SetSeed(i);
hasSeedChanged = true;
newSeed = i;
}
} else if (command == "background") {
QColor c(param);
if (!c.isValid()) throw Exception(QString("Command 'background' expected a valid color identifier: Found: %1").arg(param));
renderTarget->setBackgroundColor(Vector3f(c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0) );
} else if (command == "scale") {
bool succes;
double s = param.toDouble(&succes);
if (!succes) throw Exception(QString("Command 'scale' expected floating point parameter. Found: %1").arg(param));
renderTarget->setScale(s);
} else if (command == "translation") {
bool succes;
Vector3f v3(param, succes);
if (!succes) throw Exception(QString("Command 'translation' expected vector (such as [1 3 -10.1]). Found: %1").arg(param));
renderTarget->setTranslation(v3);
} else if (command == "pivot") {
bool succes;
Vector3f v3(param, succes);
if (!succes) throw Exception(QString("Command 'pivot' expected vector (such as [1 3 -10.1]). Found: %1").arg(param));
renderTarget->setPivot(v3);
} else if (command == "rotation") {
bool succes;
Matrix4f m4(param, succes);
if (!succes) throw Exception(QString("Command 'rotation' expected matrix (such as [1 0 0 0 1 0 0 0 1]). Found: %1").arg(param));
renderTarget->setRotation(m4);
} else if (command == "perspective-angle") {
bool succes;
double s = param.toDouble(&succes);
if (!succes) throw Exception(QString("Command 'perspective-angle' expected floating point parameter. Found: %1").arg(param));
renderTarget->setPerspectiveAngle(s);
} else if (command == "opengl") {
INFO("Render commands for 'opengl' not impl'ed yet!");
} else if (command == "template") {
renderTarget->callCommand(command,param);
} else {
throw Exception(QString("Unknown command: %1").arg(command));
}
}
ExecutionStack& Builder::getNextStack() {
return nextStack;
}
Builder::~Builder() {
//delete(ruleSet);
//delete(currentState);
delete(colorPool);
}
}
}

View File

@ -1,73 +0,0 @@
#pragma once
#include <QString>
#include <QProgressDialog>
#include "Rendering/Renderer.h"
#include "RuleSet.h"
#include "State.h"
#include "ColorPool.h"
#include "ExecutionStack.h"
#include "../../SyntopiaCore/Math/Matrix4.h"
#include "../../SyntopiaCore/GLEngine/EngineWidget.h"
namespace StructureSynth {
namespace Model {
using namespace SyntopiaCore;
/// A Builder executes the rule set on a Renderer object
class Builder {
public:
Builder(Rendering::Renderer* renderTarget, RuleSet* ruleSet, bool verbose);
~Builder();
void build();
void setCommand(QString command, QString param);
ExecutionStack& getNextStack();
State& getState() { return state; };
Rendering::Renderer* getRenderer() { return renderTarget; };
void increaseObjectCount() { objects++; };
// True, if the random seed was changed by the builder (by 'set seed <int>')
bool seedChanged() { return hasSeedChanged; }
int getNewSeed() { return newSeed; }
ColorPool* getColorPool() { return colorPool; }
QVector<GLEngine::Command> getRaytracerCommands() { return raytracerCommands; };
bool wasCancelled() { return userCancelled; }
private:
void recurseBreadthFirst(QProgressDialog& progressDialog, int& maxTerminated, int& minTerminated, int& generationCounter);
void recurseDepthFirst(QProgressDialog& progressDialog, int& maxTerminated, int& minTerminated, int& generationCounter);
State state;
bool userCancelled;
ExecutionStack stack;
ExecutionStack nextStack;
Rendering::Renderer* renderTarget;
RuleSet* ruleSet;
bool verbose;
int maxGenerations;
int maxObjects;
int objects;
int newSeed;
bool hasSeedChanged;
float minDim;
float maxDim;
bool syncRandom;
int initialSeed;
State* currentState;
ColorPool* colorPool;
QVector<GLEngine::Command> raytracerCommands;
};
}
}

View File

@ -1,87 +0,0 @@
#include "ColorPool.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "Builder.h"
#include "RandomStreams.h"
#include <QFile>
#include <QFileInfo>
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Exceptions;
namespace StructureSynth {
namespace Model {
ColorPool::ColorPool(QString initString) {
initString = initString.toLower();
picture = 0;
if (initString == "randomhue") {
type = RandomHue;
} else if (initString == "greyscale" || initString == "grayscale" ) {
type = GreyScale;
} else if (initString == "randomrgb") {
type = RandomRGB;
} else if (initString.startsWith("image:")) {
initString = initString.remove("image:");
type = Picture;
if (!QFile::exists(initString)) {
throw Exception(QString("Could not open file: %1").arg(QFileInfo(initString).absoluteFilePath()));
}
picture = new QImage(initString);
if (picture->isNull()) {
throw Exception(QString("Could not parse image file: %1").arg(QFileInfo(initString).absoluteFilePath()));
}
} else if (initString.startsWith("list:")) {
initString = initString.remove("list:");
QStringList l = initString.split(",");
for (int i = 0; i < l.count(); i++) {
QColor c(l[i]);
if (!c.isValid()) {
throw Exception(QString("Could not parse color in colorlist: %1").arg(initString));
}
colorList.append(c);
}
type = ColorList;
} else {
throw Exception(QString("Could not understand the color pool: %1. Try: RandomHue, RandomRGB, GrayScale, Image:test.png, List:#234,Red,Blue").arg(initString));
}
}
ColorPool::~ColorPool() {
delete picture;
}
QColor ColorPool::drawColor() {
if (type == RandomHue) {
return QColor::fromHsv(RandomStreams::Color()->getInt(359),255,255);
} else if (type == GreyScale) {
int r = RandomStreams::Color()->getInt(255);
return QColor(r,r,r).toHsv();
} else if (type == RandomRGB) {
// We can only pull one random number, so we must use a few tricks to get three ints
int r = RandomStreams::Color()->getInt(255);
int g = RandomStreams::Color()->getInt(255);
int b = RandomStreams::Color()->getInt(255);
return QColor(r,g,b).toHsv();
} else if (type == Picture) {
int x = RandomStreams::Color()->getInt(picture->width()-1);
int y = RandomStreams::Color()->getInt(picture->height()-1);
QRgb rgb = picture->pixel(x,y);
return QColor(rgb).toHsv();
} else if (type == ColorList) {
int id = RandomStreams::Color()->getInt(colorList.count()-1);
return colorList[id];
}
return QColor();
}
}
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <QColor>
#include <QString>
#include <QVector>
#include <QImage>
namespace StructureSynth {
namespace Model {
/// A ColorPool is a set or colors. It is used for drawing random colors using the 'color random' operator.
/// The Builder maintainse single color pool in a project.
class ColorPool {
enum PoolType { RandomHue, GreyScale, RandomRGB, Picture, ColorList };
public:
ColorPool(QString initString);
~ColorPool();
QColor drawColor(); // returns a random color from the pool (in HSV)
private:
PoolType type;
QVector<QColor> colorList; // only used by type: ColorList.
QImage* picture; // Only used by type: Picture.
};
}
}

View File

@ -1,70 +0,0 @@
#include "CustomRule.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "Builder.h"
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Model {
CustomRule::CustomRule(QString name) : Rule(name) {
weight = 1.0;
retirementRule = 0;
}
CustomRule::~CustomRule() {
//delete (retirementRule);
}
void CustomRule::apply(Builder* b) const {
int newDepth = -1;
/// If there is a maxdepth set for this object check it.
if (getMaxDepth() != -1) {
if (!b->getState().maxDepths.contains(this)) {
/// We will add a new maxdepth for this rule to the state.
newDepth = getMaxDepth()-1;
} else {
int depth = b->getState().maxDepths[this];
if (depth <= 0) {
/// This rule is retired.
if (retirementRule) {
b->getState().maxDepths[this] = maxDepth;
retirementRule->rule()->apply(b);
}
return;
} else {
/// Decrease depth.
newDepth = depth-1;
}
}
}
/// Apply all actions.
for (int i = 0; i < actions.size(); i++) {
if (getMaxDepth() != -1) {
actions[i].apply(b, this, newDepth);
} else {
actions[i].apply(b, 0 ,0);
}
}
}
QList<RuleRef*> CustomRule::getRuleRefs() const {
QList<RuleRef*> list;
for (int i = 0; i < actions.size(); i++) {
RuleRef* a = actions[i].getRuleRef();
if (a) list.append(a);
}
if (retirementRule) list.append(retirementRule);
return list;
}
}
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "Rule.h"
#include "Action.h"
namespace StructureSynth {
namespace Model {
/// A custom rule is a user defined rule.
/// It consist of a number of actions,
/// and a weight that is used if the rule definition is ambiguous (see 'AmbiguousRule').
class CustomRule : public Rule {
public:
CustomRule(QString name);
virtual ~CustomRule();
virtual void apply(Builder* builder) const;
/// Returns a list over rules that this rule references.
virtual QList<RuleRef*> getRuleRefs() const;
void appendAction(Action a) { actions.append(a); }
double getWeight() const { return weight; }
void setWeight(double w) { weight = w; }
void setRetirementRule(QString ruleName) { retirementRule = new RuleRef(ruleName); };
private:
QList<Action> actions;
double weight;
RuleRef* retirementRule;
};
}
}

View File

@ -1,8 +0,0 @@
#include "Rule.h"
namespace StructureSynth {
namespace Model {
}
}

View File

@ -1,33 +0,0 @@
#pragma once
#include "Rule.h"
#include "State.h"
namespace StructureSynth {
namespace Model {
struct RuleState {
RuleState() {};
RuleState(Rule* rule, State state) : rule(rule), state(state) {};
Rule* rule;
State state;
};
/// The ExecutionStack keeps track of which operations to perform next.
/// Rules are executed in generations:
/// The rules on the stack are all executed in each generation,
/// and each rule will add a number of new rules to the next generation of the stack.
/// Only one level is recursion is followed at each generation.
typedef QVector<RuleState> ExecutionStack;
/*
struct ExecutionStack {
QList< RuleState > currentStack;
};
*/
}
}

View File

@ -1,8 +0,0 @@
#include "PrimitiveClass.h"
namespace StructureSynth {
namespace Model {
}
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <QString>
namespace StructureSynth {
namespace Model {
/// Every PrimitiveRule can be assigned a class.
}
}

View File

@ -1,143 +0,0 @@
#include "PrimitiveRule.h"
#include "Builder.h"
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../../SyntopiaCore/Misc/ColorUtils.h"
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Logging;
#include "../../SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace StructureSynth {
namespace Model {
PrimitiveRule::PrimitiveRule(PrimitiveType type, PrimitiveClass* primitiveClass) : primitiveClass(primitiveClass), type(type) {
// enum PrimitiveType { Box, Sphere, Point, Cylinder, Line } ;
if (type == Box) {
name = "box";
} else if (type == Sphere) {
name = "sphere";
} else if (type == Dot) {
name = "dot";
} else if (type == Grid) {
name = "grid";
} else if (type == Cylinder) {
name = "cylinder";
} else if (type == Line) {
name = "line";
} else if (type == Mesh) {
name = "mesh";
} else if (type == Template) {
name = "template";
} else if (type == Other) {
name = "other";
} else {
WARNING("PrimitiveRule constructor: unknown PrimitiveType");
}
};
void PrimitiveRule::apply(Builder* b) const {
if (type == Template) {
b->getRenderer()->callGeneric(primitiveClass);
return;
}
b->increaseObjectCount();
b->getRenderer()->setColor(
SyntopiaCore::Misc::ColorUtils::HSVtoRGB( b->getState().hsv)
);
b->getRenderer()->setAlpha( b->getState().alpha );
if (type == Sphere) {
Vector3f v(0,0,0);
Vector3f v1 = b->getState().matrix * v;
Vector3f c = b->getState().matrix * Vector3f(0.5,0.5,0.5);
Vector3f c2 = (b->getState().matrix * Vector3f(0.5,0.5,0.0));
double r = (c- c2).length();
b->getRenderer()->drawSphere(c,r,primitiveClass);
} else if (type == Mesh) {
if (b->getState().previous) {
Vector3f v1 = (b->getState().previous->matrix) * Vector3f(0,0,0);
Vector3f v2 = (b->getState().previous->matrix) * Vector3f(1,0,0);
Vector3f v3 = (b->getState().previous->matrix) * Vector3f(0,1,0);
Vector3f u1 = b->getState().matrix * Vector3f(0,0,0);
Vector3f u2 = b->getState().matrix * Vector3f(1,0,0);
Vector3f u3 = b->getState().matrix * Vector3f(0,1,0);
b->getRenderer()->setPreviousColor(
SyntopiaCore::Misc::ColorUtils::HSVtoRGB( b->getState().previous->hsv));
b->getRenderer()->setPreviousAlpha(b->getState().previous->alpha);
b->getRenderer()->drawMesh(v1,v2-v1,v3-v1,u1,u2-u1,u3-u1,primitiveClass);
} else {
INFO("No prev");
}
} else if (type == Box) {
Vector3f v(0,0,0);
Vector3f v1 = b->getState().matrix * v;
Vector3f v2 = b->getState().matrix * Vector3f(1,0,0);
Vector3f v3 = b->getState().matrix * Vector3f(0,1,0);
Vector3f v4 = b->getState().matrix * Vector3f(0,0,1);
b->getRenderer()->drawBox(v1,v2-v1,v3-v1,v4-v1,primitiveClass);
} else if (type == Grid) {
Vector3f v(0,0,0);
Vector3f v1 = b->getState().matrix * v;
Vector3f v2 = b->getState().matrix * Vector3f(1,0,0);
Vector3f v3 = b->getState().matrix * Vector3f(0,1,0);
Vector3f v4 = b->getState().matrix * Vector3f(0,0,1);
b->getRenderer()->drawGrid(v1,v2-v1,v3-v1,v4-v1,primitiveClass);
} else if (type == Dot) {
Vector3f v = b->getState().matrix * Vector3f(0.5,0.5,0.5);
b->getRenderer()->drawDot(v,primitiveClass);
} else if (type == Line) {
Vector3f v = b->getState().matrix * Vector3f(0,0.5,0.5);
Vector3f v2 = b->getState().matrix * Vector3f(1,0.5,0.5);
b->getRenderer()->drawLine(v,v2,primitiveClass);
}
};
TriangleRule::TriangleRule(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3, PrimitiveClass* primitiveClass)
: PrimitiveRule(Other, primitiveClass), p1(p1), p2(p2), p3(p3) {
name = "Triangle";
}
void TriangleRule::apply(Builder* b) const {
b->increaseObjectCount();
b->getRenderer()->setColor(
SyntopiaCore::Misc::ColorUtils::HSVtoRGB( b->getState().hsv)
);
b->getRenderer()->setAlpha( b->getState().alpha );
Vector3f v1 = b->getState().matrix * p1;
Vector3f v2 = b->getState().matrix * p2;
Vector3f v3 = b->getState().matrix * p3;
b->getRenderer()->drawTriangle(v1,v2,v3,primitiveClass);
}
}
}

View File

@ -1,62 +0,0 @@
#pragma once
#include "Rule.h"
#include "PrimitiveClass.h"
#include "../../SyntopiaCore/GLEngine/Object3D.h"
namespace StructureSynth {
namespace Model {
using namespace SyntopiaCore::GLEngine;
/// These are the built-in primitives,
/// for drawing boxes, spheres and other simple geometric shapes.
class PrimitiveRule : public Rule {
public:
enum PrimitiveType { Box, Sphere, Dot, Grid, Cylinder, Line, Mesh, Template, Other } ;
PrimitiveRule(PrimitiveType type, PrimitiveClass* primitiveClass);
virtual void apply(Builder* builder) const;
/// Returns a list over rules that this rule references.
/// (Empty for all PrimitiveRules!)
virtual QList<RuleRef*> getRuleRefs() const { return QList<RuleRef*>(); }
/// 'class' is an identifier used for distinguishing between
/// different forms of the the same PrimiteType.
/// This is used together with Template Renderers.
///
/// For instance 'box::metal' will be parsed in to a 'box' primitive with a 'metal' class identifier.
void setClass(PrimitiveClass* primitiveClass) { this->primitiveClass = primitiveClass; }
PrimitiveClass* getClass() { return primitiveClass; }
protected:
PrimitiveClass* primitiveClass;
private:
PrimitiveType type;
};
/// Triangle rules are special, since they have explicit coordinate representation.
class TriangleRule : public PrimitiveRule {
public:
TriangleRule(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* primitiveClass);
virtual void apply(Builder* builder) const;
private:
SyntopiaCore::Math::Vector3f p1;
SyntopiaCore::Math::Vector3f p2;
SyntopiaCore::Math::Vector3f p3;
};
}
}

View File

@ -1,11 +0,0 @@
#include "RandomStreams.h"
namespace StructureSynth {
namespace Model {
SyntopiaCore::Math::RandomNumberGenerator* RandomStreams::geometry = new SyntopiaCore::Math::RandomNumberGenerator(false);
SyntopiaCore::Math::RandomNumberGenerator* RandomStreams::color = new SyntopiaCore::Math::RandomNumberGenerator(false);
}
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "../../SyntopiaCore/Math/Random.h"
namespace StructureSynth {
namespace Model {
/// These two independent random number generator streams are used in Structure Synth
class RandomStreams {
public:
static SyntopiaCore::Math::RandomNumberGenerator* Geometry() { return geometry; }
static SyntopiaCore::Math::RandomNumberGenerator* Color() { return color; }
static void SetSeed(int seed) { geometry->setSeed(seed); color->setSeed(seed); }
static void UseOldRandomGenerators(bool useOld) { geometry->useStdLib(useOld); color->useStdLib(useOld); }
private:
static SyntopiaCore::Math::RandomNumberGenerator* geometry;
static SyntopiaCore::Math::RandomNumberGenerator* color;
};
}
}

View File

@ -1,338 +0,0 @@
#include "ObjRenderer.h"
#include "SyntopiaCore/Logging/Logging.h"
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::GLEngine;
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Model {
namespace Rendering {
void ObjGroup::addGroup(ObjGroup g) {
int vertexCount = vertices.count();
int normalCount = normals.count();
for (int i = 0; i < g.vertices.count(); i++) {
vertices.append(g.vertices[i]);
}
for (int i = 0; i < g.normals.count(); i++) {
normals.append(g.normals[i]);
}
for (int i = 0; i < g.faces.count(); i++) {
for (int j = 0; j < g.faces[i].count(); j++) {
g.faces[i][j].vID = g.faces[i][j].vID + vertexCount;
g.faces[i][j].nID = g.faces[i][j].nID + normalCount;
}
faces.append(g.faces[i]);
}
}
// Removes redundant vertices.
void ObjGroup::reduceVertices() {
QVector<Vector3f> newVertices;
QVector<Vector3f> newNormals;
QMap<int,int> oldToNewVertex;
QMap<int,int> oldToNewNormals;
// Reduce vertices and normals (if present)
for (int i = 0; i < vertices.count(); i++) {
int index = newVertices.indexOf(vertices[i]);
if (index==-1) {
newVertices.append(vertices[i]);
index=newVertices.count()-1;
}
oldToNewVertex[i] = index;
}
// Reduce vertices and normals (if present)
for (int i = 0; i < normals.count(); i++) {
int index = newNormals.indexOf(normals[i]);
if (index==-1) {
newNormals.append(normals[i]);
index=newNormals.count()-1;
}
oldToNewNormals[i] = index;
}
// Update indices
for (int i = 0; i < faces.count(); i++) {
for (int j = 0; j < faces[i].count(); j++) {
faces[i][j].vID = oldToNewVertex[faces[i][j].vID-1]+1; // beware OBJ is 1-based!
faces[i][j].nID = oldToNewNormals[faces[i][j].nID-1]+1;
}
}
vertices = newVertices;
normals = newNormals;
}
namespace {
/*
This function was taken from Paul Bourkes website:
http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/sphere_cylinder/
Create a unit sphere centered at the origin
This code illustrates the concept rather than implements it efficiently
It is called with two arguments, the theta and phi angle increments in degrees
Note that at the poles only 3 vertex facet result
while the rest of the sphere has 4 point facets
*/
void CreateUnitSphere(int dt,int dp, ObjGroup& motherGroup, Matrix4f m)
{
float DTOR = 3.1415/180.0;
double dtheta = 180.0/dt;
double dphi = 360.0/dp;
ObjGroup group;
for (int i = 0; i < dt; i++) {
double theta = -90.0 + ((i*180.0)/(double)dt);
for (int j=0;j<dp;j++) {
double phi = ((j*360.0)/(double)dp);
int vi = group.vertices.count()+1;
int vn = group.normals.count()+1;
group.vertices.append(m*Vector3f(cos(theta*DTOR) * cos(phi*DTOR), cos(theta*DTOR) * sin(phi*DTOR), sin(theta*DTOR)));
group.normals.append(Vector3f(cos(theta*DTOR) * cos(phi*DTOR), cos(theta*DTOR) * sin(phi*DTOR), sin(theta*DTOR)));
QVector<VertexNormal> vns;
if (theta > -90 && theta < 90) {
group.vertices.append(m*Vector3f( cos(theta*DTOR) * cos((phi+dphi)*DTOR),cos(theta*DTOR) * sin((phi+dphi)*DTOR), sin(theta*DTOR)));
group.normals.append(Vector3f( cos(theta*DTOR) * cos((phi+dphi)*DTOR),cos(theta*DTOR) * sin((phi+dphi)*DTOR), sin(theta*DTOR)));
for (int j = 0; j<4; j++) vns.append(VertexNormal(j+vi,j+vn));
} else {
for (int j = 0; j<3; j++) vns.append(VertexNormal(j+vi,j+vn));
}
group.vertices.append(m*Vector3f( cos((theta+dtheta)*DTOR) * cos((phi+dphi)*DTOR), cos((theta+dtheta)*DTOR) * sin((phi+dphi)*DTOR), sin((theta+dtheta)*DTOR)));
group.normals.append(Vector3f( cos((theta+dtheta)*DTOR) * cos((phi+dphi)*DTOR), cos((theta+dtheta)*DTOR) * sin((phi+dphi)*DTOR), sin((theta+dtheta)*DTOR)));
group.vertices.append(m*Vector3f(cos((theta+dtheta)*DTOR) * cos(phi*DTOR), cos((theta+dtheta)*DTOR) * sin(phi*DTOR), sin((theta+dtheta)*DTOR)));
group.normals.append(Vector3f(cos((theta+dtheta)*DTOR) * cos(phi*DTOR), cos((theta+dtheta)*DTOR) * sin(phi*DTOR), sin((theta+dtheta)*DTOR)));
group.faces.append(vns);
}
}
group.reduceVertices();
motherGroup.addGroup(group);
}
}
void ObjRenderer::addLineQuad(ObjGroup& group, Vector3f v1,Vector3f v2,Vector3f v3,Vector3f v4) {
int vi = group.vertices.count()+1;
group.vertices.append(v1);
group.vertices.append(v2);
group.vertices.append(v3);
group.vertices.append(v4);
for (int j = 0; j<4; j++) {
QVector<VertexNormal> vns;
vns.append(VertexNormal(vi+j, -1));
vns.append(VertexNormal(vi+(j+1 % 4), -1));
group.faces.append(vns);
}
}
void ObjRenderer::addQuad(ObjGroup& group, Vector3f v1,Vector3f v2,Vector3f v3,Vector3f v4) {
int vi = group.vertices.count()+1;
int vn = group.normals.count()+1;
group.vertices.append(v1);
group.vertices.append(v2);
group.vertices.append(v3);
group.vertices.append(v4);
Vector3f normal = Vector3f::cross((v2-v1), (v4-v1)).normalized();
group.normals.append(normal);
group.normals.append(normal);
group.normals.append(normal);
group.normals.append(normal);
QVector<VertexNormal> vns;
for (int j = 0; j<4; j++) vns.append(VertexNormal(vi+j, vn+j));
group.faces.append(vns);
}
void ObjRenderer::setClass(QString classID, Vector3f rgb, double /*alpha*/) {
// Should we also group by alpha channel?
QString className;
if (groupByTagging) className += classID;
if (groupByColor) className += QColor(int(rgb[0]*255),int(rgb[1]*255),int(rgb[2]*255)).name();
if (className.isEmpty()) className = "default";
if (!groups.contains(className)) groups[className] = ObjGroup();
groups[className].groupName = className;
currentGroup = className;
}
void ObjRenderer::drawBox(SyntopiaCore::Math::Vector3f O,
SyntopiaCore::Math::Vector3f v1 ,
SyntopiaCore::Math::Vector3f v2,
SyntopiaCore::Math::Vector3f v3, PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
ObjGroup group;
addQuad(group, O, O+v2,O+v2+v1,O+v1);
addQuad(group, O+v3, O+v1+v3, O+v2+v1+v3, O+v2+v3);
addQuad(group, O, O+v3, O+v3+v2,O+v2);
addQuad(group, O+v1,O+v2+v1, O+v3+v2+v1, O+v3+v1);
addQuad(group, O, O+v1, O+v3+v1, O+v3);
addQuad(group, O+v2, O+v3+v2 , O+v3+v2+v1,O+v1+v2);
group.reduceVertices();
groups[currentGroup].addGroup(group);
};
void ObjRenderer::drawMesh( SyntopiaCore::Math::Vector3f O,
SyntopiaCore::Math::Vector3f v1,
SyntopiaCore::Math::Vector3f v2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f u1,
SyntopiaCore::Math::Vector3f u2,
PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
ObjGroup group;
Vector3f v3 = endBase-O;
addQuad(group, O, O+v2,O+v2+v1,O+v1);
addQuad(group, O+v3, O+u1+v3, O+u2+u1+v3, O+u2+v3);
addQuad(group, O, O+v3, O+v3+u2,O+v2);
addQuad(group, O+v1,O+v2+v1, O+v3+u2+u1, O+v3+u1);
addQuad(group, O, O+v1, O+v3+u1, O+v3);
addQuad(group, O+v2, O+v3+u2 , O+v3+u2+u1,O+v1+v2);
group.reduceVertices();
groups[currentGroup].addGroup(group);
};
void ObjRenderer::drawGrid(SyntopiaCore::Math::Vector3f O,
SyntopiaCore::Math::Vector3f v1 ,
SyntopiaCore::Math::Vector3f v2,
SyntopiaCore::Math::Vector3f v3,
PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
ObjGroup group;
addLineQuad(group,O, O+v2,O+v2+v1,O+v1);
addLineQuad(group,O+v3, O+v1+v3, O+v2+v1+v3, O+v2+v3);
addLineQuad(group,O, O+v3, O+v3+v2,O+v2);
addLineQuad(group,O+v1,O+v2+v1, O+v3+v2+v1, O+v3+v1);
addLineQuad(group,O, O+v1, O+v3+v1, O+v3);
addLineQuad(group,O+v2, O+v3+v2 , O+v3+v2+v1,O+v1+v2);
group.reduceVertices();
groups[currentGroup].addGroup(group);
};
void ObjRenderer::drawLine(SyntopiaCore::Math::Vector3f from, SyntopiaCore::Math::Vector3f to, PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
ObjGroup group;
group.vertices.append(from);
group.vertices.append(to);
QVector<VertexNormal> vns;
vns.append(VertexNormal(1, -1));
vns.append(VertexNormal(2, -1));
group.faces.append(vns);
groups[currentGroup].addGroup(group);
};
void ObjRenderer::drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID)
{
setClass(classID->name,rgb,alpha);
ObjGroup group;
group.vertices.append(p1);
group.vertices.append(p2);
group.vertices.append(p3);
QVector<VertexNormal> vns;
for (int j = 0; j<3; j++) vns.append(VertexNormal(1+j, -1));
group.faces.append(vns);
groups[currentGroup].addGroup(group);
}
void ObjRenderer::drawDot(SyntopiaCore::Math::Vector3f v, PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
ObjGroup group;
group.vertices.append(v);
QVector<VertexNormal> vns;
vns.append(VertexNormal(1, -1));
group.faces.append(vns);
groups[currentGroup].addGroup(group);
};
void ObjRenderer::drawSphere(SyntopiaCore::Math::Vector3f center, float radius, PrimitiveClass* classID) {
setClass(classID->name,rgb,alpha);
Matrix4f m = Matrix4f::Translation(center.x(),center.y(),center.z())*(Matrix4f::ScaleMatrix(radius));
CreateUnitSphere(sphereDT,sphereDP,groups[currentGroup],m);
};
void ObjRenderer::begin() {
rgb = Vector3f(1,0,0);
alpha = 1;
};
void ObjRenderer::end() {
};
void ObjRenderer::writeToStream(QTextStream& ts) {
int vertexCount = 0;
int normalCount = 0;
foreach (ObjGroup o, groups) {
// Group name
INFO(o.groupName);
ts << "g " << o.groupName << endl;
ts << "usemtl " << o.groupName << endl;
// Vertices
foreach (Vector3f v, o.vertices) {
ts << "v " << QString::number(v.x()) << " "
<< QString::number(v.y()) << " "
<< QString::number(v.z()) << " "
<< endl;
}
// Normals
foreach (Vector3f v, o.normals) {
ts << "vn " << QString::number(v.x()) << " "
<< QString::number(v.y()) << " "
<< QString::number(v.z()) << " "
<< endl;
}
// Faces
foreach (QVector<VertexNormal> vi, o.faces) {
if (vi.count() == 1) {
ts << "p ";
} else if (vi.count() == 2) {
ts << "l ";
} else {
ts << "f ";
}
foreach (VertexNormal vn, vi) {
if (vn.nID == -1) {
ts << QString::number(vn.vID+vertexCount) << " ";
} else {
ts << QString::number(vn.vID+vertexCount) << "//"
<< QString::number(vn.nID+normalCount) << " ";
}
}
ts << endl;
}
vertexCount += o.vertices.count();
normalCount += o.normals.count();
}
};
}
}
}

View File

@ -1,126 +0,0 @@
#pragma once
#include <QString>
#include "../../../SyntopiaCore/Math/Vector3.h"
#include "../../../SyntopiaCore/Math/Matrix4.h"
#include "../../../SyntopiaCore/GLEngine/Object3D.h"
#include "Renderer.h"
namespace StructureSynth {
namespace Model {
namespace Rendering {
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::GLEngine;
struct VertexNormal {
VertexNormal() {};
VertexNormal(int vID, int nID) : vID(vID), nID(nID) {};
int vID;
int nID;
};
struct ObjGroup {
QString groupName;
QVector<Vector3f> vertices;
QVector<Vector3f> normals;
QVector<QVector<VertexNormal> > faces;
void addGroup(ObjGroup g);
void reduceVertices();
};
/// Obj file renderer
class ObjRenderer : public Renderer {
public:
ObjRenderer(int sphereDT, int sphereDP, bool groupByTagging, bool groupByColor)
: sphereDT(sphereDT), sphereDP(sphereDP), groupByTagging(groupByTagging), groupByColor(groupByColor) {};
virtual ~ObjRenderer() {};
/// Flow
virtual void begin();
virtual void end();
/// This defines the identifier for our renderer.
virtual QString renderClass() { return "ObjRenderer"; }
/// The primitives
virtual void drawBox(Vector3f base,
Vector3f dir1,
Vector3f dir2,
Vector3f dir3,
PrimitiveClass* classID);
virtual void drawMesh( Vector3f startBase,
Vector3f startDir1,
Vector3f startDir2,
Vector3f endBase,
Vector3f endDir1,
Vector3f endDir2,
PrimitiveClass* classID);
virtual void drawGrid(Vector3f base,
Vector3f dir1,
Vector3f dir2,
Vector3f dir3,
PrimitiveClass* classID);
virtual void drawLine(Vector3f from,
Vector3f to,
PrimitiveClass* classID);
virtual void drawDot(Vector3f pos,
PrimitiveClass* classID);
virtual void drawSphere(Vector3f center, float radius,
PrimitiveClass* classID);
virtual void drawTriangle(Vector3f p1,
Vector3f p2,
Vector3f p3,
PrimitiveClass* classID);
virtual void callGeneric(PrimitiveClass* ) {};
// Color
// RGB in [0;1] intervals.
virtual void setColor(Vector3f rgb) { this->rgb = rgb; }
virtual void setBackgroundColor(Vector3f /*rgb*/) {};
virtual void setAlpha(double alpha) { this->alpha = alpha; }
virtual void setPreviousColor(Vector3f /*rgb*/) {};
virtual void setPreviousAlpha(double /*alpha*/) {};
// Camera settings
virtual void setTranslation(Vector3f /*translation*/) {};
virtual void setScale(double /*scale*/) {};
virtual void setRotation(Matrix4f /*rotation*/) {};
virtual void setPivot(Vector3f /*pivot*/) {};
virtual void setPerspectiveAngle(double /*angle*/) {};
// Issues a command for a specific renderclass such as 'template' or 'opengl'
virtual void callCommand(const QString& /*renderClass*/, const QString& /*command*/) {};
void addQuad(ObjGroup& group, Vector3f v1,Vector3f v2,Vector3f v3,Vector3f v4);
void addLineQuad(ObjGroup& group, Vector3f v1,Vector3f v2,Vector3f v3,Vector3f v4);
void setClass(QString classID, Vector3f rgb, double alpha);
void writeToStream(QTextStream& ts);
private:
QMap<QString, ObjGroup> groups;
QString currentGroup;
SyntopiaCore::Math::Vector3f rgb;
double alpha;
int sphereDT;
int sphereDP;
bool groupByTagging;
bool groupByColor;
};
}
}
}

View File

@ -1,137 +0,0 @@
#include "OpenGLRenderer.h"
#include "../../../SyntopiaCore/GLEngine/Sphere.h"
#include "../../../SyntopiaCore/GLEngine/Box.h"
#include "../../../SyntopiaCore/GLEngine/Grid.h"
#include "../../../SyntopiaCore/GLEngine/Dot.h"
#include "../../../SyntopiaCore/GLEngine/Line.h"
#include "../../../SyntopiaCore/GLEngine/Mesh.h"
#include "../../../SyntopiaCore/GLEngine/Triangle.h"
#include "../../../SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::GLEngine;
using namespace SyntopiaCore::Math;
#include "../../../SyntopiaCore/Logging/Logging.h"
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Model {
namespace Rendering {
void OpenGLRenderer::drawBox(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3, PrimitiveClass* classID) {
Object3D* o =new Box( base, dir1, dir2, dir3);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
};
void OpenGLRenderer::drawMesh( SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1,
SyntopiaCore::Math::Vector3f endDir2,
PrimitiveClass* classID) {
Mesh* o =new Mesh( startBase, startDir1, startDir2, endBase, endDir1, endDir2);
o->setPrimitiveClass(classID);
o->setPreviousColor(rgb, alpha);
o->setColor(oldRgb, oldAlpha);
engine->addObject(o);
};
void OpenGLRenderer::drawGrid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID) {
Object3D* o = new Grid( base, dir1, dir2, dir3);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
};
void OpenGLRenderer::drawLine(SyntopiaCore::Math::Vector3f from, SyntopiaCore::Math::Vector3f to, PrimitiveClass* classID) {
Object3D* o = new Line( from, to);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
};
void OpenGLRenderer::drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID) {
Object3D* o = new Triangle(p1, p2,p3);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
}
void OpenGLRenderer::drawDot(SyntopiaCore::Math::Vector3f v, PrimitiveClass* classID) {
Object3D* o = new Dot(v);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
};
void OpenGLRenderer::drawSphere(SyntopiaCore::Math::Vector3f center, float radius, PrimitiveClass* classID) {
Object3D* o = new Sphere( center, radius);
o->setPrimitiveClass(classID);
o->setColor(rgb, alpha);
engine->addObject(o);
};
void OpenGLRenderer::begin() {
engine->clearWorld();
engine->setBackgroundColor(0,0,0);
rgb = Vector3f(1,0,0);
alpha = 1;
};
void OpenGLRenderer::end() {
INFO(QString("Build done. Created %1 objects.").arg(engine->objectCount()));
engine->requireRedraw();
};
void OpenGLRenderer::setBackgroundColor(SyntopiaCore::Math::Vector3f rgb) {
engine->setBackgroundColor(rgb.x(),rgb.y(),rgb.z());
}
void OpenGLRenderer::callCommand(const QString& /*renderClass*/, const QString& /*command*/) {
}
void OpenGLRenderer::setTranslation(SyntopiaCore::Math::Vector3f translation) {
engine->setTranslation(translation);
};
void OpenGLRenderer::setScale(double scale) {
engine->setScale(scale);
};
void OpenGLRenderer::setRotation(SyntopiaCore::Math::Matrix4f rotation) {
engine->setRotation(rotation);
};
void OpenGLRenderer::setPivot(SyntopiaCore::Math::Vector3f pivot) {
engine->setPivot(pivot);
};
void OpenGLRenderer::setPerspectiveAngle(double angle) {
engine->setPerspectiveAngle(angle);
}
}
}
}

View File

@ -1,90 +0,0 @@
#pragma once
#include <QString>
#include "../../../SyntopiaCore/GLEngine/EngineWidget.h"
#include "Renderer.h"
#include "../../../SyntopiaCore/Math/Vector3.h"
namespace StructureSynth {
namespace Model {
namespace Rendering {
using namespace SyntopiaCore::GLEngine;
/// A renderer implementation based on the SyntopiaCore openGL widget.
class OpenGLRenderer : public Renderer {
public:
OpenGLRenderer(SyntopiaCore::GLEngine::EngineWidget* engine) : engine(engine) {};
virtual ~OpenGLRenderer() {};
/// The primitives
virtual void drawBox(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID);
virtual void drawMesh( SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1,
SyntopiaCore::Math::Vector3f endDir2,
PrimitiveClass* classID);
virtual void drawSphere(SyntopiaCore::Math::Vector3f center, float radius,
PrimitiveClass* classID);
virtual void drawGrid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID);
virtual void drawLine(SyntopiaCore::Math::Vector3f from,
SyntopiaCore::Math::Vector3f to,
PrimitiveClass* classID);
virtual void drawDot(SyntopiaCore::Math::Vector3f pos,
PrimitiveClass* classID);
virtual void drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID);
virtual void begin();
virtual void end();
virtual void setColor(SyntopiaCore::Math::Vector3f rgb) { this->rgb = rgb; }
virtual void setBackgroundColor(SyntopiaCore::Math::Vector3f rgb);
virtual void setAlpha(double alpha) { this->alpha = alpha; }
virtual void setPreviousColor(SyntopiaCore::Math::Vector3f rgb) { this->oldRgb = rgb; }
virtual void setPreviousAlpha(double alpha) { this->oldAlpha = alpha; }
virtual void setTranslation(SyntopiaCore::Math::Vector3f /*translation*/);
virtual void setScale(double /*scale*/);
virtual void setRotation(SyntopiaCore::Math::Matrix4f /*rotation*/);
virtual void setPivot(SyntopiaCore::Math::Vector3f /*pivot*/);
virtual void setPerspectiveAngle(double /*angle*/);
// Issues a command for a specific renderclass such as 'template' or 'opengl'
virtual void callCommand(const QString& renderClass, const QString& command);
private:
SyntopiaCore::GLEngine::EngineWidget* engine;
SyntopiaCore::Math::Vector3f rgb;
double alpha;
SyntopiaCore::Math::Vector3f oldRgb;
double oldAlpha;
};
}
}
}

View File

@ -1,10 +0,0 @@
#include "Renderer.h"
namespace StructureSynth {
namespace Model {
namespace Renderer {
}
}
}

View File

@ -1,90 +0,0 @@
#pragma once
#include <QString>
#include "../../../SyntopiaCore/Math/Vector3.h"
#include "../../../SyntopiaCore/Math/Matrix4.h"
#include "../../../SyntopiaCore/GLEngine/Object3D.h"
namespace StructureSynth {
namespace Model {
namespace Rendering {
using namespace SyntopiaCore::GLEngine;
/// Abstract base class for implementing a renderer
class Renderer {
public:
Renderer() {};
virtual ~Renderer() {};
/// Flow
virtual void begin() {};
virtual void end() {};
/// This defines the identifier for our renderer.
virtual QString renderClass() { return ""; }
/// The primitives
virtual void drawBox(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID) = 0;
virtual void drawMesh( SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1,
SyntopiaCore::Math::Vector3f endDir2,
PrimitiveClass* classID) = 0;
virtual void drawGrid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID) = 0;
virtual void drawLine(SyntopiaCore::Math::Vector3f from,
SyntopiaCore::Math::Vector3f to,
PrimitiveClass* classID) = 0;
virtual void drawDot(SyntopiaCore::Math::Vector3f pos,
PrimitiveClass* classID) = 0;
virtual void drawSphere(SyntopiaCore::Math::Vector3f center, float radius,
PrimitiveClass* classID) = 0;
virtual void drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID) = 0;
virtual void callGeneric(PrimitiveClass* ) {};
// Color
// RGB in [0;1] intervals.
virtual void setColor(SyntopiaCore::Math::Vector3f rgb) = 0;
virtual void setBackgroundColor(SyntopiaCore::Math::Vector3f rgb) = 0;
virtual void setAlpha(double alpha) = 0;
virtual void setPreviousColor(SyntopiaCore::Math::Vector3f rgb) = 0;
virtual void setPreviousAlpha(double alpha) = 0;
// Camera settings
virtual void setTranslation(SyntopiaCore::Math::Vector3f /*translation*/) {};
virtual void setScale(double /*scale*/) {};
virtual void setRotation(SyntopiaCore::Math::Matrix4f /*rotation*/) {};
virtual void setPivot(SyntopiaCore::Math::Vector3f /*pivot*/) {};
virtual void setPerspectiveAngle(double /*angle*/) {};
// Issues a command for a specific renderclass such as 'template' or 'opengl'
virtual void callCommand(const QString& /*renderClass*/, const QString& /*command*/) {};
};
}
}
}

View File

@ -1,488 +0,0 @@
#include "TemplateRenderer.h"
#include "../../../SyntopiaCore/Math/Vector3.h"
#include "../../../SyntopiaCore/Logging/Logging.h"
#include "../../../SyntopiaCore/Exceptions/Exception.h"
#include "../PrimitiveClass.h"
#include <QDomDocument>
#include <QIODevice>
#include <QFile>
#include <QFileInfo>
#include <QMap>
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Exceptions;
namespace StructureSynth {
namespace Model {
namespace Rendering {
void Template::read(QString xml) {
QDomDocument doc;
QString errorMessage;
int errorLine = 0;
int errorColumn = 0;
if (!doc.setContent(xml, false, &errorMessage, &errorLine, &errorColumn )) {
QString error = QString("[Line %1, Col %2] %3").arg(errorLine).arg(errorColumn).arg(errorMessage);
WARNING("Unable to parse xml: " + error);
throw Exception("Unable to parse xml from string: " + error );
return;
}
fullText = doc.toString();
parse(doc);
}
void Template::read(QFile& file) {
QDomDocument doc;
if (!file.open(QIODevice::ReadOnly)) {
throw Exception("Unable to open file: " + QFileInfo(file).absoluteFilePath());
}
QString errorMessage;
int errorLine = 0;
int errorColumn = 0;
if (!doc.setContent(&file, false, &errorMessage, &errorLine, &errorColumn)) {
file.close();
QString error = QString("[Line %1, Col %2] %3").arg(errorLine).arg(errorColumn).arg(errorMessage);
throw Exception("Unable to parse file: " + error + " in file: " + QFileInfo(file).absoluteFilePath());
}
file.close();
fullText = doc.toString();
parse(doc);
}
void Template::parse(QDomDocument& doc) {
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChild();
QDomElement ne = docElem.toElement(); // try to convert the node to an element.
if(!ne.isNull()) {
if (ne.hasAttribute("name")) {
this->name = ne.attribute("name");
} else {
this->name = "NONAME";
}
if (ne.hasAttribute("defaultExtension")) {
this->defaultExtension = ne.attribute("defaultExtension");
} else {
this->defaultExtension = "Unknown file type (*.txt)";
}
if (ne.hasAttribute("runAfter")) {
this->runAfter = ne.attribute("runAfter");
} else {
this->runAfter = "";
}
}
while(!n.isNull()) {
QDomElement e = n.toElement(); // try to convert the node to an element.
if(!e.isNull()) {
if (e.tagName() == "primitive" || e.tagName() == "substitution") {
if (e.tagName() == "substitution") {
WARNING("Element-name 'substitution' is a deprecated name. Please rename to 'primitive'.");
}
if (!e.hasAttribute("name")) {
WARNING("Primitive without name attribute found!");
n = n.nextSibling();
continue;
}
QString type = "";
if (e.hasAttribute("type")) {
type = "::" + e.attribute("type");
}
QString name = e.attribute("name") + type;
primitives[name] = TemplatePrimitive(e.text());
} else if (e.tagName() == "description") {
description = e.text();
} else {
WARNING("Expected 'primitive' or 'description' element, found: " + e.tagName());
}
}
n = n.nextSibling();
}
}
TemplateRenderer::TemplateRenderer(QString xmlDefinitionFile) {
counter = 0;
QFile file(xmlDefinitionFile);
workingTemplate.read(file);
}
TemplateRenderer::TemplateRenderer(Template myTemplate) {
counter = 0;
workingTemplate = myTemplate;
}
TemplateRenderer::TemplateRenderer()
{
counter = 0;
}
TemplateRenderer::~TemplateRenderer() {
}
bool TemplateRenderer::assertPrimitiveExists(QString templateName) {
if (!workingTemplate.getPrimitives().contains(templateName)) {
QString error =
QString("Template error: the primitive '%1' is not defined.").arg(templateName);
if (!missingTypes.contains(error)) {
// Only show each error once.
WARNING(error);
INFO("(A template may not support all drawing primitives. Either update the template or choose another primitive)");
missingTypes.insert(error);
}
return false;
}
return true;
}
void TemplateRenderer::doBeginEndSubstitutions(TemplatePrimitive& t)
{
t.substitute("{CamPosX}", QString::number(cameraPosition.x()));
t.substitute("{CamPosY}", QString::number(cameraPosition.y()));
t.substitute("{CamPosZ}", QString::number(cameraPosition.z()));
t.substitute("{CamUpX}", QString::number(cameraUp.x()));
t.substitute("{CamUpY}", QString::number(cameraUp.y()));
t.substitute("{CamUpZ}", QString::number(cameraUp.z()));
Vector3f cameraDir = cameraTarget-cameraPosition;
cameraDir.normalize();
t.substitute("{CamDirX}", QString::number(cameraDir.x()));
t.substitute("{CamDirY}", QString::number(cameraDir.y()));
t.substitute("{CamDirZ}", QString::number(cameraDir.z()));
t.substitute("{CamRightX}", QString::number(cameraRight.x()));
t.substitute("{CamRightY}", QString::number(cameraRight.y()));
t.substitute("{CamRightZ}", QString::number(cameraRight.z()));
t.substitute("{CamTargetX}", QString::number(cameraTarget.x()));
t.substitute("{CamTargetY}", QString::number(cameraTarget.y()));
t.substitute("{CamTargetZ}", QString::number(cameraTarget.z()));
if (t.contains("{CamColumnMatrix}")) {
const Vector3f u = -cameraRight;
const Vector3f v = cameraUp;
const Vector3f w = -cameraDir;
QString mat = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 0.0 0.0 0.0 1.0")
.arg(u.x()).arg(v.x()).arg(w.x()).arg(cameraPosition.x())
.arg(u.y()).arg(v.y()).arg(w.y()).arg(cameraPosition.y())
.arg(u.z()).arg(v.z()).arg(w.z()).arg(cameraPosition.z());
t.substitute("{CamColumnMatrix}", mat);
}
t.substitute("{aspect}", QString::number(aspect));
t.substitute("{width}", QString::number(width));
t.substitute("{height}", QString::number(height));
t.substitute("{fov}", QString::number(fov));
t.substitute("{BR}", QString::number(backRgb.x()));
t.substitute("{BG}", QString::number(backRgb.y()));
t.substitute("{BB}", QString::number(backRgb.z()));
t.substitute("{BR256}", QString::number(backRgb.x()*255));
t.substitute("{BG256}", QString::number(backRgb.y()*255));
t.substitute("{BB256}", QString::number(backRgb.z()*255));
}
void TemplateRenderer::doStandardSubstitutions(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3, TemplatePrimitive& t) {
if (t.contains("{matrix}")) {
QString mat = QString("%1 %2 %3 0 %4 %5 %6 0 %7 %8 %9 0 %10 %11 %12 1")
.arg(dir1.x()).arg(dir1.y()).arg(dir1.z())
.arg(dir2.x()).arg(dir2.y()).arg(dir2.z())
.arg(dir3.x()).arg(dir3.y()).arg(dir3.z())
.arg(base.x()).arg(base.y()).arg(base.z());
t.substitute("{matrix}", mat);
}
if (t.contains("{columnmatrix}")) {
QString mat = QString("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 0 0 0 1")
.arg(dir1.x()).arg(dir2.x()).arg(dir3.x()).arg(base.x())
.arg(dir1.y()).arg(dir2.y()).arg(dir3.y()).arg(base.y())
.arg(dir1.z()).arg(dir2.z()).arg(dir3.z()).arg(base.z());
t.substitute("{columnmatrix}", mat);
}
if (t.contains("{povmatrix}")) {
QString mat = QString("%1, %2, %3, %4, %5, %6, %7, %8, %9, %10, %11, %12")
.arg(dir1.x()).arg(dir1.y()).arg(dir1.z())
.arg(dir2.x()).arg(dir2.y()).arg(dir2.z())
.arg(dir3.x()).arg(dir3.y()).arg(dir3.z())
.arg(base.x()).arg(base.y()).arg(base.z());
t.substitute("{povmatrix}", mat);
}
t.substitute("{r}", QString::number(rgb.x()));
t.substitute("{g}", QString::number(rgb.y()));
t.substitute("{b}", QString::number(rgb.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
}
void TemplateRenderer::drawBox(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID)
{
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("box"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("box"+alternateID));
doStandardSubstitutions(base, dir1, dir2, dir3, t);
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Box%1").arg(counter++));
}
output.append(t.getText());
};
void TemplateRenderer::drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("triangle"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("triangle"+alternateID));
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Triangle%1").arg(counter++));
}
t.substitute("{p1x}", QString::number(p1.x()));
t.substitute("{p1y}", QString::number(p1.y()));
t.substitute("{p1z}", QString::number(p1.z()));
t.substitute("{p2x}", QString::number(p2.x()));
t.substitute("{p2y}", QString::number(p2.y()));
t.substitute("{p2z}", QString::number(p2.z()));
t.substitute("{p3x}", QString::number(p3.x()));
t.substitute("{p3y}", QString::number(p3.y()));
t.substitute("{p3z}", QString::number(p3.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
output.append(t.getText());
}
void TemplateRenderer::drawGrid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("grid"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("grid"+alternateID));
doStandardSubstitutions(base, dir1, dir2, dir3, t);
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Grid%1").arg(counter++));
}
output.append(t.getText());
};
void TemplateRenderer::drawLine(SyntopiaCore::Math::Vector3f from, SyntopiaCore::Math::Vector3f to,PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("line"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("line"+alternateID));
t.substitute("{x1}", QString::number(from.x()));
t.substitute("{y1}", QString::number(from.y()));
t.substitute("{z1}", QString::number(from.z()));
t.substitute("{x2}", QString::number(to.x()));
t.substitute("{y2}", QString::number(to.y()));
t.substitute("{z2}", QString::number(to.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Line%1").arg(counter++));
}
output.append(t.getText());
};
void TemplateRenderer::drawDot(SyntopiaCore::Math::Vector3f v,PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("dot"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("dot"+alternateID));
t.substitute("{x}", QString::number(v.x()));
t.substitute("{y}", QString::number(v.y()));
t.substitute("{z}", QString::number(v.z()));
t.substitute("{r}", QString::number(rgb.x()));
t.substitute("{g}", QString::number(rgb.y()));
t.substitute("{b}", QString::number(rgb.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Dot%1").arg(counter++));
}
output.append(t.getText());
};
void TemplateRenderer::drawSphere(SyntopiaCore::Math::Vector3f center, float radius,PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("sphere"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("sphere"+alternateID));
t.substitute("{cx}", QString::number(center.x()));
t.substitute("{cy}", QString::number(center.y()));
t.substitute("{cz}", QString::number(center.z()));
t.substitute("{rad}", QString::number(radius));
t.substitute("{r}", QString::number(rgb.x()));
t.substitute("{g}", QString::number(rgb.y()));
t.substitute("{b}", QString::number(rgb.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Sphere%1").arg(counter++));
}
output.append(t.getText());
};
void TemplateRenderer::begin() {
if (!assertPrimitiveExists("begin")) return;
TemplatePrimitive t(workingTemplate.get("begin"));
doBeginEndSubstitutions(t);
output.append(t.getText());
};
void TemplateRenderer::end() {
if (!assertPrimitiveExists("end")) return;
TemplatePrimitive t(workingTemplate.get("end"));
doBeginEndSubstitutions(t);
output.append(t.getText());
};
void TemplateRenderer::callGeneric(PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("template"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("template"+alternateID));
output.append(t.getText());
}
void TemplateRenderer::setBackgroundColor(SyntopiaCore::Math::Vector3f rgb) {
backRgb = rgb;
}
void TemplateRenderer::drawMesh( SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1,
SyntopiaCore::Math::Vector3f endDir2,
PrimitiveClass* classID) {
QString alternateID = ((classID->name).isEmpty() ? "" : "::" + (classID->name));
if (!assertPrimitiveExists("mesh"+alternateID)) return;
TemplatePrimitive t(workingTemplate.get("mesh"));
if (t.contains("{uid}")) {
t.substitute("{uid}", QString("Box%1").arg(counter++));
}
// TODO: This really isn't a matrix, we need to find a better way to export the mesh.
if (t.contains("{matrix}")) {
QString mat = QString("%1 %2 %3 0 %4 %5 %6 0 %7 %8 %9 0 %10 %11 %12 0 %13 %14 %15 0 %16 %17 %18 1")
.arg(startBase.x()).arg(startBase.y()).arg(startBase.z())
.arg(startDir1.x()).arg(startDir1.y()).arg(startDir1.z())
.arg(startDir2.x()).arg(startDir2.y()).arg(startDir2.z())
.arg(endBase.x()).arg(endBase.y()).arg(endBase.z())
.arg(endDir1.x()).arg(endDir1.y()).arg(endDir1.z())
.arg(endDir2.x()).arg(endDir2.y()).arg(endDir2.z());
t.substitute("{matrix}", mat);
}
t.substitute("{r}", QString::number(rgb.x()));
t.substitute("{g}", QString::number(rgb.y()));
t.substitute("{b}", QString::number(rgb.z()));
t.substitute("{alpha}", QString::number(alpha));
t.substitute("{oneminusalpha}", QString::number(1-alpha));
output.append(t.getText());
};
void TemplateRenderer::callCommand(const QString& renderClass, const QString& /*command*/) {
if (renderClass != this->renderClass()) return;
}
QString TemplateRenderer::getOutput() {
QString out = output.join("");
// Normalize output (seems the '\n' converts to CR+LF on windows while saving
// whereas '\r\n' converts to CR+CR+LF? so we remove the \r's).
out = out.replace("\r","");
return out;
}
}
}
}

View File

@ -1,187 +0,0 @@
#pragma once
#include <QString>
#include <QMap>
#include <QSet>
#include <QStringList>
#include <QFile>
#include <QDomDocument>
#include "Renderer.h"
#include "../../../SyntopiaCore/Math/Vector3.h"
namespace StructureSynth {
namespace Model {
namespace Rendering {
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::GLEngine;
/// A TemplatePrimitive is the definition for a single primitive (like Box or Sphere).
/// It is a simple text string with placeholders for stuff like coordinates and color.
class TemplatePrimitive {
public:
TemplatePrimitive() {};
TemplatePrimitive(QString def) : def(def) {};
TemplatePrimitive(const TemplatePrimitive& t) { this->def = t.def; };
QString getText() { return def; }
void substitute(QString before, QString after) {
def.replace(before, after);
};
bool contains(QString input) {
return def.contains(input);
};
private:
QString def;
};
// A Template contains a number of TemplatePrimitives:
// text definitions for each of the standard primitives (box, sphere, ...) with placeholders
// for stuff like coordinates and color.
class Template {
public:
Template() {};
Template(QFile& file) { read(file); }
Template(QString xmlString) { read(xmlString); }
void read(QFile& file);
void read(QString xmlString);
void parse(QDomDocument& doc);
QMap<QString, TemplatePrimitive>& getPrimitives() { return primitives; }
TemplatePrimitive get(QString name) { return primitives[name]; }
QString getDescription() { return description; }
QString getFullText() { return fullText; }
QString getName() { return name; }
QString getDefaultExtension() { return defaultExtension; }
QString getRunAfter() { return runAfter; }
private:
QMap<QString, TemplatePrimitive> primitives;
QString description;
QString name;
QString defaultExtension;
QString fullText;
QString runAfter;
};
/// A renderer implementation based on the SyntopiaCore POV widget.
class TemplateRenderer : public Renderer {
public:
TemplateRenderer();
TemplateRenderer(QString xmlDefinitionFile);
TemplateRenderer(Template myTemplate);
virtual ~TemplateRenderer();
virtual QString renderClass() { return "template"; }
/// The primitives
virtual void drawBox(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID);
virtual void drawSphere(SyntopiaCore::Math::Vector3f center, float radius,
PrimitiveClass* classID);
virtual void drawMesh( SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1,
SyntopiaCore::Math::Vector3f endDir2,
PrimitiveClass* classID);
virtual void drawGrid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
PrimitiveClass* classID);
virtual void drawLine(SyntopiaCore::Math::Vector3f from,
SyntopiaCore::Math::Vector3f to,
PrimitiveClass* classID);
virtual void drawDot(SyntopiaCore::Math::Vector3f pos,
PrimitiveClass* classID);
virtual void drawTriangle(SyntopiaCore::Math::Vector3f p1,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3,
PrimitiveClass* classID);
virtual void callGeneric(PrimitiveClass* classID);
virtual void begin();
virtual void end();
virtual void setColor(SyntopiaCore::Math::Vector3f rgb) { this->rgb = rgb; }
virtual void setBackgroundColor(SyntopiaCore::Math::Vector3f rgb);
virtual void setAlpha(double alpha) { this->alpha = alpha; }
virtual void setPreviousColor(SyntopiaCore::Math::Vector3f rgb) { this->oldRgb = rgb; }
virtual void setPreviousAlpha(double alpha) { this->oldAlpha = alpha; }
QString getOutput() ;
// Issues a command for a specific renderclass such as 'template' or 'opengl'
virtual void callCommand(const QString& renderClass, const QString& command);
bool assertPrimitiveExists(QString templateName);
void setCamera(Vector3f cameraPosition, Vector3f cameraUp, Vector3f cameraRight, Vector3f cameraTarget, int width, int height, double aspect, double fov) {
this->cameraPosition = cameraPosition;
this->cameraUp = cameraUp;
this->cameraRight = cameraRight;
this->cameraTarget = cameraTarget;
this->width = width;
this->height = height;
this->aspect = aspect;
this->fov = fov;
}
void doBeginEndSubstitutions(TemplatePrimitive& t);
void doStandardSubstitutions(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3,
TemplatePrimitive& t);
private:
SyntopiaCore::Math::Vector3f cameraPosition;
SyntopiaCore::Math::Vector3f cameraUp;
SyntopiaCore::Math::Vector3f cameraRight;
SyntopiaCore::Math::Vector3f cameraTarget;
SyntopiaCore::Math::Vector3f rgb;
SyntopiaCore::Math::Vector3f backRgb;
double alpha;
Template workingTemplate;
QStringList output;
int counter;
int width;
int height;
double aspect;
double fov;
QSet<QString> missingTypes;
SyntopiaCore::Math::Vector3f oldRgb;
double oldAlpha;
};
}
}
}

View File

@ -1,8 +0,0 @@
#include "Rule.h"
namespace StructureSynth {
namespace Model {
}
}

View File

@ -1,40 +0,0 @@
#pragma once
#include "Rendering/Renderer.h"
#include "State.h"
namespace StructureSynth {
namespace Model {
class RuleRef; // forward decl.
class Builder; // forward decl.
/// (Abstract) Base class for rules.
class Rule {
public:
/// Every rule must have a name.
Rule(QString name) : name(name) { maxDepth = -1; };
Rule() { maxDepth = -1; };
virtual ~Rule() {};
QString getName() const { return name; }
/// When applied the rule will add new pending rules to the ExecutionStack for execution.
/// Only PrimitiveRules will make use of the renderer.
virtual void apply(Builder* builder) const = 0;
/// Returns a list over rules that this rule references.
virtual QList<RuleRef*> getRuleRefs() const { return QList<RuleRef*>(); }
virtual void setMaxDepth(int maxDepth) { this->maxDepth = maxDepth; }
virtual int getMaxDepth() const { return maxDepth; }
protected:
QString name;
int maxDepth;
};
}
}

View File

@ -1,8 +0,0 @@
#include "Rule.h"
namespace StructureSynth {
namespace Model {
}
}

View File

@ -1,29 +0,0 @@
#pragma once
#include "Rule.h"
namespace StructureSynth {
namespace Model {
/// A RuleRef holds a pointer to a rule.
/// Its is a placeholder, since rule are parsed as symbolic references,
/// and need to be resolved into actual pointers after the complete parsing of the script.
class RuleRef {
public:
RuleRef(QString namedReference) : reference(namedReference) { rulePtr = 0; };
~RuleRef() {};
Rule* rule() { return rulePtr; }
QString getReference() const { return reference; }
void setRef(Rule* rule) { rulePtr = rule; }
private:
Rule* rulePtr;
QString reference;
};
}
}

View File

@ -1,258 +0,0 @@
#include "RuleSet.h"
#include "RuleRef.h"
#include "CustomRule.h"
#include "AmbiguousRule.h"
#include "PrimitiveRule.h"
#include <QStringList>
#include <QMap>
#include <typeinfo>
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../../SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Math;
namespace StructureSynth {
namespace Model {
/// Constructor. Automatically adds built-in rules.
RuleSet::RuleSet() {
topLevelRule = new CustomRule("TopLevelRule");
recurseDepth = false;
defaultClass = new PrimitiveClass();
/// Add instances of predefined built-in types.
rules.append(new PrimitiveRule(PrimitiveRule::Box,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Sphere,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Cylinder,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Mesh,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Line,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Dot,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Grid,defaultClass));
rules.append(new PrimitiveRule(PrimitiveRule::Template,defaultClass));
rules.append(topLevelRule);
};
void RuleSet::setRulesMaxDepth(int maxDepth) {
for (int i = 0; i < rules.size(); i++) {
int md = rules[i]->getMaxDepth();
//INFO(QString("Rule: %1, %2 -> %3").arg(rules[i]->getName()).arg(md).arg(maxDepth));
if (md <= 0) rules[i]->setMaxDepth(maxDepth);
}
}
/// Deletes rules
RuleSet::~RuleSet() {
for (int i = 0; i < rules.size(); i++) delete(rules[i]);
//for (int i = 0; i < primitiveClasses.size(); i++) delete(primitiveClasses[i]);
}
void RuleSet::addRule(Rule* rule) {
// Check if the rule name is already used...
QString name = rule->getName();
for (int i = 0; i < rules.size(); i++) {
if (rules[i]->getName() == name) {
if (typeid(*rules[i]) == typeid(CustomRule)) {
// A Custom rule already exists with the same name.
// Now we must remove the existing rule, and create a new ambiguous rule hosting them both.
Rule* r = rules[i];
rules.removeAll(r);
CustomRule* cr1 = dynamic_cast<CustomRule*>(r);
AmbiguousRule* ar = new AmbiguousRule(name);
ar->appendRule(cr1);
CustomRule* cr2 = dynamic_cast<CustomRule*>(rule);
if (!cr2) throw Exception("Trying to add non-custom rule to ambiguous rule: '%1'. "+name);
ar->appendRule(cr2);
rules.append(ar);
return;
} else if (typeid(*rules[i]) == typeid(PrimitiveRule)) {
// A primitive rule already exists with the same name. This is not acceptable.
throw Exception(QString("A primitive rule already exists with the name: '%1'. New definitions can not merged.").arg(name));
} else if (typeid(*rules[i]) == typeid(AmbiguousRule)) {
// A ambiguous rule already exists with the same name. We will add to it.
AmbiguousRule* ar = dynamic_cast<AmbiguousRule*>(rules[i]);
CustomRule* cr = dynamic_cast<CustomRule*>(rule);
if (!cr) throw Exception("Trying to add non-custom rule to ambiguous rule: '%1'. "+name);
ar->appendRule(cr);
return;
} else {
WARNING("Unknown typeid");
}
}
}
rules.append(rule);
}
/// Resolve symbolic names into pointers
QStringList RuleSet::resolveNames() {
// build map
QMap<QString, Rule*> map;
for (int i = 0; i < rules.size(); i++) map[rules[i]->getName()] = rules[i];
QStringList usedPrimitives;
// resolve rules.
for (int i = 0; i < rules.size(); i++) {
QList<RuleRef*> refs = rules[i]->getRuleRefs();
for (int j = 0; j < refs.size(); j++) {
QString name = refs[j]->getReference();
if (!map.contains(name)) {
// We could not resolve the name.
// Check if it has a class specifier.
QStringList sl = name.split("::");
if (sl.size() == 2) {
QString baseName = sl[0];
QString classID = sl[1];
if (!map.contains(baseName)) {
throw Exception(QString("Unable to resolve base rule name: %1 for rule %2").arg(baseName).arg(name));
}
// Now we have to create a new instance of this rule.
Rule* r = map[baseName];
if (typeid(*r) != typeid(PrimitiveRule)) {
throw Exception(QString("Only primitive rules (box, sphere, ...) may have a class specifier: %1 is invalid").arg(name));
}
PrimitiveRule* pr = (PrimitiveRule*)r;
PrimitiveRule* newRule = new PrimitiveRule(*pr);
newRule->setClass(getPrimitiveClass(classID));
map[name] = newRule;
//INFO("Created new class for rule: " + name);
} else {
// The Polygons rules (i.e. Triangle[x,y,z]) are special rules, each created on the fly.
QRegExp r("triangle\\[(.*)\\]");
if (r.exactMatch(name)) {
// Check the arguments.
INFO("Found:" + r.cap(1));
QVector<Vector3f> v;
QStringList l = r.cap(1).split(";");
if (l.size() != 3) {
throw Exception(QString("Unable to parse Triangle definition - must be triangle(p1;p2;p3) - found : %1").arg(name));
}
for (unsigned int i = 0; i < 3; i++) {
QStringList l2 = l[i].split(",");
if (l2.size() != 3) {
throw Exception(QString("Unable to parse Triangle definition - coordinates must be like '0.1,0.2,0.3' - found : %1").arg(l[i]));
}
bool ok = false;
float f1 = l2[0].toFloat(&ok);
if (!ok) throw Exception(QString("Unable to parse Triangle definition - error in first coordinate - found in : %1").arg(name));
float f2 = l2[1].toFloat(&ok);
if (!ok) throw Exception(QString("Unable to parse Triangle definition - error in second coordinate - found in : %1").arg(name));
float f3 = l2[2].toFloat(&ok);
if (!ok) throw Exception(QString("Unable to parse Triangle definition - error in third coordinate - found in : %1").arg(name));
v.append(Vector3f(f1,f2,f3));
}
map[name] = new TriangleRule(v[0], v[1], v[2], defaultClass);
} else {
throw Exception(QString("Unable to resolve rule: %1").arg(name));
}
}
}
if ( dynamic_cast<PrimitiveRule*>(map[name]) ) {
if (!usedPrimitives.contains(name)) usedPrimitives.append(name);
}
refs[j]->setRef(map[name]);
}
}
return usedPrimitives;
}
///
QStringList RuleSet::getUnreferencedNames() {
WARNING("RuleSet::getUnreferencedNames(): Not implemented yet!");
return QStringList();
};
Rule* RuleSet::getStartRule() const {
return topLevelRule;
};
/// For debug
void RuleSet::dumpInfo() const {
int custom = 0;
int ambi = 0;
int primitive = 0;
int rulesCount = 0;
for (int i = 0; i < rules.size(); i++) {
rulesCount++;
CustomRule* cr = dynamic_cast<CustomRule*>(rules[i]);
if (cr) custom++;
AmbiguousRule* ar = dynamic_cast<AmbiguousRule*>(rules[i]);
if (ar) ambi++;
PrimitiveRule* pr = dynamic_cast<PrimitiveRule*>(rules[i]);
if (pr) primitive++;
}
/*
Debug(QString("Loaded %1 user rules: %2 Custom Rules, %3 Ambiguous Rules")
.arg(rulesCount-primitive).arg(custom).arg(ambi));
Debug(QString("Loaded %1 built-in rules.").arg(primitive));
*/
}
bool RuleSet::existsPrimitiveClass(QString classLabel) {
for (int i = 0; i < primitiveClasses.count(); i++) {
if (primitiveClasses[i]->name == classLabel) return true;
}
return false;
}
PrimitiveClass* RuleSet::getPrimitiveClass(QString classLabel) {
for (int i = 0; i < primitiveClasses.count(); i++) {
if (primitiveClasses[i]->name == classLabel) return primitiveClasses[i];
}
PrimitiveClass* p = new PrimitiveClass(*defaultClass);
p->name = classLabel;
//INFO("Created new primitiveClass: " + classLabel);
primitiveClasses.append(p);
return p;
}
}
}

View File

@ -1,59 +0,0 @@
#pragma once
#include "Rule.h"
#include "PrimitiveClass.h"
#include "CustomRule.h"
#include "../../SyntopiaCore/GLEngine/Object3D.h"
namespace StructureSynth {
namespace Model {
using namespace SyntopiaCore::GLEngine;
/// Container for all rules.
class RuleSet {
public:
/// Constructor. Automatically adds built-in rules.
RuleSet();
/// Deletes rules
~RuleSet();
/// Added rules belong to the RuleSet and will be deleted by the RuleSet destructor.
void addRule(Rule* rule);
/// Resolve symbolic names into pointers
/// Returns a list of the primitives used
QStringList resolveNames();
/// TODO: Implement
QStringList getUnreferencedNames();
Rule* getStartRule() const ;
CustomRule* getTopLevelRule() const { return topLevelRule; }
/// For debug
void dumpInfo() const;
void setRecurseDepthFirst(bool value) { recurseDepth = value; };
bool recurseDepthFirst() { return recurseDepth; }
void setRulesMaxDepth(int maxDepth);
/// Returns the PrimitiveClass with this name. Constructs a new one if missing.
PrimitiveClass* getPrimitiveClass(QString classLabel);
bool existsPrimitiveClass(QString classLabel);
PrimitiveClass* getDefaultClass() { return defaultClass; }
private:
QList<Rule*> rules;
QVector<PrimitiveClass*> primitiveClasses;
PrimitiveClass* defaultClass;
CustomRule* topLevelRule;
bool recurseDepth;
};
}
}

View File

@ -1,62 +0,0 @@
#include "State.h"
namespace StructureSynth {
namespace Model {
State::State() :
matrix(SyntopiaCore::Math::Matrix4f::Identity()),
hsv(SyntopiaCore::Math::Vector3f(0,1.0f,1.0f)),
alpha(1.0f), previous(0), seed(0) {
}
State& State::operator=(const State& rhs){
this->matrix = rhs.matrix;
this->hsv = rhs.hsv;
this->alpha = rhs.alpha;
this->maxDepths = rhs.maxDepths;
this->seed = rhs.seed;
if (rhs.previous) {
delete(this->previous);
this->previous = new PreviousState();
*(this->previous) = *rhs.previous;
} else {
delete(this->previous);
this->previous = 0;
}
return *this;
}
void State::setPreviousState(SyntopiaCore::Math::Matrix4f matrix,SyntopiaCore::Math::Vector3f hsv, float alpha) {
if (previous) {delete (previous); }
this->previous = new PreviousState();
this->previous->matrix = matrix;
this->previous->hsv = hsv;
this->previous->alpha = alpha;
}
State::State(const State& rhs) : matrix(rhs.matrix),
hsv(rhs.hsv),
alpha(rhs.alpha), maxDepths(rhs.maxDepths), previous(0), seed(rhs.seed) {
if (rhs.previous) {
delete(this->previous);
this->previous = new PreviousState();
*(this->previous) = *rhs.previous;
} else {
delete(this->previous);
this->previous = 0;
}
}
State::~State() {
delete(previous);
}
}
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <QString>
#include <QMap>
#include "../../SyntopiaCore/Math/Matrix4.h"
namespace StructureSynth {
namespace Model {
class Rule; // Forward
// A slight trimmed version of a State.
struct PreviousState {
SyntopiaCore::Math::Matrix4f matrix; // Transformation matrix (4x4 homogenous representation)
SyntopiaCore::Math::Vector3f hsv; // Hue, Saturation, Value colorspace state
float alpha; // Transparency
};
/// A state represent the current rendering projection matrix and other rendering settings.
struct State {
State();
State(const State& rhs);
~State();
State& operator=(const State& rhs);
void setPreviousState(SyntopiaCore::Math::Matrix4f matrix,SyntopiaCore::Math::Vector3f hsv,float alpha);
SyntopiaCore::Math::Matrix4f matrix; // Transformation matrix (4x4 homogenous representation)
SyntopiaCore::Math::Vector3f hsv; // Hue, Saturation, Value colorspace state
float alpha; // Transparency
QMap<const Rule*, int> maxDepths; // Rules may have a max. recursion depth before they are retired.
// We need to keep track of this in the state.
PreviousState* previous;
int seed;
};
}
}

View File

@ -1,269 +0,0 @@
#include "Transformation.h"
#include "ColorPool.h"
#include "../../SyntopiaCore/Math/Matrix4.h"
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include <QColor>
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Math;
namespace StructureSynth {
namespace Model {
Transformation::Transformation() {
matrix = Matrix4f::Identity();
deltaH = 0;
scaleS = 1;
scaleV = 1;
scaleAlpha = 1;
absoluteColor = false;
strength = 0;
}
Transformation::~Transformation() {
};
State Transformation::apply(const State& s, ColorPool* colorPool) const {
State s2(s);
s2.matrix = s.matrix*matrix;
if (absoluteColor) {
// if the absolute hue is larger than 360, we will choose a random color.
if (deltaH > 360) {
QColor c = colorPool->drawColor();
s2.hsv = Vector3f(c.hue(), c.saturation()/255.0, c.value()/255.0);
s2.alpha = 1.0;
} else {
s2.hsv = Vector3f(deltaH,scaleS,scaleV);
s2.alpha = scaleAlpha;
}
} else {
float h = s2.hsv[0] + deltaH;
float sat = s2.hsv[1]*scaleS;
float v = s2.hsv[2]*scaleV;
float a = s2.alpha * scaleAlpha;
if (sat<0) sat=0;
if (v<0) v=0;
if (a<0) a=0;
if (sat>1) sat=1;
if (v>1) v=1;
if (a>1) a=1;
while (h>360) h-=360;
while (h<0) h+=360;
s2.hsv = Vector3f(h,sat,v);
s2.alpha = a;
}
if (strength) {
/*
// We will blend the two colors (in RGB space)
QColor original = QColor::fromHsv((int)(s2.hsv[0]),(int)(s2.hsv[1]*255.0),(int)(s2.hsv[2]*255.0));
double r = original.red() + strength*blendColor.red();
double g = original.green() + strength*blendColor.green();
double b = original.blue() + strength*blendColor.blue();
if (r<0) r=0;
if (g<0) g=0;
if (b<0) b=0;
double max = r;
if (g>max) max = g;
if (b>max) max = b;
if (max > 255) {
r = r * 255 / max;
g = g * 255 / max;
b = b * 255 / max;
}
QColor mixed(r,g,b);
s2.hsv = Vector3f(mixed.hue(), mixed.saturation()/255.0,mixed.value()/255.0);
*/
// We will blend the two colors (in HSV space)
Vector3f bl = Vector3f(blendColor.hue(), blendColor.saturation()/255.0,blendColor.value()/255.0);
Vector3f b(s2.hsv[0]+strength*bl[0], s2.hsv[1]+strength*bl[1], s2.hsv[2]+strength*bl[2]);
b = b/(1+strength);
while (b[0] < 0) b[0]+= 360;
while (b[0] > 360) b[0]-= 360;
if (b[1]>1) b[1]=1;
if (b[2]>1) b[2]=1;
if (b[1]<0) b[1]=0;
if (b[2]<0) b[2]=0;
s2.hsv = b;
}
return s2;
}
void Transformation::append(const Transformation& t) {
this->matrix = this->matrix * t.matrix;
if (!(t.absoluteColor && absoluteColor)) {
if (t.absoluteColor) this->absoluteColor = true;
if (absoluteColor) this->absoluteColor = true;
this->scaleAlpha = this->scaleAlpha * t.scaleAlpha;
this->deltaH = this->deltaH + t.deltaH;
this->scaleS = this->scaleS * t.scaleS;
this->scaleV = this->scaleV * t.scaleV;
} else {
// Mix two absolute colors - this is not possible, so we will just choose one of them
this->absoluteColor = true;
this->scaleAlpha = t.scaleAlpha;
this->deltaH = t.deltaH;
this->scaleS = t.scaleS;
this->scaleV = t.scaleV;
}
if (t.strength != 0) {
this->strength = t.strength;
this->blendColor = t.blendColor;
}
}
// The predefined operators
// Translations
Transformation Transformation::createX(double offset) {
Transformation t;
t.matrix(0,3) = offset;
return t;
}
Transformation Transformation::createY(double offset) {
Transformation t;
t.matrix(1,3) = offset;
return t;
}
Transformation Transformation::createZ(double offset) {
Transformation t;
t.matrix(2,3) = offset;
return t;
}
Transformation Transformation::createPlaneReflection(SyntopiaCore::Math::Vector3f normal) {
Transformation t;
t.matrix = Matrix4f::PlaneReflection(normal);
return t;
}
// Rotations
Transformation Transformation::createRX(double angle) {
Transformation t;
t.matrix =
Matrix4f::Translation(0,0.5,0.5)*
Matrix4f::Rotation(Vector3f(1,0,0), angle)*
Matrix4f::Translation(0,-0.5,-0.5);
return t;
}
Transformation Transformation::createRY(double angle) {
Transformation t;
t.matrix =
Matrix4f::Translation(0.5,0,0.5)*
Matrix4f::Rotation(Vector3f(0,1,0), angle)*
Matrix4f::Translation(-0.5,0,-0.5)
;
return t;
}
Transformation Transformation::createRZ(double angle) {
Transformation t;
t.matrix =
Matrix4f::Translation(0.5,0.5,0)*
Matrix4f::Rotation(Vector3f(0,0,1), angle)*
Matrix4f::Translation(-0.5,-0.5,0);
return t;
}
Transformation Transformation::createHSV(float h, float s, float v, float a) {
Transformation t;
t.deltaH = h;
t.scaleAlpha = a;
t.scaleS = s;
t.scaleV = v;
return t;
}
Transformation Transformation::createColor(QString color) {
Transformation t;
if (color.toLower() != "random") {
QColor c(color);
QColor hsv = c.toHsv();
t.deltaH = hsv.hue();
t.scaleAlpha = hsv.alpha()/255.0;
t.scaleS = hsv.saturation()/255.0;
t.scaleV = hsv.value()/255.0;
t.absoluteColor = true;
} else {
t.deltaH = 1000;
t.absoluteColor = true;
}
//Debug(QString("Abs Color: %1, %2, %3, %4").arg(t.deltaH).arg(t.scaleS).arg(t.scaleV).arg(t.scaleAlpha));
return t;
}
Transformation Transformation::createBlend(QString color, double strength) {
Transformation t;
t.blendColor = QColor(color);
t.strength = strength;
return t;
}
Transformation Transformation::createScale(double x, double y, double z) {
Transformation t;
t.matrix(0,0) = x;
t.matrix(1,1) = y;
t.matrix(2,2) = z;
t.matrix =
Matrix4f::Translation(0.5,0.5,0.5)*
t.matrix*
Matrix4f::Translation(-0.5,-0.5,-0.5);
return t;
}
Transformation Transformation::createMatrix(QVector<double> vals) {
Transformation t;
t.matrix(0,0) = vals[0];
t.matrix(0,1) = vals[1];
t.matrix(0,2) = vals[2];
t.matrix(1,0) = vals[3];
t.matrix(1,1) = vals[4];
t.matrix(1,2) = vals[5];
t.matrix(2,0) = vals[6];
t.matrix(2,1) = vals[7];
t.matrix(2,2) = vals[8];
t.matrix =
Matrix4f::Translation(0.5,0.5,0.5)*
t.matrix*
Matrix4f::Translation(-0.5,-0.5,-0.5);
return t;
}
}
}

View File

@ -1,66 +0,0 @@
#pragma once
#include <QString>
#include <QColor>
#include "State.h"
#include "ColorPool.h"
#include "../../SyntopiaCore/Math/Matrix4.h"
namespace StructureSynth {
namespace Model {
class Transformation {
public:
Transformation();
~Transformation();
/// 'Applies' the transformation 'T' to this transformation.
/// (For the matrix this corresponds to matrix multiplication).
void append(const Transformation& T);
State apply(const State& s, ColorPool* colorPool) const;
// The predefined operators
// Translations
static Transformation createX(double offset);
static Transformation createY(double offset);
static Transformation createZ(double offset);
// Rotations
static Transformation createRX(double angle);
static Transformation createRY(double angle);
static Transformation createRZ(double angle);
// Plane reflection
static Transformation createPlaneReflection(SyntopiaCore::Math::Vector3f normal);
// Scaling
static Transformation createScale(double x, double y, double z);
// Free transformation
static Transformation createMatrix(QVector<double> vals);
// Color stuff
static Transformation createHSV(float h, float s, float v, float a);
static Transformation createColor(QString color);
static Transformation createBlend(QString color, double strength);
private:
// Matrix and Color transformations here.
SyntopiaCore::Math::Matrix4f matrix;
// For color alterations
float deltaH;
float scaleS;
float scaleV;
float scaleAlpha;
bool absoluteColor;
// For color blends.
QColor blendColor;
double strength;
};
}
}

View File

@ -1,8 +0,0 @@
#include "TransformationLoop.h"
namespace StructureSynth {
namespace Model {
}
}

View File

@ -1,23 +0,0 @@
#pragma once
#include "Transformation.h"
namespace StructureSynth {
namespace Model {
/// A loop is a transformation which is repeated a number of times:
/// e.g.:
/// 20 * { x 1 } R1
/// will create twenty R1 objects each succesively moved one unit in the x-directions.
struct TransformationLoop {
TransformationLoop() {};
TransformationLoop(int repetitions, Transformation transformation) : repetitions(repetitions), transformation(transformation) {};
int repetitions;
Transformation transformation;
};
}
}

View File

@ -1,353 +0,0 @@
#include "EisenParser.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../Model/CustomRule.h"
#include <QColor>
using namespace SyntopiaCore::Logging;
using namespace StructureSynth::Model;
namespace StructureSynth {
namespace Parser {
/*
EISENSCRIPT
-----------
EBNF:
-----
program = { set | rule } ;
rule = 'RULE' , rule_name, { rule_modifier } , '{', { set | action } , '}' ;
rule_modifier = { 'MAXDEPTH' integer | WEIGHT float }
action = transformation rule_ref ;
set = 'SET' , var_name , string ;
'rule_ref', 'var_name', and 'string' are text strings with a reasonable set of allowed characters.
Example:
--------
//
// C++ style multiline comments are allowed.
// A pre-processor strips comments, and imports '#include's
//
#include "../basicstuff.es"
set background #F00 // All 'SET' commands outside the scope of a rule are executed at startup.
RULE core {
r1
}
RULE r1 5 {
}
RULE r2 1 {
}
*/
EisenParser::EisenParser(Tokenizer* tokenizer) : tokenizer(tokenizer) {
recurseDepth = false;
};
EisenParser::~EisenParser() {
};
void EisenParser::getSymbol() {
symbol = tokenizer->getSymbol();
};
bool EisenParser::accept(Symbol::SymbolType st) {
if (symbol.type == st) {
getSymbol();
return true;
}
return false;
}
void EisenParser::ruleModifierList(CustomRule* customRule) {
while (symbol.type == Symbol::Operator) {
if (symbol.text == "weight") {
getSymbol();
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) {
throw (ParseError("Rule modifier 'weight' expected numerical argument. Found: " + symbol.text, symbol.pos));
}
customRule->setWeight(param);
} else if (symbol.text == "maxdepth") {
getSymbol();
int param = (int)symbol.getNumerical();
if (!symbol.isInteger || !accept(Symbol::Number)) {
throw (ParseError("Rule modifier 'maxdepth' expected integer argument. Found: " + symbol.text, symbol.pos));
}
customRule->setMaxDepth(param);
if (symbol.type == Symbol::MoreThan) {
getSymbol();
QString ruleName = symbol.text;
if (!accept(Symbol::UserString)) throw (ParseError("After maxdepth retirement operator a rule name is expected. Found: " + symbol.text, symbol.pos));
customRule->setRetirementRule(ruleName);
}
} else {
throw (ParseError("In rule modifier list: expected maxdepth or weight. Found: " + symbol.text, symbol.pos));
}
}
if (symbol.type != Symbol::LeftBracket) {
throw (ParseError("After rule modifier list: expected a left bracket. Found: " + symbol.text, symbol.pos));
}
}
Rule* EisenParser::rule() {
// rule = 'RULE' , rule_name, '{', { set | action } , '}' ;
if (!accept(Symbol::Rule)) throw (ParseError("Unexpected: trying to parse Rule not starting with rule identifier. Found: " + symbol.text, symbol.pos));
QString ruleName = symbol.text;
if (!accept(Symbol::UserString)) throw (ParseError("After rule identifier a rule name is expected. Found: " + symbol.text, symbol.pos));
CustomRule* customRule = new CustomRule(ruleName);
if (symbol.type != Symbol::LeftBracket) {
// This must be a rule_modifier list.
ruleModifierList(customRule);
}
if (!accept(Symbol::LeftBracket)) throw (ParseError("After rule name a left bracket is expected. Found: " + symbol.text, symbol.pos));
// TODO: implement rest of types:
// Possible actions:
// SET something = something
// rulename
// { yaw 20 size 0.1 } rulename
// 20 * { forward 10 } rulename
while (symbol.type == Symbol::LeftBracket || symbol.type == Symbol::UserString ||
symbol.type == Symbol::Number || symbol.type == Symbol::Set) {
if (symbol.type == Symbol::Set) {
Action a = setAction();
customRule->appendAction(a);
} else {
Action a = action();
customRule->appendAction(a);
}
}
if (!accept(Symbol::RightBracket)) throw (ParseError("A rule definition must end with a right bracket. Found: "+symbol.text, symbol.pos));
return customRule;
}
double degreeToRad(double degrees) {
return degrees*3.14159265/180.0;
}
Transformation EisenParser::transformation() {
QString type = symbol.text;
if (!accept(Symbol::Operator)) throw (ParseError("Transformation: Expected transformation identifier (e.g. 'x' or 'rx'). Found: " + symbol.text, symbol.pos));
if (type == "x") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'X' (X-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createX(param);
} else if (type == "y") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'Y' (Y-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createY(param);
} else if (type == "z") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'Z' (Z-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createZ(param);
} else if (type == "rx") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RX' (X-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createRX(degreeToRad(param));
} else if (type == "ry") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RY' (Y-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createRY(degreeToRad(param));
} else if (type == "rz") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RZ' (Z-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createRZ(degreeToRad(param));
} else if (type == "hue") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'hue': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createHSV(param, 1,1,1);
} else if (type == "sat") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'sat': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createHSV(0, param,1,1);
} else if (type == "brightness") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'brightness': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createHSV(0, 1,param,1);
} else if (type == "color") {
QString param = symbol.text;
if (!QColor(param).isValid() && param.toLower()!="random") throw (ParseError("Transformation 'color': Expected a valid color. Found: " + symbol.text, symbol.pos));
getSymbol();
return Transformation::createColor(param);
} else if (type == "blend") {
QString param = symbol.text;
if (!QColor(param).isValid()) throw (ParseError("Transformation 'blend': Expected a valid color as first argument. Found: " + symbol.text, symbol.pos));
getSymbol();
double param2 = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'blend': Expected a numerical value as second argument. Found: " + symbol.text, symbol.pos));
return Transformation::createBlend(param, param2);
} else if (type == "alpha") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'alpha': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createHSV(0, 1,1,param);
} else if (type == "matrix") {
QVector<double> ds;
for (unsigned int i = 0; i < 9; i++) {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'matrix': Expected nine (9) parameters. Found: " + symbol.text, symbol.pos));
ds.append(param);
}
return Transformation::createMatrix(ds);
} else if (type == "s") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'S' (size): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
if (symbol.type == Symbol::Number) {
double param2 = symbol.getNumerical();
getSymbol();
double param3 = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'S' (size): Expected third numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createScale(param,param2,param3);
}
return Transformation::createScale(param,param,param);
} else if (type == "reflect") {
double param = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'reflect': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
double param2 = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'reflect': Expected second numerical parameter. Found: " + symbol.text, symbol.pos));
double param3 = symbol.getNumerical();
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'reflect': Expected third numerical parameter. Found: " + symbol.text, symbol.pos));
return Transformation::createPlaneReflection(SyntopiaCore::Math::Vector3f(param,param2,param3));
} else if (type == "fx") {
return Transformation::createScale(-1,1,1);
} else if (type == "fy") {
return Transformation::createScale(1,-1,1);
} else if (type == "fz") {
return Transformation::createScale(1,1,-1);
} else {
throw (ParseError("Unknown transformation type: " + type, symbol.pos));
}
}
Transformation EisenParser::transformationList() {
// A transformationlist is something like:
// { x 23 rx 23 }
Transformation t;
if (!accept(Symbol::LeftBracket)) throw (ParseError("Transformation List: Expected a left bracket. Found: " + symbol.text, symbol.pos));
while (symbol.type == Symbol::Operator) {
t.append(transformation());
}
if (!accept(Symbol::RightBracket)) throw (ParseError("Transformation List: Expected a right bracket or an operator. Found: " + symbol.text, symbol.pos));
return t;
}
Action EisenParser::action() {
// There are 3 types of action statements:
// { rx 20 ry 30 rz 20 } rulename
// rulename
// 20 * { x 10 } 10 * { y 10 } rulename
if (symbol.type == Symbol::LeftBracket) {
Transformation t = transformationList();
QString ruleName = symbol.text.trimmed();
if (!accept(Symbol::UserString)) throw (ParseError("Expected a rule name after the transformation list. Found: " + symbol.text, symbol.pos));
return Action(t, ruleName);
} else if (symbol.type == Symbol::UserString) {
QString ruleName = symbol.text.trimmed();
accept(Symbol::UserString);
return Action(ruleName);
} else if (symbol.type == Symbol::Number) {
Action action;
while (symbol.type == Symbol::Number) {
// number of loops...
if (!symbol.isInteger) throw (ParseError("Expected an integer count in the transformation loop. Found: " + symbol.text, symbol.pos));
int count = symbol.intValue;
getSymbol();
// '*'
if (!accept(Symbol::Multiply)) throw (ParseError("Expected a '*' after the transformation count. Found: " + symbol.text, symbol.pos));
// transformation list
Transformation t = transformationList();
action.addTransformationLoop(TransformationLoop(count, t));
}
// Rule reference
QString ruleName = symbol.text.trimmed();
if (!accept(Symbol::UserString)) throw (ParseError("Expected a rule name or a new loop after the transformation list. Found: " + symbol.text, symbol.pos));
action.setRule(ruleName);
return action;
} else {
throw (ParseError("A rule action must start with either a number, a rule name or a left bracket. Found: "+symbol.text, symbol.pos));
}
}
Action EisenParser::setAction() {
accept(Symbol::Set);
QString key = symbol.text;
if (symbol.type == Symbol::Operator && key == "maxdepth") {
getSymbol();
} else if (!accept(Symbol::UserString)) throw (ParseError("Expected a valid setting name. Found: " + symbol.text, symbol.pos));
QString value = symbol.text;
getSymbol(); // We will accept everything here!
if (key == "recursion" && value == "depth") recurseDepth = true;
return Action(key,value);
}
RuleSet* EisenParser::ruleset() {
RuleSet* rs = new RuleSet();
getSymbol();
while (symbol.type == Symbol::Rule || symbol.type == Symbol::Set
|| symbol.type == Symbol::LeftBracket || symbol.type == Symbol::UserString || symbol.type == Symbol::Number) {
if (symbol.type == Symbol::Rule) {
Rule* r = rule();
rs->addRule(r);
} else if (symbol.type == Symbol::Set) {
Action a = setAction();
rs->getTopLevelRule()->appendAction(a);
} else {
Action a = action();
rs->getTopLevelRule()->appendAction(a);
}
}
if (!accept(Symbol::End)) throw (ParseError("Unexpected symbol found. At this scope only RULE and SET statements are allowed. Found: " + symbol.text, symbol.pos));
if (recurseDepth) rs->setRecurseDepthFirst(true);
return rs;
}
RuleSet* EisenParser::parseRuleset() {
return ruleset();
}
}
}

View File

@ -1,50 +0,0 @@
#pragma once
#include "Tokenizer.h"
#include "../Model/Transformation.h"
#include "../Model/Rule.h"
#include "../Model/CustomRule.h"
#include "../Model/RuleSet.h"
#include "../Model/Action.h"
namespace StructureSynth {
namespace Parser {
/// The' Eisenstein Engine' is a simple recursive descent parser,
/// for parsing 'EisenScript'.
class EisenParser {
public:
/// Constructor.
EisenParser(Tokenizer* tokenizer);
/// Destructor, The tokenizer is not deleted.
~EisenParser();
/// Parses the input, and returns the corresponding ruleset.
/// Throws a ParseError if any errors are encountered
Model::RuleSet* parseRuleset();
bool recurseDepthFirst() { return recurseDepth; }
private:
bool recurseDepth;
void getSymbol();
Model::Rule* rule();
Model::RuleSet* ruleset();
Model::Action action();
Model::Action setAction();
Model::Transformation transformationList();
Model::Transformation transformation();
void ruleModifierList(Model::CustomRule* customRule);
bool accept(Symbol::SymbolType st);
bool expect(Symbol::SymbolType st);
Symbol symbol;
Tokenizer* tokenizer;
};
}
}

View File

@ -1,149 +0,0 @@
#include "Preprocessor.h"
#include <QStringList>
#include <QRegExp>
#include <QMap>
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Logging/Logging.h"
#include "../../SyntopiaCore/Math/Random.h"
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Math;
namespace StructureSynth {
namespace Parser {
QString Preprocessor::Process(QString input, int seed) {
RandomNumberGenerator rg;
rg.setSeed(seed);
QStringList in = input.split(QRegExp("\r\n|\r|\n"));
QMap<QString, QString> substitutions;
QRegExp ppCommand("^#"); // Look for #define varname value
QRegExp defineCommand("^#define\\s([^\\s]+)\\s(.*)*$"); // Look for #define varname value
QRegExp defineFloatCommand("^#define\\s([^\\s]+)\\s(.*)\\s\\(float:([^\\s]*)\\)$"); // Look for #define varname value
QRegExp defineIntCommand("^#define\\s([^\\s]+)\\s(.*)\\s\\(int:([^\\s]*)\\)$"); // Look for #define varname value
// Match a number: [-+]?[0-9]*\.?[0-9]+
QRegExp randomNumber("random\\[([-+]?[0-9]*\\.?[0-9]+),([-+]?[0-9]*\\.?[0-9]+)\\]"); // random[-2.3,3.4]
for (QStringList::iterator it = in.begin(); it != in.end(); ++it) {
if (ppCommand.indexIn(*it) != -1) {
// Preprocessor command
if (defineFloatCommand.indexIn(*it) != -1) {
//INFO(QString("Found ppC (%1)->(%2): ").arg(defineCommandWithGUI.cap(1)).arg(defineCommandWithGUI.cap(2)) + *it);
if (defineFloatCommand.cap(2).contains(defineFloatCommand.cap(1))) {
WARNING(QString("#define command is recursive - skipped: %1 -> %2")
.arg(defineFloatCommand.cap(1))
.arg(defineFloatCommand.cap(2)));
}
//substitutions[defineCommandWithGUI.cap(1)] = defineCommandWithGUI.cap(2);
QString defaultValue = defineFloatCommand.cap(2);
QString floatInterval = defineFloatCommand.cap(3);
QStringList fi = floatInterval.split("-");
if (fi.count() != 2) {
WARNING("Could not understand #define gui command: " + floatInterval);
continue;
}
bool succes = false;
double d1 = fi[0].toDouble(&succes);
bool succes2 = false;
double d2 = fi[1].toDouble(&succes2);
if (!succes || !succes2) {
WARNING("Could not parse float interval in #define gui command: " + floatInterval);
continue;
}
bool succes3 = false;
double d3 = defineFloatCommand.cap(2).toDouble(&succes3);
if (!succes3) {
WARNING("Could not parse default argument in #define gui command: " + defineFloatCommand.cap(2));
continue;
}
FloatParameter* fp= new FloatParameter(defineFloatCommand.cap(1), d1, d2, d3);
params.append(fp);
} else if (defineIntCommand.indexIn(*it) != -1) {
INFO("INT");
if (defineIntCommand.cap(2).contains(defineIntCommand.cap(1))) {
WARNING(QString("#define command is recursive - skipped: %1 -> %2")
.arg(defineIntCommand.cap(1))
.arg(defineIntCommand.cap(2)));
}
QString defaultValue = defineIntCommand.cap(2);
QString intInterval = defineIntCommand.cap(3);
QStringList ii = intInterval.split("-");
if (ii.count() != 2) {
WARNING("Could not understand #define gui command: " + intInterval);
continue;
}
bool succes = false;
int i1 = ii[0].toInt(&succes);
bool succes2 = false;
int i2 = ii[1].toInt(&succes2);
if (!succes || !succes2) {
WARNING("Could not parse int interval in #define gui command: " + intInterval);
continue;
}
bool succes3 = false;
int i3 = defineIntCommand.cap(2).toInt(&succes3);
if (!succes3) {
WARNING("Could not parse default argument in #define gui command: " + defineIntCommand.cap(2));
continue;
}
IntParameter* fp= new IntParameter(defineIntCommand.cap(1), i1, i2, i3);
params.append(fp);
} else if (defineCommand.indexIn(*it) != -1) {
if (defineCommand.cap(2).contains(defineCommand.cap(1))) {
WARNING(QString("#define command is recursive - skipped: %1 -> %2")
.arg(defineCommand.cap(1))
.arg(defineCommand.cap(2)));
}
substitutions[defineCommand.cap(1)] = defineCommand.cap(2);
} else {
WARNING("Could not understand preprocessor command: " + *it);
}
} else {
// Non-preprocessor command
// Check for substitutions.
QMap<QString, QString>::const_iterator it2 = substitutions.constBegin();
int subst = 0;
while (it2 != substitutions.constEnd()) {
if (subst>100) {
WARNING("More than 100 recursive preprocessor substitutions... breaking.");
break;
}
if ((*it).contains(it2.key())) {
//INFO("Replacing: " + it2.key() + " with " + it2.value());
(*it).replace(it2.key(), it2.value());
it2 = substitutions.constBegin();
subst++;
} else {
it2++;
}
}
}
while (randomNumber.indexIn(*it) != -1) {
double d1 = randomNumber.cap(1).toDouble();
double d2 = randomNumber.cap(2).toDouble();
double r = rg.getDouble(d1,d2);
INFO(QString("Random number: %1 -> %2 ").arg(randomNumber.cap(0)).arg(r));
(*it).replace(randomNumber.cap(0), QString::number(r));
}
}
QStringList out = in;
return out.join("\r\n");
}
}
}

View File

@ -1,66 +0,0 @@
#pragma once
#include <QString>
#include <QList>
#include <QVector>
#include "../../SyntopiaCore/Exceptions/Exception.h"
namespace StructureSynth {
namespace Parser {
class GuiParameter {
public:
GuiParameter(QString name) : name(name) {};
virtual QString getName() { return name; }
protected:
QString name;
};
class FloatParameter : public GuiParameter {
public:
FloatParameter(QString name, double from, double to, double defaultValue) :
GuiParameter(name), from(from), to(to), defaultValue(defaultValue) {};
double getFrom() { return from; }
double getTo() { return to; }
double getDefaultValue() { return defaultValue; }
private:
double from;
double to;
double defaultValue;
};
class IntParameter : public GuiParameter {
public:
IntParameter(QString name, int from, int to, int defaultValue) :
GuiParameter(name), from(from), to(to), defaultValue(defaultValue) {};
int getFrom() { return from; }
int getTo() { return to; }
int getDefaultValue() { return defaultValue; }
private:
int from;
int to;
int defaultValue;
};
/// The preprocessor is responsible for expanding '#define'
///
class Preprocessor {
public:
Preprocessor() {};
// The preprocess replaces 'random[2,4]' statements with random numbers.
// This requires a seed. Using the same seed as controls the EisenScript is probably the best idea her.
QString Process(QString input, int seed = 0);
QVector<GuiParameter*> getParameters() { return params; }
private:
QVector<GuiParameter*> params;
};
}
}

View File

@ -1,202 +0,0 @@
#include "Tokenizer.h"
#include <QStringList>
#include "../../SyntopiaCore/Exceptions/Exception.h"
#include "../../SyntopiaCore/Logging/Logging.h"
using namespace SyntopiaCore::Exceptions;
using namespace SyntopiaCore::Logging;
namespace StructureSynth {
namespace Parser {
Tokenizer::~Tokenizer() {
}
Tokenizer::Tokenizer(QString input) {
QStringList operators;
operators << "c" << "reflect" << "color" << "blend" << "a" << "alpha" << "matrix" << "h" << "hue" << "sat" << "b" << "brightness" << "v" << "x" << "y" << "z" << "rx" << "ry" << "rz" << "s" << "fx" << "fy" << "fz" << "maxdepth" << "weight" << "md" << "w";
currentSymbol = -1;
// We will split on whitespace and line breaks
// TODO: Respect quotations ( file = "C:\Program Files\Test" )
QStringList l;
QVector<int> positions;
// We will use our own split routine
QString current;
int startPos = 0;
input += " "; // to ensure last symbol gets parsed.
bool inMultiComment = false;
bool inComment = false;
int newlines = 0;
for (int i = 0; i < input.length(); i++) {
if (input.at(i) == '\r') {
inComment = false;
newlines++;
}
// Check if we found a preprocessor comment (there must occur at the beginning of a line.
if (input.at(i) == '#' && ((i == 0) || (input.at(i-1) == '\r') || (input.at(i-1) == '\n'))) {
inComment = true; i++; continue;
}
if (i < input.length()-1) {
if (input.at(i) == '*' && input.at(i+1) == '/') {
inMultiComment = false; i++; continue;
}
if (input.at(i) == '/' && input.at(i+1) == '/') {
inComment = true; i++; continue;
}
if (input.at(i) == '/' && input.at(i+1) == '*') {
inMultiComment = true; i++; continue;
}
}
if (inMultiComment || inComment) continue;
if (input.at(i) == '[') {
while (input.at(i) != ']' && i < input.length()) {
current += input.at(i);
i++;
}
current += ']';
if (input.at(i) != ']') {
throw ParseError("No matching ']' found for '['", startPos);
}
l.append(current);
positions.append(startPos-newlines);
startPos = i;
current = "";
} else
if (input.at(i) == '{' || input.at(i) == '}' || input.at(i) == ' ' || (input.at(i) == '\r') || (input.at(i) == '\n')) {
QString trimmed = current.remove(QRegExp("\\s|\\r|\\n"));
if (!current.trimmed().isEmpty()) { l.append(trimmed); positions.append(startPos-newlines); }
if (input.at(i) == '{' || input.at(i) == '}') { l.append(QString(input.at(i))); positions.append(i-newlines); }
current = "";
startPos = i;
} else {
current += input.at(i);
}
}
for (int i = 0; i < l.size(); i++) {
QString s = l[i];
int pos = positions[i];
QString sl = l[i].toLower();
if (sl == "rule") {
symbols.append(Symbol(pos, Symbol::Rule, s));
} else if (sl == "{") {
symbols.append(Symbol(pos, Symbol::LeftBracket, s));
} else if (sl == ">") {
symbols.append(Symbol(pos, Symbol::MoreThan, s));
}else if (sl == "}") {
symbols.append(Symbol(pos, Symbol::RightBracket, s));
} else if (sl == "*") {
symbols.append(Symbol(pos, Symbol::Multiply, s));
} else if (sl == "set") {
symbols.append(Symbol(pos, Symbol::Set, s));
} else if (QString("+-0123456789").contains(s[0])) {
// It is a number (hopefully)
if (s.count("/") == 1) {
QString s1 = s.section("/",0,0);
QString s2 = s.section("/",1,1);
bool succes1 = false;
int i1 = s1.toInt(&succes1);
bool succes2 = false;
int i2 = s2.toInt(&succes2);
if ((i1 && i2) && (i2 != 0)) {
Symbol ns(pos, Symbol::Number, s);
ns.isInteger = false;
ns.floatValue = ((double)i1)/i2;
symbols.append(ns);
continue;
} else {
throw ParseError("Invalid fraction found: " + s, pos);
}
}
bool succes = false;
int i = s.toInt(&succes);
if (succes) {
Symbol ns(pos, Symbol::Number, s);
ns.isInteger = true;
ns.intValue = i;
symbols.append(ns);
continue;
}
// the number was not an integer... Is it a floating-point value?
double d = s.toDouble(&succes);
if (succes) {
Symbol ns(pos, Symbol::Number, s);
ns.isInteger = false;
ns.floatValue = d;
//INFO(QString("Added float value:%1").arg(ns.floatValue));
symbols.append(ns);
continue;
}
throw ParseError("Invalid symbol found: " + s, pos);
} else if (operators.contains(sl) ) {
QString longName = sl;
// Resolve abbreviations
if (longName == "md") longName = "maxdepth";
if (longName == "w") longName = "weight";
if (longName == "h") longName = "hue";
if (longName == "b") longName = "brightness";
if (longName == "a") longName = "alpha";
if (longName == "c") longName = "color";
Symbol ns(pos, Symbol::Operator, longName);
symbols.append(ns);
} else {
// TODO: We should check syntax of userstring here... (we dont want strings like slk"{/})
Symbol ns(pos, Symbol::UserString, sl);
symbols.append(ns);
}
}
for (int i = 0; i < symbols.size(); i++) {
//INFO(QString("%1. Symbol: %2, Position: %3").arg(i).arg(symbols[i].text).arg(positions[i]));
}
}
Symbol Tokenizer::getSymbol() {
currentSymbol++;
if (currentSymbol < symbols.size()) {
return symbols[currentSymbol];
}
return Symbol(-1, Symbol::End, "#END#");
}
}
}

View File

@ -1,65 +0,0 @@
#pragma once
#include <QString>
#include <QList>
#include "../../SyntopiaCore/Exceptions/Exception.h"
namespace StructureSynth {
namespace Parser {
class ParseError : public SyntopiaCore::Exceptions::Exception {
public:
ParseError(QString message, int position) : Exception(message), position(position) {};
int getPosition() { return position; }
private:
int position;
};
struct Symbol {
enum SymbolType { Undefined, LeftBracket, RightBracket, MoreThan, End, Number, Multiply, UserString, Rule, Set, Operator } ;
Symbol() : floatValue(0), intValue(0), isInteger(false),pos(-1), type(Undefined) { };
Symbol(int pos, SymbolType s, QString original) : text(original),floatValue(0), intValue(0),isInteger(false), pos(pos), type(s) { };
/// yes, yes, it is a bloated representation. (I don't like unions...)
QString text; // The original text-string we parsed. Notice userstrings are converted to lower-case
double floatValue;
int intValue;
bool isInteger;
int pos; // the position (char-index) of the original text parsed.
SymbolType type;
double getNumerical() {
if (isInteger) return intValue;
return floatValue;
}
};
/// The Tokenizer divides an input stream into distinct symbols,
/// for subsequent parsing.
class Tokenizer {
public:
/// Constructor.
Tokenizer(QString input);
/// Destructor
~Tokenizer();
/// Returns the next symbol
Symbol getSymbol();
private:
QList<Symbol> symbols;
int currentSymbol;
};
}
}

View File

@ -1,29 +0,0 @@
#pragma once
namespace SyntopiaCore {
namespace Exceptions {
/// A base exception class.
///
/// When using Exceptions:
/// (1) Throw temporaries (throw Exception("Error occoured");)
/// (2) Catch by reference ( try {} catch (Exception& e) {} )
///
/// (Perhaps this ought to inherit from std::exception?)
class Exception {
public:
/// Constructor.
Exception(QString message) : message(message) {};
/// Returns the error message.
QString getMessage() const { return message; }
private:
QString message;
};
}
}

View File

@ -1,164 +0,0 @@
#include "Box.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
Box::Box(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3) : base(base), v1(dir1), v2(dir2), v3(dir3)
{
/// Bounding box
from = base;
to = base;
Expand(from,to, base+v1);
Expand(from,to, base+v2);
Expand(from,to, base+v3);
Expand(from,to, base+v1+v2);
Expand(from,to, base+v2+v3);
Expand(from,to, base+v1+v3);
Expand(from,to, base+v1+v2+v3);
n21 = Vector3f::cross(v2,v1).normalized();
n32 = Vector3f::cross(v3,v2).normalized();
n13 = Vector3f::cross(v1,v3).normalized();
ac = base + v1*0.5 + v2*0.5 + v3*0.5;
a[0] = v1.normalized();
a[1] = v2.normalized();
a[2] = v3.normalized();
h[0] = v1.length()/2;
h[1] = v2.length()/2;
h[2] = v3.length()/2;
};
Box::~Box() { };
void Box::draw() const {
glPushMatrix();
glTranslatef( base.x(), base.y(), base.z() );
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, primaryColor );
glBegin( GL_QUADS );
Vector3f O(0,0,0);
vertex4n(O, v2,v2+v1,v1);
vertex4rn(O+v3, v2+v3, v2+v1+v3, v1+v3);
vertex4n(O, v3, v3+v2,v2);
vertex4rn(O+v1, v3+v1, v3+v2+v1, v2+v1);
vertex4n(O, v1, v3+v1, v3);
vertex4rn(O+v2, v1+v2, v3+v2+v1, v3+v2);
glEnd();
glPopMatrix();
};
void Box::prepareForRaytracing() {
// Determine whether the box is skewed.
// if this is the case, we triangulate it.
// Using triangles for ray checks is much slower (x ~2.4 slower),
// and we could probably avoid it by transforming both box and rayinfo
// into a non-skewed coordinate system.
const double treshold = 1E-4;
useTriangles = false;
if (fabs(Vector3f::dot(v1,v2)) > treshold) useTriangles = true;
if (fabs(Vector3f::dot(v2,v3)) > treshold) useTriangles = true;
if (fabs(Vector3f::dot(v1,v3)) > treshold) useTriangles = true;
if (useTriangles) {
triangles.clear();
RaytraceTriangle::Vertex4(base, base+v1,base+v3+v1,base+v3, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(base, base+v3,base+v3+v2,base+v2, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(base+v1, base+v1+v2, base+v3+v1+v2, base+v3+v1, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(base+v2, base+v3+v2, base+v3+v1+v2, base+v1+v2, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(base, base+v2,base+v1+v2,base+v1, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(base+v3, base+v3+v1,base+v3+v1+v2, base+v3+v2, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
}
}
bool Box::intersectsRay(RayInfo* ri) {
if (useTriangles) {
for (int i = 0; i < triangles.count(); i++) {
if (triangles[i].intersectsRay(ri)) return true;
}
return false;
} else {
// Following the Real-Time Rendering book p. 574
float tmin = -1.0E37f;
float tmax = 1.0E37f;
float temp = 0;
Vector3f p = ac - ri->startPoint;
int minhit = 0;
int maxhit = 0;
for (int i = 0; i < 3; i++) {
bool reverse = false;
float e = Vector3f::dot(a[i], p);
float f = Vector3f::dot(a[i], ri->lineDirection);
if (fabs(f)>1E-17) {
float t1 = (e+h[i])/f;
float t2 = (e-h[i])/f;
if (t1 > t2) { temp = t1; t1 = t2; t2 = temp; reverse = true; }
if (t1 > tmin) {
tmin = t1;
minhit = reverse ? (i+1) : -i;
}
if (t2 < tmax) {
tmax = t2;
maxhit = reverse ? (i+1) : -i;
}
if (tmin > tmax) return false;
if (tmax < 0) return false;
} else {
if ( (-e-h[i] > 0) || (-e+h[i]<0)) return false;
}
}
if (tmin>0) {
ri->intersection = tmin;
for (int i = 0; i < 4; i++) ri->color[i] = primaryColor[i];
// TODO: Find a better solution or at least make a switch...
if (minhit == 1) { ri->normal = n32; }
else if (minhit == 2) { ri->normal = n13; }
else if (minhit == 3) { ri->normal = n21; }
else if (minhit == 0) { ri->normal = -n32; }
else if (minhit == -1) { ri->normal = -n13; }
else if (minhit == -2) { ri->normal = -n21; }
return true;
} else {
ri->intersection = tmax;
for (int i = 0; i < 4; i++) ri->color[i] = primaryColor[i];
if (maxhit == 0) {ri->normal = n32; ri->color[2] = 1; }
else if (maxhit == 1){ ri->normal = n13; ri->color[2] = 1; }
else { ri->normal = n21; ri->color[2] = 1; }
ri->color[0] = 0; ri->color[1] = 1; ri->color[2] = 1;
return true;
}
}
}
bool Box::intersectsAABB(SyntopiaCore::Math::Vector3f from2, SyntopiaCore::Math::Vector3f to2) {
return
(from.x() < to2.x()) && (to.x() > from2.x()) &&
(from.y() < to2.y()) && (to.y() > from2.y()) &&
(from.z() < to2.z()) && (to.z() > from2.z());
}
}
}

View File

@ -1,48 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
#include "RaytraceTriangle.h"
namespace SyntopiaCore {
namespace GLEngine {
class Box : public Object3D {
public:
Box(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3);
virtual ~Box();
virtual QString name() { return "Box"; }
virtual void draw() const;
virtual bool intersectsRay(RayInfo* /*rayInfo*/);
virtual bool intersectsAABB(SyntopiaCore::Math::Vector3f /*from*/, SyntopiaCore::Math::Vector3f /*to*/);
virtual void prepareForRaytracing();
private:
bool useTriangles;
QVector<RaytraceTriangle> triangles;
SyntopiaCore::Math::Vector3f base;
SyntopiaCore::Math::Vector3f v1;
SyntopiaCore::Math::Vector3f v2;
SyntopiaCore::Math::Vector3f v3;
SyntopiaCore::Math::Vector3f n21 ;
SyntopiaCore::Math::Vector3f n32;
SyntopiaCore::Math::Vector3f n13;
SyntopiaCore::Math::Vector3f ac;
SyntopiaCore::Math::Vector3f a[3];
float h[3];
};
}
}

View File

@ -1,32 +0,0 @@
#include "Dot.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
Dot::Dot(SyntopiaCore::Math::Vector3f pos) : pos(pos)
{
/// Bounding box
from = pos;
to = pos;
};
Dot::~Dot() { };
void Dot::draw() const {
glDisable (GL_LIGHTING);
glColor4fv(primaryColor);
glBegin(GL_POINTS);
glVertex3f(pos.x(), pos.y(), pos.z());
glEnd();
glEnable (GL_LIGHTING);
};
}
}

View File

@ -1,25 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
class Dot : public Object3D {
public:
Dot(SyntopiaCore::Math::Vector3f pos);
virtual QString name() { return "Dot"; }
virtual ~Dot();
virtual void draw() const;
private:
SyntopiaCore::Math::Vector3f pos;
};
}
}

View File

@ -1,658 +0,0 @@
#include "EngineWidget.h"
#include "../Math/Vector3.h"
#include "../Math/Random.h"
#include "../Logging/Logging.h"
#include "Sphere.h"
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::Logging;
#include <QWheelEvent>
namespace SyntopiaCore {
namespace GLEngine {
EngineWidget::EngineWidget(QMainWindow* mainWindow, QWidget* parent)
: QGLWidget(parent), mainWindow(mainWindow)
{
showDepth = false;
disabled = false;
updatePerspective();
showCoordinateSystem = false;
pendingRedraws = 0;
requiredRedraws = 2; // for double buffering
startTimer( 10 );
oldPos = QPoint(0,0);
// Engine settings
scale = 0.4;
minimumScale = 0.01;
mouseSpeed = 1.0;
mouseTranslationSpeed = 64.0;
translation = Vector3f(0,0,-20);
setMouseTracking(true);
rotation = Matrix4f::Identity();
pivot = Vector3f(0,0,0);
backgroundColor = QColor(30,30,30);
contextMenu = 0;
rmbDragging = false;
fastRotate = false;
doingRotate = false;
shaderProgram = 0;
//setupFragmentShader();
}
QImage EngineWidget::getScreenShot() {
if (!staticImage.isNull()) return staticImage;
return grabFrameBuffer();
}
void EngineWidget::paintEvent(QPaintEvent * ev) {
if (staticImage.isNull()) {
QGLWidget::paintEvent(ev);
} else {
setAutoFillBackground(false);
glDisable( GL_CULL_FACE );
glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST );
glViewport(0,0,width(),height());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,100,0,100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
QPainter p;
p.begin(this);
p.drawImage(QPoint(0,0), staticImage);
p.end();
ev->accept();
pendingRedraws = 0;
}
}
void EngineWidget::setFastRotate(bool enabled) {
fastRotate = enabled;
}
void EngineWidget::setImage(QImage im) {
staticImage = im;
if (im.isNull()) {
glEnable( GL_CULL_FACE );
glEnable( GL_LIGHTING );
glEnable( GL_DEPTH_TEST );
updatePerspective();
initializeGL();
} else {
update();
}
}
EngineWidget::~EngineWidget() {
}
void EngineWidget::mouseReleaseEvent(QMouseEvent* ev) {
doingRotate = false;
if (ev->button() != Qt::RightButton) return;
if (rmbDragging) { return; }
if (contextMenu) contextMenu->exec(ev->globalPos());
}
void EngineWidget::contextMenuEvent(QContextMenuEvent* /*ev*/ ) {
// Implementing this here gives trouble on Linux...
// if (rmbDragging) { return; }
// if (contextMenu) contextMenu->exec(ev->globalPos());
}
void EngineWidget::reset() {
translation = Vector3f(0,0,-20);
rotation = Matrix4f::Identity();
pivot = Vector3f(0,0,0);
scale = 0.4f;
settings = Settings();
updatePerspective();
//backgroundColor = QColor(0,0,0);
requireRedraw();
if (shaderProgram) {
shaderProgram->release();
setupFragmentShader();
}
}
void EngineWidget::requireRedraw() {
if (!staticImage.isNull()) setImage(QImage());
pendingRedraws = requiredRedraws;
}
void vertexm(SyntopiaCore::Math::Vector3f v) { glVertex3f(v.x(), v.y(), v.z()); }
void EngineWidget::setupFragmentShader() {
// As of now, these are not used...
shaderProgram = new QGLShaderProgram(this);
bool s = shaderProgram->addShaderFromSourceCode(QGLShader::Vertex,
"varying vec2 coord;\n"
"void main(void)\n"
"{\n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;;\n"
" coord = vec2(gl_Position.x,gl_Position.y);\n "
"}");
if (!s) WARNING("Could not create vertex shader: " + shaderProgram->log());
s = shaderProgram->addShaderFromSourceCode(QGLShader::Fragment,
"uniform mediump vec4 color;\n"
"varying vec2 coord;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = vec4(1.0,coord.x,coord.y, 1.0);\n"
" if (coord.y<0.5) gl_FragColor = vec4(1.0,1.0,1.0, 1.0); "
"}");
if (!s) WARNING("Could not create vertex shader: " + shaderProgram->log());
s = shaderProgram->link();
if (!s) WARNING("Could not link shaders: " + shaderProgram->log());
s = shaderProgram->bind();
if (!s) WARNING("Could not bind shaders: " + shaderProgram->log());
INFO("Shader setup complete!");
}
double EngineWidget::getDepthAt(int x,int y) {
RayInfo ri;
Vector3f front = screenTo3D(x, y, 0);
Vector3f back = screenTo3D(x, y, 1);
ri.startPoint = front;
ri.lineDirection = back - front;
double dist = -1;
Object3D* obj = 0;
for (int i = 0; i < objects.count(); i++) {
if (objects[i]->intersectsRay(&ri)) {
if (ri.intersection<dist || dist ==-1) {
dist = ri.intersection;
obj = objects[i];
}
}
}
if (obj) {
float d = -Vector3f::dot(obj->getCenter()-cameraPosition,
(cameraTarget-cameraPosition).normalized());
QString s = (QString("Object: %0 (%1,%2,%3). Viewport depth: %4. Camera plane depth: %5")
.arg(obj->name()).arg(obj->getCenter().x()).arg(obj->getCenter().y()).arg(obj->getCenter().z())
.arg(dist).arg(d));
mainWindow->statusBar()->showMessage(s);
} else {
mainWindow->statusBar()->showMessage("");
}
return dist;
}
namespace {
bool depthSorter( const Object3D *a, const Object3D *b )
{
return a->getDepth() < b->getDepth();
}
}
void EngineWidget::paintGL() {
if (!staticImage.isNull()) {
return;
}
static int count = 0;
count++;
if (pendingRedraws > 0) pendingRedraws--;
if (disabled) {
qglClearColor(backgroundColor);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
return;
}
// Experimental shader stuff (not enabled)
if (shaderProgram) {
glDisable( GL_CULL_FACE );
glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST );
glViewport(0,0,width(),height());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,100,0,100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
int colorLocation = shaderProgram->uniformLocation("color");
QColor color(0, 255, 0, 255);
shaderProgram->setUniformValue(colorLocation, color);
glColor3d(1.0,1.0,0.0);
glRectf(2,2,98,98);
INFO("Drawing...");
return;
}
qglClearColor(backgroundColor);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Calc camera position
glTranslatef( translation.x(), translation.y(), translation.z() );
glScalef( scale, scale, scale );
Vector3f v(1,2,3);
Vector3f v2 = rotation*v;
glMultMatrixf(rotation.getArray());
glTranslatef( -pivot.x(), -pivot.y(), -pivot.z() );
glGetDoublev(GL_MODELVIEW_MATRIX, modelViewCache );
glGetDoublev(GL_PROJECTION_MATRIX, projectionCache );
glGetIntegerv(GL_VIEWPORT, viewPortCache);
cameraPosition = screenTo3D(width()/2, height()/2, 0);
cameraTarget = screenTo3D(width()/2, height()/2, 1);
cameraUp = screenTo3D(width()/2, height()/2-height()/4, 0)-cameraPosition;
cameraUp.normalize();
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
qglColor(getVisibleForegroundColor());
if (showCoordinateSystem) {
glPushMatrix();
GLUquadric* g = gluNewQuadric();
gluQuadricDrawStyle(g, GLU_FILL);
const float red[4] = { 1,0,0,1 };
const float green[4] = { 0,1,0,1 };
const float blue[4] = { 0,0,1,1 };
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue );
gluCylinder(g, 0.1,0.1,9,15,1); // z
glTranslatef(0,0,9);
gluCylinder(g, 0.3,0,1,15,1); // z-arrow
glTranslatef(0,0,-9);
glRotatef(90,0,1,0);
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red );
gluCylinder(g, 0.1,0.1,9,15,1); // z
glTranslatef(0,0,9);
gluCylinder(g, 0.3,0,1,15,1); // z-arrow
glTranslatef(0,0,-9);
glRotatef(-90,1,0,0);
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green );
gluCylinder(g, 0.1,0.1,9,15,1); // z
glTranslatef(0,0,9);
gluCylinder(g, 0.3,0,1,15,1); // z-arrow
glTranslatef(0,0,-9);
glPopMatrix();
}
if (QApplication::keyboardModifiers() == Qt::AltModifier || (doingRotate && fastRotate && ( objects.size()>1000))) {
/*
glDisable (GL_LIGHTING);
glPointSize(3);
glColor4f(1,1,1,1);
glBegin(GL_POINTS);
double stepX = 1.0/160.0;
Math::RandomNumberGenerator rg;
static int seeder = 0;
//rg.setSeed(seeder++);
for (double uz1 = 0; uz1 <=1; uz1+=stepX)
{
for (double uz2 = 0; uz2 <=1; uz2+=stepX)
{
double u1 = rg.getDouble(0,1);
double u2 = rg.getDouble(0,1);
u1 = uz1;
u2 = uz2;
double z = 1.0 - 2.0*u1;
double r = r = sqrt(1.0-z*z);
double phi = 2.0 * 3.1415926 * u2;
double x = r * cos(phi);
double y = r * sin(phi);
//glVertex3d(x,y,z);
glVertex3d(cos(2*3.1415*u1)*sqrt(u2),sin(2*3.1415*u1)*sqrt(u2),0);
}
}
glEnd();
glEnable (GL_LIGHTING);
*/
// Fast-draw
int objs = objects.size();
int step = objs/5000;
if (step < 1) step = 1;
if (count >= step) count = 0;
qglColor(getVisibleForegroundColor());
for (int i = count; i < objects.size(); i+=step) {
objects[i]->draw();
}
glEnable (GL_LIGHTING);
requireRedraw();
} else {
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
glEnable (GL_LIGHTING);
glEnable(GL_CULL_FACE);
glMateriali( GL_FRONT, GL_SPECULAR, 30 );
glMateriali(GL_FRONT, GL_SHININESS, 127);
glDisable (GL_BLEND);
QVector<Object3D*> transparentObjects;
for (int i = 0; i < objects.size(); i++) {
if (objects[i]->getColor()[3]==1.0) {
objects[i]->draw();
} else {
transparentObjects.append(objects[i]);
float d = -Vector3f::dot(objects[i]->getCenter()-cameraPosition,
cameraTarget-cameraPosition);
objects[i]->setDepth(d);
}
}
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glDisable(GL_DEPTH_TEST);
//glEnable(GL_CULL_FACE);
qSort(transparentObjects.begin(), transparentObjects.end(), depthSorter);
for (int i = 0; i < transparentObjects.size(); i++) {
transparentObjects[i]->draw();
}
}
renderText(10, 20, infoText);
};
void EngineWidget::resizeGL( int /* width */, int /* height */) {
// When resizing the perspective must be recalculated
requireRedraw();
updatePerspective();
};
void EngineWidget::updatePerspective() {
if (!staticImage.isNull()) return;
if (height() == 0) return;
GLfloat w = (float) width() / (float) height();
infoText = QString("[%1x%2] Aspect=%3").arg(width()).arg(height()).arg((float)width()/height());
textTimer = QTime::currentTime();
//GLfloat h = 1.0;
glViewport( 0, 0, width(), height() );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//settings.perspectiveAngle = 90;
gluPerspective(settings.perspectiveAngle, w, (float)settings.nearClipping, (float)settings.farClipping);
//glOrtho( -w, w, -h, h, (float)0, (float) 60 );
}
void EngineWidget::timerEvent(QTimerEvent*) {
static bool firstTime = true;
if (firstTime) {
firstTime = false;
updatePerspective();
requireRedraw();
infoText = "";
}
// Check if we are displaying a message.
if (infoText != "" && abs((int)(textTimer.msecsTo(QTime::currentTime()))>1000)) {
infoText = "";
requireRedraw();
}
if (pendingRedraws) updateGL();
}
void EngineWidget::initializeGL()
{
if (!staticImage.isNull()) return;
requireRedraw();
GLfloat pos[4] = {-55.0f, -25.0f, 20.0f, 1.0f };
//GLfloat pos2[4] = {52.0f, 25.0f, 50.0f, 1.0f };
GLfloat agreen[4] = {0.0f, 0.8f, 0.2f, 1.0f };
float ambient = 0.4f;
float diffuse = 0.6f;
float specular = 0.1f;
Vector3f color = Vector3f(1,1,1);
GLfloat ambientLight[] = { ambient * color.x(), ambient * color.y(), ambient * color.z(), 1.0f };
GLfloat diffuseLight[] = { diffuse * color.x(), diffuse * color.y(), diffuse * color.z(), 1.0f };
GLfloat specularLight[] = {specular * color.x(), specular * color.y(), specular* color.z(), 1.0f };
glLightfv(0, GL_AMBIENT, ambientLight);
glLightfv(0, GL_DIFFUSE, diffuseLight);
glLightfv(0, GL_SPECULAR, specularLight);
glLightfv(0, GL_POSITION, pos );
glEnable( GL_LIGHT0 );
glEnable( GL_CULL_FACE );
glEnable( GL_LIGHTING );
glEnable( GL_DEPTH_TEST );
glEnable( GL_NORMALIZE );
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, agreen );
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
}
void EngineWidget::wheelEvent(QWheelEvent* e) {
e->accept();
double interval = (double)e->delta() / 800.0;
if ( scale <= mouseSpeed*interval ) return;
scale -= mouseSpeed*interval;
requireRedraw();
}
void EngineWidget::mouseMoveEvent( QMouseEvent *e ) {
e->accept();
if (showDepth) getDepthAt(e->pos().x(),e->pos().y());
// store old position
if (oldPos.x() == 0 && oldPos.y() == 0) {
// first time
oldPos = e->pos();
return;
}
double dx = e->x() - oldPos.x();
double dy = e->y() - oldPos.y();
// normalize wrt screen size
double rx = dx / width();
double ry = dy / height();
oldPos = e->pos();
if ( (e->buttons() == Qt::LeftButton && e->modifiers() == Qt::ShiftModifier )
|| e->buttons() == (Qt::LeftButton | Qt::RightButton ))
{
doingRotate = true;
// 1) dragging with left mouse button + shift down, or
// 2) dragging with left and right mouse button down
//
// This results in zooming for vertical movement, and Z axis rotation for horizontal movement.
scale += (ry * 4.0f);
if (scale < minimumScale) scale = minimumScale;
rotateWorldZ( rx );
requireRedraw();
if (e->buttons() == (Qt::LeftButton | Qt::RightButton )) {
rmbDragging = true;
}
} else if ( ( e->buttons() == Qt::RightButton )
|| (e->buttons() == Qt::LeftButton && e->modifiers() == Qt::ControlModifier )
|| (e->buttons() == Qt::LeftButton && e->modifiers() == Qt::MetaModifier ) )
{
doingRotate = true;
// 1) dragging with right mouse button,
// 2) dragging with left button + control button,
// 3) dragging with left button + meta button (Mac)
//
// results in translation
if (rx != 0 || ry != 0) {
translateWorld( mouseSpeed*mouseTranslationSpeed* rx, - mouseSpeed*mouseTranslationSpeed*ry, 0 );
requireRedraw();
rmbDragging = true;
}
} else if ( e->buttons() == Qt::LeftButton ) {
doingRotate = true;
// Dragging with left mouse button.
// Results in rotation.
rotateWorldXY( rx, ry );
requireRedraw();
rmbDragging = false;
} else {
rmbDragging = false;
}
}
Vector3f EngineWidget::screenTo3D(int sx, int sy, int sz) {
// 2D -> 3D conversion
/*
GLdouble modelView[16];
GLdouble projection[16];
GLint viewPort[16];
glGetDoublev( GL_MODELVIEW_MATRIX, modelView );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewPort);
*/
GLdouble x, y, z;
float h = (float)height();
gluUnProject(sx, h-sy, sz, modelViewCache, projectionCache, viewPortCache, &x, &y ,&z);
return Vector3f(x,y,z);
}
void EngineWidget::rotateWorldXY(double x, double y) {
double rotateSpeed = 5.0;
Vector3f startPoint = screenTo3D(oldPos.x(), oldPos.y(),1);
Vector3f xDir = (screenTo3D(oldPos.x()+10, oldPos.y(),1) - startPoint).normalized() ;
Vector3f yDir = (screenTo3D(oldPos.x(), oldPos.y()+10,1) - startPoint).normalized() ;
Matrix4f mx = Matrix4f::Rotation(xDir, y*rotateSpeed);
Matrix4f my = Matrix4f::Rotation(yDir, -x*rotateSpeed);
rotation = rotation*my * mx ;
requireRedraw();
}
void EngineWidget::rotateWorldZ(double z) {
double rotateSpeed = 5.0;
Vector3f startPoint = screenTo3D(oldPos.x(), oldPos.y(), 0);
Vector3f endPoint = screenTo3D(oldPos.x(), oldPos.y(), 1);
Matrix4f mz = Matrix4f::Rotation(startPoint-endPoint, z*rotateSpeed);
rotation = rotation*mz ;
requireRedraw();
}
void EngineWidget::translateWorld(double x, double y, double z) {
translation = Vector3f(translation.x() + x, translation.y() + y, translation.z() + z);
}
void EngineWidget::clearWorld() {
for (int i = 0; i < objects.size(); i++) delete(objects[i]);
objects.clear();
}
void EngineWidget::addObject(Object3D* object) {
objects.append(object);
}
SyntopiaCore::Math::Vector3f EngineWidget::getCameraPosition() {
return cameraPosition;
};
SyntopiaCore::Math::Vector3f EngineWidget::getCameraUp() {
return cameraUp;
};
SyntopiaCore::Math::Vector3f EngineWidget::getCameraTarget() {
return cameraTarget;
};
double EngineWidget::getFOV() {
double ar = width()/(double)height();
return 29.0*ar; // Hack - this is not entirely accurate for large AR's.
}
QColor EngineWidget::getVisibleForegroundColor() {
int r = backgroundColor.red() < 127 ? 255 : 0;
int g = backgroundColor.green() < 127 ? 255 : 0;
int b = backgroundColor.blue() < 127 ? 255 : 0;
return QColor(r,g,b);
}
void EngineWidget::getBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const {
from = Vector3f(0,0,0);
to = Vector3f(0,0,0);
for (int i = 0; i < objects.count(); i++) {
if (i == 0) { objects[i]->getBoundingBox(from,to); }
else { objects[i]->expandBoundingBox(from,to); }
}
}
}
}

View File

@ -1,225 +0,0 @@
#pragma once
#include <QVector>
#include <QGLWidget>
#include <QMainWindow>
#include <QPoint>
#include <QList>
#include "SyntopiaCore/Math/Vector3.h"
#include "SyntopiaCore/Math/Matrix4.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
struct Command {
Command() {};
Command(QString command, QString arg) : command(command), arg(arg) {};
QString command;
QString arg;
};
/// Settings for the GLEngine
class Settings {
public:
// Default constructor
Settings() {
perspectiveAngle = 22.0;
nearClipping = 5.0;
farClipping = 60.0;
}
// Projection settings
double perspectiveAngle;
double nearClipping;
double farClipping;
};
class ProgressBox : public QWidget {
Q_OBJECT
public:
ProgressBox(QWidget* parent) : QWidget(parent) {
QHBoxLayout *layout = new QHBoxLayout(this);
cancelButton = new QPushButton(this);
bar = new QProgressBar(this);
layout->addWidget(cancelButton);
layout->addWidget(bar);
canceled = false;
connect(cancelButton, SIGNAL(clicked()), this, SLOT(setCanceled()));
dismiss();
started = false;
}
bool wasCanceled() { return canceled; }
void setValue(int v) { bar->setValue(v); }
void start() { cancelButton->setEnabled(true); started = true; setValue(0); bar->setVisible(true); setEnabled(true); cancelButton->setText("Cancel"); canceled = false; }
void dismiss() { cancelButton->setEnabled(true); started = false; setValue(0); bar->setVisible(false); cancelButton->setText("Raytrace (in Window)"); }
public slots:
void setCanceled() {
if (started) {
canceled = true;
cancelButton->setEnabled(false);
} else {
emit startPressed();
}
}
signals:
void startPressed();
private:
QProgressBar* bar;
QPushButton* cancelButton;
bool canceled;
bool started;
};
/// Widget for the mini OpenGL engine.
class EngineWidget : public QGLWidget {
public:
/// Constructor
EngineWidget(QMainWindow* mainWindow, QWidget* parent);
/// Destructor
~EngineWidget();
/// Use this whenever the an redraw is required.
/// Calling this function multiple times will still only result in one redraw
void requireRedraw();
QImage getScreenShot();
void toggleShowDepth() { showDepth = !showDepth; };
void clearWorld();
void reset();
void addObject(Object3D* object);
QList<Object3D*> getObjects() { return objects; };
int objectCount() const { return objects.size(); }
SyntopiaCore::Math::Vector3f getPivot() { return pivot; }
SyntopiaCore::Math::Matrix4f getRotation() { return rotation; }
SyntopiaCore::Math::Vector3f getTranslation() { return translation; }
SyntopiaCore::Math::Vector3f getCameraPosition();
SyntopiaCore::Math::Vector3f getCameraUp();
SyntopiaCore::Math::Vector3f getCameraTarget();
double getScale() { return scale; }
void setPivot(SyntopiaCore::Math::Vector3f pivot) { this->pivot = pivot; }
void setRotation(SyntopiaCore::Math::Matrix4f rotation) { this->rotation = rotation; }
void setTranslation(SyntopiaCore::Math::Vector3f translation) { this->translation = translation; }
void setScale(double scale) { this->scale = scale; }
void setPerspectiveAngle(double angle) { this->settings.perspectiveAngle = angle; updatePerspective(); }
/// RGB in [0;1]
void setBackgroundColor(double r, double g, double b) {
backgroundColor = QColor((int)(r*255.0), (int)(g*255.0), (int)(b*255.0));
};
SyntopiaCore::Math::Vector3f getBackgroundColor() {
return SyntopiaCore::Math::Vector3f(backgroundColor.red()/255.0f,backgroundColor.green()/255.0f,backgroundColor.blue()/255.0f);
}
void setContextMenu(QMenu* contextMenu) { this->contextMenu = contextMenu; }
double getFOV();
double getDepthAt(int x,int y);
QColor getVisibleForegroundColor();
void setDisabled(bool disabled) { this->disabled = disabled; }
void setFastRotate(bool enabled);
GLdouble* getModelViewCache() { return modelViewCache; };
GLdouble* getProjectionCache() { return projectionCache; };
GLint* getViewPortCache() { return viewPortCache; };
void getBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const;
void setRaytracerCommands(QVector<GLEngine::Command> raytracerCommands) { this->raytracerCommands = raytracerCommands; }
QVector<GLEngine::Command> getRaytracerCommands() { return raytracerCommands; }
void setupFragmentShader(); // For experimenting with shader effects.
void setImage(QImage im);
void setShowCoordinateSystem(bool val) { showCoordinateSystem = val; }
protected:
void mouseMoveEvent(QMouseEvent* ev) ;
void contextMenuEvent (QContextMenuEvent* ev);
void mouseReleaseEvent ( QMouseEvent * ev);
void initializeGL();
void timerEvent( QTimerEvent * );
void paintEvent(QPaintEvent * ev);
/// Actual drawing is implemented here
void paintGL();
/// Triggers a perspective update and a redraw
void resizeGL(int w, int h);
void wheelEvent(QWheelEvent* e);
void rotateWorldXY(double x, double y);
void rotateWorldZ(double z);
void translateWorld(double x, double y, double z);
private:
bool showCoordinateSystem;
bool fragmentShader;
QGLShaderProgram* shaderProgram;
QVector<GLEngine::Command> raytracerCommands;
// Creates the appropriate GL_PROJECTION matrix
void updatePerspective();
SyntopiaCore::Math::Vector3f screenTo3D(int sx, int sy, int sz);
int pendingRedraws; // the number of times we must redraw
// (when a redraw is requested we must draw two times, when double buffering)
int requiredRedraws;
Settings settings;
double scale;
double mouseSpeed;
double mouseTranslationSpeed;
double minimumScale;
QPoint oldPos;
QColor backgroundColor;
SyntopiaCore::Math::Vector3f translation;
SyntopiaCore::Math::Vector3f pivot;
SyntopiaCore::Math::Matrix4f rotation;
QList<Object3D*> objects;
QString infoText;
QMenu* contextMenu;
bool rmbDragging;
SyntopiaCore::Math::Vector3f cameraPosition;
SyntopiaCore::Math::Vector3f cameraUp;
SyntopiaCore::Math::Vector3f cameraTarget;
GLdouble modelViewCache[16];
GLdouble projectionCache[16];
GLint viewPortCache[16];
QTime textTimer;
bool disabled;
bool fastRotate;
bool doingRotate;
bool showDepth;
QMainWindow* mainWindow;
QImage staticImage;
};
};
};

View File

@ -1,60 +0,0 @@
#include "Grid.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
Grid::Grid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3) : base(base), v1(dir1), v2(dir2), v3(dir3)
{
/// Bounding box
from = base;
to = base + dir1 + dir2 + dir3;
};
Grid::~Grid() { };
void Grid::draw() const {
glPushMatrix();
glTranslatef( base.x(), base.y(), base.z() );
glLineWidth( 1.0 );
glDisable (GL_LIGHTING);
glColor4fv( primaryColor );
glBegin( GL_LINE_LOOP );
Vector3f O(0,0,0);
vertex(O);
vertex(v2);
vertex(v2+v1);
vertex(v1);
glEnd();
glBegin( GL_LINE_LOOP );
vertex(v3);
vertex(v2+v3);
vertex(v2+v1+v3);
vertex(v1+v3);
glEnd();
glBegin( GL_LINES );
vertex( v3 ); vertex( O );
vertex( v2 ); vertex( v2+v3 );
vertex( v1+v2 ); vertex( v1+v2+v3 );
vertex( v1 ); vertex( v1+v3 );
glEnd();
glEnable (GL_LIGHTING);
glPopMatrix();
};
}
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
class Grid : public Object3D {
public:
Grid(SyntopiaCore::Math::Vector3f base,
SyntopiaCore::Math::Vector3f dir1 ,
SyntopiaCore::Math::Vector3f dir2,
SyntopiaCore::Math::Vector3f dir3);
virtual ~Grid();
virtual QString name() { return "Grid"; }
virtual void draw() const;
private:
SyntopiaCore::Math::Vector3f base;
SyntopiaCore::Math::Vector3f v1;
SyntopiaCore::Math::Vector3f v2;
SyntopiaCore::Math::Vector3f v3;
};
}
}

View File

@ -1,44 +0,0 @@
#include "Line.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
//GLUquadric* Sphere::myQuad = 0;
Line::Line(SyntopiaCore::Math::Vector3f from, SyntopiaCore::Math::Vector3f to) : from(from), to(to)
{
/// Bounding box
from = from;
to = to;
};
Line::~Line() {
};
void vertex(Vector3f v) {
glVertex3f(v.x(), v.y(), v.z());
}
void Line::draw() const {
glLineWidth( 1.0 );
glDisable (GL_LIGHTING);
glColor4fv(primaryColor);
glBegin(GL_LINES);
vertex(from);
vertex(to);
glEnd();
glEnable (GL_LIGHTING);
};
}
}

View File

@ -1,26 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
class Line : public Object3D {
public:
Line(SyntopiaCore::Math::Vector3f from, SyntopiaCore::Math::Vector3f to);
virtual ~Line();
virtual QString name() { return "Line"; }
virtual void draw() const;
private:
SyntopiaCore::Math::Vector3f from;
SyntopiaCore::Math::Vector3f to;
};
}
}

View File

@ -1,131 +0,0 @@
#include "Mesh.h"
#include "SyntopiaCore/Math/Vector3.h"
#include "../Logging/Logging.h"
using namespace SyntopiaCore::Logging;
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
Mesh::Mesh(SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1 ,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1 ,
SyntopiaCore::Math::Vector3f endDir2) :
startBase(startBase), startDir1(startDir1), startDir2(startDir2),
endBase(endBase), endDir1(endDir1), endDir2(endDir2)
{
/// Bounding Mesh (not really accurate)
from = startBase;
to = startBase;
Expand(from,to, startBase+startDir1);
Expand(from,to, startBase+startDir2);
Expand(from,to, startBase+startDir2+startDir1);
Expand(from,to, endBase);
Expand(from,to, endBase+endDir1);
Expand(from,to, endBase+endDir2);
Expand(from,to, endBase+endDir1+endDir2);
};
Mesh::~Mesh() { };
void Mesh::draw() const {
// --- TODO: Rewrite - way to many state changes...
glPushMatrix();
glTranslatef( startBase.x(), startBase.y(), startBase.z() );
GLfloat col[4] = {0.0f, 1.0f, 1.0f, 0.1f} ;
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, col );
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
Vector3f O(0,0,0);
Vector3f end = endBase - startBase;
glEnable (GL_LIGHTING);
glDisable(GL_CULL_FACE); // TODO: do we need this?
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLfloat c3[4] = {0.3f, 0.3f, 0.3f, 0.5f};
glMaterialfv( GL_FRONT, GL_SPECULAR, c3 );
glMateriali(GL_FRONT, GL_SHININESS, 127);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glBegin( GL_QUADS );
glColor4fv(primaryColor);
GLfloat secondaryColor[4] = {oldRgb[0], oldRgb[1], oldRgb[2], oldAlpha};
//vertex4n(O, startDir1,startDir2+startDir1,startDir2);
Vector3f c1(startDir1*0.5f+startDir2*0.5f);
Vector3f c2(end+endDir1*0.5f+endDir2*0.5f);
vertex4(primaryColor, c1, O, startDir1, secondaryColor,c2, end+endDir1,end,false);
vertex4(primaryColor,c1, O, startDir2, secondaryColor,c2, end+endDir2,end,false);
vertex4(primaryColor,c1, startDir1, startDir1+startDir2,secondaryColor,c2, end+endDir1+endDir2, end+endDir1,false);
vertex4(primaryColor,c1, startDir2, startDir1+startDir2,secondaryColor,c2, end+endDir1+endDir2, end+endDir2,false);
//vertex4n(O+end, endDir1+end,endDir2+endDir1+end,endDir2+end);
glEnd();
glDisable(GL_COLOR_MATERIAL);
glPopMatrix();
};
bool Mesh::intersectsRay(RayInfo* ri) {
if (triangles.count()==0) initTriangles();
for (int i = 0; i < triangles.count(); i++) {
if (triangles[i].intersectsRay(ri)) return true;
}
return false;
};
void Mesh::initTriangles() {
triangles.clear();
RaytraceTriangle::Vertex4(startBase, startBase+startDir1,endBase+endDir1,endBase, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(startBase, endBase,endBase+endDir2,startBase+startDir2, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(startBase+startDir1, startBase+startDir1+startDir2, endBase+endDir1+endDir2, endBase+endDir1, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
RaytraceTriangle::Vertex4(startBase+startDir2, endBase+endDir2, endBase+endDir1+endDir2, startBase+startDir1+startDir2, true,triangles,primaryColor[0],primaryColor[1],primaryColor[2],primaryColor[3]);
from = startBase;
to = startBase;
for (int i = 0; i < triangles.count(); i++) {
triangles[i].expandBoundingBox(from,to);
}
}
bool Mesh::intersectsAABB(Vector3f from2, Vector3f to2) {
if (triangles.count()==0) initTriangles();
return
(from.x() < to2.x()) && (to.x() > from2.x()) &&
(from.y() < to2.y()) && (to.y() > from2.y()) &&
(from.z() < to2.z()) && (to.z() > from2.z());
};
}
}

View File

@ -1,48 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
#include "RaytraceTriangle.h"
namespace SyntopiaCore {
namespace GLEngine {
class Mesh : public Object3D {
public:
Mesh(SyntopiaCore::Math::Vector3f startBase,
SyntopiaCore::Math::Vector3f startDir1 ,
SyntopiaCore::Math::Vector3f startDir2,
SyntopiaCore::Math::Vector3f endBase,
SyntopiaCore::Math::Vector3f endDir1 ,
SyntopiaCore::Math::Vector3f endDir2
);
virtual ~Mesh();
virtual QString name() { return "Mesh"; }
virtual void draw() const;
void initTriangles();
virtual bool intersectsRay(RayInfo* /*rayInfo*/);
virtual bool intersectsAABB(SyntopiaCore::Math::Vector3f /*from*/, SyntopiaCore::Math::Vector3f /*to*/);
virtual void prepareForRaytracing() {initTriangles(); };
void setPreviousColor(SyntopiaCore::Math::Vector3f oldRgb, float oldAlpha) { this->oldRgb = oldRgb; this->oldAlpha = oldAlpha; }
private:
SyntopiaCore::Math::Vector3f startBase;
SyntopiaCore::Math::Vector3f startDir1;
SyntopiaCore::Math::Vector3f startDir2;
SyntopiaCore::Math::Vector3f endBase;
SyntopiaCore::Math::Vector3f endDir1;
SyntopiaCore::Math::Vector3f endDir2;
QVector<RaytraceTriangle> triangles;
SyntopiaCore::Math::Vector3f oldRgb;
float oldAlpha;
};
}
}

View File

@ -1,129 +0,0 @@
#include "Object3D.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
void Object3D::setColor(SyntopiaCore::Math::Vector3f rgb, float alpha) {
primaryColor[0] = rgb[0];
primaryColor[1] = rgb[1];
primaryColor[2] = rgb[2];
primaryColor[3] = alpha;
}
void Object3D::Expand(Vector3f& from, Vector3f& to, Vector3f test) {
if (test.x()<from.x()) from.x() = test.x();
if (test.y()<from.y()) from.y() = test.y();
if (test.z()<from.z()) from.z() = test.z();
if (test.x()>to.x()) to.x() = test.x();
if (test.y()>to.y()) to.y() = test.y();
if (test.z()>to.z()) to.z() = test.z();
}
void Object3D::vertex4n(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const {
Vector3f n = (v2-v1).cross(v4-v1);
n.normalize();
normal(n);
vertex(v1);
vertex(v2);
vertex(v3);
vertex(v4);
}
void Object3D::vertex4(const GLfloat* col1, SyntopiaCore::Math::Vector3f c1, SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2, const GLfloat* col2, SyntopiaCore::Math::Vector3f c2, SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4, bool reverse) const {
/*
Vector3f n = (v2-v1).cross(v4-v1);
n.normalize();
if (reverse) n =-n;
normal(n);
*/
glColor4fv(col1);
if (reverse) {
normal((c1-v1).normalized());
} else {
normal((v1-c1).normalized());
}
vertex(v1);
if (reverse) {
normal((c1-v2).normalized());
} else {
normal((v2-c1).normalized());
}
vertex(v2);
glColor4fv(col2);
if (reverse) {
normal((c2-v3).normalized());
} else {
normal((v3-c2).normalized());
}
vertex(v3);
if (reverse) {
normal((c2-v4).normalized());
} else {
normal((v4-c2).normalized());
}
vertex(v4);
}
void Object3D::vertex3n(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3) const {
Vector3f n = (v2-v1).cross(v3-v1);
n.normalize();
normal(n);
vertex(v1);
vertex(v2);
vertex(v3);
}
void Object3D::vertex4rn(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const {
Vector3f n = (v1-v2).cross(v4-v1);
n.normalize();
normal(n);
vertex(v4);
vertex(v3);
vertex(v2);
vertex(v1);
}
void Object3D::vertex4nc(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4,SyntopiaCore::Math::Vector3f center) const {
normal((v1-center).normalized());
vertex(v1);
normal((v2-center).normalized());
vertex(v2);
normal((v3-center).normalized());
vertex(v3);
normal((v4-center).normalized());
vertex(v4);
}
void Object3D::vertex4rnc(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4,SyntopiaCore::Math::Vector3f center) const {
normal((v4-center).normalized());
vertex(v4);
normal((v3-center).normalized());
vertex(v3);
normal((v2-center).normalized());
vertex(v2);
normal((v1-center).normalized());
vertex(v1);
}
void Object3D::getBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const {
from = this->from;
to = this->to;
};
void Object3D::expandBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const {
for (unsigned int i = 0; i < 3; i++) if (this->from[i] < from[i]) from[i] = this->from[i];
for (unsigned int i = 0; i < 3; i++) if (this->to[i] > to[i]) to[i] = this->to[i];
};
}
}

View File

@ -1,100 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include <QGLWidget>
#include <QtOpenGL>
namespace SyntopiaCore {
namespace GLEngine {
/// Every Primitive can be assigned a class.
struct PrimitiveClass {
PrimitiveClass() : reflection(0), hasShadows(true), castShadows(true)
, ambient(0.6), specular(0.6), diffuse(0.6) {};
QString name;
double reflection;
bool hasShadows; // not implemented yet
bool castShadows; // not implemented yet
double ambient;
double specular;
double diffuse;
};
/// Used by the raytracer, when tracing rays.
struct RayInfo {
SyntopiaCore::Math::Vector3f startPoint;
SyntopiaCore::Math::Vector3f lineDirection;
// 'Return' variables - if a hit is found these will be overwritten.
float intersection;
SyntopiaCore::Math::Vector3f normal;
GLfloat color[4];
};
class Object3D {
public:
Object3D() : objectID(-1) {};
virtual ~Object3D() {};
virtual QString name() { return "Object3D base"; }
virtual void draw() const = 0;
virtual void setColor(SyntopiaCore::Math::Vector3f rgb, float alpha);
const GLfloat (&getColor() const)[4] { return primaryColor; }
void getBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const;
void expandBoundingBox(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to) const;
SyntopiaCore::Math::Vector3f getCenter() { return (from+to)*0.5; }
/// These must be implemented for an Object3D to support raytracing.
virtual bool intersectsRay(RayInfo* /*rayInfo*/) { return false; };
virtual bool intersectsAABB(SyntopiaCore::Math::Vector3f /*from*/, SyntopiaCore::Math::Vector3f /*to*/) { return false; };
virtual void prepareForRaytracing() { }; // Implement for additional preprocessing
int getObjectID() { return objectID; }
void setObjectID(int id) { objectID = id; }
PrimitiveClass* getPrimitiveClass() { return primitiveClass; }
void setPrimitiveClass(PrimitiveClass* value) { primitiveClass = value; }
static void Expand(SyntopiaCore::Math::Vector3f& from, SyntopiaCore::Math::Vector3f& to, SyntopiaCore::Math::Vector3f test);
GLfloat getDepth() const { return depth; }
void setDepth(GLfloat d) { this->depth =d; }
bool operator<(Object3D& other) {
return (this->depth < other.depth);
}
protected:
void vertex(SyntopiaCore::Math::Vector3f v) const { glVertex3f(v.x(), v.y(), v.z()); }
void normal(SyntopiaCore::Math::Vector3f v) const { glNormal3f(v.x(), v.y(), v.z()); }
void vertex4(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const { vertex(v1); vertex(v2); vertex(v3); vertex(v4); }
void vertex3n(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3) const;
void vertex4r(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const { vertex(v4); vertex(v3); vertex(v2); vertex(v1); }
void vertex4(const GLfloat* col1, SyntopiaCore::Math::Vector3f c1,SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,const GLfloat* col2, SyntopiaCore::Math::Vector3f c2, SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4, bool reverse) const;
void vertex4n(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const;
void vertex4rn(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4) const ;
void vertex4nc(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4,SyntopiaCore::Math::Vector3f center) const ;
void vertex4rnc(SyntopiaCore::Math::Vector3f v1,SyntopiaCore::Math::Vector3f v2,SyntopiaCore::Math::Vector3f v3,SyntopiaCore::Math::Vector3f v4,SyntopiaCore::Math::Vector3f center) const;
GLfloat primaryColor[4];
// Bounding box
SyntopiaCore::Math::Vector3f from;
SyntopiaCore::Math::Vector3f to;
// Used by Voxel Stepper when raytracing.
int objectID;
PrimitiveClass* primitiveClass;
GLfloat depth;
};
}
}

View File

@ -1,119 +0,0 @@
#include "RaytraceTriangle.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
RaytraceTriangle::RaytraceTriangle(Vector3f p1, Vector3f p2, Vector3f p3, Vector3f n1, Vector3f n2, Vector3f n3)
: p1(p1),p2(p2),p3(p3),n1(n1),n2(n2),n3(n3) , cullBackFaces(true)
{
// We will use barycentric coordiantes.
Vector3f p12 = p2-p1;
Vector3f p13 = p3-p1;
Vector3f p312 = (p12/p12.sqrLength())*Vector3f::dot(p12,p13);
Vector3f pn3 = p13-p312;
npn3 = pn3/pn3.sqrLength();
Vector3f p32 = p2-p3;
Vector3f p31 = p1-p3;
Vector3f p132 = (p32/p32.sqrLength())*Vector3f::dot(p32,p31);
Vector3f pn1 = p31-p132;
npn1 = pn1/pn1.sqrLength();
Vector3f p213 = (p13/p13.sqrLength())*Vector3f::dot(p13,p12);
Vector3f pn2 = p12-p213;
npn2 = pn2/pn2.sqrLength();
if (npn1.sqrLength() * 0 != 0 || npn2.sqrLength() * 0 != 0 || npn3.sqrLength() * 0 != 0 ||
p13.sqrLength() < 1E-8 || p12.sqrLength() < 1E-8 || p32.sqrLength() < 1E-8) {
//INFO(QString("Bad Triangle (%0): %1, %2, %3").arg((p3-p2).sqrLength()).arg(p1.toString()).arg(p2.toString()).arg(p3.toString()));
bad = true;
} else {
bad = false;
}
n1.normalize();
n2.normalize();
n3.normalize();
n = Vector3f::cross(p13,p12);
}
void RaytraceTriangle::expandBoundingBox(Vector3f& from,Vector3f& to) {
Vector3f p = p1;
for (int i = 0; i < 3; i++) {
if (i==1) p=p2;
if (i==2) p=p3;
if ( p.x() < from.x()) from.x() = p.x();
if ( p.y() < from.y()) from.y() = p.y();
if ( p.z() < from.z()) from.z() = p.z();
if ( p.x() > to.x()) to.x() = p.x();
if ( p.y() > to.y()) to.y() = p.y();
if ( p.z() > to.z()) to.z() = p.z();
}
}
//bool RayTraceTriangle::getIntersectionAndNormal(const Vector3f& startPoint, const Vector3f& lineDirection, double& intersection, Vector3f& normal, Vector3f& color, float& alpha ) {
bool RaytraceTriangle::intersectsRay(RayInfo* ri) {
if (bad) return false;
if (cullBackFaces && Vector3f::dot(n,ri->lineDirection)>0) return false;
float is = Vector3f::dot(n, p1-ri->startPoint)/Vector3f::dot(n, ri->lineDirection);
Vector3f ip = ri->startPoint + ri->lineDirection * is;
float k312 = Vector3f::dot((ip-p1),npn3);
if (k312 > 1 || k312 <= 0) return false;
float k132 = Vector3f::dot((ip-p3),npn1);
if (k132 > 1 || k132 <= 0) return false;
float k213 = Vector3f::dot((ip-p1),npn2);
if (k213 > 1 || k213 <= 0) return false;
ri->intersection = is;
ri->normal = n3*k312+ n1*k132+n2*k213;
if (!cullBackFaces && Vector3f::dot( ri->normal , ri->lineDirection)>0) ri->normal=-ri->normal;
float c0 = k312*color3[0]+k132*color1[0]+k213*color2[0];
float c1 = k312*color3[1]+k132*color1[1]+k213*color2[1];
float c2 = k312*color3[2]+k132*color1[2]+k213*color2[2];
float a = 1; //k312*color3[3]+k132*color1[3]+k213*color2[3];
ri->color[0] = c0;
ri->color[1] = c1;
ri->color[2] = c2;
ri->color[3] = a;
return true;
}
RaytraceTriangle::~RaytraceTriangle(void) { }
void RaytraceTriangle::Vertex4(Vector3f p1,Vector3f p2,Vector3f p3,Vector3f p4,bool reverse, QVector<RaytraceTriangle>& list, float r, float g, float b, float a) {
Vector3f n = -Vector3f::cross(p2-p1, p4-p1);
if (reverse) n=-n;
n.normalize();
RaytraceTriangle r1(p1,p2,p4,n,n,n);
r1.n = n;
r1.setColor(r,g,b,a);
list.append(r1);
RaytraceTriangle r2(p3,p4,p2,n,n,n);
r2.setColor(r,g,b,a);
r2.n = n;
list.append(r2);
}
}
}

View File

@ -1,50 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
// Helper class for tesselated objects
class RaytraceTriangle {
public:
RaytraceTriangle(Vector3f p1, Vector3f p2, Vector3f p3, Vector3f n1, Vector3f n2, Vector3f n3);
RaytraceTriangle() : cullBackFaces(false) {};
~RaytraceTriangle(void);
void expandBoundingBox(Vector3f& from,Vector3f& to);
void setColor(float r, float g, float b, float a) {
color1[0] = r; color1[1] = g; color1[2] = b; color1[3] = a;
color2[0] = r; color2[1] = g; color2[2] = b; color2[3] = a;
color3[0] = r; color3[1] = g; color3[2] = b; color3[3] = a;
}
virtual bool intersectsRay(RayInfo* /*rayInfo*/);
static void Vertex4(Vector3f p1,Vector3f p2,Vector3f p3,Vector3f p4,bool reverse, QVector<RaytraceTriangle>& list, float r, float b, float g, float a);
Vector3f p1;
Vector3f p2;
Vector3f p3;
Vector3f n1;
Vector3f n2;
Vector3f n3;
GLfloat color1[4];
GLfloat color2[4];
GLfloat color3[4];
Vector3f npn1;
Vector3f npn2;
Vector3f npn3;
Vector3f to;
Vector3f from;
Vector3f n;
bool bad;
bool cullBackFaces;
};
}
}

View File

@ -1,13 +0,0 @@
#include "AtomicCounter.h"
#include "SyntopiaCore/Math/Vector3.h"
#include "AtomicCounter.h"
namespace SyntopiaCore {
namespace GLEngine {
}
}

View File

@ -1,41 +0,0 @@
#pragma once
#include <climits>
#include <QMutex>
#include <QWaitCondition>
namespace SyntopiaCore {
namespace GLEngine {
class AtomicCounter {
public:
AtomicCounter() : current(0) { };
int increase() {
mutex.lock();
int i = ++current;
mutex.unlock();
wc.wakeAll();
return i;
}
void setValue(int value) {
mutex.lock();
current = value;
mutex.unlock();
wc.wakeAll();
}
bool wait(unsigned long time = ULONG_MAX) { wcm.lock(); bool w = wc.wait(&wcm,time); wcm.unlock(); return w; }
int value() { mutex.lock(); int i = current; mutex.unlock(); return i; }
private:
int current;
QMutex mutex;
QMutex wcm;
QWaitCondition wc;
};
}
}

View File

@ -1,106 +0,0 @@
#pragma once
#include <QMutex>
#include <QImage>
#include "SyntopiaCore/Math/Vector3.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
class ProgressiveOutput {
public:
ProgressiveOutput(int w, int h) : w(w), h(h) {
mutex = new QMutex();
colors = new Vector3f[w*h];
weights = new double[w*h];
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
colors[x+y*w] = Vector3f(0,0,0);
weights[x+y*w] = 0.0;
}
}
}
~ProgressiveOutput() {
delete mutex;
delete[] colors;
delete[] weights;
}
void addIteration(Vector3f* newColors, double* newWeights) {
mutex->lock();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
colors[x+y*w] = colors[x+y*w] + newColors[x+y*w];
weights[x+y*w] = weights[x+y*w] + newWeights[x+y*w];
}
}
mutex->unlock();
};
void addColumn(int x, Vector3f* newColors) {
mutex->lock();
for (int y = 0; y < h; y++) {
colors[x+y*w] = colors[x+y*w] + newColors[y];
weights[x+y*w] = 1.0;
}
mutex->unlock();
};
// This function is responsible for tonemapping and gamma-conversion.
// I prefer not converting to sRGB encode
// and a simply clipping functions works better than an 'exposure'-simulation
int encode(float c, float /*exposure*/)
{
// Enable below for sRGB encoding
/*
float out = 1.0f - expf(c * exposure);
if (out <= 0.0031308f)
{
out = 12.92f * out;
}
else
{
out = 1.055f * powf(out, 0.4166667f) - 0.055f; // Inverse gamma 2.4
}
float out = 1.0f - expf(c * exposure);
out = powf(c, 1);
*/
if (c>1) c = 1; // Simple clipping...
c *= 255;
return (int)c;
}
QImage getImage() {
QImage im(w,h, QImage::Format_RGB32);
//mutex->lock();
float exposure = -2;
for (int y = 0; y < h; y++) {
QRgb* s = (QRgb*) im.scanLine(y);
for (int x = 0; x < w; x++) {
Vector3f c = colors[x+y*w]/weights[x+y*w];
*s = qRgb(encode(c.x(),exposure), encode(c.y(),exposure), encode(c.z(),exposure));
s++;
//im.setPixel(x,y,qRgb(c.x()*255, c.y()*255, c.z()*255));
}
}
//mutex->unlock();
return im;
}
private:
int w;
int h;
Vector3f* colors;
double* weights;
QMutex* mutex;
};
}
}

View File

@ -1,273 +0,0 @@
#include <QThread>
#include "RayTracer.h"
#include "SyntopiaCore/Math/Vector3.h"
#include "SyntopiaCore/Logging/Logging.h"
#include "SyntopiaCore/Misc/MiniParser.h"
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::Misc;
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Logging;
RayTracer::RayTracer(EngineWidget* engine, ProgressBox* progressBox, bool inGUI) {
this->progressBox = progressBox;
for (int i = 0; i < 16; i++) modelView[i] = engine->getModelViewCache()[i];
for (int i = 0; i < 16; i++) projection[i] = engine->getProjectionCache()[i];
for (int i = 0; i < 16; i++) viewPort[i] = engine->getViewPortCache()[i];
sizeX = 0;
sizeY = 0;
userCancelled = false;
this->progressiveGUI = inGUI;
maxThreads = QThread::idealThreadCount();
progressiveRender = inGUI;
voxelSteps = 0;
rt.aaSamples = inGUI ? 6 : 8; // Default quality: 36 samples in GUI, 64 in Final.
foreach (Command c, engine->getRaytracerCommands()) {
QString arg = c.arg;
arg = arg.remove("[");
arg = arg.remove("]");
setParameter(c.command, arg);
}
objects = engine->getObjects();
if (voxelSteps == 0) {
// voxel steps is not explicitly set.
// We will try this simple heuristic
if (objects.count()<10000) {
voxelSteps = 35;
} else if (objects.count()<50000) {
voxelSteps = 60;
} else {
voxelSteps = 100;
}
INFO(QString("Settings voxel steps to %1.").arg(voxelSteps));
}
Vector3f from;
Vector3f to;
engine->getBoundingBox(from,to);
rt.accelerator = new VoxelStepper(from,to, voxelSteps);
windowHeight = engine->height();
windowWidth = engine->width();
for (int i = 0; i < objects.count(); i++) rt.accelerator->registerObject(objects[i]);
rt.setObjects(objects.count());
for (int i = 0; i < objects.count(); i++) objects[i]->setObjectID(i);
rt.backgroundColor = engine->getBackgroundColor();
this->engine = engine;
}
void RayTracer::startJobs(ProgressBox* progress) {
completedUnits.setValue(0);
nextUnit.setValue(0);
INFO(QString("Starting %1 threads...").arg(maxThreads));
for (int i = 0; i < maxThreads; i++) {
threads[i]->start();
}
int c = completedUnits.value();
int oldC = 0;
int screenUpdates=0;
QTime lastTime = QTime::currentTime().addMSecs(-2000);
while (c<maxUnits) {
/*bool s = */completedUnits.wait(1000); // Wait to see if a new unit is completed.
if (progress && progress->wasCanceled()) {
// in order to cancel, we will swallow
// all pending job units...
userCancelled = true;
int newUnit = nextUnit.increase();
while (newUnit <= maxUnits) {
completedUnits.increase();
newUnit = nextUnit.increase();
}
for (int i = 0; i < maxThreads; i++) {
threads[i]->setTerminated(true);
}
}
c = completedUnits.value();
if (progress && !progress->wasCanceled()) progress->setValue((c*100)/maxUnits);
if (lastTime.msecsTo(QTime::currentTime())>1000) {
//Debug(QString::number(c));
lastTime = QTime::currentTime();
if (oldC != c) {
screenUpdates++;
oldC = c;
if (progressiveGUI) engine->setImage(progressiveOutput->getImage());
}
qApp->processEvents();
}
};
//INFO(QString("Screen updates: %1").arg(screenUpdates));
}
QImage RayTracer::calculateImage(int w, int h) {
// Setup dimensions
if (w == 0) w = sizeX;
if (h == 0) h = sizeY;
if (w==0 && h==0) {
w = windowWidth;
h = windowHeight;
} else if (w==0) {
w = h * (windowWidth / (float)windowHeight);
} else if (h==0) {
h = w * (windowHeight / (float)windowWidth);
}
INFO(QString("Rendering size: %3x%4").arg(w).arg(h));
if (progressiveRender) {
maxUnits = rt.aaSamples*rt.aaSamples;
rt.sampler = new ProgressiveStratifiedSampler(&rt.rg);
((ProgressiveStratifiedSampler*)rt.sampler)->setAAOrder(rt.rg.getRandomIndices(maxUnits));
INFO("Using progressive stratification (renders 1 ray layer at a time)");
} else {
maxUnits = w;
rt.sampler = new StratifiedSampler(&rt.rg);
INFO("Using stratified sampling (renders 1 pixel column a time)");
}
rt.alloc(w,h);
// Initialize rng loopup tables
RandomNumberGenerator rg;
// Calculate viewport
GLdouble ox1, oy1, oz1;
gluUnProject(0, windowHeight, 0.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.frontStart = Vector3f(ox1,oy1,oz1);
gluUnProject(windowWidth, windowHeight, 0.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.frontX = Vector3f(ox1,oy1,oz1)-rt.frontStart;
gluUnProject(0, 0, 0.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.frontY = Vector3f(ox1,oy1,oz1)-rt.frontStart;
gluUnProject(0, windowHeight, 1.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.backStart = Vector3f(ox1,oy1,oz1);
gluUnProject(windowWidth, windowHeight, 1.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.backX = Vector3f(ox1,oy1,oz1)-rt.backStart;
gluUnProject(0, 0, 1.0f, modelView, projection, viewPort, &ox1, &oy1 ,&oz1);
rt.backY = Vector3f(ox1,oy1,oz1)-rt.backStart;
// Setup progress dialog.
if (progressBox) progressBox->start();
QTime start = QTime::currentTime();
QImage im(w,h, QImage::Format_RGB32);
if (rt.lightPos == Vector3f(0,0,0)) {
GLdouble sx1, sy1, sz1;
gluUnProject((float)-200, windowHeight, 0.0f, modelView, projection, viewPort, &sx1, &sy1 ,&sz1);
rt.lightPos = Vector3f((GLfloat)sx1, (GLfloat)sy1, (GLfloat)sz1);
}
rt.aaSamples=abs(rt.aaSamples);
rt.setCounters(&nextUnit, &completedUnits, maxUnits);
progressiveOutput = new ProgressiveOutput(w,h);
rt.progressiveOutput = progressiveOutput;
if (progressiveRender) {
rt.setTask(RenderThread::RaytraceProgressive);
} else {
rt.setTask(RenderThread::Raytrace);
}
threads.append(&rt);
for (int i = 1; i < maxThreads; i++) {
threads.append(new RenderThread(rt));
}
// To avoid coherence we will seed the threads differently.
// (This was actually very visible near the left of the window).
for (int i = 0; i < maxThreads; i++) {
threads[i]->seed(rg.getInt());
}
startJobs(progressBox);
double renderTime = start.msecsTo(QTime::currentTime())/1000.0;
im = progressiveOutput->getImage();
for (int i = 0; i < maxThreads; i++) {
if (i!=0) delete(threads[i]);
}
INFO(QString("Time: %1s for %2 pixels.").arg(renderTime).arg(w*h));
if (progressBox) progressBox->dismiss();
delete (progressiveOutput);
return im;
}
void RayTracer::setParameter(QString param, QString value) {
param=param.toLower();
if (param == "ambient-occlusion-samples") {
MiniParser(value, ',').getInt(rt.aoSamples);
INFO(QString("Ambient Occlusion samples: %3x%4 ")
.arg(rt.aoSamples).arg(rt.aoSamples));
} else if (param == "samples") {
MiniParser(value, ',').getInt(rt.aaSamples);
INFO(QString("Samples per pixel (anti-alias, DOF, AO, ...): %1x%2 ")
.arg(rt.aaSamples).arg(rt.aaSamples));
} else if (param == "max-depth") {
MiniParser(value, ',').getInt(rt.maxDepth);
INFO(QString("Maximum ray recursion depth: %1x ")
.arg(rt.maxDepth));
} else if (param == "voxel-steps") {
MiniParser(value, ',').getInt(voxelSteps);
INFO(QString("Voxel steps: %1")
.arg(voxelSteps));
} else if (param == "progressive") {
MiniParser(value, ',').getBool(progressiveRender);
INFO(QString("Progressive Render: %3").arg(progressiveRender ? "true" : "false"));
} else if (param == "dof") {
MiniParser(value, ',').getDouble(rt.dofCenter).getDouble(rt.dofFalloff);
INFO(QString("Depth-of-field: center %1, falloff %2 ")
.arg(rt.dofCenter).arg(rt.dofFalloff));
} else if (param == "shadows") {
MiniParser(value, ',').getBool(rt.useShadows);
INFO(QString("Shadows: %3").arg(rt.useShadows ? "true" : "false"));
} else if (param == "max-threads") {
MiniParser(value, ',').getInt(maxThreads);
if (maxThreads<=0) maxThreads = QThread::idealThreadCount();
INFO(QString("Max threads: %3").arg(maxThreads));
} else if (param == "size") {
MiniParser(value, 'x').getInt(sizeX).getInt(sizeY);
INFO(QString("Size: %1, %2").arg(sizeX).arg(sizeY));
} else if (param == "light") {
MiniParser(value, ',').getFloat(rt.lightPos.x()).getFloat(rt.lightPos.y()).getFloat(rt.lightPos.z());
INFO(QString("Light position: %1, %2, %3").arg(rt.lightPos.x())
.arg(rt.lightPos.y()).arg(rt.lightPos.z()));
} else {
WARNING("Unknown parameter: " + param);
}
}
}
}

View File

@ -1,59 +0,0 @@
#pragma once
#include "../Object3D.h"
#include "../EngineWidget.h"
#include <QImage>
#include "SyntopiaCore/Math/Vector3.h"
#include "SyntopiaCore/Math/Matrix4.h"
#include "SyntopiaCore/Math/Random.h"
#include "VoxelStepper.h"
#include "RenderThread.h"
#include "ProgressiveOutput.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
class RayTracer {
public:
RayTracer(EngineWidget* widget, ProgressBox* progressBox, bool inGUI);
QImage calculateImage(int width, int height);
void setParameter(QString param, QString value);
bool wasCancelled() { return userCancelled; }
private:
bool progressiveGUI;
ProgressiveOutput* progressiveOutput;
EngineWidget* engine;
void startJobs(ProgressBox* progress);
QList<Object3D*> objects;
float xmin;
float ymin;
float xmax;
float ymax;
int voxelSteps;
int windowWidth;
int windowHeight;
// Matrices from the OpenGL view.
GLdouble modelView[16];
GLdouble projection[16];
GLint viewPort[16];
long aaPixels;
bool userCancelled;
int sizeX;
int sizeY;
int maxThreads;
QVector<RenderThread*> threads;
AtomicCounter nextUnit;
AtomicCounter completedUnits;
int maxUnits;
RenderThread rt;
bool progressiveRender;
ProgressBox* progressBox;
};
}
}

View File

@ -1,388 +0,0 @@
#include "RenderThread.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
RenderThread::RenderThread() {
backgroundColor = Vector3f(0,0,0);
aoSamples = 1;
aaSamples = 8;
useShadows = true;
copy = false;
lightPos = Vector3f(0,0,0);
dofCenter = 0;
dofFalloff = 0;
sampler = 0;
terminated = false;
//filter = new GaussianFilter(0.75,1);
//filter = new TriangleFilter(1);
filter = new BoxFilter();
maxDepth = 2;
}
RenderThread::~RenderThread() {
delete accelerator;
delete sampler;
if (!copy) delete (filter);
}
RenderThread::RenderThread(const RenderThread& other) : QThread() {
rayIDs = other.rayIDs;
terminated = false;
frontStart = other.frontStart;
frontX = other.frontX;
frontY = other.frontY;
backStart = other.backStart;
backX = other.backX;
backY = other.backY;
lightPos = other.lightPos;
backgroundColor = other.backgroundColor;
accelerator = new VoxelStepper(*other.accelerator);
accelerator->setCopy(true);
aoSamples = other.aoSamples;
maxDepth = other.maxDepth;
width = other.width;
height = other.height;
useShadows = other.useShadows;
w = other.w;
h = other.h;
task = other.task;
nextUnit = other.nextUnit;
completedUnits = other.completedUnits;
maxUnits = other.maxUnits;
aaSamples = other.aaSamples;
dofCenter = other.dofCenter;
dofFalloff = other.dofFalloff;
copy = true;
rayID = 0;
pixels = 0;
checks = 0;
rg.randomizeUniformCounter(); // to avoid coherence between threads
sampler = other.sampler->clone(&rg);
progressiveOutput = other.progressiveOutput;
filter = other.filter;
};
double RenderThread::getAOStrength(Object3D* object, Vector3f objectNormal, Vector3f objectIntersection) {
if (aoSamples == 0 || object==0) return 1.0;
double tests = 0;
double hits = 0;
for (int ix = 0; ix < aoSamples*aoSamples; ix++) {
Vector3f random = sampler->getAODirection(rayNumber*aoSamples*aoSamples+ix);
if (Vector3f::dot(random, objectNormal)<0) random = random*-1.0; // Only check away from surface.
random.normalize();
double maxT = 0;
QList<Object3D*>* list = accelerator->setupRay(objectIntersection,random, maxT);
RayInfo ri;
ri.startPoint = objectIntersection;
ri.lineDirection = random;
bool occluded = false;
while (list != 0 && !occluded) {
// check objects
for (int i = 0; i < list->size(); i++) {
if (list->at(i) == object) continue; // self-shadow?
occluded = list->at(i)->intersectsRay(&ri);
if (ri.intersection < 1E-5) occluded = false;
if (occluded) break;
}
if (!occluded) list = accelerator->advance(maxT);
}
double weight = 1.0; // Vector3f::dot(random, objectNormal);
if (occluded) hits+=weight;
tests += weight;
}
return 1-hits/tests;
}
void RenderThread::raytrace(int newUnit) {
int x = newUnit-1;
float fx = x/(float)(w);
float xs = (1.0/w);
float ys = (1.0/h);
Vector3f* colors = new Vector3f[h];
for (int y = 0; y < h; y++) {
float weightSum = 0.0;
sampler->prepareSamples(aaSamples,aoSamples);
float fy = y/(float)(h);
Vector3f color(0,0,0);
for (int i = 0; i < aaSamples*aaSamples; i++) {
rayNumber = i;
Vector3f ls = sampler->getAASample(rayNumber);
float weight = 1.0;
weightSum += weight;
color = color + weight*rayCastPixel(fx+ls.x()*xs,fy+ls.y()*ys);
}
colors[y] = color / weightSum;
}
progressiveOutput->addColumn(x,colors);
delete[] colors;
};
void RenderThread::raytraceProgressive(int newUnit) {
// rayNumber = (newUnit-1);
RandomNumberGenerator rg;
rg.setSeed(0);
double* weights = new double[w*h];
Vector3f* colors = new Vector3f[w*h];
for (int i = 0; i < w*h; i++) { weights[i] = 0; colors[i] = Vector3f(0,0,0); }
float xs = (1.0/w);
float ys = (1.0/h);
sampler->prepareSamples(aaSamples,aoSamples);
int extent = filter->getExtent();
for (int y = 0; y < h; y++) {
int yFrom = y - extent; if (yFrom<0) yFrom=0;
int yTo = y + extent; if (yTo>=h) yTo=h-1;
float fy = y*ys;
if (terminated) break;
for (int x = 0; x < w; x++) {
rayNumber = ((newUnit-1) + rg.getInt(maxUnits)) % maxUnits;
int xFrom = x - extent; if (xFrom<0) xFrom=0;
int xTo = x + extent; if (xTo>=w) xTo=w-1;
float fx = x*xs;
Vector3f ls = sampler->getAASample(rayNumber);
Vector3f sample = rayCastPixel(fx+ls.x()*xs,fy+ls.y()*ys);
for (int xf = xFrom; xf<= xTo; xf++) {
for (int yf = yFrom; yf<= yTo; yf++) {
float dx = (xf - x)+ls.x();
float dy = (yf - y)+ls.y();
float wi = filter->getWeight(dx*dx,dy*dy);
colors[xf+yf*w] = colors[xf+yf*w] + wi*sample;
weights[xf+yf*w] += wi;
}
}
}
}
progressiveOutput->addIteration(colors, weights);
delete[] weights;
delete[] colors;
};
void RenderThread::run() {
int newUnit = nextUnit->increase();
while (newUnit <= maxUnits) {
// do work here...
switch (task) {
case Raytrace: raytrace(newUnit); break;
case RaytraceProgressive: raytraceProgressive(newUnit); break;
default: throw(1);
}
completedUnits->increase();
newUnit = nextUnit->increase();
}
}
void RenderThread::setCounters(AtomicCounter* nextUnit, AtomicCounter* completedUnits, int maxUnits)
{
this->nextUnit = nextUnit;
this->completedUnits = completedUnits;
this->maxUnits = maxUnits;
}
void RenderThread::alloc(int w, int h) {
this->w = w;
this->h = h;
rayID = 0;
pixels = 0;
checks = 0;
}
Vector3f RenderThread::rayCastPixel(float x, float y) {
Vector3f startPoint = frontStart + frontX*x + frontY*y;
Vector3f endPoint = backStart + backX*x + backY*y;
if (dofCenter == 0) {
Vector3f direction = endPoint - startPoint;
return rayCast(startPoint, direction, 0);
} else {
Vector3f centerPoint =(endPoint-startPoint)* dofCenter+ startPoint;
// --- Uniform Disc Sampling
Vector3f displace = sampler->getLensSample(rayNumber)*dofFalloff;
Vector3f newStartPoint = frontStart + frontX*(x+displace.x())+ frontY*(y+displace.y());
Vector3f direction = (centerPoint - newStartPoint)*(1/dofCenter);
return rayCast(newStartPoint, direction, 0);
}
}
Vector3f RenderThread::rayCast(Vector3f startPoint, Vector3f direction, Object3D* excludeThis, int level) {
if (level>maxDepth) return Vector3f(backgroundColor.x(),backgroundColor.y(),backgroundColor.z());
rayID++;
pixels++;
double lengthToClosest = -1;
Vector3f foundNormal;
GLfloat foundColor[4];
for (int i = 0; i < 4; i++) foundColor[i] = 0;
Object3D* bestObj = 0;
double maxT = 0;
RayInfo ri;
QList<Object3D*>* list = accelerator->setupRay(startPoint, direction, maxT);
ri.startPoint = startPoint;
ri.lineDirection = direction;
while (list != 0) {
// check objects
for (int i = 0; i < list->count(); i++) {
checks++;
if (list->at(i) == excludeThis) continue;
// Check if we already tested this...
if (rayIDs[list->at(i)->getObjectID()] == rayID) continue;
bool found = list->at(i)->intersectsRay(&ri);
rayIDs[list->at(i)->getObjectID()]= rayID;
if (!found) continue;
if ((ri.intersection<1E-7)) continue;
if ((ri.intersection>0) && ((ri.intersection <= lengthToClosest) || (lengthToClosest == -1))) {
// We hit something and it was closer to us than the object before...
foundNormal = ri.normal;
for (int ix = 0; ix < 4; ix++) foundColor[ix] = ri.color[ix];
lengthToClosest = ri.intersection;
bestObj = list->at(i);
}
}
if (bestObj != 0 && lengthToClosest < maxT) break;
list = accelerator->advance(maxT);
}
// Now we can calculate the lightning.
if (lengthToClosest>0) {
// iPoint is the intersection point in 3D.
Vector3f iPoint = startPoint + direction*lengthToClosest;
Vector3f lightDirection = (lightPos-iPoint);
double light = 0;
// This is a Phong lightning model, see e.g. (http://ai.autonomy.net.au/wiki/Graphics/Illumination)
// -- Diffuse light
double diffuse = bestObj->getPrimitiveClass()->diffuse*(Vector3f::dot(foundNormal, (lightDirection).normalized()));
if (diffuse<0) diffuse = 0;
light += diffuse;
// -- Specular light
double spec = 0;
double dot = Vector3f::dot(foundNormal, lightDirection);
if (dot<0.1) {
} else {
Vector3f reflected = foundNormal*dot*2 - lightDirection;
reflected.normalize();
spec = -(Vector3f::dot(reflected, (direction).normalized()));
if (spec< 0.1) {
spec = 0;
} else {
spec = bestObj->getPrimitiveClass()->specular*pow(spec,50);
if (spec<0) spec = 0;
}
}
light += spec;
// -- Ambient light
double aoStrength = 1.0;
// We will only check for AO at first intersection...
if (level == 0) aoStrength = getAOStrength(bestObj, foundNormal, iPoint);
double ambient = bestObj->getPrimitiveClass()->ambient*aoStrength;
light += ambient;
// -- calculate shadow...
// TODO: Calculate shadow in transperant media
bool inShadow = false;
if (useShadows) {
double maxT = 0;
QList<Object3D*>* list = accelerator->setupRay(iPoint,(lightPos-iPoint), maxT);
ri.startPoint = iPoint;
ri.lineDirection = lightPos-iPoint;
while (list != 0 && !inShadow) {
// check objects
for (int i = 0; i < list->size(); i++) {
if (list->at(i) == bestObj) continue; // self-shadow? (probably not neccesary, since the specular light will be negative)
inShadow = list->at(i)->intersectsRay(&ri);
if (ri.intersection < 1E-5 || ri.intersection > 1) inShadow = false;
if (ri.color[3]<1) inShadow=false;
if (inShadow) break;
}
if (!inShadow) list = accelerator->advance(maxT);
}
}
if (useShadows && inShadow) light=ambient; // drop-shadow strength (only ambient light...)
if (light < 0) light = 0;
foundColor[0] = foundColor[0]*light;
foundColor[1] = foundColor[1]*light;
foundColor[2] = foundColor[2]*light;
if (foundColor[3] < 1) {
Vector3f color = rayCast(iPoint, direction, bestObj, level+1);
foundColor[0] = foundColor[0]*(foundColor[3]) + color.x()*(1-foundColor[3]);
foundColor[1] = foundColor[1]*(foundColor[3]) + color.y()*(1-foundColor[3]);
foundColor[2] = foundColor[2]*(foundColor[3]) + color.z()*(1-foundColor[3]);
}
double reflection = bestObj->getPrimitiveClass()->reflection;
if (reflection > 0) {
Vector3f nDir = foundNormal*(-2)*Vector3f::dot(foundNormal, direction)/foundNormal.sqrLength() + direction;
//Vector3f v = rg.getUniform3D();
//if (Vector3f::dot(v,nDir)<0) v = -v;
//nDir = v+nDir;
Vector3f color = rayCast(iPoint, nDir, bestObj, level+1);
foundColor[0] = foundColor[0]*(1-reflection) + color.x()*reflection;
foundColor[1] = foundColor[1]*(1-reflection) + color.y()*reflection;
foundColor[2] = foundColor[2]*(1-reflection) + color.z()*reflection;
}
color = Vector3f(foundColor[0],foundColor[1],foundColor[2]);
return color;
} else {
color = Vector3f(backgroundColor.x(),backgroundColor.y(),backgroundColor.z());
return color;
}
}
void RenderThread::setObjects(int count) {
rayIDs = QVector<int>(count, -1);
}
}
}

View File

@ -1,88 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "../Object3D.h"
#include "AtomicCounter.h"
#include "VoxelStepper.h"
#include "Sampler.h"
#include "ProgressiveOutput.h"
#include "SyntopiaCore/Math/Random.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
class RenderThread : public QThread {
public:
enum Task { Raytrace, RaytraceProgressive } ;
RenderThread();
~RenderThread();
void setTask(Task task) { this->task = task; };
RenderThread(const RenderThread& other);
void raytrace(int newUnit);
void raytraceProgressive(int newUnit);
void setCounters(AtomicCounter* nextUnit, AtomicCounter* completedUnits, int maxUnits);
void alloc(int w, int h);
void setObjects(int count);
static void msleep(int i) { QThread::msleep(i); }
void run();
Vector3f rayCastPixel(float x, float y);
void setTerminated(bool value) { terminated = value; }
void seed(int value) { rg.setSeed(value); };
double getAOStrength(Object3D* object, Vector3f objectNormal, Vector3f objectIntersection);
Vector3f rayCast(Vector3f startPoint, Vector3f direction, Object3D* excludeThis, int level = 0);
private:
Task task;
AtomicCounter* nextUnit;
AtomicCounter* completedUnits;
int maxUnits;
int w;
int h;
bool terminated;
Vector3f frontStart;
Vector3f frontX;
Vector3f frontY;
Vector3f backStart;
Vector3f backX;
Vector3f backY;
Vector3f lightPos;
Vector3f backgroundColor;
int rayID;
QVector<int> rayIDs;
int pixels;
int maxDepth;
int checks;
VoxelStepper* accelerator;
int aoSamples;
int totalAOCasts;
int aaSamples;
int width;
int height;
bool useShadows;
double dofCenter;
double dofFalloff;
Math::RandomNumberGenerator rg;
Vector3f color;
bool copy;
Sampler* sampler;
ProgressiveOutput* progressiveOutput;
friend class RayTracer;
int rayNumber;
Filter* filter;
};
}
}

View File

@ -1,174 +0,0 @@
#include "Sampler.h"
namespace SyntopiaCore {
namespace GLEngine {
// Out-of-line for easier linking
Filter::~Filter() = default;
Sampler::Sampler(Math::RandomNumberGenerator* rg) : rg(rg) {
}
Sampler::~Sampler() {
}
namespace {
// This code is based on the method in Physical Based Rendering p. 666, and the Sunflow implementation
Vector3f concentricSampleDisk(float u1, float u2) {
float r, angle;
float sx = 2 * u1 - 1;
float sy = 2 * u2 - 1;
if (sx == 0.0 && sy == 0.0) return Vector3f(0,0,0);
if (sx >= -sy) {
if (sx > sy) {
r = sx;
if (sy > 0.0) angle = sy/r; else angle = 8.0f + sy/r;
}
else {
r = sy;
angle = 2.0f - sx/r;
}
}
else {
if (sx <= sy) {
r = -sx;
angle = 4.0f - sy/r;
}
else {
r = -sy;
angle = 6.0f + sx/r;
}
}
angle *= 3.1415f / 4.f;
return Vector3f(r * cosf(angle),r * sinf(angle),0);
}
}
Vector3f StratifiedSampler::getAODirection(int index) {
if (index>=aoSamples.count()) throw 1;
return aoSamples[index];
}
Vector3f StratifiedSampler::getAASample(int index) {
if (index>=aaSamples.count()) throw 1;
return aaSamples[index];
}
Vector3f StratifiedSampler::getLensSample(int index) {
if (index>=lensSamples.count()) throw 1;
return lensSamples[index];
}
// Based on the Physical Based Rendering book
Vector3f StratifiedSampler::sampleSphere(double u1, double u2) {
double z = 1.0 - 2.0*u1;
double r = 0;
if (1.0-z*z > 0) r = sqrt(1.0-z*z);
double phi = 2.0 * 3.1415926 * u2;
double x = r * cos(phi);
double y = r * sin(phi);
return Vector3f(x,y,z);
}
void StratifiedSampler::prepareSamples(int nAASamplesSqrt, int nAOSamplesSqrt) {
int nSqrt = nAASamplesSqrt*nAOSamplesSqrt;
aoSamples = QVector<Vector3f>(nSqrt*nSqrt);
aaSamples = QVector<Vector3f>(nAASamplesSqrt*nAASamplesSqrt);
lensSamples = QVector<Vector3f>(nAASamplesSqrt*nAASamplesSqrt);
int count = 0;
for (int i = 0; i < nSqrt; i++) {
for (int j = 0; j < nSqrt; j++) {
// we need a uniform number in the interval
// [i/nSqrt;(i+1)/nSqrt]
double x = rg->getDouble( ((double)i)/(double)nSqrt,((double)(i+1.0))/(double)nSqrt);
double y = rg->getDouble( ((double)j)/(double)nSqrt,((double)(j+1.0))/(double)nSqrt);
aoSamples[count++] = sampleSphere(x,y);
}
}
count = 0;
for (int i = 0; i < nAASamplesSqrt; i++) {
for (int j = 0; j < nAASamplesSqrt; j++) {
// we need a uniform number in the interval
// [i/nSqrt;(i+1)/nSqrt]
double x = rg->getDouble( ((double)i)/(double)nAASamplesSqrt,((double)(i+1.0))/(double)nAASamplesSqrt);
double y = rg->getDouble( ((double)j)/(double)nAASamplesSqrt,((double)(j+1.0))/(double)nAASamplesSqrt);
aaSamples[count] = Vector3f(x-0.5,y-0.5,1);
x = rg->getDouble( ((double)i)/(double)nSqrt,((double)(i+1.0))/(double)nSqrt);
y = rg->getDouble( ((double)j)/(double)nSqrt,((double)(j+1.0))/(double)nSqrt);
lensSamples[count++] = concentricSampleDisk(x,y);
}
}
// We randomize the samples to avoid coherence.
aaSamples = rg->randomize(aaSamples);
lensSamples = rg->randomize(lensSamples);
};
/// --- Progressive versions...
Vector3f ProgressiveStratifiedSampler::getAODirection(int index) {
if (index>=aoSamplesSqrt*aoSamplesSqrt) throw 1;
int j = index / aoSamplesSqrt;
int i = index % aoSamplesSqrt;
double x = rg->getDouble( ((double)i)/(double)aoSamplesSqrt,((double)(i+1.0))/(double)aoSamplesSqrt);
double y = rg->getDouble( ((double)j)/(double)aoSamplesSqrt,((double)(j+1.0))/(double)aoSamplesSqrt);
return sampleSphere(x,y);
}
Vector3f ProgressiveStratifiedSampler::getAASample(int ix) {
if (ix>=(aaSamplesSqrt*aaSamplesSqrt)) throw 1;
int index = (aaSamplesSqrt*aaSamplesSqrt-1)-aaOrder[ix];
int i = index / aaSamplesSqrt;
int j = index % aaSamplesSqrt;
double x = rg->getDouble( ((double)i)/(double)aaSamplesSqrt,((double)(i+1.0))/(double)aaSamplesSqrt);
double y = rg->getDouble( ((double)j)/(double)aaSamplesSqrt,((double)(j+1.0))/(double)aaSamplesSqrt);
return Vector3f(x-0.5,y-0.5,1);
}
Vector3f ProgressiveStratifiedSampler::getLensSample(int ix) {
if (ix>=(aaSamplesSqrt*aaSamplesSqrt)) throw 1;
int index = aaOrder[ix];
int i = index / aaSamplesSqrt;
int j = index % aaSamplesSqrt;
double x = rg->getDouble( ((double)i)/(double)aaSamplesSqrt,((double)(i+1.0))/(double)aaSamplesSqrt);
double y = rg->getDouble( ((double)j)/(double)aaSamplesSqrt,((double)(j+1.0))/(double)aaSamplesSqrt);
//return rg->getUniform2D();
return concentricSampleDisk(x,y);
}
// Based on the Physical Based Rendering book
Vector3f ProgressiveStratifiedSampler::sampleSphere(double u1, double u2) {
double z = 1.0 - 2.0*u1;
double r = 0;
if (1.0-z*z > 0) r = sqrt(1.0-z*z);
double phi = 2.0 * 3.1415926 * u2;
double x = r * cos(phi);
double y = r * sin(phi);
return Vector3f(x,y,z);
}
void ProgressiveStratifiedSampler::prepareSamples(int nAASamplesSqrt, int nAOSamplesSqrt) {
this->aoSamplesSqrt = nAASamplesSqrt*nAOSamplesSqrt;
this->aaSamplesSqrt = nAASamplesSqrt;
};
Sampler* ProgressiveStratifiedSampler::clone(Math::RandomNumberGenerator* rg) {
ProgressiveStratifiedSampler* ps = new ProgressiveStratifiedSampler(rg);
ps->aaOrder = aaOrder;
return ps;
}
}
}

View File

@ -1,124 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Random.h"
#include "SyntopiaCore/Math/Vector3.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
class Filter {
public:
Filter() {};
virtual ~Filter();
virtual float getWeight(float xSqr, float ySqr) = 0;
virtual int getExtent() = 0;
};
class BoxFilter : public Filter {
public:
BoxFilter() {};
virtual float getWeight(float, float) { return 1.0; }
virtual int getExtent() { return 0; };
};
class GaussianFilter : public Filter {
public:
GaussianFilter(double ext, double alpha) : extent(ext), alpha(alpha) {
this->extent = int(0.5+ext);
s = -exp(-alpha*(ext*ext));
};
virtual float getWeight(float xSqr, float ySqr) {
return (gaussian(xSqr)*gaussian(ySqr));
};
float gaussian(float v) {
float a= exp(-alpha*v)+s;
return a > 0 ? a : 0;
}
virtual int getExtent() { return extent; };
private:
int extent;
double s;
double alpha;
};
class TriangleFilter : public Filter {
public:
TriangleFilter(double halfwidth) : halfwidth(halfwidth) {
this->extent = int(halfwidth-0.5+1);
};
virtual float getWeight(float xSqr, float ySqr) {
return (triangle(sqrt(xSqr))*triangle(sqrt(ySqr)));
};
float triangle(float v) {
float a= 1-v/halfwidth;
return a > 0 ? a : 0;
}
virtual int getExtent() { return extent; };
private:
int extent;
double halfwidth;
};
// A simple sampler. Draws uniform numbers, but no stratification.
class Sampler {
public:
Sampler(Math::RandomNumberGenerator* rg);
virtual ~Sampler();
virtual Vector3f getAASample(int /*index*/) { return Vector3f(rg->getDouble(-0.5,0.5), rg->getDouble(-0.5,0.5),1.0); }
virtual Vector3f getAODirection(int /*index*/) { return rg->getUniform3D(); }
virtual Vector3f getLensSample(int /*index*/) { return rg->getUniform2D(); }
virtual void prepareSamples(int /*nSamplesSqrt*/, int /*nAOSamplesSqrt*/) {};
virtual Sampler* clone(Math::RandomNumberGenerator* rg) { return new Sampler(rg); }
protected:
Math::RandomNumberGenerator* rg;
};
// Stratified sampling
class StratifiedSampler : public Sampler {
public:
StratifiedSampler(Math::RandomNumberGenerator* rg) : Sampler(rg) {};
virtual Vector3f getAASample(int index);
virtual Vector3f getAODirection(int index);
virtual Vector3f getLensSample(int index);
Vector3f sampleSphere(double u1, double u2);
virtual void prepareSamples(int nSamplesSqrt, int nAOSamplesSqrt);
virtual Sampler* clone(Math::RandomNumberGenerator* rg) { return new StratifiedSampler(rg); }
private:
QVector<Vector3f> aoSamples;
QVector<Vector3f> aaSamples;
QVector<Vector3f> lensSamples;
};
// Stratified sampling
class ProgressiveStratifiedSampler : public Sampler {
public:
ProgressiveStratifiedSampler(Math::RandomNumberGenerator* rg) :
Sampler(rg) {}
virtual Vector3f getAASample(int index);
virtual Vector3f getAODirection(int index);
virtual Vector3f getLensSample(int index);
Vector3f sampleSphere(double u1, double u2);
virtual void prepareSamples(int nSamplesSqrt, int nAOSamplesSqrt);
virtual Sampler* clone(Math::RandomNumberGenerator* rg);
void setAAOrder(QVector<int> aaOrder) { this->aaOrder = aaOrder; }
private:
int aoSamplesSqrt;
int aaSamplesSqrt;
QVector<int> aaOrder;
};
}
}

View File

@ -1,193 +0,0 @@
#include "VoxelStepper.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
VoxelStepper::VoxelStepper(Vector3f minPos, Vector3f maxPos, int steps)
: steps(steps), minPos(minPos), maxPos(maxPos), size(( maxPos - minPos)/(double)steps) {
currentT = 0;
copy = false;
grid = new QList<Object3D*>[steps*steps*steps];
for (int i = 0; i < steps*steps*steps; i++) grid[i] = QList<Object3D*>();
};
VoxelStepper::~VoxelStepper() { if (!copy) delete[] grid; };
void VoxelStepper::registerObject(Object3D* obj) {
// Simple method - check all cells intersecting the objects bounding boxes.
obj->prepareForRaytracing();
Vector3f from;
Vector3f to;
obj->getBoundingBox(from,to);
from = from - minPos;
to = to - minPos;
int xStart = floor(from.x()/size.x());
int xEnd = ceil(to.x()/size.x());
int yStart = floor(from.y()/size.y());
int yEnd = ceil(to.y()/size.y());
int zStart = floor(from.z()/size.z());
int zEnd = ceil(to.z()/size.z());
if (xStart < 0) xStart = 0;
if (yStart < 0) yStart = 0;
if (zStart < 0) zStart = 0;
if (xEnd > (int)steps) xEnd = steps;
if (yEnd > (int)steps) yEnd = steps;
if (zEnd > (int)steps) zEnd = steps;
for (unsigned int x = xStart; x < (unsigned int)xEnd; x++) {
for (unsigned int y = yStart; y < (unsigned int)yEnd; y++) {
for (unsigned int z = zStart; z < (unsigned int)zEnd; z++) {
if (obj->intersectsAABB(minPos + Vector3f(size.x()*x,size.y()*y,size.z()*z),minPos + Vector3f(size.x()*(x+1),size.y()*(y+1),size.z()*(z+1)) )) {
grid[x+y*steps+z*steps*steps].append(obj);
}
}
}
}
}
QList<Object3D*>* VoxelStepper::setupRay(Vector3f pos, Vector3f dir, double& maxT) {
this->pos = pos;
this->dir = dir;
currentT = 0;
const Vector3f ro = pos - minPos;
cx = floor(ro.x() / size.x());
cy = floor(ro.y() / size.y());
cz = floor(ro.z() / size.z());
if ((cx < 0 || cx >= steps) ||
(cy < 0 || cy >= steps) ||
(cz < 0 || cz >= steps)) {
// we are outside grid.
// advance ray to inside grid.
bool found = false;
double p;
if (dir.x() > 0) {
p = (minPos.x()-pos.x())/dir.x();
cx = 0;
} else {
p = (maxPos.x()-pos.x())/dir.x();
cx = steps-1;
}
Vector3f v = pos + dir*p - minPos;
cy = floor(v.y() / size.y());
cz = floor(v.z() / size.z());
if ((cy >= 0 && cy < steps) && (cz >= 0 && cz < steps)) {
found = true;
pos = v+minPos;
}
if (!found) {
if (dir.y() > 0) {
p = (minPos.y()-pos.y())/dir.y();
cy = 0;
} else {
p = (maxPos.y()-pos.y())/dir.y();
cy = steps-1;
}
Vector3f v = pos + dir*p - minPos;
cx = floor(v.x() / size.x());
cz = floor(v.z() / size.z());
if ((cx >= 0 && cx < steps) && (cz >= 0 && cz < steps)) {
pos = v+minPos;
found = true;
}
}
if (!found) {
if (dir.z() > 0) {
p = (minPos.z()-pos.z())/dir.z();
cz = 0;
} else {
p = (maxPos.z()-pos.z())/dir.z();
cz = steps-1;
}
Vector3f v = pos + dir*p - minPos;
cx = floor(v.x() / size.x());
cy = floor(v.y() / size.y());
if ((cy >= 0 && cy < steps) && (cx >= 0 && cx < steps)) {
pos = v+minPos;
found = true;
}
}
currentT = p;
// We do not intersect grid.
if (!found) return nullptr;
}
stepX = (dir.x() > 0) ? 1 : -1;
stepY = (dir.y() > 0) ? 1 : -1;
stepZ = (dir.z() > 0) ? 1 : -1;
tDeltaX = stepX*size.x() / dir.x();
tDeltaY = stepY*size.y() / dir.y();
tDeltaZ = stepZ*size.z() / dir.z();
Vector3f orv = pos- (minPos + Vector3f(size.x()*cx, size.y()*cy, size.z()*cz));
tMaxX = stepX*orv.x()/dir.x();
if (stepX>0) tMaxX = tDeltaX - tMaxX;
tMaxY = stepY*orv.y()/dir.y();
if (stepY>0) tMaxY = tDeltaY - tMaxY;
tMaxZ = stepZ*orv.z()/dir.z();
if (stepZ>0) tMaxZ = tDeltaZ - tMaxZ;
// Now pos is advanced properly.
// cx,cy,cz contains current cell.
QList<Object3D*>* list = &grid[cx+cy*steps+cz*steps*steps];
if (list && (list->count() == 0)) {
list = advance(maxT);
} else {
maxT = currentT + minn(tMaxX, tMaxY, tMaxZ);
}
return list;
}
QList<Object3D*>* VoxelStepper::advance(double& maxT) {
QList<Object3D*>* list = 0;
do {
if(tMaxX < tMaxY) {
if(tMaxX < tMaxZ) {
cx += stepX;
if (cx >= steps || cx < 0) return 0;
tMaxX = tMaxX + tDeltaX;
} else {
cz += stepZ;
if (cz >= steps || cz < 0) return 0;
tMaxZ = tMaxZ + tDeltaZ;
}
} else {
if(tMaxY < tMaxZ) {
cy += stepY;
if (cy >= steps || cy < 0) return 0;
tMaxY = tMaxY + tDeltaY;
} else {
cz += stepZ;
if (cz >= steps || cz < 0) return 0;
tMaxZ = tMaxZ + tDeltaZ;
}
}
list = &grid[cx+cy*steps+cz*steps*steps];
if (list && (list->count() == 0)) list = 0; // Continue until we find an non-empty list.
} while(list == 0);
maxT = currentT + minn(tMaxX, tMaxY, tMaxZ);
return(list);
}
}
}

View File

@ -1,56 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "../Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
using namespace SyntopiaCore::Math;
/// See here for details about this approach:
/// http://www.devmaster.net/articles/raytracing_series/part4.php
class VoxelStepper {
public:
VoxelStepper(Vector3f minPos, Vector3f maxPos, int steps) ;
~VoxelStepper();
void registerObject(Object3D* obj) ;
QList<Object3D*>* setupRay(Vector3f pos, Vector3f dir, double& maxT);
inline double minn(double a, double b, double c) {
if (a<b) return (a<c ? a : c);
return (b<c ? b : c);
}
QList<Object3D*>* advance(double& maxT);
void setCopy(bool value) { this->copy = value; }
private:
bool copy;
double currentT;
double tDeltaX;
double tDeltaY;
double tDeltaZ;
double tMaxX;
double tMaxY;
double tMaxZ;
int stepX;
int stepY;
int stepZ;
int cx;
int cy;
int cz;
Vector3f pos;
Vector3f dir;
int steps;
Vector3f minPos;
Vector3f maxPos;
const Vector3f size;
QList<Object3D*>* grid;
};
}
}

View File

@ -1,105 +0,0 @@
#include "Sphere.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
//GLUquadric* Sphere::myQuad = 0;
int Sphere::displayListIndex = 0;
Sphere::Sphere(SyntopiaCore::Math::Vector3f center, float radius) : center(center), radius(radius) {
myQuad = gluNewQuadric();
gluQuadricDrawStyle(myQuad, GLU_FILL);
/// Bounding box
Vector3f v = Vector3f(radius,radius,radius);
from = center-v;
to = center+v;
if (displayListIndex == 0) {
displayListIndex = glGenLists(1);
glNewList(displayListIndex, GL_COMPILE);
gluSphere(myQuad, 1, 7,7);
glEndList();
}
};
Sphere::~Sphere() {
gluDeleteQuadric(myQuad);
};
void Sphere::draw() const {
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, primaryColor );
if (primaryColor[3] < 1) {
glEnable( GL_BLEND );
}
glPushMatrix();
glTranslatef( center.x(), center.y(), center.z() );
if (displayListIndex!=0) {
glScalef(radius,radius,radius);
glCallList(displayListIndex);
} else {
gluSphere(myQuad, radius, 7, 7);
}
glPopMatrix();
};
bool Sphere::intersectsRay(RayInfo* ri) {
// Following: http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/
double a = ri->lineDirection.sqrLength();
double b = 2*(Vector3f::dot(ri->lineDirection, (ri->startPoint - center)));
double c = (center-ri->startPoint).sqrLength() - radius*radius;
double d = b*b-4*a*c;
if (d<0) {
ri->intersection = -1;
return false;
} else {
// We always choose the negative solution, since it will be closest to the startPoint. (If intersection is positive)
ri->intersection = (b - sqrt(d))/(-2*a);
double intersection2 = (b + sqrt(d))/(-2*a);
if (intersection2<ri->intersection) ri->intersection = intersection2;
ri->normal = (ri->startPoint + ri->lineDirection*ri->intersection)-center;
ri->normal.normalize();
for (int i = 0; i < 4; i++) ri->color[i] = primaryColor[i];
return true;
}
};
bool Sphere::intersectsAABB(Vector3f from, Vector3f to) {
// Based on http://www.gamasutra.com/features/19991018/Gomez_4.htm
float s, d = 0;
//find the square of the distance
//from the sphere to the box
for( long i=0 ; i<3 ; i++ )
{
if( center[i] < from[i] )
{
s = center[i] - from[i];
d += s*s;
} else if( center[i] > to[i] ) {
s = center[i] - to[i];
d += s*s;
}
}
return d <= radius*radius;
};
}
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
namespace SyntopiaCore {
namespace GLEngine {
class Sphere : public Object3D {
public:
Sphere(SyntopiaCore::Math::Vector3f center, float radius);
virtual ~Sphere();
virtual QString name() { return "Sphere"; }
virtual void draw() const;
void setCenter(SyntopiaCore::Math::Vector3f center) { this->center = center; }
virtual bool intersectsRay(RayInfo* /*rayInfo*/);
virtual bool intersectsAABB(SyntopiaCore::Math::Vector3f /*from*/, SyntopiaCore::Math::Vector3f /*to*/);
private:
SyntopiaCore::Math::Vector3f center;
float radius;
GLUquadric* myQuad;
static int displayListIndex;
};
}
}

View File

@ -1,69 +0,0 @@
#include "Triangle.h"
#include "SyntopiaCore/Math/Vector3.h"
using namespace SyntopiaCore::Math;
namespace SyntopiaCore {
namespace GLEngine {
Triangle::Triangle(SyntopiaCore::Math::Vector3f p1 ,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3) : p1(p1), p2(p2), p3(p3)
{
from = p1;
to = p3;
Vector3f n1 = Vector3f::cross(p3-p1, p2-p1);
n1.normalize();
rt = RaytraceTriangle(p1,p2,p3,n1,n1,n1);
rt.expandBoundingBox(from,to);
rt.cullBackFaces = false;
};
Triangle::~Triangle() { };
void Triangle::prepareForRaytracing() {
rt.setColor(primaryColor[0],primaryColor[1],primaryColor[2], primaryColor[3]);
}
void Triangle::draw() const {
glPushMatrix();
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, primaryColor );
glPolygonMode(GL_FRONT, GL_FILL);
glEnable(GL_CULL_FACE);
glEnable (GL_LIGHTING);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMateriali(GL_FRONT, GL_SPECULAR, 30);
glMateriali(GL_FRONT, GL_SHININESS, 127);
glBegin(GL_TRIANGLES);
vertex3n(p1,p2,p3);
glEnd();
glPopMatrix();
};
bool Triangle::intersectsRay(RayInfo* ri) {
return rt.intersectsRay(ri);
};
bool Triangle::intersectsAABB(Vector3f from2, Vector3f to2) {
return (from.x() < to2.x()) && (to.x() > from2.x()) &&
(from.y() < to2.y()) && (to.y() > from2.y()) &&
(from.z() < to2.z()) && (to.z() > from2.z());
};
}
}

View File

@ -1,33 +0,0 @@
#pragma once
#include "SyntopiaCore/Math/Vector3.h"
#include "Object3D.h"
#include "RaytraceTriangle.h"
namespace SyntopiaCore {
namespace GLEngine {
class Triangle : public Object3D {
public:
Triangle(SyntopiaCore::Math::Vector3f p1 ,
SyntopiaCore::Math::Vector3f p2,
SyntopiaCore::Math::Vector3f p3);
virtual ~Triangle();
virtual QString name() { return "Triangle"; }
virtual void draw() const;
virtual void prepareForRaytracing();
virtual bool intersectsRay(RayInfo* /*rayInfo*/);
virtual bool intersectsAABB(SyntopiaCore::Math::Vector3f /*from*/, SyntopiaCore::Math::Vector3f /*to*/);
private:
RaytraceTriangle rt;
SyntopiaCore::Math::Vector3f p1;
SyntopiaCore::Math::Vector3f p2;
SyntopiaCore::Math::Vector3f p3;
};
}
}

View File

@ -1,39 +0,0 @@
#include "ListWidgetLogger.h"
namespace SyntopiaCore {
namespace Logging {
ListWidgetLogger::ListWidgetLogger(QWidget* parent) : parent(parent) {
listWidget = new QListWidget(parent);
}
ListWidgetLogger::~ListWidgetLogger() {
}
void ListWidgetLogger::log(QString message, LogLevel priority) {
QListWidgetItem* i = new QListWidgetItem(message, listWidget);
// Levels: NoneLevel, DebugLevel, TimingLevel, InfoLevel, WarningLevel, CriticalLevel, AllLevel
if ( priority == InfoLevel ) {
i->setBackgroundColor(QColor(255,255,255));
} else if ( priority == WarningLevel ) {
parent->show();
i->setBackgroundColor(QColor(255,243,73));
} else if ( priority == CriticalLevel ) {
parent->show();
i->setBackgroundColor(QColor(255,2,0));
} else if ( priority == TimingLevel ) {
parent->show();
i->setBackgroundColor(QColor(25,255,0));
} else {
i->setBackgroundColor(QColor(220,220,220));
}
listWidget->scrollToItem(i);
}
}
}

View File

@ -1,30 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <QListWidget>
#include "Logging.h"
namespace SyntopiaCore {
namespace Logging {
class ListWidgetLogger : public Logger {
public:
ListWidgetLogger(QWidget* parent);
virtual ~ListWidgetLogger();
/// This method all loggers must implement
void log(QString message, LogLevel priority);
QListWidget* getListWidget() { return listWidget; }
private:
QListWidget* listWidget;
QWidget* parent;
};
}
}

View File

@ -1,61 +0,0 @@
#include "Logging.h"
#ifdef WIN32
#include "windows.h"
#endif
/// TODO's
/// - Nested log entris
/// - Time
/// - Setting a log view level
namespace SyntopiaCore {
namespace Logging {
QVector<Logger*> Logger::loggers;
QStack<QTime> Logger::timeStack;
QStack<QString> Logger::timeStringStack;
void LOG(QString message, LogLevel priority) {
// On Windows this allows us to see debug in the Output::Debug window while running.
#ifdef WIN32
OutputDebugStringW((LPCWSTR) (message+"\r\n").utf16());
#endif
for (int i = 0; i < Logger::loggers.size(); i++) {
Logger::loggers[i]->log(message, priority);
}
}
/// Useful aliases
void Debug(QString text) { LOG(text, DebugLevel); }
void INFO(QString text) { LOG(text, InfoLevel); }
void WARNING(QString text) { LOG(text, WarningLevel); }
void CRITICAL(QString text) { LOG(text, CriticalLevel); }
void TIME(QString text) {
LOG(text, TimingLevel);
Logger::timeStack.push(QTime::currentTime());
Logger::timeStringStack.push(text);
} ;
void TIME(int repetitions) {
QTime t = Logger::timeStack.pop();
QString s = Logger::timeStringStack.pop();
int secs = t.msecsTo(QTime::currentTime());
if (repetitions == 0) {
LOG(QString("Time: %1s for ").arg(secs/1000.0f) + s, TimingLevel);
} else {
LOG(QString("Time: %1s for %2. %3 repetitions, %4s per repetition.").arg(secs/1000.0f).arg(s)
.arg(repetitions).arg((secs/repetitions)/1000.0f), TimingLevel);
}
}; // End time...
}
}

View File

@ -1,52 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <QTime>
#include <QStack>
namespace SyntopiaCore {
namespace Logging {
/// Predefined logging levels
enum LogLevel { NoneLevel, DebugLevel, TimingLevel, InfoLevel, WarningLevel, CriticalLevel, AllLevel };
/// Abstract base class for all loggers
class Logger {
public:
/// The destructors and constructors automatically add to the list of installed loggers.
Logger() {
loggers.append(this);
}
virtual ~Logger() {
// Remove from list of available loggers.
for (int i = loggers.size()-1; i >= 0; i--) {
if (loggers[i] == this) loggers.remove(i);
}
}
/// This method all loggers must implement
virtual void log(QString message, LogLevel priority) = 0;
/// Log messages are sent to this list of loggers.
static QVector<Logger*> loggers;
static QStack<QTime> timeStack;
static QStack<QString> timeStringStack;
private:
};
void LOG(QString message, LogLevel priority);
/// Useful aliases
void Debug(QString text);
void INFO(QString text);
void TIME(QString text);
void TIME(int repetitions = 0); // End time...
void WARNING(QString text);
void CRITICAL(QString text);
}
}

View File

@ -1,11 +0,0 @@
#include "Matrix4.h"
namespace SyntopiaCore {
namespace Math {
}
}

View File

@ -1,192 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include "Vector3.h"
namespace SyntopiaCore {
namespace Math {
/// A simple class for representing 4x4 Matrices
/// The internal representation has the rows increasing fastest: index = row + col*4;
template <class scalar> class Matrix4 {
public:
/// Constructor (inits to zero value).
Matrix4() { for (unsigned int i = 0; i < 16; i++) v[i] = 0; };
/// Construct from a string (with 3x3 entries) - such as "[1 0 0 0 1 0 0 0 1]"
Matrix4(QString input, bool& succes2) {
for (unsigned int i = 0; i < 16; i++) v[i] = 0;
v[0] = 1; v[5] = 1; v[10] = 1; v[15] = 1;
input.remove('[');
input.remove(']');
QStringList sl = input.split(" ");
if (sl.size() != 9) { succes2 = false; return; }
bool succes = false;
float f = 0;
f = sl[0].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[0] = f;
f = sl[1].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[4] = f;
f = sl[2].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[8] = f;
f = sl[3].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[1] = f;
f = sl[4].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[5] = f;
f = sl[5].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[9] = f;
f = sl[6].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[2] = f;
f = sl[7].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[6] = f;
f = sl[8].toFloat(&succes); if (!succes) { succes2 = false; return; }; v[10] = f;
succes2 = true;
}
/// Identity matrix
static Matrix4<scalar> Identity() {
Matrix4<scalar> m;
m.v[0] = 1; m.v[5] = 1; m.v[10] = 1; m.v[15] = 1;
return m;
};
static Matrix4<scalar> ScaleMatrix(scalar s) {
Matrix4<scalar> m;
m.v[0] = s; m.v[5] = s; m.v[10] = s; m.v[15] = 1;
return m;
};
/// at(row, col) return a copy of the value.
scalar at(int row, int col) const { return v[row+col*4]; }
scalar at(int index) const { return v[index]; }
scalar operator() (int row, int col) const { return v[row+col*4]; }
scalar operator() (int index) const { return v[index]; }
/// getRef(row, col) returns a reference (for writing into matrix)
scalar& getRef(int row, int col) { return v[row+col*4]; }
scalar& getRef(int index) { return v[index]; }
scalar& operator() (int row, int col) { return v[row+col*4]; }
scalar& operator() (int index) { return v[index]; }
/// Internal representation (can be used in OpenGL functions)
scalar* getArray(void) { return v; }
static Matrix4<scalar> Translation(scalar x,scalar y,scalar z) {
Matrix4<scalar> m;
m(0,3) = x;
m(1,3) = y;
m(2,3) = z;
m(0,0) = 1;
m(1,1) = 1;
m(2,2) = 1;
m(3,3) = 1;
return m;
};
static Matrix4<scalar> PlaneReflection(Vector3<scalar> n) {
n.normalize();
Matrix4<scalar> m;
m(0,0) = 1.0 - 2.0*n.x()*n.x(); m(1,0) = -2.0*n.y()*n.x(); m(2,0) = -2.0*n.z()*n.x();
m(0,1) = -2.0*n.x()*n.y(); m(1,1) = 1.0 - 2.0*n.y()*n.y(); m(2,1) = -2.0*n.z()*n.y();
m(0,2) = -2.0*n.x()*n.z(); m(1,2) = -2.0*n.y()*n.z(); m(2,2) = 1.0 - 2.0*n.z()*n.z();
m(3,3) = 1;
return m;
};
/// Rotation about axis with angle
/// Taken from http://www.gamedev.net/reference/articles/605/math3d.h
static Matrix4<scalar> Rotation(Vector3<scalar> axis, scalar angle) {
Matrix4<scalar> m;
scalar c = cos(angle);
scalar s = sin(angle);
// One minus c (short name for legibility of formulai)
scalar omc = (1 - c);
if (axis.length() != 1) axis.normalize();
scalar x = axis[0];
scalar y = axis[1];
scalar z = axis[2];
scalar xs = x * s;
scalar ys = y * s;
scalar zs = z * s;
scalar xyomc = x * y * omc;
scalar xzomc = x * z * omc;
scalar yzomc = y * z * omc;
m.v[0] = x*x*omc + c;
m.v[1] = xyomc + zs;
m.v[2] = xzomc - ys;
m.v[3] = 0;
m.v[4] = xyomc - zs;
m.v[5] = y*y*omc + c;
m.v[6] = yzomc + xs;
m.v[7] = 0;
m.v[8] = xzomc + ys;
m.v[9] = yzomc - xs;
m.v[10] = z*z*omc + c;
m.v[11] = 0;
m.v[12] = 0;
m.v[13] = 0;
m.v[14] = 0;
m.v[15] = 1;
return m;
};
Matrix4<scalar> operator* (const Matrix4<scalar>& rhs) const {
Matrix4<scalar> m;
for (int x=0; x<4; x++) {
for (int y=0;y <4; y++) {
for (int i=0; i<4; i++) m.getRef(x,y) += (this->at(x,i) * rhs.at(i,y));
}
}
return m;
};
Vector3<scalar> operator* (const Vector3<scalar>& rhs) const {
Vector3<scalar> v; // Is initialized to zeros.
for (int i=0; i<3; i++) {
for (int j = 0; j < 3; j++) {
v[i] += this->at(i,j)*rhs[j];
}
v[i] += this->at(i,3);
}
return v;
};
QString toString() {
QString s = QString(" Row1 = [%1 %2 %3 %4], Row2 = [%5 %6 %7 %8]").
arg(at(0,0)).arg(at(0,1)).arg(at(0,2)).arg(at(0,3)).
arg(at(1,0)).arg(at(1,1)).arg(at(1,2)).arg(at(1,3));
QString s2 = QString(" Row3 = [%1 %2 %3 %4], Row4 = [%5 %6 %7 %8]").
arg(at(2,0)).arg(at(2,1)).arg(at(2,2)).arg(at(2,3)).
arg(at(3,0)).arg(at(3,1)).arg(at(3,2)).arg(at(3,3));
return s+s2;
}
// Only return the 3x3 part of the matrix.
QString toStringAs3x3() {
QString s = QString("[%1 %2 %3 %4 %5 %6 %7 %8 %9]").
arg(at(0,0)).arg(at(0,1)).arg(at(0,2)).
arg(at(1,0)).arg(at(1,1)).arg(at(1,2)).
arg(at(2,0)).arg(at(2,1)).arg(at(2,2))
;
return s;
}
private:
scalar v[16];
};
typedef Matrix4<float> Matrix4f ;
typedef Matrix4<double> Matrix4d ;
}
}

View File

@ -1,75 +0,0 @@
#include "Random.h"
namespace SyntopiaCore {
namespace Math {
namespace {
struct SortPair {
SortPair() {};
SortPair(int index, double sortValue) : index(index), sortValue(sortValue) {};
int index;
double sortValue;
bool operator< (const SortPair& rhs) const { return sortValue < rhs.sortValue; }
};
}
QVector<int> RandomNumberGenerator::getRandomIndices(int count) {
QVector<SortPair> sp(count);
for (int i = 0; i < count; i++) sp[i] = SortPair(i, getDouble());
qSort(sp);
QVector<int> out(count);
for (int i = 0; i < count; i++) out[i] = sp[i].index;
return out;
}
Vector3f RandomNumberGenerator::getUniform2D() {
Vector3f v;
do {
v = Vector3f(getDouble(-1,1),getDouble(-1,1),0);
} while (v.sqrLength()>1.0);
return v;
}
Vector3f RandomNumberGenerator::getUniform3D() {
Vector3f v;
do {
v = Vector3f(getDouble(-1,1),getDouble(-1,1),getDouble(-1,1));
} while (v.sqrLength()>1.0);
return v;
}
namespace {
const int UniformTableSize = 10000; // size of precalculated tables.
}
// Uses precalculated tables.
// Initialized on first use (so init before using in multithreaded code)
Vector3f RandomNumberGenerator::getUniform2DFromTable() {
static QVector<Vector3f> uniform2D;
if (uniform2D.count() == 0) {
for (int i = 0; i < UniformTableSize; i++) uniform2D.append(getUniform2D());
}
uniformCounter2D++;
if (uniformCounter2D>=uniform2D.count()) uniformCounter2D = 0;
return uniform2D[uniformCounter2D];
}
Vector3f RandomNumberGenerator::getUniform3DFromTable() {
static QVector<Vector3f> uniform3D;
if (uniform3D.count() == 0) {
for (int i = 0; i < UniformTableSize; i++) uniform3D.append(getUniform3D());
}
uniformCounter3D++;
if (uniformCounter3D>=uniform3D.count()) uniformCounter3D = 0;
return uniform3D[uniformCounter3D];
}
void RandomNumberGenerator::randomizeUniformCounter(){
uniformCounter2D = getInt(UniformTableSize);
uniformCounter3D = getInt(UniformTableSize);
}
}
}

View File

@ -1,127 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <QStringList>
#include <cmath>
#include <random>
#include <memory>
#include "Vector3.h"
namespace SyntopiaCore {
namespace Math {
/// A simple class for generating random numbers
/// It is possible to have multiple independent streams, if the underlying RNG is the Mersenne Twister.
/// If set to useStdLib, the CStdLib 'rand' and 'srand' functions are used - these are not independent - not even with multiple instances of this class.
class RandomNumberGenerator {
public:
RandomNumberGenerator(bool useOldLibrary = false) : uniformCounter2D(0), uniformCounter3D(0) { if (!useOldLibrary) { rng.reset(new std::mt19937()); } setSeed(0); };
// This is only useful for backward compatibility.
// The Mersenne Twister is much better since it allows multiple independent streams.
void useStdLib(bool useOldLibrary) {
rng.reset();
if (!useOldLibrary) {
rng.reset(new std::mt19937());
}
setSeed(lastSeed);
};
QVector<int> getRandomIndices(int count);
// Returns a vector, where the elements are ranked randomly.
template <typename T>
QVector<T> randomize(QVector<T> list) {
QVector<int> indices = getRandomIndices(list.count());
QVector<T> copy(list.count());
for (int i = 0; i < list.count(); i++) copy[i] = list[indices[i]];
return copy;
}
bool isUsingStdLib() { return (rng == nullptr); }
// Returns a double in the interval [0;1]
double getDouble() {
if (rng) {
std::uniform_real_distribution<> d{0.0, 1.0};
return d(*rng);
//return static_cast<double>((*rng)()) / static_cast<double>(std::numeric_limits<uint_fast32_t>::max());
} else {
return rand()/(double)RAND_MAX;
/*
This one would be more correct, but the old cstdlib rand is implemented for backward compatibility:
return (double)rand() / ((double)(RAND_MAX)+(double)(1)) ; // There are reasons for the multiple (double) casts, see: http://members.cox.net/srice1/random/crandom.html
*/
}
};
// Normal distributed number with mean zero.
double getNormal(double variance) {
/// Note: requires MT RNG!
std::normal_distribution<> d{0, std::sqrt(variance)};
return d(*rng);
}
double getDouble(double min, double max) {
if (rng) {
std::uniform_real_distribution<> d{min, max};
return d(*rng);
}
return getDouble()*(max-min)+min;
}
// Returns an integer between 0 and max (both inclusive).
int getInt(int max) {
if (rng) {
std::uniform_int_distribution<> d{0, max};
return d(*rng);
} else {
return rand() % (max+1); // Probably not very good, use mersenne instead
}
}
int getInt() {
if (rng) {
std::uniform_int_distribution<> d;
return d(*rng);
} else {
return rand();
}
}
void setSeed(int seed) {
lastSeed = seed;
if (rng) {
rng->seed(seed);
} else {
srand(seed);
}
};
// Return uniform samples on either unit disc (z=0) or unit sphere
// Uses Monto-carlo sampling which might be slow
Vector3f getUniform2D();
Vector3f getUniform3D();
// Uses precalculated tables.
// Initialized on first use (so init before using in multithreaded code)
Vector3f getUniform2DFromTable();
Vector3f getUniform3DFromTable();
void randomizeUniformCounter(); // use this to avoid coherence between different rg's
void setUniformCounter2D(int value) { uniformCounter2D = value; }
void setUniformCounter3D(int value) { uniformCounter3D = value; }
private:
int lastSeed;
std::unique_ptr<std::mt19937> rng;
int uniformCounter2D;
int uniformCounter3D;
};
}
}

View File

@ -1,8 +0,0 @@
#include "Vector3.h"
namespace SyntopiaCore {
namespace Math {
}
}

View File

@ -1,94 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <QStringList>
#include <cmath>
namespace SyntopiaCore {
namespace Math {
/// A simple class for representing three-dimensional vectors.
template <class scalar> class Vector3 {
public:
Vector3() { s[0] = 0; s[1] = 0; s[2] = 0;}
Vector3(scalar x, scalar y, scalar z) { s[0] = x; s[1] = y; s[2] = z; }
// Constructor. Parses input such as "[1 2 3]";
Vector3(QString input, bool& succes2) {
input.remove('[');
input.remove(']');
QStringList sl = input.split(" ");
if (sl.size() != 3) { succes2 = false; return; }
bool succes = false;
float f;
f = sl[0].toFloat(&succes); if (!succes) { succes2 = false; return; }; s[0] = f;
f = sl[1].toFloat(&succes); if (!succes) { succes2 = false; return; }; s[1] = f;
f = sl[2].toFloat(&succes); if (!succes) { succes2 = false; return; }; s[2] = f;
succes2 = true;
}
// data access
scalar x() const { return s[0]; }
scalar y() const { return s[1]; };
scalar z() const { return s[2]; };
scalar& x() { return s[0]; }
scalar& y() { return s[1]; };
scalar& z() { return s[2]; };
scalar operator[] (int index) const { return s[index]; };
scalar& operator[] (int index) { return s[index]; };
scalar sqrLength() const { return s[0]*s[0]+s[1]*s[1]+s[2]*s[2]; }
scalar length() const { return sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2]); }
Vector3<scalar> normalized() const { scalar l = 1.0/length(); return Vector3<scalar>(s[0]*l,s[1]*l,s[2]*l); }
void normalize() { scalar l = 1.0/length(); s[0]*=l; s[1]*=l; s[2]*=l; }
Vector3<scalar> operator- (const Vector3<scalar>& rhs) const { return Vector3<scalar>(s[0]-rhs.s[0], s[1]-rhs.s[1], s[2]-rhs.s[2]); }
Vector3<scalar> operator+ (const Vector3<scalar>& rhs) const { return Vector3<scalar>(s[0]+rhs.s[0], s[1]+rhs.s[1], s[2]+rhs.s[2]); }
Vector3<scalar> operator- () const { return Vector3<scalar>(-s[0], -s[1], -s[2]); }
bool operator== (const Vector3<scalar>& rhs) const { return (s[0]==rhs.s[0] && s[1]==rhs.s[1] && s[2]==rhs.s[2]); }
Vector3<scalar> operator* (scalar rhs) const { return Vector3<scalar>(s[0]*rhs, s[1]*rhs, s[2]*rhs); }
Vector3<scalar> operator/ (scalar rhs) const { scalar t = 1.0/rhs; return Vector3<scalar>(s[0]*t, s[1]*t, s[2]*t); }
QString toString() const {
return QString("[%1 %2 %3]").arg(s[0]).arg(s[1]).arg(s[2]);
}
Vector3<scalar> cross(const Vector3<scalar> b) const {
return Vector3<scalar>(
s[1]*b.s[2] - s[2]*b.s[1] ,
s[2]*b.s[0] - s[0]*b.s[2] ,
s[0]*b.s[1] - s[1]*b.s[0]);
}
static Vector3<scalar> cross(const Vector3<scalar> a, const Vector3<scalar> b) {
return Vector3<scalar>(
a.s[1]*b.s[2] - a.s[2]*b.s[1] ,
a.s[2]*b.s[0] - a.s[0]*b.s[2] ,
a.s[0]*b.s[1] - a.s[1]*b.s[0]);
}
static scalar dot(const Vector3<scalar> a, const Vector3<scalar> b) {
return a.s[0]*b.s[0] + a.s[1]*b.s[1] + a.s[2]*b.s[2] ;
}
private:
scalar s[3];
};
template <typename T, typename S>
Vector3<S> operator*(T lhs, Vector3<S> rhs) { return Vector3<S>(rhs[0]*lhs, rhs[1]*lhs, rhs[2]*lhs); }
typedef Vector3<float> Vector3f ;
typedef Vector3<double> Vector3d ;
}
}

View File

@ -1,32 +0,0 @@
#include "ColorUtils.h"
#include "../Logging/Logging.h"
using namespace SyntopiaCore::Math;
using namespace SyntopiaCore::Logging;
namespace SyntopiaCore {
namespace Misc {
SyntopiaCore::Math::Vector3f ColorUtils::HSVtoRGB(SyntopiaCore::Math::Vector3f hsv) {
/// Implementation based on: http://en.wikipedia.org/wiki/HSV_color_space
if (hsv[0] >= 360) hsv[0]-=360;
int Hi = (int)(hsv[0] / 60) % 6;
double f = (hsv[0] / 60) - Hi;
double p = hsv[2]*(1-hsv[1]);
double q = hsv[2]*(1-f*hsv[1]);
double t = hsv[2]*(1-(1-f)*hsv[1]);
if (Hi == 0) return Vector3f(hsv[2],t,p);
if (Hi == 1) return Vector3f(q,hsv[2],p);
if (Hi == 2) return Vector3f(p,hsv[2],t);
if (Hi == 3) return Vector3f(p,q,hsv[2]);
if (Hi == 4) return Vector3f(t,p,hsv[2]);
if (Hi == 5) return Vector3f(hsv[2],p,q);
WARNING("ColorUtils::HSVtoRGB failed");
return Vector3f(0,0,0);
}
}
}

View File

@ -1,20 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <cmath>
#include "../Math/Vector3.h"
namespace SyntopiaCore {
namespace Misc {
class ColorUtils {
public:
static SyntopiaCore::Math::Vector3f HSVtoRGB(SyntopiaCore::Math::Vector3f hsv);
};
}
}

View File

@ -1,98 +0,0 @@
#include "MiniParser.h"
#include "../Logging/Logging.h"
using namespace SyntopiaCore::Logging;
namespace SyntopiaCore {
namespace Misc {
MiniParser::MiniParser( QString value, QChar separator ) : separator(separator), original(value), value(value), paramCount(0) {
}
MiniParser& MiniParser::getInt(int& val) {
paramCount++;
QString first = value.section(separator, 0,0);
value = value.section(separator, 1);
if (first.isEmpty()) {
WARNING(QString("Expected argument number %1 for %2").arg(paramCount).arg(original));
}
bool succes = false;
int i = first.toInt(&succes);
if (!succes) {
WARNING(QString("Expected argument number %1 to be an integer. Found: %2").arg(paramCount).arg(first));
}
val = i;
return *this;
}
MiniParser& MiniParser::getBool(bool& val) {
paramCount++;
QString first = value.section(separator, 0,0);
value = value.section(separator, 1);
if (first.isEmpty()) {
WARNING(QString("Expected argument number %1 for %2").arg(paramCount).arg(original));
}
if (first.toLower() == "true") {
val = true;
} else if (first.toLower() == "false") {
val = false;
} else {
WARNING(QString("Expected argument number %1 to be either true or false. Found: %2").arg(paramCount).arg(first));
}
return *this;
}
MiniParser& MiniParser::getDouble(double& val) {
paramCount++;
QString first = value.section(separator, 0,0);
value = value.section(separator, 1);
if (first.isEmpty()) {
WARNING(QString("Expected argument number %1 for %2").arg(paramCount).arg(original));
}
bool succes = false;
double d = first.toDouble(&succes);
if (!succes) {
WARNING(QString("Expected argument number %1 to be an double. Found: %2").arg(paramCount).arg(first));
}
val = d;
return *this;
}
MiniParser& MiniParser::getFloat(float& val) {
paramCount++;
QString first = value.section(separator, 0,0);
value = value.section(separator, 1);
if (first.isEmpty()) {
WARNING(QString("Expected argument number %1 for %2").arg(paramCount).arg(original));
}
bool succes = false;
float d = first.toFloat(&succes);
if (!succes) {
WARNING(QString("Expected argument number %1 to be an float. Found: %2").arg(paramCount).arg(first));
}
val = d;
return *this;
}
}
}

View File

@ -1,29 +0,0 @@
#pragma once
#include <QString>
#include <QList>
namespace SyntopiaCore {
namespace Misc {
/// Small class for parsing values from simple text strings.
class MiniParser {
public:
MiniParser( QString value, QChar separator = ',');
MiniParser& getInt(int& val);
MiniParser& getBool(bool& val);
MiniParser& getDouble(double& val);
MiniParser& getFloat(float& val);
private:
QChar separator;
QString original;
QString value;
int paramCount ;
};
}
}

View File

@ -1,104 +0,0 @@
#include "Persistence.h"
#include "../Logging/Logging.h"
#include <QMap>
#include <QComboBox>
#include <QLineEdit>
#include <QCheckBox>
#include <QSpinBox>
#include <QRadioButton>
#include <QVariant>
using namespace SyntopiaCore::Logging;
namespace SyntopiaCore {
namespace Misc {
QMap<QString, QVariant>& Persistence::GetStore() {
static QMap<QString, QVariant> p;
return p;
}
void Persistence::Store(QWidget* widget, QString storageName) {
if (storageName.isEmpty()) storageName = widget->objectName();
if (qobject_cast<QLineEdit*>(widget)) {
QLineEdit* lineEdit = qobject_cast<QLineEdit*>(widget);
GetStore()[storageName] = QVariant(lineEdit->text());
} else if (qobject_cast<QCheckBox*>(widget)) {
QCheckBox* cb = qobject_cast<QCheckBox*>(widget);
GetStore()[storageName] = QVariant(cb->isChecked());
} else if (qobject_cast<QSpinBox*>(widget)) {
QSpinBox* sb = qobject_cast<QSpinBox*>(widget);
GetStore()[storageName] = QVariant(sb->value());
} else if (qobject_cast<QRadioButton*>(widget)) {
QRadioButton* rb = qobject_cast<QRadioButton*>(widget);
GetStore()[storageName] = QVariant(rb->isChecked());
} else if (qobject_cast<QComboBox*>(widget)) {
QComboBox* cb = qobject_cast<QComboBox*>(widget);
GetStore()[storageName] = QVariant(cb->currentText());
} else {
WARNING("Unsupported widget: " + widget->objectName());
}
}
void Persistence::Restore(QWidget* widget, QString storageName) {
if (storageName.isEmpty()) storageName = widget->objectName();
if (qobject_cast<QLineEdit*>(widget)) {
QLineEdit* lineEdit = qobject_cast<QLineEdit*>(widget);
if (GetStore().contains(storageName)) {
lineEdit->setText(GetStore()[storageName].toString());
}
} else if (qobject_cast<QCheckBox*>(widget)) {
QCheckBox* cb = qobject_cast<QCheckBox*>(widget);
if (GetStore().contains(storageName)) {
cb->setChecked(GetStore()[storageName].toBool());
}
} else if (qobject_cast<QSpinBox*>(widget)) {
QSpinBox* sb = qobject_cast<QSpinBox*>(widget);
if (GetStore().contains(storageName)) {
sb->setValue(GetStore()[storageName].toInt());
}
} else if (qobject_cast<QRadioButton*>(widget)) {
QRadioButton* rb = qobject_cast<QRadioButton*>(widget);
if (GetStore().contains(storageName)) {
rb->setChecked(GetStore()[storageName].toInt());
}
} else if (qobject_cast<QComboBox*>(widget)) {
QComboBox* rb = qobject_cast<QComboBox*>(widget);
if (GetStore().contains(storageName)) {
int i = rb->findText(GetStore()[storageName].toString());
if (i!=-1) {
rb->setCurrentIndex(i);
} else {
}
}
} else {
WARNING("Unsupported widget: " + widget->objectName());
}
}
bool Persistence::Contains(QString storageName) {
return GetStore().contains(storageName);
}
QVariant Persistence::Get(QString storageName) {
return GetStore()[storageName];
}
void Persistence::Put(QString storageName, QVariant value) {
GetStore()[storageName] = value;
}
}
}

View File

@ -1,34 +0,0 @@
#pragma once
#include <QString>
#include <QWidget>
#include <QList>
namespace SyntopiaCore {
namespace Misc {
/// Util classes for making GUI settings persistent.
/// (i.e. remember the state of drop-downs, line-edits, and so on).
///
/// Per default, the widget is stored under its objectName, but it possible to specify
/// another name (e.g. to share widget data between dialogs).
///
/// Notice that widgets must be hardcoded into the cpp-file to be supported
class Persistence {
public:
static void Store(QWidget* widget, QString storageName = QString());
static void Restore(QWidget* widget, QString storageName = QString());
static bool Contains(QString storageName);
static QVariant Get(QString storageName);
static void Put(QString storageName, QVariant value);
private:
static QMap<QString, QVariant>& GetStore();
};
}
}

View File

@ -1,63 +0,0 @@
#include "Version.h"
#include "../Logging/Logging.h"
using namespace SyntopiaCore::Logging;
namespace SyntopiaCore {
namespace Misc {
Version::Version() : revision(0), build(0), codename("") {
this->major = 0;
this->minor = 0;
}
Version::Version(int major, int minor, int revision, int build, QString codename) : revision(revision), build(build), codename(codename) {
this->major = major;
this->minor = minor;
}
QList<Version> Version::GetNewVersions(QString /*url*/) const {
return QList<Version>();
}
QString Version::toLongString() const {
QString s = QString("%1.%2").arg(major).arg(minor);
if (revision >= 0) s+= QString(".%3").arg(revision);
if (build >= 0) s+= QString(".%4").arg(build);
if (!codename.isEmpty()) s+= " " + codename;
return s;
}
bool Version::operator<(const Version &rhs) {
if (major == rhs.major) {
if (minor == rhs.minor) {
if (revision == rhs.revision) {
return (build < rhs.build);
} else {
return (revision < rhs.revision);
}
} else {
return (minor < rhs.minor);
}
} else {
return (major < rhs.major);
}
}
bool Version::operator>(const Version &rhs) {
if ( (*this) == rhs ) return false;
return !((*this)<rhs);
}
bool Version::operator==(const Version &rhs) {
return ((major == rhs.major) && (minor == rhs.minor) && (revision == rhs.revision) && (build == rhs.build));
}
}
}

View File

@ -1,46 +0,0 @@
#pragma once
#include <QString>
#include <QList>
namespace SyntopiaCore {
namespace Misc {
/// For keeping track of versions
/// Having a formalized version object, can
/// be helpful when checking the internet for updates.
class Version {
public:
/// Constructor.
/// Settings revision or build to -1 indicates they are not in use.
Version();
Version(int major, int minor, int revision = -1, int build = -1, QString codename = "");
/// Long human-readable version.
QString toLongString() const;
/// Returns a list of versions, newer than 'this' version,
/// by reading an XML-file from the specified URL.
///
/// Todo: Implement
QList<Version> GetNewVersions(QString url) const;
/// Comparisons
bool operator<(const Version &rhs);
bool operator>(const Version &rhs);
bool operator==(const Version &rhs);
private:
int major;
int minor;
int revision;
int build;
QString codename;
};
}
}

View File

@ -1,427 +0,0 @@
#ifdef WIN32
#pragma warning( disable : 4146 )
#endif
// MersenneTwister.h
// Mersenne Twister random number generator -- a C++ class MTRand
// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
// Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
// The Mersenne Twister is an algorithm for generating random numbers. It
// was designed with consideration of the flaws in various other generators.
// The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
// are far greater. The generator is also fast; it avoids multiplication and
// division, and it benefits from caches and pipelines. For more information
// see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
// Reference
// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
// Copyright (C) 2000 - 2003, Richard J. Wagner
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The names of its contributors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The original code included the following notice:
//
// When you use this, send an email to: matumoto@math.keio.ac.jp
// with an appropriate reference to your work.
//
// It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu
// when you write.
#ifndef MERSENNETWISTER_H
#define MERSENNETWISTER_H
// Not thread safe (unless auto-initialization is avoided and each thread has
// its own MTRand object)
#include <iostream>
#include <limits.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
class MTRand {
// Data
public:
typedef unsigned long uint32; // unsigned integer type, at least 32 bits
enum { N = 624 }; // length of state vector
enum { SAVE = N + 1 }; // length of array for save()
protected:
enum { M = 397 }; // period parameter
uint32 state[N]; // internal state
uint32 *pNext; // next value to get from state
int left; // number of values left before reload needed
//Methods
public:
MTRand( const uint32& oneSeed ); // initialize with a simple uint32
MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array
MTRand(); // auto-initialize with /dev/urandom or time() and clock()
// Do NOT use for CRYPTOGRAPHY without securely hashing several returned
// values together, otherwise the generator state can be learned after
// reading 624 consecutive values.
// Access to 32-bit random numbers
double rand(); // real number in [0,1]
double rand( const double& n ); // real number in [0,n]
double randExc(); // real number in [0,1)
double randExc( const double& n ); // real number in [0,n)
double randDblExc(); // real number in (0,1)
double randDblExc( const double& n ); // real number in (0,n)
uint32 randInt(); // integer in [0,2^32-1]
uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32
double operator()() { return rand(); } // same as rand()
// Access to 53-bit random numbers (capacity of IEEE double precision)
double rand53(); // real number in [0,1)
// Access to nonuniform random number distributions
double randNorm( const double& mean = 0.0, const double& variance = 0.0 );
// Re-seeding functions with same behavior as initializers
void seed( const uint32 oneSeed );
void seed( uint32 *const bigSeed, const uint32 seedLength = N );
void seed();
// Saving and loading generator state
void save( uint32* saveArray ) const; // to array of size SAVE
void load( uint32 *const loadArray ); // from such array
friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand );
friend std::istream& operator>>( std::istream& is, MTRand& mtrand );
protected:
void initialize( const uint32 oneSeed );
void reload();
uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; }
uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; }
uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; }
uint32 mixBits( const uint32& u, const uint32& v ) const
{ return hiBit(u) | loBits(v); }
uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const
{ return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); }
static uint32 hash( time_t t, clock_t c );
};
inline MTRand::MTRand( const uint32& oneSeed )
{ seed(oneSeed); }
inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength )
{ seed(bigSeed,seedLength); }
inline MTRand::MTRand()
{ seed(); }
inline double MTRand::rand()
{ return double(randInt()) * (1.0/4294967295.0); }
inline double MTRand::rand( const double& n )
{ return rand() * n; }
inline double MTRand::randExc()
{ return double(randInt()) * (1.0/4294967296.0); }
inline double MTRand::randExc( const double& n )
{ return randExc() * n; }
inline double MTRand::randDblExc()
{ return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); }
inline double MTRand::randDblExc( const double& n )
{ return randDblExc() * n; }
inline double MTRand::rand53()
{
uint32 a = randInt() >> 5, b = randInt() >> 6;
return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada
}
inline double MTRand::randNorm( const double& mean, const double& variance )
{
// Return a real number from a normal (Gaussian) distribution with given
// mean and variance by Box-Muller method
double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance;
double phi = 2.0 * 3.14159265358979323846264338328 * randExc();
return mean + r * cos(phi);
}
inline MTRand::uint32 MTRand::randInt()
{
// Pull a 32-bit integer from the generator state
// Every other access function simply transforms the numbers extracted here
if( left == 0 ) reload();
--left;
register uint32 s1;
s1 = *pNext++;
s1 ^= (s1 >> 11);
s1 ^= (s1 << 7) & 0x9d2c5680UL;
s1 ^= (s1 << 15) & 0xefc60000UL;
return ( s1 ^ (s1 >> 18) );
}
inline MTRand::uint32 MTRand::randInt( const uint32& n )
{
// Find which bits are used in n
// Optimized by Magnus Jonsson (magnus@smartelectronix.com)
uint32 used = n;
used |= used >> 1;
used |= used >> 2;
used |= used >> 4;
used |= used >> 8;
used |= used >> 16;
// Draw numbers until one is found in [0,n]
uint32 i;
do
i = randInt() & used; // toss unused bits to shorten search
while( i > n );
return i;
}
inline void MTRand::seed( const uint32 oneSeed )
{
// Seed the generator with a simple uint32
initialize(oneSeed);
reload();
}
inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
{
// Seed the generator with an array of uint32's
// There are 2^19937-1 possible initial states. This function allows
// all of those to be accessed by providing at least 19937 bits (with a
// default seed length of N = 624 uint32's). Any bits above the lower 32
// in each element are discarded.
// Just call seed() if you want to get array from /dev/urandom
initialize(19650218UL);
register int i = 1;
register uint32 j = 0;
register int k = ( (uint32)N > seedLength ? (uint32)N : seedLength );
for( ; k; --k )
{
state[i] =
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL );
state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
state[i] &= 0xffffffffUL;
++i; ++j;
if( i >= N ) { state[0] = state[N-1]; i = 1; }
if( j >= seedLength ) j = 0;
}
for( k = N - 1; k; --k )
{
state[i] =
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL );
state[i] -= i;
state[i] &= 0xffffffffUL;
++i;
if( i >= N ) { state[0] = state[N-1]; i = 1; }
}
state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
reload();
}
inline void MTRand::seed()
{
// Seed the generator with an array from /dev/urandom if available
// Otherwise use a hash of time() and clock() values
// First try getting an array from /dev/urandom
FILE* urandom = fopen( "/dev/urandom", "rb" );
if( urandom )
{
uint32 bigSeed[N];
register uint32 *s = bigSeed;
register int i = N;
register bool success = true;
while( success && i-- )
success = fread( s++, sizeof(uint32), 1, urandom );
fclose(urandom);
if( success ) { seed( bigSeed, N ); return; }
}
// Was not successful, so use time() and clock() instead
seed( hash( time(NULL), clock() ) );
}
inline void MTRand::initialize( const uint32 seed )
{
// Initialize generator state with seed
// See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
// In previous versions, most significant bits (MSBs) of the seed affect
// only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
register uint32 *s = state;
register uint32 *r = state;
register int i = 1;
*s++ = seed & 0xffffffffUL;
for( ; i < N; ++i )
{
*s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
r++;
}
}
inline void MTRand::reload()
{
// Generate N new values in state
// Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
register uint32 *p = state;
register int i;
for( i = N - M; i--; ++p )
*p = twist( p[M], p[0], p[1] );
for( i = M; --i; ++p )
*p = twist( p[M-N], p[0], p[1] );
*p = twist( p[M-N], p[0], state[0] );
left = N, pNext = state;
}
inline MTRand::uint32 MTRand::hash( time_t t, clock_t c )
{
// Get a uint32 from t and c
// Better than uint32(x) in case x is floating point in [0,1]
// Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
static uint32 differ = 0; // guarantee time-based seeds will change
uint32 h1 = 0;
unsigned char *p = (unsigned char *) &t;
for( size_t i = 0; i < sizeof(t); ++i )
{
h1 *= UCHAR_MAX + 2U;
h1 += p[i];
}
uint32 h2 = 0;
p = (unsigned char *) &c;
for( size_t j = 0; j < sizeof(c); ++j )
{
h2 *= UCHAR_MAX + 2U;
h2 += p[j];
}
return ( h1 + differ++ ) ^ h2;
}
inline void MTRand::save( uint32* saveArray ) const
{
register uint32 *sa = saveArray;
register const uint32 *s = state;
register int i = N;
for( ; i--; *sa++ = *s++ ) {}
*sa = left;
}
inline void MTRand::load( uint32 *const loadArray )
{
register uint32 *s = state;
register uint32 *la = loadArray;
register int i = N;
for( ; i--; *s++ = *la++ ) {}
left = *la;
pNext = &state[N-left];
}
inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand )
{
register const MTRand::uint32 *s = mtrand.state;
register int i = mtrand.N;
for( ; i--; os << *s++ << "\t" ) {}
return os << mtrand.left;
}
inline std::istream& operator>>( std::istream& is, MTRand& mtrand )
{
register MTRand::uint32 *s = mtrand.state;
register int i = mtrand.N;
for( ; i--; is >> *s++ ) {}
is >> mtrand.left;
mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left];
return is;
}
#endif // MERSENNETWISTER_H
// Change log:
//
// v0.1 - First release on 15 May 2000
// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
// - Translated from C to C++
// - Made completely ANSI compliant
// - Designed convenient interface for initialization, seeding, and
// obtaining numbers in default or user-defined ranges
// - Added automatic seeding from /dev/urandom or time() and clock()
// - Provided functions for saving and loading generator state
//
// v0.2 - Fixed bug which reloaded generator one step too late
//
// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew
//
// v0.4 - Removed trailing newline in saved generator format to be consistent
// with output format of built-in types
//
// v0.5 - Improved portability by replacing static const int's with enum's and
// clarifying return values in seed(); suggested by Eric Heimburg
// - Removed MAXINT constant; use 0xffffffffUL instead
//
// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits
// - Changed integer [0,n] generator to give better uniformity
//
// v0.7 - Fixed operator precedence ambiguity in reload()
// - Added access for real numbers in (0,1) and (0,n)
//
// v0.8 - Included time.h header to properly support time_t and clock_t
//
// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto
// - Allowed for seeding with arrays of any length
// - Added access for real numbers in [0,1) with 53-bit resolution
// - Added access for real numbers from normal (Gaussian) distributions
// - Increased overall speed by optimizing twist()
// - Doubled speed of integer [0,n] generation
// - Fixed out-of-range number generation on 64-bit machines
// - Improved portability by substituting literal constants for long enum's
// - Changed license from GNU LGPL to BSD