2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file)
5 This file is part of Lugaru.
7 Lugaru is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Lugaru is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Lugaru. If not, see <http://www.gnu.org/licenses/>.
21 #include "Objects/Object.hpp"
24 extern float viewdistance;
25 extern float fadestart;
26 extern int environment;
27 extern float texscale;
29 extern float multiplier;
31 extern FRUSTUM frustum;
32 extern Terrain terrain;
35 extern float blurness;
37 extern float playerdist;
38 extern bool skyboxtexture;
40 std::vector<std::unique_ptr<Object>> Object::objects;
42 float Object::radius = 0;
43 Texture Object::boxtextureptr;
44 Texture Object::treetextureptr;
45 Texture Object::bushtextureptr;
46 Texture Object::rocktextureptr;
73 Object::Object(object_type _type, XYZ _position, float _yaw, float _pitch, float _scale)
84 model.loaddecal("Models/Box.solid");
88 model.loaddecal("Models/Cool.solid");
92 model.loaddecal("Models/Wall.solid");
96 model.loaddecal("Models/Tunnel.solid");
100 model.loaddecal("Models/Chimney.solid");
104 model.load("Models/Spike.solid");
108 model.loaddecal("Models/Weird.solid");
112 model.loaddecal("Models/Rock.solid");
120 model.load("Models/TreeTrunk.solid");
124 scale += fabs((float)(Random() % 100) / 900) * scale;
125 model.load("Models/Leaves.solid");
129 position.y = terrain.getHeight(position.x, position.z) - .3;
130 model.load("Models/Bush.solid");
133 model.loaddecal("Models/Platform.solid");
134 model.Rotate(90, 0, 0);
142 if (friction == 1.5 && fabs(pitch) > 5) {
146 if (type == boxtype || type == cooltype || type == spiketype || type == weirdtype || type == walltype || type == chimneytype || type == tunneltype || type == platformtype) {
147 model.ScaleTexCoords(scale * 1.5);
149 if (type == rocktype) {
150 model.ScaleTexCoords(scale * 3);
153 if (type == treetrunktype || type == treeleavestype || type == rocktype) {
156 model.Scale(.3 * scale, .3 * scale, .3 * scale);
157 model.Rotate(90, 1, 1);
158 model.Rotate(pitch, 0, 0);
159 if (type == rocktype) {
160 model.Rotate(yaw * 5, 0, 0);
162 model.CalculateNormals(1);
163 model.ScaleNormals(-1, -1, -1);
166 void Object::handleFire()
168 if (type == firetype) {
173 if ((type == bushtype) || (type == firetype)) {
174 flamedelay -= multiplier * 3;
175 } else if (type == treeleavestype) {
176 flamedelay -= multiplier * 4;
178 while ((flamedelay < 0) && onfire) {
180 if ((type == bushtype) || (type == firetype)) {
181 spawnpoint.x = ((float)(Random() % 100)) / 30 * scale;
182 spawnpoint.y = ((float)(Random() % 100) + 60) / 30 * scale;
184 spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0);
185 spawnpoint += position;
186 Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 5 * scale, 1);
187 } else if (type == treeleavestype) {
188 spawnpoint.x = ((float)(Random() % 100)) / 80 * scale;
189 spawnpoint.y = ((float)(Random() % 100) + 80) / 12 * scale;
191 spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0);
192 spawnpoint += position;
193 Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 6, 1);
199 void Object::doShadows(XYZ lightloc)
201 XYZ testpoint, testpoint2, terrainpoint, col;
203 if (type != treeleavestype && type != treetrunktype && type != bushtype && type != firetype) {
204 for (int j = 0; j < model.vertexNum; j++) {
205 terrainpoint = position + DoRotation(model.vertex[j] + model.normals[j] * .1, 0, yaw, 0);
207 patchx = terrainpoint.x / (terrain.size / subdivision * terrain.scale);
208 patchz = terrainpoint.z / (terrain.size / subdivision * terrain.scale);
209 if (patchx >= 0 && patchz >= 0 && patchx < subdivision && patchz < subdivision) {
210 for (unsigned int k = 0; k < terrain.patchobjects[patchx][patchz].size(); k++) {
211 unsigned int l = terrain.patchobjects[patchx][patchz][k];
212 if (objects[l]->type != treetrunktype) {
213 testpoint = terrainpoint;
214 testpoint2 = terrainpoint + lightloc * 50 * (1 - shadowed);
215 if (objects[l]->model.LineCheck(&testpoint, &testpoint2, &col, &objects[l]->position, &objects[l]->yaw) != -1) {
216 shadowed = 1 - (findDistance(&terrainpoint, &col) / 50);
222 col = model.normals[j] - DoRotation(lightloc * shadowed, 0, -yaw, 0);
224 for (unsigned int k = 0; k < model.Triangles.size(); k++) {
225 if (model.Triangles[k].vertex[0] == j) {
227 model.vArray[l + 2] = col.x;
228 model.vArray[l + 3] = col.y;
229 model.vArray[l + 4] = col.z;
231 if (model.Triangles[k].vertex[1] == j) {
233 model.vArray[l + 10] = col.x;
234 model.vArray[l + 11] = col.y;
235 model.vArray[l + 12] = col.z;
237 if (model.Triangles[k].vertex[2] == j) {
239 model.vArray[l + 18] = col.x;
240 model.vArray[l + 19] = col.y;
241 model.vArray[l + 20] = col.z;
250 void Object::handleRot(int divide)
252 messedwith -= multiplier;
253 if (rotxvel || rotx) {
255 rotxvel -= multiplier * 8 * fabs(rotx);
258 rotxvel += multiplier * 8 * fabs(rotx);
261 rotxvel -= multiplier * 4;
264 rotxvel += multiplier * 4;
267 rotxvel -= multiplier * 4;
270 rotxvel += multiplier * 4;
272 if (fabs(rotx) < multiplier * 4) {
275 if (fabs(rotxvel) < multiplier * 4) {
279 rotx += rotxvel * multiplier * 4;
281 if (rotyvel || roty) {
283 rotyvel -= multiplier * 8 * fabs(roty);
286 rotyvel += multiplier * 8 * fabs(roty);
289 rotyvel -= multiplier * 4;
292 rotyvel += multiplier * 4;
295 rotyvel -= multiplier * 4;
298 rotyvel += multiplier * 4;
300 if (fabs(roty) < multiplier * 4) {
303 if (fabs(rotyvel) < multiplier * 4) {
307 roty += rotyvel * multiplier * 4;
310 glRotatef(roty / divide, 1, 0, 0);
313 glRotatef(-rotx / divide, 0, 0, 1);
331 static float distance;
332 static XYZ moved, terrainlight;
334 if (type == firetype) {
337 moved = DoRotation(model.boundingspherecenter, 0, yaw, 0);
338 if (type == tunneltype || frustum.SphereInFrustum(position.x + moved.x, position.y + moved.y, position.z + moved.z, model.boundingsphereradius)) {
339 distance = distsq(&viewer, &position);
341 hidden = !(distsqflat(&viewer, &position) > playerdist + 3 || (type != bushtype && type != treeleavestype));
344 if (detail == 2 && distance > viewdistance * viewdistance / 4 && environment == desertenvironment) {
345 glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, blurness);
347 glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0);
349 distance = (viewdistance * viewdistance - (distance - (viewdistance * viewdistance * fadestart)) * (1 / (1 - fadestart))) / viewdistance / viewdistance;
356 glMatrixMode(GL_MODELVIEW);
359 glEnable(GL_LIGHTING);
361 glDisable(GL_LIGHTING);
364 glTranslatef(position.x, position.y, position.z);
365 if (type == bushtype) {
368 if (type == treetrunktype || type == treeleavestype) {
369 if (type == treetrunktype || environment == desertenvironment) {
375 if (environment == snowyenvironment) {
376 if (type == treeleavestype) {
377 glRotatef((sin(windvar + position.x * .3) + .5) * 1.5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
379 if (type == treetrunktype) {
380 glRotatef((sin(windvar + position.x * .3) + .5) * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
382 if (type == bushtype) {
383 glRotatef((sin(windvar + position.x * .3) + .5) * 4 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
386 if (environment == grassyenvironment) {
387 if (type == treeleavestype) {
388 glRotatef((sin(windvar + position.x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
390 if (type == treetrunktype) {
391 glRotatef((sin(windvar + position.x * .3) + .5) * .5 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
393 if (type == bushtype) {
394 glRotatef((sin(windvar + position.x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
397 if (environment == desertenvironment) {
398 if (type == bushtype) {
399 glRotatef((sin(windvar + position.x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
402 glRotatef(yaw, 0, 1, 0);
403 glColor4f((1 - shadowed) / 2 + .5, (1 - shadowed) / 2 + .5, (1 - shadowed) / 2 + .5, distance);
406 glAlphaFunc(GL_GREATER, 0.5);
409 glAlphaFunc(GL_GREATER, 0.1);
411 if (type != treetrunktype && type != treeleavestype && type != bushtype && type != rocktype) {
412 glEnable(GL_CULL_FACE);
413 glAlphaFunc(GL_GREATER, 0.0001);
414 model.drawdifftex(boxtextureptr);
415 model.drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture);
417 if (type == rocktype) {
418 glEnable(GL_CULL_FACE);
419 glAlphaFunc(GL_GREATER, 0.0001);
420 glColor4f((1 - shadowed) / 2 + light.ambient[0], (1 - shadowed) / 2 + light.ambient[1], (1 - shadowed) / 2 + light.ambient[2], distance);
421 model.drawdifftex(rocktextureptr);
422 model.drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture);
424 if (type == treeleavestype) {
425 glDisable(GL_CULL_FACE);
426 glDisable(GL_LIGHTING);
427 terrainlight = terrain.getLighting(position.x, position.z);
428 glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance);
430 glAlphaFunc(GL_GREATER, 0.2);
432 model.drawdifftex(treetextureptr);
434 if (type == bushtype) {
435 glDisable(GL_CULL_FACE);
436 glDisable(GL_LIGHTING);
437 terrainlight = terrain.getLighting(position.x, position.z);
438 glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance);
440 glAlphaFunc(GL_GREATER, 0.2);
442 model.drawdifftex(bushtextureptr);
444 if (type == treetrunktype) {
445 glEnable(GL_CULL_FACE);
446 terrainlight = terrain.getLighting(position.x, position.z);
447 glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance);
448 model.drawdifftex(treetextureptr);
457 void Object::drawSecondPass()
459 static float distance;
460 static XYZ moved, terrainlight;
462 if (type != treeleavestype && type != bushtype) {
465 moved = DoRotation(model.boundingspherecenter, 0, yaw, 0);
466 if (frustum.SphereInFrustum(position.x + moved.x, position.y + moved.y, position.z + moved.z, model.boundingsphereradius)) {
467 hidden = distsqflat(&viewer, &position) <= playerdist + 3;
470 glMatrixMode(GL_MODELVIEW);
472 glEnable(GL_LIGHTING);
474 glTranslatef(position.x, position.y, position.z);
475 if (type == bushtype) {
478 if (type == treetrunktype || type == treeleavestype) {
481 if (environment == snowyenvironment) {
482 if (type == treeleavestype) {
483 glRotatef((sin(windvar + position.x * .3) + .5) * 1.5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
485 if (type == treetrunktype) {
486 glRotatef((sin(windvar + position.x * .3) + .5) * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
488 if (type == bushtype) {
489 glRotatef((sin(windvar + position.x * .3) + .5) * 4 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
492 if (environment == grassyenvironment) {
493 if (type == treeleavestype) {
494 glRotatef((sin(windvar + position.x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
496 if (type == treetrunktype) {
497 glRotatef((sin(windvar + position.x * .3) + .5) * .5 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
499 if (type == bushtype) {
500 glRotatef((sin(windvar + position.x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position.x * .3) + 1) / 2, 1, 0, 0);
503 glRotatef(yaw, 0, 1, 0);
504 glColor4f(1, 1, 1, distance);
505 if (type == treeleavestype) {
506 glDisable(GL_CULL_FACE);
507 glDisable(GL_LIGHTING);
508 terrainlight = terrain.getLighting(position.x, position.z);
511 glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3);
512 glAlphaFunc(GL_GREATER, 0);
513 glDisable(GL_ALPHA_TEST);
514 model.drawdifftex(treetextureptr);
516 if (type == bushtype) {
517 glDisable(GL_CULL_FACE);
518 glDisable(GL_LIGHTING);
519 terrainlight = terrain.getLighting(position.x, position.z);
522 glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3);
523 glAlphaFunc(GL_GREATER, 0);
524 glDisable(GL_ALPHA_TEST);
525 model.drawdifftex(bushtextureptr);
532 void Object::ComputeCenter()
535 for (unsigned i = 0; i < objects.size(); i++) {
536 center += objects[i]->position;
538 center /= objects.size();
541 void Object::ComputeRadius()
543 float maxdistance = 0;
545 for (unsigned int i = 0; i < objects.size(); i++) {
546 tempdist = distsq(¢er, &objects[i]->position);
547 if (tempdist > maxdistance) {
548 maxdistance = tempdist;
551 radius = fast_sqrt(maxdistance);
554 void Object::LoadObjectsFromFile(FILE* tfile, bool skip)
559 float yaw, pitch, scale, lastscale;
560 funpackf(tfile, "Bi", &numobjects);
564 for (int i = 0; i < numobjects; i++) {
565 funpackf(tfile, "Bi Bf Bf Bf Bf Bf Bf", &type, &yaw, &pitch, &position.x, &position.y, &position.z, &scale);
567 if (type == treeleavestype) {
570 objects.emplace_back(new Object(object_type(type), position, yaw, pitch, scale));
576 void Object::addToTerrain(unsigned id)
578 if ((type != treeleavestype) && (type != bushtype) && (type != firetype)) {
579 terrain.AddObject(position + DoRotation(model.boundingspherecenter, 0, yaw, 0), model.boundingsphereradius, id);
583 if ((type == treetrunktype) && (position.y < (terrain.getHeight(position.x, position.z) + 1))) {
584 terrain.MakeDecal(shadowdecalpermanent, position, 2, .4, 0);
587 if ((type == bushtype) && (position.y < (terrain.getHeight(position.x, position.z) + 1))) {
588 terrain.MakeDecal(shadowdecalpermanent, position, 1, .4, 0);
593 void Object::AddObjectsToTerrain()
595 for (unsigned i = 0; i < objects.size(); i++) {
596 objects[i]->addToTerrain(i);
600 void Object::SphereCheckPossible(XYZ* p1, float radius)
602 int whichpatchx = p1->x / (terrain.size / subdivision * terrain.scale);
603 int whichpatchz = p1->z / (terrain.size / subdivision * terrain.scale);
605 if (whichpatchx >= 0 && whichpatchz >= 0 && whichpatchx < subdivision && whichpatchz < subdivision) {
606 if (terrain.patchobjects[whichpatchx][whichpatchz].size() < 500) {
607 for (unsigned int j = 0; j < terrain.patchobjects[whichpatchx][whichpatchz].size(); j++) {
608 unsigned int i = terrain.patchobjects[whichpatchx][whichpatchz][j];
609 objects[i]->possible = false;
610 if (objects[i]->model.SphereCheckPossible(p1, radius, &objects[i]->position, &objects[i]->yaw) != -1) {
611 objects[i]->possible = true;
620 for (unsigned i = 0; i < objects.size(); i++) {
624 glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0);
625 for (unsigned i = 0; i < objects.size(); i++) {
626 objects[i]->drawSecondPass();
628 if (environment == desertenvironment) {
629 glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0);
631 glEnable(GL_ALPHA_TEST);
632 SetUpLight(&light, 0);
635 void Object::DeleteObject(int which)
637 objects.erase(objects.begin() + which);
640 void Object::MakeObject(int atype, XYZ where, float ayaw, float apitch, float ascale)
642 if ((atype != treeleavestype && atype != bushtype) || foliage == 1) {
643 unsigned nextid = objects.size();
644 objects.emplace_back(new Object(object_type(atype), where, ayaw, apitch, ascale));
645 objects.back()->addToTerrain(nextid);
649 void Object::DoStuff()
651 for (unsigned i = 0; i < objects.size(); i++) {
652 objects[i]->handleFire();
656 void Object::DoShadows()
659 lightloc = light.location;
660 if (!skyboxtexture) {
664 Normalise(&lightloc);
666 for (unsigned i = 0; i < objects.size(); i++) {
667 objects[i]->doShadows(lightloc);
671 int Object::checkcollide(XYZ startpoint, XYZ endpoint)
673 float minx, minz, maxx, maxz, miny, maxy;
675 minx = min(startpoint.x, endpoint.x) - 1;
676 miny = min(startpoint.y, endpoint.y) - 1;
677 minz = min(startpoint.z, endpoint.z) - 1;
678 maxx = max(startpoint.x, endpoint.x) + 1;
679 maxy = max(startpoint.y, endpoint.y) + 1;
680 maxz = max(startpoint.z, endpoint.z) + 1;
682 for (unsigned int i = 0; i < objects.size(); i++) {
683 if (checkcollide(startpoint, endpoint, i, minx, miny, minz, maxx, maxy, maxz) != -1) {
691 int Object::checkcollide(XYZ startpoint, XYZ endpoint, int what)
693 float minx, minz, maxx, maxz, miny, maxy;
695 minx = min(startpoint.x, endpoint.x) - 1;
696 miny = min(startpoint.y, endpoint.y) - 1;
697 minz = min(startpoint.z, endpoint.z) - 1;
698 maxx = max(startpoint.x, endpoint.x) + 1;
699 maxy = max(startpoint.y, endpoint.y) + 1;
700 maxz = max(startpoint.z, endpoint.z) + 1;
702 return checkcollide(startpoint, endpoint, what, minx, miny, minz, maxx, maxy, maxz);
705 int Object::checkcollide(XYZ startpoint, XYZ endpoint, int what, float minx, float miny, float minz, float maxx, float maxy, float maxz)
707 XYZ colpoint, colviewer, coltarget;
710 if (terrain.lineTerrain(startpoint, endpoint, &colpoint) != -1) {
714 if (objects[what]->position.x > minx - objects[what]->model.boundingsphereradius &&
715 objects[what]->position.x < maxx + objects[what]->model.boundingsphereradius &&
716 objects[what]->position.y > miny - objects[what]->model.boundingsphereradius &&
717 objects[what]->position.y < maxy + objects[what]->model.boundingsphereradius &&
718 objects[what]->position.z > minz - objects[what]->model.boundingsphereradius &&
719 objects[what]->position.z < maxz + objects[what]->model.boundingsphereradius) {
720 if (objects[what]->type != treeleavestype &&
721 objects[what]->type != bushtype &&
722 objects[what]->type != firetype) {
723 colviewer = startpoint;
724 coltarget = endpoint;
725 if (objects[what]->model.LineCheck(&colviewer, &coltarget, &colpoint, &objects[what]->position, &objects[what]->yaw) != -1) {
735 //~ Object::~Objects()
737 //~ boxtextureptr.destroy();
738 //~ treetextureptr.destroy();
739 //~ bushtextureptr.destroy();
740 //~ rocktextureptr.destroy();