git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
365
deps/juce/examples/Assets/WavefrontObjParser.h
vendored
Normal file
365
deps/juce/examples/Assets/WavefrontObjParser.h
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
||||
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
||||
PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
{
|
||||
zerostruct (ambient);
|
||||
zerostruct (diffuse);
|
||||
zerostruct (specular);
|
||||
zerostruct (transmittance);
|
||||
zerostruct (emission);
|
||||
}
|
||||
|
||||
String name;
|
||||
|
||||
Vertex ambient, diffuse, specular, transmittance, emission;
|
||||
float shininess = 1.0f, refractiveIndex = 0.0f;
|
||||
|
||||
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 {}
|
||||
|
||||
bool operator< (const TripleIndex& other) const noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return false;
|
||||
|
||||
if (vertexIndex != other.vertexIndex)
|
||||
return vertexIndex < other.vertexIndex;
|
||||
|
||||
if (textureIndex != other.textureIndex)
|
||||
return textureIndex < other.textureIndex;
|
||||
|
||||
return normalIndex < other.normalIndex;
|
||||
}
|
||||
|
||||
int vertexIndex = -1, textureIndex = -1, normalIndex = -1;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
auto 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.incrementToEndOfWhitespace();
|
||||
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)
|
||||
{
|
||||
auto len = (int) strlen (token);
|
||||
|
||||
if (CharacterFunctions::compareUpTo (CharPointer_ASCII (token), t, len) == 0)
|
||||
{
|
||||
auto 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 (auto 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.incrementToEndOfWhitespace();
|
||||
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().getCharPointer());
|
||||
}
|
||||
};
|
||||
|
||||
static Shape* parseFaceGroup (const Mesh& srcMesh,
|
||||
Array<Face>& faceGroup,
|
||||
const Material& material,
|
||||
const String& name)
|
||||
{
|
||||
if (faceGroup.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<Shape> shape (new Shape());
|
||||
shape->name = name;
|
||||
shape->material = material;
|
||||
|
||||
IndexMap indexMap;
|
||||
|
||||
for (auto& f : faceGroup)
|
||||
f.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 (auto lineNum = 0; lineNum < lines.size(); ++lineNum)
|
||||
{
|
||||
auto 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"))
|
||||
{
|
||||
auto name = String (l).trim();
|
||||
|
||||
for (auto i = knownMaterials.size(); --i >= 0;)
|
||||
{
|
||||
if (knownMaterials.getReference (i).name == name)
|
||||
{
|
||||
lastMaterial = knownMaterials.getReference (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchToken (l, "mtllib"))
|
||||
{
|
||||
auto r = parseMaterial (knownMaterials, String (l).trim());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchToken (l, "g") || matchToken (l, "o"))
|
||||
{
|
||||
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
|
||||
shapes.add (shape);
|
||||
|
||||
faceGroup.clear();
|
||||
lastName = StringArray::fromTokens (l, " \t", "")[0];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
|
||||
shapes.add (shape);
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
Result parseMaterial (Array<Material>& materials, const String& filename)
|
||||
{
|
||||
jassert (sourceFile.exists());
|
||||
auto f = sourceFile.getSiblingFile (filename);
|
||||
|
||||
if (! f.exists())
|
||||
return Result::fail ("Cannot open file: " + filename);
|
||||
|
||||
auto lines = StringArray::fromLines (f.loadFileAsString());
|
||||
|
||||
materials.clear();
|
||||
Material material;
|
||||
|
||||
for (auto line : lines)
|
||||
{
|
||||
auto l = line.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; }
|
||||
|
||||
auto tokens = StringArray::fromTokens (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)
|
||||
};
|
Reference in New Issue
Block a user