support for low lod models definitions and pipe
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-05-24 17:42:49 -04:00
parent 6e55b7ee18
commit ae72912649
8 changed files with 206 additions and 46 deletions

View File

@ -588,7 +588,8 @@
"priorityCategory" : "IDLE" "priorityCategory" : "IDLE"
} }
}, },
"path" : "Models/creatures/person2/person2_1.glb" "path" : "Models/creatures/person2/person2_1.glb",
"lodPath": "Models/creatures/person2/person2_1_lod.glb"
} }
}, },
"viewModelData" : { "viewModelData" : {

Binary file not shown.

View File

@ -68,6 +68,7 @@ public class PhysicsEntityUtils {
if(physicsTemplate.getKinematic()){ if(physicsTemplate.getKinematic()){
categoryBit = Collidable.TYPE_STATIC_BIT; categoryBit = Collidable.TYPE_STATIC_BIT;
} }
CollisionEngine.lockOde();
if(physicsTemplate.getKinematic()){ if(physicsTemplate.getKinematic()){
DGeom geom = null; DGeom geom = null;
switch(physicsTemplate.getType()){ switch(physicsTemplate.getType()){
@ -364,6 +365,7 @@ public class PhysicsEntityUtils {
ClientPhysicsSyncTree.attachTree(rVal); ClientPhysicsSyncTree.attachTree(rVal);
} }
} }
CollisionEngine.unlockOde();
} }
@ -383,6 +385,7 @@ public class PhysicsEntityUtils {
if(physicsTemplate.getKinematic()){ if(physicsTemplate.getKinematic()){
categoryBit = Collidable.TYPE_STATIC_BIT; categoryBit = Collidable.TYPE_STATIC_BIT;
} }
CollisionEngine.lockOde();
if(physicsTemplate.getKinematic()){ if(physicsTemplate.getKinematic()){
DGeom geom = null; DGeom geom = null;
switch(physicsTemplate.getType()){ switch(physicsTemplate.getType()){
@ -686,6 +689,7 @@ public class PhysicsEntityUtils {
ServerPhysicsSyncTree.attachTree(rVal); ServerPhysicsSyncTree.attachTree(rVal);
} }
} }
CollisionEngine.unlockOde();
} }

View File

@ -31,6 +31,11 @@ public class NonproceduralModel {
*/ */
private Map<String,Vector3f> meshColorMap; private Map<String,Vector3f> meshColorMap;
/**
* The path to the lower-resolution model
*/
private String lodPath;
/** /**
* Gets the path of the model * Gets the path of the model
* @return The path of the model * @return The path of the model
@ -94,6 +99,22 @@ public class NonproceduralModel {
public void setMeshColorMap(Map<String,Vector3f> meshColorMap) { public void setMeshColorMap(Map<String,Vector3f> meshColorMap) {
this.meshColorMap = meshColorMap; this.meshColorMap = meshColorMap;
} }
/**
* Gets the path to the lower resolution model
* @return The path to the lower resolution model
*/
public String getLODPath(){
return lodPath;
}
/**
* Sets the path to the lower resolution model
* @param lodPath The path to the lower resolution model
*/
public void setLODPath(String lodPath){
this.lodPath = lodPath;
}
} }

View File

@ -1,5 +1,14 @@
package electrosphere.entity; package electrosphere.entity;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.joml.Vector3f;
import electrosphere.data.entity.graphics.NonproceduralModel;
import electrosphere.engine.Globals;
import electrosphere.entity.state.idle.ClientIdleTree;
import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.Actor;
/** /**
@ -36,4 +45,55 @@ public class DrawableUtils {
return entity.containsKey(EntityDataStrings.HAS_UNIQUE_MODEL); return entity.containsKey(EntityDataStrings.HAS_UNIQUE_MODEL);
} }
/**
* Applies non-procedural model data to the entity
* @param entity The entity
* @param modelData The model data
*/
public static void applyNonproceduralModel(Entity entity, NonproceduralModel modelData){
if(modelData == null){
throw new Error("Null model data!");
}
if(modelData.getPath() == null){
throw new Error("Path undefined!");
}
//make the entity drawable
EntityCreationUtils.makeEntityDrawable(entity, modelData.getPath());
Actor creatureActor = EntityUtils.getActor(entity);
//add low-res model path to actor if present
if(modelData.getLODPath() != null){
creatureActor.setLowResPath(modelData.getLODPath());
Globals.assetManager.addModelPathToQueue(modelData.getLODPath());
}
//idle tree & generic stuff all entities have
if(modelData.getIdleData() != null){
ClientIdleTree.attachTree(entity, modelData.getIdleData());
}
//apply uniforms
if(modelData.getUniforms() != null){
Map<String,Map<String,Object>> meshUniformMap = modelData.getUniforms();
Set<String> meshNames = meshUniformMap.keySet();
for(String meshName : meshNames){
Map<String,Object> uniforms = meshUniformMap.get(meshName);
Set<String> uniformNames = uniforms.keySet();
for(String uniformName : uniformNames){
Object value = uniforms.get(uniformName);
creatureActor.setUniformOnMesh(meshName, uniformName, value);
}
}
}
//apply mesh color map
if(modelData.getMeshColorMap() != null){
Map<String,Vector3f> meshColorMap = modelData.getMeshColorMap();
for(Entry<String,Vector3f> entry : meshColorMap.entrySet()){
creatureActor.setUniformOnMesh(entry.getKey(), "color", entry.getValue());
}
}
}
} }

View File

@ -1,12 +1,7 @@
package electrosphere.entity.types.common; package electrosphere.entity.types.common;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3f;
import org.ode4j.ode.DBody; import org.ode4j.ode.DBody;
import electrosphere.client.interact.ClientInteractionEngine; import electrosphere.client.interact.ClientInteractionEngine;
@ -31,6 +26,7 @@ import electrosphere.data.entity.graphics.GraphicsTemplate;
import electrosphere.data.entity.item.Item; import electrosphere.data.entity.item.Item;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.PhysicsMeshQueueItem; import electrosphere.engine.assetmanager.PhysicsMeshQueueItem;
import electrosphere.entity.DrawableUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
@ -55,7 +51,6 @@ import electrosphere.entity.state.gravity.ServerGravityTree;
import electrosphere.entity.state.growth.ClientGrowthComponent; import electrosphere.entity.state.growth.ClientGrowthComponent;
import electrosphere.entity.state.growth.ServerGrowthComponent; import electrosphere.entity.state.growth.ServerGrowthComponent;
import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.idle.ClientIdleTree;
import electrosphere.entity.state.idle.ServerIdleTree; import electrosphere.entity.state.idle.ServerIdleTree;
import electrosphere.entity.state.inventory.ClientInventoryState; import electrosphere.entity.state.inventory.ClientInventoryState;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
@ -169,32 +164,8 @@ public class CommonEntityUtils {
// //
if(rawType.getGraphicsTemplate() != null){ if(rawType.getGraphicsTemplate() != null){
GraphicsTemplate graphicsTemplate = rawType.getGraphicsTemplate(); GraphicsTemplate graphicsTemplate = rawType.getGraphicsTemplate();
if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getPath() != null && EntityUtils.getActor(entity) == null && generateDrawable == true){ if(graphicsTemplate.getModel() != null && EntityUtils.getActor(entity) == null && generateDrawable == true){
EntityCreationUtils.makeEntityDrawable(entity, graphicsTemplate.getModel().getPath()); DrawableUtils.applyNonproceduralModel(entity, graphicsTemplate.getModel());
}
//idle tree & generic stuff all entities have
if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getIdleData() != null){
ClientIdleTree.attachTree(entity, graphicsTemplate.getModel().getIdleData());
}
if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getUniforms() != null){
Actor creatureActor = EntityUtils.getActor(entity);
Map<String,Map<String,Object>> meshUniformMap = graphicsTemplate.getModel().getUniforms();
Set<String> meshNames = meshUniformMap.keySet();
for(String meshName : meshNames){
Map<String,Object> uniforms = meshUniformMap.get(meshName);
Set<String> uniformNames = uniforms.keySet();
for(String uniformName : uniformNames){
Object value = uniforms.get(uniformName);
creatureActor.setUniformOnMesh(meshName, uniformName, value);
}
}
}
if(graphicsTemplate.getModel() != null && graphicsTemplate.getModel().getMeshColorMap() != null){
Actor creatureActor = EntityUtils.getActor(entity);
Map<String,Vector3f> meshColorMap = graphicsTemplate.getModel().getMeshColorMap();
for(Entry<String,Vector3f> entry : meshColorMap.entrySet()){
creatureActor.setUniformOnMesh(entry.getKey(), "color", entry.getValue());
}
} }
} }
Actor creatureActor = EntityUtils.getActor(entity); Actor creatureActor = EntityUtils.getActor(entity);

View File

@ -44,11 +44,32 @@ public class Actor {
*/ */
public static final int INVALID_ANIMATION = -1; public static final int INVALID_ANIMATION = -1;
/**
* full-resolution lod level
*/
public static final int LOD_LEVEL_FULL = 1;
/**
* lower-resolution lod level
*/
public static final int LOD_LEVEL_LOWER = 0;
/** /**
* the model path of the model backing the actor * the model path of the model backing the actor
*/ */
private String modelPath; private String modelPath;
/**
* Path to the low res model
*/
private String lowResPath;
/**
* The LOD level of the actor
*/
private int lodLevel = Actor.LOD_LEVEL_FULL;
/** /**
* used for statically binding textures in pipelines that dont bind textures on a per-mesh level * used for statically binding textures in pipelines that dont bind textures on a per-mesh level
* ie when in the ui, this can be set to bind a texture at the actor level * ie when in the ui, this can be set to bind a texture at the actor level
@ -124,6 +145,16 @@ public class Actor {
* Sets scaling applied at the actor level * Sets scaling applied at the actor level
*/ */
private float scale = 1.0f; private float scale = 1.0f;
/**
* The transform matrix
*/
private Matrix4d modelMatrix = new Matrix4d();
/**
* The world position vector
*/
private Vector3d worldPos = new Vector3d();
/** /**
* Creates an achor * Creates an achor
@ -438,7 +469,7 @@ public class Actor {
* Calculates the node transforms for the actor * Calculates the node transforms for the actor
* @param model The model that backs the actor * @param model The model that backs the actor
*/ */
void calculateNodeTransforms(Model model){ private void calculateNodeTransforms(Model model){
model.updateNodeTransform(boneRotators,staticMorph); model.updateNodeTransform(boneRotators,staticMorph);
for(Bone bone : model.getBones()){ for(Bone bone : model.getBones()){
//store position //store position
@ -478,8 +509,8 @@ public class Actor {
public void applySpatialData(Matrix4d modelMatrix, Vector3d worldPos){ public void applySpatialData(Matrix4d modelMatrix, Vector3d worldPos){
Model model = Globals.assetManager.fetchModel(modelPath); Model model = Globals.assetManager.fetchModel(modelPath);
if(model != null){ if(model != null){
model.setModelMatrix(modelMatrix); this.modelMatrix.set(modelMatrix);
model.setWorldPos(worldPos); this.worldPos.set(worldPos);
} }
} }
@ -489,14 +520,29 @@ public class Actor {
*/ */
public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){ public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
Globals.profiler.beginAggregateCpuSample("Actor.draw"); Globals.profiler.beginAggregateCpuSample("Actor.draw");
Model model = Globals.assetManager.fetchModel(modelPath);
if(model != null && Actor.isWithinFrustumBox(renderPipelineState,model,frustumCull)){ //fetch the model
String pathToFetch = this.modelPath;
if(this.lodLevel == Actor.LOD_LEVEL_LOWER && this.lowResPath != null){
pathToFetch = this.lowResPath;
}
Model model = Globals.assetManager.fetchModel(pathToFetch);
if(model == null){
Globals.profiler.endCpuSample();
return;
}
model.setModelMatrix(this.modelMatrix);
model.setWorldPos(this.worldPos);
//frustum cull then draw
if(Actor.isWithinFrustumBox(renderPipelineState,model,frustumCull)){
this.applyAnimationMasks(model); this.applyAnimationMasks(model);
meshMask.processMeshMaskQueue(); meshMask.processMeshMaskQueue();
model.setMeshMask(meshMask); model.setMeshMask(meshMask);
model.setTextureMask(textureMap); model.setTextureMask(textureMap);
for(ActorShaderMask shaderMask : shaderMasks){ for(ActorShaderMask shaderMask : shaderMasks){
if(shaderMask.getModelName().equals(modelPath)){ if(shaderMask.getModelName().equals(pathToFetch)){
model.getShaderMask().put(shaderMask.getMeshName(),shaderMask); model.getShaderMask().put(shaderMask.getMeshName(),shaderMask);
} }
} }
@ -534,13 +580,18 @@ public class Actor {
*/ */
public boolean isStaticDrawCall(){ public boolean isStaticDrawCall(){
return return
this.animationQueue.size() == 0 && //is low lod level
this.meshMask.getBlockedMeshes().size() == 0 && this.lodLevel == Actor.LOD_LEVEL_LOWER ||
this.textureMap == null && //actor doesn't have anything complicated render-wise (animations, custom textures, etc)
this.shaderMasks.size() == 0 && (
this.textureOverride == null && this.animationQueue.size() == 0 &&
this.uniformMap.isEmpty() && this.meshMask.getBlockedMeshes().size() == 0 &&
this.staticMorph == null this.textureMap == null &&
this.shaderMasks.size() == 0 &&
this.textureOverride == null &&
this.uniformMap.isEmpty() &&
this.staticMorph == null
)
; ;
} }
@ -873,4 +924,37 @@ public class Actor {
this.scale = scale; this.scale = scale;
} }
/**
* Gets the path to the low res model
* @return Path to the low res model
*/
public String getLowResPath() {
return lowResPath;
}
/**
* Sets the path to the low res model
* @param lowResPath The path to the low res model
*/
public void setLowResPath(String lowResPath) {
this.lowResPath = lowResPath;
}
/**
* Gets the lod level
* @return The lod level
*/
public int getLodLevel(){
return this.lodLevel;
}
/**
* Sets the lod level of the actor
* @param lodLevel The lod level
*/
public void setLodLevel(int lodLevel){
this.lodLevel = lodLevel;
}
} }

View File

@ -19,6 +19,12 @@ import electrosphere.renderer.pipelines.MainContentPipeline;
* Evaluates the draw targets * Evaluates the draw targets
*/ */
public class DrawTargetEvaluator { public class DrawTargetEvaluator {
/**
* Cutoff after which we start using LOD models
*/
public static final int LOD_CUTOFF = 100;
/** /**
* Evaluates the draw targets * Evaluates the draw targets
@ -54,6 +60,19 @@ public class DrawTargetEvaluator {
Vector3d position = EntityUtils.getPosition(currentEntity); Vector3d position = EntityUtils.getPosition(currentEntity);
//fetch actor //fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity); Actor currentActor = EntityUtils.getActor(currentEntity);
//evaluate LOD level
if(currentActor.getLowResPath() != null){
posVec.set(position);
double dist = posVec.distance(cameraCenter);
if(dist < LOD_CUTOFF){
currentActor.setLodLevel(Actor.LOD_LEVEL_FULL);
} else {
currentActor.setLodLevel(Actor.LOD_LEVEL_LOWER);
}
}
//queue calls accordingly
if(!DrawableUtils.hasUniqueModel(currentEntity) && currentActor.isStaticDrawCall()){ if(!DrawableUtils.hasUniqueModel(currentEntity) && currentActor.isStaticDrawCall()){
//calculate camera-modified vector3d //calculate camera-modified vector3d
Vector3d cameraModifiedPosition = positionVec.set(position).sub(cameraCenter); Vector3d cameraModifiedPosition = positionVec.set(position).sub(cameraCenter);