Clean project structure.

This commit is contained in:
jmsgrogan 2023-01-17 10:13:25 +00:00
parent 78a4fa99ff
commit 947bf937fd
496 changed files with 206 additions and 137 deletions

View file

@ -0,0 +1,4 @@
add_subdirectory(fonts)
add_subdirectory(graphics)
add_subdirectory(mesh)
add_subdirectory(visual_elements)

View file

View file

@ -0,0 +1,15 @@
#pragma once
#include "IFontEngine.h"
#include "Image.h"
#include "FontGlyph.h"
class BasicFontEngine : public IFontEngine
{
virtual void initialize(){};
virtual void loadFontFace(const std::filesystem::path& fontFile, float penSize = 16){};
virtual std::unique_ptr<FontGlyph> loadGlyph(unsigned charCode){return nullptr;};
};

View file

@ -0,0 +1,60 @@
set(MODULE_NAME fonts)
message(STATUS "Checking dependencies for module: " ${MODULE_NAME})
set(LIB_DEPENDS "")
list(APPEND LIB_INCLUDES
FontReader.cpp
TrueTypeFont.cpp
FontsManager.cpp
FontGlyph.cpp
FontReader.h
TrueTypeFont.h
FontsManager.h
FontGlyph.h
IFont.h
IFontEngine.h
BasicFontEngine.h
BasicFontEngine.cpp
FontItem.h
FontItem.cpp
)
if(UNIX)
find_package(Freetype QUIET)
if(Freetype_FOUND)
list(APPEND LIB_INCLUDES
FreeTypeFontEngine.cpp
)
list(APPEND LIB_DEPENDS
Freetype::Freetype
)
list(APPEND DEFINES "HAS_FREETYPE")
else()
message(STATUS "Freetype not found - skipping support")
endif()
else()
list(APPEND LIB_INCLUDES
directx/DirectWriteFontEngine.h
directx/DirectWriteHelpers.h
directx/DirectWriteFontEngine.cpp
directx/DirectWriteHelpers.cpp
)
list(APPEND LIB_DEPENDS
Dwrite.lib D2d1.lib uuid.lib
)
endif()
add_library(${MODULE_NAME} SHARED ${LIB_INCLUDES})
target_include_directories(${MODULE_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/directx
)
target_link_libraries(${MODULE_NAME} PUBLIC core geometry image ${LIB_DEPENDS})
target_compile_definitions(${MODULE_NAME} PRIVATE ${DEFINES})
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/rendering)
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -0,0 +1,97 @@
#include "FontGlyph.h"
void GlyphRunOutlines::addSegment(const GlyphRunSegment& segment)
{
mFeatures[mFeatures.size() - 1].mSegments.push_back(segment);
}
void GlyphRunOutlines::startFeature(const Point& point, bool isFilled)
{
GlyphRunFeature feature;
feature.mFilled = isFilled;
GlyphRunSegment segment;
segment.mType = GlyphRunSegment::Type::POINT;
segment.mPoints.push_back(point);
feature.mSegments.push_back(segment);
mFeatures.push_back(feature);
}
std::string GlyphRunOutlines::toPostScriptPath()
{
std::string path;
for (const auto& feature : mFeatures)
{
if (feature.mSegments.empty())
{
continue;
}
auto start_point = feature.mSegments[0].mPoints[0];
path += "M" + std::to_string(start_point.getX()) + " " + std::to_string(start_point.getY()) + " ";
for (std::size_t idx = 1; idx < feature.mSegments.size(); idx++)
{
if (feature.mSegments[idx].mType == GlyphRunSegment::Type::LINE)
{
for (const auto& point : feature.mSegments[idx].mPoints)
{
path += "L" + std::to_string(point.getX()) + " " + std::to_string(point.getY()) + " ";
}
}
else if (feature.mSegments[idx].mType == GlyphRunSegment::Type::BEZIER)
{
path += "C";
for (const auto& point : feature.mSegments[idx].mPoints)
{
path += std::to_string(point.getX()) + " " + std::to_string(point.getY()) + " ";
}
}
}
path += "z ";
}
return path;
}
FontGlyph::FontGlyph(unsigned width, unsigned height, int bearingX, int bearingY, int advanceX, std::unique_ptr<Image> image)
: mImage(std::move(image)),
mWidth(width),
mHeight(height),
mBearingX(bearingX),
mBearingY(bearingY),
mAdvanceX(advanceX)
{
}
Image* FontGlyph::getImage() const
{
return mImage.get();
}
unsigned FontGlyph::getWidth() const
{
return mWidth;
}
unsigned FontGlyph::getHeight() const
{
return mHeight;
}
int FontGlyph::getBearingX() const
{
return mBearingX;
}
int FontGlyph::getBearingY() const
{
return mBearingY;
}
int FontGlyph::getAdvanceX() const
{
return mAdvanceX;
}

View file

@ -0,0 +1,59 @@
#pragma once
#include "Point.h"
#include "Image.h"
#include <memory>
#include <vector>
#include <string>
struct GlyphRunSegment
{
enum class Type
{
POINT,
LINE,
BEZIER
};
std::vector<Point> mPoints;
Type mType{ Type::LINE };
};
struct GlyphRunFeature
{
bool mFilled{ true };
std::vector<GlyphRunSegment> mSegments;
};
class GlyphRunOutlines
{
public:
void addSegment(const GlyphRunSegment& point);
void startFeature(const Point& point, bool isFilled);
std::string toPostScriptPath();
private:
std::vector<GlyphRunFeature> mFeatures;
};
class FontGlyph
{
public:
FontGlyph(unsigned width, unsigned height, int bearingX, int bearingY, int advanceX, std::unique_ptr<Image> image);
Image* getImage() const;
unsigned getWidth() const;
unsigned getHeight() const;
int getBearingX() const;
int getBearingY() const;
int getAdvanceX() const;
private:
unsigned mWidth{0};
unsigned mHeight{0};
int mBearingX{0};
int mBearingY{0};
int mAdvanceX{0};
std::unique_ptr<Image> mImage;
};

View file

@ -0,0 +1,33 @@
#include "FontItem.h"
FontItem::FontItem(const std::string& faceName, float size)
: mFaceName(faceName),
mSize(size)
{
}
bool FontItem::hasPath() const
{
return !mPath.empty();
}
const Path& FontItem::getPath() const
{
return mPath;
}
void FontItem::setPath(const Path& path)
{
mPath = path;
}
const std::string& FontItem::getFaceName() const
{
return mFaceName;
}
float FontItem::getSize() const
{
return mSize;
}

View file

@ -0,0 +1,38 @@
#pragma once
#include <filesystem>
#include <string>
using Path = std::filesystem::path;
class FontItem
{
public:
FontItem(const std::string& faceName = "Arial", float size = 16);
const Path& getPath() const;
const std::string& getFaceName() const;
float getSize() const;
void setPath(const Path& path);
bool hasPath() const;
bool operator==(const FontItem& rhs) const
{
return (mSize == rhs.mSize)
&& (mFaceName == rhs.mFaceName);
}
bool operator!=(const FontItem& rhs) const
{
return !operator==(rhs);
}
private:
float mSize{24};
std::string mFaceName;
Path mPath;
};

View file

@ -0,0 +1,181 @@
#include "FontReader.h"
#include "TrueTypeFont.h"
#include "File.h"
#include "BinaryStream.h"
//#include <iostream>
#include <sstream>
FontReader::~FontReader()
{
}
void FontReader::setPath(const std::string& path)
{
mPath = path;
}
bool FontReader::readOffsetSubtable()
{
mOffsetSubtable.scaler_type = *BinaryStream::getNextDWord(mFile->getInHandle());
mCurrentOffset += 4;
mOffsetSubtable.num_tables = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 2;
mOffsetSubtable.search_range = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 2;
mOffsetSubtable.entry_selector = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 2;
mOffsetSubtable.range_shift = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 2;
return true;
}
void FontReader::logOffsetSubtable()
{
std::stringstream sstr;
sstr << "Offset Subtable\n";
sstr << "scaler_type: " << mOffsetSubtable.scaler_type << "\n";
sstr << "num_tables: " << mOffsetSubtable.num_tables << "\n";
sstr << "search_range: " << mOffsetSubtable.search_range << "\n";
sstr << "entry_selector: " << mOffsetSubtable.entry_selector << "\n";
sstr << "range_shift: " << mOffsetSubtable.range_shift << "\n";
sstr << "************\n";
//std::cout << sstr.str() << std::endl;
}
void FontReader::readTableDirectory()
{
mTables.clear();
for (unsigned idx=0; idx<mOffsetSubtable.num_tables; idx++)
{
Table table;
BinaryStream::getNextString(mFile->getInHandle(), table.name, 4);
mCurrentOffset += 4;
table.checksum = *BinaryStream::getNextDWord(mFile->getInHandle());
mCurrentOffset += 4;
table.offset = *BinaryStream::getNextDWord(mFile->getInHandle());
mCurrentOffset += 4;
table.length = *BinaryStream::getNextDWord(mFile->getInHandle());
mCurrentOffset += 4;
logTable(table);
mTables.push_back(table);
}
}
void FontReader::logTable(const Table& table)
{
std::stringstream sstr;
sstr << "Table\n";
sstr << "name: " << table.name << "\n";
sstr << "checksum: " << table.checksum << "\n";
sstr << "offset: " << table.offset << "\n";
sstr << "length: " << table.length << "\n";
sstr << "************\n";
//std::cout << sstr.str() << std::endl;
}
void FontReader::readTable()
{
std::string working_table_name;
for (const auto& table : mTables)
{
if (mCurrentOffset == table.offset)
{
working_table_name = table.name;
break;
}
}
if (working_table_name == "head")
{
readHeadTable();
}
}
void FontReader::readHeadTable()
{
//std::cout << "Reading head table" << std::endl;
TrueTypeFont::HeadTable table;
table.version = *BinaryStream::getNextDWord(mFile->getInHandle());
table.fontRevision = *BinaryStream::getNextDWord(mFile->getInHandle());
table.checksumAdjustment = *BinaryStream::getNextDWord(mFile->getInHandle());
table.magicNumber = *BinaryStream::getNextDWord(mFile->getInHandle());
mCurrentOffset += 16;
table.flags = *BinaryStream::getNextWord(mFile->getInHandle());
table.unitsPerEm = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 4;
table.created = *BinaryStream::getNextQWord(mFile->getInHandle());
table.modified = *BinaryStream::getNextQWord(mFile->getInHandle());
mCurrentOffset += 16;
table.xMin = *BinaryStream::getNextWord(mFile->getInHandle());
table.yMin = *BinaryStream::getNextWord(mFile->getInHandle());
table.xMax = *BinaryStream::getNextWord(mFile->getInHandle());
table.yMax = *BinaryStream::getNextWord(mFile->getInHandle());
table.macStyle = *BinaryStream::getNextWord(mFile->getInHandle());
table.lowestRecPPEM = *BinaryStream::getNextWord(mFile->getInHandle());
table.fontDirectionHint = *BinaryStream::getNextWord(mFile->getInHandle());
table.indexToLocFormat = *BinaryStream::getNextWord(mFile->getInHandle());
table.glyphDataFormat = *BinaryStream::getNextWord(mFile->getInHandle());
mCurrentOffset += 18;
auto working_ttf_font = dynamic_cast<TrueTypeFont*>(mWorkingFont.get());
working_ttf_font->setHeadTable(table);
//std::cout << working_ttf_font->logHeadTable() << std::endl;
}
std::unique_ptr<IFont> FontReader::read()
{
mWorkingFont = std::make_unique<TrueTypeFont>();
mFile = std::make_unique<File>(mPath);
mFile->open(File::AccessMode::Read);
readOffsetSubtable();
//std::cout << "Current offset: " << mCurrentOffset << std::endl;
logOffsetSubtable();
readTableDirectory();
//std::cout << "Current offset: " << mCurrentOffset << std::endl;
for (unsigned idx=0; idx<mOffsetSubtable.num_tables; idx++)
{
readTable();
//std::cout << "Current offset: " << mCurrentOffset << std::endl;
break;
}
mFile->close();
return std::move(mWorkingFont);
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <string>
#include <memory>
#include <vector>
#include "File.h"
#include "IFont.h"
class File;
class FontReader
{
public:
~FontReader();
void setPath(const std::string& path);
std::unique_ptr<IFont> read();
private:
struct OffsetSubtable
{
uint32_t scaler_type{0};
uint16_t num_tables{0};
uint16_t search_range{0};
uint16_t entry_selector{0};
uint16_t range_shift{0};
};
struct Table
{
std::string name;
unsigned checksum{0};
unsigned offset{0};
unsigned length{0};
};
bool readOffsetSubtable();
void logOffsetSubtable();
void logTable(const Table& table);
void readTableDirectory();
void readTable();
void readHeadTable();
unsigned mCurrentOffset{0};
OffsetSubtable mOffsetSubtable;
std::vector<Table> mTables;
std::unique_ptr<IFont> mWorkingFont;
std::unique_ptr<File> mFile;
std::string mPath;
};

View file

@ -0,0 +1,57 @@
#include "FontsManager.h"
#ifdef HAS_FREETYPE
#include "FreeTypeFontEngine.h"
#else
#include "BasicFontEngine.h"
#endif
#include "FontGlyph.h"
FontsManager::FontsManager()
{
#ifdef HAS_FREETYPE
mFontEngine = std::make_unique<FreeTypeFontEngine>();
mUsesGlyphs = true;
#else
mFontEngine = std::make_unique<BasicFontEngine>();
#endif
mFontEngine->initialize();
}
std::unique_ptr<FontsManager> FontsManager::Create()
{
return std::make_unique<FontsManager>();
}
bool FontsManager::usesGlyphs() const
{
return mUsesGlyphs;
}
IFontEngine* FontsManager::getFontEngine() const
{
return mFontEngine.get();
}
FontGlyph* FontsManager::getGlyph(const std::string& fontFace, float size, uint32_t c)
{
auto path = std::filesystem::path("truetype/liberation/LiberationSans-Regular.ttf");
mFontEngine->loadFontFace(path, size);
auto iter = mGlyphs.find(c);
if(iter != mGlyphs.end())
{
return iter->second.get();
}
else
{
auto glyph = mFontEngine->loadGlyph(c);
auto glyph_ptr = glyph.get();
mGlyphs[c] = std::move(glyph);
return glyph_ptr;
}
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "IFontEngine.h"
#include "FontGlyph.h"
#include <memory>
#include <string>
#include <unordered_map>
class FontsManager
{
public:
FontsManager();
static std::unique_ptr<FontsManager> Create();
IFontEngine* getFontEngine() const;
bool usesGlyphs() const;
FontGlyph* getGlyph(const std::string& fontFace, float size, uint32_t c);
private:
bool mUsesGlyphs{ false };
std::unique_ptr<IFontEngine> mFontEngine;
std::unordered_map<uint32_t, std::unique_ptr<FontGlyph> > mGlyphs;
};
using FontsManagerPtr = std::unique_ptr<FontsManager>;

View file

@ -0,0 +1,103 @@
#include "FreeTypeFontEngine.h"
#include "Image.h"
#include "Grid.h"
#include "FileLogger.h"
#include "FontGlyph.h"
#include <iostream>
void FreeTypeFontEngine::initialize()
{
if (mLibrary)
{
return;
}
const auto error = FT_Init_FreeType(&mLibrary);
if (error)
{
MLOG_ERROR("Error initializing FreeType. Code " << static_cast<int>(error));
}
}
void FreeTypeFontEngine::loadFontFace(const std::filesystem::path& fontFile, float penSize)
{
if (!mLibrary)
{
initialize();
}
if (mWorkingFontFace)
{
return;
}
const auto full_path = mSystemFontLocation / fontFile;
const auto error = FT_New_Face(mLibrary, full_path.c_str(), 0, &mWorkingFontFace );
if ( error == FT_Err_Unknown_File_Format )
{
MLOG_ERROR("Found file but format not supported. Code " << static_cast<int>(error));
}
else if ( error )
{
MLOG_ERROR("Failed to find or open file. Code " << static_cast<int>(error));
}
FT_Set_Pixel_Sizes(mWorkingFontFace, penSize, 0);
}
std::unique_ptr<FontGlyph> FreeTypeFontEngine::loadGlyph(uint32_t charCode)
{
auto glyph_index = FT_Get_Char_Index( mWorkingFontFace, charCode );
if (glyph_index == 0)
{
MLOG_ERROR("Got the null glyph");
return nullptr;
}
auto error = FT_Load_Glyph(mWorkingFontFace, glyph_index, FT_LOAD_DEFAULT );
if (error == FT_Err_Invalid_Argument)
{
MLOG_ERROR("Error loading glyph - bad arg. Code " << static_cast<int>(error));
return nullptr;
}
else if(error)
{
MLOG_ERROR("Error loading glyph. Code " << static_cast<int>(error));
return nullptr;
}
if (mWorkingFontFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
{
error = FT_Render_Glyph( mWorkingFontFace->glyph, FT_RENDER_MODE_NORMAL );
}
if (error)
{
MLOG_ERROR("Error rendering glyph. Code " << static_cast<int>(error));
return nullptr;
}
auto rows = mWorkingFontFace->glyph->bitmap.rows;
auto columns = mWorkingFontFace->glyph->bitmap.width;
auto image = std::make_unique<Image>(columns, rows);
unsigned counter = 0;
std::vector<unsigned char> data(rows*columns, 0);
for (unsigned idx=0; idx<rows; idx++)
{
for (unsigned jdx=0; jdx<columns; jdx++)
{
data[counter] = mWorkingFontFace->glyph->bitmap.buffer[counter];
counter++;
}
}
image->getGridT<unsigned char>()->setData(data);
auto glyph = std::make_unique<FontGlyph>(mWorkingFontFace->glyph->bitmap.width,
mWorkingFontFace->glyph->bitmap.rows,
mWorkingFontFace->glyph->bitmap_left,
mWorkingFontFace->glyph->bitmap_top,
mWorkingFontFace->glyph->advance.x,
std::move(image));
return glyph;
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "IFontEngine.h"
#include "Image.h"
class Image;
#include <ft2build.h>
#include FT_FREETYPE_H
class FreeTypeFontEngine : public IFontEngine
{
public:
FreeTypeFontEngine() = default;
void initialize() override;
void loadFontFace(const std::filesystem::path& fontFile, float penSize = 16) override;
std::unique_ptr<FontGlyph> loadGlyph(uint32_t charCode) override;
private:
std::filesystem::path mSystemFontLocation{"/usr/share/fonts/"};
FT_Face mWorkingFontFace{nullptr};
FT_Library mLibrary{nullptr};
};

View file

@ -0,0 +1,8 @@
#pragma once
class IFont
{
public:
virtual ~IFont() = default;
virtual void dumpInfo() = 0;
};

View file

@ -0,0 +1,19 @@
#pragma once
#include <memory>
#include <filesystem>
class FontGlyph;
class IFontEngine
{
public:
IFontEngine() = default;
virtual ~IFontEngine() = default;
virtual void initialize(){};
virtual void loadFontFace(const std::filesystem::path& fontFile, float penSize = 16) = 0;
virtual std::unique_ptr<FontGlyph> loadGlyph(uint32_t charCode) = 0;
};

View file

@ -0,0 +1,31 @@
#include "TrueTypeFont.h"
#include <sstream>
std::string TrueTypeFont::logHeadTable() const
{
std::stringstream sstr;
sstr << "Head Table\n";
sstr << "version: " << mHeadTable.version << "\n";
sstr << "fontRevision: " << mHeadTable.fontRevision << "\n";
sstr << "checksumAdjustment: " << mHeadTable.checksumAdjustment << "\n";
sstr << "magicNumber: " << mHeadTable.magicNumber << "\n";
sstr << "flags: " << mHeadTable.flags << "\n";
sstr << "unitsPerEm: " << mHeadTable.unitsPerEm << "\n";
sstr << "created: " << mHeadTable.created << "\n";
sstr << "modified: " << mHeadTable.modified << "\n";
sstr << "xMin: " << mHeadTable.xMin << "\n";
sstr << "yMin: " << mHeadTable.yMin << "\n";
sstr << "xMax: " << mHeadTable.xMax << "\n";
sstr << "yMax: " << mHeadTable.yMax << "\n";
sstr << "macStyle: " << mHeadTable.macStyle << "\n";
sstr << "lowestRecPPEM: " << mHeadTable.lowestRecPPEM << "\n";
sstr << "fontDirectionHint: " << mHeadTable.fontDirectionHint << "\n";
sstr << "indexToLocFormat: " << mHeadTable.indexToLocFormat << "\n";
sstr << "glyphDataFormat: " << mHeadTable.glyphDataFormat << "\n";
return sstr.str();
}

View file

@ -0,0 +1,52 @@
#pragma once
#include <iostream>
#include <stdint.h>
#include "IFont.h"
class TrueTypeFont : public IFont
{
public:
using Fixed = int32_t;
using LongDateTime = int64_t;
using FWord = int16_t;
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6head.html
struct HeadTable
{
Fixed version{0};
Fixed fontRevision{0};
uint32_t checksumAdjustment{0};
uint32_t magicNumber{0};
uint16_t flags{0};
uint16_t unitsPerEm{0};
LongDateTime created{0};
LongDateTime modified{0};
FWord xMin{0};
FWord yMin{0};
FWord xMax{0};
FWord yMax{0};
uint16_t macStyle{0};
uint16_t lowestRecPPEM{0};
int16_t fontDirectionHint{0};
int16_t indexToLocFormat{0};
int16_t glyphDataFormat{0};
};
std::string logHeadTable() const;
void setHeadTable(const HeadTable& table)
{
mHeadTable = table;
}
void dumpInfo() override
{
//std::cout << "Got ttf" << std::endl;
}
private:
HeadTable mHeadTable;
};

View file

@ -0,0 +1,54 @@
#include "DirectWriteFontEngine.h"
#include "FontGlyph.h"
#include "UnicodeUtils.h"
#include "DirectWriteHelpers.h"
#include <dwrite.h>
void DirectWriteFontEngine::initialize()
{
mIsValid = SUCCEEDED(::DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &mDWriteFactory));
loadFontFaceFromName("Verdana", 16);
}
void DirectWriteFontEngine::initializeOffScreenRenderer()
{
if (mOffScreenRenderer)
{
return;
}
auto pInterface = OffScreenTextRenderer::Create();
pInterface->QueryInterface(IID_IOffScreenTextRenderer, &mOffScreenRenderer);
}
void DirectWriteFontEngine::loadFontFaceFromName(const std::string& fontName, float penSize)
{
mDWriteFactory->CreateTextFormat(UnicodeUtils::utf8ToUtf16WString(fontName).c_str(), nullptr, DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, penSize, L"en-us", &mTextFormat);
}
std::unique_ptr<FontGlyph> DirectWriteFontEngine::loadGlyph(uint32_t charCode)
{
return nullptr;
}
std::unique_ptr<GlyphRunOutlines> DirectWriteFontEngine::getGlyphRunOutlines(const std::vector<uint32_t>& charCodes)
{
initializeOffScreenRenderer();
std::wstring text;
for (auto code : charCodes)
{
text.push_back(code);
}
auto hr = mDWriteFactory->CreateTextLayout(text.c_str(), static_cast<UINT32>(text.size()), mTextFormat.Get(), 50.0, 50.0, &mTextLayout);
mTextLayout->Draw(nullptr, mOffScreenRenderer.Get(), 0.0, 0.0);
return mOffScreenRenderer->getGlypyRunOutline();
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "IFontEngine.h"
#include "FontGlyph.h"
#include <wrl.h>
#include <vector>
class GlyphOutlineGeometrySink;
class OffScreenTextRenderer;
struct IDWriteFactory;
struct IDWriteTextFormat;
struct IDWriteTextLayout;
class DirectWriteFontEngine : public IFontEngine
{
public:
void initialize() override;
void loadFontFace(const std::filesystem::path& fontFile, float penSize = 16) override {};
void loadFontFaceFromName(const std::string& fontName, float penSize = 16);
std::unique_ptr<FontGlyph> loadGlyph(uint32_t charCode) override;
std::unique_ptr<GlyphRunOutlines> getGlyphRunOutlines(const std::vector<uint32_t>& charCodes);
private:
void initializeOffScreenRenderer();
bool mIsValid{ false };
Microsoft::WRL::ComPtr<IDWriteFactory> mDWriteFactory;
Microsoft::WRL::ComPtr<IDWriteTextFormat> mTextFormat;
Microsoft::WRL::ComPtr<IDWriteTextLayout> mTextLayout;
Microsoft::WRL::ComPtr<OffScreenTextRenderer> mOffScreenRenderer;
};

View file

@ -0,0 +1,240 @@
#include "DirectWriteHelpers.h"
#include "FileLogger.h"
#include <wrl.h>
GlyphOutlineGeometrySink::GlyphOutlineGeometrySink()
: mGlyphRunOutline(std::make_unique<GlyphRunOutlines>())
{
}
IUnknown* GlyphOutlineGeometrySink::Create()
{
return static_cast<IGlyphOutlineGeometrySink*>(new GlyphOutlineGeometrySink);
}
HRESULT __stdcall GlyphOutlineGeometrySink::QueryInterface(const IID& iid, void** ppv)
{
if (IsEqualIID(iid, __uuidof(IUnknown)))
{
*ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
}
else if (IsEqualIID(iid, IID_ID2D1SimplifiedGeometrySink))
{
*ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
}
else if (IsEqualIID(iid, IID_IGlyphOutlineGeometrySink))
{
*ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
}
else
{
*ppv = nullptr;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
ULONG __stdcall GlyphOutlineGeometrySink::AddRef()
{
return ::InterlockedIncrement(&mRefCount);
}
ULONG __stdcall GlyphOutlineGeometrySink::Release()
{
return 0;
if (::InterlockedDecrement(&mRefCount) == 0)
{
delete this;
return 0;
}
return mRefCount;
}
void __stdcall GlyphOutlineGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
{
//MLOG_INFO("SetFillMode: " << bool(fillMode == D2D1_FILL_MODE_ALTERNATE));
}
void __stdcall GlyphOutlineGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags)
{
if (vertexFlags == D2D1_PATH_SEGMENT_NONE)
{
}
else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_UNSTROKED)
{
}
else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN)
{
}
else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_DWORD)
{
}
else
{
}
}
void __stdcall GlyphOutlineGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin)
{
mGlyphRunOutline->startFeature(Point(startPoint.x, startPoint.y), figureBegin == D2D1_FIGURE_BEGIN_FILLED);
}
void __stdcall GlyphOutlineGeometrySink::AddLines(_In_reads_(pointsCount) CONST D2D1_POINT_2F* points, UINT32 pointsCount)
{
GlyphRunSegment segment;
segment.mType = GlyphRunSegment::Type::LINE;
for (UINT32 idx = 0; idx < pointsCount; idx++)
{
auto point = points[idx];
segment.mPoints.push_back(Point(point.x, point.y));
}
mGlyphRunOutline->addSegment(segment);
}
void __stdcall GlyphOutlineGeometrySink::AddBeziers(_In_reads_(beziersCount) CONST D2D1_BEZIER_SEGMENT* beziers, UINT32 beziersCount)
{
for (UINT32 idx = 0; idx < beziersCount; idx++)
{
GlyphRunSegment segment;
segment.mType = GlyphRunSegment::Type::BEZIER;
auto bezier = beziers[idx];
segment.mPoints.push_back(Point(bezier.point1.x, bezier.point1.y));
segment.mPoints.push_back(Point(bezier.point2.x, bezier.point2.y));
segment.mPoints.push_back(Point(bezier.point3.x, bezier.point3.y));
mGlyphRunOutline->addSegment(segment);
}
}
void __stdcall GlyphOutlineGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
{
}
HRESULT __stdcall GlyphOutlineGeometrySink::Close()
{
return S_OK;
}
std::unique_ptr<GlyphRunOutlines> GlyphOutlineGeometrySink::getGlypyRunOutline()
{
return std::move(mGlyphRunOutline);
}
IUnknown* OffScreenTextRenderer::Create()
{
return static_cast<IOffScreenTextRenderer*>(new OffScreenTextRenderer);
}
HRESULT __stdcall OffScreenTextRenderer::QueryInterface(const IID& iid, void** ppv)
{
if (IsEqualIID(iid, __uuidof(IUnknown)))
{
*ppv = static_cast<IOffScreenTextRenderer*>(this);
}
else if (IsEqualIID(iid, __uuidof(IDWritePixelSnapping)))
{
*ppv = static_cast<IOffScreenTextRenderer*>(this);
}
else if (IsEqualIID(iid, __uuidof(IDWriteTextRenderer)))
{
*ppv = static_cast<IOffScreenTextRenderer*>(this);
}
else if (IsEqualIID(iid, IID_IOffScreenTextRenderer))
{
*ppv = static_cast<IOffScreenTextRenderer*>(this);
}
else
{
*ppv = nullptr;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
ULONG __stdcall OffScreenTextRenderer::AddRef()
{
return ::InterlockedIncrement(&mRefCount);
}
ULONG __stdcall OffScreenTextRenderer::Release()
{
return 0;
if (::InterlockedDecrement(&mRefCount) == 0)
{
delete this;
return 0;
}
return mRefCount;
}
HRESULT __stdcall OffScreenTextRenderer::DrawGlyphRun(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode,
_In_ DWRITE_GLYPH_RUN const* glyphRun, _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, _In_opt_ IUnknown* clientDrawingEffect)
{
auto pInterface = GlyphOutlineGeometrySink::Create();
Microsoft::WRL::ComPtr<GlyphOutlineGeometrySink> sink;
pInterface->QueryInterface(IID_IGlyphOutlineGeometrySink, &sink);
auto hr = glyphRun->fontFace->GetGlyphRunOutline(
glyphRun->fontEmSize,
glyphRun->glyphIndices,
glyphRun->glyphAdvances,
glyphRun->glyphOffsets,
glyphRun->glyphCount,
glyphRun->isSideways,
glyphRun->bidiLevel % 2,
sink.Get()
);
sink->Close();
mGlyphRunOutline = sink->getGlypyRunOutline();
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::DrawUnderline(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_UNDERLINE const* underline,
_In_opt_ IUnknown* clientDrawingEffect)
{
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::DrawStrikethrough(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_STRIKETHROUGH const* strikethrough,
_In_opt_ IUnknown* clientDrawingEffect)
{
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::DrawInlineObject(_In_opt_ void* clientDrawingContext, FLOAT originX, FLOAT originY, _In_ IDWriteInlineObject* inlineObject,
BOOL isSideways, BOOL isRightToLeft, _In_opt_ IUnknown* clientDrawingEffect)
{
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::IsPixelSnappingDisabled(_In_opt_ void* clientDrawingContext, _Out_ BOOL* isDisabled)
{
*isDisabled = FALSE;
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::GetCurrentTransform(_In_opt_ void* clientDrawingContext, _Out_ DWRITE_MATRIX* transform)
{
*transform = DWRITE_MATRIX(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
return S_OK;
}
HRESULT __stdcall OffScreenTextRenderer::GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _Out_ FLOAT* pixelsPerDip)
{
*pixelsPerDip = 1.0;
return S_OK;
}
std::unique_ptr<GlyphRunOutlines> OffScreenTextRenderer::getGlypyRunOutline()
{
return std::move(mGlyphRunOutline);
}

View file

@ -0,0 +1,90 @@
#pragma once
#include "FontGlyph.h"
#include <d2d1_1.h>
#include <dwrite.h>
#include <memory>
static const IID IID_IGlyphOutlineGeometrySink = { 0xaaf6ab8d, 0xd6cf, 0x4a3b, { 0xa6, 0x7a, 0x12, 0xf1, 0x42, 0xe0, 0x6b, 0xb9 } };
class IGlyphOutlineGeometrySink : public IDWriteGeometrySink
{
public:
virtual std::unique_ptr<GlyphRunOutlines> getGlypyRunOutline() = 0;
};
class GlyphOutlineGeometrySink :public IGlyphOutlineGeometrySink
{
public:
GlyphOutlineGeometrySink();
// IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
static IUnknown* Create();
// IDWriteGeometrySink aka ID2D1SimplifiedGeometrySink
STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode);
STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags);
STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin);
STDMETHOD_(void, AddLines)(_In_reads_(pointsCount) CONST D2D1_POINT_2F* points, UINT32 pointsCount);
STDMETHOD_(void, AddBeziers)(_In_reads_(beziersCount) CONST D2D1_BEZIER_SEGMENT* beziers, UINT32 beziersCount);
STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd);
STDMETHOD(Close)();
// IGlyphOutlineGeometrySink
std::unique_ptr<GlyphRunOutlines> getGlypyRunOutline() override;
private:
std::unique_ptr<GlyphRunOutlines> mGlyphRunOutline;
long mRefCount{ 0 };
};
static const IID IID_IOffScreenTextRenderer = { 0xa43b0c49, 0x6080, 0x4731, { 0xa8, 0x72, 0x61, 0x50, 0x47, 0x3, 0x78, 0x10 } };
class IOffScreenTextRenderer : public IDWriteTextRenderer
{
virtual std::unique_ptr<GlyphRunOutlines> getGlypyRunOutline() = 0;
};
class OffScreenTextRenderer :public IOffScreenTextRenderer
{
public:
// IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
static IUnknown* Create();
STDMETHOD(DrawGlyphRun)(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode,
_In_ DWRITE_GLYPH_RUN const* glyphRun, _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, _In_opt_ IUnknown* clientDrawingEffect);
STDMETHOD(DrawUnderline)(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_UNDERLINE const* underline,
_In_opt_ IUnknown* clientDrawingEffect);
STDMETHOD(DrawStrikethrough)(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_STRIKETHROUGH const* strikethrough,
_In_opt_ IUnknown* clientDrawingEffect);
STDMETHOD(DrawInlineObject)(_In_opt_ void* clientDrawingContext, FLOAT originX, FLOAT originY, _In_ IDWriteInlineObject* inlineObject,
BOOL isSideways, BOOL isRightToLeft, _In_opt_ IUnknown* clientDrawingEffect);
STDMETHOD(IsPixelSnappingDisabled)(_In_opt_ void* clientDrawingContext, _Out_ BOOL* isDisabled);
STDMETHOD(GetCurrentTransform)(_In_opt_ void* clientDrawingContext, _Out_ DWRITE_MATRIX* transform);
STDMETHOD(GetPixelsPerDip)(_In_opt_ void* clientDrawingContext, _Out_ FLOAT* pixelsPerDip);
std::unique_ptr<GlyphRunOutlines> getGlypyRunOutline() override;
private:
std::unique_ptr<GlyphRunOutlines> mGlyphRunOutline;
long mRefCount{ 0 };
};

View file

@ -0,0 +1,20 @@
#pragma once
class DrawingContext;
class AbstractPainter
{
public:
AbstractPainter(DrawingContext* context)
: mDrawingContext(context)
{
}
virtual ~AbstractPainter() = default;
virtual void paint() = 0;
virtual bool supportsGeometryPrimitives() const { return false; };
protected:
DrawingContext* mDrawingContext{ nullptr };
};

View file

@ -0,0 +1,89 @@
set(MODULE_NAME graphics)
message(STATUS "Checking dependencies for module: " ${MODULE_NAME})
set(platform_LIB_INCLUDES "")
set(platform_INCLUDE_DIRS "")
set(platform_HEADERS "")
set(platform_LIBS "")
list(APPEND graphics_LIB_INCLUDES
DrawingContext.cpp
DrawingSurface.cpp
RasterPainter.cpp
PainterFactory.cpp
${platform_LIB_INCLUDES}
)
list(APPEND graphics_HEADERS
${platform_HEADERS}
RasterPainter.h
DrawingContext.h
PainterFactory.h
AbstractPainter.h
DrawingSurface.h
)
if(UNIX)
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL QUIET)
if (OpenGL_FOUND)
list(APPEND platform_LIBS OpenGL::GL OpenGL::GLU)
list(APPEND graphics_LIB_INCLUDES
opengl/OpenGlPainter.cpp
opengl/OpenGlTextPainter.cpp
opengl/OpenGlMeshPainter.cpp
opengl/OpenGlFontTexture.cpp
opengl/OpenGlShaderProgram.cpp
)
list(APPEND ${MODULE_NAME}
opengl/OpenGlPainter.h
opengl/OpenGlTextPainter.h
opengl/OpenGlMeshPainter.h
opengl/OpenGlFontTexture.h
opengl/OpenGlShaderProgram.h)
list(APPEND DEFINES "HAS_OPENGL")
else()
message(STATUS "OpenGL not found - skipping support")
endif()
else()
list(APPEND graphics_LIB_INCLUDES
directx/DirectXInterface.cpp
directx/DirectX2dInterface.cpp
directx/DirectXMesh.cpp
directx/DirectXPainter.cpp
directx/DirectX2dPainter.cpp
directx/DirectXTextPainter.cpp
directx/DirectXMeshPainter.cpp
directx/DirectXShaderProgram.cpp
)
list(APPEND graphics_HEADERS
directx/DirectXInterface.h
directx/DirectX2dInterface.h
directx/DirectXMesh.h
directx/DirectXPainter.h
directx/DirectX2dPainter.h
directx/DirectXTextPainter.h
directx/DirectXMeshPainter.h
directx/DirectXShaderProgram.h
)
find_package(DirectX-Headers REQUIRED)
list(APPEND platform_LIBS D3D12.lib D3DCompiler.lib DXGI.lib Dwrite.lib D2d1.lib D3D11.lib Microsoft::DirectX-Headers)
endif()
add_library(${MODULE_NAME} SHARED
${graphics_LIB_INCLUDES}
${graphics_HEADERS})
target_include_directories(${MODULE_NAME} PUBLIC
${platform_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/opengl
${CMAKE_CURRENT_SOURCE_DIR}/directx
)
target_compile_definitions(${MODULE_NAME} PRIVATE ${DEFINES})
target_link_libraries(${MODULE_NAME} PUBLIC geometry mesh fonts image visual_elements ${platform_LIBS})
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/rendering)
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -0,0 +1,50 @@
#include "DrawingContext.h"
#include "AbstractPainter.h"
#include "PainterFactory.h"
#include "Grid.h"
#include "TriMesh.h"
#include "DrawingSurface.h"
#include "Scene.h"
DrawingContext::DrawingContext(DrawingSurface* surface, FontsManager* fontsManager, DrawingMode requestedDrawingMode)
: mSurface(surface),
mDrawingMode(requestedDrawingMode),
mFontsManager(fontsManager)
{
mPainter = PainterFactory::Create(this, mDrawingMode);
surface->getScene()->setSupportsGeometryPrimitives(mPainter->supportsGeometryPrimitives());
surface->getScene()->setFontsManager(fontsManager);
}
std::unique_ptr<DrawingContext> DrawingContext::Create(DrawingSurface* surface, FontsManager* fontsManager, DrawingMode requestedDrawingMode)
{
return std::make_unique<DrawingContext>(surface, fontsManager, requestedDrawingMode);
}
DrawingSurface* DrawingContext::getSurface() const
{
return mSurface;
}
FontsManager* DrawingContext::getFontsManager() const
{
return mFontsManager;
}
void DrawingContext::paint()
{
if (mDrawingMode == DrawingMode::GRAPH)
{
mSurface->getScene()->update();
}
mPainter->paint();
}
AbstractPainter* DrawingContext::getPainter() const
{
return mPainter.get();
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <memory>
#include <vector>
class AbstractPainter;
class DrawingSurface;
class FontsManager;
enum class DrawingMode
{
RASTER,
GRAPH
};
class DrawingContext
{
public:
DrawingContext(DrawingSurface* surface, FontsManager* fontsManager = nullptr, DrawingMode requestedDrawingMode = DrawingMode::GRAPH);
static std::unique_ptr<DrawingContext> Create(DrawingSurface* surface, FontsManager* fontsManager, DrawingMode requestedDrawingMode);
DrawingSurface* getSurface() const;
FontsManager* getFontsManager() const;
void paint();
AbstractPainter* getPainter() const;
private:
DrawingMode mDrawingMode;
FontsManager* mFontsManager{nullptr};
DrawingSurface* mSurface{nullptr};
std::unique_ptr<AbstractPainter> mPainter;
};
using DrawingContextPtr = std::unique_ptr<DrawingContext>;

View file

@ -0,0 +1,50 @@
#include "DrawingSurface.h"
#include "Image.h"
#include "Scene.h"
#include "RootNode.h"
DrawingSurface::DrawingSurface()
{
}
std::unique_ptr<DrawingSurface> DrawingSurface::Create()
{
return std::make_unique<DrawingSurface>();
}
void DrawingSurface::setSize(unsigned width, unsigned height, bool triggerResize)
{
mWidth = width;
mHeight = height;
}
unsigned DrawingSurface::getWidth() const
{
return mWidth;
}
unsigned DrawingSurface::getHeight() const
{
return mHeight;
}
Scene* DrawingSurface::getScene()
{
if (!mScene)
{
mScene = std::make_unique<Scene>();
}
return mScene.get();
}
Image* DrawingSurface::getImage()
{
if (!mBackingImage)
{
mBackingImage = std::make_unique<Image>(mWidth, mHeight);
}
return mBackingImage.get();
}

View file

@ -0,0 +1,31 @@
#pragma once
#include <memory>
class Scene;
class Image;
class DrawingSurface
{
public:
DrawingSurface();
virtual ~DrawingSurface() = default;
static std::unique_ptr<DrawingSurface> Create();
virtual void setSize(unsigned width, unsigned height, bool triggerResize = true);
unsigned getWidth() const;
unsigned getHeight() const;
Image* getImage();
Scene* getScene();
protected:
unsigned mWidth = 0;
unsigned mHeight = 0;
std::unique_ptr<Scene> mScene;
std::unique_ptr<Image> mBackingImage;
};

View file

@ -0,0 +1,50 @@
#include "PainterFactory.h"
#ifdef HAS_OPENGL
#include "OpenGlPainter.h"
#include "OpenGlMeshPainter.h"
#include "OpenGlTextPainter.h"
#include "OpenGlShaderProgram.h"
#include "OpenGlFontTexture.h"
#endif
#include "Grid.h"
#include "RasterPainter.h"
#include "DrawingContext.h"
#ifdef _WIN32
#include "DirectXPainter.h"
#include "DirectXMesh.h"
#include "DirectXMeshPainter.h"
#include "DirectXTextPainter.h"
#endif
std::unique_ptr<AbstractPainter> PainterFactory::Create(DrawingContext* context, DrawingMode drawMode)
{
#ifdef _WIN32
if (drawMode == DrawingMode::GRAPH)
{
return std::make_unique<DirectXPainter>(context);
}
else
{
return std::make_unique<RasterPainter>(context);
}
#else
#ifdef HAS_OPENGL
if (drawMode == DrawingMode::GRAPH)
{
return std::make_unique<OpenGlPainter>(context);
}
else
{
return std::make_unique<RasterPainter>(context);
}
#else
return std::make_unique<RasterPainter>(context);
#endif
#endif
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "AbstractPainter.h"
#include <memory>
enum class DrawingMode;
class DrawingContext;
class PainterFactory
{
public:
static std::unique_ptr<AbstractPainter> Create(DrawingContext* context, DrawingMode drawMode);
};

View file

@ -0,0 +1,30 @@
#include "RasterPainter.h"
#include "DrawingSurface.h"
#include "DrawingContext.h"
#include "Grid.h"
RasterPainter::RasterPainter(DrawingContext* context)
: AbstractPainter(context)
{
const auto width = context->getSurface()->getWidth();
const auto height = context->getSurface()->getHeight();
mGrid = std::make_unique<Grid<unsigned char> >(Bounds{ 0.0, 0.0, 100.0, 100.0 }, width, height);
}
void RasterPainter::paint()
{
const auto width = mDrawingContext->getSurface()->getWidth();
const auto height = mDrawingContext->getSurface()->getHeight();
mGrid->resetBounds(Bounds{ 0.0, 0.0, static_cast<double>(width), static_cast<double>(height) });
/*
for (unsigned idx=0; idx< context->getNumItems(); idx++)
{
context->getDrawable(idx)->sample(mGrid.get());
}
context->getSurface()->getImage()->setData(mGrid->getValues());
*/
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "AbstractPainter.h"
#include <memory>
class DrawingContext;
template<typename T>
class Grid;
class RasterPainter : public AbstractPainter
{
public:
RasterPainter(DrawingContext* context);
void paint() override;
private:
std::unique_ptr<Grid<unsigned char> > mGrid;
};

View file

@ -0,0 +1,76 @@
#include "DirectX2dInterface.h"
#include "DirectXInterface.h"
#include "FileLogger.h"
#include <dxgi.h>
#include <d2d1_3.h>
#include <d2d1_1.h>
#include <dwrite.h>
DirectX2dInterface::DirectX2dInterface(DirectXInterface* dxInterface)
: mDxInterface(dxInterface)
{
}
bool DirectX2dInterface::isValid() const
{
return mIsValid;
}
ID2D1Factory3* DirectX2dInterface::getFactory() const
{
return mFactory.Get();
}
IDWriteFactory* DirectX2dInterface::getDirectWriteFactory() const
{
return mDWriteFactory.Get();
}
ID2D1DeviceContext2* DirectX2dInterface::getContext() const
{
return mDeviceContext.Get();
}
ID2D1RenderTarget* DirectX2dInterface::getRenderTarget() const
{
if (mRenderTarget)
{
return mRenderTarget.Get();
}
else
{
return mDeviceContext.Get();
}
}
void DirectX2dInterface::initialize(IWICBitmap* bitmap, bool useSoftware)
{
D2D1_FACTORY_OPTIONS d2dFactoryOptions = {};
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &mFactory);
auto hr = mFactory->CreateWicBitmapRenderTarget(bitmap, D2D1::RenderTargetProperties(), &mRenderTarget);
initializeDirectWrite();
}
bool DirectX2dInterface::initialize(IDXGIDevice* device)
{
D2D1_FACTORY_OPTIONS d2dFactoryOptions = {};
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &mFactory);
mFactory->CreateDevice(device, &mDevice);
D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
mDevice->CreateDeviceContext(deviceOptions, &mDeviceContext);
return initializeDirectWrite();
}
bool DirectX2dInterface::initializeDirectWrite()
{
return SUCCEEDED(::DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &mDWriteFactory));
}

View file

@ -0,0 +1,44 @@
#pragma once
#include <wrl.h>
#include <dwrite.h>
#include <d2d1_3.h>
class DirectXInterface;
struct IDWriteFactory;
struct ID2D1Factory3;
struct ID2D1Device2;
struct ID2D1DeviceContext2;
struct ID2D1RenderTarget;
struct IWICBitmap;
struct IDXGIDevice;
class DirectX2dInterface
{
public:
DirectX2dInterface(DirectXInterface* dxInterface);
ID2D1Factory3* getFactory() const;
ID2D1DeviceContext2* getContext() const;
IDWriteFactory* getDirectWriteFactory() const;
ID2D1RenderTarget* getRenderTarget() const;
bool initialize(IDXGIDevice* device);
void initialize(IWICBitmap* bitmap, bool useSoftware = true);
bool isValid() const;
private:
bool initializeDirectWrite();
bool mIsValid{ false };
DirectXInterface* mDxInterface{ nullptr };
Microsoft::WRL::ComPtr<ID2D1Factory3> mFactory;
Microsoft::WRL::ComPtr<ID2D1Device2> mDevice;
Microsoft::WRL::ComPtr<ID2D1DeviceContext2> mDeviceContext;
Microsoft::WRL::ComPtr<IDWriteFactory> mDWriteFactory;
Microsoft::WRL::ComPtr<ID2D1RenderTarget> mRenderTarget;
};

View file

@ -0,0 +1,58 @@
#include "DirectX2dPainter.h"
#include "DirectX2dInterface.h"
#include "AbstractGeometricItem.h"
#include "Rectangle.h"
#include "SceneModel.h"
void DirectX2dPainter::startDrawing()
{
mD2dInterface->getRenderTarget()->BeginDraw();
}
void DirectX2dPainter::finishDrawing()
{
mD2dInterface->getRenderTarget()->EndDraw();
}
D2D1::ColorF DirectX2dPainter::toD2dColor(const Color& color)
{
return D2D1::ColorF(static_cast<float>(color.getR() / 255.0), static_cast<float>(color.getG() / 255.0),
static_cast<float>(color.getB() / 255.0), static_cast<float>(color.getAlpha()));
}
void DirectX2dPainter::clearBackground(const Color& color)
{
mD2dInterface->getRenderTarget()->Clear(toD2dColor(color));
}
void DirectX2dPainter::paint(SceneModel* model)
{
auto rt = mD2dInterface->getRenderTarget();
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
{
const auto loc = model->getTransform().getLocation();
const auto scale_x = model->getTransform().getScaleX();
const auto scale_y = model->getTransform().getScaleY();
D2D1_RECT_F d2d_rect{ static_cast<float>(loc.getX()), static_cast<float>(loc.getY() + scale_y), static_cast<float>(loc.getX() + scale_x), static_cast<float>(loc.getY()) };
if (model->hasFillColor())
{
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
rt->FillRectangle(d2d_rect, mSolidBrush.Get());
}
if (model->hasOutlineColor())
{
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f);
}
}
}
void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface)
{
mD2dInterface = d2dIterface;
auto hr = mD2dInterface->getRenderTarget()->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0f), &mSolidBrush);
}

View file

@ -0,0 +1,36 @@
#pragma once
#include "Color.h"
#include <wrl.h>
class SceneModel;
class DirectX2dInterface;
struct ID2D1SolidColorBrush;
namespace D2D1
{
class ColorF;
}
class DirectX2dPainter
{
public:
void startDrawing();
void clearBackground(const Color& color);
void finishDrawing();
void paint(SceneModel* model);
void setD2dInterface(DirectX2dInterface* d2dIterface);
private:
static D2D1::ColorF toD2dColor(const Color& color);
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> mSolidBrush;
DirectX2dInterface* mD2dInterface{ nullptr };
};

View file

@ -0,0 +1,168 @@
#include "DirectXInterface.h"
#include "DirectX2dInterface.h"
#include "FileLogger.h"
#include <dxgi.h>
#include <dxgi1_6.h>
#include <d3d11.h>
#include <d3d11on12.h>
#include <d3d12.h>
#include <d3d12sdklayers.h>
DirectXInterface::DirectXInterface()
: mD2dInterface(std::make_unique<DirectX2dInterface>(this))
{
}
void DirectXInterface::getHardwareAdapter(IDXGIAdapter1** ppAdapter)
{
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; ; ++adapterIndex)
{
IDXGIAdapter1* pAdapter = nullptr;
if (mDxgiFactory->EnumAdapters1(adapterIndex, &pAdapter) == DXGI_ERROR_NOT_FOUND)
{
break;
}
if (SUCCEEDED(::D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
*ppAdapter = pAdapter;
return;
}
pAdapter->Release();
}
}
bool DirectXInterface::isValid() const
{
return mIsValid;
}
ID3D12Device* DirectXInterface::getD3dDevice() const
{
return mD3dDevice.Get();
}
ID3D11On12Device* DirectXInterface::get11On12Device() const
{
return mD3d11On12Device.Get();
}
IDXGIFactory7* DirectXInterface::getDxgiFactory() const
{
return mDxgiFactory.Get();
}
ID3D12CommandQueue* DirectXInterface::getCommandQueue() const
{
return mCommandQueue.Get();
}
ID3D11DeviceContext* DirectXInterface::getD3d11DeviceContext() const
{
return mD3d11DeviceContext.Get();
}
DirectX2dInterface* DirectXInterface::getD2dInterface() const
{
return mD2dInterface.get();
}
void DirectXInterface::initialize()
{
MLOG_INFO("Initialize DirectX");
createD3dFactory();
Microsoft::WRL::ComPtr<IDXGIAdapter1> pAdapter;
getHardwareAdapter(&pAdapter);
createD3dDevice(pAdapter.Get());
createCommandQueue();
if (!initializeD11on12())
{
mIsValid = false;
return;
}
Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
mD3d11On12Device.As(&dxgiDevice);
mIsValid = mD2dInterface->initialize(dxgiDevice.Get());
}
bool DirectXInterface::createD3dFactory()
{
UINT dxgiFactoryFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
{
Microsoft::WRL::ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(::D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
{
debugController->EnableDebugLayer();
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
}
}
#endif
if (FAILED(::CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&mDxgiFactory))))
{
MLOG_ERROR("Failed to create DXGI Factory");
}
return true;
}
bool DirectXInterface::createD3dDevice(IDXGIAdapter1* pAdapter)
{
if (pAdapter)
{
if (FAILED(::D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mD3dDevice))))
{
MLOG_ERROR("Failed to create D3D12 device, will try WARP.");
return false;
}
else
{
Microsoft::WRL::ComPtr<IDXGIAdapter> pWarpAdapter;
mDxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter));
if (FAILED(::D3D12CreateDevice(pWarpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mD3dDevice))))
{
MLOG_ERROR("Failed to create WARP device.");
return false;
}
}
}
else
{
MLOG_ERROR("Failed to get DirectX adapter.");
return false;
}
return true;
}
bool DirectXInterface::createCommandQueue()
{
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (FAILED(mD3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue))))
{
MLOG_ERROR("Failed to create command queue.");
return false;
}
return true;
}
bool DirectXInterface::initializeD11on12()
{
Microsoft::WRL::ComPtr<ID3D11Device> d3d11Device;
UINT d3d11DeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
auto pCommandQueue = reinterpret_cast<IUnknown**>(mCommandQueue.GetAddressOf());
if (FAILED(D3D11On12CreateDevice(mD3dDevice.Get(), d3d11DeviceFlags, nullptr, 0, pCommandQueue, 1, 0, &d3d11Device, &mD3d11DeviceContext, nullptr)))
{
MLOG_ERROR("Failed to create D3D11 on 12 Device");
return false;
}
d3d11Device.As(&mD3d11On12Device);
return true;
}

View file

@ -0,0 +1,55 @@
#pragma once
#include <wrl.h>
#include <d3d11on12.h>
#include <dxgi1_6.h>
#include <memory>
class DirectX2dInterface;
struct ID3D12Device;
struct IDXGIFactory7;
struct IDXGIAdapter1;
struct ID3D12CommandQueue;
struct ID3D11DeviceContext;
struct ID3D11On12Device;
class DirectXInterface
{
public:
DirectXInterface();
IDXGIFactory7* getDxgiFactory() const;
ID3D12Device* getD3dDevice() const;
ID3D12CommandQueue* getCommandQueue() const;
ID3D11On12Device* get11On12Device() const;
ID3D11DeviceContext* getD3d11DeviceContext() const;
DirectX2dInterface* getD2dInterface() const;
void initialize();
bool isValid() const;
private:
bool createD3dFactory();
bool createD3dDevice(IDXGIAdapter1* ppAdapter);
bool createCommandQueue();
void getHardwareAdapter(IDXGIAdapter1** ppAdapter);
bool initializeD11on12();
bool mIsValid{ false };
Microsoft::WRL::ComPtr<IDXGIFactory7> mDxgiFactory;
Microsoft::WRL::ComPtr<ID3D12Device> mD3dDevice;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> mD3d11DeviceContext;
Microsoft::WRL::ComPtr<ID3D11On12Device> mD3d11On12Device;
std::unique_ptr<DirectX2dInterface> mD2dInterface;
};

View file

@ -0,0 +1,141 @@
#include "DirectXMesh.h"
#include "SceneModel.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "TriMesh.h"
#include "TriFace.h"
#include "FileLogger.h"
#include <directx/d3dx12.h>
DirectXMesh::DirectXMesh(SceneModel* model)
: mModel(model)
{
}
void DirectXMesh::update(DrawingContext* context, ID3D12Device* device)
{
mVertexBuffer.clear();
mIndexBuffer.clear();
const auto width = float(context->getSurface()->getWidth());
const auto height = float(context->getSurface()->getHeight());
auto model_color = mModel->getFillColor().getAsVectorDouble();
std::vector<float> color = { float(model_color[0]), float(model_color[1]), float(model_color[2]), float(model_color[3]) };
auto transform = mModel->getTransform();
for (const auto& face : dynamic_cast<TriMesh*>(mModel->getMesh())->getFaces())
{
for (const auto& loc : face->getNodeLocations(AbstractFace::Orientation::CW))
{
auto x = loc.getX() * transform.getScaleX() + transform.getLocation().getX();
auto y = loc.getY() * transform.getScaleY() + transform.getLocation().getY();
x = 2 * x - 1;
y = 2 * y - 1;
Vertex vert;
vert.position = DirectX::XMFLOAT3(static_cast<float>(x), static_cast<float>(y), 0.0);
vert.color = DirectX::XMFLOAT4(color[0], color[1], color[2], color[3]);
MLOG_INFO("Adding vert: " << x << " | " << y);
mVertexBuffer.push_back(vert);
}
}
/*
for (const auto& node : mModel->getMesh()->getNodes())
{
auto x = node->getPoint().getX() * transform.getScaleX() + transform.getLocation().getX();
//x = 2 * x / width - 1.0;
auto y = node->getPoint().getY() * transform.getScaleY() + transform.getLocation().getY();
//y = 2 * y / height;
Vertex vert;
vert.position = DirectX::XMFLOAT3(x, y, 0.0);
vert.color = DirectX::XMFLOAT4(color[0], color[1], color[2], color[3]);
MLOG_INFO("Adding vert: " << x << " | " << y);
mVertexBuffer.push_back(vert);
}
*/
//mIndexBuffer = dynamic_cast<TriMesh*>(mModel->getMesh())->getFaceNodeIds();
mIndexBuffer = { 0, 1, 2 };
for (auto id : mIndexBuffer)
{
MLOG_INFO("Adding id: " << id);
}
createD3dVertexBuffer(device);
createD3dIndexBuffer(device);
mVertexBuffer.clear();
mIndexBuffer.clear();
}
void DirectXMesh::createD3dVertexBuffer(ID3D12Device* device)
{
// Note: using upload heaps to transfer static data like vert buffers is not
// recommended. Every time the GPU needs it, the upload heap will be marshalled
// over. Please read up on Default Heap usage. An upload heap is used here for
// code simplicity and because there are very few verts to actually transfer.
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);
const auto buffer_size = mVertexBuffer.size() * sizeof(Vertex);
auto desc = CD3DX12_RESOURCE_DESC::Buffer(buffer_size);
device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&mD3dVertexBuffer));
UINT8* pDataBegin;
CD3DX12_RANGE readRange(0, 0);
mD3dVertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pDataBegin));
uploadVertexBuffer(pDataBegin);
mD3dVertexBuffer->Unmap(0, nullptr);
mVertexBufferView.BufferLocation = mD3dVertexBuffer->GetGPUVirtualAddress();
mVertexBufferView.StrideInBytes = sizeof(Vertex);
mVertexBufferView.SizeInBytes = static_cast<UINT>(buffer_size);
}
void DirectXMesh::uploadVertexBuffer(unsigned char* pBuffer) const
{
memcpy(pBuffer, mVertexBuffer.data(), mVertexBuffer.size() * sizeof(Vertex));
}
void DirectXMesh::createD3dIndexBuffer(ID3D12Device* device)
{
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);
const auto buffer_size = mIndexBuffer.size() * sizeof(unsigned);
auto desc = CD3DX12_RESOURCE_DESC::Buffer(buffer_size);
device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&mD3dIndexBuffer));
UINT8* pDataBegin;
CD3DX12_RANGE readRange(0, 0);
mD3dIndexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pDataBegin));
uploadIndexBuffer(pDataBegin);
mD3dIndexBuffer->Unmap(0, nullptr);
mIndexBufferView.BufferLocation = mD3dIndexBuffer->GetGPUVirtualAddress();
mIndexBufferView.SizeInBytes = static_cast<UINT>(buffer_size);
mIndexBufferView.Format = DXGI_FORMAT_R16_UINT;
}
void DirectXMesh::uploadIndexBuffer(unsigned char* pBuffer) const
{
memcpy(pBuffer, mIndexBuffer.data(), mIndexBuffer.size() * sizeof(unsigned));
}
const D3D12_VERTEX_BUFFER_VIEW& DirectXMesh::getVertBufferView() const
{
return mVertexBufferView;
}
const D3D12_INDEX_BUFFER_VIEW& DirectXMesh::getIndexBufferView() const
{
return mIndexBufferView;
}
SceneModel* DirectXMesh::getModel() const
{
return mModel;
}

View file

@ -0,0 +1,48 @@
#pragma once
#include <wrl.h>
#include <directxmath.h>
#include <d3d12.h>
#include <memory>
#include <vector>
class SceneModel;
class DrawingContext;
class DirectXMesh
{
public:
DirectXMesh(SceneModel* model);
const D3D12_VERTEX_BUFFER_VIEW& getVertBufferView() const;
const D3D12_INDEX_BUFFER_VIEW& getIndexBufferView() const;
SceneModel* getModel() const;
void update(DrawingContext* context, ID3D12Device* device);
private:
struct Vertex
{
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT4 color;
};
void createD3dVertexBuffer(ID3D12Device* device);
void uploadVertexBuffer(unsigned char* pBuffer) const;
void createD3dIndexBuffer(ID3D12Device* device);
void uploadIndexBuffer(unsigned char* pBuffer) const;
SceneModel* mModel{ nullptr };
std::vector<Vertex> mVertexBuffer;
Microsoft::WRL::ComPtr<ID3D12Resource> mD3dVertexBuffer;
D3D12_VERTEX_BUFFER_VIEW mVertexBufferView{};
std::vector<unsigned> mIndexBuffer;
Microsoft::WRL::ComPtr<ID3D12Resource> mD3dIndexBuffer;
D3D12_INDEX_BUFFER_VIEW mIndexBufferView{};
};

View file

@ -0,0 +1,131 @@
#include "DirectXMeshPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "Scene.h"
#include "SceneModel.h"
#include "TriMesh.h"
#include "LineMesh.h"
#include "DirectXShaderProgram.h"
#include "DirectXMesh.h"
#include "DirectXInterface.h"
#include <windows.h>
#include <iostream>
#include <filesystem>
#include <directx/d3dx12.h>
DirectXMeshPainter::DirectXMeshPainter()
{
auto shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders.hlsl";
mShaderProgram = std::make_unique<DirectXShaderProgram>(shader_path, shader_path);
}
void DirectXMeshPainter::setDxInterface(DirectXInterface* dxInterface)
{
mDxInterface = dxInterface;
}
void DirectXMeshPainter::initializeShader()
{
}
void DirectXMeshPainter::initializeD3d()
{
createRootSignature();
createPipelineStateObject();
}
void DirectXMeshPainter::createRootSignature()
{
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
Microsoft::WRL::ComPtr<ID3DBlob> signature;
Microsoft::WRL::ComPtr<ID3DBlob> error;
D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
mDxInterface->getD3dDevice()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature));
}
void DirectXMeshPainter::createPipelineStateObject()
{
auto vert_shader = mShaderProgram->getVertexShader();
auto pixel_shader = mShaderProgram->getPixelShader();
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
psoDesc.pRootSignature = mRootSignature.Get();
psoDesc.VS = CD3DX12_SHADER_BYTECODE(vert_shader);
psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixel_shader);
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState.DepthEnable = FALSE;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc.Count = 1;
mDxInterface->getD3dDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPipelineState));
}
ID3D12PipelineState* DirectXMeshPainter::getPipelineState() const
{
return mPipelineState.Get();
}
ID3D12RootSignature* DirectXMeshPainter::getRootSignature() const
{
return mRootSignature.Get();
}
void DirectXMeshPainter::updateCommandList(ID3D12GraphicsCommandList* commandList)
{
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
for (const auto& mesh : mDxMeshes)
{
commandList->IASetIndexBuffer(&mesh->getIndexBufferView());
commandList->IASetVertexBuffers(0, 1, &mesh->getVertBufferView());
commandList->DrawInstanced(mesh->getVertBufferView().SizeInBytes/ mesh->getVertBufferView().StrideInBytes, 1, 0, 0);
//commandList->DrawIndexedInstanced(3, 1, 0, 0, 0);
}
}
void DirectXMeshPainter::updateBuffers(DrawingContext* context)
{
mDxMeshes.clear();
auto scene = context->getSurface()->getScene();
for (const auto item : scene->getItems())
{
if (!item->isVisible())
{
continue;
}
if (item->getType() == SceneItem::Type::MODEL)
{
auto model = dynamic_cast<SceneModel*>(item);
if (model->getGeometry())
{
continue;
}
auto dx_mesh = std::make_unique<DirectXMesh>(model);
dx_mesh->update(context, mDxInterface->getD3dDevice());
mDxMeshes.push_back(std::move(dx_mesh));
}
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <wrl.h>
#include <d3d12.h>
#include <memory>
#include <vector>
class DrawingContext;
class DirectXShaderProgram;
class DirectXMesh;
class DirectXInterface;
class DirectXMeshPainter
{
public:
DirectXMeshPainter();
ID3D12PipelineState* getPipelineState() const;
ID3D12RootSignature* getRootSignature() const;
void initializeD3d();
void setDxInterface(DirectXInterface* dxInterface);
void updateBuffers(DrawingContext* context);
void updateCommandList(ID3D12GraphicsCommandList* commandList);
private:
void initializeShader();
void createRootSignature();
void createPipelineStateObject();
DirectXInterface* mDxInterface{ nullptr };
std::vector<std::unique_ptr<DirectXMesh> > mDxMeshes;
Microsoft::WRL::ComPtr<ID3D12RootSignature> mRootSignature;
Microsoft::WRL::ComPtr<ID3D12PipelineState> mPipelineState;
std::unique_ptr<DirectXShaderProgram> mShaderProgram;
};

View file

@ -0,0 +1,144 @@
#include "DirectXPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "Scene.h"
#include "SceneItem.h"
#include "SceneModel.h"
#include "SceneText.h"
#include "TriMesh.h"
#include "FontsManager.h"
#include "FontGlyph.h"
#include "TextData.h"
#include "DirectXShaderProgram.h"
#include "DirectXMeshPainter.h"
#include "DirectXTextPainter.h"
#include "DirectX2dPainter.h"
#include "DirectXMesh.h"
#include "DirectXInterface.h"
#include "DirectX2dInterface.h"
#include <wincodec.h>
#include "Win32WicImage.h"
#include "File.h"
#include <windows.h>
DirectXPainter::DirectXPainter(DrawingContext* context)
: AbstractPainter(context),
mMeshPainter(std::make_unique<DirectXMeshPainter>()),
mTextPainter(std::make_unique<DirectXTextPainter>()),
m2dPainter(std::make_unique<DirectX2dPainter>())
{
}
DirectXMeshPainter* DirectXPainter::getMeshPainter() const
{
return mMeshPainter.get();
}
void DirectXPainter::setDxInterface(DirectXInterface* dxInterface)
{
mRawDxInterface = dxInterface;
resetPainters();
}
DirectXInterface* DirectXPainter::getDxInterface() const
{
if (mDxInterface)
{
return mDxInterface.get();
}
else
{
return mRawDxInterface;
}
}
void DirectXPainter::initializeMesh()
{
mMeshPainter->initializeD3d();
}
void DirectXPainter::updateMesh()
{
mMeshPainter->updateBuffers(mDrawingContext);
}
void DirectXPainter::initializeDxInterface()
{
mDxInterface = std::make_unique<DirectXInterface>();
auto backing_image = mDrawingContext->getSurface()->getImage();
auto wic_bitmap = dynamic_cast<Win32WicImage*>(backing_image->getPlatformImage())->getBitmap();
mDxInterface->getD2dInterface()->initialize(wic_bitmap);
resetPainters();
}
void DirectXPainter::resetPainters()
{
m2dPainter->setD2dInterface(getDxInterface()->getD2dInterface());
mTextPainter->setD2dInterface(getDxInterface()->getD2dInterface());
mMeshPainter->setDxInterface(getDxInterface());
}
void DirectXPainter::paint()
{
if (!getDxInterface())
{
initializeDxInterface();
}
auto scene = mDrawingContext->getSurface()->getScene();
m2dPainter->startDrawing();
m2dPainter->clearBackground(scene->getBackgroundColor());
for (const auto item : scene->getItems())
{
if (!item->isVisible())
{
continue;
}
if (item->getType() == SceneItem::Type::MODEL)
{
auto model = dynamic_cast<SceneModel*>(item);
if (model->getGeometry())
{
m2dPainter->paint(model);
}
}
else if (item->getType() == SceneItem::Type::TEXT)
{
auto text = dynamic_cast<SceneText*>(item);
mTextPainter->paint(text, mDrawingContext);
}
}
m2dPainter->finishDrawing();
}
void DirectXPainter::paintBackground(const D3D12_CPU_DESCRIPTOR_HANDLE& rtvHandle, ID3D12GraphicsCommandList* commandList)
{
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
}
void DirectXPainter::paintMesh(ID3D12GraphicsCommandList* commandList)
{
mMeshPainter->updateCommandList(commandList);
}
bool DirectXPainter::supportsGeometryPrimitives() const
{
return true;
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "AbstractPainter.h"
#include <dwrite.h>
#include <d2d1_3.h>
#include <d2d1_1.h>
#include <d3d12.h>
#include <memory>
class DrawingContext;
class DirectXMeshPainter;
class DirectXTextPainter;
class DirectX2dPainter;
class DirectXInterface;
class DirectXPainter : public AbstractPainter
{
public:
DirectXPainter(DrawingContext* context);
DirectXMeshPainter* getMeshPainter() const;
void initializeMesh();
void paint() override;
void paintBackground(const D3D12_CPU_DESCRIPTOR_HANDLE& rtvHandle, ID3D12GraphicsCommandList* commandList);
void paintMesh(ID3D12GraphicsCommandList* commandList);
void setDxInterface(DirectXInterface* dxInterface);
bool supportsGeometryPrimitives() const override;
void updateMesh();
private:
DirectXInterface* getDxInterface() const;
void initializeDxInterface();
void resetPainters();
std::unique_ptr<DirectXMeshPainter> mMeshPainter;
std::unique_ptr<DirectXTextPainter> mTextPainter;
std::unique_ptr<DirectX2dPainter> m2dPainter;
DirectXInterface* mRawDxInterface{ nullptr };
std::unique_ptr<DirectXInterface> mDxInterface;
};

View file

@ -0,0 +1,31 @@
#include "DirectXShaderProgram.h"
#include "File.h"
#include "FileLogger.h"
#include <d3d12.h>
#include <d3dcompiler.h>
DirectXShaderProgram::DirectXShaderProgram(const Path& vertShaderPath, const Path& fragShaderPath)
{
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
// UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
UINT compileFlags = 0;
#else
UINT compileFlags = 0;
#endif
D3DCompileFromFile(vertShaderPath.c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &mVertexShader, nullptr);
D3DCompileFromFile(fragShaderPath.c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &mPixelShader, nullptr);
}
ID3DBlob* DirectXShaderProgram::getVertexShader() const
{
return mVertexShader.Get();
}
ID3DBlob* DirectXShaderProgram::getPixelShader() const
{
return mPixelShader.Get();
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <wrl.h>
#include <D3Dcommon.h>
#include <filesystem>
using Path = std::filesystem::path;
class DirectXShaderProgram
{
public:
DirectXShaderProgram(const Path& vertShaderPath, const Path& pixelShaderPath);
ID3DBlob* getVertexShader() const;
ID3DBlob* getPixelShader() const;
private:
Microsoft::WRL::ComPtr<ID3DBlob> mVertexShader;
Microsoft::WRL::ComPtr<ID3DBlob> mPixelShader;
};

View file

@ -0,0 +1,62 @@
#include "DirectXTextPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "FontsManager.h"
#include "FontGlyph.h"
#include "TextData.h"
#include "SceneText.h"
#include "DirectX2dInterface.h"
#include "UnicodeUtils.h"
#include "File.h"
#include "Win32BaseIncludes.h"
#include <d2d1_3.h>
#include <d2d1_1.h>
#include <dwrite.h>
DirectXTextPainter::DirectXTextPainter()
{
}
void DirectXTextPainter::setD2dInterface(DirectX2dInterface* d2dIterface)
{
mD2dInterface = d2dIterface;
mD2dInterface->getRenderTarget()->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &mTextBrush);
}
void DirectXTextPainter::updateTextFormat(const FontItem& font)
{
mD2dInterface->getDirectWriteFactory()->CreateTextFormat(
UnicodeUtils::utf8ToUtf16WString(font.getFaceName()).c_str(),
nullptr,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
static_cast<float>(font.getSize()),
L"en-us",
&mTextFormat
);
//mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
//mTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}
void DirectXTextPainter::paint(SceneText* text, DrawingContext* context)
{
const auto location = text->getTransform().getLocation();
D2D1_RECT_F textRect = D2D1::RectF(static_cast<float>(location.getX()), static_cast<float>(location.getY()), static_cast<float>(location.getX() + 200), static_cast<float>(location.getY() + 100));
updateTextFormat(text->getTextData().mFont);
auto content = UnicodeUtils::utf8ToUtf16WString(text->getTextData().mContent);
mD2dInterface->getRenderTarget()->DrawText(content.c_str(), static_cast<UINT32>(content.size()), mTextFormat.Get(), &textRect, mTextBrush.Get());
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "FontItem.h"
#include <wrl.h>
#include <dwrite.h>
#include <d2d1_3.h>
#include <d2d1_1.h>
#include <d3d12.h>
#include <unordered_map>
#include <memory>
class DrawingContext;
class DirectXShaderProgram;
class DirectX2dInterface;
class TextData;
class SceneText;
struct ID2D1DeviceContext2;
struct ID2D1SolidColorBrush;
struct IDWriteFactory;
class DirectXTextPainter
{
public:
DirectXTextPainter();
void paint(SceneText* text, DrawingContext* context);
void setD2dInterface(DirectX2dInterface* d2dIterface);
private:
void updateTextFormat(const FontItem& font);
DirectX2dInterface* mD2dInterface{ nullptr };
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> mTextBrush;
Microsoft::WRL::ComPtr<IDWriteTextFormat> mTextFormat;
};

View file

@ -0,0 +1,20 @@
struct PSInput
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
PSInput VSMain(float4 position : POSITION, float4 color : COLOR)
{
PSInput result;
result.position = position;
result.color = color;
return result;
}
float4 PSMain(PSInput input) : SV_TARGET
{
return input.color;
}

View file

@ -0,0 +1,42 @@
#include "OpenGlFontTexture.h"
#include "FontGlyph.h"
#include "Grid.h"
#include "Image.h"
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
OpenGlFontTexture::OpenGlFontTexture(FontGlyph* glyph)
: mGlyph(glyph)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &mHandle);
glBindTexture(GL_TEXTURE_2D, mHandle);
auto buffer = glyph->getImage()->getGridT<unsigned char>()->getInternalData()->getDataPtr();
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
mGlyph->getWidth(),
mGlyph->getHeight(),
0,
GL_RED,
GL_UNSIGNED_BYTE,
buffer
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
FontGlyph* OpenGlFontTexture::getGlyph() const
{
return mGlyph;
}

View file

@ -0,0 +1,20 @@
#pragma once
class FontGlyph;
class OpenGlFontTexture
{
public:
OpenGlFontTexture(FontGlyph* glyph);
unsigned int getHandle() const
{
return mHandle;
}
FontGlyph* getGlyph() const;
private:
unsigned int mHandle{0};
FontGlyph* mGlyph{nullptr};
};

View file

@ -0,0 +1,137 @@
#include "OpenGlMeshPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "SceneModel.h"
#include "TriMesh.h"
#include "LineMesh.h"
#include "OpenGlShaderProgram.h"
#ifdef _WIN32
#include <windows.h>
#endif
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
OpenGlMeshPainter::OpenGlMeshPainter()
{
}
void OpenGlMeshPainter::initializeShader()
{
auto vert_shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders/default.vert";
auto frag_shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders/default.frag";
mShaderProgram = std::make_unique<OpenGlShaderProgram>(vert_shader_path, frag_shader_path);
}
void OpenGlMeshPainter::initializeBuffers()
{
glGenBuffers(1, &mVertexBuffer);
glGenBuffers(1, &mElementBuffer);
glGenVertexArrays(1, &mVertexArray);
}
void OpenGlMeshPainter::paint(const std::vector<float>& verts, const std::vector<std::size_t>& elements, const std::vector<float>& color, bool lines)
{
glBindVertexArray(mVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), verts.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(unsigned), elements.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//glm::mat4 projection = glm::ortho(0.0f, width, 0.0f, height);
//glUniformMatrix4fv(glGetUniformLocation(mShaderProgram->getHandle(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUseProgram(mShaderProgram->getHandle());
glBindVertexArray(mVertexArray);
int vertexColorLocation = glGetUniformLocation(mShaderProgram->getHandle(), "ourColor");
glUniform4f(vertexColorLocation, float(color[0]), float(color[1]), float(color[2]), float(color[3]));
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if (lines)
{
glDrawElements(GL_LINES, elements.size(), GL_UNSIGNED_INT, 0);
}
else
{
glDrawElements( GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, 0);
}
glBindVertexArray(0);
}
void OpenGlMeshPainter::paint(SceneModel* model, DrawingContext* context)
{
if (!mShaderProgram)
{
initializeShader();
}
if (mVertexArray == 0)
{
initializeBuffers();
}
auto surface = context->getSurface();
const auto width = float(surface->getWidth());
const auto height = float(surface->getHeight());
auto transform = model->getTransform();
auto vertices = model->getMesh()->getVerticesFlat<float>();
for (std::size_t idx = 0; idx<vertices.size(); idx++)
{
if (idx % 3 == 0)
{
auto x = vertices[idx]*transform.getScaleX() + transform.getLocation().getX();
vertices[idx] = 2*x/width - 1.0;
}
else if(idx%3 == 1)
{
auto y = vertices[idx]*transform.getScaleY() + transform.getLocation().getY();
vertices[idx] = 1.0 - 2*y/height;
}
}
std::vector<std::size_t> indices;
const bool line_mesh = model->getMesh()->getType() == AbstractMesh::MeshType::LINE;
if (line_mesh)
{
indices = dynamic_cast<LineMesh*>(model->getMesh())->getEdgeNodeIds();
}
else
{
indices = dynamic_cast<TriMesh*>(model->getMesh())->getFaceNodeIds();
}
auto model_color = model->getFillColor().getAsVectorDouble();
std::vector<float> color = {float(model_color[0]), float(model_color[1]), float(model_color[2]), float(model_color[3])};
paint(vertices, indices, color, line_mesh);
if (model->getShowOutline())
{
auto edge_indices = dynamic_cast<TriMesh*>(model->getMesh())->getEdgeNodeIds();
paint(vertices, edge_indices, {0, 0, 0, 1}, true);
}
}

View file

@ -0,0 +1,27 @@
#pragma once
#include <memory>
#include <vector>
class DrawingContext;
class OpenGlShaderProgram;
class SceneModel;
class OpenGlMeshPainter
{
public:
OpenGlMeshPainter();
void paint(SceneModel* model, DrawingContext* context);
private:
void initializeShader();
void initializeBuffers();
void paint(const std::vector<float>& verts, const std::vector<std::size_t>& elements, const std::vector<float>& color, bool lines = false);
unsigned mVertexBuffer{0};
unsigned mElementBuffer{0};
unsigned mVertexArray{0};
std::unique_ptr<OpenGlShaderProgram> mShaderProgram;
};

View file

@ -0,0 +1,68 @@
#include "OpenGlPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "Scene.h"
#include "SceneItem.h"
#include "SceneModel.h"
#include "SceneText.h"
#include "TriMesh.h"
#include "FontsManager.h"
#include "FontGlyph.h"
#include "TextData.h"
#include "OpenGlShaderProgram.h"
#include "OpenGlMeshPainter.h"
#include "OpenGlTextPainter.h"
#include "OpenGlFontTexture.h"
#include "File.h"
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
OpenGlPainter::OpenGlPainter(DrawingContext* context)
: AbstractPainter(context),
mMeshPainter(std::make_unique<OpenGlMeshPainter>()),
mTextPainter(std::make_unique<OpenGlTextPainter>())
{
}
void OpenGlPainter::paint()
{
auto surface = mDrawingContext->getSurface();
const auto width = double(surface->getWidth());
const auto height = double(surface->getHeight());
glViewport(0, 0, width, height);
glOrtho(0, width, 0, height, -1.0, 1.0);
glClearColor(0.5, 0.5, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
auto scene = mDrawingContext->getSurface()->getScene();
for (const auto item : scene->getItems())
{
if (item->getType() == SceneItem::Type::MODEL)
{
mMeshPainter->paint(dynamic_cast<SceneModel*>(item), mDrawingContext);
}
else if (item->getType() == SceneItem::Type::TEXT)
{
mTextPainter->paint(dynamic_cast<SceneText*>(item), mDrawingContext);
}
}
glFlush();
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "AbstractPainter.h"
#include <unordered_map>
#include <memory>
class DrawingContext;
class OpenGlMeshPainter;
class OpenGlTextPainter;
class TriMesh;
class OpenGlPainter : public AbstractPainter
{
public:
OpenGlPainter(DrawingContext* context);
void paint() override;
private:
std::unique_ptr<OpenGlMeshPainter> mMeshPainter;
std::unique_ptr<OpenGlTextPainter> mTextPainter;
};

View file

@ -0,0 +1,68 @@
#include "OpenGlShaderProgram.h"
#include "File.h"
#include "FileLogger.h"
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
OpenGlShaderProgram::OpenGlShaderProgram(const Path& vertShaderPath, const Path& fragShaderPath)
{
const auto vert_shader_source = File(vertShaderPath).read();
const auto frag_shader_source = File(fragShaderPath).read();
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
const char* vert_source = vert_shader_source.c_str();
glShaderSource(vertexShader, 1, &vert_source, nullptr);
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
MLOG_ERROR("ERROR::SHADER::VERTEX::COMPILATION_FAILED: " << infoLog);
if (auto errCode = glGetError(); errCode != GL_NO_ERROR)
{
auto errString = gluErrorString(errCode);
MLOG_ERROR("Got gl error " << errString);
}
}
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* frag_source = frag_shader_source.c_str();
glShaderSource(fragmentShader, 1, &frag_source, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
MLOG_ERROR("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED: " << infoLog);
}
mHandle = glCreateProgram();
glAttachShader(mHandle, vertexShader);
glAttachShader(mHandle, fragmentShader);
glLinkProgram(mHandle);
glGetProgramiv(mHandle, GL_LINK_STATUS, &success);
if(!success)
{
glGetProgramInfoLog(mHandle, 512, NULL, infoLog);
MLOG_ERROR("Shader linking FAILED: " << infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
unsigned int OpenGlShaderProgram::getHandle() const
{
return mHandle;
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <filesystem>
using Path = std::filesystem::path;
class OpenGlShaderProgram
{
public:
OpenGlShaderProgram(const Path& vertShaderPath, const Path& fragShaderPath);
unsigned int getHandle() const;
private:
unsigned int mHandle{0};
};

View file

@ -0,0 +1,147 @@
#include "OpenGlTextPainter.h"
#include "DrawingContext.h"
#include "DrawingSurface.h"
#include "FontsManager.h"
#include "FontGlyph.h"
#include "OpenGlFontTexture.h"
#include "OpenGlShaderProgram.h"
#include "TextData.h"
#include "SceneText.h"
#include "File.h"
#ifdef _WIN32
#include <windows.h>
#endif
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
OpenGlTextPainter::OpenGlTextPainter()
{
}
void OpenGlTextPainter::initializeShader()
{
auto vert_shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders/text.vert";
auto frag_shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders/text.frag";
mShaderProgram = std::make_unique<OpenGlShaderProgram>(vert_shader_path, frag_shader_path);
}
void OpenGlTextPainter::initializeTextures(const TextData& textData, DrawingContext* context)
{
for (auto line : textData.mLines)
{
for (auto c : line)
{
if (auto iter = mFontTextures.find(c); iter == mFontTextures.end())
{
auto glyph = context->getFontsManager()->getGlyph(textData.mFont.getFaceName(), textData.mFont.getSize(), c);
auto new_texture = std::make_unique<OpenGlFontTexture>(glyph);
mFontTextures[c] = std::move(new_texture);
}
}
}
}
void OpenGlTextPainter::initializeBuffers()
{
glGenVertexArrays(1, &mVertexArray);
glGenBuffers(1, &mVertexBuffer);
glBindVertexArray(mVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void OpenGlTextPainter::paint(SceneText* text, DrawingContext* context)
{
if (!mShaderProgram)
{
initializeShader();
}
if (mVertexArray == 0)
{
initializeBuffers();
}
auto text_data = text->getTextData();
initializeTextures(text_data, context);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(mShaderProgram->getHandle());
glUniform3f(glGetUniformLocation(mShaderProgram->getHandle(), "textColor"), 0.0, 0.0, 0.0);
const auto width = float(context->getSurface()->getWidth());
const auto height = float(context->getSurface()->getHeight());
glm::mat4 projection = glm::ortho(0.0f, width, 0.0f, height);
glUniformMatrix4fv(glGetUniformLocation(mShaderProgram->getHandle(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(mVertexArray);
auto transform = text->getTransform();
float line_delta = 20;
float line_offset = 0;
for (auto line : text_data.mLines)
{
float x = transform.getLocation().getX();
const float y = height - line_offset - transform.getLocation().getY();
for (auto c : line)
{
auto texture = mFontTextures[c].get();
float xpos = x + texture->getGlyph()->getBearingX();
float ypos = y - (int(texture->getGlyph()->getHeight()) - texture->getGlyph()->getBearingY());
float w = texture->getGlyph()->getWidth();
float h = texture->getGlyph()->getHeight();
float vertices[6][4] = {
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos, ypos, 0.0f, 1.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos + w, ypos + h, 1.0f, 0.0f }
};
glBindTexture(GL_TEXTURE_2D, texture->getHandle());
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
auto offset = (texture->getGlyph()->getAdvanceX() >> 6); // bitshift by 6 to get value in pixels (2^6 = 64)
x += offset;
}
line_offset += line_delta;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <unordered_map>
#include <memory>
class DrawingContext;
class OpenGlFontTexture;
class OpenGlShaderProgram;
class TextData;
class SceneText;
class OpenGlTextPainter
{
public:
OpenGlTextPainter();
void paint(SceneText* text, DrawingContext* context);
private:
void initializeShader();
void initializeTextures(const TextData& textData, DrawingContext* context);
void initializeBuffers();
unsigned mVertexBuffer{0};
unsigned mVertexArray{0};
std::unique_ptr<OpenGlShaderProgram> mShaderProgram;
std::unordered_map<char, std::unique_ptr<OpenGlFontTexture> > mFontTextures;
};

View file

@ -0,0 +1,11 @@
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // the input variable from the vertex shader (same name and same type)
uniform vec4 ourColor; // we set this variable in the OpenGL code.
void main()
{
FragColor = ourColor;
}

View file

@ -0,0 +1,7 @@
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos, 1.0);
}

View file

@ -0,0 +1,12 @@
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}

View file

@ -0,0 +1,11 @@
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}

View file

@ -0,0 +1,34 @@
#include "AbstractFace.h"
#include "Edge.h"
AbstractFace::AbstractFace(std::size_t id)
: mId(id)
{
}
AbstractFace::~AbstractFace()
{
}
void AbstractFace::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
std::vector<double> AbstractFace::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
void AbstractFace::setIndex(std::size_t idx)
{
mId = idx;
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "Point.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <string>
class Edge;
class AbstractFace
{
public:
enum class Orientation
{
CW,
CCW
};
AbstractFace(std::size_t id=0);
virtual ~AbstractFace();
virtual std::vector<std::size_t> getNodeIds() const = 0;
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
std::vector<double> getVectorAttribute(const std::string& tag) const;
virtual std::size_t getNumNodes() const = 0;
virtual void associateWidthEdges() = 0;
virtual std::vector<std::size_t> getEdgeIds() const = 0;
virtual std::vector<Point> getNodeLocations(Orientation orientation = Orientation::CCW) const = 0;
virtual void replaceEdge(Edge* original, Edge* replacement) = 0;
void setIndex(std::size_t idx);
protected:
std::size_t mId{0};
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
};

View file

@ -0,0 +1,75 @@
#include "AbstractMesh.h"
void AbstractMesh::addConstantNodeVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
}
std::vector<std::vector<double> > AbstractMesh::getNodeVectorAttributes(const std::string& tag)
{
std::vector<std::vector<double> > attribs(mNodes.size());
return attribs;
}
void AbstractMesh::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
bool AbstractMesh::hasVectorAttribute(const std::string& tag) const
{
return mVectorAttributes.find(tag) != mVectorAttributes.end();
}
unsigned AbstractMesh::getNumNodes() const
{
return unsigned(mNodes.size());
}
const VecNodes& AbstractMesh::getNodes() const
{
return mNodes;
}
std::vector<double> AbstractMesh::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
void AbstractMesh::scale(double scaleX, double scaleY)
{
Transform transform({ 0.0, 0.0 }, scaleX, scaleY);
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::transform(const Transform& transform)
{
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::translate(double offsetX, double offsetY, double offsetZ)
{
const Point loc {-offsetX, -offsetY, -offsetZ};
Transform transform(loc);
for (auto& node : mNodes)
{
node->apply(transform);
}
}
void AbstractMesh::translate(const Point& offset)
{
translate(offset.getX(), offset.getY(), offset.getZ());
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "Point.h"
#include "Node.h"
#include "Transform.h"
#include <memory>
#include <vector>
using NodePtr = std::unique_ptr<Node>;
using VecNodes = std::vector<NodePtr>;
class AbstractMesh
{
public:
enum class MeshType
{
LINE,
TRI,
QUAD
};
virtual ~AbstractMesh() = default;
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
void addConstantNodeVectorAttribute(const std::string& tag, const std::vector<double>& values);
template<typename T>
std::vector<T> getVerticesFlat(T scaleX = 1.0, T scaleY = 1.0, T scaleZ = 1.0) const
{
std::vector<T> ret(3*mNodes.size());
for(std::size_t idx = 0; idx<mNodes.size(); idx++)
{
auto node = mNodes[idx].get();
ret[3*idx] = node->getPoint().getX()/scaleX;
ret[3*idx + 1] = node->getPoint().getY()/scaleY;
ret[3*idx + 2] = node->getPoint().getZ()/scaleZ;
}
return ret;
}
virtual std::unique_ptr<AbstractMesh> copy() const = 0;
std::vector<std::vector<double> > getNodeVectorAttributes(const std::string& tag);
std::vector<double> getVectorAttribute(const std::string& tag) const;
virtual MeshType getType() const = 0;
bool hasVectorAttribute(const std::string& tag) const;
void scale(double scaleX, double scaleY);
void translate(const Point& offset);
void translate(double offsetX, double offsetY, double offsetZ = 0.0);
void transform(const Transform& transform);
unsigned getNumNodes() const;
const VecNodes& getNodes() const;
protected:
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
VecNodes mNodes;
};

View file

@ -0,0 +1,38 @@
list(APPEND mesh_LIB_INCLUDES
AbstractMesh.cpp
AbstractMesh.h
Edge.cpp
Edge.h
AbstractFace.cpp
AbstractFace.h
QuadFace.cpp
QuadFace.h
TriFace.cpp
TriFace.h
Node.cpp
Node.h
QuadMesh.cpp
QuadMesh.h
TriMesh.cpp
TriMesh.h
FaceMesh.cpp
FaceMesh.h
LineMesh.cpp
LineMesh.h
MeshPrimitives.cpp
MeshPrimitives.h
MeshBuilder.cpp
MeshBuilder.h
MeshObjWriter.h
MeshObjWriter.cpp
)
# add the library
add_library(mesh SHARED ${mesh_LIB_INCLUDES})
target_link_libraries(mesh core geometry)
target_include_directories(mesh PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/")
set_target_properties( mesh PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
set_property(TARGET mesh PROPERTY FOLDER src/rendering)

122
src/rendering/mesh/Edge.cpp Normal file
View file

@ -0,0 +1,122 @@
#include "Edge.h"
#include "Node.h"
Edge::Edge(Node* node0, Node* node1, std::size_t id)
: mNode0(node0),
mNode1(node1),
mId(id)
{
}
std::unique_ptr<Edge> Edge::Create(Node* node0, Node* node1, std::size_t id)
{
return std::make_unique<Edge>(node0, node1, id);
}
Edge::~Edge()
{
}
std::size_t Edge::getNode0Id() const
{
return mNode0->getIndex();
}
std::size_t Edge::getNode1Id() const
{
return mNode1->getIndex();
}
bool Edge::isOverlapping(Edge* edge) const
{
auto overlaps = edge->getNode0()->isCoincident(mNode0) && edge->getNode1()->isCoincident(mNode1);
if (overlaps)
{
return true;
}
else
{
return edge->getNode1()->isCoincident(mNode0) && edge->getNode0()->isCoincident(mNode1);
}
}
bool Edge::hasSameNodes(Edge* edge) const
{
return edge->getNode0() == edge->getNode1() || edge->getNode1() == edge->getNode0();
}
void Edge::replaceNodes(Node* node0, Node* node1)
{
mNode0 = node0;
mNode1 = node1;
}
Node* Edge::getNode0() const
{
return mNode0;
}
Node* Edge::getNode1() const
{
return mNode1;
}
Node* Edge::getOtherNode(Node* node) const
{
if (node == mNode0)
{
return mNode1;
}
else
{
return mNode0;
}
}
std::size_t Edge::getId() const
{
return mId;
}
void Edge::setState(State state)
{
mState = state;
}
Edge::State Edge::getState() const
{
return mState;
}
void Edge::clearConnectivity()
{
mAssociatedFaceIds.clear();
}
std::size_t Edge::getNumConnectedFaces() const
{
return mAssociatedFaceIds.size();
}
void Edge::associateFace(std::size_t faceId)
{
mAssociatedFaceIds.push_back(faceId);
}
std::size_t Edge::getConnectedFaceId(std::size_t idx) const
{
return mAssociatedFaceIds[idx];
}
void Edge::associateWithNodes()
{
mNode0->associateEdge(mId);
mNode1->associateEdge(mId);
}
void Edge::setIndex(std::size_t idx)
{
mId = idx;
}

65
src/rendering/mesh/Edge.h Normal file
View file

@ -0,0 +1,65 @@
#pragma once
#include <memory>
#include <vector>
class Node;
class Edge
{
public:
enum class State
{
HEALTHY,
DIRTY
};
Edge(Node* node0, Node* node1, std::size_t id = 0);
~Edge();
static std::unique_ptr<Edge> Create(Node* node0, Node* node1, std::size_t id = 0);
void associateFace(std::size_t faceId);
void associateWithNodes();
void clearConnectivity();
std::size_t getConnectedFaceId(std::size_t idx) const;
State getState() const;
std::size_t getId() const;
std::size_t getNode0Id() const;
std::size_t getNode1Id() const;
Node* getNode0() const;
Node* getNode1() const;
std::size_t getNumConnectedFaces() const;
Node* getOtherNode(Node* node) const;
bool isOverlapping(Edge* edge) const;
bool hasSameNodes(Edge* edge) const;
void replaceNodes(Node* node0, Node* node1);
void setState(State state);
void setIndex(std::size_t idx);
private:
std::size_t mId{0};
Node* mNode0{nullptr};
Node* mNode1{nullptr};
std::vector<std::size_t> mAssociatedFaceIds;
State mState{State::HEALTHY};
};

View file

@ -0,0 +1,201 @@
#include "FaceMesh.h"
#include "Node.h"
#include "Edge.h"
#include "AbstractFace.h"
FaceMesh::~FaceMesh()
{
}
void FaceMesh::populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces)
{
mNodes = std::move(nodes);
mEdges = std::move(edges);
mFaces = std::move(faces);
resetIds();
resetConnectivity();
}
const VecFaces& FaceMesh::getFaces() const
{
return mFaces;
}
std::vector<std::size_t> FaceMesh::getFaceNodeIds() const
{
if (mFaces.empty())
{
return {};
}
auto nodes_per_face = mFaces[0]->getNumNodes();
std::vector<std::size_t> ids(nodes_per_face*mFaces.size());
for(std::size_t idx=0; idx<mFaces.size(); idx++)
{
const auto nodeIds = mFaces[idx]->getNodeIds();
for(std::size_t jdx=0; jdx<nodes_per_face; jdx++)
{
ids[nodes_per_face*idx + jdx] = nodeIds[jdx];
}
}
return ids;
}
std::vector<std::vector<double> > FaceMesh::getFaceVectorAttributes(const std::string& tag)
{
std::vector<std::vector<double> > attribs(mFaces.size());
for(std::size_t idx=0; idx<mFaces.size(); idx++)
{
attribs[idx] = {mFaces[idx]->getVectorAttribute(tag)};
}
return attribs;
}
void FaceMesh::addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
for (const auto& face : mFaces)
{
face->addVectorAttribute(tag, values);
}
}
void FaceMesh::resetIds()
{
std::size_t count = 0;
for (auto& node : mNodes)
{
node->setIndex(count);
count++;
}
count = 0;
for (auto& edge : mEdges)
{
edge->setIndex(count);
count++;
}
count = 0;
for (auto& face : mFaces)
{
face->setIndex(count);
count++;
}
}
void FaceMesh::resetConnectivity()
{
for (auto& node : mNodes)
{
node->clearConnectivity();
}
for (auto& edge : mEdges)
{
edge->associateWithNodes();
edge->clearConnectivity();
}
for (auto& face : mFaces)
{
face->associateWidthEdges();
}
}
void FaceMesh::replaceIfOverlapping(FaceMesh* mesh, Node* target_node) const
{
for (auto& node : mNodes)
{
if (node->getNumConnectedEdges() == 3 && node->isCoincident(target_node))
{
target_node->setState(Node::State::DIRTY);
for (std::size_t idx=0; idx<3; idx++)
{
auto edge = mesh->mEdges[idx].get();
edge->replaceNodes(target_node, node.get());
}
break;
}
}
}
void FaceMesh::replaceIfOverlapping(FaceMesh* mesh, Edge* target_edge) const
{
for (auto& edge : mEdges)
{
if (edge->getNumConnectedFaces() == 2 && target_edge->hasSameNodes(edge.get()))
{
target_edge->setState(Edge::State::DIRTY);
for (std::size_t idx=0; idx<2; idx++)
{
auto face = mesh->mFaces[idx].get();
face->replaceEdge(target_edge, edge.get());
}
break;
}
}
}
std::vector<std::size_t> FaceMesh::getEdgeNodeIds() const
{
std::vector<std::size_t> ids(2*mEdges.size());
for(std::size_t idx=0; idx<mEdges.size(); idx++)
{
ids[2*idx] = mEdges[idx]->getNode0Id();
ids[2*idx + 1] = mEdges[idx]->getNode1Id();
}
return ids;
}
void FaceMesh::merge(std::unique_ptr<FaceMesh> mesh)
{
if (mesh->getType() != getType())
{
return;
}
for (auto& node : mesh->mNodes)
{
if (node->getNumConnectedEdges() == 3)
{
replaceIfOverlapping(mesh.get(), node.get());
}
}
for (auto& node : mesh->mNodes)
{
if (node->getState() == Node::State::HEALTHY)
{
mNodes.push_back(std::move(node));
}
}
for (auto& edge : mesh->mEdges)
{
if (edge->getNumConnectedFaces() == 2)
{
replaceIfOverlapping(mesh.get(), edge.get());
}
}
for (auto& edge : mesh->mEdges)
{
if (edge->getState() == Edge::State::HEALTHY)
{
mEdges.push_back(std::move(edge));
}
}
for (auto& face : mesh->mFaces)
{
mFaces.push_back(std::move(face));
}
mesh.reset();
resetIds();
resetConnectivity();
}

View file

@ -0,0 +1,51 @@
#pragma once
#include "AbstractMesh.h"
#include <string>
#include <unordered_map>
class Edge;
class AbstractFace;
using EdgePtr = std::unique_ptr<Edge>;
using AbstractFacePtr = std::unique_ptr<AbstractFace>;
using VecEdges = std::vector<EdgePtr>;
using VecFaces = std::vector<AbstractFacePtr>;
class FaceMesh : public AbstractMesh
{
public:
FaceMesh() = default;
~FaceMesh();
virtual std::unique_ptr<AbstractMesh> copy() const = 0;
void populate(VecNodes& nodes, VecEdges& edges, VecFaces& faces);
std::vector<std::size_t> getFaceNodeIds() const;
std::vector<std::size_t> getEdgeNodeIds() const;
void addConstantFaceVectorAttribute(const std::string& tag, const std::vector<double>& values);
std::vector<std::vector<double> > getFaceVectorAttributes(const std::string& tag);
void merge(std::unique_ptr<FaceMesh> mesh);
const VecFaces& getFaces() const;
protected:
void resetConnectivity();
void resetIds();
void replaceIfOverlapping(FaceMesh* mesh, Node* node) const;
void replaceIfOverlapping(FaceMesh* mesh, Edge* edge) const;
VecEdges mEdges;
VecFaces mFaces;
};

View file

@ -0,0 +1,35 @@
#include "LineMesh.h"
#include "Edge.h"
LineMesh::~LineMesh()
{
}
void LineMesh::populate(VecNodes& nodes, VecEdges& edges)
{
mNodes = std::move(nodes);
mEdges = std::move(edges);
}
std::unique_ptr<AbstractMesh> LineMesh::copy() const
{
return nullptr;
}
LineMesh::MeshType LineMesh::getType() const
{
return LineMesh::MeshType::LINE;
}
std::vector<std::size_t> LineMesh::getEdgeNodeIds() const
{
std::vector<std::size_t> ids(2*mEdges.size());
for(std::size_t idx=0; idx<mEdges.size(); idx++)
{
ids[2*idx] = mEdges[idx]->getNode0Id();
ids[2*idx + 1] = mEdges[idx]->getNode1Id();
}
return ids;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "AbstractMesh.h"
#include <string>
#include <unordered_map>
class Edge;
using EdgePtr = std::unique_ptr<Edge>;
using VecEdges = std::vector<EdgePtr>;
class LineMesh : public AbstractMesh
{
public:
LineMesh() = default;
~LineMesh();
void populate(VecNodes& nodes, VecEdges& edges);
std::vector<std::size_t> getEdgeNodeIds() const;
MeshType getType() const override;
std::unique_ptr<AbstractMesh> copy() const override;
private:
VecEdges mEdges;
};

View file

@ -0,0 +1,55 @@
#include "MeshBuilder.h"
#include "Node.h"
#include "Edge.h"
#include "TriFace.h"
std::unique_ptr<TriMesh> MeshBuilder::buildTriMesh(const VecPoints& locations, const EdgeIds& edgeIds, const FaceIds& faceIds)
{
auto mesh = std::make_unique<TriMesh>();
VecNodes nodes(locations.size());
for (std::size_t idx=0; idx<locations.size(); idx++)
{
nodes[idx] = Node::Create(locations[idx], idx);
}
VecEdges edges(edgeIds.size());
for (std::size_t idx=0; idx<edgeIds.size(); idx++)
{
edges[idx] = Edge::Create(nodes[edgeIds[idx].first].get(), nodes[edgeIds[idx].second].get(), idx);
}
VecFaces faces(faceIds.size());
for (std::size_t idx=0; idx<faceIds.size(); idx++)
{
faces[idx] = TriFace::Create(
edges[faceIds[idx][0]].get(),
edges[faceIds[idx][1]].get(),
edges[faceIds[idx][2]].get(),
idx);
}
mesh->populate(nodes, edges, faces);
return mesh;
}
std::unique_ptr<LineMesh> MeshBuilder::buildLineMesh(const VecPoints& locations, const EdgeIds& edgeIds)
{
auto mesh = std::make_unique<LineMesh>();
VecNodes nodes(locations.size());
for (std::size_t idx=0; idx<locations.size(); idx++)
{
nodes[idx] = Node::Create(locations[idx], idx);
}
VecEdges edges(edgeIds.size());
for (std::size_t idx=0; idx<edgeIds.size(); idx++)
{
edges[idx] = Edge::Create(nodes[edgeIds[idx].first].get(), nodes[edgeIds[idx].second].get(), idx);
}
mesh->populate(nodes, edges);
return mesh;
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "TriMesh.h"
#include "LineMesh.h"
#include "Point.h"
#include <memory>
using EdgeIds = std::vector<std::pair<std::size_t, std::size_t> >;
using FaceIds = std::vector<std::vector<std::size_t> >;
using VecPoints = std::vector<Point>;
class MeshBuilder
{
public:
static std::unique_ptr<TriMesh> buildTriMesh(const VecPoints& locations, const EdgeIds& edgeIds, const FaceIds& faceIds);
static std::unique_ptr<LineMesh> buildLineMesh(const VecPoints& locations, const EdgeIds& edgeIds);
};

View file

@ -0,0 +1,32 @@
#include "MeshObjWriter.h"
#include "File.h"
#include "TriMesh.h"
#include "AbstractFace.h"
#include <sstream>
std::string MeshObjWriter::serialize(TriMesh* mesh)
{
std::stringstream output;
for (const auto& node : mesh->getNodes())
{
const auto x = node->getPoint().getX();
const auto y = node->getPoint().getY();
const auto z = node->getPoint().getZ();
output << "v "<< x << " " << y << " " << z << "\n";
}
for (const auto& face : mesh->getFaces())
{
auto ids = face->getNodeIds();
output << "f " << 1 + ids[0] << " " << 1 + ids[1] << " " << 1 + ids[2] << "\n";
}
return output.str();
}
void MeshObjWriter::write(const Path& path, TriMesh* mesh)
{
File file(path);
file.writeText(serialize(mesh));
}

View file

@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <filesystem>
using Path = std::filesystem::path;
class TriMesh;
class MeshObjWriter
{
public:
static std::string serialize(TriMesh* mesh);
static void write(const Path& path, TriMesh* mesh);
};

View file

@ -0,0 +1,305 @@
#include "MeshPrimitives.h"
#include "MeshBuilder.h"
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
std::unique_ptr<TriMesh> MeshPrimitives::buildRectangleAsTriMesh()
{
VecPoints locations = {
{0, 0},
{1, 0},
{1, 1},
{0, 1}
};
EdgeIds edge_ids = {
{0, 1},
{1, 2},
{2, 0},
{2, 3},
{3, 0}
};
FaceIds face_ids = {
{0, 1, 2},
{2, 3, 4}
};
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildCircleAsTriMesh(std::size_t numSegments)
{
VecPoints locations(numSegments + 1);
locations[0] = {0, 0};
const double delta_theta = (2.0*M_PI)/double(numSegments);
double theta = 0.0;
for(std::size_t idx=1; idx<=numSegments; idx++)
{
const double x = sin(theta);
const double y = cos(theta);
locations[idx] = {x, y};
theta += delta_theta;
std::cout << "Adding node at: " << x << " | " << y << std::endl;
}
EdgeIds edge_ids(2*numSegments);
for(std::size_t idx=0; idx<numSegments; idx++)
{
edge_ids[idx] = {0, idx+1};
auto wrap_node = idx + 2;
if (wrap_node > numSegments)
{
wrap_node = 1;
}
edge_ids[idx + numSegments] = {idx + 1, wrap_node};
}
FaceIds face_ids(numSegments);
for(std::size_t idx=0; idx<numSegments; idx++)
{
auto top_edge_inner = idx + 1;
if (top_edge_inner == numSegments)
{
top_edge_inner = 0;
}
const auto outer_edge = idx + numSegments;
face_ids[idx] = {idx, outer_edge, top_edge_inner};
}
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<LineMesh> MeshPrimitives::buildCircleAsLineMesh(std::size_t numSegments)
{
VecPoints locations(numSegments);
const double delta_theta = (2.0*M_PI)/double(numSegments);
double theta = 0.0;
for(unsigned idx=0; idx<numSegments; idx++)
{
const double x = sin(theta);
const double y = cos(theta);
locations[idx] = {x, y};
}
EdgeIds edge_ids(2*numSegments);
for(unsigned idx=0; idx<numSegments; idx++)
{
auto top_node = idx + 1;
if (top_node == numSegments)
{
top_node = 0;
}
edge_ids[idx] = {idx, top_node};
}
return MeshBuilder::buildLineMesh(locations, edge_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio, std::size_t num_segments)
{
std::size_t num_fans = 4;
std::size_t num_nodes_per_fan = num_segments + 2;
VecPoints locations(num_fans * num_nodes_per_fan);
double rect_start_x = radius;
double rect_end_x = 1.0 - radius;
double rect_end_y = 1.0 - radius;
double delta_theta = (M_PI/4.0) / double (num_segments);
double running_theta = 0;
locations[0] = {rect_end_x, radius};
std::size_t offset = 1;
for (std::size_t idx=0; idx<=num_segments; idx++)
{
locations[offset + idx] = {rect_end_x + radius*sin(running_theta), radius*(1.0 - cos(running_theta))};
running_theta += delta_theta;
}
offset += num_segments;
locations[offset] = {rect_end_x, rect_end_y};
offset++;
running_theta = 0;
for (std::size_t idx=0; idx<=num_segments;idx++)
{
locations[offset + idx] = {rect_end_x + radius*cos(running_theta), rect_end_y + radius*sin(running_theta)};
running_theta += delta_theta;
}
offset += num_segments;
locations[offset] = {rect_start_x, rect_end_y};
offset ++;
running_theta = 0;
for (std::size_t idx=0; idx<=num_segments;idx++)
{
locations[offset + idx] = {rect_start_x - radius*sin(running_theta), rect_end_y + radius*cos(running_theta)};
running_theta += delta_theta;
}
offset += num_segments;
locations[offset] = {rect_start_x, radius};
offset++;
running_theta = 0;
for (std::size_t idx=0; idx<=num_segments;idx++)
{
locations[offset + idx] = {rect_start_x - radius*cos(running_theta), radius *(1 - sin(running_theta))};
running_theta += delta_theta;
}
std::size_t num_edges_per_fan = 2*num_segments + 1;
std::size_t num_inner_rect_edges = 3*num_fans;
EdgeIds edge_ids(num_edges_per_fan*num_fans + num_inner_rect_edges + 1);
// Fan edges
for (std::size_t jdx=0; jdx< num_fans; jdx++)
{
std::size_t node_offset = jdx*num_nodes_per_fan;
std::size_t edge_offset = jdx*num_edges_per_fan;
// Inner edges
for(std::size_t idx=0; idx<=num_segments; idx++)
{
edge_ids[edge_offset + idx] = {node_offset, node_offset + idx + 1};
}
// Outer edges
for(std::size_t idx=0; idx<num_segments; idx++)
{
edge_ids[edge_offset + num_segments + 1 + idx] = {node_offset + idx + 1, node_offset + idx + 2};
}
}
// Inner rect edges
std::size_t edge_offset = num_edges_per_fan*num_fans;
return nullptr;
}
std::unique_ptr<LineMesh> MeshPrimitives::buildRectangleAsLineMesh()
{
VecPoints locations = {
{0, 0},
{1, 0},
{1, 1},
{0, 1}
};
EdgeIds edge_ids = {
{0, 1},
{1, 2},
{2, 3},
{3, 0}
};
return MeshBuilder::buildLineMesh(locations, edge_ids);
}
std::unique_ptr<TriMesh> MeshPrimitives::buildExplodedGridAsTriMesh(std::size_t numX, std::size_t numY)
{
double delta_x = 1.0/double(numX);
double delta_y = 1.0/double(numY);
VecPoints locations (4 * numX * numY);
double offset_x = delta_x/2.0;
double offset_y = delta_y/2.0;
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
auto locX0 = offset_x - delta_x/2.0;
auto locX1 = offset_x + delta_x/2.0;
auto locY0 = offset_y - delta_y/2.0;
auto locY1 = offset_y + delta_y/2.0;
auto id_offset = 4* (jdx + numX*idx);
locations[id_offset] = Point(locX0, locY0);
locations[id_offset + 1] = Point(locX1, locY0);
locations[id_offset + 2] = Point(locX1, locY1);
locations[id_offset + 3] = Point(locX0, locY1);
offset_x += delta_x;
}
offset_x = delta_x/2.0;
offset_y += delta_y;
}
EdgeIds edge_ids(5 * numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t node_offset = 4 * (jdx + numX * idx);
auto id_offset = 5* (jdx + numX*idx);
edge_ids[id_offset] = {node_offset, node_offset + 1};
edge_ids[id_offset + 1] = {node_offset + 1, node_offset + 2};
edge_ids[id_offset + 2] = {node_offset + 2, node_offset + 3};
edge_ids[id_offset + 3] = {node_offset + 3, node_offset};
edge_ids[id_offset + 4] = {node_offset + 2, node_offset};
}
}
FaceIds face_ids(2 *numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t edge_offset = 5 * (jdx + numX * idx);
std::size_t face_offset = 2 * (jdx + numX * idx);
face_ids[face_offset] = {edge_offset, edge_offset + 1, edge_offset + 4};
face_ids[face_offset + 1] = {edge_offset + 4, edge_offset + 2, edge_offset + 3};
}
}
return MeshBuilder::buildTriMesh(locations, edge_ids, face_ids);
}
std::unique_ptr<LineMesh> MeshPrimitives::buildExplodedGridAsLineMesh(std::size_t numX, std::size_t numY)
{
double delta_x = 1.0/double(numX);
double delta_y = 1.0/double(numY);
VecPoints locations (4 * numX * numY);
double offset_x = delta_x/2.0;
double offset_y = delta_y/2.0;
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
auto locX0 = offset_x - delta_x/2.0;
auto locX1 = offset_x + delta_x/2.0;
auto locY0 = offset_y - delta_y/2.0;
auto locY1 = offset_y + delta_y/2.0;
auto id_offset = 4* (jdx + numX*idx);
locations[id_offset] = Point(locX0, locY0);
locations[id_offset + 1] = Point(locX1, locY0);
locations[id_offset + 2] = Point(locX1, locY1);
locations[id_offset + 3] = Point(locX0, locY1);
offset_x += delta_x;
}
offset_x = delta_x/2.0;
offset_y += delta_y;
}
EdgeIds edge_ids(4 * numX * numY);
for (std::size_t idx=0; idx<numY; idx++)
{
for(std::size_t jdx=0; jdx<numX; jdx++)
{
std::size_t node_offset = 4 * (jdx + numX * idx);
auto id_offset = 4* (jdx + numX*idx);
edge_ids[id_offset] = {node_offset, node_offset + 1};
edge_ids[id_offset + 1] = {node_offset + 1, node_offset + 2};
edge_ids[id_offset + 2] = {node_offset + 2, node_offset + 3};
edge_ids[id_offset + 3] = {node_offset + 3, node_offset};
}
}
return MeshBuilder::buildLineMesh(locations, edge_ids);
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "TriMesh.h"
#include "LineMesh.h"
class MeshPrimitives
{
public:
static std::unique_ptr<TriMesh> buildCircleAsTriMesh(std::size_t numSegments = 24);
static std::unique_ptr<LineMesh> buildCircleAsLineMesh(std::size_t numSegments = 24);
static std::unique_ptr<TriMesh> buildRectangleAsTriMesh();
static std::unique_ptr<LineMesh> buildRectangleAsLineMesh();
static std::unique_ptr<TriMesh> buildExplodedGridAsTriMesh(std::size_t numX, std::size_t numY);
static std::unique_ptr<LineMesh> buildExplodedGridAsLineMesh(std::size_t numX, std::size_t numY);
static std::unique_ptr<TriMesh> buildRoundedRectangleAsTriMesh(double radius, double aspect_ratio = 1.0, std::size_t num_segments = 4);
};

111
src/rendering/mesh/Node.cpp Normal file
View file

@ -0,0 +1,111 @@
#include "Node.h"
std::unique_ptr<Node> Node::Create(const Point& p, std::size_t index)
{
return std::make_unique<Node>(p, index);
}
Node::~Node()
{
}
Node::Node(const Point& p, std::size_t index)
: mPoint(p),
mIndex(index)
{
}
std::size_t Node::getIndex() const
{
return mIndex;
}
void Node::updateIndex(std::size_t index)
{
mIndex = index;
}
void Node::addVectorAttribute(const std::string& tag, const std::vector<double>& values)
{
mVectorAttributes[tag] = values;
}
std::vector<double> Node::getVectorAttribute(const std::string& tag) const
{
auto iter = mVectorAttributes.find(tag);
if (iter != mVectorAttributes.end())
{
return iter->second;
}
return {};
}
const Point& Node::getPoint() const
{
return mPoint;
}
void Node::apply(const Transform& transform)
{
mPoint.apply(transform);
}
bool Node::isCoincident(Node* node) const
{
return node->getPoint() == mPoint;
}
void Node::setState(State state)
{
mState = state;
}
Node::State Node::getState() const
{
return mState;
}
void Node::clearConnectivity()
{
mAssociatedEdgeIds.clear();
mAssociatedFaceIds.clear();
}
std::size_t Node::getNumConnectedEdges() const
{
return mAssociatedEdgeIds.size();
}
std::size_t Node::getNumConnectedFaces() const
{
return mAssociatedFaceIds.size();
}
void Node::associateEdge(std::size_t edgeId)
{
mAssociatedEdgeIds.push_back(edgeId);
}
void Node::associateFace(std::size_t faceId)
{
mAssociatedFaceIds.push_back(faceId);
}
std::size_t Node::getConnectedEdgeId(std::size_t idx) const
{
return mAssociatedEdgeIds[idx];
}
std::size_t Node::getConnectedFaceId(std::size_t idx) const
{
return mAssociatedFaceIds[idx];
}
void Node::setIndex(std::size_t idx)
{
mIndex = idx;
}

69
src/rendering/mesh/Node.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#include "Point.h"
#include "Transform.h"
#include <unordered_map>
#include <string>
#include <vector>
class Node
{
public:
enum class State
{
HEALTHY,
DIRTY
};
Node(const Point& p, std::size_t index = 0);
static std::unique_ptr<Node> Create(const Point& p, std::size_t index = 0);
~Node();
void apply(const Transform& transform);
void associateEdge(std::size_t edgeId);
void associateFace(std::size_t faceId);
void addVectorAttribute(const std::string& tag, const std::vector<double>& values);
void clearConnectivity();
std::size_t getIndex() const;
std::size_t getNumConnectedEdges() const;
std::size_t getNumConnectedFaces() const;
std::size_t getConnectedEdgeId(std::size_t idx) const;
std::size_t getConnectedFaceId(std::size_t idx) const;
State getState() const;
const Point& getPoint() const;
std::vector<double> getVectorAttribute(const std::string& tag) const;
bool isCoincident(Node* node) const;
void setState(State state);
void setIndex(std::size_t idx);
void updateIndex(std::size_t index);
private:
std::unordered_map<std::string, std::vector<double> > mVectorAttributes;
std::size_t mIndex{0};
Point mPoint;
std::vector<std::size_t> mAssociatedEdgeIds;
std::vector<std::size_t> mAssociatedFaceIds;
State mState{State::HEALTHY};
};

View file

View file

@ -0,0 +1,8 @@
#pragma once
class Edge;
class QuadFace
{
};

View file

View file

View file

@ -0,0 +1,108 @@
#include "TriFace.h"
#include "Edge.h"
#include "Node.h"
TriFace::TriFace(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id)
: AbstractFace(id),
mEdge0(edge0),
mEdge1(edge1),
mEdge2(edge2)
{
}
std::unique_ptr<TriFace> TriFace::Create(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id)
{
return std::make_unique<TriFace>(edge0, edge1, edge2, id);
}
TriFace::~TriFace()
{
}
std::vector<std::size_t> TriFace::getNodeIds() const
{
return {mEdge0->getNode0Id(), mEdge0->getNode1Id(), mEdge1->getNode1Id()};
}
std::size_t TriFace::getNumNodes() const
{
return 3;
}
void TriFace::replaceEdge(Edge* original, Edge* replacement)
{
if (original == mEdge0)
{
mEdge0 = replacement;
}
else if (original == mEdge1)
{
mEdge1 = replacement;
}
else if (original == mEdge2)
{
mEdge2 = replacement;
}
}
std::size_t TriFace::getEdge0Id () const
{
return mEdge0->getId();
}
std::size_t TriFace::getEdge1Id () const
{
return mEdge1->getId();
}
std::size_t TriFace::getEdge2Id () const
{
return mEdge2->getId();
}
std::vector<std::size_t> TriFace::getEdgeIds() const
{
return {mEdge0->getId(), mEdge1->getId(), mEdge2->getId()};
}
void TriFace::associateWidthEdges()
{
mEdge0->associateFace(mId);
mEdge1->associateFace(mId);
mEdge2->associateFace(mId);
}
std::vector<Point> TriFace::getNodeLocations(Orientation orientation) const
{
if (orientation != getOrientation())
{
return { mEdge0->getNode0()->getPoint(), mEdge0->getNode1()->getPoint(), mEdge1->getNode1()->getPoint() };
}
else
{
return { mEdge0->getNode0()->getPoint(), mEdge1->getNode1()->getPoint(), mEdge0->getNode1()->getPoint() };
}
}
Vector TriFace::getNormal() const
{
auto v0 = mEdge0->getNode0()->getPoint().getDelta(mEdge0->getNode1()->getPoint());
auto v1 = mEdge0->getNode0()->getPoint().getDelta(mEdge1->getNode1()->getPoint());
return v0.crossProduct(v1).getNormalized();
}
AbstractFace::Orientation TriFace::getOrientation() const
{
Vector z_norm(0, 0, 1);
if (z_norm.dotProduct(getNormal()) < 0.0)
{
return Orientation::CW;
}
else
{
return Orientation::CCW;
}
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "AbstractFace.h"
#include "Vector.h"
class Edge;
class TriFace : public AbstractFace
{
public:
TriFace(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id=0);
~TriFace();
static std::unique_ptr<TriFace> Create(Edge* edge0, Edge* edge1, Edge* edge2, std::size_t id=0);
void associateWidthEdges() override;
std::vector<std::size_t> getNodeIds() const override;
std::size_t getNumNodes() const override;
void replaceEdge(Edge* original, Edge* replacement);
std::size_t getEdge0Id () const;
std::size_t getEdge1Id () const;
std::size_t getEdge2Id () const;
std::vector<std::size_t> getEdgeIds() const override;
std::vector<Point> getNodeLocations(Orientation orientation = Orientation::CCW) const override;
Vector getNormal() const;
AbstractFace::Orientation getOrientation() const;
private:
Edge* mEdge0{nullptr};
Edge* mEdge1{nullptr};
Edge* mEdge2{nullptr};
};

View file

@ -0,0 +1,46 @@
#include "TriMesh.h"
#include "Edge.h"
#include "TriFace.h"
TriMesh::~TriMesh()
{
}
AbstractMesh::MeshType TriMesh::getType() const
{
return AbstractMesh::MeshType::TRI;
}
std::unique_ptr<AbstractMesh> TriMesh::copy() const
{
VecNodes nodes(mNodes.size());
unsigned count = 0;
for (auto& node : mNodes)
{
nodes[count] = std::make_unique<Node>(node->getPoint());
count++;
}
VecEdges edges(mEdges.size());
count = 0;
for (auto& edge : mEdges)
{
edges[count] = std::make_unique<Edge>(nodes[edge->getNode0Id()].get(), nodes[edge->getNode0Id()].get());
count++;
}
VecFaces faces(mFaces.size());
count = 0;
for (auto& face : mFaces)
{
auto ids = face->getEdgeIds();
faces[count] = std::make_unique<TriFace>(edges[ids[0]].get(), edges[ids[1]].get(), edges[ids[2]].get());
count++;
}
auto mesh = std::make_unique<TriMesh>();
mesh->populate(nodes, edges, faces);
return mesh;
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "FaceMesh.h"
class TriMesh : public FaceMesh
{
public:
TriMesh() = default;
~TriMesh();
std::unique_ptr<AbstractMesh> copy() const override;
MeshType getType() const;
};

View file

@ -0,0 +1,46 @@
set(MODULE_NAME visual_elements)
list(APPEND visual_elements_LIB_INCLUDES
basic_shapes/RectangleNode.h
basic_shapes/RectangleNode.cpp
basic_shapes/CircleNode.h
basic_shapes/CircleNode.cpp
basic_shapes/LineNode.h
basic_shapes/LineNode.cpp
scene/Scene.h
scene/Scene.cpp
scene/SceneInfo.h
scene/SceneModel.h
scene/SceneModel.cpp
scene/SceneItem.h
scene/SceneItem.cpp
scene/SceneText.h
scene/SceneText.cpp
nodes/MaterialNode.h
nodes/MaterialNode.cpp
nodes/MeshNode.h
nodes/MeshNode.cpp
nodes/TextNode.h
nodes/TextNode.cpp
nodes/GridNode.h
nodes/GridNode.cpp
nodes/GeometryNode.h
nodes/GeometryNode.cpp
nodes/AbstractVisualNode.h
nodes/AbstractVisualNode.cpp
Texture.cpp
)
add_library(${MODULE_NAME} SHARED ${visual_elements_LIB_INCLUDES})
target_include_directories(${MODULE_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/basic_shapes
${CMAKE_CURRENT_SOURCE_DIR}/scene
${CMAKE_CURRENT_SOURCE_DIR}/nodes
)
target_link_libraries(${MODULE_NAME} PUBLIC core geometry fonts mesh image)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/rendering)
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -0,0 +1,29 @@
#pragma once
#include "Color.h"
#include "FontItem.h"
#include <vector>
class TextData
{
public:
bool operator==(const TextData& rhs) const
{
return (mContent == rhs.mContent)
&& (mLines == rhs.mLines)
&& (mFont == rhs.mFont);
}
bool operator!=(const TextData& rhs) const
{
return !operator==(rhs);
}
public:
TextData() = default;
std::string mContent;
std::vector<std::string> mLines;
FontItem mFont;
};

View file

@ -0,0 +1,7 @@
#pragma once
class Texture
{
public:
Texture() = default;
};

View file

@ -0,0 +1,64 @@
#include "CircleNode.h"
#include "SceneModel.h"
#include "AbstractMesh.h"
#include "MeshPrimitives.h"
#include "SceneInfo.h"
#include "Circle.h"
CircleNode::CircleNode(const Point& location, double radius)
: GeometryNode(location),
mRadius(radius)
{
mMinorRadius = mRadius;
}
CircleNode::Type CircleNode::getType()
{
return Type::Circle;
}
double CircleNode::getRadius() const
{
return mRadius;
}
void CircleNode::setRadius(double radius)
{
if (mRadius != radius)
{
mRadius = radius;
mTransformIsDirty = true;
}
}
void CircleNode::setMinorRadius(double radius)
{
if (mMinorRadius != radius)
{
mMinorRadius = radius;
mTransformIsDirty = true;
}
}
void CircleNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (sceneInfo->mSupportsGeometryPrimitives)
{
double unit_circle_radius = 0.5;
auto circle = std::make_unique<Circle>(Point{ 0, 0 }, unit_circle_radius);
circle->setMinorRadius(unit_circle_radius*mMinorRadius/mRadius);
mBackgroundItem = std::make_unique<SceneModel>(std::move(circle));
}
else
{
auto mesh = MeshPrimitives::buildCircleAsTriMesh();
mBackgroundItem = std::make_unique<SceneModel>(std::move(mesh));
}
mBackgroundItem->setName(mName + "_Model");
}
void CircleNode::updateTransform()
{
mBackgroundItem->updateTransform({ mLocation, 2 * mRadius, 2 * mRadius });
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "GeometryNode.h"
class CircleNode : public GeometryNode
{
public:
CircleNode(const Point& location, double radius);
Type getType() override;
double getRadius() const;
void setMinorRadius(double radius);
void setRadius(double radius);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo) override;
void updateTransform() override;
double mRadius{1.0};
double mMinorRadius{ 1.0 };
};

View file

@ -0,0 +1,28 @@
#include "LineNode.h"
LineNode::LineNode(const Point& location, const std::vector<Point>& points)
: GeometryNode(location),
mPoints(points)
{
}
LineNode::Type LineNode::getType()
{
return Type::Line;
}
void LineNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (sceneInfo->mSupportsGeometryPrimitives)
{
auto line = std::make_unique<Line>(Point{ 0, 0 }, mPoints);
mBackgroundItem = std::make_unique<SceneModel>(std::move(line));
}
mBackgroundItem->setName(mName + "_Model");
}
void LineNode::updateTransform()
{
mBackgroundItem->updateTransform({ mLocation });
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "GeometryNode.h"
#include "SceneInfo.h"
#include "Line.h"
#include <vector>
class LineNode : public GeometryNode
{
public:
LineNode(const Point& location, const std::vector<Point>& points);
Type getType();
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo) override;
void updateTransform() override;
std::vector<Point> mPoints;
};

View file

@ -0,0 +1,73 @@
#include "RectangleNode.h"
#include "Rectangle.h"
#include "MeshPrimitives.h"
#include "SceneInfo.h"
RectangleNode::RectangleNode(const Point& loc, double width, double height)
: GeometryNode(loc),
mWidth(width),
mHeight(height)
{
}
std::unique_ptr<RectangleNode> RectangleNode::Create(const Point& loc, double width, double height)
{
return std::make_unique<RectangleNode>(loc, width, height);
}
GeometryNode::Type RectangleNode::getType()
{
return GeometryNode::Type::Rectangle;
}
double RectangleNode::getWidth() const
{
return mWidth;
}
double RectangleNode::getHeight() const
{
return mHeight;
}
void RectangleNode::setWidth(double width)
{
if (mWidth != width)
{
mTransformIsDirty = true;
mWidth = width;
}
}
void RectangleNode::setHeight(double height)
{
if (mHeight != height)
{
mTransformIsDirty = true;
mHeight = height;
}
}
void RectangleNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (sceneInfo->mSupportsGeometryPrimitives)
{
auto rect = std::make_unique<ntk::Rectangle>(Point{ 0, 0 }, 1, 1);
mBackgroundItem = std::make_unique<SceneModel>(std::move(rect));
}
else
{
auto mesh = MeshPrimitives::buildRectangleAsTriMesh();
mBackgroundItem = std::make_unique<SceneModel>(std::move(mesh));
}
mBackgroundItem->setName(mName + "_Model");
}
void RectangleNode::updateTransform()
{
mBackgroundItem->updateTransform({ mLocation, mWidth, mHeight });
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "GeometryNode.h"
#include <memory>
class RectangleNode : public GeometryNode
{
public:
RectangleNode(const Point& loc, double width, double height);
static std::unique_ptr<RectangleNode> Create(const Point& loc, double width, double height);
GeometryNode::Type getType() override;
double getWidth() const;
double getHeight() const;
void setWidth(double width);
void setHeight(double height);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo) override;
void updateTransform() override;
double mWidth{1};
double mHeight{1};
};
using RectangleNodePtr = std::unique_ptr<RectangleNode>;

Some files were not shown because too many files have changed in this diff Show more