UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
r_grass.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2013-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "r_local.h"
27 #include "r_grass.h"
28 
29 #define GRASS_MARK -956 /* arbitrary number from top of my head -- Sandro */
30 
31 #define TRIS_PER_CLUMP 10
32 #define MAX_CLUMPS 2046
33 
34 #define MAX_CLUMP_TRIS (TRIS_PER_CLUMP * MAX_CLUMPS)
35 
36 struct Clump {
37  int firstTriangle, numTriangles; /* populated only when actual geometry is generated */
40  float rotation;
41  int level;
42 };
43 
44 static int clumpCount = 0;
46 
49 
50 static int clumpTriangleCount = 0;
51 
52 /* gfv -- grass fragment vertex */
55 
56 void R_ClearGrass ()
57 {
58  clumpCount = 0;
62 }
63 
64 static void R_PlantGrass (Clump& clump)
65 {
68  clump.numTriangles = 0;
69  return;
70  }
71 
73  clump.numTriangles = 0; /* safeguard */
74 
75  vec3_t rot[3]; /* rotation matrix for the plant */
76 #define xt (rot[0])
77 #define yt (rot[1])
78 #define zt (rot[2])
79 
80 #if 0
81  /* horisontal planting */
82  VectorSet(xt, 1, 0, 0);
83  VectorSet(zt, 0, 0, 1);
84 #else
85  /* normal-based planting */
86 
87  /* we can calculate the downslope vector and use it instead;
88  * a bit more of math, but call allow us to create plants that interact with the slope in various ways */
89  VectorCopy(clump.normal, zt);
90  VectorSet(xt, zt[2], 0.0, -zt[0]); /* short-circuit CrossProduct(yaxis, normal, xt) since it degenerates to a simple shuffle */
92 #endif
93 
94  /* generate geometry */
95 #if 0
96  /* prebuilt mesh or debug grass marker */
97 
98  /* randomly rotate plant around the base */
99  RotatePointAroundVector(yt, zt, xt, frand() * 360);
100  CrossProduct(yt, zt, xt);
101 
102  /* randomly mirror the plant, too */
103  if (rand() & 1)
104  VectorInverse(yt);
105 
106  /* marker */
107  vec_t* ptr = gfv_pos[clumpTriangleCount * 3];
108  VectorMA(clump.position, 1, zt, ptr);
109  VectorMA(ptr, 16, yt, ptr + 3);
110  VectorMA(ptr, 16, xt, ptr + 6);
111 
113  Vector2Set(texc, 0, 0);
114  Vector2Set(texc + 2, 1, 0);
115  Vector2Set(texc + 4, 0, 1);
116 
118 #else
119  /* programmatically generated clump */
120  CrossProduct(zt, xt, yt);
121  for (int i = 0; i < TRIS_PER_CLUMP; i += 2) {
122  vec3_t sdir, tdir;
123  vec2_t sprrot;
124  vec3_t tmp;
125 
126  sprrot[0] = frand() * 360;
127  sprrot[1] = frand() * 60 + 15;
128 
129  PolarToVec(sprrot, tmp);
130  VectorRotate(rot, tmp, tdir);
131 
132  sprrot[0] += 90;
133  sprrot[1] = 0;
134 
135  PolarToVec(sprrot, tmp);
136  VectorRotate(rot, tmp, sdir);
137 #if 0
138  /* debug marker */
139  vec_t* ptr = gfv_pos[clumpTriangleCount * 3];
140  VectorCopy(clump.position, ptr);
141  VectorMA(ptr, 16, sdir, ptr + 3);
142  VectorMA(ptr, 16, tdir, ptr + 6);
143 
145  Vector2Set(texc, 0, 0);
146  Vector2Set(texc + 2, 1, 0);
147  Vector2Set(texc + 4, 0, 1);
148 
150 #else
151  /* billboard sprite */
152  vec_t* ptr = gfv_pos[clumpTriangleCount * 3];
153  VectorCopy(clump.position, ptr);
155  VectorMA(clump.position, -24, sdir, ptr); /* quad vertex 0 */
156  VectorMA(ptr, 32, tdir, ptr + 3); /* quad vertex 1 */
157  VectorMA(ptr + 3, 48, sdir, ptr + 6); /* quad vertex 2 */
158 
159  VectorCopy(ptr, ptr + 9); /* quad vertex 0 */
160  VectorCopy(ptr + 6, ptr + 12); /* quad vertex 2 */
161  VectorMA(ptr + 6, -32, tdir, ptr + 15); /* quad vertex 3 */
162 
164  Vector2Set(texc, 0, 1);
165  Vector2Set(texc + 2, 0, 0);
166  Vector2Set(texc + 4, 1, 0);
167 
168  Vector2Set(texc + 6, 0, 1);
169  Vector2Set(texc + 8, 1, 0);
170  Vector2Set(texc + 12, 1, 1);
171 
172  clumpTriangleCount += 2;
173 #endif
174  }
175 #endif
176 
177 #undef xt
178 #undef yt
179 #undef zt
181 }
182 
183 static void R_AddClump (const vec3_t pos, const vec3_t normal, int level)
184 {
185  if (clumpCount >= MAX_CLUMPS)
186  return;
187 
188  Clump& cp = clumps[clumpCount];
189 
190  VectorCopy(pos, cp.position);
191  VectorCopy(normal, cp.normal);
192 
193  cp.rotation = frand() * 360;
194  cp.level = level;
195 
196  clumpCount++;
197 }
198 
199 static int ClumpOrder (const void* a, const void* b)
200 {
201  const Clump* pa = static_cast <const Clump*>(a);
202  const Clump* pb = static_cast <const Clump*>(b);
203 
204  if (pa->level != pb->level)
205  return pa->level - pb->level;
206 
208  return 0;
209 }
210 
211 static void R_OrganizeClumps ()
212 {
213  qsort(clumps, clumpCount, sizeof(Clump), ClumpOrder);
214 
215  int lastLevel = 0, i;
216  for (i = 0; i < clumpCount; i++)
217  while (lastLevel < clumps[i].level) {
218  clumpsForLevel[lastLevel] = i;
219  lastLevel++;
220  }
221 
222  while (lastLevel < PATHFINDING_HEIGHT) {
223  clumpsForLevel[lastLevel] = i;
224  lastLevel++;
225  }
226 }
227 
229 {
230  float density = 1.0f; /* count of clumps per 32x32 unit tile */
231  double area = 0.0; /* float does not provide enough mantissa precision for this; also note that area is doubled */
232 
233  Com_Printf("Planting grass ...\n");
234 
235  /* 1st pass: walk through all brushes and determine which surfaces to use and their total area */
236  for (int tile = 0; tile < r_numMapTiles; tile++) {
237  /* ignore weaponclip, actorclip and stepon */
238  for (int i = 0; i <= LEVEL_LASTVISIBLE; i++) {
239  const mBspModel_t* const bspModel = &r_mapTiles[tile]->bsp;
240  const mBspHeader_t* const header = &bspModel->submodels[i];
241 
242  if (!header->numfaces)
243  continue;
244 
245  for (int j = 0; j < header->numfaces; j++) {
246  mBspSurface_t* const surf = &bspModel->surfaces[header->firstface + j];
247  const cBspPlane_t* const plane = surf->plane;
248 
249  if (surf->frame == GRASS_MARK)
250  continue; /* already processed it */
251 
252  if (!(surf->texinfo->flags & SURF_FOLIAGE))
253  continue; /* not tagged as overgrown */
254 
255  if (surf->firstTriangle < 0 || surf->numTriangles == 0)
256  continue; /* no geometry for this surface, skip it */
257 
258  /* reject way too inclined or downward facing planes */
259  if (plane->normal[2] < 0.5f) /* cutoff angle is 60 degrees */
260  continue;
261 
262  /* walk triangle list and sum areas */
263  double surfArea = 0.0;
264 
265  for (int k = 0; k < surf->numTriangles; k++) {
266  const int vofs = (k + surf->firstTriangle) * 3;
267  const int indo = bspModel->indexes[vofs] & 0xffff;
268  const int inda = bspModel->indexes[vofs + 1] & 0xffff;
269  const int indb = bspModel->indexes[vofs + 2] & 0xffff;
270  vec3_t vo, va, vb;
271  vec3_t cross;
272 
273  /* calculate barycentric origin and coordinate axes */
274  VectorCopy(&bspModel->verts[indo * 3], vo);
275  VectorSubtract(&bspModel->verts[inda * 3], vo, va);
276  VectorSubtract(&bspModel->verts[indb * 3], vo, vb);
277 
278  /* calculate area */
279  CrossProduct(va, vb, cross);
280  surfArea += VectorLength(cross);
281  }
282 
283  if (surfArea < 80.0)
284  continue; /* skip tiny surfaces */
285 
286  area += surfArea;
287  surf->frame = GRASS_MARK;
288  }
289  }
290  }
291  Com_Printf("Total grassy area is %7.0f units (%i cells)\n", area / 2, (int)(area / 2048));
292 
293  double areaPerClump = area / MAX_CLUMPS;
294  if (areaPerClump < 2048 / (density * density))
295  areaPerClump = 2048 / (density * density);
296 
297  /* 2nd pass: actually plant the grass */
299  double clumpsToPlant = 0.0;
300  int planted = 0;
301 
302  for (int tile = 0; tile < r_numMapTiles; tile++) {
303  /* ignore weaponclip, actorclip and stepon */
304  for (int i = 0; i <= LEVEL_LASTVISIBLE; i++) {
305  const mBspModel_t* const bspModel = &r_mapTiles[tile]->bsp;
306  const mBspHeader_t* const header = &bspModel->submodels[i];
307 
308  if (!header->numfaces)
309  continue;
310 
311  int level;
312 
313  for (level = 0; level < PATHFINDING_HEIGHT - 1; level++)
314  if (!i || ((1 << level) & i))
315  break;
316 
317  for (int j = 0; j < header->numfaces; j++) {
318  mBspSurface_t* const surf = &bspModel->surfaces[header->firstface + j];
319  const cBspPlane_t* const plane = surf->plane;
320 
321  if (surf->frame != GRASS_MARK)
322  continue; /* not suitable for grass or got grass already generated */
323 
324  surf->frame--; /* screen from any future stumblings upon it */
325 
326  /* walk triangle list and plant grass */
327  for (int k = 0; k < surf->numTriangles; k++) {
328  const int vofs = (k + surf->firstTriangle) * 3;
329  const int indo = bspModel->indexes[vofs] & 0xffff;
330  const int inda = bspModel->indexes[vofs + 1] & 0xffff;
331  const int indb = bspModel->indexes[vofs + 2] & 0xffff;
332  vec3_t vo, va, vb;
333  vec3_t cross;
334 
335  /* calculate barycentric origin and coordinate axes */
336  VectorCopy(&bspModel->verts[indo * 3], vo);
337  VectorSubtract(&bspModel->verts[inda * 3], vo, va);
338  VectorSubtract(&bspModel->verts[indb * 3], vo, vb);
339 
340  /* calculate area and convert it to clump count */
341  CrossProduct(va, vb, cross);
342  clumpsToPlant += VectorLength(cross) / areaPerClump;
343 
344  while (clumpsToPlant >= 1.0) {
345  /* generate random point within a triangle using barycentic coordinates */
346  float u = frand();
347  float v = frand();
348 
349  /* if barycentric coordinates are outside of triangle, rotate them 180 deg by flipping both coords
350  * explanation: they just got on other half of parallelogram defined by va and vb, which half complements this triangle to said parallelogram
351  */
352  if (u + v > 1.0f) {
353  u = 1.0f - u;
354  v = 1.0f - v;
355  }
356 
357  vec3_t pos;
358 
359  VectorMA(vo, u, va, pos);
360  VectorMA(pos, v, vb, pos);
361 
362  R_AddClump(pos, plane->normal, level);
363 
364  clumpsToPlant -= 1.0;
365  planted++;
366  }
367  }
368  }
369  }
370  }
371 
373 
374  for (int i = 0; i < clumpCount; i++)
375  R_PlantGrass(clumps[i]);
376 
377  if (clumpTriangleCount <= 0) {
378  /* no grass geometry generated, so zero triangle counts */
379  for (int i = 0; i < PATHFINDING_HEIGHT; i++)
380  clumpTrianglesForLevel[i] = 0;
381  } else {
382  /* generate triangle counts to render the grass in a single OpenGL call */
383  int lastClumpCount = 0;
384  int triangles = 0;
385  for (int i = 0; i < PATHFINDING_HEIGHT; i++) {
386  if (clumpsForLevel[i] > lastClumpCount) {
387  lastClumpCount = clumpsForLevel[i];
388  const Clump& clump = clumps[lastClumpCount - 1];
389  triangles = clump.firstTriangle + clump.numTriangles;
390  }
391  clumpTrianglesForLevel[i] = triangles;
392  Com_Printf("%i triangles for level %i (%i clumps)\n", triangles, i + 1, lastClumpCount);
393  }
394  }
395 
396  Com_Printf("Planted %i clumps of grass\n", planted);
397 }
398 
399 void R_DrawGrass ()
400 {
401  if (clumpTriangleCount <= 0)
402  return;
403 
404  R_BindTexture(R_FindImage("models/objects/vegi/plants2/plant_skin3", it_pic)->texnum);
405 
406  R_EnableAlphaTest(true);
407 
408  R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, gfv_texcoord);
409  R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, gfv_pos);
410  glDrawArrays(GL_TRIANGLES, 0, clumpTrianglesForLevel[refdef.worldlevel] * 3);
411  R_BindDefaultArray(GL_VERTEX_ARRAY);
412  R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
413 
414  R_EnableAlphaTest(false);
415 
416  refdef.batchCount++;
417 }
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
static int clumpsForLevel[PATHFINDING_HEIGHT]
Definition: r_grass.cpp:47
#define VectorCopy(src, dest)
Definition: vector.h:51
#define VectorSet(v, x, y, z)
Definition: vector.h:59
static int clumpCount
Definition: r_grass.cpp:44
#define yt
void VectorMA(const vec3_t veca, const float scale, const vec3_t vecb, vec3_t outVector)
Sets vector_out (vc) to vevtor1 (va) + scale * vector2 (vb)
Definition: mathlib.cpp:261
static void R_OrganizeClumps()
Definition: r_grass.cpp:211
static int clumpTrianglesForLevel[PATHFINDING_HEIGHT]
Definition: r_grass.cpp:48
static int ClumpOrder(const void *a, const void *b)
Definition: r_grass.cpp:199
#define xt
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
#define TRIS_PER_CLUMP
Definition: r_grass.cpp:31
#define zt
vec3_t normal
Definition: typedefs.h:21
cBspPlane_t * plane
Definition: r_model_brush.h:84
model_t * r_mapTiles[MAX_MAPTILES]
The world model(s)
Definition: r_model.cpp:32
void R_EnableAlphaTest(bool enable)
Definition: r_state.cpp:277
#define MAX_CLUMP_TRIS
Definition: r_grass.cpp:34
#define LEVEL_LASTVISIBLE
Definition: defines.h:348
void VectorInverse(vec3_t v)
Inverse a vector.
Definition: mathlib.cpp:460
float vec_t
Definition: ufotypes.h:37
unsigned int numTriangles
void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross)
binary operation on vectors in a three-dimensional space
Definition: mathlib.cpp:820
local graphics definitions
int firstTriangle
Definition: r_grass.cpp:37
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees)
Rotate a point around a given vector.
Definition: mathlib.cpp:849
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
vec3_t normal
Definition: r_grass.cpp:39
void VectorNormalizeFast(vec3_t v)
fast vector normalize routine that does not check to make sure that length != 0, nor does it return l...
Definition: mathlib.cpp:762
Definition: r_image.h:45
#define GRASS_MARK
Definition: r_grass.cpp:29
static vec2_t gfv_texcoord[MAX_CLUMP_TRIS *3]
Definition: r_grass.cpp:54
int r_numMapTiles
Definition: r_model.cpp:33
image_t * R_FindImage(const char *pname, imagetype_t type)
Finds or loads the given image.
Definition: r_image.cpp:603
glElementIndex_t * indexes
#define SURF_FOLIAGE
Definition: defines.h:267
static void R_AddClump(const vec3_t pos, const vec3_t normal, int level)
Definition: r_grass.cpp:183
rendererData_t refdef
Definition: r_main.cpp:45
#define OBJZERO(obj)
Definition: shared.h:178
#define Vector2Set(v, x, y)
Definition: vector.h:61
int numTriangles
Definition: r_grass.cpp:37
void PolarToVec(const vec2_t a, vec3_t v)
Converts longitude and latitude to a 3D vector in Euclidean coordinates.
Definition: mathlib.cpp:910
#define MAX_CLUMPS
Definition: r_grass.cpp:32
float rotation
Definition: r_grass.cpp:40
static vec3_t gfv_pos[MAX_CLUMP_TRIS *3]
Definition: r_grass.cpp:53
mBspTexInfo_t * texinfo
void R_BindDefaultArray(GLenum target)
Binds the appropriate shared vertex array to the specified target.
Definition: r_state.cpp:182
mBspSurface_t * surfaces
static void R_PlantGrass(Clump &clump)
Definition: r_grass.cpp:64
void R_GenerateGrass()
Definition: r_grass.cpp:228
static int clumpTriangleCount
Definition: r_grass.cpp:50
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
vec3_t position
Definition: r_grass.cpp:38
Pseudoinstanced grass generation and rendering.
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
uint32_t flags
Definition: r_model_brush.h:70
void R_DrawGrass()
Definition: r_grass.cpp:399
float * verts
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
void R_ClearGrass()
Definition: r_grass.cpp:56
mBspHeader_t * submodels
int level
Definition: r_grass.cpp:41
brush model
vec_t vec3_t[3]
Definition: ufotypes.h:39
vec_t vec2_t[2]
Definition: ufotypes.h:38
void VectorRotate(vec3_t m[3], const vec3_t va, vec3_t vb)
Rotate a vector with a rotation matrix.
Definition: mathlib.cpp:395
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
plane_t structure
Definition: typedefs.h:20
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
#define R_BindTexture(tn)
Definition: r_state.h:184
level_locals_t level
Definition: g_main.cpp:38
static Clump clumps[MAX_CLUMPS]
Definition: r_grass.cpp:45
void R_BindArray(GLenum target, GLenum type, const void *array)
Definition: r_state.cpp:148
mBspModel_t bsp
Definition: r_model.h:60