diff --git a/Documentation/ScriptingAPI-Documentation.md b/Documentation/ScriptingAPI-Documentation.md index 03cf23119..93cc393a1 100644 --- a/Documentation/ScriptingAPI-Documentation.md +++ b/Documentation/ScriptingAPI-Documentation.md @@ -473,13 +473,14 @@ A ray is defined by an origin Vector and a normalized direction Vector. It can b #### AABB Axis Aligned Bounding Box. Can be intersected with other primitives. - [constructor]AABB(Vector min,max) -- Intersects(AABB aabb) : int result -- result can be 0 (outside, no intersection), 1 (intersection), 2 (completely inside) +- Intersects(AABB aabb) : bool result - Intersects(Sphere sphere) : bool result - Intersects(Ray ray) : bool result - GetMin() : Vector result - GetMax() : Vector result - GetCenter() : Vector result - GetHalfExtents() : Vector result +- Transform(Matrix matrix) : AABB result -- transforms the AABB with a matrix and returns the resulting conservative AABB #### Sphere Sphere defined by center Vector and radius. Can be intersected with other primitives. diff --git a/WickedEngine/wiIntersect_BindLua.cpp b/WickedEngine/wiIntersect_BindLua.cpp index 772499087..f51e1e677 100644 --- a/WickedEngine/wiIntersect_BindLua.cpp +++ b/WickedEngine/wiIntersect_BindLua.cpp @@ -1,5 +1,6 @@ #include "wiIntersect_BindLua.h" #include "Vector_BindLua.h" +#include "Matrix_BindLua.h" namespace wiIntersect_BindLua { @@ -104,6 +105,7 @@ namespace wiIntersect_BindLua lunamethod(AABB_BindLua, GetMax), lunamethod(AABB_BindLua, GetCenter), lunamethod(AABB_BindLua, GetHalfExtents), + lunamethod(AABB_BindLua, Transform), { NULL, NULL } }; Luna::PropertyType AABB_BindLua::properties[] = { @@ -147,8 +149,9 @@ namespace wiIntersect_BindLua AABB_BindLua* _aabb = Luna::lightcheck(L, 1); if (_aabb) { - int intersects = (int)aabb.intersects(_aabb->aabb); - wiLua::SSetInt(L, intersects); + //int intersects = (int)aabb.intersects(_aabb->aabb); + //wiLua::SSetInt(L, intersects); + wiLua::SSetBool(L, aabb.intersects(_aabb->aabb) != AABB::INTERSECTION_TYPE::OUTSIDE); // int intersection type cannot be checked like bool in lua so we give simple bool result here! return 1; } @@ -192,6 +195,25 @@ namespace wiIntersect_BindLua Luna::push(L, new Vector_BindLua(XMLoadFloat3(&aabb.getHalfWidth()))); return 1; } + int AABB_BindLua::Transform(lua_State* L) + { + int argc = wiLua::SGetArgCount(L); + if (argc > 0) + { + Matrix_BindLua* _matrix = Luna::lightcheck(L, 1); + if (_matrix) + { + Luna::push(L, new AABB_BindLua(aabb.get(_matrix->matrix))); + return 1; + } + else + { + wiLua::SError(L, "Transform(Matrix matrix) argument is not a Matrix! "); + } + } + wiLua::SError(L, "Transform(Matrix matrix) not enough arguments! "); + return 0; + } diff --git a/WickedEngine/wiIntersect_BindLua.h b/WickedEngine/wiIntersect_BindLua.h index aea72fd1f..059311db3 100644 --- a/WickedEngine/wiIntersect_BindLua.h +++ b/WickedEngine/wiIntersect_BindLua.h @@ -44,6 +44,7 @@ namespace wiIntersect_BindLua int GetMax(lua_State* L); int GetCenter(lua_State* L); int GetHalfExtents(lua_State* L); + int Transform(lua_State* L); }; class Sphere_BindLua diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 76585052d..4526b0338 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 26; // minor bug fixes, alterations, refactors, updates - const int revision = 3; + const int revision = 4; long GetVersion() diff --git a/scripts/dungeon_generator.lua b/scripts/dungeon_generator.lua new file mode 100644 index 000000000..e6742230b --- /dev/null +++ b/scripts/dungeon_generator.lua @@ -0,0 +1,181 @@ +-- Simplistic random dungeon generator lua script +-- You can call this from Editor for example, and it will load a complete dungeon from set pieces + +local modelpath = "../models/" + +dungeon={ + + Generate = function(complexity) + + local scalingMat = matrix.Scale(Vector(1,1,1)) + + -- This will hold the bounding boxes of the placed segments + local boxes = {} + -- This holds unconnected exits + local exits = {} + -- If an exit should be placed, look up its transform here + local exitTransforms = {} + + -- threshold so that aabbs snapped to each other are not detected as intersecting + -- because segments are needed to be snapped to each other + -- chose this value so that it also eliminates floating point errors + local aabb_threshold = Vector(0.05,0.05,0.05) + + -- Shrink an AABB by a threshold vector + local function ShrinkAABBThreshold(aabb, threshold) + return AABB(aabb.GetMin():Add(threshold), aabb.GetMax():Subtract(threshold)) + end + + -- Check if a segment can be placed or not by specifying its axis aligned bounding box + local function CheckSegmentCanBePlaced(aabb) + aabb = ShrinkAABBThreshold(aabb, aabb_threshold) + + for i,box in ipairs(boxes) do + if box.Intersects(aabb) then + return false + end + end + + table.insert(boxes,aabb) + + return true + end + + -- Merge rooms if their exits are intersecting, but close them if not + local function MergeExits() + for i = 1, #exits do + local place = true + for j = 1, #exits do + if i~=j and exits[i].Intersects(exits[j]) then + place = false + break + end + end + + if place then + LoadModel(modelpath .. "dungeon/end.wiscene",exitTransforms[i]) + end + end + end + + -- Create physical dungeon segments recursively + local function GenerateDungeon(remaining, pos, rotY) + remaining = remaining - 1 + local rotMat = matrix.RotationY(rotY) + local transformMat = matrix.Multiply( rotMat, matrix.Multiply( matrix.Translation(pos), scalingMat ) ) + + -- Mark exit point if the generation ended and return from recursion + if (remaining < 0) then + table.insert(exits, ShrinkAABBThreshold( AABB(Vector(-0.5,0,0),Vector(0.5,1,0.5)).Transform(transformMat), aabb_threshold) ) + table.insert(exitTransforms, transformMat) + return; + end + + local select = math.random(0, 100) + if(select < 80) then -- common pieces + local select2 = math.random(0, 6) + if(select2 < 1) then --left turn + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/turnleft.wiscene",transformMat) + GenerateDungeon(remaining,pos:Add(Vector(-1,0,1).Transform(rotMat)),rotY-0.5*math.pi) + else + GenerateDungeon(remaining,pos,rotY) + end + elseif(select2 < 2) then --right turn + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/turnright.wiscene",transformMat) + GenerateDungeon(remaining,pos:Add(Vector(1,0,1).Transform(rotMat)),rotY+0.5*math.pi) + else + GenerateDungeon(remaining,pos,rotY) + end + elseif(select2 < 3) then --t-junction + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/tjunction.wiscene",transformMat) + -- right + GenerateDungeon(remaining,pos:Add(Vector(1,0,1).Transform(rotMat)),rotY+0.5*math.pi) + -- left + GenerateDungeon(remaining,pos:Add(Vector(-1,0,1).Transform(rotMat)),rotY-0.5*math.pi) + else + GenerateDungeon(remaining,pos,rotY) + end + elseif(select2 < 4) then --cross-junction + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/crossjunction.wiscene",transformMat) + -- right + GenerateDungeon(remaining,pos:Add(Vector(1,0,1).Transform(rotMat)),rotY+0.5*math.pi) + -- left + GenerateDungeon(remaining,pos:Add(Vector(-1,0,1).Transform(rotMat)),rotY-0.5*math.pi) + -- straight + GenerateDungeon(remaining,pos:Add(Vector(0,0,2).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + else --straight block + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/block.wiscene",transformMat) + GenerateDungeon(remaining,pos:Add(Vector(0,0,2).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + end + elseif(select < 98) then -- average pieces + local select2 = math.random(0, 100) + if( select2 < 20 ) then --small room left + if CheckSegmentCanBePlaced(AABB(Vector(-5,0,0),Vector(1,2,6)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/smallroomleft.wiscene",transformMat ) + GenerateDungeon(remaining,pos:Add(Vector(-5,0,5).Transform(rotMat)),rotY-0.5*math.pi) + else + GenerateDungeon(remaining,pos,rotY) + end + elseif( select2 < 30 ) then --odd corridor + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(3,2,8)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/oddcorridor.wiscene",transformMat ) + GenerateDungeon(remaining,pos:Add(Vector(2,0,8).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + elseif( select2 < 60 ) then --up corridor + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,4,6)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/upcorridor.wiscene",transformMat ) + GenerateDungeon(remaining,pos:Add(Vector(0,2,6).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + else --corridor + if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,6)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/corridor.wiscene",transformMat ) + GenerateDungeon(remaining,pos:Add(Vector(0,0,6).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + end + else -- rare pieces + if CheckSegmentCanBePlaced(AABB(Vector(-4,0,0),Vector(4,8,8)).Transform(transformMat)) then + LoadModel(modelpath .. "dungeon/room.wiscene",transformMat ) + -- right + GenerateDungeon(remaining,pos:Add(Vector(4,0,4).Transform(rotMat)),rotY + 0.5*math.pi) + -- left + GenerateDungeon(remaining,pos:Add(Vector(-4,0,4).Transform(rotMat)),rotY - 0.5*math.pi) + -- straight + GenerateDungeon(remaining,pos:Add(Vector(0,0,8).Transform(rotMat)),rotY) + else + GenerateDungeon(remaining,pos,rotY) + end + end + end + + + -- Place the start piece of the dungeon + LoadModel(modelpath .. "dungeon/start.wiscene",scalingMat) + CheckSegmentCanBePlaced(AABB(Vector(-1,0,-2),Vector(1,2,0))) + -- Call recursive generator function + GenerateDungeon(complexity,Vector(0,0,0),0) + MergeExits() + + + end, + +} + +-- Just call this to generate a dungeon of some complexity +dungeon.Generate(15) \ No newline at end of file