mirror of
https://github.com/IllusionMan1212/O2048.git
synced 2026-10-24 10:12:37 +00:00
initial
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
o2048
|
||||||
|
ols.json
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
Binary file not shown.
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2015 The Rubik Project Authors (https://github.com/googlefonts/rubik)
|
||||||
|
|
||||||
|
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:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
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.
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 581 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,314 @@
|
|||||||
|
package freetype
|
||||||
|
|
||||||
|
FT_Error :: i32
|
||||||
|
@private
|
||||||
|
FT_Short :: i16
|
||||||
|
@private
|
||||||
|
FT_UShort :: u16
|
||||||
|
@private
|
||||||
|
FT_Int :: i32
|
||||||
|
@private
|
||||||
|
FT_UInt :: u32
|
||||||
|
@private
|
||||||
|
FT_Long :: i64
|
||||||
|
@private
|
||||||
|
FT_ULong :: u64
|
||||||
|
@private
|
||||||
|
FT_String :: u8
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Pos :: i64
|
||||||
|
@private
|
||||||
|
FT_Fixed :: i64
|
||||||
|
@private
|
||||||
|
FT_SubGlyph :: ^u32 // internal object
|
||||||
|
@private
|
||||||
|
FT_Slot_Internal :: ^u32 // opaque handle to internal slot structure
|
||||||
|
@private
|
||||||
|
FT_Size_Internal :: ^u32 // opaque handle to internal size structure
|
||||||
|
@private
|
||||||
|
FT_Driver :: ^u32 // a handle to a given FreeType font driver object
|
||||||
|
@private
|
||||||
|
FT_Memory :: ^u32 // a handle to a given memory manager object
|
||||||
|
|
||||||
|
Library :: ^u32 // handle to a library object
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Render_Mode_ :: enum {
|
||||||
|
NORMAL = 0,
|
||||||
|
LIGHT,
|
||||||
|
MONO,
|
||||||
|
LCD,
|
||||||
|
LCD_V,
|
||||||
|
SDF,
|
||||||
|
|
||||||
|
MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadFlags :: enum {
|
||||||
|
DEFAULT = 0x0,
|
||||||
|
NO_SCALE = 1 << 0,
|
||||||
|
NO_HINTING = 1 << 1,
|
||||||
|
RENDER = 1 << 2,
|
||||||
|
NO_BITMAP = 1 << 3,
|
||||||
|
VERTICAL_LAYOUT = 1 << 4,
|
||||||
|
FORCE_AUTOHINT = 1 << 5,
|
||||||
|
CROP_BITMAP = 1 << 6,
|
||||||
|
PEDANTIC = 1 << 7,
|
||||||
|
IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9,
|
||||||
|
NO_RECURSE = 1 << 10,
|
||||||
|
IGNORE_TRANSFORM = 1 << 11,
|
||||||
|
MONOCHROME = 1 << 12,
|
||||||
|
LINEAR_DESIGN = 1 << 13,
|
||||||
|
NO_AUTOHINT = 1 << 15,
|
||||||
|
// Bits 16-19 are used by `FT_LOAD_TARGET_`
|
||||||
|
COLOR = 1 << 20,
|
||||||
|
COMPUTE_METRICS = 1 << 21,
|
||||||
|
BITMAP_METRICS_ONLY = 1 << 22,
|
||||||
|
|
||||||
|
/* used internally only by certain font drivers */
|
||||||
|
ADVANCE_ONLY = 1 << 8,
|
||||||
|
SBITS_ONLY = 1 << 14,
|
||||||
|
|
||||||
|
FT_LOAD_TARGET_NORMAL = (int(FT_Render_Mode_.NORMAL) & 15) << 16,
|
||||||
|
FT_LOAD_TARGET_LIGHT = (int(FT_Render_Mode_.LIGHT) & 15) << 16,
|
||||||
|
FT_LOAD_TARGET_MONO = (int(FT_Render_Mode_.MONO) & 15) << 16,
|
||||||
|
FT_LOAD_TARGET_LCD = (int(FT_Render_Mode_.LCD) & 15) << 16,
|
||||||
|
FT_LOAD_TARGET_LCD_V = (int(FT_Render_Mode_.LCD_V) & 15) << 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Encoding :: enum {
|
||||||
|
// TODO: fill this out
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_CharMapRec_ :: struct {
|
||||||
|
face: Face,
|
||||||
|
encoding: FT_Encoding,
|
||||||
|
platform_id: FT_UShort,
|
||||||
|
encoding_id: FT_UShort,
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_CharMap :: ^FT_CharMapRec_
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Bitmap_Size :: struct {
|
||||||
|
height: FT_Short,
|
||||||
|
width: FT_Short,
|
||||||
|
|
||||||
|
size: FT_Pos,
|
||||||
|
|
||||||
|
x_ppem: FT_Pos,
|
||||||
|
y_ppem: FT_Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Generic :: struct {
|
||||||
|
data: rawptr,
|
||||||
|
finalizer: ^proc(object: rawptr),
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_BBox :: struct {
|
||||||
|
xMin, yMin: FT_Pos,
|
||||||
|
xMax, yMax: FT_Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Vector :: struct {
|
||||||
|
x, y: FT_Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Glyph_Metrics :: struct {
|
||||||
|
width: FT_Pos,
|
||||||
|
height: FT_Pos,
|
||||||
|
|
||||||
|
horiBearingX: FT_Pos,
|
||||||
|
horiBearingY: FT_Pos,
|
||||||
|
horiAdvance: FT_Pos,
|
||||||
|
|
||||||
|
vertBearingX: FT_Pos,
|
||||||
|
vertBearingY: FT_Pos,
|
||||||
|
vertAdvance: FT_Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Outline :: struct {
|
||||||
|
n_contours: i16,
|
||||||
|
n_points: i16,
|
||||||
|
|
||||||
|
points: ^FT_Vector,
|
||||||
|
tags: ^u8,
|
||||||
|
contours: ^i16,
|
||||||
|
|
||||||
|
flags: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_GlyphSlotRec_ :: struct {
|
||||||
|
library: Library,
|
||||||
|
face: Face,
|
||||||
|
next: FT_GlyphSlot,
|
||||||
|
glyph_index: FT_UInt, /* new in 2.10; was reserved previously */
|
||||||
|
generic: FT_Generic,
|
||||||
|
|
||||||
|
metrics: FT_Glyph_Metrics,
|
||||||
|
linearHoriAdvance: FT_Fixed,
|
||||||
|
linearVertAdvance: FT_Fixed,
|
||||||
|
advance: FT_Vector,
|
||||||
|
|
||||||
|
format: u32 /*FT_Glyph_Format*/, // stub this to u32 for now since I can't be bothered to define the enum
|
||||||
|
|
||||||
|
bitmap: Bitmap,
|
||||||
|
bitmap_left: FT_Int,
|
||||||
|
bitmap_top: FT_Int,
|
||||||
|
|
||||||
|
outline: FT_Outline,
|
||||||
|
|
||||||
|
num_subglyphs: FT_UInt,
|
||||||
|
subglyphs: FT_SubGlyph,
|
||||||
|
|
||||||
|
control_data: rawptr,
|
||||||
|
control_len: i64,
|
||||||
|
|
||||||
|
lsb_delta: FT_Pos,
|
||||||
|
rsb_delta: FT_Pos,
|
||||||
|
|
||||||
|
other: rawptr,
|
||||||
|
|
||||||
|
internal: FT_Slot_Internal,
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_GlyphSlot :: ^FT_GlyphSlotRec_
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Size_Metrics :: struct {
|
||||||
|
x_ppem: FT_UShort, /* horizontal pixels per EM */
|
||||||
|
y_ppem: FT_UShort, /* vertical pixels per EM */
|
||||||
|
|
||||||
|
x_scale: FT_Fixed, /* scaling values used to convert font */
|
||||||
|
y_scale: FT_Fixed, /* units to 26.6 fractional pixels */
|
||||||
|
|
||||||
|
ascender: FT_Pos, /* ascender in 26.6 frac. pixels */
|
||||||
|
descender: FT_Pos, /* descender in 26.6 frac. pixels */
|
||||||
|
height: FT_Pos, /* text height in 26.6 frac. pixels */
|
||||||
|
max_advance: FT_Pos, /* max horizontal advance, in 26.6 pixels */
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_SizeRec :: struct {
|
||||||
|
face: Face,
|
||||||
|
generic: FT_Generic,
|
||||||
|
metrics: FT_Size_Metrics,
|
||||||
|
internal: FT_Size_Internal,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_Size :: ^FT_SizeRec
|
||||||
|
|
||||||
|
//@private
|
||||||
|
//FT_StreamRec_ :: struct {
|
||||||
|
// unsigned char* base,
|
||||||
|
// size: u64,
|
||||||
|
// pos: u64,
|
||||||
|
//
|
||||||
|
// FT_StreamDesc descriptor,
|
||||||
|
// FT_StreamDesc pathname,
|
||||||
|
// FT_Stream_IoFunc read,
|
||||||
|
// FT_Stream_CloseFunc close,
|
||||||
|
//
|
||||||
|
// FT_Memory memory,
|
||||||
|
// unsigned char* cursor,
|
||||||
|
// unsigned char* limit,
|
||||||
|
//}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_ListNodeRec_ :: struct {
|
||||||
|
prev: FT_ListNode,
|
||||||
|
next: FT_ListNode,
|
||||||
|
data: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_ListNode :: ^FT_ListNodeRec_
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_ListRec :: struct {
|
||||||
|
head: FT_ListNode,
|
||||||
|
tail: FT_ListNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
FT_FaceRec_ :: struct {
|
||||||
|
num_faces: FT_Long,
|
||||||
|
face_index: FT_Long,
|
||||||
|
|
||||||
|
face_flags: FT_Long,
|
||||||
|
style_flags: FT_Long,
|
||||||
|
|
||||||
|
num_glyphs: FT_Long,
|
||||||
|
|
||||||
|
family_name: ^FT_String,
|
||||||
|
style_name: ^FT_String,
|
||||||
|
|
||||||
|
num_fixed_sizes: FT_Int,
|
||||||
|
available_sizes: ^FT_Bitmap_Size,
|
||||||
|
|
||||||
|
num_charmaps: FT_Int,
|
||||||
|
charmaps: ^FT_CharMap,
|
||||||
|
|
||||||
|
generic: FT_Generic,
|
||||||
|
|
||||||
|
/*# The following member variables (down to `underline_thickness`) */
|
||||||
|
/*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */
|
||||||
|
/*# for bitmap fonts. */
|
||||||
|
bbox: FT_BBox,
|
||||||
|
|
||||||
|
units_per_EM: FT_UShort,
|
||||||
|
ascender: FT_Short,
|
||||||
|
descender: FT_Short,
|
||||||
|
height: FT_Short,
|
||||||
|
|
||||||
|
max_advance_width: FT_Short,
|
||||||
|
max_advance_height: FT_Short,
|
||||||
|
|
||||||
|
underline_position: FT_Short,
|
||||||
|
underline_thickness: FT_Short,
|
||||||
|
|
||||||
|
glyph: FT_GlyphSlot,
|
||||||
|
size: FT_Size,
|
||||||
|
charmap: FT_CharMap,
|
||||||
|
|
||||||
|
/*@private begin */
|
||||||
|
driver: FT_Driver,
|
||||||
|
memory: FT_Memory,
|
||||||
|
stream: /*FT_Stream*/ ^u32, // TODO: stub it out for now
|
||||||
|
|
||||||
|
sizes_list: FT_ListRec,
|
||||||
|
|
||||||
|
autohint: FT_Generic, // face-specific auto-hinter data
|
||||||
|
extensions: rawptr, // unused
|
||||||
|
|
||||||
|
internal: /*FT_Face_Internal*/ ^u32, // TODO: stub it out for now
|
||||||
|
|
||||||
|
/*@private end */
|
||||||
|
|
||||||
|
//@private
|
||||||
|
//@packed
|
||||||
|
//@align(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap :: struct {
|
||||||
|
rows: u32,
|
||||||
|
width: u32,
|
||||||
|
pitch: i32,
|
||||||
|
buffer: [^]u8,
|
||||||
|
num_grays: u16,
|
||||||
|
pixel_mode: u8,
|
||||||
|
palette_mode: u8,
|
||||||
|
palette: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
Face :: ^FT_FaceRec_
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package freetype
|
||||||
|
|
||||||
|
foreign import freetype "system:freetype"
|
||||||
|
|
||||||
|
foreign freetype {
|
||||||
|
@(link_name = "FT_Init_FreeType")
|
||||||
|
Init_FreeType :: proc(lib: ^Library) -> FT_Error ---
|
||||||
|
@(link_name = "FT_New_Face")
|
||||||
|
New_Face :: proc(lib: Library, file_pathname: cstring, face_index: FT_Long, face: ^Face) -> FT_Error ---
|
||||||
|
@(link_name = "FT_Set_Pixel_Sizes")
|
||||||
|
Set_Pixel_Sizes :: proc(face: Face, pixel_width: FT_UInt, pixel_height: FT_UInt) -> FT_Error ---
|
||||||
|
@(link_name = "FT_Load_Char")
|
||||||
|
Load_Char :: proc(face: Face, char_code: FT_ULong, load_flags: LoadFlags) -> FT_Error ---
|
||||||
|
@(link_name = "FT_Done_Face")
|
||||||
|
Done_Face :: proc(face: Face) -> FT_Error ---
|
||||||
|
@(link_name = "FT_Done_FreeType")
|
||||||
|
Done_FreeType :: proc(lib: Library) -> FT_Error ---
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package glx
|
||||||
|
|
||||||
|
RENDER_TYPE :: 0x8011
|
||||||
|
RGBA_BIT :: 0x00000001
|
||||||
|
DEPTH_SIZE :: 12
|
||||||
|
DOUBLEBUFFER :: 5
|
||||||
|
SAMPLES :: 100001
|
||||||
|
CONTEXT_MAJOR_VERSION_ARB :: 0x2091
|
||||||
|
CONTEXT_MINOR_VERSION_ARB :: 0x2092
|
||||||
|
CONTEXT_PROFILE_MASK_ARB :: 0x9126
|
||||||
|
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package glx
|
||||||
|
|
||||||
|
import x11 "vendor:x11/xlib"
|
||||||
|
foreign import glx "system:GL"
|
||||||
|
|
||||||
|
Drawable :: x11.XID
|
||||||
|
|
||||||
|
@private
|
||||||
|
__FBConfigRec :: struct {
|
||||||
|
visualType: i32,
|
||||||
|
|
||||||
|
transparentType: i32,
|
||||||
|
transparentRed, transparentGreen, transparentBlue, transparentAlpha: i32,
|
||||||
|
transparentIndex: i32,
|
||||||
|
|
||||||
|
visualCaveat: i32,
|
||||||
|
|
||||||
|
associatedVisualId: i32,
|
||||||
|
screen: i32,
|
||||||
|
|
||||||
|
drawableType: i32,
|
||||||
|
renderType: i32,
|
||||||
|
|
||||||
|
maxPbufferWidth, maxPbufferHeight, maxPbufferPixels: i32,
|
||||||
|
optimalPbufferWidth, optimalPbufferHeight: i32, /* for SGIX_pbuffer */
|
||||||
|
|
||||||
|
visualSelectGroup: i32, /* visuals grouped by select priority */
|
||||||
|
|
||||||
|
id: u32,
|
||||||
|
|
||||||
|
rgbMode: b8,
|
||||||
|
colorIndexMode: b8,
|
||||||
|
doubleBufferMode: b8,
|
||||||
|
stereoMode: b8,
|
||||||
|
haveAccumBuffer: b8,
|
||||||
|
haveDepthBuffer: b8,
|
||||||
|
haveStencilBuffer: b8,
|
||||||
|
|
||||||
|
/* The number of bits present in various buffers */
|
||||||
|
accumRedBits, accumGreenBits, accumBlueBits, accumAlphaBits: i32,
|
||||||
|
depthBits: i32,
|
||||||
|
stencilBits: i32,
|
||||||
|
indexBits: i32,
|
||||||
|
redBits, greenBits, blueBits, alphaBits: i32,
|
||||||
|
redMask, greenMask, blueMask, alphaMask: u32,
|
||||||
|
|
||||||
|
multiSampleSize: u32,/* Number of samples per pixel (0 if no ms) */
|
||||||
|
|
||||||
|
nMultiSampleBuffers: u32,/* Number of available ms buffers */
|
||||||
|
maxAuxBuffers: i32,
|
||||||
|
|
||||||
|
/* frame buffer level */
|
||||||
|
level: i32,
|
||||||
|
|
||||||
|
/* color ranges (for SGI_color_range) */
|
||||||
|
extendedRange: b8,
|
||||||
|
minRed, maxRed: f64,
|
||||||
|
minGreen, maxGreen: f64,
|
||||||
|
minBlue, maxBlue: f64,
|
||||||
|
minAlpha, maxAlpha: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// apparently this isn't defined anywhere ???
|
||||||
|
// so we define a minimal one according to this
|
||||||
|
// https://dri.freedesktop.org/docs/gallium/glxapi_8h-source.html
|
||||||
|
// see also: https://github.com/Tarnyko/EGLX/blob/b82af86a8a0daf3408fb7a6e7b07b02b01476626/EGLX.h#L280
|
||||||
|
@private
|
||||||
|
__ContextRec :: struct {
|
||||||
|
currentDpy: ^x11.Display,
|
||||||
|
isDirect: b8,
|
||||||
|
currentDrawable: Drawable,
|
||||||
|
currentReadable: Drawable,
|
||||||
|
xid: x11.XID
|
||||||
|
}
|
||||||
|
|
||||||
|
FBConfig :: ^__FBConfigRec
|
||||||
|
Context :: ^__ContextRec
|
||||||
|
|
||||||
|
foreign glx {
|
||||||
|
@(link_name = "glXGetProcAddressARB")
|
||||||
|
GetProcAddressARB :: proc(name: ^u8) -> rawptr ---
|
||||||
|
@(link_name = "glXSwapBuffers")
|
||||||
|
SwapBuffers :: proc(display: ^x11.Display, drawable: Drawable) ---
|
||||||
|
@(link_name = "glXQueryVersion")
|
||||||
|
QueryVersion :: proc(display: ^x11.Display, major: ^i32, minor: ^i32) -> bool ---
|
||||||
|
@(link_name = "glXChooseFBConfig")
|
||||||
|
ChooseFBConfig :: proc(display: ^x11.Display, screen: i32, attrib_list: ^i32, nelements: ^i32) -> [^]FBConfig ---
|
||||||
|
@(link_name = "glXCreateContextAttribsARB")
|
||||||
|
CreateContextAttribsARB :: proc(display: ^x11.Display, config: FBConfig, share_context: Context, direct: bool, attrib_list: ^i32) -> Context ---
|
||||||
|
@(link_name = "glXDestroyContext")
|
||||||
|
DestroyContext :: proc(display: ^x11.Display, ctx: Context) ---
|
||||||
|
@(link_name = "glXSwapIntervalEXT")
|
||||||
|
SwapIntervalEXT :: proc(display: ^x11.Display, drawable: Drawable, interval: i32) -> bool ---
|
||||||
|
@(link_name = "glXMakeCurrent")
|
||||||
|
MakeCurrent :: proc(display: ^x11.Display, drawable: Drawable, ctx: Context) -> bool ---
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
import "glx"
|
||||||
|
import "zephr"
|
||||||
|
|
||||||
|
font_path :: "assets/fonts/Rubik/Rubik-VariableFont_wght.ttf"
|
||||||
|
icon_path :: "assets/icon-128x128.png"
|
||||||
|
title :: "2048"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
zephr.init(font_path, icon_path, title, zephr.Vec2{1200, 800}, true)
|
||||||
|
|
||||||
|
game_loop()
|
||||||
|
|
||||||
|
zephr.deinit()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform bool isSlider;
|
||||||
|
uniform float sliderPercentage;
|
||||||
|
|
||||||
|
//vec3 rgb2hsv(vec3 c){
|
||||||
|
// vec4 K = vec4(0.0,-1.0 / 3.0,2.0 / 3.0,-1.0);
|
||||||
|
// vec4 p = mix(vec4(c.bg,K.wz),vec4(c.gb,K.xy),step(c.b,c.g));
|
||||||
|
// vec4 q = mix(vec4(p.xyw,c.r),vec4(c.r,p.yzx),step(p.x,c.r));
|
||||||
|
// float d = q.x - min(q.w,q.y);
|
||||||
|
// float e = 1.0e-10;
|
||||||
|
// return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),d / (q.x + e),q.x);
|
||||||
|
//}
|
||||||
|
|
||||||
|
vec3 hsv2rgb(vec3 color){
|
||||||
|
vec3 rgb = clamp(abs(mod(color.r * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
||||||
|
rgb = rgb * rgb * (3.0 - 2.0 * rgb);
|
||||||
|
return color.b * mix(vec3(1.0), rgb, color.g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (isSlider) {
|
||||||
|
vec3 color = hsv2rgb(vec3(v_TexCoords.y, 1.0, 1.0));
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
} else {
|
||||||
|
vec3 color = hsv2rgb(vec3(sliderPercentage, v_TexCoords.x, 1.0 - v_TexCoords.y));
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#version 330 core
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
in vec4 textColor;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform sampler2D text;
|
||||||
|
|
||||||
|
const float smoothness = 0.05;
|
||||||
|
const float outlineDistance = 0.50;
|
||||||
|
const vec4 outlineColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
//float distance = texture(text, v_TexCoords).r;
|
||||||
|
//float outlineFactor = smoothstep(0.5 - smoothness, 0.5 + smoothness, distance);
|
||||||
|
//vec4 color = mix(outlineColor, textColor, outlineFactor);
|
||||||
|
//float alpha = smoothstep(outlineDistance - smoothness, outlineDistance + smoothness, distance);
|
||||||
|
|
||||||
|
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, v_TexCoords).r);
|
||||||
|
FragColor = textColor * sampled;
|
||||||
|
//FragColor = vec4(color.rgb, textColor.a * alpha);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec2 vertex;
|
||||||
|
|
||||||
|
// per instance
|
||||||
|
layout (location = 1) in vec4 offset;
|
||||||
|
layout (location = 2) in int tex_index;
|
||||||
|
layout (location = 3) in vec4 color;
|
||||||
|
layout (location = 4) in mat4 model; // this takes locations 4,5,6,7
|
||||||
|
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
out vec4 textColor;
|
||||||
|
uniform mat4 projection;
|
||||||
|
// buffer for all the 96 ascii characters' texcoords
|
||||||
|
uniform vec2 texcoords[96 * 4];
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 pos = vec2((vertex.x) * offset.z, (vertex.y) * offset.w);
|
||||||
|
gl_Position = projection * model * vec4(pos + offset.xy, 0.0, 1.0);
|
||||||
|
v_TexCoords = texcoords[tex_index * 4 + gl_VertexID];
|
||||||
|
textColor = color;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform vec4 aColor;
|
||||||
|
uniform float borderRadius;
|
||||||
|
uniform float uiWidth;
|
||||||
|
uniform float uiHeight;
|
||||||
|
uniform bool hasTexture;
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
const float smoothness = 1.0;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float alpha = aColor.a;
|
||||||
|
|
||||||
|
if (borderRadius > 0.0) {
|
||||||
|
vec2 dimensions = v_TexCoords * vec2(uiWidth, uiHeight);
|
||||||
|
float xMax = uiWidth - borderRadius;
|
||||||
|
float yMax = uiHeight - borderRadius;
|
||||||
|
|
||||||
|
if (dimensions.x < borderRadius && dimensions.y < borderRadius) {
|
||||||
|
alpha *= 1.0 - smoothstep(borderRadius - smoothness, borderRadius + smoothness, length(dimensions - vec2(borderRadius)));
|
||||||
|
} else if (dimensions.x < borderRadius && dimensions.y > yMax) {
|
||||||
|
alpha *= 1.0 - smoothstep(borderRadius - smoothness, borderRadius + smoothness, length(dimensions - vec2(borderRadius, yMax)));
|
||||||
|
} else if (dimensions.x > xMax && dimensions.y < borderRadius) {
|
||||||
|
alpha *= 1.0 - smoothstep(borderRadius - smoothness, borderRadius + smoothness, length(dimensions - vec2(xMax, borderRadius)));
|
||||||
|
} else if (dimensions.x > xMax && dimensions.y > yMax) {
|
||||||
|
alpha *= 1.0 - smoothstep(borderRadius - smoothness, borderRadius + smoothness, length(dimensions - vec2(xMax, yMax)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTexture) {
|
||||||
|
FragColor = texture(tex, v_TexCoords) * vec4(aColor.rgb, alpha);
|
||||||
|
} else {
|
||||||
|
FragColor = vec4(aColor.rgb, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec4 vertex;
|
||||||
|
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform mat4 model;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
|
||||||
|
v_TexCoords = vertex.zw;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package xcursor
|
||||||
|
|
||||||
|
import x11 "vendor:x11/xlib"
|
||||||
|
foreign import xcursor "system:Xcursor"
|
||||||
|
|
||||||
|
foreign xcursor {
|
||||||
|
@(link_name = "XcursorLibraryLoadCursor")
|
||||||
|
LibraryLoadCursor :: proc(display: ^x11.Display, name: cstring) -> x11.Cursor ---
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
// TODO: do audio which requries writing a binding for fmod
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
|
||||||
|
Shader :: struct {
|
||||||
|
program: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
create_shader :: proc(vertex_path: string, fragment_path: string) -> (Shader, bool) {
|
||||||
|
shader: Shader
|
||||||
|
|
||||||
|
program, success := gl.load_shaders(vertex_path, fragment_path)
|
||||||
|
shader.program = program;
|
||||||
|
|
||||||
|
return shader, success;
|
||||||
|
}
|
||||||
|
|
||||||
|
use_shader :: proc(shader: Shader) {
|
||||||
|
gl.UseProgram(shader.program);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_mat4f :: proc(shader: Shader, name: cstring, mat4: Mat4, transpose: bool = false) {
|
||||||
|
mat4 := mat4
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.UniformMatrix4fv(loc, 1, transpose, raw_data(&mat4))
|
||||||
|
}
|
||||||
|
|
||||||
|
set_float :: proc(shader: Shader, name: cstring, val: f32) {
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.Uniform1f(loc, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_int :: proc(shader: Shader, name: cstring, val: i32) {
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.Uniform1i(loc, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_bool :: proc(shader: Shader, name: cstring, val: bool) {
|
||||||
|
set_int(shader, name, cast(i32)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_vec2f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32) {
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.Uniform2f(loc, val1, val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_vec3f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32) {
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.Uniform3f(loc, val1, val2, val3);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_vec4f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32, val4: f32) {
|
||||||
|
loc := gl.GetUniformLocation(shader.program, name);
|
||||||
|
gl.Uniform4f(loc, val1, val2, val3, val4);
|
||||||
|
}
|
||||||
+419
@@ -0,0 +1,419 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
|
||||||
|
import FT "../freetype"
|
||||||
|
|
||||||
|
Character :: struct {
|
||||||
|
size: Vec2,
|
||||||
|
bearing: Vec2,
|
||||||
|
advance: u32,
|
||||||
|
tex_coords: [4]Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
Font :: struct {
|
||||||
|
atlas_tex_id: TextureId,
|
||||||
|
characters: [128]Character,
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlyphInstance needs to be 100 bytes (meaning no padding) otherwise the data we send to the shader
|
||||||
|
// is misaligned and we get rendering errors
|
||||||
|
#assert(size_of(GlyphInstance) == 100)
|
||||||
|
|
||||||
|
GlyphInstance :: struct #packed {
|
||||||
|
pos: Vec4,
|
||||||
|
tex_coords_idx: u32,
|
||||||
|
color: Vec4,
|
||||||
|
model: Mat4,
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphInstanceList :: [dynamic]GlyphInstance
|
||||||
|
|
||||||
|
@private
|
||||||
|
FONT_PIXEL_SIZE :: 100
|
||||||
|
@private
|
||||||
|
LINE_HEIGHT :: 2.0
|
||||||
|
|
||||||
|
@private
|
||||||
|
font_shader : Shader
|
||||||
|
@private
|
||||||
|
font_vao : u32
|
||||||
|
@private
|
||||||
|
font_instance_vbo : u32
|
||||||
|
|
||||||
|
|
||||||
|
init_freetype :: proc(font_path: cstring) -> i32 {
|
||||||
|
ft: FT.Library;
|
||||||
|
if (FT.Init_FreeType(&ft) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
face: FT.Face;
|
||||||
|
if (FT.New_Face(ft, font_path, 0, &face) != 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the variable font to be bold
|
||||||
|
/* if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { */
|
||||||
|
/* printf("[INFO] Got a variable font\n"); */
|
||||||
|
/* FT_MM_Var *mm; */
|
||||||
|
/* FT_Get_MM_Var(face, &mm); */
|
||||||
|
|
||||||
|
/* FT_Set_Var_Design_Coordinates(face, mm->num_namedstyles, mm->namedstyle[mm->num_namedstyles - 4].coords); */
|
||||||
|
|
||||||
|
/* FT_Done_MM_Var(ft, mm); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE);
|
||||||
|
|
||||||
|
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
pen_x, pen_y: u32
|
||||||
|
|
||||||
|
/* FT_UInt glyph_idx; */
|
||||||
|
/* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */
|
||||||
|
|
||||||
|
tex_width, tex_height: u32;
|
||||||
|
for i in 32..<128 {
|
||||||
|
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
||||||
|
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
||||||
|
|
||||||
|
tex_width += face.glyph.bitmap.width + 1;
|
||||||
|
tex_height = max(tex_height, face.glyph.bitmap.rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels := make([dynamic]u8, tex_width * tex_height);
|
||||||
|
defer delete(pixels)
|
||||||
|
|
||||||
|
for i in 32..<128 {
|
||||||
|
/* while (glyph_idx) { */
|
||||||
|
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
||||||
|
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
||||||
|
|
||||||
|
bmp := &face.glyph.bitmap;
|
||||||
|
|
||||||
|
if (pen_x + bmp.width >= tex_width) {
|
||||||
|
pen_x = 0;
|
||||||
|
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in 0..<bmp.rows {
|
||||||
|
for col in 0..<bmp.width {
|
||||||
|
x := pen_x + col;
|
||||||
|
y := pen_y + row;
|
||||||
|
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width;
|
||||||
|
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height;
|
||||||
|
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width;
|
||||||
|
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height;
|
||||||
|
|
||||||
|
top_left := Vec2{ atlas_x0, atlas_y1 };
|
||||||
|
top_right := Vec2{ atlas_x1, atlas_y1 };
|
||||||
|
bottom_right := Vec2{ atlas_x1, atlas_y0 };
|
||||||
|
bottom_left := Vec2{ atlas_x0, atlas_y0 };
|
||||||
|
|
||||||
|
character: Character
|
||||||
|
|
||||||
|
character.tex_coords[0] = top_left;
|
||||||
|
character.tex_coords[1] = top_right;
|
||||||
|
character.tex_coords[2] = bottom_right;
|
||||||
|
character.tex_coords[3] = bottom_left;
|
||||||
|
character.advance = cast(u32)face.glyph.advance.x;
|
||||||
|
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows };
|
||||||
|
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top };
|
||||||
|
|
||||||
|
zephr_ctx.font.characters[i] = character;
|
||||||
|
|
||||||
|
pen_x += bmp.width + 1;
|
||||||
|
|
||||||
|
/* c = FT_Get_Next_Char(face, c, &glyph_idx); */
|
||||||
|
}
|
||||||
|
|
||||||
|
texture: TextureId;
|
||||||
|
gl.GenTextures(1, &texture);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, cast(i32)tex_width,
|
||||||
|
cast(i32)tex_height, 0, gl.RED, gl.UNSIGNED_BYTE,
|
||||||
|
raw_data(pixels))
|
||||||
|
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
|
|
||||||
|
zephr_ctx.font.atlas_tex_id = texture;
|
||||||
|
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
FT.Done_Face(face);
|
||||||
|
FT.Done_FreeType(ft);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_fonts :: proc(font_path: cstring) -> i32 {
|
||||||
|
font_vbo, font_ebo: u32;
|
||||||
|
|
||||||
|
res := init_freetype(font_path);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag");
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
fmt.println("[FATAL] Failed to create font shader")
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
font_shader = l_font_shader;
|
||||||
|
|
||||||
|
gl.GenVertexArrays(1, &font_vao);
|
||||||
|
gl.GenBuffers(1, &font_vbo);
|
||||||
|
gl.GenBuffers(1, &font_instance_vbo);
|
||||||
|
gl.GenBuffers(1, &font_ebo);
|
||||||
|
|
||||||
|
quad_vertices := [4][2]f32 {
|
||||||
|
{0.0, 1.0}, // top left
|
||||||
|
{1.0, 1.0}, // top right
|
||||||
|
{1.0, 0.0}, // bottom right
|
||||||
|
{0.0, 0.0}, // bottom left
|
||||||
|
};
|
||||||
|
|
||||||
|
quad_indices := [6]u32 {
|
||||||
|
0, 1, 2,
|
||||||
|
2, 3, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindVertexArray(font_vao);
|
||||||
|
|
||||||
|
// font quad vbo
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo);
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW);
|
||||||
|
gl.EnableVertexAttribArray(0);
|
||||||
|
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0);
|
||||||
|
|
||||||
|
// font instance vbo
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||||
|
gl.EnableVertexAttribArray(1);
|
||||||
|
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0);
|
||||||
|
gl.VertexAttribDivisor(1, 1);
|
||||||
|
gl.EnableVertexAttribArray(2);
|
||||||
|
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4));
|
||||||
|
gl.VertexAttribDivisor(2, 1);
|
||||||
|
gl.EnableVertexAttribArray(3);
|
||||||
|
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)));
|
||||||
|
gl.VertexAttribDivisor(3, 1);
|
||||||
|
gl.EnableVertexAttribArray(4);
|
||||||
|
gl.EnableVertexAttribArray(5);
|
||||||
|
gl.EnableVertexAttribArray(6);
|
||||||
|
gl.EnableVertexAttribArray(7);
|
||||||
|
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)));
|
||||||
|
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)));
|
||||||
|
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)));
|
||||||
|
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)));
|
||||||
|
gl.VertexAttribDivisor(4, 1);
|
||||||
|
gl.VertexAttribDivisor(5, 1);
|
||||||
|
gl.VertexAttribDivisor(6, 1);
|
||||||
|
gl.VertexAttribDivisor(7, 1);
|
||||||
|
|
||||||
|
// font ebo
|
||||||
|
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo);
|
||||||
|
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW);
|
||||||
|
|
||||||
|
use_shader(font_shader);
|
||||||
|
|
||||||
|
for i in 32..<128 {
|
||||||
|
for j in 0..<4 {
|
||||||
|
buf: [24]byte
|
||||||
|
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j);
|
||||||
|
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
calculate_text_size :: proc(text: string, font_size: u32) -> Vec2 {
|
||||||
|
scale := cast(f32)font_size / FONT_PIXEL_SIZE;
|
||||||
|
size : Vec2 = ---
|
||||||
|
w: f32 = 0;
|
||||||
|
h: f32 = 0;
|
||||||
|
max_bearing_h: f32 = 0;
|
||||||
|
|
||||||
|
// NOTE: I don't like looping through the characters twice, but it's fine for now
|
||||||
|
for i in 0..<len(text) {
|
||||||
|
ch := zephr_ctx.font.characters[text[i]];
|
||||||
|
max_bearing_h = max(max_bearing_h, ch.bearing.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..<len(text) {
|
||||||
|
ch := zephr_ctx.font.characters[text[i]];
|
||||||
|
w += cast(f32)ch.advance / 64;
|
||||||
|
|
||||||
|
// remove bearing of first character
|
||||||
|
if (i == 0 && len(text) > 1) {
|
||||||
|
w -= ch.bearing.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the trailing width of the last character
|
||||||
|
if (i == len(text) - 1) {
|
||||||
|
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we only have one character in the text, then remove the bearing width
|
||||||
|
if (len(text) == 1) {
|
||||||
|
w -= (ch.bearing.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y);
|
||||||
|
|
||||||
|
if (text[i] == '\n') {
|
||||||
|
w = 0;
|
||||||
|
h += max_bearing_h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size.x = w * scale;
|
||||||
|
size.y = h * scale;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
||||||
|
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
|
||||||
|
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
|
||||||
|
gl.BindVertexArray(font_vao);
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(glyph_instance_list))
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
delete(glyph_instance_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text_batch :: proc(batch: ^GlyphInstanceList) {
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
|
||||||
|
gl.BindVertexArray(font_vao);
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(batch))
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
delete(batch^)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) -> GlyphInstanceList {
|
||||||
|
constraints := constraints
|
||||||
|
use_shader(font_shader);
|
||||||
|
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 };
|
||||||
|
|
||||||
|
set_mat4f(font_shader, "projection", zephr_ctx.projection)
|
||||||
|
|
||||||
|
rect: Rect
|
||||||
|
|
||||||
|
apply_constraints(&constraints, &rect.pos, &rect.size);
|
||||||
|
|
||||||
|
text_size := calculate_text_size(text, FONT_PIXEL_SIZE);
|
||||||
|
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x;
|
||||||
|
|
||||||
|
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos);
|
||||||
|
|
||||||
|
model := identity()
|
||||||
|
scale(&model, Vec3{font_scale, font_scale, 1});
|
||||||
|
|
||||||
|
// rotate around the center point of the text
|
||||||
|
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0});
|
||||||
|
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
|
||||||
|
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||||
|
|
||||||
|
max_bearing_h: f32 = 0;
|
||||||
|
for i in 0..<len(text) {
|
||||||
|
ch := zephr_ctx.font.characters[text[i]];
|
||||||
|
max_bearing_h = max(max_bearing_h, ch.bearing.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x;
|
||||||
|
|
||||||
|
glyph_instance_list: GlyphInstanceList
|
||||||
|
reserve(&glyph_instance_list, 16)
|
||||||
|
|
||||||
|
// we use the original text and character sizes in the loop and then we just
|
||||||
|
// scale up or down the model matrix to get the desired font size.
|
||||||
|
// this way everything works out fine and we get to transform the text using the
|
||||||
|
// model matrix
|
||||||
|
c := 0;
|
||||||
|
x: u32 = 0;
|
||||||
|
y: f32 = 0;
|
||||||
|
for c != len(text) {
|
||||||
|
ch := zephr_ctx.font.characters[text[c]]
|
||||||
|
// subtract the bearing width of the first character to remove the extra space
|
||||||
|
// at the start of the text and move every char to the left by that width
|
||||||
|
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w));
|
||||||
|
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h));
|
||||||
|
|
||||||
|
if (text[c] == '\n') {
|
||||||
|
x = 0;
|
||||||
|
y += max_bearing_h + (36 * LINE_HEIGHT);
|
||||||
|
c += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_model := Mat4 {
|
||||||
|
1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 10, 11, 12,
|
||||||
|
13, 14, 15, 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := GlyphInstance {
|
||||||
|
pos = Vec4{xpos, ypos, ch.size.x, ch.size.y},
|
||||||
|
tex_coords_idx = cast(u32)text[c] - 32,
|
||||||
|
color = text_color,
|
||||||
|
model = model,
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&glyph_instance_list, instance)
|
||||||
|
|
||||||
|
x += (ch.advance >> 6);
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glyph_instance_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_text_instance :: proc(batch: ^GlyphInstanceList, text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
||||||
|
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
|
||||||
|
|
||||||
|
append(batch, ..glyph_instance_list[:])
|
||||||
|
|
||||||
|
delete(glyph_instance_list);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
import stb "vendor:stb/image"
|
||||||
|
|
||||||
|
TextureId :: u32
|
||||||
|
|
||||||
|
load_texture :: proc(path: cstring) -> TextureId {
|
||||||
|
texture_id: TextureId;
|
||||||
|
width, height, channels: i32
|
||||||
|
data := stb.load(path, &width, &height, &channels, 4) // RGBA
|
||||||
|
defer stb.image_free(data)
|
||||||
|
|
||||||
|
format := gl.RGBA;
|
||||||
|
|
||||||
|
switch (channels) {
|
||||||
|
case 1:
|
||||||
|
format = gl.RED;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
format = gl.RG;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
format = gl.RGB;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
format = gl.RGBA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.GenTextures(1, &texture_id);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, texture_id);
|
||||||
|
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||||
|
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
|
|
||||||
|
gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data);
|
||||||
|
|
||||||
|
gl.GenerateMipmap(gl.TEXTURE_2D);
|
||||||
|
|
||||||
|
return texture_id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
TimerState :: enum {
|
||||||
|
RUNNING,
|
||||||
|
PAUSED,
|
||||||
|
STOPPED,
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer :: struct {
|
||||||
|
start: f64,
|
||||||
|
elapsed: f64,
|
||||||
|
duration: f32,
|
||||||
|
state: TimerState,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
TIMER : time.Stopwatch
|
||||||
|
|
||||||
|
@private
|
||||||
|
start_internal_timer :: proc() {
|
||||||
|
time.stopwatch_start(&TIMER)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_time :: proc() -> time.Duration {
|
||||||
|
return time.stopwatch_duration(TIMER)
|
||||||
|
}
|
||||||
|
|
||||||
|
start_timer :: proc(timer: ^time.Stopwatch) {
|
||||||
|
time.stopwatch_start(timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: incomplete
|
||||||
+938
@@ -0,0 +1,938 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
import gl "vendor:OpenGL"
|
||||||
|
|
||||||
|
UiIdHash :: u32
|
||||||
|
|
||||||
|
Alignment :: enum {
|
||||||
|
TOP_LEFT,
|
||||||
|
TOP_CENTER,
|
||||||
|
TOP_RIGHT,
|
||||||
|
LEFT_CENTER,
|
||||||
|
CENTER,
|
||||||
|
RIGHT_CENTER,
|
||||||
|
BOTTOM_LEFT,
|
||||||
|
BOTTOM_CENTER,
|
||||||
|
BOTTOM_RIGHT,
|
||||||
|
}
|
||||||
|
|
||||||
|
UiConstraint :: enum {
|
||||||
|
FIXED,
|
||||||
|
RELATIVE,
|
||||||
|
RELATIVE_PIXELS,
|
||||||
|
ASPECT_RATIO,
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonState :: enum {
|
||||||
|
ACTIVE,
|
||||||
|
INACTIVE,
|
||||||
|
DISABLED,
|
||||||
|
}
|
||||||
|
|
||||||
|
UiConstraints :: struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
rotation: f32,
|
||||||
|
scale: Vec2,
|
||||||
|
parent: ^UiConstraints,
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect :: struct {
|
||||||
|
pos: Vec2,
|
||||||
|
size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
UiStyle :: struct {
|
||||||
|
bg_color: Color,
|
||||||
|
fg_color: Color,
|
||||||
|
border_radius: f32,
|
||||||
|
align: Alignment,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui :: struct {
|
||||||
|
active_element: UiIdHash,
|
||||||
|
popup_open: bool,
|
||||||
|
popup_parent_hash: UiIdHash,
|
||||||
|
popup_rect: Rect,
|
||||||
|
popup_parent_constraints: UiConstraints,
|
||||||
|
popup_revert_color: ^Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
ui_shader: Shader
|
||||||
|
@private
|
||||||
|
color_chooser_shader: Shader
|
||||||
|
@private
|
||||||
|
ui_vao: u32
|
||||||
|
@private
|
||||||
|
ui_vbo: u32
|
||||||
|
|
||||||
|
DEFAULT_UI_CONSTRAINTS :: UiConstraints{
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0,
|
||||||
|
rotation = 0,
|
||||||
|
scale = Vec2{1, 1},
|
||||||
|
parent = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
ui_init :: proc(font_path: cstring) {
|
||||||
|
res := init_fonts(font_path);
|
||||||
|
if (res == -1) {
|
||||||
|
fmt.printf("[ERROR]: could not initialize freetype library\n");
|
||||||
|
os.exit(1)
|
||||||
|
} else if (res == -2) {
|
||||||
|
fmt.printf("[ERROR]: could not load font file: \"%s\"\n", font_path);
|
||||||
|
os.exit(1)
|
||||||
|
} else if (res != 0) {
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
l_ui_shader, success1 := create_shader("shaders/ui.vert", "shaders/ui.frag")
|
||||||
|
l_color_chooser_shader, success2 := create_shader("shaders/ui.vert", "shaders/color_chooser.frag")
|
||||||
|
|
||||||
|
ui_shader = l_ui_shader
|
||||||
|
color_chooser_shader = l_color_chooser_shader
|
||||||
|
|
||||||
|
if !success1 || !success2 {
|
||||||
|
fmt.printf("[ERROR]: could not load ui shaders\n");
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.GenVertexArrays(1, &ui_vao);
|
||||||
|
gl.GenBuffers(1, &ui_vbo);
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao);
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(f32) * 6 * 4, nil, gl.DYNAMIC_DRAW);
|
||||||
|
gl.EnableVertexAttribArray(0);
|
||||||
|
gl.VertexAttribPointer(0, 4, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0);
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_parent_constraint :: proc(constraints: ^UiConstraints, parent_constraints: ^UiConstraints) {
|
||||||
|
constraints.parent = parent_constraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_x_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) {
|
||||||
|
switch (type) {
|
||||||
|
case .ASPECT_RATIO: // no-op. fall back to FIXED
|
||||||
|
fallthrough
|
||||||
|
case .FIXED:
|
||||||
|
constraints.x = value;
|
||||||
|
case .RELATIVE:
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.x = (value * constraints.parent.width);
|
||||||
|
} else {
|
||||||
|
constraints.x = (value * zephr_ctx.window.size.x);
|
||||||
|
}
|
||||||
|
case .RELATIVE_PIXELS:
|
||||||
|
constraints.x = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.x += constraints.parent.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_y_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) {
|
||||||
|
switch (type) {
|
||||||
|
case .ASPECT_RATIO: // no-op. fall back to FIXED
|
||||||
|
fallthrough
|
||||||
|
case .FIXED:
|
||||||
|
constraints.y = value;
|
||||||
|
case .RELATIVE:
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.y = (value * constraints.parent.height);
|
||||||
|
} else {
|
||||||
|
constraints.y = (value * zephr_ctx.window.size.y);
|
||||||
|
}
|
||||||
|
case .RELATIVE_PIXELS:
|
||||||
|
constraints.y = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.y += constraints.parent.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_width_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) {
|
||||||
|
switch (type) {
|
||||||
|
case .FIXED:
|
||||||
|
constraints.width = value;
|
||||||
|
case .RELATIVE:
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.width = (value * constraints.parent.width);
|
||||||
|
} else {
|
||||||
|
constraints.width = (value * zephr_ctx.window.size.x);
|
||||||
|
}
|
||||||
|
case .RELATIVE_PIXELS:
|
||||||
|
constraints.width = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value;
|
||||||
|
case .ASPECT_RATIO:
|
||||||
|
constraints.width = constraints.height * value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_height_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) {
|
||||||
|
switch (type) {
|
||||||
|
case .FIXED:
|
||||||
|
constraints.height = value;
|
||||||
|
case .RELATIVE:
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
constraints.height = (value * constraints.parent.height);
|
||||||
|
} else {
|
||||||
|
constraints.height = (value * zephr_ctx.window.size.y);
|
||||||
|
}
|
||||||
|
case .RELATIVE_PIXELS:
|
||||||
|
constraints.height = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value;
|
||||||
|
case .ASPECT_RATIO:
|
||||||
|
constraints.height = constraints.width * value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_rotation_constraint :: proc(constraints: ^UiConstraints, angle_d: f32) {
|
||||||
|
constraints.rotation = angle_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
apply_constraints :: proc(constraints: ^UiConstraints, pos: ^Vec2, size: ^Vec2) {
|
||||||
|
pos^ = Vec2{constraints.x, constraints.y};
|
||||||
|
size^ = Vec2{constraints.width, constraints.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
apply_alignment :: proc(align: Alignment, constraints: ^UiConstraints, size: Vec2, pos: ^Vec2) {
|
||||||
|
// if we don't have a parent then we're a top level element and we should align against the window
|
||||||
|
parent_size := Vec2{zephr_ctx.window.size.x, zephr_ctx.window.size.y};
|
||||||
|
if (constraints.parent != nil) {
|
||||||
|
parent_size = Vec2{constraints.parent.width, constraints.parent.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (align) {
|
||||||
|
case .TOP_LEFT:
|
||||||
|
pos.x = pos.x;
|
||||||
|
pos.y = pos.y;
|
||||||
|
case .TOP_CENTER:
|
||||||
|
pos.x += parent_size.x / 2 - size.x / 2;
|
||||||
|
pos.y = pos.y;
|
||||||
|
case .TOP_RIGHT:
|
||||||
|
pos.x += parent_size.x - size.x;
|
||||||
|
pos.y = pos.y;
|
||||||
|
case .BOTTOM_LEFT:
|
||||||
|
pos.x = pos.x;
|
||||||
|
pos.y += parent_size.y - size.y;
|
||||||
|
case .BOTTOM_CENTER:
|
||||||
|
pos.x += parent_size.x / 2 - size.x / 2;
|
||||||
|
pos.y += parent_size.y - size.y;
|
||||||
|
case .BOTTOM_RIGHT:
|
||||||
|
pos.x += parent_size.x - size.x;
|
||||||
|
pos.y += parent_size.y - size.y;
|
||||||
|
case .LEFT_CENTER:
|
||||||
|
pos.x = pos.x;
|
||||||
|
pos.y += parent_size.y / 2 - size.y / 2;
|
||||||
|
case .RIGHT_CENTER:
|
||||||
|
pos.x += parent_size.x - size.x;
|
||||||
|
pos.y += parent_size.y / 2 - size.y / 2;
|
||||||
|
case .CENTER:
|
||||||
|
pos.x += parent_size.x / 2 - size.x / 2;
|
||||||
|
pos.y += parent_size.y / 2 - size.y / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
// TODO: make this handle rotated and scaled rects too
|
||||||
|
inside_rect :: proc(rect: Rect, point: Vec2) -> bool {
|
||||||
|
return point.x >= rect.pos.x && point.x <= rect.pos.x + rect.size.x &&
|
||||||
|
point.y >= rect.pos.y && point.y <= rect.pos.y + rect.size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_quad :: proc(constraints: ^UiConstraints, style: UiStyle) {
|
||||||
|
use_shader(ui_shader)
|
||||||
|
|
||||||
|
set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255)
|
||||||
|
set_float(ui_shader, "uiWidth", constraints.width)
|
||||||
|
set_float(ui_shader, "uiHeight", constraints.height)
|
||||||
|
set_float(ui_shader, "borderRadius", style.border_radius)
|
||||||
|
set_mat4f(ui_shader, "projection", zephr_ctx.projection)
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size)
|
||||||
|
apply_alignment(style.align, constraints, rect.size, &rect.pos)
|
||||||
|
|
||||||
|
// set the positions after applying alignment so children can use them
|
||||||
|
constraints.x = rect.pos.x;
|
||||||
|
constraints.y = rect.pos.y;
|
||||||
|
|
||||||
|
model := identity();
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||||
|
|
||||||
|
set_mat4f(ui_shader, "model", model);
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao)
|
||||||
|
|
||||||
|
vertices: [6][4]f32 = {
|
||||||
|
// bottom left tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0, 0, 0.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
|
||||||
|
// top right tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices));
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_circle :: proc(constraints: ^UiConstraints, style: UiStyle) {
|
||||||
|
radius: f32 = 0;
|
||||||
|
|
||||||
|
if (constraints.width > constraints.height) {
|
||||||
|
radius = constraints.height / 2;
|
||||||
|
} else {
|
||||||
|
radius = constraints.width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_style := style;
|
||||||
|
new_style.border_radius = radius;
|
||||||
|
|
||||||
|
draw_quad(constraints, new_style);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_triangle :: proc(constraints: ^UiConstraints, style: UiStyle) {
|
||||||
|
use_shader(ui_shader);
|
||||||
|
|
||||||
|
set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255);
|
||||||
|
set_float(ui_shader, "borderRadius", 0);
|
||||||
|
set_mat4f(ui_shader, "projection", zephr_ctx.projection)
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(style.align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
// set the positions after applying alignment so children can use them
|
||||||
|
constraints.x = rect.pos.x;
|
||||||
|
constraints.y = rect.pos.y;
|
||||||
|
|
||||||
|
model := identity();
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||||
|
|
||||||
|
set_mat4f(ui_shader, "model", model);
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao);
|
||||||
|
|
||||||
|
vertices := [3][4]f32 {
|
||||||
|
{0 + rect.size.x, 0, 0.0, 0.0},
|
||||||
|
{0, 0, 0.0, 0.0},
|
||||||
|
{0 + rect.size.x / 2, 0 + rect.size.y, 0.0, 0.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices))
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 3);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: Color, radius: f32, align: Alignment) {
|
||||||
|
use_shader(ui_shader);
|
||||||
|
|
||||||
|
set_vec4f(ui_shader, "aColor", cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255);
|
||||||
|
set_float(ui_shader, "uiWidth", constraints.width);
|
||||||
|
set_float(ui_shader, "uiHeight", constraints.height);
|
||||||
|
set_float(ui_shader, "borderRadius", radius);
|
||||||
|
set_bool(ui_shader, "hasTexture", true);
|
||||||
|
|
||||||
|
set_mat4f(ui_shader, "projection", zephr_ctx.projection)
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
// set the positions after applying alignment so children can use them
|
||||||
|
constraints.x = rect.pos.x;
|
||||||
|
constraints.y = rect.pos.y;
|
||||||
|
|
||||||
|
model := identity();
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0});
|
||||||
|
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
|
||||||
|
translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0});
|
||||||
|
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||||
|
|
||||||
|
set_mat4f(ui_shader, "model", model)
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao);
|
||||||
|
|
||||||
|
vertices := [6][4]f32 {
|
||||||
|
// bottom left tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0, 0, 0.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
|
||||||
|
// top right tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices))
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.ActiveTexture(gl.TEXTURE0);
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, texture_id);
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
set_bool(ui_shader, "hasTexture", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_button :: proc(constraints: ^UiConstraints, text: string, style: UiStyle, state: ButtonState, caller := #caller_location) -> bool {
|
||||||
|
line := caller.line
|
||||||
|
line_bytes := mem.byte_slice(&line, size_of(line))
|
||||||
|
hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT)
|
||||||
|
hash = fnv_hash32(line_bytes, size_of(caller.line), hash)
|
||||||
|
|
||||||
|
style := style
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(style.align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
is_hovered := inside_rect(rect, zephr_ctx.mouse.pos);
|
||||||
|
is_held := zephr_ctx.ui.active_element == hash
|
||||||
|
left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
clicked := false;
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.active_element == 0) {
|
||||||
|
if (is_hovered && left_mouse_pressed) {
|
||||||
|
zephr_ctx.ui.active_element = hash;
|
||||||
|
}
|
||||||
|
} else if (zephr_ctx.ui.active_element == hash) {
|
||||||
|
if (left_mouse_released) {
|
||||||
|
zephr_ctx.ui.active_element = 0;
|
||||||
|
|
||||||
|
if (is_hovered) {
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_hovered && state == .ACTIVE) {
|
||||||
|
set_cursor(Cursor.HAND);
|
||||||
|
|
||||||
|
if (is_held) {
|
||||||
|
style.bg_color = mult_color(style.bg_color, 0.8);
|
||||||
|
draw_quad(constraints, style);
|
||||||
|
} else {
|
||||||
|
style.bg_color = mult_color(style.bg_color, 0.9);
|
||||||
|
draw_quad(constraints, style);
|
||||||
|
}
|
||||||
|
} else if (state == .DISABLED) {
|
||||||
|
if (is_hovered) {
|
||||||
|
set_cursor(Cursor.DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
style.bg_color.a = 100;
|
||||||
|
style.fg_color.a = 100;
|
||||||
|
draw_quad(constraints, style);
|
||||||
|
} else {
|
||||||
|
draw_quad(constraints, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text != "") {
|
||||||
|
text_constraints := DEFAULT_UI_CONSTRAINTS
|
||||||
|
|
||||||
|
set_parent_constraint(&text_constraints, constraints);
|
||||||
|
set_x_constraint(&text_constraints, 0, .RELATIVE_PIXELS);
|
||||||
|
set_y_constraint(&text_constraints, 0, .RELATIVE_PIXELS);
|
||||||
|
set_width_constraint(&text_constraints, 1.0, .RELATIVE_PIXELS);
|
||||||
|
|
||||||
|
font_size := ((constraints.width) / (text_constraints.width * 0.1 * 65));
|
||||||
|
|
||||||
|
// TODO: need to adjust the font size based on the text height
|
||||||
|
|
||||||
|
draw_text(text, cast(u32)font_size, text_constraints, style.fg_color, .CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clicked && state == .ACTIVE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_icon_button :: proc(constraints: ^UiConstraints, icon_tex_id: TextureId, style: UiStyle, state: ButtonState, caller := #caller_location) -> bool {
|
||||||
|
assert(icon_tex_id != 0, "draw_icon_button() requires that you provide an icon texture");
|
||||||
|
|
||||||
|
line := caller.line
|
||||||
|
line_bytes := mem.byte_slice(&line, size_of(line))
|
||||||
|
hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT)
|
||||||
|
hash = fnv_hash32(line_bytes, size_of(caller.line), hash)
|
||||||
|
|
||||||
|
style := style
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(style.align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
is_held := zephr_ctx.ui.active_element == hash;
|
||||||
|
is_hovered := inside_rect(rect, zephr_ctx.mouse.pos);
|
||||||
|
left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
clicked := false;
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.active_element == 0) {
|
||||||
|
if (is_hovered && left_mouse_pressed) {
|
||||||
|
zephr_ctx.ui.active_element = hash;
|
||||||
|
}
|
||||||
|
} else if (zephr_ctx.ui.active_element == hash) {
|
||||||
|
if (left_mouse_released) {
|
||||||
|
zephr_ctx.ui.active_element = 0;
|
||||||
|
|
||||||
|
if (is_hovered) {
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_hovered && state == .ACTIVE) {
|
||||||
|
set_cursor(.HAND);
|
||||||
|
|
||||||
|
if (is_held) {
|
||||||
|
style.bg_color = mult_color(style.bg_color, 0.8);
|
||||||
|
style.fg_color = mult_color(style.fg_color, 0.8);
|
||||||
|
} else {
|
||||||
|
style.bg_color = mult_color(style.bg_color, 0.9);
|
||||||
|
style.fg_color = mult_color(style.fg_color, 0.9);
|
||||||
|
}
|
||||||
|
} else if (state == .DISABLED) {
|
||||||
|
if (is_hovered) {
|
||||||
|
set_cursor(.DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
style.fg_color.a = 100;
|
||||||
|
style.bg_color.a = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_quad(constraints, style);
|
||||||
|
|
||||||
|
icon_constraints := DEFAULT_UI_CONSTRAINTS
|
||||||
|
|
||||||
|
set_parent_constraint(&icon_constraints, constraints);
|
||||||
|
set_x_constraint(&icon_constraints, 0, .RELATIVE_PIXELS);
|
||||||
|
set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS);
|
||||||
|
set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED);
|
||||||
|
set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED);
|
||||||
|
draw_texture(&icon_constraints, icon_tex_id, style.fg_color, 0, .CENTER);
|
||||||
|
|
||||||
|
if (clicked && state == .ACTIVE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
draw_color_picker_slider :: proc(constraints: ^UiConstraints, align: Alignment, caller := #caller_location) -> f32 {
|
||||||
|
@static
|
||||||
|
slider_selection: f32 = 0
|
||||||
|
@static
|
||||||
|
dragging := false
|
||||||
|
|
||||||
|
line := caller.line
|
||||||
|
line_bytes := mem.byte_slice(&line, size_of(line))
|
||||||
|
hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT);
|
||||||
|
hash = fnv_hash32(line_bytes, size_of(caller.line), hash);
|
||||||
|
|
||||||
|
use_shader(color_chooser_shader);
|
||||||
|
|
||||||
|
set_bool(color_chooser_shader, "isSlider", true);
|
||||||
|
set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection);
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
// set the positions after applying alignment so children can use them
|
||||||
|
constraints.x = rect.pos.x;
|
||||||
|
constraints.y = rect.pos.y;
|
||||||
|
|
||||||
|
is_hovered := inside_rect(rect, zephr_ctx.mouse.pos);
|
||||||
|
left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.active_element == 0) {
|
||||||
|
if (is_hovered && left_mouse_pressed) {
|
||||||
|
zephr_ctx.ui.active_element = hash;
|
||||||
|
dragging = true;
|
||||||
|
}
|
||||||
|
} else if (zephr_ctx.ui.active_element == hash) {
|
||||||
|
if (left_mouse_released) {
|
||||||
|
zephr_ctx.ui.active_element = 0;
|
||||||
|
dragging = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dragging) {
|
||||||
|
slider_selection = clamp((zephr_ctx.mouse.pos.y - constraints.y) / constraints.height, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
model := identity();
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0})
|
||||||
|
|
||||||
|
set_mat4f(color_chooser_shader, "model", model);
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao);
|
||||||
|
|
||||||
|
vertices := [6][4]f32 {
|
||||||
|
// bottom left tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0, 0, 0.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
|
||||||
|
// top right tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices))
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
set_bool(color_chooser_shader, "isSlider", false);
|
||||||
|
|
||||||
|
// draw the selection
|
||||||
|
triangle_con := DEFAULT_UI_CONSTRAINTS
|
||||||
|
set_parent_constraint(&triangle_con, constraints);
|
||||||
|
set_x_constraint(&triangle_con, -11, .RELATIVE_PIXELS);
|
||||||
|
set_y_constraint(&triangle_con, slider_selection, .RELATIVE);
|
||||||
|
set_rotation_constraint(&triangle_con, -90);
|
||||||
|
set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS);
|
||||||
|
set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO);
|
||||||
|
triangle_con.y += -(triangle_con.height / 2);
|
||||||
|
tri_style := UiStyle {
|
||||||
|
bg_color = COLOR_BLACK,
|
||||||
|
border_radius = 0,
|
||||||
|
align = .TOP_LEFT,
|
||||||
|
};
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
set_x_constraint(&triangle_con, 1, .RELATIVE);
|
||||||
|
triangle_con.x -= triangle_con.height / 2;
|
||||||
|
set_rotation_constraint(&triangle_con, 90);
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
return slider_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
draw_color_picker_canvas :: proc(constraints: ^UiConstraints, slider_percentage: f32, align: Alignment, caller := #caller_location) -> Color {
|
||||||
|
@static
|
||||||
|
dragging := false;
|
||||||
|
@static
|
||||||
|
canvas_pos := Vec2{0, 0};
|
||||||
|
|
||||||
|
line := caller.line
|
||||||
|
line_bytes := mem.byte_slice(&line, size_of(line))
|
||||||
|
hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT);
|
||||||
|
hash = fnv_hash32(line_bytes, size_of(caller.line), hash);
|
||||||
|
|
||||||
|
use_shader(color_chooser_shader);
|
||||||
|
|
||||||
|
set_bool(color_chooser_shader, "isSlider", false);
|
||||||
|
set_float(color_chooser_shader, "sliderPercentage", slider_percentage);
|
||||||
|
set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection);
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
|
||||||
|
apply_constraints(constraints, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(align, constraints, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
// set the positions after applying alignment so children can use them
|
||||||
|
constraints.x = rect.pos.x;
|
||||||
|
constraints.y = rect.pos.y;
|
||||||
|
|
||||||
|
is_hovered := inside_rect(rect, zephr_ctx.mouse.pos);
|
||||||
|
left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.active_element == 0) {
|
||||||
|
if (is_hovered && left_mouse_pressed) {
|
||||||
|
zephr_ctx.ui.active_element = hash;
|
||||||
|
dragging = true;
|
||||||
|
}
|
||||||
|
} else if (zephr_ctx.ui.active_element == hash) {
|
||||||
|
if (left_mouse_released) {
|
||||||
|
zephr_ctx.ui.active_element = 0;
|
||||||
|
dragging = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dragging) {
|
||||||
|
x := (zephr_ctx.mouse.pos.x - constraints.x) / constraints.width;
|
||||||
|
y := (zephr_ctx.mouse.pos.y - constraints.y) / constraints.height;
|
||||||
|
|
||||||
|
canvas_pos.x = clamp(x, 0, 1);
|
||||||
|
canvas_pos.y = clamp(y, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
model := identity();
|
||||||
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||||
|
|
||||||
|
set_mat4f(color_chooser_shader, "model", model);
|
||||||
|
|
||||||
|
gl.BindVertexArray(ui_vao);
|
||||||
|
|
||||||
|
vertices := [6][4]f32 {
|
||||||
|
// bottom left tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0, 0, 0.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
|
||||||
|
// top right tri
|
||||||
|
{0, 0 + rect.size.y, 0.0, 1.0},
|
||||||
|
{0 + rect.size.x, 0, 1.0, 0.0},
|
||||||
|
{0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo);
|
||||||
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices))
|
||||||
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.DrawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
|
||||||
|
selected_color := hsv2rgb(slider_percentage * 360, canvas_pos.x, 1.0 - canvas_pos.y);
|
||||||
|
|
||||||
|
// draw the selection
|
||||||
|
triangle_con := DEFAULT_UI_CONSTRAINTS
|
||||||
|
set_parent_constraint(&triangle_con, constraints);
|
||||||
|
set_x_constraint(&triangle_con, canvas_pos.x, .RELATIVE);
|
||||||
|
set_y_constraint(&triangle_con, canvas_pos.y, .RELATIVE);
|
||||||
|
set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS);
|
||||||
|
set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO);
|
||||||
|
triangle_con.x -= triangle_con.height;
|
||||||
|
triangle_con.y -= triangle_con.height + triangle_con.height / 2 + triangle_con.width / 2;
|
||||||
|
set_rotation_constraint(&triangle_con, 0);
|
||||||
|
tri_style := UiStyle {
|
||||||
|
bg_color = determine_color_contrast(selected_color),
|
||||||
|
border_radius = 0,
|
||||||
|
align = .TOP_LEFT,
|
||||||
|
};
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
triangle_con.x += triangle_con.height * 2;
|
||||||
|
triangle_con.y += triangle_con.height * 2;
|
||||||
|
set_rotation_constraint(&triangle_con, 90);
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
triangle_con.x -= triangle_con.height * 2;
|
||||||
|
triangle_con.y += triangle_con.height * 2;
|
||||||
|
set_rotation_constraint(&triangle_con, 180);
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
triangle_con.x -= triangle_con.height * 2;
|
||||||
|
triangle_con.y -= triangle_con.height * 2;
|
||||||
|
set_rotation_constraint(&triangle_con, 270);
|
||||||
|
draw_triangle(&triangle_con, tri_style);
|
||||||
|
|
||||||
|
return selected_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consume mouse events on the popup
|
||||||
|
@private
|
||||||
|
draw_color_picker_popup :: proc(picker_button_con: ^UiConstraints) {
|
||||||
|
popup_con := DEFAULT_UI_CONSTRAINTS
|
||||||
|
set_parent_constraint(&popup_con, picker_button_con);
|
||||||
|
set_x_constraint(&popup_con, 0, .FIXED);
|
||||||
|
set_y_constraint(&popup_con, 1.4, .RELATIVE);
|
||||||
|
set_width_constraint(&popup_con, 450, .RELATIVE_PIXELS);
|
||||||
|
set_height_constraint(&popup_con, 480, .RELATIVE_PIXELS);
|
||||||
|
popup_style := UiStyle {
|
||||||
|
bg_color = mult_color(COLOR_WHITE, 0.94),
|
||||||
|
border_radius = popup_con.width * 0.02,
|
||||||
|
align = .TOP_LEFT,
|
||||||
|
};
|
||||||
|
draw_quad(&popup_con, popup_style);
|
||||||
|
|
||||||
|
color_slider_con := DEFAULT_UI_CONSTRAINTS
|
||||||
|
set_parent_constraint(&color_slider_con, &popup_con);
|
||||||
|
set_x_constraint(&color_slider_con, -0.04, .RELATIVE);
|
||||||
|
set_y_constraint(&color_slider_con, 0.05, .RELATIVE);
|
||||||
|
set_height_constraint(&color_slider_con, 400 * 0.9, .RELATIVE_PIXELS);
|
||||||
|
set_width_constraint(&color_slider_con, 0.1, .ASPECT_RATIO);
|
||||||
|
slider_selection := draw_color_picker_slider(&color_slider_con, .TOP_RIGHT);
|
||||||
|
|
||||||
|
set_x_constraint(&color_slider_con, 0.04, .RELATIVE);
|
||||||
|
set_y_constraint(&color_slider_con, 0.05, .RELATIVE);
|
||||||
|
set_width_constraint(&color_slider_con, popup_con.width * 0.8, .FIXED);
|
||||||
|
selected_color := draw_color_picker_canvas(&color_slider_con, slider_selection, .TOP_LEFT);
|
||||||
|
|
||||||
|
button_con := DEFAULT_UI_CONSTRAINTS
|
||||||
|
set_parent_constraint(&button_con, &popup_con);
|
||||||
|
set_x_constraint(&button_con, 0.04, .RELATIVE);
|
||||||
|
set_y_constraint(&button_con, -0.05, .RELATIVE);
|
||||||
|
set_width_constraint(&button_con, 0.43, .RELATIVE);
|
||||||
|
set_height_constraint(&button_con, 0.3, .ASPECT_RATIO);
|
||||||
|
button_style := UiStyle {
|
||||||
|
bg_color = zephr_ctx.ui.popup_revert_color^,
|
||||||
|
fg_color = determine_color_contrast(zephr_ctx.ui.popup_revert_color^),
|
||||||
|
border_radius = 4,
|
||||||
|
align = .BOTTOM_LEFT,
|
||||||
|
}
|
||||||
|
if (draw_button(&button_con, "Revert", button_style, .ACTIVE)) {
|
||||||
|
zephr_ctx.ui.popup_open = false;
|
||||||
|
zephr_ctx.ui.popup_parent_hash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_style.align = .BOTTOM_RIGHT;
|
||||||
|
button_style.fg_color = determine_color_contrast(selected_color);
|
||||||
|
set_x_constraint(&button_con, -0.04, .RELATIVE);
|
||||||
|
set_y_constraint(&button_con, -0.05, .RELATIVE);
|
||||||
|
button_style.bg_color = selected_color;
|
||||||
|
if (draw_button(&button_con, "Apply", button_style, .ACTIVE)) {
|
||||||
|
zephr_ctx.ui.popup_revert_color^ = selected_color;
|
||||||
|
zephr_ctx.ui.popup_open = false;
|
||||||
|
zephr_ctx.ui.popup_parent_hash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect := Rect {
|
||||||
|
{popup_con.x, popup_con.y},
|
||||||
|
{popup_con.width, popup_con.height},
|
||||||
|
};
|
||||||
|
|
||||||
|
zephr_ctx.ui.popup_rect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_color_picker :: proc(constraints: ^UiConstraints, color: ^Color, align: Alignment, state: ButtonState, id: u32 = 0, caller := #caller_location) {
|
||||||
|
line := caller.line
|
||||||
|
line_bytes := mem.byte_slice(&line, size_of(line))
|
||||||
|
id := id
|
||||||
|
id_bytes := mem.byte_slice(&id, size_of(id))
|
||||||
|
hash := fnv_hash32(id_bytes, size_of(id), FNV_HASH32_INIT);
|
||||||
|
hash = fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), hash);
|
||||||
|
hash = fnv_hash32(line_bytes, size_of(line), hash);
|
||||||
|
|
||||||
|
rect: Rect = ---
|
||||||
|
display_color := color^;
|
||||||
|
|
||||||
|
con := constraints^;
|
||||||
|
|
||||||
|
apply_constraints(&con, &rect.pos, &rect.size);
|
||||||
|
apply_alignment(align, &con, rect.size, &rect.pos);
|
||||||
|
|
||||||
|
is_held := zephr_ctx.ui.active_element == hash
|
||||||
|
is_hovered := inside_rect(rect, zephr_ctx.mouse.pos);
|
||||||
|
left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT;
|
||||||
|
clicked := false;
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.active_element == 0) {
|
||||||
|
if (is_hovered && left_mouse_pressed) {
|
||||||
|
zephr_ctx.ui.active_element = hash;
|
||||||
|
}
|
||||||
|
} else if (zephr_ctx.ui.active_element == hash) {
|
||||||
|
if (left_mouse_released) {
|
||||||
|
zephr_ctx.ui.active_element = 0;
|
||||||
|
|
||||||
|
if (is_hovered) {
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_hovered && state == .ACTIVE) {
|
||||||
|
set_cursor(.HAND);
|
||||||
|
con.scale.y *= 1.08;
|
||||||
|
con.scale.x *= 1.08;
|
||||||
|
|
||||||
|
if (is_held) {
|
||||||
|
display_color = mult_color(display_color, 0.8);
|
||||||
|
} else {
|
||||||
|
display_color = mult_color(display_color, 0.9);
|
||||||
|
}
|
||||||
|
} else if (state == .DISABLED) {
|
||||||
|
if (is_hovered) {
|
||||||
|
set_cursor(.DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
display_color.a = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
style := UiStyle {
|
||||||
|
bg_color = display_color,
|
||||||
|
border_radius = con.width * 0.08,
|
||||||
|
align = align,
|
||||||
|
};
|
||||||
|
draw_quad(&con, style);
|
||||||
|
|
||||||
|
if (clicked && state == .ACTIVE) {
|
||||||
|
zephr_ctx.ui.popup_open = true;
|
||||||
|
zephr_ctx.ui.popup_parent_constraints = con;
|
||||||
|
zephr_ctx.ui.popup_revert_color = color;
|
||||||
|
zephr_ctx.ui.popup_parent_hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zephr_ctx.ui.popup_parent_hash == hash) {
|
||||||
|
zephr_ctx.ui.popup_open = true;
|
||||||
|
zephr_ctx.ui.popup_parent_constraints = con;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if (is_hovered && state == BUTTON_STATE_ACTIVE && zephr_ctx->mouse.released && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { */
|
||||||
|
/* return true; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* return false; */
|
||||||
|
}
|
||||||
+1250
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
|||||||
|
package zephr
|
||||||
|
|
||||||
|
import "core:math"
|
||||||
|
|
||||||
|
Vec2 :: [2]f32
|
||||||
|
Vec3 :: [3]f32
|
||||||
|
Vec4 :: [4]f32
|
||||||
|
|
||||||
|
Mat3 :: matrix[3, 3]f32
|
||||||
|
Mat4 :: matrix[4, 4]f32
|
||||||
|
|
||||||
|
Color :: struct { r, g, b, a: u8 }
|
||||||
|
|
||||||
|
orthographic_projection_2d :: proc(left, right, bottom, top: f32) -> Mat4 {
|
||||||
|
result := identity();
|
||||||
|
|
||||||
|
result[0][0] = 2 / (right - left);
|
||||||
|
result[3][0] = -(right + left) / (right - left);
|
||||||
|
result[1][1] = 2 / (top - bottom);
|
||||||
|
result[3][1] = -(top + bottom) / (top - bottom);
|
||||||
|
result[2][2] = -1;
|
||||||
|
result[3][3] = 1;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
identity :: proc() -> Mat4 {
|
||||||
|
return Mat4 {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translate :: proc(m: ^Mat4, v: Vec3) {
|
||||||
|
temp := identity()
|
||||||
|
|
||||||
|
temp[3][0] = v.x
|
||||||
|
temp[3][1] = v.y
|
||||||
|
temp[3][2] = v.z
|
||||||
|
|
||||||
|
m^ = temp * m^
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate :: proc(m: ^Mat4, angle: f32, axis: Vec3) {
|
||||||
|
temp := identity()
|
||||||
|
|
||||||
|
c := math.cos(math.to_radians(angle))
|
||||||
|
s := math.sin(math.to_radians(angle))
|
||||||
|
t := 1 - c
|
||||||
|
|
||||||
|
x, y, z := axis.x, axis.y, axis.z
|
||||||
|
|
||||||
|
res := Mat4{
|
||||||
|
t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0,
|
||||||
|
t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0,
|
||||||
|
t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0,
|
||||||
|
0 , 0, 0, 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
m^ = res * m^
|
||||||
|
}
|
||||||
|
|
||||||
|
scale :: proc(m: ^Mat4, v: Vec3) {
|
||||||
|
temp := identity()
|
||||||
|
|
||||||
|
temp[0][0] = v.x
|
||||||
|
temp[1][1] = v.y
|
||||||
|
temp[2][2] = v.z
|
||||||
|
|
||||||
|
m^ = temp * m^
|
||||||
|
}
|
||||||
|
|
||||||
|
mult_color :: proc(color: Color, scalar: f32) -> Color {
|
||||||
|
color := color
|
||||||
|
|
||||||
|
color.r = clamp(cast(u8)(cast(f32)color.r * scalar), 0, 255)
|
||||||
|
color.g = clamp(cast(u8)(cast(f32)color.g * scalar), 0, 255)
|
||||||
|
color.b = clamp(cast(u8)(cast(f32)color.b * scalar), 0, 255)
|
||||||
|
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color {
|
||||||
|
c := v * s;
|
||||||
|
x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1)));
|
||||||
|
m := v - c;
|
||||||
|
|
||||||
|
r, g, b: f32
|
||||||
|
|
||||||
|
if (h >= 0 && h < 60) {
|
||||||
|
r = c
|
||||||
|
g = x
|
||||||
|
b = 0
|
||||||
|
} else if (h >= 60 && h < 120) {
|
||||||
|
r = x
|
||||||
|
g = c
|
||||||
|
b = 0
|
||||||
|
} else if (h >= 120 && h < 180) {
|
||||||
|
r = 0
|
||||||
|
g = c
|
||||||
|
b = x
|
||||||
|
} else if (h >= 180 && h < 240) {
|
||||||
|
r = 0
|
||||||
|
g = x
|
||||||
|
b = c
|
||||||
|
} else if (h >= 240 && h < 300) {
|
||||||
|
r = x
|
||||||
|
g = 0
|
||||||
|
b = c
|
||||||
|
} else {
|
||||||
|
r = c
|
||||||
|
g = 0
|
||||||
|
b = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255};
|
||||||
|
}
|
||||||
|
|
||||||
|
determine_color_contrast :: proc(bg: Color) -> Color {
|
||||||
|
white_contrast := get_contrast(bg, COLOR_WHITE);
|
||||||
|
black_contrast := get_contrast(bg, COLOR_BLACK);
|
||||||
|
|
||||||
|
return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
get_srgb :: proc(component: f32) -> f32 {
|
||||||
|
return (component / 255 <= 0.03928
|
||||||
|
? component / 255 / 12.92
|
||||||
|
: math.pow((component / 255 + 0.055) / 1.055, 2.4));
|
||||||
|
}
|
||||||
|
|
||||||
|
get_luminance :: proc(color: Color) -> f32 {
|
||||||
|
return ((0.2126 * get_srgb(cast(f32)color.r)) +
|
||||||
|
(0.7152 * get_srgb(cast(f32)color.g)) +
|
||||||
|
(0.0722 * get_srgb(cast(f32)color.b))) / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
get_contrast :: proc(fg: Color, bg: Color) -> f32 {
|
||||||
|
l1 := get_luminance(fg)
|
||||||
|
l2 := get_luminance(bg)
|
||||||
|
|
||||||
|
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user