- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
FragmentShader.cpp
|
||||
Created: 11 Nov 2014 12:17:53pm
|
||||
Author: Felix Faire
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated by the Introjucer!
|
||||
|
||||
It contains the basic startup code for a Juce application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
|
||||
Component* createMainContentComponent();
|
||||
|
||||
//==============================================================================
|
||||
class OpenGLAppExampleApplication : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OpenGLAppExampleApplication() {}
|
||||
|
||||
const String getApplicationName() override { return ProjectInfo::projectName; }
|
||||
const String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String& commandLine) override
|
||||
{
|
||||
// This method is where you should put your application's initialisation code..
|
||||
|
||||
mainWindow = new MainWindow (getApplicationName());
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Add your application's shutdown code here..
|
||||
|
||||
mainWindow = nullptr; // (deletes our window)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
// This is called when the app is being asked to quit: you can ignore this
|
||||
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||
quit();
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const String& commandLine) override
|
||||
{
|
||||
// When another instance of the app is launched while this one is running,
|
||||
// this method is invoked, and the commandLine parameter tells you what
|
||||
// the other instance's command-line arguments were.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This class implements the desktop window that contains an instance of
|
||||
our MainContentComponent class.
|
||||
*/
|
||||
class MainWindow : public DocumentWindow
|
||||
{
|
||||
public:
|
||||
MainWindow (String name) : DocumentWindow (name,
|
||||
Colours::lightgrey,
|
||||
DocumentWindow::allButtons)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setContentOwned (createMainContentComponent(), true);
|
||||
setResizable (true, true);
|
||||
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
// This is called when the user tries to close this window. Here, we'll just
|
||||
// ask the app to quit when this happens, but you can change this to do
|
||||
// whatever you need.
|
||||
JUCEApplication::getInstance()->systemRequestedQuit();
|
||||
}
|
||||
|
||||
/* Note: Be careful if you override any DocumentWindow methods - the base
|
||||
class uses a lot of them, so by overriding you might break its functionality.
|
||||
It's best to do all your work in your content component instead, but if
|
||||
you really have to override any DocumentWindow methods, make sure your
|
||||
subclass also calls the superclass's method.
|
||||
*/
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
private:
|
||||
ScopedPointer<MainWindow> mainWindow;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This macro generates the main() routine that launches the app.
|
||||
START_JUCE_APPLICATION (OpenGLAppExampleApplication)
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef MAINCOMPONENT_H_INCLUDED
|
||||
#define MAINCOMPONENT_H_INCLUDED
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "Resources/WavefrontObjParser.h"
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class MainContentComponent : public OpenGLAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MainContentComponent()
|
||||
{
|
||||
setSize (800, 600);
|
||||
}
|
||||
|
||||
~MainContentComponent()
|
||||
{
|
||||
shutdownOpenGL();
|
||||
}
|
||||
|
||||
void initialise() override
|
||||
{
|
||||
createShaders();
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
shader = nullptr;
|
||||
shape = nullptr;
|
||||
attributes = nullptr;
|
||||
uniforms = nullptr;
|
||||
}
|
||||
|
||||
Matrix3D<float> getProjectionMatrix() const
|
||||
{
|
||||
float w = 1.0f / (0.5f + 0.1f);
|
||||
float h = w * getLocalBounds().toFloat().getAspectRatio (false);
|
||||
return Matrix3D<float>::fromFrustum (-w, w, -h, h, 4.0f, 30.0f);
|
||||
}
|
||||
|
||||
Matrix3D<float> getViewMatrix() const
|
||||
{
|
||||
Matrix3D<float> viewMatrix (Vector3D<float> (0.0f, 0.0f, -10.0f));
|
||||
Matrix3D<float> rotationMatrix
|
||||
= viewMatrix.rotated (Vector3D<float> (-0.3f, 5.0f * std::sin (getFrameCounter() * 0.01f), 0.0f));
|
||||
|
||||
return viewMatrix * rotationMatrix;
|
||||
}
|
||||
|
||||
void render() override
|
||||
{
|
||||
|
||||
jassert (OpenGLHelpers::isContextActive());
|
||||
|
||||
const float desktopScale = (float) openGLContext.getRenderingScale();
|
||||
OpenGLHelpers::clear (Colour::greyLevel (0.1f));
|
||||
|
||||
glEnable (GL_BLEND);
|
||||
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glViewport (0, 0, roundToInt (desktopScale * getWidth()), roundToInt (desktopScale * getHeight()));
|
||||
|
||||
shader->use();
|
||||
|
||||
if (uniforms->projectionMatrix != nullptr)
|
||||
uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false);
|
||||
|
||||
if (uniforms->viewMatrix != nullptr)
|
||||
uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false);
|
||||
|
||||
shape->draw (openGLContext, *attributes);
|
||||
|
||||
// Reset the element buffers so child Components draw correctly
|
||||
openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
// You can add your component specific drawing code here!
|
||||
// This will draw over the top of the openGL background.
|
||||
|
||||
g.setColour(Colours::white);
|
||||
g.setFont (20);
|
||||
g.drawText ("OpenGL Example", 25, 20, 300, 30, Justification::left);
|
||||
g.drawLine (20, 20, 170, 20);
|
||||
g.drawLine (20, 50, 170, 50);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
|
||||
}
|
||||
|
||||
void createShaders()
|
||||
{
|
||||
vertexShader =
|
||||
"attribute vec4 position;\n"
|
||||
"attribute vec4 sourceColour;\n"
|
||||
"attribute vec2 texureCoordIn;\n"
|
||||
"\n"
|
||||
"uniform mat4 projectionMatrix;\n"
|
||||
"uniform mat4 viewMatrix;\n"
|
||||
"\n"
|
||||
"varying vec4 destinationColour;\n"
|
||||
"varying vec2 textureCoordOut;\n"
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" destinationColour = sourceColour;\n"
|
||||
" textureCoordOut = texureCoordIn;\n"
|
||||
" gl_Position = projectionMatrix * viewMatrix * position;\n"
|
||||
"}\n";
|
||||
|
||||
fragmentShader =
|
||||
#if JUCE_OPENGL_ES
|
||||
"varying lowp vec4 destinationColour;\n"
|
||||
"varying lowp vec2 textureCoordOut;\n"
|
||||
#else
|
||||
"varying vec4 destinationColour;\n"
|
||||
"varying vec2 textureCoordOut;\n"
|
||||
#endif
|
||||
"\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec4 colour = vec4(0.95, 0.57, 0.03, 0.7);\n"
|
||||
" gl_FragColor = colour;\n"
|
||||
"}\n";
|
||||
|
||||
ScopedPointer<OpenGLShaderProgram> newShader (new OpenGLShaderProgram (openGLContext));
|
||||
String statusText;
|
||||
|
||||
if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (vertexShader))
|
||||
&& newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (fragmentShader))
|
||||
&& newShader->link())
|
||||
{
|
||||
shape = nullptr;
|
||||
attributes = nullptr;
|
||||
uniforms = nullptr;
|
||||
|
||||
shader = newShader;
|
||||
shader->use();
|
||||
|
||||
shape = new Shape (openGLContext);
|
||||
attributes = new Attributes (openGLContext, *shader);
|
||||
uniforms = new Uniforms (openGLContext, *shader);
|
||||
|
||||
statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusText = newShader->getLastError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
float normal[3];
|
||||
float colour[4];
|
||||
float texCoord[2];
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This class just manages the attributes that the shaders use.
|
||||
struct Attributes
|
||||
{
|
||||
Attributes (OpenGLContext& openGLContext, OpenGLShaderProgram& shader)
|
||||
{
|
||||
position = createAttribute (openGLContext, shader, "position");
|
||||
normal = createAttribute (openGLContext, shader, "normal");
|
||||
sourceColour = createAttribute (openGLContext, shader, "sourceColour");
|
||||
texureCoordIn = createAttribute (openGLContext, shader, "texureCoordIn");
|
||||
}
|
||||
|
||||
void enable (OpenGLContext& openGLContext)
|
||||
{
|
||||
if (position != nullptr)
|
||||
{
|
||||
openGLContext.extensions.glVertexAttribPointer (position->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), 0);
|
||||
openGLContext.extensions.glEnableVertexAttribArray (position->attributeID);
|
||||
}
|
||||
|
||||
if (normal != nullptr)
|
||||
{
|
||||
openGLContext.extensions.glVertexAttribPointer (normal->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 3));
|
||||
openGLContext.extensions.glEnableVertexAttribArray (normal->attributeID);
|
||||
}
|
||||
|
||||
if (sourceColour != nullptr)
|
||||
{
|
||||
openGLContext.extensions.glVertexAttribPointer (sourceColour->attributeID, 4, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 6));
|
||||
openGLContext.extensions.glEnableVertexAttribArray (sourceColour->attributeID);
|
||||
}
|
||||
|
||||
if (texureCoordIn != nullptr)
|
||||
{
|
||||
openGLContext.extensions.glVertexAttribPointer (texureCoordIn->attributeID, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 10));
|
||||
openGLContext.extensions.glEnableVertexAttribArray (texureCoordIn->attributeID);
|
||||
}
|
||||
}
|
||||
|
||||
void disable (OpenGLContext& openGLContext)
|
||||
{
|
||||
if (position != nullptr) openGLContext.extensions.glDisableVertexAttribArray (position->attributeID);
|
||||
if (normal != nullptr) openGLContext.extensions.glDisableVertexAttribArray (normal->attributeID);
|
||||
if (sourceColour != nullptr) openGLContext.extensions.glDisableVertexAttribArray (sourceColour->attributeID);
|
||||
if (texureCoordIn != nullptr) openGLContext.extensions.glDisableVertexAttribArray (texureCoordIn->attributeID);
|
||||
}
|
||||
|
||||
ScopedPointer<OpenGLShaderProgram::Attribute> position, normal, sourceColour, texureCoordIn;
|
||||
|
||||
private:
|
||||
static OpenGLShaderProgram::Attribute* createAttribute (OpenGLContext& openGLContext,
|
||||
OpenGLShaderProgram& shader,
|
||||
const char* attributeName)
|
||||
{
|
||||
if (openGLContext.extensions.glGetAttribLocation (shader.getProgramID(), attributeName) < 0)
|
||||
return nullptr;
|
||||
|
||||
return new OpenGLShaderProgram::Attribute (shader, attributeName);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This class just manages the uniform values that the demo shaders use.
|
||||
struct Uniforms
|
||||
{
|
||||
Uniforms (OpenGLContext& openGLContext, OpenGLShaderProgram& shader)
|
||||
{
|
||||
projectionMatrix = createUniform (openGLContext, shader, "projectionMatrix");
|
||||
viewMatrix = createUniform (openGLContext, shader, "viewMatrix");
|
||||
}
|
||||
|
||||
ScopedPointer<OpenGLShaderProgram::Uniform> projectionMatrix, viewMatrix;
|
||||
|
||||
private:
|
||||
static OpenGLShaderProgram::Uniform* createUniform (OpenGLContext& openGLContext,
|
||||
OpenGLShaderProgram& shader,
|
||||
const char* uniformName)
|
||||
{
|
||||
if (openGLContext.extensions.glGetUniformLocation (shader.getProgramID(), uniformName) < 0)
|
||||
return nullptr;
|
||||
|
||||
return new OpenGLShaderProgram::Uniform (shader, uniformName);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This loads a 3D model from an OBJ file and converts it into some vertex buffers
|
||||
that we can draw.
|
||||
*/
|
||||
struct Shape
|
||||
{
|
||||
Shape (OpenGLContext& openGLContext)
|
||||
{
|
||||
if (shapeFile.load (BinaryData::teapot_obj).wasOk())
|
||||
for (int i = 0; i < shapeFile.shapes.size(); ++i)
|
||||
vertexBuffers.add (new VertexBuffer (openGLContext, *shapeFile.shapes.getUnchecked(i)));
|
||||
|
||||
}
|
||||
|
||||
void draw (OpenGLContext& openGLContext, Attributes& attributes)
|
||||
{
|
||||
for (int i = 0; i < vertexBuffers.size(); ++i)
|
||||
{
|
||||
VertexBuffer& vertexBuffer = *vertexBuffers.getUnchecked (i);
|
||||
vertexBuffer.bind();
|
||||
|
||||
attributes.enable (openGLContext);
|
||||
glDrawElements (GL_TRIANGLES, vertexBuffer.numIndices, GL_UNSIGNED_INT, 0);
|
||||
attributes.disable (openGLContext);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct VertexBuffer
|
||||
{
|
||||
VertexBuffer (OpenGLContext& context, WavefrontObjFile::Shape& shape) : openGLContext (context)
|
||||
{
|
||||
numIndices = shape.mesh.indices.size();
|
||||
|
||||
openGLContext.extensions.glGenBuffers (1, &vertexBuffer);
|
||||
openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
|
||||
|
||||
Array<Vertex> vertices;
|
||||
createVertexListFromMesh (shape.mesh, vertices, Colours::green);
|
||||
|
||||
openGLContext.extensions.glBufferData (GL_ARRAY_BUFFER, vertices.size() * sizeof (Vertex),
|
||||
vertices.getRawDataPointer(), GL_STATIC_DRAW);
|
||||
|
||||
openGLContext.extensions.glGenBuffers (1, &indexBuffer);
|
||||
openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
openGLContext.extensions.glBufferData (GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof (juce::uint32),
|
||||
shape.mesh.indices.getRawDataPointer(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
~VertexBuffer()
|
||||
{
|
||||
openGLContext.extensions.glDeleteBuffers (1, &vertexBuffer);
|
||||
openGLContext.extensions.glDeleteBuffers (1, &indexBuffer);
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
|
||||
openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
}
|
||||
|
||||
GLuint vertexBuffer, indexBuffer;
|
||||
int numIndices;
|
||||
OpenGLContext& openGLContext;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VertexBuffer)
|
||||
};
|
||||
|
||||
WavefrontObjFile shapeFile;
|
||||
OwnedArray<VertexBuffer> vertexBuffers;
|
||||
|
||||
static void createVertexListFromMesh (const WavefrontObjFile::Mesh& mesh, Array<Vertex>& list, Colour colour)
|
||||
{
|
||||
const float scale = 0.2f;
|
||||
WavefrontObjFile::TextureCoord defaultTexCoord = { 0.5f, 0.5f };
|
||||
WavefrontObjFile::Vertex defaultNormal = { 0.5f, 0.5f, 0.5f };
|
||||
|
||||
for (int i = 0; i < mesh.vertices.size(); ++i)
|
||||
{
|
||||
const WavefrontObjFile::Vertex& v = mesh.vertices.getReference (i);
|
||||
|
||||
const WavefrontObjFile::Vertex& n
|
||||
= i < mesh.normals.size() ? mesh.normals.getReference (i) : defaultNormal;
|
||||
|
||||
const WavefrontObjFile::TextureCoord& tc
|
||||
= i < mesh.textureCoords.size() ? mesh.textureCoords.getReference (i) : defaultTexCoord;
|
||||
|
||||
Vertex vert =
|
||||
{
|
||||
{ scale * v.x, scale * v.y, scale * v.z, },
|
||||
{ scale * n.x, scale * n.y, scale * n.z, },
|
||||
{ colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue(), colour.getFloatAlpha() },
|
||||
{ tc.x, tc.y }
|
||||
};
|
||||
|
||||
list.add (vert);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const char* vertexShader;
|
||||
const char* fragmentShader;
|
||||
|
||||
ScopedPointer<OpenGLShaderProgram> shader;
|
||||
ScopedPointer<Shape> shape;
|
||||
ScopedPointer<Attributes> attributes;
|
||||
ScopedPointer<Uniforms> uniforms;
|
||||
|
||||
String newVertexShader, newFragmentShader;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
|
||||
};
|
||||
|
||||
|
||||
// (This function is called by the app startup code to create our main component)
|
||||
Component* createMainContentComponent() { return new MainContentComponent(); }
|
||||
|
||||
|
||||
#endif // MAINCOMPONENT_H_INCLUDED
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-12 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This is a quick-and-dirty parser for the 3D OBJ file format.
|
||||
|
||||
Just call load() and if there aren't any errors, the 'shapes' array should
|
||||
be filled with all the shape objects that were loaded from the file.
|
||||
*/
|
||||
class WavefrontObjFile
|
||||
{
|
||||
public:
|
||||
WavefrontObjFile() {}
|
||||
|
||||
Result load (const String& objFileContent)
|
||||
{
|
||||
shapes.clear();
|
||||
return parseObjFile (StringArray::fromLines (objFileContent));
|
||||
}
|
||||
|
||||
Result load (const File& file)
|
||||
{
|
||||
sourceFile = file;
|
||||
return load (file.loadFileAsString());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
typedef juce::uint32 Index;
|
||||
|
||||
struct Vertex { float x, y, z; };
|
||||
struct TextureCoord { float x, y; };
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
Array<Vertex> vertices, normals;
|
||||
Array<TextureCoord> textureCoords;
|
||||
Array<Index> indices;
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
Material() noexcept : shininess (1.0f), refractiveIndex (0.0f)
|
||||
{
|
||||
zerostruct (ambient);
|
||||
zerostruct (diffuse);
|
||||
zerostruct (specular);
|
||||
zerostruct (transmittance);
|
||||
zerostruct (emission);
|
||||
}
|
||||
|
||||
String name;
|
||||
|
||||
Vertex ambient, diffuse, specular, transmittance, emission;
|
||||
float shininess, refractiveIndex;
|
||||
|
||||
String ambientTextureName, diffuseTextureName,
|
||||
specularTextureName, normalTextureName;
|
||||
|
||||
StringPairArray parameters;
|
||||
};
|
||||
|
||||
struct Shape
|
||||
{
|
||||
String name;
|
||||
Mesh mesh;
|
||||
Material material;
|
||||
};
|
||||
|
||||
OwnedArray<Shape> shapes;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File sourceFile;
|
||||
|
||||
struct TripleIndex
|
||||
{
|
||||
TripleIndex() noexcept : vertexIndex (-1), textureIndex (-1), normalIndex (-1) {}
|
||||
bool operator< (const TripleIndex& other) const noexcept { return vertexIndex < other.vertexIndex; }
|
||||
|
||||
int vertexIndex, textureIndex, normalIndex;
|
||||
};
|
||||
|
||||
struct IndexMap
|
||||
{
|
||||
std::map<TripleIndex, Index> map;
|
||||
|
||||
Index getIndexFor (TripleIndex i, Mesh& newMesh, const Mesh& srcMesh)
|
||||
{
|
||||
const std::map<TripleIndex, Index>::iterator it (map.find (i));
|
||||
|
||||
if (it != map.end())
|
||||
return it->second;
|
||||
|
||||
const Index index = (Index) newMesh.vertices.size();
|
||||
|
||||
if (isPositiveAndBelow (i.vertexIndex, srcMesh.vertices.size()))
|
||||
newMesh.vertices.add (srcMesh.vertices.getReference (i.vertexIndex));
|
||||
|
||||
if (isPositiveAndBelow (i.normalIndex, srcMesh.normals.size()))
|
||||
newMesh.normals.add (srcMesh.normals.getReference (i.normalIndex));
|
||||
|
||||
if (isPositiveAndBelow (i.textureIndex, srcMesh.textureCoords.size()))
|
||||
newMesh.textureCoords.add (srcMesh.textureCoords.getReference (i.textureIndex));
|
||||
|
||||
map[i] = index;
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
static float parseFloat (String::CharPointerType& t)
|
||||
{
|
||||
t = t.findEndOfWhitespace();
|
||||
return (float) CharacterFunctions::readDoubleValue (t);
|
||||
}
|
||||
|
||||
static Vertex parseVertex (String::CharPointerType t)
|
||||
{
|
||||
Vertex v;
|
||||
v.x = parseFloat (t);
|
||||
v.y = parseFloat (t);
|
||||
v.z = parseFloat (t);
|
||||
return v;
|
||||
}
|
||||
|
||||
static TextureCoord parseTextureCoord (String::CharPointerType t)
|
||||
{
|
||||
TextureCoord tc;
|
||||
tc.x = parseFloat (t);
|
||||
tc.y = parseFloat (t);
|
||||
return tc;
|
||||
}
|
||||
|
||||
static bool matchToken (String::CharPointerType& t, const char* token)
|
||||
{
|
||||
const int len = (int) strlen (token);
|
||||
|
||||
if (CharacterFunctions::compareUpTo (CharPointer_ASCII (token), t, len) == 0)
|
||||
{
|
||||
String::CharPointerType end = t + len;
|
||||
|
||||
if (end.isEmpty() || end.isWhitespace())
|
||||
{
|
||||
t = end.findEndOfWhitespace();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Face
|
||||
{
|
||||
Face (String::CharPointerType t)
|
||||
{
|
||||
while (! t.isEmpty())
|
||||
triples.add (parseTriple (t));
|
||||
}
|
||||
|
||||
Array<TripleIndex> triples;
|
||||
|
||||
void addIndices (Mesh& newMesh, const Mesh& srcMesh, IndexMap& indexMap)
|
||||
{
|
||||
TripleIndex i0 (triples[0]), i1, i2 (triples[1]);
|
||||
|
||||
for (int i = 2; i < triples.size(); ++i)
|
||||
{
|
||||
i1 = i2;
|
||||
i2 = triples.getReference (i);
|
||||
|
||||
newMesh.indices.add (indexMap.getIndexFor (i0, newMesh, srcMesh));
|
||||
newMesh.indices.add (indexMap.getIndexFor (i1, newMesh, srcMesh));
|
||||
newMesh.indices.add (indexMap.getIndexFor (i2, newMesh, srcMesh));
|
||||
}
|
||||
}
|
||||
|
||||
static TripleIndex parseTriple (String::CharPointerType& t)
|
||||
{
|
||||
TripleIndex i;
|
||||
|
||||
t = t.findEndOfWhitespace();
|
||||
i.vertexIndex = t.getIntValue32() - 1;
|
||||
t = findEndOfFaceToken (t);
|
||||
|
||||
if (t.isEmpty() || t.getAndAdvance() != '/')
|
||||
return i;
|
||||
|
||||
if (*t == '/')
|
||||
{
|
||||
++t;
|
||||
}
|
||||
else
|
||||
{
|
||||
i.textureIndex = t.getIntValue32() - 1;
|
||||
t = findEndOfFaceToken (t);
|
||||
|
||||
if (t.isEmpty() || t.getAndAdvance() != '/')
|
||||
return i;
|
||||
}
|
||||
|
||||
i.normalIndex = t.getIntValue32() - 1;
|
||||
t = findEndOfFaceToken (t);
|
||||
return i;
|
||||
}
|
||||
|
||||
static String::CharPointerType findEndOfFaceToken (String::CharPointerType t) noexcept
|
||||
{
|
||||
return CharacterFunctions::findEndOfToken (t, CharPointer_ASCII ("/ \t"), String::empty.getCharPointer());
|
||||
}
|
||||
};
|
||||
|
||||
static Shape* parseFaceGroup (const Mesh& srcMesh,
|
||||
const Array<Face>& faceGroup,
|
||||
const Material& material,
|
||||
const String& name)
|
||||
{
|
||||
if (faceGroup.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
ScopedPointer<Shape> shape (new Shape());
|
||||
shape->name = name;
|
||||
shape->material = material;
|
||||
|
||||
IndexMap indexMap;
|
||||
|
||||
for (int i = 0; i < faceGroup.size(); ++i)
|
||||
faceGroup.getReference(i).addIndices (shape->mesh, srcMesh, indexMap);
|
||||
|
||||
return shape.release();
|
||||
}
|
||||
|
||||
Result parseObjFile (const StringArray& lines)
|
||||
{
|
||||
Mesh mesh;
|
||||
Array<Face> faceGroup;
|
||||
|
||||
Array<Material> knownMaterials;
|
||||
Material lastMaterial;
|
||||
String lastName;
|
||||
|
||||
for (int lineNum = 0; lineNum < lines.size(); ++lineNum)
|
||||
{
|
||||
String::CharPointerType l = lines[lineNum].getCharPointer().findEndOfWhitespace();
|
||||
|
||||
if (matchToken (l, "v")) { mesh.vertices.add (parseVertex (l)); continue; }
|
||||
if (matchToken (l, "vn")) { mesh.normals.add (parseVertex (l)); continue; }
|
||||
if (matchToken (l, "vt")) { mesh.textureCoords.add (parseTextureCoord (l)); continue; }
|
||||
if (matchToken (l, "f")) { faceGroup.add (Face (l)); continue; }
|
||||
|
||||
if (matchToken (l, "usemtl"))
|
||||
{
|
||||
const String name (String (l).trim());
|
||||
|
||||
for (int i = knownMaterials.size(); --i >= 0;)
|
||||
{
|
||||
if (knownMaterials.getReference(i).name == name)
|
||||
{
|
||||
lastMaterial = knownMaterials.getReference(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchToken (l, "mtllib"))
|
||||
{
|
||||
Result r = parseMaterial (knownMaterials, String (l).trim());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchToken (l, "g") || matchToken (l, "o"))
|
||||
{
|
||||
if (Shape* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
|
||||
shapes.add (shape);
|
||||
|
||||
faceGroup.clear();
|
||||
lastName = StringArray::fromTokens (l, " \t", "")[0];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Shape* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
|
||||
shapes.add (shape);
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
Result parseMaterial (Array<Material>& materials, const String& filename)
|
||||
{
|
||||
jassert (sourceFile.exists());
|
||||
File f (sourceFile.getSiblingFile (filename));
|
||||
|
||||
if (! f.exists())
|
||||
return Result::fail ("Cannot open file: " + filename);
|
||||
|
||||
StringArray lines;
|
||||
lines.addLines (f.loadFileAsString());
|
||||
|
||||
materials.clear();
|
||||
Material material;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
String::CharPointerType l (lines[i].getCharPointer().findEndOfWhitespace());
|
||||
|
||||
if (matchToken (l, "newmtl")) { materials.add (material); material.name = String (l).trim(); continue; }
|
||||
|
||||
if (matchToken (l, "Ka")) { material.ambient = parseVertex (l); continue; }
|
||||
if (matchToken (l, "Kd")) { material.diffuse = parseVertex (l); continue; }
|
||||
if (matchToken (l, "Ks")) { material.specular = parseVertex (l); continue; }
|
||||
if (matchToken (l, "Kt")) { material.transmittance = parseVertex (l); continue; }
|
||||
if (matchToken (l, "Ke")) { material.emission = parseVertex (l); continue; }
|
||||
if (matchToken (l, "Ni")) { material.refractiveIndex = parseFloat (l); continue; }
|
||||
if (matchToken (l, "Ns")) { material.shininess = parseFloat (l); continue; }
|
||||
|
||||
if (matchToken (l, "map_Ka")) { material.ambientTextureName = String (l).trim(); continue; }
|
||||
if (matchToken (l, "map_Kd")) { material.diffuseTextureName = String (l).trim(); continue; }
|
||||
if (matchToken (l, "map_Ks")) { material.specularTextureName = String (l).trim(); continue; }
|
||||
if (matchToken (l, "map_Ns")) { material.normalTextureName = String (l).trim(); continue; }
|
||||
|
||||
StringArray tokens;
|
||||
tokens.addTokens (l, " \t", "");
|
||||
|
||||
if (tokens.size() >= 2)
|
||||
material.parameters.set (tokens[0].trim(), tokens[1].trim());
|
||||
}
|
||||
|
||||
materials.add (material);
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavefrontObjFile)
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
VertexShader.cpp
|
||||
Created: 11 Nov 2014 12:17:40pm
|
||||
Author: Felix Faire
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
Reference in New Issue
Block a user