Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86d07eb62b | |||
| 39c7ae8d17 | |||
| 5c369fd515 | |||
| 2b0926a097 | |||
| 9b6b67b178 | |||
| bb07e3db9a | |||
| 46e65704d7 | |||
| d5ec17551e | |||
| 8390cfc6fb | |||
| 2711fbf01a | |||
| e4f64df660 | |||
| a6c9d0df98 | |||
| 136dd5b47a | |||
| 277def2acc | |||
| e941b553e7 | |||
| 92e651c779 | |||
| 2b82eb88c8 | |||
| 13fecf9d1d | |||
| cdd1529a13 | |||
| 7915444713 | |||
| 2135ae769d | |||
| b98cfb9666 | |||
| a28da86d44 | |||
| 799b0e2481 | |||
| e380c7c069 | |||
| 5a3b8f5baa | |||
| a1c547b72e | |||
| bb5613e3fe | |||
| a29060bf4f | |||
| 79ee23e0b0 | |||
| d3231a480d | |||
| 3694dcba4c |
@@ -22,4 +22,5 @@ eclipse
|
||||
run
|
||||
|
||||
# Files from Forge MDK
|
||||
logs
|
||||
forge*changelog.txt
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
Minecraft Forge: Credits/Thank You
|
||||
|
||||
Forge is a set of tools and modifications to the Minecraft base game code to assist
|
||||
mod developers in creating new and exciting content. It has been in development for
|
||||
several years now, but I would like to take this time thank a few people who have
|
||||
helped it along it's way.
|
||||
|
||||
First, the people who originally created the Forge projects way back in Minecraft
|
||||
alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
|
||||
of me taking over the project, who knows what Minecraft modding would be today.
|
||||
|
||||
Secondly, someone who has worked with me, and developed some of the core features
|
||||
that allow modding to be as functional, and as simple as it is, cpw. For developing
|
||||
FML, which stabelized the client and server modding ecosystem. As well as the base
|
||||
loading system that allows us to modify Minecraft's code as elegently as possible.
|
||||
|
||||
Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
|
||||
sane as well as guiding the community into creating better additions to Forge.
|
||||
|
||||
Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
|
||||
I am a part}. For creating some of the core tools needed to make Minecraft modding
|
||||
both possible, and as stable as can be.
|
||||
On that note, here is some specific information of the MCP data we use:
|
||||
* Minecraft Coder Pack (MCP) *
|
||||
Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
|
||||
download components of MCP and distribute MCP data files. This permission is not
|
||||
transitive and others wishing to redistribute the Minecraft Forge source independently
|
||||
should seek permission of MCP or remove the MCP data files and request their users
|
||||
to download MCP separately.
|
||||
|
||||
And lastly, the countless community members who have spent time submitting bug reports,
|
||||
pull requests, and just helping out the community in general. Thank you.
|
||||
|
||||
--LexManos
|
||||
|
||||
=========================================================================
|
||||
|
||||
This is Forge Mod Loader.
|
||||
|
||||
You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
|
||||
|
||||
This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
|
||||
and minecraft clients.
|
||||
|
||||
The code is authored by cpw.
|
||||
|
||||
It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
|
||||
http://www.minecraftforum.net/topic/75440-
|
||||
This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
|
||||
|
||||
It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
|
||||
http://www.minecraftforge.net/
|
||||
|
||||
Additionally, it contains an implementation of topological sort based on that
|
||||
published at http://keithschwarz.com/interesting/code/?dir=topological-sort
|
||||
|
||||
It also contains code from the Maven project for performing versioned dependency
|
||||
resolution. http://maven.apache.org/
|
||||
|
||||
It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
|
||||
with credit to it's authors.
|
||||
|
||||
Forge Mod Loader downloads components from the Minecraft Coder Pack
|
||||
(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
|
||||
|
||||
-520
@@ -1,520 +0,0 @@
|
||||
Unless noted below, Minecraft Forge, Forge Mod Loader, and all
|
||||
parts herein are licensed under the terms of the LGPL 2.1 found
|
||||
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
|
||||
copied below.
|
||||
|
||||
Homepage: http://minecraftforge.net/
|
||||
https://github.com/MinecraftForge/MinecraftForge
|
||||
|
||||
|
||||
A note on authorship:
|
||||
All source artifacts are property of their original author, with
|
||||
the exclusion of the contents of the patches directory and others
|
||||
copied from it from time to time. Authorship of the contents of
|
||||
the patches directory is retained by the Minecraft Forge project.
|
||||
This is because the patches are partially machine generated
|
||||
artifacts, and are changed heavily due to the way forge works.
|
||||
Individual attribution within them is impossible.
|
||||
|
||||
Consent:
|
||||
All contributions to Forge must consent to the release of any
|
||||
patch content to the Forge project.
|
||||
|
||||
A note on infectivity:
|
||||
The LGPL is chosen specifically so that projects may depend on Forge
|
||||
features without being infected with its license. That is the
|
||||
purpose of the LGPL. Mods and others using this code via ordinary
|
||||
Java mechanics for referencing libraries are specifically not bound
|
||||
by Forge's license for the Mod code.
|
||||
|
||||
|
||||
=== MCP Data ===
|
||||
This software includes data from the Minecraft Coder Pack (MCP), with kind permission
|
||||
from them. The license to MCP data is not transitive - distribution of this data by
|
||||
third parties requires independent licensing from the MCP team. This data is not
|
||||
redistributable without permission from the MCP team.
|
||||
|
||||
=== Sharing ===
|
||||
I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
|
||||
the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
|
||||
|
||||
-- Runtime patcher --
|
||||
License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
|
||||
and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
|
||||
|
||||
-- ASM transformers --
|
||||
License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
|
||||
under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
|
||||
|
||||
=========================================================================
|
||||
This software includes portions from the Apache Maven project at
|
||||
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
|
||||
included based on guidelines at
|
||||
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
|
||||
with notices intact. The only change is a non-functional change of package name.
|
||||
|
||||
This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
|
||||
binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
|
||||
authored by genman, heikok, pivot.
|
||||
The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
|
||||
=========================================================================
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -12,9 +12,6 @@ Forge version: 1.16.4-35.1.4
|
||||
Notes:
|
||||
This version has been confirmed to work in Eclipse and retail Minecraft.
|
||||
(retail running forge 1.16.4-35.1.37)
|
||||
That being said only singleplayer is currently supported; connecting
|
||||
to servers (local or otherwise) will cause no LODs to be drawn and
|
||||
may cause instibility.
|
||||
|
||||
|
||||
========================
|
||||
|
||||
-1664
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
package com.seibel.lod;
|
||||
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
@@ -4,22 +4,27 @@ import java.util.concurrent.Executors;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.seibel.lod.builders.worldGeneration.LodChunkGenWorker;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarBuffer;
|
||||
import com.seibel.lod.render.LodRender;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager;
|
||||
|
||||
/**
|
||||
* This object is used to create NearFarBuffer objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-06-2021
|
||||
* @version 06-19-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
@@ -46,12 +51,12 @@ public class LodBufferBuilder
|
||||
/** If this is greater than 0 no new chunk generation requests will be made
|
||||
* this is to prevent chunks from being generated for a long time in an area
|
||||
* the player is no longer in. */
|
||||
public int numberOfChunksWaitingToGenerate = 0;
|
||||
public volatile int numberOfChunksWaitingToGenerate = 0;
|
||||
|
||||
/** how many chunks to generate outside of the player's
|
||||
* view distance at one time. (or more specifically how
|
||||
* many requests to make at one time) */
|
||||
public int maxChunkGenRequests = 8;
|
||||
public int maxChunkGenRequests = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
|
||||
public LodBufferBuilder(LodBuilder newLodBuilder)
|
||||
@@ -61,7 +66,8 @@ public class LodBufferBuilder
|
||||
}
|
||||
|
||||
|
||||
|
||||
private BiomeContainer biomeContainer = null;
|
||||
private LodDimension previousDimension = null;
|
||||
|
||||
|
||||
/**
|
||||
@@ -73,7 +79,7 @@ public class LodBufferBuilder
|
||||
* After the buildable buffers have been generated they must be
|
||||
* swapped with the drawable buffers in the LodRenderer to be drawn.
|
||||
*/
|
||||
public void generateLodBuffersAsync(LodRender renderer, LodDimension lodDim,
|
||||
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
|
||||
double playerX, double playerZ, int numbChunksWide)
|
||||
{
|
||||
// only allow one generation process to happen at a time
|
||||
@@ -83,6 +89,12 @@ public class LodBufferBuilder
|
||||
if (buildableNearBuffer == null || buildableFarBuffer == null)
|
||||
throw new IllegalStateException("generateLodBuffersAsync was called before the buildableNearBuffer and buildableFarBuffer were created.");
|
||||
|
||||
if (previousDimension != lodDim)
|
||||
{
|
||||
biomeContainer = LodUtil.getServerWorldFromDimension(lodDim.dimension).getChunk(0, 0, ChunkStatus.EMPTY).getBiomes();
|
||||
previousDimension = lodDim;
|
||||
}
|
||||
|
||||
|
||||
|
||||
generatingBuffers = true;
|
||||
@@ -99,7 +111,6 @@ public class LodBufferBuilder
|
||||
int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
|
||||
|
||||
|
||||
|
||||
Thread t = new Thread(()->
|
||||
{
|
||||
// index of the chunk currently being added to the
|
||||
@@ -107,13 +118,16 @@ public class LodBufferBuilder
|
||||
int chunkGenIndex = 0;
|
||||
|
||||
ChunkPos[] chunksToGen = new ChunkPos[maxChunkGenRequests];
|
||||
// if we don't have a full number of chunks to generate in chunksToGen
|
||||
// we can top it off from the reserve
|
||||
ChunkPos[] chunksToGenReserve = new ChunkPos[maxChunkGenRequests];
|
||||
int minChunkDist = Integer.MAX_VALUE;
|
||||
ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH);
|
||||
|
||||
|
||||
// generate our new buildable buffers
|
||||
buildableNearBuffer.begin(GL11.GL_QUADS, LodRender.LOD_VERTEX_FORMAT);
|
||||
buildableFarBuffer.begin(GL11.GL_QUADS, LodRender.LOD_VERTEX_FORMAT);
|
||||
buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
|
||||
// x axis
|
||||
for (int i = 0; i < numbChunksWide; i++)
|
||||
@@ -121,12 +135,12 @@ public class LodBufferBuilder
|
||||
// z axis
|
||||
for (int j = 0; j < numbChunksWide; j++)
|
||||
{
|
||||
// skip the middle
|
||||
// (As the player moves some chunks will overlap or be missing,
|
||||
// this is just how chunk loading/unloading works. This can hopefully
|
||||
// be hidden with careful use of fog)
|
||||
int middle = (numbChunksWide / 2);
|
||||
if (isCoordInCenterArea(i, j, middle))
|
||||
int chunkX = i + (startX / LodChunk.WIDTH);
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
if(isCoordInCenterArea(i, j, (numbChunksWide / 2))
|
||||
&& renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -138,16 +152,13 @@ public class LodBufferBuilder
|
||||
double yOffset = 0;
|
||||
double zOffset = (LodChunk.WIDTH * j) + startZ;
|
||||
|
||||
int chunkX = i + (startX / LodChunk.WIDTH);
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
LodChunk lod = lodDim.getLodFromCoordinates(chunkX, chunkZ);
|
||||
|
||||
if (lod == null || lod.isLodEmpty())
|
||||
{
|
||||
// generate a new chunk if no chunk currently exists
|
||||
// and we aren't waiting on any other chunks to generate
|
||||
if (lod == null && numberOfChunksWaitingToGenerate == 0)
|
||||
if (lod == null && numberOfChunksWaitingToGenerate < maxChunkGenRequests)
|
||||
{
|
||||
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
||||
|
||||
@@ -160,6 +171,7 @@ public class LodBufferBuilder
|
||||
// this chunk is closer, clear any previous
|
||||
// positions and update the new minimum distance
|
||||
minChunkDist = newDistance;
|
||||
chunksToGenReserve = chunksToGen;
|
||||
|
||||
chunkGenIndex = 0;
|
||||
chunksToGen = new ChunkPos[maxChunkGenRequests];
|
||||
@@ -193,8 +205,8 @@ public class LodBufferBuilder
|
||||
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodConfig.CLIENT.lodTemplate.get().template.
|
||||
addLodToBuffer(currentBuffer, lodDim, lod,
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, lod,
|
||||
xOffset, yOffset, zOffset, renderer.debugging);
|
||||
}
|
||||
}
|
||||
@@ -202,6 +214,17 @@ public class LodBufferBuilder
|
||||
// TODO add a way for a server side mod to generate chunks requested here
|
||||
if(mc.isIntegratedServerRunning())
|
||||
{
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// make sure we have as many chunks to generate as we are allowed
|
||||
if (chunkGenIndex < maxChunkGenRequests)
|
||||
{
|
||||
for(int i = chunkGenIndex, j = 0; i < maxChunkGenRequests; i++, j++)
|
||||
{
|
||||
chunksToGen[i] = chunksToGenReserve[j];
|
||||
}
|
||||
}
|
||||
|
||||
// start chunk generation
|
||||
for(ChunkPos chunkPos : chunksToGen)
|
||||
{
|
||||
@@ -210,7 +233,7 @@ public class LodBufferBuilder
|
||||
|
||||
numberOfChunksWaitingToGenerate++;
|
||||
|
||||
LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodBuilder, this, lodDim);
|
||||
LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodBuilder, this, lodDim, serverWorld, biomeContainer);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
}
|
||||
@@ -238,7 +261,6 @@ public class LodBufferBuilder
|
||||
|
||||
|
||||
|
||||
|
||||
//====================//
|
||||
// generation helpers //
|
||||
//====================//
|
||||
|
||||
@@ -4,18 +4,22 @@ import java.awt.Color;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.ColorDirection;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.util.LodUtils;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.color.BlockColors;
|
||||
import net.minecraft.block.FlowingFluidBlock;
|
||||
import net.minecraft.block.GrassBlock;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
@@ -26,12 +30,10 @@ import net.minecraft.world.gen.Heightmap;
|
||||
* (specifically: Lod World, Dimension, Region, and Chunk objects)
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 5-29-2021
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
|
||||
|
||||
/** Default size of any LOD regions we use */
|
||||
@@ -41,13 +43,6 @@ public class LodBuilder
|
||||
public static final int CHUNK_DATA_WIDTH = LodChunk.WIDTH;
|
||||
public static final int CHUNK_SECTION_HEIGHT = LodChunk.WIDTH;
|
||||
|
||||
/**
|
||||
* This is how many blocks are
|
||||
* required at a specific y-value
|
||||
* to constitute a LOD point
|
||||
*/
|
||||
private final int LOD_BLOCK_REQ = 32;
|
||||
// the max number of blocks per layer = 64 (8*8)
|
||||
|
||||
|
||||
public LodBuilder()
|
||||
@@ -56,18 +51,15 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
generateLodChunkAsync(chunk, new LodBuilderConfig(), lodWorld, world);
|
||||
}
|
||||
|
||||
public void generateLodChunkAsync(IChunk chunk, LodBuilderConfig config, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// is this chunk from the same world as the lodWorld?
|
||||
if (!lodWorld.getWorldName().equals(LodUtils.getWorldID(world)))
|
||||
// we are not in the same world anymore
|
||||
// don't add this LOD
|
||||
return;
|
||||
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
@@ -81,7 +73,7 @@ public class LodBuilder
|
||||
{
|
||||
DimensionType dim = world.getDimensionType();
|
||||
|
||||
LodChunk lod = generateLodFromChunk(chunk);
|
||||
LodChunk lod = generateLodFromChunk(chunk, config);
|
||||
|
||||
LodDimension lodDim;
|
||||
|
||||
@@ -113,8 +105,6 @@ public class LodBuilder
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
@@ -122,39 +112,58 @@ public class LodBuilder
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException
|
||||
{
|
||||
return generateLodFromChunk(chunk, new LodBuilderConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException
|
||||
{
|
||||
if(chunk == null)
|
||||
{
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
}
|
||||
|
||||
Color[] colors = new Color[ColorDirection.values().length];
|
||||
|
||||
|
||||
short height = determineTopPoint(chunk.getSections());
|
||||
short depth = determineBottomPoint(chunk.getSections());
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
LodDataPoint[][] dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
|
||||
|
||||
// determine the average color for each direction
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
colors[dir.value] = generateLodColorForDirection(chunk, dir);
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
Color color;
|
||||
|
||||
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
|
||||
|
||||
|
||||
short height;
|
||||
short depth;
|
||||
|
||||
if (!config.useHeightmap)
|
||||
{
|
||||
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = determineHeightPoint(chunk.getHeightmap(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ);
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
int x = i / detail.dataPointLengthCount;
|
||||
int z = i % detail.dataPointLengthCount;
|
||||
|
||||
dataPoints[x][z] = new LodDataPoint(height, depth, color);
|
||||
}
|
||||
|
||||
// check to see if there are any invisible sides
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
// if there are any invisible sides
|
||||
// replace them with the top side
|
||||
// (this is done to make sure oceans and other totally
|
||||
// flat locations have the correct side colors)
|
||||
if (dir == ColorDirection.BOTTOM || dir == ColorDirection.TOP)
|
||||
continue;
|
||||
if (colors[dir.value] == INVISIBLE)
|
||||
colors[dir.value] = colors[ColorDirection.TOP.value];
|
||||
}
|
||||
|
||||
return new LodChunk(chunk.getPos(), height, depth, colors);
|
||||
return new LodChunk(chunk.getPos(), dataPoints, detail);
|
||||
}
|
||||
|
||||
|
||||
@@ -169,21 +178,42 @@ public class LodBuilder
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* @param chunkSections
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
*/
|
||||
private short determineBottomPoint(ChunkSection[] chunkSections)
|
||||
private short determineBottomPointForArea(ChunkSection[] chunkSections,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2);
|
||||
|
||||
// search from the bottom up
|
||||
for(int i = 0; i < CHUNK_DATA_WIDTH; i++)
|
||||
for(int section = 0; section < CHUNK_DATA_WIDTH; section++)
|
||||
{
|
||||
for(int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, i, y))
|
||||
int numberOfBlocksFound = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (i * CHUNK_SECTION_HEIGHT));
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,21 +237,42 @@ public class LodBuilder
|
||||
|
||||
/**
|
||||
* Find the highest valid point from the Top
|
||||
* @param chunkSections
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
*/
|
||||
private short determineTopPoint(ChunkSection[] chunkSections)
|
||||
private short determineHeightPointForArea(ChunkSection[] chunkSections,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2);
|
||||
|
||||
// search from the top down
|
||||
for(int section = chunkSections.length - 1; section >= 0; section--)
|
||||
{
|
||||
for(int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, section, y))
|
||||
int numberOfBlocksFound = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
if(isLayerValidLodPoint(chunkSections, section, y, x, z))
|
||||
{
|
||||
numberOfBlocksFound++;
|
||||
|
||||
if (numberOfBlocksFound >= numberOfBlocksRequired)
|
||||
{
|
||||
// we found
|
||||
// enough blocks in this
|
||||
// layer to count as an
|
||||
// LOD point
|
||||
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,13 +284,13 @@ public class LodBuilder
|
||||
/**
|
||||
* Find the highest point from the Top
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private short determineTopPoint(Heightmap heightmap, int endZ)
|
||||
private short determineHeightPoint(Heightmap heightmap,
|
||||
int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
short highest = 0;
|
||||
for(int x = 0; x < LodChunk.WIDTH; x++)
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = 0; z < LodChunk.WIDTH; z++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
short newHeight = (short) heightmap.getHeight(x, z);
|
||||
if (newHeight > highest)
|
||||
@@ -251,96 +302,20 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the color for the given chunk in the given ColorDirection.
|
||||
*/
|
||||
private Color generateLodColorForDirection(IChunk chunk, ColorDirection colorDir)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
BlockColors bc = mc.getBlockColors();
|
||||
|
||||
switch (colorDir)
|
||||
{
|
||||
case TOP:
|
||||
return generateLodColorVertical(chunk, colorDir, bc);
|
||||
case BOTTOM:
|
||||
return generateLodColorVertical(chunk, colorDir, bc);
|
||||
|
||||
case NORTH:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
case SOUTH:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
|
||||
case EAST:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
case WEST:
|
||||
return generateLodColorHorizontal(chunk, colorDir, bc);
|
||||
}
|
||||
|
||||
return INVISIBLE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Is the layer between the given X, Z, and dataIndex
|
||||
* values a valid LOD point?
|
||||
*/
|
||||
private boolean isLayerValidLodPoint(
|
||||
ChunkSection[] chunkSections,
|
||||
int sectionIndex, int y)
|
||||
{
|
||||
// search through this layer
|
||||
int layerBlocks = 0;
|
||||
|
||||
for(int x = 0; x < LodChunk.WIDTH; x++)
|
||||
{
|
||||
for(int z = 0; z < LodChunk.WIDTH; z++)
|
||||
{
|
||||
if(chunkSections[sectionIndex] == null)
|
||||
{
|
||||
// this section doesn't have any blocks,
|
||||
// it is not a valid section
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(chunkSections[sectionIndex].getBlockState(x, y, z) != null &&
|
||||
chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR)
|
||||
{
|
||||
// we found a valid block in
|
||||
// in this layer
|
||||
layerBlocks++;
|
||||
|
||||
if(layerBlocks >= LOD_BLOCK_REQ)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // z
|
||||
} // x
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the color of the top or bottom of the given chunk.
|
||||
* Generate the color for the given chunk using biome
|
||||
* water color, foliage color, and grass color.
|
||||
*
|
||||
* @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM
|
||||
* @param config_useSolidBlocksInColorGen <br>
|
||||
* If true we look down from the top of the <br>
|
||||
* chunk until we find a non-invisible block, and then use <br>
|
||||
* its color. If false we generate the color immediately for <br>
|
||||
* each x and z.
|
||||
* @param config_useBiomeColors <br>
|
||||
* If true use biome foliage, water, and grass colors, <br>
|
||||
* otherwise use the
|
||||
*/
|
||||
private Color generateLodColorVertical(IChunk chunk, ColorDirection colorDir, BlockColors bc)
|
||||
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM)
|
||||
{
|
||||
throw new IllegalArgumentException("generateLodColorVertical only accepts the ColorDirection TOP or BOTTOM");
|
||||
}
|
||||
|
||||
|
||||
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
@@ -348,42 +323,73 @@ public class LodBuilder
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
boolean goTopDown = (colorDir == ColorDirection.TOP);
|
||||
|
||||
|
||||
// either go top down or bottom up
|
||||
int dataStart = goTopDown? chunkSections.length - 1 : 0;
|
||||
int dataMax = chunkSections.length;
|
||||
int dataMin = 0;
|
||||
int dataIncrement = goTopDown? -1 : 1;
|
||||
|
||||
int topStart = goTopDown? CHUNK_SECTION_HEIGHT - 1 : 0;
|
||||
int topMax = CHUNK_SECTION_HEIGHT;
|
||||
int topMin = 0;
|
||||
int topIncrement = goTopDown? -1 : 1;
|
||||
|
||||
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = 0; z < CHUNK_DATA_WIDTH; z++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
for(int i = dataStart; !foundBlock && i >= dataMin && i < dataMax; i += dataIncrement)
|
||||
// go top down
|
||||
for(int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
|
||||
{
|
||||
if(!foundBlock && chunkSections[i] != null)
|
||||
if( !foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
|
||||
{
|
||||
for(int y = topStart; !foundBlock && y >= topMin && y < topMax; y += topIncrement)
|
||||
for(int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
|
||||
{
|
||||
int ci;
|
||||
ci = chunkSections[i].getBlockState(x, y, z).materialColor.colorValue;
|
||||
int colorInt = 0;
|
||||
BlockState blockState = null;
|
||||
|
||||
if(ci == 0)
|
||||
if (chunkSections[i] != null)
|
||||
{
|
||||
blockState = chunkSections[i].getBlockState(x, y, z);
|
||||
colorInt = blockState.materialColor.colorValue;
|
||||
}
|
||||
|
||||
if(colorInt == 0 && config.useSolidBlocksInColorGen)
|
||||
{
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = intToColor(ci);
|
||||
if (config.useBiomeColors)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
|
||||
if (biome.getCategory() == Biome.Category.OCEAN ||
|
||||
biome.getCategory() == Biome.Category.RIVER)
|
||||
{
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.EXTREME_HILLS)
|
||||
{
|
||||
colorInt = Blocks.STONE.getMaterialColor().colorValue;
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.ICY)
|
||||
{
|
||||
colorInt = LodUtil.colorToInt(Color.WHITE);
|
||||
}
|
||||
else if (biome.getCategory() == Biome.Category.THEEND)
|
||||
{
|
||||
colorInt = Blocks.END_STONE.getDefaultState().materialColor.colorValue;
|
||||
}
|
||||
else if (config.useSolidBlocksInColorGen)
|
||||
{
|
||||
colorInt = getColorForBlock(x, z, blockState, biome);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z);
|
||||
colorInt = getColorForBlock(x,z, blockState, biome);
|
||||
}
|
||||
|
||||
|
||||
Color c = LodUtil.intToColor(colorInt);
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
@@ -411,224 +417,72 @@ public class LodBuilder
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/*
|
||||
* unused variation that can be used with only the heightmap,
|
||||
* although it just returns the foliage color, so it shouldn't
|
||||
* be used normally.
|
||||
|
||||
Heightmap heightmap = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE_WG);
|
||||
|
||||
int numbOfBlocks = CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x,z, heightmap.getHeight(x, z));
|
||||
Color c = intToColor(biome.getFoliageColor());
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
}
|
||||
}
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generates the color for the sides of the given chunk.
|
||||
* Returns a color int for a given block.
|
||||
*/
|
||||
private Color generateLodColorHorizontal(IChunk chunk, ColorDirection colorDir, BlockColors bc)
|
||||
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
|
||||
{
|
||||
if(colorDir != ColorDirection.NORTH && colorDir != ColorDirection.SOUTH && colorDir != ColorDirection.EAST && colorDir != ColorDirection.WEST)
|
||||
int colorInt = 0;
|
||||
|
||||
if (blockState == Blocks.AIR.getDefaultState())
|
||||
{
|
||||
throw new IllegalArgumentException("generateLodColorHorizontal only accepts the ColorDirection N (North), S (South), E (East), or W (West)");
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
|
||||
// these don't change since the over direction doesn't matter
|
||||
int overStart = 0;
|
||||
int overIncrement = 1;
|
||||
|
||||
// determine which direction is "in"
|
||||
int inStart = 0;
|
||||
int inIncrement = 1;
|
||||
switch (colorDir)
|
||||
else if (blockState.getBlock() instanceof LeavesBlock)
|
||||
{
|
||||
case NORTH:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case SOUTH:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
case EAST:
|
||||
inStart = 0;
|
||||
inIncrement = 1;
|
||||
break;
|
||||
case WEST:
|
||||
inStart = CHUNK_DATA_WIDTH - 1;
|
||||
inIncrement = -1;
|
||||
break;
|
||||
default:
|
||||
// we were given an invalid position, return invisible.
|
||||
// this shouldn't happen and is mostly here to make the
|
||||
// compiler happy
|
||||
return INVISIBLE;
|
||||
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
|
||||
colorInt = LodUtil.colorToInt(leafColor);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof GrassBlock)
|
||||
{
|
||||
colorInt = biome.getGrassColor(x, z);
|
||||
}
|
||||
else if (blockState.getBlock() instanceof FlowingFluidBlock)
|
||||
{
|
||||
colorInt = biome.getWaterColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
colorInt = blockState.materialColor.colorValue;
|
||||
}
|
||||
|
||||
|
||||
for (int section = 0; section < chunkSections.length; section++)
|
||||
{
|
||||
if (chunkSections[section] == null)
|
||||
continue;
|
||||
|
||||
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
|
||||
{
|
||||
boolean foundBlock = false;
|
||||
|
||||
// over moves "over" the side of the chunk
|
||||
// in moves "into" the chunk until it finds a block
|
||||
for (int over = overStart; !foundBlock && over >= 0 && over < CHUNK_DATA_WIDTH; over += overIncrement)
|
||||
{
|
||||
for (int in = inStart; !foundBlock && in >= 0 && in < CHUNK_DATA_WIDTH; in += inIncrement)
|
||||
{
|
||||
int x = -1;
|
||||
int z = -1;
|
||||
|
||||
// determine which should be X and Z
|
||||
switch(colorDir)
|
||||
{
|
||||
case NORTH:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case SOUTH:
|
||||
x = over;
|
||||
z = in;
|
||||
break;
|
||||
case EAST:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
case WEST:
|
||||
x = in;
|
||||
z = over;
|
||||
break;
|
||||
default:
|
||||
// here to make the compiler happy
|
||||
break;
|
||||
}
|
||||
|
||||
// if this block is buried, under other blocks
|
||||
// don't add it
|
||||
if(!isBlockPosVisible(chunkSections[section], x,y,z))
|
||||
{
|
||||
// go to the next "over" block location,
|
||||
// don't look at the next "in" location,
|
||||
// since the next "in" location will more than
|
||||
// likely still be covered
|
||||
in = CHUNK_DATA_WIDTH + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
int ci;
|
||||
ci = chunkSections[section].getBlockState(x, y, z).getMaterial().getColor().colorValue;
|
||||
|
||||
if (ci == 0) {
|
||||
// skip air or invisible blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
Color c = intToColor(ci);
|
||||
|
||||
red += c.getRed();
|
||||
green += c.getGreen();
|
||||
blue += c.getBlue();
|
||||
|
||||
numbOfBlocks++;
|
||||
|
||||
// we found a valid block, skip to the
|
||||
// next x and z
|
||||
foundBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find any blocks return invisible
|
||||
if(numbOfBlocks == 0)
|
||||
return INVISIBLE;
|
||||
|
||||
red /= numbOfBlocks;
|
||||
green /= numbOfBlocks;
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static BlockState airState = Blocks.AIR.getDefaultState();
|
||||
|
||||
/**
|
||||
* returns true if the block at the given coordinates is open to
|
||||
* air on at least one side.
|
||||
/**
|
||||
* Is the layer between the given X, Z, and dataIndex
|
||||
* values a valid LOD point?
|
||||
*/
|
||||
private boolean isBlockPosVisible(ChunkSection chunkSection, int x, int y, int z)
|
||||
private boolean isLayerValidLodPoint(
|
||||
ChunkSection[] chunkSections,
|
||||
int sectionIndex, int y,
|
||||
int x, int z)
|
||||
{
|
||||
/*
|
||||
// above
|
||||
if (y+1 < CHUNK_SECTION_HEIGHT) // don't go over the top
|
||||
if (chunkSection.getBlockState(x, y+1, z).getBlock() == (Blocks.AIR))
|
||||
if(chunkSections[sectionIndex] == null)
|
||||
{
|
||||
// this section doesn't have any blocks,
|
||||
// it is not a valid section
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(chunkSections[sectionIndex].getBlockState(x, y, z) != null &&
|
||||
chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR)
|
||||
{
|
||||
return true;
|
||||
// below
|
||||
if (y-1 >= 0) // don't go below the bottom
|
||||
if (chunkSection.getBlockState(x, y-1, z).getBlock() == (Blocks.AIR))
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// north
|
||||
if (z-1 > 0)
|
||||
if (chunkSection.getBlockState(x, y, z-1) == airState)
|
||||
return true;
|
||||
// south
|
||||
if (z+1 < LodChunk.WIDTH)
|
||||
if (chunkSection.getBlockState(x, y, z+1) == airState)
|
||||
return true;
|
||||
|
||||
// east
|
||||
if (x+1 <= LodChunk.WIDTH)
|
||||
if (chunkSection.getBlockState(x+1, y, z) == airState)
|
||||
return true;
|
||||
// west
|
||||
if (x-1 >= 0)
|
||||
if (chunkSection.getBlockState(x-1, y, z) == airState)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -636,28 +490,4 @@ public class LodBuilder
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a BlockColors int into a Color object.
|
||||
*/
|
||||
private Color intToColor(int num)
|
||||
{
|
||||
int filter = 0b11111111;
|
||||
|
||||
int red = (num >> 16 ) & filter;
|
||||
int green = (num >> 8 ) & filter;
|
||||
int blue = num & filter;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Color into a BlockColors object.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int colorToInt(Color color)
|
||||
{
|
||||
return color.getRGB();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
/**
|
||||
* This is used to easily configure how LodChunks are generated.
|
||||
* Generally this will only be used if we want to generate a
|
||||
* LodChunk using a incomplete Chunk, otherwise the defaults
|
||||
* work best for a fully generated chunk (IE has correct surface blocks).
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodBuilderConfig
|
||||
{
|
||||
public boolean useHeightmap;
|
||||
public boolean useBiomeColors;
|
||||
public boolean useSolidBlocksInColorGen;
|
||||
|
||||
/** default settings for a normal chunk
|
||||
* useHeightmap = false
|
||||
* useBiomeColors = false
|
||||
* useSolidBlocksInColorGen = true
|
||||
*/
|
||||
public LodBuilderConfig()
|
||||
{
|
||||
useHeightmap = false;
|
||||
useBiomeColors = false;
|
||||
useSolidBlocksInColorGen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUseHeightmap
|
||||
* @param newUseBiomeColors
|
||||
* @param newUseSolidBlocksInBiomeColor
|
||||
*/
|
||||
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
|
||||
{
|
||||
useHeightmap = newUseHeightmap;
|
||||
useBiomeColors = newUseBiomeColors;
|
||||
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRender;
|
||||
import com.seibel.lod.util.LodUtils;
|
||||
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-31-2021
|
||||
*/
|
||||
public class LodChunkGenWorker implements IWorker
|
||||
{
|
||||
private ServerWorld serverWorld;
|
||||
private ChunkPos pos;
|
||||
private LodDimension lodDim;
|
||||
private LodBuilder lodBuilder;
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
private LodRender lodRender;
|
||||
|
||||
public LodChunkGenWorker(ChunkPos newPos, LodRender newLodRenderer,
|
||||
LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension)
|
||||
{
|
||||
serverWorld = LodUtils.getServerWorldFromDimension(newLodDimension.dimension);
|
||||
if (serverWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ServerWorld");
|
||||
|
||||
pos = newPos;
|
||||
lodDim = newLodDimension;
|
||||
lodBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodRender = newLodRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (pos != null)
|
||||
{
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
|
||||
{
|
||||
IChunk chunk;// = serverWorld.getChunk(x, z, ChunkStatus.EMPTY, true);
|
||||
|
||||
//long startTime = System.currentTimeMillis();
|
||||
chunk = serverWorld.getChunk(x, z, ChunkStatus.FEATURES);
|
||||
//long endTime = System.currentTimeMillis();
|
||||
//System.out.println(endTime - startTime + "\t" + lodBuilder.hasBlockData(chunk));
|
||||
|
||||
|
||||
lodBuilder.generateLodChunkAsync(chunk, ClientProxy.getLodWorld(), serverWorld);
|
||||
// this is called so that the new LOD chunk is drawn
|
||||
// after it is generated
|
||||
lodRender.regenerateLODsNextFrame();
|
||||
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(lodDim.getNumberOfLods());
|
||||
|
||||
// if (lodDim.getLodFromCoordinates(x, z) != null)
|
||||
// ClientProxy.LOGGER.info(x + " " + z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(x + " " + z);
|
||||
}
|
||||
// can be used for debugging
|
||||
//else
|
||||
//{
|
||||
// System.out.println("Out of range " + x + " " + z);
|
||||
//}
|
||||
|
||||
lodBufferBuilder.numberOfChunksWaitingToGenerate--;
|
||||
|
||||
pos = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return pos != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* performance/generation tests related to
|
||||
* serverWorld.getChunk(x, z, ChunkStatus. *** )
|
||||
|
||||
true/false is whether they generated blocks or not
|
||||
the time is how long it took to generate
|
||||
|
||||
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
|
||||
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
|
||||
ChunkStatus.BIOMES 1 - 10 ms false (no height)
|
||||
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
|
||||
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.FEATURES 7 - 25 ms true
|
||||
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
|
||||
ChunkStatus.LIGHT 20 - 40 ms true
|
||||
ChunkStatus.FULL 30 - 50 ms true
|
||||
ChunkStatus.SPAWN 50 - 80 ms true
|
||||
|
||||
|
||||
At this point I would suggest using FEATURES, as it generates snow and trees
|
||||
(and any other object that is needed to make biomes distinct)
|
||||
|
||||
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
|
||||
*/
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
@@ -12,16 +11,10 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||
* BufferBuilders.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-07-2021
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
/** alpha used when drawing chunks in debug mode */
|
||||
protected int debugAlpha = 255; // 0 - 255
|
||||
protected Color debugBlack = new Color(0, 0, 0, debugAlpha);
|
||||
protected Color debugWhite = new Color(255, 255, 255, debugAlpha);
|
||||
|
||||
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
@@ -35,7 +28,9 @@ public abstract class AbstractLodTemplate
|
||||
buffer.pos(x, y, z).color(red, green, blue, alpha).endVertex();
|
||||
}
|
||||
|
||||
|
||||
/** Returns in bytes how much buffer memory is required
|
||||
* for one LOD object */
|
||||
public abstract int getBufferMemoryForSingleLod(LodDetail detail);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import com.seibel.lod.enums.ColorDirection;
|
||||
import com.seibel.lod.enums.RelativeChunkPos;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
@@ -16,7 +15,7 @@ import net.minecraft.util.math.AxisAlignedBB;
|
||||
* Builds LODs as rectangular prisms.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-31-2021
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@@ -37,140 +36,37 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
|
||||
// Add this LOD to the BufferBuilder
|
||||
// using the quality setting set by the config
|
||||
switch(LodConfig.CLIENT.lodDetail.get())
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
|
||||
int halfWidth = detail.dataPointWidth / 2;
|
||||
|
||||
for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
// add a single LOD object for this chunk
|
||||
case SINGLE:
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
// returns null if the lod is empty at the given location
|
||||
bbox = generateBoundingBox(centerLod.getHeight(), centerLod.getDepth(), LodChunk.WIDTH, xOffset, yOffset, zOffset);
|
||||
bbox = generateBoundingBox(
|
||||
centerLod.getAverageHeightOverArea(startX, startZ, endX, endZ),
|
||||
centerLod.getAverageDepthOverArea(startX, startZ, endX, endZ),
|
||||
detail.dataPointWidth,
|
||||
xOffset - (halfWidth / 2) + detail.startX[i],
|
||||
yOffset,
|
||||
zOffset - (halfWidth / 2) + detail.startZ[i]);
|
||||
|
||||
if (bbox != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, generateLodColors(centerLod, false));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// add 4 LOD objects for this chunk
|
||||
case DOUBLE:
|
||||
/*
|
||||
* This method generates LODs using the LodChunks that
|
||||
* are adjacent to create an average quarter and thus
|
||||
* smooth the transition between chunks.
|
||||
*/
|
||||
|
||||
// get the adjacent LodChunks
|
||||
LodChunk[] lods = new LodChunk[RelativeChunkPos.values().length];
|
||||
for(RelativeChunkPos pos : RelativeChunkPos.values())
|
||||
lods[pos.index] = lodDim.getLodFromCoordinates(centerLod.x + pos.x, centerLod.z + pos.z);
|
||||
|
||||
|
||||
int halfWidth = LodChunk.WIDTH / 2;
|
||||
|
||||
// use the adjacent chunks to generate quarter sections
|
||||
for(EnumSet<RelativeChunkPos> set : RelativeChunkPos.CORNERS)
|
||||
{
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
|
||||
// Weight the center LodChunk by this amount
|
||||
// when taking the average.
|
||||
// this should be between 3 and 6;
|
||||
// if set to 1 (no extra weight)
|
||||
// then the chunks don't appear to be averaged.
|
||||
int centerWeight = 3;
|
||||
|
||||
// how many LodChunks adjacent to the center
|
||||
// are valid?
|
||||
int validPoints = centerWeight;
|
||||
|
||||
int avgHeight = centerLod.getHeight() * centerWeight;
|
||||
int avgDepth = centerLod.getDepth() * centerWeight;
|
||||
|
||||
int[][] colorAverages = new int[ColorDirection.values().length][3];
|
||||
Color[] colorToAdd = generateLodColors(centerLod, debugging);
|
||||
for(int i = 0; i < centerWeight; i++)
|
||||
colorAverages = addColorToColorAverages(colorAverages, colorToAdd);
|
||||
|
||||
for(RelativeChunkPos cornerPos : set)
|
||||
{
|
||||
// set the x and y location based on which
|
||||
// corner we are working on
|
||||
if (RelativeChunkPos.DIAGONAL.contains(cornerPos))
|
||||
{
|
||||
x = Math.min(cornerPos.x, 0) * halfWidth;
|
||||
z = Math.min(cornerPos.z, 0) * halfWidth;
|
||||
}
|
||||
|
||||
LodChunk cornerLod = lods[cornerPos.index];
|
||||
if (cornerLod != null && !cornerLod.isLodEmpty())
|
||||
{
|
||||
validPoints++;
|
||||
|
||||
avgHeight += cornerLod.getHeight();
|
||||
avgDepth += cornerLod.getDepth();
|
||||
|
||||
// only generate average colors if we aren't debugging
|
||||
// (this is to prevent everything from becoming grey)
|
||||
if (!debugging)
|
||||
colorToAdd = generateLodColors(cornerLod, debugging);
|
||||
else
|
||||
colorToAdd = generateLodColors(centerLod, debugging);
|
||||
// add to the running color average
|
||||
colorAverages = addColorToColorAverages(colorAverages, colorToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert the heights into actual averages
|
||||
avgHeight /= validPoints;
|
||||
avgDepth /= validPoints;
|
||||
// calculate the average colors
|
||||
Color[] colors = new Color[ColorDirection.values().length];
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++)
|
||||
colorAverages[dir.value][rgbIndex] /= validPoints;
|
||||
colors[dir.value] = new Color(colorAverages[dir.value][0], colorAverages[dir.value][1], colorAverages[dir.value][2]);
|
||||
}
|
||||
|
||||
|
||||
// returns null if the lod is empty at the given location
|
||||
bbox = generateBoundingBox(avgHeight, avgDepth, halfWidth, xOffset - (halfWidth / 2) + x + 12, yOffset, zOffset - (halfWidth / 2) + z + 12);
|
||||
|
||||
if (bbox != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, colors);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} // case
|
||||
}
|
||||
|
||||
|
||||
private int[][] addColorToColorAverages(int[][] colorAverages, Color[] colorToAdd)
|
||||
{
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
{
|
||||
// convert the colorToAdd to an int array
|
||||
float[] colorCompoments = new float[4];
|
||||
colorCompoments = colorToAdd[dir.value].getColorComponents(colorCompoments);
|
||||
|
||||
// add each color component to the array
|
||||
for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++)
|
||||
{
|
||||
// * 255 + 0.5 taken from the Color java class
|
||||
colorAverages[dir.value][rgbIndex] += (int) (colorCompoments[rgbIndex] * 255 + 0.5);
|
||||
addBoundingBoxToBuffer(buffer, bbox, centerLod.getAverageColorOverArea(startX, startZ, endX, endZ, debugging));
|
||||
}
|
||||
}
|
||||
|
||||
return colorAverages;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
@@ -189,6 +85,45 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
|
||||
|
||||
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color c)
|
||||
{
|
||||
// top (facing up)
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// bottom (facing down)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
|
||||
// south (facing -Z)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// north (facing +Z)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
|
||||
// west (facing -X)
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
// east (facing +X)
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, Color[] c)
|
||||
{
|
||||
// top (facing up)
|
||||
@@ -224,54 +159,16 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, c[ColorDirection.EAST.value].getRed(), c[ColorDirection.EAST.value].getGreen(), c[ColorDirection.EAST.value].getBlue(), c[ColorDirection.EAST.value].getAlpha());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determine the color for each side of this LOD.
|
||||
*/
|
||||
private Color[] generateLodColors(LodChunk lod, boolean debugging)
|
||||
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail)
|
||||
{
|
||||
Color[] colors = new Color[ColorDirection.values().length];
|
||||
|
||||
if (!debugging)
|
||||
{
|
||||
// if NOT debugging, look to the config to determine
|
||||
// how this LOD should be colored
|
||||
switch (LodConfig.CLIENT.lodColorStyle.get())
|
||||
{
|
||||
case TOP:
|
||||
// only add the top's color to the array
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
colors[dir.value] = lod.getColor(ColorDirection.TOP);
|
||||
break;
|
||||
|
||||
case INDIVIDUAL_SIDES:
|
||||
// add each direction's color to the array
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
colors[dir.value] = lod.getColor(dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if debugging draw the squares as a black and white checker board
|
||||
if ((lod.x + lod.z) % 2 == 0)
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
// have each direction be the same
|
||||
// color if debugging
|
||||
colors[dir.value] = debugWhite;
|
||||
else
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
colors[dir.value] = debugBlack;
|
||||
}
|
||||
|
||||
return colors;
|
||||
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints))) * howManyPointsPerLodChunk
|
||||
return (6 * 4 * (3 + 4)) * detail.dataPointLengthCount * detail.dataPointLengthCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
@@ -12,7 +13,7 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||
* is at a significantly different height.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-07-2021
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@@ -24,4 +25,10 @@ public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
* TODO TriangularLodTemplate
|
||||
* TODO #21 TriangularLodTemplate
|
||||
* Builds each LOD chunk as a singular rectangular prism.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-07-2021
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@@ -22,4 +23,10 @@ public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
package com.seibel.lod.builders.worldGeneration;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.builders.LodBuilderConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.WeightedList.Entry;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.palette.UpgradeData;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
import net.minecraft.world.chunk.ChunkPrimer;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.ChunkGenerator;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider;
|
||||
import net.minecraft.world.gen.feature.BlockClusterFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.FeatureSpread;
|
||||
import net.minecraft.world.gen.feature.FeatureSpreadConfig;
|
||||
import net.minecraft.world.gen.feature.IceAndSnowFeature;
|
||||
import net.minecraft.world.gen.feature.NoFeatureConfig;
|
||||
import net.minecraft.world.gen.placement.ConfiguredPlacement;
|
||||
import net.minecraft.world.gen.placement.DecoratedPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.IPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.NoiseDependant;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraft.world.server.ServerWorldLightManager;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodChunkGenWorker implements IWorker
|
||||
{
|
||||
public static final ExecutorService genThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
|
||||
|
||||
public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld,
|
||||
BiomeContainer newBiomeContainer)
|
||||
{
|
||||
if (newServerWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ServerWorld");
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newLodRenderer,
|
||||
newLodBuilder, newLodBufferBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (!threadStarted)
|
||||
{
|
||||
// make sure we don't generate this chunk again
|
||||
thread.lodDim.addLod(new LodChunk(thread.pos));
|
||||
|
||||
thread.lodBufferBuilder.numberOfChunksWaitingToGenerate--;
|
||||
|
||||
if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
|
||||
{
|
||||
// if we are using SERVER generation that has to be done
|
||||
// synchronously to prevent crashing and harmful
|
||||
// interactions with the normal world generator
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
threadStarted = true;
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return !threadStarted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class LodChunkGenThread implements Runnable
|
||||
{
|
||||
public final ServerWorld serverWorld;
|
||||
public final LodDimension lodDim;
|
||||
public final LodBuilder lodBuilder;
|
||||
public final LodRenderer lodRenderer;
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
|
||||
{
|
||||
// long startTime = System.currentTimeMillis();
|
||||
|
||||
switch(LodConfig.CLIENT.distanceGenerationMode.get())
|
||||
{
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
generateUsingBiomesOnly();
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
generateUsingSurface();
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
generateUsingFeatures();
|
||||
break;
|
||||
case SERVER:
|
||||
// very slow
|
||||
generateWithServer();
|
||||
break;
|
||||
}
|
||||
|
||||
lodRenderer.regenerateLODsNextFrame();
|
||||
|
||||
|
||||
// if (lodDim.getLodFromCoordinates(pos.x, pos.z) != null)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
|
||||
}// run
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 2-5 ms
|
||||
*/
|
||||
private void generateUsingBiomesOnly()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
|
||||
ChunkGenerator chunkGen = serverWorld.getWorld().getChunkProvider().getChunkGenerator();
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
|
||||
|
||||
// generate fake height data for this LOD
|
||||
int seaLevel = serverWorld.getSeaLevel();
|
||||
|
||||
boolean simulateHeight = LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
boolean inTheEnd = false;
|
||||
|
||||
// add fake heightmap data so our LODs aren't at height 0
|
||||
Heightmap heightmap = new Heightmap(chunk, LodChunk.DEFAULT_HEIGHTMAP);
|
||||
for(int x = 0; x < LodChunk.WIDTH && !inTheEnd; x++)
|
||||
{
|
||||
for(int z = 0; z < LodChunk.WIDTH && !inTheEnd; z++)
|
||||
{
|
||||
if (simulateHeight)
|
||||
{
|
||||
// TODO use the biomes around each block to smooth out the transition
|
||||
|
||||
// these heights are of course aren't super accurate,
|
||||
// they are just to simulate height data where there isn't any
|
||||
switch(chunk.getBiomes().getNoiseBiome(x, seaLevel, z).getCategory())
|
||||
{
|
||||
case NETHER:
|
||||
heightmap.set(x, z, serverWorld.getHeight() / 2);
|
||||
break;
|
||||
|
||||
case EXTREME_HILLS:
|
||||
heightmap.set(x, z, seaLevel + 30);
|
||||
break;
|
||||
case MESA:
|
||||
heightmap.set(x, z, seaLevel + 20);
|
||||
break;
|
||||
case JUNGLE:
|
||||
heightmap.set(x, z, seaLevel + 20);
|
||||
break;
|
||||
case BEACH:
|
||||
heightmap.set(x, z, seaLevel + 5);
|
||||
break;
|
||||
case NONE:
|
||||
heightmap.set(x, z, 0);
|
||||
break;
|
||||
|
||||
case OCEAN:
|
||||
case RIVER:
|
||||
heightmap.set(x, z, seaLevel);
|
||||
break;
|
||||
|
||||
case THEEND:
|
||||
inTheEnd = true;
|
||||
break;
|
||||
|
||||
// DESERT
|
||||
// FOREST
|
||||
// ICY
|
||||
// MUSHROOM
|
||||
// SAVANNA
|
||||
// SWAMP
|
||||
// TAIGA
|
||||
// PLAINS
|
||||
default:
|
||||
heightmap.set(x, z, seaLevel + 10);
|
||||
break;
|
||||
}// heightmap switch
|
||||
}
|
||||
else
|
||||
{
|
||||
// we aren't simulating height
|
||||
// always use sea level
|
||||
heightmap.set(x, z, seaLevel);
|
||||
}
|
||||
}// z
|
||||
}// x
|
||||
|
||||
chunk.setHeightmap(LodChunk.DEFAULT_HEIGHTMAP, heightmap.getDataArray());
|
||||
|
||||
|
||||
LodChunk lod;
|
||||
if (!inTheEnd)
|
||||
{
|
||||
lod = lodBuilder.generateLodFromChunk(chunk, new LodBuilderConfig(true, true, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are in the end, don't generate any chunks.
|
||||
// Since we don't know where the islands are, everything
|
||||
// generates the same and it looks really bad.
|
||||
lod = new LodChunk(chunk.getPos().x, chunk.getPos().z);
|
||||
}
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 10 - 20 ms
|
||||
*/
|
||||
private void generateUsingSurface()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(chunk);
|
||||
|
||||
ChunkGenerator chunkGen = serverWorld.getWorld().getChunkProvider().getChunkGenerator();
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
ChunkStatus.NOISE.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
ChunkStatus.SURFACE.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
|
||||
// this feature has proved to be thread safe
|
||||
// so we will add it
|
||||
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.field_236558_a_);
|
||||
snowFeature.generate(lodServerWorld, chunkGen, serverWorld.rand, chunk.getPos().asBlockPos(), null);
|
||||
|
||||
|
||||
LodChunk lod = lodBuilder.generateLodFromChunk(chunk, new LodBuilderConfig(false, true, true));
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 15 - 20 ms
|
||||
*
|
||||
* Causes concurrentModification Exceptions,
|
||||
* which could cause instability or world generation bugs
|
||||
*/
|
||||
private void generateUsingFeatures()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(chunk);
|
||||
|
||||
ChunkGenerator chunkGen = serverWorld.getWorld().getChunkProvider().getChunkGenerator();
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
ChunkStatus.NOISE.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
ChunkStatus.SURFACE.doGenerationWork(serverWorld, chunkGen, serverWorld.getStructureTemplateManager(), (ServerWorldLightManager) serverWorld.getLightManager(), null, chunkList);
|
||||
|
||||
|
||||
// get all the biomes in the chunk
|
||||
HashSet<Biome> biomes = new HashSet<>();
|
||||
for (int x = 0; x < LodChunk.WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodChunk.WIDTH; z++)
|
||||
{
|
||||
// should probably use the heightmap here instead of seaLevel,
|
||||
// but this seems to get the job done well enough
|
||||
biomes.add(chunk.getBiomes().getNoiseBiome(x, serverWorld.getSeaLevel(), z));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate all the features related to this chunk.
|
||||
// this may or may not be thread safe
|
||||
for (Biome biome : biomes)
|
||||
{
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.biomeGenerationSettings.getFeatures();
|
||||
|
||||
for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
|
||||
{
|
||||
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
|
||||
{
|
||||
ConfiguredFeature<?, ?> configuredfeature = featureSupplier.get();
|
||||
|
||||
/*
|
||||
// clone any items that aren't thread safe to prevent
|
||||
// them from causing issues
|
||||
if(configuredfeature.config.getClass() == BlockClusterFeatureConfig.class)
|
||||
{
|
||||
config = cloneBlockClusterFeatureConfig((BlockClusterFeatureConfig) configuredfeature.config);
|
||||
}
|
||||
else if (configuredfeature.config.getClass() == DecoratedFeatureConfig.class)
|
||||
{
|
||||
config = cloneDecoratedFeatureConfig((DecoratedFeatureConfig) configuredfeature.config);
|
||||
}
|
||||
*/
|
||||
|
||||
try
|
||||
{
|
||||
configuredfeature.generate(lodServerWorld, chunkGen, serverWorld.rand, chunk.getPos().asBlockPos());
|
||||
}
|
||||
catch(ConcurrentModificationException e)
|
||||
{
|
||||
// This will happen. I'm not sure what to do about it
|
||||
// except pray that it doesn't effect the normal world generation
|
||||
// in any harmful way
|
||||
|
||||
// I tried cloning the config for each feature, but that
|
||||
// path was blocked since I can't clone lambda methods.
|
||||
// I tried using a deep cloning library and discovered
|
||||
// the problem there.
|
||||
// ( https://github.com/kostaskougios/cloning )
|
||||
}
|
||||
catch(UnsupportedOperationException e)
|
||||
{
|
||||
// This will happen when the LodServerWorld
|
||||
// isn't able to return something that a feature
|
||||
// generator needs
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// I'm not sure what happened, print to the log
|
||||
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
e.printStackTrace();
|
||||
System.out.println();
|
||||
//ClientProxy.LOGGER.error("error class: \"" + configuredfeature.config.getClass() + "\"");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate a Lod like normal
|
||||
LodChunk lod = lodBuilder.generateLodFromChunk(chunk);
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* on pre generated chunks 0 - 1 ms
|
||||
* on un generated chunks 0 - 50 ms
|
||||
* with the median seeming to hover around 15 - 30 ms
|
||||
* and outliers in the 100 - 200 ms range
|
||||
*
|
||||
* Note this should not be multithreaded and does cause server/simulation lag
|
||||
* (Higher lag for generating than loading)
|
||||
*/
|
||||
private void generateWithServer()
|
||||
{
|
||||
lodBuilder.generateLodChunkAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// Unused methods //
|
||||
//================//
|
||||
|
||||
// Sadly I wasn't able to get these to work,
|
||||
// they are here for documentation purposes
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
|
||||
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
|
||||
{
|
||||
IPlacementConfig placementConfig = null;
|
||||
|
||||
Class oldConfigClass = config.decorator.func_242877_b().getClass();
|
||||
|
||||
if (oldConfigClass == FeatureSpreadConfig.class)
|
||||
{
|
||||
FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.func_242877_b();
|
||||
FeatureSpread oldSpread = oldPlacementConfig.func_242799_a();
|
||||
|
||||
placementConfig = new FeatureSpreadConfig(oldSpread);
|
||||
}
|
||||
else if(oldConfigClass == DecoratedPlacementConfig.class)
|
||||
{
|
||||
DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.func_242877_b();
|
||||
placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.getInner(), oldPlacementConfig.getOuter());
|
||||
}
|
||||
else if(oldConfigClass == NoiseDependant.class)
|
||||
{
|
||||
NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.func_242877_b();
|
||||
placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.func_242877_b().getClass() + "\"");
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
ConfiguredPlacement<?> newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig);
|
||||
return new DecoratedFeatureConfig(config.feature, newPlacement);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
|
||||
{
|
||||
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
|
||||
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedStates.field_220658_a)
|
||||
provider.weightedStates.field_220658_a.add(state);
|
||||
|
||||
HashSet<Block> whitelist = new HashSet<>();
|
||||
for(Block block : config.whitelist)
|
||||
whitelist.add(block);
|
||||
|
||||
HashSet<BlockState> blacklist = new HashSet<>();
|
||||
for(BlockState state : config.blacklist)
|
||||
blacklist.add(state);
|
||||
|
||||
|
||||
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
|
||||
builder.whitelist(whitelist);
|
||||
builder.blacklist(blacklist);
|
||||
builder.xSpread(config.xSpread);
|
||||
builder.ySpread(config.ySpread);
|
||||
builder.zSpread(config.zSpread);
|
||||
if(config.isReplaceable) { builder.replaceable(); }
|
||||
if(config.requiresWater) { builder.requiresWater(); }
|
||||
if(config.field_227298_k_) { builder.func_227317_b_(); }
|
||||
builder.tries(config.tryCount);
|
||||
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* performance/generation tests related to
|
||||
* serverWorld.getChunk(x, z, ChunkStatus. *** )
|
||||
|
||||
true/false is whether they generated blocks or not
|
||||
the time is how long it took to generate
|
||||
|
||||
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
|
||||
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
|
||||
ChunkStatus.BIOMES 1 - 10 ms false (no height)
|
||||
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
|
||||
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.FEATURES 7 - 25 ms true
|
||||
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
|
||||
ChunkStatus.LIGHT 20 - 40 ms true
|
||||
ChunkStatus.FULL 30 - 50 ms true
|
||||
ChunkStatus.SPAWN 50 - 80 ms true
|
||||
|
||||
|
||||
At this point I would suggest using FEATURES, as it generates snow and trees
|
||||
(and any other object that is needed to make biomes distinct)
|
||||
|
||||
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package com.seibel.lod.builders.worldGeneration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.particles.IParticleData;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.util.registry.DynamicRegistries;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.EmptyTickList;
|
||||
import net.minecraft.world.ISeedReader;
|
||||
import net.minecraft.world.ITickList;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeManager;
|
||||
import net.minecraft.world.border.WorldBorder;
|
||||
import net.minecraft.world.chunk.AbstractChunkProvider;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
import net.minecraft.world.gen.Heightmap.Type;
|
||||
import net.minecraft.world.gen.feature.structure.Structure;
|
||||
import net.minecraft.world.gen.feature.structure.StructureStart;
|
||||
import net.minecraft.world.lighting.WorldLightManager;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraft.world.storage.IWorldInfo;
|
||||
|
||||
|
||||
/**
|
||||
* This is a fake ServerWorld used when generating features.
|
||||
* This allows us to keep each LodChunk generation independent
|
||||
* of the actual ServerWorld, allowing us
|
||||
* to multithread generation.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodServerWorld implements ISeedReader {
|
||||
|
||||
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
|
||||
|
||||
public IChunk chunk;
|
||||
|
||||
public LodServerWorld(IChunk newChunk)
|
||||
{
|
||||
chunk = newChunk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getHeight(Type heightmapType, int x, int z)
|
||||
{
|
||||
// make sure the block position is set relative to the chunk
|
||||
x = x % LodChunk.WIDTH;
|
||||
x = (x < 0) ? x + 16 : x;
|
||||
|
||||
z = z % LodChunk.WIDTH;
|
||||
z = (z < 0) ? z + 16 : z;
|
||||
|
||||
return chunk.getHeightmap(LodChunk.DEFAULT_HEIGHTMAP).getHeight(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getBiome(BlockPos pos)
|
||||
{
|
||||
return chunk.getBiomes().getNoiseBiome(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlockState(BlockPos pos, BlockState state, int flags, int recursionLeft)
|
||||
{
|
||||
return chunk.setBlockState(pos, state, false) == state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos)
|
||||
{
|
||||
return chunk.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos)
|
||||
{
|
||||
return chunk.getFluidState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightFor(LightType type, BlockPos pos)
|
||||
{
|
||||
// this needs to be low for snow generation to work
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBlockState(BlockPos pos, Predicate<BlockState> state)
|
||||
{
|
||||
return state.test(chunk.getBlockState(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITickList<Block> getPendingBlockTicks()
|
||||
{
|
||||
return EmptyTickList.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* All methods below shouldn't be needed
|
||||
* and thus have been left unimplemented.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ServerWorld getWorld() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITickList<Fluid> getPendingFluidTicks() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorldInfo getWorldInfo() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DifficultyInstance getDifficultyForLocation(BlockPos pos) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractChunkProvider getChunkProvider() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Random getRandom() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume,
|
||||
float pitch) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed,
|
||||
double zSpeed) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playEvent(PlayerEntity player, int type, BlockPos pos, int data) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicRegistries func_241828_r() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> getEntitiesInAABBexcluding(Entity entityIn, AxisAlignedBB boundingBox,
|
||||
Predicate<? super Entity> predicate) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> List<T> getEntitiesWithinAABB(Class<? extends T> clazz, AxisAlignedBB aabb,
|
||||
Predicate<? super T> filter) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends PlayerEntity> getPlayers() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkylightSubtracted() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeManager getBiomeManager() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getNoiseBiomeRaw(int x, int y, int z) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemote() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DimensionType getDimensionType() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public float func_230487_a_(Direction p_230487_1_, boolean p_230487_2_) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldLightManager getLightManager() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity getTileEntity(BlockPos pos) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldBorder getWorldBorder() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeBlock(BlockPos pos, boolean isMoving) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends StructureStart<?>> func_241827_a(SectionPos p_241827_1_, Structure<?> p_241827_2_) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
* BIOME_ONLY <br>
|
||||
* BIOME_ONLY_SIMULATE_HEIGHT <br>
|
||||
* SURFACE <br>
|
||||
* FEATURES <br>
|
||||
* SERVER <br><br>
|
||||
*
|
||||
* In order of fastest to slowest.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public enum DistanceGenerationMode
|
||||
{
|
||||
/** Only generate the biomes and use biome
|
||||
* grass/foliage color, water color, or ice color
|
||||
* to generate the color.
|
||||
* Doesn't generate height, everything is shown at sea level.
|
||||
* Multithreaded - Fastest (2-5 ms) */
|
||||
BIOME_ONLY,
|
||||
|
||||
/**
|
||||
* Same as BIOME_ONLY, except instead
|
||||
* of always using sea level as the LOD height
|
||||
* different biome types (mountain, ocean, forest, etc.)
|
||||
* use predetermined heights to simulate having height data.
|
||||
*/
|
||||
BIOME_ONLY_SIMULATE_HEIGHT,
|
||||
|
||||
/** Generate the world surface,
|
||||
* this does NOT include caves, trees,
|
||||
* or structures.
|
||||
* Multithreaded - Faster (10-20 ms) */
|
||||
SURFACE,
|
||||
|
||||
/** Generate everything except structures.
|
||||
* NOTE: This may cause world generation bugs or instability,
|
||||
* since some features cause concurrentModification exceptions.
|
||||
* Multithreaded - Fast (15-20 ms) */
|
||||
FEATURES,
|
||||
|
||||
/** Ask the server to generate/load each chunk.
|
||||
* This is the most compatible, but causes server/simulation lag.
|
||||
* This will also show player made structures if you
|
||||
* are adding the mod to a pre-existing world.
|
||||
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) */
|
||||
SERVER;
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
* top, individual_sides
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-08-2021
|
||||
*/
|
||||
public enum LodColorStyle
|
||||
{
|
||||
/** Use the color from the top of the LOD chunk
|
||||
* for all sides */
|
||||
TOP,
|
||||
|
||||
/** For each side of the LOD use the color corresponding
|
||||
* to that side */
|
||||
INDIVIDUAL_SIDES;
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
|
||||
/**
|
||||
* single, quad
|
||||
* single, double, quad, half, full
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-29-2021
|
||||
* @version 06-13-2021
|
||||
*/
|
||||
public enum LodDetail
|
||||
{
|
||||
@@ -12,14 +15,83 @@ public enum LodDetail
|
||||
SINGLE(1),
|
||||
|
||||
/** render 4 LODs for each chunk */
|
||||
DOUBLE(2);
|
||||
DOUBLE(2),
|
||||
|
||||
/** How many data points wide the related
|
||||
* LodChunk object should contain */
|
||||
public final int value;
|
||||
/** render 16 LODs for each chunk */
|
||||
QUAD(4),
|
||||
|
||||
private LodDetail(int newValue)
|
||||
/** render 64 LODs for each chunk */
|
||||
HALF(8),
|
||||
|
||||
/** render 256 LODs for each chunk */
|
||||
FULL(16);
|
||||
|
||||
|
||||
/** How many DataPoints should
|
||||
* be drawn per side per LodChunk */
|
||||
public final int dataPointLengthCount;
|
||||
/** How wide each LOD DataPoint is */
|
||||
public final int dataPointWidth;
|
||||
|
||||
/* Start/End X/Z give the block positions
|
||||
* for each individual dataPoint in a LodChunk */
|
||||
public final int[] startX;
|
||||
public final int[] startZ;
|
||||
|
||||
public final int[] endX;
|
||||
public final int[] endZ;
|
||||
|
||||
/** This is how many pieces of data should be expected
|
||||
* when creating a LodChunk with this detail level */
|
||||
public final int lodChunkStringDelimiterCount;
|
||||
|
||||
|
||||
|
||||
|
||||
private LodDetail(int newLengthCount)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
dataPointLengthCount = newLengthCount;
|
||||
dataPointWidth = 16 / dataPointLengthCount;
|
||||
|
||||
if(newLengthCount == LodChunk.WIDTH)
|
||||
{
|
||||
// this is to prevent overflow
|
||||
newLengthCount = LodChunk.WIDTH - 1;
|
||||
}
|
||||
|
||||
startX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
startZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
|
||||
int index = 0;
|
||||
for(int x = 0; x < newLengthCount; x++)
|
||||
{
|
||||
for(int z = 0; z < newLengthCount; z++)
|
||||
{
|
||||
startX[index] = x * dataPointWidth;
|
||||
startZ[index] = z * dataPointWidth;
|
||||
|
||||
// special case for FULL
|
||||
if(dataPointWidth != 1)
|
||||
{
|
||||
endX[index] = (x*dataPointWidth) + dataPointWidth - 1;
|
||||
endZ[index] = (z*dataPointWidth) + dataPointWidth - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
endX[index] = (x*dataPointWidth) + dataPointWidth;
|
||||
endZ[index] = (z*dataPointWidth) + dataPointWidth;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lodChunkStringDelimiterCount = 2 + (dataPointLengthCount * dataPointLengthCount * LodDataPoint.NUMBER_OF_DELIMITERS);
|
||||
|
||||
}// constructor
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.seibel.lod.builders.lodTemplates.TriangularLodTemplate;
|
||||
* Cubic, Triangular, Dynamic
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-07-2021
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public enum LodTemplate
|
||||
{
|
||||
@@ -35,4 +35,10 @@ public enum LodTemplate
|
||||
{
|
||||
template = newTemplate;
|
||||
}
|
||||
|
||||
|
||||
public int getBufferMemoryForSingleLod(LodDetail detail)
|
||||
{
|
||||
return template.getBufferMemoryForSingleLod(detail);
|
||||
}
|
||||
}
|
||||
|
||||
+59
-21
@@ -1,4 +1,4 @@
|
||||
package com.seibel.lod.util;
|
||||
package com.seibel.lod.handlers;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -9,8 +9,8 @@ import org.apache.logging.log4j.LogManager;
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.electronwill.nightconfig.core.io.WritingMode;
|
||||
import com.seibel.lod.ModInfo;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.LodColorStyle;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
|
||||
@@ -22,7 +22,7 @@ import net.minecraftforge.fml.config.ModConfig;
|
||||
/**
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-31-2021
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class LodConfig
|
||||
@@ -39,7 +39,7 @@ public class LodConfig
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> lodDetail;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodColorStyle> lodColorStyle;
|
||||
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
|
||||
/** this is multiplied by the default view distance
|
||||
* to determine how far out to generate/render LODs */
|
||||
@@ -50,21 +50,21 @@ public class LodConfig
|
||||
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
|
||||
|
||||
drawLODs = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " If false LODs will not be drawn, \n"
|
||||
+ " however they will still be generated \n"
|
||||
+ " and saved to file for later use.")
|
||||
.define("drawLODs", true);
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " At what distance should Fog be drawn on the LODs? \n"
|
||||
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
|
||||
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ".")
|
||||
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " If false the LODs will draw with their normal world colors. \n"
|
||||
+ " If true they will draw as a black and white checkerboard. \n"
|
||||
+ " This can be used for debugging or imagining you are playing a \n"
|
||||
@@ -72,35 +72,73 @@ public class LodConfig
|
||||
.define("drawCheckerBoard", false);
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, "
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. ")
|
||||
.defineEnum("lodTemplate", LodTemplate.CUBIC);
|
||||
|
||||
lodDetail = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " How detailed should the LODs be? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk.")
|
||||
.defineEnum("lodGeometryQuality", LodDetail.SINGLE);
|
||||
|
||||
lodColorStyle = builder
|
||||
.comment("\n"
|
||||
+ " How should the LODs be colored? \n"
|
||||
+ " " + LodColorStyle.TOP.toString() + ": Use the color from the top of the LOD chunk for all sides. \n"
|
||||
+ " " + LodColorStyle.INDIVIDUAL_SIDES.toString() + ": For each side of the LOD use the color corresponding to that side. ")
|
||||
.defineEnum("lodColorStyle", LodColorStyle.TOP);
|
||||
.defineEnum("lodGeometryQuality", LodDetail.QUAD);
|
||||
|
||||
lodChunkRadiusMultiplier = builder
|
||||
.comment("\n"
|
||||
.comment("\n\n"
|
||||
+ " This is multiplied by the default view distance \n"
|
||||
+ " to determine how far out to generate/render LODs. \n"
|
||||
+ " A value of 2 means that there is 1 render distance worth \n"
|
||||
+ " of LODs in each cardinal direction.")
|
||||
.defineInRange("lodChunkRadiusMultiplier", 6, 2, 1023);
|
||||
+ " of LODs in each cardinal direction. ")
|
||||
.defineInRange("lodChunkRadiusMultiplier", 6, 2, 32);
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Note: The times listed here are based on the developer's \n"
|
||||
+ " PC, and are included to show the speed difference \n"
|
||||
+ " between options. Your mileage may vary. \n"
|
||||
+ "\n"
|
||||
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
|
||||
+ " Only generate the biomes and use biome \n"
|
||||
+ " grass/foliage color, water color, or ice color \n"
|
||||
+ " to generate the color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
|
||||
+ " Same as BIOME_ONLY, except instead \n"
|
||||
+ " of always using sea level as the LOD height \n"
|
||||
+ " different biome types (mountain, ocean, forest, etc.) \n"
|
||||
+ " use predetermined heights to simulate having height data. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include caves, trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability, \n"
|
||||
+ " since some features cause concurrentModification exceptions. \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
|
||||
+ " Ask the server to generate/load each chunk. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " This will also show player made structures if you \n"
|
||||
+ " are adding the mod to a pre-existing world. \n"
|
||||
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
|
||||
.defineEnum("distanceBiomeOnlyGeneration", DistanceGenerationMode.FEATURES);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
@@ -19,22 +20,29 @@ import com.seibel.lod.proxy.ClientProxy;
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-31-2021
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/** This is what separates each piece of data */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
private File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
private final String FILE_EXTENSION = ".txt";
|
||||
|
||||
/** This is the file version currently accepted by this
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read. */
|
||||
public static final int LOD_SAVE_FILE_VERSION = 1;
|
||||
public static final int LOD_SAVE_FILE_VERSION = 2;
|
||||
|
||||
/** This is the string written before the file version */
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
|
||||
@@ -74,7 +82,8 @@ public class LodDimensionFileHandler
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
|
||||
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
File f = new File(fileName);
|
||||
|
||||
@@ -149,7 +158,7 @@ public class LodDimensionFileHandler
|
||||
try
|
||||
{
|
||||
// convert each line into an LOD object and add it to the region
|
||||
LodChunk lod = new LodChunk(s);
|
||||
LodChunk lod = new LodChunk(s, LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
region.addLod(lod);
|
||||
}
|
||||
@@ -225,7 +234,7 @@ public class LodDimensionFileHandler
|
||||
int x = region.x;
|
||||
int z = region.z;
|
||||
|
||||
File f = new File(getFileNameAndPathForRegion(x, z));
|
||||
File f = new File(getFileNameAndPathForRegion(x, z, LodConfig.CLIENT.lodDetail.get()));
|
||||
|
||||
try
|
||||
{
|
||||
@@ -311,9 +320,11 @@ public class LodDimensionFileHandler
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write.
|
||||
* Returns null if this object isn't ready to read and write. <br><br>
|
||||
*
|
||||
* example: "lod.FULL.0.0.txt"
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ)
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, LodDetail detail)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -321,8 +332,8 @@ public class LodDimensionFileHandler
|
||||
// ".\Super Flat\DIM-1\data"
|
||||
// or
|
||||
// ".\Super Flat\data"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + "\\" +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + detail.toString() + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.seibel.lod.LodMain;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
|
||||
@@ -2,30 +2,44 @@ package com.seibel.lod.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.enums.ColorDirection;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
|
||||
/**
|
||||
* This object contains position
|
||||
* and color data for an LOD object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-29-2021
|
||||
* @version 6-27-2021
|
||||
*/
|
||||
public class LodChunk
|
||||
{
|
||||
/** how many different pieces of data are in one line */
|
||||
private static final int DATA_DELIMITER_COUNT = 22;
|
||||
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
/** Width of a Minecraft Chunk */
|
||||
public static final int WIDTH = 16;
|
||||
|
||||
/** alpha used when drawing chunks in debug mode */
|
||||
private static final int DEBUG_ALPHA = 255; // 0 - 255
|
||||
private static final Color DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA);
|
||||
private static final Color DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA);
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
/** If we ever have to use a heightmap for any reason, use this one. */
|
||||
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
|
||||
|
||||
|
||||
public LodDetail detail = LodDetail.SINGLE;
|
||||
|
||||
/** If this is set to true then toData will return
|
||||
* the empty string */
|
||||
public boolean dontSave = false;
|
||||
|
||||
// TODO store the DistanceGenerationMethod used for this chunk (so we can upgrade old chunks if we want to)
|
||||
|
||||
|
||||
/** The x coordinate of the chunk. */
|
||||
@@ -33,47 +47,50 @@ public class LodChunk
|
||||
/** The z coordinate of the chunk. */
|
||||
public int z = 0;
|
||||
|
||||
/*
|
||||
* The reason we are only using 1 height and depth point
|
||||
* instead of multiple is because:
|
||||
* 1. more points would drastically increase the amount of
|
||||
* memory and disk space needed
|
||||
* 2. more height/depth points require more color points,
|
||||
* which can get out of hand quickly
|
||||
* 3. with the increased disk space reading/writing would
|
||||
* take far too long
|
||||
* 4. the increased resolution is generally not worth it,
|
||||
* 4 LODs per chunk is the limit of diminishing returns.
|
||||
* And some of that could potentially be faked through
|
||||
* smart LodTemplates
|
||||
*/
|
||||
private short height;
|
||||
private short depth;
|
||||
|
||||
/** The average color for the 6 cardinal directions */
|
||||
public Color colors[];
|
||||
/** This stores the height and color for each data point in the LodChunk */
|
||||
public LodDataPoint dataPoints[][];
|
||||
|
||||
/** If true then this LodChunk contains no data */
|
||||
private boolean empty = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create an empty invisible LodChunk at (0,0)
|
||||
* Create an empty, invisible, non-saving LodChunk at (0,0)
|
||||
*/
|
||||
public LodChunk()
|
||||
{
|
||||
dontSave = true;
|
||||
empty = true;
|
||||
|
||||
x = 0;
|
||||
z = 0;
|
||||
|
||||
height = 0;
|
||||
depth = 0;
|
||||
colors = new Color[ColorDirection.values().length];
|
||||
detail = LodDetail.SINGLE;
|
||||
|
||||
// by default have the colors invisible
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
colors[dir.value] = INVISIBLE;
|
||||
dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty, invisible, non-saving LodChunk at the given ChunkPos
|
||||
*/
|
||||
public LodChunk(ChunkPos pos)
|
||||
{
|
||||
this();
|
||||
|
||||
x = pos.x;
|
||||
z = pos.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty, invisible, non-saving LodChunk at the given ChunkPos
|
||||
*/
|
||||
public LodChunk(int newX, int newZ)
|
||||
{
|
||||
this();
|
||||
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,28 +101,33 @@ public class LodChunk
|
||||
* @throws IllegalArgumentException if the data isn't valid to create a LodChunk
|
||||
* @throws NumberFormatException if any piece of data can't be converted at any point
|
||||
*/
|
||||
public LodChunk(String data) throws IllegalArgumentException, NumberFormatException
|
||||
public LodChunk(String data, LodDetail newDetail) throws IllegalArgumentException, NumberFormatException
|
||||
{
|
||||
/*
|
||||
* data format:
|
||||
* x, z, height, depth, rgb color data
|
||||
* data format:
|
||||
* x, z, dataPoint[0][0], dataPoint[0][1], ...
|
||||
* x, z, height, depth, rgb color data, height, depth, rgb....
|
||||
*
|
||||
* example:
|
||||
* 5,8, 4, 0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||
* 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ...
|
||||
*/
|
||||
|
||||
dontSave = false;
|
||||
|
||||
// make sure there are the correct number of entries
|
||||
// in the data string (28)
|
||||
// in the data string
|
||||
int count = 0;
|
||||
detail = newDetail;
|
||||
|
||||
for(int i = 0; i < data.length(); i++)
|
||||
if(data.charAt(i) == DATA_DELIMITER)
|
||||
count++;
|
||||
|
||||
if(count != DATA_DELIMITER_COUNT)
|
||||
throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + ".");
|
||||
if(count != detail.lodChunkStringDelimiterCount)
|
||||
throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + detail.lodChunkStringDelimiterCount + ".");
|
||||
|
||||
|
||||
dataPoints = new LodDataPoint[detail.dataPointLengthCount][detail.dataPointLengthCount];
|
||||
|
||||
// index we will use when going through the String
|
||||
int index = 0;
|
||||
@@ -115,76 +137,78 @@ public class LodChunk
|
||||
|
||||
// x and z position
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
x = Integer.parseInt(data.substring(0,index));
|
||||
this.x = Integer.parseInt(data.substring(0,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
z = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
// height
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
height = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
|
||||
// depth
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
depth = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
this.z = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
|
||||
|
||||
// color
|
||||
colors = new Color[6];
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
// LodDataPoints
|
||||
for(int blockX = 0; blockX < detail.dataPointLengthCount; blockX++)
|
||||
{
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
// get r,g,b
|
||||
for(int i = 0; i < 3; i++)
|
||||
for(int blockZ = 0; blockZ < detail.dataPointLengthCount; blockZ++)
|
||||
{
|
||||
// height
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int height = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
|
||||
String raw = "";
|
||||
switch(i)
|
||||
// depth
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int depth = Short.parseShort(data.substring(lastIndex+1,index));
|
||||
|
||||
|
||||
// color
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
// get r,g,b
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
case 0:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
red = Short.parseShort(raw);
|
||||
break;
|
||||
case 1:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
green = Short.parseShort(raw);
|
||||
break;
|
||||
case 2:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
blue = Short.parseShort(raw);
|
||||
break;
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
|
||||
String raw = "";
|
||||
switch(i)
|
||||
{
|
||||
case 0:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
red = Short.parseShort(raw);
|
||||
break;
|
||||
case 1:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
green = Short.parseShort(raw);
|
||||
break;
|
||||
case 2:
|
||||
raw = data.substring(lastIndex+1,index);
|
||||
blue = Short.parseShort(raw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dataPoints[blockX][blockZ] = new LodDataPoint((short)height, (short)depth, new Color(red, green, blue));
|
||||
}
|
||||
|
||||
colors[dir.value] = new Color(red, green, blue);
|
||||
}
|
||||
|
||||
|
||||
// after going through all this
|
||||
// is this LOD empty?
|
||||
empty = determineIfEmtpy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LodChunk from the given values.
|
||||
*/
|
||||
public LodChunk(ChunkPos pos, short newHeight, short newDepth, Color[] newColors)
|
||||
public LodChunk(ChunkPos pos, LodDataPoint[][] newDataPoints, LodDetail newDetail)
|
||||
{
|
||||
x = pos.x;
|
||||
z = pos.z;
|
||||
|
||||
height = newHeight;
|
||||
depth = newDepth;
|
||||
colors = newColors;
|
||||
dataPoints = newDataPoints;
|
||||
|
||||
dontSave = false;
|
||||
detail = newDetail;
|
||||
|
||||
empty = determineIfEmtpy();
|
||||
}
|
||||
@@ -217,27 +241,32 @@ public class LodChunk
|
||||
*/
|
||||
private boolean determineIfEmtpy()
|
||||
{
|
||||
// we don't check the depth since the
|
||||
// height should always be greater than or equal
|
||||
// to the depth
|
||||
if(height >= 0)
|
||||
for(LodDataPoint[] dataPointArray : dataPoints)
|
||||
{
|
||||
// the height is valid,
|
||||
// do we have at least 1 non-invisible color?
|
||||
|
||||
for(ColorDirection dir : ColorDirection.values())
|
||||
if(!colors[dir.value].equals(INVISIBLE))
|
||||
// at least one direction has a non-invisible color
|
||||
for(LodDataPoint dataPoint : dataPointArray)
|
||||
{
|
||||
if (dataPoint == null)
|
||||
continue;
|
||||
|
||||
// we don't check the depth since the
|
||||
// height should always be greater than or equal
|
||||
// to the depth
|
||||
if(dataPoint.height >= 0)
|
||||
{
|
||||
// the height is valid,
|
||||
|
||||
if (dataPoint.color == INVISIBLE)
|
||||
continue;
|
||||
|
||||
// the color and height are valid
|
||||
// this LodChunk isn't empty
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the height is negative,
|
||||
// this LodChunk is empty
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we checked everywhere, this LodChunk is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,20 +276,164 @@ public class LodChunk
|
||||
// output //
|
||||
//========//
|
||||
|
||||
/** Returns the color for the given direction */
|
||||
public Color getColor(ColorDirection dir)
|
||||
/**
|
||||
* Returns the data point for the given relative block position.
|
||||
*/
|
||||
public LodDataPoint getDataPointForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return colors[dir.value];
|
||||
return dataPoints[blockX / detail.dataPointWidth][blockZ / detail.dataPointWidth];
|
||||
}
|
||||
|
||||
public short getHeight()
|
||||
public Color getColorForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return height;
|
||||
return getDataPointForBlockPos(blockX, blockZ).color;
|
||||
}
|
||||
|
||||
public short getDepth()
|
||||
public short getHeightForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return depth;
|
||||
return getDataPointForBlockPos(blockX, blockZ).height;
|
||||
}
|
||||
|
||||
public short getDepthForBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
return getDataPointForBlockPos(blockX, blockZ).depth;
|
||||
}
|
||||
|
||||
|
||||
public Color getColor(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].color;
|
||||
}
|
||||
|
||||
public short getHeight(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].height;
|
||||
}
|
||||
|
||||
public short getDepth(int xIndex, int zIndex)
|
||||
{
|
||||
return dataPoints[xIndex][zIndex].depth;
|
||||
}
|
||||
|
||||
public short calculateHighestPoint()
|
||||
{
|
||||
short highest = 0;
|
||||
|
||||
for(int x = 0; x < detail.dataPointLengthCount; x++)
|
||||
{
|
||||
for(int z = 0; z < detail.dataPointLengthCount; z++)
|
||||
{
|
||||
if (getHeight(x,z) > highest)
|
||||
highest = getHeight(x,z);
|
||||
}
|
||||
}
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
* @return
|
||||
*/
|
||||
public short getAverageHeightOverArea(int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getHeightForBlockPos(startX,startZ);
|
||||
|
||||
int average = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
average += getHeightForBlockPos(x,z);
|
||||
|
||||
return (short) (average / ((endX - startX) * (endZ - startZ)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startX
|
||||
* @param startZ
|
||||
* @param endX
|
||||
* @param endZ
|
||||
* @return
|
||||
*/
|
||||
public short getAverageDepthOverArea(int startX, int startZ, int endX, int endZ)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getDepthForBlockPos(startX,startZ);
|
||||
|
||||
int average = 0;
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
average += getDepthForBlockPos(x,z);
|
||||
|
||||
return (short) (average / ((endX - startX) * (endZ - startZ)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the color of this LOD.
|
||||
*/
|
||||
public Color getAverageColorOverArea(int startX, int startZ, int endX, int endZ, boolean debugging)
|
||||
{
|
||||
if (startX == endX || startZ == endZ)
|
||||
// we were given an area with 0 blocks in it
|
||||
return getColorForBlockPos(startX,startZ);
|
||||
|
||||
|
||||
|
||||
int[] colorComponents = new int[3];
|
||||
|
||||
|
||||
if (debugging)
|
||||
{
|
||||
// draw the squares as a black and white,
|
||||
// like a checker board
|
||||
|
||||
// only return 1 color to prevent
|
||||
// getting back gray
|
||||
if ((startX + startZ) % 2 == 0)
|
||||
return DEBUG_WHITE;
|
||||
else
|
||||
return DEBUG_BLACK;
|
||||
}
|
||||
|
||||
|
||||
for(int x = startX; x < endX; x++)
|
||||
{
|
||||
for(int z = startZ; z < endZ; z++)
|
||||
{
|
||||
colorComponents = addColorToColorAverages(colorComponents, getColorForBlockPos(x,z));
|
||||
}
|
||||
}
|
||||
|
||||
int numbPoints = ((endX - startX) * (endZ - startZ));
|
||||
|
||||
return new Color(colorComponents[0]/numbPoints, colorComponents[1]/numbPoints, colorComponents[2]/numbPoints);
|
||||
}
|
||||
|
||||
|
||||
private int[] addColorToColorAverages(int[] colorAverages, Color colorToAdd)
|
||||
{
|
||||
// convert the colorToAdd to an int array
|
||||
float[] colorCompoments = new float[4];
|
||||
colorCompoments = colorToAdd.getColorComponents(colorCompoments);
|
||||
|
||||
// add each color component to the array
|
||||
for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++)
|
||||
{
|
||||
// * 255 + 0.5 taken from the Color java class
|
||||
colorAverages[rgbIndex] += (int) (colorCompoments[rgbIndex] * 255 + 0.5);
|
||||
}
|
||||
|
||||
return colorAverages;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,32 +441,28 @@ public class LodChunk
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in csv format
|
||||
* with the given delimiter.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* x, z, height, depth, rgb color data
|
||||
* Outputs all data in a csv format
|
||||
* with the given delimiter, if
|
||||
* dontSave is false. <br><br>
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 5,8, 4, 0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||
* data format: <br>
|
||||
* x, z, dataPoint[0][0], dataPoint[0][1], ... <br>
|
||||
* x, z, height, depth, rgb color data, height, depth, rgb.... <br><br>
|
||||
*
|
||||
* example: <br>
|
||||
* 5,8, 4, 0, 255,255,255, 4, 0, 255, 255, 255, ...
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
String s = "";
|
||||
if (dontSave)
|
||||
return "";
|
||||
|
||||
String s = "";
|
||||
s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(height) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(depth) + DATA_DELIMITER;
|
||||
|
||||
for(int i = 0; i < colors.length; i++)
|
||||
{
|
||||
s += Integer.toString(colors[i].getRed()) + DATA_DELIMITER + Integer.toString(colors[i].getGreen()) + DATA_DELIMITER + Integer.toString(colors[i].getBlue()) + DATA_DELIMITER;
|
||||
}
|
||||
for (int i = 0; i < dataPoints.length; i++)
|
||||
for (int j = 0; j < dataPoints[i].length; j++)
|
||||
s += dataPoints[i][j].toData();
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -306,8 +475,7 @@ public class LodChunk
|
||||
|
||||
s += "x: " + x + " z: " + z + "\t";
|
||||
|
||||
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
|
||||
/**
|
||||
* This stores the height and color
|
||||
* for a specific area in a LodChunk.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 6-19-2021
|
||||
*/
|
||||
public class LodDataPoint
|
||||
{
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
/** this is how many pieces of data are exported when toData is called */
|
||||
public static final int NUMBER_OF_DELIMITERS = 5;
|
||||
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
/** highest point */
|
||||
public short height;
|
||||
|
||||
/** lowest point */
|
||||
public short depth;
|
||||
|
||||
/** The average color for the 6 cardinal directions */
|
||||
public Color color;
|
||||
|
||||
|
||||
/**
|
||||
* Creates and empty LodDataPoint
|
||||
*/
|
||||
public LodDataPoint()
|
||||
{
|
||||
height = -1;
|
||||
depth = -1;
|
||||
color = INVISIBLE;
|
||||
}
|
||||
|
||||
|
||||
public LodDataPoint(short newHeight, short newDepth, Color newColor)
|
||||
{
|
||||
height = newHeight;
|
||||
depth = newDepth;
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
public LodDataPoint(int newHeight, int newDepth, Color newColor)
|
||||
{
|
||||
height = (short) newHeight;
|
||||
depth = (short) newDepth;
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in a csv format
|
||||
* with the given delimiter.
|
||||
* <br>
|
||||
* Exports data in the form:
|
||||
* <br>
|
||||
* height, depth, rgb color data
|
||||
*
|
||||
* <br>
|
||||
* example output:
|
||||
* <br>
|
||||
* 4, 0, 255,255,255,
|
||||
*/
|
||||
public String toData()
|
||||
{
|
||||
String s = Short.toString(height) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(depth) + DATA_DELIMITER;
|
||||
|
||||
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = Short.toString(height) + DATA_DELIMITER;
|
||||
|
||||
s += Short.toString(depth) + DATA_DELIMITER;
|
||||
|
||||
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.util.LodUtils;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
@@ -17,7 +17,7 @@ import net.minecraft.world.server.ServerWorld;
|
||||
* for a given dimension.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-19-2021
|
||||
* @version 06-27-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
@@ -26,8 +26,8 @@ public class LodDimension
|
||||
private volatile int width;
|
||||
private volatile int halfWidth;
|
||||
|
||||
public LodRegion regions[][];
|
||||
public boolean isRegionDirty[][];
|
||||
public volatile LodRegion regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
|
||||
private int centerX;
|
||||
private int centerZ;
|
||||
@@ -49,18 +49,18 @@ public class LodDimension
|
||||
{
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtils.getServerWorldFromDimension(newDimension);
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
ServerChunkProvider provider = serverWorld.getChunkProvider();
|
||||
saveDir = new File(provider.getSavedData().folder.getCanonicalFile() + "\\lod");
|
||||
saveDir = new File(provider.getSavedData().folder.getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDir.getCanonicalFile() +
|
||||
"\\lod server data\\" + LodUtils.getDimensionIDFromWorld(mc.world));
|
||||
saveDir = new File(mc.gameDir.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.world));
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
@@ -242,7 +242,7 @@ public class LodDimension
|
||||
*/
|
||||
public void addLod(LodChunk lod)
|
||||
{
|
||||
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(lod.x, lod.z));
|
||||
RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(lod.x, lod.z));
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
if (!regionIsInRange(pos.x, pos.z))
|
||||
@@ -281,7 +281,7 @@ public class LodDimension
|
||||
*/
|
||||
public LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
|
||||
{
|
||||
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
|
||||
RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
|
||||
|
||||
LodRegion region = getRegion(pos.x, pos.z);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ package com.seibel.lod.objects;
|
||||
* one file in the file system.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 1-22-2021
|
||||
* @version 6-12-2021
|
||||
*/
|
||||
public class LodRegion
|
||||
{
|
||||
@@ -61,9 +61,6 @@ public class LodRegion
|
||||
int arrayX = Math.abs(chunkX % SIZE);
|
||||
int arrayZ = Math.abs(chunkZ % SIZE);
|
||||
|
||||
if(arrayX >= SIZE || arrayZ >= SIZE)
|
||||
return null;
|
||||
|
||||
return chunks[arrayX][arrayZ];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.render.LodRender;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
import com.seibel.lod.util.LodUtils;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
@@ -25,7 +25,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
* and is the starting point for most of this program.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 05-31-2021
|
||||
* @version 06-27-2021
|
||||
*/
|
||||
public class ClientProxy
|
||||
{
|
||||
@@ -34,7 +34,7 @@ public class ClientProxy
|
||||
private static LodWorld lodWorld = new LodWorld();
|
||||
private static LodBuilder lodBuilder = new LodBuilder();
|
||||
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(lodBuilder);
|
||||
private static LodRender renderer = new LodRender(lodBufferBuilder);
|
||||
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
@@ -98,6 +98,7 @@ public class ClientProxy
|
||||
// LodConfig.CLIENT.lodDetail.set(LodDetail.DOUBLE);
|
||||
// LodConfig.CLIENT.lodColorStyle.set(LodColorStyle.INDIVIDUAL_SIDES);
|
||||
// LodConfig.CLIENT.lodChunkRadiusMultiplier.set(12);
|
||||
// LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES);
|
||||
|
||||
// Note to self:
|
||||
// if "unspecified" shows up in the pie chart, it is
|
||||
@@ -131,7 +132,7 @@ public class ClientProxy
|
||||
public void worldLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
// the player just loaded a new world/dimension
|
||||
lodWorld.selectWorld(LodUtils.getWorldID(event.getWorld()));
|
||||
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
|
||||
// make sure the correct LODs are being rendered
|
||||
// (if this isn't done the previous world's LODs may be drawn)
|
||||
renderer.regenerateLODsNextFrame();
|
||||
@@ -182,7 +183,7 @@ public class ClientProxy
|
||||
return lodBuilder;
|
||||
}
|
||||
|
||||
public static LodRender getRenderer()
|
||||
public static LodRenderer getRenderer()
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
|
||||
+144
-25
@@ -3,6 +3,7 @@ package com.seibel.lod.render;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -12,12 +13,13 @@ import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.handlers.ReflectionHandler;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarBuffer;
|
||||
import com.seibel.lod.objects.NearFarFogSetting;
|
||||
import com.seibel.lod.util.LodConfig;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
@@ -25,11 +27,14 @@ import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.potion.Effects;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
@@ -41,14 +46,28 @@ import net.minecraft.util.math.vector.Vector3f;
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-08-2021
|
||||
* @version 06-19-2021
|
||||
*/
|
||||
public class LodRender
|
||||
public class LodRenderer
|
||||
{
|
||||
/** this is the light used when rendering the LODs,
|
||||
* it should be something different than what is used by Minecraft */
|
||||
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
|
||||
|
||||
|
||||
/**
|
||||
* 64 MB by default is the maximum amount of memory that
|
||||
* can be directly allocated. <br><br>
|
||||
*
|
||||
* I know there are commands to change that amount
|
||||
* (specifically "-XX:MaxDirectMemorySize"), but
|
||||
* I have no idea how to access that amount. <br>
|
||||
* So I guess this will be the hard limit for now. <br><br>
|
||||
*
|
||||
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
|
||||
*/
|
||||
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
|
||||
/** If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging. */
|
||||
public boolean debugging = false;
|
||||
@@ -85,13 +104,15 @@ public class LodRender
|
||||
|
||||
/** if this is true the LOD buffers should be regenerated,
|
||||
* provided they aren't already being regenerated. */
|
||||
private boolean regen = false;
|
||||
private volatile boolean regen = false;
|
||||
|
||||
/** This HashSet contains every chunk that Vanilla Minecraft
|
||||
* is going to render */
|
||||
public HashSet<ChunkPos> vanillaRenderedChunks = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodRender(LodBufferBuilder newLodBufferBuilder)
|
||||
public LodRenderer(LodBufferBuilder newLodBufferBuilder)
|
||||
{
|
||||
mc = Minecraft.getInstance();
|
||||
gameRender = mc.gameRenderer;
|
||||
@@ -108,7 +129,6 @@ public class LodRender
|
||||
* @param newDimension The dimension to draw, if null doesn't replace the current dimension.
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler)
|
||||
{
|
||||
if (lodDim == null)
|
||||
@@ -168,7 +188,19 @@ public class LodRender
|
||||
int totalLength = (int) farPlaneDistance * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() * 2;
|
||||
int numbChunksWide = (totalLength / LodChunk.WIDTH);
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<ChunkPos> chunkPosToSkip = getNearbyLodChunkPosToSkip(lodDim, player.getPosition());
|
||||
|
||||
// see if the chunks Minecraft is going to render are the
|
||||
// same as last time
|
||||
if (!vanillaRenderedChunks.containsAll(chunkPosToSkip))
|
||||
{
|
||||
regen = true;
|
||||
vanillaRenderedChunks = chunkPosToSkip;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
@@ -476,15 +508,18 @@ public class LodRender
|
||||
/**
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
{
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.world.getSunBrightness(partialTicks) : 0.2f;
|
||||
float gammaMultiplyer = (float)mc.gameSettings.gamma - 0.5f;
|
||||
float lightStrength = sunBrightness - 0.4f + (gammaMultiplyer * 0.2f);
|
||||
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.2f);
|
||||
|
||||
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
|
||||
|
||||
// can be used for debugging
|
||||
// if (partialTicks < 0.005)
|
||||
// ClientProxy.LOGGER.debug(lightStrength);
|
||||
|
||||
ByteBuffer temp = ByteBuffer.allocateDirect(16);
|
||||
temp.order(ByteOrder.nativeOrder());
|
||||
GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
|
||||
@@ -493,30 +528,39 @@ public class LodRender
|
||||
RenderSystem.enableLighting();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create all buffers that will be used.
|
||||
*/
|
||||
private void setupBuffers(int numbChunksWide)
|
||||
{
|
||||
// calculate the max amount of storage needed (in bytes)
|
||||
int bufferMaxCapacity = (numbChunksWide * numbChunksWide * (6 * 4 * (3 + 4)));
|
||||
// (numbChunksWide * numbChunksWide *
|
||||
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints)))
|
||||
// calculate the max amount of memory needed (in bytes)
|
||||
int bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(LodConfig.CLIENT.lodChunkRadiusMultiplier.get());
|
||||
|
||||
// TODO complain or do something when memory is too low
|
||||
// currently the VM will just crash and complain there is no more memory
|
||||
// issue #4
|
||||
drawableNearBuffer = new BufferBuilder(bufferMaxCapacity);
|
||||
drawableFarBuffer = new BufferBuilder(bufferMaxCapacity);
|
||||
// if the required memory is greater than the
|
||||
// MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier
|
||||
// to fit.
|
||||
if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY)
|
||||
{
|
||||
int maxRadiusMultiplier = RenderUtil.getMaxRadiusMultiplierWithAvaliableMemory(LodConfig.CLIENT.lodTemplate.get(), LodConfig.CLIENT.lodDetail.get());
|
||||
|
||||
ClientProxy.LOGGER.warn("The lodChunkRadiusMultiplier was set too high "
|
||||
+ "and had to be lowered to fit memory constraints "
|
||||
+ "from " + LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + " "
|
||||
+ "to " + maxRadiusMultiplier);
|
||||
|
||||
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(
|
||||
maxRadiusMultiplier);
|
||||
|
||||
bufferMemory = RenderUtil.getBufferMemoryForRadiusMultiplier(maxRadiusMultiplier);
|
||||
}
|
||||
|
||||
lodBufferBuilder.setupBuffers(bufferMaxCapacity);
|
||||
drawableNearBuffer = new BufferBuilder(bufferMemory);
|
||||
drawableFarBuffer = new BufferBuilder(bufferMemory);
|
||||
|
||||
lodBufferBuilder.setupBuffers(bufferMemory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -636,4 +680,79 @@ public class LodRender
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a HashSet of all ChunkPos within the normal render distance
|
||||
* that should not be rendered.
|
||||
*/
|
||||
private HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
|
||||
{
|
||||
int chunkRenderDist = mc.gameSettings.renderDistanceChunks;
|
||||
int blockRenderDist = chunkRenderDist * 16;
|
||||
ChunkPos centerChunk = new ChunkPos(playerPos);
|
||||
|
||||
// skip chunks that are already going to be rendered by Minecraft
|
||||
HashSet<ChunkPos> posToSkip = getRenderedChunks();
|
||||
|
||||
|
||||
// go through each chunk within the normal view distance
|
||||
for(int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
|
||||
{
|
||||
for(int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
|
||||
{
|
||||
LodChunk lod = lodDim.getLodFromCoordinates(x, z);
|
||||
if (lod != null)
|
||||
{
|
||||
short lodHighestPoint = lod.calculateHighestPoint();
|
||||
|
||||
if (playerPos.getY() < lodHighestPoint)
|
||||
{
|
||||
// don't draw Lod's that are taller than the player
|
||||
// to prevent LODs being drawn on top of the player
|
||||
posToSkip.add(new ChunkPos(x, z));
|
||||
}
|
||||
else if (blockRenderDist < Math.abs(playerPos.getY() - lodHighestPoint))
|
||||
{
|
||||
// draw Lod's that are lower than the player's view range
|
||||
posToSkip.remove(new ChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return posToSkip;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame. <br><br>
|
||||
*
|
||||
* Note: This isn't perfect. It will return some chunks that are outside
|
||||
* the clipping plane. (For example, if you are high above the ground some chunks
|
||||
* will be incorrectly added, even though they are outside render range).
|
||||
*/
|
||||
public static HashSet<ChunkPos> getRenderedChunks()
|
||||
{
|
||||
HashSet<ChunkPos> loadedPos = new HashSet<>();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
// Wow those are some long names!
|
||||
|
||||
// go through every RenderInfo to get the compiled chunks
|
||||
for(WorldRenderer.LocalRenderInformationContainer
|
||||
worldrenderer$localrenderinformationcontainer : mc.worldRenderer.renderInfos)
|
||||
{
|
||||
if (!worldrenderer$localrenderinformationcontainer.renderChunk.getCompiledChunk().isEmpty())
|
||||
{
|
||||
// add the ChunkPos for every empty compiled chunk
|
||||
BlockPos bpos = worldrenderer$localrenderinformationcontainer.renderChunk.getPosition();
|
||||
|
||||
loadedPos.add(new ChunkPos(bpos.getX() / 16, bpos.getZ() / 16));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedPos;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +1,39 @@
|
||||
package com.seibel.lod.render;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
/**
|
||||
* This holds miscellaneous helper code
|
||||
* to be used in the rendering process.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2-13-2021
|
||||
* @version 6-17-2021
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
private static final Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the given ChunkPos is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world (probably the player's ChunkPos)
|
||||
*/
|
||||
public static boolean isChunkPosInLoadedArea(ChunkPos pos, ChunkPos center)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
return (pos.x >= center.x - mc.gameSettings.renderDistanceChunks
|
||||
&& pos.x <= center.x + mc.gameSettings.renderDistanceChunks)
|
||||
&&
|
||||
(pos.z >= center.z - mc.gameSettings.renderDistanceChunks
|
||||
&& pos.z <= center.z + mc.gameSettings.renderDistanceChunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given coordinate is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world
|
||||
@@ -42,4 +65,30 @@ public class RenderUtil
|
||||
&& j <= lodRadius + halfRadius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get how much buffer memory would be required for the given radius multiplier
|
||||
*/
|
||||
public static int getBufferMemoryForRadiusMultiplier(int radiusMultiplier)
|
||||
{
|
||||
int numbChunksWide = mc.gameSettings.renderDistanceChunks *
|
||||
radiusMultiplier * 2;
|
||||
|
||||
// calculate the max amount of buffer memory needed (in bytes)
|
||||
return numbChunksWide * numbChunksWide *
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
getBufferMemoryForSingleLod(LodConfig.CLIENT.lodDetail.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maxViewDistanceMultiplier for the given LodTemplate
|
||||
* at the given LodDetail level.
|
||||
*/
|
||||
public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, LodDetail lodDetail)
|
||||
{
|
||||
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod(lodDetail);
|
||||
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
|
||||
|
||||
return numbLodsWide / (2 * mc.gameSettings.renderDistanceChunks);
|
||||
}
|
||||
}
|
||||
|
||||
+34
-9
@@ -1,5 +1,8 @@
|
||||
package com.seibel.lod.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
|
||||
@@ -18,9 +21,9 @@ import net.minecraft.world.server.ServerWorld;
|
||||
* This class holds methods that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 04-01-2021
|
||||
* @version 06-27-2021
|
||||
*/
|
||||
public class LodUtils
|
||||
public class LodUtil
|
||||
{
|
||||
private static Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
@@ -126,7 +129,7 @@ public class LodUtils
|
||||
if(mc.world == null)
|
||||
return "";
|
||||
|
||||
ServerWorld serverWorld = LodUtils.getServerWorldFromDimension(mc.world.getDimensionType());
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(mc.world.getDimensionType());
|
||||
if(serverWorld == null)
|
||||
return "";
|
||||
|
||||
@@ -141,8 +144,8 @@ public class LodUtils
|
||||
ServerData server = mc.getCurrentServerData();
|
||||
return server.serverName + ", IP " +
|
||||
server.serverIP + ", GameVersion " +
|
||||
server.gameVersion.getString() + "\\"
|
||||
+ "dim_" + mc.world.getDimensionType().getEffects().getPath() + "\\";
|
||||
server.gameVersion.getString() + File.separatorChar
|
||||
+ "dim_" + mc.world.getDimensionType().getEffects().getPath() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ public class LodUtils
|
||||
// this will return the world save location
|
||||
// and the dimension folder
|
||||
|
||||
ServerWorld serverWorld = LodUtils.getServerWorldFromDimension(world.getDimensionType());
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(world.getDimensionType());
|
||||
if(serverWorld == null)
|
||||
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerWorld for the dimension " + world.getDimensionType().getEffects().getPath());
|
||||
|
||||
@@ -179,8 +182,8 @@ public class LodUtils
|
||||
ServerData server = mc.getCurrentServerData();
|
||||
return server.serverName + ", IP " +
|
||||
server.serverIP + ", GameVersion " +
|
||||
server.gameVersion.getString() + "\\"
|
||||
+ "dim_" + world.getDimensionType().getEffects().getPath() + "\\";
|
||||
server.gameVersion.getString() + File.separatorChar
|
||||
+ "dim_" + world.getDimensionType().getEffects().getPath() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +201,7 @@ public class LodUtils
|
||||
|
||||
// get the world name
|
||||
int saveIndex = dimId.indexOf("saves") + 1 + "saves".length();
|
||||
int slashIndex = dimId.indexOf('\\', saveIndex);
|
||||
int slashIndex = dimId.indexOf(File.separatorChar, saveIndex);
|
||||
dimId = dimId.substring(saveIndex, slashIndex);
|
||||
return dimId;
|
||||
}
|
||||
@@ -212,4 +215,26 @@ public class LodUtils
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a BlockColors int into a Color object.
|
||||
*/
|
||||
public static Color intToColor(int num)
|
||||
{
|
||||
int filter = 0b11111111;
|
||||
|
||||
int red = (num >> 16 ) & filter;
|
||||
int green = (num >> 8 ) & filter;
|
||||
int blue = num & filter;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Color into a BlockColors object.
|
||||
*/
|
||||
public static int colorToInt(Color color)
|
||||
{
|
||||
return color.getRGB();
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.seibel.lod.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* This object is used to get variables from methods
|
||||
* where they are private.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-18-2021
|
||||
*/
|
||||
public class ReflectionHandler
|
||||
{
|
||||
private Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
public Field ofFogField = null;
|
||||
|
||||
|
||||
public ReflectionHandler()
|
||||
{
|
||||
setupFogField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to setupFovMethod.
|
||||
*/
|
||||
private void setupFogField()
|
||||
{
|
||||
// get every variable from the entity renderer
|
||||
Field[] vars = mc.gameSettings.getClass().getDeclaredFields();
|
||||
|
||||
// try and find the ofFogType variable in gameSettings
|
||||
for(Field f : vars)
|
||||
{
|
||||
if(f.getName().equals("ofFogType"))
|
||||
{
|
||||
ofFogField = f;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find the field,
|
||||
// either optifine isn't installed, or
|
||||
// optifine changed the name of the variable
|
||||
ofFogField = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get what type of fog optifine is currently set to render.
|
||||
*/
|
||||
public FogQuality getFogQuality()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.OFF;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
|
||||
try
|
||||
{
|
||||
returnNum = (int)ofFogField.get(mc.gameSettings);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
switch (returnNum)
|
||||
{
|
||||
case 0:
|
||||
return FogQuality.FAST;
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
|
||||
default:
|
||||
return FogQuality.FAST;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,29 @@ public net.minecraft.world.storage.DimensionSavedDataManager field_215759_d # fo
|
||||
# used when generating LodChunks
|
||||
public net.minecraft.block.AbstractBlock$AbstractBlockState field_235704_h_ # materialColor
|
||||
|
||||
# used when determining which chunks Vanilla Minecraft is going to render
|
||||
public net.minecraft.client.renderer.WorldRenderer$LocalRenderInformationContainer
|
||||
public net.minecraft.client.renderer.WorldRenderer field_72755_R # renderInfos
|
||||
public net.minecraft.client.renderer.WorldRenderer$LocalRenderInformationContainer field_178036_a # renderChunk
|
||||
|
||||
# used in world generation
|
||||
public net.minecraft.world.server.ServerWorld field_241106_P_ # structuremanager
|
||||
public net.minecraft.world.gen.Heightmap func_202267_b(II)I # getDataArrayIndex
|
||||
public net.minecraft.world.gen.Heightmap func_202272_a(III)V # set
|
||||
public net.minecraft.world.chunk.Chunk field_76634_f # heightMap
|
||||
public net.minecraft.world.chunk.Chunk field_76652_q # sections
|
||||
public net.minecraft.world.chunk.ChunkPrimer field_201661_i # sections
|
||||
public net.minecraft.world.server.ChunkManager field_219269_w # templateManager
|
||||
public net.minecraft.world.server.ChunkManager field_219256_j # lightManager
|
||||
public net.minecraft.world.gen.feature.template.TemplateManager field_186240_a # templates
|
||||
public net.minecraft.world.biome.Biome field_242424_k # biomeGenerationSettings
|
||||
public net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider field_227406_b_ # weightedStates
|
||||
public net.minecraft.world.gen.placement.ConfiguredPlacement field_215096_a # decorator
|
||||
public net.minecraft.world.gen.placement.ConfiguredPlacement field_215097_b # config
|
||||
public net.minecraft.util.WeightedList field_220658_a # weightedEntries
|
||||
public net.minecraft.world.gen.feature.FeatureSpread field_242250_b # base
|
||||
public net.minecraft.world.gen.feature.FeatureSpread field_242251_c # spread
|
||||
|
||||
|
||||
|
||||
#=====================#
|
||||
|
||||
Reference in New Issue
Block a user