sync
This commit is contained in:
commit
371c1699be
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
7
.idea/.gitignore
generated
vendored
Normal file
7
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# GitHub Copilot persisted chat sessions
|
||||||
|
/copilot/chatSessions
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/untitled.iml" filepath="$PROJECT_DIR$/.idea/untitled.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/untitled.iml
generated
Normal file
12
.idea/untitled.iml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.idea/copilot/chatSessions" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2172
Cargo.lock
generated
Normal file
2172
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "radar-g"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
imgui = "0.12.0"
|
||||||
|
glow = "0.13.1"
|
||||||
|
imgui-glow-renderer = "0.12.0"
|
||||||
|
imgui-winit-support = "0.12.0"
|
||||||
|
glutin = "0.31.3"
|
||||||
|
glutin-winit = "0.4.2"
|
||||||
|
copypasta = "0.10.1"
|
||||||
|
raw-window-handle = "0.5.2"
|
||||||
|
winit = "0.29.15"
|
||||||
|
cgmath = "0.18.0"
|
||||||
|
nalgebra-glm = "0.18.0"
|
||||||
|
regex = "1.10.5"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
nom = "7.1.3"
|
||||||
|
thiserror = "1.0.61"
|
||||||
|
log = "0.4.22"
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
bytemuck = { version = "1.16.1", features = ["derive"] }
|
||||||
BIN
resources/Dokdo-Regular.ttf
Normal file
BIN
resources/Dokdo-Regular.ttf
Normal file
Binary file not shown.
93
resources/LICENSE-Dokdo.txt
Normal file
93
resources/LICENSE-Dokdo.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright (c) 2005-2017 FONTRIX. All Rights Reserved.
|
||||||
|
|
||||||
|
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.
|
||||||
201
resources/LICENSE-Roboto.txt
Normal file
201
resources/LICENSE-Roboto.txt
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
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.
|
||||||
BIN
resources/Lenna.jpg
Normal file
BIN
resources/Lenna.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
resources/Roboto-Regular.ttf
Normal file
BIN
resources/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
resources/mplus-1p-regular.ttf
Normal file
BIN
resources/mplus-1p-regular.ttf
Normal file
Binary file not shown.
22
shaders/agg-fast-path.frag
Normal file
22
shaders/agg-fast-path.frag
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#include "antialias/antialias.glsl"
|
||||||
|
|
||||||
|
// Varyings
|
||||||
|
// ------------------------------------
|
||||||
|
in vec4 v_color;
|
||||||
|
in float v_distance;
|
||||||
|
in float v_linewidth;
|
||||||
|
in float v_antialias;
|
||||||
|
|
||||||
|
// Main
|
||||||
|
// ------------------------------------
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
<viewport.clipping()>;
|
||||||
|
|
||||||
|
if (v_color.a == 0) { discard; }
|
||||||
|
gl_FragColor = stroke(v_distance, v_linewidth, v_antialias, v_color);
|
||||||
|
}
|
||||||
77
shaders/agg-fast-path.vert
Normal file
77
shaders/agg-fast-path.vert
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Hooks:
|
||||||
|
// <transform> : vec4 function(position, ...)
|
||||||
|
//
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
#include "misc/viewport-NDC.glsl"
|
||||||
|
|
||||||
|
// Externs
|
||||||
|
// ------------------------------------
|
||||||
|
// extern vec3 prev;
|
||||||
|
// extern vec3 curr;
|
||||||
|
// extern vec3 next;
|
||||||
|
// extern float id;
|
||||||
|
// extern vec4 color;
|
||||||
|
// extern float antialias;
|
||||||
|
// extern float linewidth;
|
||||||
|
|
||||||
|
// Varyings
|
||||||
|
// ------------------------------------
|
||||||
|
out float v_antialias;
|
||||||
|
out float v_linewidth;
|
||||||
|
out float v_distance;
|
||||||
|
out vec4 v_color;
|
||||||
|
|
||||||
|
|
||||||
|
// Main
|
||||||
|
// ------------------------------------
|
||||||
|
void main ()
|
||||||
|
{
|
||||||
|
// This function is externally generated
|
||||||
|
fetch_uniforms();
|
||||||
|
v_linewidth = linewidth;
|
||||||
|
v_antialias = antialias;
|
||||||
|
v_color = color;
|
||||||
|
|
||||||
|
// transform prev/curr/next
|
||||||
|
vec4 prev_ = <transform(prev)>;
|
||||||
|
vec4 curr_ = <transform(curr)>;
|
||||||
|
vec4 next_ = <transform(next)>;
|
||||||
|
|
||||||
|
// prev/curr/next in viewport coordinates
|
||||||
|
vec2 _prev = NDC_to_viewport(prev_, <viewport.viewport_global>.zw);
|
||||||
|
vec2 _curr = NDC_to_viewport(curr_, <viewport.viewport_global>.zw);
|
||||||
|
vec2 _next = NDC_to_viewport(next_, <viewport.viewport_global>.zw);
|
||||||
|
|
||||||
|
// Compute vertex final position (in viewport coordinates)
|
||||||
|
float w = linewidth/2.0 + 1.5*antialias;
|
||||||
|
float z;
|
||||||
|
vec2 P;
|
||||||
|
if( curr == prev) {
|
||||||
|
vec2 v = normalize(_next.xy - _curr.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v.y,v.x));
|
||||||
|
P = _curr.xy + normal*w*id;
|
||||||
|
} else if (curr == next) {
|
||||||
|
vec2 v = normalize(_curr.xy - _prev.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v.y,v.x));
|
||||||
|
P = _curr.xy + normal*w*id;
|
||||||
|
} else {
|
||||||
|
vec2 v0 = normalize(_curr.xy - _prev.xy);
|
||||||
|
vec2 v1 = normalize(_next.xy - _curr.xy);
|
||||||
|
vec2 normal = normalize(vec2(-v0.y,v0.x));
|
||||||
|
vec2 tangent = normalize(v0+v1);
|
||||||
|
vec2 miter = vec2(-tangent.y, tangent.x);
|
||||||
|
float l = abs(w / dot(miter,normal));
|
||||||
|
P = _curr.xy + miter*l*sign(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( abs(id) > 1.5 ) v_color.a = 0.0;
|
||||||
|
|
||||||
|
v_distance = w*id;
|
||||||
|
gl_Position = viewport_to_NDC(P, <viewport.viewport_global>.zw, curr_.z / curr_.w);
|
||||||
|
|
||||||
|
<viewport.transform()>;
|
||||||
|
}
|
||||||
7
shaders/antialias/antialias.glsl
Normal file
7
shaders/antialias/antialias.glsl
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#include "antialias/stroke.glsl"
|
||||||
|
#include "antialias/filled.glsl"
|
||||||
|
#include "antialias/outline.glsl"
|
||||||
45
shaders/antialias/filled.glsl
Normal file
45
shaders/antialias/filled.glsl
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------
|
||||||
|
Compute antialiased fragment color for a filled shape.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
|
||||||
|
distance : signed distance to border (in pixels)
|
||||||
|
linewidth: Stroke line width (in pixels)
|
||||||
|
antialias: Stroke antialiased area (in pixels)
|
||||||
|
fill: Fill color
|
||||||
|
|
||||||
|
Return:
|
||||||
|
-------
|
||||||
|
Fragment color (vec4)
|
||||||
|
|
||||||
|
--------------------------------------------------------- */
|
||||||
|
|
||||||
|
vec4 filled(float distance, float linewidth, float antialias, vec4 bg_color)
|
||||||
|
{
|
||||||
|
vec4 frag_color;
|
||||||
|
float t = linewidth/2.0 - antialias;
|
||||||
|
float signed_distance = distance;
|
||||||
|
float border_distance = abs(signed_distance) - t;
|
||||||
|
float alpha = border_distance/antialias;
|
||||||
|
alpha = exp(-alpha*alpha);
|
||||||
|
|
||||||
|
if( border_distance < 0.0 )
|
||||||
|
frag_color = bg_color;
|
||||||
|
else if( signed_distance < 0.0 )
|
||||||
|
frag_color = bg_color;
|
||||||
|
else
|
||||||
|
frag_color = vec4(bg_color.rgb, alpha * bg_color.a);
|
||||||
|
|
||||||
|
return frag_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 filled(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color)
|
||||||
|
{
|
||||||
|
return filled(distance, linewidth, antialias, fg_color);
|
||||||
|
}
|
||||||
45
shaders/antialias/outline.glsl
Normal file
45
shaders/antialias/outline.glsl
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------
|
||||||
|
Compute antialiased fragment color for an outlined shape.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
|
||||||
|
distance : signed distance to border (in pixels)
|
||||||
|
linewidth: Stroke line width (in pixels)
|
||||||
|
antialias: Stroke antialiased area (in pixels)
|
||||||
|
stroke: Stroke color
|
||||||
|
fill: Fill color
|
||||||
|
|
||||||
|
Return:
|
||||||
|
-------
|
||||||
|
Fragment color (vec4)
|
||||||
|
|
||||||
|
--------------------------------------------------------- */
|
||||||
|
|
||||||
|
vec4 outline(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color)
|
||||||
|
{
|
||||||
|
vec4 frag_color;
|
||||||
|
float t = linewidth/2.0 - antialias;
|
||||||
|
float signed_distance = distance;
|
||||||
|
float border_distance = abs(signed_distance) - t;
|
||||||
|
float alpha = border_distance/antialias;
|
||||||
|
alpha = exp(-alpha*alpha);
|
||||||
|
|
||||||
|
if( border_distance < 0.0 )
|
||||||
|
frag_color = fg_color;
|
||||||
|
else if( signed_distance < 0.0 )
|
||||||
|
frag_color = mix(bg_color, fg_color, sqrt(alpha));
|
||||||
|
else {
|
||||||
|
if( abs(signed_distance) < (linewidth/2.0 + antialias) ) {
|
||||||
|
frag_color = vec4(fg_color.rgb, fg_color.a * alpha);
|
||||||
|
} else {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frag_color;
|
||||||
|
}
|
||||||
43
shaders/antialias/stroke.glsl
Normal file
43
shaders/antialias/stroke.glsl
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------
|
||||||
|
Compute antialiased fragment color for a stroke line.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
|
||||||
|
distance : signed distance to border (in pixels)
|
||||||
|
linewidth: Stroke line width (in pixels)
|
||||||
|
antialias: Stroke antialiased area (in pixels)
|
||||||
|
stroke: Stroke color
|
||||||
|
|
||||||
|
Return:
|
||||||
|
-------
|
||||||
|
Fragment color (vec4)
|
||||||
|
|
||||||
|
--------------------------------------------------------- */
|
||||||
|
|
||||||
|
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color)
|
||||||
|
{
|
||||||
|
vec4 frag_color;
|
||||||
|
float t = linewidth/2.0 - antialias;
|
||||||
|
float signed_distance = distance;
|
||||||
|
float border_distance = abs(signed_distance) - t;
|
||||||
|
float alpha = border_distance/antialias;
|
||||||
|
alpha = exp(-alpha*alpha);
|
||||||
|
|
||||||
|
if( border_distance < 0.0 )
|
||||||
|
frag_color = fg_color;
|
||||||
|
else
|
||||||
|
frag_color = vec4(fg_color.rgb, fg_color.a * alpha);
|
||||||
|
|
||||||
|
return frag_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color)
|
||||||
|
{
|
||||||
|
return stroke(distance, linewidth, antialias, fg_color);
|
||||||
|
}
|
||||||
14
shaders/geo-position-struct.glsl
Normal file
14
shaders/geo-position-struct.glsl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef _GLUMPY__GEO_POSITION_STRUCT__
|
||||||
|
#define _GLUMPY__GEO_POSITION_STRUCT__
|
||||||
|
struct GeoPosition
|
||||||
|
{
|
||||||
|
float longitude;
|
||||||
|
float latitude;
|
||||||
|
bool frozen; // Prevent further transformation if true
|
||||||
|
};
|
||||||
|
#endif
|
||||||
39
shaders/line.vert
Normal file
39
shaders/line.vert
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
uniform vec2 resolution;
|
||||||
|
uniform float antialias;
|
||||||
|
|
||||||
|
layout() float thickness;
|
||||||
|
attribute vec2 p0, p1, uv;
|
||||||
|
|
||||||
|
varying float v_alpha, v_thickness;
|
||||||
|
varying vec2 v_p0, v_p1, v_p;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
if( abs(thickness) < 1.0 ) {
|
||||||
|
v_thickness = 1.0;
|
||||||
|
v_alpha = abs(thickness);
|
||||||
|
} else {
|
||||||
|
v_thickness = abs(thickness);
|
||||||
|
v_alpha = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float t = v_thickness/2.0 + antialias;
|
||||||
|
float l = length(p1-p0);
|
||||||
|
float u = 2.0*uv.x - 1.0;
|
||||||
|
float v = 2.0*uv.y - 1.0;
|
||||||
|
|
||||||
|
// Screen space
|
||||||
|
vec2 T = normalize(p1-p0);
|
||||||
|
vec2 O = vec2(-T.y , T.x);
|
||||||
|
vec2 p = p0 + vec2(0.5,0.5) + uv.x*T*l + u*T*t + v*O*t;
|
||||||
|
gl_Position = vec4(2.0*p/resolution-1.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Local space
|
||||||
|
T = vec2(1.0, 0.0);
|
||||||
|
O = vec2(0.0, 1.0);
|
||||||
|
p = uv.x*T*l + u*T*t + v*O*t;
|
||||||
|
|
||||||
|
v_p0 = vec2(0.0, 0.0);
|
||||||
|
v_p1 = vec2( l, 0.0);
|
||||||
|
v_p = p;
|
||||||
|
}
|
||||||
54
shaders/math/constants.glsl
Normal file
54
shaders/math/constants.glsl
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef _GLUMPY__CONSTANTS__
|
||||||
|
#define _GLUMPY__CONSTANTS__
|
||||||
|
|
||||||
|
// The base of natural logarithms (e)
|
||||||
|
const float M_E = 2.71828182845904523536028747135266250;
|
||||||
|
|
||||||
|
// The logarithm to base 2 of M_E (log2(e))
|
||||||
|
const float M_LOG2E = 1.44269504088896340735992468100189214;
|
||||||
|
|
||||||
|
// The logarithm to base 10 of M_E (log10(e))
|
||||||
|
const float M_LOG10E = 0.434294481903251827651128918916605082;
|
||||||
|
|
||||||
|
// The natural logarithm of 2 (loge(2))
|
||||||
|
const float M_LN2 = 0.693147180559945309417232121458176568;
|
||||||
|
|
||||||
|
// The natural logarithm of 10 (loge(10))
|
||||||
|
const float M_LN10 = 2.30258509299404568401799145468436421;
|
||||||
|
|
||||||
|
// Pi, the ratio of a circle's circumference to its diameter.
|
||||||
|
const float M_PI = 3.14159265358979323846264338327950288;
|
||||||
|
|
||||||
|
// Pi divided by two (pi/2)
|
||||||
|
const float M_PI_2 = 1.57079632679489661923132169163975144;
|
||||||
|
|
||||||
|
// Pi divided by four (pi/4)
|
||||||
|
const float M_PI_4 = 0.785398163397448309615660845819875721;
|
||||||
|
|
||||||
|
// The reciprocal of pi (1/pi)
|
||||||
|
const float M_1_PI = 0.318309886183790671537767526745028724;
|
||||||
|
|
||||||
|
// Two times the reciprocal of pi (2/pi)
|
||||||
|
const float M_2_PI = 0.636619772367581343075535053490057448;
|
||||||
|
|
||||||
|
// Two times the reciprocal of the square root of pi (2/sqrt(pi))
|
||||||
|
const float M_2_SQRTPI = 1.12837916709551257389615890312154517;
|
||||||
|
|
||||||
|
// The square root of two (sqrt(2))
|
||||||
|
const float M_SQRT2 = 1.41421356237309504880168872420969808;
|
||||||
|
|
||||||
|
// The reciprocal of the square root of two (1/sqrt(2))
|
||||||
|
const float M_SQRT1_2 = 0.707106781186547524400844362104849039;
|
||||||
|
|
||||||
|
// 1 degree in radians
|
||||||
|
const float degree = 180.0/M_PI;
|
||||||
|
|
||||||
|
// 1 radian in degrees
|
||||||
|
const float radian = M_PI/180.0;
|
||||||
|
|
||||||
|
#endif
|
||||||
20
shaders/misc/viewport-NDC.glsl
Normal file
20
shaders/misc/viewport-NDC.glsl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
vec2 NDC_to_viewport(vec4 position, vec2 viewport)
|
||||||
|
{
|
||||||
|
vec2 p = position.xy/position.w;
|
||||||
|
return (p+1.0)/2.0 * viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 viewport_to_NDC(vec2 position, vec2 viewport)
|
||||||
|
{
|
||||||
|
return vec4(2.0*(position/viewport) - 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 viewport_to_NDC(vec2 position, vec2 viewport, float z)
|
||||||
|
{
|
||||||
|
return vec4(2.0*(position/viewport) - 1.0, z, 1.0);
|
||||||
|
}
|
||||||
10
shaders/pcolormesh.frag
Normal file
10
shaders/pcolormesh.frag
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 location;
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec4 position = inverse(location);
|
||||||
|
|
||||||
|
fragColor = vec4(position.x, position.y, 0.0, 1.0);
|
||||||
|
|
||||||
|
}
|
||||||
54
shaders/pcolormesh.geom
Normal file
54
shaders/pcolormesh.geom
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (points) in;
|
||||||
|
layout (triangle_strip, max_vertices = 4) out;
|
||||||
|
|
||||||
|
uniform vec2 dpi;
|
||||||
|
uniform vec2 origin;
|
||||||
|
in vec4 near_coord[1];
|
||||||
|
|
||||||
|
out vec2 location;
|
||||||
|
|
||||||
|
|
||||||
|
void max_bound(vec4 near_coord, out vec2 _a0, out vec2 _a1, out vec2 _b0, out vec2 _b1) {
|
||||||
|
|
||||||
|
vec2 a0 = vec2(near_coord.x, near_coord.z);
|
||||||
|
vec2 a1 = vec2(near_coord.x, near_coord.w);
|
||||||
|
|
||||||
|
vec2 b0 = vec2(near_coord.y, near_coord.z);
|
||||||
|
vec2 b1 = vec2(near_coord.y, near_coord.w);
|
||||||
|
|
||||||
|
vec2 a_c = (a1 + b1) / 2;
|
||||||
|
|
||||||
|
float l1 = length(a_c - origin);
|
||||||
|
|
||||||
|
float rate = near_coord.w / l1;
|
||||||
|
|
||||||
|
vec2 r1 = a1 - origin;
|
||||||
|
vec2 r2 = b1 - origin;
|
||||||
|
|
||||||
|
vec2 r1_ = r1 * rate;
|
||||||
|
vec2 r2_ = r2 * rate;
|
||||||
|
|
||||||
|
_a0 = a0;
|
||||||
|
_a1 = a1;
|
||||||
|
_b0 = r2_ + origin;
|
||||||
|
_b1 = r1_ + origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 position = vec4(0,0,0,0);
|
||||||
|
|
||||||
|
vec2 a0, a1, b0, b1;
|
||||||
|
|
||||||
|
max_bound(near_coord[0], a0, a1, b0, b1);
|
||||||
|
|
||||||
|
gl_Position = vec4(a0, 0.0, 1.0);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(a1, 0.0, 1.0);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(b0, 0.0, 1.0);
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = vec4(b1, 0.0, 1.0);
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
17
shaders/pcolormesh.vert
Normal file
17
shaders/pcolormesh.vert
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 in_position;
|
||||||
|
layout (location = 1) in vec3 value;
|
||||||
|
layout (location = 2) in vec4 coord;
|
||||||
|
|
||||||
|
out vec4 near_coord;
|
||||||
|
|
||||||
|
uniform vec2 dpi;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
near_coord = coord;
|
||||||
|
|
||||||
|
vec2 cart = forward(in_position);
|
||||||
|
|
||||||
|
gl_Position = cart;
|
||||||
|
}
|
||||||
3
shaders/polar.frag
Normal file
3
shaders/polar.frag
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
void main(){}
|
||||||
1
shaders/polar.vert
Normal file
1
shaders/polar.vert
Normal file
@ -0,0 +1 @@
|
|||||||
|
#version 330 core
|
||||||
33
shaders/transform/polar.glsl
Normal file
33
shaders/transform/polar.glsl
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "math/constants.glsl"
|
||||||
|
|
||||||
|
uniform float polar_origin;
|
||||||
|
|
||||||
|
|
||||||
|
vec4 forward(float rho, float theta, float z, float w)
|
||||||
|
{
|
||||||
|
return vec4(rho * cos(theta + polar_origin),
|
||||||
|
rho * sin(theta + polar_origin),
|
||||||
|
z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 forward(float x, float y) {return forward(x, y, 0.0, 1.0);}
|
||||||
|
vec4 forward(float x, float y, float z) {return forward(x, y, z, 1.0);}
|
||||||
|
vec4 forward(vec2 P) { return forward(P.x, P.y); }
|
||||||
|
vec4 forward(vec3 P) { return forward(P.x, P.y, P.z, 1.0); }
|
||||||
|
vec4 forward(vec4 P) { return forward(P.x, P.y, P.z, P.w); }
|
||||||
|
|
||||||
|
vec4 inverse(float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
float rho = length(vec2(x,y));
|
||||||
|
float theta = atan(y,x);
|
||||||
|
if( theta < 0.0 )
|
||||||
|
theta = 2.0*M_PI+theta;
|
||||||
|
return vec4(rho, theta-polar_origin, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec4 inverse(float x, float y) {return inverse(x,y,0.0,1.0); }
|
||||||
|
vec4 inverse(float x, float y, float z) {return inverse(x,y,z,1.0); }
|
||||||
|
vec4 inverse(vec2 P) { return inverse(P.x, P.y, 0.0, 1.0); }
|
||||||
|
vec4 inverse(vec3 P) { return inverse(P.x, P.y, P.z, 1.0); }
|
||||||
|
vec4 inverse(vec4 P) { return inverse(P.x, P.y, P.z, P.w); }
|
||||||
50
shaders/transform/position.glsl
Normal file
50
shaders/transform/position.glsl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#include "geo-position-struct.glsl"
|
||||||
|
|
||||||
|
vec4 position(float x)
|
||||||
|
{
|
||||||
|
return vec4(x, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, float y)
|
||||||
|
{
|
||||||
|
return vec4(x, y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec2 xy)
|
||||||
|
{
|
||||||
|
return vec4(xy, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, float y, float z)
|
||||||
|
{
|
||||||
|
return vec4(x, y, z, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec3 xyz)
|
||||||
|
{
|
||||||
|
return vec4(xyz, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec4 xyzw)
|
||||||
|
{
|
||||||
|
return xyzw;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(vec2 xy, float z)
|
||||||
|
{
|
||||||
|
return vec4(xy, z, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(float x, vec2 yz)
|
||||||
|
{
|
||||||
|
return vec4(x, yz, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 position(GeoPosition P)
|
||||||
|
{
|
||||||
|
return vec4(P.longitude, P.latitude, 0.0, 1.0);
|
||||||
|
}
|
||||||
15
shaders/transform/trackball.glsl
Normal file
15
shaders/transform/trackball.glsl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
uniform mat4 trackball_view;
|
||||||
|
uniform mat4 trackball_model;
|
||||||
|
uniform mat4 trackball_projection;
|
||||||
|
|
||||||
|
vec4 transform(vec4 position)
|
||||||
|
{
|
||||||
|
return trackball_projection
|
||||||
|
* trackball_view
|
||||||
|
* trackball_model
|
||||||
|
* position;
|
||||||
|
}
|
||||||
50
shaders/transform/viewport.glsl
Normal file
50
shaders/transform/viewport.glsl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
|
||||||
|
// Distributed under the (new) BSD License.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
uniform vec4 viewport_local;
|
||||||
|
uniform vec4 viewport_global;
|
||||||
|
uniform int viewport_transform;
|
||||||
|
uniform int viewport_clipping;
|
||||||
|
|
||||||
|
#ifdef _GLUMPY__VERTEX_SHADER__
|
||||||
|
void transform()
|
||||||
|
{
|
||||||
|
if (viewport_transform == 0) return;
|
||||||
|
|
||||||
|
vec4 position = gl_Position;
|
||||||
|
|
||||||
|
float w = viewport_local.z / viewport_global.z;
|
||||||
|
float h = viewport_local.w / viewport_global.w;
|
||||||
|
float x = 2.0*(viewport_local.x / viewport_global.z) - 1.0 + w;
|
||||||
|
float y = 2.0*(viewport_local.y / viewport_global.w) - 1.0 + h;
|
||||||
|
|
||||||
|
gl_Position = vec4((x + w*position.x/position.w)*position.w,
|
||||||
|
(y + h*position.y/position.w)*position.w,
|
||||||
|
position.z, position.w);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _GLUMPY__FRAGMENT_SHADER__
|
||||||
|
void clipping()
|
||||||
|
{
|
||||||
|
if (viewport_clipping == 0) return;
|
||||||
|
|
||||||
|
vec2 position = gl_FragCoord.xy;
|
||||||
|
if( position.x < (viewport_local.x)) discard;
|
||||||
|
else if( position.x > (viewport_local.x+viewport_local.z)) discard;
|
||||||
|
else if( position.y < (viewport_local.y)) discard;
|
||||||
|
else if( position.y > (viewport_local.y+viewport_local.w)) discard;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if( length(position.x - viewport_local.x) < 1.0 )
|
||||||
|
gl_FragColor = vec4(0,0,0,1);
|
||||||
|
else if( length(position.x - viewport_local.x - viewport_local.z) < 1.0 )
|
||||||
|
gl_FragColor = vec4(0,0,0,1);
|
||||||
|
else if( length(position.y - viewport_local.y) < 1.0 )
|
||||||
|
gl_FragColor = vec4(0,0,0,1);
|
||||||
|
else if( length(position.y - viewport_local.y - viewport_local.w) < 1.0 )
|
||||||
|
gl_FragColor = vec4(0,0,0,1);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
#endif
|
||||||
55
src/camera.rs
Normal file
55
src/camera.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use std::num::NonZeroU32;
|
||||||
|
use cgmath::Euler;
|
||||||
|
use glow::NativeProgram;
|
||||||
|
use nalgebra_glm::{
|
||||||
|
Vec3, Mat4x4, look_at
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct Camera {
|
||||||
|
pos: Vec3,
|
||||||
|
upward: Vec3,
|
||||||
|
front: Vec3
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CameraPositon = Vec3;
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub(crate) fn new(world_loc: CameraPositon, upward: Vec3, front: Vec3) -> Self {
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pos: world_loc,
|
||||||
|
upward,
|
||||||
|
front
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> CameraPositon {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&mut self, pos: CameraPositon) {
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_front(&self) -> Vec3 {
|
||||||
|
self.front
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_front(&mut self, front: Vec3) {
|
||||||
|
self.front = front;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_upward(&self) -> Vec3 {
|
||||||
|
self.upward
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_upward(&mut self, upward: Vec3) {
|
||||||
|
self.upward = upward;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view_matrix(&self) -> Mat4x4 {
|
||||||
|
let l = self.pos + self.front;
|
||||||
|
look_at(&self.pos, &l, &self.upward)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
src/components/mod.rs
Normal file
13
src/components/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
mod program;
|
||||||
|
mod shader;
|
||||||
|
mod snippets;
|
||||||
|
|
||||||
|
pub use program::Program;
|
||||||
|
pub use shader::{fetchcode, Shader};
|
||||||
|
pub use snippets::*;
|
||||||
|
|
||||||
|
pub trait CodeComponent: Sized {
|
||||||
|
fn add_snippet_before(self, snippet: Snippet) -> Self;
|
||||||
|
|
||||||
|
fn add_snippet_after(self, snippet: Snippet) -> Self;
|
||||||
|
}
|
||||||
137
src/components/program.rs
Normal file
137
src/components/program.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
use super::shader::Shader;
|
||||||
|
use super::snippets::{CodeType, Snippet};
|
||||||
|
use crate::graphics::transforms::{viewport::Viewport, Transform};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Program {
|
||||||
|
version: &'static str,
|
||||||
|
vertex: Shader,
|
||||||
|
fragment: Shader,
|
||||||
|
|
||||||
|
geometry: Option<Shader>,
|
||||||
|
pub native_program: Option<<glow::Context as HasContext>::Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new(
|
||||||
|
vertex: Shader,
|
||||||
|
fragment: Shader,
|
||||||
|
geometry: Option<Shader>,
|
||||||
|
version: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
let res = Self {
|
||||||
|
version,
|
||||||
|
vertex,
|
||||||
|
fragment,
|
||||||
|
geometry,
|
||||||
|
native_program: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertex(&self) -> &Shader {
|
||||||
|
&self.vertex
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fragment(&self) -> &Shader {
|
||||||
|
&self.fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
||||||
|
self.vertex.set_hook(hook, code);
|
||||||
|
self.fragment.set_hook(hook, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_transform<T: Transform>(&mut self, value: &T) {
|
||||||
|
self.set_hook("transform", value.snippet());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_viewport(&mut self, viewport: &Viewport) {
|
||||||
|
self.set_hook("viewport", viewport.snippet());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
let vertex_array = gl
|
||||||
|
.create_vertex_array()
|
||||||
|
.expect("Cannot create vertex array");
|
||||||
|
|
||||||
|
let program = gl.create_program().expect("Cannot create program");
|
||||||
|
|
||||||
|
let vertex_shader =
|
||||||
|
self.create_shader(gl, &self.vertex.to_string(), glow::VERTEX_SHADER);
|
||||||
|
let fragment_shader =
|
||||||
|
self.create_shader(gl, &self.fragment.to_string(), glow::FRAGMENT_SHADER);
|
||||||
|
|
||||||
|
gl.link_program(program);
|
||||||
|
if !gl.get_program_link_status(program) {
|
||||||
|
panic!("{}", gl.get_program_info_log(program));
|
||||||
|
}
|
||||||
|
gl.detach_shader(program, vertex_shader);
|
||||||
|
gl.detach_shader(program, fragment_shader);
|
||||||
|
|
||||||
|
self.native_program = Some(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_shader(
|
||||||
|
&self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
code: &str,
|
||||||
|
shader_type: u32,
|
||||||
|
) -> <glow::Context as HasContext>::Shader {
|
||||||
|
let shader = unsafe {
|
||||||
|
let shader = gl
|
||||||
|
.create_shader(glow::VERTEX_SHADER)
|
||||||
|
.expect("Cannot create shader");
|
||||||
|
gl.shader_source(
|
||||||
|
shader,
|
||||||
|
&format!("{}\n{}", &format!("#version {}", self.version), code),
|
||||||
|
);
|
||||||
|
gl.compile_shader(shader);
|
||||||
|
if !gl.get_shader_compile_status(shader) {
|
||||||
|
panic!("{}", gl.get_shader_info_log(shader))
|
||||||
|
}
|
||||||
|
shader
|
||||||
|
};
|
||||||
|
shader
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
self.native_program.map(|p| gl.delete_program(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.clear_color(0.05, 0.05, 0.1, 1.0);
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
gl.use_program(self.native_program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_program() {
|
||||||
|
let vertex = Shader::new(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let fragment = Shader::new(
|
||||||
|
glow::FRAGMENT_SHADER,
|
||||||
|
CodeType::<&'static str>::Path("agg-fast-path.frag".into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let program = Program::new(vertex, fragment, None, "330 core");
|
||||||
|
}
|
||||||
|
}
|
||||||
220
src/components/shader.rs
Normal file
220
src/components/shader.rs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
snippets::{merge_includes, CodeType, Snippet, Variable},
|
||||||
|
CodeComponent,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
errors::{Error, Result},
|
||||||
|
utils::{find_file, CodeBlock},
|
||||||
|
};
|
||||||
|
use log::{info, warn};
|
||||||
|
pub use utils::fetchcode;
|
||||||
|
pub type ShaderType = u32;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Shader {
|
||||||
|
code: String,
|
||||||
|
target: u32,
|
||||||
|
parsed: CodeBlock,
|
||||||
|
pub hooks: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Shader {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shader {
|
||||||
|
pub fn new<S: Borrow<str>>(target: ShaderType, code: CodeType<S>) -> Result<Self> {
|
||||||
|
let code = match code {
|
||||||
|
CodeType::Code(code) => code.borrow().to_string(),
|
||||||
|
CodeType::Path(path) => {
|
||||||
|
let code = find_file(path).expect("Failed to find file");
|
||||||
|
code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let code = merge_includes(code).map_err(|e| Error::InvalidSnippet(e.to_string()))?;
|
||||||
|
let parsed = CodeBlock::new(&code)?;
|
||||||
|
|
||||||
|
let hooks = parsed.all_hooks().iter().map(|h| h.name.clone()).collect();
|
||||||
|
info!("Shader:{} Hooks: {:?}", target, hooks);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
target,
|
||||||
|
code,
|
||||||
|
parsed,
|
||||||
|
hooks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&self) {}
|
||||||
|
|
||||||
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
||||||
|
self.parsed.set_hook(hook, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeComponent for Shader {
|
||||||
|
fn add_snippet_after(self, snippet: Snippet) -> Self {
|
||||||
|
let new_code = self.to_string() + &snippet.to_string();
|
||||||
|
let result = Self::new(self.target, CodeType::Code(new_code)).unwrap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_snippet_before(self, snippet: Snippet) -> Self {
|
||||||
|
let new_code = snippet.to_string() + &self.to_string();
|
||||||
|
let result = Self::new(self.target, CodeType::Code(new_code)).unwrap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod utils {
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{CodeType, Snippet},
|
||||||
|
utils::CodeBlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
static TYPES: Lazy<HashMap<usize, &'static str>> = Lazy::new(|| {
|
||||||
|
[
|
||||||
|
(1, "float"),
|
||||||
|
(2, "vec2 "),
|
||||||
|
(3, "vec3 "),
|
||||||
|
(4, "vec4 "),
|
||||||
|
(9, "mat3 "),
|
||||||
|
(16, "mat4 "),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect::<std::collections::HashMap<_, _>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn fetchcode<'a, V: AsRef<[(&'a str, usize)]>>(data_types: V, prefix: &str) -> Snippet {
|
||||||
|
let mut header = String::from(
|
||||||
|
"
|
||||||
|
uniform sampler2D uniforms;
|
||||||
|
uniform vec3 uniforms_shape;
|
||||||
|
layout(location = 0) in float collection_index;\n
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
for data_type in data_types.as_ref().iter() {
|
||||||
|
if data_type.0 != "__unused__" {
|
||||||
|
if let Some(type_name) = TYPES.get(&data_type.1) {
|
||||||
|
header.push_str(&format!("out {} {}{};\n", type_name, prefix, data_type.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = String::from(
|
||||||
|
"
|
||||||
|
void fetch_uniforms() {
|
||||||
|
float rows = uniforms_shape.x;
|
||||||
|
float cols = uniforms_shape.y;
|
||||||
|
float count = uniforms_shape.z;
|
||||||
|
float index = collection_index;
|
||||||
|
int index_x = int(mod(index, (floor(cols/(count/4.0))))) * int(count/4.0);
|
||||||
|
int index_y = int(floor(index / (floor(cols/(count/4.0)))));
|
||||||
|
float size_x = cols - 1.0;
|
||||||
|
float size_y = rows - 1.0;
|
||||||
|
float ty = 0.0;
|
||||||
|
if (size_y > 0.0)
|
||||||
|
ty = float(index_y)/size_y;
|
||||||
|
int i = index_x;
|
||||||
|
vec4 _uniform;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
for data_type in data_types.as_ref().iter() {
|
||||||
|
if data_type.0 != "__unused__" {
|
||||||
|
let component_count = data_type.1;
|
||||||
|
let mut store = 0;
|
||||||
|
let mut shift = 0;
|
||||||
|
let mut count = component_count;
|
||||||
|
while count > 0 {
|
||||||
|
if store == 0 {
|
||||||
|
body.push_str(&format!(
|
||||||
|
"\n _uniform = texture2D(uniforms, vec2(float(i++)/size_x,ty));\n"
|
||||||
|
));
|
||||||
|
store = 4;
|
||||||
|
}
|
||||||
|
let (a, b) = match store {
|
||||||
|
4 => (
|
||||||
|
"xyzw",
|
||||||
|
match shift {
|
||||||
|
0 => "xyzw",
|
||||||
|
1 => "yzw",
|
||||||
|
2 => "zw",
|
||||||
|
3 => "w",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
3 => (
|
||||||
|
"yzw",
|
||||||
|
match shift {
|
||||||
|
0 => "yzw",
|
||||||
|
1 => "zw",
|
||||||
|
2 => "w",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
2 => (
|
||||||
|
"zw",
|
||||||
|
match shift {
|
||||||
|
0 => "zw",
|
||||||
|
1 => "w",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
1 => ("w", "w"),
|
||||||
|
_ => ("", ""),
|
||||||
|
};
|
||||||
|
let min_length = std::cmp::min(b.len(), count);
|
||||||
|
body.push_str(&format!(
|
||||||
|
" {}{}{} = _uniform.{};\n",
|
||||||
|
prefix,
|
||||||
|
data_type.0,
|
||||||
|
&b[0..min_length],
|
||||||
|
&a[0..min_length]
|
||||||
|
));
|
||||||
|
count -= min_length;
|
||||||
|
shift += min_length;
|
||||||
|
store -= min_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.push_str("}\n\n");
|
||||||
|
let input = header + &body;
|
||||||
|
|
||||||
|
let result = Snippet::new("fetch_uniform", CodeType::Code(input), None).unwrap();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::utils::fetchcode;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shader() {
|
||||||
|
let shader = Shader::new(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{}", shader.parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fetchcode() {
|
||||||
|
let code = fetchcode([("position", 3), ("color", 4)], "a_");
|
||||||
|
println!("{}", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
192
src/components/snippets/mod.rs
Normal file
192
src/components/snippets/mod.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
use regex::{Captures, Regex};
|
||||||
|
use std::{
|
||||||
|
cell::{Ref, RefCell},
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
ops::Add,
|
||||||
|
rc::Rc,
|
||||||
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
pub use util::Variable;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::{Error, Result},
|
||||||
|
utils::{find_file, Code, CodeBlock, Function, Hook, ShareVariable, SnippetCode},
|
||||||
|
};
|
||||||
|
mod util;
|
||||||
|
pub use util::merge_includes;
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
use super::CodeComponent;
|
||||||
|
|
||||||
|
static SNIP_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum InputType {
|
||||||
|
Vertex(usize, Variable),
|
||||||
|
Other(Variable),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CodeType<S: std::borrow::Borrow<str>> {
|
||||||
|
Code(S),
|
||||||
|
Path(std::path::PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Snippet {
|
||||||
|
id: usize,
|
||||||
|
name: &'static str,
|
||||||
|
parsed: CodeBlock,
|
||||||
|
link: Option<Box<Snippet>>,
|
||||||
|
alias: HashMap<String, String>,
|
||||||
|
chained: String,
|
||||||
|
main: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snippet {
|
||||||
|
pub fn new<S: std::borrow::Borrow<str>>(
|
||||||
|
name: &'static str,
|
||||||
|
code: CodeType<S>,
|
||||||
|
main: Option<String>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let code = match code {
|
||||||
|
CodeType::Code(code) => code.borrow().to_string(),
|
||||||
|
CodeType::Path(path) => {
|
||||||
|
let code = find_file(path).expect("Failed to find file");
|
||||||
|
code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let merged_code = merge_includes(code)
|
||||||
|
.map(|code| {
|
||||||
|
let clean_code = remove_comments(&code);
|
||||||
|
let clean_code = remove_version(&clean_code);
|
||||||
|
clean_code
|
||||||
|
})
|
||||||
|
.map_err(|v| {
|
||||||
|
let err_msg = format!("Failed to merge includes cause of {:?}", v);
|
||||||
|
Error::InvalidSnippet(err_msg)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let id = SNIP_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let mut parsed_code = CodeBlock::new(&merged_code)?;
|
||||||
|
let call = if let Some(main) = main {
|
||||||
|
main
|
||||||
|
} else {
|
||||||
|
parsed_code
|
||||||
|
.all_functions()
|
||||||
|
.first()
|
||||||
|
.map(|f| f.borrow().name.clone())
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let alias = parsed_code.mangling(id.to_string());
|
||||||
|
|
||||||
|
let chained = format!(
|
||||||
|
"// START {} //\n{}\n// END {} //\n",
|
||||||
|
name, parsed_code, name
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
parsed: parsed_code,
|
||||||
|
chained,
|
||||||
|
alias,
|
||||||
|
link: None,
|
||||||
|
main: call,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_hooks(&self) -> HashSet<Hook> {
|
||||||
|
self.parsed.all_hooks()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chain(mut self, snippet: Snippet) -> Self {
|
||||||
|
self.chained = format!(
|
||||||
|
"// START {} //\n{}\n// END {} //\n{}\n\n",
|
||||||
|
snippet.name, snippet.chained, snippet.name, self.chained,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.link = Some(Box::new(snippet));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(&self, paras: &Vec<String>) -> String {
|
||||||
|
if let Some(link) = &self.link {
|
||||||
|
let call_name = self.alias.get(&self.main).unwrap();
|
||||||
|
let c = link.call(paras);
|
||||||
|
format!("{}({})", call_name, c)
|
||||||
|
} else {
|
||||||
|
let call_name = self.alias.get(&self.main).unwrap();
|
||||||
|
format!("{}({})", call_name, paras.join(", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare_code(&self) -> &str {
|
||||||
|
&self.chained
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_symbol(&self, name: &str) -> Option<&String> {
|
||||||
|
self.alias.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_variable(&self, name: &str) -> Option<&Rc<RefCell<ShareVariable>>> {
|
||||||
|
self.find_symbol(name)
|
||||||
|
.map(|v| self.parsed.find_variable(v))
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Snippet {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Snippet {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
let mut raw_code = self.parsed.to_string();
|
||||||
|
let code = rhs.parsed.to_string();
|
||||||
|
|
||||||
|
raw_code.push_str(&code);
|
||||||
|
Snippet::new(self.name, CodeType::Code(raw_code), None).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeComponent for Snippet {
|
||||||
|
fn add_snippet_after(self, snippet: Snippet) -> Self {
|
||||||
|
self + snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_snippet_before(self, snippet: Snippet) -> Self {
|
||||||
|
snippet + self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snippet_new() {
|
||||||
|
let code = r#"
|
||||||
|
#version 450
|
||||||
|
#include "math/constants.glsl"
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(0.0);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let snippet = Snippet::new("polar", CodeType::Code(code), None).unwrap();
|
||||||
|
|
||||||
|
let snippet2 = Snippet::new("polar2", CodeType::Code(code), None).unwrap();
|
||||||
|
|
||||||
|
let snippet3 = snippet.clone() + snippet2.clone();
|
||||||
|
|
||||||
|
let snippet = snippet.chain(snippet2.chain(snippet3));
|
||||||
|
|
||||||
|
println!("{}", snippet.prepare_code());
|
||||||
|
}
|
||||||
|
}
|
||||||
278
src/components/snippets/util.rs
Normal file
278
src/components/snippets/util.rs
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
use include_dir::{include_dir, Dir};
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::{
|
||||||
|
complete::{tag, take_while1},
|
||||||
|
streaming::take_while,
|
||||||
|
},
|
||||||
|
character::{
|
||||||
|
complete::{alpha1, char, multispace0, multispace1, space0, space1},
|
||||||
|
streaming::none_of,
|
||||||
|
},
|
||||||
|
combinator::{map, opt, recognize},
|
||||||
|
multi::{fold_many0, many0, separated_list0},
|
||||||
|
sequence::{delimited, pair, preceded, tuple},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{collections::HashMap, io};
|
||||||
|
use std::{collections::HashSet, env::var};
|
||||||
|
|
||||||
|
use crate::utils::find_file;
|
||||||
|
|
||||||
|
static COMMENT_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r#"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*\n)"#).unwrap());
|
||||||
|
static VERSION_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"\#\s*version[^\r\n]*\n"#).unwrap());
|
||||||
|
static INCLUDE_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r#"\#\s*include\s*\"(?P<filename>[a-zA-Z0-9\-\.\/_]+)\""#).unwrap());
|
||||||
|
|
||||||
|
static VARIABLE_TYPE_PAT: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r#"(\w+)\s+([\w\[\] ]+);"#).unwrap());
|
||||||
|
|
||||||
|
static VARIABLE_NAME_PAT: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r#"(\w+)\s*(\[(\d+)\])?(\s*[^,]+)?"#).unwrap());
|
||||||
|
|
||||||
|
static TYPE_MAP: Lazy<HashMap<&'static str, u32>> = Lazy::new(|| {
|
||||||
|
let mut gtypes: HashMap<&str, u32> = HashMap::new();
|
||||||
|
gtypes.insert("float", glow::FLOAT);
|
||||||
|
gtypes.insert("vec2", glow::FLOAT_VEC2);
|
||||||
|
gtypes.insert("vec3", glow::FLOAT_VEC3);
|
||||||
|
gtypes.insert("vec4", glow::FLOAT_VEC4);
|
||||||
|
gtypes.insert("int", glow::INT);
|
||||||
|
gtypes.insert("ivec2", glow::INT_VEC2);
|
||||||
|
gtypes.insert("ivec3", glow::INT_VEC3);
|
||||||
|
gtypes.insert("ivec4", glow::INT_VEC4);
|
||||||
|
gtypes.insert("bool", glow::BOOL);
|
||||||
|
gtypes.insert("bvec2", glow::BOOL_VEC2);
|
||||||
|
gtypes.insert("bvec3", glow::BOOL_VEC3);
|
||||||
|
gtypes.insert("bvec4", glow::BOOL_VEC4);
|
||||||
|
gtypes.insert("mat2", glow::FLOAT_MAT2);
|
||||||
|
gtypes.insert("mat3", glow::FLOAT_MAT3);
|
||||||
|
gtypes.insert("mat4", glow::FLOAT_MAT4);
|
||||||
|
gtypes.insert("sampler1D", glow::SAMPLER_1D);
|
||||||
|
gtypes.insert("sampler2D", glow::SAMPLER_2D);
|
||||||
|
gtypes.insert("sampler3D", glow::SAMPLER_3D);
|
||||||
|
gtypes.insert("samplerCube", glow::SAMPLER_CUBE);
|
||||||
|
gtypes
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Variable {
|
||||||
|
pub name: String,
|
||||||
|
pub vtype: String,
|
||||||
|
pub type_code: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_declarations(code: &str, qualifier: Option<&[&str]>) -> Option<Vec<Variable>> {
|
||||||
|
if code.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let qualifier_pattern = if let Some(quals) = qualifier {
|
||||||
|
quals.join("|")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_pattern = if !qualifier_pattern.is_empty() {
|
||||||
|
format!(r"{}\s+(\w+)\s+([\w,\[\]\n =\.]+);", qualifier_pattern)
|
||||||
|
} else {
|
||||||
|
String::from(r"(\w+)\s+([\w\[\] ]+);")
|
||||||
|
};
|
||||||
|
|
||||||
|
let re_type = Regex::new(&type_pattern).unwrap();
|
||||||
|
let mut variables = Vec::new();
|
||||||
|
|
||||||
|
for caps in re_type.captures_iter(code) {
|
||||||
|
let vtype = caps.get(1).unwrap().as_str().to_string();
|
||||||
|
let names = caps.get(2).unwrap().as_str();
|
||||||
|
|
||||||
|
for cap in VARIABLE_NAME_PAT.captures_iter(names) {
|
||||||
|
let name = cap.get(1).unwrap().as_str().to_string();
|
||||||
|
if let Some(size_match) = cap.get(3) {
|
||||||
|
let size: usize = size_match.as_str().parse().unwrap();
|
||||||
|
if size == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
for i in 0..size {
|
||||||
|
let iname = format!("{}[{}]", name, i);
|
||||||
|
variables.push(Variable {
|
||||||
|
name: iname,
|
||||||
|
vtype: vtype.clone(),
|
||||||
|
type_code: *TYPE_MAP.get(vtype.as_str()).unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
variables.push(Variable {
|
||||||
|
name,
|
||||||
|
vtype: vtype.clone(),
|
||||||
|
type_code: *TYPE_MAP.get(vtype.as_str()).unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(variables)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_uniform(code: &str) -> Vec<Variable> {
|
||||||
|
let uniforms = get_declarations(code, Some(&["uniform"]));
|
||||||
|
uniforms.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_const(code: &str) -> Vec<Variable> {
|
||||||
|
let consts = get_declarations(code, Some(&["const"]));
|
||||||
|
consts.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_in(code: &str) -> Vec<(Option<usize>, Variable)> {
|
||||||
|
let ins = get_declarations(code, Some(&[r#"(layout\s?\(location\s?=(\d+)\)\s+)?in"#]));
|
||||||
|
|
||||||
|
if ins.is_none() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut variables: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
|
let type_pattern = r#"layout\s?\(location\s?=(\d+)\)"#;
|
||||||
|
let re_type = Regex::new(&type_pattern).unwrap();
|
||||||
|
|
||||||
|
for cap in re_type.captures_iter(code) {
|
||||||
|
if let Some(matched) = cap.get(1) {
|
||||||
|
variables.push(matched.as_str().parse().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ins = ins.unwrap();
|
||||||
|
|
||||||
|
if ins.len() != variables.len() && !variables.is_empty() {
|
||||||
|
return vec![];
|
||||||
|
} else {
|
||||||
|
ins.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, v)| (variables.get(i).copied(), v))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_out(code: &str) -> Vec<Variable> {
|
||||||
|
let outs = get_declarations(code, Some(&[r#"(layout\(.+\)\w+)?out"#]));
|
||||||
|
outs.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_hooks(code: &str) -> Vec<(String, Option<String>)> {
|
||||||
|
if code.is_empty() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let re_hooks = Regex::new(r#"\<(?P<hook>\w+)(\.(?P<subhook>.+))?(?:\([^<>]+\))?\>"#).unwrap();
|
||||||
|
let mut hooks = HashSet::new();
|
||||||
|
|
||||||
|
for cap in re_hooks.captures_iter(code) {
|
||||||
|
let hook = cap.name("hook").unwrap().as_str().to_string();
|
||||||
|
let subhook = cap.name("subhook").map(|m| m.as_str().to_string());
|
||||||
|
hooks.insert((hook, subhook));
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除 GLSL 代码中的注释
|
||||||
|
pub(super) fn remove_comments(code: &str) -> String {
|
||||||
|
COMMENT_RE
|
||||||
|
.replace_all(code, |caps: ®ex::Captures| {
|
||||||
|
if caps.get(2).is_some() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
caps.get(1).unwrap().as_str().to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除 GLSL 代码中的版本指令
|
||||||
|
pub(super) fn remove_version(code: &str) -> String {
|
||||||
|
VERSION_RE.replace_all(code, "\n").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归合并包含的文件内容
|
||||||
|
pub fn merge_includes(mut code: String) -> io::Result<String> {
|
||||||
|
let mut includes: Vec<String> = vec![];
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
if INCLUDE_RE.is_match(&code) {
|
||||||
|
let c = INCLUDE_RE.replace_all(&code, |caps: ®ex::Captures| {
|
||||||
|
let filename = caps.name("filename").unwrap().as_str().to_string();
|
||||||
|
|
||||||
|
if !includes.contains(&filename) {
|
||||||
|
let file_data =
|
||||||
|
find_file(&filename).expect(&format!("File {} not found", filename));
|
||||||
|
|
||||||
|
let cleaned_contents = remove_comments(&file_data);
|
||||||
|
let new_code = format!(
|
||||||
|
"\n// --- start of \"{}\" ---\n{}\n// --- end of \"{}\" ---\n",
|
||||||
|
filename, cleaned_contents, filename
|
||||||
|
);
|
||||||
|
includes.push(filename);
|
||||||
|
new_code
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
code = c.to_string();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use super::{get_declarations, get_hooks, get_in, merge_includes};
|
||||||
|
use std::{fs::read_to_string, io::Write};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_include() {
|
||||||
|
let file =
|
||||||
|
std::fs::read_to_string("/Users/tsuki/projects/untitled/shaders/transform/polar.glsl")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut merged = std::fs::File::create(
|
||||||
|
"/Users/tsuki/projects/untitled/shaders/transform/polar_merged.glsl",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
merged
|
||||||
|
.write_all(merge_includes(file).unwrap().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_declare() {
|
||||||
|
let code = "
|
||||||
|
uniform float u_time;
|
||||||
|
attribute vec3 position;
|
||||||
|
uniform vec4 colors[3];
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
";
|
||||||
|
|
||||||
|
let variables = get_in(code);
|
||||||
|
println!("{:?}", variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hook() {
|
||||||
|
let code = r"
|
||||||
|
<update>
|
||||||
|
<render.xyz(1, 2)>
|
||||||
|
<input.key(escape)>
|
||||||
|
";
|
||||||
|
|
||||||
|
let hooks = get_hooks(code);
|
||||||
|
println!("{:?}", hooks);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/errors.rs
Normal file
9
src/errors.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Invalid Snippet {0}")]
|
||||||
|
InvalidSnippet(String),
|
||||||
|
}
|
||||||
68
src/final_pg.rs
Normal file
68
src/final_pg.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{CodeType, Program, Shader},
|
||||||
|
graphics::transforms::{
|
||||||
|
polar::Polar, position::Position, trackball::Trackball, viewport::Viewport, Transform,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PPI {
|
||||||
|
transform: Trackball,
|
||||||
|
program: Program,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PPI {
|
||||||
|
pub fn new(version: &'static str) -> Self {
|
||||||
|
let trackball = Trackball::new().unwrap();
|
||||||
|
let projection = Polar::new().unwrap();
|
||||||
|
let position = Position::new().unwrap();
|
||||||
|
|
||||||
|
let transform = trackball.chain(projection.chain(position));
|
||||||
|
|
||||||
|
let vertex = Shader::new(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let fragment = Shader::new(
|
||||||
|
glow::FRAGMENT_SHADER,
|
||||||
|
CodeType::<&'static str>::Path("agg-fast-path.frag".into()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut program = Program::new(vertex, fragment, None, version);
|
||||||
|
|
||||||
|
let viewport = Viewport::new().unwrap();
|
||||||
|
|
||||||
|
program.set_transform(&transform);
|
||||||
|
|
||||||
|
program.set_viewport(&viewport);
|
||||||
|
|
||||||
|
Self { transform, program }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, gl: &glow::Context) {
|
||||||
|
self.program.compile(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native_program(&self) -> Option<<glow::Context as HasContext>::Program> {
|
||||||
|
self.program.native_program
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ppi() {
|
||||||
|
let ppi = PPI::new("450");
|
||||||
|
|
||||||
|
println!("{}", ppi.program.vertex());
|
||||||
|
}
|
||||||
|
}
|
||||||
139
src/graphics/collections/agg_fast_path.rs
Normal file
139
src/graphics/collections/agg_fast_path.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
|
use crate::components::{fetchcode, CodeComponent, CodeType, Program, Shader};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::transforms::viewport::Viewport;
|
||||||
|
use crate::graphics::transforms::Transform;
|
||||||
|
use crate::graphics::ty::Ty;
|
||||||
|
use crate::graphics::Graphics;
|
||||||
|
|
||||||
|
use super::Colletion;
|
||||||
|
|
||||||
|
struct AggFastPath {
|
||||||
|
program: Program,
|
||||||
|
buffer: Vec<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AggFastPath {
|
||||||
|
pub fn new<T: Transform>(transform: &T, viewport: &Viewport) -> Result<Self> {
|
||||||
|
let vertex = Shader::new(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
CodeType::<String>::Path("agg-fast-path.vert".into()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let fragment = Shader::new(
|
||||||
|
glow::FRAGMENT_SHADER,
|
||||||
|
CodeType::<String>::Path("agg-fast-path.frag".into()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let vertex = vertex.add_snippet_after(fetchcode([], "agg_fast"));
|
||||||
|
|
||||||
|
let mut program = Program::new(vertex, fragment, None, "");
|
||||||
|
|
||||||
|
program.set_transform(transform);
|
||||||
|
program.set_viewport(viewport);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
program,
|
||||||
|
buffer: Vec::with_capacity(500),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Colletion for AggFastPath {
|
||||||
|
type Item = Path;
|
||||||
|
|
||||||
|
fn append(&mut self, item: Self::Item) {
|
||||||
|
self.buffer.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graphics for AggFastPath {
|
||||||
|
fn draw(&self) -> Result<()> {
|
||||||
|
for path in self.buffer.iter() {
|
||||||
|
let p: &[u8] = &path;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||||
|
pub struct Point {
|
||||||
|
prev: [f32; 3],
|
||||||
|
curr: [f32; 3],
|
||||||
|
next: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ty for Point {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Path {
|
||||||
|
points: Vec<Point>,
|
||||||
|
is_closed: bool,
|
||||||
|
is_empty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
pub fn new(is_closed: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
points: Vec::with_capacity(500),
|
||||||
|
is_closed,
|
||||||
|
is_empty: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, point: [f32; 3]) {
|
||||||
|
if self.is_empty {
|
||||||
|
self.points.push(Point {
|
||||||
|
prev: point,
|
||||||
|
curr: point,
|
||||||
|
next: point,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.points.push(Point {
|
||||||
|
prev: point,
|
||||||
|
curr: point,
|
||||||
|
next: point,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.is_empty = false;
|
||||||
|
} else {
|
||||||
|
let len = self.points.len();
|
||||||
|
let prev = self.points[len - 1].curr;
|
||||||
|
let curr = point;
|
||||||
|
let next = point;
|
||||||
|
self.points.push(Point { prev, curr, next });
|
||||||
|
self.points[len - 1].next = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) {
|
||||||
|
if self.is_closed {
|
||||||
|
let len = self.points.len();
|
||||||
|
let prev = self.points[len - 1].curr;
|
||||||
|
let curr = self.points[0].curr;
|
||||||
|
let next = self.points[1].curr;
|
||||||
|
self.points.push(Point { prev, curr, next });
|
||||||
|
self.points[len - 1].next = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Path {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
cast_slice(&self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Path {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
use bytemuck::cast_slice_mut;
|
||||||
|
cast_slice_mut(&mut self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/graphics/collections/mod.rs
Normal file
7
src/graphics/collections/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub mod agg_fast_path;
|
||||||
|
|
||||||
|
pub trait Colletion {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn append(&mut self, item: Self::Item);
|
||||||
|
}
|
||||||
1
src/graphics/colormesh.rs
Normal file
1
src/graphics/colormesh.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub struct ColorMesh {}
|
||||||
12
src/graphics/mod.rs
Normal file
12
src/graphics/mod.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pub mod collections;
|
||||||
|
mod colormesh;
|
||||||
|
pub mod tools;
|
||||||
|
pub mod transforms;
|
||||||
|
pub mod ty;
|
||||||
|
use crate::errors::*;
|
||||||
|
|
||||||
|
pub use colormesh::ColorMesh;
|
||||||
|
|
||||||
|
pub trait Graphics {
|
||||||
|
fn draw(&self) -> Result<()>;
|
||||||
|
}
|
||||||
0
src/graphics/tools/mod.rs
Normal file
0
src/graphics/tools/mod.rs
Normal file
26
src/graphics/transforms/mod.rs
Normal file
26
src/graphics/transforms/mod.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
pub mod polar;
|
||||||
|
pub mod position;
|
||||||
|
pub mod trackball;
|
||||||
|
pub mod viewport;
|
||||||
|
use crate::components::Snippet;
|
||||||
|
|
||||||
|
pub trait Transform {
|
||||||
|
fn snippet(&self) -> &Snippet;
|
||||||
|
|
||||||
|
fn chain(self, other: impl Transform) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform() {
|
||||||
|
let polar = polar::Polar::new().unwrap();
|
||||||
|
let trackball = trackball::Trackball::new().unwrap();
|
||||||
|
let polar_trackball = polar.chain(trackball);
|
||||||
|
|
||||||
|
println!("{}", polar_trackball.snippet().prepare_code());
|
||||||
|
|
||||||
|
// println!("{}", polar_trackball.snippet().call(vec![]));
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/graphics/transforms/polar.rs
Normal file
49
src/graphics/transforms/polar.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use crate::{
|
||||||
|
components::{CodeType, Snippet},
|
||||||
|
errors::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Transform;
|
||||||
|
|
||||||
|
pub struct Polar {
|
||||||
|
snippet: Snippet,
|
||||||
|
origin: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Polar {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let snippets = Snippet::new(
|
||||||
|
"polar",
|
||||||
|
CodeType::<&'static str>::Path("transform/polar.glsl".into()),
|
||||||
|
Some("forward".to_string()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
snippet: snippets,
|
||||||
|
origin: 0.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform for Polar {
|
||||||
|
fn snippet(&self) -> &Snippet {
|
||||||
|
&self.snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain(mut self, other: impl Transform) -> Self {
|
||||||
|
let new_snippet = self.snippet.chain(other.snippet().to_owned());
|
||||||
|
self.snippet = new_snippet;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_polar() {
|
||||||
|
let polar = Polar::new().unwrap();
|
||||||
|
|
||||||
|
println!("{}", polar.snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/graphics/transforms/position.rs
Normal file
41
src/graphics/transforms/position.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use super::Transform;
|
||||||
|
use crate::{
|
||||||
|
components::{CodeType, Snippet},
|
||||||
|
errors::*,
|
||||||
|
};
|
||||||
|
pub struct Position {
|
||||||
|
snippet: Snippet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let snippets = Snippet::new(
|
||||||
|
"position",
|
||||||
|
CodeType::<&'static str>::Path("transform/position.glsl".into()),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { snippet: snippets })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform for Position {
|
||||||
|
fn snippet(&self) -> &Snippet {
|
||||||
|
&self.snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain(mut self, other: impl Transform) -> Self {
|
||||||
|
let new_snippet = self.snippet.chain(other.snippet().to_owned());
|
||||||
|
self.snippet = new_snippet;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position() {
|
||||||
|
let position = Position::new().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/graphics/transforms/trackball.rs
Normal file
43
src/graphics/transforms/trackball.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::components::{CodeType, Snippet};
|
||||||
|
use crate::errors::Result;
|
||||||
|
|
||||||
|
use super::Transform;
|
||||||
|
|
||||||
|
pub struct Trackball {
|
||||||
|
snippet: Snippet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trackball {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let snippets = Snippet::new(
|
||||||
|
"trackball",
|
||||||
|
CodeType::<&'static str>::Path("transform/trackball.glsl".into()),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { snippet: snippets })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform for Trackball {
|
||||||
|
fn snippet(&self) -> &Snippet {
|
||||||
|
&self.snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain(mut self, other: impl Transform) -> Self {
|
||||||
|
let new_snippet = self.snippet.chain(other.snippet().to_owned());
|
||||||
|
self.snippet = new_snippet;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trackball() {
|
||||||
|
let trackball = Trackball::new().unwrap();
|
||||||
|
|
||||||
|
println!("{}", trackball.snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/graphics/transforms/viewport.rs
Normal file
23
src/graphics/transforms/viewport.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crate::{
|
||||||
|
components::{self, CodeType, Snippet},
|
||||||
|
errors::*,
|
||||||
|
};
|
||||||
|
pub struct Viewport {
|
||||||
|
snippet: Snippet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Viewport {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let snippets = Snippet::new(
|
||||||
|
"viewport",
|
||||||
|
CodeType::<&'static str>::Path("transform/viewport.glsl".into()),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { snippet: snippets })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snippet(&self) -> &Snippet {
|
||||||
|
&self.snippet
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/graphics/ty/mod.rs
Normal file
3
src/graphics/ty/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
|
pub trait Ty: Pod {}
|
||||||
55
src/main.rs
Normal file
55
src/main.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use imgui::Condition;
|
||||||
|
|
||||||
|
mod camera;
|
||||||
|
mod components;
|
||||||
|
mod errors;
|
||||||
|
mod graphics;
|
||||||
|
mod support;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
mod final_pg;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
let choices = ["test test this is 1", "test test this is 2"];
|
||||||
|
let mut value = 0;
|
||||||
|
support::supporter::init(move |_, ui| {
|
||||||
|
ui.window("Hello world")
|
||||||
|
.size([300.0, 110.0], Condition::FirstUseEver)
|
||||||
|
.build(|| {
|
||||||
|
ui.text_wrapped("Hello world!");
|
||||||
|
ui.text_wrapped("こんにちは世界!");
|
||||||
|
if ui.button(choices[value]) {
|
||||||
|
value += 1;
|
||||||
|
value %= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.button("This...is...imgui-rs!");
|
||||||
|
ui.separator();
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
ui.text(format!(
|
||||||
|
"Mouse Position: ({:.1},{:.1})",
|
||||||
|
mouse_pos[0], mouse_pos[1]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.window("Hello world")
|
||||||
|
.size([300.0, 110.0], Condition::FirstUseEver)
|
||||||
|
.build(|| {
|
||||||
|
ui.text_wrapped("Hello world!");
|
||||||
|
ui.text_wrapped("こんにちは世界!");
|
||||||
|
if ui.button(choices[value]) {
|
||||||
|
value += 1;
|
||||||
|
value %= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.button("This...is...imgui-rs!");
|
||||||
|
ui.separator();
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
ui.text(format!(
|
||||||
|
"Mouse Position: ({:.1},{:.1})",
|
||||||
|
mouse_pos[0], mouse_pos[1]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
18
src/support/clipboard.rs
Normal file
18
src/support/clipboard.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use copypasta::{ClipboardContext, ClipboardProvider};
|
||||||
|
use imgui::ClipboardBackend;
|
||||||
|
|
||||||
|
pub struct ClipboardSupport(pub ClipboardContext);
|
||||||
|
|
||||||
|
pub fn init() -> Option<ClipboardSupport> {
|
||||||
|
ClipboardContext::new().ok().map(ClipboardSupport)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClipboardBackend for ClipboardSupport {
|
||||||
|
fn get(&mut self) -> Option<String> {
|
||||||
|
self.0.get_contents().ok()
|
||||||
|
}
|
||||||
|
fn set(&mut self, text: &str) {
|
||||||
|
// ignore errors?
|
||||||
|
let _ = self.0.set_contents(text.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/support/mod.rs
Normal file
2
src/support/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod clipboard;
|
||||||
|
pub mod supporter;
|
||||||
284
src/support/supporter.rs
Normal file
284
src/support/supporter.rs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
use crate::utils::Triangler;
|
||||||
|
use nalgebra_glm::perspective;
|
||||||
|
use glutin::{
|
||||||
|
config::ConfigTemplateBuilder,
|
||||||
|
context::{ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext},
|
||||||
|
display::{GetGlDisplay, GlDisplay},
|
||||||
|
surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface},
|
||||||
|
};
|
||||||
|
use imgui::Ui;
|
||||||
|
use imgui_winit_support::{HiDpiMode, winit::{
|
||||||
|
dpi::{LogicalSize, PhysicalPosition},
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::{Window, WindowBuilder},
|
||||||
|
}, WinitPlatform};
|
||||||
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
use std::time::Instant;
|
||||||
|
use cgmath::InnerSpace;
|
||||||
|
use glow::HasContext;
|
||||||
|
use nalgebra_glm::Vec3;
|
||||||
|
use crate::camera::Camera;
|
||||||
|
|
||||||
|
pub fn init<FUi>(mut run_ui: FUi)
|
||||||
|
where
|
||||||
|
FUi: FnMut(&mut bool, &mut Ui) + 'static,
|
||||||
|
{
|
||||||
|
let (event_loop, window, surface, context) = create_window();
|
||||||
|
let (mut winit_platform, mut imgui_context) = imgui_init(&window);
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
let dpi_mode = if let Ok(factor) = std::env::var("IMGUI_EXAMPLE_FORCE_DPI_FACTOR") {
|
||||||
|
// Allow forcing of HiDPI factor for debugging purposes
|
||||||
|
match factor.parse::<f64>() {
|
||||||
|
Ok(f) => HiDpiMode::Locked(f),
|
||||||
|
Err(e) => panic!("Invalid scaling factor: {}", e),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HiDpiMode::Default
|
||||||
|
};
|
||||||
|
|
||||||
|
winit_platform.attach_window(imgui_context.io_mut(), &window, dpi_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gl = unsafe {
|
||||||
|
let gl = glow_context(&context);
|
||||||
|
gl.enable(glow::DEPTH_TEST);
|
||||||
|
gl
|
||||||
|
};
|
||||||
|
|
||||||
|
// OpenGL renderer from this crate
|
||||||
|
let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context)
|
||||||
|
.expect("failed to create renderer");
|
||||||
|
|
||||||
|
let mut last_frame = Instant::now();
|
||||||
|
let tri_renderer = Triangler::new(ig_renderer.gl_context(), "#version 330");
|
||||||
|
|
||||||
|
let program = tri_renderer.program();
|
||||||
|
let mut camera = Camera::new(
|
||||||
|
Vec3::new(0.5, 0.5, 1.0), // Camera position (world location
|
||||||
|
Vec3::new(0.0, 1.0, 0.0), // Upward vector
|
||||||
|
Vec3::new(0.0, 0.0, -1.0), // Look at vector
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut last_position: Option<PhysicalPosition<f64>> = None;
|
||||||
|
let mut yaw = -90.0;
|
||||||
|
let mut pitch = 0.0;
|
||||||
|
|
||||||
|
event_loop
|
||||||
|
.run(move |event, window_target| {
|
||||||
|
match event {
|
||||||
|
winit::event::Event::NewEvents(_) => {
|
||||||
|
let now = Instant::now();
|
||||||
|
imgui_context
|
||||||
|
.io_mut()
|
||||||
|
.update_delta_time(now.duration_since(last_frame));
|
||||||
|
last_frame = now;
|
||||||
|
}
|
||||||
|
winit::event::Event::AboutToWait => {
|
||||||
|
winit_platform
|
||||||
|
.prepare_frame(imgui_context.io_mut(), &window)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
winit::event::Event::WindowEvent {
|
||||||
|
event: winit::event::WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Render your custom scene, note we need to borrow the OpenGL
|
||||||
|
// context from the `AutoRenderer`, which takes ownership of it.
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let gl = ig_renderer.gl_context();
|
||||||
|
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
let loc = gl.get_uniform_location(program, "projection");
|
||||||
|
let size = window.inner_size();
|
||||||
|
let projection = perspective(size.width as f32 / size.height as f32, 45.0, 0.1, 100.0);
|
||||||
|
gl.uniform_matrix_4_f32_slice(loc.as_ref(), false, projection.as_slice());
|
||||||
|
|
||||||
|
let loc = gl.get_uniform_location(program, "view");
|
||||||
|
let view = camera.get_view_matrix();
|
||||||
|
gl.uniform_matrix_4_f32_slice(loc.as_ref(), false, view.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
tri_renderer.render(ig_renderer.gl_context());
|
||||||
|
|
||||||
|
let ui = imgui_context.frame();
|
||||||
|
|
||||||
|
if ui.is_mouse_pos_valid(ui.io().mouse_pos) {
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
if ui.is_mouse_dragging(imgui::MouseButton::Right) {
|
||||||
|
mouse_callback(&mut last_position, PhysicalPosition::new(mouse_pos[0] as f64, mouse_pos[1] as f64), 0.05, &mut pitch, &mut yaw, &mut camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut run = true;
|
||||||
|
run_ui(&mut run, ui);
|
||||||
|
|
||||||
|
if !run {
|
||||||
|
window_target.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_data = imgui_context.render();
|
||||||
|
|
||||||
|
// Render imgui on top of it
|
||||||
|
ig_renderer
|
||||||
|
.render(draw_data)
|
||||||
|
.expect("error rendering imgui");
|
||||||
|
|
||||||
|
surface
|
||||||
|
.swap_buffers(&context)
|
||||||
|
.expect("Failed to swap buffers");
|
||||||
|
}
|
||||||
|
winit::event::Event::WindowEvent {
|
||||||
|
event: winit::event::WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
window_target.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
winit::event::Event::WindowEvent {
|
||||||
|
event: winit::event::WindowEvent::Resized(new_size),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
|
surface.resize(
|
||||||
|
&context,
|
||||||
|
NonZeroU32::new(new_size.width).unwrap(),
|
||||||
|
NonZeroU32::new(new_size.height).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
|
||||||
|
}
|
||||||
|
winit::event::Event::LoopExiting => {
|
||||||
|
let gl = ig_renderer.gl_context();
|
||||||
|
tri_renderer.destroy(gl);
|
||||||
|
}
|
||||||
|
event => {
|
||||||
|
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_callback(last_position: &mut Option<PhysicalPosition<f64>>, current_position:PhysicalPosition<f64>, sensitivity: f64, pitch: &mut f64, yaw: &mut f64, camera: &mut Camera) {
|
||||||
|
use cgmath::{Euler, Deg, Quaternion};
|
||||||
|
|
||||||
|
let xpos = current_position.x;
|
||||||
|
let ypos = current_position.y;
|
||||||
|
|
||||||
|
if last_position.is_none() {
|
||||||
|
*last_position = Some(PhysicalPosition::new(xpos, ypos));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _last_position = last_position.unwrap();
|
||||||
|
|
||||||
|
let mut xoffset = xpos - _last_position.x;
|
||||||
|
let mut yoffset = _last_position.y - ypos; // reversed since y-coordinates go from bottom to top
|
||||||
|
|
||||||
|
*last_position = Some(PhysicalPosition::new(xpos, ypos));
|
||||||
|
|
||||||
|
xoffset *= sensitivity;
|
||||||
|
yoffset *= sensitivity;
|
||||||
|
|
||||||
|
*yaw += xoffset;
|
||||||
|
*pitch += yoffset;
|
||||||
|
|
||||||
|
if *pitch > 89.0 {
|
||||||
|
*pitch = 89.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *pitch < -89.0 {
|
||||||
|
*pitch = -89.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let euler_deg = Euler::new(
|
||||||
|
Deg(*pitch),
|
||||||
|
Deg(*yaw),
|
||||||
|
Deg(0.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let c = Quaternion::from(euler_deg).normalize();
|
||||||
|
|
||||||
|
camera.set_front(Vec3::new(c.v.x as f32, c.v.y as f32, c.v.z as f32));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_window() -> (
|
||||||
|
EventLoop<()>,
|
||||||
|
Window,
|
||||||
|
Surface<WindowSurface>,
|
||||||
|
PossiblyCurrentContext,
|
||||||
|
) {
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window_builder = WindowBuilder::new()
|
||||||
|
.with_title("TEST")
|
||||||
|
.with_inner_size(LogicalSize::new(1024, 768));
|
||||||
|
|
||||||
|
let (window, cfg) = glutin_winit::DisplayBuilder::new()
|
||||||
|
.with_window_builder(Some(window_builder))
|
||||||
|
.build(&event_loop, ConfigTemplateBuilder::new(), |mut cfgs| {
|
||||||
|
cfgs.next().unwrap()
|
||||||
|
})
|
||||||
|
.expect("Failed to create window");
|
||||||
|
|
||||||
|
let window = window.unwrap();
|
||||||
|
|
||||||
|
let context_attributes =
|
||||||
|
ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));
|
||||||
|
|
||||||
|
let context = unsafe {
|
||||||
|
cfg.display()
|
||||||
|
.create_context(&cfg, &context_attributes)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let surface_attributes = SurfaceAttributesBuilder::<WindowSurface>::new()
|
||||||
|
.with_srgb(Some(true))
|
||||||
|
.build(
|
||||||
|
window.raw_window_handle(),
|
||||||
|
NonZeroU32::new(1024).unwrap(),
|
||||||
|
NonZeroU32::new(768).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let surface = unsafe {
|
||||||
|
cfg.display()
|
||||||
|
.create_window_surface(&cfg, &surface_attributes)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = context.make_current(&surface).unwrap();
|
||||||
|
|
||||||
|
(event_loop, window, surface, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn glow_context(context: &PossiblyCurrentContext) -> glow::Context {
|
||||||
|
unsafe {
|
||||||
|
glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
|
||||||
|
let mut imgui_context = imgui::Context::create();
|
||||||
|
imgui_context.set_ini_filename(None);
|
||||||
|
|
||||||
|
let mut winit_platform = WinitPlatform::init(&mut imgui_context);
|
||||||
|
winit_platform.attach_window(
|
||||||
|
imgui_context.io_mut(),
|
||||||
|
window,
|
||||||
|
imgui_winit_support::HiDpiMode::Rounded,
|
||||||
|
);
|
||||||
|
|
||||||
|
imgui_context
|
||||||
|
.fonts()
|
||||||
|
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
|
||||||
|
|
||||||
|
imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32;
|
||||||
|
|
||||||
|
(winit_platform, imgui_context)
|
||||||
|
}
|
||||||
239
src/utils/mod.rs
Normal file
239
src/utils/mod.rs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
mod parser;
|
||||||
|
use include_dir::{include_dir, Dir};
|
||||||
|
pub use parser::*;
|
||||||
|
use std::{num::NonZeroU32, path::Path};
|
||||||
|
|
||||||
|
use glow::HasContext;
|
||||||
|
use glutin::{
|
||||||
|
config::ConfigTemplateBuilder,
|
||||||
|
context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext},
|
||||||
|
display::{GetGlDisplay, GlDisplay},
|
||||||
|
surface::{GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface},
|
||||||
|
};
|
||||||
|
use imgui_winit_support::WinitPlatform;
|
||||||
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event_loop::EventLoop,
|
||||||
|
window::{Window, WindowBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SHADERS: Dir = include_dir!("$CARGO_MANIFEST_DIR/shaders");
|
||||||
|
|
||||||
|
pub fn create_window(
|
||||||
|
title: &str,
|
||||||
|
context_api: Option<ContextApi>,
|
||||||
|
) -> (
|
||||||
|
EventLoop<()>,
|
||||||
|
Window,
|
||||||
|
Surface<WindowSurface>,
|
||||||
|
PossiblyCurrentContext,
|
||||||
|
) {
|
||||||
|
let event_loop = EventLoop::new().unwrap();
|
||||||
|
|
||||||
|
let window_builder = WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_inner_size(LogicalSize::new(1024, 768));
|
||||||
|
let (window, cfg) = glutin_winit::DisplayBuilder::new()
|
||||||
|
.with_window_builder(Some(window_builder))
|
||||||
|
.build(&event_loop, ConfigTemplateBuilder::new(), |mut configs| {
|
||||||
|
configs.next().unwrap()
|
||||||
|
})
|
||||||
|
.expect("Failed to create OpenGL window");
|
||||||
|
|
||||||
|
let window = window.unwrap();
|
||||||
|
|
||||||
|
let mut context_attribs = ContextAttributesBuilder::new();
|
||||||
|
if let Some(context_api) = context_api {
|
||||||
|
context_attribs = context_attribs.with_context_api(context_api);
|
||||||
|
}
|
||||||
|
let context_attribs = context_attribs.build(Some(window.raw_window_handle()));
|
||||||
|
let context = unsafe {
|
||||||
|
cfg.display()
|
||||||
|
.create_context(&cfg, &context_attribs)
|
||||||
|
.expect("Failed to create OpenGL context")
|
||||||
|
};
|
||||||
|
|
||||||
|
let surface_attribs = SurfaceAttributesBuilder::<WindowSurface>::new()
|
||||||
|
.with_srgb(Some(true))
|
||||||
|
.build(
|
||||||
|
window.raw_window_handle(),
|
||||||
|
NonZeroU32::new(1024).unwrap(),
|
||||||
|
NonZeroU32::new(768).unwrap(),
|
||||||
|
);
|
||||||
|
let surface = unsafe {
|
||||||
|
cfg.display()
|
||||||
|
.create_window_surface(&cfg, &surface_attribs)
|
||||||
|
.expect("Failed to create OpenGL surface")
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = context
|
||||||
|
.make_current(&surface)
|
||||||
|
.expect("Failed to make OpenGL context current");
|
||||||
|
|
||||||
|
surface
|
||||||
|
.set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
|
||||||
|
.expect("Failed to set swap interval");
|
||||||
|
|
||||||
|
(event_loop, window, surface, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glow_context(context: &PossiblyCurrentContext) -> glow::Context {
|
||||||
|
unsafe {
|
||||||
|
glow::Context::from_loader_function_cstr(|s| context.display().get_proc_address(s).cast())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
|
||||||
|
let mut imgui_context = imgui::Context::create();
|
||||||
|
imgui_context.set_ini_filename(None);
|
||||||
|
|
||||||
|
let mut winit_platform = WinitPlatform::init(&mut imgui_context);
|
||||||
|
winit_platform.attach_window(
|
||||||
|
imgui_context.io_mut(),
|
||||||
|
window,
|
||||||
|
imgui_winit_support::HiDpiMode::Rounded,
|
||||||
|
);
|
||||||
|
|
||||||
|
imgui_context
|
||||||
|
.fonts()
|
||||||
|
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
|
||||||
|
|
||||||
|
imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32;
|
||||||
|
|
||||||
|
(winit_platform, imgui_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_file<P: AsRef<Path>>(filename: P) -> Option<String> {
|
||||||
|
let path = SHADERS.get_file(filename.as_ref());
|
||||||
|
|
||||||
|
path.map(|p| p.contents_utf8())
|
||||||
|
.flatten()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Triangler {
|
||||||
|
pub program: <glow::Context as HasContext>::Program,
|
||||||
|
pub vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Triangler {
|
||||||
|
pub fn new(gl: &glow::Context, shader_header: &str) -> Self {
|
||||||
|
const VERTEX_SHADER_SOURCE: &str = r#"
|
||||||
|
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform mat4 view;
|
||||||
|
|
||||||
|
const vec2 verts[3] = vec2[3](
|
||||||
|
vec2(0.5f, 1.0f),
|
||||||
|
vec2(0.0f, 0.0f),
|
||||||
|
vec2(1.0f, 0.0f)
|
||||||
|
);
|
||||||
|
|
||||||
|
out vec2 vert;
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
vec4 srgb_to_linear(vec4 srgb_color) {
|
||||||
|
// Calcuation as documented by OpenGL
|
||||||
|
vec3 srgb = srgb_color.rgb;
|
||||||
|
vec3 selector = ceil(srgb - 0.04045);
|
||||||
|
vec3 less_than_branch = srgb / 12.92;
|
||||||
|
vec3 greater_than_branch = pow((srgb + 0.055) / 1.055, vec3(2.4));
|
||||||
|
return vec4(
|
||||||
|
mix(less_than_branch, greater_than_branch, selector),
|
||||||
|
srgb_color.a
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vert = verts[gl_VertexID];
|
||||||
|
color = srgb_to_linear(vec4(vert, 0.5, 1.0));
|
||||||
|
gl_Position = projection * view * vec4(vert - 0.5, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
const FRAGMENT_SHADER_SOURCE: &str = r#"
|
||||||
|
in vec2 vert;
|
||||||
|
in vec4 color;
|
||||||
|
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
vec4 linear_to_srgb(vec4 linear_color) {
|
||||||
|
vec3 linear = linear_color.rgb;
|
||||||
|
vec3 selector = ceil(linear - 0.0031308);
|
||||||
|
vec3 less_than_branch = linear * 12.92;
|
||||||
|
vec3 greater_than_branch = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055;
|
||||||
|
return vec4(
|
||||||
|
mix(less_than_branch, greater_than_branch, selector),
|
||||||
|
linear_color.a
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
frag_color = linear_to_srgb(color);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut shaders = [
|
||||||
|
(glow::VERTEX_SHADER, VERTEX_SHADER_SOURCE, None),
|
||||||
|
(glow::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE, None),
|
||||||
|
];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let vertex_array = gl
|
||||||
|
.create_vertex_array()
|
||||||
|
.expect("Cannot create vertex array");
|
||||||
|
|
||||||
|
let program = gl.create_program().expect("Cannot create program");
|
||||||
|
|
||||||
|
for (kind, source, handle) in &mut shaders {
|
||||||
|
let shader = gl.create_shader(*kind).expect("Cannot create shader");
|
||||||
|
gl.shader_source(shader, &format!("{}\n{}", shader_header, *source));
|
||||||
|
gl.compile_shader(shader);
|
||||||
|
if !gl.get_shader_compile_status(shader) {
|
||||||
|
panic!("{}", gl.get_shader_info_log(shader));
|
||||||
|
}
|
||||||
|
gl.attach_shader(program, shader);
|
||||||
|
*handle = Some(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.link_program(program);
|
||||||
|
if !gl.get_program_link_status(program) {
|
||||||
|
panic!("{}", gl.get_program_info_log(program));
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.get_uniform_location(program, "u_resolution");
|
||||||
|
|
||||||
|
for &(_, _, shader) in &shaders {
|
||||||
|
gl.detach_shader(program, shader.unwrap());
|
||||||
|
gl.delete_shader(shader.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
program,
|
||||||
|
vertex_array,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.clear_color(0.05, 0.05, 0.1, 1.0);
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
gl.use_program(Some(self.program));
|
||||||
|
gl.bind_vertex_array(Some(self.vertex_array));
|
||||||
|
gl.draw_arrays(glow::TRIANGLES, 0, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_program(self.program);
|
||||||
|
gl.delete_vertex_array(self.vertex_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&self) -> <glow::Context as HasContext>::Program {
|
||||||
|
self.program
|
||||||
|
}
|
||||||
|
}
|
||||||
1803
src/utils/parser.rs
Normal file
1803
src/utils/parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user