1154 lines
33 KiB
C++
1154 lines
33 KiB
C++
/* --------------------------------------------------------------------
|
|
EXTREME TUXRACER
|
|
|
|
Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
|
|
Copyright (C) 2010 Extreme Tuxracer Team
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
---------------------------------------------------------------------*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <etr_config.h>
|
|
#endif
|
|
|
|
#include "particles.h"
|
|
#include "textures.h"
|
|
#include "ogl.h"
|
|
#include "course.h"
|
|
#include "view.h"
|
|
#include "env.h"
|
|
#include "game_over.h"
|
|
#include "winsys.h"
|
|
#include "physics.h"
|
|
#include <cstdlib>
|
|
#include <list>
|
|
#include <algorithm>
|
|
|
|
// ====================================================================
|
|
// gui particles 2D
|
|
// ====================================================================
|
|
|
|
#define MAX_num_snowparticles 4000
|
|
#define BASE_snowparticles 1000.0/1024 // This is intentionally not divided by height*width to make particle count increasing slower than screen size
|
|
#define GRAVITY_FACTOR 0.015
|
|
#define BASE_VELOCITY 0.05
|
|
#define VELOCITY_RANGE 0.02
|
|
#define PUSH_DECAY_TIME_CONSTANT 0.2
|
|
#define PUSH_DIST_DECAY 100
|
|
#define PUSH_FACTOR 0.5
|
|
#define MAX_PUSH_FORCE 5.0
|
|
#define AIR_DRAG 0.4
|
|
#define TUX_WIDTH 0.45
|
|
|
|
#define PARTICLE_MIN_SIZE 1
|
|
#define PARTICLE_SIZE_RANGE 10
|
|
|
|
struct TGuiParticle {
|
|
TVector2d pt;
|
|
float size;
|
|
TVector2d vel;
|
|
const GLfloat* tex;
|
|
|
|
TGuiParticle(double x, double y);
|
|
void Draw(double xres, double yres) const;
|
|
void Update(double time_step, double push_timestep, const TVector2d& push_vector);
|
|
};
|
|
|
|
static list<TGuiParticle> particles_2d;
|
|
static TVector2d push_position(0, 0);
|
|
static TVector2d last_push_position;
|
|
static double last_update_time = -1;
|
|
static bool push_position_initialized = false;
|
|
|
|
TGuiParticle::TGuiParticle(double x, double y) {
|
|
pt.x = x;
|
|
pt.y = y;
|
|
double p_dist = FRandom();
|
|
|
|
size = PARTICLE_MIN_SIZE + (1.0 - p_dist) * PARTICLE_SIZE_RANGE;
|
|
vel.x = 0;
|
|
vel.y = -BASE_VELOCITY - p_dist * VELOCITY_RANGE;
|
|
|
|
static const GLfloat tex_coords[4][8] = {
|
|
{
|
|
0.0, 0.0,
|
|
0.5, 0.0,
|
|
0.5, 0.5,
|
|
0.0, 0.5
|
|
}, {
|
|
0.5, 0.0,
|
|
1.0, 0.0,
|
|
1.0, 0.5,
|
|
0.5, 0.5
|
|
}, {
|
|
0.0, 0.5,
|
|
0.5, 0.5,
|
|
0.5, 1.0,
|
|
0.0, 1.0
|
|
}, {
|
|
0.5, 0.5,
|
|
1.0, 0.5,
|
|
1.0, 1.0,
|
|
0.5, 1.0
|
|
}
|
|
};
|
|
int type = rand() % 4;
|
|
tex = tex_coords[type];
|
|
}
|
|
|
|
void TGuiParticle::Draw(double xres, double yres) const {
|
|
const GLfloat vtx[] = {
|
|
pt.x * xres, pt.y * yres,
|
|
pt.x * xres + size, pt.y * yres,
|
|
pt.x * xres + size, pt.y * yres + size,
|
|
pt.x * xres, pt.y * yres + size
|
|
};
|
|
glVertexPointer(2, GL_FLOAT, 0, vtx);
|
|
glTexCoordPointer(2, GL_FLOAT, 0, tex);
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
|
|
void TGuiParticle::Update(double time_step, double push_timestep, const TVector2d& push_vector) {
|
|
TVector2d f;
|
|
|
|
double dist_from_push = (pow((pt.x - push_position.x), 2) +
|
|
pow((pt.y - push_position.y), 2));
|
|
if (push_timestep > 0) {
|
|
f.x = PUSH_FACTOR * push_vector.x / push_timestep;
|
|
f.y = PUSH_FACTOR * push_vector.y / push_timestep;
|
|
f.x = clamp(-MAX_PUSH_FORCE, f.x, MAX_PUSH_FORCE);
|
|
f.y = clamp(-MAX_PUSH_FORCE, f.y, MAX_PUSH_FORCE);
|
|
f.x *= 1.0/(PUSH_DIST_DECAY*dist_from_push + 1) *
|
|
size/PARTICLE_SIZE_RANGE;
|
|
f.y *= 1.0/(PUSH_DIST_DECAY*dist_from_push + 1) *
|
|
size/PARTICLE_SIZE_RANGE;
|
|
}
|
|
|
|
vel.x += (f.x - vel.x * AIR_DRAG) * time_step;
|
|
vel.y += (f.y - GRAVITY_FACTOR - vel.y * AIR_DRAG) * time_step;
|
|
|
|
pt.x += vel.x * time_step * (size / PARTICLE_SIZE_RANGE);
|
|
pt.y += vel.y * time_step * (size / PARTICLE_SIZE_RANGE);
|
|
|
|
if (pt.x < 0) {
|
|
pt.x = 1;
|
|
} else if (pt.x > 1) {
|
|
pt.x = 0.0;
|
|
}
|
|
}
|
|
|
|
void init_ui_snow() {
|
|
particles_2d.clear();
|
|
for (int i = 0; i < BASE_snowparticles * Winsys.resolution.width; i++)
|
|
particles_2d.push_back(TGuiParticle(FRandom(), FRandom()));
|
|
push_position = TVector2d(0.0, 0.0);
|
|
}
|
|
|
|
void update_ui_snow(double time_step) {
|
|
double time = Winsys.ClockTime();
|
|
|
|
TVector2d push_vector;
|
|
double push_timestep = 0;
|
|
|
|
if (push_position_initialized) {
|
|
push_vector.x = push_position.x - last_push_position.x;
|
|
push_vector.y = push_position.y - last_push_position.y;
|
|
push_timestep = time - last_update_time;
|
|
}
|
|
last_push_position = push_position;
|
|
last_update_time = time;
|
|
|
|
for (list<TGuiParticle>::iterator p = particles_2d.begin(); p != particles_2d.end(); ++p) {
|
|
p->Update(time_step, push_timestep, push_vector);
|
|
}
|
|
|
|
if (FRandom() < time_step*20.0*(MAX_num_snowparticles - particles_2d.size()) / 1000.0) {
|
|
particles_2d.push_back(TGuiParticle(FRandom(), 1));
|
|
}
|
|
|
|
for (list<TGuiParticle>::iterator p = particles_2d.begin(); p != particles_2d.end();) {
|
|
if (p->pt.y < -0.05) {
|
|
if (particles_2d.size() > BASE_snowparticles * Winsys.resolution.width && FRandom() > 0.2) {
|
|
p = particles_2d.erase(p);
|
|
} else {
|
|
p->pt.x = FRandom();
|
|
p->pt.y = 1 + FRandom()*BASE_VELOCITY;
|
|
double p_dist = FRandom();
|
|
p->size = PARTICLE_MIN_SIZE + (1.0 - p_dist) * PARTICLE_SIZE_RANGE;
|
|
p->vel.x = 0;
|
|
p->vel.y = -BASE_VELOCITY-p_dist*VELOCITY_RANGE;
|
|
++p;
|
|
}
|
|
} else
|
|
++p;
|
|
}
|
|
|
|
if (time_step < PUSH_DECAY_TIME_CONSTANT) {
|
|
push_vector.x *= 1.0 - time_step/PUSH_DECAY_TIME_CONSTANT;
|
|
push_vector.y *= 1.0 - time_step/PUSH_DECAY_TIME_CONSTANT;
|
|
} else {
|
|
push_vector.x = 0.0;
|
|
push_vector.y = 0.0;
|
|
}
|
|
}
|
|
void draw_ui_snow() {
|
|
double xres = Winsys.resolution.width;
|
|
double yres = Winsys.resolution.height;
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
Tex.BindTex(SNOW_PART);
|
|
glColor4f(1.f, 1.f, 1.f, 0.3f);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
for (list<TGuiParticle>::const_iterator i = particles_2d.begin(); i != particles_2d.end(); ++i) {
|
|
i->Draw(xres, yres);
|
|
}
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
}
|
|
|
|
void push_ui_snow(const TVector2i& pos) {
|
|
push_position = TVector2d(pos.x/(double)Winsys.resolution.width, 1.0 - pos.y/(double)Winsys.resolution.height);
|
|
if (!push_position_initialized) last_push_position = push_position;
|
|
push_position_initialized = true;
|
|
}
|
|
|
|
// ====================================================================
|
|
// tux particles
|
|
// ====================================================================
|
|
|
|
#define MAX_PARTICLES 500000
|
|
#define START_RADIUS 0.04
|
|
#define OLD_PART_SIZE 0.12 // orig 0.07
|
|
#define NEW_PART_SIZE 0.035 // orig 0.02
|
|
#define MIN_AGE -0.2
|
|
#define MAX_AGE 1.0
|
|
#define VARIANCE_FACTOR 0.8
|
|
#define PARTICLE_SHADOW_HEIGHT 0.05
|
|
#define PARTICLE_SHADOW_ALPHA 0.1
|
|
|
|
#define MAX_TURN_PARTICLES 500
|
|
#define BRAKE_PARTICLES 2000
|
|
#define MAX_ROLL_PARTICLES 3000
|
|
#define PARTICLE_SPEED_FACTOR 40
|
|
#define MAX_PARTICLE_ANGLE 80.0
|
|
#define MAX_PARTICLE_ANGLE_SPEED 50
|
|
#define PARTICLE_SPEED_MULTIPLIER 0.3
|
|
#define MAX_PARTICLE_SPEED 2.0
|
|
|
|
|
|
struct Particle {
|
|
TVector3d pt;
|
|
short type;
|
|
double base_size;
|
|
double cur_size;
|
|
double terrain_height;
|
|
double age;
|
|
double death;
|
|
double alpha;
|
|
TVector3d vel;
|
|
|
|
void Draw(const CControl* ctrl) const;
|
|
private:
|
|
void draw_billboard(const CControl *ctrl, double width, double height, bool use_world_y_axis, const GLfloat* tex) const;
|
|
};
|
|
|
|
static list<Particle> particles;
|
|
|
|
void Particle::Draw(const CControl* ctrl) const {
|
|
static const GLfloat tex_coords[4][8] = {
|
|
{
|
|
0.0, 0.0,
|
|
0.5, 0.0,
|
|
0.5, 0.5,
|
|
0.0, 0.5
|
|
}, {
|
|
0.5, 0.0,
|
|
1.0, 0.0,
|
|
1.0, 0.5,
|
|
0.5, 0.5
|
|
}, {
|
|
0.0, 0.5,
|
|
0.5, 0.5,
|
|
0.5, 1.0,
|
|
0.0, 1.0
|
|
}, {
|
|
0.5, 0.5,
|
|
1.0, 0.5,
|
|
1.0, 1.0,
|
|
0.5, 1.0
|
|
}
|
|
};
|
|
|
|
const TColor& particle_colour = Env.ParticleColor();
|
|
glColor(particle_colour, particle_colour.a * alpha);
|
|
|
|
draw_billboard(ctrl, cur_size, cur_size, false, tex_coords[type]);
|
|
}
|
|
|
|
void Particle::draw_billboard(const CControl *ctrl, double width, double height, bool use_world_y_axis, const GLfloat* tex) const {
|
|
TVector3d x_vec;
|
|
TVector3d y_vec;
|
|
TVector3d z_vec;
|
|
|
|
x_vec.x = ctrl->view_mat[0][0];
|
|
x_vec.y = ctrl->view_mat[0][1];
|
|
x_vec.z = ctrl->view_mat[0][2];
|
|
|
|
if (use_world_y_axis) {
|
|
y_vec = TVector3d(0, 1, 0);
|
|
x_vec = ProjectToPlane(y_vec, x_vec);
|
|
x_vec.Norm();
|
|
z_vec = CrossProduct(x_vec, y_vec);
|
|
} else {
|
|
y_vec.x = ctrl->view_mat[1][0];
|
|
y_vec.y = ctrl->view_mat[1][1];
|
|
y_vec.z = ctrl->view_mat[1][2];
|
|
z_vec.x = ctrl->view_mat[2][0];
|
|
z_vec.y = ctrl->view_mat[2][1];
|
|
z_vec.z = ctrl->view_mat[2][2];
|
|
}
|
|
|
|
TVector3d pt1 = pt + -width/2.0 * x_vec;
|
|
pt1 += -height / 2.0 * y_vec;
|
|
TVector3d pt2 = pt1 + width * x_vec;
|
|
TVector3d pt3 = pt2 + height * y_vec;
|
|
TVector3d pt4 = pt3 + -width * x_vec;
|
|
const GLfloat vtx[] = {
|
|
pt1.x, pt1.y, pt1.z,
|
|
pt2.x, pt2.y, pt2.z,
|
|
pt3.x, pt3.y, pt3.z,
|
|
pt4.x, pt4.y, pt4.z,
|
|
};
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, 0, vtx);
|
|
glTexCoordPointer(2, GL_FLOAT, 0, tex);
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
}
|
|
|
|
void create_new_particles(const TVector3d& loc, const TVector3d& vel, int num) {
|
|
double speed = vel.Length();
|
|
|
|
if (particles.size() + num > MAX_PARTICLES) {
|
|
Message("maximum number of particles exceeded");
|
|
}
|
|
for (int i=0; i<num; i++) {
|
|
particles.push_back(Particle());
|
|
Particle* newp = &particles.back();
|
|
newp->pt.x = loc.x + 2.*(FRandom() - 0.5) * START_RADIUS;
|
|
newp->pt.y = loc.y;
|
|
newp->pt.z = loc.z + 2.*(FRandom() - 0.5) * START_RADIUS;
|
|
newp->type = rand() % 4;
|
|
newp->base_size = (FRandom() + 0.5) * OLD_PART_SIZE;
|
|
newp->cur_size = NEW_PART_SIZE;
|
|
newp->age = FRandom() * MIN_AGE;
|
|
newp->death = FRandom() * MAX_AGE;
|
|
newp->vel = vel +
|
|
TVector3d(VARIANCE_FACTOR * (FRandom() - 0.5) * speed,
|
|
VARIANCE_FACTOR * (FRandom() - 0.5) * speed,
|
|
VARIANCE_FACTOR * (FRandom() - 0.5) * speed);
|
|
}
|
|
}
|
|
void update_particles(double time_step) {
|
|
for (list<Particle>::iterator p = particles.begin(); p != particles.end();) {
|
|
p->age += time_step;
|
|
if (p->age < 0) {
|
|
++p;
|
|
continue;
|
|
}
|
|
|
|
p->pt += time_step * p->vel;
|
|
double ycoord = Course.FindYCoord(p->pt.x, p->pt.z);
|
|
if (p->pt.y < ycoord - 3) {p->age = p->death + 1;}
|
|
if (p->age >= p->death) {
|
|
p = particles.erase(p);
|
|
continue;
|
|
}
|
|
p->alpha = (p->death - p->age) / p->death;
|
|
p->cur_size = NEW_PART_SIZE +
|
|
(OLD_PART_SIZE - NEW_PART_SIZE) * (p->age / p->death);
|
|
p->vel.y += -EARTH_GRAV * time_step;
|
|
++p;
|
|
}
|
|
}
|
|
void draw_particles(const CControl *ctrl) {
|
|
ScopedRenderMode rm(PARTICLES);
|
|
Tex.BindTex(SNOW_PART);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor4f(1.f, 1.f, 1.f, 0.8f);
|
|
|
|
for (list<Particle>::const_iterator p = particles.begin(); p != particles.end(); ++p) {
|
|
if (p->age >= 0)
|
|
p->Draw(ctrl);
|
|
}
|
|
}
|
|
void clear_particles() {
|
|
particles.clear();
|
|
}
|
|
|
|
double adjust_particle_count(double particles) {
|
|
if (particles < 1) {
|
|
if (((double) rand()) / RAND_MAX < particles) return 1.0;
|
|
else return 0.0;
|
|
} else return particles;
|
|
}
|
|
|
|
void generate_particles(const CControl *ctrl, double dtime, const TVector3d& pos, double speed) {
|
|
TTerrType *TerrList = &Course.TerrList[0];
|
|
|
|
double surf_y = Course.FindYCoord(pos.x, pos.z);
|
|
|
|
int id = Course.GetTerrainIdx(pos.x, pos.z, 0.5);
|
|
if (id >= 0 && TerrList[id].particles && pos.y < surf_y) {
|
|
TVector3d xvec = CrossProduct(ctrl->cdirection, ctrl->plane_nml);
|
|
|
|
TVector3d right_part_pt = pos + TUX_WIDTH/2.0 * xvec;
|
|
|
|
TVector3d left_part_pt = pos + -TUX_WIDTH/2.0 * xvec;
|
|
|
|
right_part_pt.y = left_part_pt.y = surf_y;
|
|
|
|
double brake_particles = dtime *
|
|
BRAKE_PARTICLES * (ctrl->is_braking ? 1.0 : 0.0)
|
|
* min(speed / PARTICLE_SPEED_FACTOR, 1.0);
|
|
double turn_particles = dtime * MAX_TURN_PARTICLES
|
|
* min(speed / PARTICLE_SPEED_FACTOR, 1.0);
|
|
double roll_particles = dtime * MAX_ROLL_PARTICLES
|
|
* min(speed / PARTICLE_SPEED_FACTOR, 1.0);
|
|
|
|
double left_particles = turn_particles *
|
|
fabs(min(ctrl->turn_fact, 0.)) +
|
|
brake_particles +
|
|
roll_particles * fabs(min(ctrl->turn_animation, 0.));
|
|
|
|
double right_particles = turn_particles *
|
|
fabs(max(ctrl->turn_fact, 0.)) +
|
|
brake_particles +
|
|
roll_particles * fabs(max(ctrl->turn_animation, 0.));
|
|
|
|
left_particles = adjust_particle_count(left_particles);
|
|
right_particles = adjust_particle_count(right_particles);
|
|
|
|
TMatrix<4, 4> rot_mat = RotateAboutVectorMatrix(
|
|
ctrl->cdirection,
|
|
max(-MAX_PARTICLE_ANGLE,
|
|
-MAX_PARTICLE_ANGLE * speed / MAX_PARTICLE_ANGLE_SPEED));
|
|
TVector3d left_part_vel = TransformVector(rot_mat, ctrl->plane_nml);
|
|
left_part_vel = min(MAX_PARTICLE_SPEED, speed * PARTICLE_SPEED_MULTIPLIER);
|
|
|
|
rot_mat = RotateAboutVectorMatrix(
|
|
ctrl->cdirection,
|
|
min(MAX_PARTICLE_ANGLE,
|
|
MAX_PARTICLE_ANGLE * speed / MAX_PARTICLE_ANGLE_SPEED));
|
|
TVector3d right_part_vel = TransformVector(rot_mat, ctrl->plane_nml);
|
|
right_part_vel *= min(MAX_PARTICLE_SPEED, speed * PARTICLE_SPEED_MULTIPLIER);
|
|
|
|
|
|
create_new_particles(left_part_pt, left_part_vel,
|
|
(int)left_particles);
|
|
create_new_particles(right_part_pt, right_part_vel,
|
|
(int)right_particles);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// snow flakes
|
|
// --------------------------------------------------------------------
|
|
|
|
#define SNOW_WIND_DRIFT 0.1
|
|
|
|
static CFlakes Flakes;
|
|
|
|
|
|
void TFlake::Draw(const TPlane& lp, const TPlane& rp, bool rotate_flake, float dir_angle) const {
|
|
if ((DistanceToPlane(lp, pt) < 0) && (DistanceToPlane(rp, pt) < 0)) {
|
|
glPushMatrix();
|
|
glTranslate(pt);
|
|
if (rotate_flake) glRotatef(dir_angle, 0, 1, 0);
|
|
|
|
const GLfloat vtx[] = {
|
|
0, 0, 0,
|
|
size, 0, 0,
|
|
size, size, 0,
|
|
0, size, 0
|
|
};
|
|
glVertexPointer(3, GL_FLOAT, 0, vtx);
|
|
glTexCoordPointer(2, GL_FLOAT, 0, tex);
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
|
|
TFlakeArea::TFlakeArea(
|
|
int num_flakes,
|
|
float _xrange,
|
|
float _ytop,
|
|
float _yrange,
|
|
float _zback,
|
|
float _zrange,
|
|
float _minSize,
|
|
float _maxSize,
|
|
float _speed,
|
|
bool rotate) {
|
|
xrange = _xrange;
|
|
ytop = _ytop;
|
|
yrange = _yrange;
|
|
zback = _zback;
|
|
zrange = _zrange;
|
|
minSize = _minSize;
|
|
maxSize = _maxSize;
|
|
speed = _speed;
|
|
rotate_flake = rotate;
|
|
|
|
flakes.resize(num_flakes);
|
|
}
|
|
|
|
void TFlakeArea::Draw(const CControl *ctrl) const {
|
|
if (g_game.snow_id < 1) return;
|
|
|
|
const TPlane& lp = get_left_clip_plane();
|
|
const TPlane& rp = get_right_clip_plane();
|
|
float dir_angle(atan(ctrl->viewdir.x / ctrl->viewdir.z) * 180 / 3.14159);
|
|
|
|
ScopedRenderMode rm(PARTICLES);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
Tex.BindTex(SNOW_PART);
|
|
const TColor& particle_colour = Env.ParticleColor();
|
|
glColor(particle_colour);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
for (size_t i=0; i < flakes.size(); i++) {
|
|
flakes[i].Draw(lp, rp, rotate_flake, dir_angle);
|
|
}
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
void TFlakeArea::Update(float timestep, float xcoeff, float ycoeff, float zcoeff) {
|
|
for (size_t i=0; i<flakes.size(); i++) {
|
|
flakes[i].pt.x += xcoeff;
|
|
flakes[i].pt.y += flakes[i].vel.y * timestep + ycoeff;
|
|
flakes[i].pt.z += zcoeff;
|
|
|
|
if (flakes[i].pt.y < bottom) {
|
|
flakes[i].pt.y += yrange;
|
|
} else if (flakes[i].pt.x < left) {
|
|
flakes[i].pt.x += xrange;
|
|
} else if (flakes[i].pt.x > right) {
|
|
flakes[i].pt.x -= xrange;
|
|
} else if (flakes[i].pt.y > top) {
|
|
flakes[i].pt.y -= yrange;
|
|
} else if (flakes[i].pt.z < front) {
|
|
flakes[i].pt.z += zrange;
|
|
} else if (flakes[i].pt.z > back) {
|
|
flakes[i].pt.z -= zrange;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CFlakes::Reset() {
|
|
areas.clear();
|
|
}
|
|
|
|
void CFlakes::MakeSnowFlake(size_t ar, size_t i) {
|
|
areas[ar].flakes[i].pt.x = XRandom(areas[ar].left, areas[ar].right);
|
|
areas[ar].flakes[i].pt.y = -XRandom(areas[ar].top, areas[ar].bottom);
|
|
areas[ar].flakes[i].pt.z = areas[ar].back - FRandom() * (areas[ar].back - areas[ar].front);
|
|
|
|
areas[ar].flakes[i].size = XRandom(areas[ar].minSize, areas[ar].maxSize);
|
|
areas[ar].flakes[i].vel.x = 0;
|
|
areas[ar].flakes[i].vel.z = 0;
|
|
areas[ar].flakes[i].vel.y = -areas[ar].flakes[i].size * areas[ar].speed;
|
|
|
|
int type = rand() % 4;
|
|
|
|
static const GLfloat tex_coords[4][8] = {
|
|
{
|
|
0.0, 0.5,
|
|
0.5, 0.5,
|
|
0.5, 0.0,
|
|
0.0, 0.0
|
|
}, {
|
|
0.5, 0.5,
|
|
1.0, 0.5,
|
|
1.0, 0.0,
|
|
0.5, 0.0
|
|
}, {
|
|
0.0, 1.0,
|
|
0.5, 1.0,
|
|
0.5, 0.5,
|
|
0.0, 0.5
|
|
}, {
|
|
0.5, 1.0,
|
|
1.0, 1.0,
|
|
1.0, 0.5,
|
|
0.5, 0.5
|
|
}
|
|
};
|
|
|
|
areas[ar].flakes[i].tex = tex_coords[type];
|
|
}
|
|
|
|
void CFlakes::GenerateSnowFlakes(const CControl *ctrl) {
|
|
if (g_game.snow_id < 1) return;
|
|
snow_lastpos = ctrl->cpos;
|
|
for (size_t ar=0; ar<areas.size(); ar++) {
|
|
for (size_t i=0; i<areas[ar].flakes.size(); i++) MakeSnowFlake(ar, i);
|
|
}
|
|
}
|
|
|
|
void CFlakes::UpdateAreas(const CControl *ctrl) {
|
|
for (size_t ar=0; ar<areas.size(); ar++) {
|
|
areas[ar].left = ctrl->cpos.x - areas[ar].xrange / 2;
|
|
areas[ar].right = areas[ar].left + areas[ar].xrange;
|
|
areas[ar].back = ctrl->cpos.z - areas[ar].zback;
|
|
areas[ar].front = areas[ar].back - areas[ar].zrange;
|
|
areas[ar].top = ctrl->cpos.y + areas[ar].ytop;
|
|
areas[ar].bottom = areas[ar].top - areas[ar].yrange;
|
|
}
|
|
}
|
|
|
|
#define YDRIFT 0.8
|
|
#define ZDRIFT 0.6
|
|
|
|
void CFlakes::Init(int grade, const CControl *ctrl) {
|
|
Reset();
|
|
switch (grade) {
|
|
case 1:
|
|
// areas.push_back(TFlakeArea(400, 5, 4, 4, -2, 4, 0.01, 0.02, 5, true));
|
|
// areas.push_back(TFlakeArea(400, 12, 5, 8, 2, 8, 0.03, 0.045, 5, false));
|
|
// areas.push_back(TFlakeArea(400, 30, 6, 15, 10, 15, 0.06, 0.12, 5, false));
|
|
areas.push_back(TFlakeArea(400, 5, 4, 4, -2, 4, 0.015, 0.03, 5, true));
|
|
areas.push_back(TFlakeArea(400, 12, 5, 8, 2, 8, 0.045, 0.07, 5, false));
|
|
areas.push_back(TFlakeArea(400, 30, 6, 15, 10, 15, 0.09, 0.18, 5, false));
|
|
// areas.push_back(TFlakeArea(400, 5, 4, 4, -2, 4, 0.02, 0.04, 5, true));
|
|
// areas.push_back(TFlakeArea(400, 12, 5, 8, 2, 8, 0.06, 0.09, 5, false));
|
|
// areas.push_back(TFlakeArea(400, 30, 6, 15, 10, 15, 0.15, 0.25, 5, false));
|
|
break;
|
|
case 2:
|
|
// areas.push_back(TFlakeArea(500, 5, 4, 4, -2, 4, 0.02, 0.03, 5, true));
|
|
// areas.push_back(TFlakeArea(500, 12, 5, 8, 2, 8, 0.045, 0.07, 5, false));
|
|
// areas.push_back(TFlakeArea(500, 30, 6, 15, 10, 15, 0.1, 0.15, 5, false));
|
|
areas.push_back(TFlakeArea(500, 5, 4, 4, -2, 4, 0.03, 0.045, 5, true));
|
|
areas.push_back(TFlakeArea(500, 12, 5, 8, 2, 8, 0.07, 0.1, 5, false));
|
|
areas.push_back(TFlakeArea(500, 30, 6, 15, 10, 15, 0.15, 0.22, 5, false));
|
|
// areas.push_back(TFlakeArea(500, 5, 4, 4, -2, 4, 0.04, 0.06, 5, true));
|
|
// areas.push_back(TFlakeArea(500, 12, 5, 8, 2, 8, 0.09, 0.15, 5, false));
|
|
// areas.push_back(TFlakeArea(500, 30, 6, 15, 10, 15, 0.2, 0.32, 5, false));
|
|
break;
|
|
case 3:
|
|
// areas.push_back(TFlakeArea(1000, 5, 4, 4, -2, 4, 0.025, 0.04, 5, true));
|
|
// areas.push_back(TFlakeArea(1000, 12, 5, 9, 2, 8, 0.06, 0.10, 5, false));
|
|
// areas.push_back(TFlakeArea(1000, 30, 6, 15, 10, 15, 0.12, 0.2, 5, false));
|
|
areas.push_back(TFlakeArea(1000, 5, 4, 4, -2, 4, 0.037, 0.05, 5, true));
|
|
areas.push_back(TFlakeArea(1000, 12, 5, 9, 2, 8, 0.09, 0.15, 5, false));
|
|
areas.push_back(TFlakeArea(1000, 30, 6, 15, 10, 15, 0.18, 0.35, 5, false));
|
|
// areas.push_back(TFlakeArea(800, 5, 4, 4, -2, 4, 0.05, 0.08, 5, true));
|
|
// areas.push_back(TFlakeArea(800, 12, 5, 9, 2, 8, 0.12, 0.20, 5, false));
|
|
// areas.push_back(TFlakeArea(800, 30, 6, 15, 10, 15, 0.25, 0.5, 5, false));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
UpdateAreas(ctrl);
|
|
GenerateSnowFlakes(ctrl);
|
|
}
|
|
|
|
void CFlakes::Update(double timestep, const CControl *ctrl) {
|
|
if (g_game.snow_id < 1)
|
|
return;
|
|
|
|
UpdateAreas(ctrl);
|
|
|
|
float zdiff = ctrl->cpos.z - snow_lastpos.z;
|
|
float ydiff = 0.f;
|
|
if (State::manager.CurrentState() != &GameOver) {
|
|
ydiff = ctrl->cpos.y - snow_lastpos.y;
|
|
}
|
|
|
|
TVector3d winddrift = SNOW_WIND_DRIFT * Wind.WindDrift();
|
|
float xcoeff = winddrift.x * timestep;
|
|
float ycoeff = (ydiff * YDRIFT) + (winddrift.z * timestep);
|
|
float zcoeff = (zdiff * ZDRIFT) + (winddrift.z * timestep);
|
|
|
|
for (size_t ar=0; ar<areas.size(); ar++) {
|
|
areas[ar].Update(timestep, xcoeff, ycoeff, zcoeff);
|
|
}
|
|
snow_lastpos = ctrl->cpos;
|
|
}
|
|
|
|
void CFlakes::Draw(const CControl *ctrl) const {
|
|
for (size_t ar=0; ar<areas.size(); ar++)
|
|
areas[ar].Draw(ctrl);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// snow curtains
|
|
// --------------------------------------------------------------------
|
|
|
|
#define NUM_CHANGES 6
|
|
#define CHANGE_DRIFT 15
|
|
#define CHANGE_SPEED 0.05
|
|
#define CURTAIN_WINDDRIFT 0.35
|
|
|
|
struct TChange {
|
|
float min;
|
|
float max;
|
|
float curr;
|
|
float step;
|
|
bool forward;
|
|
};
|
|
|
|
TChange changes[NUM_CHANGES];
|
|
|
|
void InitChanges() {
|
|
for (int i=0; i<NUM_CHANGES; i++) {
|
|
changes[i].min = XRandom(-0.15, -0.05);
|
|
changes[i].max = XRandom(0.05, 0.15);
|
|
changes[i].curr = (changes[i].min + changes[i].max) / 2;
|
|
changes[i].step = CHANGE_SPEED;
|
|
changes[i].forward = true;
|
|
}
|
|
}
|
|
|
|
void UpdateChanges(double timestep) {
|
|
for (int i=0; i<NUM_CHANGES; i++) {
|
|
TChange* ch = &changes[i];
|
|
if (ch->forward) {
|
|
ch->curr += ch->step * timestep;
|
|
if (ch->curr > ch->max) ch->forward = false;
|
|
} else {
|
|
ch->curr -= ch->step * timestep;
|
|
if (ch->curr < ch->min) ch->forward = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
TCurtain::TCurtain(int num_rows, float z_dist, float tex_size,
|
|
float base_speed, float start_angle, float min_height, int dense) {
|
|
numRows = num_rows;
|
|
zdist = z_dist;
|
|
size = tex_size;
|
|
speed = base_speed;
|
|
startangle = start_angle;
|
|
minheight = min_height;
|
|
switch (dense) {
|
|
case 1:
|
|
texture = T_SNOW1;
|
|
break;
|
|
case 2:
|
|
texture = T_SNOW2;
|
|
break;
|
|
case 3:
|
|
texture = T_SNOW3;
|
|
break;
|
|
}
|
|
|
|
angledist = atan(size / 2 / zdist) * 360 / 3.14159;
|
|
numCols = (unsigned int)(-2 * startangle / angledist) + 1;
|
|
if (numCols > MAX_CURTAIN_COLS) numCols = MAX_CURTAIN_COLS;
|
|
lastangle = startangle + (numCols-1) * angledist;
|
|
|
|
for (unsigned int i=0; i<numRows; i++)
|
|
chg[i] = IRandom(0, 5);
|
|
}
|
|
|
|
void TCurtain::SetStartParams(const CControl* ctrl) {
|
|
for (unsigned int co=0; co<numCols; co++) {
|
|
for (unsigned int row=0; row<numRows; row++) {
|
|
TCurtainElement* curt = &curtains[co][row];
|
|
curt->height = minheight + row * size;
|
|
float x, z;
|
|
curt->angle = co * angledist + startangle;
|
|
CurtainVec(curt->angle, zdist, x, z);
|
|
curt->pt.x = ctrl->cpos.x + x;
|
|
curt->pt.z = ctrl->cpos.z + z;
|
|
curt->pt.y = ctrl->cpos.y + curt->height;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TCurtain::Draw() const {
|
|
Tex.BindTex(texture);
|
|
float halfsize = size / 2.f;
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
for (unsigned int co=0; co<numCols; co++) {
|
|
for (unsigned int row=0; row<numRows; row++) {
|
|
const TVector3d& pt = curtains[co][row].pt;
|
|
glPushMatrix();
|
|
glTranslate(pt);
|
|
glRotatef(-curtains[co][row].angle, 0, 1, 0);
|
|
|
|
static const GLshort tex[] = {
|
|
0, 0,
|
|
1, 0,
|
|
1, 1,
|
|
0, 1
|
|
};
|
|
const GLfloat vtx[] = {
|
|
-halfsize, -halfsize, 0,
|
|
halfsize, -halfsize, 0,
|
|
halfsize, halfsize, 0,
|
|
-halfsize, halfsize, 0
|
|
};
|
|
glVertexPointer(3, GL_FLOAT, 0, vtx);
|
|
glTexCoordPointer(2, GL_SHORT, 0, tex);
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
}
|
|
|
|
void TCurtain::Update(float timestep, const TVector3d& drift, const CControl* ctrl) {
|
|
for (unsigned int co=0; co<numCols; co++) {
|
|
for (unsigned int row=0; row<numRows; row++) {
|
|
TCurtainElement* curt = &curtains[co][row];
|
|
|
|
curt->angle += changes[chg[row]].curr * timestep * CHANGE_DRIFT;
|
|
curt->angle += drift.x * timestep * CURTAIN_WINDDRIFT;
|
|
curt->height -= speed * timestep;
|
|
|
|
if (curt->angle > lastangle + angledist) curt->angle = startangle;
|
|
if (curt->angle < startangle - angledist) curt->angle = lastangle;
|
|
float x, z;
|
|
CurtainVec(curt->angle, zdist, x, z);
|
|
curt->pt.x = ctrl->cpos.x + x;
|
|
curt->pt.z = ctrl->cpos.z + z;
|
|
curt->pt.y = ctrl->cpos.y + curt->height;
|
|
if (curt->height < minheight - size) curt->height += numRows * size;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static CCurtain Curtain;
|
|
void TCurtain::CurtainVec(float angle, float zdist, float &x, float &z) {
|
|
x = zdist * sin(angle * 3.14159 / 180);
|
|
if (angle > 90 || angle < -90) z = sqrt(zdist * zdist - x * x);
|
|
else z = -sqrt(zdist * zdist - x * x);
|
|
}
|
|
|
|
void CCurtain::Draw() {
|
|
if (g_game.snow_id < 1) return;
|
|
|
|
ScopedRenderMode rm(PARTICLES);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
const TColor& particle_colour = Env.ParticleColor();
|
|
glColor(particle_colour, 1.0);
|
|
|
|
// glEnable (GL_NORMALIZE);
|
|
for (size_t i=0; i<curtains.size(); i++) {
|
|
curtains[i].Draw();
|
|
}
|
|
}
|
|
|
|
void CCurtain::Update(float timestep, const CControl *ctrl) {
|
|
if (g_game.snow_id < 1) return;
|
|
const TVector3d& drift = Wind.WindDrift();
|
|
|
|
UpdateChanges(timestep);
|
|
for (size_t i=0; i<curtains.size(); i++) {
|
|
curtains[i].Update(timestep, drift, ctrl);
|
|
}
|
|
Draw();
|
|
}
|
|
|
|
void CCurtain::Reset() {
|
|
curtains.clear();
|
|
}
|
|
|
|
void CCurtain::SetStartParams(const CControl *ctrl) {
|
|
for (size_t i=0; i<curtains.size(); i++) {
|
|
curtains[i].SetStartParams(ctrl);
|
|
}
|
|
}
|
|
|
|
void CCurtain::Init(const CControl *ctrl) {
|
|
Reset();
|
|
InitChanges();
|
|
switch (g_game.snow_id) {
|
|
case 1:
|
|
// curtains.push_back(TCurtain(3, 60, 10, 3, -100, -10, 1));
|
|
// curtains.push_back(TCurtain(3, 50, 13, 3, -100, -10, 1));
|
|
// curtains.push_back(TCurtain(3, 40, 16, 3, -100, -10, 1));
|
|
curtains.push_back(TCurtain(3, 60, 15, 3, -100, -10, 1));
|
|
curtains.push_back(TCurtain(3, 50, 19, 3, -100, -10, 1));
|
|
curtains.push_back(TCurtain(3, 40, 23, 3, -100, -10, 1));
|
|
// curtains.push_back(TCurtain(3, 60, 20, 3, -100, -10, 1));
|
|
// curtains.push_back(TCurtain(3, 50, 25, 3, -100, -10, 1));
|
|
// curtains.push_back(TCurtain(3, 40, 30, 3, -100, -10, 1));
|
|
break;
|
|
case 2:
|
|
// curtains.push_back(TCurtain(3, 60, 15, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 50, 17, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 40, 20, 3, -100, -10, 2));
|
|
curtains.push_back(TCurtain(3, 60, 22, 3, -100, -10, 2));
|
|
curtains.push_back(TCurtain(3, 50, 25, 3, -100, -10, 2));
|
|
curtains.push_back(TCurtain(3, 40, 30, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 60, 30, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 50, 35, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 40, 40, 3, -100, -10, 2));
|
|
break;
|
|
case 3:
|
|
// curtains.push_back(TCurtain(3, 60, 20, 3, -100, -10, 3));
|
|
// curtains.push_back(TCurtain(3, 50, 25, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 40, 30, 3, -100, -10, 2));
|
|
curtains.push_back(TCurtain(3, 60, 22, 3, -100, -10, 3));
|
|
curtains.push_back(TCurtain(3, 50, 27, 3, -100, -10, 2));
|
|
curtains.push_back(TCurtain(3, 40, 32, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 60, 25, 3, -100, -10, 3));
|
|
// curtains.push_back(TCurtain(3, 50, 30, 3, -100, -10, 2));
|
|
// curtains.push_back(TCurtain(3, 40, 35, 3, -100, -10, 2));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
SetStartParams(ctrl);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// wind
|
|
// --------------------------------------------------------------------
|
|
|
|
#define UPDATE_TIME 0.04
|
|
|
|
CWind Wind;
|
|
|
|
CWind::CWind()
|
|
: WVector(0, 0, 0) {
|
|
windy = false;
|
|
CurrTime = 0.0;
|
|
|
|
SpeedMode = 0;
|
|
AngleMode = 0;
|
|
WSpeed = 0;
|
|
WAngle = 0;
|
|
DestSpeed = 0;
|
|
DestAngle = 0;
|
|
WindChange = 0;
|
|
AngleChange = 0;
|
|
}
|
|
|
|
void CWind::SetParams(int grade) {
|
|
float min_base_speed = 0;
|
|
float max_base_speed = 0;
|
|
float min_speed_var = 0;
|
|
float max_speed_var = 0;
|
|
float min_base_angle = 0;
|
|
float max_base_angle = 0;
|
|
float min_angle_var = 0;
|
|
float max_angle_var = 0;
|
|
float alt_angle = 0;
|
|
|
|
if (grade == 0) {
|
|
min_base_speed = 20;
|
|
max_base_speed = 35;
|
|
min_speed_var = 20;
|
|
max_speed_var = 20;
|
|
params.minChange = 0.1;
|
|
params.maxChange = 0.3;
|
|
|
|
min_base_angle = 70;
|
|
max_base_angle = 110;
|
|
min_angle_var = 0;
|
|
max_angle_var = 90;
|
|
params.minAngleChange = 0.1;
|
|
params.maxAngleChange = 1.0;
|
|
|
|
params.topSpeed = 100;
|
|
params.topProbability = 0;
|
|
params.nullProbability = 6;
|
|
alt_angle = 180;
|
|
} else if (grade == 1) {
|
|
min_base_speed = 30;
|
|
max_base_speed = 60;
|
|
min_speed_var = 40;
|
|
max_speed_var = 40;
|
|
params.minChange = 0.1;
|
|
params.maxChange = 0.5;
|
|
|
|
min_base_angle = 70;
|
|
max_base_angle = 110;
|
|
min_angle_var = 0;
|
|
max_angle_var = 90;
|
|
params.minAngleChange = 0.1;
|
|
params.maxAngleChange = 1.0;
|
|
|
|
params.topSpeed = 100;
|
|
params.topProbability = 0;
|
|
params.nullProbability = 10;
|
|
alt_angle = 180;
|
|
} else {
|
|
min_base_speed = 40;
|
|
max_base_speed = 80;
|
|
min_speed_var = 30;
|
|
max_speed_var = 60;
|
|
params.minChange = 0.1;
|
|
params.maxChange = 1.0;
|
|
|
|
min_base_angle = 0;
|
|
max_base_angle = 180;
|
|
min_angle_var = 180;
|
|
max_angle_var = 360;
|
|
params.minAngleChange = 0.1;
|
|
params.maxAngleChange = 1.0;
|
|
|
|
params.topSpeed = 100;
|
|
params.topProbability = 10;
|
|
params.nullProbability = 10;
|
|
alt_angle = 0;
|
|
}
|
|
|
|
float speed, var, angle;
|
|
|
|
speed = XRandom(min_base_speed, max_base_speed);
|
|
var = XRandom(min_speed_var, max_speed_var) / 2;
|
|
params.minSpeed = speed - var;
|
|
params.maxSpeed = speed + var;
|
|
if (params.minSpeed < 0) params.minSpeed = 0;
|
|
if (params.maxSpeed > 100) params.maxSpeed = 100;
|
|
|
|
angle = XRandom(min_base_angle, max_base_angle);
|
|
if (XRandom(0, 100) > 50) angle = angle + alt_angle;
|
|
var = XRandom(min_angle_var, max_angle_var) / 2;
|
|
params.minAngle = angle - var;
|
|
params.maxAngle = angle + var;
|
|
}
|
|
|
|
void CWind::CalcDestSpeed() {
|
|
float rand = XRandom(0, 100);
|
|
if (rand > (100 - params.topProbability)) {
|
|
DestSpeed = XRandom(params.maxSpeed, params.topSpeed);
|
|
WindChange = params.maxChange;
|
|
} else if (rand < params.nullProbability) {
|
|
DestSpeed = 0.0;
|
|
WindChange = XRandom(params.minChange, params.maxChange);
|
|
} else {
|
|
DestSpeed = XRandom(params.minSpeed, params.maxSpeed);
|
|
WindChange = XRandom(params.minChange, params.maxChange);
|
|
}
|
|
|
|
if (DestSpeed > WSpeed) SpeedMode = 1;
|
|
else SpeedMode = 0;
|
|
}
|
|
|
|
void CWind::CalcDestAngle() {
|
|
DestAngle = XRandom(params.minAngle, params.maxAngle);
|
|
AngleChange = XRandom(params.minAngleChange, params.maxAngleChange);
|
|
|
|
if (DestAngle > WAngle) AngleMode = 1;
|
|
else AngleMode = 0;
|
|
}
|
|
|
|
void CWind::Update(float timestep) {
|
|
if (!windy) return;
|
|
|
|
// the wind needn't be updated in each frame
|
|
CurrTime = CurrTime + timestep;
|
|
if (CurrTime > UPDATE_TIME) {
|
|
CurrTime = 0.0;
|
|
|
|
if (SpeedMode == 1) { // current speed lesser than destination speed
|
|
if (WSpeed < DestSpeed) {
|
|
WSpeed = WSpeed + WindChange;
|
|
} else CalcDestSpeed();
|
|
} else {
|
|
if (WSpeed > DestSpeed) {
|
|
WSpeed = WSpeed - WindChange;
|
|
} else CalcDestSpeed();
|
|
}
|
|
if (WSpeed > params.topSpeed) WSpeed = params.topSpeed;
|
|
if (WSpeed < 0) WSpeed = 0;
|
|
|
|
|
|
if (AngleMode == 1) {
|
|
if (WAngle < DestAngle) {
|
|
WAngle = WAngle + AngleChange;
|
|
} else CalcDestAngle();
|
|
} else {
|
|
if (WAngle > DestAngle) {
|
|
WAngle = WAngle - AngleChange;
|
|
} else CalcDestAngle();
|
|
}
|
|
if (WAngle > params.maxAngle) WAngle = params.maxAngle;
|
|
if (WAngle < params.minAngle) WAngle = params.minAngle;
|
|
|
|
float xx = sin(WAngle * 3.14159 / 180);
|
|
float zz = sqrt(1 - xx * xx);
|
|
if ((WAngle > 90 && WAngle < 270) || (WAngle > 450 && WAngle < 630)) {
|
|
zz = -zz;
|
|
}
|
|
|
|
WVector.x = WSpeed * xx;
|
|
WVector.z = WSpeed * zz * 0.2;
|
|
}
|
|
}
|
|
|
|
void CWind::Init(int wind_id) {
|
|
if (wind_id < 1 || wind_id > 3) {
|
|
windy = false;
|
|
WVector = TVector3d(0, 0, 0);
|
|
WAngle = 0;
|
|
WSpeed = 0;
|
|
return;
|
|
}
|
|
windy = true;;
|
|
SetParams(wind_id -1);
|
|
WSpeed = XRandom(params.minSpeed, (params.minSpeed + params.maxSpeed) / 2);
|
|
WAngle = XRandom(params.minAngle, params.maxAngle);
|
|
CalcDestSpeed();
|
|
CalcDestAngle();
|
|
}
|
|
|
|
// ====================================================================
|
|
// access functions
|
|
// ====================================================================
|
|
|
|
void InitSnow(const CControl *ctrl) {
|
|
if (g_game.snow_id < 1 || g_game.snow_id > 3) return;
|
|
Flakes.Init(g_game.snow_id, ctrl);
|
|
Curtain.Init(ctrl);
|
|
}
|
|
|
|
void UpdateSnow(double timestep, const CControl *ctrl) {
|
|
if (g_game.snow_id < 1 || g_game.snow_id > 3) return;
|
|
Flakes.Update(timestep, ctrl);
|
|
Curtain.Update(timestep, ctrl);
|
|
}
|
|
|
|
void DrawSnow(const CControl *ctrl) {
|
|
if (g_game.snow_id < 1 || g_game.snow_id > 3) return;
|
|
Flakes.Draw(ctrl);
|
|
Curtain.Draw();
|
|
}
|
|
|
|
void InitWind() {
|
|
Wind.Init(g_game.wind_id);
|
|
}
|
|
|
|
void UpdateWind(double timestep) {
|
|
Wind.Update(timestep);
|
|
}
|