Compare commits

...
20 Commits
Author SHA1 Message Date
tobid7 dac78d9c82 yet another memory debugger fix 2026-01-11 16:12:47 +01:00
tobid7 a16bce419d Add safe Memory Freeing
(cause in some cases random crashes could happen)
2026-01-10 12:58:04 +01:00
tobid7 bed24ff28a Add Memory debugger 2026-01-04 18:02:45 +01:00
tobid7 1acecd03ee Merge branch 'main' of github.com:tobid7/amethyst 2026-01-01 21:37:05 +01:00
tobid7 99b330b136 # Compile time hexcolor support ported from palladium 2026-01-01 21:36:15 +01:00
tobid7 5e5907ed30 Rename to DrawFromAtlas 2025-12-31 10:30:20 +01:00
tobid7 b4b1144c60 Implemet CutTex Rendering 2025-12-30 22:28:03 +01:00
tobid7 6e502d73d8 Add Clearcolor support 2025-12-22 12:50:40 +01:00
tobid7 468a112274 Load Fonts in A8 format, Fix Freeze with BMF 2025-12-22 11:41:54 +01:00
tobid7 4cf3685832 Fix the shader loader
- fixed cmake file as well?
- remove useless files from example
- reenable runtime shader compiler

for some reason the raw data of a shader must stay in ram while the shader is loaded. It can be unloaded when the shader gets unloaded
2025-12-17 08:53:59 +01:00
tobid7 38a2b21ba0 Add BMF loading support
- Add ability to make textures not unloadable
2025-12-14 22:59:16 +01:00
tobid7 38a1cad76a Fix some issues 2025-12-08 22:20:33 +01:00
tobid7 d85610a0bd Add GTrace
Make Fonts an Asset
Add FormatMillis and FormatNanos
2025-12-08 18:37:12 +01:00
tobid7 88e367a299 Implement Text Rendering
Added STB truetype
Switched AssetMgr to use Shared Ptr
2025-12-07 23:20:15 +01:00
tobid7 28d2e291b3 Add color class
Add Palladium Unique and Shared defs
Make stb_image an internal dependency
Start some work on font loader
2025-12-07 18:08:36 +01:00
tobid7 c18ef2161a Remove ctru / Add pre font work 2025-12-05 19:33:58 +01:00
tobid7 d136e6ac5e - Add Exit func 2025-12-01 22:39:39 +01:00
tobid7 777b23f4c8 Get it finally working (somehow)
- Add working example
- Add Namespace defs for Iron Ctr and C3D
- Add Exit func / Vertex / Index counter to Iron
- Add format bytes function to utils
- Make stb_image optional
- Compute texture uv's
- Clear Everything in Drawlist::Clear
- Use Amy::mat4 as matrix lib
- Fix the most stupid rendering bug (i -> m_idx)
2025-12-01 22:10:18 +01:00
tobid7 078af99ae5 Upload all my tests
- Add Count<> to AssetManager
- Initialize pCode in Shader
- Rename linearAllocator to LinearAllocator
- Add Construct and destroy to linear allocator
- Add Shader unloader (fixes atexit crash i guess)
-
2025-11-30 21:57:01 +01:00
tobid7 febf506a5e # Changes
- Add Debugstuff to main
- Add IsType and AutoLoad to AssetManager
- Add ID class for at compile time hashing
- Add A8 to image (but not supported yet)
- Fix error in Vertex constructor
- Add PathClear to PathFill func
- Add pCheckSize to check for overflows
- Make Tex in Texture a pointer ref
- Add default uv to texture
- Add own c++ exception
- Add FNV32 Hash func (compile and runtime)
- Fix Power2 check in texture loader
- Load Shader manualy in iron (cause it seems not working correctly with files)
2025-11-27 13:32:44 +01:00
42 changed files with 7012 additions and 307 deletions
+322
View File
@@ -0,0 +1,322 @@
---
Language: Cpp
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AllowShortNamespacesOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: BinPack
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakBinaryOperations: Never
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: Yes
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExportBlock: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: true
KeepFormFeed: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakBeforeMemberAccess: 150
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: Always
RemoveBracesLLVM: false
RemoveEmptyLinesInUnwrappedLines: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
WrapNamespaceBodyWithEmptyLines: Leave
...
+23 -6
View File
@@ -5,31 +5,48 @@ project(amethyst)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED true)
option(AMY_BUILD_3DS "Build for 3ds" ON)
option(AMY_GOD_DEV "Turn this on if you think you are god" OFF)
# THis option should be disabled if you use STB_IMAGE in you main project
option(AMY_BUILD_STB_IMAGE ON)
option(AMY_BUILD_STB_TRUETYPE ON)
add_subdirectory(vendor/libpicasso)
add_library(${PROJECT_NAME} STATIC
source/app.cpp
source/assets.cpp
source/amethyst.cpp
source/color.cpp
source/godtrace.cpp
source/image.cpp
source/renderer.cpp
source/texture.cpp
source/utils.cpp
source/c3d.cpp
source/ctru.cpp
source/iron/iron.cpp
source/iron/drawlist.cpp
source/iron/font.cpp
source/maths/mat.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${DEVKITPRO}/portlibs/3ds/include
)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/source/internal
)
target_link_libraries(${PROJECT_NAME} PUBLIC pica::pica)
if(${AMY_BUILD_3DS})
target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d)
target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_3DS)
target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d mpg123)
if(${AMY_BUILD_STB_IMAGE})
target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_STB_IMAGE)
endif()
if(${AMY_BUILD_STB_TRUETYPE})
target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_STB_TT)
endif()
target_compile_options(${PROJECT_NAME} PUBLIC -Wno-psabi)
add_subdirectory(example)
install(TARGETS ${PROJECT_NAME})
install(DIRECTORY include DESTINATION .)
+26
View File
@@ -1,3 +1,29 @@
# amethyst
2d Renderer Library made for ReCraft3DS based on palladium.
## Usage
### Submodule:
Add amethyst as submodule to your repository
```bash
git submodule add https://github.com/tobid7/amethyst vendor/amethyst
```
Add it as submodule to your `CMakeLists.txt`
```cmake
add_subdirectory(vendor/amethyst)
target_link_libraries(${PROJECT_NAME} PUBLIC amethyst)
```
### Installed
Coming Soon...
## Credits
- [tobid7](https://github.com/tobid7) Lead Developer, Stealing a lot of code of [Palladium](https://github.com/tobid7/palladium)
- [nothings](https://github.com/nothings) stb_image and stb_truetype from [here](https://github.com/nothings/stb)
-18
View File
@@ -1,27 +1,9 @@
cmake_minimum_required(VERSION 3.22)
find_program(PICASSO NAMES picasso REQUIRED)
# Function origanally Created in Re-Craft-3DS
function(__amy_make_shader arg1 arg2)
# These only exist cause i was stupid
set(__FILE ${arg1})
set(__NAME ${arg2})
# Need to build shaders during config stage :(
execute_process(
COMMAND ${PICASSO} -o "${CMAKE_CURRENT_SOURCE_DIR}/romfs/shaders/${__NAME}.shbin" "${__FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endfunction()
project(amethyst-example)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED true)
__amy_make_shader(${CMAKE_CURRENT_SOURCE_DIR}/shaders/shader2d.v.pica shader2d)
__amy_make_shader(${CMAKE_CURRENT_SOURCE_DIR}/shaders/lithium.v.pica lithium)
add_executable(${PROJECT_NAME} source/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE amethyst)
target_compile_options(${PROJECT_NAME} PRIVATE -O2)
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014 The Comic Neue Project Authors (https://github.com/crozynski/comicneue)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

-34
View File
@@ -1,34 +0,0 @@
; LI7 Shader
; Constants
.constf myconst(0.0, 1.0, 0.00392156862745, 0.0)
.alias ones myconst.yyyy ; Vector full of ones
; Uniforms
.fvec projection[4]
; Outputs
.out out_position position
.out out_color color
.out out_uv texcoord0
; Inputs
.alias in_xy v0
.alias in_uvc v1
.alias in_col v2
.entry vmain
.proc vmain
mov r0.xy, in_xy.xy
mov r0.w, ones
dp4 out_position.x, projection[0], r0
dp4 out_position.y, projection[1], r0
dp4 out_position.z, projection[2], r0
dp4 out_position.w, projection[3], r0
mov out_uv, in_uvc.xy
mul r1, myconst.zzzz, in_col
mov out_color, r1
end
.end
-38
View File
@@ -1,38 +0,0 @@
; Example PICA200 vertex shader
; Uniforms
.fvec projection[4]
; Constants
.constf myconst(0.0, 1.0, -1.0, 0.1)
.constf myconst2(0.3, 0.0, 0.0, 0.0)
.alias zeros myconst.xxxx ; Vector full of zeros
.alias ones myconst.yyyy ; Vector full of ones
; Outputs
.out outpos position
.out outclr color
; Inputs (defined as aliases for convenience)
.alias inpos v0
.alias inclr v1
.bool test
.proc main
; Force the w component of inpos to be 1.0
mov r0.xyz, inpos
mov r0.w, ones
; outpos = projectionMatrix * inpos
dp4 outpos.x, projection[0], r0
dp4 outpos.y, projection[1], r0
dp4 outpos.z, projection[2], r0
dp4 outpos.w, projection[3], r0
; outclr = inclr
mov outclr, inclr
; We're finished
end
.end
+65 -34
View File
@@ -1,46 +1,77 @@
#include <amethyst.hpp>
#include <iostream>
#include "amethyst/iron.hpp"
class example : public Amy::App {
class Example : public Amy::App {
public:
example() {
Amy::Ctr::Init();
consoleInit(GFX_BOTTOM, NULL);
Amy::C3D::Init();
m_top = Amy::C3D::CreateScreen(GFX_TOP, GFX_LEFT);
Amy::Iron::Init();
dl = new Amy::Iron::Drawlist();
dl->DrawSolid();
};
~example() {
Amy::C3D::DeleteScreen(m_top);
Amy::C3D::Deinit();
};
void Main() override {
Amy::C3D::StartFrame();
m_top->Use();
m_top->Clear();
dl->DrawRectFilled(0, 50, 0xff00ff00);
Amy::Iron::NewFrame();
Amy::Iron::DrawOn(m_top);
Amy::Iron::Draw(*dl);
dl->Clear();
Amy::C3D::EndFrame();
std::cout << std::format("\x1b[1;1HDelta: {:.3f}\x1b[K", this->Delta());
std::cout << std::format("\x1b[2;1HTime: {:.2f}\x1b[K", this->Time());
Example() {
Amy::GTrace::Scope s("Init");
gfxInitDefault();
romfsInit();
C3D::Init();
Top = C3D::CreateScreen(GFX_TOP);
Mgr = new Amy::AssetMgr();
Iron::Init();
Mgr->AutoLoad("icon", "romfs:/icon.png");
auto fnt = Iron::Font::New();
fnt->LoadBMF("romfs:/ComicNeue.png");
Mgr->Add("font", fnt);
// Mgr->AutoLoad("font", "romfs:/ComicNeue.ttf");
dl = Iron::Drawlist::New();
dl->SetFont(Mgr->Get<Iron::Font>("font"));
}
private:
Amy::C3D::Screen* m_top = nullptr;
Amy::Iron::Drawlist* dl = nullptr;
~Example() {
dl.reset();
delete Top;
delete Mgr;
Iron::Exit();
C3D::Deinit();
romfsExit();
gfxExit();
}
void Main() {
hidScanInput();
if (hidKeysDown() & KEY_START) {
Exit();
}
std::string stats = std::format(
"Delta: {:.3} -> {:.3} FPS\nTime: {}\nLinearRam: {}\nVIT: "
"[{}/{}/{}]\nAssets: {}\nMain: {}",
this->Delta(), 1000.0 / this->Delta(),
Amy::Utils::FormatMillis(this->Time() * 1000.f),
Amy::Utils::FormatBytes(linearSpaceFree()), Amy::Iron::VerticesDrawn(),
Amy::Iron::IndicesDrawn(), Amy::Iron::IndicesDrawn() / 3,
Mgr->CountAll(),
Amy::Utils::FormatNanos(
Amy::GTrace::GetTraceRef("Main")->GetLastDiff()));
C3D::StartFrame();
Amy::GTrace::Beg("Main");
Top->Clear();
Top->Use();
dl->DrawTex(Mgr->Get<Amy::Texture>("icon"));
dl->DrawRectFilled(Amy::fvec2(50, 0), 48, Amy::Color(255, 255, 255, 160));
dl->DrawCircleFilled(Amy::fvec2(200, 120), 50, Amy::Color("#ffffff"), 40);
dl->DrawSolid();
dl->DrawRectFilled(0, 50, Amy::Color(0.f, 1.f, 0.f, 1.f));
dl->DrawText(Amy::fvec2(5, 50), stats, Amy::Color(255, 0, 255));
Iron::NewFrame();
Iron::DrawOn(Top);
Iron::Draw(dl->Data());
dl->Clear();
C3D::EndFrame();
Amy::GTrace::End("Main");
}
C3D::Screen* Top;
Amy::AssetMgr* Mgr;
Iron::Drawlist::Ref dl = nullptr;
};
int main() {
Amy::RegisterCxxExceptionHandler();
example app;
app.Run();
Example App;
App.Run();
return 0;
}
+26 -2
View File
@@ -3,11 +3,35 @@
#include <amethyst/app.hpp>
#include <amethyst/assets.hpp>
#include <amethyst/c3d.hpp>
#include <amethyst/ctru.hpp>
#include <amethyst/color.hpp>
#include <amethyst/godtrace.hpp>
#include <amethyst/image.hpp>
#include <amethyst/iron.hpp>
#include <amethyst/renderer.hpp>
#include <amethyst/texture.hpp>
#include <amethyst/utils.hpp>
#include <atomic>
namespace Amy {
void RegisterCxxExceptionHandler();
}
void* Malloc(size_t size);
void Free(void* ptr);
namespace Memory {
struct MemMetrics {
std::atomic<ull> Allocated = 0; ///< Total Allocated Memory
std::atomic<ull> Current = 0; ///< Current Allocated Memory
std::atomic<ull> Deleted = 0; ///< Total Deleted Memory
std::atomic<ull> Allocations = 0; ///< Current Allocations count
/// @brief Gets the Currently Allocated Memory
ull CurrentlyAllocated() { return Current; }
};
ull GetTotalAllocated();
ull GetTotalFreed();
ull GetCurrent();
ull GetAllocationCount();
} // namespace Memory
} // namespace Amy
using Iron = Amy::Iron;
using C3D = Amy::C3D;
using GTrace = Amy::GTrace;
+4
View File
@@ -2,6 +2,7 @@
#include <amethyst/asset.hpp>
#include <amethyst/types.hpp>
#include <amethyst/utils.hpp>
namespace Amy {
class App {
@@ -10,6 +11,7 @@ class App {
pDelta = 0.0;
pTime = 0.0;
pLast = 0LL;
pExit = false;
}
~App() {}
@@ -17,8 +19,10 @@ class App {
void Run();
double Delta() const { return pDelta; }
double Time() const { return pTime; }
void Exit() { pExit = true; }
private:
bool pExit;
ull pLast;
double pDelta;
double pTime;
+3
View File
@@ -1,9 +1,12 @@
#pragma once
#include <amethyst/types.hpp>
namespace Amy {
class Asset {
public:
Asset() = default;
virtual ~Asset() = default;
AMY_SHARED(Asset);
};
} // namespace Amy
+45 -10
View File
@@ -1,34 +1,69 @@
#pragma once
#include <amethyst/id.hpp>
#include <amethyst/texture.hpp>
#include <amethyst/types.hpp>
#include <memory>
namespace Amy {
class Assets {
class AssetMgr {
public:
Assets() = default;
~Assets() = default;
AssetMgr() = default;
~AssetMgr() = default;
void add(ksr id, Asset* v) { pAssets[id] = v; }
void remove(ksr id) {
void AutoLoad(const ID& name, ksr path);
void Add(const ID& id, Asset::Ref v) {
if (pAssets.count(id)) {
throw std::runtime_error("[amy]: assets: " + id.GetName() +
" already exists!");
}
pAssets[id] = v;
}
void Remove(const ID& id) {
if (pAssets.count(id)) {
pAssets.erase(id);
}
}
template <typename T>
T* get(ksr id) {
std::shared_ptr<T> Get(const ID& id) {
auto r = pAssets.find(id);
if (r == pAssets.end()) {
throw std::runtime_error("[amy] assets: unable to find " + id);
throw std::runtime_error("[amy] assets: unable to find " + id.GetName());
}
if (auto v = dynamic_cast<T*>(r->second)) {
if (auto v = std::dynamic_pointer_cast<T>(r->second)) {
return v;
} else {
throw std::runtime_error(id + " is not of type " + typeid(T).name());
throw std::runtime_error(id.GetName() + " is not of type " +
typeid(T).name());
}
}
template <typename T>
bool IsType(const ID& id) {
auto r = pAssets.find(id);
if (r == pAssets.end()) {
throw std::runtime_error("[amy] assets: unable to find " + id.GetName());
}
return std::dynamic_pointer_cast<T>(r->second) != nullptr;
}
size_t CountAll() const { return pAssets.size(); }
template <typename T>
size_t Count() const {
size_t ret = 0;
for (auto& it : pAssets) {
if (std::dynamic_pointer_cast<T>(it.second)) {
ret++;
}
}
return ret;
}
private:
std::map<str, Asset*> pAssets;
std::map<ID, Asset::Ref> pAssets;
};
} // namespace Amy
+8 -3
View File
@@ -28,8 +28,11 @@ class C3D {
int Width() const { return pWidth; }
int Height() const { return pHeight; }
ivec2 Size() const { return ivec2(pWidth, pHeight); }
void Clear() { C3D_RenderTargetClear(pTarget, C3D_CLEAR_ALL, 0, 0); }
void Clear(ui clr = 0x0, ui depth_clr = 0x0) {
C3D_RenderTargetClear(pTarget, C3D_CLEAR_ALL, clr, depth_clr);
}
void Use() { C3D_FrameDrawOn(pTarget); }
C3D_RenderTarget* Ptr() { return pTarget; }
private:
C3D_RenderTarget* pTarget = nullptr;
@@ -42,6 +45,7 @@ class C3D {
Shader(ksr path);
Shader() {}
~Shader();
AMY_SHARED(Shader);
void Load(ksr path);
void Load(kvr<uc> data);
@@ -53,11 +57,12 @@ class C3D {
void SetMat4(int loc, const mat4& m);
int loc(ksr name);
private:
// private:
C3D_AttrInfo pInfo;
shaderProgram_s pProgram;
DVLB_s* pCode;
DVLB_s* pCode = nullptr;
int pReg = 0;
std::vector<uc> pRawData;
};
class Frag {
+124
View File
@@ -0,0 +1,124 @@
#pragma once
#include <amethyst/types.hpp>
#include <amethyst/utils.hpp>
// We all know where the code is from ... RenderD7 -> Palladium/Amy
namespace Amy {
class Color {
public:
/**
* Default Constructor (all variables are set to 0)
*/
constexpr Color() : r(0), g(0), b(0), a(0) {}
constexpr ~Color() {}
/**
* Constructor for 32Bit Color Input
* @param color 32Bit Color value
*/
constexpr Color(ui clr) {
a = (clr >> 24) & 0xff;
b = (clr >> 16) & 0xff;
g = (clr >> 8) & 0xff;
r = clr & 0xff;
}
/**
* Constructor for 8Bit Input
* @param r Red Value
* @param g Green Value
* @param b Blue Value
* @param a Optional Alpha Value (Defaults to 255)
*/
constexpr Color(int r, int g, int b, int a = 255) : r(r), g(g), b(b), a(a) {}
/**
* Constructor for float Input
* @param r Red Value
* @param g Green Value
* @param b Blue Value
* @param a Optional Alpha Value (Defaults to 1.0f)
* @note There is no Check if the number is between 0.0 and 1.0
*/
constexpr Color(float r, float g, float b, float a = 1.f)
: r(static_cast<uc>(255.f * r)),
g(static_cast<uc>(255.f * g)),
b(static_cast<uc>(255.f * b)),
a(static_cast<uc>(255.f * a)) {}
/**
* Constructor for Hex Input
* @param hex Hex String in `#ffffff` or `#ffffffff` format
*/
constexpr Color(const std::string& hex) { Hex(hex); }
/**
* Create Color Object by Hex String
* @param hex Hex String in `#ffffff` or `#ffffffff` format
* @return Color class itself
*/
constexpr Color& Hex(const std::string& hex) {
if (!(hex.length() == 7 || hex.length() == 9)) {
throw "[PD] Color: hex string is not rgb or rgba!";
}
r = Utils::HexChar2Int(hex[1]) * 16 + Utils::HexChar2Int(hex[2]);
g = Utils::HexChar2Int(hex[3]) * 16 + Utils::HexChar2Int(hex[4]);
b = Utils::HexChar2Int(hex[5]) * 16 + Utils::HexChar2Int(hex[6]);
if (hex.length() == 9) {
a = Utils::HexChar2Int(hex[7]) * 16 + Utils::HexChar2Int(hex[8]);
} else {
a = 255;
}
return *this;
}
/**
* Convert this Color Object to Hex string
* @param rgba [default false] sets if 8 or 6 digit color should be returned
* @return Color Hex String
*/
std::string Hex(bool rgba = false) const;
/**
* Fade from Current to another Color
* @param color Color to fade to
* @param p Amount (supports -1.0 to 1.0 for use of sine)
* @return Class Reference
*/
constexpr Color& Fade(const Color& color, float p) {
a = static_cast<uc>((color.a - a) * ((p + 1.f) / 2));
b = static_cast<uc>((color.b - b) * ((p + 1.f) / 2));
g = static_cast<uc>((color.g - g) * ((p + 1.f) / 2));
r = static_cast<uc>((color.r - r) * ((p + 1.f) / 2));
return *this;
}
/**
* Get 32Bit Color Value
* @return 32Bit Color Value (ABGR iirc)
*/
constexpr ui Get() const { return (a << 24) | (b << 16) | (g << 8) | r; }
/**
* Get The Luminance of the Color
* @return luminance (from 0.0 to 1.0)
*/
constexpr float Luminance() const {
// For Reference https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness
return (0.3 * (r / 255.f) + 0.59 * (g / 255.f) + 0.11 * (b / 255.f));
}
/**
* Check if the Color is Light or Dark
* @return true if light
*/
constexpr bool IsLight() const { return (Luminance() >= 0.5); }
/**
* Operator to cast Color to 32Bit Value
* @return 32Bit Color Value
*/
constexpr operator ui() const { return Get(); }
/** Public Access Data section */
uc r;
uc g;
uc b;
uc a;
};
} // namespace Amy
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include <amethyst/types.hpp>
namespace Amy {
namespace Ctr {
enum Services {
Romfs = 1 << 0,
Cfgu = 1 << 1,
Gfx = 1 << 2,
GfxDefault = 1 << 3,
Default = Romfs | GfxDefault,
};
void Init(ui srv = Default);
ull GetTime();
} // namespace Ctr
} // namespace Amy
+200 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <amethyst/types.hpp>
#include <amethyst/id.hpp>
namespace Amy {
class GTrace {
@@ -8,6 +9,204 @@ class GTrace {
GTrace() = default;
~GTrace() = default;
class Stats {
public:
/**
* Constructor taking a lengh for the List
* @param l Lengh of the data list
*/
Stats(int l) : len(l), val(l, 0) {}
~Stats() = default;
AMY_SHARED(Stats);
/**
* Add a New Value to the list
* @param v value to add
*/
void Add(ull v) {
val[idx] = v;
idx = next(idx);
num_val = std::min(num_val + 1, len);
}
/**
* Get Avarage Num
* @return Average
*/
ull GetAverage() {
if (!num_val) return 0.f;
ull res = 0;
for (int i = 0; i < num_val; i++) {
res += val[smart_idx(i)];
}
return res / num_val;
}
/**
* Get Minimum Num
* @return Minimum value
*/
ull GetMin() {
if (!num_val) return 0.f;
ull res = std::numeric_limits<ull>::max();
for (int i = 0; i < num_val; i++) {
res = std::min(val[smart_idx(i)], res);
}
return res;
}
/**
* Get Maximum Value
* @return Max Value
*/
ull GetMax() {
if (!num_val) return 0.f;
ull res = 0;
for (int i = 0; i < num_val; i++) {
res = std::max(val[smart_idx(i)], res);
}
return res;
}
/**
* Clear the List
*/
void Clear() {
val.assign(len, 0);
idx = 0;
num_val = 0;
}
/**
* Get Data Buffer
* @return data bufer (not edidable)
*/
const std::vector<ull>& GetData() { return val; }
/**
* Access an element in the list [not edidable]
* @return value to access
*/
const ull& operator[](int i) { return val[smart_idx(i)]; }
/**
* Get List Lengh
* @return Lengh
*/
const size_t GetLen() { return len; }
/**
* Get Number of Values
* @return number of values
*/
const size_t GetNumValues() { return num_val; }
private:
/**
* Get the Next Position to write to
* @param c current position
* @return next position
*/
size_t next(size_t c) const { return (c + 1) % len; }
/**
* Smart Indexing in for loops to make sure to
* not index a value that was not set yet
* @param v pos in for loop
* @return indexing pos
*/
size_t smart_idx(size_t v) const { return (idx + len - num_val + v) % len; }
/** Lengh of the list */
int len = 0;
/** Value Storage */
std::vector<ull> val;
int idx = 0;
int num_val = 0;
};
class Trace {
public:
Trace() { protocol = Stats::New(60); }
~Trace() = default;
AMY_SHARED(Trace);
void SetID(ksr v) { Name = v; }
kstr GetID() { return Name; }
void SetStart(ull v) { Start = v; }
ull GetStart() { return Start; }
void SetEnd(ull v) {
End = v;
Diff = End - Start;
protocol->Add(GetLastDiff());
}
ull GetEnd() { return End; }
ull GetLastDiff() { return Diff; }
Stats::Ref GetProtocol() { return protocol; }
private:
str Name;
ull Start = 0;
ull End = 0;
ull Diff = 0;
Stats::Ref protocol;
};
/**
* Begin a Trace
* @param id Name of the Trace
*/
static void Beg(ksr id);
/**
* End a Trace
* @param id Name of the Trace
*/
static void End(ksr id);
/**
* Collect Start end end of the trace by tracking
* when the Scope object goes out of scope
*
* Example:
* ```cpp
* void SomeFunction() {
* // Create a Scoped Trace called "SomeFunc"
* PD::TT::Scope st("SomeFunc");
* // Do your functions stuff
* // End at the end it goes out of
* // scope which collects the end time
* }
* ```
*/
class Scope {
public:
/**
* Constructor requiring a Name for the Trace
* @param id Name of the Trace
*/
Scope(ksr id) {
this->ID = id;
GTrace::Beg(id);
}
/**
* Deconstructor getting the end time when going out of scope
*/
~Scope() { GTrace::End(ID); }
private:
/** Trace Name/ID */
str ID;
};
using TraceMap = std::unordered_map<std::string, Trace::Ref>;
static TraceMap& GetTraceMap() { return GetTraceMap(); }
static Trace::Ref& GetTraceRef(ksr id) {
if (!pTraces.count(id)) {
pTraces[id] = Trace::New();
pTraces[id]->SetID(id);
}
return pTraces[id];
}
static bool TraceExist(ksr id) { return pTraces.count(id); }
private:
static TraceMap pTraces;
};
} // namespace Amy
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <amethyst/types.hpp>
#include <amethyst/utils.hpp>
namespace Amy {
class ID {
public:
constexpr ID(const ui& id) { pID = id; }
constexpr ID(std::string name) {
pID = Utils::FNV32(name);
#ifdef AMY_KEEP_STR_ID
pName = name;
#endif
}
constexpr ID(const char* name) {
pID = Utils::FNV32(name);
#ifdef AMY_KEEP_STR_ID
pName = name;
#endif
}
~ID() {}
ui GetID() { return pID; }
std::string GetName() const {
#ifdef AMY_KEEP_STR_ID
return pName;
#else
return std::format("hash({:#08x})", pID);
#endif
}
operator ui() const { return pID; }
operator std::string() const { return GetName(); }
private:
ui pID;
#ifdef AMY_KEEP_STR_ID
str pName;
#endif
};
} // namespace Amy
+1
View File
@@ -12,6 +12,7 @@ class Image {
BGR, // bpp == 3
ABGR, // bpp == 4
BGRA, // bpp == 4
A8, // bpp == 1 (not supported in laoding)
};
Image() = default;
Image(ksr path) { this->Load(path); }
+96 -22
View File
@@ -8,6 +8,17 @@
#include <amethyst/texture.hpp>
#include <amethyst/types.hpp>
using AmyTextFlags = Amy::ui;
enum AmyTextFlags_ {
AmyTextFlags_None = 0, ///< Do nothing
AmyTextFlags_AlignRight = 1 << 0, ///< Align Right of position
AmyTextFlags_AlignMid = 1 << 1, ///< Align in the middle of pos and box
AmyTextFlags_Shaddow = 1 << 2, ///< Draws the text twice to create shaddow
AmyTextFlags_Wrap = 1 << 3, ///< Wrap Text: May be runs better with TMS
AmyTextFlags_Short = 1 << 4, ///< Short Text: May be runs better with TMS
AmyTextFlags_Scroll = 1 << 5, ///< Not implemented [scoll text if to long]
};
namespace Amy {
class Iron {
public:
@@ -15,8 +26,8 @@ class Iron {
Vertex(float x, float y, float u, float v, ui clr) {
pos.x = x;
pos.y = y;
uv.x = x;
uv.y = y;
uv.x = u;
uv.y = v;
color = clr;
}
Vertex(const fvec2& pos, const fvec2& uv, ui clr) {
@@ -33,7 +44,9 @@ class Iron {
class Command {
public:
Command() = default;
using ref = up<Command>;
~Command() = default;
AMY_UNIQUE(Command)
Command& Add(const u16& idx) {
IndexBuf.push_back(VertexBuf.size() + idx);
return *this;
@@ -47,14 +60,52 @@ class Iron {
std::vector<u16> IndexBuf;
ivec4 ScissorRect;
bool ScissorOn = false;
int Layer;
int Index;
Texture* Tex;
int Layer = 0;
int Index = 0;
Texture::Ref Tex = nullptr;
};
class Font : public Asset {
public:
struct Codepoint {
ui Cp = 0;
fvec4 Uv;
Texture::Ref Tex = nullptr;
fvec2 Size;
float Offset = 0; // Unused??
bool Valid = true;
};
Font() = default;
~Font() = default;
AMY_SHARED(Font)
void LoadTTF(ksr path, int pxh = 32);
void LoadTTF(kvr<uc> data, int pxh = 32);
/**
* Bitmap Font BTW
*/
void LoadBMF(ksr path);
Codepoint& GetCodepoint(ui c);
fvec2 GetTextBounds(ksr text, float scale);
void CmdTextEx(vec<Command::Ref>& cmds, const fvec2& pos, ui color,
float scale, ksr text, AmyTextFlags flags = 0,
const fvec2& box = 0);
void pMakeAtlas(bool final, vec<uc>& pixels, int size, Texture::Ref tex);
int PxHeight;
int PxFactor = 24;
vec<Texture::Ref> Textures;
std::map<u32, Codepoint> pCodeMap;
};
class Drawlist {
public:
Drawlist() { DrawSolid(); }
~Drawlist() { pData.clear(); }
AMY_SHARED(Drawlist)
// required due to memory management
Drawlist(const Drawlist&) = delete;
@@ -62,16 +113,27 @@ class Iron {
Drawlist(Drawlist&&) noexcept = default;
Drawlist& operator=(Drawlist&&) noexcept = default;
std::vector<Command::ref>& Data() { return pData; }
std::vector<Command::Ref>& Data() { return pData; }
void Merge(Drawlist* list);
Command::ref NewCommand();
void Push(Command ::ref cmd);
Command::Ref NewCommand();
void Push(Command ::Ref cmd);
void Clear();
void DrawSolid();
void DrawTex(Texture* tex) { pTex = tex; }
void DrawTex(Texture::Ref tex) { pTex = tex; }
void DrawFromAtlas(fvec2 pos, fvec2 size, fvec4 cut_rect,
ui color = 0xffffffff);
void DrawFromAtlas(fvec2 pos, fvec2 size, fvec4 cut_rect, Texture::Ref tex,
ui color = 0xffffffff) {
DrawTex(tex);
DrawFromAtlas(pos, size, cut_rect, color);
DrawSolid();
}
void SetFont(Font::Ref fnt) { pCurrentFont = fnt; }
void SetFontScale(float v) { pFontScale = v; }
float GetFontScale() { return pFontScale; }
void DefaultFontScale() { pFontScale = 0.7; }
/** Draw Api */
void DrawRect(const fvec2& pos, const fvec2& size, ui color,
int thickness = 1);
@@ -100,7 +162,10 @@ class Iron {
DrawPolyLine(pPath, color, flags, thickness);
PathClear();
}
void PathFill(ui color) { DrawConvexPolyFilled(pPath, color); }
void PathFill(ui color) {
DrawConvexPolyFilled(pPath, color);
PathClear();
}
void PathArcToN(const fvec2& c, float radius, float a_min, float a_max,
int segments);
void PathFastArcToN(const fvec2& c, float radius, float a_min, float a_max,
@@ -113,24 +178,27 @@ class Iron {
if (!ClipRects.empty()) ClipRects.pop();
}
operator std::vector<Command::ref>&() { return pData; }
operator std::vector<Command::Ref>&() { return pData; }
private:
void clipCmd(Command* ptr);
std::vector<Command::ref> pData;
std::vector<Command::Ref> pData;
std::vector<fvec2> pPath;
Texture* pTex = nullptr;
Texture::Ref pTex = nullptr;
std::stack<fvec4> ClipRects;
Font::Ref pCurrentFont;
int pLayer = 0;
float pFontScale = 0.7;
};
Iron() = default;
~Iron() = default;
static void Init();
static void Exit();
static void NewFrame();
static void DrawOn(C3D::Screen* screen);
static void Draw(const std::vector<Command::ref>& data);
static Texture* WhiteTex() { return m_solid; }
static void Draw(const std::vector<Command::Ref>& data);
static Texture::Ref WhiteTex() { return m_solid; }
/** Static renderer utility funcs */
@@ -142,23 +210,29 @@ class Iron {
const fvec2& c, ui clr);
static void CmdConvexPolyFilled(Command* cmd,
const std::vector<fvec2>& points, ui clr,
Texture* tex);
Texture::Ref tex);
static bool InBox(const fvec2& pos, const fvec2& size, const fvec4& area);
static bool InBox(const fvec2& pos, const fvec4& area);
static bool InBox(const fvec2& a, const fvec2& b, const fvec2& c,
const fvec4& area);
static ui VerticesDrawn() { return VertexCount; }
static ui IndicesDrawn() { return IndexCount; }
private:
static void pSetupShader();
static void pFragConfig();
static void pFragConfig(GPU_TEXCOLOR clr);
static void pInitSolidTex();
static bool pCheckSize(size_t idx, size_t vtx);
static std::vector<Vertex, linearAllocator<Vertex>> m_vbuf;
static std::vector<u16, linearAllocator<u16>> m_ibuf;
static std::vector<Vertex, LinearAllocator<Vertex>> m_vbuf;
static std::vector<u16, LinearAllocator<u16>> m_ibuf;
static int uLocProj;
static C3D::Shader* m_shader;
static mat4 m_mtx;
static int m_idx, m_vtx;
static Texture* m_solid;
static Texture::Ref m_solid;
static ui VertexCount;
static ui IndexCount;
};
} // namespace Amy
+17 -7
View File
@@ -11,25 +11,35 @@
namespace Amy {
template <typename T>
class linearAllocator {
class LinearAllocator {
public:
using value_type = T;
linearAllocator() = default;
LinearAllocator() noexcept = default;
template <typename U>
constexpr linearAllocator(const linearAllocator<U>&) noexcept {}
constexpr LinearAllocator(const LinearAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
if (n > std::allocator_traits<linearAllocator>::max_size(*this)) {
throw std::bad_alloc();
if (n > max_size()) {
throw std::runtime_error("[Amy] LinearAllocator: Bad alloc!");
}
return static_cast<T*>(linearAlloc(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) noexcept { linearFree(p); }
friend bool operator==(const linearAllocator, const linearAllocator) {
template <class U, class... Args>
void construct(U* p, Args&&... args) {
::new ((void*)p) U(std::forward<Args>(args)...);
}
template <class U>
void destroy(U* p) {
p->~U();
}
friend bool operator==(const LinearAllocator, const LinearAllocator) {
return true;
}
friend bool operator!=(const linearAllocator, const linearAllocator) {
friend bool operator!=(const LinearAllocator, const LinearAllocator) {
return false;
}
+17 -3
View File
@@ -12,11 +12,25 @@ class Texture : public Asset {
public:
Texture() = default;
Texture(ksr path);
Texture(C3D_Tex* tex, const ivec2& size, const Rect& uv) {
Load(tex, size, uv);
}
~Texture();
AMY_SHARED(Texture);
void Load(ksr path);
void Load(kvr<uc> pixels, int w, int h, int bpp = 4,
Image::Format fmt = Image::RGBA);
void Load(C3D_Tex* tex, const ivec2& size, const Rect& uv) {
Unload();
pTex = tex;
pSize = size;
pUv = uv;
// Dont set as loaded as only the root tex can be loaded
}
void Unload();
void Unloadable(bool v) { pLoaded = v; }
bool Unloadable() { return pLoaded; }
int W() const { return pSize.x; }
int& W() { return pSize.x; }
@@ -26,14 +40,14 @@ class Texture : public Asset {
ivec2& Size() { return pSize; }
Rect& Uv() { return pUv; }
C3D_Tex* Ptr() { return pLoaded ? &pTex : nullptr; }
C3D_Tex* Ptr() { return pTex; }
void Bind(int reg = 0);
private:
C3D_Tex pTex;
C3D_Tex* pTex = nullptr;
ivec2 pSize;
Rect pUv;
Rect pUv = fvec4(0, 1, 1, 0);
bool pLoaded = false;
};
} // namespace Amy
+15
View File
@@ -4,6 +4,7 @@
#include <amethyst/maths/vec.hpp>
#include <chrono>
#include <cinttypes>
#include <format>
#include <functional>
#include <map>
#include <memory>
@@ -13,6 +14,20 @@
#include <string>
#include <vector>
#define AMY_SHARED(x) \
using Ref = std::shared_ptr<x>; \
template <typename... Args> \
static Ref New(Args&&... args) { \
return std::make_shared<x>(std::forward<Args>(args)...); \
}
#define AMY_UNIQUE(x) \
using Ref = std::unique_ptr<x>; \
template <typename... Args> \
static Ref New(Args&&... args) { \
return std::make_unique<x>(std::forward<Args>(args)...); \
}
namespace Amy {
using uc = unsigned char;
using us = unsigned short;
+45 -1
View File
@@ -3,15 +3,59 @@
#include <amethyst/types.hpp>
namespace Amy {
class Error : public std::runtime_error {
public:
Error(ksr str) : std::runtime_error("[amy] " + str) {}
};
namespace Utils {
ui FastHash(ksr s);
constexpr int HexChar2Int(char c) {
/** Imagine man hat ne lookup table dafür verwendet :/ */
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
return -1; // Error
}
vec<uc> LoadFile2Mem(ksr path);
str FormatBytes(ull bytes);
ui HashMemory(kvr<uc> data);
ui NextPow2(ui v);
bool IsSingleBitNum(ui v);
ull GetTimeNano();
ull GetTimeMicro();
ull GetTimeMilli();
void String2U16(us* res, ksr src, size_t max);
std::string U16toU8(us* in, size_t max);
/**
* Generate a Formatted String by an Nanoseconds Input
* @param nanos Nanoseconds Input
* @return Result String
*/
kstr FormatNanos(ull nanos);
/**
* Generate a Formatted String by an Milliseconds Input
* @param millis Milliseconds Input
* @return Result String
*/
kstr FormatMillis(ull millis);
/**
* Create a formatted String by an input bytes value
* @param bytes value in bytes
* @result Formatted String for example `2.5MB`
*/
/**
* FNV Hash functiom (32 Bit)
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
* Able to run at compile time btw
*/
constexpr ui FNV32(std::string_view str) {
ui ret = 0x811C9DC5;
for (char it : str) {
ret ^= static_cast<ui>(it);
ret *= 16777619;
}
return ret;
}
namespace Image {
void ReverseBuf(vec<uc>& buf, int w, int h, int c);
void RemoveAlphaChannel(vec<uc>& buf, int w, int h);
+120
View File
@@ -1,6 +1,10 @@
#include <amethyst.hpp>
#include <exception>
#include <iostream>
#include <map>
#include <vector>
#define TRACK_NEW_DELETE
#ifdef AMY_3DS
#include <3ds.h>
@@ -26,10 +30,126 @@ void NpiD7CxxExceptionHandler() {
}
#endif
static Amy::Memory::MemMetrics metrics;
#ifdef TRACK_NEW_DELETE
constexpr uint32_t MemDBGMagic = 0x774733d7;
struct MemDBGHeader {
MemDBGHeader(size_t s) {
Magic = MemDBGMagic;
Size = s;
}
uint32_t Magic = MemDBGMagic;
size_t Size = 0;
};
void *MemDBGAllocate(size_t size) {
void *ptr = malloc(size + sizeof(MemDBGHeader));
if (!ptr) return reinterpret_cast<void *>(ptr);
MemDBGHeader *hdr = reinterpret_cast<MemDBGHeader *>(ptr);
hdr->Magic = MemDBGMagic;
hdr->Size = size;
metrics.Allocated.fetch_add(hdr->Size, std::memory_order_relaxed);
metrics.Current.fetch_add(hdr->Size, std::memory_order_relaxed);
metrics.Allocations.fetch_add(1, std::memory_order_relaxed);
return reinterpret_cast<void *>(hdr + 1);
}
void MemDBGFree(void *ptr) {
if (!ptr) return;
MemDBGHeader *hdr = reinterpret_cast<MemDBGHeader *>(ptr) - 1;
if (hdr->Magic == MemDBGMagic) {
hdr->Magic = 0;
size_t szs = hdr->Size;
metrics.Deleted.fetch_add(szs, std::memory_order_relaxed);
metrics.Current.fetch_sub(szs, std::memory_order_relaxed);
metrics.Allocations.fetch_sub(1, std::memory_order_relaxed);
free(reinterpret_cast<void *>(hdr));
} else {
std::abort();
}
}
void *operator new(size_t size) { return MemDBGAllocate(size); }
void *operator new[](size_t size) { return MemDBGAllocate(size); }
void operator delete(void *ptr) noexcept { MemDBGFree(ptr); }
void operator delete(void *ptr, size_t) noexcept { MemDBGFree(ptr); }
void operator delete[](void *ptr) noexcept { MemDBGFree(ptr); }
void operator delete[](void *ptr, size_t) noexcept { MemDBGFree(ptr); }
void *operator new(size_t size, const std::nothrow_t &) noexcept {
return MemDBGAllocate(size);
}
void *operator new[](size_t size, const std::nothrow_t &) noexcept {
return MemDBGAllocate(size);
}
void operator delete(void *ptr, const std::nothrow_t &) noexcept {
MemDBGFree(ptr);
}
void operator delete[](void *ptr, const std::nothrow_t &) noexcept {
MemDBGFree(ptr);
}
void *operator new(size_t size, std::align_val_t) {
return MemDBGAllocate(size);
}
void *operator new[](size_t size, std::align_val_t) {
return MemDBGAllocate(size);
}
void operator delete(void *ptr, std::align_val_t) noexcept { MemDBGFree(ptr); }
void operator delete[](void *ptr, std::align_val_t) noexcept {
MemDBGFree(ptr);
}
void *operator new(size_t size, std::align_val_t,
const std::nothrow_t &) noexcept {
return MemDBGAllocate(size);
}
void *operator new[](size_t size, std::align_val_t,
const std::nothrow_t &) noexcept {
return MemDBGAllocate(size);
}
void operator delete(void *ptr, size_t, std::align_val_t) noexcept {
MemDBGFree(ptr);
}
void operator delete[](void *ptr, size_t, std::align_val_t) noexcept {
MemDBGFree(ptr);
}
void operator delete(void *ptr, size_t, std::align_val_t,
const std::nothrow_t &) noexcept {
MemDBGFree(ptr);
}
void operator delete[](void *ptr, size_t, std::align_val_t,
const std::nothrow_t &) noexcept {
MemDBGFree(ptr);
}
#endif
namespace Amy {
void RegisterCxxExceptionHandler() {
#ifdef AMY_3DS
std::set_terminate(NpiD7CxxExceptionHandler);
#endif
}
void *Malloc(size_t size) {
#ifdef TRACK_NEW_DELETE
return MemDBGAllocate(size);
#else
return malloc(size);
#endif
}
void Free(void *ptr) {
#ifdef TRACK_NEW_DELETE
MemDBGFree(ptr);
#else
free(ptr);
#endif
}
ull Memory::GetTotalAllocated() { return metrics.Allocated; }
ull Memory::GetTotalFreed() { return metrics.Deleted; }
ull Memory::GetCurrent() { return metrics.CurrentlyAllocated(); }
ull Memory::GetAllocationCount() { return metrics.Allocations; }
} // namespace Amy
+1 -3
View File
@@ -1,14 +1,12 @@
#include <3ds.h>
#include <amethyst/app.hpp>
#include <amethyst/ctru.hpp>
#include <amethyst/utils.hpp>
namespace Amy {
void App::Run() {
pLast = Utils::GetTimeNano();
consoleInit(GFX_BOTTOM, NULL);
while (aptMainLoop()) {
while (aptMainLoop() && !pExit) {
ull c = Utils::GetTimeNano();
pDelta = static_cast<double>(static_cast<double>(c) -
static_cast<double>(pLast)) *
+26
View File
@@ -0,0 +1,26 @@
#include <amethyst/assets.hpp>
#include <amethyst/c3d.hpp>
#include <amethyst/iron.hpp>
#include <amethyst/texture.hpp>
namespace Amy {
void AssetMgr::AutoLoad(const ID& id, ksr path) {
if (path.ends_with(".png") || path.ends_with(".jpg") ||
path.ends_with(".bmp")) {
auto tex = Texture::New();
tex->Load(path);
Add(id, tex);
} else if (path.ends_with(".shbin")) {
auto shader = C3D::Shader::New();
shader->Load(path);
Add(id, shader);
} else if (path.ends_with(".ttf")) {
auto font = Iron::Font::New();
font->LoadTTF(path);
Add(id, font);
} else {
throw std::runtime_error("[amy]: assets: " + id.GetName() + " (" + path +
") is unsupported for AssetMgr::AutoLoad!");
}
}
} // namespace Amy
+16 -9
View File
@@ -40,36 +40,43 @@ void C3D::DeleteScreen(C3D::Screen* screen) { delete screen; }
C3D::Shader::Shader(const std::string& path) { Load(path); }
C3D::Shader::~Shader() {}
C3D::Shader::~Shader() {
if (pCode) {
shaderProgramFree(&pProgram);
DVLB_Free(pCode);
pCode = nullptr;
}
}
void C3D::Shader::Load(const std::string& path) {
auto code = Utils::LoadFile2Mem(path);
if (!code.size()) {
throw std::runtime_error(
std::format("[amy] unsable to load shader ({})", path));
std::format("[amy] unable to load shader ({})", path));
}
Load(code);
}
void C3D::Shader::Load(const std::vector<uc>& data) {
pCode = DVLB_ParseFile((u32*)&data[0], data.size());
if (!data.size()) {
throw std::runtime_error("[amy] unable to load shader from data!");
}
pRawData = data;
pCode = DVLB_ParseFile((u32*)&pRawData[0], pRawData.size());
shaderProgramInit(&pProgram);
shaderProgramSetVsh(&pProgram, &pCode->DVLE[0]);
C3D_BindProgram(&pProgram);
AttrInfo_Init(&pInfo);
}
void C3D::Shader::Compile(const std::string& code) {
auto ret = Pica::AssembleCode(code.c_str());
Load(ret);
Load(Pica::AssembleCode(code.c_str()));
}
void C3D::Shader::Use() {
C3D_BindProgram(&pProgram);
// for some reason i need both ???
// code works perfectly without C3D_BindProgram
// but nor withour shaderProgramUse ...
// but nor without shaderProgramUse ...
shaderProgramUse(&pProgram);
C3D_BindProgram(&pProgram);
C3D_SetAttrInfo(&pInfo);
}
+16
View File
@@ -0,0 +1,16 @@
#include <amethyst/color.hpp>
namespace Amy {
std::string Color::Hex(bool rgba) const {
/** Need to int cast (so it is used as num and not char...) */
std::stringstream s;
s << "#";
s << std::hex << std::setw(2) << std::setfill('0') << (int)r;
s << std::hex << std::setw(2) << std::setfill('0') << (int)g;
s << std::hex << std::setw(2) << std::setfill('0') << (int)b;
if (rgba) {
s << std::hex << std::setw(2) << std::setfill('0') << (int)a;
}
return s.str();
}
} // namespace Amy
-17
View File
@@ -1,17 +0,0 @@
#include <3ds.h>
#include <amethyst/ctru.hpp>
namespace Amy {
namespace Ctr {
void Init(ui srvs) {
if (srvs & Romfs) {
romfsInit();
}
if (srvs & GfxDefault) {
gfxInitDefault();
}
}
ull GetTime() { return osGetTime(); }
} // namespace Ctr
} // namespace Amy
+14
View File
@@ -0,0 +1,14 @@
#include <amethyst/godtrace.hpp>
#include <amethyst/utils.hpp>
Amy::GTrace::TraceMap Amy::GTrace::pTraces;
void Amy::GTrace::Beg(ksr id) {
auto trace = GetTraceRef(id);
trace->SetStart(Amy::Utils::GetTimeNano());
}
void Amy::GTrace::End(ksr id) {
auto trace = GetTraceRef(id);
trace->SetEnd(Amy::Utils::GetTimeNano());
}
+19 -1
View File
@@ -2,7 +2,11 @@
#include <amethyst/utils.hpp>
#include <stdexcept>
// #define STB_IMAGE_IMPLEMENTATION
// Only include stb Code if we need it
// Otherwise we just build the header
#ifdef AMY_STB_IMAGE
#define STB_IMAGE_IMPLEMENTATION
#endif
#include <stb_image.h>
namespace Amy {
@@ -75,6 +79,9 @@ int Image::GetBppOfFmt(const Image::Format& fmt) {
case RGB565:
return 2;
break;
case A8:
return 1;
break;
default:
return 0; // Unknown
@@ -85,6 +92,17 @@ int Image::GetBppOfFmt(const Image::Format& fmt) {
void Image::Convert(Image& img, const Format& dst) {
if (img.pFmt == dst) {
return;
} else if (img.pFmt == RGBA && dst == A8) {
std::vector<uc> cpy = img.pBuffer;
img.pBuffer.resize(img.pW * img.pH);
for (int y = 0; y < img.pH; y++) {
for (int x = 0; x < img.pW; x++) {
int src = (y * img.pW + x) * 4;
int dst = (y * img.pW + x);
img.pBuffer[dst] = cpy[src + 3];
}
}
img.pFmt = A8;
} else if (img.pFmt == RGB && dst == BGR) {
Utils::Image::ReverseBuf(img.pBuffer, img.pW, img.pH, 3);
img.pFmt = BGR;
View File
File diff suppressed because it is too large Load Diff
+34 -15
View File
@@ -23,14 +23,22 @@ void Iron::Drawlist::Merge(Iron::Drawlist* list) {
list->Clear();
}
void Iron::Drawlist::Clear() { pData.clear(); }
void Iron::Drawlist::Clear() {
pData.clear();
pPath.clear();
DrawSolid();
while (!ClipRects.empty()) {
ClipRects.pop();
}
pLayer = 0;
}
Iron::Command::ref Iron::Drawlist::NewCommand() {
auto ret = std::make_unique<Command>();
Iron::Command::Ref Iron::Drawlist::NewCommand() {
auto ret = Command::New();
ret->Layer = pLayer;
ret->Index = pData.size();
ret->Tex = pTex;
return ret;
return std::move(ret);
}
void Iron::Drawlist::clipCmd(Command* ptr) {
@@ -40,7 +48,7 @@ void Iron::Drawlist::clipCmd(Command* ptr) {
}
}
void Iron::Drawlist::Push(Command::ref cmd) { pData.push_back(std::move(cmd)); }
void Iron::Drawlist::Push(Command::Ref cmd) { pData.push_back(std::move(cmd)); }
void Iron::Drawlist::DrawSolid() { pTex = Iron::WhiteTex(); }
@@ -201,6 +209,17 @@ void Iron::Drawlist::DrawCircleFilled(const fvec2& center, float rad, ui color,
PathFill(color);
}
void Iron::Drawlist::DrawFromAtlas(fvec2 pos, fvec2 size, fvec4 cut_rect,
ui color) {
auto r = Iron::PrimRect(pos, size);
auto cmd = NewCommand();
auto uv = cut_rect / fvec4(pTex->Size(), pTex->Size());
uv.y = 1.f - uv.y;
uv.w = 1.f - uv.w;
Iron::CmdQuad(cmd.get(), r, uv, color);
Push(std::move(cmd));
}
void Iron::Drawlist::DrawPolyLine(const std::vector<fvec2>& points, ui clr,
ui flags, int thickness) {
if (points.size() < 2) {
@@ -235,30 +254,30 @@ void Iron::Drawlist::DrawConvexPolyFilled(const std::vector<fvec2>& points,
void Iron::Drawlist::DrawText(const fvec2& pos, const std::string& text,
ui color) {
/*if (!pCurrentFont) {
if (!pCurrentFont) {
return;
}
std::vector<Command::Ref> cmds;
pCurrentFont->CmdTextEx(cmds, pos, color, pFontScale, text);
for (size_t i = 0; i < cmds.size(); i++) {
cmds[i]->Index = pDrawlist.size();
cmds[i]->Layer = Layer;
AddCommand(std::move(cmds[i]));
}*/
cmds[i]->Index = pData.size();
cmds[i]->Layer = pLayer;
Push(std::move(cmds[i]));
}
}
void Iron::Drawlist::DrawTextEx(const fvec2& p, const std::string& text,
ui color, ui flags, const fvec2& box) {
/*if (!pCurrentFont) {
if (!pCurrentFont) {
return;
}
std::vector<Command::Ref> cmds;
pCurrentFont->CmdTextEx(cmds, p, color, pFontScale, text, flags, box);
for (size_t i = 0; i < cmds.size(); i++) {
cmds[i]->Index = pDrawlist.size();
cmds[i]->Layer = Layer;
AddCommand(std::move(cmds[i]));
}*/
cmds[i]->Index = pData.size();
cmds[i]->Layer = pLayer;
Push(std::move(cmds[i]));
}
}
void Iron::Drawlist::DrawLine(const fvec2& a, const fvec2& b, ui color, int t) {
+307
View File
@@ -0,0 +1,307 @@
#include <amethyst/iron.hpp>
#include <amethyst/utils.hpp>
#include <filesystem>
#include <fstream>
//#ifdef AMY_STB_TT
#define STB_TRUETYPE_IMPLEMENTATION
//#endif
#include <stb_truetype.h>
namespace Amy {
// Dont read this code please ... tnaks
void Iron::Font::LoadBMF(ksr path) {
Image img(path);
img.Convert(Image::A8);
if (img.Width() != img.Height() || img.Bpp() != 1 || img.Width() <= 0 ||
img.Height() <= 0) {
throw std::runtime_error(
"[Amy] Font: BMF is not in rgba or not 1x1 dimensioned!");
}
auto base = Amy::Texture::New();
base->Load(img.GetBuffer(), img.Width(), img.Height(), img.Bpp(), img.Fmt());
base->Unloadable(false);
PxHeight = img.Height() / 16;
for (int i = 0; i < img.Height(); i += PxHeight) {
for (int j = 0; j < img.Width(); j += PxHeight) {
int maxw = 0;
Amy::Texture::Ref tex = Amy::Texture::New();
for (int y = i; y < i + PxHeight; y++) {
for (int x = j; x < j + PxHeight; x++) {
if (img.GetBuffer()[y * img.Width() + x] != 0) {
maxw = std::max(maxw, x - j);
}
}
}
Codepoint cp;
cp.Valid = maxw != 0;
cp.Cp = (i / PxHeight) * 16 + (j / PxHeight);
cp.Offset = 0.f;
if (cp.Valid) {
tex->Load(base->Ptr(), base->Size(), base->Uv());
cp.Size = fvec2(maxw + 1, PxHeight);
cp.Tex = tex;
cp.Uv = fvec4(float(j) / float(img.Width()),
1.f - float(i) / float(img.Height()),
float(j + maxw + 1) / float(img.Width()),
1.f - float(i + PxHeight) / float(img.Height()));
Textures.push_back(tex);
}
pCodeMap[(i / PxHeight) * 16 + (j / PxHeight)] = cp;
}
}
}
void Iron::Font::LoadTTF(ksr path, int size) {
auto data = Amy::Utils::LoadFile2Mem(path);
LoadTTF(data, size);
}
void Iron::Font::LoadTTF(const vec<uc>& data, int size) {
/**
* Some additional Info:
* Removed the stbtt get bitmapbox as we dont need to place
* the glyps nicely in the tex. next step would be using the free
* space on the y axis to get mor glyphs inside
*/
PxHeight = size;
int texszs = Amy::Utils::NextPow2(PxHeight * 16);
if (texszs > 1024) {
texszs = 1024; // Max size
}
stbtt_fontinfo inf;
if (!stbtt_InitFont(&inf, data.data(), 0)) {
return;
}
float scale = stbtt_ScaleForPixelHeight(&inf, PxHeight);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
int baseline = static_cast<int>(ascent * scale);
// Cache to not render same codepoint tex twice
std::map<u32, int> buf_cache;
std::vector<u8> font_tex(texszs * texszs, 0);
auto tex = Texture::New();
fvec2 off;
bool empty = true;
for (u32 ii = 0x0000; ii <= 0xFFFF; ii++) {
int gi = stbtt_FindGlyphIndex(&inf, ii);
if (gi == 0) continue;
if (stbtt_IsGlyphEmpty(&inf, gi)) continue;
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char* bitmap =
stbtt_GetCodepointBitmap(&inf, scale, scale, ii, &w, &h, &xo, &yo);
if (!bitmap || w <= 0 || h <= 0) {
if (bitmap) free(bitmap);
continue;
}
u32 hashed_map =
Utils::HashMemory(std::vector<u8>(bitmap, bitmap + (w * h)));
if (buf_cache.find(hashed_map) != buf_cache.end()) {
Codepoint c = GetCodepoint(buf_cache[hashed_map]);
c.Cp = ii;
pCodeMap[ii] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = ii;
}
// Next row
if (off.x + w > texszs) {
off.y += PxHeight;
off.x = 0.0f;
}
// Bake cause we go out of the tex
if (off.y + PxHeight > texszs) {
pMakeAtlas(false, font_tex, texszs, tex);
tex = Texture::New();
off = 0;
std::fill(font_tex.begin(), font_tex.end(), 0);
empty = true;
}
// UVs & Codepoint
Codepoint c;
fvec4 uvs;
// cast the ints to floats and not the floats...
// dont know where my mind was when creating the code
uvs.x = off.x / static_cast<float>(texszs);
uvs.y = off.y / static_cast<float>(texszs);
uvs.z = (off.x + w) / static_cast<float>(texszs);
uvs.w = (off.y + h) / static_cast<float>(texszs);
// Flip (too lazy to place it into the seq at top)
uvs.y = 1.f - uvs.y;
uvs.w = 1.f - uvs.w;
c.Uv = uvs;
c.Tex = tex;
c.Size = fvec2(w, h);
c.Offset = baseline + yo;
c.Cp = ii;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int map_pos = ((static_cast<int>(off.y) + y) * texszs +
(static_cast<int>(off.x) + x));
font_tex[map_pos] = bitmap[x + y * w];
}
}
empty = false;
pCodeMap[ii] = c;
free(bitmap);
// offset by 1 (prevents visual glitches i had)
off.x += w + 1;
}
if (!empty) {
pMakeAtlas(true, font_tex, texszs, tex);
}
}
void Iron::Font::pMakeAtlas(bool final, vec<uc>& font_tex, int texszs,
Texture::Ref tex) {
tex->Load(font_tex, texszs, texszs, 1, Amy::Image::A8);
Textures.push_back(tex);
}
Iron::Font::Codepoint& Iron::Font::GetCodepoint(ui cp) {
// Check if codepoijt exist or return a static invalid one
auto res = pCodeMap.find(cp);
if (res == pCodeMap.end()) {
static Codepoint invalid;
invalid.Valid = false;
return invalid;
}
return res->second;
}
fvec2 Iron::Font::GetTextBounds(ksr text, float scale) {
// Use wstring for exemple for german äöü
auto wtext = std::filesystem::path(text).wstring();
// Create a temp position and offset as [0, 0]
fvec2 res;
float x = 0;
// Curent Font Scale
float cfs = (PxFactor * scale) / (float)PxHeight;
float lh = (float)PxHeight * cfs;
size_t index = 0;
for (auto& it : wtext) {
if (it == L'\0') {
break;
}
index++;
auto cp = GetCodepoint(it);
if (!cp.Valid && it != '\n' && it != '\t' && it != ' ') {
continue;
}
switch (it) {
case L'\n':
res.y += lh;
res.x = std::max(res.x, x);
x = 0.f;
break;
case L'\t':
x += 16 * cfs;
break;
case L' ':
x += 4 * cfs;
// Fall trough here to get the same result as in
// TextCommand if/else Section
default:
x += cp.Size.x * cfs;
if (index != wtext.size()) {
x += 2 * cfs;
}
break;
}
}
res.x = std::max(res.x, x);
res.y += lh;
return res;
}
void Iron::Font::CmdTextEx(vec<Command::Ref>& cmds, const fvec2& pos, ui color,
float scale, const std::string& text,
AmyTextFlags flags, const fvec2& box) {
fvec2 off;
float cfs = (PxFactor * scale) / (float)PxHeight;
float lh = (float)PxHeight * cfs;
fvec2 td;
fvec2 rpos = pos;
fvec2 rbox = box;
if (flags & (AmyTextFlags_AlignMid | AmyTextFlags_AlignRight)) {
td = GetTextBounds(text, scale);
}
if (flags & AmyTextFlags_AlignMid) {
rpos = rbox * 0.5 - td * 0.5 + pos;
}
if (flags & AmyTextFlags_AlignRight) {
rpos.x = rpos.x - td.x;
}
std::vector<std::string> lines;
std::istringstream iss(text);
std::string tmp;
while (std::getline(iss, tmp)) {
lines.push_back(tmp);
}
for (auto& it : lines) {
/*if (flags & AmyTextFlags_Short) {
fvec2 tmp_dim;
it = ShortText(it, box.x() - pos.x(), tmp_dim);
}*/
// Bitte nicht nachmachen... Also ernsthaft jz, bitte macht das nicht
auto wline = std::filesystem::path(it).wstring();
auto cmd = Command::New();
auto Tex = GetCodepoint(wline[0]).Tex;
cmd->Tex = Tex;
for (auto& jt : wline) {
auto cp = GetCodepoint(jt);
if ((!cp.Valid && jt != L' ' && jt != L'\n' && jt != L'\t') &&
jt != L'\r') {
continue;
}
if (Tex != cp.Tex) {
cmds.push_back(std::move(cmd));
cmd = Command::New();
Tex = cp.Tex;
cmd->Tex = Tex;
}
if (jt == L'\t') {
off.x += 16 * cfs;
} else {
if (jt != L' ') {
if (flags & AmyTextFlags_Shaddow) {
// Draw
Rect rec = Iron::PrimRect(
rpos + vec2(off.x + 1, off.y + (cp.Offset * cfs)) + 1,
cp.Size * cfs, 0.f);
Iron::CmdQuad(cmd.get(), rec, cp.Uv, 0xff111111);
}
// Draw
Rect rec = Iron::PrimRect(rpos + off + fvec2(0, (cp.Offset * cfs)),
cp.Size * cfs, 0.f);
Iron::CmdQuad(cmd.get(), rec, cp.Uv, color);
off.x += cp.Size.x * cfs + 2 * cfs;
} else {
off.x += 4 * cfs;
}
}
}
cmds.push_back(std::move(cmd));
off.y += lh;
off.x = 0;
}
}
} // namespace Amy
+56 -36
View File
@@ -1,19 +1,8 @@
#include <amethyst/iron.hpp>
#include <amethyst/utils.hpp>
#include <fstream>
#include <iostream>
#define catch2() \
; \
({ \
std::cout << std::format("{}:{}", __FILE__, __LINE__) << std::endl; \
while (aptMainLoop()) { \
hidScanInput(); \
if (hidKeysDown() & KEY_A) { \
break; \
} \
} \
})
namespace Amy {
const char* __ironshader__ = R"(; LI7 Shader
; Constants
@@ -50,19 +39,15 @@ const char* __ironshader__ = R"(; LI7 Shader
end
.end)";
// clang-format off
unsigned char li_shader[] = {
0x44, 0x56, 0x4c, 0x42, 0x1, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x50, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0x1, 0xf0, 0x7, 0x4e, 0x2, 0x8, 0x2, 0x8, 0x3, 0x18, 0x2, 0x8, 0x4, 0x28, 0x2, 0x8, 0x5, 0x38, 0x2, 0x8, 0x6, 0x10, 0x40, 0x4c, 0x7, 0xf1, 0x27, 0x22, 0x8, 0x10, 0x21, 0x4c, 0x0, 0x0, 0x0, 0x88, 0xac, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa1, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xd5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x45, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x1, 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xf, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x13, 0x0, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x0,
};
// clang-format on
size_t li_shader_size = 0x124;
std::vector<Iron::Vertex, linearAllocator<Iron::Vertex>> Iron::m_vbuf;
std::vector<u16, linearAllocator<u16>> Iron::m_ibuf;
std::vector<Iron::Vertex, LinearAllocator<Iron::Vertex>> Iron::m_vbuf;
std::vector<u16, LinearAllocator<u16>> Iron::m_ibuf;
int Iron::uLocProj = 0;
C3D::Shader* Iron::m_shader = nullptr;
mat4 Iron::m_mtx;
int Iron::m_idx = 0, Iron::m_vtx = 0;
Texture* Iron::m_solid = nullptr;
Texture::Ref Iron::m_solid = nullptr;
ui Iron::VertexCount = 0;
ui Iron::IndexCount = 0;
void Iron::Init() {
pSetupShader();
@@ -71,7 +56,16 @@ void Iron::Init() {
pInitSolidTex();
}
void Iron::Exit() {
m_solid.reset();
delete m_shader;
m_vbuf.clear();
m_ibuf.clear();
}
void Iron::NewFrame() {
VertexCount = m_vtx;
IndexCount = m_idx;
m_idx = 0;
m_vtx = 0;
}
@@ -83,31 +77,37 @@ void Iron::DrawOn(C3D::Screen* screen) {
m_shader->SetMat4(uLocProj, m_mtx);
}
void Iron::Draw(const std::vector<Iron::Command::ref>& data) {
void Iron::Draw(const std::vector<Iron::Command::Ref>& data) {
// disable depthtest cause we have no z buffer
C3D::DepthTest(false);
pFragConfig();
size_t i = 0;
while (i < data.size()) {
Texture* tex = data[i]->Tex;
if (!tex) {
Texture::Ref tex = data[i]->Tex;
if (!tex || !tex->Ptr()) {
i++;
continue;
}
pFragConfig(tex->Ptr()->fmt);
auto scissorOn = data[i]->ScissorOn;
auto scissor = data[i]->ScissorRect;
auto start = i;
auto start = m_idx;
// Loop until a statgechange and copy all data into Vertex/index buf
while (i < data.size() && scissorOn == data[i]->ScissorOn &&
scissor == data[i]->ScissorRect && tex == data[i]->Tex) {
auto c = data[i].get();
if (!pCheckSize(c->IndexBuf.size(), c->VertexBuf.size())) {
throw Error("iron: too much draw data!!!" +
std::format("\nIdx: {}\nVtx: {}", c->IndexBuf.size(),
c->VertexBuf.size()));
}
for (int j = 0; j < c->IndexBuf.size(); j++) {
m_ibuf[m_idx++] = m_vtx + c->IndexBuf[j];
}
for (int j = 0; j < c->VertexBuf.size(); j++) {
m_vbuf[m_vtx++] = c->VertexBuf[j];
m_vbuf[m_vtx++] = std::move(c->VertexBuf[j]);
}
i++;
}
@@ -115,32 +115,51 @@ void Iron::Draw(const std::vector<Iron::Command::ref>& data) {
///// SCISSOR LOGIC END /////
tex->Bind();
C3D::BufCfg<3>(m_vbuf.data(), sizeof(Vertex));
C3D::DrawElements(i - start, m_ibuf.data() + start);
C3D::DrawElements(m_idx - start, m_ibuf.data() + start);
}
C3D::DepthTest(true);
}
bool Iron::pCheckSize(size_t idx, size_t vtx) {
return idx < m_ibuf.size() && vtx < m_vbuf.size();
}
void Iron::pSetupShader() {
m_shader = new C3D::Shader();
m_shader->Load("romfs:/shaders/lithium.shbin");
// m_shader->Compile(__ironshader__);
m_shader->Compile(__ironshader__);
m_shader->Input(GPU_FLOAT, 2); // pos
m_shader->Input(GPU_FLOAT, 2); // uv
m_shader->Input(GPU_UNSIGNED_BYTE, 4); // color
uLocProj = m_shader->loc("projection");
}
void Iron::pFragConfig() {
void Iron::pFragConfig(GPU_TEXCOLOR clr) {
C3D::Frag::Edit();
switch (clr) {
case GPU_A4:
case GPU_A8:
case GPU_L4:
case GPU_L8:
C3D::Frag::Src(C3D_Alpha, GPU_TEXTURE0);
C3D::Frag::Func(C3D_RGB, GPU_REPLACE);
C3D::Frag::Func(C3D_Alpha, GPU_MODULATE);
break;
default:
C3D::Frag::Src(C3D_Both, GPU_TEXTURE0);
C3D::Frag::Func(C3D_Both, GPU_MODULATE);
break;
}
}
void Iron::pInitSolidTex() {
// i know there is a lot of memory wasted :(
std::vector<uc> pixels(16 * 16 * 4, 0xff);
m_solid = new Texture();
m_solid->Load(pixels, 16, 16);
std::vector<uc> pixels(16 * 16, 0xff);
m_solid = Texture::New();
m_solid->Load(pixels, 16, 16, 1, Amy::Image::A8);
if (!m_solid->Ptr()) {
throw Error("white tex failed to load!");
}
}
bool Iron::InBox(const fvec2& pos, const fvec2& size, const fvec4& area) {
@@ -218,14 +237,15 @@ void Iron::CmdTriangle(Command* cmd, const fvec2& a, const fvec2& b,
}
void Iron::CmdConvexPolyFilled(Command* cmd, const std::vector<fvec2>& points,
ui color, Texture* tex) {
ui color, Texture::Ref tex) {
if (points.size() < 3 || tex == nullptr) {
#ifdef AMY_GOD_DEV
return;
#else
throw std::runtime_error("[amy] iron: trying to render convex poly with " +
std::to_string(points.size()) +
" points and Texture " + std::to_string((ui)tex));
" points and Texture " +
std::to_string((ui)tex.get()));
#endif
}
// Support for Custom Textures (UV calculation)
+20 -16
View File
@@ -24,9 +24,11 @@ GPU_TEXCOLOR image2TexFmt(const Image::Format& fmt) {
case Image::RGB565:
return GPU_RGB565;
break;
case Image::A8:
return GPU_A8;
break;
default:
// Dummy
return GPU_A4;
throw std::runtime_error(
"[amy] texture: Unsupported texture Format used!");
break;
@@ -38,55 +40,57 @@ Texture::~Texture() { Unload(); }
void Texture::Unload() {
if (pLoaded) {
C3D_TexDelete(&pTex);
C3D_TexDelete(pTex);
delete pTex;
pTex = nullptr;
pLoaded = false;
}
}
void Texture::Load(ksr path) {
Image img(path);
if (img.Width() > 1024 || img.Height() > 1024) {
throw std::runtime_error("Max Texture Size is 1024x1024!");
}
Load(img.GetBuffer(), img.Width(), img.Height(), img.Bpp(), img.Fmt());
}
void Texture::Load(kvr<uc> pixels, int w, int h, int bpp, Image::Format fmt) {
if (w > 1024 || h > 1024) {
throw std::runtime_error("Max Texture Size is 1024x1024!");
throw std::runtime_error("[amy] texture: Max Texture Size is 1024x1024!");
}
Unload();
pSize.x = w;
if (Utils::IsSingleBitNum(pSize.x)) {
if (!Utils::IsSingleBitNum(pSize.x)) {
pSize.x = Utils::NextPow2(pSize.x);
}
pSize.y = h;
if (Utils::IsSingleBitNum(pSize.y)) {
if (!Utils::IsSingleBitNum(pSize.y)) {
pSize.y = Utils::NextPow2(pSize.y);
}
pUv = Amy::fvec4(0, 1.f, (float)w / (float)pSize.x,
1.f - ((float)h / (float)pSize.y));
auto filter = GPU_NEAREST;
auto Format = image2TexFmt(fmt);
C3D_TexInit(&pTex, (u16)pSize.x, (u16)pSize.y, Format);
C3D_TexSetFilter(&pTex, filter, filter);
pTex = new C3D_Tex;
C3D_TexInit(pTex, (u16)pSize.x, (u16)pSize.y, Format);
C3D_TexSetFilter(pTex, filter, filter);
// Using std::fill_n instead cause i hate this error lines
// under the memset func in my editor
std::fill_n((unsigned char*)pTex.data, pTex.size, 0);
std::fill_n((uc*)pTex->data, pTex->size, 0);
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int dst_pos = tile3dsTex(x, y, pSize.x) * bpp;
int src_pos = (y * w + x) * bpp;
/// Best idea i had
for (int i = 0; i < bpp; i++) {
((u8*)pTex.data)[dst_pos + bpp - 1 - i] = pixels[src_pos + i];
((u8*)pTex->data)[dst_pos + bpp - 1 - i] = pixels[src_pos + i];
}
}
}
C3D_TexFlush(&pTex);
pTex.border = 0x00000000;
C3D_TexSetWrap(&pTex, GPU_REPEAT, GPU_REPEAT);
C3D_TexFlush(pTex);
pTex->border = 0x00000000;
C3D_TexSetWrap(pTex, GPU_REPEAT, GPU_REPEAT);
pLoaded = true;
}
void Texture::Bind(int reg) { C3D_TexBind(reg, &pTex); }
void Texture::Bind(int reg) { C3D_TexBind(reg, pTex); }
} // namespace Amy
+126 -8
View File
@@ -3,14 +3,6 @@
namespace Amy {
namespace Utils {
ui fastHash(ksr s) {
ui hash = 5381;
for (auto& it : s) {
hash = (hash * 33) + static_cast<uc>(it);
}
return hash;
}
vec<uc> LoadFile2Mem(ksr path) {
std::ifstream iff(path, std::ios::binary);
if (!iff) {
@@ -25,6 +17,22 @@ vec<uc> LoadFile2Mem(ksr path) {
return res;
}
str FormatBytes(ull bytes) {
static const vec<str> endings = {
"B", "KB", "MB", "GB", "TB", "Unk",
};
int i = 0;
double b = bytes;
while (b > 1024) {
i++;
b /= 1024;
}
if (i >= (int)endings.size()) {
i = (int)endings.size() - 1;
}
return std::format("{:.1f} {}", b, endings[i]);
}
ui HashMemory(kvr<uc> data) {
ui hash = 4477;
for (auto& it : data) {
@@ -62,6 +70,116 @@ ull GetTimeMilli() {
.count();
}
void String2U16(us* res, ksr src, size_t max) {
/// GOT FORCED TO REPLACE std::wstring_convert by some
/// manual work as it got removed in cxx20
/// TODO ///
/// ADD SOME ERROR API IN HERE
if (max == 0) return;
size_t len = 0;
size_t i = 0;
while (i < src.size() && len < max) {
uc c = src[i];
if (c < 0x80) {
// 1byte
res[len++] = c;
i++;
} else if ((c >> 5) == 0x6) {
// 2byte
if (i + 1 >= src.size())
throw std::invalid_argument("Invalid UTF-8 sequence");
res[len++] = ((c & 0x1F) << 6) | (src[i + 1] & 0x3F);
i += 2;
} else if ((c >> 4) == 0xE) {
// 3byte
if (i + 2 >= src.size())
throw std::invalid_argument("Invalid UTF-8 sequence");
res[len++] =
((c & 0x0F) << 12) | ((src[i + 1] & 0x3F) << 6) | (src[i + 2] & 0x3F);
i += 3;
} else if ((c >> 3) == 0x1E) {
// 4byte
if (i + 3 >= src.size())
throw std::invalid_argument("Invalid UTF-8 sequence");
ui codepoint = ((c & 0x07) << 18) | ((src[i + 1] & 0x3F) << 12) |
((src[i + 2] & 0x3F) << 6) | (src[i + 3] & 0x3F);
codepoint -= 0x10000;
res[len++] = 0xD800 | ((codepoint >> 10) & 0x3FF);
res[len++] = 0xDC00 | (codepoint & 0x3FF);
i += 4;
} else {
return;
}
}
}
std::string U16toU8(us* in, size_t max) {
/// GOT FORCED TO REPLACE std::wstring_convert by some
/// manual work as it got removed in cxx20
if (!in || max == 0) {
return "";
}
std::string result;
result.reserve(max * 3);
for (size_t i = 0; i < max; i++) {
uint16_t c = in[i];
if (c < 0x80) {
result.push_back(static_cast<char>(c));
} else if (c < 0x800) {
result.push_back(static_cast<char>(0xC0 | (c >> 6)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
} else if (c < 0x10000) {
result.push_back(static_cast<char>(0xE0 | (c >> 12)));
result.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
} else {
continue;
}
}
return result;
}
kstr FormatNanos(ull nanos) {
// Based on some code of my minecraft plugins
if (nanos < 1000) {
return std::format("{}ns", nanos);
} else if (nanos < 1000000) {
ull micros = nanos / 1000;
return std::format("{}us {}ns", micros, nanos % 1000);
} else if (nanos < 1000000000) {
ull millis = nanos / 1000000;
return std::format("{}ms {}us", millis, (nanos % 1000000) / 1000);
} else if (nanos < 60000000000ULL) {
ull seconds = nanos / 1000000000;
return std::format("{}s {}ms", seconds, (nanos % 1000000000) / 1000000);
} else {
ull minutes = nanos / 60000000000ULL;
ull seconds = (nanos % 60000000000ULL) / 1000000000;
return std::format("{}m {}s", minutes, seconds);
}
return "";
}
kstr FormatMillis(ull millis) {
// Original Code can be found in some of my mc plugins
if (millis < 1000) {
return std::format("{}ms", millis);
} else if (millis < 60000) {
ull seconds = millis / 1000;
return std::format("{}s {}ms", seconds, (millis % 1000));
} else {
ull minutes = millis / 60000;
ull seconds = (millis % 60000) / 1000;
return std::format("{}m {}s {}ms", minutes, seconds, (millis % 1000));
}
return "";
}
namespace Image {
void ReverseBuf(vec<uc>& buf, int w, int h, int c) {
vec<uc> cpy = buf;