From 4ca453e2272beed121b957244408a61b0b0d8b9b Mon Sep 17 00:00:00 2001
From: Stijn Buys <ingar@osirion.org>
Date: Sat, 15 Aug 2009 08:18:13 +0000
Subject: don't render entites behind the camera

---
 src/client/keyboard.cc  |   2 +-
 src/client/targets.cc   |   1 -
 src/core/range.h        |  13 ++-
 src/model/asefile.cc    |   1 -
 src/model/mapfile.cc    | 235 +++++++++++++++++++++++++++---------------------
 src/model/parts.cc      |   2 +-
 src/model/parts.h       | 103 ++++++++++++++-------
 src/render/draw.cc      |  15 ++--
 src/render/renderext.cc |  55 +++++++-----
 src/render/renderext.h  |   8 ++
 10 files changed, 261 insertions(+), 174 deletions(-)

diff --git a/src/client/keyboard.cc b/src/client/keyboard.cc
index ba67395..23e6634 100644
--- a/src/client/keyboard.cc
+++ b/src/client/keyboard.cc
@@ -176,7 +176,7 @@ Keyboard::Keyboard()
 	add_key("pageup", SDLK_PAGEUP);
 	add_key("pagedown", SDLK_PAGEDOWN);
 
-	add_key("f1", SDLK_F1, 0, "ui_menu");
+	add_key("f1", SDLK_F1);
 	add_key("f2", SDLK_F2);
 	add_key("f3", SDLK_F3, 0, "@dock");
 	key = add_key("f4", SDLK_F4);
diff --git a/src/client/targets.cc b/src/client/targets.cc
index 496f4e5..e478d84 100644
--- a/src/client/targets.cc
+++ b/src/client/targets.cc
@@ -41,7 +41,6 @@ core::Cvar *snd_engines = 0;
 
 bool is_valid_hud_target(core::Entity *entity)
 {
-
 	if (entity->serverside()) {
 		return false;
 	} else if (!ext_render(entity)) {
diff --git a/src/core/range.h b/src/core/range.h
index aa194bb..d530f0b 100644
--- a/src/core/range.h
+++ b/src/core/range.h
@@ -9,19 +9,18 @@
 
 namespace core {
 
+/**
+ * @brief range and scale constants
+ */
 namespace range {
-	/// maximal visiblae range (world distance)
+	/// maximal visible range (world distance)
 	/** This is the distance of the frustum far plane, 
-	 *  the maximal distance at which non-controlable entities can be drawn.
-	 *  the maximal radar range for controlable entities,
+	 *  the maximal distance at which entities can be drawn.
+	 *  the maximal radar range
 	 *  and the maximal range to send entity updates 
 	 */
 	const float maxdistance = 1024.0f;
 
-	/// maximal visible range for controlable entities  (world distance)
-	const float maxvisible = 512.0f;
-
-
 	/// detail/fx distance  (world distance)
 	const float fxdistance = 256.0f;
 }
diff --git a/src/model/asefile.cc b/src/model/asefile.cc
index edbd848..a9e57d4 100644
--- a/src/model/asefile.cc
+++ b/src/model/asefile.cc
@@ -462,7 +462,6 @@ Model * ASEFile::load(const std::string &name)
 		fragment->add_vertex((triangle->v2() - center) * scale , triangle->n2(), triangle->t2(), false);
 		
 		model->model_tris_count++;
-		model->model_tris_detail_count++;
 	}
 
 	model->add_group(group);
diff --git a/src/model/mapfile.cc b/src/model/mapfile.cc
index 36dc536..a86e06e 100644
--- a/src/model/mapfile.cc
+++ b/src/model/mapfile.cc
@@ -1018,85 +1018,55 @@ Model * MapFile::load(std::string const &name)
 			// new light
 			light = new Light();
 			model->add_light(light);
+			continue;
 			
 		} else if (mapfile.classname().compare("light") == 0) {
 		
 			// light attributes
 			if (mapfile.got_key_vector3f("origin", location)) {
-				light->set_location(location * SCALE);
+				light->get_location().assign(location * SCALE);
 				continue;
 				
-			} else if (mapfile.got_key_color("_color", light->light_color)) {
+			} else if (mapfile.got_key_color("_color", color)) {
+				light->get_color().assign(color);
 				continue;
 				
 			} else if (mapfile.got_key_int("spawnflags", u)) {
 				light->set_strobe(spawnflag_isset(u, 1));
 				light->set_entity(spawnflag_isset(u, 2));
 				light->set_engine(spawnflag_isset(u, 4));
+				continue;
 				
-			} else if (mapfile.got_key_float("light", light->light_radius)) {
-				light->light_radius *= LIGHTSCALE;
+			} else if (mapfile.got_key_float("light", r)) {
+				light->set_radius( r * LIGHTSCALE);
+				continue;
 				
-			} else if (mapfile.got_key_float("radius", light->light_radius)) {
-				light->light_radius *= LIGHTSCALE;
+			} else if (mapfile.got_key_float("radius", r)) {
+				light->set_radius( r * LIGHTSCALE);
+				continue;
 
-			} else if (mapfile.got_key_float("frequency", light->light_frequency)) {
+			} else if (mapfile.got_key_float("frequency", r)) {
+				light->set_frequency(r);
 				continue;
 				
-			} else if (mapfile.got_key_float("offset", light->light_offset)) {
+			} else if (mapfile.got_key_float("offset", r)) {
+				light->set_offset(r);
 				continue;
 				
-			} else if (mapfile.got_key_float("time", light->light_time)) {
+			} else if (mapfile.got_key_float("time", r)) {
+				light->set_time(r);
 				continue;
 				
-			}  else if (mapfile.got_key_int("flare", light->light_flare)) {
+			}  else if (mapfile.got_key_int("flare", u)) {
+				light->set_flare(u);
 				continue;
 				
 			} else if (mapfile.got_key()) {
 				mapfile.unknown_key();
-				
-			}
-			
-		} else if (mapfile.got_classname("location_dock")) {
-
-			// new docking location
-			dock = new Dock();
-			model->add_dock(dock);
-
-		} else if (mapfile.classname().compare("location_dock") == 0) {
-
-			// dock attributes
-			if (mapfile.got_key_vector3f("origin", dock->dock_location)) {
-				dock->dock_location *= SCALE;
-				continue;
-							
-			} else if (mapfile.got_key_float("radius", dock->dock_radius)) {
-				dock->dock_radius *= SCALE;
-				continue;
-							
-			} else if (mapfile.got_key("angle")) {
 				continue;
-
-			} else if (mapfile.got_key()) {
-				mapfile.unknown_key();
-	
+				
 			}
 
-		} else if (mapfile.got_classname("location_cannon")) {
-			// new cannon
-
-		} else if (mapfile.classname().compare("location_cannon") == 0) {
-
-		} else if (mapfile.got_classname("location_turret")) {
-			// new turret
-
-		} else if (mapfile.classname().compare("location_turret") == 0) {
-
-		} else if (mapfile.got_classname("location_cockpit")) {
-			// cockpit location
-
-		} else if (mapfile.classname().compare("location_cockpit") == 0) {
-
 		} else if (mapfile.got_classname("fx_flare")) {
 		
 			// new flare
@@ -1107,10 +1077,11 @@ Model * MapFile::load(std::string const &name)
 		
 			// flare attributes
 			if (mapfile.got_key_vector3f("origin", location)) {
-				flare->set_location(location * SCALE);
+				flare->get_location().assign(location * SCALE);
 				continue;
 				
-			} else if (mapfile.got_key_color("_color", flare->light_color)) {
+			} else if (mapfile.got_key_color("_color", color)) {
+				flare->get_color().assign(color);
 				continue;
 				
 			} else if (mapfile.got_key_int("spawnflags", u)) {
@@ -1118,19 +1089,24 @@ Model * MapFile::load(std::string const &name)
 				flare->set_entity(spawnflag_isset(u, 2));
 				flare->set_engine(spawnflag_isset(u, 4));
 				
-			} else if (mapfile.got_key_float("radius", flare->light_radius)) {
-				flare->light_radius *= LIGHTSCALE;
-		
-			} else if (mapfile.got_key_float("frequency", flare->light_frequency)) {
+			} else if (mapfile.got_key_float("radius", r)) {
+				flare->set_radius( r * LIGHTSCALE);
+				continue;
+
+			} else if (mapfile.got_key_float("frequency", r)) {
+				flare->set_frequency(r);
 				continue;
 				
-			} else if (mapfile.got_key_float("offset", flare->light_offset)) {
+			} else if (mapfile.got_key_float("offset", r)) {
+				flare->set_offset(r);
 				continue;
 				
-			} else if (mapfile.got_key_float("time", flare->light_time)) {
+			} else if (mapfile.got_key_float("time", r)) {
+				flare->set_time(r);
 				continue;
 				
-			}  else if (mapfile.got_key_int("flare", flare->light_flare)) {
+			}  else if (mapfile.got_key_int("flare", u)) {
+				flare->set_flare(u);
 				continue;
 
 			} else if (mapfile.got_key_float("angle", angle)) {
@@ -1167,46 +1143,6 @@ Model * MapFile::load(std::string const &name)
 				mapfile.unknown_key();
 			}
 
-		} else if (mapfile.got_classname("misc_model")) {
-
-			// new submodel
-			submodel = new SubModel();
-			submodel_list.push_back(submodel);
-			
-		} else if (mapfile.classname().compare("misc_model") == 0) {
-
-			// submodel attributes
-			if (mapfile.got_key_vector3f("origin", location)) {
-				submodel->set_location(location * SCALE);
-				continue;
-
-			} else if (mapfile.got_key_string("model", modelname)) {
-
-				// remove extension
-				if (modelname[modelname.size()-4] == '.') {
-					modelname.erase(modelname.size()-4);
-				}
-
-				submodel->set_name(modelname);
-				continue;
-
-			} else if (mapfile.got_key_float("angle", angle)) {
-				if (angle == ANGLEUP) {
-					submodel->axis().change_pitch(90.0f);
-				} else if (angle == ANGLEDOWN) {
-					submodel->axis().change_pitch(-90.0f);
-				} else {
-					submodel->axis().change_direction(angle);
-				}
-
-			} else if (mapfile.got_key_float("modelscale", s)) {
-				if (s) {
-					submodel->set_scale(s);
-				} else {
-					submodel->set_scale(1.0f);
-				}
-			}
-			
 		} else if (mapfile.got_classname("fx_particles")) {
 
 			// new particle system
@@ -1217,10 +1153,13 @@ Model * MapFile::load(std::string const &name)
 
 			// particle system attributes
 			if (mapfile.got_key_vector3f("origin", location)) {
-				particles->set_location(location * SCALE);
+				particles->get_location().assign(location * SCALE);
 				continue;
-			}  else if (mapfile.got_key_string("script", particles->particles_script)) {
+
+			}  else if (mapfile.got_key_string("script", str)) {
+				particles->set_script(str);
 				continue;
+
 			} else if (mapfile.got_key_float("angle", angle)) {
 				if (angle == ANGLEUP) {
 					particles->get_axis().change_pitch(90.0f);
@@ -1261,7 +1200,99 @@ Model * MapFile::load(std::string const &name)
 			} else if (mapfile.got_key()) {
 				mapfile.unknown_key();
 			}
+
+		} else if (mapfile.got_classname("misc_model")) {
+
+			// new submodel
+			submodel = new SubModel();
+			submodel_list.push_back(submodel);
+			
+		} else if (mapfile.classname().compare("misc_model") == 0) {
+
+			// submodel attributes
+			if (mapfile.got_key_vector3f("origin", location)) {
+				submodel->set_location(location * SCALE);
+				continue;
+
+			} else if (mapfile.got_key_string("model", modelname)) {
+
+				// remove extension
+				if (modelname[modelname.size()-4] == '.') {
+					modelname.erase(modelname.size()-4);
+				}
+
+				submodel->set_name(modelname);
+				continue;
+
+			} else if (mapfile.got_key_float("angle", angle)) {
+				if (angle == ANGLEUP) {
+					submodel->axis().change_pitch(90.0f);
+				} else if (angle == ANGLEDOWN) {
+					submodel->axis().change_pitch(-90.0f);
+				} else {
+					submodel->axis().change_direction(angle);
+				}
+
+			} else if (mapfile.got_key_float("modelscale", s)) {
+				if (s) {
+					submodel->set_scale(s);
+				} else {
+					submodel->set_scale(1.0f);
+				}
+			}
+		
+		} else if (mapfile.got_classname("location_dock")) {
+
+			// new docking location
+			dock = new Dock();
+			model->add_dock(dock);
+
+		} else if (mapfile.classname().compare("location_dock") == 0) {
+
+			// dock attributes
+			if (mapfile.got_key_vector3f("origin", location)) {
+				dock->get_location().assign(location * SCALE);
+				continue;
+							
+			} else if (mapfile.got_key_float("radius", r)) {
+				dock->set_radius (r * SCALE);
+				continue;
+							
+			} else if (mapfile.got_key("angle")) {
+				// TODO
+				continue;
+
+			} else if (mapfile.got_key()) {
+				mapfile.unknown_key();
+	
+			}
+
+		} else if (mapfile.got_classname("location_cannon")) {
+			// TODO cannon attachment point
+			continue;
+
+		} else if (mapfile.classname().compare("location_cannon") == 0) {
+			// TODO cannon options
+			continue;
+
+		} else if (mapfile.got_classname("location_turret")) {
+			// TODO turret attachment point
+			continue;
+
+		} else if (mapfile.classname().compare("location_turret") == 0) {
+			// TODO turret options
+			continue;
+
+		} else if (mapfile.got_classname("location_cockpit")) {
+			// TODO cockpit location
+			continue;
+
+		} else if (mapfile.classname().compare("location_cockpit") == 0) {
+			// TODO cockpit options
+			continue;
+
 		} else if (mapfile.got_classname()) {
+
 				mapfile.unknown_class();
 		}
 	}
@@ -1282,7 +1313,7 @@ Model * MapFile::load(std::string const &name)
 	}
 
 	for (Model::Docks::iterator dit = model->docks().begin(); dit != model->docks().end(); dit++) {
-		(*dit)->dock_location -= mapfile.map_center;
+		(*dit)->get_location() -= mapfile.map_center;
 	}
 
 	// FIXME this will go wrong if a Rotate group is imported as submodel
diff --git a/src/model/parts.cc b/src/model/parts.cc
index bcd8446..eabc108 100644
--- a/src/model/parts.cc
+++ b/src/model/parts.cc
@@ -25,7 +25,7 @@ Light::Light() :
 
 	light_flare = 0;
 	
-	render_texture = 0;
+	light_texture = 0;
 }
 
 Light::~Light()
diff --git a/src/model/parts.h b/src/model/parts.h
index 78ad5e4..c3a0401 100644
--- a/src/model/parts.h
+++ b/src/model/parts.h
@@ -120,7 +120,7 @@ public:
 	}
 
 	/// true if this light has engine activation
-	inline const bool engine() const
+	inline bool engine() const
 	{
 		return light_engine;
 	}
@@ -137,7 +137,7 @@ public:
 		return light_offset;
 	}
 	
-	/// frequency in strobes per second
+	/// strobe frequency in strobes per second, default is 1.0f
 	inline float frequency() const
 	{
 		return light_frequency;
@@ -155,41 +155,78 @@ public:
 		return light_flare;
 	}
 	
-	/// render texture number
+	/// render texture id
 	inline size_t texture() const
 	{
-		return render_texture;
+		return light_texture;
 	}
 	/* ---- mutators ------------------------------------------- */
 
-	/// set strobe on or off
-	inline void set_strobe(bool strobe) { light_strobe = strobe; }
+	/**
+	 * @brief set strobe color on or off
+	 */
+	inline void set_strobe(const bool strobe) { light_strobe = strobe; }
 
 	/**
 	 * @brief set entity color on or off
 	 */
-	inline void set_entity(bool entity) { light_entity = entity; }
+	inline void set_entity(const bool entity) { light_entity = entity; }
 
 	/**
 	 * @brief set engine activation on or off
 	 */
-	inline void set_engine(bool engine) { light_engine = engine; }
+	inline void set_engine(const bool engine) { light_engine = engine; }
 
+	/**
+	 * @brief set the light radius
+	 */
+	inline void set_radius(const float radius) { light_radius = radius; }
 
-	math::Color		light_color;
-	float			light_radius;
-	float			light_frequency;
-	float			light_offset;
-	float			light_time;
+	/**
+	 * @brief set the light strobe frequency, in strobes per second
+	 */
+	inline void set_frequency(const float frequency) { light_frequency = frequency; }
 
-	unsigned int		light_flare;
+	/**
+	 * @brief set the light on time, from 0.0 (always off) to 1.0 (always on)
+	 */
+	inline void set_time(const float time) { light_radius = time; }
 
-	size_t			render_texture;
+	/**
+	 * @brief set the light strobe time offset, in seconds
+	 */
+	inline void set_offset(const float offset) { light_offset = offset; }
+
+	/**
+	 * @brief set the flare texture number
+	 */
+	inline void set_flare(unsigned int flare) { light_flare = flare; }
+
+	/**
+	 * @brief set the render texture id
+	 */
+	inline void set_texture(size_t texture) { light_texture = texture; }
 
+	/**
+	 * @brief mutable reference to the color
+	 */
+	inline math::Color& get_color() { return light_color; }
+	
 private:
 	bool			light_strobe;
 	bool			light_engine;
 	bool			light_entity;
+
+	unsigned int		light_flare;
+
+	float			light_radius;
+	float			light_frequency;
+	float			light_offset;
+	float			light_time;
+
+	math::Color		light_color;
+
+	size_t			light_texture;
 };
 
 /* ---- class Flare ------------------------------------------------ */
@@ -223,11 +260,11 @@ public:
 	/**
 	 * @brief mutable reference to the axis
 	 */
-	inline math::Axis &get_axis() { return flare_axis; }
+	inline math::Axis& get_axis() { return flare_axis; }
 
 private:
-	math::Axis		flare_axis;
 	Cull			flare_cull;
+	math::Axis		flare_axis;
 };
 
 /* ---- class Particles -------------------------------------------- */
@@ -285,7 +322,9 @@ public:
 	inline void set_radius(const float radius) { particles_radius = radius;	}
 
 	inline void set_cull(const Cull cull) { particles_cull = cull; }
-	
+
+	inline void set_script(const std::string &script) { particles_script.assign(script); }
+
 	/* ---- actors --------------------------------------------- */
 
 	/**
@@ -293,39 +332,37 @@ public:
 	 */
 	inline math::Axis &get_axis() { return particles_axis; }
 
-	std::string		particles_script;
-
 private:
-	math::Axis		particles_axis;	
-	float			particles_radius;
 	bool			particles_entity;
 	bool			particles_engine;
+
 	Cull			particles_cull;
+
+	float			particles_radius;
+
+	math::Axis		particles_axis;	
+	std::string		particles_script;
 };
 
 /* ---- class Dock ------------------------------------------------- */
 
 /// a docking location
-class Dock
+class Dock : public Part
 {
 public:
 	Dock();
 	~Dock();
 	
-	/// location of the dock
-	inline const math::Vector3f & location() const
-	{
-		return dock_location;
-	}
-	
-	/// trigger distance default is 0.01f
+	/// dock radius, default is 0.01f
 	inline float radius() const
 	{
 		return dock_radius;
 	}
-	
-	
-	math::Vector3f		dock_location;
+
+	/// set dock radius	
+	inline void set_radius(const float radius) { dock_radius = radius; }	
+
+private:
 	float			dock_radius;
 };
 
diff --git a/src/render/draw.cc b/src/render/draw.cc
index 0b6fba2..e9e655c 100644
--- a/src/render/draw.cc
+++ b/src/render/draw.cc
@@ -97,7 +97,7 @@ void pass_prepare(float seconds)
 			core::EntityGlobe *globe = static_cast<core::EntityGlobe *>(entity);
 
 			// add the globe to the globes list
-			if (globe->visible()) {
+			if (globe->visible() && !ext_render(globe)->behind()) {
 				globes_list[ext_render(globe)->distance()] = globe;
 			}
 
@@ -495,7 +495,7 @@ void draw_pass_default()
 	for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); ++it) {
 
 		core::Entity *entity = (*it);
-		if (!entity->serverside() && !entity->model() && (entity->type() != core::Entity::Globe)) {
+		if (!entity->model() && (entity->type() != core::Entity::Globe) && !entity->serverside() && !ext_render(entity)->behind()) {
 
 			gl::push();
 			gl::translate(entity->location());
@@ -854,7 +854,7 @@ void draw_pass_model_fragments()
 	for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) {
 		core::Entity *entity = (*it);
 
-		if (entity->model() && ext_render(entity)->visible()) {
+		if (entity->model() && ext_render(entity)->visible() && !ext_render(entity)->behind()) {
 			gl::push();
 			gl::translate(entity->location());
 			gl::multmatrix(entity->axis());
@@ -1209,7 +1209,6 @@ void draw(float seconds)
 	gl::enable(GL_CULL_FACE);	// enable culling
 	gl::enable(GL_COLOR_MATERIAL);	// enable color tracking
 
-	gl::enable(GL_LIGHTING);	// enable lighting
 	if (r_normalize && r_normalize->value()) {
 		// enable full normalization
 		gl::enable(GL_NORMALIZE);
@@ -1217,6 +1216,8 @@ void draw(float seconds)
 		// enable rescaling of normals
 		gl::enable(GL_RESCALE_NORMAL);
 	}
+
+	gl::enable(GL_LIGHTING);	// enable lighting
 	
 	draw_pass_globes();		// draw globes
 
@@ -1224,6 +1225,8 @@ void draw(float seconds)
 
 	draw_pass_model_fragments();
 
+	gl::disable(GL_LIGHTING);	// disable lighting
+
 	if (r_normalize && r_normalize->value()) {
 		// disable full normalization
 		gl::disable(GL_NORMALIZE);
@@ -1231,7 +1234,6 @@ void draw(float seconds)
 		// disable resaling of normals
 		gl::disable(GL_RESCALE_NORMAL);
 	}
-	gl::disable(GL_LIGHTING);	// disable lighting
 	
 	gl::enable(GL_BLEND);
 	gl::depthmask(GL_FALSE);	// disable depth buffer writing
@@ -1284,6 +1286,7 @@ void draw(float seconds)
 	// GL_BLEND must be enabled for the GUI
 }
 
+// draw HUD target world space geometry, like dock indicators
 void draw_target(core::Entity *entity)
 {
 	model::Model *model = entity->model();	
@@ -1294,7 +1297,7 @@ void draw_target(core::Entity *entity)
 		return;
 
 	float d = math::distance(core::localcontrol()->location(), entity->location()) - entity->radius() - core::localcontrol()->radius();
-	if (d > 100.0f)
+	if (d > core::range::fxdistance)
 		return;
 
 	gl::enable(GL_DEPTH_TEST);
diff --git a/src/render/renderext.cc b/src/render/renderext.cc
index b0462d4..b743686 100644
--- a/src/render/renderext.cc
+++ b/src/render/renderext.cc
@@ -41,7 +41,7 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re
 			// load light texture
 			std::stringstream flarename;
 			flarename << "textures/fx/flare" << std::setfill('0') << std::setw(2) << light->flare();
-			light->render_texture = Textures::load(flarename.str());
+			light->set_texture(Textures::load(flarename.str()));
 		}
 
 		for (model::Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) {
@@ -50,7 +50,7 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re
 			// load flare texture
 			std::stringstream flarename;
 			flarename << "textures/fx/flare" << std::setfill('0') << std::setw(2) << flare->flare();
-			flare->render_texture = Textures::load(flarename.str());
+			flare->set_texture(Textures::load(flarename.str()));
 		}
 
 		for(model::Model::ParticleSystems::iterator pit = model->particles().begin(); pit != model->particles().end(); pit++) {
@@ -103,6 +103,10 @@ void RenderExt::frame(float elapsed)
 
 	state_visible = entity()->visible();
 	state_detailvisible = false;
+	state_behind = false;
+
+	if (!state_visible)
+		return;
 
 	if ((entity()->type() == core::Entity::Controlable)) {
 		if (static_cast<core::EntityDynamic *>(entity())->state() == core::Entity::Docked) {
@@ -111,37 +115,44 @@ void RenderExt::frame(float elapsed)
 		}
 	}
 
-	if (state_visible && entity()->model()) {
+	if (!entity()->model())
+		return;
 
-		if (distance() < core::range::fxdistance) {
-			// entity within detail range
-			state_visible = true;
-			state_detailvisible = true;
+	if (distance() < core::range::fxdistance) {
+		// entity within detail range
+		state_visible = true;
+		state_detailvisible = true;
+
+	} else if (distance() < core::range::maxdistance) {
+
+		// funky radius factor
+		float r = entity()->model()->radius();
+		math::clamp(r, entity()->model()->radius(), core::range::maxdistance / core::range::fxdistance);
 
-		} else if (distance() < core::range::maxvisible) {
+		if (distance() < core::range::fxdistance * r) {
 			// entity within drawing distance, outside detail range
 			state_visible = true;
 			state_detailvisible = false;
 
-		} else if (distance() < core::range::maxdistance) {
-
-			if ((entity()->type() == core::Entity::Controlable)) {
-				// controlable entity out of range
-				state_visible = false;
-				state_detailvisible = false;
-			} else {
-				// entity within drawing distance, outside detail range
-				state_visible = true;
-				state_detailvisible = false;
-
-			}
 		} else {
-			// entity out of range
-			state_visible = false;
+			// entity within drawing distance, outside detail range
+			state_visible = true;
 			state_detailvisible = false;
 		}
+		
+	} else {
+		// entity out of range
+		state_visible = false;
+		state_detailvisible = false;
+		return;
 	}
 
+	if (math::dotproduct(Camera::axis().forward(), entity()->location() + Camera::axis().forward() * entity()->radius() - Camera::eye()) < 0.0f) {
+		state_behind = true;
+	}
+
+	
+
 }
 
 } // namespace render
diff --git a/src/render/renderext.h b/src/render/renderext.h
index 28a2c23..3834769 100644
--- a/src/render/renderext.h
+++ b/src/render/renderext.h
@@ -26,8 +26,14 @@ public:
 	virtual void frame(float elapsed);
 
 	inline bool visible() const { return state_visible; }
+
 	inline bool detailvisible() const { return state_detailvisible; }
 
+	/**
+	 * true if the entity is behind the camera
+	 */
+	inline bool behind() const { return state_behind; }
+
 	inline float fuzz() const {  return state_fuzz; }
 
 	/// distance to the camera
@@ -35,9 +41,11 @@ public:
 
 	/// particles
 	inline ParticleSystems &particles() { return state_particles; }
+
 private:
 	bool 			state_visible;
 	bool 			state_detailvisible;
+	bool			state_behind;
 	
 	float 			state_fuzz;
 	float 			state_distance;
-- 
cgit v1.2.3