Initial sauer
[SauerbratenRemote.git] / src / engine / grass.cpp
blobcd40471584d987f0310fa71b8cb9f760f9c1717e
1 #include "pch.h"
2 #include "engine.h"
4 VARP(grassanimdist, 0, 500, 10000);
5 VARP(grassdist, 0, 500, 10000);
6 VARP(grassfalloff, 0, 100, 1000);
8 VAR(grasswidth, 1, 6, 64);
9 VAR(grassheight, 1, 8, 64);
11 void resetgrasssamples()
13 extern vector<vtxarray *> valist;
14 loopv(valist)
16 vtxarray *va = valist[i];
17 DELETEP(va->grasssamples);
21 VARF(grassgrid, 1, 6, 32, resetgrasssamples());
23 void gengrasssample(vtxarray *va, const vec &o, float tu, float tv, LightMap *lm)
25 grasssample &g = va->grasssamples->add();
27 g.x = ushort(o.x-va->x) | GRASS_SAMPLE;
28 g.y = ushort(o.y-va->y);
29 g.z = ushort(4*(o.z-va->z));
31 if(lm)
33 tu = min(tu, LM_PACKW-0.01f);
34 tv = min(tv, LM_PACKH-0.01f);
35 memcpy(g.color, &lm->data[3*(int(tv)*LM_PACKW + int(tu))], 3);
37 else loopk(3) g.color[k] = hdr.ambient;
40 bool gengrassheader(vtxarray *va, const vec *v)
42 vec center = v[0];
43 center.add(v[1]);
44 center.add(v[2]);
45 center.div(3);
47 float r1 = center.dist(v[0]),
48 r2 = center.dist(v[1]),
49 r3 = center.dist(v[2]),
50 radius = min(r1, min(r2, r3));
51 if(radius < grassgrid*2) return false;
53 grassbounds &g = *(grassbounds *)&va->grasssamples->add();
54 g.x = ushort(center.x-va->x) | GRASS_BOUNDS;
55 g.y = ushort(center.y-va->y);
56 g.z = ushort(4*(center.z-va->z));
57 g.radius = ushort(radius + grasswidth);
58 g.numsamples = 0;
59 return true;
62 void gengrasssamples(vtxarray *va, const vec *v, float *tc, LightMap *lm)
64 int u, l, r;
65 if(v[1].y < v[0].y) u = v[1].y < v[2].y ? 1 : 2;
66 else u = v[0].y < v[2].y ? 0 : 2;
67 l = (u+2)%3;
68 r = (u+1)%3;
69 if(v[l].x > v[r].x) swap(int, l, r);
70 if(v[u].y == v[l].y)
72 if(v[l].x <= v[u].x) swap(int, u, l);
73 swap(int, l, r);
75 vec o1 = v[u], dl = v[l];
76 dl.sub(o1);
77 if(dl.x==0 && dl.y==0) return;
78 float endl = v[l].y,
79 ls = tc[2*u], lt = tc[2*u+1],
80 lds = tc[2*l] - ls, ldt = tc[2*l+1] - lt;
82 vec o2, dr;
83 float endr, rs, rt, rds, rdt;
84 if(v[u].y==v[r].y)
86 if(v[u].x==v[r].x) return;
87 o2 = v[r];
88 dr = v[l];
89 dr.sub(o2);
90 endr = v[l].y;
92 rs = tc[2*r];
93 rt = tc[2*r+1];
94 rds = tc[2*l] - rs;
95 rdt = tc[2*l+1] - rt;
97 else
99 o2 = v[u];
100 dr = v[r];
101 dr.sub(o2);
102 endr = v[r].y;
103 rs = ls;
104 rt = lt;
105 rds = tc[2*r] - rs;
106 rdt = tc[2*r+1] - rt;
108 if(dr.y==0 && (dr.x==0 || dl.y==0)) return;
109 if(dr.x==0 && dl.x==0) return;
111 bool header = false;
112 int numsamples = 0;
113 float dy = grassgrid - fmodf(o1.y, grassgrid);
114 for(;;)
116 if(endl > o1.y) dy = min(dy, endl - o1.y);
117 if(endr > o2.y) dy = min(dy, endr - o2.y);
119 o1.y += dy;
120 o1.x += dl.x * dy/dl.y;
121 o1.z += dl.z * dy/dl.y;
122 ls += lds * dy/dl.y;
123 lt += ldt * dy/dl.y;
125 o2.y += dy;
126 o2.x += dr.x * dy/dr.y;
127 o2.z += dr.z * dy/dr.y;
128 rs += rds * dy/dr.y;
129 rt += rdt * dy/dr.y;
131 if(o1.y <= endl && o2.y <= endr && fmod(o1.y, grassgrid) < 0.01f)
133 vec p = o1, dp = o2;
134 dp.sub(o1);
135 float s = ls, t = lt,
136 ds = rs - ls, dt = rt - lt;
137 float dx = grassgrid - fmodf(o1.x, grassgrid);
138 if(o1.x==o2.x && dx==grassgrid)
140 if(!numsamples++) header = gengrassheader(va, v);
141 gengrasssample(va, p, s, t, lm);
143 else while(!header || numsamples<USHRT_MAX)
145 p.x += dx;
146 p.y += dp.y * dx/dp.x;
147 p.z += dp.z * dx/dp.x;
148 s += ds * dx/dp.x;
149 t += dt * dx/dp.x;
151 if(p.x > o2.x) break;
153 if(!numsamples++) header = gengrassheader(va, v);
154 gengrasssample(va, p, s, t, lm);
156 dx = grassgrid;
158 if(header && numsamples>=USHRT_MAX) break;
161 if(o1.y >= endl)
163 if(v[r].y <= endl) break;
164 dl = v[r];
165 dl.sub(v[l]);
166 endl = v[r].y;
167 lds = tc[2*r] - tc[2*l];
168 ldt = tc[2*r+1] - tc[2*l+1];
170 dy = grassgrid - fmod(o1.y, grassgrid);
171 continue;
174 if(o2.y >= endr)
176 if(v[l].y <= endr) break;
177 dr = v[l];
178 dr.sub(v[r]);
179 endr = v[l].y;
180 rds = tc[2*l] - tc[2*r];
181 rdt = tc[2*l+1] - tc[2*r+1];
183 dy = grassgrid - fmod(o1.y, grassgrid);
184 continue;
187 dy = grassgrid;
189 if(header)
191 grassbounds &g = *(grassbounds *)&(*va->grasssamples)[va->grasssamples->length() - numsamples - 1];
192 g.numsamples = numsamples;
196 void gengrasssamples(vtxarray *va)
198 if(va->grasssamples) return;
199 va->grasssamples = new vector<grasssample>;
200 int lasttex = -1;
201 loopv(*va->grasstris)
203 grasstri &g = (*va->grasstris)[i];
204 if(g.texture != lasttex)
206 grasstexture &t = *(grasstexture *)&va->grasssamples->add();
207 t.x = GRASS_TEXTURE;
208 t.texture = g.texture;
209 lasttex = g.texture;
211 vec v[4];
212 float tc[8];
213 static int remap[4] = { 1, 2, 0, 3 };
214 loopk(4)
216 int j = remap[k];
217 v[k] = g.v[j].tovec(va->x, va->y, va->z);
218 if(g.surface)
220 tc[2*k] = float(g.surface->x + (g.surface->texcoords[j*2] / 255.0f) * (g.surface->w - 1) + 0.5f);
221 tc[2*k+1] = float(g.surface->y + (g.surface->texcoords[j*2 + 1] / 255.0f) * (g.surface->h - 1) + 0.5f);
224 LightMap *lm = g.surface && g.surface->lmid >= LMID_RESERVED ? &lightmaps[g.surface->lmid-LMID_RESERVED] : NULL;
225 gengrasssamples(va, v, tc, lm);
226 gengrasssamples(va, &v[1], &tc[2], lm);
230 VAR(grasstest, 0, 0, 3);
232 static Texture *grasstex = NULL;
234 VARP(grasslod, 0, 25, 1000);
236 VARP(grasslodz, 0, 150, 10000);
238 float loddist(const vec &o)
240 float dx = o.x - camera1->o.x, dy = o.y - camera1->o.y, dz = camera1->o.z - o.z;
241 float dist = sqrt(dx*dx + dy*dy);
242 dist -= grasslodz/100.0f * max(dz, 0);
243 return max(dist, 0);
246 VAR(grassrand, 0, 30, 90);
248 VARP(grasssamples, 0, 50, 10000);
250 VARP(grassbillboard, 0, 1, 100);
251 VARP(grassbbcorrect, 0, 1, 1);
252 VARP(grasstaper, 0, 200, 10000);
254 void rendergrasssample(const grasssample &g, const vec &o, float dist, int seed, float height, int numsamples)
256 if(grasstest>2) return;
258 if(seed >= 2*numsamples) return;
260 vec up(0, 0, 1), right(seed%2, (seed+1)%2, 0);
261 float width = grasswidth;
262 if(numsamples<=grassbillboard)
264 if(seed%2) return;
265 right = camright;
266 if(grassrand) right.rotate_around_z((detrnd((size_t)&g * (seed + 1), 2*grassrand)-grassrand)*RAD);
267 if(grassbbcorrect)
269 if(fabs(right.x) > fabs(right.y)) width *= sqrt(right.y*right.y/(right.x*right.x) + 1);
270 else width *= sqrt(right.x*right.x/(right.y*right.y) + 1);
273 else if(grassrand) right.rotate_around_z((detrnd((size_t)&g * (seed + 1), 2*grassrand)-grassrand)*RAD);
275 vec b1 = right;
276 b1.mul(-0.5f*width);
277 b1.add(o);
278 b1[seed%2] += (seed/2 * grassgrid) / float(numsamples) - grassgrid/2.0f;
280 vec b2 = right;
281 b2.mul(width);
282 b2.add(b1);
284 vec t1 = b1, t2 = b2;
285 t1.z += grassheight * height;
286 t2.z += grassheight * height;
288 float w1 = 0, w2 = 0;
289 if(grasstest>0) t1 = t2 = b1;
290 else if(dist < grassanimdist)
292 w1 = detrnd((size_t)&g * (seed + 1)*7, 360)*RAD + t1.x*0.4f + t1.y*0.5f;
293 w1 += lastmillis*0.0015f;
294 w1 = sinf(w1);
295 vec d1(1.0f, 1.0f, 0.5f);
296 d1.mul(grassheight/4.0f * w1);
297 t1.add(d1);
299 w2 = detrnd((size_t)&g * (seed + 1)*11, 360)*RAD + t2.x*0.55f + t2.y*0.45f;
300 w2 += lastmillis*0.0015f;
301 w2 = sinf(w2);
302 vec d2(0.4f, 0.4f, 0.2f);
303 d2.mul(grassheight/4.0f * w2);
304 t2.add(d2);
307 if(grasstest>1) return;
309 extern int fullbright;
310 if(nolights || (fullbright && editmode)) glColor3ub(128, 128, 128);
311 else glColor3ubv(g.color);
312 float offset = detrnd((size_t)&g * (seed + 1)*13, grasstex->xs)/float(grasstex->xs);
313 glTexCoord2f(offset, 1); glVertex3fv(b1.v);
314 glTexCoord2f(offset, 0); glVertex3fv(t1.v);
315 glTexCoord2f(offset + float(grasswidth)*64.0f/grasstex->xs, 0); glVertex3fv(t2.v);
316 glTexCoord2f(offset + float(grasswidth)*64.0f/grasstex->xs, 1); glVertex3fv(b2.v);
317 xtraverts += 4;
320 void rendergrasssamples(vtxarray *va, const vec &dir)
322 if(!va->grasssamples) return;
323 loopv(*va->grasssamples)
325 grasssample &g = (*va->grasssamples)[i];
327 vec o((g.x&~GRASS_TYPE)+va->x, g.y+va->y, g.z/4.0f+va->z), tograss;
328 switch(g.x&GRASS_TYPE)
330 case GRASS_BOUNDS:
332 grassbounds &b = *(grassbounds *)&g;
333 if(reflecting && (refracting ? o.z-b.radius>=refracting : o.z+b.radius<reflecting))
335 i += b.numsamples;
336 continue;
338 float dist = o.dist(camera1->o, tograss);
339 if(dist > grassdist + b.radius || (dir.dot(tograss)<0 && dist > b.radius + 2*(grassgrid + player->eyeheight)))
340 i += b.numsamples;
341 break;
344 case GRASS_TEXTURE:
346 grasstexture &t = *(grasstexture *)&g;
347 Slot &s = lookuptexture(t.texture, false);
348 if(!s.grasstex || s.grasstex!=grasstex)
350 glEnd();
351 if(!s.grasstex) s.grasstex = textureload(s.autograss, 2);
352 glBindTexture(GL_TEXTURE_2D, s.grasstex->gl);
353 glBegin(GL_QUADS);
354 grasstex = s.grasstex;
356 break;
359 case GRASS_SAMPLE:
361 if(reflecting && (refracting ? o.z>=reflecting : o.z+grassheight<=reflecting)) continue;
362 float dist = o.dist(camera1->o, tograss);
363 if(dist > grassdist || (dir.dot(tograss)<0 && dist > grasswidth/2 + 2*(grassgrid + player->eyeheight))) continue;
365 float ld = loddist(o);
366 int numsamples = int(grasssamples/100.0f*max(grassgrid - ld/grasslod, 100.0f/grasssamples));
367 float height = 1 - (dist + grasstaper - grassdist) / (grasstaper ? grasstaper : 1);
368 height = min(height, 1);
369 loopj(2*numsamples)
371 rendergrasssample(g, o, dist, j, height, numsamples);
373 break;
379 VAR(grassblend, 0, 0, 100);
381 void setupgrass()
383 glDisable(GL_CULL_FACE);
384 glEnable(GL_ALPHA_TEST);
385 glAlphaFunc(GL_GREATER, grassblend ? grassblend/100.0f : 0.6f);
386 if(grassblend)
388 glEnable(GL_BLEND);
389 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
392 static Shader *grassshader = NULL;
393 if(!grassshader) grassshader = lookupshaderbyname("grass");
394 grassshader->set();
396 grasstex = NULL;
398 setuptmu(0, "C * T x 2");
400 glBegin(GL_QUADS);
403 void cleanupgrass()
405 glEnd();
407 resettmu(0);
409 defaultshader->set();
411 if(grassblend) glDisable(GL_BLEND);
412 glDisable(GL_ALPHA_TEST);
413 glEnable(GL_CULL_FACE);
416 VARP(grass, 0, 1, 1);
418 void rendergrass()
420 if(!grass || !grasssamples || !grassdist) return;
422 vec dir;
423 vecfromyawpitch(camera1->yaw, 0, 1, 0, dir);
425 int rendered = 0;
426 extern vtxarray *visibleva;
427 for(vtxarray *va = visibleva; va; va = va->next)
429 if(!va->grasstris || va->occluded >= OCCLUDE_GEOM || va->curlod) continue;
430 if(va->distance > grassdist) continue;
431 if(reflecting && (refracting ? va->z>=refracting : va->z+va->size<reflecting)) continue;
432 if(!va->grasssamples) gengrasssamples(va);
433 if(!rendered++) setupgrass();
434 rendergrasssamples(va, dir);
437 if(rendered) cleanupgrass();