Skip to content

Commit 20013e1

Browse files
committed
editor: rotational gizmo
#976
1 parent d83c6db commit 20013e1

8 files changed

Lines changed: 661 additions & 82 deletions

File tree

‎common/graphics/renderer.cpp‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,10 @@ void Renderer::onWorldChanged() {
229229
resetSkyFog();
230230
}
231231

232-
void Renderer::setGizmo(bool enable, Tempest::Vec3 center) {
232+
void Renderer::setGizmo(bool enable, Tempest::Vec3 center, int mode) {
233233
gizmo.enable = enable;
234234
gizmo.center = center;
235+
gizmo.mode = mode;
235236
}
236237

237238
void Renderer::setLightsHud(const Tempest::Texture2d* tex) {
@@ -940,12 +941,27 @@ void Renderer::drawGizmo(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const Wo
940941
auto vp = wview.sceneGlobals().viewProject();
941942
vp.project(cen.x, cen.y, cen.z, w);
942943

943-
cmd.setPushData(gizmo.center);
944+
struct Push {
945+
Tempest::Vec3 center;
946+
uint32_t axisBits;
947+
} push = {};
948+
push.center = gizmo.center;
949+
push.axisBits = 0x7;
950+
944951
cmd.setDebugMarker("Hud-Gizmo");
945952
cmd.setBinding(0, wview.sceneGlobals().uboGlobal[SceneGlobals::V_Main]);
946953
cmd.setBinding(1, zbuffer);
947954
cmd.setPipeline(shaders.gizmo);
948-
cmd.draw(nullptr, 0, 36, 0, 3);
955+
if(gizmo.mode==0) {
956+
push.axisBits = 0xF;
957+
cmd.setPushData(push);
958+
cmd.draw(nullptr, 0, 36, 0, 4);
959+
}
960+
else if(gizmo.mode==1) {
961+
push.axisBits = 0x70;
962+
cmd.setPushData(push);
963+
cmd.draw(nullptr, 0, 36, 4, 3);
964+
}
949965
}
950966

951967
void Renderer::drawLightsHud(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const WorldView& wview) {

‎common/graphics/renderer.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Renderer final {
2121
~Renderer();
2222

2323
void onWorldChanged();
24-
void setGizmo(bool enable, Tempest::Vec3 center);
24+
void setGizmo(bool enable, Tempest::Vec3 center, int mode);
2525
void setLightsHud(const Tempest::Texture2d* tex);
2626

2727
void draw(Tempest::Attachment& result, Tempest::Encoder<Tempest::CommandBuffer>& cmd, uint8_t fId,
@@ -300,6 +300,7 @@ class Renderer final {
300300
struct {
301301
Tempest::Vec3 center = {};
302302
bool enable = false;
303+
int mode = 0;
303304
} gizmo;
304305

305306
struct {

‎shader/hud/gizmo.frag‎

Lines changed: 210 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,36 @@ layout(location = 0) out vec4 outColor;
99

1010
layout(location = 0) in flat uint axis;
1111
layout(location = 1) in flat float scale;
12+
layout(location = 2) in vec3 baseColor;
13+
14+
const float TMax = 1e30f;
1215

1316
layout(std140, push_constant) uniform Push {
1417
vec3 origin;
18+
uint axisBits;
1519
};
1620
layout(binding = 0, std140) uniform UboScene {
1721
SceneDesc scene;
1822
};
1923
layout(binding = 1) uniform texture2D depth;
2024

25+
struct HitResult {
26+
vec3 norm;
27+
float hitT;
28+
int axis;
29+
};
30+
31+
void handleHit(inout HitResult hit, vec3 n, float t, int axis) {
32+
if(t >= hit.hitT)
33+
return;
34+
hit.norm = n;
35+
hit.hitT = t;
36+
hit.axis = axis;
37+
}
2138

2239
float dot2(vec3 v) { return dot(v,v); }
2340

24-
vec4 cylIntersect(vec3 ro, vec3 rd, vec3 a, vec3 b, float ra ) {
41+
vec4 cylIntersect(inout HitResult hit, int axis, vec3 ro, vec3 rd, vec3 a, vec3 b, float ra) {
2542
vec3 ba = b - a;
2643
vec3 oc = ro - a;
2744

@@ -39,18 +56,51 @@ vec4 cylIntersect(vec3 ro, vec3 rd, vec3 a, vec3 b, float ra ) {
3956
float t = (-k1-h)/k2;
4057
// body
4158
float y = baoc + t*bard;
42-
if( y>0.0 && y<baba )
59+
if( y>0.0 && y<baba ) {
60+
handleHit(hit, (oc+t*rd - ba*y/baba)/ra, t, axis);
4361
return vec4((oc+t*rd - ba*y/baba)/ra, t);
62+
}
4463

4564
// caps
4665
t = ( ((y<0.0) ? 0.0 : baba) - baoc)/bard;
47-
if(abs(k1+k2*t) < h)
66+
if(abs(k1+k2*t) < h) {
67+
handleHit(hit, ba*sign(y)/sqrt(baba), t, axis);
4868
return vec4(ba*sign(y)/sqrt(baba), t);
69+
}
4970

5071
return vec4(-1.0);//no intersection
5172
}
5273

53-
vec4 coneIntersect(vec3 ro, vec3 rd, vec3 pa, vec3 pb, float ra, float rb) {
74+
vec4 sphereIntersect(inout HitResult hit, int axis, vec3 ro, vec3 rd, vec3 center, float radius) {
75+
vec3 oc = ro - center;
76+
77+
float b = dot(oc, rd);
78+
float c = dot(oc, oc) - radius * radius;
79+
80+
float h = b*b - c;
81+
82+
if(h < 0.0)
83+
return vec4(-1.0);
84+
85+
h = sqrt(h);
86+
87+
float t = -b - h;
88+
89+
// Camera is inside sphere: use far intersection.
90+
if(t < 0.0)
91+
t = -b + h;
92+
93+
if(t < 0.0)
94+
return vec4(-1.0);
95+
96+
vec3 p = ro + rd * t;
97+
vec3 n = normalize(p - center);
98+
99+
handleHit(hit, n, t, axis);
100+
return vec4(n, t);
101+
}
102+
103+
bool coneIntersect(inout HitResult hit, int axis, vec3 ro, vec3 rd, vec3 pa, vec3 pb, float ra, float rb) {
54104
vec3 ba = pb - pa;
55105
vec3 oa = ro - pa;
56106
vec3 ob = ro - pb;
@@ -63,13 +113,18 @@ vec4 coneIntersect(vec3 ro, vec3 rd, vec3 pa, vec3 pb, float ra, float rb) {
63113

64114
// caps
65115
if(m1 < 0.0) {
66-
if(dot2(oa*m2-rd*m1) < (ra*ra*m2*m2)) // delayed division
67-
return vec4(-ba*inversesqrt(m0), -m1/m2);
116+
if(dot2(oa*m2-rd*m1) < (ra*ra*m2*m2)) {
117+
// delayed division
118+
handleHit(hit, -ba*inversesqrt(m0), -m1/m2, axis);
119+
return true;
120+
}
68121
}
69122
else if(m9 > 0.0) {
70123
float t = -m9/m2; // NOTE delayed division
71-
if(dot2(ob+rd*t) < (rb*rb))
72-
return vec4(ba*inversesqrt(m0), t);
124+
if(dot2(ob+rd*t) < (rb*rb)) {
125+
handleHit(hit, ba*inversesqrt(m0), t, axis);
126+
return true;
127+
}
73128
}
74129

75130
// body
@@ -80,25 +135,158 @@ vec4 coneIntersect(vec3 ro, vec3 rd, vec3 pa, vec3 pb, float ra, float rb) {
80135
float k0 = m0*m0*m5 - m1*m1*hy + m0*ra*(rr*m1*2.0 - m0*ra);
81136
float h = k1*k1 - k2*k0;
82137
if(h < 0.0)
83-
return vec4(-1.0); //no intersection
138+
return false; //no intersection
84139
float t = (-k1-sqrt(h))/k2;
85140
float y = m1 + t*m2;
86141
if(y < 0.0 || y > m0)
87-
return vec4(-1.0); //no intersection
88-
return vec4(normalize(m0*(m0*(oa+t*rd)+rr*ba*ra)-ba*hy*y), t);
142+
return false; //no intersection
143+
handleHit(hit, normalize(m0*(m0*(oa+t*rd)+rr*ba*ra)-ba*hy*y), t, axis);
144+
return true;
89145
}
90146

91-
vec4 arrowIntersect(vec3 ro, vec3 rd) {
147+
void arrowIntersect(inout HitResult hit, int axis, vec3 ro, vec3 rd) {
92148
vec3 off0 = vec3(0), off1 = vec3(0), off2 = vec3(0);
93149
off0[axis] = scale*10;
94150
off1[axis] = scale*160;
95151
off2[axis] = scale*200;
96152

97-
vec4 body = cylIntersect (ro, rd, origin, origin + off1, 4.0 * scale);
98-
vec4 cap = coneIntersect(ro, rd, origin + off1, origin + off2, 10.0 * scale, 0);
99-
if(body.w>=0 && (body.w<cap.w || cap.w<=0))
100-
return body;
101-
return cap;
153+
cylIntersect (hit, axis, ro, rd, origin, origin + off1, 4.0 * scale);
154+
coneIntersect(hit, axis, ro, rd, origin + off1, origin + off2, 10.0 * scale, 0);
155+
}
156+
157+
bool rotationRingIntersect(inout HitResult hit, int axis, vec3 ro, vec3 rd, float rIn, float rOut) {
158+
ro -= origin;
159+
if(axis == 0) {
160+
ro = ro.zyx;
161+
rd = rd.zyx;
162+
}
163+
else if(axis == 1) {
164+
ro = ro.xzy;
165+
rd = rd.xzy;
166+
}
167+
else {
168+
ro = ro.xyz;
169+
rd = rd.xyz;
170+
}
171+
172+
float po = 1.0;
173+
float Ra2 = 0.5*(rIn + rOut); Ra2*=Ra2;
174+
float ra2 = 0.5*(rIn - rOut); ra2*=ra2;
175+
176+
float m = dot(ro, ro);
177+
float n = dot(ro, rd);
178+
float k = (m + Ra2 - ra2)/2.0;
179+
float k3 = n;
180+
float k2 = n*n - Ra2*dot(rd.xy,rd.xy) + k;
181+
float k1 = n*k - Ra2*dot(rd.xy,ro.xy);
182+
float k0 = k*k - Ra2*dot(ro.xy,ro.xy);
183+
184+
if(abs(k3*(k3*k3-k2)+k1) < 0.01) {
185+
po = -1.0;
186+
float tmp=k1; k1=k3; k3=tmp;
187+
k0 = 1.0/k0;
188+
k1 = k1*k0;
189+
k2 = k2*k0;
190+
k3 = k3*k0;
191+
}
192+
193+
float c2 = k2*2.0 - 3.0*k3*k3;
194+
float c1 = k3*(k3*k3-k2)+k1;
195+
float c0 = k3*(k3*(c2+2.0*k2)-8.0*k1)+4.0*k0;
196+
c2 /= 3.0;
197+
c1 *= 2.0;
198+
c0 /= 3.0;
199+
float Q = c2*c2 + c0;
200+
float R = c2*c2*c2 - 3.0*c2*c0 + c1*c1;
201+
float h = R*R - Q*Q*Q;
202+
203+
if( h>=0.0 ) {
204+
h = sqrt(h);
205+
float v = sign(R+h)*pow(abs(R+h),1.0/3.0); // cube root
206+
float u = sign(R-h)*pow(abs(R-h),1.0/3.0); // cube root
207+
vec2 s = vec2( (v+u)+4.0*c2, (v-u)*sqrt(3.0));
208+
float y = sqrt(0.5*(length(s)+s.x));
209+
float x = 0.5*s.y/y;
210+
float r = 2.0*c1/(x*x+y*y);
211+
float t1 = x - r - k3; t1 = (po<0.0)?2.0/t1:t1;
212+
float t2 = -x - r - k3; t2 = (po<0.0)?2.0/t2:t2;
213+
float t = TMax;
214+
if( t1>0.0 ) t=t1;
215+
if( t2>0.0 ) t=min(t,t2);
216+
217+
vec3 pos = ro + t*rd;
218+
vec3 norm = normalize(pos*(dot(pos,pos)-ra2 - Ra2*vec3(1.0,1.0,-1.0)));
219+
if(axis==0)
220+
norm = norm.zyx;
221+
else if(axis==1)
222+
norm = norm.xzy;
223+
handleHit(hit, norm, t, axis);
224+
return true;
225+
}
226+
227+
float sQ = sqrt(Q);
228+
float w = sQ*cos( acos(-R/(sQ*Q)) / 3.0 );
229+
float d2 = -(w+c2);
230+
if( d2<0.0 )
231+
return false;
232+
float d1 = sqrt(d2);
233+
float h1 = sqrt(w - 2.0*c2 + c1/d1);
234+
float h2 = sqrt(w - 2.0*c2 - c1/d1);
235+
float t1 = -d1 - h1 - k3; t1 = (po<0.0)?2.0/t1:t1;
236+
float t2 = -d1 + h1 - k3; t2 = (po<0.0)?2.0/t2:t2;
237+
float t3 = d1 - h2 - k3; t3 = (po<0.0)?2.0/t3:t3;
238+
float t4 = d1 + h2 - k3; t4 = (po<0.0)?2.0/t4:t4;
239+
float t = TMax;
240+
if( t1>0.0 ) t=t1;
241+
if( t2>0.0 ) t=min(t,t2);
242+
if( t3>0.0 ) t=min(t,t3);
243+
if( t4>0.0 ) t=min(t,t4);
244+
245+
vec3 pos = ro + t*rd;
246+
vec3 norm = normalize(pos*(dot(pos,pos)-ra2 - Ra2*vec3(1.0,1.0,-1.0)));
247+
if(axis==0)
248+
norm = norm.zyx;
249+
else if(axis==1)
250+
norm = norm.xzy;
251+
handleHit(hit, norm, t, axis);
252+
return true;
253+
}
254+
255+
vec4 gizmoIntersect(vec3 ro, vec3 rd, int axis) {
256+
HitResult hit;
257+
hit.hitT = TMax;
258+
if(axis < 4) {
259+
arrowIntersect (hit, 0, ro, rd);
260+
arrowIntersect (hit, 1, ro, rd);
261+
arrowIntersect (hit, 2, ro, rd);
262+
sphereIntersect(hit, 3, ro, rd, origin, 10.0 * scale);
263+
if(hit.axis!=axis)
264+
return vec4(-1);
265+
return vec4(hit.norm, hit.hitT);
266+
}
267+
268+
axis -= 4;
269+
if(axis < 3) {
270+
rotationRingIntersect(hit, 0, ro, rd, 145.0 * scale, 155.0 * scale);
271+
rotationRingIntersect(hit, 1, ro, rd, 145.0 * scale, 155.0 * scale);
272+
rotationRingIntersect(hit, 2, ro, rd, 145.0 * scale, 155.0 * scale);
273+
sphereIntersect(hit, -1, ro, rd, origin, 145.0 * scale);
274+
275+
if(hit.axis!=axis)
276+
return vec4(-1);
277+
return vec4(hit.norm, hit.hitT);
278+
}
279+
280+
return vec4(-1);
281+
}
282+
283+
bool depthTest(vec3 ro, vec3 rd, float t) {
284+
vec3 hitPos = ro + rd * t;
285+
vec4 hitClip = scene.viewProject * vec4(hitPos, 1.0);
286+
float hitNdcZ = hitClip.z / hitClip.w;
287+
288+
float d = texelFetch(depth, ivec2(gl_FragCoord.xy), 0).x;
289+
return hitNdcZ > d;
102290
}
103291

104292
void main() {
@@ -109,20 +297,17 @@ void main() {
109297
const vec3 camPos = scene.camPos;
110298
const vec3 view = normalize(start - camPos);
111299

112-
const vec4 nort = arrowIntersect(camPos, view);
113-
if(nort.w>0) {
114-
vec3 clr = vec3(0);
115-
clr[axis] = 1;
300+
const vec4 nort = gizmoIntersect(camPos, view, int(axis));
301+
if(nort.w>0 && nort.w!=TMax) {
302+
vec3 clr = baseColor;
116303
clr *= (max(0.0, dot(scene.sunDir, nort.xyz))*0.8 + 0.2);
117-
float d = texelFetch(depth, ivec2(gl_FragCoord.xy), 0).x;
118-
float dl = linearDepth(d, scene.clipInfo);
119-
if(nort.w > dl) {
304+
if(depthTest(camPos, view, nort.w)) {
120305
ivec2 id = ivec2(gl_FragCoord.xy)/4;
121306
clr *= (id.x+id.y)%2==0 ? 0.1 : 0.9;
122307
}
123308
outColor = vec4(clr,1);
124309
return;
125310
}
126-
311+
outColor = vec4(0,0,0,1);
127312
discard;
128313
}

0 commit comments

Comments
 (0)