Merge remote-tracking branch 'origin/master' into c++11
Conflicts: src/Noise/Noise.h src/World.h
@ -37,6 +37,21 @@ if(DEFINED ENV{MCSERVER_BUILD_ID})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# We need C++11 features, Visual Studio has those from VS2012, but it needs a new platform toolset for those; VS2013 supports them natively:
|
||||
# Adapted from http://binglongx.wordpress.com/2013/06/28/set-non-default-platform-toolset-in-cmake/
|
||||
if(MSVC OR MSVC_IDE)
|
||||
if( MSVC_VERSION LESS 1700 ) # VC10- / VS2010-
|
||||
message(FATAL_ERROR "The project requires C++11 features. "
|
||||
"You need at least Visual Studio 11 (Microsoft Visual Studio 2012), "
|
||||
"with Microsoft Visual C++ Compiler Nov 2012 CTP (v120_CTP_Nov2012).")
|
||||
elseif( MSVC_VERSION EQUAL 1700 ) # VC11 / VS2012
|
||||
message( "VC11: using Microsoft Visual Studio 2012 "
|
||||
"with Microsoft Visual C++ Compiler Nov 2012 CTP (v120_CTP_Nov2012)" )
|
||||
set(CMAKE_GENERATOR_TOOLSET "v120_CTP_Nov2012" CACHE STRING "Platform Toolset" FORCE)
|
||||
else() # VC12+, assuming C++11 supported.
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# This has to be done before any flags have been set up.
|
||||
if(${BUILD_TOOLS})
|
||||
add_subdirectory(Tools/MCADefrag/)
|
||||
|
@ -534,6 +534,7 @@ end
|
||||
GetUUID = { Params = "", Return = "string", Notes = "Returns the authentication-based UUID of the client. This UUID should be used to identify the player when persisting any player-related data. Returns a 32-char UUID (no dashes)" },
|
||||
GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" },
|
||||
GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" },
|
||||
GetRequestedViewDistance = { Params = "", Return = "number", Notes = "Returns the view distance that the player request, not the used view distance." },
|
||||
HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." },
|
||||
IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."},
|
||||
Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" },
|
||||
@ -614,7 +615,7 @@ function OnPlayerJoined(a_Player)
|
||||
-- Send an example composite chat message to the player:
|
||||
a_Player:SendMessage(cCompositeChat()
|
||||
:AddTextPart("Hello, ")
|
||||
:AddUrlPart(a_Player:GetName(), "www.mc-server.org", "u@2") -- Colored underlined link
|
||||
:AddUrlPart(a_Player:GetName(), "http://www.mc-server.org", "u@2") -- Colored underlined link
|
||||
:AddSuggestCommandPart(", and welcome.", "/help", "u") -- Underlined suggest-command
|
||||
:AddRunCommandPart(" SetDay", "/time set 0") -- Regular text that will execute command when clicked
|
||||
:SetMessageType(mtJoin) -- It is a join-message
|
||||
@ -2137,6 +2138,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
|
||||
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
|
||||
CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br/><br/><b>NOTE</b>This function is currently unsafe, do not use!" },
|
||||
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
|
||||
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
|
||||
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
|
||||
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
|
||||
Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." },
|
||||
@ -2406,6 +2408,7 @@ end
|
||||
{ Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
|
||||
},
|
||||
FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." },
|
||||
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
|
||||
ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
|
||||
ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
|
||||
ForEachEntity = { Params = "CallbackFunction, [CallbackData]", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
|
||||
@ -2497,7 +2500,7 @@ end
|
||||
SetCommandBlocksEnabled = { Params = "IsEnabled (bool)", Return = "", Notes = "Sets whether command blocks should be enabled on the (entire) server." },
|
||||
SetShouldUseChatPrefixes = { Params = "", Return = "ShouldUse (bool)", Notes = "Sets whether coloured chat prefixes such as [INFO] is used with the SendMessageXXX() or BroadcastChatXXX(), or simply the entire message is coloured in the respective colour." },
|
||||
ShouldUseChatPrefixes = { Params = "", Return = "bool", Notes = "Returns whether coloured chat prefixes are prepended to chat messages or the entire message is simply coloured." },
|
||||
SetSignLines = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as UpdateSign()." },
|
||||
SetSignLines = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil." },
|
||||
SetTicksUntilWeatherChange = { Params = "NumTicks", Return = "", Notes = "Sets the number of ticks after which the weather will be changed." },
|
||||
SetTimeOfDay = { Params = "TimeOfDayTicks", Return = "", Notes = "Sets the time of day, expressed as number of ticks past sunrise, in the range 0 .. 24000." },
|
||||
SetWeather = { Params = "Weather", Return = "", Notes = "Sets the current weather (wSunny, wRain, wStorm) and resets the TicksUntilWeatherChange to the default value for the new weather. The normal weather-changing hooks are called for the change." },
|
||||
@ -2512,7 +2515,7 @@ end
|
||||
SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" },
|
||||
SpawnPrimedTNT = { Params = "X, Y, Z, FuseTicks, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse ticks. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." },
|
||||
TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." },
|
||||
UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as SetSignLiens()" },
|
||||
UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "(<b>DEPRECATED</b>) Please use SetSignLines()." },
|
||||
UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." },
|
||||
WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." },
|
||||
WakeUpSimulatorsInArea = { Params = "MinBlockX, MaxBlockX, MinBlockY, MaxBlockY, MinBlockZ, MaxBlockZ", Return = "", Notes = "Wakes up the simulators for all the blocks in the specified area (edges inclusive)." },
|
||||
|
@ -123,35 +123,47 @@ return
|
||||
|
||||
cSplashPotionEntity =
|
||||
{
|
||||
Desc = "",
|
||||
Functions = {},
|
||||
Desc = [[
|
||||
Represents a thrown splash potion.
|
||||
]],
|
||||
Functions =
|
||||
{
|
||||
GetEntityEffect = { Params = "", Return = "{{cEntityEffect}}", Notes = "Returns the entity effect in this potion" },
|
||||
GetEntityEffectType = { Params = "", Return = "{{cEntityEffect|Entity effect type}}", Notes = "Returns the effect type of this potion" },
|
||||
GetPotionColor = { Params = "", Return = "number", Notes = "Returns the color index of the particles emitted by this potion" },
|
||||
SetEntityEffect = { Params = "{{cEntityEffect}}", Return = "", Notes = "Sets the entity effect for this potion" },
|
||||
SetEntityEffectType = { Params = "{{cEntityEffect|Entity effect type}}", Return = "", Notes = "Sets the effect type of this potion" },
|
||||
SetPotionColor = { Params = "number", Return = "", Notes = "Sets the color index of the particles for this potion" },
|
||||
},
|
||||
Inherits = "cProjectileEntity",
|
||||
}, -- cSplashPotionEntity
|
||||
|
||||
cThrownEggEntity =
|
||||
{
|
||||
Desc = "",
|
||||
Desc = [[
|
||||
Represents a thrown egg.
|
||||
]],
|
||||
Functions = {},
|
||||
Inherits = "cProjectileEntity",
|
||||
}, -- cThrownEggEntity
|
||||
|
||||
cThrownEnderPearlEntity =
|
||||
{
|
||||
Desc = "",
|
||||
Desc = "Represents a thrown ender pearl.",
|
||||
Functions = {},
|
||||
Inherits = "cProjectileEntity",
|
||||
}, -- cThrownEnderPearlEntity
|
||||
|
||||
cThrownSnowballEntity =
|
||||
{
|
||||
Desc = "",
|
||||
Desc = "Represents a thrown snowball.",
|
||||
Functions = {},
|
||||
Inherits = "cProjectileEntity",
|
||||
}, -- cThrownSnowballEntity
|
||||
|
||||
cWitherSkullEntity =
|
||||
{
|
||||
Desc = "",
|
||||
Desc = "Represents a wither skull being shot.",
|
||||
Functions = {},
|
||||
Inherits = "cProjectileEntity",
|
||||
}, -- cWitherSkullEntity
|
||||
|
@ -51,6 +51,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
|
||||
return a_CmdInfo.Handler(a_Split, a_Player);
|
||||
end
|
||||
-- Let the player know they need to give a subcommand:
|
||||
assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level)
|
||||
ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString);
|
||||
return true;
|
||||
end
|
||||
|
2
MCServer/webadmin/files/guest.html
Normal file
@ -0,0 +1,2 @@
|
||||
Hello! Welcome to the MCServer WebAdmin.<br>
|
||||
This is a default message, edit <b>files/guest.html</b> to add your own custom message.
|
BIN
MCServer/webadmin/files/header.png
Normal file
After Width: | Height: | Size: 221 B |
BIN
MCServer/webadmin/files/home.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
MCServer/webadmin/files/loading.gif
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
MCServer/webadmin/files/log_out.png
Normal file
After Width: | Height: | Size: 995 B |
219
MCServer/webadmin/files/login.css
Normal file
@ -0,0 +1,219 @@
|
||||
/* Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: #fff url(header.png) repeat-x top left;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #555;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:active {
|
||||
color: #000;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #069;
|
||||
}
|
||||
|
||||
.row1 {
|
||||
border-bottom: 1px solid #000;
|
||||
height: 100px;
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.row2 {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.contention {
|
||||
color: #000;
|
||||
text-align: left;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
margin: -3px 0;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-top-color: #28597a;
|
||||
background: #28597a;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
button:active {
|
||||
border-top-color: #1b435e;
|
||||
background: #1b435e;
|
||||
}
|
||||
|
||||
.push10 {
|
||||
padding-bottom: 75px;
|
||||
}
|
||||
|
||||
#panel .upper {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
border-top: 1px solid #fff;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
#footer ul.menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#footer ul.menu li {
|
||||
margin: 0 5px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#footer .upper {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
border-top: 1px solid #bbb;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#footer .upper ul.bottom_links {
|
||||
float: left;
|
||||
margin: 3px 0 0 -5px;
|
||||
}
|
||||
|
||||
#footer .lower {
|
||||
background: #a1a2a2 url(thead.png) top left repeat-x;
|
||||
color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ddd;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#footer .lower a:link, #footer .lower a:visited {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#footer .lower a:hover, #footer .lower a:active {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#footer .lower #current_time {
|
||||
float: right;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 85%;
|
||||
min-width: 970px;
|
||||
max-width: 1500px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
background: #999;
|
||||
border-top: 1px #000 solid;
|
||||
}
|
||||
|
||||
* html #footer {
|
||||
position: absolute;
|
||||
top: expression((0-(footer.offsetHeight)+(document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight)+(ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop))+'px');
|
||||
}
|
||||
|
||||
tr td.trow2:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
tr td.trow2:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.tborder {
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.thead, .rounded_top {
|
||||
-moz-border-radius-topleft: 6px;
|
||||
-moz-border-radius-topright: 6px;
|
||||
-webkit-border-top-left-radius: 6px;
|
||||
-webkit-border-top-right-radius: 6px;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
table {
|
||||
color: #000;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tborder {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
border: 1px solid #ccc;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.thead {
|
||||
background: #a1a2a2 url(thead.png) top left repeat-x;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #8e8f8f;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.trow2 {
|
||||
background: #efefef;
|
||||
border: 1px solid;
|
||||
border-color: #fff #ddd #ddd #fff;
|
||||
}
|
||||
|
||||
.padtopp {
|
||||
padding-top: 25px;
|
||||
}
|
BIN
MCServer/webadmin/files/login.gif
Normal file
After Width: | Height: | Size: 586 B |
BIN
MCServer/webadmin/files/logo_login.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
MCServer/webadmin/files/pmfolder.gif
Normal file
After Width: | Height: | Size: 995 B |
@ -1,353 +1,427 @@
|
||||
body, html
|
||||
{
|
||||
font-family: "Open Sans", Tahoma, sans-serif;
|
||||
padding: 0;
|
||||
/* Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */
|
||||
* {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
background-color: #fbe9e7;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.light { font-weight: 300; }
|
||||
.bold { font-weight: 600; }
|
||||
|
||||
#wrapper
|
||||
{
|
||||
background-color: #ff5722;
|
||||
margin: 40px auto;
|
||||
width: 99%;
|
||||
max-width: 1200px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.15);
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.title
|
||||
{
|
||||
font-size: 30pt;
|
||||
padding: 10px 40px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sidebar
|
||||
{
|
||||
float: left;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.sideNav
|
||||
{
|
||||
list-style: none;
|
||||
background-color: #fafafa;
|
||||
margin: 20px 0;
|
||||
padding: 5px 0;
|
||||
body {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
box-shadow: 1px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
min-width: 100%;
|
||||
height:100%;
|
||||
min-height:100%;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.sideNav li
|
||||
{
|
||||
padding: 10px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
.sideNav li.link
|
||||
{
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.sideNav li.link a
|
||||
{
|
||||
a:link {
|
||||
color: #555;
|
||||
text-decoration: none;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
#container
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: #f5f5f5;
|
||||
a:visited {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#main
|
||||
{
|
||||
float: right;
|
||||
width: 80%;
|
||||
padding: 0 15px 20px 15px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
a:hover, a:active {
|
||||
color: #000;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
clear: both;
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
h1 {
|
||||
color: #069;
|
||||
text-shadow: 2px 2px #000;
|
||||
}
|
||||
|
||||
table td
|
||||
{
|
||||
padding: 5px;
|
||||
.row1 {
|
||||
border-bottom: 1px #000 solid;
|
||||
height: 100px;
|
||||
max-height: 100px;
|
||||
background: #fff url(header.png) repeat-x top left;
|
||||
}
|
||||
|
||||
table th
|
||||
{
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
padding: 5px;
|
||||
.row2 {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
table tr:nth-child(odd)
|
||||
{
|
||||
background-color: rgba(0, 0, 0, 0.015);
|
||||
.contention {
|
||||
color: #000;
|
||||
text-align: left;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
font-size: 13px;
|
||||
margin-bottom:75px;
|
||||
}
|
||||
|
||||
p
|
||||
{
|
||||
margin: 8px 0;
|
||||
padding: 8px 3px;
|
||||
.push25 {
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
text-decoration: none;
|
||||
color: #0277bd;
|
||||
-webkit-transition: color 0.1s linear;
|
||||
-moz-transition: color 0.1s linear;
|
||||
transition: color 0.1s linear;
|
||||
#panel ul.menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: #01579b;
|
||||
#panel ul.menu li {
|
||||
margin: 0 5px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.welcome-msg
|
||||
{
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
#panel ul.menu li a {
|
||||
padding-left: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
}
|
||||
|
||||
.username
|
||||
{
|
||||
text-transform: capitalize;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
#panel .upper ul.top_links {
|
||||
float: right;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: black;
|
||||
#panel .upper {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
border-top: 1px solid #fff;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
input, select
|
||||
{
|
||||
#footer ul.menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#footer ul.menu li {
|
||||
margin: 0 5px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#footer .upper {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
border-top: 1px solid #bbb;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#footer .upper ul.bottom_links {
|
||||
float: left;
|
||||
margin: 3px 0 0 -5px;
|
||||
}
|
||||
|
||||
#footer .lower {
|
||||
background: #a1a2a2 url(thead.png) top left repeat-x;
|
||||
color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ddd;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#footer .lower a:link,#footer .lower a:visited {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#footer .lower a:hover,#footer .lower a:active {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#footer .lower #current_time {
|
||||
float: right;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 85%;
|
||||
min-width: 970px;
|
||||
max-width: 1500px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
left:0;
|
||||
bottom:0;
|
||||
height: 61px;
|
||||
width: 100%;
|
||||
background: #999;
|
||||
border-top: 1px #000 solid;
|
||||
border-bottom: 1px #000 solid;
|
||||
}
|
||||
|
||||
* html #footer {
|
||||
position: absolute;
|
||||
top: expression((0-(footer.offsetHeight)+(document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight)+(ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop))+'px');
|
||||
}
|
||||
|
||||
tr td.trow1:first-child, tr td.trow2:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
tr td.trow1:last-child, tr td.trow2:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.tborder {
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.thead {
|
||||
-moz-border-radius-topleft: 6px;
|
||||
-moz-border-radius-topright: 6px;
|
||||
-webkit-border-top-left-radius: 6px;
|
||||
-webkit-border-top-right-radius: 6px;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
table {
|
||||
color: #000;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tborder {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
border: 1px solid #ccc;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.thead {
|
||||
background: #a1a2a2 url(thead.png) top left repeat-x;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #8e8f8f;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
form
|
||||
{
|
||||
padding: 4px;
|
||||
.tcat {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.info input[type="submit"], .info button, .info input[type="button"],
|
||||
.warn input[type="submit"], .warn button, .warn input[type="button"],
|
||||
.err input[type="submit"], .err button, .err input[type="button"]
|
||||
{
|
||||
float: right;
|
||||
.trow1 {
|
||||
background: #f5f5f5;
|
||||
border: 1px solid;
|
||||
border-color: #fff #ddd #ddd #fff;
|
||||
}
|
||||
|
||||
.err
|
||||
{
|
||||
color: white;
|
||||
display: block;
|
||||
background-color: #e51c23 !important;
|
||||
.trow2 {
|
||||
background: #efefef;
|
||||
border: 1px solid;
|
||||
border-color: #fff #ddd #ddd #fff;
|
||||
padding: 15px;
|
||||
line-height: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.err:before
|
||||
{
|
||||
content: "ERROR: ";
|
||||
}
|
||||
|
||||
.warn
|
||||
{
|
||||
color: white;
|
||||
display: block;
|
||||
background-color: #ff5722 !important;
|
||||
padding: 15px;
|
||||
line-height: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.warn:before
|
||||
{
|
||||
content: "WARNING: ";
|
||||
}
|
||||
|
||||
.info
|
||||
{
|
||||
color: white;
|
||||
display: block;
|
||||
background-color: #5677fc !important;
|
||||
padding: 15px;
|
||||
line-height: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.info:before
|
||||
{
|
||||
content: "INFORMATION: ";
|
||||
}
|
||||
|
||||
#footer .fleft
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
|
||||
#footer .fright
|
||||
{
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#footer
|
||||
{
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
font-size: 9pt;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
#footer a
|
||||
{
|
||||
text-transform: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
input[type="submit"], button, input[type="button"]
|
||||
{
|
||||
background-color: #ffc107;
|
||||
padding: 8px 15px 8px 15px;
|
||||
margin: 0 2px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
color: black;
|
||||
box-shadow: 0px 2px 3px rgba(0,0,0,0.2);
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover, button:hover, input[type="button"]:hover
|
||||
{
|
||||
background-color: #ffca28;
|
||||
}
|
||||
|
||||
input[type="submit"]:active, button:active, input[type="button"]:active
|
||||
{
|
||||
background-color: #ffd54f;
|
||||
-webkit-transform: translateY(1px);
|
||||
-moz-transform: translateY(1px);
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
|
||||
/**** PAGE SPECIFIC CSS ****/
|
||||
|
||||
/* remove the * for disabling: */
|
||||
|
||||
.page-core-server-settings table td
|
||||
{
|
||||
text-align: center;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.page-core-server-settings.no-param table td:nth-child(1) a,
|
||||
.page-core-server-settings.param-tab-general table td:nth-child(1) a
|
||||
{
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.page-core-server-settings.param-tab-monsters table td:nth-child(2) a
|
||||
{
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.page-core-server-settings.param-tab-worlds table td:nth-child(3) a
|
||||
{
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.page-core-server-settings.param-tab-world table td:nth-child(4) a
|
||||
{
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.page-core-permissions form table tr,
|
||||
.page-core-permissions form table td,
|
||||
.page-core-permissions form table th
|
||||
{
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.page-core-permissions form table tr:nth-child(1) th
|
||||
{
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.page-core-permissions form table tr:nth-child(1) td
|
||||
{
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
.page-core-permissions form table td input
|
||||
{
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#ChatDiv
|
||||
{
|
||||
table {
|
||||
color: #000;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tborder {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
border: 1px solid #ccc;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.thead {
|
||||
background: #a1a2a2 url(thead.png) top left repeat-x;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #8e8f8f;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tcat {
|
||||
background: #dcdbdc url(tcat.png) repeat-x;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.trow1 {
|
||||
background: #f5f5f5;
|
||||
border: 1px solid;
|
||||
border-color: #fff #ddd #ddd #fff;
|
||||
}
|
||||
|
||||
.trow2 {
|
||||
background: #efefef;
|
||||
border: 1px solid;
|
||||
border-color: #fff #ddd #ddd #fff;
|
||||
}
|
||||
|
||||
.smalltext {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
line-height: 1.4;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
select {
|
||||
background: #fff;
|
||||
padding: 3px;
|
||||
border: 1px solid #ccc;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
}
|
||||
|
||||
.usercp_nav_item {
|
||||
display: block;
|
||||
padding: 1px 0 1px 23px;
|
||||
}
|
||||
|
||||
.usercp_nav_pmfolder {
|
||||
background: url(pmfolder.gif) no-repeat left center;
|
||||
}
|
||||
|
||||
.usercp_nav_sub_pmfolder {
|
||||
padding-left: 40px;
|
||||
background: url(sub_pmfolder.gif) no-repeat left center;
|
||||
}
|
||||
|
||||
.usercp_nav_home {
|
||||
background: url(home.gif) no-repeat left center;
|
||||
}
|
||||
|
||||
.pagehead {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table th {
|
||||
border-bottom: 1px solid rgba(0,0,0,0.12);
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table tr:nth-child(odd) {
|
||||
background-color: rgba(0,0,0,0.015);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
padding: 4px 3px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
-webkit-transition: color .1s linear;
|
||||
-moz-transition: color .1s linear;
|
||||
transition: color .1s linear;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
line-height: 1.4;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
font-size: 13px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
input[type="text"]:hover {
|
||||
background-color: #E5E4E2;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
background-color: #E5E4E2;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
#ChatDiv {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#ChatMessage
|
||||
{
|
||||
width: 100%;
|
||||
#ChatMessage {
|
||||
width: 92%;
|
||||
margin-right: 5px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
/**/
|
||||
input[type="submit"] {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
cursor: pointer;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover {
|
||||
background-color: #E5E4E2;
|
||||
}
|
||||
|
||||
button:disabled,input:disabled {
|
||||
padding: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
cursor: pointer;
|
||||
font-family: Tahoma,Verdana,Arial,Sans-Serif;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
border: none!important;
|
||||
color: #fff!important;
|
||||
background-color: #ccc!important;
|
||||
}
|
||||
|
BIN
MCServer/webadmin/files/sub_pmfolder.gif
Normal file
After Width: | Height: | Size: 1022 B |
BIN
MCServer/webadmin/files/tcat.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
MCServer/webadmin/files/thead.png
Normal file
After Width: | Height: | Size: 132 B |
@ -1,25 +1,69 @@
|
||||
<!-- Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */ -->
|
||||
<html>
|
||||
<head>
|
||||
<title>MCServer WebAdmin - Login</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link href="login.css" rel="stylesheet" type="text/css">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<style type="text/css">
|
||||
header {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<img src="mc-logo.png" alt="MCServer Logo" class="logo">
|
||||
<h1>MCServer - WebAdmin</h1>
|
||||
<form method="get" action="webadmin/">
|
||||
<input type="submit" value="Log in">
|
||||
<div class="contention">
|
||||
<div class="row1">
|
||||
<div class="wrapper">
|
||||
<img src="logo_login.png" alt="MCServer Logo" class="logo">
|
||||
</div>
|
||||
</div>
|
||||
<div id="panel">
|
||||
<div class="upper">
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<form method="get" action="webadmin/" />
|
||||
<button type="submit" value="Log in" style="width:150px;height:25px;font-family:'Source Sans Pro',sans-serif;background:transparent;border:none!important;vertical-align:middle">
|
||||
<strong><img src="login.gif" style="vertical-align:bottom" /> WebAdmin Log in</strong>
|
||||
</button>
|
||||
</form>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row2 push10">
|
||||
<div class="wrapper padtopp">
|
||||
<table border="0" cellspacing="0" cellpadding="5" class="tborder" style="margin-bottom:5px">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="thead rounded_top">
|
||||
<div style="float:left!important"><strong>MCServer WebAdmin</strong></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="trow2 post_content">
|
||||
<div class="post_body">
|
||||
<iframe width="100%" height="100%" style="border:none;min-height:350px;max-height:450px" src="/guest.html"></iframe>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="upper">
|
||||
<div class="wrapper">
|
||||
<ul class="menu bottom_links">
|
||||
<li><a href="http://www.mc-server.org" target="_blank">MCServer</a></li>
|
||||
<li><a href="http://forum.mc-server.org" target="_blank">Forums</a></li>
|
||||
<li><a href="http://builds.cuberite.org" target="_blank">Buildserver</a></li>
|
||||
<li><a href="http://mc-server.xoft.cz/LuaAPI" target="_blank">API Documentation</a></li>
|
||||
<li><a href="http://book.mc-server.org/" target="_blank">User's Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lower">
|
||||
<div class="wrapper">
|
||||
<span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -81,22 +81,56 @@ function ShowPage(WebAdmin, TemplateRequest)
|
||||
end
|
||||
|
||||
Output([[
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title>]] .. Title .. [[</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,600,300' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<title>]] .. Title .. [[</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div id="containerHolder">
|
||||
<a href="./" class="title light">MCServer</a>
|
||||
<div id="container">
|
||||
<div id="sidebar">
|
||||
<ul class="sideNav">
|
||||
<li class='link'><a href=']] .. BaseURL .. [['>Home</a></li>
|
||||
<div class="contention push25">
|
||||
<div class="pagehead">
|
||||
<div class="row1">
|
||||
<div class="wrapper">
|
||||
<img src="/logo_login.png" alt="MCServer Logo" class="logo">
|
||||
</div>
|
||||
</div>
|
||||
<div id="panel">
|
||||
<div class="upper">
|
||||
<div class="wrapper">
|
||||
<ul class="menu top_links">
|
||||
<li><a>Server Name: <strong>]] .. cRoot:Get():GetServer():GetServerID() .. [[</strong></a></li>
|
||||
<li><a>Memory: <strong>]] .. MemoryUsageKiB / 1024 .. [[MB</strong></a></li>
|
||||
<li><a>Chunks: <strong>]] .. NumChunks .. [[</strong></a></li>
|
||||
</ul>
|
||||
<div class="welcome"><strong>Welcome back, ]] .. TemplateRequest.Request.Username .. [[</strong> <a href=".././"><img src="/log_out.png" style="vertical-align:bottom;"> Log Out</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row2">
|
||||
<div class="wrapper">
|
||||
<table width="100%" border="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="180" valign="top">
|
||||
<table border="0" cellspacing="0" cellpadding="5" class="tborder">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="thead"><strong>Menu</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="trow1 smalltext"><a href=']] .. BaseURL .. [[' class='usercp_nav_item usercp_nav_home'>Home</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tcat"><div><span class="smalltext"><strong><font color="#000">Server Management</font></strong></span></div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody style="" id="usercppms_e">
|
||||
<tr>
|
||||
<td class="trow1 smalltext">
|
||||
]])
|
||||
|
||||
|
||||
@ -105,30 +139,58 @@ function ShowPage(WebAdmin, TemplateRequest)
|
||||
local PluginWebTitle = value:GetWebTitle()
|
||||
local TabNames = value:GetTabNames()
|
||||
if (GetTableSize(TabNames) > 0) then
|
||||
Output("<li>"..PluginWebTitle.."</li>\n");
|
||||
Output("<div><a class='usercp_nav_item usercp_nav_pmfolder' style='text-decoration:none;'><b>"..PluginWebTitle.."</b></a></div>\n");
|
||||
|
||||
for webname,prettyname in pairs(TabNames) do
|
||||
Output("<li class='link'><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "'>" .. prettyname .. "</a></li>\n")
|
||||
Output("<div><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "' class='usercp_nav_item usercp_nav_sub_pmfolder'>" .. prettyname .. "</a></div>\n")
|
||||
end
|
||||
|
||||
Output("<br>\n");
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Output([[
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td valign="top" style='padding-left:25px;'>
|
||||
<table border="0" cellspacing="0" cellpadding="5" class="tborder">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="thead" colspan="2"><strong>]] .. SubTitle .. [[</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="trow2">]] .. PageContent .. [[</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="upper">
|
||||
<div class="wrapper">
|
||||
<ul class="menu bottom_links">
|
||||
<li><a href="http://www.mc-server.org" target="_blank">MCServer</a></li>
|
||||
<li><a href="http://forum.mc-server.org" target="_blank">Forums</a></li>
|
||||
<li><a href="http://builds.cuberite.org" target="_blank">Buildserver</a></li>
|
||||
<li><a href="http://mc-server.xoft.cz/LuaAPI" target="_blank">API Documentation</a></li>
|
||||
<li><a href="http://book.mc-server.org/" target="_blank">User's Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="main" class="page-]] .. string.lower(PluginPage.PluginName .. "-" .. string.gsub(PluginPage.TabName, "[^a-zA-Z0-9]+", "-")) .. reqParamsClass .. [[">
|
||||
<h2 class="welcome-msg">Welcome <span class="username">]] .. TemplateRequest.Request.Username .. [[</span></h2>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h3>]] .. SubTitle .. [[</h3>
|
||||
]] .. PageContent .. [[</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<div class="lower">
|
||||
<div class="wrapper">
|
||||
<span id="copyright">Copyright © <a href="http://www.mc-server.org" target="_blank">MCServer Team</a> 2014.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer"><div class="fleft">running MCServer using <span class="bold">]] .. MemoryUsageKiB / 1024 .. [[MB</span> of memory; <span class="bold">]] .. NumChunks .. [[</span> chunks</div><div class="fright">design by <a href="//www.github.com/WebFreak001">WebFreak001</a></div><div class="clear"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,4 +1,4 @@
|
||||
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
|
||||
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
|
||||
========
|
||||
|
||||
MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API.
|
||||
@ -10,6 +10,8 @@ We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions.
|
||||
Installation
|
||||
------------
|
||||
|
||||
[![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer)
|
||||
|
||||
For Linux there is an easy installation method, just run this in your terminal:
|
||||
|
||||
curl -s https://raw.githubusercontent.com/mc-server/MCServer/master/easyinstall.sh | sh
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "Globals.h"
|
||||
#include "BiomeView.h"
|
||||
#include "QtChunk.h"
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include "Region.h"
|
||||
|
||||
|
||||
|
||||
@ -14,6 +14,116 @@ static const int DELTA_STEP = 120; // The normal per-notch wheel delta
|
||||
|
||||
|
||||
|
||||
/** Map for converting biome values to colors. Initialized from biomeColors[]. */
|
||||
static uchar biomeToColor[256 * 4];
|
||||
|
||||
/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/
|
||||
static struct
|
||||
{
|
||||
EMCSBiome m_Biome;
|
||||
uchar m_Color[3];
|
||||
} biomeColors[] =
|
||||
{
|
||||
{ biOcean, { 0x00, 0x00, 0x70 }, },
|
||||
{ biPlains, { 0x8d, 0xb3, 0x60 }, },
|
||||
{ biDesert, { 0xfa, 0x94, 0x18 }, },
|
||||
{ biExtremeHills, { 0x60, 0x60, 0x60 }, },
|
||||
{ biForest, { 0x05, 0x66, 0x21 }, },
|
||||
{ biTaiga, { 0x0b, 0x66, 0x59 }, },
|
||||
{ biSwampland, { 0x2f, 0xff, 0xda }, },
|
||||
{ biRiver, { 0x30, 0x30, 0xaf }, },
|
||||
{ biHell, { 0x7f, 0x00, 0x00 }, },
|
||||
{ biSky, { 0x00, 0x7f, 0xff }, },
|
||||
{ biFrozenOcean, { 0xa0, 0xa0, 0xdf }, },
|
||||
{ biFrozenRiver, { 0xa0, 0xa0, 0xff }, },
|
||||
{ biIcePlains, { 0xff, 0xff, 0xff }, },
|
||||
{ biIceMountains, { 0xa0, 0xa0, 0xa0 }, },
|
||||
{ biMushroomIsland, { 0xff, 0x00, 0xff }, },
|
||||
{ biMushroomShore, { 0xa0, 0x00, 0xff }, },
|
||||
{ biBeach, { 0xfa, 0xde, 0x55 }, },
|
||||
{ biDesertHills, { 0xd2, 0x5f, 0x12 }, },
|
||||
{ biForestHills, { 0x22, 0x55, 0x1c }, },
|
||||
{ biTaigaHills, { 0x16, 0x39, 0x33 }, },
|
||||
{ biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, },
|
||||
{ biJungle, { 0x53, 0x7b, 0x09 }, },
|
||||
{ biJungleHills, { 0x2c, 0x42, 0x05 }, },
|
||||
|
||||
{ biJungleEdge, { 0x62, 0x8b, 0x17 }, },
|
||||
{ biDeepOcean, { 0x00, 0x00, 0x30 }, },
|
||||
{ biStoneBeach, { 0xa2, 0xa2, 0x84 }, },
|
||||
{ biColdBeach, { 0xfa, 0xf0, 0xc0 }, },
|
||||
{ biBirchForest, { 0x30, 0x74, 0x44 }, },
|
||||
{ biBirchForestHills, { 0x1f, 0x5f, 0x32 }, },
|
||||
{ biRoofedForest, { 0x40, 0x51, 0x1a }, },
|
||||
{ biColdTaiga, { 0x31, 0x55, 0x4a }, },
|
||||
{ biColdTaigaHills, { 0x59, 0x7d, 0x72 }, },
|
||||
{ biMegaTaiga, { 0x59, 0x66, 0x51 }, },
|
||||
{ biMegaTaigaHills, { 0x59, 0x66, 0x59 }, },
|
||||
{ biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, },
|
||||
{ biSavanna, { 0xbd, 0xb2, 0x5f }, },
|
||||
{ biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, },
|
||||
{ biMesa, { 0xd9, 0x45, 0x15 }, },
|
||||
{ biMesaPlateauF, { 0xb0, 0x97, 0x65 }, },
|
||||
{ biMesaPlateau, { 0xca, 0x8c, 0x65 }, },
|
||||
|
||||
// M variants:
|
||||
{ biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, },
|
||||
{ biDesertM, { 0xff, 0xbc, 0x40 }, },
|
||||
{ biExtremeHillsM, { 0x88, 0x88, 0x88 }, },
|
||||
{ biFlowerForest, { 0x2d, 0x8e, 0x49 }, },
|
||||
{ biTaigaM, { 0x33, 0x8e, 0x81 }, },
|
||||
{ biSwamplandM, { 0x07, 0xf9, 0xb2 }, },
|
||||
{ biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, },
|
||||
{ biJungleM, { 0x7b, 0xa3, 0x31 }, },
|
||||
{ biJungleEdgeM, { 0x62, 0x8b, 0x17 }, },
|
||||
{ biBirchForestM, { 0x58, 0x9c, 0x6c }, },
|
||||
{ biBirchForestHillsM, { 0x47, 0x87, 0x5a }, },
|
||||
{ biRoofedForestM, { 0x68, 0x79, 0x42 }, },
|
||||
{ biColdTaigaM, { 0x24, 0x3f, 0x36 }, },
|
||||
{ biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, },
|
||||
{ biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, },
|
||||
{ biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, },
|
||||
{ biSavannaM, { 0xe5, 0xda, 0x87 }, },
|
||||
{ biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, },
|
||||
{ biMesaBryce, { 0xff, 0x6d, 0x3d }, },
|
||||
{ biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, },
|
||||
{ biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, },
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static class BiomeColorsInitializer
|
||||
{
|
||||
public:
|
||||
BiomeColorsInitializer(void)
|
||||
{
|
||||
// Reset all colors to gray:
|
||||
for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++)
|
||||
{
|
||||
biomeToColor[i] = 0x7f;
|
||||
}
|
||||
|
||||
// Set known biomes to their colors:
|
||||
for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++)
|
||||
{
|
||||
uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome];
|
||||
color[0] = biomeColors[i].m_Color[2];
|
||||
color[1] = biomeColors[i].m_Color[1];
|
||||
color[2] = biomeColors[i].m_Color[0];
|
||||
color[3] = 0xff;
|
||||
}
|
||||
}
|
||||
} biomeColorInitializer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BiomeView:
|
||||
|
||||
BiomeView::BiomeView(QWidget * parent) :
|
||||
super(parent),
|
||||
m_X(0),
|
||||
@ -40,7 +150,7 @@ BiomeView::BiomeView(QWidget * parent) :
|
||||
redraw();
|
||||
|
||||
// Add a chunk-update callback mechanism:
|
||||
connect(&m_Cache, SIGNAL(chunkAvailable(int, int)), this, SLOT(chunkAvailable(int, int)));
|
||||
connect(&m_Cache, SIGNAL(regionAvailable(int, int)), this, SLOT(regionAvailable(int, int)));
|
||||
|
||||
// Allow mouse and keyboard interaction:
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
@ -143,9 +253,15 @@ void BiomeView::redraw()
|
||||
|
||||
|
||||
|
||||
void BiomeView::chunkAvailable(int a_ChunkX, int a_ChunkZ)
|
||||
void BiomeView::regionAvailable(int a_RegionX, int a_RegionZ)
|
||||
{
|
||||
drawChunk(a_ChunkX, a_ChunkZ);
|
||||
for (int z = 0; z < 32; z++)
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
drawChunk(a_RegionX * 32 + x, a_RegionZ * 32 + z);
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@ -175,8 +291,11 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ)
|
||||
return;
|
||||
}
|
||||
|
||||
//fetch the chunk:
|
||||
ChunkPtr chunk = m_Cache.fetch(a_ChunkX, a_ChunkZ);
|
||||
// Fetch the region:
|
||||
int regionX;
|
||||
int regionZ;
|
||||
Region::chunkToRegion(a_ChunkX, a_ChunkZ, regionX, regionZ);
|
||||
RegionPtr region = m_Cache.fetch(regionX, regionZ);
|
||||
|
||||
// Figure out where on the screen this chunk should be drawn:
|
||||
// first find the center chunk
|
||||
@ -194,11 +313,10 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ)
|
||||
centerx += (a_ChunkX - centerchunkx) * chunksize;
|
||||
centery += (a_ChunkZ - centerchunkz) * chunksize;
|
||||
|
||||
int srcoffset = 0;
|
||||
uchar * bits = m_Image.bits();
|
||||
int imgstride = m_Image.bytesPerLine();
|
||||
|
||||
int skipx = 0,skipy = 0;
|
||||
int skipx = 0, skipy = 0;
|
||||
int blockwidth = chunksize, blockheight = chunksize;
|
||||
// now if we're off the screen we need to crop
|
||||
if (centerx < 0)
|
||||
@ -227,29 +345,52 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ)
|
||||
int imgoffset = centerx * 4 + centery * imgstride;
|
||||
|
||||
// If the chunk is valid, use its data; otherwise use the empty placeholder:
|
||||
const uchar * src = m_EmptyChunkImage;
|
||||
if (chunk.get() != nullptr)
|
||||
const short * src = m_EmptyChunkBiomes;
|
||||
if (region.get() != nullptr)
|
||||
{
|
||||
src = chunk->getImage();
|
||||
int relChunkX = a_ChunkX - regionX * 32;
|
||||
int relChunkZ = a_ChunkZ - regionZ * 32;
|
||||
Chunk & chunk = region->getRelChunk(relChunkX, relChunkZ);
|
||||
if (chunk.isValid())
|
||||
{
|
||||
src = chunk.getBiomes();
|
||||
}
|
||||
}
|
||||
|
||||
// Blit or scale-blit the image:
|
||||
// Scale-blit the image:
|
||||
for (int z = skipy; z < blockheight; z++, imgoffset += imgstride)
|
||||
{
|
||||
srcoffset = floor((double)z / m_Zoom) * 16 * 4;
|
||||
if (m_Zoom == 1.0)
|
||||
size_t srcoffset = static_cast<size_t>(std::floor((double)z / m_Zoom)) * 16;
|
||||
int imgxoffset = imgoffset;
|
||||
for (int x = skipx; x < blockwidth; x++)
|
||||
{
|
||||
memcpy(bits + imgoffset, src + srcoffset + skipx * 4, (blockwidth - skipx) * 4);
|
||||
short biome = src[srcoffset + static_cast<size_t>(std::floor((double)x / m_Zoom))];
|
||||
const uchar * color;
|
||||
if (biome < 0)
|
||||
{
|
||||
static const uchar emptyBiome1[] = { 0x44, 0x44, 0x44, 0xff };
|
||||
static const uchar emptyBiome2[] = { 0x88, 0x88, 0x88, 0xff };
|
||||
color = ((x & 8) ^ (z & 8)) ? emptyBiome1 : emptyBiome2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int xofs = 0;
|
||||
for (int x = skipx; x < blockwidth; x++, xofs +=4)
|
||||
if (biome * 4 >= ARRAYCOUNT(biomeToColor))
|
||||
{
|
||||
memcpy(bits + imgoffset + xofs, src + srcoffset + (int)floor((double)x / m_Zoom) * 4, 4);
|
||||
}
|
||||
static const uchar errorImage[] = { 0xff, 0x00, 0x00, 0xff };
|
||||
color = errorImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = biomeToColor + biome * 4;
|
||||
}
|
||||
}
|
||||
bits[imgxoffset] = color[0];
|
||||
bits[imgxoffset + 1] = color[1];
|
||||
bits[imgxoffset + 2] = color[2];
|
||||
bits[imgxoffset + 3] = color[3];
|
||||
imgxoffset += 4;
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
@ -317,11 +458,12 @@ void BiomeView::mouseMoveEvent(QMouseEvent * a_Event)
|
||||
// Update the status bar info text:
|
||||
int blockX = floor((a_Event->x() - width() / 2) / m_Zoom + m_X);
|
||||
int blockZ = floor((a_Event->y() - height() / 2) / m_Zoom + m_Z);
|
||||
int chunkX, chunkZ;
|
||||
int relX = blockX, relY, relZ = blockZ;
|
||||
cChunkDef::AbsoluteToRelative(relX, relY, relZ, chunkX, chunkZ);
|
||||
auto chunk = m_Cache.fetch(chunkX, chunkZ);
|
||||
int biome = (chunk.get() != nullptr) ? chunk->getBiome(relX, relZ) : biInvalidBiome;
|
||||
int regionX, regionZ;
|
||||
Region::blockToRegion(blockX, blockZ, regionX, regionZ);
|
||||
int relX = blockX - regionX * 512;
|
||||
int relZ = blockZ - regionZ * 512;
|
||||
auto region = m_Cache.fetch(regionX, regionZ);
|
||||
int biome = (region.get() != nullptr) ? region->getRelBiome(relX, relZ) : biInvalidBiome;
|
||||
emit hoverChanged(blockX, blockZ, biome);
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
#include "ChunkCache.h"
|
||||
#include "RegionCache.h"
|
||||
#include "ChunkSource.h"
|
||||
|
||||
|
||||
@ -51,8 +51,8 @@ public slots:
|
||||
/** Redraw the entire widget area. */
|
||||
void redraw();
|
||||
|
||||
/** A specified chunk has become available, redraw it. */
|
||||
void chunkAvailable(int a_ChunkX, int a_ChunkZ);
|
||||
/** A specified region has become available, redraw it. */
|
||||
void regionAvailable(int a_RegionX, int a_RegionZ);
|
||||
|
||||
/** Reloads the current chunk source and redraws the entire workspace. */
|
||||
void reload();
|
||||
@ -62,7 +62,7 @@ protected:
|
||||
double m_Zoom;
|
||||
|
||||
/** Cache for the loaded chunk data. */
|
||||
ChunkCache m_Cache;
|
||||
RegionCache m_Cache;
|
||||
|
||||
/** The entire view's contents in an offscreen image. */
|
||||
QImage m_Image;
|
||||
@ -79,6 +79,9 @@ protected:
|
||||
/** Data used for rendering a chunk that hasn't been loaded yet */
|
||||
uchar m_EmptyChunkImage[16 * 16 * 4];
|
||||
|
||||
/** Data placeholder for chunks that aren't valid. */
|
||||
short m_EmptyChunkBiomes[16 * 16];
|
||||
|
||||
|
||||
/** Draws the specified chunk into m_Image */
|
||||
void drawChunk(int a_ChunkX, int a_ChunkZ);
|
||||
|
@ -1,126 +0,0 @@
|
||||
#include "Globals.h"
|
||||
#include "ChunkCache.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QThreadPool>
|
||||
#include "ChunkSource.h"
|
||||
#include "ChunkLoader.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ChunkCache::ChunkCache(QObject * parent) :
|
||||
super(parent)
|
||||
{
|
||||
m_Cache.setMaxCost(1024 * 1024 * 1024); // 1 GiB of memory for the cache
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ChunkPtr ChunkCache::fetch(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
// Retrieve from the cache:
|
||||
quint32 hash = getChunkHash(a_ChunkX, a_ChunkZ);
|
||||
ChunkPtr * res;
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
res = m_Cache[hash];
|
||||
// If succesful and chunk loaded, return the retrieved value:
|
||||
if ((res != nullptr) && (*res)->isValid())
|
||||
{
|
||||
return *res;
|
||||
}
|
||||
}
|
||||
|
||||
// If the chunk is in cache but not valid, it means it has been already queued for rendering, do nothing now:
|
||||
if (res != nullptr)
|
||||
{
|
||||
return ChunkPtr(nullptr);
|
||||
}
|
||||
|
||||
// There's no such item in the cache, create it now:
|
||||
res = new ChunkPtr(new Chunk);
|
||||
if (res == nullptr)
|
||||
{
|
||||
return ChunkPtr(nullptr);
|
||||
}
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.insert(hash, res, sizeof(Chunk));
|
||||
}
|
||||
|
||||
// Queue the chunk for rendering:
|
||||
queueChunkRender(a_ChunkX, a_ChunkZ, *res);
|
||||
|
||||
// Return failure, the chunk is not yet rendered:
|
||||
return ChunkPtr(nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkCache::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource)
|
||||
{
|
||||
// Replace the chunk source:
|
||||
m_ChunkSource = a_ChunkSource;
|
||||
|
||||
// Clear the cache:
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkCache::reload()
|
||||
{
|
||||
assert(m_ChunkSource.get() != nullptr);
|
||||
|
||||
// Reload the chunk source:
|
||||
m_ChunkSource->reload();
|
||||
|
||||
// Clear the cache:
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkCache::gotChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
emit chunkAvailable(a_ChunkX, a_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
quint32 ChunkCache::getChunkHash(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
// Simply join the two coords into a single int
|
||||
// The coords will never be larger than 16-bits, so we can do this safely
|
||||
return (((static_cast<quint32>(a_ChunkX) & 0xffff) << 16) | (static_cast<quint32>(a_ChunkZ) & 0xffff));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkCache::queueChunkRender(int a_ChunkX, int a_ChunkZ, ChunkPtr & a_Chunk)
|
||||
{
|
||||
// Create a new loader task:
|
||||
ChunkLoader * loader = new ChunkLoader(a_ChunkX, a_ChunkZ, a_Chunk, m_ChunkSource);
|
||||
connect(loader, SIGNAL(loaded(int, int)), this, SLOT(gotChunk(int, int)));
|
||||
|
||||
QThreadPool::globalInstance()->start(loader);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
#include "Globals.h"
|
||||
#include "ChunkLoader.h"
|
||||
#include "ChunkSource.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ChunkLoader::ChunkLoader(int a_ChunkX, int a_ChunkZ, ChunkPtr a_Chunk, ChunkSourcePtr a_ChunkSource) :
|
||||
m_ChunkX(a_ChunkX),
|
||||
m_ChunkZ(a_ChunkZ),
|
||||
m_Chunk(a_Chunk),
|
||||
m_ChunkSource(a_ChunkSource)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ChunkLoader::run()
|
||||
{
|
||||
m_ChunkSource->getChunkBiomes(m_ChunkX, m_ChunkZ, m_Chunk);
|
||||
emit loaded(m_ChunkX, m_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class Chunk;
|
||||
typedef std::shared_ptr<Chunk> ChunkPtr;
|
||||
|
||||
class ChunkSource;
|
||||
typedef std::shared_ptr<ChunkSource> ChunkSourcePtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ChunkLoader :
|
||||
public QObject,
|
||||
public QRunnable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChunkLoader(int a_ChunkX, int a_ChunkZ, ChunkPtr a_Chunk, ChunkSourcePtr a_ChunkSource);
|
||||
virtual ~ChunkLoader() {}
|
||||
|
||||
signals:
|
||||
void loaded(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
protected:
|
||||
virtual void run() override;
|
||||
|
||||
private:
|
||||
int m_ChunkX, m_ChunkZ;
|
||||
ChunkPtr m_Chunk;
|
||||
ChunkSourcePtr m_ChunkSource;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -24,14 +24,14 @@ BioGenSource::BioGenSource(cIniFilePtr a_IniFile) :
|
||||
|
||||
|
||||
|
||||
void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk)
|
||||
void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk)
|
||||
{
|
||||
cChunkDef::BiomeMap biomes;
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes);
|
||||
}
|
||||
a_DestChunk->setBiomes(biomes);
|
||||
int tag;
|
||||
cBiomeGenPtr biomeGen = getBiomeGen(tag);
|
||||
biomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes);
|
||||
releaseBiomeGen(std::move(biomeGen), tag);
|
||||
a_DestChunk.setBiomes(biomes);
|
||||
}
|
||||
|
||||
|
||||
@ -40,10 +40,53 @@ void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChu
|
||||
|
||||
void BioGenSource::reload()
|
||||
{
|
||||
int seed = m_IniFile->GetValueSetI("Seed", "Seed", 0);
|
||||
bool unused = false;
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_BiomeGen = cBiomeGen::CreateBiomeGen(*m_IniFile, seed, unused);
|
||||
m_CurrentTag += 1;
|
||||
m_BiomeGens.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBiomeGenPtr BioGenSource::getBiomeGen(int & a_Tag)
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
a_Tag = m_CurrentTag;
|
||||
if (m_BiomeGens.empty())
|
||||
{
|
||||
// Create a new biogen:
|
||||
lock.unlock();
|
||||
int seed = m_IniFile->GetValueSetI("Seed", "Seed", 0);
|
||||
bool unused;
|
||||
cBiomeGenPtr res = cBiomeGen::CreateBiomeGen(*m_IniFile, seed, unused);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return an existing biogen:
|
||||
cBiomeGenPtr res = m_BiomeGens.back();
|
||||
m_BiomeGens.pop_back();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void BioGenSource::releaseBiomeGen(cBiomeGenPtr && a_BiomeGen, int a_Tag)
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
|
||||
// If the tag differs, the source has been reloaded and this biogen is old, dispose:
|
||||
if (a_Tag != m_CurrentTag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The tag is the same, put the biogen back to list:
|
||||
m_BiomeGens.push_back(std::move(a_BiomeGen));
|
||||
}
|
||||
|
||||
|
||||
@ -160,7 +203,7 @@ AnvilSource::AnvilSource(QString a_WorldRegionFolder) :
|
||||
|
||||
|
||||
|
||||
void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk)
|
||||
void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk)
|
||||
{
|
||||
// Load the compressed data:
|
||||
AString compressedChunkData = getCompressedChunkData(a_ChunkX, a_ChunkZ);
|
||||
@ -200,7 +243,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun
|
||||
{
|
||||
biomeMap[i] = (EMCSBiome)GetBEInt(beBiomes + 4 * i);
|
||||
}
|
||||
a_DestChunk->setBiomes(biomeMap);
|
||||
a_DestChunk.setBiomes(biomeMap);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -216,7 +259,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun
|
||||
{
|
||||
biomeMap[i] = EMCSBiome(vanillaBiomes[i]);
|
||||
}
|
||||
a_DestChunk->setBiomes(biomeMap);
|
||||
a_DestChunk.setBiomes(biomeMap);
|
||||
}
|
||||
|
||||
|
||||
@ -260,7 +303,7 @@ AnvilSource::AnvilFilePtr AnvilSource::getAnvilFile(int a_ChunkX, int a_ChunkZ)
|
||||
|
||||
// Search the cache for the file:
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
for (auto itr = m_Files.cbegin(), end = m_Files.cend(); itr != end; ++itr)
|
||||
for (auto itr = m_Files.begin(), end = m_Files.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_RegionX == RegionX) && ((*itr)->m_RegionZ == RegionZ))
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// fwd:
|
||||
class cBiomeGen;
|
||||
typedef std::shared_ptr<cBiomeGen> cBiomeGenPtr;
|
||||
typedef SharedPtr<cBiomeGen> cBiomeGenPtr;
|
||||
class cIniFile;
|
||||
typedef std::shared_ptr<cIniFile> cIniFilePtr;
|
||||
|
||||
@ -26,7 +26,7 @@ public:
|
||||
|
||||
/** Fills the a_DestChunk with the biomes for the specified coords.
|
||||
It is expected to be thread-safe and re-entrant. Usually QThread::idealThreadCount() threads are used. */
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) = 0;
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) = 0;
|
||||
|
||||
/** Forces a fresh reload of the source. Useful mainly for the generator, whose underlying definition file may have been changed. */
|
||||
virtual void reload() = 0;
|
||||
@ -45,7 +45,7 @@ public:
|
||||
BioGenSource(cIniFilePtr a_IniFile);
|
||||
|
||||
// ChunkSource overrides:
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override;
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) override;
|
||||
virtual void reload(void) override;
|
||||
|
||||
protected:
|
||||
@ -53,10 +53,30 @@ protected:
|
||||
cIniFilePtr m_IniFile;
|
||||
|
||||
/** The generator used for generating biomes. */
|
||||
cBiomeGenPtr m_BiomeGen;
|
||||
std::vector<cBiomeGenPtr> m_BiomeGens;
|
||||
|
||||
/** Guards m_BiomeGen against multithreaded access. */
|
||||
/** Guards m_BiomeGens against multithreaded access. */
|
||||
QMutex m_Mtx;
|
||||
|
||||
/** Keeps track of the current settings of the biomegens.
|
||||
Incremented by one each time reload() is called. Provides the means of releasing old biomegens that were
|
||||
in use while reload() was being processed and thus couldn't be changed back then. releaseBiomeGen() does
|
||||
the job of filtering the biogens before reusing them. */
|
||||
int m_CurrentTag;
|
||||
|
||||
|
||||
/** Retrieves one cBiomeGenPtr from m_BiomeGens.
|
||||
If there's no biogen available there, creates a new one based on the ini file.
|
||||
When done with it, the caller should call releaseBiomeGen() to put the biogen back to m_BiomeGens.
|
||||
a_Tag receives the value of m_CurrentTag from when the lock was held; it should be passed to
|
||||
releaseBiomeGen() together with the biogen. */
|
||||
cBiomeGenPtr getBiomeGen(int & a_Tag);
|
||||
|
||||
/** Marks the specified biogen as available for reuse (puts it back into m_BiomeGens).
|
||||
a_Tag is the value of m_CurrentTag from the time when the biogen was retrieved; if it is different from
|
||||
current m_CurrentTagValue, the biogen will be disposed of (because reload() has been called in the
|
||||
meantime). */
|
||||
void releaseBiomeGen(cBiomeGenPtr && a_BiomeGen, int a_Tag);
|
||||
};
|
||||
|
||||
|
||||
@ -70,7 +90,7 @@ public:
|
||||
AnvilSource(QString a_WorldRegionFolder);
|
||||
|
||||
// ChunkSource overrides:
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override;
|
||||
virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) override;
|
||||
virtual void reload() override;
|
||||
|
||||
protected:
|
||||
|
@ -14,6 +14,8 @@ static const QString s_GeneratorNames[] =
|
||||
QString("Checkerboard"),
|
||||
QString("Constant"),
|
||||
QString("DistortedVoronoi"),
|
||||
QString("Grown"),
|
||||
QString("GrownProt"),
|
||||
QString("MultiStepMap"),
|
||||
QString("TwoLevel"),
|
||||
QString("Voronoi"),
|
||||
|
@ -8,12 +8,13 @@
|
||||
#include <QSettings>
|
||||
#include <QDirIterator>
|
||||
#include <QStatusBar>
|
||||
#include "src/IniFile.h"
|
||||
#include "ChunkSource.h"
|
||||
#include "src/IniFile.h"
|
||||
#include "src/Generating/BioGen.h"
|
||||
#include "src/StringCompression.h"
|
||||
#include "src/WorldStorage/FastNBT.h"
|
||||
#include "GeneratorSetup.h"
|
||||
#include "RegionLoader.h"
|
||||
|
||||
|
||||
|
||||
@ -31,7 +32,8 @@ const double MainWindow::m_ViewZooms[] =
|
||||
MainWindow::MainWindow(QWidget * parent) :
|
||||
QMainWindow(parent),
|
||||
m_GeneratorSetup(nullptr),
|
||||
m_LineSeparator(nullptr)
|
||||
m_LineSeparator(nullptr),
|
||||
m_CurrentZoomLevel(2)
|
||||
{
|
||||
initMinecraftPath();
|
||||
|
||||
@ -40,6 +42,7 @@ MainWindow::MainWindow(QWidget * parent) :
|
||||
connect(m_BiomeView, SIGNAL(decreaseZoom()), this, SLOT(decreaseZoom()));
|
||||
connect(m_BiomeView, SIGNAL(wheelUp()), this, SLOT(increaseZoom()));
|
||||
connect(m_BiomeView, SIGNAL(wheelDown()), this, SLOT(decreaseZoom()));
|
||||
m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]);
|
||||
|
||||
m_StatusBar = new QStatusBar();
|
||||
this->setStatusBar(m_StatusBar);
|
||||
@ -70,7 +73,7 @@ MainWindow::MainWindow(QWidget * parent) :
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
|
||||
RegionLoader::shutdown();
|
||||
}
|
||||
|
||||
|
||||
@ -172,7 +175,8 @@ void MainWindow::setViewZoom()
|
||||
{
|
||||
return;
|
||||
}
|
||||
double newZoom = m_ViewZooms[action->data().toInt()];
|
||||
m_CurrentZoomLevel = action->data().toInt();
|
||||
double newZoom = m_ViewZooms[m_CurrentZoomLevel];
|
||||
m_BiomeView->setZoomLevel(newZoom);
|
||||
action->setChecked(true);
|
||||
}
|
||||
@ -284,15 +288,11 @@ void MainWindow::createActions()
|
||||
{
|
||||
m_actViewZoom[i] = new QAction(tr("&Zoom %1%").arg(std::floor(m_ViewZooms[i] * 100)), this);
|
||||
m_actViewZoom[i]->setCheckable(true);
|
||||
if ((int)(m_ViewZooms[i] * 16) == 16)
|
||||
{
|
||||
m_actViewZoom[i]->setChecked(true);
|
||||
m_CurrentZoomLevel = i;
|
||||
}
|
||||
m_actViewZoom[i]->setData(QVariant(i));
|
||||
zoomGroup->addAction(m_actViewZoom[i]);
|
||||
connect(m_actViewZoom[i], SIGNAL(triggered()), this, SLOT(setViewZoom()));
|
||||
}
|
||||
m_actViewZoom[m_CurrentZoomLevel]->setChecked(true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@ TARGET = QtBiomeVisualiser
|
||||
TEMPLATE = app
|
||||
|
||||
|
||||
SOURCES +=\
|
||||
SOURCES += \
|
||||
MainWindow.cpp \
|
||||
BiomeView.cpp \
|
||||
../../src/Generating/BioGen.cpp \
|
||||
@ -26,9 +26,6 @@ SOURCES +=\
|
||||
../../src/OSSupport/CriticalSection.cpp \
|
||||
../../src/OSSupport/IsThread.cpp \
|
||||
../../src/BiomeDef.cpp \
|
||||
ChunkCache.cpp \
|
||||
ChunkSource.cpp \
|
||||
ChunkLoader.cpp \
|
||||
../../src/StringCompression.cpp \
|
||||
../../src/WorldStorage/FastNBT.cpp \
|
||||
../../lib/zlib/adler32.c \
|
||||
@ -48,12 +45,22 @@ SOURCES +=\
|
||||
../../lib/zlib/zutil.c \
|
||||
GeneratorSetup.cpp \
|
||||
QtBiomeVisualiser.cpp \
|
||||
QtChunk.cpp
|
||||
QtChunk.cpp \
|
||||
RegionCache.cpp \
|
||||
Region.cpp \
|
||||
ChunkSource.cpp \
|
||||
RegionLoader.cpp
|
||||
|
||||
HEADERS += MainWindow.h \
|
||||
|
||||
|
||||
HEADERS += \
|
||||
MainWindow.h \
|
||||
QtChunk.h \
|
||||
Globals.h \
|
||||
BiomeView.h \
|
||||
../../src/Generating/BioGen.h \
|
||||
../../src/Generating/IntGen.h \
|
||||
../../src/Generating/ProtIntGen.h \
|
||||
../../src/VoronoiMap.h \
|
||||
../../src/Noise.h \
|
||||
../../src/StringUtils.h \
|
||||
@ -64,9 +71,6 @@ HEADERS += MainWindow.h \
|
||||
../../src/OSSupport/CriticalSection.h \
|
||||
../../src/OSSupport/IsThread.h \
|
||||
../../src/BiomeDef.h \
|
||||
ChunkCache.h \
|
||||
ChunkSource.h \
|
||||
ChunkLoader.h \
|
||||
../../src/StringCompression.h \
|
||||
../../src/WorldStorage/FastNBT.h \
|
||||
../../lib/zlib/crc32.h \
|
||||
@ -81,7 +85,13 @@ HEADERS += MainWindow.h \
|
||||
../../lib/zlib/zlib.h \
|
||||
../../lib/zlib/zutil.h \
|
||||
GeneratorSetup.h \
|
||||
QtChunk.h
|
||||
QtChunk.h \
|
||||
RegionCache.h \
|
||||
Region.h \
|
||||
ChunkSource.h \
|
||||
RegionLoader.h
|
||||
|
||||
|
||||
|
||||
INCLUDEPATH += $$_PRO_FILE_PWD_ \
|
||||
$$_PRO_FILE_PWD_/../../lib \
|
||||
|
@ -5,138 +5,6 @@
|
||||
|
||||
|
||||
|
||||
/** Map for converting biome values to colors. Initialized from biomeColors[]. */
|
||||
static uchar biomeToColor[256 * 4];
|
||||
|
||||
/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/
|
||||
static struct
|
||||
{
|
||||
EMCSBiome m_Biome;
|
||||
uchar m_Color[3];
|
||||
} biomeColors[] =
|
||||
{
|
||||
{ biOcean, { 0x00, 0x00, 0x70 }, },
|
||||
{ biPlains, { 0x8d, 0xb3, 0x60 }, },
|
||||
{ biDesert, { 0xfa, 0x94, 0x18 }, },
|
||||
{ biExtremeHills, { 0x60, 0x60, 0x60 }, },
|
||||
{ biForest, { 0x05, 0x66, 0x21 }, },
|
||||
{ biTaiga, { 0x0b, 0x66, 0x59 }, },
|
||||
{ biSwampland, { 0x2f, 0xff, 0xda }, },
|
||||
{ biRiver, { 0x30, 0x30, 0xaf }, },
|
||||
{ biHell, { 0x7f, 0x00, 0x00 }, },
|
||||
{ biSky, { 0x00, 0x7f, 0xff }, },
|
||||
{ biFrozenOcean, { 0xa0, 0xa0, 0xdf }, },
|
||||
{ biFrozenRiver, { 0xa0, 0xa0, 0xff }, },
|
||||
{ biIcePlains, { 0xff, 0xff, 0xff }, },
|
||||
{ biIceMountains, { 0xa0, 0xa0, 0xa0 }, },
|
||||
{ biMushroomIsland, { 0xff, 0x00, 0xff }, },
|
||||
{ biMushroomShore, { 0xa0, 0x00, 0xff }, },
|
||||
{ biBeach, { 0xfa, 0xde, 0x55 }, },
|
||||
{ biDesertHills, { 0xd2, 0x5f, 0x12 }, },
|
||||
{ biForestHills, { 0x22, 0x55, 0x1c }, },
|
||||
{ biTaigaHills, { 0x16, 0x39, 0x33 }, },
|
||||
{ biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, },
|
||||
{ biJungle, { 0x53, 0x7b, 0x09 }, },
|
||||
{ biJungleHills, { 0x2c, 0x42, 0x05 }, },
|
||||
|
||||
{ biJungleEdge, { 0x62, 0x8b, 0x17 }, },
|
||||
{ biDeepOcean, { 0x00, 0x00, 0x30 }, },
|
||||
{ biStoneBeach, { 0xa2, 0xa2, 0x84 }, },
|
||||
{ biColdBeach, { 0xfa, 0xf0, 0xc0 }, },
|
||||
{ biBirchForest, { 0x30, 0x74, 0x44 }, },
|
||||
{ biBirchForestHills, { 0x1f, 0x5f, 0x32 }, },
|
||||
{ biRoofedForest, { 0x40, 0x51, 0x1a }, },
|
||||
{ biColdTaiga, { 0x31, 0x55, 0x4a }, },
|
||||
{ biColdTaigaHills, { 0x59, 0x7d, 0x72 }, },
|
||||
{ biMegaTaiga, { 0x59, 0x66, 0x51 }, },
|
||||
{ biMegaTaigaHills, { 0x59, 0x66, 0x59 }, },
|
||||
{ biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, },
|
||||
{ biSavanna, { 0xbd, 0xb2, 0x5f }, },
|
||||
{ biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, },
|
||||
{ biMesa, { 0xd9, 0x45, 0x15 }, },
|
||||
{ biMesaPlateauF, { 0xb0, 0x97, 0x65 }, },
|
||||
{ biMesaPlateau, { 0xca, 0x8c, 0x65 }, },
|
||||
|
||||
// M variants:
|
||||
{ biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, },
|
||||
{ biDesertM, { 0xff, 0xbc, 0x40 }, },
|
||||
{ biExtremeHillsM, { 0x88, 0x88, 0x88 }, },
|
||||
{ biFlowerForest, { 0x2d, 0x8e, 0x49 }, },
|
||||
{ biTaigaM, { 0x33, 0x8e, 0x81 }, },
|
||||
{ biSwamplandM, { 0x07, 0xf9, 0xb2 }, },
|
||||
{ biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, },
|
||||
{ biJungleM, { 0x7b, 0xa3, 0x31 }, },
|
||||
{ biJungleEdgeM, { 0x62, 0x8b, 0x17 }, },
|
||||
{ biBirchForestM, { 0x58, 0x9c, 0x6c }, },
|
||||
{ biBirchForestHillsM, { 0x47, 0x87, 0x5a }, },
|
||||
{ biRoofedForestM, { 0x68, 0x79, 0x42 }, },
|
||||
{ biColdTaigaM, { 0x24, 0x3f, 0x36 }, },
|
||||
{ biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, },
|
||||
{ biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, },
|
||||
{ biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, },
|
||||
{ biSavannaM, { 0xe5, 0xda, 0x87 }, },
|
||||
{ biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, },
|
||||
{ biMesaBryce, { 0xff, 0x6d, 0x3d }, },
|
||||
{ biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, },
|
||||
{ biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, },
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static class BiomeColorsInitializer
|
||||
{
|
||||
public:
|
||||
BiomeColorsInitializer(void)
|
||||
{
|
||||
// Reset all colors to gray:
|
||||
for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++)
|
||||
{
|
||||
biomeToColor[i] = 0x7f;
|
||||
}
|
||||
|
||||
// Set known biomes to their colors:
|
||||
for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++)
|
||||
{
|
||||
uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome];
|
||||
color[0] = biomeColors[i].m_Color[2];
|
||||
color[1] = biomeColors[i].m_Color[1];
|
||||
color[2] = biomeColors[i].m_Color[0];
|
||||
color[3] = 0xff;
|
||||
}
|
||||
}
|
||||
} biomeColorInitializer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts biomes in an array into the chunk image data. */
|
||||
static void biomesToImage(const cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image)
|
||||
{
|
||||
// Make sure the two arrays are of the same size, compile-time.
|
||||
// Note that a_Image is actually 4 items per pixel, so the array is 4 times bigger:
|
||||
static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1] = {};
|
||||
static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1] = {};
|
||||
|
||||
// Convert the biomes into color:
|
||||
for (size_t i = 0; i < ARRAYCOUNT(a_Biomes); i++)
|
||||
{
|
||||
a_Image[4 * i + 0] = biomeToColor[4 * a_Biomes[i] + 0];
|
||||
a_Image[4 * i + 1] = biomeToColor[4 * a_Biomes[i] + 1];
|
||||
a_Image[4 * i + 2] = biomeToColor[4 * a_Biomes[i] + 2];
|
||||
a_Image[4 * i + 3] = biomeToColor[4 * a_Biomes[i] + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Chunk:
|
||||
|
||||
Chunk::Chunk() :
|
||||
m_IsValid(false)
|
||||
{
|
||||
@ -146,20 +14,12 @@ Chunk::Chunk() :
|
||||
|
||||
|
||||
|
||||
const uchar * Chunk::getImage(void) const
|
||||
{
|
||||
ASSERT(m_IsValid);
|
||||
return m_Image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Chunk::setBiomes(const cChunkDef::BiomeMap & a_Biomes)
|
||||
{
|
||||
memcpy(m_Biomes, a_Biomes, sizeof(m_Biomes));
|
||||
renderBiomes();
|
||||
for (size_t idx = 0; idx < ARRAYCOUNT(a_Biomes); ++idx)
|
||||
{
|
||||
m_Biomes[idx] = static_cast<short>(a_Biomes[idx]);
|
||||
}
|
||||
m_IsValid = true;
|
||||
}
|
||||
|
||||
@ -173,16 +33,7 @@ EMCSBiome Chunk::getBiome(int a_RelX, int a_RelZ)
|
||||
{
|
||||
return biInvalidBiome;
|
||||
}
|
||||
return cChunkDef::GetBiome(m_Biomes, a_RelX, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Chunk::renderBiomes()
|
||||
{
|
||||
biomesToImage(m_Biomes, m_Image);
|
||||
return static_cast<EMCSBiome>(m_Biomes[a_RelX + 16 * a_RelZ]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,9 +18,6 @@ public:
|
||||
/** Returns true iff the chunk data is valid - loaded or generated. */
|
||||
bool isValid(void) const { return m_IsValid; }
|
||||
|
||||
/** Returns the image of the chunk's biomes. Assumes that the chunk is valid. */
|
||||
const uchar * getImage(void) const;
|
||||
|
||||
/** Sets the biomes to m_Biomes and renders them into m_Image. */
|
||||
void setBiomes(const cChunkDef::BiomeMap & a_Biomes);
|
||||
|
||||
@ -28,19 +25,16 @@ public:
|
||||
Coords must be valid inside this chunk. */
|
||||
EMCSBiome getBiome(int a_RelX, int a_RelZ);
|
||||
|
||||
/** Returns the raw biome data for this chunk. */
|
||||
const short * getBiomes(void) const { return m_Biomes; }
|
||||
|
||||
protected:
|
||||
/** Flag that specifies if the chunk data is valid - loaded or generated. */
|
||||
bool m_IsValid;
|
||||
|
||||
/** Cached rendered image of this chunk's biomes. Updated in render(). */
|
||||
Image m_Image;
|
||||
|
||||
/** Biomes comprising the chunk, in the X + 16 * Z ordering. */
|
||||
cChunkDef::BiomeMap m_Biomes;
|
||||
|
||||
|
||||
/** Renders biomes from m_Biomes into m_Image. */
|
||||
void renderBiomes();
|
||||
/** Biomes comprising the chunk, in the X + 16 * Z ordering.
|
||||
Typed as short to save on memory, converted automatically when needed. */
|
||||
short m_Biomes[16 * 16];
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Chunk> ChunkPtr;
|
||||
|
72
Tools/QtBiomeVisualiser/Region.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Region.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Region::Region()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Chunk & Region::getRelChunk(int a_RelChunkX, int a_RelChunkZ)
|
||||
{
|
||||
ASSERT(a_RelChunkX >= 0);
|
||||
ASSERT(a_RelChunkZ >= 0);
|
||||
ASSERT(a_RelChunkX < 32);
|
||||
ASSERT(a_RelChunkZ < 32);
|
||||
|
||||
return m_Chunks[a_RelChunkX + a_RelChunkZ * 32];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int Region::getRelBiome(int a_RelBlockX, int a_RelBlockZ)
|
||||
{
|
||||
ASSERT(a_RelBlockX >= 0);
|
||||
ASSERT(a_RelBlockZ >= 0);
|
||||
ASSERT(a_RelBlockX < 512);
|
||||
ASSERT(a_RelBlockZ < 512);
|
||||
|
||||
int chunkX = a_RelBlockX / 16;
|
||||
int chunkZ = a_RelBlockZ / 16;
|
||||
Chunk & chunk = m_Chunks[chunkX + 32 * chunkZ];
|
||||
if (chunk.isValid())
|
||||
{
|
||||
return chunk.getBiome(a_RelBlockX - 16 * chunkX, a_RelBlockZ - 16 * chunkZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
return biInvalidBiome;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Region::blockToRegion(int a_BlockX, int a_BlockZ, int & a_RegionX, int & a_RegionZ)
|
||||
{
|
||||
a_RegionX = static_cast<int>(std::floor(static_cast<float>(a_BlockX) / 512));
|
||||
a_RegionZ = static_cast<int>(std::floor(static_cast<float>(a_BlockZ) / 512));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Region::chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ)
|
||||
{
|
||||
a_RegionX = static_cast<int>(std::floor(static_cast<float>(a_ChunkX) / 32));
|
||||
a_RegionZ = static_cast<int>(std::floor(static_cast<float>(a_ChunkZ) / 32));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
46
Tools/QtBiomeVisualiser/Region.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "QtChunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Region
|
||||
{
|
||||
public:
|
||||
Region();
|
||||
|
||||
/** Retrieves the chunk with the specified relative coords. */
|
||||
Chunk & getRelChunk(int a_RelChunkX, int a_RelChunkZ);
|
||||
|
||||
/** Returns true iff the chunk data for all chunks has been loaded.
|
||||
This doesn't mean that all the chunks are valid, only that the entire region has been processed and should
|
||||
be displayed. */
|
||||
bool isValid(void) const { return m_IsValid; }
|
||||
|
||||
/** Returns the biome in the block coords relative to this region.
|
||||
Returns biInvalidBiome if the underlying chunk is not valid. */
|
||||
int getRelBiome(int a_RelBlockX, int a_RelBlockZ);
|
||||
|
||||
/** Converts block coordinates into region coordinates. */
|
||||
static void blockToRegion(int a_BlockX, int a_BlockZ, int & a_RegionX, int & a_RegionZ);
|
||||
|
||||
/** Converts chunk coordinates into region coordinates. */
|
||||
static void chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ);
|
||||
|
||||
protected:
|
||||
friend class RegionLoader;
|
||||
|
||||
|
||||
Chunk m_Chunks[32 * 32];
|
||||
|
||||
/** True iff the data for all the chunks has been loaded.
|
||||
This doesn't mean that all the chunks are valid, only that the entire region has been processed and should
|
||||
be displayed. */
|
||||
bool m_IsValid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
138
Tools/QtBiomeVisualiser/RegionCache.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "Globals.h"
|
||||
#include "RegionCache.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QThreadPool>
|
||||
#include "ChunkSource.h"
|
||||
#include "RegionLoader.h"
|
||||
#include "Region.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RegionCache::RegionCache(QObject * parent) :
|
||||
super(parent)
|
||||
{
|
||||
m_Cache.setMaxCost(1024 * 1024 * 1024); // 1 GiB of memory for the cache
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RegionPtr RegionCache::fetch(int a_RegionX, int a_RegionZ)
|
||||
{
|
||||
// Retrieve from the cache:
|
||||
quint32 hash = getRegionHash(a_RegionX, a_RegionZ);
|
||||
RegionPtr * res;
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
res = m_Cache[hash];
|
||||
// If succesful and region loaded, return the retrieved value:
|
||||
if ((res != nullptr) && (*res)->isValid())
|
||||
{
|
||||
return *res;
|
||||
}
|
||||
}
|
||||
|
||||
// If the region is in cache but not valid, it means it has been already queued for rendering, do nothing now:
|
||||
if (res != nullptr)
|
||||
{
|
||||
return RegionPtr(nullptr);
|
||||
}
|
||||
|
||||
// There's no such item in the cache, create it now:
|
||||
try
|
||||
{
|
||||
res = new RegionPtr(new Region);
|
||||
}
|
||||
catch (const std::bad_alloc &)
|
||||
{
|
||||
/* Allocation failed (32-bit process hit the 2 GiB barrier?)
|
||||
This may happen even with the cache set to 1 GiB, because it contains shared ptrs and so they may be
|
||||
held by another place in the code even when they are removed from cache.
|
||||
*/
|
||||
return RegionPtr(nullptr);
|
||||
}
|
||||
if (res == nullptr)
|
||||
{
|
||||
return RegionPtr(nullptr);
|
||||
}
|
||||
{
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.insert(hash, res, sizeof(Region));
|
||||
}
|
||||
|
||||
// Queue the region for rendering:
|
||||
queueRegionRender(a_RegionX, a_RegionZ, *res);
|
||||
|
||||
// Return failure, the region is not yet rendered:
|
||||
return RegionPtr(nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void RegionCache::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource)
|
||||
{
|
||||
// Replace the chunk source:
|
||||
m_ChunkSource = a_ChunkSource;
|
||||
|
||||
// Clear the cache:
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void RegionCache::reload()
|
||||
{
|
||||
assert(m_ChunkSource.get() != nullptr);
|
||||
|
||||
// Reload the chunk source:
|
||||
m_ChunkSource->reload();
|
||||
|
||||
// Clear the cache:
|
||||
QMutexLocker lock(&m_Mtx);
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void RegionCache::gotRegion(int a_RegionX, int a_RegionZ)
|
||||
{
|
||||
emit regionAvailable(a_RegionX, a_RegionZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
quint32 RegionCache::getRegionHash(int a_RegionX, int a_RegionZ)
|
||||
{
|
||||
// Simply join the two coords into a single int
|
||||
// The coords will never be larger than 16-bits, so we can do this safely
|
||||
return (((static_cast<quint32>(a_RegionX) & 0xffff) << 16) | (static_cast<quint32>(a_RegionZ) & 0xffff));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void RegionCache::queueRegionRender(int a_RegionX, int a_RegionZ, RegionPtr & a_Region)
|
||||
{
|
||||
// Create a new loader task:
|
||||
RegionLoader * loader = new RegionLoader(a_RegionX, a_RegionZ, a_Region, m_ChunkSource);
|
||||
connect(loader, SIGNAL(loaded(int, int)), this, SLOT(gotRegion(int, int)));
|
||||
|
||||
QThreadPool::globalInstance()->start(loader);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -9,8 +9,9 @@
|
||||
|
||||
|
||||
|
||||
class Chunk;
|
||||
typedef std::shared_ptr<Chunk> ChunkPtr;
|
||||
// fwd:
|
||||
class Region;
|
||||
typedef std::shared_ptr<Region> RegionPtr;
|
||||
|
||||
class ChunkSource;
|
||||
|
||||
@ -18,19 +19,19 @@ class ChunkSource;
|
||||
|
||||
|
||||
|
||||
/** Caches chunk data for reuse */
|
||||
class ChunkCache :
|
||||
/** Caches regions' chunk data for reuse */
|
||||
class RegionCache :
|
||||
public QObject
|
||||
{
|
||||
typedef QObject super;
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ChunkCache(QObject * parent = NULL);
|
||||
explicit RegionCache(QObject * parent = NULL);
|
||||
|
||||
/** Retrieves the specified chunk from the cache.
|
||||
Only returns valid chunks; if the chunk is invalid, queues it for rendering and returns an empty ptr. */
|
||||
ChunkPtr fetch(int a_ChunkX, int a_ChunkZ);
|
||||
/** Retrieves the specified region from the cache.
|
||||
Only returns valid regions; if the region is invalid, queues it for rendering and returns an empty ptr. */
|
||||
RegionPtr fetch(int a_RegionX, int a_RegionZ);
|
||||
|
||||
/** Replaces the chunk source used by the biome view to get the chunk biome data.
|
||||
The cache is then invalidated. */
|
||||
@ -43,16 +44,16 @@ public:
|
||||
void reload();
|
||||
|
||||
signals:
|
||||
void chunkAvailable(int a_ChunkX, int a_ChunkZ);
|
||||
void regionAvailable(int a_RegionX, int a_RegionZ);
|
||||
|
||||
protected slots:
|
||||
void gotChunk(int a_ChunkX, int a_ChunkZ);
|
||||
void gotRegion(int a_RegionX, int a_RegionZ);
|
||||
|
||||
protected:
|
||||
/** The cache of the chunks */
|
||||
QCache<quint32, ChunkPtr> m_Cache;
|
||||
QCache<quint32, RegionPtr> m_Cache;
|
||||
|
||||
/** Locks te cache against multithreaded access */
|
||||
/** Locks the cache against multithreaded access */
|
||||
QMutex m_Mtx;
|
||||
|
||||
/** The source used to get the biome data. */
|
||||
@ -60,10 +61,10 @@ protected:
|
||||
|
||||
|
||||
/** Returns the hash used by the chunk in the cache */
|
||||
quint32 getChunkHash(int a_ChunkX, int a_ChunkZ);
|
||||
quint32 getRegionHash(int a_RegionX, int a_RegionZ);
|
||||
|
||||
/** Queues the specified chunk for rendering by m_ChunkSource. */
|
||||
void queueChunkRender(int a_ChunkX, int a_ChunkZ, ChunkPtr & a_Chunk);
|
||||
/** Queues the specified region for rendering by m_RegionSource. */
|
||||
void queueRegionRender(int a_RegionX, int a_RegionZ, RegionPtr & a_Region);
|
||||
};
|
||||
|
||||
|
49
Tools/QtBiomeVisualiser/RegionLoader.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "Globals.h"
|
||||
#include "RegionLoader.h"
|
||||
#include "ChunkSource.h"
|
||||
#include "Region.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
volatile bool RegionLoader::m_IsShuttingDown = false;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RegionLoader::RegionLoader(int a_RegionX, int a_RegionZ, RegionPtr a_Region, ChunkSourcePtr a_ChunkSource) :
|
||||
m_RegionX(a_RegionX),
|
||||
m_RegionZ(a_RegionZ),
|
||||
m_Region(a_Region),
|
||||
m_ChunkSource(a_ChunkSource)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void RegionLoader::run()
|
||||
{
|
||||
// Load all the chunks in this region:
|
||||
for (int z = 0; z < 32; z++)
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
m_ChunkSource->getChunkBiomes(m_RegionX * 32 + x, m_RegionZ * 32 + z, m_Region->getRelChunk(x, z));
|
||||
if (m_IsShuttingDown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Region->m_IsValid = true;
|
||||
|
||||
emit loaded(m_RegionX, m_RegionZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
56
Tools/QtBiomeVisualiser/RegionLoader.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class Region;
|
||||
typedef std::shared_ptr<Region> RegionPtr;
|
||||
|
||||
class ChunkSource;
|
||||
typedef std::shared_ptr<ChunkSource> ChunkSourcePtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class RegionLoader :
|
||||
public QObject,
|
||||
public QRunnable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RegionLoader(int a_RegionX, int a_RegionZ, RegionPtr a_Region, ChunkSourcePtr a_ChunkSource);
|
||||
virtual ~RegionLoader() {}
|
||||
|
||||
/** Signals to all loaders that the app is shutting down and the loading should be aborted. */
|
||||
static void shutdown() { m_IsShuttingDown = true; }
|
||||
|
||||
signals:
|
||||
void loaded(int a_RegionX, int a_RegionZ);
|
||||
|
||||
protected:
|
||||
virtual void run() override;
|
||||
|
||||
private:
|
||||
/** Coords of the region to be loaded. */
|
||||
int m_RegionX, m_RegionZ;
|
||||
|
||||
/** The region to be loaded. */
|
||||
RegionPtr m_Region;
|
||||
|
||||
/** The chunk source to be used for individual chunks within the region. */
|
||||
ChunkSourcePtr m_ChunkSource;
|
||||
|
||||
/** Flag that is set upon app exit to terminate the queued loaders faster. */
|
||||
static volatile bool m_IsShuttingDown;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
11
app.yml
Normal file
@ -0,0 +1,11 @@
|
||||
name: MCServer
|
||||
image: ubuntu-14-04-x64
|
||||
config:
|
||||
#cloud-config
|
||||
packages:
|
||||
- curl
|
||||
- screen
|
||||
runcmd:
|
||||
- mkdir /minecraft
|
||||
- cd /minecraft && curl -s https://raw.githubusercontent.com/mc-server/MCServer/master/easyinstall.sh | sh
|
||||
- cd /minecraft/MCServer && screen -S mcserver -d -m ./MCServer
|
@ -7,6 +7,9 @@
|
||||
|
||||
|
||||
#include "../BlockInfo.h"
|
||||
#include "../World.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "LuaState.h"
|
||||
|
||||
|
||||
|
||||
@ -222,6 +225,64 @@ static int tolua_get_AllToLua_g_BlockFullyOccupiesVoxel(lua_State* tolua_S)
|
||||
|
||||
|
||||
|
||||
/** function: cWorld:SetSignLines */
|
||||
static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
|
||||
{
|
||||
cLuaState LuaState(tolua_S);
|
||||
|
||||
#ifndef TOLUA_RELEASE
|
||||
tolua_Error tolua_err;
|
||||
if (
|
||||
!tolua_isusertype (LuaState, 1, "cWorld", 0, &tolua_err) ||
|
||||
!tolua_isnumber (LuaState, 2, 0, &tolua_err) ||
|
||||
!tolua_isnumber (LuaState, 3, 0, &tolua_err) ||
|
||||
!tolua_isnumber (LuaState, 4, 0, &tolua_err) ||
|
||||
!tolua_iscppstring(LuaState, 5, 0, &tolua_err) ||
|
||||
!tolua_iscppstring(LuaState, 6, 0, &tolua_err) ||
|
||||
!tolua_iscppstring(LuaState, 7, 0, &tolua_err) ||
|
||||
!tolua_iscppstring(LuaState, 8, 0, &tolua_err) ||
|
||||
!tolua_isusertype (LuaState, 9, "cPlayer", 1, &tolua_err) ||
|
||||
!tolua_isnoobj (LuaState, 10, &tolua_err)
|
||||
)
|
||||
goto tolua_lerror;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
cWorld * self = (cWorld *) tolua_tousertype (LuaState, 1, nullptr);
|
||||
int BlockX = (int) tolua_tonumber (LuaState, 2, 0);
|
||||
int BlockY = (int) tolua_tonumber (LuaState, 3, 0);
|
||||
int BlockZ = (int) tolua_tonumber (LuaState, 4, 0);
|
||||
const AString Line1 = tolua_tocppstring(LuaState, 5, 0);
|
||||
const AString Line2 = tolua_tocppstring(LuaState, 6, 0);
|
||||
const AString Line3 = tolua_tocppstring(LuaState, 7, 0);
|
||||
const AString Line4 = tolua_tocppstring(LuaState, 8, 0);
|
||||
cPlayer * Player = (cPlayer *)tolua_tousertype (LuaState, 9, nullptr);
|
||||
#ifndef TOLUA_RELEASE
|
||||
if (self == nullptr)
|
||||
{
|
||||
tolua_error(LuaState, "invalid 'self' in function 'UpdateSign'", nullptr);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
|
||||
tolua_pushboolean(LuaState, res ? 1 : 0);
|
||||
}
|
||||
}
|
||||
LOGWARNING("Warning in function call 'UpdateSign': UpdateSign() is deprecated. Please use SetSignLines()");
|
||||
LuaState.LogStackTrace(0);
|
||||
return 1;
|
||||
|
||||
#ifndef TOLUA_RELEASE
|
||||
tolua_lerror:
|
||||
tolua_error(LuaState, "#ferror in function 'UpdateSign'.", &tolua_err);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void DeprecatedBindings::Bind(lua_State * tolua_S)
|
||||
{
|
||||
tolua_beginmodule(tolua_S, nullptr);
|
||||
@ -235,6 +296,10 @@ void DeprecatedBindings::Bind(lua_State * tolua_S)
|
||||
tolua_array(tolua_S, "g_BlockIsSolid", tolua_get_AllToLua_g_BlockIsSolid, nullptr);
|
||||
tolua_array(tolua_S, "g_BlockFullyOccupiesVoxel", tolua_get_AllToLua_g_BlockFullyOccupiesVoxel, nullptr);
|
||||
|
||||
tolua_beginmodule(tolua_S, "cWorld");
|
||||
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
|
||||
tolua_endmodule(tolua_S);
|
||||
|
||||
tolua_endmodule(tolua_S);
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,11 @@ public:
|
||||
template <typename FnT, typename... Args>
|
||||
bool Call(const FnT & a_Function, Args &&... args)
|
||||
{
|
||||
PushFunction(a_Function);
|
||||
if (!PushFunction(a_Function))
|
||||
{
|
||||
// Pushing the function failed
|
||||
return false;
|
||||
}
|
||||
return PushCallPop(args...);
|
||||
}
|
||||
|
||||
|
@ -1034,11 +1034,11 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
|
||||
#ifndef TOLUA_RELEASE
|
||||
if (self == nullptr)
|
||||
{
|
||||
tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", nullptr);
|
||||
tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines'", nullptr);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
|
||||
bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
|
||||
tolua_pushboolean(tolua_S, res ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@ -1046,7 +1046,7 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
|
||||
|
||||
#ifndef TOLUA_RELEASE
|
||||
tolua_lerror:
|
||||
tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err);
|
||||
tolua_error(tolua_S, "#ferror in function 'SetSignLines'.", &tolua_err);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -3368,6 +3368,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
|
||||
|
||||
tolua_beginmodule(tolua_S, "cRoot");
|
||||
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
|
||||
tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
|
||||
tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
|
||||
tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
|
||||
tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
|
||||
@ -3389,6 +3390,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
|
||||
tolua_function(tolua_S, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
|
||||
tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
|
||||
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
|
||||
tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
|
||||
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
|
||||
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
|
||||
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
|
||||
@ -3403,7 +3405,6 @@ void ManualBindings::Bind(lua_State * tolua_S)
|
||||
tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
|
||||
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
|
||||
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
|
||||
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
|
||||
tolua_endmodule(tolua_S);
|
||||
|
||||
tolua_beginmodule(tolua_S, "cMapManager");
|
||||
|
@ -160,3 +160,65 @@ bool IsBiomeNoDownfall(EMCSBiome a_Biome)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsBiomeVeryCold(EMCSBiome a_Biome)
|
||||
{
|
||||
switch (a_Biome)
|
||||
{
|
||||
case biFrozenOcean:
|
||||
case biFrozenRiver:
|
||||
case biIcePlains:
|
||||
case biIceMountains:
|
||||
case biColdBeach:
|
||||
case biColdTaiga:
|
||||
case biColdTaigaHills:
|
||||
case biIcePlainsSpikes:
|
||||
case biColdTaigaM:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsBiomeCold(EMCSBiome a_Biome)
|
||||
{
|
||||
switch (a_Biome)
|
||||
{
|
||||
case biExtremeHills:
|
||||
case biTaiga:
|
||||
case biTaigaHills:
|
||||
case biExtremeHillsEdge:
|
||||
case biStoneBeach:
|
||||
case biMegaTaiga:
|
||||
case biMegaTaigaHills:
|
||||
case biExtremeHillsPlus:
|
||||
case biExtremeHillsM:
|
||||
case biTaigaM:
|
||||
case biColdTaigaM:
|
||||
case biMegaSpruceTaiga:
|
||||
case biMegaSpruceTaigaHills:
|
||||
case biExtremeHillsPlusM:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -113,5 +113,20 @@ extern AString BiomeToString(int a_Biome);
|
||||
/** Returns true if the biome has no downfall - deserts and savannas */
|
||||
extern bool IsBiomeNoDownfall(EMCSBiome a_Biome);
|
||||
|
||||
/** Returns true if the biome is an ocean biome. */
|
||||
inline bool IsBiomeOcean(int a_Biome)
|
||||
{
|
||||
return ((a_Biome == biOcean) || (a_Biome == biDeepOcean));
|
||||
}
|
||||
|
||||
/** Returns true if the biome is very cold
|
||||
(has snow on ground everywhere, turns top water to ice, has snowfall instead of rain everywhere).
|
||||
Doesn't report mildly cold biomes (where it snows above certain elevation), use IsBiomeCold() for those. */
|
||||
extern bool IsBiomeVeryCold(EMCSBiome a_Biome);
|
||||
|
||||
/** Returns true if the biome is cold
|
||||
(has snow and snowfall at higher elevations but not at regular heights).
|
||||
Doesn't report Very Cold biomes, use IsBiomeVeryCold() for those. */
|
||||
extern bool IsBiomeCold(EMCSBiome a_Biome);
|
||||
|
||||
// tolua_end
|
||||
|
@ -217,7 +217,12 @@ BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString)
|
||||
|
||||
bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
|
||||
{
|
||||
return gsBlockIDMap.ResolveItem(TrimString(a_ItemTypeString), a_Item);
|
||||
AString ItemName = TrimString(a_ItemTypeString);
|
||||
if (ItemName.substr(0, 10) == "minecraft:")
|
||||
{
|
||||
ItemName = ItemName.substr(10);
|
||||
}
|
||||
return gsBlockIDMap.ResolveItem(ItemName, a_Item);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// The following hackery is to allow typed C++ enum for C++ code, yet have ToLua process the values.
|
||||
// ToLua doesn't understand typed enums, so we use preprocessor to hide it from ToLua.
|
||||
|
||||
enum ENUM_BLOCK_ID : BLOCKTYPE
|
||||
#if 0
|
||||
enum ENUM_BLOCK_ID // tolua_export
|
||||
#endif
|
||||
// tolua_begin
|
||||
enum ENUM_BLOCK_ID
|
||||
{
|
||||
E_BLOCK_AIR = 0,
|
||||
E_BLOCK_STONE = 1,
|
||||
@ -221,6 +227,10 @@ enum ENUM_BLOCK_ID
|
||||
};
|
||||
// tolua_end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
enum ENUM_ITEM_ID
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include
|
||||
set(FOLDERS
|
||||
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
|
||||
WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs
|
||||
Noise
|
||||
)
|
||||
|
||||
SET (SRCS
|
||||
@ -50,7 +51,6 @@ SET (SRCS
|
||||
MobProximityCounter.cpp
|
||||
MobSpawner.cpp
|
||||
MonsterConfig.cpp
|
||||
Noise.cpp
|
||||
ProbabDistrib.cpp
|
||||
RankManager.cpp
|
||||
RCONServer.cpp
|
||||
@ -65,7 +65,8 @@ SET (SRCS
|
||||
VoronoiMap.cpp
|
||||
WebAdmin.cpp
|
||||
World.cpp
|
||||
main.cpp)
|
||||
main.cpp
|
||||
)
|
||||
|
||||
SET (HDRS
|
||||
AllocationPool.h
|
||||
@ -119,7 +120,6 @@ SET (HDRS
|
||||
MobProximityCounter.h
|
||||
MobSpawner.h
|
||||
MonsterConfig.h
|
||||
Noise.h
|
||||
ProbabDistrib.h
|
||||
RankManager.h
|
||||
RCONServer.h
|
||||
@ -136,7 +136,8 @@ SET (HDRS
|
||||
VoronoiMap.h
|
||||
WebAdmin.h
|
||||
World.h
|
||||
XMLParser.h)
|
||||
XMLParser.h
|
||||
)
|
||||
|
||||
include_directories(".")
|
||||
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
|
||||
@ -313,7 +314,7 @@ endif ()
|
||||
|
||||
if (NOT MSVC)
|
||||
target_link_libraries(${EXECUTABLE}
|
||||
OSSupport HTTPServer Bindings Items Blocks
|
||||
OSSupport HTTPServer Bindings Items Blocks Noise
|
||||
Protocol Generating Generating_Prefabs WorldStorage
|
||||
Mobs Entities Simulator UI BlockEntities PolarSSL++
|
||||
)
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "BlockEntities/FlowerPotEntity.h"
|
||||
#include "Entities/Pickup.h"
|
||||
#include "Item.h"
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
#include "Root.h"
|
||||
#include "Entities/Player.h"
|
||||
#include "BlockArea.h"
|
||||
|
@ -822,6 +822,7 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M
|
||||
int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
|
||||
cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ);
|
||||
cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
|
||||
cCSLock Lock(m_CSLayers);
|
||||
for (int z = MinChunkZ; z <= MaxChunkZ; z++)
|
||||
{
|
||||
int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width);
|
||||
|
@ -184,7 +184,7 @@ void cChunkSender::Execute(void)
|
||||
while (!m_ShouldTerminate)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
while (m_ChunksReady.empty() && m_SendChunksLowPriority.empty() && m_SendChunksHighPriority.empty())
|
||||
while (m_ChunksReady.empty() && m_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.empty())
|
||||
{
|
||||
int RemoveCount = m_RemoveCount;
|
||||
m_RemoveCount = 0;
|
||||
|
@ -52,7 +52,8 @@ int cClientHandle::s_ClientCount = 0;
|
||||
// cClientHandle:
|
||||
|
||||
cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
|
||||
m_ViewDistance(a_ViewDistance),
|
||||
m_CurrentViewDistance(a_ViewDistance),
|
||||
m_RequestedViewDistance(a_ViewDistance),
|
||||
m_IPString(a_Socket->GetIPString()),
|
||||
m_OutgoingData(64 KiB),
|
||||
m_Player(nullptr),
|
||||
@ -78,6 +79,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
|
||||
m_UniqueID(0),
|
||||
m_HasSentPlayerChunk(false),
|
||||
m_Locale("en_GB"),
|
||||
m_LastPlacedSign(0, -1, 0),
|
||||
m_ProtocolVersion(0)
|
||||
{
|
||||
m_Protocol = new cProtocolRecognizer(this);
|
||||
@ -412,7 +414,7 @@ bool cClientHandle::StreamNextChunk(void)
|
||||
cCSLock Lock(m_CSChunkLists);
|
||||
|
||||
// High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
|
||||
for (int Range = 0; Range < m_ViewDistance; Range++)
|
||||
for (int Range = 0; Range < m_CurrentViewDistance; Range++)
|
||||
{
|
||||
Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
|
||||
|
||||
@ -429,7 +431,7 @@ bool cClientHandle::StreamNextChunk(void)
|
||||
cChunkCoords Coords(ChunkX, ChunkZ);
|
||||
|
||||
// Checks if the chunk is in distance
|
||||
if ((Diff(ChunkX, ChunkPosX) > m_ViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_ViewDistance))
|
||||
if ((Diff(ChunkX, ChunkPosX) > m_CurrentViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_CurrentViewDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -452,7 +454,7 @@ bool cClientHandle::StreamNextChunk(void)
|
||||
}
|
||||
|
||||
// Low priority: Add all chunks that are in range. (From the center out to the edge)
|
||||
for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
|
||||
for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
|
||||
{
|
||||
// For each distance add chunks in a hollow square centered around current position:
|
||||
cChunkCoordsList CurcleChunks;
|
||||
@ -510,7 +512,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
|
||||
{
|
||||
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
|
||||
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
|
||||
if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
|
||||
if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
|
||||
{
|
||||
ChunksToRemove.push_back(*itr);
|
||||
itr = m_LoadedChunks.erase(itr);
|
||||
@ -525,7 +527,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
|
||||
{
|
||||
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
|
||||
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
|
||||
if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
|
||||
if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
|
||||
{
|
||||
itr = m_ChunksToSend.erase(itr);
|
||||
}
|
||||
@ -539,7 +541,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
|
||||
for (cChunkCoordsList::iterator itr = ChunksToRemove.begin(); itr != ChunksToRemove.end(); ++itr)
|
||||
{
|
||||
m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
|
||||
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1482,6 +1484,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
{
|
||||
m_Player->GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
|
||||
cChunkInterface ChunkInterface(World->GetChunkMap());
|
||||
NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
|
||||
|
||||
@ -1659,8 +1662,11 @@ void cClientHandle::HandleUpdateSign(
|
||||
const AString & a_Line3, const AString & a_Line4
|
||||
)
|
||||
{
|
||||
cWorld * World = m_Player->GetWorld();
|
||||
World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
|
||||
if (m_LastPlacedSign.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
|
||||
{
|
||||
m_LastPlacedSign.Set(0, -1, 0);
|
||||
m_Player->GetWorld()->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2237,6 +2243,7 @@ void cClientHandle::SendDisconnect(const AString & a_Reason)
|
||||
|
||||
void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
m_LastPlacedSign.Set(a_BlockX, a_BlockY, a_BlockZ);
|
||||
m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
|
||||
@ -2827,8 +2834,15 @@ void cClientHandle::SetUsername( const AString & a_Username)
|
||||
|
||||
void cClientHandle::SetViewDistance(int a_ViewDistance)
|
||||
{
|
||||
m_ViewDistance = Clamp(a_ViewDistance, MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE);
|
||||
LOGD("Setted %s's view distance to %i", GetUsername().c_str(), m_ViewDistance);
|
||||
m_RequestedViewDistance = a_ViewDistance;
|
||||
LOGD("%s is requesting ViewDistance of %d!", GetUsername().c_str(), m_RequestedViewDistance);
|
||||
|
||||
// Set the current view distance based on the requested VD and world max VD:
|
||||
cWorld * world = m_Player->GetWorld();
|
||||
if (world != nullptr)
|
||||
{
|
||||
m_CurrentViewDistance = Clamp(a_ViewDistance, cClientHandle::MIN_VIEW_DISTANCE, world->GetMaxViewDistance());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -217,8 +217,14 @@ public:
|
||||
|
||||
inline short GetPing(void) const { return static_cast<short>(std::chrono::duration_cast<std::chrono::milliseconds>(m_Ping).count()); }
|
||||
|
||||
/** Sets the maximal view distance. */
|
||||
void SetViewDistance(int a_ViewDistance);
|
||||
int GetViewDistance(void) const { return m_ViewDistance; }
|
||||
|
||||
/** Returns the view distance that the player currently have. */
|
||||
int GetViewDistance(void) const { return m_CurrentViewDistance; }
|
||||
|
||||
/** Returns the view distance that the player request, not the used view distance. */
|
||||
int GetRequestedViewDistance(void) const { return m_RequestedViewDistance; }
|
||||
|
||||
void SetLocale(AString & a_Locale) { m_Locale = a_Locale; }
|
||||
AString GetLocale(void) const { return m_Locale; }
|
||||
@ -333,11 +339,11 @@ private:
|
||||
/** The type used for storing the names of registered plugin channels. */
|
||||
typedef std::set<AString> cChannels;
|
||||
|
||||
/** Number of chunks the player can see in each direction */
|
||||
int m_ViewDistance;
|
||||
/** The actual view distance used, the minimum of client's requested view distance and world's max view distance. */
|
||||
int m_CurrentViewDistance;
|
||||
|
||||
/** Server generates this many chunks AHEAD of player sight. */
|
||||
static const int GENERATEDISTANCE = 2;
|
||||
/** The requested view distance from the player. It isn't clamped with 1 and the max view distance of the world. */
|
||||
int m_RequestedViewDistance;
|
||||
|
||||
AString m_IPString;
|
||||
|
||||
@ -433,6 +439,9 @@ private:
|
||||
/** Client Settings */
|
||||
AString m_Locale;
|
||||
|
||||
/** The positions from the last sign that the player placed. It's needed to verify the sign text change. */
|
||||
Vector3i m_LastPlacedSign;
|
||||
|
||||
/** The plugin channels that the client has registered. */
|
||||
cChannels m_PluginChannels;
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Enchantments.h"
|
||||
#include "WorldStorage/FastNBT.h"
|
||||
#include "FastRandom.h"
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -1596,6 +1596,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
a_World->AddPlayer(this);
|
||||
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
|
||||
|
||||
// Update the view distance.
|
||||
m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,10 @@
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BioGen.h"
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include "IntGen.h"
|
||||
#include "ProtIntGen.h"
|
||||
#include "../IniFile.h"
|
||||
#include "../LinearUpscale.h"
|
||||
|
||||
@ -916,6 +920,214 @@ void cBioGenTwoLevel::InitializeBiomeGen(cIniFile & a_IniFile)
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenGrown:
|
||||
|
||||
class cBioGenGrown:
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenGrown(int a_Seed)
|
||||
{
|
||||
auto FinalRivers =
|
||||
std::make_shared<cIntGenSmooth<8>> (a_Seed + 1,
|
||||
std::make_shared<cIntGenZoom <10>> (a_Seed + 2,
|
||||
std::make_shared<cIntGenRiver <7>> (a_Seed + 3,
|
||||
std::make_shared<cIntGenZoom <9>> (a_Seed + 4,
|
||||
std::make_shared<cIntGenSmooth<6>> (a_Seed + 5,
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 8,
|
||||
std::make_shared<cIntGenSmooth<6>> (a_Seed + 5,
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 9,
|
||||
std::make_shared<cIntGenSmooth<6>> (a_Seed + 5,
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 10,
|
||||
std::make_shared<cIntGenSmooth<6>> (a_Seed + 5,
|
||||
std::make_shared<cIntGenSmooth<8>> (a_Seed + 6,
|
||||
std::make_shared<cIntGenZoom <10>> (a_Seed + 11,
|
||||
std::make_shared<cIntGenChoice<2, 7>>(a_Seed + 12
|
||||
))))))))))))));
|
||||
|
||||
auto alteration =
|
||||
std::make_shared<cIntGenZoom <8>>(a_Seed,
|
||||
std::make_shared<cIntGenLandOcean<6>>(a_Seed, 20
|
||||
));
|
||||
|
||||
auto alteration2 =
|
||||
std::make_shared<cIntGenZoom <8>>(a_Seed + 1,
|
||||
std::make_shared<cIntGenZoom <6>>(a_Seed + 2,
|
||||
std::make_shared<cIntGenZoom <5>>(a_Seed + 1,
|
||||
std::make_shared<cIntGenZoom <4>>(a_Seed + 2,
|
||||
std::make_shared<cIntGenLandOcean<4>>(a_Seed + 1, 10
|
||||
)))));
|
||||
|
||||
auto FinalBiomes =
|
||||
std::make_shared<cIntGenSmooth <8>> (a_Seed + 1,
|
||||
std::make_shared<cIntGenZoom <10>>(a_Seed + 15,
|
||||
std::make_shared<cIntGenSmooth <7>> (a_Seed + 1,
|
||||
std::make_shared<cIntGenZoom <9>> (a_Seed + 16,
|
||||
std::make_shared<cIntGenBeaches <6>> (
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 1,
|
||||
std::make_shared<cIntGenAddIslands <6>> (a_Seed + 2004, 10,
|
||||
std::make_shared<cIntGenAddToOcean <6>> (a_Seed + 10, 500, biDeepOcean,
|
||||
std::make_shared<cIntGenReplaceRandomly<8>> (a_Seed + 1, biPlains, biSunflowerPlains, 20,
|
||||
std::make_shared<cIntGenMBiomes <8>> (a_Seed + 5, alteration2,
|
||||
std::make_shared<cIntGenAlternateBiomes<8>> (a_Seed + 1, alteration,
|
||||
std::make_shared<cIntGenBiomeEdges <8>> (a_Seed + 3,
|
||||
std::make_shared<cIntGenZoom <10>>(a_Seed + 2,
|
||||
std::make_shared<cIntGenZoom <7>> (a_Seed + 4,
|
||||
std::make_shared<cIntGenReplaceRandomly<5>> (a_Seed + 99, biIcePlains, biIcePlainsSpikes, 50,
|
||||
std::make_shared<cIntGenZoom <5>> (a_Seed + 8,
|
||||
std::make_shared<cIntGenAddToOcean <4>> (a_Seed + 10, 300, biDeepOcean,
|
||||
std::make_shared<cIntGenAddToOcean <6>> (a_Seed + 9, 8, biMushroomIsland,
|
||||
std::make_shared<cIntGenBiomes <8>> (a_Seed + 3000,
|
||||
std::make_shared<cIntGenAddIslands <8>> (a_Seed + 2000, 200,
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 5,
|
||||
std::make_shared<cIntGenRareBiomeGroups<6>> (a_Seed + 5, 50,
|
||||
std::make_shared<cIntGenBiomeGroupEdges<6>> (
|
||||
std::make_shared<cIntGenAddIslands <8>> (a_Seed + 2000, 200,
|
||||
std::make_shared<cIntGenZoom <8>> (a_Seed + 7,
|
||||
std::make_shared<cIntGenSetRandomly <6>> (a_Seed + 8, 50, bgOcean,
|
||||
std::make_shared<cIntGenReplaceRandomly<6>> (a_Seed + 101, bgIce, bgTemperate, 150,
|
||||
std::make_shared<cIntGenAddIslands <6>> (a_Seed + 2000, 200,
|
||||
std::make_shared<cIntGenSetRandomly <6>> (a_Seed + 9, 50, bgOcean,
|
||||
std::make_shared<cIntGenZoom <6>> (a_Seed + 10,
|
||||
std::make_shared<cIntGenLandOcean <5>> (a_Seed + 100, 30
|
||||
)))))))))))))))))))))))))))))));
|
||||
|
||||
m_Gen =
|
||||
std::make_shared<cIntGenSmooth <16>>(a_Seed,
|
||||
std::make_shared<cIntGenZoom <18>>(a_Seed,
|
||||
std::make_shared<cIntGenSmooth <11>>(a_Seed,
|
||||
std::make_shared<cIntGenZoom <13>>(a_Seed,
|
||||
std::make_shared<cIntGenMixRivers<8>> (
|
||||
FinalBiomes, FinalRivers
|
||||
)))));
|
||||
}
|
||||
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override
|
||||
{
|
||||
cIntGen<16, 16>::Values vals;
|
||||
m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, vals);
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
cChunkDef::SetBiome(a_Biomes, x, z, (EMCSBiome)vals[x + cChunkDef::Width * z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<cIntGen<16, 16>> m_Gen;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenGrown:
|
||||
|
||||
class cBioGenProtGrown:
|
||||
public cBiomeGen
|
||||
{
|
||||
public:
|
||||
cBioGenProtGrown(int a_Seed)
|
||||
{
|
||||
auto FinalRivers =
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 2,
|
||||
std::make_shared<cProtIntGenRiver >(a_Seed + 3,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 4,
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 5,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 8,
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 5,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 9,
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 5,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 10,
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 5,
|
||||
std::make_shared<cProtIntGenSmooth>(a_Seed + 6,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 11,
|
||||
std::make_shared<cProtIntGenChoice>(a_Seed + 12, 2
|
||||
))))))))))))));
|
||||
|
||||
auto alteration =
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed,
|
||||
std::make_shared<cProtIntGenLandOcean>(a_Seed, 20
|
||||
));
|
||||
|
||||
auto alteration2 =
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 2,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 2,
|
||||
std::make_shared<cProtIntGenLandOcean>(a_Seed + 1, 10
|
||||
)))));
|
||||
|
||||
auto FinalBiomes =
|
||||
std::make_shared<cProtIntGenSmooth >(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 15,
|
||||
std::make_shared<cProtIntGenSmooth >(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 16,
|
||||
std::make_shared<cProtIntGenBeaches >(
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 1,
|
||||
std::make_shared<cProtIntGenAddIslands >(a_Seed + 2004, 10,
|
||||
std::make_shared<cProtIntGenAddToOcean >(a_Seed + 10, 500, biDeepOcean,
|
||||
std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 1, biPlains, biSunflowerPlains, 20,
|
||||
std::make_shared<cProtIntGenMBiomes >(a_Seed + 5, alteration2,
|
||||
std::make_shared<cProtIntGenAlternateBiomes>(a_Seed + 1, alteration,
|
||||
std::make_shared<cProtIntGenBiomeEdges >(a_Seed + 3,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 2,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 4,
|
||||
std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 99, biIcePlains, biIcePlainsSpikes, 50,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 8,
|
||||
std::make_shared<cProtIntGenAddToOcean >(a_Seed + 10, 300, biDeepOcean,
|
||||
std::make_shared<cProtIntGenAddToOcean >(a_Seed + 9, 8, biMushroomIsland,
|
||||
std::make_shared<cProtIntGenBiomes >(a_Seed + 3000,
|
||||
std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 5,
|
||||
std::make_shared<cProtIntGenRareBiomeGroups>(a_Seed + 5, 50,
|
||||
std::make_shared<cProtIntGenBiomeGroupEdges>(
|
||||
std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 7,
|
||||
std::make_shared<cProtIntGenSetRandomly >(a_Seed + 8, 50, bgOcean,
|
||||
std::make_shared<cProtIntGenReplaceRandomly>(a_Seed + 101, bgIce, bgTemperate, 150,
|
||||
std::make_shared<cProtIntGenAddIslands >(a_Seed + 2000, 200,
|
||||
std::make_shared<cProtIntGenSetRandomly >(a_Seed + 9, 50, bgOcean,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed + 10,
|
||||
std::make_shared<cProtIntGenLandOcean >(a_Seed + 100, 30
|
||||
)))))))))))))))))))))))))))))));
|
||||
|
||||
m_Gen =
|
||||
std::make_shared<cProtIntGenSmooth >(a_Seed,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed,
|
||||
std::make_shared<cProtIntGenSmooth >(a_Seed,
|
||||
std::make_shared<cProtIntGenZoom >(a_Seed,
|
||||
std::make_shared<cProtIntGenMixRivers>(
|
||||
FinalBiomes, FinalRivers
|
||||
)))));
|
||||
}
|
||||
|
||||
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override
|
||||
{
|
||||
int vals[16 * 16];
|
||||
m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, 16, 16, vals);
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
cChunkDef::SetBiome(a_Biomes, x, z, (EMCSBiome)vals[x + cChunkDef::Width * z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<cProtIntGen> m_Gen;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBiomeGen:
|
||||
|
||||
@ -952,6 +1164,14 @@ cBiomeGenPtr cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool &
|
||||
{
|
||||
res = new cBioGenTwoLevel(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(BiomeGenName, "grown") == 0)
|
||||
{
|
||||
res = new cBioGenGrown(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(BiomeGenName, "grownprot") == 0)
|
||||
{
|
||||
res = new cBioGenProtGrown(a_Seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NoCaseCompare(BiomeGenName, "multistepmap") != 0)
|
||||
@ -981,3 +1201,51 @@ cBiomeGenPtr cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool &
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Performance tests:
|
||||
|
||||
// Change to 1 to enable the perf test:
|
||||
#if 0
|
||||
|
||||
class cBioGenPerfTest
|
||||
{
|
||||
public:
|
||||
cBioGenPerfTest()
|
||||
{
|
||||
std::cout << "BioGen performance tests commencing, please wait..." << std::endl;
|
||||
TestGen("MultiStepMap", std::make_unique<cBioGenMultiStepMap>(1).get());
|
||||
TestGen("Grown", std::make_unique<cBioGenGrown>(1).get());
|
||||
TestGen("GrownProt", std::make_unique<cBioGenProtGrown>(1).get());
|
||||
std::cout << "BioGen performance tests complete." << std::endl;
|
||||
}
|
||||
|
||||
protected:
|
||||
void TestGen(const AString && a_GenName, cBiomeGen * a_BioGen)
|
||||
{
|
||||
// Initialize the default settings for the generator:
|
||||
cIniFile iniFile;
|
||||
a_BioGen->InitializeBiomeGen(iniFile);
|
||||
|
||||
// Generate the biomes:
|
||||
auto start = std::chrono::system_clock::now();
|
||||
for (int z = 0; z < 100; z++)
|
||||
{
|
||||
for (int x = 0; x < 100; x++)
|
||||
{
|
||||
cChunkDef::BiomeMap biomes;
|
||||
a_BioGen->GenBiomes(x, z, biomes);
|
||||
} // for x
|
||||
} // for z
|
||||
auto dur = std::chrono::system_clock::now() - start;
|
||||
double milliseconds = static_cast<double>((std::chrono::duration_cast<std::chrono::milliseconds>(dur)).count());
|
||||
|
||||
std::cout << a_GenName << ": " << 1000.0 * 100.0 * 100.0 / milliseconds << " chunks per second" << std::endl;
|
||||
}
|
||||
} g_BioGenPerfTest;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ Interfaces to the various biome generators:
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
#include "../VoronoiMap.h"
|
||||
|
||||
|
||||
|
@ -46,6 +46,7 @@ SET (HDRS
|
||||
FinishGen.h
|
||||
GridStructGen.h
|
||||
HeiGen.h
|
||||
IntGen.h
|
||||
MineShafts.h
|
||||
NetherFortGen.h
|
||||
Noise3DGenerator.h
|
||||
@ -53,6 +54,7 @@ SET (HDRS
|
||||
PieceGenerator.h
|
||||
Prefab.h
|
||||
PrefabPiecePool.h
|
||||
ProtIntGen.h
|
||||
RainbowRoadsGen.h
|
||||
Ravines.h
|
||||
RoughRavines.h
|
||||
|
@ -13,7 +13,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "ChunkDesc.h"
|
||||
#include "../BlockArea.h"
|
||||
#include "../Cuboid.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
#include "../BlockEntities/BlockEntity.h"
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -73,6 +73,10 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
|
||||
{
|
||||
res = new cCompoGenNether(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
|
||||
{
|
||||
res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
|
||||
}
|
||||
else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
|
||||
{
|
||||
res = new cNoise3DComposable(a_Seed);
|
||||
|
@ -227,15 +227,15 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
|
||||
/* biMesaPlateau */ { 2.0f, 2.0f}, // 39
|
||||
|
||||
// biomes 40 .. 128 are unused, 89 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 40 .. 49
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 50 .. 59
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 60 .. 69
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 70 .. 79
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 80 .. 89
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 90 .. 99
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 100 .. 109
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 110 .. 119
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, // 120 .. 128
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 40 .. 49
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 50 .. 59
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 60 .. 69
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 70 .. 79
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 80 .. 89
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 90 .. 99
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 100 .. 109
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 110 .. 119
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 120 .. 128
|
||||
|
||||
// Release 1.7 /* biome variants:
|
||||
/* biSunflowerPlains */ { 1.0f, 1.0f}, // 129
|
||||
@ -246,22 +246,22 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
|
||||
/* biSwamplandM */ { 0.0f, 0.0f}, // 134
|
||||
|
||||
// Biomes 135 .. 139 unused, 5 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, // 135 .. 139
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 135 .. 139
|
||||
|
||||
/* biIcePlainsSpikes */ { 1.0f, 1.0f}, // 140
|
||||
|
||||
// Biomes 141 .. 148 unused, 8 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 141 .. 148
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 141 .. 148
|
||||
|
||||
/* biJungleM */ { 4.0f, 4.0f}, // 149
|
||||
{}, // 150
|
||||
{0.0f, 0.0f}, // 150
|
||||
/* biJungleEdgeM */ { 3.0f, 3.0f}, // 151
|
||||
{}, {}, {}, // 152 .. 154
|
||||
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 152 .. 154
|
||||
/* biBirchForestM */ { 3.0f, 3.0f}, // 155
|
||||
/* biBirchForestHillsM */ { 5.0f, 5.0f}, // 156
|
||||
/* biRoofedForestM */ { 2.0f, 2.0f}, // 157
|
||||
/* biColdTaigaM */ { 1.0f, 1.0f}, // 158
|
||||
{}, // 159
|
||||
{0.0f, 0.0f}, // 159
|
||||
/* biMegaSpruceTaiga */ { 3.0f, 3.0f}, // 160
|
||||
/* biMegaSpruceTaigaHills */ { 3.0f, 3.0f}, // 161
|
||||
/* biExtremeHillsPlusM */ {32.0f, 32.0f}, // 162
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "HeiGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "Globals.h"
|
||||
|
||||
#include "FinishGen.h"
|
||||
#include "../Noise.h"
|
||||
#include "../BlockID.h"
|
||||
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
|
||||
#include "../Simulator/FireSimulator.h"
|
||||
@ -200,6 +199,11 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
// Get the top block + 1. This is the place where the grass would finaly be placed:
|
||||
int y = a_ChunkDesc.GetHeight(x, z) + 1;
|
||||
|
||||
if (y >= 255)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if long grass can be placed:
|
||||
if (
|
||||
(a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
|
||||
@ -414,6 +418,11 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// There's no snow in the other biomes.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for z
|
||||
@ -454,6 +463,11 @@ void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// No icy water in other biomes.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for z
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
#include "../ProbabDistrib.h"
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -16,81 +16,6 @@
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cTerrainHeightGen:
|
||||
|
||||
cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
|
||||
{
|
||||
AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
|
||||
if (HeightGenName.empty())
|
||||
{
|
||||
LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
|
||||
HeightGenName = "Biomal";
|
||||
}
|
||||
|
||||
a_CacheOffByDefault = false;
|
||||
cTerrainHeightGen * res = nullptr;
|
||||
if (NoCaseCompare(HeightGenName, "flat") == 0)
|
||||
{
|
||||
res = new cHeiGenFlat;
|
||||
a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "classic") == 0)
|
||||
{
|
||||
res = new cHeiGenClassic(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
|
||||
{
|
||||
res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "End") == 0)
|
||||
{
|
||||
res = new cEndGen(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
|
||||
{
|
||||
res = new cHeiGenMountains(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
|
||||
{
|
||||
res = new cNoise3DComposable(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "biomal") == 0)
|
||||
{
|
||||
res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
|
||||
|
||||
/*
|
||||
// Performance-testing:
|
||||
LOGINFO("Measuring performance of cHeiGenBiomal...");
|
||||
clock_t BeginTick = clock();
|
||||
for (int x = 0; x < 500; x++)
|
||||
{
|
||||
cChunkDef::HeightMap Heights;
|
||||
res->GenHeightMap(x * 5, x * 5, Heights);
|
||||
}
|
||||
clock_t Duration = clock() - BeginTick;
|
||||
LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
|
||||
//*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match found, force-set the default and retry
|
||||
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
|
||||
a_IniFile.DeleteValue("Generator", "HeightGen");
|
||||
a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
|
||||
return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
|
||||
}
|
||||
|
||||
// Read the settings:
|
||||
res->InitializeHeightGen(a_IniFile);
|
||||
|
||||
return cTerrainHeightGenPtr(res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cHeiGenFlat:
|
||||
|
||||
@ -430,15 +355,15 @@ const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[256] =
|
||||
/* biMesaPlateau */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80},
|
||||
|
||||
// biomes 40 .. 128 are unused, 89 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 40 .. 49
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 50 .. 59
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 60 .. 69
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 70 .. 79
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 80 .. 89
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 90 .. 99
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 100 .. 109
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, // 110 .. 119
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}, // 120 .. 128
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 40 .. 49
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 50 .. 59
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 60 .. 69
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 70 .. 79
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 80 .. 89
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 90 .. 99
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 100 .. 109
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 110 .. 119
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 120 .. 128
|
||||
|
||||
/* biSunflowerPlains */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 129
|
||||
/* biDesertM */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 130
|
||||
@ -448,22 +373,22 @@ const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[256] =
|
||||
/* biSwamplandM */ { 1.0f, 3.0f, 1.10f, 7.0f, 0.01f, 0.01f, 60}, // 134
|
||||
|
||||
// Biomes 135 .. 139 unused, 5 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, // 135 .. 139
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 135 .. 139
|
||||
|
||||
/* biIcePlainsSpikes */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 140
|
||||
|
||||
// Biomes 141 .. 148 unused, 8 empty placeholders here:
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, // 141 .. 148
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 141 .. 148
|
||||
|
||||
/* biJungleM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 149
|
||||
{}, // 150
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 150
|
||||
/* biJungleEdgeM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 151
|
||||
{}, {}, {}, // 152 .. 154
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 152 .. 154
|
||||
/* biBirchForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 155
|
||||
/* biBirchForestHillsM */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 156
|
||||
/* biRoofedForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 157
|
||||
/* biColdTaigaM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 158
|
||||
{}, // 159
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 159
|
||||
/* biMegaSpruceTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 160
|
||||
/* biMegaSpruceTaigaHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 161
|
||||
/* biExtremeHillsPlusM */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 120}, // 162
|
||||
@ -611,3 +536,287 @@ NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX,
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cHeiGenMinMax:
|
||||
|
||||
class cHeiGenMinMax:
|
||||
public cTerrainHeightGen
|
||||
{
|
||||
typedef cTerrainHeightGen super;
|
||||
|
||||
/** Size of the averaging process, in columns (for each direction). Must be less than 16. */
|
||||
static const int AVERAGING_SIZE = 4;
|
||||
|
||||
public:
|
||||
cHeiGenMinMax(int a_Seed, cBiomeGenPtr a_BiomeGen):
|
||||
m_Noise(a_Seed),
|
||||
m_BiomeGen(a_BiomeGen),
|
||||
m_TotalWeight(0)
|
||||
{
|
||||
// Initialize the weights:
|
||||
for (int z = 0; z <= AVERAGING_SIZE * 2; z++)
|
||||
{
|
||||
for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
|
||||
{
|
||||
m_Weights[z][x] = 1 + 2 * AVERAGING_SIZE - std::abs(x - AVERAGING_SIZE) - std::abs(z - AVERAGING_SIZE);
|
||||
m_TotalWeight += m_Weights[z][x];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the Perlin generator:
|
||||
m_Perlin.AddOctave(0.04f, 0.2f);
|
||||
m_Perlin.AddOctave(0.02f, 0.1f);
|
||||
m_Perlin.AddOctave(0.01f, 0.05f);
|
||||
}
|
||||
|
||||
|
||||
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
|
||||
{
|
||||
// Generate the biomes for the 3*3 neighbors:
|
||||
cChunkDef::BiomeMap neighborBiomes[3][3];
|
||||
for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++)
|
||||
{
|
||||
m_BiomeGen->GenBiomes(a_ChunkX + x - 1, a_ChunkZ + z - 1, neighborBiomes[z][x]);
|
||||
}
|
||||
|
||||
// Get the min and max heights based on the biomes:
|
||||
double minHeight[cChunkDef::Width * cChunkDef::Width];
|
||||
double maxHeight[cChunkDef::Width * cChunkDef::Width];
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
// For each column, sum the min and max values of the neighborhood around it:
|
||||
double min = 0, max = 0;
|
||||
for (int relz = 0; relz <= AVERAGING_SIZE * 2; relz++)
|
||||
{
|
||||
int bz = z + 16 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start
|
||||
int cz = bz / 16; // Chunk Z coord relative to the neighborBiomes start
|
||||
bz = bz % 16; // Biome Z coord relative to cz in neighborBiomes
|
||||
for (int relx = 0; relx <= AVERAGING_SIZE * 2; relx++)
|
||||
{
|
||||
int bx = x + 16 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start
|
||||
int cx = bx / 16; // Chunk X coord relative to the neighborBiomes start
|
||||
bx = bx % 16; // Biome X coord relative to cz in neighborBiomes
|
||||
|
||||
// Get the biome's min and max heights:
|
||||
double bmin, bmax;
|
||||
getBiomeMinMax(cChunkDef::GetBiome(neighborBiomes[cz][cx], bx, bz), bmin, bmax);
|
||||
|
||||
// Add them to the total, with the weight depending on their relative position to the column:
|
||||
min += bmin * m_Weights[relz][relx];
|
||||
max += bmax * m_Weights[relz][relx];
|
||||
} // for relx
|
||||
} // for relz
|
||||
minHeight[x + z * cChunkDef::Width] = min / m_TotalWeight;
|
||||
maxHeight[x + z * cChunkDef::Width] = max / m_TotalWeight;
|
||||
} // for x
|
||||
} // for z
|
||||
|
||||
// Generate the base noise:
|
||||
NOISE_DATATYPE noise[cChunkDef::Width * cChunkDef::Width];
|
||||
NOISE_DATATYPE workspace[cChunkDef::Width * cChunkDef::Width];
|
||||
NOISE_DATATYPE startX = static_cast<float>(a_ChunkX * cChunkDef::Width);
|
||||
NOISE_DATATYPE endX = startX + cChunkDef::Width - 1;
|
||||
NOISE_DATATYPE startZ = static_cast<float>(a_ChunkZ * cChunkDef::Width);
|
||||
NOISE_DATATYPE endZ = startZ + cChunkDef::Width - 1;
|
||||
m_Perlin.Generate2D(noise, 16, 16, startX, endX, startZ, endZ, workspace);
|
||||
|
||||
// Make the height by ranging the noise between min and max:
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
double min = minHeight[x + z * cChunkDef::Width];
|
||||
double max = maxHeight[x + z * cChunkDef::Width];
|
||||
double h = (max + min) / 2 + noise[x + z * cChunkDef::Width] * (max - min);
|
||||
cChunkDef::SetHeight(a_HeightMap, x, z, static_cast<HEIGHTTYPE>(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void InitializeHeightGen(cIniFile & a_IniFile)
|
||||
{
|
||||
// No settings available
|
||||
}
|
||||
|
||||
protected:
|
||||
cNoise m_Noise;
|
||||
|
||||
cPerlinNoise m_Perlin;
|
||||
|
||||
/** The biome generator to query for the underlying biomes. */
|
||||
cBiomeGenPtr m_BiomeGen;
|
||||
|
||||
/** Weights applied to each of the min / max values in the neighborhood of the currently evaluated column. */
|
||||
double m_Weights[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
|
||||
|
||||
/** Sum of all the m_Weights items. */
|
||||
double m_TotalWeight;
|
||||
|
||||
|
||||
/** Returns the minimum and maximum heights for the given biome. */
|
||||
void getBiomeMinMax(EMCSBiome a_Biome, double & a_Min, double & a_Max)
|
||||
{
|
||||
switch (a_Biome)
|
||||
{
|
||||
case biBeach: a_Min = 61; a_Max = 64; break;
|
||||
case biBirchForest: a_Min = 63; a_Max = 75; break;
|
||||
case biBirchForestHills: a_Min = 63; a_Max = 90; break;
|
||||
case biBirchForestHillsM: a_Min = 63; a_Max = 90; break;
|
||||
case biBirchForestM: a_Min = 63; a_Max = 75; break;
|
||||
case biColdBeach: a_Min = 61; a_Max = 64; break;
|
||||
case biColdTaiga: a_Min = 63; a_Max = 75; break;
|
||||
case biColdTaigaHills: a_Min = 63; a_Max = 90; break;
|
||||
case biColdTaigaM: a_Min = 63; a_Max = 75; break;
|
||||
case biDeepOcean: a_Min = 30; a_Max = 60; break;
|
||||
case biDesert: a_Min = 63; a_Max = 70; break;
|
||||
case biDesertHills: a_Min = 63; a_Max = 85; break;
|
||||
case biDesertM: a_Min = 63; a_Max = 70; break;
|
||||
case biEnd: a_Min = 10; a_Max = 100; break;
|
||||
case biExtremeHills: a_Min = 60; a_Max = 120; break;
|
||||
case biExtremeHillsEdge: a_Min = 63; a_Max = 100; break;
|
||||
case biExtremeHillsM: a_Min = 60; a_Max = 120; break;
|
||||
case biExtremeHillsPlus: a_Min = 60; a_Max = 140; break;
|
||||
case biExtremeHillsPlusM: a_Min = 60; a_Max = 140; break;
|
||||
case biFlowerForest: a_Min = 63; a_Max = 75; break;
|
||||
case biForest: a_Min = 63; a_Max = 75; break;
|
||||
case biForestHills: a_Min = 63; a_Max = 90; break;
|
||||
case biFrozenOcean: a_Min = 45; a_Max = 64; break;
|
||||
case biFrozenRiver: a_Min = 60; a_Max = 62; break;
|
||||
case biIceMountains: a_Min = 63; a_Max = 90; break;
|
||||
case biIcePlains: a_Min = 63; a_Max = 70; break;
|
||||
case biIcePlainsSpikes: a_Min = 60; a_Max = 70; break;
|
||||
case biJungle: a_Min = 60; a_Max = 80; break;
|
||||
case biJungleEdge: a_Min = 62; a_Max = 75; break;
|
||||
case biJungleEdgeM: a_Min = 62; a_Max = 75; break;
|
||||
case biJungleHills: a_Min = 60; a_Max = 90; break;
|
||||
case biJungleM: a_Min = 60; a_Max = 75; break;
|
||||
case biMegaSpruceTaiga: a_Min = 63; a_Max = 75; break;
|
||||
case biMegaSpruceTaigaHills: a_Min = 63; a_Max = 90; break;
|
||||
case biMegaTaiga: a_Min = 63; a_Max = 75; break;
|
||||
case biMegaTaigaHills: a_Min = 63; a_Max = 90; break;
|
||||
case biMesa: a_Min = 63; a_Max = 90; break;
|
||||
case biMesaBryce: a_Min = 60; a_Max = 67; break;
|
||||
case biMesaPlateau: a_Min = 75; a_Max = 85; break;
|
||||
case biMesaPlateauF: a_Min = 80; a_Max = 90; break;
|
||||
case biMesaPlateauFM: a_Min = 80; a_Max = 90; break;
|
||||
case biMesaPlateauM: a_Min = 75; a_Max = 85; break;
|
||||
case biMushroomIsland: a_Min = 63; a_Max = 90; break;
|
||||
case biMushroomShore: a_Min = 60; a_Max = 75; break;
|
||||
case biNether: a_Min = 10; a_Max = 100; break;
|
||||
case biOcean: a_Min = 45; a_Max = 64; break;
|
||||
case biPlains: a_Min = 63; a_Max = 70; break;
|
||||
case biRiver: a_Min = 60; a_Max = 62; break;
|
||||
case biRoofedForest: a_Min = 63; a_Max = 75; break;
|
||||
case biRoofedForestM: a_Min = 63; a_Max = 75; break;
|
||||
case biSavanna: a_Min = 63; a_Max = 75; break;
|
||||
case biSavannaM: a_Min = 63; a_Max = 80; break;
|
||||
case biSavannaPlateau: a_Min = 75; a_Max = 100; break;
|
||||
case biSavannaPlateauM: a_Min = 80; a_Max = 160; break;
|
||||
case biStoneBeach: a_Min = 60; a_Max = 64; break;
|
||||
case biSunflowerPlains: a_Min = 63; a_Max = 70; break;
|
||||
case biSwampland: a_Min = 60; a_Max = 67; break;
|
||||
case biSwamplandM: a_Min = 61; a_Max = 67; break;
|
||||
case biTaiga: a_Min = 63; a_Max = 75; break;
|
||||
case biTaigaHills: a_Min = 63; a_Max = 90; break;
|
||||
case biTaigaM: a_Min = 63; a_Max = 80; break;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unknown biome");
|
||||
a_Min = 10;
|
||||
a_Max = 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cTerrainHeightGen:
|
||||
|
||||
cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
|
||||
{
|
||||
AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
|
||||
if (HeightGenName.empty())
|
||||
{
|
||||
LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
|
||||
HeightGenName = "Biomal";
|
||||
}
|
||||
|
||||
a_CacheOffByDefault = false;
|
||||
cTerrainHeightGen * res = nullptr;
|
||||
if (NoCaseCompare(HeightGenName, "flat") == 0)
|
||||
{
|
||||
res = new cHeiGenFlat;
|
||||
a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "classic") == 0)
|
||||
{
|
||||
res = new cHeiGenClassic(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
|
||||
{
|
||||
res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "End") == 0)
|
||||
{
|
||||
res = new cEndGen(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "MinMax") == 0)
|
||||
{
|
||||
res = new cHeiGenMinMax(a_Seed, a_BiomeGen);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
|
||||
{
|
||||
res = new cHeiGenMountains(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
|
||||
{
|
||||
res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
|
||||
{
|
||||
res = new cNoise3DComposable(a_Seed);
|
||||
}
|
||||
else if (NoCaseCompare(HeightGenName, "biomal") == 0)
|
||||
{
|
||||
res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
|
||||
|
||||
/*
|
||||
// Performance-testing:
|
||||
LOGINFO("Measuring performance of cHeiGenBiomal...");
|
||||
clock_t BeginTick = clock();
|
||||
for (int x = 0; x < 500; x++)
|
||||
{
|
||||
cChunkDef::HeightMap Heights;
|
||||
res->GenHeightMap(x * 5, x * 5, Heights);
|
||||
}
|
||||
clock_t Duration = clock() - BeginTick;
|
||||
LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
|
||||
//*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match found, force-set the default and retry
|
||||
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
|
||||
a_IniFile.DeleteValue("Generator", "HeightGen");
|
||||
a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
|
||||
return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
|
||||
}
|
||||
|
||||
// Read the settings:
|
||||
res->InitializeHeightGen(a_IniFile);
|
||||
|
||||
return cTerrainHeightGenPtr(res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ Interfaces to the various height generators:
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
1406
src/Generating/IntGen.h
Normal file
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Globals.h"
|
||||
#include "Noise3DGenerator.h"
|
||||
#include "../OSSupport/File.h"
|
||||
#include "../OSSupport/Timer.h"
|
||||
#include "../IniFile.h"
|
||||
#include "../LinearInterpolation.h"
|
||||
#include "../LinearUpscale.h"
|
||||
@ -61,6 +62,91 @@ public:
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// Perform speed test of the cInterpolNoise class
|
||||
static class cInterpolNoiseSpeedTest
|
||||
{
|
||||
public:
|
||||
cInterpolNoiseSpeedTest(void)
|
||||
{
|
||||
TestSpeed2D();
|
||||
TestSpeed3D();
|
||||
printf("InterpolNoise speed comparison finished.\n");
|
||||
}
|
||||
|
||||
|
||||
/** Compare the speed of the 3D InterpolNoise vs 3D CubicNoise. */
|
||||
void TestSpeed3D(void)
|
||||
{
|
||||
printf("Evaluating 3D noise performance...\n");
|
||||
static const int SIZE_X = 128;
|
||||
static const int SIZE_Y = 128;
|
||||
static const int SIZE_Z = 128;
|
||||
static const NOISE_DATATYPE MUL = 80;
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
|
||||
cTimer timer;
|
||||
|
||||
// Test the cInterpolNoise:
|
||||
cInterpolNoise<Interp5Deg> interpNoise(1);
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
interpNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
|
||||
}
|
||||
long long end = timer.GetNowTime();
|
||||
printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
|
||||
|
||||
// Test the cCubicNoise:
|
||||
cCubicNoise cubicNoise(1);
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
|
||||
}
|
||||
end = timer.GetNowTime();
|
||||
printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
|
||||
printf("3D noise performance comparison finished.\n");
|
||||
}
|
||||
|
||||
|
||||
/** Compare the speed of the 2D InterpolNoise vs 2D CubicNoise. */
|
||||
void TestSpeed2D(void)
|
||||
{
|
||||
printf("Evaluating 2D noise performance...\n");
|
||||
static const int SIZE_X = 128;
|
||||
static const int SIZE_Y = 128;
|
||||
static const NOISE_DATATYPE MUL = 80;
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y]);
|
||||
cTimer timer;
|
||||
|
||||
// Test the cInterpolNoise:
|
||||
cInterpolNoise<Interp5Deg> interpNoise(1);
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
interpNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
|
||||
}
|
||||
long long end = timer.GetNowTime();
|
||||
printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
|
||||
|
||||
// Test the cCubicNoise:
|
||||
cCubicNoise cubicNoise(1);
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
cubicNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
|
||||
}
|
||||
end = timer.GetNowTime();
|
||||
printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
|
||||
printf("2D noise performance comparison finished.\n");
|
||||
}
|
||||
} g_InterpolNoiseSpeedTest;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cNoise3DGenerator:
|
||||
|
||||
@ -69,9 +155,17 @@ cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
|
||||
m_Perlin(1000),
|
||||
m_Cubic(1000)
|
||||
{
|
||||
m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
|
||||
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
|
||||
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
|
||||
m_Perlin.AddOctave(1, 1);
|
||||
m_Perlin.AddOctave(2, 0.5);
|
||||
m_Perlin.AddOctave(4, 0.25);
|
||||
m_Perlin.AddOctave(8, 0.125);
|
||||
m_Perlin.AddOctave(16, 0.0625);
|
||||
|
||||
m_Cubic.AddOctave(1, 1);
|
||||
m_Cubic.AddOctave(2, 0.5);
|
||||
m_Cubic.AddOctave(4, 0.25);
|
||||
m_Cubic.AddOctave(8, 0.125);
|
||||
m_Cubic.AddOctave(16, 0.0625);
|
||||
|
||||
#if 0
|
||||
// DEBUG: Test the noise generation:
|
||||
@ -154,8 +248,8 @@ void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
|
||||
{
|
||||
// Params:
|
||||
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
|
||||
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
|
||||
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
|
||||
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.1);
|
||||
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 68);
|
||||
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
|
||||
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
|
||||
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
|
||||
@ -221,9 +315,9 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
|
||||
|
||||
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
|
||||
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
|
||||
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
|
||||
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
|
||||
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
|
||||
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
|
||||
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
|
||||
NOISE_DATATYPE StartY = 0;
|
||||
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
|
||||
|
||||
@ -233,23 +327,23 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
|
||||
|
||||
// Precalculate a "height" array:
|
||||
NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
|
||||
m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
|
||||
m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 5, EndX / 5, StartZ / 5, EndZ / 5);
|
||||
for (size_t i = 0; i < ARRAYCOUNT(Height); i++)
|
||||
{
|
||||
Height[i] = std::abs(Height[i]) * m_HeightAmplification + 1;
|
||||
Height[i] = Height[i] * m_HeightAmplification;
|
||||
}
|
||||
|
||||
// Modify the noise by height data:
|
||||
for (int y = 0; y < DIM_Y; y++)
|
||||
{
|
||||
NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
|
||||
AddHeight *= AddHeight * AddHeight;
|
||||
NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 30;
|
||||
// AddHeight *= AddHeight * AddHeight;
|
||||
for (int z = 0; z < DIM_Z; z++)
|
||||
{
|
||||
NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
|
||||
for (int x = 0; x < DIM_X; x++)
|
||||
{
|
||||
CurRow[x] += AddHeight / Height[x + DIM_X * z];
|
||||
CurRow[x] += AddHeight + Height[x + DIM_X * z];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,9 +440,10 @@ void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
|
||||
// cNoise3DComposable:
|
||||
|
||||
cNoise3DComposable::cNoise3DComposable(int a_Seed) :
|
||||
m_Noise1(a_Seed + 1000),
|
||||
m_Noise2(a_Seed + 2000),
|
||||
m_Noise3(a_Seed + 3000)
|
||||
m_ChoiceNoise(a_Seed),
|
||||
m_DensityNoiseA(a_Seed + 1),
|
||||
m_DensityNoiseB(a_Seed + 2),
|
||||
m_BaseNoise(a_Seed + 3)
|
||||
{
|
||||
}
|
||||
|
||||
@ -359,13 +454,51 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) :
|
||||
void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
|
||||
{
|
||||
// Params:
|
||||
// The defaults generate extreme hills terrain
|
||||
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
|
||||
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
|
||||
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.045);
|
||||
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
|
||||
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
|
||||
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
|
||||
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
|
||||
m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
|
||||
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 40);
|
||||
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 40);
|
||||
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 40);
|
||||
m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyX", 40);
|
||||
m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseFrequencyZ", 40);
|
||||
m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyX", 40);
|
||||
m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyY", 80);
|
||||
m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DChoiceFrequencyZ", 40);
|
||||
m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0);
|
||||
int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumChoiceOctaves", 4);
|
||||
int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumDensityOctaves", 6);
|
||||
int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "Noise3DNumBaseOctaves", 6);
|
||||
NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DBaseAmplitude", 1);
|
||||
|
||||
// Add octaves for the choice noise:
|
||||
NOISE_DATATYPE wavlen = 1, ampl = 0.5;
|
||||
for (int i = 0; i < NumChoiceOctaves; i++)
|
||||
{
|
||||
m_ChoiceNoise.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
|
||||
// Add octaves for the density noises:
|
||||
wavlen = 1, ampl = 1;
|
||||
for (int i = 0; i < NumDensityOctaves; i++)
|
||||
{
|
||||
m_DensityNoiseA.AddOctave(wavlen, ampl);
|
||||
m_DensityNoiseB.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
|
||||
// Add octaves for the base noise:
|
||||
wavlen = 1, ampl = BaseNoiseAmplitude;
|
||||
for (int i = 0; i < NumBaseOctaves; i++)
|
||||
{
|
||||
m_BaseNoise.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -376,119 +509,47 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
|
||||
{
|
||||
// The noise for this chunk is already generated in m_Noise
|
||||
// The noise for this chunk is already generated in m_NoiseArray
|
||||
return;
|
||||
}
|
||||
m_LastChunkX = a_ChunkX;
|
||||
m_LastChunkZ = a_ChunkZ;
|
||||
|
||||
// Upscaling parameters:
|
||||
const int UPSCALE_X = 8;
|
||||
const int UPSCALE_Y = 4;
|
||||
const int UPSCALE_Z = 8;
|
||||
// Generate all the noises:
|
||||
NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
|
||||
NOISE_DATATYPE Workspace[5 * 5 * 33];
|
||||
NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
|
||||
NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
|
||||
NOISE_DATATYPE BaseNoise[5 * 5];
|
||||
NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
|
||||
NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
|
||||
// Note that we have to swap the coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
|
||||
m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
|
||||
m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
|
||||
m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
|
||||
m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
|
||||
|
||||
// Precalculate a "height" array:
|
||||
NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
|
||||
for (int z = 0; z < 17; z += UPSCALE_Z)
|
||||
// Calculate the final noise based on the partial noises:
|
||||
for (int y = 0; y < 33; y++)
|
||||
{
|
||||
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
|
||||
for (int x = 0; x < 17; x += UPSCALE_X)
|
||||
NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
|
||||
|
||||
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
|
||||
if (AddHeight < 0)
|
||||
{
|
||||
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
|
||||
NOISE_DATATYPE val = std::abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
|
||||
Height[x + 17 * z] = val * val * val;
|
||||
}
|
||||
AddHeight *= 4;
|
||||
}
|
||||
|
||||
for (int y = 0; y < 257; y += UPSCALE_Y)
|
||||
for (int z = 0; z < 5; z++)
|
||||
{
|
||||
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
|
||||
NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
|
||||
AddHeight *= AddHeight * AddHeight;
|
||||
NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
|
||||
for (int z = 0; z < 17; z += UPSCALE_Z)
|
||||
for (int x = 0; x < 5; x++)
|
||||
{
|
||||
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
|
||||
for (int x = 0; x < 17; x += UPSCALE_X)
|
||||
{
|
||||
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
|
||||
CurFloor[x + 17 * z] = (
|
||||
m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
|
||||
m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
|
||||
m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
|
||||
AddHeight / Height[x + 17 * z]
|
||||
);
|
||||
int idx = x + 5 * z + 5 * 5 * y;
|
||||
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
|
||||
}
|
||||
}
|
||||
// Linear-interpolate this XZ floor:
|
||||
LinearUpscale2DArrayInPlace<17, 17, UPSCALE_X, UPSCALE_Z>(CurFloor);
|
||||
}
|
||||
|
||||
// Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
|
||||
for (int y = 1; y < cChunkDef::Height; y++)
|
||||
{
|
||||
if ((y % UPSCALE_Y) == 0)
|
||||
{
|
||||
// This is the interpolation source floor, already calculated
|
||||
continue;
|
||||
}
|
||||
int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
|
||||
int HiFloorY = LoFloorY + UPSCALE_Y;
|
||||
NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
|
||||
NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
|
||||
NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
|
||||
NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
|
||||
int idx = 0;
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
|
||||
idx += 1;
|
||||
}
|
||||
idx += 1; // Skipping one X column
|
||||
}
|
||||
}
|
||||
|
||||
// The noise array is now fully interpolated
|
||||
/*
|
||||
// DEBUG: Output two images of the array, sliced by XY and XZ:
|
||||
cFile f1;
|
||||
if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
|
||||
{
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int y = 0; y < cChunkDef::Height; y++)
|
||||
{
|
||||
int idx = y * 17 * 17 + z * 17;
|
||||
unsigned char buf[16];
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
|
||||
}
|
||||
f1.Write(buf, 16);
|
||||
} // for y
|
||||
} // for z
|
||||
} // if (XY file open)
|
||||
|
||||
cFile f2;
|
||||
if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
|
||||
{
|
||||
for (int y = 0; y < cChunkDef::Height; y++)
|
||||
{
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
int idx = y * 17 * 17 + z * 17;
|
||||
unsigned char buf[16];
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
|
||||
}
|
||||
f2.Write(buf, 16);
|
||||
} // for z
|
||||
} // for y
|
||||
} // if (XZ file open)
|
||||
*/
|
||||
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
|
||||
}
|
||||
|
||||
|
||||
@ -573,3 +634,348 @@ void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBiomalNoise3DComposable:
|
||||
|
||||
cBiomalNoise3DComposable::cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen) :
|
||||
m_ChoiceNoise(a_Seed),
|
||||
m_DensityNoiseA(a_Seed + 1),
|
||||
m_DensityNoiseB(a_Seed + 2),
|
||||
m_BaseNoise(a_Seed + 3),
|
||||
m_BiomeGen(a_BiomeGen)
|
||||
{
|
||||
// Generate the weight distribution for summing up neighboring biomes:
|
||||
m_WeightSum = 0;
|
||||
for (int z = 0; z <= AVERAGING_SIZE * 2; z++)
|
||||
{
|
||||
for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
|
||||
{
|
||||
m_Weight[z][x] = static_cast<NOISE_DATATYPE>((5 - std::abs(5 - x)) + (5 - std::abs(5 - z)));
|
||||
m_WeightSum += m_Weight[z][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile)
|
||||
{
|
||||
// Params:
|
||||
// The defaults generate extreme hills terrain
|
||||
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DSeaLevel", 62);
|
||||
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyX", 40);
|
||||
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyY", 40);
|
||||
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyZ", 40);
|
||||
m_BaseFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyX", 40);
|
||||
m_BaseFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseFrequencyZ", 40);
|
||||
m_ChoiceFrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyX", 40);
|
||||
m_ChoiceFrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyY", 80);
|
||||
m_ChoiceFrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DChoiceFrequencyZ", 40);
|
||||
m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DAirThreshold", 0);
|
||||
int NumChoiceOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumChoiceOctaves", 4);
|
||||
int NumDensityOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumDensityOctaves", 6);
|
||||
int NumBaseOctaves = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DNumBaseOctaves", 6);
|
||||
NOISE_DATATYPE BaseNoiseAmplitude = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DBaseAmplitude", 1);
|
||||
|
||||
// Add octaves for the choice noise:
|
||||
NOISE_DATATYPE wavlen = 1, ampl = 0.5;
|
||||
for (int i = 0; i < NumChoiceOctaves; i++)
|
||||
{
|
||||
m_ChoiceNoise.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
|
||||
// Add octaves for the density noises:
|
||||
wavlen = 1, ampl = 1;
|
||||
for (int i = 0; i < NumDensityOctaves; i++)
|
||||
{
|
||||
m_DensityNoiseA.AddOctave(wavlen, ampl);
|
||||
m_DensityNoiseB.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
|
||||
// Add octaves for the base noise:
|
||||
wavlen = 1, ampl = BaseNoiseAmplitude;
|
||||
for (int i = 0; i < NumBaseOctaves; i++)
|
||||
{
|
||||
m_BaseNoise.AddOctave(wavlen, ampl);
|
||||
wavlen = wavlen * 2;
|
||||
ampl = ampl / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
|
||||
{
|
||||
// The noise for this chunk is already generated in m_NoiseArray
|
||||
return;
|
||||
}
|
||||
m_LastChunkX = a_ChunkX;
|
||||
m_LastChunkZ = a_ChunkZ;
|
||||
|
||||
// Calculate the parameters for the biomes:
|
||||
ChunkParam MidPoint;
|
||||
ChunkParam HeightAmp;
|
||||
CalcBiomeParamArrays(a_ChunkX, a_ChunkZ, HeightAmp, MidPoint);
|
||||
|
||||
// Generate all the noises:
|
||||
NOISE_DATATYPE ChoiceNoise[5 * 5 * 33];
|
||||
NOISE_DATATYPE Workspace[5 * 5 * 33];
|
||||
NOISE_DATATYPE DensityNoiseA[5 * 5 * 33];
|
||||
NOISE_DATATYPE DensityNoiseB[5 * 5 * 33];
|
||||
NOISE_DATATYPE BaseNoise[5 * 5];
|
||||
NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
|
||||
NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
|
||||
// Note that we have to swap the coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
|
||||
m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
|
||||
m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
|
||||
m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
|
||||
m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
|
||||
|
||||
// Calculate the final noise based on the partial noises:
|
||||
for (int y = 0; y < 33; y++)
|
||||
{
|
||||
NOISE_DATATYPE BlockHeight = static_cast<NOISE_DATATYPE>(y * 8);
|
||||
for (int z = 0; z < 5; z++)
|
||||
{
|
||||
for (int x = 0; x < 5; x++)
|
||||
{
|
||||
NOISE_DATATYPE AddHeight = (BlockHeight - MidPoint[x + 5 * z]) * HeightAmp[x + 5 * z];
|
||||
|
||||
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
|
||||
if (AddHeight < 0)
|
||||
{
|
||||
AddHeight *= 4;
|
||||
}
|
||||
|
||||
int idx = x + 5 * z + 5 * 5 * y;
|
||||
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
|
||||
}
|
||||
}
|
||||
}
|
||||
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint)
|
||||
{
|
||||
// Generate the 3*3 chunks of biomes around this chunk:
|
||||
cChunkDef::BiomeMap neighborBiomes[3 * 3];
|
||||
for (int z = 0; z < 3; z++)
|
||||
{
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
m_BiomeGen->GenBiomes(a_ChunkX + x - 1, a_ChunkZ + z - 1, neighborBiomes[x + 3 * z]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up the biome values:
|
||||
for (int z = 0; z < 5; z++)
|
||||
{
|
||||
for (int x = 0; x < 5; x++)
|
||||
{
|
||||
NOISE_DATATYPE totalHeightAmp = 0;
|
||||
NOISE_DATATYPE totalMidPoint = 0;
|
||||
// Add up the biomes around this point:
|
||||
for (int relz = 0; relz <= AVERAGING_SIZE * 2; ++relz)
|
||||
{
|
||||
int colz = 16 + z * 4 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start
|
||||
int neicellz = colz / 16; // Chunk Z coord relative to the neighborBiomes start
|
||||
int neirelz = colz % 16; // Biome Z coord relative to cz in neighborBiomes
|
||||
for (int relx = 0; relx <= AVERAGING_SIZE * 2; ++relx)
|
||||
{
|
||||
int colx = 16 + x * 4 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start
|
||||
int neicellx = colx / 16; // Chunk X coord relative to the neighborBiomes start
|
||||
int neirelx = colx % 16; // Biome X coord relative to cz in neighborBiomes
|
||||
EMCSBiome biome = cChunkDef::GetBiome(neighborBiomes[neicellx + neicellz * 3], neirelx, neirelz);
|
||||
NOISE_DATATYPE heightAmp, midPoint;
|
||||
GetBiomeParams(biome, heightAmp, midPoint);
|
||||
totalHeightAmp += heightAmp * m_Weight[relz][relx];
|
||||
totalMidPoint += midPoint * m_Weight[relz][relx];
|
||||
} // for relx
|
||||
} // for relz
|
||||
a_HeightAmp[x + 5 * z] = totalHeightAmp / m_WeightSum;
|
||||
a_MidPoint[x + 5 * z] = totalMidPoint / m_WeightSum;
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint)
|
||||
{
|
||||
switch (a_Biome)
|
||||
{
|
||||
case biBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biBirchForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
|
||||
case biBirchForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biBirchForestHillsM: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biBirchForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
|
||||
case biColdBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biDesertHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biDeepOcean: a_HeightAmp = 0.17f; a_MidPoint = 35; break;
|
||||
case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
|
||||
case biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
|
||||
case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
|
||||
case biExtremeHillsPlus: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
|
||||
case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
|
||||
case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
|
||||
case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 53; break;
|
||||
case biFrozenOcean: a_HeightAmp = 0.17f; a_MidPoint = 47; break;
|
||||
case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biIcePlainsSpikes: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biJungle: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
|
||||
case biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
|
||||
case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
case biMushroomShore: a_HeightAmp = 0.15f; a_MidPoint = 15; break;
|
||||
case biOcean: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
|
||||
case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 53; break;
|
||||
case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
|
||||
case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
|
||||
case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
|
||||
|
||||
/*
|
||||
// Still missing:
|
||||
case biColdTaiga: a_HeightAmp = 0.15f; a_MidPoint = 30; break;
|
||||
case biColdTaigaHills: a_HeightAmp = 0.15f; a_MidPoint = 31; break;
|
||||
case biColdTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biDesertM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biExtremeHillsEdge: a_HeightAmp = 0.15f; a_MidPoint = 20; break;
|
||||
case biExtremeHillsM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biExtremeHillsPlusM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 23; break;
|
||||
case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biMegaSpruceTaiga: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biMegaTaiga: a_HeightAmp = 0.15f; a_MidPoint = 32; break;
|
||||
case biMesa: a_HeightAmp = 0.15f; a_MidPoint = 37; break;
|
||||
case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biMesaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 39; break;
|
||||
case biMesaPlateauF: a_HeightAmp = 0.15f; a_MidPoint = 38; break;
|
||||
case biMesaPlateauFM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biMesaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biMushroomIsland: a_HeightAmp = 0.15f; a_MidPoint = 14; break;
|
||||
case biNether: a_HeightAmp = 0.15f; a_MidPoint = 68; break;
|
||||
case biRoofedForest: a_HeightAmp = 0.15f; a_MidPoint = 29; break;
|
||||
case biRoofedForestM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biSavanna: a_HeightAmp = 0.15f; a_MidPoint = 35; break;
|
||||
case biSavannaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biSavannaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 36; break;
|
||||
case biSavannaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biStoneBeach: a_HeightAmp = 0.15f; a_MidPoint = 25; break;
|
||||
case biSunflowerPlains: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
case biTaiga: a_HeightAmp = 0.15f; a_MidPoint = 65; break;
|
||||
case biTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
|
||||
*/
|
||||
|
||||
default:
|
||||
{
|
||||
// Make a crazy terrain so that it stands out
|
||||
a_HeightAmp = 0.001f;
|
||||
a_MidPoint = 90;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
|
||||
{
|
||||
GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
|
||||
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
|
||||
for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
|
||||
{
|
||||
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
|
||||
{
|
||||
cChunkDef::SetHeight(a_HeightMap, x, z, y);
|
||||
break;
|
||||
}
|
||||
} // for y
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
|
||||
|
||||
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
|
||||
|
||||
// Make basic terrain composition:
|
||||
for (int z = 0; z < cChunkDef::Width; z++)
|
||||
{
|
||||
for (int x = 0; x < cChunkDef::Width; x++)
|
||||
{
|
||||
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
|
||||
bool HasHadWater = false;
|
||||
for (int y = LastAir; y < m_SeaLevel; y++)
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
|
||||
}
|
||||
for (int y = LastAir - 1; y > 0; y--)
|
||||
{
|
||||
if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
|
||||
{
|
||||
// "air" part
|
||||
LastAir = y;
|
||||
if (y < m_SeaLevel)
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
|
||||
HasHadWater = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// "ground" part:
|
||||
if (LastAir - y > 4)
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
|
||||
continue;
|
||||
}
|
||||
if (HasHadWater)
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
|
||||
}
|
||||
} // for y
|
||||
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
|
||||
} // for x
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
|
||||
// Noise3DGenerator.h
|
||||
|
||||
// Generates terrain using 3D noise, rather than composing. Is a test.
|
||||
// Declares cNoise3DGenerator and cNoise3DComposable classes, representing 3D-noise-based generators.
|
||||
// They generate terrain shape by combining a lerp of two 3D noises with a vertical linear gradient
|
||||
// cNoise3DGenerator is obsolete and unmaintained.
|
||||
// cNoise3DComposable is used to test parameter combinations for single-biome worlds.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -9,7 +13,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
#include "../Noise/InterpolNoise.h"
|
||||
|
||||
|
||||
|
||||
@ -30,17 +35,20 @@ public:
|
||||
|
||||
protected:
|
||||
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
|
||||
static const int UPSCALE_X = 8;
|
||||
static const int UPSCALE_Y = 4;
|
||||
static const int UPSCALE_Z = 8;
|
||||
static const int UPSCALE_X = 4;
|
||||
static const int UPSCALE_Y = 8;
|
||||
static const int UPSCALE_Z = 4;
|
||||
|
||||
// Linear interpolation buffer dimensions, calculated from the step sizes:
|
||||
static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
|
||||
static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
|
||||
static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
|
||||
|
||||
cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
|
||||
cCubicNoise m_Cubic; // The noise used for heightmap directing
|
||||
/** The base 3D noise source for the actual composition */
|
||||
cOctavedNoise<cInterp5DegNoise> m_Perlin;
|
||||
|
||||
/** The noise used for heightmap directing. */
|
||||
cOctavedNoise<cInterp5DegNoise> m_Cubic;
|
||||
|
||||
int m_SeaLevel;
|
||||
NOISE_DATATYPE m_HeightAmplification;
|
||||
@ -74,31 +82,147 @@ public:
|
||||
void Initialize(cIniFile & a_IniFile);
|
||||
|
||||
protected:
|
||||
cNoise m_Noise1;
|
||||
cNoise m_Noise2;
|
||||
cNoise m_Noise3;
|
||||
/** The noise that is used to choose between density noise A and B. */
|
||||
cPerlinNoise m_ChoiceNoise;
|
||||
|
||||
/** Density 3D noise, variant A. */
|
||||
cPerlinNoise m_DensityNoiseA;
|
||||
|
||||
/** Density 3D noise, variant B. */
|
||||
cPerlinNoise m_DensityNoiseB;
|
||||
|
||||
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
|
||||
cPerlinNoise m_BaseNoise;
|
||||
|
||||
/** Block height of the sealevel, used for composing the terrain. */
|
||||
int m_SeaLevel;
|
||||
|
||||
/** The main parameter of the generator, specifies the slope of the vertical linear gradient.
|
||||
A higher value means a steeper slope and a smaller total amplitude of the generated terrain. */
|
||||
NOISE_DATATYPE m_HeightAmplification;
|
||||
NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
|
||||
|
||||
/** Where the vertical "center" of the noise should be, as block height. */
|
||||
NOISE_DATATYPE m_MidPoint;
|
||||
|
||||
// Frequency of the 3D noise's first octave:
|
||||
NOISE_DATATYPE m_FrequencyX;
|
||||
NOISE_DATATYPE m_FrequencyY;
|
||||
NOISE_DATATYPE m_FrequencyZ;
|
||||
|
||||
// Frequency of the base terrain noise:
|
||||
NOISE_DATATYPE m_BaseFrequencyX;
|
||||
NOISE_DATATYPE m_BaseFrequencyZ;
|
||||
|
||||
// Frequency of the choice noise:
|
||||
NOISE_DATATYPE m_ChoiceFrequencyX;
|
||||
NOISE_DATATYPE m_ChoiceFrequencyY;
|
||||
NOISE_DATATYPE m_ChoiceFrequencyZ;
|
||||
|
||||
// Threshold for when the values are considered air:
|
||||
NOISE_DATATYPE m_AirThreshold;
|
||||
|
||||
// Cache for the last calculated chunk (reused between heightmap and composition queries):
|
||||
int m_LastChunkX;
|
||||
int m_LastChunkZ;
|
||||
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
|
||||
|
||||
|
||||
/// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
|
||||
/** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
|
||||
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
// cTerrainHeightGen overrides:
|
||||
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
|
||||
virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
|
||||
|
||||
// cTerrainCompositionGen overrides:
|
||||
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
|
||||
virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBiomalNoise3DComposable :
|
||||
public cTerrainHeightGen,
|
||||
public cTerrainCompositionGen
|
||||
{
|
||||
public:
|
||||
cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
|
||||
|
||||
void Initialize(cIniFile & a_IniFile);
|
||||
|
||||
protected:
|
||||
/** Number of columns around the pixel to query for biomes for averaging. */
|
||||
static const int AVERAGING_SIZE = 5;
|
||||
|
||||
/** Type used for a single parameter across the entire (downscaled) chunk. */
|
||||
typedef NOISE_DATATYPE ChunkParam[5 * 5];
|
||||
|
||||
|
||||
/** The noise that is used to choose between density noise A and B. */
|
||||
cPerlinNoise m_ChoiceNoise;
|
||||
|
||||
/** Density 3D noise, variant A. */
|
||||
cPerlinNoise m_DensityNoiseA;
|
||||
|
||||
/** Density 3D noise, variant B. */
|
||||
cPerlinNoise m_DensityNoiseB;
|
||||
|
||||
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
|
||||
cPerlinNoise m_BaseNoise;
|
||||
|
||||
/** The underlying biome generator. */
|
||||
cBiomeGenPtr m_BiomeGen;
|
||||
|
||||
/** Block height of the sealevel, used for composing the terrain. */
|
||||
int m_SeaLevel;
|
||||
|
||||
// Frequency of the 3D noise's first octave:
|
||||
NOISE_DATATYPE m_FrequencyX;
|
||||
NOISE_DATATYPE m_FrequencyY;
|
||||
NOISE_DATATYPE m_FrequencyZ;
|
||||
|
||||
// Frequency of the base terrain noise:
|
||||
NOISE_DATATYPE m_BaseFrequencyX;
|
||||
NOISE_DATATYPE m_BaseFrequencyZ;
|
||||
|
||||
// Frequency of the choice noise:
|
||||
NOISE_DATATYPE m_ChoiceFrequencyX;
|
||||
NOISE_DATATYPE m_ChoiceFrequencyY;
|
||||
NOISE_DATATYPE m_ChoiceFrequencyZ;
|
||||
|
||||
// Threshold for when the values are considered air:
|
||||
NOISE_DATATYPE m_AirThreshold;
|
||||
|
||||
// Cache for the last calculated chunk (reused between heightmap and composition queries):
|
||||
int m_LastChunkX;
|
||||
int m_LastChunkZ;
|
||||
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
|
||||
|
||||
/** Weights for summing up neighboring biomes. */
|
||||
NOISE_DATATYPE m_Weight[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
|
||||
|
||||
/** The sum of m_Weight[]. */
|
||||
NOISE_DATATYPE m_WeightSum;
|
||||
|
||||
|
||||
/** Generates the 3D noise array used for terrain generation (m_NoiseArray), unless the LastChunk coords are equal to coords given */
|
||||
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
/** Calculates the biome-related parameters for the chunk. */
|
||||
void CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint);
|
||||
|
||||
/** Returns the parameters for the specified biome. */
|
||||
void GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint);
|
||||
|
||||
// cTerrainHeightGen overrides:
|
||||
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
|
||||
virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
|
||||
|
||||
// cTerrainCompositionGen overrides:
|
||||
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
|
||||
virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ Each uses a slightly different approach to generating:
|
||||
|
||||
#include "../Defines.h"
|
||||
#include "../Cuboid.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
1351
src/Generating/ProtIntGen.h
Normal file
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -361,7 +361,109 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
|
||||
|
||||
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
|
||||
{
|
||||
// TODO
|
||||
int Height = 7 + a_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) % 4;
|
||||
|
||||
// Array with possible directions for a branch to go to.
|
||||
const Vector3d AvailableDirections[] =
|
||||
{
|
||||
{ -1, 0, 0 }, { 0, 0, -1 },
|
||||
{ -1, 0, 1 }, { -1, 0, -1 },
|
||||
{ 1, 0, 1 }, { 1, 0, -1 },
|
||||
{ 1, 0, 0 }, { 0, 0, 1 },
|
||||
|
||||
{ -0.5, 0, 0 }, { 0, 0, -0.5 },
|
||||
{ -0.5, 0, 0.5 }, { -0.5, 0, -0.5 },
|
||||
{ 0.5, 0, 0.5 }, { 0.5, 0, -0.5 },
|
||||
{ 0.5, 0, 0 }, { 0, 0, 0.5 },
|
||||
|
||||
{ -1, 0.5, 0 }, { 0, 0.5, -1 },
|
||||
{ -1, 0.5, 1 }, { -1, 0.5, -1 },
|
||||
{ 1, 0.5, 1 }, { 1, 0.5, -1 },
|
||||
{ 1, 0.5, 0 }, { 0, 0.5, 1 },
|
||||
|
||||
{ -0.5, 0.5, 0 }, { 0, 0.5, -0.5 },
|
||||
{ -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 },
|
||||
{ 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 },
|
||||
{ 0.5, 0.5, 0 }, { 0, 0.5, 0.5 },
|
||||
|
||||
};
|
||||
|
||||
// Create branches
|
||||
for (int i = 4; i < Height; i++)
|
||||
{
|
||||
// Get a direction for the trunk to go to.
|
||||
Vector3d BranchStartDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)];
|
||||
Vector3d BranchDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY / i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)] / 3;
|
||||
|
||||
int BranchLength = 2 + a_Noise.IntNoise3DInt(a_BlockX * a_Seq, a_BlockY * a_Seq, a_BlockZ * a_Seq) % 3;
|
||||
GetLargeAppleTreeBranch(a_BlockX, a_BlockY + i, a_BlockZ, BranchLength, BranchStartDirection, BranchDirection, a_BlockY + Height, a_Noise, a_LogBlocks);
|
||||
}
|
||||
|
||||
// Place leaves around each log block
|
||||
for (auto itr : a_LogBlocks)
|
||||
{
|
||||
// Get the log's X and Z coordinates
|
||||
int X = itr.ChunkX * 16 + itr.x;
|
||||
int Z = itr.ChunkZ * 16 + itr.z;
|
||||
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
}
|
||||
PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
}
|
||||
|
||||
// Trunk:
|
||||
for (int i = 0; i < Height; i++)
|
||||
{
|
||||
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks)
|
||||
{
|
||||
Vector3d CurrentPos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
|
||||
Vector3d Direction = a_StartDirection;
|
||||
for (int i = 0; i < a_BranchLength; i++)
|
||||
{
|
||||
CurrentPos += Direction;
|
||||
if (CurrentPos.y >= a_TreeHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Direction -= a_Direction;
|
||||
Direction.clamp(-1.0, 1.0);
|
||||
a_LogBlocks.push_back(sSetBlock(FloorC(CurrentPos.x), FloorC(CurrentPos.y), FloorC(CurrentPos.z), E_BLOCK_LOG, GetLogMetaFromDirection(E_META_LOG_APPLE, Direction)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction)
|
||||
{
|
||||
a_Direction.abs();
|
||||
|
||||
if ((a_Direction.y > a_Direction.x) && (a_Direction.y > a_Direction.z))
|
||||
{
|
||||
return a_BlockMeta;
|
||||
}
|
||||
else if (a_Direction.x > a_Direction.z)
|
||||
{
|
||||
return a_BlockMeta + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_BlockMeta + 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is
|
||||
#pragma once
|
||||
|
||||
#include "../ChunkDef.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
@ -62,6 +62,12 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
|
||||
/// Generates an image of a large (branching) apple tree
|
||||
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
|
||||
|
||||
/// Generates a branch for a large apple tree
|
||||
void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks);
|
||||
|
||||
/// Returns the meta for a log from the given direction
|
||||
NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction);
|
||||
|
||||
/// Generates an image of a random birch tree
|
||||
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Email: Shane.Hill@dsto.defence.gov.au
|
||||
// Reason: Remove dependancy on MFC. Code should compile on any
|
||||
// platform.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
!! MODIFIED BY FAKETRUTH and xoft !!
|
||||
@ -866,7 +866,7 @@ AString cIniFile::CheckCase(const AString & s) const
|
||||
|
||||
void cIniFile::RemoveBom(AString & a_line) const
|
||||
{
|
||||
// The BOM sequence for UTF-8 is 0xEF,0xBB,0xBF
|
||||
// The BOM sequence for UTF-8 is 0xEF, 0xBB, 0xBF
|
||||
static unsigned const char BOM[] = { 0xEF, 0xBB, 0xBF };
|
||||
|
||||
// The BOM sequence, if present, is always th e first three characters of the input.
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Email: Shane.Hill@dsto.defence.gov.au
|
||||
// Reason: Remove dependancy on MFC. Code should compile on any
|
||||
// platform. Tested on Windows/Linux/Irix
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
!! MODIFIED BY FAKETRUTH and madmaxoft!!
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Globals.h"
|
||||
#include "ItemGrid.h"
|
||||
#include "Items/ItemHandler.h"
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -98,3 +98,23 @@ void cPig::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
if (!super::DoTakeDamage(a_TDI))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtLightning)
|
||||
{
|
||||
Destroy();
|
||||
m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtZombiePigman);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -17,6 +17,9 @@ public:
|
||||
|
||||
CLASS_PROTODEF(cPig)
|
||||
|
||||
// cEntity overrides
|
||||
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
|
||||
|
||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
|
||||
virtual void OnRightClicked(cPlayer & a_Player) override;
|
||||
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
|
||||
|
@ -37,6 +37,13 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
m_World->BroadcastEntityStatus(*this, esVillagerAngry);
|
||||
}
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtLightning)
|
||||
{
|
||||
Destroy();
|
||||
m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtWitch);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
21
src/Noise/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
project (MCServer)
|
||||
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
SET (SRCS
|
||||
Noise.cpp
|
||||
)
|
||||
|
||||
SET (HDRS
|
||||
Noise.h
|
||||
OctavedNoise.h
|
||||
RidgedNoise.h
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
add_library(Noise ${SRCS} ${HDRS})
|
||||
|
||||
target_link_libraries(Noise OSSupport)
|
||||
endif()
|
524
src/Noise/InterpolNoise.h
Normal file
@ -0,0 +1,524 @@
|
||||
|
||||
// InterpolNoise.h
|
||||
|
||||
// Implements the cInterpolNoise class template representing a noise that interpolates the values between integer coords from a single set of neighbors
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Noise.h"
|
||||
|
||||
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cInterpolCell2D:
|
||||
|
||||
template <typename T>
|
||||
class cInterpolCell2D
|
||||
{
|
||||
public:
|
||||
cInterpolCell2D(
|
||||
const cNoise & a_Noise, ///< Noise to use for generating the random values
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
|
||||
const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values
|
||||
):
|
||||
m_Noise(a_Noise),
|
||||
m_WorkRnds(&m_Workspace1),
|
||||
m_CurFloorX(0),
|
||||
m_CurFloorY(0),
|
||||
m_Array(a_Array),
|
||||
m_SizeX(a_SizeX),
|
||||
m_SizeY(a_SizeY),
|
||||
m_FracX(a_FracX),
|
||||
m_FracY(a_FracY)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Generates part of the output noise array using the current m_WorkRnds[] values */
|
||||
void Generate(
|
||||
int a_FromX, int a_ToX,
|
||||
int a_FromY, int a_ToY
|
||||
)
|
||||
{
|
||||
for (int y = a_FromY; y < a_ToY; y++)
|
||||
{
|
||||
NOISE_DATATYPE Interp[2];
|
||||
NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
|
||||
Interp[0] = Lerp((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], FracY);
|
||||
Interp[1] = Lerp((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], FracY);
|
||||
int idx = y * m_SizeX + a_FromX;
|
||||
for (int x = a_FromX; x < a_ToX; x++)
|
||||
{
|
||||
m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
|
||||
} // for x
|
||||
} // for y
|
||||
}
|
||||
|
||||
|
||||
/** Initializes m_WorkRnds[] with the specified values of the noise at the specified integral coords. */
|
||||
void InitWorkRnds(int a_FloorX, int a_FloorY)
|
||||
{
|
||||
m_CurFloorX = a_FloorX;
|
||||
m_CurFloorY = a_FloorY;
|
||||
(*m_WorkRnds)[0][0] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY);
|
||||
(*m_WorkRnds)[0][1] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY + 1);
|
||||
(*m_WorkRnds)[1][0] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY);
|
||||
(*m_WorkRnds)[1][1] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY + 1);
|
||||
}
|
||||
|
||||
|
||||
/** Updates m_WorkRnds[] for the new integral coords */
|
||||
void Move(int a_NewFloorX, int a_NewFloorY)
|
||||
{
|
||||
// Swap the doublebuffer:
|
||||
int OldFloorX = m_CurFloorX;
|
||||
int OldFloorY = m_CurFloorY;
|
||||
Workspace * OldWorkRnds = m_WorkRnds;
|
||||
m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
|
||||
|
||||
// Reuse as much of the old workspace as possible:
|
||||
// TODO: Try out if simply calculating all 4 elements each time is faster than this monster loop
|
||||
int DiffX = OldFloorX - a_NewFloorX;
|
||||
int DiffY = OldFloorY - a_NewFloorY;
|
||||
for (int x = 0; x < 2; x++)
|
||||
{
|
||||
int cx = a_NewFloorX + x;
|
||||
int OldX = x - DiffX; // Where would this X be in the old grid?
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
int cy = a_NewFloorY + y;
|
||||
int OldY = y - DiffY; // Where would this Y be in the old grid?
|
||||
if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2))
|
||||
{
|
||||
(*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY];
|
||||
}
|
||||
else
|
||||
{
|
||||
(*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_CurFloorX = a_NewFloorX;
|
||||
m_CurFloorY = a_NewFloorY;
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef NOISE_DATATYPE Workspace[2][2];
|
||||
|
||||
/** The noise used for generating the values at integral coords. */
|
||||
const cNoise & m_Noise;
|
||||
|
||||
/** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
|
||||
Workspace * m_WorkRnds;
|
||||
|
||||
/** Buffer 1 for workspace doublebuffering, used in Move() */
|
||||
Workspace m_Workspace1;
|
||||
|
||||
/** Buffer 2 for workspace doublebuffering, used in Move() */
|
||||
Workspace m_Workspace2;
|
||||
|
||||
/** Coords of the currently calculated m_WorkRnds[]. */
|
||||
int m_CurFloorX, m_CurFloorY;
|
||||
|
||||
/** The output array to generate into. */
|
||||
NOISE_DATATYPE * m_Array;
|
||||
|
||||
/** Dimensions of the output array. */
|
||||
int m_SizeX, m_SizeY;
|
||||
|
||||
/** Arrays holding the fractional values of the coords in each direction. */
|
||||
const NOISE_DATATYPE * m_FracX;
|
||||
const NOISE_DATATYPE * m_FracY;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cInterpolCell3D:
|
||||
|
||||
/** Holds a cache of the last calculated integral noise values and interpolates between them en masse.
|
||||
Provides a massive optimization for cInterpolNoise.
|
||||
Works by calculating multiple noise values (that have the same integral noise coords) at once. The underlying noise values
|
||||
needn't be recalculated for these values, only the interpolation is done within the unit cube. */
|
||||
template <typename T>
|
||||
class cInterpolCell3D
|
||||
{
|
||||
public:
|
||||
cInterpolCell3D(
|
||||
const cNoise & a_Noise, ///< Noise to use for generating the random values
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
|
||||
const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values
|
||||
const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values
|
||||
):
|
||||
m_Noise(a_Noise),
|
||||
m_WorkRnds(&m_Workspace1),
|
||||
m_CurFloorX(0),
|
||||
m_CurFloorY(0),
|
||||
m_CurFloorZ(0),
|
||||
m_Array(a_Array),
|
||||
m_SizeX(a_SizeX),
|
||||
m_SizeY(a_SizeY),
|
||||
m_SizeZ(a_SizeZ),
|
||||
m_FracX(a_FracX),
|
||||
m_FracY(a_FracY),
|
||||
m_FracZ(a_FracZ)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Generates part of the output array using current m_WorkRnds[]. */
|
||||
void Generate(
|
||||
int a_FromX, int a_ToX,
|
||||
int a_FromY, int a_ToY,
|
||||
int a_FromZ, int a_ToZ
|
||||
)
|
||||
{
|
||||
for (int z = a_FromZ; z < a_ToZ; z++)
|
||||
{
|
||||
int idxZ = z * m_SizeX * m_SizeY;
|
||||
NOISE_DATATYPE Interp2[2][2];
|
||||
NOISE_DATATYPE FracZ = T::coeff(m_FracZ[z]);
|
||||
for (int x = 0; x < 2; x++)
|
||||
{
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
Interp2[x][y] = Lerp((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], FracZ);
|
||||
}
|
||||
}
|
||||
for (int y = a_FromY; y < a_ToY; y++)
|
||||
{
|
||||
NOISE_DATATYPE Interp[2];
|
||||
NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
|
||||
Interp[0] = Lerp(Interp2[0][0], Interp2[0][1], FracY);
|
||||
Interp[1] = Lerp(Interp2[1][0], Interp2[1][1], FracY);
|
||||
int idx = idxZ + y * m_SizeX + a_FromX;
|
||||
for (int x = a_FromX; x < a_ToX; x++)
|
||||
{
|
||||
m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
|
||||
} // for x
|
||||
} // for y
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
/** Initializes m_WorkRnds[] with the specified Floor values. */
|
||||
void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ)
|
||||
{
|
||||
m_CurFloorX = a_FloorX;
|
||||
m_CurFloorY = a_FloorY;
|
||||
m_CurFloorZ = a_FloorZ;
|
||||
(*m_WorkRnds)[0][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ);
|
||||
(*m_WorkRnds)[0][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ + 1);
|
||||
(*m_WorkRnds)[0][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ);
|
||||
(*m_WorkRnds)[0][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ + 1);
|
||||
(*m_WorkRnds)[1][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ);
|
||||
(*m_WorkRnds)[1][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ + 1);
|
||||
(*m_WorkRnds)[1][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ);
|
||||
(*m_WorkRnds)[1][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ + 1);
|
||||
}
|
||||
|
||||
|
||||
/** Updates m_WorkRnds[] for the new Floor values. */
|
||||
void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ)
|
||||
{
|
||||
// Swap the doublebuffer:
|
||||
int OldFloorX = m_CurFloorX;
|
||||
int OldFloorY = m_CurFloorY;
|
||||
int OldFloorZ = m_CurFloorZ;
|
||||
Workspace * OldWorkRnds = m_WorkRnds;
|
||||
m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
|
||||
|
||||
// Reuse as much of the old workspace as possible:
|
||||
// TODO: Try out if simply calculating all 8 elements each time is faster than this monster loop
|
||||
int DiffX = OldFloorX - a_NewFloorX;
|
||||
int DiffY = OldFloorY - a_NewFloorY;
|
||||
int DiffZ = OldFloorZ - a_NewFloorZ;
|
||||
for (int x = 0; x < 2; x++)
|
||||
{
|
||||
int cx = a_NewFloorX + x;
|
||||
int OldX = x - DiffX; // Where would this X be in the old grid?
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
int cy = a_NewFloorY + y;
|
||||
int OldY = y - DiffY; // Where would this Y be in the old grid?
|
||||
for (int z = 0; z < 2; z++)
|
||||
{
|
||||
int cz = a_NewFloorZ + z;
|
||||
int OldZ = z - DiffZ;
|
||||
if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2) && (OldZ >= 0) && (OldZ < 2))
|
||||
{
|
||||
(*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ];
|
||||
}
|
||||
else
|
||||
{
|
||||
(*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz);
|
||||
}
|
||||
} // for z
|
||||
} // for y
|
||||
} // for x
|
||||
m_CurFloorX = a_NewFloorX;
|
||||
m_CurFloorY = a_NewFloorY;
|
||||
m_CurFloorZ = a_NewFloorZ;
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef NOISE_DATATYPE Workspace[2][2][2];
|
||||
|
||||
/** The noise used for generating the values at integral coords. */
|
||||
const cNoise & m_Noise;
|
||||
|
||||
/** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
|
||||
Workspace * m_WorkRnds;
|
||||
|
||||
/** Buffer 1 for workspace doublebuffering, used in Move() */
|
||||
Workspace m_Workspace1;
|
||||
|
||||
/** Buffer 2 for workspace doublebuffering, used in Move() */
|
||||
Workspace m_Workspace2;
|
||||
|
||||
/** The integral coords of the currently calculated WorkRnds[] */
|
||||
int m_CurFloorX, m_CurFloorY, m_CurFloorZ;
|
||||
|
||||
/** The output array where the noise is calculated. */
|
||||
NOISE_DATATYPE * m_Array;
|
||||
|
||||
/** Dimensions of the output array. */
|
||||
int m_SizeX, m_SizeY, m_SizeZ;
|
||||
|
||||
/** Arrays holding the fractional values of the coords in each direction. */
|
||||
const NOISE_DATATYPE * m_FracX;
|
||||
const NOISE_DATATYPE * m_FracY;
|
||||
const NOISE_DATATYPE * m_FracZ;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cInterpolNoise:
|
||||
|
||||
template <typename T>
|
||||
class cInterpolNoise
|
||||
{
|
||||
/** Maximum size, for each direction, of the generated array. */
|
||||
static const int MAX_SIZE = 256;
|
||||
|
||||
public:
|
||||
cInterpolNoise(int a_Seed):
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Sets a new seed for the generators. Relays the seed to the underlying noise. */
|
||||
void SetSeed(int a_Seed)
|
||||
{
|
||||
m_Noise.SetSeed(a_Seed);
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
|
||||
) const
|
||||
{
|
||||
ASSERT(a_SizeX > 0);
|
||||
ASSERT(a_SizeY > 0);
|
||||
ASSERT(a_SizeX < MAX_SIZE);
|
||||
ASSERT(a_SizeY < MAX_SIZE);
|
||||
ASSERT(a_StartX < a_EndX);
|
||||
ASSERT(a_StartY < a_EndY);
|
||||
|
||||
// Calculate the integral and fractional parts of each coord:
|
||||
int FloorX[MAX_SIZE];
|
||||
int FloorY[MAX_SIZE];
|
||||
NOISE_DATATYPE FracX[MAX_SIZE];
|
||||
NOISE_DATATYPE FracY[MAX_SIZE];
|
||||
int SameX[MAX_SIZE];
|
||||
int SameY[MAX_SIZE];
|
||||
int NumSameX, NumSameY;
|
||||
CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
|
||||
CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
|
||||
|
||||
cInterpolCell2D<T> Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY);
|
||||
|
||||
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
|
||||
|
||||
// Calculate query values using Cell:
|
||||
int FromY = 0;
|
||||
for (int y = 0; y < NumSameY; y++)
|
||||
{
|
||||
int ToY = FromY + SameY[y];
|
||||
int FromX = 0;
|
||||
int CurFloorY = FloorY[FromY];
|
||||
for (int x = 0; x < NumSameX; x++)
|
||||
{
|
||||
int ToX = FromX + SameX[x];
|
||||
Cell.Generate(FromX, ToX, FromY, ToY);
|
||||
Cell.Move(FloorX[ToX], CurFloorY);
|
||||
FromX = ToX;
|
||||
} // for x
|
||||
Cell.Move(FloorX[0], FloorY[ToY]);
|
||||
FromY = ToY;
|
||||
} // for y
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
|
||||
) const
|
||||
{
|
||||
// Check params:
|
||||
ASSERT(a_SizeX > 1);
|
||||
ASSERT(a_SizeY > 1);
|
||||
|
||||
ASSERT(a_SizeX < MAX_SIZE);
|
||||
ASSERT(a_SizeY < MAX_SIZE);
|
||||
ASSERT(a_SizeZ < MAX_SIZE);
|
||||
ASSERT(a_StartX < a_EndX);
|
||||
ASSERT(a_StartY < a_EndY);
|
||||
ASSERT(a_StartZ < a_EndZ);
|
||||
|
||||
// Calculate the integral and fractional parts of each coord:
|
||||
int FloorX[MAX_SIZE];
|
||||
int FloorY[MAX_SIZE];
|
||||
int FloorZ[MAX_SIZE];
|
||||
NOISE_DATATYPE FracX[MAX_SIZE];
|
||||
NOISE_DATATYPE FracY[MAX_SIZE];
|
||||
NOISE_DATATYPE FracZ[MAX_SIZE];
|
||||
int SameX[MAX_SIZE];
|
||||
int SameY[MAX_SIZE];
|
||||
int SameZ[MAX_SIZE];
|
||||
int NumSameX, NumSameY, NumSameZ;
|
||||
CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
|
||||
CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
|
||||
CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ);
|
||||
|
||||
cInterpolCell3D<T> Cell(
|
||||
m_Noise, a_Array,
|
||||
a_SizeX, a_SizeY, a_SizeZ,
|
||||
FracX, FracY, FracZ
|
||||
);
|
||||
|
||||
Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]);
|
||||
|
||||
// Calculate query values using Cell:
|
||||
int FromZ = 0;
|
||||
for (int z = 0; z < NumSameZ; z++)
|
||||
{
|
||||
int ToZ = FromZ + SameZ[z];
|
||||
int CurFloorZ = FloorZ[FromZ];
|
||||
int FromY = 0;
|
||||
for (int y = 0; y < NumSameY; y++)
|
||||
{
|
||||
int ToY = FromY + SameY[y];
|
||||
int CurFloorY = FloorY[FromY];
|
||||
int FromX = 0;
|
||||
for (int x = 0; x < NumSameX; x++)
|
||||
{
|
||||
int ToX = FromX + SameX[x];
|
||||
Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ);
|
||||
Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ);
|
||||
FromX = ToX;
|
||||
}
|
||||
Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ);
|
||||
FromY = ToY;
|
||||
} // for y
|
||||
Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]);
|
||||
FromZ = ToZ;
|
||||
} // for z
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/** The noise used for the underlying value generation. */
|
||||
cNoise m_Noise;
|
||||
|
||||
|
||||
/** Calculates the integral and fractional parts along one axis.
|
||||
a_Floor will receive the integral parts (array of a_Size ints).
|
||||
a_Frac will receive the fractional parts (array of a_Size floats).
|
||||
a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
|
||||
a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
|
||||
void CalcFloorFrac(
|
||||
int a_Size,
|
||||
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
|
||||
int * a_Floor, NOISE_DATATYPE * a_Frac,
|
||||
int * a_Same, int & a_NumSame
|
||||
) const
|
||||
{
|
||||
ASSERT(a_Size > 0);
|
||||
|
||||
// Calculate the floor and frac values:
|
||||
NOISE_DATATYPE val = a_Start;
|
||||
NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1);
|
||||
for (int i = 0; i < a_Size; i++)
|
||||
{
|
||||
a_Floor[i] = FAST_FLOOR(val);
|
||||
a_Frac[i] = val - a_Floor[i];
|
||||
val += dif;
|
||||
}
|
||||
|
||||
// Mark up the same floor values into a_Same / a_NumSame:
|
||||
int CurFloor = a_Floor[0];
|
||||
int LastSame = 0;
|
||||
a_NumSame = 0;
|
||||
for (int i = 1; i < a_Size; i++)
|
||||
{
|
||||
if (a_Floor[i] != CurFloor)
|
||||
{
|
||||
a_Same[a_NumSame] = i - LastSame;
|
||||
LastSame = i;
|
||||
a_NumSame += 1;
|
||||
CurFloor = a_Floor[i];
|
||||
}
|
||||
} // for i - a_Floor[]
|
||||
if (LastSame < a_Size)
|
||||
{
|
||||
a_Same[a_NumSame] = a_Size - LastSame;
|
||||
a_NumSame += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A fifth-degree curve for interpolating.
|
||||
Implemented as a functor for better chance of inlining. */
|
||||
struct Interp5Deg
|
||||
{
|
||||
static NOISE_DATATYPE coeff(NOISE_DATATYPE a_Val)
|
||||
{
|
||||
return a_Val * a_Val * a_Val * (a_Val * (a_Val * 6 - 15) + 10);
|
||||
}
|
||||
};
|
||||
|
||||
typedef cInterpolNoise<Interp5Deg> cInterp5DegNoise;
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Noise.h"
|
||||
#include "OSSupport/Timer.h"
|
||||
|
||||
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
|
||||
|
||||
@ -9,10 +10,110 @@
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
/** cImprovedPerlin noise test suite:
|
||||
- Generate a rather large 2D and 3D noise array and output it to a file
|
||||
- Compare performance of cCubicNoise and cImprovedNoise, both in single-value and 3D-array usages */
|
||||
static class cImprovedPerlinNoiseTest
|
||||
{
|
||||
public:
|
||||
cImprovedPerlinNoiseTest(void)
|
||||
{
|
||||
printf("Performing Improved Perlin Noise tests...\n");
|
||||
TestImage();
|
||||
TestSpeed();
|
||||
TestSpeedArr();
|
||||
printf("Improved Perlin Noise tests complete.\n");
|
||||
}
|
||||
|
||||
|
||||
/** Tests the noise by generating 2D and 3D images and dumping them to files. */
|
||||
void TestImage(void)
|
||||
{
|
||||
static const int SIZE_X = 256;
|
||||
static const int SIZE_Y = 256;
|
||||
static const int SIZE_Z = 16;
|
||||
|
||||
cImprovedNoise noise(1);
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
|
||||
noise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
Debug3DNoise(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, "ImprovedPerlinNoiseTest3D", 128);
|
||||
noise.Generate2D(arr.get(), SIZE_X, SIZE_Y, 0, 14, 15, 28);
|
||||
Debug2DNoise(arr.get(), SIZE_X, SIZE_Y, "ImprovedPerlinNoiseTest2D", 128);
|
||||
}
|
||||
|
||||
|
||||
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating individual values. */
|
||||
void TestSpeed(void)
|
||||
{
|
||||
cImprovedNoise improvedNoise(1);
|
||||
cNoise noise(1);
|
||||
cTimer timer;
|
||||
|
||||
// Measure the improvedNoise:
|
||||
NOISE_DATATYPE sum = 0;
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 100000000; i++)
|
||||
{
|
||||
sum += improvedNoise.GetValueAt(i, 0, -i);
|
||||
}
|
||||
long long finish = timer.GetNowTime();
|
||||
printf("cImprovedNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
|
||||
|
||||
// Measure the cubicNoise:
|
||||
sum = 0;
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 100000000; i++)
|
||||
{
|
||||
sum += noise.IntNoise3D(i, 0, -i);
|
||||
}
|
||||
finish = timer.GetNowTime();
|
||||
printf("cCubicNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
|
||||
}
|
||||
|
||||
|
||||
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating arrays. */
|
||||
void TestSpeedArr(void)
|
||||
{
|
||||
static const int SIZE_X = 256;
|
||||
static const int SIZE_Y = 256;
|
||||
static const int SIZE_Z = 16;
|
||||
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
|
||||
cTimer timer;
|
||||
cImprovedNoise improvedNoise(1);
|
||||
cCubicNoise cubicNoise(1);
|
||||
|
||||
// Measure the improvedNoise:
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
improvedNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
}
|
||||
long long finish = timer.GetNowTime();
|
||||
printf("cImprovedNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
|
||||
|
||||
// Measure the cubicNoise:
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
}
|
||||
finish = timer.GetNowTime();
|
||||
printf("cCubicNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
|
||||
}
|
||||
} g_Test;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Globals:
|
||||
|
||||
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase)
|
||||
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
|
||||
{
|
||||
const int BUF_SIZE = 512;
|
||||
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
|
||||
@ -29,7 +130,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f1.Write(buf, a_SizeX);
|
||||
} // for y
|
||||
@ -50,7 +151,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f2.Write(buf, a_SizeX);
|
||||
} // for z
|
||||
@ -65,7 +166,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
|
||||
|
||||
|
||||
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase)
|
||||
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
|
||||
{
|
||||
const int BUF_SIZE = 512;
|
||||
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
|
||||
@ -79,7 +180,7 @@ void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, cons
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f1.Write(buf, a_SizeX);
|
||||
} // for y
|
||||
@ -594,13 +695,6 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cCubicNoise:
|
||||
|
||||
#ifdef _DEBUG
|
||||
int cCubicNoise::m_NumSingleX = 0;
|
||||
int cCubicNoise::m_NumSingleXY = 0;
|
||||
int cCubicNoise::m_NumSingleY = 0;
|
||||
int cCubicNoise::m_NumCalls = 0;
|
||||
#endif // _DEBUG
|
||||
|
||||
cCubicNoise::cCubicNoise(int a_Seed) :
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
@ -639,23 +733,6 @@ void cCubicNoise::Generate2D(
|
||||
|
||||
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Statistics on the noise-space coords:
|
||||
if (NumSameX == 1)
|
||||
{
|
||||
m_NumSingleX++;
|
||||
if (NumSameY == 1)
|
||||
{
|
||||
m_NumSingleXY++;
|
||||
}
|
||||
}
|
||||
if (NumSameY == 1)
|
||||
{
|
||||
m_NumSingleY++;
|
||||
}
|
||||
m_NumCalls++;
|
||||
#endif // _DEBUG
|
||||
|
||||
// Calculate query values using Cell:
|
||||
int FromY = 0;
|
||||
for (int y = 0; y < NumSameY; y++)
|
||||
@ -792,341 +869,161 @@ void cCubicNoise::CalcFloorFrac(
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cPerlinNoise:
|
||||
// cImprovedNoise:
|
||||
|
||||
cPerlinNoise::cPerlinNoise(void) :
|
||||
m_Seed(0)
|
||||
cImprovedNoise::cImprovedNoise(int a_Seed)
|
||||
{
|
||||
// Initialize the permutations with identity:
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
m_Perm[i] = i;
|
||||
}
|
||||
|
||||
// Randomize the permutation table - swap each element with a random other element:
|
||||
cNoise noise(a_Seed);
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
int rnd = (noise.IntNoise1DInt(i) / 7) % 256;
|
||||
std::swap(m_Perm[i], m_Perm[rnd]);
|
||||
}
|
||||
|
||||
// Copy the lower 256 entries into upper 256 entries:
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
m_Perm[i + 256] = m_Perm[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPerlinNoise::cPerlinNoise(int a_Seed) :
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::SetSeed(int a_Seed)
|
||||
{
|
||||
m_Seed = a_Seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude)
|
||||
{
|
||||
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
void cImprovedNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array,
|
||||
int a_SizeX, int a_SizeY,
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
size_t idx = 0;
|
||||
for (int y = 0; y < a_SizeY; y++)
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"Perlin: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
if (ShouldFreeWorkspace)
|
||||
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
|
||||
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
|
||||
int noiseYInt = FAST_FLOOR(noiseY);
|
||||
int yCoord = noiseYInt & 255;
|
||||
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
|
||||
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
|
||||
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
|
||||
int noiseXInt = FAST_FLOOR(noiseX);
|
||||
int xCoord = noiseXInt & 255;
|
||||
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
|
||||
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
// Hash the coordinates:
|
||||
int A = m_Perm[xCoord] + yCoord;
|
||||
int AA = m_Perm[A];
|
||||
int AB = m_Perm[A + 1];
|
||||
int B = m_Perm[xCoord + 1] + yCoord;
|
||||
int BA = m_Perm[B];
|
||||
int BB = m_Perm[B + 1];
|
||||
|
||||
FirstOctave.m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
|
||||
// Lerp the gradients:
|
||||
a_Array[idx++] = Lerp(
|
||||
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, 0), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, 0), fadeX),
|
||||
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, 0), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, 0), fadeX),
|
||||
fadeY
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
} // for x
|
||||
} // for y
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
void cImprovedNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array,
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ,
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY,
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
size_t idx = 0;
|
||||
for (int z = 0; z < a_SizeZ; z++)
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"Perlin: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
if (ShouldFreeWorkspace)
|
||||
NOISE_DATATYPE ratioZ = static_cast<NOISE_DATATYPE>(z) / (a_SizeZ - 1);
|
||||
NOISE_DATATYPE noiseZ = Lerp(a_StartZ, a_EndZ, ratioZ);
|
||||
int noiseZInt = FAST_FLOOR(noiseZ);
|
||||
int zCoord = noiseZInt & 255;
|
||||
NOISE_DATATYPE noiseZFrac = noiseZ - noiseZInt;
|
||||
NOISE_DATATYPE fadeZ = Fade(noiseZFrac);
|
||||
for (int y = 0; y < a_SizeY; y++)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
|
||||
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
|
||||
int noiseYInt = FAST_FLOOR(noiseY);
|
||||
int yCoord = noiseYInt & 255;
|
||||
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
|
||||
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
|
||||
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
|
||||
int noiseXInt = FAST_FLOOR(noiseX);
|
||||
int xCoord = noiseXInt & 255;
|
||||
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
|
||||
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
// Hash the coordinates:
|
||||
int A = m_Perm[xCoord] + yCoord;
|
||||
int AA = m_Perm[A] + zCoord;
|
||||
int AB = m_Perm[A + 1] + zCoord;
|
||||
int B = m_Perm[xCoord + 1] + yCoord;
|
||||
int BA = m_Perm[B] + zCoord;
|
||||
int BB = m_Perm[B + 1] + zCoord;
|
||||
|
||||
FirstOctave.m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
|
||||
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
|
||||
// Lerp the gradients:
|
||||
// TODO: This may be optimized by swapping the coords and recalculating most lerps only "once every x"
|
||||
a_Array[idx++] = Lerp(
|
||||
Lerp(
|
||||
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, noiseZFrac), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, noiseZFrac), fadeX),
|
||||
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, noiseZFrac), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac), fadeX),
|
||||
fadeY
|
||||
),
|
||||
Lerp(
|
||||
Lerp(Grad(m_Perm[AA + 1], noiseXFrac, noiseYFrac, noiseZFrac - 1), Grad(m_Perm[BA + 1], noiseXFrac - 1, noiseYFrac, noiseZFrac - 1), fadeX),
|
||||
Lerp(Grad(m_Perm[AB + 1], noiseXFrac, noiseYFrac - 1, noiseZFrac - 1), Grad(m_Perm[BB + 1], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac - 1), fadeX),
|
||||
fadeY
|
||||
),
|
||||
fadeZ
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
|
||||
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
} // for x
|
||||
} // for y
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cRidgedMultiNoise:
|
||||
|
||||
cRidgedMultiNoise::cRidgedMultiNoise(void) :
|
||||
m_Seed(0)
|
||||
NOISE_DATATYPE cImprovedNoise::GetValueAt(int a_X, int a_Y, int a_Z)
|
||||
{
|
||||
// Hash the coordinates:
|
||||
a_X = a_X & 255;
|
||||
a_Y = a_Y & 255;
|
||||
a_Z = a_Z & 255;
|
||||
int A = m_Perm[a_X] + a_Y;
|
||||
int AA = m_Perm[A] + a_Z;
|
||||
|
||||
return Grad(m_Perm[AA], 1, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cRidgedMultiNoise::cRidgedMultiNoise(int a_Seed) :
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::SetSeed(int a_Seed)
|
||||
{
|
||||
m_Seed = a_Seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::AddOctave(float a_Frequency, float a_Amplitude)
|
||||
{
|
||||
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"RidgedMulti: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = fabs(a_Workspace[i] * Amplitude);
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += fabs(a_Workspace[i] * Amplitude);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"RidgedMulti: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
|
||||
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
|
||||
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -7,22 +7,11 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/** The datatype used by all the noise generators. */
|
||||
typedef float NOISE_DATATYPE;
|
||||
|
||||
|
||||
|
||||
|
||||
// Some settings
|
||||
#define NOISE_DATATYPE float
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define INLINE __forceinline
|
||||
#else
|
||||
#define INLINE inline
|
||||
#endif
|
||||
#include "OctavedNoise.h"
|
||||
#include "RidgedNoise.h"
|
||||
|
||||
|
||||
|
||||
@ -35,20 +24,20 @@ public:
|
||||
cNoise(const cNoise & a_Noise);
|
||||
|
||||
// The following functions, if not marked INLINE, are about 20 % slower
|
||||
INLINE NOISE_DATATYPE IntNoise1D(int a_X) const;
|
||||
INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
|
||||
INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
|
||||
inline NOISE_DATATYPE IntNoise1D(int a_X) const;
|
||||
inline NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
|
||||
inline NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
|
||||
|
||||
// Return a float number in the specified range:
|
||||
INLINE NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
|
||||
inline NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
|
||||
{
|
||||
return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min);
|
||||
}
|
||||
|
||||
// Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
|
||||
INLINE int IntNoise1DInt(int a_X) const;
|
||||
INLINE int IntNoise2DInt(int a_X, int a_Y) const;
|
||||
INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
|
||||
inline int IntNoise1DInt(int a_X) const;
|
||||
inline int IntNoise2DInt(int a_X, int a_Y) const;
|
||||
inline int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
|
||||
|
||||
NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const;
|
||||
NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const;
|
||||
@ -61,9 +50,9 @@ public:
|
||||
|
||||
void SetSeed(int a_Seed) { m_Seed = a_Seed; }
|
||||
|
||||
INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
|
||||
INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
|
||||
private:
|
||||
int m_Seed;
|
||||
@ -76,19 +65,15 @@ private:
|
||||
class cCubicNoise
|
||||
{
|
||||
public:
|
||||
static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays.
|
||||
/** Maximum size of each dimension of the query arrays. */
|
||||
static const int MAX_SIZE = 512;
|
||||
|
||||
|
||||
/** Creates a new instance with the specified seed. */
|
||||
cCubicNoise(int a_Seed);
|
||||
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
@ -97,6 +82,7 @@ public:
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
@ -106,163 +92,87 @@ public:
|
||||
) const;
|
||||
|
||||
protected:
|
||||
typedef NOISE_DATATYPE Workspace1D[4];
|
||||
typedef NOISE_DATATYPE Workspace2D[4][4];
|
||||
|
||||
cNoise m_Noise; // Used for integral rnd values
|
||||
/** Noise used for integral random values. */
|
||||
cNoise m_Noise;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Statistics on the noise-space coords:
|
||||
static int m_NumSingleX;
|
||||
static int m_NumSingleXY;
|
||||
static int m_NumSingleY;
|
||||
static int m_NumCalls;
|
||||
#endif // _DEBUG
|
||||
|
||||
/// Calculates the integral and fractional parts along one axis.
|
||||
/** Calculates the integral and fractional parts along one axis.
|
||||
a_Floor will receive the integral parts (array of a_Size ints).
|
||||
a_Frac will receive the fractional parts (array of a_Size floats).
|
||||
a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
|
||||
a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
|
||||
void CalcFloorFrac(
|
||||
int a_Size,
|
||||
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
|
||||
int * a_Floor, NOISE_DATATYPE * a_Frac,
|
||||
int * a_Same, int & a_NumSame
|
||||
) const;
|
||||
|
||||
void UpdateWorkRnds2DX(
|
||||
Workspace2D & a_WorkRnds,
|
||||
Workspace1D & a_Interps,
|
||||
int a_LastFloorX, int a_NewFloorX,
|
||||
int a_FloorY,
|
||||
NOISE_DATATYPE a_FractionY
|
||||
) const;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPerlinNoise
|
||||
/** Improved noise, as described by Ken Perlin: http://mrl.nyu.edu/~perlin/paper445.pdf
|
||||
Implementation adapted from Perlin's Java implementation: http://mrl.nyu.edu/~perlin/noise/ */
|
||||
class cImprovedNoise
|
||||
{
|
||||
public:
|
||||
cPerlinNoise(void);
|
||||
cPerlinNoise(int a_Seed);
|
||||
|
||||
|
||||
void SetSeed(int a_Seed);
|
||||
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
/** Constructs a new instance of the noise obbject.
|
||||
Note that this operation is quite expensive (the permutation array being constructed). */
|
||||
cImprovedNoise(int a_Seed);
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
|
||||
) const;
|
||||
|
||||
/** Returns the value at the specified integral coords. Used for raw speed measurement. */
|
||||
NOISE_DATATYPE GetValueAt(int a_X, int a_Y, int a_Z);
|
||||
|
||||
protected:
|
||||
class cOctave
|
||||
{
|
||||
public:
|
||||
cCubicNoise m_Noise;
|
||||
|
||||
NOISE_DATATYPE m_Frequency; // Coord multiplier
|
||||
NOISE_DATATYPE m_Amplitude; // Value multiplier
|
||||
/** The permutation table used by the noise function. Initialized using seed. */
|
||||
int m_Perm[512];
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
|
||||
/** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */
|
||||
inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T)
|
||||
{
|
||||
return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
int m_Seed;
|
||||
cOctaves m_Octaves;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cRidgedMultiNoise
|
||||
{
|
||||
public:
|
||||
cRidgedMultiNoise(void);
|
||||
cRidgedMultiNoise(int a_Seed);
|
||||
|
||||
|
||||
void SetSeed(int a_Seed);
|
||||
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
protected:
|
||||
class cOctave
|
||||
{
|
||||
public:
|
||||
cCubicNoise m_Noise;
|
||||
|
||||
NOISE_DATATYPE m_Frequency; // Coord multiplier
|
||||
NOISE_DATATYPE m_Amplitude; // Value multiplier
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
/** Returns the gradient value based on the hash. */
|
||||
inline static NOISE_DATATYPE Grad(int a_Hash, NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z)
|
||||
{
|
||||
int hash = a_Hash % 16;
|
||||
NOISE_DATATYPE u = (hash < 8) ? a_X : a_Y;
|
||||
NOISE_DATATYPE v = (hash < 4) ? a_Y : (((hash == 12) || (hash == 14)) ? a_X : a_Z);
|
||||
return (((hash & 1) == 0) ? u : -u) + (((hash & 2) == 0) ? v : -v);
|
||||
}
|
||||
} ;
|
||||
};
|
||||
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
int m_Seed;
|
||||
cOctaves m_Octaves;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
typedef cOctavedNoise<cCubicNoise> cPerlinNoise;
|
||||
typedef cOctavedNoise<cRidgedNoise<cCubicNoise>> cRidgedMultiNoise;
|
||||
|
||||
|
||||
|
||||
@ -376,8 +286,46 @@ NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global functions:
|
||||
|
||||
extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase);
|
||||
extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
|
||||
/** Exports the noise array into a file.
|
||||
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
|
||||
extern void Debug2DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
|
||||
|
||||
/** Exports the noise array into a set of files, ordered by XY and XZ.
|
||||
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
|
||||
extern void Debug3DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
|
||||
|
||||
|
||||
|
||||
|
||||
/** Linearly interpolates between two values.
|
||||
Assumes that a_Ratio is in range [0, 1]. */
|
||||
inline NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
|
||||
{
|
||||
return a_Val1 + (a_Val2 - a_Val1) * a_Ratio;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
|
||||
inline NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
|
||||
{
|
||||
if (a_Ratio < 0)
|
||||
{
|
||||
return a_Val1;
|
||||
}
|
||||
if (a_Ratio > 1)
|
||||
{
|
||||
return a_Val2;
|
||||
}
|
||||
return Lerp(a_Val1, a_Val2, a_Ratio);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
192
src/Noise/OctavedNoise.h
Normal file
@ -0,0 +1,192 @@
|
||||
|
||||
// OctavedNoise.h
|
||||
|
||||
// Implements the cOctavedNoise class template representing a noise generator that layers several octaves of another noise
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename N>
|
||||
class cOctavedNoise
|
||||
{
|
||||
public:
|
||||
cOctavedNoise(int a_Seed = 0):
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Sets a new seed for the generators. Relays the seed to all underlying octaves. */
|
||||
void SetSeed(int a_Seed)
|
||||
{
|
||||
m_Seed = a_Seed;
|
||||
for (auto oct: m_Octaves)
|
||||
{
|
||||
oct->SetSeed(a_Seed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Adds a new octave to the list of octaves that compose this noise. */
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude)
|
||||
{
|
||||
m_Octaves.emplace_back(m_Seed, a_Frequency, a_Amplitude);
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash.
|
||||
) const
|
||||
{
|
||||
// Check that state is alright:
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
ASSERT(!"cOctavedNoise: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate the workspace on the heap, if it wasn't given:
|
||||
std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
|
||||
if (a_Workspace == nullptr)
|
||||
{
|
||||
workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY]);
|
||||
a_Workspace = workspaceHeap.get();
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
FirstOctave.m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
|
||||
{
|
||||
// Generate the noise for the octave:
|
||||
itr->m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
|
||||
);
|
||||
// Add it into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
} // for itr - m_Octaves[]
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash, same size as a_Array
|
||||
) const
|
||||
{
|
||||
// Check that state is alright:
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
ASSERT(!"cOctavedNoise: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate the workspace on the heap, if it wasn't given:
|
||||
std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
|
||||
if (a_Workspace == nullptr)
|
||||
{
|
||||
workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY * a_SizeZ]);
|
||||
a_Workspace = workspaceHeap.get();
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
FirstOctave.m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
|
||||
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
|
||||
{
|
||||
// Generate the noise for the octave:
|
||||
itr->m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
|
||||
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
|
||||
);
|
||||
// Add it into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
} // for itr - m_Octaves[]
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Stores information and state for one octave of the noise. */
|
||||
class cOctave
|
||||
{
|
||||
public:
|
||||
N m_Noise;
|
||||
|
||||
/** Coord multiplier. */
|
||||
NOISE_DATATYPE m_Frequency;
|
||||
|
||||
/** Value multiplier. */
|
||||
NOISE_DATATYPE m_Amplitude;
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
|
||||
/** The seed used by the underlying generators. */
|
||||
int m_Seed;
|
||||
|
||||
/** The octaves that compose this noise. */
|
||||
cOctaves m_Octaves;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
91
src/Noise/RidgedNoise.h
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
// RidgedNoise.h
|
||||
|
||||
// Implements the cRidgedNoise template class that generates ridged noise based on another noise provider.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename N>
|
||||
class cRidgedNoise
|
||||
{
|
||||
public:
|
||||
/** Creates a new instance with the seed set to 0. */
|
||||
cRidgedNoise(void):
|
||||
m_Noise(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new instance with the specified seed. */
|
||||
cRidgedNoise(int a_Seed):
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/** Sets the seed for the underlying noise. */
|
||||
void SetSeed(int a_Seed)
|
||||
{
|
||||
m_Noise.SetSeed(a_Seed);
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
|
||||
) const
|
||||
{
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
m_Noise.Generate2D(
|
||||
a_Array, a_SizeX, a_SizeY,
|
||||
a_StartX, a_EndX,
|
||||
a_StartY, a_EndY
|
||||
);
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = fabs(a_Array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
|
||||
) const
|
||||
{
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
m_Noise.Generate2D(
|
||||
a_Array, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX, a_EndX,
|
||||
a_StartY, a_EndY,
|
||||
a_StartZ, a_EndZ
|
||||
);
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = fabs(a_Array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
N m_Noise;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
16
src/Root.cpp
@ -627,6 +627,22 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
|
||||
|
||||
|
||||
|
||||
bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
|
||||
{
|
||||
for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end();itr++)
|
||||
{
|
||||
if (itr->second->DoWithPlayerByUUID(a_PlayerUUID, a_Callback))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
|
||||
{
|
||||
return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion);
|
||||
|
@ -127,6 +127,9 @@ public:
|
||||
/// Finds a player from a partial or complete player name and calls the callback - case-insensitive
|
||||
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
/** Finds the player over his uuid and calls the callback */
|
||||
bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/// Sends a chat message to all connected clients (in all worlds)
|
||||
|
@ -93,6 +93,21 @@ public:
|
||||
return x * a_Rhs.x + y * a_Rhs.y + z * a_Rhs.z;
|
||||
}
|
||||
|
||||
inline void abs()
|
||||
{
|
||||
x = (x < 0) ? -x : x;
|
||||
y = (y < 0) ? -y : y;
|
||||
z = (z < 0) ? -z : z;
|
||||
}
|
||||
|
||||
// We can't use a capital letter, because we wouldn't be able to call the normal Clamp function.
|
||||
inline void clamp(T a_Min, T a_Max)
|
||||
{
|
||||
x = Clamp(x, a_Min, a_Max);
|
||||
y = Clamp(y, a_Min, a_Max);
|
||||
z = Clamp(z, a_Min, a_Max);
|
||||
}
|
||||
|
||||
inline Vector3<T> Cross(const Vector3<T> & a_Rhs) const
|
||||
{
|
||||
return Vector3<T>(
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -277,6 +277,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
|
||||
m_bCommandBlocksEnabled(true),
|
||||
m_bUseChatPrefixes(false),
|
||||
m_TNTShrapnelLevel(slNone),
|
||||
m_MaxViewDistance(12),
|
||||
m_Scoreboard(this),
|
||||
m_MapManager(this),
|
||||
m_GeneratorCallbacks(*this),
|
||||
@ -555,6 +556,8 @@ void cWorld::Start(void)
|
||||
m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true);
|
||||
m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true);
|
||||
|
||||
SetMaxViewDistance(IniFile.GetValueSetI("SpawnPosition", "MaxViewDistance", 12));
|
||||
|
||||
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
|
||||
int KeyNum = IniFile.FindKey("SpawnPosition");
|
||||
m_IsSpawnExplicitlySet =
|
||||
@ -2723,6 +2726,23 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
|
||||
|
||||
|
||||
|
||||
bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->GetUUID() == a_PlayerUUID)
|
||||
{
|
||||
return a_Callback.Item(*itr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: This interface is dangerous!
|
||||
cPlayer * cWorld::FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, bool a_CheckLineOfSight)
|
||||
{
|
||||
@ -2903,15 +2923,18 @@ bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AStrin
|
||||
AString Line2(a_Line2);
|
||||
AString Line3(a_Line3);
|
||||
AString Line4(a_Line4);
|
||||
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(*this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4))
|
||||
{
|
||||
cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(*this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2919,15 +2942,6 @@ bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AStrin
|
||||
|
||||
|
||||
|
||||
bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player)
|
||||
{
|
||||
return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cWorld::SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command)
|
||||
{
|
||||
class cUpdateCommandBlock : public cCommandBlockCallback
|
||||
|
18
src/World.h
@ -26,6 +26,7 @@
|
||||
#include "Blocks/WorldInterface.h"
|
||||
#include "Blocks/BroadcastInterface.h"
|
||||
#include "FastRandom.h"
|
||||
#include "ClientHandle.h"
|
||||
|
||||
|
||||
|
||||
@ -323,6 +324,9 @@ public:
|
||||
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
|
||||
cPlayer * FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, bool a_CheckLineOfSight = true);
|
||||
|
||||
/** Finds the player over his uuid and calls the callback */
|
||||
bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
|
||||
|
||||
/** Adds the entity into its appropriate chunk; takes ownership of the entity ptr.
|
||||
@ -374,12 +378,9 @@ public:
|
||||
/** Marks the chunk as failed-to-load: */
|
||||
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
/** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. Same as UpdateSign() */
|
||||
/** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. */
|
||||
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = nullptr); // Exported in ManualBindings.cpp
|
||||
|
||||
/** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be nullptr. Returns true if sign text changed. Same as SetSignLines() */
|
||||
bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = nullptr); // Exported in ManualBindings.cpp
|
||||
|
||||
/** Sets the command block command. Returns true if command changed. */
|
||||
bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export
|
||||
|
||||
@ -646,6 +647,12 @@ public:
|
||||
eShrapnelLevel GetTNTShrapnelLevel(void) const { return m_TNTShrapnelLevel; }
|
||||
void SetTNTShrapnelLevel(eShrapnelLevel a_Flag) { m_TNTShrapnelLevel = a_Flag; }
|
||||
|
||||
int GetMaxViewDistance(void) const { return m_MaxViewDistance; }
|
||||
void SetMaxViewDistance(int a_MaxViewDistance)
|
||||
{
|
||||
m_MaxViewDistance = Clamp(a_MaxViewDistance, cClientHandle::MIN_VIEW_DISTANCE, cClientHandle::MAX_VIEW_DISTANCE);
|
||||
}
|
||||
|
||||
bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; }
|
||||
void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; }
|
||||
|
||||
@ -961,6 +968,9 @@ private:
|
||||
*/
|
||||
eShrapnelLevel m_TNTShrapnelLevel;
|
||||
|
||||
/** The maximum view distance that a player can have in this world. */
|
||||
int m_MaxViewDistance;
|
||||
|
||||
/** Name of the nether world */
|
||||
AString m_NetherWorldName;
|
||||
|
||||
|
@ -604,6 +604,28 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
|
||||
m_Writer.AddByte("IsConverting", (((const cZombie *)a_Monster)->IsConverting() ? 1 : 0));
|
||||
break;
|
||||
}
|
||||
case mtInvalidType:
|
||||
case mtBlaze:
|
||||
case mtCaveSpider:
|
||||
case mtChicken:
|
||||
case mtCow:
|
||||
case mtEnderDragon:
|
||||
case mtGhast:
|
||||
case mtGiant:
|
||||
case mtIronGolem:
|
||||
case mtMooshroom:
|
||||
case mtOcelot:
|
||||
case mtPig:
|
||||
case mtSilverfish:
|
||||
case mtSnowGolem:
|
||||
case mtSpider:
|
||||
case mtSquid:
|
||||
case mtWitch:
|
||||
case mtZombiePigman:
|
||||
{
|
||||
// Other mobs have no special tags.
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_Writer.EndCompound();
|
||||
}
|
||||
|
@ -696,11 +696,28 @@ cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a
|
||||
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
{
|
||||
int Type = a_NBT.FindChildByName(a_TagIdx, "id");
|
||||
if ((Type < 0) || (a_NBT.GetType(Type) != TAG_Short))
|
||||
if (Type <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a_NBT.GetType(Type) == TAG_String)
|
||||
{
|
||||
if (!StringToItem(a_NBT.GetString(Type), a_Item))
|
||||
{
|
||||
// Can't resolve item type
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (a_NBT.GetType(Type) == TAG_Short)
|
||||
{
|
||||
a_Item.m_ItemType = a_NBT.GetShort(Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a_Item.m_ItemType < 0)
|
||||
{
|
||||
a_Item.Empty();
|
||||
|