Procedural Tree Trunk code

This commit is contained in:
austin 2023-09-02 19:40:17 -04:00
parent a16a310b25
commit 9811c18cf7
29 changed files with 941 additions and 34 deletions

View File

@ -21,6 +21,39 @@
"scale" : [0.0015, 0.0015, 0.0015]
}
]
},
{
"path" : "Models/proceduralTree1/proceduralTrunk1.fbx",
"meshes" : [
{
"meshName" : "Cube",
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
"offset" : [0.0, 0.0, 0.0],
"scale" : [1.0, 1.0, 1.0]
}
]
},
{
"path" : "Models/proceduralTree2/proceduralTree2.fbx",
"meshes" : [
{
"meshName" : "Trunk",
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
"offset" : [0.0, 0.0, 0.0],
"scale" : [1.0, 1.0, 1.0]
}
]
},
{
"path" : "Models/proceduralTree2/proceduralTree2v2.fbx",
"meshes" : [
{
"meshName" : "Trunk",
"rotation" : [-0.7071068, 0.0, 0.0, 0.7071068],
"offset" : [0.0, 0.0, 0.0],
"scale" : [1.0, 1.0, 1.0]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -67,10 +67,8 @@ float calcLightIntensityTotal(vec3 normal);
float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal);
void main(){
if(hasTransparency == 1){
if(texture(material.diffuse, TexCoord).a < 0.01){
discard;
}
if(texture(material.diffuse, TexCoord).a < 0.01){
discard;
}
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);

View File

@ -0,0 +1,238 @@
#version 330 core
#define NR_POINT_LIGHTS 10
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);
//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, texture(material.diffuse, TexCoord).a);//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,74 @@
//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 vec4 boneA;
layout (location = 10) in vec4 boneB;
layout (location = 11) in vec4 boneC;
layout (location = 12) in vec4 boneD;
layout (location = 13) in float baseSize;
//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);
mat4 scaleMatrix = mat4(
baseSize,0,0,0,
0,baseSize,0,0,
0,0,baseSize,0,
0,0,0,1
);
mat4 bone = mat4(boneA,boneB,boneC,boneD);
mat4 BoneTransform = (bone * aWeights[0]) + (scaleMatrix * (1.0 - aWeights[0]));
//normalize posiiton and normal
vec4 FinalVertex = BoneTransform * vec4(aPos, 1.0);
vec4 FinalNormal = BoneTransform * 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

@ -445,6 +445,18 @@
"Textures/leaf3layer1.png",
"Textures/leaf3layer1.png"
]
},
"Models/proceduralTree2/proceduralTree2.fbx": {
"Trunk" : [
"Models/proceduralTree2/Trunk.png",
"Models/proceduralTree2/Trunk.png"
]
},
"Models/proceduralTree2/proceduralTree2v2.fbx": {
"Trunk" : [
"Models/proceduralTree2/Trunk.png",
"Models/proceduralTree2/Trunk.png"
]
}
}
}

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Tue Jul 25 19:29:28 EDT 2023
buildNumber=5
#Sun Aug 20 22:06:17 EDT 2023
buildNumber=10

View File

@ -195,7 +195,7 @@ public class ClientFoliageManager {
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
Globals.clientScene.registerEntity(entity);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED_MANAGED);
}
/**

View File

@ -22,6 +22,7 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.BehaviorTree;
import electrosphere.entity.state.movement.ApplyRotationTree;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.entity.types.tree.ProceduralTree;
import electrosphere.logger.LoggerInterface;
import electrosphere.menu.MenuGenerators;
import electrosphere.menu.MenuGeneratorsMultiplayer;
@ -238,6 +239,11 @@ public class ClientLoading {
}
});
// for(int i = 0; i < 6; i++){
Entity tree = ProceduralTree.clientGenerateProceduralTree("asdf", 0);
// EntityUtils.getPosition(tree).set(5,0,i * 3);
// }
}
static void initDrawCellManager(){

View File

@ -23,6 +23,12 @@ public class EntityDataStrings {
public static final String DRAW_OUTLINE = "drawOutline";
public static final String INSTANCED_ACTOR = "instancedActor";
public static final String DRAW_INSTANCED = "drawInstanced";
/*
Instanced Entity
*/
public static final String INSTANCED_MODEL_ATTRIBUTE = "instancedModelAttribute";
/*
@ -160,9 +166,11 @@ public class EntityDataStrings {
*/
public static final String ATTACH_ENTITY_IS_ATTACHED = "attachIsAttached";
public static final String ATTACH_PARENT = "attachParent";
public static final String ATTACH_TARGET_BONE = "attachTargetBone";
public static final String ATTACH_TARGET_BONE = "attachTargetBone"; //Attaches to a specific bone of the entity
public static final String ATTACH_TARGET_BASE = "attachTargetBase"; //Attaches to the base of the entity (should be the same as getPosition(entity))
public static final String ATTACH_CHILDREN_LIST = "attachChildrenList";
public static final String ATTACH_ROTATION_OFFSET = "attachRotationOffset";
public static final String ATTACH_POSITION_OFFSET = "attachPositionOffset";
/*
Item Entity

View File

@ -12,7 +12,8 @@ public class EntityTags {
public static final String CREATURE = "creature";
public static final String UI = "ui";
public static final String DRAWABLE = "drawable";
public static final String DRAW_INSTANCED = "drawInstanced";
public static final String DRAW_INSTANCED = "drawInstanced"; //if it's instanced, but not necessarily managed by a service (ie a tree branch)
public static final String DRAW_INSTANCED_MANAGED = "drawInstancedManaged"; //if it's managed by a service (ie foliage manager)
public static final String LIGHT = "light";
public static final String ITEM = "item";
public static final String GRAVITY = "gravity";

View File

@ -119,6 +119,10 @@ public class AttachUtils {
.mul(offsetRotation)
.normalize();
}
} else if(currentEntity.getData(EntityDataStrings.ATTACH_TARGET_BASE)!=null){
Vector3d positionOffset = getAttachPositionOffset(currentEntity);
Vector3d parentPosition = EntityUtils.getPosition(parent);
EntityUtils.getPosition(currentEntity).set(new Vector3d(parentPosition).add(positionOffset));
}
}
}
@ -142,6 +146,29 @@ public class AttachUtils {
getChildrenList(parent).remove(toAttach);
}
}
/**
* Attaches an entity to another based on the parent's absolute position in the game engine
* @param parent The parent to attach to
* @param toAttach The entity to attach to the parent
*/
public static void clientAttachEntityAtCurrentOffset(Entity parent, Entity toAttach){
Vector3d parentPosition = EntityUtils.getPosition(parent);
Vector3d childPosition = EntityUtils.getPosition(toAttach);
Vector3d offset = new Vector3d(childPosition).sub(parentPosition);
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_BASE, true);
toAttach.putData(EntityDataStrings.ATTACH_POSITION_OFFSET, offset);
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 boolean isAttached(Entity e){
return e.containsKey(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED);
@ -162,6 +189,10 @@ public class AttachUtils {
public static boolean hasChildren(Entity e){
return e.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST) && !getChildrenList(e).isEmpty();
}
public static Vector3d getAttachPositionOffset(Entity e){
return (Vector3d)e.getData(EntityDataStrings.ATTACH_POSITION_OFFSET);
}
public static LinkedList<Entity> getChildrenList(Entity e){
return (LinkedList<Entity>)e.getData(EntityDataStrings.ATTACH_CHILDREN_LIST);

View File

@ -20,30 +20,20 @@ import org.joml.Vector3f;
import org.joml.Vector4f;
/**
*
* @author amaterasu
* Utilities for generating foliage
*/
public class FoliageUtils {
/**
* Spawns a basic foliage object
* @param type The type of foliage object
* @return The entity for the foliage
*/
public static Entity spawnBasicFoliage(String type){
FoliageType rawType = Globals.gameConfigCurrent.getFoliageMap().getFoliage(type);
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
EntityCreationUtils.makeEntityDrawable(rVal, rawType.getModelPath());
// if(rawType.getPhysicsObjects() != null){
// List<PhysicsObject> physicsObjects = rawType.getPhysicsObjects();
// for(PhysicsObject physicsTemplate : physicsObjects){
// switch(physicsTemplate.getType()){
// case "CYLINDER":
//// Matrix4f rotationTransform = new Matrix4f().rotate(rotation);
//// Vector4f rotatedPosition = rotationTransform.transform(new Vector4f(physicsTemplate.(),physicsTemplate.getPositionY(),physicsTemplate.getPositionZ(),1.0f));
//// Vector3f cubePosition = new Vector3f(position).add(rotatedPosition.x,rotatedPosition.y,rotatedPosition.z);
//// Quaternionf cubeRotation = new Quaternionf(rotation).mul(new Quaternionf(physicsTemplate.getRotationX(),template.getRotationY(),physicsTemplate.getRotationZ(),physicsTemplate.getRotationW())).normalize();
// CollisionObjUtils.attachCollisionCube(new Vector3f(1,1,1), position, rotation, rVal);
// break;
// }
// }
// }
for(String token : rawType.getTokens()){
switch(token){
case "BLENDER_TRANSFORM":

View File

@ -0,0 +1,47 @@
package electrosphere.entity.types.instance;
import java.util.Map;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
/**
* A template for creating an instance entity. Stores thing like capacity and the model
*/
public class InstanceTemplate {
//The path for the model for this instance
protected String modelPath;
//The total number of instances to draw at a given time
protected int capacity;
//The map of all attributes for instanced foliage
protected Map<ShaderAttribute,HomogenousBufferTypes> attributes;
//shader paths
protected String vertexPath;
protected String fragmentPath;
/**
* Creates a template to create instanced actors off of
* @param capacity The number of actors that can be drawn per frame (going over this limit will result in drawing the closest ${capacity} number of actors)
* @param modelPath The path for the model of the instance
* @param vertexPath The vertex shader path
* @param fragmentPath The fragment shader path
* @param attributes The attributes used on this instance
* @return The template
*/
public static InstanceTemplate createInstanceTemplate(int capacity, String modelPath, String vertexPath, String fragmentPath, Map<ShaderAttribute,HomogenousBufferTypes> attributes){
InstanceTemplate rVal = new InstanceTemplate();
rVal.capacity = capacity;
rVal.modelPath = modelPath;
rVal.vertexPath = vertexPath;
rVal.fragmentPath = fragmentPath;
rVal.attributes = attributes;
return rVal;
}
}

View File

@ -0,0 +1,71 @@
package electrosphere.entity.types.instance;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
/**
* Utilities for working with entities that are instanced models
*/
public class InstancedEntityUtils {
/**
* Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor
* @param entity The entity
* @param template The instance template to create the entity off of
* @return The instanced actor that is attached to this entity
*/
public static InstancedActor makeEntityInstanced(Entity entity, InstanceTemplate template){
InstancedActor instancedActor = Globals.clientInstanceManager.createInstancedActor(
template.modelPath,
template.vertexPath,
template.fragmentPath,
template.attributes,
template.capacity);
entity.putData(EntityDataStrings.INSTANCED_ACTOR, instancedActor);
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
Globals.clientScene.registerEntity(entity);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED);
return instancedActor;
}
/**
* Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor
* This variation also specifies a shader attribute that represents the model transform of the instance
* The model transform is used to constantly automatically calculate the position of the mesh
* @param entity The entity
* @param template The instance template to create the entity off of
* @param modelTransformAttribute The shader attribute that is the model transform
* @return The instanced actor that is attached to this entity
*/
public static InstancedActor makeEntityInstancedWithModelTransform(Entity entity, InstanceTemplate template, ShaderAttribute modelTransformAttribute){
InstancedActor instancedActor = Globals.clientInstanceManager.createInstancedActor(
template.modelPath,
template.vertexPath,
template.fragmentPath,
template.attributes,
template.capacity);
entity.putData(EntityDataStrings.INSTANCED_ACTOR, instancedActor);
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
entity.putData(EntityDataStrings.INSTANCED_MODEL_ATTRIBUTE, modelTransformAttribute);
Globals.clientScene.registerEntity(entity);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED);
return instancedActor;
}
}

View File

@ -0,0 +1,305 @@
package electrosphere.entity.types.tree;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
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.Vector4f;
import electrosphere.engine.Globals;
import electrosphere.entity.DrawableUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
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.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
/**
* Used for generating procedural trees
*/
public class ProceduralTree {
//The instance template for the branch
static InstanceTemplate branchInstanceTemplate;
//The model attribute
static final ShaderAttribute modelMatrixAttribute = new ShaderAttribute(new int[]{
5,6,7,8
});
//the bone attribute
static final ShaderAttribute boneMatrixAttribute = new ShaderAttribute(new int[]{
9,10,11,12
});
static final ShaderAttribute baseSizeAttribute = new ShaderAttribute(13);
//The static setup logic
static {
//create map of attributes and register them
Map<ShaderAttribute,HomogenousBufferTypes> attributes = new HashMap<ShaderAttribute,HomogenousBufferTypes>();
attributes.put(modelMatrixAttribute,HomogenousBufferTypes.MAT4F);
attributes.put(boneMatrixAttribute,HomogenousBufferTypes.MAT4F);
attributes.put(baseSizeAttribute, HomogenousBufferTypes.FLOAT);
branchInstanceTemplate = InstanceTemplate.createInstanceTemplate(
1000,
"Models/proceduralTree2/proceduralTree2v2.fbx",
"Shaders/proceduraltree/proceduraltree.vs",
"Shaders/proceduraltree/proceduraltree.fs",
attributes);
}
//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
*/
public static Entity clientGenerateProceduralTree(String type, long seed){
Random treeRandom = new Random(seed);
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
//generate trunk
Entity trunkChild = EntityCreationUtils.createClientSpatialEntity();
InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(trunkChild, branchInstanceTemplate, modelMatrixAttribute);
instancedActor.setAttribute(boneMatrixAttribute, new Matrix4f().identity());
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);
// // }});
// }
// }
//attach btress
//..attach wind
//...
return rVal;
}
// 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;
// //the rotation applied to the bone
// Quaternionf boneRotation = new Quaternionf(0,0,0,1).rotateLocalX((float)(pitchFactor * peelRotation)).rotateLocalZ((float)(rollFactor * peelRotation)).normalize();
// //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();
// //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();
// 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++){
//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);
//new position transform
Matrix4f newPositionTransform = new Matrix4f(transform).mul(boneTransform).translate(0,3,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());
//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);
//debug stuff
// Entity debugSphere = EntityCreationUtils.createClientSpatialEntity();
// EntityCreationUtils.makeEntityDrawable(debugSphere, "Models/unitsphere_1.fbx");
// EntityUtils.getScale(debugSphere).set(0.5f);
// EntityUtils.getPosition(debugSphere).set(newAbsolutePosition);
clientGenerateBranches(type, branch, rand, newPositionTransform, scalar - scalarFalloffFactor);
}
}
}
}

View File

@ -378,6 +378,14 @@ public class Mesh {
weight[2] = weight[2] * (1.0f / total);
weight[3] = weight[3] * (1.0f / total);
}
//If all are 0 (for instance the vertex doesn't have any bones with any weight > 0), the values for each weight will be NaN after the divide immediately above
//If NaN, set all to 0
if(Float.isNaN(weight[0])){
weight[0] = 0;
weight[1] = 0;
weight[2] = 0;
weight[3] = 0;
}
boneIndexDataBuffer.put(index);
boneWeightDataBuffer.put(weight);
}

View File

@ -87,6 +87,8 @@ import electrosphere.game.data.creature.type.CollidableTemplate;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.RenderPipelineState.SelectedShaderEnum;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.instance.InstancedActor;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.debug.DebugRendering;
import electrosphere.renderer.framebuffer.Framebuffer;
import electrosphere.renderer.framebuffer.FramebufferUtils;
@ -147,6 +149,11 @@ public class RenderingEngine {
static float volumeDepthLinearCoef = 0.1f;
static float volumeDepthQuadCoef = 0.01f;
/*
Necessary static variables for drawing
*/
static Matrix4d modelTransformMatrix = new Matrix4d();
/*
Vertical volumetrics
TODO: implement
@ -667,6 +674,38 @@ public class RenderingEngine {
currentActor.draw(renderPipelineState);
}
}
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(
currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null &&
currentEntity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null &&
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){
//fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity);
//if the shader attribute for model matrix exists, calculate the model matrix and apply
if(InstancedActor.getInstanceModelAttribute(currentEntity) != null){
ShaderAttribute modelAttribute = InstancedActor.getInstanceModelAttribute(currentEntity);
//calculate model matrix
Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Quaterniond rotation = EntityUtils.getRotation(currentEntity);
// modelTransformMatrix.identity();
modelTransformMatrix.identity().translationRotateScale(
cameraModifiedPosition,
new Quaternionf((float)rotation.x,(float)rotation.y,(float)rotation.z,(float)rotation.w),
new Vector3f(EntityUtils.getScale(currentEntity))
);
// modelTransformMatrix.translate(cameraModifiedPosition);
// modelTransformMatrix.rotate(rotation);
// modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity)));
//set actor value
currentActor.setAttribute(modelAttribute, new Matrix4f(modelTransformMatrix));
// ((Matrix4f)currentActor.getAttributeValue(modelAttribute)).set(modelTransformMatrix);
}
//draw
currentActor.draw(renderPipelineState);
}
}
//draw all instanced models
Globals.clientInstanceManager.draw(renderPipelineState);
@ -724,6 +763,21 @@ public class RenderingEngine {
currentActor.draw(renderPipelineState);
}
}
for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAW_INSTANCED)){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(
currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW) != null &&
currentEntity.getData(EntityDataStrings.DRAW_TRANSPARENT_PASS) != null &&
drawPoint(cameraPos,new Vector3f((float)position.x,(float)position.y,(float)position.z))
){
//fetch actor
InstancedActor currentActor = InstancedActor.getInstancedActor(currentEntity);
//draw
if(currentActor != null){
currentActor.draw(renderPipelineState);
}
}
}
//draw all instanced models
Globals.clientInstanceManager.draw(renderPipelineState);
@ -740,8 +794,6 @@ public class RenderingEngine {
static void renderGameContentNoOIT(){
Matrix4d modelTransformMatrix = new Matrix4d();
//bind screen fbo
screenFramebuffer.bind();
glEnable(GL_DEPTH_TEST);

View File

@ -218,9 +218,9 @@ public class InstanceData {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
buffer.put(mat.m00());
buffer.put(mat.m10());
buffer.put(mat.m20());
buffer.put(mat.m30());
buffer.put(mat.m01());
buffer.put(mat.m02());
buffer.put(mat.m03());
buffer.put(mat.m10());
buffer.put(mat.m11());
@ -241,9 +241,9 @@ public class InstanceData {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
buffer.put((float)mat.m00());
buffer.put((float)mat.m10());
buffer.put((float)mat.m20());
buffer.put((float)mat.m30());
buffer.put((float)mat.m01());
buffer.put((float)mat.m02());
buffer.put((float)mat.m03());
buffer.put((float)mat.m10());
buffer.put((float)mat.m11());

View File

@ -51,6 +51,10 @@ public class InstanceManager {
InstanceData instanceData = new InstanceData(capacity,vertexShaderPath,fragmentShaderPath);
//queue shader
Globals.assetManager.addShaderToQueue(vertexShaderPath, fragmentShaderPath);
//if asset manager doesn't have model, queue model
if(Globals.assetManager.fetchModel(modelPath) == null){
Globals.assetManager.addModelPathToQueue(modelPath);
}
//set attributes
for(ShaderAttribute attribute : attributeTypes.keySet()){
instanceData.addDataType(attribute, attributeTypes.get(attribute));

View File

@ -86,6 +86,13 @@ public class InstancedActor implements Comparable<InstancedActor> {
attributes.put(attribute, new Matrix4d((Matrix4d)value));
}
}
if(
value instanceof Double ||
value instanceof Float ||
value instanceof Integer
){
attributes.put(attribute, value);
}
// attributes.put(attribute, value);
}
@ -123,4 +130,14 @@ public class InstancedActor implements Comparable<InstancedActor> {
return this.priority;
}
/**
* If it exists, gets the model attribute for this instance
* @param entity The entity to get the attribute from
* @return The attribute if it exists, or null
*/
public static ShaderAttribute getInstanceModelAttribute(Entity entity){
return (ShaderAttribute)entity.getData(EntityDataStrings.INSTANCED_MODEL_ATTRIBUTE);
}
}

View File

@ -192,6 +192,16 @@ public class HomogenousInstancedArray {
GL45.glVertexAttribDivisor(matrixAttributeIndices[1], 1);
GL45.glVertexAttribDivisor(matrixAttributeIndices[2], 1);
GL45.glVertexAttribDivisor(matrixAttributeIndices[3], 1);
} else if(type == HomogenousBufferTypes.FLOAT){
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, 1, 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());
}

View File

@ -226,6 +226,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
public void unloadPlayerlessChunks(){
//TODO: improve to make have less performance impact
for(ServerDataCell cell : loadedCells){
loadedCellsLock.acquireUninterruptibly();
if(cell.getPlayers().size() < 1){
int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
cellPlayerlessFrameMap.put(cell,frameCount);
@ -237,6 +238,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
cellPlayerlessFrameMap.put(cell, 0);
}
}
loadedCellsLock.release();
}
for(ServerDataCell cell : toCleanQueue){
parent.deregisterCell(cell);