Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tsd/src/tsd/app/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ void Context::parseCommandLine(std::vector<std::string> &args)
importerType = tsd::io::ImporterType::OBJ;
else if (arg == "-pdb")
importerType = tsd::io::ImporterType::PDB;
else if (arg == "-pbrt")
importerType = tsd::io::ImporterType::PBRT;
else if (arg == "-ply")
importerType = tsd::io::ImporterType::PLY;
else if (arg == "-pointsbin") {
Expand Down
4 changes: 4 additions & 0 deletions tsd/src/tsd/io/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ PRIVATE
importers/detail/ensight_io.cpp
importers/detail/HDRImage.cpp
importers/detail/importer_common.cpp
importers/detail/pbrt/PbrtLexer.cpp
importers/detail/pbrt/PbrtParser.cpp
importers/detail/pbrt/PbrtScene.cpp
importers/detail/usd/MaterialCommon.cpp
importers/detail/usd/OmniPbrMaterial.cpp
importers/import_AGX.cpp
Expand All @@ -29,6 +32,7 @@ PRIVATE
importers/import_NBODY.cpp
importers/import_NVDB.cpp
importers/import_OBJ.cpp
importers/import_PBRT.cpp
importers/import_USD.cpp
importers/import_PDB.cpp
importers/import_PLY.cpp
Expand Down
2 changes: 2 additions & 0 deletions tsd/src/tsd/io/importers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void import_HSMESH(Scene &scene, tsd::animation::AnimationManager &animMgr, cons
void import_NBODY(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {}, bool useDefaultMaterial = false);
void import_OBJ(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {}, bool useDefaultMaterial = false);
void import_PDB(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {});
void import_PBRT(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {});
void import_PLY(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {});
void import_POINTSBIN(Scene &scene, tsd::animation::AnimationManager &animMgr, const std::vector<std::string> &filepaths, LayerNodeRef location = {});
void import_PT(Scene &scene, tsd::animation::AnimationManager &animMgr, const char *filename, LayerNodeRef location = {});
Expand Down Expand Up @@ -146,6 +147,7 @@ enum class ImporterType
NBODY,
OBJ,
PDB,
PBRT,
PLY,
POINTSBIN_MULTIFILE,
PT,
Expand Down
49 changes: 48 additions & 1 deletion tsd/src/tsd/io/importers/detail/importer_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#include "mikktspace.h"
// stb_image
#include "stb_image.h"
#ifndef _WIN32
#include "tinyexr.h"
#endif
// anari
#include <anari/anari_cpp/ext/linalg.h>
// std
Expand Down Expand Up @@ -390,6 +393,41 @@ SamplerRef importStbTexture(
: SamplerRef{};
}

#ifndef _WIN32
// Follow actual HDRI importer: tinyexr is excluded on Windows; to be
// investigated.
static SamplerRef importExrTexture(
Scene &scene, const std::string &filepath, TextureCache &cache)
{
// EXR is always linear (no sRGB encoding); collapse both cache buckets onto
// the linear key so a .exr can't be imported twice as srgb vs linear.
auto cacheKey = makeTextureCacheKey(filepath, /*isLinear=*/true);
if (auto dataArray = cache[cacheKey]; dataArray.valid())
return makeTextureSampler(scene, dataArray, filepath);

float *rgba = nullptr;
int width = 0;
int height = 0;
const char *err = nullptr;
int ret = LoadEXR(&rgba, &width, &height, filepath.c_str(), &err);
if (ret != TINYEXR_SUCCESS) {
logError("[importTexture] failed to load EXR '%s': %s",
filepath.c_str(),
err ? err : "unknown error");
if (err)
FreeEXRErrorMessage(err);
return {};
}

auto dataArray = scene.createArray(ANARI_FLOAT32_VEC4, width, height);
dataArray->setData(rgba);
cache[cacheKey] = dataArray;
free(rgba);

return makeTextureSampler(scene, dataArray, filepath);
}
#endif

SamplerRef importTexture(
Scene &scene, std::string filepath, TextureCache &cache, bool isLinear)
{
Expand All @@ -398,9 +436,18 @@ SamplerRef importTexture(
return c == '\\' ? '/' : c;
});

auto ext = extensionOf(filepath);
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) {
return std::tolower(c);
});

SamplerRef tex;
if (filepath.size() > 4 && filepath.substr(filepath.size() - 4) == ".dds") {
if (ext == ".dds") {
tex = importDdsTexture(scene, filepath, cache);
#ifndef _WIN32
} else if (ext == ".exr") {
tex = importExrTexture(scene, filepath, cache);
#endif
} else {
tex = importStbTexture(scene, filepath, cache, isLinear);
}
Expand Down
210 changes: 210 additions & 0 deletions tsd/src/tsd/io/importers/detail/pbrt/PbrtLexer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright 2026 NVIDIA Corporation
// SPDX-License-Identifier: Apache-2.0

#include "PbrtLexer.hpp"
#include <cctype>
#include <stdexcept>

namespace pbrt {

Lexer::Lexer(std::string_view source, const std::string &filename)
: m_source(source), m_filename(filename)
{}

Token Lexer::next()
{
if (m_hasPeeked) {
m_hasPeeked = false;
return std::move(m_peeked);
}

skipWhitespaceAndComments();

if (m_pos >= m_source.size())
return {TokenType::Eof, "", m_line};

char c = m_source[m_pos];

if (c == '[') {
++m_pos;
return {TokenType::LBracket, "[", m_line};
}

if (c == ']') {
++m_pos;
return {TokenType::RBracket, "]", m_line};
}

if (c == '"')
return readString();

// Sign followed by digit or dot → number; bare digit or dot → number
if (std::isdigit(static_cast<unsigned char>(c)) || c == '.') {
return readNumber();
}

if (c == '-' || c == '+') {
if (m_pos + 1 < m_source.size()) {
char next = m_source[m_pos + 1];
if (std::isdigit(static_cast<unsigned char>(next)) || next == '.')
return readNumber();
}
}

if (std::isalpha(static_cast<unsigned char>(c)) || c == '_')
return readIdentifier();

throw std::runtime_error(m_filename + ":" + std::to_string(m_line)
+ ": unexpected character '" + c + "'");
}

Token Lexer::peek()
{
if (!m_hasPeeked) {
m_peeked = next();
m_hasPeeked = true;
}
return m_peeked;
}

const std::string &Lexer::currentFile() const
{
return m_filename;
}

size_t Lexer::currentLine() const
{
return m_line;
}

void Lexer::skipWhitespaceAndComments()
{
while (m_pos < m_source.size()) {
char c = m_source[m_pos];

if (c == '\n') {
++m_line;
++m_pos;
continue;
}

if (std::isspace(static_cast<unsigned char>(c))) {
++m_pos;
continue;
}

if (c == '#') {
while (m_pos < m_source.size() && m_source[m_pos] != '\n')
++m_pos;
continue;
}

break;
}
}

Token Lexer::readString()
{
size_t line = m_line;
++m_pos; // skip opening quote

std::string result;
while (m_pos < m_source.size()) {
char c = m_source[m_pos];

if (c == '"') {
++m_pos;
return {TokenType::String, result, line};
}

if (c == '\\' && m_pos + 1 < m_source.size()) {
++m_pos;
char escaped = m_source[m_pos];
switch (escaped) {
case '\\':
result += '\\';
break;
case '"':
result += '"';
break;
case 'n':
result += '\n';
break;
case 't':
result += '\t';
break;
default:
result += escaped;
break;
}
++m_pos;
continue;
}

if (c == '\n')
++m_line;

result += c;
++m_pos;
}

throw std::runtime_error(
m_filename + ":" + std::to_string(line) + ": unterminated string");
}

Token Lexer::readNumber()
{
size_t line = m_line;
size_t start = m_pos;

// Optional sign
if (m_source[m_pos] == '-' || m_source[m_pos] == '+')
++m_pos;

// Integer part
while (m_pos < m_source.size()
&& std::isdigit(static_cast<unsigned char>(m_source[m_pos])))
++m_pos;

// Fractional part
if (m_pos < m_source.size() && m_source[m_pos] == '.') {
++m_pos;
while (m_pos < m_source.size()
&& std::isdigit(static_cast<unsigned char>(m_source[m_pos])))
++m_pos;
}

// Exponent
if (m_pos < m_source.size()
&& (m_source[m_pos] == 'e' || m_source[m_pos] == 'E')) {
++m_pos;
if (m_pos < m_source.size()
&& (m_source[m_pos] == '-' || m_source[m_pos] == '+'))
++m_pos;
while (m_pos < m_source.size()
&& std::isdigit(static_cast<unsigned char>(m_source[m_pos])))
++m_pos;
}

std::string text(m_source.substr(start, m_pos - start));
return {TokenType::Number, text, line};
}

Token Lexer::readIdentifier()
{
size_t line = m_line;
size_t start = m_pos;

while (m_pos < m_source.size()) {
char c = m_source[m_pos];
if (std::isalnum(static_cast<unsigned char>(c)) || c == '_')
++m_pos;
else
break;
}

std::string text(m_source.substr(start, m_pos - start));
return {TokenType::Identifier, text, line};
}

} // namespace pbrt
57 changes: 57 additions & 0 deletions tsd/src/tsd/io/importers/detail/pbrt/PbrtLexer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2026 NVIDIA Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstddef>
#include <string>
#include <string_view>

namespace pbrt {

enum class TokenType
{
Identifier, // Bare word: Shape, Material, WorldBegin, etc.
String, // Quoted: "trianglemesh", "float fov"
Number, // Numeric literal: 42, -3.14, 1e-5
LBracket, // [
RBracket, // ]
Eof
};

struct Token
{
TokenType type = TokenType::Eof;
std::string text;
size_t line = 0;
};

// Stream-based lexer. Constructed from file contents (string_view).
// Call next() repeatedly to consume tokens until Eof.
class Lexer
{
public:
explicit Lexer(std::string_view source, const std::string &filename = "");

Token next();
Token peek();

const std::string &currentFile() const;
size_t currentLine() const;

private:
void skipWhitespaceAndComments();
Token readString();
Token readNumber();
Token readIdentifier();

std::string_view m_source;
size_t m_pos = 0;
size_t m_line = 1;
std::string m_filename;

bool m_hasPeeked = false;
Token m_peeked;
};

} // namespace pbrt
Loading
Loading