-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRenderer.cs
More file actions
312 lines (273 loc) · 11.8 KB
/
Renderer.cs
File metadata and controls
312 lines (273 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
using OpenGL;
using StbImageSharp;
namespace Licenta_ver1
{
/// <summary>
/// Holds all per‐sphere parameters in one place.
/// </summary>
public class Sphere
{
public Vertex3f Center { get; set; }
public float Radius { get; set; }
public string TextureFile { get; set; }
public Vertex3f OrbitNormal { get; set; }
public Vertex3f SpinAxis { get; set; }
public float SpinPhase { get; set; }
public uint TextureId { get; set; }
public Sphere(
Vertex3f center,
float radius,
string textureFile,
Vertex3f orbitNormal,
Vertex3f spinAxis,
float spinPhase
) {
Center = center;
Radius = radius;
TextureFile = textureFile;
OrbitNormal = orbitNormal.Normalized;
SpinAxis = spinAxis.Normalized;
SpinPhase = spinPhase;
}
}
/// <summary>
/// Ray‐tracing renderer for a set of spheres (planets).
/// </summary>
public sealed class Renderer : IDisposable
{
// fullscreen quad geometry
private static readonly float[] QuadVerts = {
-1,-1, 1,-1, -1,1,
-1, 1, 1,-1, 1,1
};
private readonly List<Sphere> _spheres;
private readonly int[] _texUnits;
// GL objects
private readonly ShaderProgram _program;
private readonly VertexArray _vaoQuad;
// uniform locations
private readonly int
_locCameraPos,
_locInvPV,
_locSphereCount,
_locSphereCenters,
_locSphereRadii,
_locSpinAxes,
_locSpinPhases,
_locLightIndex,
_locSphereTexArray;
private bool _dataDirty = true;
public Renderer(IEnumerable<Sphere> spheres)
{
_spheres = spheres.ToList();
_texUnits = Enumerable.Range(0, _spheres.Count).ToArray();
// 1) compile shaders
_program = new ShaderProgram(
File.ReadAllText("../../../Shaders/RT_VS1.glsl")
.Replace("\r\n", "\n")
.Split('\n').Select(l => l + "\n").ToArray(),
File.ReadAllText("../../../Shaders/RT_FS4.glsl")
.Replace("\r\n","\n")
.Split('\n').Select(l=>l+"\n").ToArray()
);
Gl.UseProgram(_program.ProgramName);
// 2) create full‐screen quad VAO
_vaoQuad = new VertexArray(_program, new[]{ QuadVerts }, new[]{ 2f });
Gl.BindVertexArray(_vaoQuad.ArrayName);
// 3) get uniform locations
_locCameraPos = _program.GetUniformLocation("CameraPos");
_locInvPV = _program.GetUniformLocation("InvPV");
_locSphereCount = _program.GetUniformLocation("SphereCount");
_locSphereCenters = _program.GetUniformLocation("SphereCenters");
_locSphereRadii = _program.GetUniformLocation("SphereRadii");
_locSpinAxes = _program.GetUniformLocation("SphereSpinAxes");
_locSpinPhases = _program.GetUniformLocation("SphereSpinPhases");
_locLightIndex = _program.GetUniformLocation("LightIndex");
_locSphereTexArray = _program.GetUniformLocation("SphereTex");
// default light = sphere #0
Gl.Uniform1(_locLightIndex, _spheres.Count - 1);
// upload textures & sphere data
LoadSphereTextures();
UploadSphereDataToShader();
}
private void LoadSphereTextures()
{
for(int i=0;i<_spheres.Count;i++){
var s = _spheres[i];
// load image
StbImage.stbi_set_flip_vertically_on_load(1);
using var fs = File.OpenRead("../../../Resources/"+s.TextureFile);
var img = ImageResult.FromStream(fs,ColorComponents.RedGreenBlue);
uint tex = Gl.GenTexture();
Gl.BindTexture(TextureTarget.Texture2d, tex);
Gl.TexImage2D(TextureTarget.Texture2d,0,InternalFormat.Rgb,
img.Width,img.Height,0,
PixelFormat.Rgb,PixelType.UnsignedByte,img.Data);
Gl.GenerateMipmap(TextureTarget.Texture2d);
Gl.TexParameter(TextureTarget.Texture2d,TextureParameterName.TextureMinFilter,(int)TextureMinFilter.LinearMipmapLinear);
Gl.TexParameter(TextureTarget.Texture2d,TextureParameterName.TextureMagFilter,(int)TextureMagFilter.Linear);
s.TextureId = tex;
}
}
private void UploadSphereDataToShader()
{
int n = _spheres.Count;
Gl.Uniform1(_locSphereCount, n);
for(int i=0;i<n;i++){
var s = _spheres[i];
Gl.Uniform3f(_locSphereCenters + i,1,s.Center);
Gl.Uniform1f(_locSphereRadii + i,1,s.Radius);
Gl.Uniform3f(_locSpinAxes + i,1,s.SpinAxis);
Gl.Uniform1f(_locSpinPhases + i,1,s.SpinPhase);
}
_dataDirty = false;
}
/// <summary>
/// Rotate each sphere around its axis.
/// </summary>
public void AdvanceSpins(float dt, float spinRateRadPerSec)
{
foreach(var s in _spheres)
s.SpinPhase += dt * spinRateRadPerSec;
_dataDirty = true;
}
/// <summary>
/// Overwrite every per‐sphere uniform (center, radius, orbitNormal, spinAxis,
/// spinPhase, etc.) from your runtime body objects, and mark dirty so they get
/// re‐sent before the next draw.
/// </summary>
public void UpdateAllSphereData(List<PlanetLoader.CelestialBodyData> bodies)
{
int i = 0;
foreach (var b in bodies)
{
// 1) world‐space center
_spheres[i].Center = b.Position;
// 2) radius
_spheres[i].Radius = (float)b.Radius;
// 3) orbit normal: tilt the +Y axis by OrbitInclinationDeg around X
float tilt = b.OrbitInclinationDeg * MathF.PI / 180f;
_spheres[i].OrbitNormal = new Vertex3f(
MathF.Sin(tilt),
MathF.Cos(tilt),
0f
).Normalized;
// 4) spin axis: tilt the +Y axis by SpinAxisDeg around X
float spinAx = b.SpinAxisDeg;
_spheres[i].SpinAxis = new Vertex3f(
MathF.Sin(spinAx),
MathF.Cos(spinAx),
0f
).Normalized;
// 5) spin phase, in radians
_spheres[i].SpinPhase = b.SpinPhaseDeg;
i++;
if (i >= _spheres.Count) break;
}
// flag that we must re‐upload all uniforms next Render()
_dataDirty = true;
}
/// <summary>
/// Render all spheres, orbiting the camera around the specified primary focus.
/// </summary>
/// <param name="width">Viewport width in pixels.</param>
/// <param name="height">Viewport height in pixels.</param>
/// <param name="primaryFocusName">
/// Name of the planet to orbit around and look at. Must match Sphere.TextureName.
/// </param>
/// <param name="secondaryFocusName">
/// Optional name of a secondary planet. If provided, the camera’s base yaw/pitch
/// will initially point from primary→secondary, then yaw/pitch offsets are applied.
/// </param>
/// <param name="yawDeg">Additional yaw offset in degrees (± around vertical axis).</param>
/// <param name="pitchDeg">Additional pitch offset in degrees (± around horizontal axis).</param>
/// <param name="distanceInRadii">
/// How far the camera should be from the primary focus, in units of that planet’s radius.
/// </param>
public void Render(
int width,
int height,
string primaryTexture,
string? secondaryTexture,
float yawDeg,
float pitchDeg,
float distanceInRadii
)
{
// 1) Primary sphere lookup by texture name field
var primary =
_spheres.FirstOrDefault(s => Path.GetFileNameWithoutExtension(s.TextureFile)
.Equals(primaryTexture, StringComparison.OrdinalIgnoreCase)) ?? _spheres[9];
Sphere secondary;
if (string.IsNullOrEmpty(secondaryTexture)
|| secondaryTexture.Equals("None", StringComparison.OrdinalIgnoreCase))
{
int idx = Math.Min(9, _spheres.Count - 1);
secondary = _spheres[idx];
}
else
{
secondary = _spheres
.FirstOrDefault(s =>
Path.GetFileNameWithoutExtension(s.TextureFile)
.Equals(secondaryTexture, StringComparison.OrdinalIgnoreCase))
?? primary; // fallback if not found
}
// 3) Compute baseline yaw/pitch pointing from primary → secondary
var dir = primary.Center - secondary.Center;
float dx = dir.x, dy = dir.y, dz = dir.z;
float dist = MathF.Sqrt(dx * dx + dy * dy + dz * dz);
float baseYaw = 0f, basePitch = 0f;
if (dist > 1e-6f)
{
// now the camera will face the primary with its front, not its back
baseYaw = MathF.Atan2(dx, dz) * 180f / MathF.PI;
basePitch = MathF.Asin(dy / dist) * 180f / MathF.PI;
}
// 4) Add your yaw/pitch offsets
float finalYaw = baseYaw + yawDeg;
float finalPitch = basePitch + pitchDeg;
// 5) Convert to spherical camera position
float yRad = finalYaw * (MathF.PI / 180f);
float pRad = finalPitch * (MathF.PI / 180f);
float R = primary.Radius;
float d = distanceInRadii * R;
var camPos = new Vertex3f(
primary.Center.x + d * MathF.Sin(yRad) * MathF.Cos(pRad),
primary.Center.y + d * MathF.Sin(pRad),
primary.Center.z + d * MathF.Cos(yRad) * MathF.Cos(pRad)
);
// 6) Build invPV
var view = Matrix4x4f.LookAt(camPos, primary.Center, Vertex3f.UnitY);
var proj = Matrix4x4f.Perspective(60f, width / (float)height, 0.1f, 100f);
var invPV = (view * proj).Inverse;
// 7) GL draw setup
Gl.UseProgram(_program.ProgramName);
Gl.BindVertexArray(_vaoQuad.ArrayName);
if (_dataDirty) UploadSphereDataToShader();
// bind textures & sampler array
for (int i = 0; i < _spheres.Count; i++)
{
Gl.ActiveTexture(TextureUnit.Texture0 + i);
Gl.BindTexture(TextureTarget.Texture2d, _spheres[i].TextureId);
}
unsafe
{
fixed (int* ptr = &_texUnits[0])
Gl.Uniform1(_locSphereTexArray, _spheres.Count, ptr);
}
// camera uniforms
Gl.Uniform3f(_locCameraPos, 1, camPos);
Gl.UniformMatrix4f(_locInvPV, 1, false, invPV);
// 8) Draw fullscreen quad
Gl.DrawArrays(PrimitiveType.Triangles, 0, 6);
}
public void Dispose()
{
_vaoQuad.Dispose();
_program.Dispose();
foreach(var s in _spheres)
Gl.DeleteTextures(s.TextureId);
}
}
}