kopia lustrzana https://github.com/jameshball/osci-render
Add support for .obj files (unoptimised)
rodzic
6162a7f203
commit
0efc108e76
|
@ -31,7 +31,6 @@ x86/
|
|||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
|
@ -74,8 +73,8 @@ StyleCopReport.xml
|
|||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
|
|
|
@ -16,15 +16,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p) : audioProcessor(p) {
|
|||
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
|
||||
if (chooser.getURLResult().isLocalFile()) {
|
||||
auto file = chooser.getResult();
|
||||
if (file.getFileExtension() == ".obj") {
|
||||
audioProcessor.parser.parse();
|
||||
} else if (file.getFileExtension() == ".svg") {
|
||||
|
||||
} else if (file.getFileExtension() == ".lua") {
|
||||
|
||||
} else if (file.getFileExtension() == ".txt") {
|
||||
|
||||
}
|
||||
audioProcessor.parser.parse(file.getFileExtension(), file.createInputStream());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -139,20 +139,20 @@ void OscirenderAudioProcessor::updateAngleDelta() {
|
|||
|
||||
void OscirenderAudioProcessor::addFrame(std::vector<std::unique_ptr<Shape>> frame) {
|
||||
const auto scope = frameFifo.write(1);
|
||||
|
||||
if (scope.blockSize1 > 0) {
|
||||
frameBuffer[scope.startIndex1].clear();
|
||||
for (auto& shape : frame) {
|
||||
frameBuffer[scope.startIndex1].push_back(std::move(shape));
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.blockSize2 > 0) {
|
||||
frameBuffer[scope.startIndex2].clear();
|
||||
for (auto& shape : frame) {
|
||||
frameBuffer[scope.startIndex2].push_back(std::move(shape));
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.blockSize1 > 0) {
|
||||
frameBuffer[scope.startIndex1].clear();
|
||||
for (auto& shape : frame) {
|
||||
frameBuffer[scope.startIndex1].push_back(std::move(shape));
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.blockSize2 > 0) {
|
||||
frameBuffer[scope.startIndex2].clear();
|
||||
for (auto& shape : frame) {
|
||||
frameBuffer[scope.startIndex2].push_back(std::move(shape));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessor::updateFrame() {
|
||||
|
@ -165,21 +165,13 @@ void OscirenderAudioProcessor::updateFrame() {
|
|||
const auto scope = frameFifo.read(1);
|
||||
|
||||
if (scope.blockSize1 > 0) {
|
||||
frame.clear();
|
||||
for (auto& shape : frameBuffer[scope.startIndex1]) {
|
||||
frame.push_back(std::move(shape));
|
||||
}
|
||||
frame.swap(frameBuffer[scope.startIndex1]);
|
||||
} else if (scope.blockSize2 > 0) {
|
||||
frame.swap(frameBuffer[scope.startIndex2]);
|
||||
}
|
||||
|
||||
if (scope.blockSize2 > 0) {
|
||||
frame.clear();
|
||||
for (auto& shape : frameBuffer[scope.startIndex2]) {
|
||||
frame.push_back(std::move(shape));
|
||||
}
|
||||
}
|
||||
frameLength = Shape::totalLength(frame);
|
||||
}
|
||||
|
||||
frameLength = Shape::totalLength(frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,8 +222,10 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
|
|||
channelData[0][sample] = x;
|
||||
}
|
||||
|
||||
frameDrawn += lengthIncrement;
|
||||
shapeDrawn += lengthIncrement;
|
||||
// hard cap on how many times it can be over the length to
|
||||
// prevent audio stuttering
|
||||
frameDrawn += std::min(lengthIncrement, 20 * length);
|
||||
shapeDrawn += std::min(lengthIncrement, 20 * length);
|
||||
|
||||
// Need to skip all shapes that the lengthIncrement draws over.
|
||||
// This is especially an issue when there are lots of small lines being
|
||||
|
@ -243,7 +237,9 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
|
|||
currentShape = 0;
|
||||
break;
|
||||
}
|
||||
length = frame[currentShape]->length();
|
||||
// POTENTIAL TODO: Think of a way to make this more efficient when iterating
|
||||
// this loop many times
|
||||
length = frame[currentShape]->len;
|
||||
}
|
||||
|
||||
if (frameDrawn > frameLength) {
|
||||
|
|
|
@ -80,7 +80,7 @@ private:
|
|||
std::vector<std::unique_ptr<Shape>> frameBuffer[10];
|
||||
|
||||
int currentShape = 0;
|
||||
std::vector<std::unique_ptr<Shape>> frame;
|
||||
std::vector<std::unique_ptr<Shape>> frame;
|
||||
double frameLength;
|
||||
double shapeDrawn = 0.0;
|
||||
double frameDrawn = 0.0;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#include "Camera.h"
|
||||
#include "../shape/Line.h"
|
||||
|
||||
Camera::Camera(double focalLength, double x, double y, double z) : focalLength(focalLength), x(x), y(y), z(z) {}
|
||||
|
||||
std::vector<std::unique_ptr<Shape>> Camera::draw(WorldObject& object)
|
||||
{
|
||||
std::vector<std::unique_ptr<Shape>> shapes;
|
||||
for (auto& edge : object.edges) {
|
||||
// rotate around x-axis
|
||||
double cosValue = std::cos(object.rotateX);
|
||||
double sinValue = std::sin(object.rotateX);
|
||||
double y2 = cosValue * edge.y1 - sinValue * edge.z1;
|
||||
double z2 = sinValue * edge.y1 + cosValue * edge.z1;
|
||||
|
||||
// rotate around y-axis
|
||||
cosValue = std::cos(object.rotateY);
|
||||
sinValue = std::sin(object.rotateY);
|
||||
double x2 = cosValue * edge.x1 + sinValue * z2;
|
||||
double z3 = -sinValue * edge.x1 + cosValue * z2;
|
||||
|
||||
// rotate around z-axis
|
||||
cosValue = cos(object.rotateZ);
|
||||
sinValue = sin(object.rotateZ);
|
||||
double x3 = cosValue * x2 - sinValue * y2;
|
||||
double y3 = sinValue * x2 + cosValue * y2;
|
||||
|
||||
double startX = x3 * focalLength / (z3 - z) + x;
|
||||
double startY = y3 * focalLength / (z3 - z) + y;
|
||||
|
||||
// rotate around x-axis
|
||||
cosValue = std::cos(object.rotateX);
|
||||
sinValue = std::sin(object.rotateX);
|
||||
y2 = cosValue * edge.y2 - sinValue * edge.z2;
|
||||
z2 = sinValue * edge.y2 + cosValue * edge.z2;
|
||||
|
||||
// rotate around y-axis
|
||||
cosValue = std::cos(object.rotateY);
|
||||
sinValue = std::sin(object.rotateY);
|
||||
x2 = cosValue * edge.x2 + sinValue * z2;
|
||||
z3 = -sinValue * edge.x2 + cosValue * z2;
|
||||
|
||||
// rotate around z-axis
|
||||
cosValue = cos(object.rotateZ);
|
||||
sinValue = sin(object.rotateZ);
|
||||
x3 = cosValue * x2 - sinValue * y2;
|
||||
y3 = sinValue * x2 + cosValue * y2;
|
||||
|
||||
double endX = x3 * focalLength / (z3 - z) + x;
|
||||
double endY = y3 * focalLength / (z3 - z) + y;
|
||||
|
||||
shapes.push_back(std::make_unique<Line>(startX, startY, endX, endY));
|
||||
}
|
||||
return shapes;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "WorldObject.h"
|
||||
#include "../shape/Shape.h"
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Camera(double focalLength, double x, double y, double z);
|
||||
|
||||
std::vector<std::unique_ptr<Shape>> draw(WorldObject& object);
|
||||
private:
|
||||
double focalLength;
|
||||
double x, y, z;
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
#include "Line3D.h"
|
||||
|
||||
Line3D::Line3D(double x1, double y1, double z1, double x2, double y2, double z2) : x1(x1), y1(y1), z1(z1), x2(x2), y2(y2), z2(z2) {}
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class Line3D {
|
||||
public:
|
||||
Line3D(double, double, double, double, double, double);
|
||||
|
||||
double x1, y1, z1, x2, y2, z2;
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
#include "WorldObject.h"
|
||||
|
||||
WorldObject::WorldObject(juce::InputStream& stream) {
|
||||
std::string key;
|
||||
while (!stream.isExhausted()) {
|
||||
auto line = stream.readNextLine();
|
||||
key = "";
|
||||
std::stringstream stringstream(line.toStdString());
|
||||
stringstream >> key >> std::ws;
|
||||
|
||||
if (key == "v") { // vertex
|
||||
vertex v; float x;
|
||||
while (!stringstream.eof()) {
|
||||
stringstream >> x >> std::ws;
|
||||
v.v.push_back(x);
|
||||
}
|
||||
vertices.push_back(v);
|
||||
}
|
||||
else if (key == "vp") { // parameter
|
||||
vertex v; float x;
|
||||
while (!stringstream.eof()) {
|
||||
stringstream >> x >> std::ws;
|
||||
v.v.push_back(x);
|
||||
}
|
||||
parameters.push_back(v);
|
||||
}
|
||||
else if (key == "vt") { // texture coordinate
|
||||
vertex v; float x;
|
||||
while (!stringstream.eof()) {
|
||||
stringstream >> x >> std::ws;
|
||||
v.v.push_back(x);
|
||||
}
|
||||
texcoords.push_back(v);
|
||||
}
|
||||
else if (key == "vn") { // normal
|
||||
vertex v; float x;
|
||||
while (!stringstream.eof()) {
|
||||
stringstream >> x >> std::ws;
|
||||
v.v.push_back(x);
|
||||
}
|
||||
v.normalize();
|
||||
normals.push_back(v);
|
||||
}
|
||||
else if (key == "f") { // face
|
||||
face f; int v, t, n;
|
||||
while (!stringstream.eof()) {
|
||||
stringstream >> v >> std::ws;
|
||||
f.vertex.push_back(v - 1);
|
||||
if (stringstream.peek() == '/') {
|
||||
stringstream.get();
|
||||
if (stringstream.peek() == '/') {
|
||||
stringstream.get();
|
||||
stringstream >> n >> std::ws;
|
||||
f.normal.push_back(n - 1);
|
||||
}
|
||||
else {
|
||||
stringstream >> t >> std::ws;
|
||||
f.texture.push_back(t - 1);
|
||||
if (stringstream.peek() == '/') {
|
||||
stringstream.get();
|
||||
stringstream >> n >> std::ws;
|
||||
f.normal.push_back(n - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
faces.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
double x = 0.0, y = 0.0, z = 0.0;
|
||||
double max = 0.0;
|
||||
for (auto& v : vertices) {
|
||||
x += v.v[0];
|
||||
y += v.v[1];
|
||||
z += v.v[2];
|
||||
if (std::abs(v.v[0]) > max) max = std::abs(v.v[0]);
|
||||
if (std::abs(v.v[1]) > max) max = std::abs(v.v[1]);
|
||||
if (std::abs(v.v[2]) > max) max = std::abs(v.v[2]);
|
||||
}
|
||||
x /= vertices.size();
|
||||
y /= vertices.size();
|
||||
z /= vertices.size();
|
||||
|
||||
// IMPORTANT TODO: get rid of duplicate edges here using an unordered_set
|
||||
// as there are wayyy too many edges being rendered causing poor performance
|
||||
|
||||
for (auto& f : faces) {
|
||||
for (int i = 0; i < f.vertex.size(); i++) {
|
||||
double x1 = (vertices[f.vertex[i]].v[0] - x) / max;
|
||||
double y1 = (vertices[f.vertex[i]].v[1] - y) / max;
|
||||
double z1 = (vertices[f.vertex[i]].v[2] - z) / max;
|
||||
double x2 = (vertices[f.vertex[(i + 1) % f.vertex.size()]].v[0] - x) / max;
|
||||
double y2 = (vertices[f.vertex[(i + 1) % f.vertex.size()]].v[1] - y) / max;
|
||||
double z2 = (vertices[f.vertex[(i + 1) % f.vertex.size()]].v[2] - z) / max;
|
||||
|
||||
edges.push_back(Line3D(x1, y1, z1, x2, y2, z2));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "Line3D.h"
|
||||
|
||||
// from https://www.keithlantz.net/2011/10/a-preliminary-wavefront-obj-loader-in-c/
|
||||
struct vertex {
|
||||
std::vector<float> v;
|
||||
void normalize() {
|
||||
float magnitude = 0.0f;
|
||||
for (int i = 0; i < v.size(); i++)
|
||||
magnitude += pow(v[i], 2.0f);
|
||||
magnitude = sqrt(magnitude);
|
||||
for (int i = 0; i < v.size(); i++)
|
||||
v[i] /= magnitude;
|
||||
}
|
||||
vertex operator-(vertex v2) {
|
||||
vertex v3;
|
||||
if (v.size() != v2.v.size()) {
|
||||
v3.v.push_back(0.0f);
|
||||
v3.v.push_back(0.0f);
|
||||
v3.v.push_back(0.0f);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < v.size(); i++)
|
||||
v3.v.push_back(v[i] - v2.v[i]);
|
||||
}
|
||||
return v3;
|
||||
}
|
||||
vertex cross(vertex v2) {
|
||||
vertex v3;
|
||||
if (v.size() != 3 || v2.v.size() != 3) {
|
||||
v3.v.push_back(0.0f);
|
||||
v3.v.push_back(0.0f);
|
||||
v3.v.push_back(0.0f);
|
||||
}
|
||||
else {
|
||||
v3.v.push_back(v[1] * v2.v[2] - v[2] * v2.v[1]);
|
||||
v3.v.push_back(v[2] * v2.v[0] - v[0] * v2.v[2]);
|
||||
v3.v.push_back(v[0] * v2.v[1] - v[1] * v2.v[0]);
|
||||
}
|
||||
return v3;
|
||||
}
|
||||
};
|
||||
|
||||
struct face {
|
||||
std::vector<int> vertex;
|
||||
std::vector<int> texture;
|
||||
std::vector<int> normal;
|
||||
};
|
||||
|
||||
class WorldObject {
|
||||
public:
|
||||
WorldObject(juce::InputStream&);
|
||||
|
||||
double rotateX = 0.0, rotateY = 0.0, rotateZ = 0.0;
|
||||
|
||||
std::vector<Line3D> edges;
|
||||
private:
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<vertex> texcoords;
|
||||
std::vector<vertex> normals;
|
||||
std::vector<vertex> parameters;
|
||||
std::vector<face> faces;
|
||||
};
|
|
@ -3,10 +3,17 @@
|
|||
|
||||
FileParser::FileParser() {}
|
||||
|
||||
void FileParser::parse() {
|
||||
void FileParser::parse(juce::String extension, std::unique_ptr<juce::InputStream> stream) {
|
||||
if (extension == ".obj") {
|
||||
object = std::make_unique<WorldObject>(*stream);
|
||||
camera = std::make_unique<Camera>(1.0, 0, 0, -1.0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Shape>> FileParser::next() {
|
||||
if (object != nullptr && camera != nullptr) {
|
||||
return camera->draw(*object);
|
||||
}
|
||||
auto shapes = std::vector<std::unique_ptr<Shape>>();
|
||||
shapes.push_back(std::make_unique<Line>(0.0, 0.0, 1.0, 1.0));
|
||||
return shapes;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "FrameSource.h"
|
||||
#include "../shape/Shape.h"
|
||||
#include "../obj/WorldObject.h"
|
||||
#include "../obj/Camera.h"
|
||||
|
||||
class FileParser : public FrameSource {
|
||||
public:
|
||||
FileParser();
|
||||
|
||||
void parse() override;
|
||||
void parse(juce::String extension, std::unique_ptr<juce::InputStream>) override;
|
||||
std::vector<std::unique_ptr<Shape>> next() override;
|
||||
bool isActive() override;
|
||||
void disable() override;
|
||||
|
@ -16,4 +17,7 @@ public:
|
|||
|
||||
private:
|
||||
bool active = true;
|
||||
|
||||
std::unique_ptr<WorldObject> object;
|
||||
std::unique_ptr<Camera> camera;
|
||||
};
|
|
@ -8,7 +8,7 @@ FrameProducer::~FrameProducer() {
|
|||
}
|
||||
|
||||
void FrameProducer::run() {
|
||||
while (frameSource.isActive() && !threadShouldExit()) {
|
||||
while (!threadShouldExit() && frameSource.isActive()) {
|
||||
frameConsumer.addFrame(frameSource.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "../shape/Shape.h"
|
||||
|
||||
class FrameSource {
|
||||
public:
|
||||
virtual void parse() = 0;
|
||||
virtual void parse(juce::String extension, std::unique_ptr<juce::InputStream>) = 0;
|
||||
virtual std::vector<std::unique_ptr<Shape>> next() = 0;
|
||||
virtual bool isActive() = 0;
|
||||
virtual void disable() = 0;
|
||||
|
|
|
@ -60,7 +60,7 @@ void CubicBezierCurve::translate(double x, double y) {
|
|||
}
|
||||
|
||||
double CubicBezierCurve::length() {
|
||||
if (len == INVALID_LENGTH) {
|
||||
if (len < 0) {
|
||||
// Euclidean distance approximation based on octagonal boundary
|
||||
double dx = std::abs(x4 - x1);
|
||||
double dy = std::abs(y4 - y1);
|
||||
|
|
|
@ -14,6 +14,5 @@ public:
|
|||
double length() override;
|
||||
|
||||
double x1, y1, x2, y2, x3, y3, x4, y4;
|
||||
double len = INVALID_LENGTH;
|
||||
|
||||
};
|
|
@ -38,7 +38,7 @@ void Line::translate(double x, double y) {
|
|||
y2 += y;
|
||||
}
|
||||
|
||||
double Line::length() {
|
||||
double inline Line::length() {
|
||||
if (len < 0) {
|
||||
// Euclidean distance approximation based on octagonal boundary
|
||||
double dx = std::abs(x2 - x1);
|
||||
|
|
|
@ -14,6 +14,5 @@ public:
|
|||
double length() override;
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
double len;
|
||||
|
||||
};
|
|
@ -17,4 +17,6 @@ public:
|
|||
static double totalLength(std::vector<std::unique_ptr<Shape>>&);
|
||||
|
||||
const double INVALID_LENGTH = -1.0;
|
||||
|
||||
double len = INVALID_LENGTH;
|
||||
};
|
|
@ -33,9 +33,12 @@
|
|||
<FILE id="hCrVUD" name="FrameSource.h" compile="0" resource="0" file="Source/parser/FrameSource.h"/>
|
||||
</GROUP>
|
||||
<GROUP id="{E6ED85A9-3843-825F-EF48-BCF81E38F8AD}" name="obj">
|
||||
<FILE id="aUh7fp" name="LICENSE" compile="0" resource="1" file="Source/obj/LICENSE"/>
|
||||
<FILE id="npNAJ1" name="obj.c" compile="1" resource="0" file="Source/obj/obj.c"/>
|
||||
<FILE id="iVQVY6" name="obj.h" compile="0" resource="0" file="Source/obj/obj.h"/>
|
||||
<FILE id="Tyz6WY" name="Camera.cpp" compile="1" resource="0" file="Source/obj/Camera.cpp"/>
|
||||
<FILE id="ix12FT" name="Camera.h" compile="0" resource="0" file="Source/obj/Camera.h"/>
|
||||
<FILE id="JJTNO9" name="Line3D.cpp" compile="1" resource="0" file="Source/obj/Line3D.cpp"/>
|
||||
<FILE id="TMrur0" name="Line3D.h" compile="0" resource="0" file="Source/obj/Line3D.h"/>
|
||||
<FILE id="YNsbe9" name="WorldObject.cpp" compile="1" resource="0" file="Source/obj/WorldObject.cpp"/>
|
||||
<FILE id="SZBVI9" name="WorldObject.h" compile="0" resource="0" file="Source/obj/WorldObject.h"/>
|
||||
</GROUP>
|
||||
<FILE id="GKBQ8j" name="MainComponent.cpp" compile="1" resource="0"
|
||||
file="Source/MainComponent.cpp"/>
|
||||
|
|
Ładowanie…
Reference in New Issue