From e7665dbcbcbfcee117d1472a0a896016c9666e07 Mon Sep 17 00:00:00 2001 From: Paolo Cignoni cignoni Date: Wed, 16 Oct 2013 13:10:57 +0000 Subject: [PATCH] Added static exported version of structuresynth --- .../ssynth/StructureSynth/Model/Action.cpp | 104 +++ .../ssynth/StructureSynth/Model/Action.h | 44 ++ .../StructureSynth/Model/AmbiguousRule.cpp | 52 ++ .../StructureSynth/Model/AmbiguousRule.h | 37 + .../ssynth/StructureSynth/Model/Builder.cpp | 400 +++++++++++ .../ssynth/StructureSynth/Model/Builder.h | 73 ++ .../ssynth/StructureSynth/Model/ColorPool.cpp | 87 +++ .../ssynth/StructureSynth/Model/ColorPool.h | 27 + .../StructureSynth/Model/CustomRule.cpp | 70 ++ .../ssynth/StructureSynth/Model/CustomRule.h | 37 + .../StructureSynth/Model/ExecutionStack.cpp | 8 + .../StructureSynth/Model/ExecutionStack.h | 33 + .../StructureSynth/Model/PrimitiveClass.cpp | 8 + .../StructureSynth/Model/PrimitiveClass.h | 12 + .../StructureSynth/Model/PrimitiveRule.cpp | 143 ++++ .../StructureSynth/Model/PrimitiveRule.h | 62 ++ .../StructureSynth/Model/RandomStreams.cpp | 11 + .../StructureSynth/Model/RandomStreams.h | 22 + .../Model/Rendering/ObjRenderer.cpp | 338 +++++++++ .../Model/Rendering/ObjRenderer.h | 126 ++++ .../Model/Rendering/OpenGLRenderer.cpp | 137 ++++ .../Model/Rendering/OpenGLRenderer.h | 90 +++ .../Model/Rendering/Renderer.cpp | 10 + .../StructureSynth/Model/Rendering/Renderer.h | 90 +++ .../Model/Rendering/TemplateRenderer.cpp | 488 +++++++++++++ .../Model/Rendering/TemplateRenderer.h | 187 +++++ .../ssynth/StructureSynth/Model/Rule.cpp | 8 + .../ssynth/StructureSynth/Model/Rule.h | 40 ++ .../ssynth/StructureSynth/Model/RuleRef.cpp | 8 + .../ssynth/StructureSynth/Model/RuleRef.h | 29 + .../ssynth/StructureSynth/Model/RuleSet.cpp | 258 +++++++ .../ssynth/StructureSynth/Model/RuleSet.h | 59 ++ .../ssynth/StructureSynth/Model/State.cpp | 62 ++ .../ssynth/StructureSynth/Model/State.h | 42 ++ .../StructureSynth/Model/Transformation.cpp | 269 +++++++ .../StructureSynth/Model/Transformation.h | 66 ++ .../Model/TransformationLoop.cpp | 8 + .../StructureSynth/Model/TransformationLoop.h | 23 + .../StructureSynth/Parser/EisenParser.cpp | 353 ++++++++++ .../StructureSynth/Parser/EisenParser.h | 50 ++ .../StructureSynth/Parser/Preprocessor.cpp | 149 ++++ .../StructureSynth/Parser/Preprocessor.h | 66 ++ .../StructureSynth/Parser/Tokenizer.cpp | 202 ++++++ .../ssynth/StructureSynth/Parser/Tokenizer.h | 65 ++ .../SyntopiaCore/Exceptions/Exception.h | 29 + .../ssynth/SyntopiaCore/GLEngine/Box.cpp | 164 +++++ .../ssynth/SyntopiaCore/GLEngine/Box.h | 48 ++ .../ssynth/SyntopiaCore/GLEngine/Dot.cpp | 32 + .../ssynth/SyntopiaCore/GLEngine/Dot.h | 25 + .../SyntopiaCore/GLEngine/EngineWidget.cpp | 658 ++++++++++++++++++ .../SyntopiaCore/GLEngine/EngineWidget.h | 225 ++++++ .../ssynth/SyntopiaCore/GLEngine/Grid.cpp | 60 ++ .../ssynth/SyntopiaCore/GLEngine/Grid.h | 31 + .../ssynth/SyntopiaCore/GLEngine/Line.cpp | 44 ++ .../ssynth/SyntopiaCore/GLEngine/Line.h | 26 + .../ssynth/SyntopiaCore/GLEngine/Mesh.cpp | 131 ++++ .../ssynth/SyntopiaCore/GLEngine/Mesh.h | 48 ++ .../ssynth/SyntopiaCore/GLEngine/Object3D.cpp | 129 ++++ .../ssynth/SyntopiaCore/GLEngine/Object3D.h | 100 +++ .../GLEngine/RaytraceTriangle.cpp | 119 ++++ .../SyntopiaCore/GLEngine/RaytraceTriangle.h | 50 ++ .../GLEngine/Raytracer/AtomicCounter.cpp | 13 + .../GLEngine/Raytracer/AtomicCounter.h | 40 ++ .../GLEngine/Raytracer/ProgressiveOutput.h | 106 +++ .../GLEngine/Raytracer/RayTracer.cpp | 273 ++++++++ .../GLEngine/Raytracer/RayTracer.h | 59 ++ .../GLEngine/Raytracer/RenderThread.cpp | 388 +++++++++++ .../GLEngine/Raytracer/RenderThread.h | 88 +++ .../GLEngine/Raytracer/Sampler.cpp | 171 +++++ .../SyntopiaCore/GLEngine/Raytracer/Sampler.h | 123 ++++ .../GLEngine/Raytracer/VoxelStepper.cpp | 193 +++++ .../GLEngine/Raytracer/VoxelStepper.h | 56 ++ .../ssynth/SyntopiaCore/GLEngine/Sphere.cpp | 105 +++ .../ssynth/SyntopiaCore/GLEngine/Sphere.h | 31 + .../ssynth/SyntopiaCore/GLEngine/Triangle.cpp | 69 ++ .../ssynth/SyntopiaCore/GLEngine/Triangle.h | 33 + .../SyntopiaCore/Logging/ListWidgetLogger.cpp | 39 ++ .../SyntopiaCore/Logging/ListWidgetLogger.h | 30 + .../ssynth/SyntopiaCore/Logging/Logging.cpp | 61 ++ .../ssynth/SyntopiaCore/Logging/Logging.h | 52 ++ .../ssynth/SyntopiaCore/Math/Matrix4.cpp | 11 + .../ssynth/SyntopiaCore/Math/Matrix4.h | 192 +++++ .../ssynth/SyntopiaCore/Math/Random.cpp | 75 ++ .../ssynth/SyntopiaCore/Math/Random.h | 118 ++++ .../ssynth/SyntopiaCore/Math/Vector3.cpp | 8 + .../ssynth/SyntopiaCore/Math/Vector3.h | 94 +++ .../ssynth/SyntopiaCore/Misc/ColorUtils.cpp | 32 + .../ssynth/SyntopiaCore/Misc/ColorUtils.h | 20 + .../ssynth/SyntopiaCore/Misc/MiniParser.cpp | 98 +++ .../ssynth/SyntopiaCore/Misc/MiniParser.h | 29 + .../ssynth/SyntopiaCore/Misc/Persistence.cpp | 104 +++ .../ssynth/SyntopiaCore/Misc/Persistence.h | 34 + .../ssynth/SyntopiaCore/Misc/Version.cpp | 63 ++ .../ssynth/SyntopiaCore/Misc/Version.h | 46 ++ .../MersenneTwister/MersenneTwister.h | 427 ++++++++++++ .../structuresynth-1.5/structuresynth.pro | 124 ++++ 96 files changed, 9777 insertions(+) create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.h create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Exceptions/Exception.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/ProgressiveOutput.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.h create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Version.cpp create mode 100644 src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Version.h create mode 100644 src/external/structuresynth-1.5/ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h create mode 100644 src/external/structuresynth-1.5/structuresynth.pro diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.cpp new file mode 100644 index 000000000..b958ac191 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.cpp @@ -0,0 +1,104 @@ +#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 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; + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.h new file mode 100644 index 000000000..41293846d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Action.h @@ -0,0 +1,44 @@ +#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 loops; + RuleRef* rule; // The rule that will be called after all transformations. + SetAction* set; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.cpp new file mode 100644 index 000000000..63cb6d80c --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.cpp @@ -0,0 +1,52 @@ +#include "AmbiguousRule.h" + +#include "Builder.h" +#include "RandomStreams.h" + +#include "../../SyntopiaCore/Logging/Logging.h" + +using namespace SyntopiaCore::Logging; + + +namespace StructureSynth { + namespace Model { + + QList AmbiguousRule::getRuleRefs() const { + QList 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"); + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.h new file mode 100644 index 000000000..82c0a9e8f --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/AmbiguousRule.h @@ -0,0 +1,37 @@ +#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 getRuleRefs() const; + + QList 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 rules; + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.cpp new file mode 100644 index 000000000..357e36306 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.cpp @@ -0,0 +1,400 @@ +#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 +#include +#include + +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 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::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); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.h new file mode 100644 index 000000000..b64e49039 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Builder.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#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 ') + bool seedChanged() { return hasSeedChanged; } + int getNewSeed() { return newSeed; } + ColorPool* getColorPool() { return colorPool; } + QVector 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 raytracerCommands; + }; + + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.cpp new file mode 100644 index 000000000..236b63bc0 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.cpp @@ -0,0 +1,87 @@ +#include "ColorPool.h" + +#include "../../SyntopiaCore/Logging/Logging.h" +#include "../../SyntopiaCore/Exceptions/Exception.h" +#include "Builder.h" +#include "RandomStreams.h" + + +#include +#include + + +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(); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.h new file mode 100644 index 000000000..f01ea7ca8 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ColorPool.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +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 colorList; // only used by type: ColorList. + QImage* picture; // Only used by type: Picture. + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.cpp new file mode 100644 index 000000000..f024b9a42 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.cpp @@ -0,0 +1,70 @@ +#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 CustomRule::getRuleRefs() const { + QList 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; + } + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.h new file mode 100644 index 000000000..188c3a677 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/CustomRule.h @@ -0,0 +1,37 @@ +#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 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 actions; + double weight; + RuleRef* retirementRule; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.cpp new file mode 100644 index 000000000..891568c52 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.cpp @@ -0,0 +1,8 @@ +#include "Rule.h" + + +namespace StructureSynth { + namespace Model { + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.h new file mode 100644 index 000000000..ac6ff6d1b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/ExecutionStack.h @@ -0,0 +1,33 @@ +#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 ExecutionStack; + + /* + struct ExecutionStack { + QList< RuleState > currentStack; + }; + */ + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.cpp new file mode 100644 index 000000000..76c3d49ba --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.cpp @@ -0,0 +1,8 @@ +#include "PrimitiveClass.h" + +namespace StructureSynth { + namespace Model { + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.h new file mode 100644 index 000000000..550130ecb --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveClass.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace StructureSynth { + namespace Model { + + /// Every PrimitiveRule can be assigned a class. + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.cpp new file mode 100644 index 000000000..b6c0a0af1 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.cpp @@ -0,0 +1,143 @@ +#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); + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.h new file mode 100644 index 000000000..17616c754 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/PrimitiveRule.h @@ -0,0 +1,62 @@ +#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 getRuleRefs() const { return QList(); } + + /// '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; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.cpp new file mode 100644 index 000000000..0ad55c8b4 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.cpp @@ -0,0 +1,11 @@ +#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); + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.h new file mode 100644 index 000000000..230a619cd --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RandomStreams.h @@ -0,0 +1,22 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.cpp new file mode 100644 index 000000000..eedb82553 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.cpp @@ -0,0 +1,338 @@ +#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 newVertices; + QVector newNormals; + QMap oldToNewVertex; + QMap 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 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 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 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 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 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 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 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(); + } + }; + + + + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.h new file mode 100644 index 000000000..f63841ab8 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/ObjRenderer.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#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 vertices; + QVector normals; + QVector > 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 groups; + QString currentGroup; + SyntopiaCore::Math::Vector3f rgb; + double alpha; + int sphereDT; + int sphereDP; + bool groupByTagging; + bool groupByColor; + + }; + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp new file mode 100644 index 000000000..20320e44b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp @@ -0,0 +1,137 @@ +#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); + } + + + + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h new file mode 100644 index 000000000..ddbd9f2dd --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#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; + }; + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.cpp new file mode 100644 index 000000000..57bfec1a7 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.cpp @@ -0,0 +1,10 @@ +#include "Renderer.h" + + +namespace StructureSynth { + namespace Model { + namespace Renderer { + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.h new file mode 100644 index 000000000..c642224d0 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/Renderer.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#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*/) {}; + }; + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp new file mode 100644 index 000000000..a7dfb2ab9 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp @@ -0,0 +1,488 @@ +#include "TemplateRenderer.h" +#include "../../../SyntopiaCore/Math/Vector3.h" +#include "../../../SyntopiaCore/Logging/Logging.h" +#include "../../../SyntopiaCore/Exceptions/Exception.h" +#include "../PrimitiveClass.h" + +#include +#include +#include +#include +#include + +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; + } + + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h new file mode 100644 index 000000000..2ff8f040d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#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& 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 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 missingTypes; + SyntopiaCore::Math::Vector3f oldRgb; + double oldAlpha; + }; + + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.cpp new file mode 100644 index 000000000..891568c52 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.cpp @@ -0,0 +1,8 @@ +#include "Rule.h" + + +namespace StructureSynth { + namespace Model { + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.h new file mode 100644 index 000000000..c7ff7ddf9 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Rule.h @@ -0,0 +1,40 @@ +#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 getRuleRefs() const { return QList(); } + + virtual void setMaxDepth(int maxDepth) { this->maxDepth = maxDepth; } + virtual int getMaxDepth() const { return maxDepth; } + + protected: + QString name; + int maxDepth; + }; + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.cpp new file mode 100644 index 000000000..891568c52 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.cpp @@ -0,0 +1,8 @@ +#include "Rule.h" + + +namespace StructureSynth { + namespace Model { + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.h new file mode 100644 index 000000000..96922fe5b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleRef.h @@ -0,0 +1,29 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.cpp new file mode 100644 index 000000000..1d12ffa2b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.cpp @@ -0,0 +1,258 @@ +#include "RuleSet.h" +#include "RuleRef.h" +#include "CustomRule.h" +#include "AmbiguousRule.h" +#include "PrimitiveRule.h" + +#include +#include +#include + +#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(r); + + AmbiguousRule* ar = new AmbiguousRule(name); + ar->appendRule(cr1); + + CustomRule* cr2 = dynamic_cast(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(rules[i]); + CustomRule* cr = dynamic_cast(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 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 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 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(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(rules[i]); + if (cr) custom++; + + AmbiguousRule* ar = dynamic_cast(rules[i]); + if (ar) ambi++; + + PrimitiveRule* pr = dynamic_cast(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; + } + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.h new file mode 100644 index 000000000..add198d65 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/RuleSet.h @@ -0,0 +1,59 @@ +#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 rules; + QVector primitiveClasses; + PrimitiveClass* defaultClass; + CustomRule* topLevelRule; + bool recurseDepth; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.cpp new file mode 100644 index 000000000..040942df4 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.cpp @@ -0,0 +1,62 @@ +#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); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.h new file mode 100644 index 000000000..4b594d2b4 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/State.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#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 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; + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.cpp new file mode 100644 index 000000000..4d655c74b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.cpp @@ -0,0 +1,269 @@ +#include "Transformation.h" +#include "ColorPool.h" + +#include "../../SyntopiaCore/Math/Matrix4.h" + +#include "../../SyntopiaCore/Exceptions/Exception.h" +#include "../../SyntopiaCore/Logging/Logging.h" + +#include + +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 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; + + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.h new file mode 100644 index 000000000..b1d5dcabd --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/Transformation.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#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 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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.cpp new file mode 100644 index 000000000..956c4774d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.cpp @@ -0,0 +1,8 @@ +#include "TransformationLoop.h" + + +namespace StructureSynth { + namespace Model { + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.h new file mode 100644 index 000000000..a3c3788cd --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Model/TransformationLoop.h @@ -0,0 +1,23 @@ +#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; + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.cpp new file mode 100644 index 000000000..6e51a15f3 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.cpp @@ -0,0 +1,353 @@ +#include "EisenParser.h" + +#include "../../SyntopiaCore/Logging/Logging.h" +#include "../Model/CustomRule.h" + +#include + +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 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(); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.h new file mode 100644 index 000000000..5534e6a2c --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/EisenParser.h @@ -0,0 +1,50 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.cpp new file mode 100644 index 000000000..e76c8e29d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.cpp @@ -0,0 +1,149 @@ +#include "Preprocessor.h" + +#include +#include +#include + +#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 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::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"); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.h new file mode 100644 index 000000000..970686d53 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Preprocessor.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#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 getParameters() { return params; } + + private: + QVector params; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.cpp b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.cpp new file mode 100644 index 000000000..a78684a99 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.cpp @@ -0,0 +1,202 @@ +#include "Tokenizer.h" + +#include + +#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 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#"); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.h b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.h new file mode 100644 index 000000000..1260a26e4 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/StructureSynth/Parser/Tokenizer.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#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 symbols; + int currentSymbol; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Exceptions/Exception.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Exceptions/Exception.h new file mode 100644 index 000000000..bd6306873 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Exceptions/Exception.h @@ -0,0 +1,29 @@ +#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; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.cpp new file mode 100644 index 000000000..a0bfeb167 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.cpp @@ -0,0 +1,164 @@ +#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()); + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.h new file mode 100644 index 000000000..ec3cbfc03 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Box.h @@ -0,0 +1,48 @@ +#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 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]; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.cpp new file mode 100644 index 000000000..0c48413a5 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.cpp @@ -0,0 +1,32 @@ +#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); + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.h new file mode 100644 index 000000000..18b9e8c08 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Dot.h @@ -0,0 +1,25 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp new file mode 100644 index 000000000..a76e99dfa --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp @@ -0,0 +1,658 @@ +#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 + +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.intersectiongetCenter()-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 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); } + } + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.h new file mode 100644 index 000000000..e25d5405f --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/EngineWidget.h @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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 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 raytracerCommands) { this->raytracerCommands = raytracerCommands; } + QVector 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 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 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; + }; + }; + +}; + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.cpp new file mode 100644 index 000000000..0956ad86a --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.cpp @@ -0,0 +1,60 @@ +#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(); + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.h new file mode 100644 index 000000000..7a1585a3e --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Grid.h @@ -0,0 +1,31 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.cpp new file mode 100644 index 000000000..126b9ed95 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.cpp @@ -0,0 +1,44 @@ +#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); + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.h new file mode 100644 index 000000000..abefa2a29 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Line.h @@ -0,0 +1,26 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.cpp new file mode 100644 index 000000000..1fb7caf55 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.cpp @@ -0,0 +1,131 @@ +#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()); + }; + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.h new file mode 100644 index 000000000..b000757ae --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Mesh.h @@ -0,0 +1,48 @@ +#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 triangles; + SyntopiaCore::Math::Vector3f oldRgb; + float oldAlpha; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.cpp new file mode 100644 index 000000000..bbacadf1c --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.cpp @@ -0,0 +1,129 @@ +#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()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]; + }; + + + } + +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.h new file mode 100644 index 000000000..214cf774a --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Object3D.h @@ -0,0 +1,100 @@ +#pragma once + +#include "SyntopiaCore/Math/Vector3.h" +#include +#include + +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; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp new file mode 100644 index 000000000..f5f9bdf49 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp @@ -0,0 +1,119 @@ +#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& 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); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h new file mode 100644 index 000000000..997eef213 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h @@ -0,0 +1,50 @@ +#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& 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; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp new file mode 100644 index 000000000..c36acd4c2 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp @@ -0,0 +1,13 @@ +#include "AtomicCounter.h" + +#include "SyntopiaCore/Math/Vector3.h" + +#include "AtomicCounter.h" + +namespace SyntopiaCore { + namespace GLEngine { + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h new file mode 100644 index 000000000..129c80d8b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +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; + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/ProgressiveOutput.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/ProgressiveOutput.h new file mode 100644 index 000000000..d7897a9b7 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/ProgressiveOutput.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp new file mode 100644 index 000000000..15a009c04 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp @@ -0,0 +1,273 @@ +#include + + +#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 (cwasCanceled()) { + // 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); + } + } + + + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h new file mode 100644 index 000000000..5b96cef85 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h @@ -0,0 +1,59 @@ +#pragma once + +#include "../Object3D.h" +#include "../EngineWidget.h" +#include +#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 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 threads; + AtomicCounter nextUnit; + AtomicCounter completedUnits; + int maxUnits; + RenderThread rt; + bool progressiveRender; + ProgressBox* progressBox; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp new file mode 100644 index 000000000..7307a5dba --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp @@ -0,0 +1,388 @@ +#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* 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* 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* 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(count, -1); + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h new file mode 100644 index 000000000..882ae8e3f --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h @@ -0,0 +1,88 @@ +#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 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; + }; + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp new file mode 100644 index 000000000..34b8cd7e2 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp @@ -0,0 +1,171 @@ +#include "Sampler.h" + +namespace SyntopiaCore { + namespace GLEngine { + + + 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.1415 / 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(nSqrt*nSqrt); + aaSamples = QVector(nAASamplesSqrt*nAASamplesSqrt); + lensSamples = QVector(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; + } + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h new file mode 100644 index 000000000..926498208 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h @@ -0,0 +1,123 @@ +#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 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(extent), 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 aoSamples; + QVector aaSamples; + QVector 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 aaOrder) { this->aaOrder = aaOrder; } + private: + int aoSamplesSqrt; + int aaSamplesSqrt; + QVector aaOrder; + + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp new file mode 100644 index 000000000..857bffb15 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp @@ -0,0 +1,193 @@ +#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[steps*steps*steps]; + for (int i = 0; i < steps*steps*steps; i++) grid[i] = QList(); + }; + + 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* 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 false; + } + + 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* 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* VoxelStepper::advance(double& maxT) { + QList* 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); + } + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h new file mode 100644 index 000000000..8ff3e3894 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h @@ -0,0 +1,56 @@ +#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* setupRay(Vector3f pos, Vector3f dir, double& maxT); + + inline double minn(double a, double b, double c) { + if (a* 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* grid; + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.cpp new file mode 100644 index 000000000..c247276b7 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.cpp @@ -0,0 +1,105 @@ +#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 (intersection2intersection) 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; + }; + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.h new file mode 100644 index 000000000..1a9a8ae1a --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Sphere.h @@ -0,0 +1,31 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.cpp new file mode 100644 index 000000000..abba976fd --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.cpp @@ -0,0 +1,69 @@ +#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()); + }; + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.h new file mode 100644 index 000000000..e3aeaec3d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/GLEngine/Triangle.h @@ -0,0 +1,33 @@ +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp new file mode 100644 index 000000000..20b264269 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp @@ -0,0 +1,39 @@ +#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); + + } + + } +} diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.h new file mode 100644 index 000000000..f60b581fe --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/ListWidgetLogger.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#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; + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.cpp new file mode 100644 index 000000000..5fa8ca02d --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.cpp @@ -0,0 +1,61 @@ +#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::loggers; + QStack Logger::timeStack; + QStack 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 + OutputDebugString((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... + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.h new file mode 100644 index 000000000..26f772f21 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Logging/Logging.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +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 loggers; + static QStack timeStack; + static QStack 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); + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.cpp new file mode 100644 index 000000000..270d5c75e --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.cpp @@ -0,0 +1,11 @@ +#include "Matrix4.h" + + +namespace SyntopiaCore { + namespace Math { + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.h new file mode 100644 index 000000000..cf55833dc --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Matrix4.h @@ -0,0 +1,192 @@ +#pragma once + +#include +#include + +#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 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 Identity() { + Matrix4 m; + m.v[0] = 1; m.v[5] = 1; m.v[10] = 1; m.v[15] = 1; + return m; + }; + + static Matrix4 ScaleMatrix(scalar s) { + Matrix4 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 Translation(scalar x,scalar y,scalar z) { + Matrix4 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 PlaneReflection(Vector3 n) { + n.normalize(); + Matrix4 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 Rotation(Vector3 axis, scalar angle) { + + Matrix4 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 operator* (const Matrix4& rhs) const { + Matrix4 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 operator* (const Vector3& rhs) const { + Vector3 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 Matrix4f ; + typedef Matrix4 Matrix4d ; + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.cpp new file mode 100644 index 000000000..42edc0582 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.cpp @@ -0,0 +1,75 @@ +#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 RandomNumberGenerator::getRandomIndices(int count) { + QVector sp(count); + for (int i = 0; i < count; i++) sp[i] = SortPair(i, getDouble()); + qSort(sp); + QVector 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 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 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); + } + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.h new file mode 100644 index 000000000..52b55a764 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Random.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include +#include +#include + +#include "../../ThirdPartyCode/MersenneTwister/MersenneTwister.h" +#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 = 0; } else { rng = new MTRand(); } setSeed(0); }; + ~RandomNumberGenerator() { delete rng; }; + + // This is only useful for backward compatibility. + // The Mersenne Twister is much better since it allows multiple independent streams. + void useStdLib(bool useOldLibrary) { + delete rng; rng = 0; + if (!useOldLibrary) { + rng = new MTRand(); + } + setSeed(lastSeed); + }; + + QVector getRandomIndices(int count); + + // Returns a vector, where the elements are ranked randomly. + template + QVector randomize(QVector list) { + QVector indices = getRandomIndices(list.count()); + QVector copy(list.count()); + for (int i = 0; i < list.count(); i++) copy[i] = list[indices[i]]; + return copy; + } + + + bool isUsingStdLib() { return (rng == 0); } + + // Returns a double in the interval [0;1] + double getDouble() { + if (rng) { + return rng->rand(); + } 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! + return rng->randNorm(0,variance); + } + + double getDouble(double min, double max) { + return getDouble()*(max-min)+min; + } + + + // Returns an integer between 0 and max (both inclusive). + int getInt(int max) { + if (rng) { + return rng->randInt(max); + } else { + return rand() % (max+1); // Probably not very good, use mersenne instead + } + } + + int getInt() { + if (rng) { + return rng->randInt(); + } 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; + MTRand* rng; + int uniformCounter2D; + int uniformCounter3D; + + }; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.cpp new file mode 100644 index 000000000..7a6d361e4 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.cpp @@ -0,0 +1,8 @@ +#include "Vector3.h" + + +namespace SyntopiaCore { + namespace Math { + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.h new file mode 100644 index 000000000..9bf52aaba --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Math/Vector3.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include + + +namespace SyntopiaCore { + namespace Math { + + /// A simple class for representing three-dimensional vectors. + template 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 normalized() const { scalar l = 1.0/length(); return Vector3(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 operator- (const Vector3& rhs) const { return Vector3(s[0]-rhs.s[0], s[1]-rhs.s[1], s[2]-rhs.s[2]); } + Vector3 operator+ (const Vector3& rhs) const { return Vector3(s[0]+rhs.s[0], s[1]+rhs.s[1], s[2]+rhs.s[2]); } + Vector3 operator- () const { return Vector3(-s[0], -s[1], -s[2]); } + bool operator== (const Vector3& rhs) const { return (s[0]==rhs.s[0] && s[1]==rhs.s[1] && s[2]==rhs.s[2]); } + + Vector3 operator* (scalar rhs) const { return Vector3(s[0]*rhs, s[1]*rhs, s[2]*rhs); } + Vector3 operator/ (scalar rhs) const { scalar t = 1.0/rhs; return Vector3(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 cross(const Vector3 b) const { + return Vector3( + 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 cross(const Vector3 a, const Vector3 b) { + return Vector3( + 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 a, const Vector3 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 + Vector3 operator*(T lhs, Vector3 rhs) { return Vector3(rhs[0]*lhs, rhs[1]*lhs, rhs[2]*lhs); } + + typedef Vector3 Vector3f ; + typedef Vector3 Vector3d ; + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.cpp new file mode 100644 index 000000000..5b6442374 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.cpp @@ -0,0 +1,32 @@ +#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); + } + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.h new file mode 100644 index 000000000..c0994329b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/ColorUtils.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include "../Math/Vector3.h" + +namespace SyntopiaCore { + namespace Misc { + + + class ColorUtils { + public: + static SyntopiaCore::Math::Vector3f HSVtoRGB(SyntopiaCore::Math::Vector3f hsv); + }; + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.cpp new file mode 100644 index 000000000..fed2b67c9 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.cpp @@ -0,0 +1,98 @@ +#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; + } + + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.h new file mode 100644 index 000000000..1aeda6a20 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/MiniParser.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + + +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 ; + }; + + } +} + + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.cpp new file mode 100644 index 000000000..d5ca19cdf --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.cpp @@ -0,0 +1,104 @@ +#include "Persistence.h" + +#include "../Logging/Logging.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace SyntopiaCore::Logging; + +namespace SyntopiaCore { + namespace Misc { + + + + + QMap& Persistence::GetStore() { + static QMap p; + return p; + } + + void Persistence::Store(QWidget* widget, QString storageName) { + if (storageName.isEmpty()) storageName = widget->objectName(); + if (qobject_cast(widget)) { + QLineEdit* lineEdit = qobject_cast(widget); + GetStore()[storageName] = QVariant(lineEdit->text()); + } else if (qobject_cast(widget)) { + QCheckBox* cb = qobject_cast(widget); + GetStore()[storageName] = QVariant(cb->isChecked()); + } else if (qobject_cast(widget)) { + QSpinBox* sb = qobject_cast(widget); + GetStore()[storageName] = QVariant(sb->value()); + } else if (qobject_cast(widget)) { + QRadioButton* rb = qobject_cast(widget); + GetStore()[storageName] = QVariant(rb->isChecked()); + } else if (qobject_cast(widget)) { + QComboBox* cb = qobject_cast(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(widget)) { + QLineEdit* lineEdit = qobject_cast(widget); + if (GetStore().contains(storageName)) { + lineEdit->setText(GetStore()[storageName].toString()); + } + + } else if (qobject_cast(widget)) { + QCheckBox* cb = qobject_cast(widget); + if (GetStore().contains(storageName)) { + cb->setChecked(GetStore()[storageName].toBool()); + } + + } else if (qobject_cast(widget)) { + QSpinBox* sb = qobject_cast(widget); + if (GetStore().contains(storageName)) { + sb->setValue(GetStore()[storageName].toInt()); + } + + } else if (qobject_cast(widget)) { + QRadioButton* rb = qobject_cast(widget); + if (GetStore().contains(storageName)) { + rb->setChecked(GetStore()[storageName].toInt()); + } + + } else if (qobject_cast(widget)) { + QComboBox* rb = qobject_cast(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; + } + + + } +} + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.h b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.h new file mode 100644 index 000000000..7b51fc392 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Persistence.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + + +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& GetStore(); + }; + + + } +} + + diff --git a/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Version.cpp b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Version.cpp new file mode 100644 index 000000000..2e54ba8a5 --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/SyntopiaCore/Misc/Version.cpp @@ -0,0 +1,63 @@ +#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::GetNewVersions(QString /*url*/) const { + return QList(); + } + + 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) +#include + + +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 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; + }; + + + } +} + + diff --git a/src/external/structuresynth-1.5/ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h b/src/external/structuresynth-1.5/ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h new file mode 100644 index 000000000..6f8d0745b --- /dev/null +++ b/src/external/structuresynth-1.5/ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h @@ -0,0 +1,427 @@ +#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 +#include +#include +#include +#include + +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 diff --git a/src/external/structuresynth-1.5/structuresynth.pro b/src/external/structuresynth-1.5/structuresynth.pro new file mode 100644 index 000000000..f23601d2a --- /dev/null +++ b/src/external/structuresynth-1.5/structuresynth.pro @@ -0,0 +1,124 @@ +TEMPLATE = lib +TARGET = ssynth +CONFIG += staticlib + +DEPENDPATH += . \ + ssynth \ + ssynth/StructureSynth/Model \ + ssynth/StructureSynth/Parser \ + ssynth/SyntopiaCore/Exceptions \ + ssynth/SyntopiaCore/GLEngine \ + ssynth/SyntopiaCore/GLEngine/Raytracer \ + ssynth/SyntopiaCore/Logging \ + ssynth/SyntopiaCore/Math \ + ssynth/SyntopiaCore/Misc \ + ssynth/ThirdPartyCode/MersenneTwister \ + ssynth/StructureSynth/Model/Rendering + + +INCLUDEPATH += ssynth + +# Input +HEADERS += ssynth/StructureSynth/Model/Action.h \ + ssynth/StructureSynth/Model/AmbiguousRule.h \ + ssynth/StructureSynth/Model/Builder.h \ + ssynth/StructureSynth/Model/ColorPool.h \ + ssynth/StructureSynth/Model/CustomRule.h \ + ssynth/StructureSynth/Model/ExecutionStack.h \ + ssynth/StructureSynth/Model/PrimitiveClass.h \ + ssynth/StructureSynth/Model/PrimitiveRule.h \ + ssynth/StructureSynth/Model/RandomStreams.h \ + ssynth/StructureSynth/Model/Rule.h \ + ssynth/StructureSynth/Model/RuleRef.h \ + ssynth/StructureSynth/Model/RuleSet.h \ + ssynth/StructureSynth/Model/State.h \ + ssynth/StructureSynth/Model/Transformation.h \ + ssynth/StructureSynth/Model/TransformationLoop.h \ + ssynth/StructureSynth/Parser/EisenParser.h \ + ssynth/StructureSynth/Parser/Preprocessor.h \ + ssynth/StructureSynth/Parser/Tokenizer.h \ + ssynth/SyntopiaCore/Exceptions/Exception.h \ + ssynth/SyntopiaCore/GLEngine/Box.h \ + ssynth/SyntopiaCore/GLEngine/Dot.h \ + ssynth/SyntopiaCore/GLEngine/EngineWidget.h \ + ssynth/SyntopiaCore/GLEngine/Grid.h \ + ssynth/SyntopiaCore/GLEngine/Line.h \ + ssynth/SyntopiaCore/GLEngine/Mesh.h \ + ssynth/SyntopiaCore/GLEngine/Object3D.h \ + ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.h \ + ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.h \ + ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.h \ + ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.h \ + ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.h \ + ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.h \ + ssynth/SyntopiaCore/GLEngine/Sphere.h \ + ssynth/SyntopiaCore/GLEngine/Triangle.h \ + ssynth/SyntopiaCore/Logging/ListWidgetLogger.h \ + ssynth/SyntopiaCore/Logging/Logging.h \ + ssynth/SyntopiaCore/Math/Matrix4.h \ + ssynth/SyntopiaCore/Math/Random.h \ + ssynth/SyntopiaCore/Math/Vector3.h \ + ssynth/SyntopiaCore/Misc/ColorUtils.h \ + ssynth/SyntopiaCore/Misc/MiniParser.h \ + ssynth/SyntopiaCore/Misc/Persistence.h \ + ssynth/SyntopiaCore/Misc/Version.h \ + ssynth/ThirdPartyCode/MersenneTwister/MersenneTwister.h \ + ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.h \ + ssynth/StructureSynth/Model/Rendering/Renderer.h \ + ssynth/StructureSynth/Model/Rendering/TemplateRenderer.h +SOURCES += ssynth/StructureSynth/Model/Action.cpp \ + ssynth/StructureSynth/Model/AmbiguousRule.cpp \ + ssynth/StructureSynth/Model/Builder.cpp \ + ssynth/StructureSynth/Model/ColorPool.cpp \ + ssynth/StructureSynth/Model/CustomRule.cpp \ + ssynth/StructureSynth/Model/ExecutionStack.cpp \ + ssynth/StructureSynth/Model/PrimitiveClass.cpp \ + ssynth/StructureSynth/Model/PrimitiveRule.cpp \ + ssynth/StructureSynth/Model/RandomStreams.cpp \ + ssynth/StructureSynth/Model/Rule.cpp \ + ssynth/StructureSynth/Model/RuleRef.cpp \ + ssynth/StructureSynth/Model/RuleSet.cpp \ + ssynth/StructureSynth/Model/State.cpp \ + ssynth/StructureSynth/Model/Transformation.cpp \ + ssynth/StructureSynth/Model/TransformationLoop.cpp \ + ssynth/StructureSynth/Parser/EisenParser.cpp \ + ssynth/StructureSynth/Parser/Preprocessor.cpp \ + ssynth/StructureSynth/Parser/Tokenizer.cpp \ + ssynth/SyntopiaCore/GLEngine/Box.cpp \ + ssynth/SyntopiaCore/GLEngine/Dot.cpp \ +# ssynth/SyntopiaCore/GLEngine/EngineWidget.cpp \ + ssynth/SyntopiaCore/GLEngine/Grid.cpp \ + ssynth/SyntopiaCore/GLEngine/Line.cpp \ + ssynth/SyntopiaCore/GLEngine/Mesh.cpp \ + ssynth/SyntopiaCore/GLEngine/Object3D.cpp \ + ssynth/SyntopiaCore/GLEngine/Raytracer/AtomicCounter.cpp \ +# ssynth/SyntopiaCore/GLEngine/Raytracer/RayTracer.cpp \ + ssynth/SyntopiaCore/GLEngine/Raytracer/RenderThread.cpp \ + ssynth/SyntopiaCore/GLEngine/Raytracer/Sampler.cpp \ + ssynth/SyntopiaCore/GLEngine/Raytracer/VoxelStepper.cpp \ + ssynth/SyntopiaCore/GLEngine/RaytraceTriangle.cpp \ +# ssynth/SyntopiaCore/GLEngine/Sphere.cpp \ + ssynth/SyntopiaCore/GLEngine/Triangle.cpp \ + ssynth/SyntopiaCore/Logging/ListWidgetLogger.cpp \ + ssynth/SyntopiaCore/Logging/Logging.cpp \ + ssynth/SyntopiaCore/Math/Matrix4.cpp \ + ssynth/SyntopiaCore/Math/Random.cpp \ + ssynth/SyntopiaCore/Math/Vector3.cpp \ + ssynth/SyntopiaCore/Misc/ColorUtils.cpp \ + ssynth/SyntopiaCore/Misc/MiniParser.cpp \ + ssynth/SyntopiaCore/Misc/Persistence.cpp \ + ssynth/SyntopiaCore/Misc/Version.cpp \ +# ssynth/StructureSynth/Model/Rendering/OpenGLRenderer.cpp \ + ssynth/StructureSynth/Model/Rendering/Renderer.cpp \ + ssynth/StructureSynth/Model/Rendering/TemplateRenderer.cpp +CONFIG+=opengl +QT+=xml opengl script +macx:DESTDIR = ../lib/macx +win32-g++:DESTDIR = ../lib/win32-gcc +win32-msvc2005:DESTDIR = ../lib/win32-msvc2005 +win32-msvc2008:DESTDIR = ../lib/win32-msvc2008 +win32-msvc2010:DESTDIR = ../lib/win32-msvc2010 +win32-msvc2012:DESTDIR = ../lib/win32-msvc2012 +linux-g++-32:DESTDIR = ../lib/linux-g++-32 +linux-g++-64:DESTDIR = ../lib/linux-g++-64 +linux-g++:DESTDIR = ../lib/linux-g++