1 /* weather.h
2    Adds purely decorative snow and rain
3 
4    Copyright (C) 2003-2004  Mathias Broxvall
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include "weather.h"
22 
23 #include "game.h"
24 #include "player.h"
25 #include "settings.h"
26 
27 #include <cstring>
28 
Weather()29 Weather::Weather() {
30   if (Settings::settings->gfx_details <= 2) max_weather_particles = 500;
31   if (Settings::settings->gfx_details == 3) max_weather_particles = 1000;
32   if (Settings::settings->gfx_details == 4) max_weather_particles = 2000;
33   if (Settings::settings->gfx_details >= 5) max_weather_particles = 3000;
34   kind = WEATHER_SNOW;
35   clear();
36 
37   bufs[0] = 0;
38   bufs[1] = 0;
39   vao = 0;
40 }
~Weather()41 Weather::~Weather() {
42   if (bufs[0] == bufs[1]) {
43     glDeleteBuffers(2, bufs);
44     glDeleteVertexArrays(1, &vao);
45   }
46 }
47 
tick(Real td,Player * player)48 void Weather::tick(Real td, Player *player) {
49   static double count = 0.0;
50   static double snowDrift = 0.0;
51 
52   if (Settings::settings->gfx_details <= 1) return;
53   if (strength < 0.0) return;
54 
55   if (kind == WEATHER_SNOW) {
56     for (count += td * 300.0 * strength; count > 0.0; count -= 1.0) {
57       Particle *p = &particles[next];
58       next = (next + 1) % max_weather_particles;
59       p->size = frandom() * (0.5 + 0.5 * strength);
60       p->position[0] = player->position[0] + (frandom() - 0.5) * 20.0;
61       p->position[1] = player->position[1] + (frandom() - 0.5) * 20.0;
62       p->position[2] = player->position[2] + 4.0;
63       p->velocity[0] = (frandom() - 0.5) * 1.0;
64       p->velocity[1] = (frandom() - 0.5) * 1.0;
65       p->velocity[2] = -1.0 - frandom();
66       for (int j = 0; j < 3; j++)
67         for (int k = 0; k < 3; k++) p->corners[j][k] = frandom() * 0.06;
68     }
69     /* Make it look like the snow particles is drifting in the wind by changing
70        their velocities randomly once every 2 seconds. */
71     for (snowDrift += (max_weather_particles * td) / 2.0; snowDrift > 0.0; snowDrift -= 1.0) {
72       Particle *p = &particles[nextSnowDrift];
73       nextSnowDrift = (nextSnowDrift + 1) % max_weather_particles;
74       p->velocity[0] += (frandom() - 0.5) * 0.5;
75       p->velocity[1] += (frandom() - 0.5) * 0.5;
76     }
77 
78   } else if (kind == WEATHER_RAIN) {
79     for (count += td * 500.0 * strength; count > 0.0; count -= 1.0) {
80       Particle *p = &particles[next];
81       next = (next + 1) % max_weather_particles;
82       p->size = frandom() * (0.5 + 0.5 * strength);
83       p->position[0] = player->position[0] + (frandom() - 0.5) * 15.0;
84       p->position[1] = player->position[1] + (frandom() - 0.5) * 15.0;
85       p->position[2] = player->position[2] + 5.0;
86       p->velocity[0] = 0.0;
87       p->velocity[1] = 0.0;
88       p->velocity[2] = -4.0 - p->size * 4.0;
89     }
90   }
91 
92   for (int i = 0; i < max_weather_particles; i++) {
93     Particle *p = &particles[i];
94     p->position[0] += p->velocity[0] * td;
95     p->position[1] += p->velocity[1] * td;
96     p->position[2] += p->velocity[2] * td;
97   }
98 }
99 
draw2(Player * player)100 void Weather::draw2(Player *player) {
101   if (Settings::settings->gfx_details <= 1) return;
102   if (strength == -1.0) return;
103   if (activeView.calculating_shadows) return;
104 
105   if (bufs[0] == bufs[1]) {
106     glGenBuffers(2, bufs);
107     glGenVertexArrays(1, &vao);
108     /* Create a fixed linear index */
109     ushort *idxs = new ushort[3 * 3000];
110     for (int i = 0; i < 3 * 3000; i++) { idxs[i] = i; }
111     glBindVertexArray(0);
112     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufs[1]);
113     glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * 3000 * sizeof(ushort), idxs, GL_STATIC_DRAW);
114     delete[] idxs;
115   }
116 
117   if (kind == WEATHER_RAIN) {
118     /** Draw RAIN particles **/
119     glEnable(GL_BLEND);
120     glLineWidth(1.5);
121     glEnable(GL_LINE_SMOOTH);
122     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
123 
124     GLfloat h = player->position[2] - 6.0;
125     int nactive = 0;
126     for (int i = 0; i < max_weather_particles; i++) {
127       if (particles[i].position[2] < h) continue;
128       nactive++;
129     }
130     if (nactive <= 0) { return; }
131 
132     GLfloat *data = new GLfloat[2 * 3 * nactive];
133     int j = 0;
134     for (int i = 0; i < max_weather_particles; i++) {
135       const Particle &p = particles[i];
136       if (p.position[2] < h) continue;
137       data[6 * j + 0] = p.position[0];
138       data[6 * j + 1] = p.position[1];
139       data[6 * j + 2] = p.position[2] + 0.2 * p.size;
140       data[6 * j + 3] = p.position[0];
141       data[6 * j + 4] = p.position[1];
142       data[6 * j + 5] = p.position[2];
143       j++;
144     }
145 
146     glBindVertexArray(vao);
147     glBindBuffer(GL_ARRAY_BUFFER, bufs[0]);
148     glBufferData(GL_ARRAY_BUFFER, 2 * 3 * nactive * sizeof(GLfloat), data, GL_STATIC_DRAW);
149     delete[] data;
150 
151     // Transfer data
152     setActiveProgramAndUniforms(Shader_Line);
153     glUniform4f(uniformLocations.line_color, 0.3, 0.3, 0.4, 0.7);
154 
155     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufs[1]);
156     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
157     glEnableVertexAttribArray(0);
158     glDrawElements(GL_LINES, (2 * nactive), GL_UNSIGNED_SHORT, (void *)0);
159   } else if (kind == WEATHER_SNOW) {
160     /** Draw SNOW particles **/
161     glDisable(GL_CULL_FACE);
162     glEnable(GL_BLEND);
163 
164     int nactive = 0;
165     GLfloat h = player->position[2] - 5.0;
166     for (int i = 0; i < max_weather_particles; i++) {
167       // Note that < and >= or not opposite for nan
168       if (particles[i].position[2] < h) continue;
169       nactive++;
170     }
171     if (nactive <= 0) { return; }
172 
173     GLfloat *data = new GLfloat[3 * 8 * nactive];
174 
175     Color color(0.8, 0.8, 0.85, 1.);
176     GLfloat flat[3] = {0.f, 0.f, 0.f};
177     GLfloat txc[3][2] = {{0.5f, 0.f}, {0.f, 1.f}, {1.f, 1.f}};
178     GLfloat size = 1.3f;
179 
180     char *pos = (char *)data;
181     for (int i = 0; i < max_weather_particles; i++) {
182       const Particle &p = particles[i];
183       if (p.position[2] < h) continue;
184       for (int k = 0; k < 3; k++) {
185         pos += packObjectVertex(pos, p.position[0] + p.corners[k][0] * size,
186                                 p.position[1] + p.corners[k][1] * size,
187                                 p.position[2] + p.corners[k][2] * size, txc[k][0], txc[k][1],
188                                 color, flat);
189       }
190     }
191 
192     // Transfer data
193     glBindVertexArray(vao);
194     glBindBuffer(GL_ARRAY_BUFFER, bufs[0]);
195     glBufferData(GL_ARRAY_BUFFER, 3 * 8 * nactive * sizeof(GLfloat), data, GL_STATIC_DRAW);
196     delete[] data;
197 
198     setActiveProgramAndUniforms(Shader_Object);
199     setObjectUniforms(Color(0., 0., 0., 1.), 1., Lighting_None);
200     glBindTexture(GL_TEXTURE_2D, textureGlitter);
201 
202     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufs[1]);
203 
204     configureObjectAttributes();
205     glDrawElements(GL_TRIANGLES, 3 * nactive, GL_UNSIGNED_SHORT, (void *)0);
206   }
207 }
208 
clear()209 void Weather::clear() {
210   // Zero in case of changes
211   memset(particles, 0, sizeof(particles));
212   for (int i = 0; i < max_weather_particles; i++) { particles[i].position[2] = -10.0; }
213   next = 0;
214   nextSnowDrift = 0;
215   strength = -1.0;
216 }
217 
snow(double s)218 void Weather::snow(double s) {
219   kind = WEATHER_SNOW;
220   strength = s;
221   if (strength < 0.0) strength = 0.0;
222   if (strength > 1.0) strength = 1.0;
223 }
rain(double s)224 void Weather::rain(double s) {
225   kind = WEATHER_RAIN;
226   strength = s;
227   if (strength < 0.0) strength = 0.0;
228   if (strength > 1.0) strength = 1.0;
229 }
230