This commit is contained in:
Tsuki 2024-07-10 15:49:05 +08:00
commit 371c1699be
57 changed files with 6842 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
.idea/.gitignore generated vendored Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

26
Cargo.toml Normal file
View 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

Binary file not shown.

View 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.

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

View 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);
}

View 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()>;
}

View 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"

View 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);
}

View 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;
}

View 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);
}

View 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
View 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;
}

View 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

View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
#version 330 core
void main(){}

1
shaders/polar.vert Normal file
View File

@ -0,0 +1 @@
#version 330 core

View 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); }

View 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);
}

View 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;
}

View 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
View 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
View 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
View 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
View 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);
}
}

View 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());
}
}

View 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: &regex::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: &regex::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
View 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
View 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());
}
}

View 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)
}
}

View File

@ -0,0 +1,7 @@
pub mod agg_fast_path;
pub trait Colletion {
type Item;
fn append(&mut self, item: Self::Item);
}

View File

@ -0,0 +1 @@
pub struct ColorMesh {}

12
src/graphics/mod.rs Normal file
View 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<()>;
}

View File

View 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![]));
}
}

View 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);
}
}

View 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();
}
}

View 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);
}
}

View 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
View File

@ -0,0 +1,3 @@
use bytemuck::{Pod, Zeroable};
pub trait Ty: Pod {}

55
src/main.rs Normal file
View 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
View 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
View File

@ -0,0 +1,2 @@
mod clipboard;
pub mod supporter;

284
src/support/supporter.rs Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff