Proper tree transforms

This commit is contained in:
austin 2023-09-09 14:58:13 -04:00
parent 9811c18cf7
commit f113f45317
18 changed files with 1550 additions and 233 deletions

View File

@ -30,6 +30,39 @@
"growthRate" : 0.001
},
"modelPath" : "Models/grass1.fbx"
},
{
"name" : "oak",
"tokens" : [
"TREE",
"REACTS_TO_WIND",
"GROWS_BACK",
"FLAMMABLE"
],
"growthModel": {
"growthRate" : 0.001
},
"treeModel": {
"limbScalarFalloffFactor": 0.3,
"minimumLimbScalar": 0.25,
"maximumLimbDispersion": 0.5,
"minimumLimbDispersion": 0.3,
"minimumNumberForks": 3,
"maximumNumberForks": 5,
"branchHeight": 3,
"centralTrunk": false,
"maximumTrunkSegments": 4,
"maximumBranchSegments": 3,
"maxBranchSegmentFalloffFactor": 1,
"minimumSegmentToSpawnLeaves": 2,
"minBranchHeightToStartSpawningLeaves": 1.5,
"maxBranchHeightToStartSpawningLeaves": 3.01,
"leafIncrement": 0.5,
"minLeavesToSpawnPerPoint": 3,
"maxLeavesToSpawnPerPoint": 5,
"leafDistanceFromCenter": 1.2
},
"modelPath" : "Models/proceduralTree2/proceduralTree2v2.fbx"
}
]

View File

@ -0,0 +1,251 @@
#version 330 core
#define NR_POINT_LIGHTS 10
#define SMALL_EPSILON 0.0001
out vec4 FragColor;
layout (std140) uniform Lights {
// this is how many because we have to align
// bytes it SHOULD in multiples of 16, this
// take it where it ACTUALLY is
//
//refer: https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
//
// base alignment aligned offset
//direct light
vec3 dLDirection; // 16 0
vec3 dLAmbient; // 16 16
vec3 dLDiffuse; // 16 32
vec3 dLSpecular; // 16 48
//point light
vec3 pLposition[NR_POINT_LIGHTS]; // 16*10 64
float pLconstant[NR_POINT_LIGHTS]; // 16*10 224
float pLlinear[NR_POINT_LIGHTS]; // 16*10 384
float pLquadratic[NR_POINT_LIGHTS]; // 16*10 544
vec3 pLambient[NR_POINT_LIGHTS]; // 16*10 704
vec3 pLdiffuse[NR_POINT_LIGHTS]; // 16*10 864
vec3 pLspecular[NR_POINT_LIGHTS]; // 16*10 1024
//for a total size of 1184
};
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
in vec4 FragPosLightSpace;
in vec3 colorShiftValue;
uniform vec3 viewPos;
// uniform DirLight dirLight;
// uniform PointLight pointLights[NR_POINT_LIGHTS];
// uniform SpotLight spotLight;
uniform Material material;
//texture stuff
// uniform sampler2D ourTexture;
uniform int hasTransparency;
// uniform sampler2D specularTexture;
//light depth map
uniform sampler2D shadowMap;
// function prototypes
// vec3 CalcDirLight(vec3 normal, vec3 viewDir);
// vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir);
// vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir);
float calcLightIntensityTotal(vec3 normal);
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
void main(){
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
//grab light intensity
float lightIntensity = calcLightIntensityTotal(norm);
if(texture(material.diffuse, TexCoord).a < SMALL_EPSILON){
discard;
}
//get color of base texture
vec3 textureColor = texture(material.diffuse, TexCoord).rgb;
textureColor = vec3(
textureColor.r * (colorShiftValue.x),
textureColor.g * (colorShiftValue.y),
textureColor.b * (colorShiftValue.z)
);
//shadow
float shadow = ShadowCalculation(FragPosLightSpace, normalize(-dLDirection), norm);
//calculate final color
vec3 finalColor = textureColor * lightIntensity * max(shadow,0.4);
// vec3 lightAmount = CalcDirLight(norm, viewDir);
// for(int i = 0; i < NR_POINT_LIGHTS; i++){
// lightAmount += CalcPointLight(i, norm, FragPos, viewDir);
// }
//this final calculation is for transparency
FragColor = vec4(finalColor, 1);//texture(ourTexture, TexCoord);//vec4(result, 1.0);
}
// calculates the color when using a directional light.
// vec3 CalcDirLight(vec3 normal, vec3 viewDir){
// vec3 lightDir = normalize(-dLDirection);
// // diffuse shading
// float diff = max(dot(normal, lightDir), 0.0);
// // specular shading
// // vec3 reflectDir = reflect(-lightDir, normal);
// // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// // combine results
// vec3 texColor = texture(material.diffuse, TexCoord).rgb;
// vec3 diffuse = dLDiffuse * diff;
// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb);
// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal);
// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular);
// }
//
float calcLightIntensityAmbient(){
//calculate average of ambient light
float avg = (dLAmbient.x + dLAmbient.y + dLAmbient.z)/3.0;
return avg;
}
//
float calcLightIntensityDir(vec3 normal){
vec3 lightDir = normalize(-dLDirection);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
return diff;
}
//
float calcLightIntensityTotal(vec3 normal){
//ambient intensity
float ambientLightIntensity = calcLightIntensityAmbient();
//get direct intensity
float directLightIntensity = calcLightIntensityDir(normal);
//sum
float total = ambientLightIntensity + directLightIntensity;
return total;
}
//
vec3 getTotalLightColor(vec3 normal){
//get the direct light color adjusted for intensity
vec3 diffuseLightColor = dLDiffuse * calcLightIntensityDir(normal);
//sum light colors
vec3 totalLightColor = diffuseLightColor;
return totalLightColor;
}
vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir){
vec3 lightDir = normalize(pLposition[i] - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
// vec3 reflectDir = reflect(-lightDir, normal);
// float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(pLposition[i] - fragPos);
float attenuation = 1.0 / (pLconstant[i] + pLlinear[i] * distance + pLquadratic[i] * (distance * distance));
// combine results
vec3 ambient = pLambient[i];// * vec4(texture(material.diffuse, TexCoord)).xyz;
vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
// vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
ambient *= attenuation;
diffuse *= attenuation;
// specular *= attenuation;
vec3 specular = vec3(0,0,0);
vec3 finalValue = (ambient + diffuse + specular);
finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0));
return finalValue;
}
// // calculates the color when using a point light.
// vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir){
// vec3 lightDir = normalize(pLposition[i] - fragPos);
// // diffuse shading
// float diff = max(dot(normal, lightDir), 0.0);
// // specular shading
// // vec3 reflectDir = reflect(-lightDir, normal);
// // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// // attenuation
// float distance = length(pLposition[i] - fragPos);
// float attenuation = 1.0 / (pLconstant[i] + pLlinear[i] * distance + pLquadratic[i] * (distance * distance));
// // combine results
// vec3 ambient = pLambient[i];// * vec4(texture(material.diffuse, TexCoord)).xyz;
// vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
// // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
// ambient *= attenuation;
// diffuse *= attenuation;
// // specular *= attenuation;
// vec3 specular = vec3(0,0,0);
// vec3 finalValue = (ambient + diffuse + specular);
// finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0));
// return finalValue;
// }
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){
// perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
//transform to NDC
projCoords = projCoords * 0.5 + 0.5;
//get closest depth from light's POV
float closestDepth = texture(shadowMap, projCoords.xy).r;
//get depth of current fragment
float currentDepth = projCoords.z;
//calculate bias
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
//calculate shadow value
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
if(projCoords.z > 1.0){
shadow = 0.0;
}
//calculate dot product, if it is >0 we know they're parallel-ish therefore should disregard the shadow mapping
//ie the fragment is already facing away from the light source
float dotprod = dot(normalize(lightDir),normalize(normal));
if(dotprod > 0.0){
shadow = 0.0;
}
// shadow = currentDepth;
return shadow;
}

View File

@ -0,0 +1,62 @@
//Vertex Shader
#version 330 core
//input buffers
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec4 aWeights;
layout (location = 4) in vec2 aTex;
layout (location = 5) in vec4 modelA;
layout (location = 6) in vec4 modelB;
layout (location = 7) in vec4 modelC;
layout (location = 8) in vec4 modelD;
layout (location = 9) in vec3 colorShift;
//coordinate space transformation matrices
uniform mat4 transform;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightSpaceMatrix;
//output buffers
out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoord;
out vec4 FragPosLightSpace;
out vec3 colorShiftValue;
void main() {
mat4 model = mat4(modelA,modelB,modelC,modelD);
colorShiftValue = colorShift;
//normalize posiiton and normal
vec4 FinalVertex = vec4(aPos, 1.0);
vec4 FinalNormal = vec4(aNormal, 1.0);
//make sure the W component is 1.0
FinalVertex = vec4(FinalVertex.xyz, 1.0);
FinalNormal = vec4(FinalNormal.xyz, 1.0);
//push frag, normal, and texture positions to fragment shader
FragPos = vec3(model * FinalVertex);
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoord = aTex;
//shadow map stuff
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
//set final position with opengl space
gl_Position = projection * view * model * FinalVertex;
}

View File

@ -0,0 +1,245 @@
#version 330 core
#define NR_POINT_LIGHTS 10
#define SMALL_EPSILON 0.0001
out vec4 FragColor;
layout (std140) uniform Lights {
// this is how many because we have to align
// bytes it SHOULD in multiples of 16, this
// take it where it ACTUALLY is
//
//refer: https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
//
// base alignment aligned offset
//direct light
vec3 dLDirection; // 16 0
vec3 dLAmbient; // 16 16
vec3 dLDiffuse; // 16 32
vec3 dLSpecular; // 16 48
//point light
vec3 pLposition[NR_POINT_LIGHTS]; // 16*10 64
float pLconstant[NR_POINT_LIGHTS]; // 16*10 224
float pLlinear[NR_POINT_LIGHTS]; // 16*10 384
float pLquadratic[NR_POINT_LIGHTS]; // 16*10 544
vec3 pLambient[NR_POINT_LIGHTS]; // 16*10 704
vec3 pLdiffuse[NR_POINT_LIGHTS]; // 16*10 864
vec3 pLspecular[NR_POINT_LIGHTS]; // 16*10 1024
//for a total size of 1184
};
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
in vec4 FragPosLightSpace;
uniform vec3 viewPos;
// uniform DirLight dirLight;
// uniform PointLight pointLights[NR_POINT_LIGHTS];
// uniform SpotLight spotLight;
uniform Material material;
//texture stuff
// uniform sampler2D ourTexture;
uniform int hasTransparency;
// uniform sampler2D specularTexture;
//light depth map
uniform sampler2D shadowMap;
// function prototypes
// vec3 CalcDirLight(vec3 normal, vec3 viewDir);
// vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir);
// vec3 CalcSpotLight(vec3 normal, vec3 fragPos, vec3 viewDir);
float calcLightIntensityTotal(vec3 normal);
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
void main(){
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
//grab light intensity
float lightIntensity = calcLightIntensityTotal(norm);
if(texture(material.diffuse, TexCoord).a < SMALL_EPSILON){
discard;
}
//get color of base texture
vec3 textureColor = texture(material.diffuse, TexCoord).rgb;
//shadow
float shadow = ShadowCalculation(FragPosLightSpace, normalize(-dLDirection), norm);
//calculate final color
vec3 finalColor = textureColor * lightIntensity * max(shadow,0.4);
// vec3 lightAmount = CalcDirLight(norm, viewDir);
// for(int i = 0; i < NR_POINT_LIGHTS; i++){
// lightAmount += CalcPointLight(i, norm, FragPos, viewDir);
// }
//this final calculation is for transparency
FragColor = vec4(finalColor, 1);//texture(ourTexture, TexCoord);//vec4(result, 1.0);
}
// calculates the color when using a directional light.
// vec3 CalcDirLight(vec3 normal, vec3 viewDir){
// vec3 lightDir = normalize(-dLDirection);
// // diffuse shading
// float diff = max(dot(normal, lightDir), 0.0);
// // specular shading
// // vec3 reflectDir = reflect(-lightDir, normal);
// // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// // combine results
// vec3 texColor = texture(material.diffuse, TexCoord).rgb;
// vec3 diffuse = dLDiffuse * diff;
// //vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoord).rgb);
// float shadow = ShadowCalculation(FragPosLightSpace, lightDir, normal);
// return ( dLAmbient + (1.0-shadow) * diffuse ) * texColor;// + specular);
// }
//
float calcLightIntensityAmbient(){
//calculate average of ambient light
float avg = (dLAmbient.x + dLAmbient.y + dLAmbient.z)/3.0;
return avg;
}
//
float calcLightIntensityDir(vec3 normal){
vec3 lightDir = normalize(-dLDirection);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
return diff;
}
//
float calcLightIntensityTotal(vec3 normal){
//ambient intensity
float ambientLightIntensity = calcLightIntensityAmbient();
//get direct intensity
float directLightIntensity = calcLightIntensityDir(normal);
//sum
float total = ambientLightIntensity + directLightIntensity;
return total;
}
//
vec3 getTotalLightColor(vec3 normal){
//get the direct light color adjusted for intensity
vec3 diffuseLightColor = dLDiffuse * calcLightIntensityDir(normal);
//sum light colors
vec3 totalLightColor = diffuseLightColor;
return totalLightColor;
}
vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir){
vec3 lightDir = normalize(pLposition[i] - fragPos);
// diffuse shading
float diff = max(dot(normal, lightDir), 0.0);
// specular shading
// vec3 reflectDir = reflect(-lightDir, normal);
// float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// attenuation
float distance = length(pLposition[i] - fragPos);
float attenuation = 1.0 / (pLconstant[i] + pLlinear[i] * distance + pLquadratic[i] * (distance * distance));
// combine results
vec3 ambient = pLambient[i];// * vec4(texture(material.diffuse, TexCoord)).xyz;
vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
// vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
ambient *= attenuation;
diffuse *= attenuation;
// specular *= attenuation;
vec3 specular = vec3(0,0,0);
vec3 finalValue = (ambient + diffuse + specular);
finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0));
return finalValue;
}
// // calculates the color when using a point light.
// vec3 CalcPointLight(int i, vec3 normal, vec3 fragPos, vec3 viewDir){
// vec3 lightDir = normalize(pLposition[i] - fragPos);
// // diffuse shading
// float diff = max(dot(normal, lightDir), 0.0);
// // specular shading
// // vec3 reflectDir = reflect(-lightDir, normal);
// // float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// // attenuation
// float distance = length(pLposition[i] - fragPos);
// float attenuation = 1.0 / (pLconstant[i] + pLlinear[i] * distance + pLquadratic[i] * (distance * distance));
// // combine results
// vec3 ambient = pLambient[i];// * vec4(texture(material.diffuse, TexCoord)).xyz;
// vec3 diffuse = pLdiffuse[i] * diff;// * vec4(texture(material.diffuse, TexCoord)).xyz;
// // vec3 specular = pLspecular[i] * spec;// * vec4(texture(material.specular, TexCoord)).xyz;
// ambient *= attenuation;
// diffuse *= attenuation;
// // specular *= attenuation;
// vec3 specular = vec3(0,0,0);
// vec3 finalValue = (ambient + diffuse + specular);
// finalValue = vec3(max(finalValue.x,0),max(finalValue.y,0),max(finalValue.z,0));
// return finalValue;
// }
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){
// perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
//transform to NDC
projCoords = projCoords * 0.5 + 0.5;
//get closest depth from light's POV
float closestDepth = texture(shadowMap, projCoords.xy).r;
//get depth of current fragment
float currentDepth = projCoords.z;
//calculate bias
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
//calculate shadow value
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
if(projCoords.z > 1.0){
shadow = 0.0;
}
//calculate dot product, if it is >0 we know they're parallel-ish therefore should disregard the shadow mapping
//ie the fragment is already facing away from the light source
float dotprod = dot(normalize(lightDir),normalize(normal));
if(dotprod > 0.0){
shadow = 0.0;
}
// shadow = currentDepth;
return shadow;
}

View File

@ -0,0 +1,60 @@
//Vertex Shader
#version 330 core
//input buffers
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec4 aWeights;
layout (location = 4) in vec2 aTex;
layout (location = 5) in vec4 modelA;
layout (location = 6) in vec4 modelB;
layout (location = 7) in vec4 modelC;
layout (location = 8) in vec4 modelD;
//coordinate space transformation matrices
uniform mat4 transform;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightSpaceMatrix;
//output buffers
out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoord;
out vec4 FragPosLightSpace;
void main() {
mat4 model = mat4(modelA,modelB,modelC,modelD);
//normalize posiiton and normal
vec4 FinalVertex = vec4(aPos, 1.0);
vec4 FinalNormal = vec4(aNormal, 1.0);
//make sure the W component is 1.0
FinalVertex = vec4(FinalVertex.xyz, 1.0);
FinalNormal = vec4(FinalNormal.xyz, 1.0);
//push frag, normal, and texture positions to fragment shader
FragPos = vec3(model * FinalVertex);
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoord = aTex;
//shadow map stuff
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
//set final position with opengl space
gl_Position = projection * view * model * FinalVertex;
}

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Sun Aug 20 22:06:17 EDT 2023
buildNumber=10
#Sat Sep 02 19:48:14 EDT 2023
buildNumber=11

View File

@ -240,7 +240,10 @@ public class ClientLoading {
});
// for(int i = 0; i < 6; i++){
Entity tree = ProceduralTree.clientGenerateProceduralTree("asdf", 0);
Entity tree = ProceduralTree.clientGenerateProceduralTree("oak", 0);
EntityUtils.getPosition(tree).set(5,0,5);
EntityUtils.getScale(tree).set(0.5f);
EntityUtils.getRotation(tree).rotateLocalX(0.5);
// EntityUtils.getPosition(tree).set(5,0,i * 3);
// }

View File

@ -171,6 +171,7 @@ public class EntityDataStrings {
public static final String ATTACH_CHILDREN_LIST = "attachChildrenList";
public static final String ATTACH_ROTATION_OFFSET = "attachRotationOffset";
public static final String ATTACH_POSITION_OFFSET = "attachPositionOffset";
public static final String ATTACH_TRANSFORM = "attachTransform";
/*
Item Entity

View File

@ -3,6 +3,7 @@ package electrosphere.entity;
public class EntityTags {
public static final String BONE_ATTACHED = "boneAttached";
public static final String TRANSFORM_ATTACHED = "transformAttached";
public static final String COLLIDABLE = "collidable";
public static final String SPRINTABLE = "sprintable";
public static final String MOVEABLE = "moveable";

View File

@ -45,6 +45,7 @@ public class Scene {
tagEntityMap.put(EntityTags.ITEM, new HashSet<Entity>());
tagEntityMap.put(EntityTags.GRAVITY, new HashSet<Entity>());
tagEntityMap.put(EntityTags.PARTICLE, new HashSet<Entity>());
tagEntityMap.put(EntityTags.TRANSFORM_ATTACHED, new HashSet<Entity>());
}
/**

View File

@ -14,48 +14,30 @@ import electrosphere.server.datacell.utils.ServerEntityTagUtils;
import java.util.LinkedList;
import java.util.List;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
/**
*
* @author amaterasu
*/
public class AttachUtils {
public static void serverAttachEntityToEntityAtBone(Entity parent, Entity toAttach, String boneName, Quaterniond rotation){
ServerEntityTagUtils.attachTagToEntity(toAttach, EntityTags.BONE_ATTACHED);
toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
toAttach.putData(EntityDataStrings.ATTACH_PARENT, parent);
toAttach.putData(EntityDataStrings.ATTACH_TARGET_BONE, boneName);
toAttach.putData(EntityDataStrings.ATTACH_ROTATION_OFFSET, rotation);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(toAttach);
} else {
LinkedList<Entity> childrenEntities = new LinkedList<Entity> ();
childrenEntities.add(toAttach);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
public static void clientAttachEntityToEntityAtBone(Entity parent, Entity toAttach, String boneName, Quaterniond rotation){
Globals.clientSceneWrapper.getScene().registerEntityToTag(toAttach, EntityTags.BONE_ATTACHED);
toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
toAttach.putData(EntityDataStrings.ATTACH_PARENT, parent);
toAttach.putData(EntityDataStrings.ATTACH_TARGET_BONE, boneName);
toAttach.putData(EntityDataStrings.ATTACH_ROTATION_OFFSET, rotation);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(toAttach);
} else {
LinkedList<Entity> childrenEntities = new LinkedList<Entity> ();
childrenEntities.add(toAttach);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
//
// FUNCTIONS TO UPDATE ATTACHMENTS FOR CURRENT FRAME
//
public static void serverUpdateAttachedEntityPositions(ServerDataCell cell){
for(Entity currentEntity : cell.getScene().getEntitiesWithTag(EntityTags.BONE_ATTACHED)){
@ -90,7 +72,11 @@ public class AttachUtils {
}
}
/**
* Client version of attachment update functions
*/
public static void clientUpdateAttachedEntityPositions(){
//update entities attached to bones of other entities
for(Entity currentEntity : Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.BONE_ATTACHED)){
Entity parent;
if((parent = (Entity)currentEntity.getData(EntityDataStrings.ATTACH_PARENT))!=null){
@ -125,28 +111,103 @@ public class AttachUtils {
EntityUtils.getPosition(currentEntity).set(new Vector3d(parentPosition).add(positionOffset));
}
}
}
public static void serverDetatchEntityFromEntityAtBone(Entity parent, Entity toAttach){
ServerEntityTagUtils.removeTagFromEntity(toAttach, EntityTags.BONE_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_PARENT);
toAttach.removeData(EntityDataStrings.ATTACH_TARGET_BONE);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).remove(toAttach);
Matrix4d parentTransform = new Matrix4d().identity();
//update entities attached to centerpoint + transform of other entities
for(Entity currentEntity : Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.TRANSFORM_ATTACHED)){
Entity parent;
if((parent = (Entity)currentEntity.getData(EntityDataStrings.ATTACH_PARENT))!=null){
Matrix4f transform;
if((transform = getTransformOffset(currentEntity))!=null){
//parent objects
Vector3d parentPosition = EntityUtils.getPosition(parent);
Quaterniond parentRotation = EntityUtils.getRotation(parent);
Vector3f parentScale = EntityUtils.getScale(parent);
// calculate new transform for current entity
parentTransform.identity()
.translate(parentPosition)
.rotate(parentRotation)
.scale(parentScale.x,parentScale.y,parentScale.z)
.mul(transform);
//transform bone space
Vector4d positionRaw = parentTransform.transform(new Vector4d(0,0,0,1));
Vector3d position = new Vector3d(positionRaw.x,positionRaw.y,positionRaw.z);
Quaterniond rotation = parentTransform.getUnnormalizedRotation(new Quaterniond()).normalize();
Vector3d scaleRaw = parentTransform.getScale(new Vector3d());
Vector3f scale = new Vector3f((float)scaleRaw.x,(float)scaleRaw.y,(float)scaleRaw.z);
//transform worldspace
// position.add(new Vector3d(EntityUtils.getPosition(parent)));
//set
EntityUtils.getPosition(currentEntity).set(position);
EntityUtils.getRotation(currentEntity).set(rotation);
EntityUtils.getScale(currentEntity).set(scale);
}
}
}
}
public static void clientDetatchEntityFromEntityAtBone(Entity parent, Entity toAttach){
Globals.clientSceneWrapper.getScene().removeEntityFromTag(toAttach, EntityTags.BONE_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_PARENT);
toAttach.removeData(EntityDataStrings.ATTACH_TARGET_BONE);
//
// FUNCTIONS TO ATTACH AN ENTITY
//
public static void serverAttachEntityToEntityAtBone(Entity parent, Entity toAttach, String boneName, Quaterniond rotation){
ServerEntityTagUtils.attachTagToEntity(toAttach, EntityTags.BONE_ATTACHED);
toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
toAttach.putData(EntityDataStrings.ATTACH_PARENT, parent);
toAttach.putData(EntityDataStrings.ATTACH_TARGET_BONE, boneName);
toAttach.putData(EntityDataStrings.ATTACH_ROTATION_OFFSET, rotation);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).remove(toAttach);
getChildrenList(parent).add(toAttach);
} else {
LinkedList<Entity> childrenEntities = new LinkedList<Entity> ();
childrenEntities.add(toAttach);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
public static void clientAttachEntityToEntityAtBone(Entity parent, Entity toAttach, String boneName, Quaterniond rotation){
Globals.clientSceneWrapper.getScene().registerEntityToTag(toAttach, EntityTags.BONE_ATTACHED);
toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
toAttach.putData(EntityDataStrings.ATTACH_PARENT, parent);
toAttach.putData(EntityDataStrings.ATTACH_TARGET_BONE, boneName);
toAttach.putData(EntityDataStrings.ATTACH_ROTATION_OFFSET, rotation);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(toAttach);
} else {
LinkedList<Entity> childrenEntities = new LinkedList<Entity> ();
childrenEntities.add(toAttach);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
/**
* Attaches an entity to another based on the parent's absolute position in the game engine
* @param parent The parent to attach to
@ -169,7 +230,130 @@ public class AttachUtils {
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
/**
* Attaches an entity such that a transform will be applied to it relative to the parent's position and rotation every frame
* @param parent The parent entity
* @param toAttach The child entity
* @param transform The transform
*/
public static void clientAttachEntityAtTransform(Entity parent, Entity toAttach, Matrix4f transform){
Globals.clientSceneWrapper.getScene().registerEntityToTag(toAttach, EntityTags.TRANSFORM_ATTACHED);
toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
toAttach.putData(EntityDataStrings.ATTACH_PARENT, parent);
toAttach.putData(EntityDataStrings.ATTACH_TARGET_BASE, true);
toAttach.putData(EntityDataStrings.ATTACH_TRANSFORM, transform);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(toAttach);
} else {
LinkedList<Entity> childrenEntities = new LinkedList<Entity> ();
childrenEntities.add(toAttach);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
/**
* Semantically attaches an entity to another entity
* @param parent The parent entity
* @param child The child entity
*/
public static void attachEntityToEntity(Entity parent, Entity child){
child.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
child.putData(EntityDataStrings.ATTACH_PARENT, parent);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(child);
} else {
List<Entity> childrenEntities = new LinkedList<Entity>();
childrenEntities.add(child);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
//
// FUNCTIONS TO DETATCH AN ENTITY
//
public static void serverDetatchEntityFromEntityAtBone(Entity parent, Entity toAttach){
ServerEntityTagUtils.removeTagFromEntity(toAttach, EntityTags.BONE_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_PARENT);
toAttach.removeData(EntityDataStrings.ATTACH_TARGET_BONE);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).remove(toAttach);
}
}
public static void clientDetatchEntityFromEntityAtBone(Entity parent, Entity toAttach){
Globals.clientSceneWrapper.getScene().removeEntityFromTag(toAttach, EntityTags.BONE_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
toAttach.removeData(EntityDataStrings.ATTACH_PARENT);
toAttach.removeData(EntityDataStrings.ATTACH_TARGET_BONE);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).remove(toAttach);
}
}
//
// GETTERS
//
public static boolean isAttached(Entity e){
return e.containsKey(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
}
@ -185,6 +369,15 @@ public class AttachUtils {
protected static Quaterniond getRotationOffset(Entity e){
return (Quaterniond)e.getData(EntityDataStrings.ATTACH_ROTATION_OFFSET);
}
/**
* Gets the transform for a transform attached entity
* @param e The entity
* @return The transform if it exists, false otherwise
*/
protected static Matrix4f getTransformOffset(Entity e){
return (Matrix4f)e.getData(EntityDataStrings.ATTACH_TRANSFORM);
}
public static boolean hasChildren(Entity e){
return e.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST) && !getChildrenList(e).isEmpty();
@ -197,18 +390,6 @@ public class AttachUtils {
public static LinkedList<Entity> getChildrenList(Entity e){
return (LinkedList<Entity>)e.getData(EntityDataStrings.ATTACH_CHILDREN_LIST);
}
public static void attachEntityToEntity(Entity parent, Entity child){
child.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true);
child.putData(EntityDataStrings.ATTACH_PARENT, parent);
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
getChildrenList(parent).add(child);
} else {
List<Entity> childrenEntities = new LinkedList<Entity>();
childrenEntities.add(child);
parent.putData(EntityDataStrings.ATTACH_CHILDREN_LIST, childrenEntities);
}
}
public static Quaterniond getEquipPointRotationOffset(List<Float> values){
return new Quaterniond(values.get(0),values.get(1),values.get(2),values.get(3));

View File

@ -21,6 +21,8 @@ import electrosphere.entity.state.BehaviorTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.instance.InstanceTemplate;
import electrosphere.entity.types.instance.InstancedEntityUtils;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.game.data.foliage.type.TreeModel;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
@ -33,6 +35,9 @@ public class ProceduralTree {
//The instance template for the branch
static InstanceTemplate branchInstanceTemplate;
//the instance template for a leaf blob
static InstanceTemplate leafInstanceTemplate;
//The model attribute
static final ShaderAttribute modelMatrixAttribute = new ShaderAttribute(new int[]{
5,6,7,8
@ -43,8 +48,11 @@ public class ProceduralTree {
9,10,11,12
});
//the size of the base of the branch segment, attribute
static final ShaderAttribute baseSizeAttribute = new ShaderAttribute(13);
static final ShaderAttribute leafColorAttribute = new ShaderAttribute(9);
//The static setup logic
static {
//create map of attributes and register them
@ -56,25 +64,30 @@ public class ProceduralTree {
branchInstanceTemplate = InstanceTemplate.createInstanceTemplate(
1000,
"Models/proceduralTree2/proceduralTree2v2.fbx",
"Shaders/proceduraltree/proceduraltree.vs",
"Shaders/proceduraltree/proceduraltree.fs",
"Shaders/instanced/proceduraltree/proceduraltree.vs",
"Shaders/instanced/proceduraltree/proceduraltree.fs",
attributes);
Map<ShaderAttribute,HomogenousBufferTypes> leafAttributes = new HashMap<ShaderAttribute,HomogenousBufferTypes>();
leafAttributes.put(modelMatrixAttribute,HomogenousBufferTypes.MAT4F);
leafAttributes.put(leafColorAttribute, HomogenousBufferTypes.VEC3F);
leafInstanceTemplate = InstanceTemplate.createInstanceTemplate(
10000,
"Models/foliageBlockTemplate1Test1.fbx",
"Shaders/instanced/colorshift/colorshift.vs",
"Shaders/instanced/colorshift/colorshift.fs",
leafAttributes
);
}
//TODO: make variable based on tree type
static final int MAX_HEIGHT_SEGMENTS = 4;
static final int MIN_HEIGHT_SEGMENTS = 4;
/**
* Client side function to generate a tree
* @param type
* @param seed
* @return
* @param type The type of tree as a string
* @param seed The seed (lol) for the tree
* @return The top level tree entity
*/
public static Entity clientGenerateProceduralTree(String type, long seed){
Random treeRandom = new Random(seed);
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
//generate trunk
Entity trunkChild = EntityCreationUtils.createClientSpatialEntity();
@ -83,189 +96,168 @@ public class ProceduralTree {
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
instancedActor.setAttribute(baseSizeAttribute, 1.0f);
// EntityCreationUtils.makeEntityDrawable(trunkChild, "Models/proceduralTree2/proceduralTree2.fbx");
AttachUtils.clientAttachEntityAtCurrentOffset(rVal, trunkChild);
//generate branches
Quaterniond currentRotation = new Quaterniond(0,0,0,1).rotateLocalX(0).rotateLocalZ(0).normalize();
// clientGenerateBranches(type,trunkChild,treeRandom,new Vector3d(0,3.0,0),currentRotation,1);
Matrix4f transform = new Matrix4f().identity().translate(0,3,0);
clientGenerateBranches(type,trunkChild,treeRandom,transform,1);
//call recursive branching routine to generate branches from trunk + leaf blobs
//...
// clientGenerateBranches(type, rVal, treeRandom, new Vector3d(0,heightSegments,0), new Quaterniond(0,0.7071068,0,0.7071068), heightSegments);
int topLayerLeavesCount = treeRandom.nextInt(3) + 3;
float topLayerRotationOffset = treeRandom.nextFloat();
// for(int i = 0; i < topLayerLeavesCount; i++){
// Entity leafBlock = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(leafBlock, "Models/foliageBlockTemplate1Test1.fbx");
// float radius = 1;
// double x = radius * Math.cos(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// double z = radius * Math.sin(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// EntityUtils.getPosition(leafBlock).set(x,20,z);
// DrawableUtils.makeEntityTransparent(leafBlock);
// }
int layerCount = 8;
int halfLayer = layerCount / 2;
float maxRadius = 2f;
float spacingBetweenLayers = 1;
float layerDensityDecreaseRate = 3;
int initialDensity = 20;
float startingHeight = 4;
// for(int leafLayer = 0; leafLayer < layerCount; leafLayer++){
// topLayerLeavesCount = treeRandom.nextInt(3) + 3 * (int)(halfLayer * ((halfLayer - Math.abs(leafLayer - halfLayer))/(float)halfLayer));
// topLayerRotationOffset = treeRandom.nextFloat();
// for(int i = 0; i < topLayerLeavesCount; i++){
// Entity leafBlock = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(leafBlock, "Models/foliageBlockTemplate1Test1.fbx");
// float radius = maxRadius * ((float)Math.sin(Math.PI * ((halfLayer - Math.abs(leafLayer - halfLayer))/(float)halfLayer) / 2.0f));
// double x = radius * Math.cos(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// double z = radius * Math.sin(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// EntityUtils.getPosition(leafBlock).set(x,20 - leafLayer,z);
// DrawableUtils.makeEntityTransparent(leafBlock);
// }
// }
// for(int leafLayer = 0; leafLayer < layerCount; leafLayer++){
// topLayerLeavesCount = initialDensity - (int)(layerDensityDecreaseRate * leafLayer);
// topLayerRotationOffset = treeRandom.nextFloat();
// for(int i = 0; i < topLayerLeavesCount; i++){
// Entity leafBlock = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(leafBlock, "Models/foliageBlockTemplate1Test1.fbx");
// float radius = maxRadius * (1.0f - (leafLayer / (float)layerCount));
// double x = radius * Math.cos(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// double z = radius * Math.sin(i * (Math.PI * 2.0 / (float)topLayerLeavesCount) + topLayerRotationOffset);
// EntityUtils.getPosition(leafBlock).set(x,startingHeight + leafLayer * spacingBetweenLayers,z);
// EntityUtils.getScale(leafBlock).set(1);
// Quaterniond rotation = EntityUtils.getRotation(leafBlock);
// rotation.rotateLocalX(treeRandom.nextFloat());
// rotation.rotateLocalY(treeRandom.nextFloat());
// rotation.rotateLocalZ(treeRandom.nextFloat());
// // Globals.clientSceneWrapper.getScene().registerBehaviorTree(new BehaviorTree() {
// // double initialX = rotation.x;
// // double initialY = rotation.y;
// // double initialZ = rotation.z;
// // public void simulate(float deltaTime) {
// // Quaterniond rotation = EntityUtils.getRotation(leafBlock);
// // rotation.rotateLocalX(
// // treeRandom.nextFloat() * 0.001f
// // );
// // rotation.rotateLocalY(treeRandom.nextFloat() * 0.001f);
// // rotation.rotateLocalZ(treeRandom.nextFloat() * 0.001f);
// // }});
// }
// }
Matrix4f transform = new Matrix4f().identity().translate(0,3,0);
FoliageType foliageType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(type);
TreeModel treeModel = foliageType.getTreeModel();
// clientGenerateBranches(
// treeModel,
// trunkChild,
// treeRandom,
// transform,
// 1,
// 1,
// true
// );
clientGenerateBranchesAlt(
treeModel,
trunkChild,
treeRandom,
new Vector3d(0,0,0),
new Quaterniond(0,0,0,1),
new Vector3d(0,3,0),
new Quaternionf(0,0,0,1),
1,
1,
true
);
//attach btress
//..attach wind
//...
return rVal;
return trunkChild;
}
// public static void clientGenerateBranches(String type, Entity parent, Random rand, Vector3d currentPosition, Quaterniond currentAbsoluteRotation, float scalar){
// float scalarFalloffFactor = 0.15f;
// if(scalar > 0.25){
// //how much does it peel off of the current vector
// double peelRotation = (rand.nextFloat() * 0.4 + 0.1);
// //the initial rotation around Y that the branch will peel towards
// double offsetRotation = rand.nextFloat();
// int branchNum = rand.nextInt(2) + 2;
// for(int i = 0; i < 1; i++){
// //get new rotation
// double pitchFactor = Math.sin(offsetRotation);
// double rollFactor = Math.cos(offsetRotation);
// //update offsetrotation
// offsetRotation = offsetRotation + i * 2.0 * Math.PI / (float)branchNum;
public static void clientGenerateBranchesAlt(
TreeModel type,
Entity parent,
Random rand,
Vector3d parentPosition, // The parent's origin bone's position in space
Quaterniond parentRotation, // The parent's origin bone's rotation
Vector3d offsetFromParent, // The offset from the parent's origin bone that this branch's origin bone should be at
Quaternionf rotationFromParent, // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch
float scalar,
int currentSegmentNumber,
boolean isCentralTrunk
){
//how fast do the branches shrink in size
float scalarFalloffFactor = type.getLimbScalarFalloffFactor();
//the minimum branch size before we stop generating branch segments/trunk segments
float minimumScalar = type.getMinimumLimbScalar();
//how high is the model for a single branch segment
float treeSegmentHeight = type.getBranchHeight();
//how much to spread the branches along the current segment
float minimumSegmentDispersion = type.getMinimumLimbDispersion();
float dispersionSpread = type.getMaximumLimbDispersion() - type.getMinimumLimbDispersion();
//the number of branches to make per segment
int minBranches = type.getMinimumNumberForks();
int maxBranches = type.getMaximumNumberForks();
//the maximum number of segments in an single arc for both trunk and branches
int maximumTrunkSegments = type.getMaximumTrunkSegments();
int maximumBranchSegments = type.getMaximumBranchSegments();
if(scalar > minimumScalar && currentSegmentNumber < maximumBranchSegments){
int minimumSegmentToSpawnLeaves = type.getMinimumSegmentToSpawnLeaves();
// //the rotation applied to the bone
// Quaternionf boneRotation = new Quaternionf(0,0,0,1).rotateLocalX((float)(pitchFactor * peelRotation)).rotateLocalZ((float)(rollFactor * peelRotation)).normalize();
//how much does it peel off of the current vector
double peelRotation = (rand.nextFloat() * dispersionSpread + minimumSegmentDispersion);
//the initial rotation around Y that the branch will peel towards
double offsetRotation = 0;
double rotationInitialOffset = rand.nextFloat();
int branchNum = rand.nextInt(maxBranches - minBranches) + minBranches;
for(int i = 0; i < branchNum; i++){
// //The new absolute rotation at the end of the bone
// Quaterniond newAbsoluteRotation = new Quaterniond(currentAbsoluteRotation).mul(new Quaterniond(boneRotation.x,boneRotation.y,boneRotation.z,boneRotation.w)).normalize();
//what we want to solve for:
//get parent position + rotation
//get an offset from the parent position for this position
//get a rotation from the parent rotation for this rotation
//get an offset from the current position for the child position
//get a rotation from the current rotation for the child rotation
//calculate transform from parent entity
//this is the transform that will be applied every time the attachutils updates
Matrix4f transformFromParent = new Matrix4f()
.translate(new Vector3f(
(float)offsetFromParent.x,
(float)offsetFromParent.y,
(float)offsetFromParent.z
))
.rotate(new Quaternionf(
(float)rotationFromParent.x,
(float)rotationFromParent.y,
(float)rotationFromParent.z,
(float)rotationFromParent.w
));
//calculate combined transform
Matrix4f combinedTransform = new Matrix4f().translate(new Vector3f(
(float)parentPosition.x,
(float)parentPosition.y,
(float)parentPosition.z
)).rotate(new Quaternionf(
(float)parentRotation.x,
(float)parentRotation.y,
(float)parentRotation.z,
(float)parentRotation.w
)).mul(transformFromParent);
//calculate current branch's stuff
//get current position
Vector4f currentPositionf = combinedTransform.transform(new Vector4f(
0,
0,
0,
1
));
Vector3d currentPosition = new Vector3d(currentPositionf.x,currentPositionf.y,currentPositionf.z);
//The new absolute rotation at the end of the bone
Quaterniond currentAbsoluteRotation = combinedTransform.getNormalizedRotation(new Quaterniond()).normalize();
// //calculates the bone transform matrix
// Matrix4f transform = new Matrix4f().identity().rotate(boneRotation).scale(scalar - scalarFalloffFactor,1,scalar - scalarFalloffFactor);
// Matrix4f newPositionTransform = new Matrix4f().identity().rotate(new Quaternionf(
// (float)currentAbsoluteRotation.x,
// (float)currentAbsoluteRotation.y,
// (float)currentAbsoluteRotation.z,
// (float)currentAbsoluteRotation.w
// )).rotate(boneRotation).scale(scalar - scalarFalloffFactor,1,scalar - scalarFalloffFactor);
// //create entity
// Entity branch = EntityCreationUtils.createClientSpatialEntity();
// InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(branch, branchInstanceTemplate, modelMatrixAttribute);
// instancedActor.setAttribute(boneMatrixAttribute, transform);
// instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
// //debug output
// Vector4f transformLocal = newPositionTransform.transform(new Vector4f(0,3,0,1));
// Vector3d transformedPosition = new Vector3d(currentPosition).add(transformLocal.x,transformLocal.y,transformLocal.z);
// //set entity stuuff
// EntityUtils.getPosition(branch).set(currentPosition);
// EntityUtils.getScale(branch).set(scalar,1,scalar);
// EntityUtils.getRotation(branch).set(currentAbsoluteRotation);
// Vector3d newPosition = new Vector3d(
// transformLocal.x,
// transformLocal.y,
// transformLocal.z
// ).add(currentPosition.x,currentPosition.y,currentPosition.z);
// AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch);
// //debug stuff
// Entity debugSphere = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(debugSphere, "Models/unitsphere_1.fbx");
// EntityUtils.getScale(debugSphere).set(0.5f);
// EntityUtils.getPosition(debugSphere).set(newPosition);
// System.out.println(transformedPosition);
// System.out.println("vs");
// System.out.println(newPosition);
// System.out.println();
//calculate child stuff
// clientGenerateBranches(type, branch, rand, newPosition, newAbsoluteRotation, scalar - scalarFalloffFactor);
// }
// }
// }
public static void clientGenerateBranches(String type, Entity parent, Random rand, Matrix4f transform, float scalar){
float scalarFalloffFactor = 0.15f;
if(scalar > 0.25){
//how much does it peel off of the current vector
double peelRotation = (rand.nextFloat() * 0.4 + 0.1);
//the initial rotation around Y that the branch will peel towards
double offsetRotation = rand.nextFloat();
int branchNum = rand.nextInt(2) + 2;
for(int i = 0; i < branchNum; i++){
//update offsetrotation
offsetRotation = rotationInitialOffset + (i + 1) * (2.0 * Math.PI / (float)branchNum);
//get new rotation
double pitchFactor = Math.sin(offsetRotation);
double rollFactor = Math.cos(offsetRotation);
//update offsetrotation
offsetRotation = offsetRotation + (i + 1) * 2.0 * Math.PI / (float)branchNum;
//the rotation applied to the bone
Quaternionf boneRotation = new Quaternionf(0,0,0,1).rotateLocalX((float)(pitchFactor * peelRotation)).rotateLocalZ((float)(rollFactor * peelRotation)).normalize();
//get current position
Vector4f currentPositionf = transform.transform(new Vector4f(0,0,0,1));
Vector3d currentPosition = new Vector3d(currentPositionf.x,currentPositionf.y,currentPositionf.z);
//The new absolute rotation at the end of the bone
Quaterniond currentAbsoluteRotation = transform.getNormalizedRotation(new Quaterniond()).normalize();
// Quaterniond newAbsoluteRotation = new Quaterniond(currentAbsoluteRotation).mul(new Quaterniond(boneRotation.x,boneRotation.y,boneRotation.z,boneRotation.w)).normalize();
//calculates the bone transform matrix
Matrix4f boneTransform = new Matrix4f().identity().rotate(boneRotation);
//calculate attachment transform
// Matrix4f attachmentTransform = new Matrix4f().identity().rotate(new Quaternionf(
// (float)currentAbsoluteRotation.x,
// (float)currentAbsoluteRotation.y,
// (float)currentAbsoluteRotation.z,
// (float)currentAbsoluteRotation.w
// )).translate(0,treeSegmentHeight,0);
//new position transform
Matrix4f newPositionTransform = new Matrix4f(transform).mul(boneTransform).translate(0,3,0);
Matrix4f newPositionTransform = new Matrix4f().rotate(boneRotation).translate(0,treeSegmentHeight,0);
Vector4f newPositionRaw = newPositionTransform.transform(new Vector4f(0,0,0,1));
Vector3d newPosition = new Vector3d(newPositionRaw.x,newPositionRaw.y,newPositionRaw.z);
//get new scalar
float newScalar = scalar - scalarFalloffFactor;
@ -278,28 +270,283 @@ public class ProceduralTree {
instancedActor.setAttribute(baseSizeAttribute, scalar);
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
//debug output
// Vector4f newPositionF = newPositionTransform.transform(new Vector4f(0,0,0,1));
// Vector3d newAbsolutePosition = new Vector3d(newPositionF.x,newPositionF.y,newPositionF.z);
//set entity stuuff
EntityUtils.getPosition(branch).set(currentPosition);
EntityUtils.getScale(branch).set(1,1,1);
EntityUtils.getRotation(branch).set(currentAbsoluteRotation);
AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch);
// AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch);
AttachUtils.clientAttachEntityAtTransform(parent, branch, transformFromParent);
//debug stuff
// Vector4f newPositionF = newPositionTransform.transform(new Vector4f(0,0,0,1));
// Vector3d newAbsolutePosition = new Vector3d(newPositionF.x,newPositionF.y,newPositionF.z);
// Entity debugSphere = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(debugSphere, "Models/unitsphere_1.fbx");
// EntityUtils.getScale(debugSphere).set(0.5f);
// EntityUtils.getPosition(debugSphere).set(newAbsolutePosition);
//attach leaf blobs
if(
!isCentralTrunk &&
currentSegmentNumber >= minimumSegmentToSpawnLeaves
){
// createLeafBlobsOnBranch(type,rand,transform,boneTransform,branch);
}
clientGenerateBranches(type, branch, rand, newPositionTransform, scalar - scalarFalloffFactor);
//recurse
clientGenerateBranchesAlt(
type,
branch,
rand,
currentPosition,
currentAbsoluteRotation,
newPosition,
boneRotation,
scalar - scalarFalloffFactor,
currentSegmentNumber + 1,
false //can't be central trunk
);
}
}
}
/**
* Generates branches
* @param type The type of branch
* @param parent The immediate parent of the branch
* @param rand The random
* @param transform The current ik transform for the branch
* @param scalar The scalar for the current width of the branch
* @param isCentralTrunk True if the tree should generate a central trunk with branches coming off of it
*/
public static void clientGenerateBranches(
TreeModel type,
Entity parent,
Random rand,
Matrix4f transform,
float scalar,
int currentSegmentNumber,
boolean isCentralTrunk
){
//how fast do the branches shrink in size
float scalarFalloffFactor = type.getLimbScalarFalloffFactor();
//the minimum branch size before we stop generating branch segments/trunk segments
float minimumScalar = type.getMinimumLimbScalar();
//how high is the model for a single branch segment
float treeSegmentHeight = type.getBranchHeight();
//how much to spread the branches along the current segment
float minimumSegmentDispersion = type.getMinimumLimbDispersion();
float dispersionSpread = type.getMaximumLimbDispersion() - type.getMinimumLimbDispersion();
//the number of branches to make per segment
int minBranches = type.getMinimumNumberForks();
int maxBranches = type.getMaximumNumberForks();
//the maximum number of segments in an single arc for both trunk and branches
int maximumTrunkSegments = type.getMaximumTrunkSegments();
int maximumBranchSegments = type.getMaximumBranchSegments();
if(scalar > minimumScalar && currentSegmentNumber < maximumTrunkSegments){
boolean hasCentralTrunk = type.getCentralTrunk();
//if there is a central trunk and this is the central trunk, generate the next central trunk segment
if(isCentralTrunk && hasCentralTrunk){
//the rotation applied to the bone
Quaternionf boneRotation = new Quaternionf(0,0,0,1).normalize();
//get current position
Vector4f currentPositionf = transform.transform(new Vector4f(0,0,0,1));
Vector3d currentPosition = new Vector3d(currentPositionf.x,currentPositionf.y,currentPositionf.z);
//The new absolute rotation at the end of the bone
Quaterniond currentAbsoluteRotation = transform.getNormalizedRotation(new Quaterniond()).normalize();
//calculates the bone transform matrix
Matrix4f boneTransform = new Matrix4f().identity().rotate(boneRotation);
//calculate attachment transform
Matrix4f attachmentTransform = new Matrix4f().identity().translate(0,treeSegmentHeight,0).rotate(boneRotation);
//new position transform
Matrix4f newPositionTransform = new Matrix4f(transform).mul(boneTransform);
//get new scalar
float newScalar = scalar - scalarFalloffFactor;
//create entity
Entity branch = EntityCreationUtils.createClientSpatialEntity();
InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(branch, branchInstanceTemplate, modelMatrixAttribute);
instancedActor.setAttribute(boneMatrixAttribute, boneTransform.scale(newScalar,1,newScalar));
instancedActor.setAttribute(baseSizeAttribute, scalar);
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
//set entity stuuff
EntityUtils.getPosition(branch).set(currentPosition);
EntityUtils.getScale(branch).set(1,1,1);
EntityUtils.getRotation(branch).set(currentAbsoluteRotation);
// AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch);
// AttachUtils.clientAttachEntityAtTransform(parent, branch, attachmentTransform);
//recurse
clientGenerateBranches(
type,
branch,
rand,
newPositionTransform,
scalar - scalarFalloffFactor,
currentSegmentNumber + 1,
true //can't be central trunk
);
}
}
if(scalar > minimumScalar && currentSegmentNumber < maximumBranchSegments){
int minimumSegmentToSpawnLeaves = type.getMinimumSegmentToSpawnLeaves();
//how much does it peel off of the current vector
double peelRotation = (rand.nextFloat() * dispersionSpread + minimumSegmentDispersion);
//the initial rotation around Y that the branch will peel towards
double offsetRotation = 0;
double rotationInitialOffset = rand.nextFloat();
int branchNum = rand.nextInt(maxBranches - minBranches) + minBranches;
for(int i = 0; i < branchNum; i++){
//update offsetrotation
offsetRotation = rotationInitialOffset + (i + 1) * (2.0 * Math.PI / (float)branchNum);
//get new rotation
double pitchFactor = Math.sin(offsetRotation);
double rollFactor = Math.cos(offsetRotation);
//the rotation applied to the bone
Quaternionf boneRotation = new Quaternionf(0,0,0,1).rotateLocalX((float)(pitchFactor * peelRotation)).rotateLocalZ((float)(rollFactor * peelRotation)).normalize();
//get current position
Vector4f currentPositionf = transform.transform(new Vector4f(0,0,0,1));
Vector3d currentPosition = new Vector3d(currentPositionf.x,currentPositionf.y,currentPositionf.z);
//The new absolute rotation at the end of the bone
Quaterniond currentAbsoluteRotation = transform.getNormalizedRotation(new Quaterniond()).normalize();
//calculates the bone transform matrix
Matrix4f boneTransform = new Matrix4f().identity().rotate(boneRotation);
//new position transform
Matrix4f newPositionTransform = new Matrix4f(transform).mul(boneTransform).translate(0,treeSegmentHeight,0);
//get new scalar
float newScalar = scalar - scalarFalloffFactor;
//create entity
Entity branch = EntityCreationUtils.createClientSpatialEntity();
InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(branch, branchInstanceTemplate, modelMatrixAttribute);
instancedActor.setAttribute(boneMatrixAttribute, boneTransform.scale(newScalar,1,newScalar));
instancedActor.setAttribute(baseSizeAttribute, scalar);
instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
//set entity stuuff
EntityUtils.getPosition(branch).set(currentPosition);
EntityUtils.getScale(branch).set(1,1,1);
EntityUtils.getRotation(branch).set(currentAbsoluteRotation);
// AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch);
// AttachUtils.clientAttachEntityAtTransform(parent, branch, attachmentTransform);
//debug stuff
// Vector4f newPositionF = newPositionTransform.transform(new Vector4f(0,0,0,1));
// Vector3d newAbsolutePosition = new Vector3d(newPositionF.x,newPositionF.y,newPositionF.z);
// Entity debugSphere = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(debugSphere, "Models/unitsphere_1.fbx");
// EntityUtils.getScale(debugSphere).set(0.5f);
// EntityUtils.getPosition(debugSphere).set(newAbsolutePosition);
//attach leaf blobs
if(
!isCentralTrunk &&
currentSegmentNumber >= minimumSegmentToSpawnLeaves
){
createLeafBlobsOnBranch(type,rand,transform,boneTransform,branch);
}
//recurse
clientGenerateBranches(
type,
branch,
rand,
newPositionTransform,
scalar - scalarFalloffFactor,
currentSegmentNumber + 1,
false //can't be central trunk
);
}
}
}
/**
* Creates leaf blobs around branch segments
* @param type The type of tree
* @param rand The random
* @param transform The current branch segment transform
* @param boneTransform The bone transform to the next branch segment
* @param branch The branch entity
*/
private static void createLeafBlobsOnBranch(TreeModel type, Random rand, Matrix4f transform, Matrix4f boneTransform, Entity branch){
//how high is the model for a single branch segment
float treeSegmentHeight = type.getBranchHeight();
float minBranchHeightToStartSpawningLeaves = type.getMinBranchHeightToStartSpawningLeaves();
float maxBranchHeightToStartSpawningLeaves = type.getMaxBranchHeightToStartSpawningLeaves();
float leafIncrement = type.getLeafIncrement();
int minLeavesToSpawnPerPoint = type.getMinLeavesToSpawnPerPoint();
int maxLeavesToSpawnPerPoint = type.getMaxLeavesToSpawnPerPoint();
for(
float positionAlongBranch = minBranchHeightToStartSpawningLeaves;
positionAlongBranch < maxBranchHeightToStartSpawningLeaves;
positionAlongBranch = positionAlongBranch + leafIncrement
){
int numToSpawn = rand.nextInt(maxLeavesToSpawnPerPoint - minLeavesToSpawnPerPoint) + minLeavesToSpawnPerPoint;
double currentLeafRotation = rand.nextFloat();
float distanceFromCenter = type.getLeafDistanceFromCenter();
for(int leafIncrementer = 0; leafIncrementer < numToSpawn; leafIncrementer++){
//offset radially
float xOffset = (float)Math.sin(currentLeafRotation) * distanceFromCenter;
float zOffset = (float)Math.cos(currentLeafRotation) * distanceFromCenter;
//update offsetrotation
currentLeafRotation = currentLeafRotation + (leafIncrementer + 1) * 2.0 * Math.PI / (float)numToSpawn;
//construct model matrix
Matrix4f leafPositionTransform = new Matrix4f(transform).mul(boneTransform).translate(xOffset,positionAlongBranch,zOffset);
Vector4f leafCurrentPositionf = leafPositionTransform.transform(new Vector4f(0,0,0,1));
Vector3d leafCurrentPosition = new Vector3d(leafCurrentPositionf.x,leafCurrentPositionf.y,leafCurrentPositionf.z);
//create entity
Entity leaf = EntityCreationUtils.createClientSpatialEntity();
InstancedActor leafInstancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(leaf, leafInstanceTemplate, modelMatrixAttribute);
leafInstancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity());
leafInstancedActor.setAttribute(leafColorAttribute, new Vector3f(36/255.0f,173/255.0f,31/255.0f));
//set entity stuuff
EntityUtils.getPosition(leaf).set(leafCurrentPosition);
EntityUtils.getScale(leaf).set(1,1,1);
EntityUtils.getRotation(leaf).set(new Quaterniond().identity());
AttachUtils.clientAttachEntityAtCurrentOffset(branch, leaf);
}
}
}
/**
* The behavior tree for branches swaying in the wind
*/
static class BranchBehaviorTree implements BehaviorTree {
/**
* Constructor
* @param branch The branch entity
*/
protected BranchBehaviorTree(Entity branch){
}
@Override
public void simulate(float deltaTime) {
}
}
}

View File

@ -9,6 +9,8 @@ public class FoliageType {
//Denotes an ambient foliage that will be placed on a voxel
public static final String TOKEN_AMBIENT = "AMBIENT";
//Denotes an tree object
public static final String TOKEN_TREE = "TREE";
//the name of the foliage type
String name;
@ -20,6 +22,8 @@ public class FoliageType {
GrowthModel growthModel;
//the list of tokens
List<String> tokens;
//The model for a tree
TreeModel treeModel;
/**
* Gets the name of the foliage type
@ -60,5 +64,13 @@ public class FoliageType {
public GrowthModel getGrowthModel(){
return growthModel;
}
/**
* Gets the tree model
* @return The tree model
*/
public TreeModel getTreeModel(){
return treeModel;
}
}

View File

@ -0,0 +1,206 @@
package electrosphere.game.data.foliage.type;
/**
* Describes characteristics about a type of tree (how do the limbs dispere, where to the leaves start growing, how sturdy is it, etc)
*/
public class TreeModel {
//how quickly do the limbs shrink
float limbScalarFalloffFactor;
//How small are the terminal limbs, basically how small can it get before it stops generating
float minimumLimbScalar;
//The maximum a single branch can disperse from the current line
float maximumLimbDispersion;
//The minimum a single branch must disperse from the current line
float minimumLimbDispersion;
//The minimum number of branch forks per iteration
int minimumNumberForks;
//The maximum number of branch forks per iteration
int maximumNumberForks;
//The height of a single branch, should be the height of the model
float branchHeight;
//if true, always generates a central trunk
boolean centralTrunk;
//The maximum number of linear segments for the trunk (ie how many times can a function recurse)
int maximumTrunkSegments;
//The maximum number of linear segments for the branch (ie how many times can a function recurse)
int maximumBranchSegments;
//The rate at which number of branch segments from the current trunk falls off over time
float maxBranchSegmentFalloffFactor;
//The minimum segment number required to start spawning leaves
int minimumSegmentToSpawnLeaves;
//the minimum distance along a given segment to start spawning leaves at
float minBranchHeightToStartSpawningLeaves;
//the maximum distance along a given segment to start spawning leaves at
float maxBranchHeightToStartSpawningLeaves;
//The increment along the branch segment to spawn leaves at
float leafIncrement;
//the minimum leaves to spawn per leaf point
int minLeavesToSpawnPerPoint;
//the maximum leaves to spawn per leaf point
int maxLeavesToSpawnPerPoint;
//The distance from the central line of a branch to spawn a leaf at
float leafDistanceFromCenter;
/**
* how quickly do the limbs shrink
* @return
*/
public float getLimbScalarFalloffFactor(){
return limbScalarFalloffFactor;
}
/**
* How small are the terminal limbs
* @return
*/
public float getMinimumLimbScalar(){
return minimumLimbScalar;
}
/**
* The maximum a single branch can disperse from the current line
* @return
*/
public float getMaximumLimbDispersion(){
return maximumLimbDispersion;
}
/**
* The minimum a single branch must disperse from the current line
* @return
*/
public float getMinimumLimbDispersion(){
return minimumLimbDispersion;
}
/**
* The minimum number of branch forks per iteration
* @return
*/
public int getMinimumNumberForks(){
return minimumNumberForks;
}
/**
* The maximum number of branch forks per iteration
* @return
*/
public int getMaximumNumberForks(){
return maximumNumberForks;
}
/**
* The height of a single branch, should be the height of the model
* @return
*/
public float getBranchHeight(){
return branchHeight;
}
/**
* if true, always generates a central trunk
* @return
*/
public boolean getCentralTrunk(){
return centralTrunk;
}
/**
* The maximum number of linear segments for the trunk (ie how many times can a function recurse)
* @return
*/
public int getMaximumTrunkSegments(){
return maximumTrunkSegments;
}
/**
* The maximum number of linear segments for the branch (ie how many times can a function recurse)
* @return
*/
public int getMaximumBranchSegments(){
return maximumBranchSegments;
}
/**
* The rate at which number of branch segments from the current trunk falls off over time
* @return
*/
public float getMaxBranchSegmentFalloffFactor(){
return maxBranchSegmentFalloffFactor;
}
/**
* The minimum segment number required to start spawning leaves
* @return
*/
public int getMinimumSegmentToSpawnLeaves(){
return minimumSegmentToSpawnLeaves;
}
/**
* the minimum distance along a given segment to start spawning leaves at
* @return
*/
public float getMinBranchHeightToStartSpawningLeaves(){
return minBranchHeightToStartSpawningLeaves;
}
/**
* the maximum distance along a given segment to start spawning leaves at
* @return
*/
public float getMaxBranchHeightToStartSpawningLeaves(){
return maxBranchHeightToStartSpawningLeaves;
}
/**
* The increment along the branch segment to spawn leaves at
* @return
*/
public float getLeafIncrement(){
return leafIncrement;
}
/**
* the minimum leaves to spawn per leaf point
* @return
*/
public int getMinLeavesToSpawnPerPoint(){
return minLeavesToSpawnPerPoint;
}
/**
* the maximum leaves to spawn per leaf point
* @return
*/
public int getMaxLeavesToSpawnPerPoint(){
return maxLeavesToSpawnPerPoint;
}
/**
* The distance from the central line of a branch to spawn a leaf at
* @return
*/
public float getLeafDistanceFromCenter(){
return leafDistanceFromCenter;
}
}

View File

@ -6,10 +6,12 @@ import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.Model;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.buffer.ShaderAttribute;
@ -78,20 +80,22 @@ public class InstancedActor implements Comparable<InstancedActor> {
} else {
attributes.put(attribute, new Matrix4f((Matrix4f)value));
}
}
if(value instanceof Matrix4d){
} else if(value instanceof Matrix4d){
if(attributes.containsKey(attribute)){
((Matrix4d)attributes.get(attribute)).set((Matrix4d)value);
} else {
attributes.put(attribute, new Matrix4d((Matrix4d)value));
}
}
if(
} else if(
value instanceof Double ||
value instanceof Float ||
value instanceof Integer
){
attributes.put(attribute, value);
} else if(value instanceof Vector3f){
attributes.put(attribute, value);
} else {
LoggerInterface.loggerRenderer.ERROR("Unsupported operation", new Exception());
}
// attributes.put(attribute, value);
}

View File

@ -202,6 +202,16 @@ public class HomogenousInstancedArray {
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, bufferPointer);
//tell opengl to send a new value from buffer for each instance (instead of whole buffer for every instance)
GL45.glVertexAttribDivisor(attributeIndex, 1);
} else if(type == HomogenousBufferTypes.VEC3F){
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, bufferPointer);
//enable attributes
GL45.glEnableVertexAttribArray(attributeIndex);
//update attribute to point to buffer at correct offset + stride
GL45.glVertexAttribPointer(attributeIndex, 3, GL45.GL_FLOAT, false, 0, 0);
//bind buffer
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, bufferPointer);
//tell opengl to send a new value from buffer for each instance (instead of whole buffer for every instance)
GL45.glVertexAttribDivisor(attributeIndex, 1);
} else {
LoggerInterface.loggerRenderer.ERROR("Unsupported operation", new Exception());
}