texture atlasing for terrain w/ triplanar mapping
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
@ -7,7 +7,7 @@
|
||||
{
|
||||
"id" : 1,
|
||||
"name" : "dirt",
|
||||
"texture" : "/Textures/Ground/Dirt1.png"
|
||||
"texture" : "/Textures/Ground/Dirt1_256.png"
|
||||
},
|
||||
{
|
||||
"id" : 2,
|
||||
@ -15,7 +15,7 @@
|
||||
"ambientFoliage" : [
|
||||
"Green Grass"
|
||||
],
|
||||
"texture" : "/Textures/Ground/GrassTileable.png"
|
||||
"texture" : "/Textures/Ground/GrassTileable256.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,7 +1,16 @@
|
||||
#version 330 core
|
||||
|
||||
//texture defines
|
||||
#define ATLAS_ELEMENT_DIM 256.0
|
||||
#define ATLAS_DIM 8192.0
|
||||
#define ATLAS_EL_PER_ROW 32
|
||||
#define ATLAS_NORMALIZED_ELEMENT_WIDTH 0.031 //within the single texture within the atlas, we use this so we never go over the end of the texture
|
||||
#define ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL 0.03125 //used to properly shift from texture to texture in the atlas
|
||||
|
||||
|
||||
#define NR_POINT_LIGHTS 10
|
||||
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
||||
@ -44,6 +53,8 @@ in vec2 texPlane1;
|
||||
in vec2 texPlane2;
|
||||
in vec2 texPlane3;
|
||||
in vec4 FragPosLightSpace;
|
||||
in vec3 samplerIndexVec; //the indices in the atlas of textures to sample
|
||||
in vec3 samplerRatioVec; //the vector of HOW MUCH to pull from each texture in the atlas
|
||||
|
||||
|
||||
uniform vec3 viewPos;
|
||||
@ -67,7 +78,7 @@ uniform sampler2D shadowMap;
|
||||
// vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir);
|
||||
float calcLightIntensityTotal(vec3 normal);
|
||||
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, Material material);
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, vec3 samplerIndexVec, vec3 samplerRatioVec, Material material);
|
||||
|
||||
void main(){
|
||||
vec3 norm = normalize(Normal);
|
||||
@ -77,7 +88,7 @@ void main(){
|
||||
float lightIntensity = calcLightIntensityTotal(norm);
|
||||
|
||||
//get color of base texture
|
||||
vec3 textureColor = getColor(texPlane1, texPlane2, texPlane3, norm, material);
|
||||
vec3 textureColor = getColor(texPlane1, texPlane2, texPlane3, norm, samplerIndexVec, samplerRatioVec, material);
|
||||
|
||||
//shadow
|
||||
float shadow = ShadowCalculation(FragPosLightSpace, normalize(-dLDirection), norm);
|
||||
@ -94,14 +105,82 @@ void main(){
|
||||
}
|
||||
|
||||
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, Material material){
|
||||
/**
|
||||
* The function that gets the texture color based on the triplanar texture mapping and the voxel type at each point along the vert.
|
||||
* See the triplanar mapping wiki article for an explanation of math involved.
|
||||
*/
|
||||
vec3 getColor(vec2 texPlane1, vec2 texPlane2, vec2 texPlane3, vec3 normal, vec3 samplerIndexVec, vec3 samplerRatioVec, Material material){
|
||||
|
||||
vec3 weights = abs(normal);
|
||||
|
||||
vec3 albedoX = texture(material.diffuse, texPlane1).rgb;
|
||||
vec3 albedoY = texture(material.diffuse, texPlane2).rgb;
|
||||
vec3 albedoZ = texture(material.diffuse, texPlane3).rgb;
|
||||
//what is the index in the atlas of the texture for a given vertex
|
||||
int vert1AtlasIndex = int(samplerIndexVec.x);
|
||||
int vert2AtlasIndex = int(samplerIndexVec.y);
|
||||
int vert3AtlasIndex = int(samplerIndexVec.z);
|
||||
|
||||
//what is the weight of that texture relative to the fragment
|
||||
float vert1Weight = samplerRatioVec.x;
|
||||
float vert2Weight = samplerRatioVec.y;
|
||||
float vert3Weight = samplerRatioVec.z;
|
||||
|
||||
//the x-wise uv of the texture for vert1
|
||||
vec2 vert1_x_uv = vec2(
|
||||
(fract(texPlane1.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.x,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane1.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.x / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the x-wise uv of the texture for vert2
|
||||
vec2 vert2_x_uv = vec2(
|
||||
(fract(texPlane1.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.y,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane1.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.y / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the x-wise uv of the texture for vert3
|
||||
vec2 vert3_x_uv = vec2(
|
||||
(fract(texPlane1.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.z,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane1.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.z / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//albedo for the X texture
|
||||
vec3 albedoX = texture(material.diffuse, vert1_x_uv).rgb * vert1Weight + texture(material.diffuse, vert2_x_uv).rgb * vert2Weight + texture(material.diffuse, vert3_x_uv).rgb * vert3Weight;
|
||||
|
||||
|
||||
//the y-wise uv of the texture for vert1
|
||||
vec2 vert1_y_uv = vec2(
|
||||
(fract(texPlane2.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.x,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane2.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.x / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the y-wise uv of the texture for vert2
|
||||
vec2 vert2_y_uv = vec2(
|
||||
(fract(texPlane2.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.y,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane2.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.y / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the y-wise uv of the texture for vert3
|
||||
vec2 vert3_y_uv = vec2(
|
||||
(fract(texPlane2.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.z,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane2.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.z / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//albedo for the X texture
|
||||
vec3 albedoY = texture(material.diffuse, vert1_y_uv).rgb * vert1Weight + texture(material.diffuse, vert2_y_uv).rgb * vert2Weight + texture(material.diffuse, vert3_y_uv).rgb * vert3Weight;
|
||||
|
||||
|
||||
|
||||
|
||||
//the z-wise uv of the texture for vert1
|
||||
vec2 vert1_z_uv = vec2(
|
||||
(fract(texPlane3.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.x,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane3.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.x / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the z-wise uv of the texture for vert2
|
||||
vec2 vert2_z_uv = vec2(
|
||||
(fract(texPlane3.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.y,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane3.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.y / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//the z-wise uv of the texture for vert3
|
||||
vec2 vert3_z_uv = vec2(
|
||||
(fract(texPlane3.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.z,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane3.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.z / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
//albedo for the X texture
|
||||
vec3 albedoZ = texture(material.diffuse, vert1_z_uv).rgb * vert1Weight + texture(material.diffuse, vert2_z_uv).rgb * vert2Weight + texture(material.diffuse, vert3_z_uv).rgb * vert3Weight;
|
||||
|
||||
|
||||
return (albedoX * weights.x + albedoY * weights.y + albedoZ * weights.z);
|
||||
}
|
||||
|
||||
@ -2,13 +2,16 @@
|
||||
#version 330 core
|
||||
|
||||
//defines
|
||||
#define TEXTURE_MAP_SCALE 3.0
|
||||
#define TEXTURE_MAP_SCALE 1.0
|
||||
#define MODEL_TOTAL_DIM 16.0
|
||||
|
||||
|
||||
//input buffers
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 4) in vec2 aTex;
|
||||
layout (location = 5) in vec3 samplerIndices; //the indices in the atlas of textures to sample
|
||||
layout (location = 6) in vec3 samplerRatioVectors; //the interpolated ratio of HOW MUCH to pull from each texture in the atlas
|
||||
|
||||
|
||||
//coordinate space transformation matrices
|
||||
@ -27,6 +30,8 @@ out vec2 texPlane1;
|
||||
out vec2 texPlane2;
|
||||
out vec2 texPlane3;
|
||||
out vec4 FragPosLightSpace;
|
||||
out vec3 samplerIndexVec; //the indices in the atlas of textures to sample
|
||||
out vec3 samplerRatioVec; //the vector of HOW MUCH to pull from each texture in the atlas
|
||||
|
||||
|
||||
|
||||
@ -52,6 +57,10 @@ void main() {
|
||||
texPlane2.x = texPlane2.x * sign(Normal.y);
|
||||
texPlane3.x = texPlane3.x * sign(Normal.z);
|
||||
|
||||
//pass through what atlas'd textures to sample
|
||||
samplerIndexVec = samplerIndices;
|
||||
samplerRatioVec = samplerRatioVectors;
|
||||
|
||||
|
||||
//shadow map stuff
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
|
||||
|
||||
BIN
assets/Textures/Ground/Dirt1_256.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
BIN
assets/Textures/Ground/GrassTileable256.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Sun Apr 21 23:25:24 EDT 2024
|
||||
buildNumber=102
|
||||
#Thu May 02 18:40:02 EDT 2024
|
||||
buildNumber=119
|
||||
|
||||
BIN
docs/src/images/renderer/terrain/polygonOverflowExplanation.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/src/images/renderer/terrain/textureAtlasExample.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
docs/src/images/renderer/terrain/triplanarTheoryCameras.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
docs/src/images/renderer/terrain/triplanarTheoryMultiTexture.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/src/images/renderer/terrain/triplanarTheoryNormals.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
@ -254,11 +254,14 @@ Data Cleanup
|
||||
Fix grass not generating for closest tiles
|
||||
- There is no distance check in the ClientFoliageManager
|
||||
|
||||
(05/04/2024)
|
||||
Ground Texture Atlas system
|
||||
- Basic atlas working with marching cubes
|
||||
- Make it work with triplanar mapping
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
Ground Texture Atlas system
|
||||
|
||||
Character movement in particular feels off
|
||||
- Bring back strafing
|
||||
- Fix interaction with networking
|
||||
@ -267,6 +270,9 @@ Character movement in particular feels off
|
||||
|
||||
Fix being able to walk off far side of the world (ie in level editor)
|
||||
|
||||
Grass System properly LOD
|
||||
- Have foliage dynamically time out cells to be reconsidered based on distance from player (if close, short cooldown, if far long cooldown)
|
||||
|
||||
Data Cleanup
|
||||
- Clean up creatures
|
||||
- Remove unused ones
|
||||
@ -277,6 +283,12 @@ Data Cleanup
|
||||
- Move model textures into models
|
||||
- Recursive model transform data
|
||||
|
||||
Clean up Material class
|
||||
- fix storing textures in the mat class ( pain :c )
|
||||
|
||||
Ground Texture Atlas system
|
||||
- Refactor to block main thread only when creating the actual texture object (load buffered image separately)
|
||||
|
||||
More Debug menus
|
||||
- Screen that shows the overall status of draw cell manager
|
||||
- Screen that shows the overall status of fluid cell manager
|
||||
|
||||
@ -6,4 +6,5 @@
|
||||
- @subpage DrawCell
|
||||
- @subpage Fonts
|
||||
- @subpage modelLoading
|
||||
- @subpage instanceindex
|
||||
- @subpage instanceindex
|
||||
- @subpage terrain
|
||||
155
docs/src/rendering/terrain/terrain.md
Normal file
@ -0,0 +1,155 @@
|
||||
@page terrain Terrain
|
||||
|
||||
# Triplanar Mapping with Texture Atlas
|
||||
|
||||
## Texture Atlas Theory
|
||||
|
||||
It's really easy to use a single texture for a plane of geometry. Think of a heightmap that has a dirt texture applied to it. If the edges of each quad are clamped to (0,1), it will show endlessly tiling dirt.
|
||||
|
||||
The basic idea of the atlas texture is to pack a whole bunch of smaller textures into a larger texture and then sample based on what you want at a particular point. The engine goes through all voxel types on startup and constructs a texture atlas based on the textures provided in the data.
|
||||
|
||||
Here's an example of what it looks like:
|
||||
|
||||

|
||||
|
||||
Notice the two small parts in the bottom left. Those are the textures for two different types of voxels.
|
||||
|
||||
## Triplanar Mapping Theory
|
||||
|
||||
The basic idea of triplanar mapping is we're going to use the vertex coordinates to take "fake" photos of the camera from the x, y, and z angles and then blend them together.
|
||||

|
||||
|
||||
We use the normals of each vertex to determine how much to sample of the x, y, and z photos respectively. IE, if the normal points straight up, you know to only sample from the Y-aligned photo.
|
||||

|
||||
|
||||
These samples aren't literally photos. We're not using framebuffers to somehow get textures.
|
||||
|
||||
Instead, what we're technically calculating is the UVs to sample with. The idea is that we want to blend from a horizontal view to a vertical view as the texture gently curves in a hill.
|
||||
|
||||
This gets complicated once you want to have multiple textures.
|
||||
|
||||
With a single triangle of terrain, you may be sampling from three separate textures based on the terrain values at each point.
|
||||

|
||||
|
||||
In order to make both of these systems work, we need to sample nine separate times. We have to sample:
|
||||
- For each vertex's potentially unique texture
|
||||
- For each camera angle (x, y, z)
|
||||
|
||||
Reference: https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/
|
||||
|
||||
|
||||
## Using Arrays instead of Elements
|
||||
|
||||
An inviolable rule of working with opengl is that to get to get the textures showing on all sides of a vertex, we will need to send duplicate vertices. Unfortunately this means we don't get to use glDrawElements.
|
||||
|
||||
With a normal mesh, we might send data that looks like:
|
||||
|
||||
Vert X | Vert Y | Vert Z | Normal X | Normal Y | Normal Z | UV X | UV Y
|
||||
------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------
|
||||
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0
|
||||
0 | 0 | 2 | 0 | 1 | 0 | 0 | 1
|
||||
2 | 0 | 2 | 0 | 1 | 0 | 1 | 1
|
||||
|
||||
There would then be a separate table of indices into the first table where groups of three indices in the second table correspond to a single triangle
|
||||
|
||||
Element 1 | Element 2 | Element 3
|
||||
--- | --- | ---
|
||||
0 | 1 | 2
|
||||
1 | 2 | 3
|
||||
|
||||
However, given the note at the top of this section, we can't use this element-index scheme because we want to have the textures blend seamlessly on all sides of a given vertex.
|
||||
|
||||
So instead, our data may look like
|
||||
|
||||
Vert X | Vert Y | Vert Z | Normal X | Normal Y | Normal Z | UV X | UV Y
|
||||
------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------
|
||||
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0
|
||||
0 | 0 | 2 | 0 | 1 | 0 | 0 | 1
|
||||
2 | 0 | 2 | 0 | 1 | 0 | 1 | 1
|
||||
0 | 0 | 2 | 0 | 1 | 0 | 0 | 1
|
||||
|
||||
(Notice rows two and four are duplicate data)
|
||||
|
||||
|
||||
## Atlas Data
|
||||
|
||||
### Indices
|
||||
|
||||
Another component required to make this scheme work is extra data to tell the mesh what texture in the atlas to sample at what point. Alongside the Vert, Normal, and UV data we also send the atlas location of the three textures to sample for the current triangle.
|
||||
|
||||
This would look something like the following table.
|
||||
Sample Index 1 | Sample Index 2 | Sample Index 3
|
||||
--- | --- | ---
|
||||
0 | 0 | 0
|
||||
0 | 0 | 0
|
||||
0 | 0 | 0
|
||||
1 | 0 | 0
|
||||
1 | 0 | 0
|
||||
1 | 0 | 0
|
||||
|
||||
The first triangle would be uniformly the same texture. The second triangle would blend from the texture at atlas location "1" to the texture at atlas location "0".
|
||||
|
||||
|
||||
### Weights
|
||||
|
||||
Another piece of information the fragment shader needs to know is how close to each vertex in the triangle it is. This lets the shader determine how much of each texture to pull from.
|
||||
|
||||
The data for this would look like
|
||||
Weight 1 | Weight 2 | Weight 3
|
||||
--- | --- | ---
|
||||
1.0 | 0.0 | 0.0
|
||||
0.0 | 1.0 | 0.0
|
||||
0.0 | 0.0 | 1.0
|
||||
1.0 | 0.0 | 0.0
|
||||
0.0 | 1.0 | 0.0
|
||||
0.0 | 0.0 | 1.0
|
||||
|
||||
The idea is that the vertex shader will automatically interpolate between the groups of three vectors and give us a single vector of weights.
|
||||
|
||||
## Shader Math
|
||||
|
||||
The math that does all texture calculations is pretty complicated and has three main parts
|
||||
|
||||
|
||||
This first block calculates the UVs to sample from for the camera view along the X axis. We need to sample a texture for each of the vertices. The idea is that vert1 could be a dirt texture, vert2 is grass, vert3 is stone, and we need to know how to blend between all three. The following code block calculates UVs for a single of the three textures.
|
||||
|
||||
``` glsl
|
||||
vec2 vert1_x_uv = vec2(
|
||||
(fract(texPlane1.x) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (mod(samplerIndexVec.x,ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL),
|
||||
(fract(texPlane1.y) * ATLAS_NORMALIZED_ELEMENT_WIDTH) + (round(samplerIndexVec.x / ATLAS_EL_PER_ROW) * ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL)
|
||||
);
|
||||
```
|
||||
The naming format is vert\<which vert \>_\<which camera (x,y,z)\>_uv.
|
||||
|
||||
`texPlane1` is a base UV value that is passed into the fragment shader. It's calculated based on the vertex position in the mesh itself (model matrix not applied). IE In the image below, the vertex a 0,0 and 1,0 would generate UVs from 0 to 1 along the X axis.
|
||||
|
||||

|
||||
|
||||
!!IMPORTANT POINT!! Because we are passing these values that are greated than 1 as we go along the model (2->3, 3->4, etc), when the UVs translate from the vertex to fragment shader they are re-normalized into the range [0,1]. This is important for later.
|
||||
|
||||
`ATLAS_NORMALIZED_ELEMENT_WIDTH` is the width of a single texture within the atlas image. Because OpenGL images all have dimensions [0,1], this variable has the value 1/32. Except, it's slightly less than 1/32. This is to keep us from sampling just past the edge of the atlas entry and into the next one (was creating weird lines).
|
||||
|
||||
`samplerIndexVec` Contains the index into the atlas that we want to sample for this particular vertex. It is some single integer that is modulo wrapped up the atlas.
|
||||
|
||||
`ATLAS_NORMALIZED_ELEMENT_WIDTH_FULL` is precisely 1/32. This is so we properly move along the atlas. If we used `ATLAS_NORMALIZED_ELEMENT_WIDTH`, we wouldn't be sampling far enough along and would gradually fall back into previous elements.
|
||||
|
||||
|
||||
|
||||
Now that we have calculated the UVs for each of the vertices along the X axis, we actually sample for each texture and combine them to get an overall x-axis texture. The vertWeights come from data pased into the GPU as an array attrib. (this is that second table that goes 1,0,0 - 0,1,0 - 0,0,1 - 1,0,0 etc)
|
||||
|
||||
``` glsl
|
||||
vec3 albedoX = texture(material.diffuse, vert1_x_uv).rgb * vert1Weight + texture(material.diffuse, vert2_x_uv).rgb * vert2Weight + texture(material.diffuse, vert3_x_uv).rgb * vert3Weight;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Finally, we sum the three different axis colors (x,y,z) and weight them based on the normals.
|
||||
|
||||
``` glsl
|
||||
return (albedoX * weights.x + albedoY * weights.y + albedoZ * weights.z);
|
||||
```
|
||||
|
||||
@ -100,7 +100,9 @@ public class ClientSimulation {
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
//update foliage
|
||||
Globals.clientFoliageManager.update();
|
||||
if(Globals.clientFoliageManager != null){
|
||||
Globals.clientFoliageManager.update();
|
||||
}
|
||||
//tally collidables and offset position accordingly
|
||||
// for(Entity currentCollidable : Globals.entityManager.getEntitiesWithTag(EntityTags.COLLIDABLE)){
|
||||
// CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable);
|
||||
|
||||
@ -63,14 +63,14 @@ public class DrawCell {
|
||||
/**
|
||||
* Generates a drawable entity based on this chunk
|
||||
*/
|
||||
public void generateDrawableEntity(){
|
||||
public void generateDrawableEntity(VoxelTextureAtlas atlas){
|
||||
if(modelEntity != null){
|
||||
Globals.clientScene.deregisterEntity(modelEntity);
|
||||
}
|
||||
|
||||
fillInData();
|
||||
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(weights, types, 0);
|
||||
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(weights, types, 0, atlas);
|
||||
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
|
||||
}
|
||||
|
||||
@ -42,6 +42,9 @@ public class DrawCellManager {
|
||||
Set<String> drawable;
|
||||
Set<String> undrawable;
|
||||
Set<String> updateable;
|
||||
|
||||
//voxel atlas
|
||||
VoxelTextureAtlas atlas;
|
||||
|
||||
|
||||
ShaderProgram program;
|
||||
@ -190,7 +193,7 @@ public class DrawCellManager {
|
||||
undrawable.remove(targetKey);
|
||||
drawable.add(targetKey);
|
||||
//make drawable entity
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
keyCellMap.get(targetKey).generateDrawableEntity(atlas);
|
||||
//evaluate for foliage
|
||||
Globals.clientFoliageManager.evaluateChunk(worldPos);
|
||||
}
|
||||
@ -222,7 +225,7 @@ public class DrawCellManager {
|
||||
// stride = stride + 1;
|
||||
// }
|
||||
keyCellMap.get(targetKey).destroy();
|
||||
keyCellMap.get(targetKey).generateDrawableEntity();
|
||||
keyCellMap.get(targetKey).generateDrawableEntity(atlas);
|
||||
}
|
||||
drawable.add(targetKey);
|
||||
}
|
||||
@ -432,6 +435,13 @@ public class DrawCellManager {
|
||||
return drawRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the voxel texture atlas
|
||||
*/
|
||||
public void attachTextureAtlas(VoxelTextureAtlas voxelTextureAtlas){
|
||||
atlas = voxelTextureAtlas;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
package electrosphere.client.terrain.cells;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.joml.Vector2i;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.data.voxel.VoxelData;
|
||||
import electrosphere.game.data.voxel.VoxelType;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
import electrosphere.util.FileUtils;
|
||||
|
||||
/**
|
||||
* An atlas texture and accompanying map of all voxel textures
|
||||
*/
|
||||
public class VoxelTextureAtlas {
|
||||
|
||||
//A map of voxel id -> coordinates in the atlas texture for its texture
|
||||
Map<Integer,Integer> typeCoordMap = new HashMap<Integer,Integer>();
|
||||
|
||||
|
||||
//the actual texture
|
||||
Texture specular;
|
||||
|
||||
//the normal texture
|
||||
Texture normal;
|
||||
|
||||
//the width in pixels of a single texture in the atlas
|
||||
public static final int ATLAS_ELEMENT_DIM = 256;
|
||||
//the width in pixels of the whole atlas texture
|
||||
public static final int ATLAS_DIM = 8192;
|
||||
//number of textures per row in the atlas
|
||||
public static final int ELEMENTS_PER_ROW = ATLAS_DIM / ATLAS_ELEMENT_DIM;
|
||||
|
||||
/**
|
||||
* Creates the voxel texture atlas object
|
||||
* @return the atlas object
|
||||
*/
|
||||
public static VoxelTextureAtlas createVoxelTextureAtlas(VoxelData data){
|
||||
Globals.profiler.beginCpuSample("createVoxelTextureAtlas");
|
||||
VoxelTextureAtlas rVal = new VoxelTextureAtlas();
|
||||
int iterator = 0;
|
||||
BufferedImage image = new BufferedImage(ATLAS_DIM, ATLAS_DIM, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
Graphics graphics = image.getGraphics();
|
||||
for(VoxelType type : data.getTypes()){
|
||||
if(type.getTexture() != null){
|
||||
int offX = iterator % ELEMENTS_PER_ROW;
|
||||
int offY = iterator / ELEMENTS_PER_ROW;
|
||||
try {
|
||||
BufferedImage newType = ImageIO.read(FileUtils.getAssetFile(type.getTexture()));
|
||||
graphics.drawImage(newType, iterator * ATLAS_ELEMENT_DIM * offX, ATLAS_DIM - ATLAS_ELEMENT_DIM - iterator * ATLAS_ELEMENT_DIM * offY, null);
|
||||
} catch (IOException e) {
|
||||
LoggerInterface.loggerRenderer.ERROR("Texture atlas failed to find texture " + type.getTexture(), e);
|
||||
}
|
||||
//put coords in map
|
||||
rVal.typeCoordMap.put(type.getId(),iterator);
|
||||
|
||||
//iterate
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
//construct texture atlas from buffered image
|
||||
rVal.specular = new Texture(image);
|
||||
rVal.normal = new Texture(image);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the atlas specular
|
||||
* @return the atlas specular
|
||||
*/
|
||||
public Texture getSpecular(){
|
||||
return specular;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the atlas normal
|
||||
* @return the atlas normal
|
||||
*/
|
||||
public Texture getNormal(){
|
||||
return normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index in the atlas of a provided voxel type (the voxel type is provided by its id)
|
||||
* @param voxelTypeId The id of the voxel type
|
||||
* @return the index in the atlas of the texture of the provided voxel type
|
||||
*/
|
||||
public int getVoxelTypeOffset(int voxelTypeId){
|
||||
return typeCoordMap.get(voxelTypeId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,6 +15,7 @@ import org.joml.Vector3i;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.cache.ClientTerrainCache;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
@ -142,11 +143,11 @@ public class ClientTerrainManager {
|
||||
* @param data The chunk data (triangles, normals, etc)
|
||||
* @return The model path that is promised to eventually reflect the terrain model when it makes it to gpu
|
||||
*/
|
||||
public static String queueTerrainGridGeneration(TerrainChunkData data){
|
||||
public static String queueTerrainGridGeneration(TerrainChunkData data, VoxelTextureAtlas atlas){
|
||||
String promisedHash = "";
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
promisedHash = newUUID.toString();
|
||||
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash);
|
||||
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas);
|
||||
terrainChunkGenerationQueue.add(queueItem);
|
||||
return promisedHash;
|
||||
}
|
||||
@ -157,7 +158,7 @@ public class ClientTerrainManager {
|
||||
public static void generateTerrainChunkGeometry(){
|
||||
Globals.profiler.beginCpuSample("generateTerrainChunkGeometry");
|
||||
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
|
||||
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData());
|
||||
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas());
|
||||
Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash());
|
||||
}
|
||||
terrainChunkGenerationQueue.clear();
|
||||
|
||||
@ -1,23 +1,54 @@
|
||||
package electrosphere.client.terrain.manager;
|
||||
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
|
||||
/**
|
||||
* Represents an item in a queue of terrain chunks to have models generated in the main thread
|
||||
*/
|
||||
public class TerrainChunkGenQueueItem {
|
||||
|
||||
//the data of the chunk (verts, normals, etc)
|
||||
TerrainChunkData data;
|
||||
//the hash promised to store the model under in asset manager
|
||||
String promisedHash;
|
||||
//the texture atlas
|
||||
VoxelTextureAtlas atlas;
|
||||
|
||||
public TerrainChunkGenQueueItem(TerrainChunkData data, String promisedHash){
|
||||
/**
|
||||
* Creates a queue item
|
||||
* @param data
|
||||
* @param promisedHash
|
||||
* @param atlas
|
||||
*/
|
||||
public TerrainChunkGenQueueItem(TerrainChunkData data, String promisedHash, VoxelTextureAtlas atlas){
|
||||
this.data = data;
|
||||
this.promisedHash = promisedHash;
|
||||
this.atlas = atlas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mesh data for the chunk
|
||||
* @return the mesh data
|
||||
*/
|
||||
public TerrainChunkData getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash promised for the chunk
|
||||
* @return the hash
|
||||
*/
|
||||
public String getPromisedHash(){
|
||||
return this.promisedHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the texture atlas assigned to the chunk
|
||||
* @return the atlas
|
||||
*/
|
||||
public VoxelTextureAtlas getAtlas(){
|
||||
return atlas;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import electrosphere.client.scene.ClientSceneWrapper;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.sim.ClientSimulation;
|
||||
import electrosphere.client.terrain.cells.DrawCellManager;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.collision.CollisionWorldData;
|
||||
@ -332,6 +333,7 @@ public class Globals {
|
||||
//chunk stuff
|
||||
//draw cell manager
|
||||
public static DrawCellManager drawCellManager;
|
||||
public static VoxelTextureAtlas voxelTextureAtlas;
|
||||
|
||||
//fluid cell manager
|
||||
public static FluidCellManager fluidCellManager;
|
||||
@ -512,7 +514,6 @@ public class Globals {
|
||||
defaultMeshShader = ShaderProgram.smart_assemble_shader(false,true);
|
||||
//init terrain shader program
|
||||
terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
|
||||
TerrainChunkModelGeneration.terrainChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain2/terrain2.vs", "/Shaders/terrain2/terrain2.fs");
|
||||
//init fluid shader program
|
||||
FluidChunkModelGeneration.fluidChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/fluid2/fluid2.vs", "/Shaders/fluid2/fluid2.fs");
|
||||
//init models
|
||||
@ -549,6 +550,9 @@ public class Globals {
|
||||
//debug shaders
|
||||
assetManager.addShaderToQueue("Shaders/ui/debug/windowBorder/windowBound.vs", null, "Shaders/ui/debug/windowBorder/windowBound.fs");
|
||||
assetManager.addShaderToQueue("Shaders/ui/debug/windowContentBorder/windowContentBound.vs", null, "Shaders/ui/debug/windowContentBorder/windowContentBound.fs");
|
||||
|
||||
//voxel texture atlas
|
||||
voxelTextureAtlas = VoxelTextureAtlas.createVoxelTextureAtlas(Globals.gameConfigCurrent.getVoxelData());
|
||||
|
||||
//as these assets are required for the renderer to work, we go ahead and
|
||||
//load them into memory now. The loading time penalty is worth it I think.
|
||||
|
||||
@ -240,6 +240,8 @@ public class ClientLoading {
|
||||
}
|
||||
//initialize draw cell manager
|
||||
Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
|
||||
//construct texture atlas
|
||||
Globals.drawCellManager.attachTextureAtlas(Globals.voxelTextureAtlas);
|
||||
//set our draw cell manager to actually generate drawable chunks
|
||||
Globals.drawCellManager.setGenerateDrawables(true);
|
||||
//Alerts the client simulation that it should start loading terrain
|
||||
|
||||
@ -2,6 +2,7 @@ package electrosphere.entity.types.terrain;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.collision.PhysicsEntityUtils;
|
||||
import electrosphere.collision.PhysicsUtils;
|
||||
@ -25,10 +26,10 @@ public class TerrainChunk {
|
||||
* @param levelOfDetail Increasing value that increments level of detail. 0 would be full resolution, 1 would be half resolution and so on. Only generates physics if levelOfDetail is 0
|
||||
* @return The terrain chunk entity
|
||||
*/
|
||||
public static Entity clientCreateTerrainChunkEntity(float[][][] weights, int[][][] values, int levelOfDetail){
|
||||
public static Entity clientCreateTerrainChunkEntity(float[][][] weights, int[][][] values, int levelOfDetail, VoxelTextureAtlas atlas){
|
||||
|
||||
TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values);
|
||||
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data);
|
||||
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
|
||||
|
||||
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
|
||||
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
|
||||
|
||||
@ -2,34 +2,87 @@ package electrosphere.entity.types.terrain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The data required to generate a texture
|
||||
*/
|
||||
public class TerrainChunkData {
|
||||
|
||||
//the verts
|
||||
List<Float> vertices;
|
||||
//normals
|
||||
List<Float> normals;
|
||||
//faces
|
||||
List<Integer> faceElements;
|
||||
//UVs
|
||||
List<Float> uvs;
|
||||
//texture samplers
|
||||
List<Float> textureSamplers; //what textures in the atlas to sample
|
||||
//texture ratio vector
|
||||
List<Float> textureRatioVectors; //HOW MUCH of each texture in the atlas to sample
|
||||
|
||||
public TerrainChunkData(List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs){
|
||||
/**
|
||||
* Creates an object to hold data required to generate a chunk
|
||||
* @param vertices
|
||||
* @param normals
|
||||
* @param faceElements
|
||||
* @param uvs
|
||||
* @param textureSamplers
|
||||
*/
|
||||
public TerrainChunkData(List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs, List<Float> textureSamplers, List<Float> textureRatioVectors){
|
||||
this.vertices = vertices;
|
||||
this.normals = normals;
|
||||
this.faceElements = faceElements;
|
||||
this.uvs = uvs;
|
||||
this.textureSamplers = textureSamplers;
|
||||
this.textureRatioVectors = textureRatioVectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the vertex data
|
||||
* @return the vertex data
|
||||
*/
|
||||
public List<Float> getVertices(){
|
||||
return vertices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the normal data
|
||||
* @return the normal data
|
||||
*/
|
||||
public List<Float> getNormals(){
|
||||
return normals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the face element data
|
||||
* @return the face element data
|
||||
*/
|
||||
public List<Integer> getFaceElements(){
|
||||
return faceElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the uv data
|
||||
* @return the uv data
|
||||
*/
|
||||
public List<Float> getUVs(){
|
||||
return uvs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the texture sampler data
|
||||
* @return the texture sampler data
|
||||
*/
|
||||
public List<Float> getTextureSamplers(){
|
||||
return textureSamplers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the texture ratio vector data
|
||||
* @return the texture ratio vector data
|
||||
*/
|
||||
public List<Float> getTextureRatioVectors(){
|
||||
return textureRatioVectors;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -71,6 +71,8 @@ public class InstanceManager {
|
||||
* Draws all models that are queued in this instance manager
|
||||
*/
|
||||
public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
|
||||
boolean instanced = renderPipelineState.getInstanced();
|
||||
boolean useMeshShader = renderPipelineState.getUseMeshShader();
|
||||
renderPipelineState.setInstanced(true);
|
||||
renderPipelineState.setUseMeshShader(false);
|
||||
for(String modelPath : modelsToDraw){
|
||||
@ -94,7 +96,8 @@ public class InstanceManager {
|
||||
//clear queue
|
||||
data.clearDrawQueue();
|
||||
}
|
||||
renderPipelineState.setInstanced(false);
|
||||
renderPipelineState.setInstanced(instanced);
|
||||
renderPipelineState.setUseMeshShader(useMeshShader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import org.lwjgl.BufferUtils;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
|
||||
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
import electrosphere.renderer.model.Material;
|
||||
@ -341,9 +342,19 @@ public class TerrainChunkModelGeneration {
|
||||
(byte)0x13
|
||||
};
|
||||
|
||||
/**
|
||||
* A single generated triangle
|
||||
*/
|
||||
static class Triangle {
|
||||
//the indices
|
||||
int[] indices = new int[3]; //array of size 3
|
||||
|
||||
/**
|
||||
* Creates a triangle object
|
||||
* @param index0
|
||||
* @param index1
|
||||
* @param index2
|
||||
*/
|
||||
public Triangle(int index0, int index1, int index2){
|
||||
indices[0] = index0;
|
||||
indices[1] = index1;
|
||||
@ -351,19 +362,30 @@ public class TerrainChunkModelGeneration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The grid cell currently being looked at
|
||||
*/
|
||||
static class GridCell {
|
||||
Vector3f[] points = new Vector3f[8]; //array of size 8
|
||||
double[] val = new double[8]; //array of size 8
|
||||
int[] atlasValues = new int[8]; //array of size 8
|
||||
public void setValues(
|
||||
Vector3f p1, Vector3f p2, Vector3f p3, Vector3f p4,
|
||||
Vector3f p5, Vector3f p6, Vector3f p7, Vector3f p8,
|
||||
double val1, double val2, double val3, double val4,
|
||||
double val5, double val6, double val7, double val8
|
||||
double val5, double val6, double val7, double val8,
|
||||
int atl1, int atl2, int atl3, int atl4,
|
||||
int atl5, int atl6, int atl7, int atl8
|
||||
){
|
||||
//triangle points
|
||||
points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4;
|
||||
points[4] = p5; points[5] = p6; points[6] = p7; points[7] = p8;
|
||||
//iso values
|
||||
val[0] = val1; val[1] = val2; val[2] = val3; val[3] = val4;
|
||||
val[4] = val5; val[5] = val6; val[6] = val7; val[7] = val8;
|
||||
//atlas values
|
||||
atlasValues[0] = atl1; atlasValues[1] = atl2; atlasValues[2] = atl3; atlasValues[3] = atl4;
|
||||
atlasValues[4] = atl5; atlasValues[5] = atl6; atlasValues[6] = atl7; atlasValues[7] = atl8;
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,9 +395,12 @@ public class TerrainChunkModelGeneration {
|
||||
|
||||
|
||||
|
||||
public static ShaderProgram terrainChunkShaderProgram = null;
|
||||
|
||||
|
||||
//location of the sampler index data in the shader
|
||||
public static final int SAMPLER_INDEX_ATTRIB_LOC = 5;
|
||||
//the ratio vectors of how much to pull from each texture
|
||||
public static final int SAMPLER_RATIO_ATTRIB_LOC = 6;
|
||||
|
||||
|
||||
|
||||
@ -391,6 +416,7 @@ public class TerrainChunkModelGeneration {
|
||||
GridCell grid,
|
||||
double isolevel,
|
||||
List<Triangle> triangles,
|
||||
List<Vector3f> samplerIndices,
|
||||
Map<String,Integer> vertMap,
|
||||
List<Vector3f> verts,
|
||||
List<Vector3f> normals,
|
||||
@ -400,6 +426,7 @@ public class TerrainChunkModelGeneration {
|
||||
int ntriang;
|
||||
int cubeIndex = 0;
|
||||
Vector3f[] vertList = new Vector3f[12];
|
||||
int[] samplerIndex = new int[12];
|
||||
|
||||
//get lookup key (index) for edge table
|
||||
//edge table tells us which vertices are inside of the surface
|
||||
@ -419,42 +446,55 @@ public class TerrainChunkModelGeneration {
|
||||
//instead of having all intersections be perfectly at the midpoint,
|
||||
//for each edge this code calculates where along the edge to place the vertex
|
||||
//this should dramatically smooth the surface
|
||||
if ((edgeTable[cubeIndex] & 1) > 0)
|
||||
vertList[0] =
|
||||
VertexInterp(isolevel,grid.points[0],grid.points[1],grid.val[0],grid.val[1]);
|
||||
if ((edgeTable[cubeIndex] & 2) > 0)
|
||||
vertList[1] =
|
||||
VertexInterp(isolevel,grid.points[1],grid.points[2],grid.val[1],grid.val[2]);
|
||||
if ((edgeTable[cubeIndex] & 4) > 0)
|
||||
vertList[2] =
|
||||
VertexInterp(isolevel,grid.points[2],grid.points[3],grid.val[2],grid.val[3]);
|
||||
if ((edgeTable[cubeIndex] & 8) > 0)
|
||||
vertList[3] =
|
||||
VertexInterp(isolevel,grid.points[3],grid.points[0],grid.val[3],grid.val[0]);
|
||||
if ((edgeTable[cubeIndex] & 16) > 0)
|
||||
vertList[4] =
|
||||
VertexInterp(isolevel,grid.points[4],grid.points[5],grid.val[4],grid.val[5]);
|
||||
if ((edgeTable[cubeIndex] & 32) > 0)
|
||||
vertList[5] =
|
||||
VertexInterp(isolevel,grid.points[5],grid.points[6],grid.val[5],grid.val[6]);
|
||||
if ((edgeTable[cubeIndex] & 64) > 0)
|
||||
vertList[6] =
|
||||
VertexInterp(isolevel,grid.points[6],grid.points[7],grid.val[6],grid.val[7]);
|
||||
if ((edgeTable[cubeIndex] & 128) > 0)
|
||||
vertList[7] =
|
||||
VertexInterp(isolevel,grid.points[7],grid.points[4],grid.val[7],grid.val[4]);
|
||||
if ((edgeTable[cubeIndex] & 256) > 0)
|
||||
vertList[8] =
|
||||
VertexInterp(isolevel,grid.points[0],grid.points[4],grid.val[0],grid.val[4]);
|
||||
if ((edgeTable[cubeIndex] & 512) > 0)
|
||||
vertList[9] =
|
||||
VertexInterp(isolevel,grid.points[1],grid.points[5],grid.val[1],grid.val[5]);
|
||||
if ((edgeTable[cubeIndex] & 1024) > 0)
|
||||
vertList[10] =
|
||||
VertexInterp(isolevel,grid.points[2],grid.points[6],grid.val[2],grid.val[6]);
|
||||
if ((edgeTable[cubeIndex] & 2048) > 0)
|
||||
vertList[11] =
|
||||
VertexInterp(isolevel,grid.points[3],grid.points[7],grid.val[3],grid.val[7]);
|
||||
if((edgeTable[cubeIndex] & 1) > 0){
|
||||
vertList[0] = VertexInterp(isolevel,grid.points[0],grid.points[1],grid.val[0],grid.val[1]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 2) > 0){
|
||||
vertList[1] = VertexInterp(isolevel,grid.points[1],grid.points[2],grid.val[1],grid.val[2]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 4) > 0){
|
||||
vertList[2] = VertexInterp(isolevel,grid.points[2],grid.points[3],grid.val[2],grid.val[3]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 8) > 0){
|
||||
vertList[3] = VertexInterp(isolevel,grid.points[3],grid.points[0],grid.val[3],grid.val[0]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 16) > 0){
|
||||
vertList[4] = VertexInterp(isolevel,grid.points[4],grid.points[5],grid.val[4],grid.val[5]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 32) > 0){
|
||||
vertList[5] = VertexInterp(isolevel,grid.points[5],grid.points[6],grid.val[5],grid.val[6]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 64) > 0){
|
||||
vertList[6] = VertexInterp(isolevel,grid.points[6],grid.points[7],grid.val[6],grid.val[7]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 128) > 0){
|
||||
vertList[7] = VertexInterp(isolevel,grid.points[7],grid.points[4],grid.val[7],grid.val[4]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 256) > 0){
|
||||
vertList[8] = VertexInterp(isolevel,grid.points[0],grid.points[4],grid.val[0],grid.val[4]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 512) > 0){
|
||||
vertList[9] = VertexInterp(isolevel,grid.points[1],grid.points[5],grid.val[1],grid.val[5]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 1024) > 0){
|
||||
vertList[10] = VertexInterp(isolevel,grid.points[2],grid.points[6],grid.val[2],grid.val[6]);
|
||||
}
|
||||
if((edgeTable[cubeIndex] & 2048) > 0){
|
||||
vertList[11] = VertexInterp(isolevel,grid.points[3],grid.points[7],grid.val[3],grid.val[7]);
|
||||
}
|
||||
|
||||
if(grid.val[0] > isolevel){ samplerIndex[0] = 0; } else { samplerIndex[0] = 1; }
|
||||
if(grid.val[1] > isolevel){ samplerIndex[1] = 1; } else { samplerIndex[1] = 2; }
|
||||
if(grid.val[2] > isolevel){ samplerIndex[2] = 2; } else { samplerIndex[2] = 3; }
|
||||
if(grid.val[3] > isolevel){ samplerIndex[3] = 3; } else { samplerIndex[3] = 0; }
|
||||
if(grid.val[4] > isolevel){ samplerIndex[4] = 4; } else { samplerIndex[4] = 5; }
|
||||
if(grid.val[5] > isolevel){ samplerIndex[5] = 5; } else { samplerIndex[5] = 6; }
|
||||
if(grid.val[6] > isolevel){ samplerIndex[6] = 6; } else { samplerIndex[6] = 7; }
|
||||
if(grid.val[7] > isolevel){ samplerIndex[7] = 7; } else { samplerIndex[7] = 4; }
|
||||
if(grid.val[0] > isolevel){ samplerIndex[8] = 0; } else { samplerIndex[8] = 4; }
|
||||
if(grid.val[1] > isolevel){ samplerIndex[9] = 1; } else { samplerIndex[9] = 5; }
|
||||
if(grid.val[2] > isolevel){ samplerIndex[10] = 2; } else { samplerIndex[10] = 6; }
|
||||
if(grid.val[3] > isolevel){ samplerIndex[11] = 3; } else { samplerIndex[11] = 7; }
|
||||
|
||||
//Create the triangle
|
||||
ntriang = 0;
|
||||
@ -484,6 +524,26 @@ public class TerrainChunkModelGeneration {
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//Sampler triangles
|
||||
//
|
||||
for(int j = 0; j < 3; j++){
|
||||
//we add the triangle three times so all three vertices have the same values
|
||||
//that way they don't interpolate when you're in a middle point of the fragment
|
||||
//this could eventually potentially be optimized to send 1/3rd the data, but
|
||||
//the current approach is easier to reason about
|
||||
Vector3f samplerTriangle = new Vector3f(
|
||||
grid.atlasValues[samplerIndex[triTable[cubeIndex][i+0]]],
|
||||
grid.atlasValues[samplerIndex[triTable[cubeIndex][i+1]]],
|
||||
grid.atlasValues[samplerIndex[triTable[cubeIndex][i+2]]]
|
||||
);
|
||||
samplerIndices.add(samplerTriangle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Normals calculation
|
||||
//
|
||||
@ -589,8 +649,8 @@ public class TerrainChunkModelGeneration {
|
||||
List<Vector3f> normals = new LinkedList<Vector3f>();
|
||||
//the list of number of triangles that share a vert
|
||||
List<Integer> trianglesSharingVert = new LinkedList<Integer>();
|
||||
//List of elements in order
|
||||
List<Integer> faceElements = new LinkedList<Integer>();
|
||||
//List of texture sampler values
|
||||
List<Vector3f> samplerTriangles = new LinkedList<Vector3f>();
|
||||
//List of UVs
|
||||
List<Float> UVs = new LinkedList<Float>();
|
||||
|
||||
@ -604,10 +664,12 @@ public class TerrainChunkModelGeneration {
|
||||
new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0),
|
||||
new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0),
|
||||
terrainGrid[x+0][y+0][z+0], terrainGrid[x+0][y+0][z+1], terrainGrid[x+1][y+0][z+1], terrainGrid[x+1][y+0][z+0],
|
||||
terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0]
|
||||
terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0],
|
||||
textureGrid[x+0][y+0][z+0], textureGrid[x+0][y+0][z+1], textureGrid[x+1][y+0][z+1], textureGrid[x+1][y+0][z+0],
|
||||
textureGrid[x+0][y+1][z+0], textureGrid[x+0][y+1][z+1], textureGrid[x+1][y+1][z+1], textureGrid[x+1][y+1][z+0]
|
||||
);
|
||||
//polygonize the current gridcell
|
||||
polygonize(currentCell, 0.01f, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,6 +680,10 @@ public class TerrainChunkModelGeneration {
|
||||
List<Float> normalsFlat = new LinkedList<Float>();
|
||||
//all elements of faces in order
|
||||
List<Integer> elementsFlat = new LinkedList<Integer>();
|
||||
//List of texture sampler values
|
||||
List<Float> textureSamplers = new LinkedList<Float>();
|
||||
//List of texture ratio values
|
||||
List<Float> textureRatioData = new LinkedList<Float>();
|
||||
|
||||
//flatten verts + normals
|
||||
for(Vector3f vert : verts){
|
||||
@ -671,8 +737,33 @@ public class TerrainChunkModelGeneration {
|
||||
UVs.add(temp[1]);
|
||||
}
|
||||
|
||||
//flatten sampler indices
|
||||
for(Vector3f samplerVec : samplerTriangles){
|
||||
textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x));
|
||||
textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y));
|
||||
textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z));
|
||||
}
|
||||
|
||||
//set ratio dat
|
||||
for(Triangle triangle : triangles){
|
||||
//first vertex
|
||||
textureRatioData.add(1.0f);
|
||||
textureRatioData.add(0.0f);
|
||||
textureRatioData.add(0.0f);
|
||||
|
||||
//second vertex
|
||||
textureRatioData.add(0.0f);
|
||||
textureRatioData.add(1.0f);
|
||||
textureRatioData.add(0.0f);
|
||||
|
||||
//third vertex
|
||||
textureRatioData.add(0.0f);
|
||||
textureRatioData.add(0.0f);
|
||||
textureRatioData.add(1.0f);
|
||||
}
|
||||
|
||||
//List<Float> vertices, List<Float> normals, List<Integer> faceElements, List<Float> uvs
|
||||
TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs);
|
||||
TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -695,84 +786,92 @@ public class TerrainChunkModelGeneration {
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//Buffer data to GPU
|
||||
//
|
||||
|
||||
try {
|
||||
int vertexCount = data.getVertices().size() / 3;
|
||||
FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(vertexCount * 3);
|
||||
float[] temp = new float[3];
|
||||
for(float vertValue : data.getVertices()){
|
||||
VertexArrayBufferData.put(vertValue);
|
||||
}
|
||||
VertexArrayBufferData.flip();
|
||||
mesh.bufferVertices(VertexArrayBufferData, 3);
|
||||
} catch (NullPointerException ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FACES
|
||||
// Buffer data to GPU
|
||||
//
|
||||
int faceCount = data.getFaceElements().size() / 3;
|
||||
int elementCount = data.getFaceElements().size();
|
||||
try {
|
||||
IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(elementCount);
|
||||
int[] temp = new int[3];
|
||||
FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
|
||||
FloatBuffer NormalArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3);
|
||||
FloatBuffer TextureArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 2);
|
||||
for(int element : data.getFaceElements()){
|
||||
elementArrayBufferData.put(element);
|
||||
//for each element, need to push vert, normal, etc
|
||||
|
||||
//push current vertex
|
||||
VertexArrayBufferData.put(data.getVertices().get(element*3+0));
|
||||
VertexArrayBufferData.put(data.getVertices().get(element*3+1));
|
||||
VertexArrayBufferData.put(data.getVertices().get(element*3+2));
|
||||
|
||||
//push current normals
|
||||
NormalArrayBufferData.put(data.getNormals().get(element*3+0));
|
||||
NormalArrayBufferData.put(data.getNormals().get(element*3+1));
|
||||
NormalArrayBufferData.put(data.getNormals().get(element*3+2));
|
||||
|
||||
//push current uvs
|
||||
TextureArrayBufferData.put(data.getUVs().get(element*2+0));
|
||||
TextureArrayBufferData.put(data.getUVs().get(element*2+1));
|
||||
}
|
||||
elementArrayBufferData.flip();
|
||||
mesh.bufferFaces(elementArrayBufferData,elementCount);
|
||||
|
||||
//actually buffer vertices
|
||||
VertexArrayBufferData.flip();
|
||||
mesh.bufferVertices(VertexArrayBufferData, 3);
|
||||
|
||||
//actually buffer normals
|
||||
NormalArrayBufferData.flip();
|
||||
mesh.bufferNormals(NormalArrayBufferData, 3);
|
||||
|
||||
//actually buffer UVs
|
||||
TextureArrayBufferData.flip();
|
||||
mesh.bufferTextureCoords(TextureArrayBufferData, 2);
|
||||
|
||||
|
||||
} catch (NullPointerException ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
//alert mesh to use direct array access and set the number of elements in the direct arrays
|
||||
mesh.setUseElementArray(false);
|
||||
mesh.setDirectArraySize(elementCount);
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// NORMALS
|
||||
// SAMPLER INDICES
|
||||
//
|
||||
try {
|
||||
int normalCount = data.getNormals().size() / 3;
|
||||
FloatBuffer NormalArrayBufferData;
|
||||
if(normalCount > 0){
|
||||
NormalArrayBufferData = BufferUtils.createFloatBuffer(normalCount * 3);
|
||||
float[] temp = new float[3];
|
||||
for(float normalValue : data.getNormals()){
|
||||
NormalArrayBufferData.put(normalValue);
|
||||
}
|
||||
NormalArrayBufferData.flip();
|
||||
mesh.bufferNormals(NormalArrayBufferData, 3);
|
||||
int vertexCount = data.getTextureSamplers().size() / 3;
|
||||
FloatBuffer samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
|
||||
for(float samplerVec : data.getTextureSamplers()){
|
||||
samplerBuffer.put(samplerVec);
|
||||
}
|
||||
samplerBuffer.flip();
|
||||
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_INDEX_ATTRIB_LOC);
|
||||
} catch (NullPointerException ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
//
|
||||
// TEXTURE COORDINATES
|
||||
// SAMPLER RATIO DATA
|
||||
//
|
||||
try {
|
||||
int textureCoordCount = data.getUVs().size() / 2;
|
||||
FloatBuffer TextureArrayBufferData;
|
||||
if(textureCoordCount > 0){
|
||||
TextureArrayBufferData = BufferUtils.createFloatBuffer(textureCoordCount * 2);
|
||||
float[] temp = new float[2];
|
||||
for(float uvValue : data.getUVs()){
|
||||
TextureArrayBufferData.put(uvValue);
|
||||
}
|
||||
TextureArrayBufferData.flip();
|
||||
mesh.bufferTextureCoords(TextureArrayBufferData, 2);
|
||||
int vertexCount = data.getTextureRatioVectors().size() / 3;
|
||||
FloatBuffer samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3);
|
||||
for(float samplerVec : data.getTextureRatioVectors()){
|
||||
samplerBuffer.put(samplerVec);
|
||||
}
|
||||
samplerBuffer.flip();
|
||||
mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC);
|
||||
} catch (NullPointerException ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//bounding sphere logic
|
||||
float halfChunk = ServerTerrainChunk.CHUNK_DIMENSION / 2.0f;
|
||||
mesh.updateBoundingSphere(
|
||||
halfChunk,
|
||||
@ -791,20 +890,21 @@ public class TerrainChunkModelGeneration {
|
||||
/**
|
||||
* Generates a model based on a terrainchunkdata object
|
||||
* @param data The terrain chunk data object
|
||||
* @param atlas The atlas texture for the chunk
|
||||
* @return The model
|
||||
*/
|
||||
public static Model generateTerrainModel(TerrainChunkData data){
|
||||
public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){
|
||||
Model rVal = new Model();
|
||||
Mesh m = generateTerrainMesh(data);
|
||||
|
||||
|
||||
//construct the material for the chunk
|
||||
Material groundMat = new Material();
|
||||
groundMat.set_diffuse("/Textures/Ground/Dirt1.png");
|
||||
groundMat.set_specular("/Textures/Ground/Dirt1.png");
|
||||
Globals.assetManager.addTexturePathtoQueue("/Textures/Ground/Dirt1.png");
|
||||
groundMat.setTexturePointer(atlas.getSpecular().getTexturePointer());
|
||||
groundMat.setNormalTexturePointer(atlas.getNormal().getTexturePointer());
|
||||
m.setMaterial(groundMat);
|
||||
|
||||
m.setShader(TerrainChunkModelGeneration.terrainChunkShaderProgram);
|
||||
//shader logic
|
||||
m.setShader(Globals.terrainShaderProgram);
|
||||
m.setParent(rVal);
|
||||
|
||||
rVal.getMeshes().add(m);
|
||||
@ -818,6 +918,13 @@ public class TerrainChunkModelGeneration {
|
||||
return x + "_" + y + "_" + z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the already existing index of this point
|
||||
* @param vert the vertex's raw position
|
||||
* @param vertMap the map of key ->Vert index
|
||||
* @param verts
|
||||
* @return
|
||||
*/
|
||||
private static int getVertIndex(Vector3f vert, Map<String,Integer> vertMap, List<Vector3f> verts){
|
||||
int rVal = -1;
|
||||
String vertKey = getVertKeyFromPoints(vert.x,vert.y,vert.z);
|
||||
|
||||
@ -25,11 +25,18 @@ public class Material {
|
||||
//or whether it should have a manually set texturePointer and not look up while binding
|
||||
boolean usesFetch = true;
|
||||
|
||||
//texture pointer for the specular
|
||||
int texturePointer;
|
||||
//texture pointer for the normal
|
||||
int normalPointer;
|
||||
|
||||
/**
|
||||
* A material that contains textures
|
||||
*/
|
||||
public Material(){
|
||||
|
||||
}
|
||||
|
||||
//basically useless because blender doesn't support exporting mats with fbx
|
||||
public static Material load_material_from_aimaterial(AIMaterial input){
|
||||
Material rVal = new Material();
|
||||
@ -65,6 +72,15 @@ public class Material {
|
||||
texturePointer = pointer;
|
||||
usesFetch = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the in-material pointer for the normal
|
||||
* @param pointer the normal texture
|
||||
*/
|
||||
public void setNormalTexturePointer(int pointer){
|
||||
normalPointer = pointer;
|
||||
usesFetch = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the material
|
||||
|
||||
@ -50,12 +50,14 @@ public class Mesh {
|
||||
private int vertexBuffer;
|
||||
private int normalBuffer;
|
||||
private int elementArrayBuffer;
|
||||
private int elementCount;
|
||||
private int elementCount; //pulls double duty as both the element array count as well as the direct array count, based on whichever is used
|
||||
private int vertexArrayObject;
|
||||
private int boneWeightBuffer;
|
||||
private int boneIndexBuffer;
|
||||
private int textureCoordBuffer;
|
||||
|
||||
//uses element instances
|
||||
private boolean useElementArray = true;
|
||||
|
||||
|
||||
//THIS IS NOT GUARANTEED TO BE THE PARENT MODEL THAT THIS WAS LOADED IN
|
||||
@ -131,6 +133,14 @@ public class Mesh {
|
||||
this.elementCount = elementCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of elements in the directly referenced arrays underneath this mesh
|
||||
* @param directArraySize The number of elements (ie the number of vertices, or normals, etc)
|
||||
*/
|
||||
public void setDirectArraySize(int directArraySize){
|
||||
this.elementCount = directArraySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffers texture coordinates to the gpu
|
||||
@ -316,6 +326,9 @@ public class Mesh {
|
||||
* @param renderPipelineState The state of the render pipeline
|
||||
*/
|
||||
public void complexDraw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
|
||||
|
||||
//bind vao off the rip
|
||||
glBindVertexArray(vertexArrayObject);
|
||||
|
||||
if(renderPipelineState.getUseMeshShader()){
|
||||
ShaderProgram selectedProgram = null;
|
||||
@ -362,9 +375,6 @@ public class Mesh {
|
||||
|
||||
|
||||
|
||||
glBindVertexArray(vertexArrayObject);
|
||||
|
||||
|
||||
|
||||
if(textureMask != null){
|
||||
int i = 0;
|
||||
@ -465,7 +475,11 @@ public class Mesh {
|
||||
if(renderPipelineState.getInstanced()){
|
||||
GL45.glDrawElementsInstanced(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0, renderPipelineState.getInstanceCount());
|
||||
} else {
|
||||
GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0);
|
||||
if(useElementArray){
|
||||
GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0);
|
||||
} else {
|
||||
GL11.glDrawArrays(GL_TRIANGLES, 0, elementCount);
|
||||
}
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
@ -548,4 +562,20 @@ public class Mesh {
|
||||
this.boneIdList.add(boneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to use an element array or a direct array
|
||||
* @param useElementArray if true, use elements, else use direct array reference
|
||||
*/
|
||||
public void setUseElementArray(boolean useElementArray){
|
||||
this.useElementArray = useElementArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the mesh uses an element array or a direct array
|
||||
* @return if true, use elements, else use direct array reference
|
||||
*/
|
||||
public boolean getUseElementArray(){
|
||||
return this.useElementArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||