mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-13 00:07:24 +00:00
remove ssynth source and download it using cmake
This commit is contained in:
parent
523fae4fb1
commit
65012cd020
118
src/external/ssynth.cmake
vendored
118
src/external/ssynth.cmake
vendored
@ -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()
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "Rule.h"
|
||||
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "PrimitiveClass.h"
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
|
||||
/// Every PrimitiveRule can be assigned a class.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
#include "Renderer.h"
|
||||
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
namespace Renderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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*/) {};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "Rule.h"
|
||||
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "Rule.h"
|
||||
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "TransformationLoop.h"
|
||||
|
||||
|
||||
namespace StructureSynth {
|
||||
namespace Model {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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#");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
#include "AtomicCounter.h"
|
||||
|
||||
#include "SyntopiaCore/Math/Vector3.h"
|
||||
|
||||
#include "AtomicCounter.h"
|
||||
|
||||
namespace SyntopiaCore {
|
||||
namespace GLEngine {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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...
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
#include "Matrix4.h"
|
||||
|
||||
|
||||
namespace SyntopiaCore {
|
||||
namespace Math {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#include "Vector3.h"
|
||||
|
||||
|
||||
namespace SyntopiaCore {
|
||||
namespace Math {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user