Hard Light Productions Forums

Modding, Mission Design, and Coding => FS2 Open Coding - The Source Code Project (SCP) => Topic started by: Rga_Noris on February 20, 2014, 11:47:14 pm

Title: SubObject Vert Limits (and canteloupe)
Post by: Rga_Noris on February 20, 2014, 11:47:14 pm
So I had a discussion with Zacam regarding the vert limitations in FSO. Here is my concern:

Currently, FSO eats polies/verts for breakfast. The problem is that there is a 64K vert limit per subobject. The issue is that this limit applies to ALL objects, detail0 included. With models becoming more and more complicated, this vert limit is easy to hit, meaning that in order to have complex and detailed detail0 meshes, we must only make a portion the mesh the 'actual' detail0, and the rest is actually subobjects attached to detail0 (the Sath is this way.... detail0 is just from the 'neck' back... the head and arms are actually just a subobject).

This is an issue because while FSO can eat polygon flavored Doritos all day, it slows down when more subobjects are introduced... the current vert limit requires we introduce more subobjects the more complicated the model gets. We could get more performance milage if we were able to make detail0 one single object as opposed to multiple.

Oh, and as promised:

(http://www.flowfit.ca/blog/wp-content/uploads/cantaloupe.jpg)

EDIT: More reasonable melon image.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: The E on February 21, 2014, 12:18:15 am
The problem lies not only with fso, but also with the pof format. In order to allow submeshes that large, we would also need to make changes there.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Rga_Noris on February 21, 2014, 12:34:59 am
I'm going to guess that updating that format would be a sticky wicket. As in FSO would need to use a new format, thus making old POF's broked.

What if we used honey dew melon instead? I think the problem lies in melon choice.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Fury on February 21, 2014, 12:44:25 am
The problem lies not only with fso, but also with the pof format. In order to allow submeshes that large, we would also need to make changes there.
At some point some serious consideration should be made to support Collada...
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: m!m on February 21, 2014, 05:46:32 am
We could use an external library like assimp (http://assimp.sourceforge.net/) to handle to actual loading. We would need some way to load the exta data which is currently stored inside the POF file like gunpoints or dockingpoints.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Rga_Noris on February 21, 2014, 06:35:32 am
Would such a library require another item to install, ala OpenAL?
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: The E on February 21, 2014, 06:41:07 am
No, assimp is a single DLL that can be shipped with the executables (maybe there are static link options available, I don't know). It would definitely not require additional installers to run.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: FUBAR-BDHR on February 21, 2014, 10:08:59 am
Are there any facts behind the use of multiple subobjects slowing down things or is it just old rumors?  We heard the same things back when Diaspora R1 was in development and did some extensive testing only to find no difference and I believe in some cases performance slowdowns when models made up of multiple small subobjects were combined into a single large detail0.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: zookeeper on February 21, 2014, 11:07:55 am
Well if you have to split a large submodel into 3 smaller ones then I doubt there's any measurable performance loss unless you have obscene amounts of those models on-screen. I believe it's more a case of rendering of for example capships with lots of turrets spending most of the time switching context from one submodel to another.

I'd think that practically any ship detailed enough to require splitting submodels would have a whole lot more turrets or other modelled subsystems to make the rendering performance loss caused by the splitting effectively meaningless.

Collision detection might be a slightly different story though; if the bounding boxes of your submodels don't overlap much, then I'd think it possible that splitting could even improve performance if some of those submodels get culled immediately with cheap bounding box checks. In any case, when hundreds of weapons are flying around, the performance drop from complicated models seems to mostly come from collision detection, not rendering. Especially when the collision detection needs to be done even when you're looking the other way.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Bobboau on February 21, 2014, 11:52:16 pm
Where exactly is the problem? I remember back in the day implementing 32bit index buffers, there was a time when that was working I thought.
are we sure this is not simply a PCS2 bug?
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Zacam on February 22, 2014, 12:25:23 am
The index buffer may very well be 32 bit, which will be nice. But the value declaration being stored there is still in ushort.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: The E on February 22, 2014, 04:12:02 am
Okay, so here's a quick patch for the FSO side of things. Be aware I haven't been able to test it yet due to a lack of matching PCS2:

Code: [Select]
Index: code/model/model.h
===================================================================
--- code/model/model.h (revision 10471)
+++ code/model/model.h (working copy)
@@ -265,6 +265,13 @@
  float u,v;
 } model_tmap_vert;
 
+// The E: This struct is used by POF models version 2118 and up, for models with subobjects that have >2^16 vertices
+typedef struct model_tmap_vert_large {
+ uint vertnum;
+ uint normnum;
+ float u, v;
+} model_tmap_vert_large;
+
 struct bsp_collision_node {
  vec3d min;
  vec3d max;
Index: code/model/modelinterp.cpp
===================================================================
--- code/model/modelinterp.cpp (revision 10471)
+++ code/model/modelinterp.cpp (working copy)
@@ -68,7 +68,7 @@
 // Local variables
 //
 
-static int Num_interp_verts_allocated = 0;
+static uint Num_interp_verts_allocated = 0;
 vec3d **Interp_verts = NULL;
 static vertex *Interp_points = NULL;
 static vertex *Interp_splode_points = NULL;
@@ -97,7 +97,7 @@
 // -------------------------------------------------------------------
 
 
-static int Num_interp_norms_allocated = 0;
+static uint Num_interp_norms_allocated = 0;
 static vec3d **Interp_norms = NULL;
 static ubyte *Interp_light_applied = NULL;
 static int Interp_num_norms = 0;
@@ -217,7 +217,7 @@
 extern void model_collide_allocate_point_list(int n_points);
 extern void model_collide_free_point_list();
 
-void model_allocate_interp_data(int n_verts = 0, int n_norms = 0, int n_list_verts = 0)
+void model_allocate_interp_data(uint n_verts = 0, uint n_norms = 0, int n_list_verts = 0)
 {
  static ubyte dealloc = 0;
 
@@ -746,11 +746,17 @@
 ubyte Interp_subspace_g = 255;
 ubyte Interp_subspace_b = 255;
 
+#define GET_VERT_NUM(i) verts != NULL ? verts[i].vertnum : vert_large[i].vertnum
+#define GET_NORM_NUM(i) verts != NULL ? verts[i].normnum : vert_large[i].normnum
+#define GET_TEXCO_U(i) verts != NULL ? verts[i].u : vert_large[i].u
+#define GET_TEXCO_V(i) verts != NULL ? verts[i].v : vert_large[i].v
+
 void model_interp_tmappoly(ubyte * p,polymodel * pm)
 {
  int i;
  int nv;
  model_tmap_vert *verts;
+ model_tmap_vert_large *vert_large;
  int cull = 0;
 
  // Goober5000
@@ -793,14 +799,23 @@
  if ( nv < 0 )
  return;
 
- verts = (model_tmap_vert *)(p+44);
+ if (pm->version < 2118) {
+ verts = (model_tmap_vert *) (p + 44);
+ vert_large = NULL;
+ }
+ else {
+ vert_large = (model_tmap_vert_large *) (p + 44);
+ verts = NULL;
+ }
 
- int max_n_verts = 0;
- int max_n_norms = 0;
+ Assertion(verts != NULL || vert_large != NULL, "No valid vertex data found in pof file!\n");
 
+ uint max_n_verts = 0;
+ uint max_n_norms = 0;
+
  for (i = 0; i < nv; i++) {
- max_n_verts = MAX(verts[i].vertnum + 1, max_n_verts);
- max_n_norms = MAX(verts[i].normnum + 1, max_n_norms);
+ max_n_verts = MAX(GET_VERT_NUM(i) + 1, max_n_verts);
+ max_n_norms = MAX(GET_NORM_NUM(i) + 1, max_n_norms);
  }
 
  model_allocate_interp_data(max_n_verts, max_n_norms, nv);
@@ -814,14 +829,14 @@
 
  if(splodeing){
  float salpha = 1.0f - splode_level;
- for (i=0;i<nv;i++){
+ for (i = 0; i < nv; i++){
  Interp_list[i] = &Interp_splode_points[verts[i].vertnum];
- Interp_list[i]->texture_position.u = verts[i].u*2;
- Interp_list[i]->texture_position.v = verts[i].v*2;
- Interp_list[i]->r = (unsigned char)(255*salpha);
- Interp_list[i]->g = (unsigned char)(250*salpha);
- Interp_list[i]->b = (unsigned char)(200*salpha);
- model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], salpha, false);
+ Interp_list[i]->texture_position.u = verts[i].u * 2;
+ Interp_list[i]->texture_position.v = verts[i].v * 2;
+ Interp_list[i]->r = (unsigned char) (255 * salpha);
+ Interp_list[i]->g = (unsigned char) (250 * salpha);
+ Interp_list[i]->b = (unsigned char) (200 * salpha);
+ model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[GET_VERT_NUM(i)], Interp_norms[GET_NORM_NUM(i)], salpha, false);
  }
  cull = gr_set_cull(0);
  gr_set_bitmap( splodeingtexture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, salpha );
@@ -832,11 +847,13 @@
  }
 
  for (i=0;i<nv;i++) {
- Interp_list[i] = &Interp_points[verts[i].vertnum];
+
+ Interp_list[i] = &Interp_points[GET_VERT_NUM(i)];
 
- Interp_list[i]->texture_position.u = verts[i].u;
- Interp_list[i]->texture_position.v = verts[i].v;
+ Interp_list[i]->texture_position.u = GET_TEXCO_U(i);
+ Interp_list[i]->texture_position.v = GET_TEXCO_V(i);
 
+
  if ( Interp_subspace ) {
  Interp_list[i]->texture_position.v += Interp_subspace_offset_u;
  Interp_list[i]->texture_position.u += Interp_subspace_offset_v;
@@ -854,11 +871,11 @@
  Interp_list[i]->spec_b = 0;
 
  if (Interp_flags & MR_EDGE_ALPHA) {
- model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, false);
+ model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[GET_VERT_NUM(i)], Interp_norms[GET_NORM_NUM(i)], Interp_warp_alpha, false);
  }
 
  if (Interp_flags & MR_CENTER_ALPHA) {
- model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, true);
+ model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[GET_VERT_NUM(i)], Interp_norms[GET_NORM_NUM(i)], Interp_warp_alpha, true);
  }
 
  SPECMAP = -1;
@@ -867,8 +884,8 @@
  MISCMAP = -1;
  } else {
 
- int vertnum = verts[i].vertnum;
- int norm = verts[i].normnum;
+ uint vertnum = GET_VERT_NUM(i);
+ uint norm = GET_NORM_NUM(i);
 
  if ( Interp_flags & MR_NO_SMOOTHING ) {
  light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
Index: code/model/modeloctant.cpp
===================================================================
--- code/model/modeloctant.cpp (revision 10471)
+++ code/model/modeloctant.cpp (working copy)
@@ -19,7 +19,7 @@
 #include "model/modelsinc.h"
 #include "cmdline/cmdline.h"
 
-extern void model_allocate_interp_data(int n_verts, int n_norms, int n_list_verts = 0);
+extern void model_allocate_interp_data(uint n_verts, uint n_norms, int n_list_verts = 0);
 
 
 // returns 1 if a point is in an octant.
Index: code/model/modelread.cpp
===================================================================
--- code/model/modelread.cpp (revision 10471)
+++ code/model/modelread.cpp (working copy)
@@ -5076,7 +5076,7 @@
 }
 
 #if BYTE_ORDER == BIG_ENDIAN
-extern void model_allocate_interp_data(int, int, int);
+extern void model_allocate_interp_data(uint, uint, int);
 
 // tigital -
 void swap_bsp_defpoints(ubyte * p)

Now, here's the changes that still need to be made on the PCS2 side of things:

1. Introduce a corresponding vert_large structure in PCS2's BSP writer
2. Switch to using it when necessary (by default would be kinda bad, I think)

This, however, requires setting up to build PCS2, which as I am discovering right now is not exactly trivial :P But that should not be an insurmountable hurdle, I think.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: Bryan See on March 17, 2014, 12:41:29 am
I've got a suggestion: How about changing the "unsigned short" declaration to "unsigned long"? That would increase the 64k limit further. I know this may require some work and changes involved, but you have to do it, because I, like many modellers, wanted to do more hi-poly models.
Title: Re: SubObject Vert Limits (and canteloupe)
Post by: The E on March 17, 2014, 01:17:21 am
No, we actually do not "have" to do anything. Way I see it, this is not a critical feature, since you can already do very complex models without issues; they just won't be as efficient as possible.

Also, this "some work" you're speaking of? It's actually more like "a lot of work".

A small tip for you: Never, ever assume that just because something sounds simple, it actually will be. There's nothing more frustrating than having to read replies that spell out the easy solution without knowledge of the ramifications of said easy solutions.