-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathProgram.cs
199 lines (147 loc) · 6.95 KB
/
Program.cs
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
namespace example;
using static example.Toolkit;
// example-0007
// draw nurb tube with triangle selection on click
//
// - click on a triangle of the mesh to get highlighted over all views
// - drag splitter handler to resize view
// - press 'c' to close splitted view, 'h' to split horizontally, 'v' to split vertically
class Program
{
static void Main(string[] args)
{
InitAvalonia();
var w = GLWindow.Create(
// specializing this method allow to to split initial control
// TIP: view layout can be saved through Control+F2 then reloaded programmtically (see example-0005)
onFocusedControlChanged: (glSplit, avaloniaGLControl, isInitial) =>
{
if (!isInitial) return;
if (glSplit.FocusedControl is GLView initialGlView)
{
initialGlView.AvaloniaGLControl.GLControl.CameraView(CameraViewType.Front);
initialGlView.AvaloniaGLControl.GLControl.Title = "Front";
// split current (1) horizontally toward 2
// (1) | *2
AvaloniaGLControl? aControl;
if ((aControl = glSplit.Split(GridSplitDirection.Horizontally)?.AvaloniaGLControl) is not null)
{
// the new splitted control will initialized by the same method onGLControlCreated
// here we change some control properties for this new view
var glCtl = aControl.GLControl;
glCtl.Wireframe = true;
glCtl.CameraView(CameraViewType.Top);
glCtl.Title = "Top";
}
// split current (2) vertically toward 3
// 1 | (2)
// ---|-----
// | *3
if ((aControl = glSplit.Split(GridSplitDirection.Vertically)?.AvaloniaGLControl) is not null)
{
var glCtl = aControl.GLControl;
glCtl.Wireframe = true;
glCtl.CameraView(CameraViewType.Right);
glCtl.Title = "Right";
}
// split current (3) horizontally toward 4
// 1 | 2
// ---|----------
// | (3) | *4
if ((aControl = glSplit.Split(GridSplitDirection.Horizontally)?.AvaloniaGLControl) is not null)
{
var glCtl = aControl.GLControl;
glCtl.Wireframe = true;
glCtl.CameraView(CameraViewType.FrontBottomLeft);
glCtl.Title = "FrontBottomLeft";
}
// back to initial control
glSplit.FocusedControl = initialGlView;
// split current (1) vertically toward 5
// (1) | 2
// -----|-------
// *5 | 3 | 4
if ((aControl = glSplit.Split(GridSplitDirection.Vertically)?.AvaloniaGLControl) is not null)
{
var glCtl = aControl.GLControl;
glCtl.Wireframe = true;
glCtl.CameraView(CameraViewType.Front);
glCtl.Title = "Front";
}
}
}
);
GLTriangleFigure? fig = null;
w.GLModel.BuildModel = (glCtl, isInitial) =>
{
if (!isInitial) return;
var glModel = glCtl.GLModel;
glModel.Clear();
fig = Example0007(SURF_DIVS: 20);
fig.Move(500, 100, 200);
glModel.AddFigure(fig);
var lightPos = new Vector3(-82, -665, 1074);
glModel.PointLights.Clear();
glModel.PointLights.Add(new GLPointLight(lightPos, Color.White));
};
DateTime? lastPressTimestamp = null;
w.PointerPressed += (a, e) =>
{
if (e.Handled) return;
lastPressTimestamp = DateTime.Now;
};
w.PointerReleased += (a, e) =>
{
if (e.Handled) return;
if (lastPressTimestamp is null) return;
var pressDurationMs = (DateTime.Now - lastPressTimestamp.Value).TotalMilliseconds;
// if not a single click return
if (pressDurationMs > 250) return;
lastPressTimestamp = null;
var glView = w.GLControlSplit?.FocusedControl;
if (glView is null) return;
// retrieve current position relative to the focused control view
var cp = e.GetCurrentPoint(glView);
var glCtl = glView.AvaloniaGLControl.GLControl;
var glModel = glCtl.GLModel;
if (cp.Properties.PointerUpdateKind == Avalonia.Input.PointerUpdateKind.LeftButtonReleased)
{
var sp = cp.Position.ToVector2();
var mm = glCtl.ModelMatrix;
var vm = glCtl.ViewMatrix;
var pm = glCtl.ProjectionMatrix;
var camp = glCtl.CameraPos;
var ssize = glCtl.Size();
var vtxMgr = glCtl.GLModel.GLVertexManager;
var perspective = glCtl.Perspective;
//! [RayCast]
// retrieve ray to cast [local] to test any object in the scene
// note: when testing a figure primitive the ray need to be backwarded to object coord (see below)
var lraycast = glCtl.RayCastLocal(screen: cp.Position.ToVector2());
// create static list of figures, because we create new figures inside the loop
var figures = glModel.Figures.ToList();
var hits = figures.SelectMany(fig => lraycast.Intersect(tol: 1e-5f, fig)).ToList();
foreach (var hit in hits)
{
if (hit.Primitive is not GLTriangle tri) continue;
Debug.WriteLine($"hit tri [{tri}]");
tri.SetColor(Color.Yellow);
tri.Order = 1;
// add a further line (orange) to hitted triangle
var line = GLLine.FromTo(
(GLVertex)tri.V1.Copy(),
new GLVertex((tri.V2.Position + tri.V3.Position) / 2));
var lineFig = new GLLineFigure(line) { Order = 1 };
lineFig.SetColor(Color.DarkOrange);
// note: linefig built using [local] figure coordinates
// so we need to copy the same object matrix to replicate effective position
lineFig.ObjectMatrix = hit.Figure.ObjectMatrix;
vtxMgr.AddFigure(lineFig);
}
// invalidate all the layout views
glCtl.InvalidateAll();
}
};
w.ShowSync();
}
}