1
0
Fork 0

Merge remote-tracking branch 'origin/master' into c++11

Conflicts:
	src/Noise/Noise.h
	src/World.h
This commit is contained in:
Tiger Wang 2014-11-23 17:12:34 +00:00
commit 79e5b82354
99 changed files with 7166 additions and 1616 deletions

View File

@ -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/)

View File

@ -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)." },

View File

@ -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

View File

@ -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

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

View File

@ -1,353 +1,427 @@
body, html
{
font-family: "Open Sans", Tahoma, sans-serif;
padding: 0;
margin: 0;
font-weight: 400;
background-color: #fbe9e7;
color: rgba(0, 0, 0, 0.87);
/* Copyright Justin S and MCServer Team, licensed under CC-BY-SA 3.0 */
* {
margin: 0;
}
.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);
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
-webkit-font-smoothing: antialiased;
background: #fff;
width: 100%;
min-width: 100%;
height:100%;
min-height:100%;
overflow-y: scroll;
overflow-x: hidden;
}
.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;
a:link {
color: #555;
text-decoration: none;
}
#sidebar
{
float: left;
width: 20%;
a:visited {
color: #444;
text-decoration: none;
}
.sideNav
{
list-style: none;
background-color: #fafafa;
margin: 20px 0;
padding: 5px 0;
width: 100%;
box-shadow: 1px 0px 10px rgba(0, 0, 0, 0.2);
a:hover, a:active {
color: #000;
text-decoration: underline;
}
.sideNav li
{
padding: 10px;
color: rgba(0, 0, 0, 0.54);
img {
border: none;
}
.sideNav li.link
{
padding-left: 30px;
h1 {
color: #069;
text-shadow: 2px 2px #000;
}
.sideNav li.link a
{
text-decoration: none;
color: rgba(0, 0, 0, 0.87);
.row1 {
border-bottom: 1px #000 solid;
height: 100px;
max-height: 100px;
background: #fff url(header.png) repeat-x top left;
}
#container
{
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f5f5f5;
.row2 {
margin: 0 auto;
text-align: center;
vertical-align: middle;
margin-top: 25px;
margin-bottom: 25px;
}
#main
{
float: right;
width: 80%;
padding: 0 15px 20px 15px;
box-sizing: border-box;
-moz-box-sizing: border-box;
.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;
}
.clear
{
clear: both;
.push25 {
}
table
{
width: 100%;
border-collapse: collapse;
#panel ul.menu {
margin: 0;
padding: 0;
list-style: none;
}
table td
{
padding: 5px;
#panel ul.menu li {
margin: 0 5px;
display: inline;
}
table th
{
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding: 5px;
text-align: center;
#panel ul.menu li a {
padding-left: 20px;
background-repeat: no-repeat;
background-position: left center;
}
table tr:nth-child(odd)
{
background-color: rgba(0, 0, 0, 0.015);
#panel .upper ul.top_links {
float: right;
font-weight: 700;
}
p
{
margin: 8px 0;
padding: 8px 3px;
#panel .upper {
background: #dcdbdc url(tcat.png) repeat-x;
border-top: 1px solid #fff;
border-bottom: 1px solid #bbb;
padding: 7px;
}
a
{
text-decoration: none;
color: #0277bd;
-webkit-transition: color 0.1s linear;
-moz-transition: color 0.1s linear;
transition: color 0.1s linear;
#footer ul.menu {
margin: 0;
padding: 0;
list-style: none;
}
a:hover
{
color: #01579b;
#footer ul.menu li {
margin: 0 5px;
display: inline;
}
.welcome-msg
{
color: rgba(0, 0, 0, 0.54);
#footer .upper {
background: #dcdbdc url(tcat.png) repeat-x;
border-top: 1px solid #bbb;
padding: 6px;
overflow: hidden;
font-size: 12px;
}
.username
{
text-transform: capitalize;
color: rgba(0, 0, 0, 0.87);
#footer .upper ul.bottom_links {
float: left;
margin: 3px 0 0 -5px;
}
a:hover
{
color: black;
#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;
}
input, select
{
padding: 8px;
#footer .lower a:link,#footer .lower a:visited {
color: #fff;
font-weight: 700;
}
form
{
padding: 4px;
#footer .lower a:hover,#footer .lower a:active {
color: #fff;
font-weight: 700;
}
.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;
#footer .lower #current_time {
float: right;
padding-right: 6px;
}
.err
{
color: white;
display: block;
background-color: #e51c23 !important;
padding: 15px;
line-height: 30px;
min-height: 30px;
.wrapper {
width: 85%;
min-width: 970px;
max-width: 1500px;
margin: auto;
}
.err:before
{
content: "ERROR: ";
#footer {
position: fixed;
left:0;
bottom:0;
height: 61px;
width: 100%;
background: #999;
border-top: 1px #000 solid;
border-bottom: 1px #000 solid;
}
.warn
{
color: white;
display: block;
background-color: #ff5722 !important;
padding: 15px;
line-height: 30px;
min-height: 30px;
* 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');
}
.warn:before
{
content: "WARNING: ";
tr td.trow1:first-child, tr td.trow2:first-child {
border-left: 0;
}
.info
{
color: white;
display: block;
background-color: #5677fc !important;
padding: 15px;
line-height: 30px;
min-height: 30px;
tr td.trow1:last-child, tr td.trow2:last-child {
border-right: 0;
}
.info:before
{
content: "INFORMATION: ";
.tborder {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border-radius: 7px;
}
#footer .fleft
{
float: left;
.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;
}
#footer .fright
{
float: right;
text-align: right;
table {
color: #000;
font-size: 13px;
}
#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;
.tborder {
background: #fff;
width: 100%;
margin: auto;
border: 1px solid #ccc;
padding: 1px;
}
#footer a
{
text-transform: none;
color: white;
.thead {
background: #a1a2a2 url(thead.png) top left repeat-x;
color: #fff;
border-bottom: 1px solid #8e8f8f;
padding: 8px;
}
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;
.tcat {
background: #dcdbdc url(tcat.png) repeat-x;
color: #fff;
border-bottom: 1px solid #bbb;
padding: 6px;
font-size: 12px;
}
input[type="submit"]:hover, button:hover, input[type="button"]:hover
{
background-color: #ffca28;
.trow1 {
background: #f5f5f5;
border: 1px solid;
border-color: #fff #ddd #ddd #fff;
}
input[type="submit"]:active, button:active, input[type="button"]:active
{
background-color: #ffd54f;
-webkit-transform: translateY(1px);
-moz-transform: translateY(1px);
transform: translateY(1px);
.trow2 {
background: #efefef;
border: 1px solid;
border-color: #fff #ddd #ddd #fff;
padding: 15px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
hr
{
border: none;
height: 1px;
background-color: rgba(0, 0, 0, 0.12);
table {
color: #000;
font-size: 13px;
text-align: left;
}
h4
{
padding-bottom: 10px;
margin-bottom: 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
.tborder {
background: #fff;
width: 100%;
margin: auto;
border: 1px solid #ccc;
padding: 1px;
}
/**** PAGE SPECIFIC CSS ****/
/* remove the * for disabling: */
.page-core-server-settings table td
{
text-align: center;
width: 25%;
.thead {
background: #a1a2a2 url(thead.png) top left repeat-x;
color: #fff;
border-bottom: 1px solid #8e8f8f;
padding: 8px;
}
.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);
.tcat {
background: #dcdbdc url(tcat.png) repeat-x;
color: #fff;
border-bottom: 1px solid #bbb;
padding: 6px;
font-size: 12px;
}
.page-core-server-settings.param-tab-monsters table td:nth-child(2) a
{
font-weight: 600;
color: rgba(0, 0, 0, 0.87);
.trow1 {
background: #f5f5f5;
border: 1px solid;
border-color: #fff #ddd #ddd #fff;
}
.page-core-server-settings.param-tab-worlds table td:nth-child(3) a
{
font-weight: 600;
color: rgba(0, 0, 0, 0.87);
.trow2 {
background: #efefef;
border: 1px solid;
border-color: #fff #ddd #ddd #fff;
}
.page-core-server-settings.param-tab-world table td:nth-child(4) a
{
font-weight: 600;
color: rgba(0, 0, 0, 0.87);
.smalltext {
font-size: 11px;
}
.page-core-permissions form table tr,
.page-core-permissions form table td,
.page-core-permissions form table th
{
border: none;
background-color: transparent;
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;
}
.page-core-permissions form table tr:nth-child(1) th
{
width: 35%;
select {
background: #fff;
padding: 3px;
border: 1px solid #ccc;
font-family: Tahoma,Verdana,Arial,Sans-Serif;
}
.page-core-permissions form table tr:nth-child(1) td
{
width: 65%;
.usercp_nav_item {
display: block;
padding: 1px 0 1px 23px;
}
.page-core-permissions form table td input
{
width: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
margin: 0;
.usercp_nav_pmfolder {
background: url(pmfolder.gif) no-repeat left center;
}
#ChatDiv
{
margin-bottom: 10px;
.usercp_nav_sub_pmfolder {
padding-left: 40px;
background: url(sub_pmfolder.gif) no-repeat left center;
}
#ChatMessage
{
width: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
.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: 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

View File

@ -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">
</form>
</header>
<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>
</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>

View File

@ -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>&nbsp;&nbsp;&nbsp;<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([[
</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>
</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="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 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>
</div>
</body>
</html>

View File

@ -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

View File

@ -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);
}
else
{
int xofs = 0;
for (int x = skipx; x < blockwidth; x++, xofs +=4)
short biome = src[srcoffset + static_cast<size_t>(std::floor((double)x / m_Zoom))];
const uchar * color;
if (biome < 0)
{
memcpy(bits + imgoffset + xofs, src + srcoffset + (int)floor((double)x / m_Zoom) * 4, 4);
static const uchar emptyBiome1[] = { 0x44, 0x44, 0x44, 0xff };
static const uchar emptyBiome2[] = { 0x88, 0x88, 0x88, 0xff };
color = ((x & 8) ^ (z & 8)) ? emptyBiome1 : emptyBiome2;
}
}
}
else
{
if (biome * 4 >= ARRAYCOUNT(biomeToColor))
{
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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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))
{

View File

@ -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:

View File

@ -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"),

View File

@ -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);
}

View File

@ -12,76 +12,86 @@ TARGET = QtBiomeVisualiser
TEMPLATE = app
SOURCES +=\
MainWindow.cpp \
BiomeView.cpp \
../../src/Generating/BioGen.cpp \
../../src/VoronoiMap.cpp \
../../src/Noise.cpp \
../../src/StringUtils.cpp \
../../src/LoggerListeners.cpp \
../../src/Logger.cpp \
../../src/IniFile.cpp \
../../src/OSSupport/File.cpp \
../../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 \
../../lib/zlib/compress.c \
../../lib/zlib/crc32.c \
../../lib/zlib/deflate.c \
../../lib/zlib/gzclose.c \
../../lib/zlib/gzlib.c \
../../lib/zlib/gzread.c \
../../lib/zlib/gzwrite.c \
../../lib/zlib/infback.c \
../../lib/zlib/inffast.c \
../../lib/zlib/inflate.c \
../../lib/zlib/inftrees.c \
../../lib/zlib/trees.c \
../../lib/zlib/uncompr.c \
../../lib/zlib/zutil.c \
GeneratorSetup.cpp \
QtBiomeVisualiser.cpp \
QtChunk.cpp
SOURCES += \
MainWindow.cpp \
BiomeView.cpp \
../../src/Generating/BioGen.cpp \
../../src/VoronoiMap.cpp \
../../src/Noise.cpp \
../../src/StringUtils.cpp \
../../src/LoggerListeners.cpp \
../../src/Logger.cpp \
../../src/IniFile.cpp \
../../src/OSSupport/File.cpp \
../../src/OSSupport/CriticalSection.cpp \
../../src/OSSupport/IsThread.cpp \
../../src/BiomeDef.cpp \
../../src/StringCompression.cpp \
../../src/WorldStorage/FastNBT.cpp \
../../lib/zlib/adler32.c \
../../lib/zlib/compress.c \
../../lib/zlib/crc32.c \
../../lib/zlib/deflate.c \
../../lib/zlib/gzclose.c \
../../lib/zlib/gzlib.c \
../../lib/zlib/gzread.c \
../../lib/zlib/gzwrite.c \
../../lib/zlib/infback.c \
../../lib/zlib/inffast.c \
../../lib/zlib/inflate.c \
../../lib/zlib/inftrees.c \
../../lib/zlib/trees.c \
../../lib/zlib/uncompr.c \
../../lib/zlib/zutil.c \
GeneratorSetup.cpp \
QtBiomeVisualiser.cpp \
QtChunk.cpp \
RegionCache.cpp \
Region.cpp \
ChunkSource.cpp \
RegionLoader.cpp
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 \
../../src/LoggerListeners.h \
../../src/Logger.h \
../../src/IniFile.h \
../../src/OSSupport/File.h \
../../src/OSSupport/CriticalSection.h \
../../src/OSSupport/IsThread.h \
../../src/BiomeDef.h \
../../src/StringCompression.h \
../../src/WorldStorage/FastNBT.h \
../../lib/zlib/crc32.h \
../../lib/zlib/deflate.h \
../../lib/zlib/gzguts.h \
../../lib/zlib/inffast.h \
../../lib/zlib/inffixed.h \
../../lib/zlib/inflate.h \
../../lib/zlib/inftrees.h \
../../lib/zlib/trees.h \
../../lib/zlib/zconf.h \
../../lib/zlib/zlib.h \
../../lib/zlib/zutil.h \
GeneratorSetup.h \
QtChunk.h \
RegionCache.h \
Region.h \
ChunkSource.h \
RegionLoader.h
HEADERS += MainWindow.h \
Globals.h \
BiomeView.h \
../../src/Generating/BioGen.h \
../../src/VoronoiMap.h \
../../src/Noise.h \
../../src/StringUtils.h \
../../src/LoggerListeners.h \
../../src/Logger.h \
../../src/IniFile.h \
../../src/OSSupport/File.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 \
../../lib/zlib/deflate.h \
../../lib/zlib/gzguts.h \
../../lib/zlib/inffast.h \
../../lib/zlib/inffixed.h \
../../lib/zlib/inflate.h \
../../lib/zlib/inftrees.h \
../../lib/zlib/trees.h \
../../lib/zlib/zconf.h \
../../lib/zlib/zlib.h \
../../lib/zlib/zutil.h \
GeneratorSetup.h \
QtChunk.h
INCLUDEPATH += $$_PRO_FILE_PWD_ \
$$_PRO_FILE_PWD_/../../lib \

View File

@ -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]);
}

View File

@ -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;

View 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));
}

View 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;
};

View 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);
}

View File

@ -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);
};

View 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);
}

View 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
View 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

View File

@ -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);
}

View File

@ -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...);
}

View File

@ -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");

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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
{

View File

@ -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++
)

View File

@ -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"

View File

@ -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);

View File

@ -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;

View File

@ -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());
}
}

View File

@ -217,9 +217,15 @@ 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,12 +339,12 @@ 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;
/** Server generates this many chunks AHEAD of player sight. */
static const int GENERATEDISTANCE = 2;
/** The actual view distance used, the minimum of client's requested view distance and world's max view distance. */
int m_CurrentViewDistance;
/** 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;
AString m_Username;
@ -432,6 +438,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;

View File

@ -6,7 +6,7 @@
#include "Enchantments.h"
#include "WorldStorage/FastNBT.h"
#include "FastRandom.h"
#include "Noise.h"
#include "Noise/Noise.h"

View File

@ -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;
}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -13,7 +13,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -7,7 +7,7 @@
#include "ChunkDesc.h"
#include "../BlockArea.h"
#include "../Cuboid.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../BlockEntities/BlockEntity.h"

View File

@ -17,7 +17,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -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);

View File

@ -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

View File

@ -11,7 +11,6 @@
#include "ComposableGenerator.h"
#include "HeiGen.h"
#include "../Noise.h"

View File

@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -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"
@ -199,6 +198,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 (
@ -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

View File

@ -16,7 +16,7 @@
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"

View File

@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -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);
@ -220,10 +314,10 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
// 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 StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / 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)) / 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)
{
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 z = 0; z < 5; z++)
{
NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
for (int x = 0; x < 17; x += UPSCALE_X)
for (int x = 0; x < 5; 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
}

View File

@ -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;
int m_SeaLevel;
/** 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); }
} ;

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -14,7 +14,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -61,7 +61,7 @@ static const sCoords BigO3[] =
static const sCoords BigO4[] = // Part of Big Jungle tree
{
/* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -3 */ {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
/* -2 */ {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
/* -1 */ {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
@ -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;
}
}

View File

@ -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);

View File

@ -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 !!
@ -79,7 +79,7 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
}
}
bool IsFirstLine = true;
bool IsFirstLine = true;
while (getline(f, line))
{
@ -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.

View File

@ -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!!

View File

@ -6,7 +6,7 @@
#include "Globals.h"
#include "ItemGrid.h"
#include "Items/ItemHandler.h"
#include "Noise.h"
#include "Noise/Noise.h"

View File

@ -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;
}

View File

@ -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;

View File

@ -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
View 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
View 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;

View File

@ -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)
{
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] = 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++)
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_Array[i] += a_Workspace[i] * Amplitude;
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
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);
// 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];
// 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
);
} // 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)
{
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++)
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_Array[i] += a_Workspace[i] * Amplitude;
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
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);
// 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;
// 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
);
} // 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;
}
}

View File

@ -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
/** The permutation table used by the noise function. Initialized using seed. */
int m_Perm[512];
/** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */
inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T)
{
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)
{
}
} ;
typedef std::vector<cOctave> cOctaves;
int m_Seed;
cOctaves m_Octaves;
} ;
return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
}
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
/** 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)
{
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)
{
}
} ;
typedef std::vector<cOctave> cOctaves;
int m_Seed;
cOctaves m_Octaves;
} ;
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 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
View 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
View 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;
} ;

View File

@ -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);

View File

@ -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)

View File

@ -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>(

View File

@ -9,7 +9,7 @@
#pragma once
#include "Noise.h"
#include "Noise/Noise.h"

View File

@ -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

View File

@ -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,11 +378,8 @@ 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;

View File

@ -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();
}

View File

@ -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;
}
a_Item.m_ItemType = a_NBT.GetShort(Type);
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();