diff --git a/BrowEdit3.vcxproj b/BrowEdit3.vcxproj
index a597773..70d5353 100644
--- a/BrowEdit3.vcxproj
+++ b/BrowEdit3.vcxproj
@@ -18,6 +18,7 @@
+
@@ -144,6 +145,7 @@
+
diff --git a/BrowEdit3.vcxproj.filters b/BrowEdit3.vcxproj.filters
index 1e96a1e..365702b 100644
--- a/BrowEdit3.vcxproj.filters
+++ b/BrowEdit3.vcxproj.filters
@@ -430,6 +430,9 @@
browedit\actions
+
+ browedit\actions
+
@@ -741,6 +744,9 @@
browedit\actions
+
+ browedit\actions
+
diff --git a/browedit/BrowEdit.h b/browedit/BrowEdit.h
index b7c1108..0e5e69f 100644
--- a/browedit/BrowEdit.h
+++ b/browedit/BrowEdit.h
@@ -247,7 +247,7 @@ class BrowEdit
} textureBrushMode = TextureBrushMode::Stamp;
TextureBrushMode brushModeBeforeDropper;
- bool heightDoodle = true;
+ bool heightDoodle = false;
std::map> tagList; // tag -> [ file ], utf8
std::map> tagListReverse; // file -> [ tag ], kr
@@ -258,7 +258,11 @@ class BrowEdit
Config config;
std::vector> newNodes;
glm::vec3 newNodesCenter;
- bool newNodeHeight = false;
+ enum {
+ Ground, // The new node will be relative to the ground
+ Absolute, // The new node position will be determined by newNodesCenter
+ Relative // The new node will be relative to the ground + newNodesCenter
+ } newNodePlacement = Ground;
std::vector newCubes;
std::vector newGatCubes;
int pasteOptions = -1;
diff --git a/browedit/HotkeyActions.cpp b/browedit/HotkeyActions.cpp
index 5bbfe84..4e25108 100644
--- a/browedit/HotkeyActions.cpp
+++ b/browedit/HotkeyActions.cpp
@@ -77,7 +77,7 @@ void BrowEdit::registerActions()
else if (editMode == EditMode::Gat && !heightDoodle)
pasteGat();
}, hasActiveMapView);
- HotkeyRegistry::registerAction(HotkeyAction::Global_PasteChangeHeight, [this]() { if (editMode == EditMode::Object) { newNodeHeight = !newNodeHeight; } }, hasActiveMapView);
+ HotkeyRegistry::registerAction(HotkeyAction::Global_PasteChangeHeight, [this]() { if (editMode == EditMode::Object) { newNodePlacement = newNodePlacement == BrowEdit::Absolute ? BrowEdit::Ground : BrowEdit::Absolute; } }, hasActiveMapView);
HotkeyRegistry::registerAction(HotkeyAction::Global_ClearZeroHeightWalls, [this]() { activeMapView->map->rootNode->getComponent()->removeZeroHeightWalls(); }, hasActiveMapView);
HotkeyRegistry::registerAction(HotkeyAction::Global_CalculateQuadtree, [this]() { activeMapView->map->recalculateQuadTree(this); }, hasActiveMapView);
diff --git a/browedit/Lightmapper.cpp b/browedit/Lightmapper.cpp
index 5ade56a..efce331 100644
--- a/browedit/Lightmapper.cpp
+++ b/browedit/Lightmapper.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -18,6 +19,7 @@ extern std::mutex debugPointMutex;
extern std::vector> debugPoints;
double startTime;
+LightmapChangeAction* lightmapChangeAction;
Lightmapper::Lightmapper(Map* map, BrowEdit* browEdit) : map(map), browEdit(browEdit)
{
@@ -48,6 +50,10 @@ void Lightmapper::begin()
std::cout << "Before:\t" << gnd->tiles.size() << " tiles, " << gnd->lightmaps.size() << " lightmaps" << std::endl;
gnd->makeLightmapsUnique();
std::cout << "After:\t" << gnd->tiles.size() << " tiles, " << gnd->lightmaps.size() << " lightmaps" << std::endl;
+
+ lightmapChangeAction = new LightmapChangeAction();
+ lightmapChangeAction->setPreviousData(gnd->lightmaps, gnd->tiles);
+
map->rootNode->getComponent()->setChunksDirty();
map->rootNode->getComponent()->gndShadowDirty = true;
@@ -92,24 +98,61 @@ void Lightmapper::run()
setProgressText("Gathering Lights");
lights.clear();
models.clear();
+ quadtree.clear();
+
+ auto rsw = map->rootNode->getComponent();
+ auto gnd = map->rootNode->getComponent();
+
+ quadtree.resize(gnd->width, std::vector(gnd->height + 1));
+
+ for (int x = 0; x < quadtree.size(); x++) {
+ for (int y = 0; y < quadtree[0].size(); y++) {
+ quadtree[x][y].range[0].x = 10.0f * x;
+ quadtree[x][y].range[0].y = 10.0f * y;
+ quadtree[x][y].range[1].x = 10.0f * (x + 1);
+ quadtree[x][y].range[1].y = 10.0f * (y + 1);
+ }
+ }
+
map->rootNode->traverse([&](Node* n) {
- if (n->getComponent())
- lights.push_back(n);
+ if (n->getComponent()) {
+ struct Lightmapper::light_data l;
+ l.light = n;
+ l.rswLight = n->getComponent();
+ l.rswObject = n->getComponent();
+ lights.push_back(l);
+ }
if (RswModel* m = n->getComponent())
- if(m->shadowStrength > 0)
- models.push_back(n);
+ if (m->shadowStrength > 0) {
+ struct Lightmapper::light_model nmodel = {};
+ nmodel.node = n;
+ nmodel.rswModel = m;
+ nmodel.collider = n->getComponent();
+ models.push_back(nmodel);
+ nmodel.collider->calculateWorldFaces();
+
+ // Adds the model to all zones where it collides in the quadtree, on the XZ plane (the Y axis is not used)
+ int xMin = glm::max(0, (int)(m->aabb.bounds[0].x / 10.0f));
+ int yMin = glm::max(0, (int)(m->aabb.bounds[0].z / 10.0f));
+ int xMax = glm::min((int)glm::ceil(m->aabb.bounds[1].x / 10.0f), (int)quadtree.size() - 1);
+ int yMax = glm::min((int)glm::ceil(m->aabb.bounds[1].z / 10.0f), (int)quadtree[0].size() - 1);
+
+ for (int x = xMin; x <= xMax; x++) {
+ for (int y = yMin; y <= yMax; y++) {
+ quadtree[x][y].models.push_back(nmodel);
+ }
+ }
+ }
});
- auto rsw = map->rootNode->getComponent();
- auto gnd = map->rootNode->getComponent();
std::cout << "Lightmapper: Complexity " << rsw->lightmapSettings.quality << "*"<< rsw->lightmapSettings.quality<<"*" << gnd->width << "*" << gnd->height << "*" << lights.size() << "*" << models.size() << "=" << rsw->lightmapSettings.quality * rsw->lightmapSettings.quality * gnd->width * gnd->height * lights.size() * models.size() << std::endl;
auto& settings = rsw->lightmapSettings;
-
- lightDirection[0] = -glm::cos(glm::radians((float)rsw->light.longitude)) * glm::sin(glm::radians((float)rsw->light.latitude));
- lightDirection[1] = glm::cos(glm::radians((float)rsw->light.latitude));
- lightDirection[2] = glm::sin(glm::radians((float)rsw->light.longitude)) * glm::sin(glm::radians((float)rsw->light.latitude));
+ glm::mat4 rot = glm::mat4(1.0f);
+ rot = glm::rotate(rot, glm::radians(-(float)rsw->light.latitude), glm::vec3(1, 0, 0));
+ rot = glm::rotate(rot, glm::radians((float)rsw->light.longitude), glm::vec3(0, 1, 0));
+ lightDirection = glm::vec3(0.0f, 1.0f, 0.0f) * glm::mat3(rot);
//setProgressText("Calculating map quads");
//mapQuads = gnd->getMapQuads();
@@ -196,48 +239,82 @@ void Lightmapper::run()
}));
}
-
-
-
-
for (auto& t : threads)
t.join();
-
}
-bool Lightmapper::collidesMap(const math::Ray& ray, float maxDistance)
+bool Lightmapper::collidesMap(const math::Ray& ray, int cx, int cy, float maxDistance)
{
- auto point = gnd->rayCast(ray, false, 0, 0, -1, -1, 0.05f);
+ auto point = gnd->rayCastLightmap(ray, cx, cy, 0, 0, -1, -1, 0.05f);
return point != glm::vec3(std::numeric_limits().max()) && glm::distance(point, ray.origin) < maxDistance;
};
+float inverse_rsqrt(float number)
+{
+ const float threehalfs = 1.5F;
+
+ float x2 = number * 0.5F;
+ float y = number;
+
+ long i = *(long*)&y;
-std::pair Lightmapper::calculateLight(const glm::vec3& groundPos, const glm::vec3& normal)
+ i = 0x5f3759df - (i >> 1);
+ y = *(float*)&i;
+
+ // 1st iteration
+ y = y * (threehalfs - (x2 * y * y));
+ return y;
+}
+
+glm::vec3 normalize_fast(glm::vec3 v) {
+ float s = inverse_rsqrt(v.x * v.x + v.y * v.y + v.z * v.z);
+ v.x *= s;
+ v.y *= s;
+ v.z *= s;
+ return v;
+}
+
+std::pair Lightmapper::calculateLight(const glm::vec3& groundPos, const glm::vec3& normal, int cx, int cy)
{
- int intensity = 0;
+ // By default, a light has 255 intensity and 0,0,0 color.
+ // An intensity of 255 means no shadow.
+ // A shadowStrength of 1 means the shadow is has an intensity of the lightmapAmbient.
+ // The strength of a shadow cannot be above the lightmapAmbient value.
+ int intensity = 255;
glm::vec3 colorInc(0.0f);
auto rsw = map->rootNode->getComponent();
auto& settings = rsw->lightmapSettings;
- if (rsw->light.lightmapAmbient > 0)
- intensity = (int)(rsw->light.lightmapAmbient * 255);
+ if (settings.additiveShadow) {
+ intensity = 0;
+
+ if (rsw->light.lightmapAmbient > 0)
+ intensity = (int)(rsw->light.lightmapAmbient * 255);
+ }
for (auto light : lights)
{
- auto rswObject = light->getComponent();
- auto rswLight = light->getComponent();
+ auto rswObject = light.rswObject;
+ auto rswLight = light.rswLight;
if (!rswLight->enabled)
continue;
glm::vec3 lightPosition(5 * gnd->width + rswObject->position.x, -rswObject->position.y, 5 * gnd->height - rswObject->position.z+10);
- glm::vec3 lightDirection2 = glm::normalize(lightPosition - groundPos);
+ //glm::vec3 lightDirection2 = glm::normalize(lightPosition - groundPos);
+ glm::vec3 lightDirection2 = normalize_fast(lightPosition - groundPos);
if (rswLight->lightType == RswLight::Type::Sun && !rswLight->sunMatchRswDirection)
lightDirection2 = rswLight->direction; //TODO: should this be -direction?
else if (rswLight->lightType == RswLight::Type::Sun && rswLight->sunMatchRswDirection)
lightDirection2 = lightDirection;
auto dotproduct = glm::dot(normal, lightDirection2);
- if (dotproduct <= 0)
+ if (dotproduct <= 0) {
+ if (rswLight->lightType == RswLight::Type::Sun) {
+ if (!settings.additiveShadow && rswLight->affectShadowMap)
+ intensity -= (int)(255.0f * rswLight->intensity);
+ }
+
continue;
+ }
float distance = glm::distance(lightPosition, groundPos);
float attenuation = 0;
@@ -253,20 +330,32 @@ std::pair Lightmapper::calculateLight(const glm::vec3& groundPos
attenuation = rswLight->intensity / (denom * denom);
if (rswLight->cutOff > 0)
attenuation = glm::max(0.0f, (attenuation - rswLight->cutOff) / (1 - rswLight->cutOff));
+ attenuation *= 255.0f;
}
else
{
if (distance > rswLight->range)
continue;
+
float d = distance / rswLight->range;
- if (rswLight->falloffStyle == RswLight::FalloffStyle::SplineTweak)
- attenuation = glm::clamp(util::interpolateSpline(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
- else if (rswLight->falloffStyle == RswLight::FalloffStyle::LagrangeTweak)
- attenuation = glm::clamp(util::interpolateLagrange(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
- else if (rswLight->falloffStyle == RswLight::FalloffStyle::LinearTweak)
- attenuation = glm::clamp(util::interpolateLinear(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
- else if (rswLight->falloffStyle == RswLight::FalloffStyle::Exponential)
- attenuation = glm::clamp((1 - glm::pow(d, rswLight->cutOff)), 0.0f, 1.0f) * 255.0f;
+
+ switch (rswLight->falloffStyle) {
+ case RswLight::FalloffStyle::SplineTweak:
+ attenuation = glm::clamp(util::interpolateSpline(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
+ break;
+ case RswLight::FalloffStyle::LagrangeTweak:
+ attenuation = glm::clamp(util::interpolateLagrange(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
+ break;
+ case RswLight::FalloffStyle::LinearTweak:
+ attenuation = glm::clamp(util::interpolateLinear(rswLight->falloff, d), 0.0f, 1.0f) * 255.0f;
+ break;
+ case RswLight::FalloffStyle::S_Curve:
+ attenuation = glm::clamp(util::interpolateSCurve(d), 0.0f, 2.0f) * 255.0f;
+ break;
+ case RswLight::FalloffStyle::Exponential:
+ attenuation = glm::clamp((1 - glm::pow(d, rswLight->cutOff)), 0.0f, 1.0f) * 255.0f;
+ break;
+ }
}
if (rswLight->lightType == RswLight::Type::Spot)
{
@@ -287,24 +376,62 @@ std::pair Lightmapper::calculateLight(const glm::vec3& groundPos
bool collides = false;
float shadowStrength = 0.0f;
+
if (settings.shadows)
{
math::Ray ray(groundPos, lightDirection2);
if (rswLight->givesShadow && attenuation > 0)
{
- for(auto& n : models) {
+ // Find all models that are on the path of the ray, using the quadtree
+ int qx = (int)(ray.origin.x / 10.0f);
+ int qy = (int)(ray.origin.z / 10.0f);
+
+ glm::ivec2 dir(ray.dir.x < 0 ? -1 : (ray.dir.x > 0 ? 1 : 0), ray.dir.z < 0 ? -1 : (ray.dir.z > 0 ? 1 : 0));
+ glm::ivec2 dirs[3] = { glm::ivec2(dir.x, 0), glm::ivec2(0, dir.y), glm::ivec2(dir.x, dir.y) };
+
+ std::set quadtree_models;
+
+ while (qx >= 0 && qx < quadtree.size() && qy >= 0 && qy < quadtree[0].size()) {
+ for (int i = 0; i < quadtree[qx][qy].models.size(); i++)
+ quadtree_models.insert(&quadtree[qx][qy].models[i]);
+
+ int j = 0;
+
+ for (; j < 3; j++) {
+ int qqx = qx + dirs[j].x;
+ int qqy = qy + dirs[j].y;
+
+ if ((qqx == qx && qqy == qy) || qqx < 0 || qqx >= quadtree.size() || qqy < 0 || qqy >= quadtree[0].size())
+ continue;
+
+ math::AABB box(glm::vec3(quadtree[qqx][qqy].range[0].x, -999999, quadtree[qqx][qqy].range[0].y), glm::vec3(quadtree[qqx][qqy].range[1].x, 999999, quadtree[qqx][qqy].range[1].y));
+
+ if (!box.hasRayCollision(ray, -999999, 9999999))
+ continue;
+
+ qx = qqx;
+ qy = qqy;
+ break;
+ }
+
+ if (j == 3)
+ break;
+ }
+
+ // Check if the ray collides with the model
+ for(auto n : quadtree_models) {
if (collides && shadowStrength >= 1)
continue;
- auto rswModel = n->getComponent();
- auto collider = n->getComponent();
- if (collider->collidesTexture(ray, 0, distance- rswLight->minShadowDistance))
+
+ if (n->collider->collidesTexture(ray, 0, distance - rswLight->minShadowDistance))
{
collides = true;
- shadowStrength += rswModel->shadowStrength;
+ shadowStrength += n->rswModel->shadowStrength;
}
}
}
- if (!collides && shadowStrength < 1 && rswLight->shadowTerrain && rswLight->givesShadow && collidesMap(math::Ray(groundPos, lightDirection2), distance))
+ // Check if the ray collides with the ground
+ if (!collides && shadowStrength < 1 && rswLight->shadowTerrain && rswLight->givesShadow && collidesMap(math::Ray(groundPos, lightDirection2), cx, cy, distance))
{
collides = true;
shadowStrength = 1;
@@ -312,16 +439,16 @@ std::pair Lightmapper::calculateLight(const glm::vec3& groundPos
}
if (shadowStrength > 1)
shadowStrength = 1;
- if (shadowStrength <= 1)
- {
- if (rswLight->diffuseLighting)
- attenuation *= dotproduct;
-
- if (rswLight->affectShadowMap)
- intensity += (int)((1-shadowStrength) * attenuation * rswLight->intensity);
- if (rswLight->affectLightmap)
- colorInc += (1-shadowStrength) * (attenuation / 255.0f) * rswLight->color * rswLight->intensity;
+ if (rswLight->diffuseLighting)
+ attenuation *= dotproduct;
+ if (rswLight->affectShadowMap) {
+ if (settings.additiveShadow)
+ intensity += (int)((1 - shadowStrength) * attenuation * rswLight->intensity);
+ else
+ intensity -= (int)(shadowStrength * attenuation * rswLight->intensity);
}
+ if (rswLight->affectLightmap)
+ colorInc += (1-shadowStrength) * (attenuation / 255.0f) * rswLight->color * rswLight->intensity;
}
return std::pair(colorInc, intensity);
};
@@ -448,7 +575,7 @@ void Lightmapper::calcPos(int direction, int tileId, int x, int y)
debugPointMutex.unlock();
}
- auto light = calculateLight(groundPos, normal);
+ auto light = calculateLight(groundPos, normal, x, y);
totalIntensity += glm::min(255, light.second);
totalColor += glm::min(glm::vec3(1.0f, 1.0f, 1.0f), light.first);
count++;
@@ -479,6 +606,8 @@ void Lightmapper::onDone()
gnd->makeLightmapBorders(browEdit);
gnd->cleanLightmaps();
gnd->cleanTiles();
+ lightmapChangeAction->setCurrentData(gnd->lightmaps, gnd->tiles);
+ map->doAction(lightmapChangeAction, browEdit);
map->rootNode->getComponent()->gndShadowDirty = true;
map->rootNode->getComponent()->setChunksDirty();
util::ResourceManager::clear();
diff --git a/browedit/Lightmapper.h b/browedit/Lightmapper.h
index 434fbbb..914e01e 100644
--- a/browedit/Lightmapper.h
+++ b/browedit/Lightmapper.h
@@ -10,14 +10,40 @@ class BrowEdit;
class Gnd;
class Rsw;
class Node;
+class RswModel;
+class RswModelCollider;
+class RswObject;
+class RswLight;
namespace math { class Ray; }
class Lightmapper
{
+public:
+ // For faster lookup, since using GetComponent gets slow if there are many models
+ struct light_model {
+ Node* node;
+ RswModel* rswModel;
+ RswModelCollider* collider;
+ };
+
+ struct light_quad_node {
+ std::vector models;
+ glm::vec2 range[2];
+ };
+
+ // For faster lookup, since using GetComponent gets slow if there are many lights
+ struct light_data {
+ Node* light;
+ RswObject* rswObject;
+ RswLight* rswLight;
+ };
+
+private:
BrowEdit* browEdit;
Gnd* gnd;
Rsw* rsw;
- std::vector lights;
- std::vector models;
+ std::vector> quadtree;
+ std::vector lights;
+ std::vector models;
glm::vec3 lightDirection;
std::thread mainThread;
@@ -32,8 +58,8 @@ class Lightmapper
void run();
void onDone();
- bool collidesMap(const math::Ray& ray, float maxDistance);
- std::pair calculateLight(const glm::vec3& groundPos, const glm::vec3& normal);
+ bool collidesMap(const math::Ray& ray, int cx, int cy, float maxDistance);
+ std::pair calculateLight(const glm::vec3& groundPos, const glm::vec3& normal, int cx, int cy);
void calcPos(int direction, int tileId, int x, int y);
void setProgressText(const std::string& text);
diff --git a/browedit/Map.cpp b/browedit/Map.cpp
index 70376f7..0ccf556 100644
--- a/browedit/Map.cpp
+++ b/browedit/Map.cpp
@@ -455,7 +455,7 @@ void Map::pasteSelection(BrowEdit* browEdit)
browEdit->newNodesCenter = center;
for (auto& n : browEdit->newNodes)
n.second = n.second - center;
- browEdit->newNodeHeight = false;
+ browEdit->newNodePlacement = BrowEdit::Ground;
}
}
catch (...) {
diff --git a/browedit/MapView.Objectmode.cpp b/browedit/MapView.Objectmode.cpp
index 3ee2bc5..fe0e470 100644
--- a/browedit/MapView.Objectmode.cpp
+++ b/browedit/MapView.Objectmode.cpp
@@ -237,30 +237,32 @@ void MapView::postRenderObjectMode(BrowEdit* browEdit)
lockedGizmo = !ImGui::GetIO().KeyCtrl;
}
- if (map->selectedNodes[0]->getComponent()) {
+ auto rotVector = map->selectedNodes[0]->getComponent() ? &map->selectedNodes[0]->getComponent()->rotation : nullptr;
+
+ if (rotVector) {
if (!lockedGizmo) {
if (map->selectedNodes.size() == 1 && gadget.mode == Gadget::Mode::Rotate) {
- mat = glm::rotate(mat, -glm::radians(map->selectedNodes[0]->getComponent()->rotation.z), glm::vec3(0, 0, 1));
- rotMat = glm::rotate(rotMat, -glm::radians(map->selectedNodes[0]->getComponent()->rotation.z), glm::vec3(0, 0, 1));
+ mat = glm::rotate(mat, -glm::radians(rotVector->z), glm::vec3(0, 0, 1));
+ rotMat = glm::rotate(rotMat, -glm::radians(rotVector->z), glm::vec3(0, 0, 1));
}
}
if (map->selectedNodes.size() == 1 && gadget.mode == Gadget::Mode::Scale) {
- mat = glm::rotate(mat, -glm::radians(map->selectedNodes[0]->getComponent()->rotation.z), glm::vec3(0, 0, 1));
- mat = glm::rotate(mat, -glm::radians(map->selectedNodes[0]->getComponent()->rotation.x), glm::vec3(1, 0, 0));
- mat = glm::rotate(mat, glm::radians(map->selectedNodes[0]->getComponent()->rotation.y), glm::vec3(0, 1, 0));
+ mat = glm::rotate(mat, -glm::radians(rotVector->z), glm::vec3(0, 0, 1));
+ mat = glm::rotate(mat, -glm::radians(rotVector->x), glm::vec3(1, 0, 0));
+ mat = glm::rotate(mat, glm::radians(rotVector->y), glm::vec3(0, 1, 0));
- rotMat = glm::rotate(rotMat, -glm::radians(map->selectedNodes[0]->getComponent()->rotation.z), glm::vec3(0, 0, 1));
- rotMat = glm::rotate(rotMat, glm::radians(map->selectedNodes[0]->getComponent()->rotation.x), glm::vec3(1, 0, 0));
- rotMat = glm::rotate(rotMat, glm::radians(map->selectedNodes[0]->getComponent()->rotation.y), glm::vec3(0, 1, 0));
+ rotMat = glm::rotate(rotMat, -glm::radians(rotVector->z), glm::vec3(0, 0, 1));
+ rotMat = glm::rotate(rotMat, glm::radians(rotVector->x), glm::vec3(1, 0, 0));
+ rotMat = glm::rotate(rotMat, glm::radians(rotVector->y), glm::vec3(0, 1, 0));
}
}
- gadget.draw(mouseRay, mat, !lockedGizmo && map->selectedNodes[0]->getComponent() ? -map->selectedNodes[0]->getComponent()->rotation.x : 0);
+ gadget.draw(mouseRay, mat, !lockedGizmo && map->selectedNodes[0]->getComponent() ? -rotVector->x : 0);
if (!lockedGizmo && map->selectedNodes[0]->getComponent() && map->selectedNodes.size() == 1 && gadget.mode == Gadget::Mode::Rotate) {
if (gadget.selectedAxis == Gadget::Axis::Y) {
- rotMat = glm::rotate(rotMat, glm::radians(map->selectedNodes[0]->getComponent()->rotation.x), glm::vec3(1, 0, 0));
+ rotMat = glm::rotate(rotMat, glm::radians(rotVector->x), glm::vec3(1, 0, 0));
}
}
@@ -676,6 +678,7 @@ void MapView::postRenderObjectMode(BrowEdit* browEdit)
{
static bool justPressed = false;
static glm::vec3 originalPosition;
+ static float originalHeightFromGround;
if (ImGui::IsMouseClicked(0))
{
auto collisions = map->rootNode->getCollisions(mouseRay);
@@ -709,6 +712,20 @@ void MapView::postRenderObjectMode(BrowEdit* browEdit)
{
originalPosition = map->selectedNodes[0]->getComponent()->position;
justPressed = true;
+
+ if (map->selectedNodes[0]->getComponent()) {
+ glm::vec3 wPosition(gnd->width * 5.0f + originalPosition.x, -originalPosition.y, gnd->height * 5.0f - originalPosition.z + 10.0f);
+
+ math::Ray ray(wPosition, glm::vec3(0, -1, 0));
+ auto rayCast = gnd->rayCast(ray, viewEmptyTiles);
+
+ if (rayCast != glm::vec3(std::numeric_limits().max())) {
+ originalHeightFromGround = rayCast.y - wPosition.y;
+ }
+ else {
+ originalHeightFromGround = 0;
+ }
+ }
}
}
@@ -726,7 +743,11 @@ void MapView::postRenderObjectMode(BrowEdit* browEdit)
auto rswObject = map->selectedNodes[0]->getComponent();
if (rswObject)
{
- rswObject->position = glm::vec3(rayCast.x - 5 * gnd->width, -rayCast.y, -(rayCast.z + (-10 - 5 * gnd->height)));
+ if (map->selectedNodes.size() == 1 && map->selectedNodes[0]->getComponent())
+ rswObject->position = glm::vec3(rayCast.x - 5 * gnd->width, -rayCast.y + originalHeightFromGround, -(rayCast.z + (-10 - 5 * gnd->height)));
+ else
+ rswObject->position = glm::vec3(rayCast.x - 5 * gnd->width, -rayCast.y, -(rayCast.z + (-10 - 5 * gnd->height)));
+
bool snap = snapToGrid;
if (ImGui::GetIO().KeyShift)
snap = !snap;
diff --git a/browedit/MapView.cpp b/browedit/MapView.cpp
index 7969908..985222d 100644
--- a/browedit/MapView.cpp
+++ b/browedit/MapView.cpp
@@ -503,9 +503,14 @@ void MapView::render(BrowEdit* browEdit)
}
rswObject->position = glm::vec3(rayCast.x - 5 * gnd->width, -rayCast.y, -(rayCast.z + (-10 - 5 * gnd->height))) + newNode.second;
- if (browEdit->newNodeHeight)
- {
- rswObject->position.y = newNode.second.y + browEdit->newNodesCenter.y;
+
+ switch (browEdit->newNodePlacement) {
+ case BrowEdit::Relative:
+ rswObject->position.y += newNode.second.y + browEdit->newNodesCenter.y;
+ break;
+ case BrowEdit::Absolute:
+ rswObject->position.y = newNode.second.y + browEdit->newNodesCenter.y;
+ break;
}
if (newNode.first->getComponent())
@@ -919,7 +924,7 @@ void MapView::drawLight(Node* n)
simpleShader->setUniform(SimpleShader::Uniforms::viewMatrix, nodeRenderContext.viewMatrix);
simpleShader->setUniform(SimpleShader::Uniforms::modelMatrix, mm);
simpleShader->setUniform(SimpleShader::Uniforms::textureFac, 0.0f);
- simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(rswLight->color, 0.05f));
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(rswLight->color, 0.10f));
glDepthMask(0);
glEnable(GL_BLEND);
@@ -929,13 +934,22 @@ void MapView::drawLight(Node* n)
float height = rswLight->range;
float width = rswLight->range * tan(glm::acos(1-rswLight->spotlightWidth));
-
for (float f = 0; f < 2 * glm::pi(); f += inc)
{
verts.push_back(VertexP3T2N3(glm::vec3(0, 0, 0), glm::vec2(0), glm::vec3(1.0f)));
verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f), width * glm::cos(f)), glm::vec2(0), glm::vec3(1.0f)));
verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f+inc), width * glm::cos(f+inc)), glm::vec2(0), glm::vec3(1.0f)));
}
+
+ if (showLightSphere) {
+ for (float f = 0; f < 2 * glm::pi(); f += inc)
+ {
+ verts.push_back(VertexP3T2N3(glm::vec3(height, 0, 0), glm::vec2(0), glm::vec3(1.0f)));
+ verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f), width * glm::cos(f)), glm::vec2(0), glm::vec3(1.0f)));
+ verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f + inc), width * glm::cos(f + inc)), glm::vec2(0), glm::vec3(1.0f)));
+ }
+ }
+
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
@@ -948,7 +962,30 @@ void MapView::drawLight(Node* n)
glDrawArrays(GL_TRIANGLES, 0, (int)verts.size());
+ if (!showLightSphere) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glLineWidth(1);
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(rswLight->color, 0.8f));
+ glDrawArrays(GL_TRIANGLES, 0, (int)verts.size());
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
glDepthMask(1);
+
+ if (!showLightSphere) {
+ simpleShader->setUniform(SimpleShader::Uniforms::color, glm::vec4(rswLight->color, 1.0f));
+ verts.clear();
+
+ for (float f = 0; f < 2 * glm::pi(); f += inc)
+ {
+ verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f), width * glm::cos(f)), glm::vec2(0), glm::vec3(1.0f)));
+ verts.push_back(VertexP3T2N3(glm::vec3(height, width * glm::sin(f + inc), width * glm::cos(f + inc)), glm::vec2(0), glm::vec3(1.0f)));
+ }
+
+ glLineWidth(2);
+ glDrawArrays(GL_LINES, 0, (int)verts.size());
+ }
+
glUseProgram(0);
}
}
diff --git a/browedit/MapView.h b/browedit/MapView.h
index e94448e..1f440bf 100644
--- a/browedit/MapView.h
+++ b/browedit/MapView.h
@@ -109,7 +109,7 @@ class MapView
float gadgetOpacity = 0.5f;
float gadgetScale = 1.0f;
- float gadgetThickness = 1.0f;
+ float gadgetThickness = 2.0f;
int quadTreeMaxLevel = 0;
@@ -206,7 +206,7 @@ class MapView
bool viewLightmapColor = true;
bool viewColors = true;
bool viewLighting = true;
- bool smoothColors = false;
+ bool smoothColors = true;
bool viewTextures = true;
bool viewEmptyTiles = true;
bool viewGat = false;
@@ -221,6 +221,9 @@ class MapView
bool viewWater = true;
bool viewEffectIcons = true;
+ bool enableLightQuickPreview = false;
+ bool hideOtherLightmaps = false;
+
void focusSelection();
void drawLight(Node* n);
};
\ No newline at end of file
diff --git a/browedit/actions/LightmapChangeAction.cpp b/browedit/actions/LightmapChangeAction.cpp
new file mode 100644
index 0000000..d247e77
--- /dev/null
+++ b/browedit/actions/LightmapChangeAction.cpp
@@ -0,0 +1,78 @@
+#include "LightmapChangeAction.h"
+
+#include
+#include
+#include
+#include
+
+LightmapChangeAction::LightmapChangeAction()
+{
+}
+
+LightmapChangeAction::~LightmapChangeAction()
+{
+ for (int i = 0; i < m_oldLightmaps.size(); i++)
+ delete m_oldLightmaps[i];
+
+ for (int i = 0; i < m_newLightmaps.size(); i++)
+ delete m_newLightmaps[i];
+}
+
+void LightmapChangeAction::setPreviousData(std::vector lightmaps, std::vector tiles)
+{
+ for (int i = 0; i < lightmaps.size(); i++)
+ m_oldLightmaps.push_back(new Gnd::Lightmap(*lightmaps[i]));
+ for (int i = 0; i < tiles.size(); i++)
+ m_oldLightmapIndex.push_back(tiles[i]->lightmapIndex);
+}
+
+void LightmapChangeAction::setCurrentData(std::vector lightmaps, std::vector tiles)
+{
+ for (int i = 0; i < lightmaps.size(); i++)
+ m_newLightmaps.push_back(new Gnd::Lightmap(*lightmaps[i]));
+ for (int i = 0; i < tiles.size(); i++)
+ m_newLightmapIndex.push_back(tiles[i]->lightmapIndex);
+}
+
+void LightmapChangeAction::perform(Map* map, BrowEdit* browEdit)
+{
+ Gnd* gnd = map->rootNode->getComponent();
+
+ for (int i = 0; i < gnd->lightmaps.size(); i++)
+ delete gnd->lightmaps[i];
+
+ gnd->lightmaps.clear();
+
+ for (int i = 0; i < m_newLightmaps.size(); i++)
+ gnd->lightmaps.push_back(new Gnd::Lightmap(*m_newLightmaps[i]));
+
+ for (int i = 0; i < m_newLightmapIndex.size(); i++)
+ gnd->tiles[i]->lightmapIndex = m_newLightmapIndex[i];
+
+ map->rootNode->getComponent()->gndShadowDirty = true;
+ map->rootNode->getComponent()->setChunksDirty();
+}
+
+void LightmapChangeAction::undo(Map* map, BrowEdit* browEdit)
+{
+ Gnd* gnd = map->rootNode->getComponent();
+
+ for (int i = 0; i < gnd->lightmaps.size(); i++)
+ delete gnd->lightmaps[i];
+
+ gnd->lightmaps.clear();
+
+ for (int i = 0; i < m_oldLightmaps.size(); i++)
+ gnd->lightmaps.push_back(new Gnd::Lightmap(*m_oldLightmaps[i]));
+
+ for (int i = 0; i < m_oldLightmapIndex.size(); i++)
+ gnd->tiles[i]->lightmapIndex = m_oldLightmapIndex[i];
+
+ map->rootNode->getComponent()->gndShadowDirty = true;
+ map->rootNode->getComponent()->setChunksDirty();
+}
+
+std::string LightmapChangeAction::str()
+{
+ return "Changed Lightmap";
+}
diff --git a/browedit/actions/LightmapChangeAction.h b/browedit/actions/LightmapChangeAction.h
new file mode 100644
index 0000000..5429f38
--- /dev/null
+++ b/browedit/actions/LightmapChangeAction.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Action.h"
+#include
+
+class LightmapChangeAction : public Action
+{
+ std::vector m_oldLightmaps;
+ std::vector m_newLightmaps;
+ std::vector m_oldLightmapIndex;
+ std::vector m_newLightmapIndex;
+public:
+ LightmapChangeAction();
+ ~LightmapChangeAction();
+
+ void setPreviousData(std::vector lightmaps, std::vector tiles);
+ void setCurrentData(std::vector lightmaps, std::vector tiles);
+
+ virtual void perform(Map* map, BrowEdit* browEdit) override;
+ virtual void undo(Map* map, BrowEdit* browEdit) override;
+
+ virtual std::string str() override;
+};
\ No newline at end of file
diff --git a/browedit/components/Gnd.cpp b/browedit/components/Gnd.cpp
index a5acfe0..894595a 100644
--- a/browedit/components/Gnd.cpp
+++ b/browedit/components/Gnd.cpp
@@ -489,7 +489,181 @@ void Gnd::save(const std::string& fileName, Rsw *rsw)
std::cout << "GND: Done saving GND" << std::endl;
}
+glm::vec3 Gnd::rayCastLightmap(const math::Ray& ray, int cx, int cy, int xMin, int yMin, int xMax, int yMax, float rayOffset)
+{
+ if (cubes.size() == 0)
+ return glm::vec3(std::numeric_limits::max());
+
+ if (xMax == -1)
+ xMax = (int)cubes.size();
+ if (yMax == -1)
+ yMax = (int)cubes[0].size();
+
+ xMin = glm::max(0, xMin);
+ yMin = glm::max(0, yMin);
+ xMax = glm::min(xMax, (int)cubes.size());
+ yMax = glm::min(yMax, (int)cubes[0].size());
+
+ float f = 0;
+
+ // Tokei:
+ // Some explanation for this...
+ // Instead of testing the collision from the ray against every gnd cube, the algorithm below
+ // draws a "2d" line, on the xz coordinate system. The line is the ray we're testing, and we simply
+ // move along the line until we reach the next gnd cube.
+ //
+ // For additional optimization, we also check for the Y position every time we hit a new cube.
+ // If the Y position is greater than any of the vertex of the cube (+ walls), then we skip the cube.
+ // The Y position is kept for the next cube check to avoid recalculating the values. This only works
+ // if the Y position goes up on the axis, which is (theoretically) always the case. It is very useful
+ // since maps tend to have many flat cubes and we can skip the majority of them.
+
+ // line function: y = xz_a * x + xz_b;
+ float xz_a = ray.dir.x == 0 ? 1 : ray.dir.z / ray.dir.x;
+ float xz_b = ray.origin.z - xz_a * ray.origin.x;
+ float xz_ray_length = glm::length(glm::vec2(ray.dir.x, ray.dir.z));
+ float y = 9999999999.0f;
+
+ glm::ivec2 dir(ray.dir.x < 0 ? -1 : (ray.dir.x > 0 ? 1 : 0), ray.dir.z < 0 ? 1 : (ray.dir.z > 0 ? -1 : 0));
+
+ // Cannot collide with another cube directly above itself.
+ if (dir[0] == 0 && dir[1] == 0)
+ return glm::vec3(std::numeric_limits::max());
+
+ // To test collision against the original cube; a tad annoying to handle
+ bool first = true;
+ int cxx, cyy, next_cx = 0;
+
+ while (cx >= xMin && cx < xMax && cy >= yMin && cy < yMax) {
+ if (first) {
+ cxx = cx;
+ cyy = cy;
+ }
+ else {
+ // Z axis only
+ if (ray.dir.x == 0) {
+ next_cx = cx;
+ }
+ else {
+ float next_x;
+
+ if (ray.dir.z > 0)
+ next_x = ((height - cy + 1) * 10.0f - xz_b) / xz_a;
+ else
+ next_x = ((height - cy) * 10.0f - xz_b) / xz_a;
+
+ next_cx = (int)(next_x / 10);
+ }
+
+ // If we don't move along the X axis, then move along the Z axis.
+ // cxx/cyy is the next cube to check for a collision.
+ cxx = cx + (next_cx != cx ? dir[0] : 0);
+ cyy = cy + (next_cx == cx ? dir[1] : 0);
+ if ((cxx == cx && cyy == cy) || cxx < xMin || cxx >= xMax || cyy < yMin || cyy >= yMax)
+ break;
+ }
+
+ Gnd::Cube* cube = cubes[cxx][cyy];
+
+ if ((cube->tileUp != -1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h1, glm::min(cube->h2, glm::min(cube->h3, cube->h4))))))
+ || (cube->tileSide != -1 && cxx < width - 1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h2, glm::min(cube->h4, glm::min(cubes[cxx + 1][cyy]->h1, cubes[cxx + 1][cyy]->h3))))))
+ || (cube->tileFront != -1 && cyy < height - 1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h3, glm::min(cube->h4, glm::min(cubes[cxx][cyy + 1]->h2, cubes[cxx][cyy + 1]->h1))))))
+ ) {
+ float xx, zz;
+
+ if (!first) {
+ if (next_cx == cx) {
+ zz = (height - cy + 1 + ((dir[1] + 1) >> 1)) * 10.0f;
+ xx = (zz - xz_b) / xz_a;
+ }
+ else {
+ xx = (cx + ((dir[0] + 1) >> 1)) * 10.0f;
+ zz = xz_a * xx + xz_b;
+ }
+
+ y = -ray.dir.y * (glm::length(glm::vec2(xx, zz) - glm::vec2(ray.origin.x, ray.origin.z)) / xz_ray_length) - ray.origin.y;
+ }
+ else {
+ y = -ray.origin.y;
+ }
+ }
+ else {
+ // y value went above the next cube vertices, skip
+ cx = cxx;
+ cy = cyy;
+ first = false;
+ continue;
+ }
+
+ cx = cxx;
+ cy = cyy;
+ first = false;
+
+ if (cube->tileUp != -1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h1, glm::min(cube->h2, glm::min(cube->h3, cube->h4))))))
+ {
+ glm::vec3 v1(10 * cx, -cube->h3, 10 * height - 10 * cy);
+ glm::vec3 v2(10 * cx + 10, -cube->h4, 10 * height - 10 * cy);
+ glm::vec3 v3(10 * cx, -cube->h1, 10 * height - 10 * cy + 10);
+ glm::vec3 v4(10 * cx + 10, -cube->h2, 10 * height - 10 * cy + 10);
+
+ {
+ std::vector v{ v4, v2, v1 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ {
+ std::vector v{ v4, v1, v3 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ }
+ if (cube->tileSide != -1 && cx < width - 1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h2, glm::min(cube->h4, glm::min(cubes[cx + 1][cy]->h1, cubes[cx + 1][cy]->h3))))))
+ {
+ glm::vec3 v1(10 * cx + 10, -cube->h2, 10 * height - 10 * cy + 10);
+ glm::vec3 v2(10 * cx + 10, -cube->h4, 10 * height - 10 * cy);
+ glm::vec3 v3(10 * cx + 10, -cubes[cx + 1][cy]->h1, 10 * height - 10 * cy + 10);
+ glm::vec3 v4(10 * cx + 10, -cubes[cx + 1][cy]->h3, 10 * height - 10 * cy);
+
+ {
+ std::vector v{ v4, v2, v1 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ {
+ std::vector v{ v4, v1, v3 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ }
+ if (cube->tileFront != -1 && cy < height - 1 && (ray.dir.y <= 0 || (y >= glm::min(cube->h3, glm::min(cube->h4, glm::min(cubes[cx][cy + 1]->h2, cubes[cx][cy + 1]->h1))))))
+ {
+ glm::vec3 v1(10 * cx, -cube->h3, 10 * height - 10 * cy);
+ glm::vec3 v2(10 * cx + 10, -cube->h4, 10 * height - 10 * cy);
+ glm::vec3 v4(10 * cx + 10, -cubes[cx][cy + 1]->h2, 10 * height - 10 * cy);
+ glm::vec3 v3(10 * cx, -cubes[cx][cy + 1]->h1, 10 * height - 10 * cy);
+
+ {
+ std::vector v{ v4, v2, v1 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ {
+ std::vector v{ v4, v1, v3 };
+ if (ray.LineIntersectPolygon(v, f))
+ if (f >= rayOffset)
+ return ray.origin + f * ray.dir;
+ }
+ }
+ }
+
+ return glm::vec3(std::numeric_limits::max());
+}
glm::vec3 Gnd::rayCast(const math::Ray& ray, bool emptyTiles, int xMin, int yMin, int xMax, int yMax, float rayOffset)
{
@@ -603,7 +777,7 @@ glm::vec3 Gnd::rayCast(const math::Ray& ray, bool emptyTiles, int xMin, int yMin
void Gnd::makeLightmapsUnique()
{
makeTilesUnique();
- cleanLightmaps();
+ cleanLightmaps();
std::set taken;
for (Tile* t : tiles)
{
@@ -927,49 +1101,52 @@ void Gnd::makeLightmapBorders(BrowEdit* browEdit)
node->getComponent()->gndShadowDirty = true;
}
-
void Gnd::cleanLightmaps()
{
- std::cout<< "Lightmap cleanup, starting with " << lightmaps.size() << " lightmaps" <> lookup;
-
+ std::cout << "Lightmap cleanup, starting with " << lightmaps.size() << " lightmaps" << std::endl;
+
+ std::map> light2lightmapIndex;
+ std::map oldIndex2newIndex;
+ std::vector newLightmaps;
+
for (int i = 0; i < (int)lightmaps.size(); i++)
{
- unsigned char hash = lightmaps[i]->hash();
+ unsigned int hash = lightmaps[i]->hash();
bool found = false;
- if (lookup.find(hash) != lookup.end())
- {
- for (const auto ii : lookup[hash])
- {
- if ((*lightmaps[i]) == (*lightmaps[ii]))
- {// if it is found
- assert(i > (int)ii);
- //change all tiles with lightmap i to ii
- for (auto tile : tiles)
- if (tile->lightmapIndex == i)
- tile->lightmapIndex = (unsigned short)ii;
- else if (tile->lightmapIndex > i)
- tile->lightmapIndex--;
- //remove lightmap i
- delete lightmaps[i];
- lightmaps.erase(lightmaps.begin() + i);
- i--;
+
+ if (light2lightmapIndex.find(hash) != light2lightmapIndex.end()) {
+ for (const auto ii : light2lightmapIndex[hash]) {
+ if ((*lightmaps[i]) == (*lightmaps[ii])) {
+ oldIndex2newIndex[i] = oldIndex2newIndex[ii];
found = true;
break;
}
}
}
- if (!found)
- {
- lookup[hash].push_back(i);
+
+ if (!found) {
+ light2lightmapIndex[hash].push_back(i);
+ oldIndex2newIndex[i] = (unsigned short)newLightmaps.size();
+ newLightmaps.push_back(new Lightmap(*lightmaps[i]));
}
-
+ }
+
+ for (int i = 0; i < (int)lightmaps.size(); i++)
+ delete lightmaps[i];
+
+ lightmaps.clear();
+
+ for (int i = 0; i < (int)newLightmaps.size(); i++) {
+ lightmaps.push_back(newLightmaps[i]);
+ }
+
+ for (auto tile : tiles) {
+ tile->lightmapIndex = oldIndex2newIndex[tile->lightmapIndex];
}
std::cout<< "Lightmap cleanup, ending with " << lightmaps.size() << " lightmaps" << std::endl;
node->getComponent()->setChunksDirty();
node->getComponent()->gndShadowDirty = true;
-
}
@@ -1367,14 +1544,17 @@ std::vector Gnd::getMapQuads()
return quads;
}
-const unsigned char Gnd::Lightmap::hash() const //actually a crc, but will work
+const unsigned int Gnd::Lightmap::hash() const
{
#define POLY 0x82f63b78
- unsigned char crc = ~0;
- for (int i = 0; i < gnd->lightmapWidth*gnd->lightmapHeight*4; i++) {
+ const int precision = 8;
+ unsigned int crc = ~0;
+ int size = gnd->lightmapWidth * gnd->lightmapHeight * 4;
+ if (size < 4)
+ return 0;
+ for (int i = 0; i < size; i++) {
crc ^= data[i];
- for (int k = 0; k < 8; k++)
- crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+ crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
}
return ~crc;
}
diff --git a/browedit/components/Gnd.h b/browedit/components/Gnd.h
index 4ecd840..6fea910 100644
--- a/browedit/components/Gnd.h
+++ b/browedit/components/Gnd.h
@@ -25,6 +25,7 @@ class Gnd : public Component, public ImguiProps
~Gnd();
void save(const std::string &fileName, Rsw* rsw);
glm::vec3 rayCast(const math::Ray& ray, bool emptyTiles = false, int xMin = 0, int yMin = 0, int xMax = -1, int yMax = -1, float offset = 0.0f);
+ glm::vec3 rayCastLightmap(const math::Ray& ray, int cx, int cy, int xMin = 0, int yMin = 0, int xMax = -1, int yMax = -1, float offset = 0.0f);
void makeLightmapsUnique();
void makeLightmapsClear();
void makeLightmapBorders(BrowEdit* browEdit);
@@ -128,7 +129,7 @@ class Gnd : public Component, public ImguiProps
unsigned char* data;
Gnd* gnd;
void expandBorders();
- const unsigned char hash() const;
+ const unsigned int hash() const;
bool operator == (const Lightmap& other) const;
LightmapRow operator [] (int x) { return LightmapRow{ this, x }; }
friend void to_json(nlohmann::json& nlohmann_json_j, const Lightmap& nlohmann_json_t) {
diff --git a/browedit/components/GndRenderer.cpp b/browedit/components/GndRenderer.cpp
index a91a7fb..d6714df 100644
--- a/browedit/components/GndRenderer.cpp
+++ b/browedit/components/GndRenderer.cpp
@@ -140,6 +140,31 @@ void GndRenderer::render()
//shader->setUniform(GndShader::Uniforms::fogExp, rsw->fog.factor);
shader->setUniform(GndShader::Uniforms::fogColor, rsw->fog.color);
+ if (quickRenderLightNode != nullptr) {
+ auto rswLight = quickRenderLightNode->getComponent();
+ auto rswObject = quickRenderLightNode->getComponent();
+
+ shader->setUniform(GndShader::Uniforms::hideOtherLights, quickRenderLight_hideOthers);
+ shader->setUniform(GndShader::Uniforms::light_position, glm::vec3(gnd->width * 5.0f + rswObject->position.x, -rswObject->position.y, gnd->height * 5.0f + 10.0f - rswObject->position.z));
+ shader->setUniform(GndShader::Uniforms::light_color, rswLight->color);
+ shader->setUniform(GndShader::Uniforms::lightCount, 1);
+ shader->setUniform(GndShader::Uniforms::light_type, (int)rswLight->lightType);
+ shader->setUniform(GndShader::Uniforms::light_falloff_style, (int)rswLight->falloffStyle);
+ shader->setUniform(GndShader::Uniforms::light_range, rswLight->range);
+ shader->setUniform(GndShader::Uniforms::light_intensity, rswLight->intensity);
+ shader->setUniform(GndShader::Uniforms::light_cutoff, rswLight->cutOff);
+ shader->setUniform(GndShader::Uniforms::light_diffuseLighting, rswLight->diffuseLighting);
+ shader->setUniform(GndShader::Uniforms::light_direction, rswLight->direction);
+ shader->setUniform(GndShader::Uniforms::light_width, rswLight->spotlightWidth);
+ shader->setUniform(GndShader::Uniforms::light_falloff_count, glm::min(10, (int)rswLight->falloff.size()));
+
+ for (int i = 0; i < rswLight->falloff.size() && i < 10; i++) {
+ shader->setUniform(GndShader::Uniforms::light_falloff_0 + i, rswLight->falloff[i]);
+ }
+ }
+ else {
+ shader->setUniform(GndShader::Uniforms::lightCount, 0);
+ }
for (auto r : chunks)
diff --git a/browedit/components/GndRenderer.h b/browedit/components/GndRenderer.h
index 6c3f429..f78a831 100644
--- a/browedit/components/GndRenderer.h
+++ b/browedit/components/GndRenderer.h
@@ -89,6 +89,8 @@ class GndRenderer : public Renderer
bool smoothColors = false;
bool viewTextures = true;
bool viewFog = true;
+ bool quickRenderLight_hideOthers = false;
+ Node* quickRenderLightNode = nullptr;
bool viewEmptyTiles = true;
};
\ No newline at end of file
diff --git a/browedit/components/Rsw.Light.cpp b/browedit/components/Rsw.Light.cpp
index 97bf843..cf2f42d 100644
--- a/browedit/components/Rsw.Light.cpp
+++ b/browedit/components/Rsw.Light.cpp
@@ -2,6 +2,8 @@
#include "Rsw.h"
#include
#include
+#include
+#include
#include
#include
@@ -181,6 +183,29 @@ void RswLight::buildImGuiMulti(BrowEdit* browEdit, const std::vector& nod
}
util::CheckboxMulti(browEdit, browEdit->activeMapView->map, rswLights, "Enabled", [](RswLight* l) { return &l->enabled; });
+ if (ImGui::Checkbox("Quick preview", &browEdit->activeMapView->enableLightQuickPreview)) {
+ if (!browEdit->activeMapView->enableLightQuickPreview) {
+ auto gndRenderer = browEdit->activeMapView->map->rootNode->getComponent();
+
+ if (gndRenderer)
+ gndRenderer->quickRenderLightNode = nullptr;
+ }
+ }
+
+ if (browEdit->activeMapView->enableLightQuickPreview) {
+ auto gndRenderer = browEdit->activeMapView->map->rootNode->getComponent();
+
+ if (gndRenderer) {
+ if (browEdit->activeMapView->enableLightQuickPreview) {
+ gndRenderer->quickRenderLightNode = rswLights[0]->node;
+ gndRenderer->quickRenderLight_hideOthers = browEdit->activeMapView->hideOtherLightmaps;
+ }
+ }
+ }
+
+ ImGui::SameLine();
+ ImGui::Checkbox("Hide other lightmaps", &browEdit->activeMapView->hideOtherLightmaps);
+
util::ColorEdit3Multi(browEdit, browEdit->activeMapView->map, rswLights, "Color", [](RswLight* l) { return &l->color; });
if(rswLights.front()->lightType != RswLight::Type::Sun)
util::DragFloatMulti(browEdit, browEdit->activeMapView->map, rswLights, "Range", [](RswLight* l) { return &l->range; }, 1.0f, 0.0f, 1000.0f);
@@ -208,7 +233,7 @@ void RswLight::buildImGuiMulti(BrowEdit* browEdit, const std::vector& nod
}
if (rswLights.front()->lightType != RswLight::Type::Sun)
{
- util::ComboBoxMulti(browEdit, browEdit->activeMapView->map, rswLights, "Falloff style", "exponential\0spline tweak\0lagrange tweak\0linear tweak\0magic\0", [](RswLight* l) { return (int*) & l->falloffStyle; });
+ util::ComboBoxMulti(browEdit, browEdit->activeMapView->map, rswLights, "Falloff style", "exponential\0spline tweak\0lagrange tweak\0linear tweak\0magic\0s curve\0", [](RswLight* l) { return (int*) & l->falloffStyle; });
differentValues = !std::all_of(rswLights.begin(), rswLights.end(), [&](RswLight* o) { return o->falloffStyle == rswLights.front()->falloffStyle; });
if (!differentValues)
@@ -226,6 +251,8 @@ void RswLight::buildImGuiMulti(BrowEdit* browEdit, const std::vector& nod
util::EditableGraphMulti(browEdit, browEdit->activeMapView->map, rswLights, "Light Falloff", [](RswLight* l) {return &l->falloff; }, util::interpolateLagrange);
else if (falloffStyle == FalloffStyle::LinearTweak)
util::EditableGraphMulti(browEdit, browEdit->activeMapView->map, rswLights, "Light Falloff", [](RswLight* l) {return &l->falloff; }, util::interpolateLinear);
+ else if (falloffStyle == FalloffStyle::S_Curve)
+ util::Graph("Light Falloff", [&](float x) { return util::interpolateSCurve(x) / 2.0f; });
else if (falloffStyle == FalloffStyle::Exponential)
{
bool differentFalloff = !std::all_of(rswLights.begin(), rswLights.end(), [&](RswLight* o) { return o->falloffStyle == rswLights.front()->falloffStyle; });
diff --git a/browedit/components/Rsw.cpp b/browedit/components/Rsw.cpp
index 33ed826..2188409 100644
--- a/browedit/components/Rsw.cpp
+++ b/browedit/components/Rsw.cpp
@@ -1114,6 +1114,9 @@ std::vector RswModelCollider::getCollisions(Rsm::Mesh* mesh, const ma
return ret;
}
+double debug5_start[20];
+double debug5_stop[20];
+
bool RswModelCollider::collidesTexture(const math::Ray& ray, float minDistance, float maxDistance)
{
std::vector ret;
@@ -1124,33 +1127,91 @@ bool RswModelCollider::collidesTexture(const math::Ray& ray, float minDistance,
rsm = node->getComponent();
if (!rsmRenderer)
rsmRenderer = node->getComponent();
+
if (!rswModel || !rsm || !rsmRenderer)
return false;
if (!rswModel->aabb.hasRayCollision(ray, 0, 10000000))
return false;
- return collidesTexture(rsm->rootMesh, ray, rsmRenderer->matrixCache, minDistance, maxDistance);
+
+ auto res = collidesTexture(rsm->rootMesh, ray, rsmRenderer->matrixCache, minDistance, maxDistance);
+ return res;
+}
+
+// Buffers all the face coordinates to their rendered position
+void RswModelCollider::calculateWorldFaces()
+{
+ buffered_faces.clear();
+
+ if (!rswModel)
+ rswModel = node->getComponent();
+ if (!rsm)
+ rsm = node->getComponent();
+ if (!rsmRenderer)
+ rsmRenderer = node->getComponent();
+
+ if (!rswModel || !rsm || !rsmRenderer)
+ return;
+
+ calculateWorldFaces(rsm->rootMesh, rsmRenderer->matrixCache);
}
+void RswModelCollider::calculateWorldFaces(Rsm::Mesh* mesh, const glm::mat4& matrix) {
+ if (!mesh || mesh->index >= rsmRenderer->renderInfo.size())
+ return;
+
+ glm::mat4 newMatrix = matrix * rsmRenderer->renderInfo[mesh->index].matrix;
+
+ if (buffered_faces.size() < rsmRenderer->renderInfo.size())
+ buffered_faces.resize(rsmRenderer->renderInfo.size());
+
+ std::vector>* faces = &buffered_faces[mesh->index];
+
+ if (buffered_faces[mesh->index].size() == 0 && mesh->faces.size() > 0) {
+ (*faces).resize(mesh->faces.size());
+
+ for (size_t i = 0; i < mesh->faces.size(); i++)
+ {
+ for (size_t ii = 0; ii < 3; ii++)
+ (*faces)[i].push_back(newMatrix * glm::vec4(mesh->vertices[mesh->faces[i].vertexIds[ii]], 1.0f));
+ }
+ }
+
+ for (size_t i = 0; i < mesh->children.size(); i++)
+ calculateWorldFaces(mesh->children[i], matrix);
+}
bool RswModelCollider::collidesTexture(Rsm::Mesh* mesh, const math::Ray& ray, const glm::mat4& matrix, float minDistance, float maxDistance)
{
if (!mesh || mesh->index >= rsmRenderer->renderInfo.size())
return false;
- std::vector ret;
glm::mat4 newMatrix = matrix * rsmRenderer->renderInfo[mesh->index].matrix;
- //math::Ray newRay(ray * glm::inverse(newMatrix)); //would rather work with the inversed ray here, but that doesn't work for scaled models
- std::vector verts;
- verts.resize(3);
+ if (buffered_faces.size() < rsmRenderer->renderInfo.size())
+ buffered_faces.resize(rsmRenderer->renderInfo.size());
+
+ std::vector> *faces = &buffered_faces[mesh->index];
+
+ if (buffered_faces[mesh->index].size() == 0 && mesh->faces.size() > 0) {
+ (*faces).resize(mesh->faces.size());
+
+ for (size_t i = 0; i < mesh->faces.size(); i++)
+ {
+ for (size_t ii = 0; ii < 3; ii++)
+ (*faces)[i].push_back(newMatrix * glm::vec4(mesh->vertices[mesh->faces[i].vertexIds[ii]], 1.0f));
+ }
+ }
+
float t;
+
+ std::vector* verts;
+
for (size_t i = 0; i < mesh->faces.size(); i++)
{
- for (size_t ii = 0; ii < 3; ii++)
- verts[ii] = newMatrix * glm::vec4(mesh->vertices[mesh->faces[i].vertexIds[ii]],1.0f);
-
- if (ray.LineIntersectPolygon(verts, t) && t > 0)
+ verts = &(*faces)[i];
+
+ if (ray.LineIntersectPolygon(*verts, t) && t > 0)
{
Image* img = nullptr;
auto rsmMesh = dynamic_cast(mesh);
@@ -1165,11 +1226,11 @@ bool RswModelCollider::collidesTexture(Rsm::Mesh* mesh, const math::Ray& ray, co
{
if (glm::distance(hitPoint, ray.origin) >= minDistance && maxDistance - t > 0)
{
- auto f1 = verts[0] - hitPoint;
- auto f2 = verts[1] - hitPoint;
- auto f3 = verts[2] - hitPoint;
+ auto f1 = (*verts)[0] - hitPoint;
+ auto f2 = (*verts)[1] - hitPoint;
+ auto f3 = (*verts)[2] - hitPoint;
- float a = glm::length(glm::cross(verts[0] - verts[1], verts[0] - verts[2]));
+ float a = glm::length(glm::cross((*verts)[0] - (*verts)[1], (*verts)[0] - (*verts)[2]));
float a1 = glm::length(glm::cross(f2, f3)) / a;
float a2 = glm::length(glm::cross(f3, f1)) / a;
float a3 = glm::length(glm::cross(f1, f2)) / a;
@@ -1195,9 +1256,8 @@ bool RswModelCollider::collidesTexture(Rsm::Mesh* mesh, const math::Ray& ray, co
return true;
}
}
- else if(glm::distance(hitPoint, ray.origin) >= minDistance && maxDistance - t > 0) //remove the if condition here????
+ else if (glm::distance(hitPoint, ray.origin) >= minDistance && maxDistance - t > 0) //remove the if condition here????
return true;
-
}
}
diff --git a/browedit/components/Rsw.h b/browedit/components/Rsw.h
index 89db5fb..af485bc 100644
--- a/browedit/components/Rsw.h
+++ b/browedit/components/Rsw.h
@@ -133,6 +133,7 @@ class Rsw : public Component, public ImguiProps
int quality = 1;
bool shadows = true;
bool heightSelectionOnly = false;
+ bool additiveShadow = true;
glm::ivec2 rangeX;
glm::ivec2 rangeY;
@@ -216,8 +217,8 @@ class RswLight : public Component
Spot,
Sun
} lightType = Type::Point;
- bool givesShadow = true;
- bool affectShadowMap = true;
+ bool givesShadow = false;
+ bool affectShadowMap = false;
bool affectLightmap = true;
bool enabled = true;
bool shadowTerrain = true;
@@ -233,8 +234,9 @@ class RswLight : public Component
LagrangeTweak = 2,
LinearTweak = 3,
Magic = 4,
+ S_Curve = 5,
};
- FalloffStyle falloffStyle = FalloffStyle::Exponential;
+ FalloffStyle falloffStyle = FalloffStyle::S_Curve;
float cutOff = 0.5f;
float intensity = 1;
@@ -249,7 +251,7 @@ class RswLight : public Component
void save(std::ofstream& file);
nlohmann::json saveExtra();
static void buildImGuiMulti(BrowEdit* browEdit, const std::vector&);
- NLOHMANN_DEFINE_TYPE_INTRUSIVE(RswLight, color, enabled, lightType, spotlightWidth, sunMatchRswDirection, direction, range, givesShadow, cutOff, cutOff, intensity, affectShadowMap, affectLightmap, falloff, falloffStyle, shadowTerrain);
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(RswLight, color, enabled, lightType, spotlightWidth, sunMatchRswDirection, direction, range, givesShadow, cutOff, cutOff, intensity, minShadowDistance, affectShadowMap, affectLightmap, falloff, falloffStyle, shadowTerrain, diffuseLighting);
};
class LubEffect : public Component
@@ -321,6 +323,7 @@ class RswSound : public Component
class RswModelCollider : public Collider
{
+ std::vector>> buffered_faces;
RswModel* rswModel = nullptr;
Rsm* rsm = nullptr;
RsmRenderer* rsmRenderer = nullptr;
@@ -328,6 +331,8 @@ class RswModelCollider : public Collider
void begin();
bool collidesTexture(const math::Ray& ray, float minDistance = 0.0f, float maxDistance = 9999999.0f);
bool collidesTexture(Rsm::Mesh* mesh, const math::Ray& ray, const glm::mat4& matrix, float minDistance, float maxDistance);
+ void calculateWorldFaces(Rsm::Mesh* mesh, const glm::mat4& matrix);
+ void calculateWorldFaces();
std::vector getVerticesWorldSpace(Rsm::Mesh* mesh = nullptr, const glm::mat4& matrix = glm::mat4(1.0f));
std::vector getAllVerticesWorldSpace(Rsm::Mesh* mesh = nullptr, const glm::mat4& matrix = glm::mat4(1.0f));
diff --git a/browedit/gl/Shader.h b/browedit/gl/Shader.h
index 7f9e001..383ac7f 100644
--- a/browedit/gl/Shader.h
+++ b/browedit/gl/Shader.h
@@ -9,9 +9,10 @@ namespace gl
class Shader
{
std::string file;
- GLuint programId;
int* uniforms;
public:
+ GLuint programId;
+
Shader(const std::string& file, int end);
~Shader();
void setUniform(int name, bool value);
diff --git a/browedit/math/Ray.cpp b/browedit/math/Ray.cpp
index e9bbc55..b0ee317 100644
--- a/browedit/math/Ray.cpp
+++ b/browedit/math/Ray.cpp
@@ -39,35 +39,38 @@ namespace math
bool Ray::LineIntersectPolygon(const std::span &vertices, float &t) const
{
- Plane plane;
- plane.normal = glm::cross(vertices[1] - vertices[0], vertices[2] - vertices[0]);
- if (glm::length(plane.normal) < 0.000001f)
+ // Möller–Trumbore intersection algorithm
+ constexpr float epsilon = 0.001f;
+
+ glm::vec3 edge1 = vertices[1] - vertices[0];
+ glm::vec3 edge2 = vertices[2] - vertices[0];
+ glm::vec3 ray_cross_e2 = glm::cross(dir, edge2);
+ float det = glm::dot(edge1, ray_cross_e2);
+
+ if (det > -epsilon && det < epsilon)
return false;
- plane.normal = glm::normalize(plane.normal);
- plane.D = -glm::dot(plane.normal, vertices[0]);
- float tt;
- if (!planeIntersection(plane, tt))
+ float inv_det = 1.0f / det;
+ glm::vec3 s = origin - vertices[0];
+ float u = inv_det * glm::dot(s, ray_cross_e2);
+
+ if ((u < 0 && abs(u) > epsilon) || (u > 1 && abs(u - 1) > epsilon))
return false;
- glm::vec3 intersection = origin + dir * tt;
+ glm::vec3 s_cross_e1 = glm::cross(s, edge1);
+ float v = inv_det * glm::dot(dir, s_cross_e1);
- /* if (Intersection == EndLine)
- return false;*/
- for (size_t vertex = 0; vertex < vertices.size(); vertex++)
- {
- Plane edgePlane;
- int NextVertex = (vertex + 1) % (int)vertices.size();
- glm::vec3 EdgeVector = vertices[NextVertex] - vertices[vertex];
- edgePlane.normal = glm::normalize(glm::cross(EdgeVector, plane.normal));
- edgePlane.D = -glm::dot(edgePlane.normal, vertices[vertex]);
-
- if (glm::dot(edgePlane.normal, intersection) + edgePlane.D > 0.001f)
- return false;
+ if ((v < 0 && abs(v) > epsilon) || (u + v > 1 && abs(u + v - 1) > epsilon))
+ return false;
+
+ float tt = inv_det * glm::dot(edge2, s_cross_e1);
+
+ if (tt > epsilon) {
+ t = tt;
+ return true;
}
- t = tt;
- return true;
+ return false;
}
diff --git a/browedit/shaders/GndShader.h b/browedit/shaders/GndShader.h
index e371962..ab26acc 100644
--- a/browedit/shaders/GndShader.h
+++ b/browedit/shaders/GndShader.h
@@ -27,6 +27,29 @@ class GndShader : public gl::Shader
fogNear,
fogFar,
//fogExp,
+ lightCount,
+ light_position,
+ light_color,
+ light_type,
+ light_falloff_style,
+ light_range,
+ light_intensity,
+ light_cutoff,
+ light_diffuseLighting,
+ light_direction,
+ light_width,
+ light_falloff_0,
+ light_falloff_1,
+ light_falloff_2,
+ light_falloff_3,
+ light_falloff_4,
+ light_falloff_5,
+ light_falloff_6,
+ light_falloff_7,
+ light_falloff_8,
+ light_falloff_9,
+ light_falloff_count,
+ hideOtherLights,
End
};
};
@@ -50,5 +73,28 @@ class GndShader : public gl::Shader
bindUniform(Uniforms::fogNear, "fogNear");
bindUniform(Uniforms::fogFar, "fogFar");
//bindUniform(Uniforms::fogExp, "fogExp");
+ bindUniform(Uniforms::lightCount, "lightCount");
+ bindUniform(Uniforms::light_position, "lights[0].position");
+ bindUniform(Uniforms::light_color, "lights[0].color");
+ bindUniform(Uniforms::light_type, "lights[0].type");
+ bindUniform(Uniforms::light_falloff_style, "lights[0].falloff_style");
+ bindUniform(Uniforms::light_range, "lights[0].range");
+ bindUniform(Uniforms::light_intensity, "lights[0].intensity");
+ bindUniform(Uniforms::light_cutoff, "lights[0].cutoff");
+ bindUniform(Uniforms::light_diffuseLighting, "lights[0].diffuseLighting");
+ bindUniform(Uniforms::light_direction, "lights[0].direction");
+ bindUniform(Uniforms::light_width, "lights[0].width");
+ bindUniform(Uniforms::light_falloff_0, "lights[0].falloff[0]");
+ bindUniform(Uniforms::light_falloff_1, "lights[0].falloff[1]");
+ bindUniform(Uniforms::light_falloff_2, "lights[0].falloff[2]");
+ bindUniform(Uniforms::light_falloff_3, "lights[0].falloff[3]");
+ bindUniform(Uniforms::light_falloff_4, "lights[0].falloff[4]");
+ bindUniform(Uniforms::light_falloff_5, "lights[0].falloff[5]");
+ bindUniform(Uniforms::light_falloff_6, "lights[0].falloff[6]");
+ bindUniform(Uniforms::light_falloff_7, "lights[0].falloff[7]");
+ bindUniform(Uniforms::light_falloff_8, "lights[0].falloff[8]");
+ bindUniform(Uniforms::light_falloff_9, "lights[0].falloff[9]");
+ bindUniform(Uniforms::light_falloff_count, "lights[0].falloff_count");
+ bindUniform(Uniforms::hideOtherLights, "hideOtherLights");
}
};
\ No newline at end of file
diff --git a/browedit/util/Util.cpp b/browedit/util/Util.cpp
index 0116089..1e24b48 100644
--- a/browedit/util/Util.cpp
+++ b/browedit/util/Util.cpp
@@ -1434,6 +1434,21 @@ namespace util
float diff = (x - before.x) / (after.x - before.x);
return before.y + diff * (after.y - before.y);
}
+ float interpolateSCurve(float x) {
+ // Alright, this isn't an S Curve formula anymore, but your typical light attenuation formula... but with the shape of an S Curve.
+ x = 2 * x - 1;
+ const float a = 1.5f;
+ const float b = 1.5f;
+
+ if (x <= -1)
+ return 2;
+ if (x >= 1)
+ return 0;
+ if (x <= 0)
+ return -1.0f / (1.0f + a * -x + b * x * x) * (1.0f + x) + 2;
+
+ return 1.0f / (1.0f + a * x + b * x * x) * (1.0f - x);
+ }
bool EditableGraph(const char* label, std::vector* points, std::function&, float)> interpolationStyle, bool& activated)
{
diff --git a/browedit/util/Util.h b/browedit/util/Util.h
index b726bed..53a5678 100644
--- a/browedit/util/Util.h
+++ b/browedit/util/Util.h
@@ -92,6 +92,7 @@ namespace util
float interpolateLagrange(const std::vector& f, float x);
float interpolateSpline(const std::vector& f, float x);
float interpolateLinear(const std::vector& f, float x);
+ float interpolateSCurve(float x);
bool EditableGraph(const char* label, std::vector* points, std::function&, float)> interpolationStyle, bool& activated);
void Graph(const char* label, std::function func);
diff --git a/browedit/windows/LightmapSettingsWindow.cpp b/browedit/windows/LightmapSettingsWindow.cpp
index 9c6867f..44c9bc6 100644
--- a/browedit/windows/LightmapSettingsWindow.cpp
+++ b/browedit/windows/LightmapSettingsWindow.cpp
@@ -25,6 +25,7 @@ void BrowEdit::showLightmapSettingsWindow()
ImGui::Checkbox("Shadows", &settings.shadows);
ImGui::Checkbox("Debug Points", &lightmapper->buildDebugPoints);
ImGui::Checkbox("Height Edit Mode Selection Only", &settings.heightSelectionOnly);
+ ImGui::Checkbox("Old shadow formula (v3.561 and below)", &settings.additiveShadow);
ImGui::DragInt2("Generate Range X", glm::value_ptr(settings.rangeX), 1, 0, gnd->width);
ImGui::DragInt2("Generate Range Y", glm::value_ptr(settings.rangeY), 1, 0, gnd->height);
ImGui::DragInt("Thread Count", &config.lightmapperThreadCount, 1, 1, 32);
diff --git a/browedit/windows/MenuBar.cpp b/browedit/windows/MenuBar.cpp
index fcc4b59..759e7bf 100644
--- a/browedit/windows/MenuBar.cpp
+++ b/browedit/windows/MenuBar.cpp
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -149,6 +150,61 @@ void BrowEdit::menuBar()
ImGui::EndMenu();
}
+ if (editMode == EditMode::Object && activeMapView && ImGui::BeginMenu("Light Edit"))
+ {
+ if (ImGui::MenuItem("Add new light")) {
+ auto l = new RswLight();
+ l->range = 60.0f;
+ l->color = glm::vec3(0.8f, 0.8f, 0.8f);
+ Node* newNode = new Node("light");
+ newNode->addComponent(new RswObject());
+ newNode->addComponent(l);
+ newNode->addComponent(new BillboardRenderer("data\\light.png", "data\\light_selected.png"));
+ newNode->addComponent(new CubeCollider(5));
+ newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
+ newNodesCenter = glm::vec3(0, -35, 0);
+ newNodePlacement = BrowEdit::Relative;
+ }
+
+ if (ImGui::MenuItem("Add new sun")) {
+ auto l = new RswLight();
+ l->range = 0.0f;
+ l->intensity = 0.5f;
+ l->givesShadow = true;
+ l->affectShadowMap = true;
+ l->sunMatchRswDirection = true;
+ l->diffuseLighting = false;
+ l->lightType = RswLight::Type::Sun;
+ Node* newNode = new Node("sun");
+ newNode->addComponent(new RswObject());
+ newNode->addComponent(l);
+ newNode->addComponent(new BillboardRenderer("data\\light.png", "data\\light_selected.png"));
+ newNode->addComponent(new CubeCollider(5));
+ newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
+ newNodesCenter = glm::vec3(0, 0, 0);
+ newNodePlacement = BrowEdit::Ground;
+ }
+
+ if (ImGui::MenuItem("Select all lights")) {
+ auto ga = new GroupAction();
+
+ activeMapView->map->rootNode->traverse([&](Node* n)
+ {
+ auto lightObject = n->getComponent();
+ if (!lightObject)
+ return;
+
+ auto action = new SelectAction(activeMapView->map, n, true, false);
+ action->perform(activeMapView->map, this);
+ ga->addAction(action);
+ });
+
+ if (!ga->isEmpty())
+ activeMapView->map->doAction(ga, this);
+ }
+
+ ImGui::EndMenu();
+ }
if(activeMapView)
if (ImGui::BeginMenu(activeMapView->map->name.c_str()))
diff --git a/browedit/windows/ObjectSelectWindow.cpp b/browedit/windows/ObjectSelectWindow.cpp
index 5c1752d..3e17d4a 100644
--- a/browedit/windows/ObjectSelectWindow.cpp
+++ b/browedit/windows/ObjectSelectWindow.cpp
@@ -325,7 +325,7 @@ void BrowEdit::showObjectWindow()
newNode->addComponent(new RswModelCollider());
newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
newNodesCenter = glm::vec3(0, 0, 0);
- newNodeHeight = false;
+ newNodePlacement = BrowEdit::Ground;
}
else if (file.substr(file.size() - 5) == ".json" &&
path.find("data\\lights") != std::string::npos)
@@ -342,7 +342,7 @@ void BrowEdit::showObjectWindow()
newNode->addComponent(new CubeCollider(5));
newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
newNodesCenter = glm::vec3(0, 0, 0);
- newNodeHeight = false;
+ newNodePlacement = BrowEdit::Ground;
}
else if (file.substr(file.size() - 5) == ".json" &&
path.find("data\\effects") != std::string::npos)
@@ -382,7 +382,7 @@ void BrowEdit::showObjectWindow()
newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
newNodesCenter = glm::vec3(0, 0, 0);
- newNodeHeight = false;
+ newNodePlacement = BrowEdit::Ground;
}
else if (file.substr(file.size() - 5) == ".json" &&
path.find("data\\prefabs") != std::string::npos)
@@ -455,7 +455,7 @@ void BrowEdit::showObjectWindow()
newNodesCenter = center;
for (auto& n : newNodes)
n.second = n.second - center;
- newNodeHeight = false;
+ newNodePlacement = BrowEdit::Ground;
}
}
else if (file.substr(file.size() - 4) == ".wav")
@@ -468,7 +468,7 @@ void BrowEdit::showObjectWindow()
newNode->addComponent(new CubeCollider(5));
newNodes.push_back(std::pair(newNode, glm::vec3(0, 0, 0)));
newNodesCenter = glm::vec3(0, 0, 0);
- newNodeHeight = false;
+ newNodePlacement = BrowEdit::Ground;
}
}
std::cout << "Click on " << file << std::endl;
diff --git a/data/shaders/gnd.fs b/data/shaders/gnd.fs
index edbfccb..22e3b6b 100644
--- a/data/shaders/gnd.fs
+++ b/data/shaders/gnd.fs
@@ -12,6 +12,8 @@ uniform float colorToggle = 1.0f;
uniform float lightColorToggle = 1.0f;
uniform float shadowMapToggle = 1.0f;
uniform float viewTextures = 1.0f;
+uniform int lightCount = 0;
+uniform bool hideOtherLights = false;
uniform bool fogEnabled;
uniform float fogNear = 0;
@@ -25,7 +27,29 @@ in vec3 normal;
in vec4 color;
out vec4 fragColor;
-//out vec4 fragSelection;
+in vec3 fragPos;
+
+#define MAX_FALLOFF 10
+#define NR_LIGHTS 1
+
+struct Light {
+ vec3 position;
+ vec3 color;
+ int type;
+ int falloff_style;
+ vec2 falloff[MAX_FALLOFF];
+ int falloff_count;
+ float range;
+ float intensity;
+ float cutoff;
+ bool diffuseLighting;
+ vec3 direction;
+ float width;
+};
+
+uniform Light lights[NR_LIGHTS];
+
+vec3 CalcPointLight(Light light, vec3 normal, vec3 inFragPos);
void main()
{
@@ -36,6 +60,11 @@ void main()
if(texture.a < 0.1)
discard;
+ vec3 light = vec3(0, 0, 0);
+
+ for (int i = 0; i < lightCount; i++)
+ light.rgb += CalcPointLight(lights[i], normalize(normal), fragPos);
+
float NL = clamp(dot(normalize(normal), vec3(1,-1,1)*lightDirection),0.0,1.0);
vec3 ambientFactor = (1.0 - lightAmbient) * lightAmbient;
vec3 ambient = lightAmbient - ambientFactor + ambientFactor * lightDiffuse;
@@ -47,16 +76,182 @@ void main()
texture.rgb *= min(mult1, mult2);
texture.rgb *= max(color, colorToggle).rgb;
texture.rgb *= max(texture2D(s_lighting, texCoord2).a, shadowMapToggle);
- texture.rgb += clamp(texture2D(s_lighting, texCoord2).rgb, 0.0, 1.0) * lightColorToggle;
-
+
+ if (!hideOtherLights) {
+ texture.rgb += clamp(texture2D(s_lighting, texCoord2).rgb, 0.0, 1.0) * lightColorToggle;
+ texture.rgb += light.rgb;
+ }
+ else {
+ if (lightCount > 0 && light.rgb != vec3(0, 0, 0))
+ texture.rgb += light.rgb;
+ else
+ texture.rgb += clamp(texture2D(s_lighting, texCoord2).rgb, 0.0, 1.0) * lightColorToggle;
+ }
+
if(fogEnabled)
{
float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogAmount = smoothstep(fogNear, fogFar, depth);
texture = mix(texture, fogColor, fogAmount);
}
-
- //gl_FragData[0] = texture;
- //gl_FragData[1] = vec4(0,0,0,0);
+
fragColor = texture;
+}
+
+// calculates the color when using a point light.
+vec3 CalcPointLight(Light light, vec3 normal, vec3 inFragPos)
+{
+ normal = vec3(normal.x, -normal.y, normal.z);
+ vec3 lightDir = normalize(light.position - inFragPos);
+ float distance = length(light.position - inFragPos);
+
+ if (light.type == 2) // sun, ignored
+ return vec3(0, 0, 0);
+
+ float attenuation = 0.0f;
+ float dotProduct = abs(dot(normalize(normal), lightDir));
+
+ if (light.falloff_style == 4) {
+ float kC = 1;
+ float kL = 2.0f / light.range;
+ float kQ = 1.0f / (light.range * light.range);
+ float maxChannel = max(max(light.color.r, light.color.g), light.color.b);
+ float realRange = (-kL + sqrt(kL * kL - 4 * kQ * (kC - 128.0f * maxChannel * light.intensity))) / (2 * kQ);
+
+ if (distance > realRange)
+ return vec3(0, 0, 0);
+
+ float d = max(distance - light.range, 0.0f);
+ float denom = d / light.range + 1;
+ attenuation = light.intensity / (denom * denom);
+ if (light.cutoff > 0)
+ attenuation = max(0.0f, (attenuation - light.cutoff) / (1 - light.cutoff));
+ }
+ else {
+ if (distance > light.range)
+ return vec3(0, 0, 0);
+
+ float d = distance / light.range;
+
+ if (light.falloff_style == 0) {
+ attenuation = clamp(1.0f - pow(d, light.cutoff), 0.0f, 1.0f);
+ }
+ else if (light.falloff_style == 1) { // Spline
+ int n = light.falloff_count;
+ float h[MAX_FALLOFF];
+ float F[MAX_FALLOFF];
+ float s[MAX_FALLOFF];
+ float m[MAX_FALLOFF * MAX_FALLOFF];
+
+ for (int i = n - 1; i > 0; i--) {
+ h[i] = 0.0f;
+ F[i] = 0.0f;
+ s[i] = 0.0f;
+ }
+
+ for (int i = n - 1; i > 0; i--)
+ {
+ F[i] = (light.falloff[i].y - light.falloff[i - 1].y) / (light.falloff[i].x - light.falloff[i - 1].x);
+ h[i - 1] = light.falloff[i].x - light.falloff[i - 1].x;
+ }
+
+ for (int i = 1; i < n - 1; i++)
+ {
+ m[i * MAX_FALLOFF + i] = 2 * (h[i - 1] + h[i]);
+ if (i != 1)
+ {
+ m[i * MAX_FALLOFF + i - 1] = h[i - 1];
+ m[(i - 1) * MAX_FALLOFF + i] = h[i - 1];
+ }
+ m[i * MAX_FALLOFF + n - 1] = 6 * (F[i + 1] - F[i]);
+ }
+
+ for (int i = 1; i < n - 2; i++)
+ {
+ float temp = (m[(i + 1) * MAX_FALLOFF + i] / m[i * MAX_FALLOFF + i]);
+ for (int j = 1; j <= n - 1; j++)
+ m[(i + 1) * MAX_FALLOFF + j] -= temp * m[i * MAX_FALLOFF + j];
+ }
+ float sum;
+ for (int i = n - 2; i > 0; i--)
+ {
+ sum = 0;
+ for (int j = i; j <= n - 2; j++)
+ sum += m[i * MAX_FALLOFF + j] * s[j];
+ s[i] = (m[i * MAX_FALLOFF + n - 1] - sum) / m[i * MAX_FALLOFF + i];
+ }
+ for (int i = 0; i < n - 1; i++) {
+ if (light.falloff[i].x <= d && d <= light.falloff[i + 1].x)
+ {
+ float a = (s[i + 1] - s[i]) / (6 * h[i]);
+ float b = s[i] / 2;
+ float c = (light.falloff[i + 1].y - light.falloff[i].y) / h[i] - (2 * h[i] * s[i] + s[i + 1] * h[i]) / 6;
+ float e = light.falloff[i].y;
+ sum = a * pow((d - light.falloff[i].x), 3.0f) + b * pow((d - light.falloff[i].x), 2.0f) + c * (d - light.falloff[i].x) + e;
+ }
+ }
+ attenuation = clamp(sum, 0.0f, 1.0f);
+ }
+ else if (light.falloff_style == 2) { // Lagrange
+ for (int i = 0; i < light.falloff_count; i++) {
+ float term = light.falloff[i].y;
+
+ for (int j = 1; j < light.falloff_count; j++) {
+ if (j != i)
+ term = term * (d - light.falloff[j].x) / (light.falloff[i].x - light.falloff[j].x);
+ }
+
+ attenuation += term;
+ }
+
+ attenuation = clamp(attenuation, 0.0f, 1.0f);
+ }
+ else if (light.falloff_style == 3) { // Linear
+ vec2 before = vec2(-9999, -9999);
+ vec2 after = vec2(9999, 9999);
+
+ for (int i = 0; i < light.falloff_count; i++) {
+ vec2 p = light.falloff[i];
+
+ if (p.x <= d && d - p.x < d - before.x)
+ before = p;
+ if (p.x >= d && p.x - d < after.x - d)
+ after = p;
+ }
+
+ float diff = (d - before.x) / (after.x - before.x);
+ attenuation = before.y + diff * (after.y - before.y);
+ attenuation = clamp(attenuation, 0.0f, 1.0f);
+ }
+ else if (light.falloff_style == 5) { // S Curve
+ d = 2.0f * d - 1.0f;
+
+ if (d <= 0.0f)
+ attenuation = -1.0f / (1.0f + 1.5f * - d + 1.5f * d * d) * (1.0f + d) + 2.0f;
+ else
+ attenuation = 1.0f / (1.0f + 1.5f * d + 1.5f * d * d) * (1.0f - d);
+ }
+ else {
+ attenuation = 0.0f;
+ }
+ }
+
+ if (light.type == 1) { // spot
+ float dp = dot(lightDir, -light.direction);
+
+ if (dp < (1 - light.width))
+ attenuation = 0.0f;
+ else {
+ float fac = 1.0f - ((1.0f - abs(dp)) / light.width);
+ attenuation *= fac;
+ }
+ }
+
+ if (light.diffuseLighting) {
+ attenuation *= dotProduct;
+ }
+
+ attenuation *= light.intensity;
+
+ return light.color * attenuation;
}
\ No newline at end of file
diff --git a/data/shaders/gnd.vs b/data/shaders/gnd.vs
index 9860a9e..361415a 100644
--- a/data/shaders/gnd.vs
+++ b/data/shaders/gnd.vs
@@ -13,6 +13,7 @@ out vec2 texCoord;
out vec2 texCoord2;
out vec3 normal;
out vec4 color;
+out vec3 fragPos;
void main()
{
@@ -20,5 +21,6 @@ void main()
texCoord2 = a_texture2;
normal = a_normal;
color = a_color;
+ fragPos = a_position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(a_position,1);
}
\ No newline at end of file