unsigned RemoveOccludedObjects( Scene &scene, int width, int height ) { unsigned nOccluded = 0; HOM *head = BuildHOM( scene, width, height ); size_t nObjs = scene.objects.size(); std::vector< Box2D > projs( nObjs ); for ( size_t i = 0; i < nObjs; ++i ) { if ( !scene.objects[i].occluder ) { // get bounding box from bounding volume std::vector< Point3D > projVerts( 8 ); Object &obj = scene.objects[i]; Box3D &bound = obj.bound; projVerts[0] = GetScreenCrds( bound.origin, scene, width, height ); projVerts[1] = GetScreenCrds( Point3D( bound.origin[0], bound.origin[1], bound.extent[2] ), scene, width, height ); projVerts[2] = GetScreenCrds( Point3D( bound.origin[0], bound.extent[1], bound.extent[2] ), scene, width, height ); projVerts[3] = GetScreenCrds( Point3D( bound.extent[0], bound.origin[1], bound.extent[2] ), scene, width, height ); projVerts[4] = GetScreenCrds( Point3D( bound.extent[0], bound.extent[1], bound.origin[2] ), scene, width, height ); projVerts[5] = GetScreenCrds( Point3D( bound.extent[0], bound.origin[1], bound.origin[2] ), scene, width, height ); projVerts[6] = GetScreenCrds( Point3D( bound.origin[0], bound.extent[1], bound.origin[2] ), scene, width, height ); projVerts[7] = GetScreenCrds( bound.extent, scene, width, height ); projs[i].origin = force_cast<Point2D>( projVerts[0] ); projs[i].extent = force_cast<Point2D>( projVerts[7] ); float depth = projVerts[0][2]; for ( size_t j = 0; j < 8; ++j ) { // x bounds if ( projVerts[j][0] < projs[i].origin[0] ) projs[i].origin[0] = projVerts[j][0]; else if ( projVerts[j][0] > projs[i].extent[0] ) projs[i].extent[0] = projVerts[j][0]; // y bounds if ( projVerts[j][1] < projs[i].origin[1] ) projs[i].origin[1] = projVerts[j][1]; else if ( projVerts[j][1] > projs[i].extent[1] ) projs[i].extent[1] = projVerts[j][1]; // depth min if ( projVerts[j][2] < depth ) depth = projVerts[j][2]; } obj.occluded = Occluded( projs[i], depth, head, width, height ); if ( obj.occluded ) nOccluded++; } } delete head; return nOccluded; }
static void DrawPart(int IsModel, struct L3PartS *PartPtr, int CurColor, float m[4][4]) { float r[4], m1[4][4]; int i, Color; struct L3LineS *LinePtr; vector3d v3d[4]; #ifdef USE_OPENGL float mm[4][4]; float det = 0; if ((ldraw_commandline_opts.F & TYPE_F_STUDLINE_MODE) != 0) if (PartPtr->IsStud) { DrawPartLine(PartPtr, CurColor, m); //DrawPartBox(PartPtr, CurColor, m, 1); return; } // Draw only bounding boxes of top level parts if in fast spin mode. if (ldraw_commandline_opts.F & TYPE_F_BBOX_MODE) if (PartPtr->FromPARTS) // (!IsModel) { if (ldraw_commandline_opts.F & TYPE_F_NO_POLYGONS) DrawPartBox(PartPtr, CurColor, m, 1); else DrawPartBox(PartPtr, CurColor, m, 0); return; } if (PartPtr->IsStud) det = M3Det(m); // Check the determinant of m to fix mirrored studs. #endif for (LinePtr = PartPtr->FirstLine; LinePtr; LinePtr = LinePtr->NextLine) { #ifdef USE_OPENGL char *s; if (Skip1Line(IsModel,LinePtr)) continue; #endif hardcolor = 0; // Assume color 16 or 24. switch (LinePtr->Color) { case 16: Color = CurColor; break; case 24: #ifdef SIXTEEN_EDGE_COLORS if (0 <= CurColor && CurColor <= 15) Color = edge_color(CurColor); else Color = 0; #else Color = edge_color(CurColor); #endif break; default: Color = LinePtr->Color; #if 0 if ((LinePtr->LineType == 3) || (LinePtr->LineType == 4)) // Hardcoded color = printed. Blend me! hardcolor = 1; #else if (LinePtr->LineType != 1) // Hardcoded color = printed. Blend me! hardcolor = 1; #endif break; } switch (LinePtr->LineType) { case 0: #ifdef USE_OPENGL // Skip whitespace for (s = LinePtr->Comment; *s != 0; s++) { if ((*s != ' ') && (*s != '\t')) break; } if (strnicmp(s,"STEP",4) == 0) { // if (include_stack_ptr <= ldraw_commandline_opts.output_depth ) { zStep(stepcount,1); stepcount++; } } else if (strncmp(s,"CLEAR",5) == 0) { // if (include_stack_ptr <= ldraw_commandline_opts.output_depth ) { zClear(); } } // Experiment with MLCAD extensions // Based on info provided on lugnet. else if (strncmp(s,"BUFEXCHG A STORE",16) == 0) { int k; if (IsModel) { k = BufA1Store(IsModel,LinePtr); printf("BUFEXCHG A STORE %d\n", k); } } else if (strncmp(s,"BUFEXCHG A RETRIEVE",19) == 0) { int j,n, opts, dcp, cp; extern int curpiece; extern int DrawToCurPiece; void DrawModel(void); if (IsModel) { n = BufA1Retrieve(IsModel,LinePtr); printf("BUFEXCHG A RETRIEVE %d\n", n); // clear and redraw model up to saved point. #if 0 opts = ldraw_commandline_opts.M; ldraw_commandline_opts.M = 'C'; dcp = DrawToCurPiece; DrawToCurPiece = 1; cp = curpiece; zClear(); curpiece = n; Init1LineCounter(); BufA1Store(IsModel,LinePtr); DrawModel(); //for(j = 0; j < n; j++) // Draw1Part(j, -1); DrawToCurPiece = dcp; curpiece = cp; ldraw_commandline_opts.M = opts; #endif } } else if (strncmp(s,"GHOST",5) == 0) { if (IsModel) { // Parse the rest of the line as a .DAT line. } } if (ldraw_commandline_opts.M != 'C') { // Non-continuous output stop after each step. } // Parse global color change meta-commands // Should?? be done by ReadMetaLine() in L3Input.cpp // Should SAVE old colors, restore after the for loop. // To save space, build a linked list of saved colors. // Do NOT resave a color if its already saved. if ((strncmp(s,"COLOR",5) == 0) || (strncmp(s,"COLOUR",6) == 0)) { if (ldraw_commandline_opts.debug_level == 1) printf("%s\n", s); //0 COLOR 4 red 0 196 0 38 255 196 0 38 255 char colorstr[256]; char name[256]; int n, inverse_index; n = sscanf(s, "%s %d %s %d %f %f %f %f %f %f %f %f", colorstr, &i, name, &inverse_index, &m1[0][0], &m1[0][1], &m1[0][2], &m1[0][3], &m1[1][0], &m1[1][1], &m1[1][2], &m1[1][3]); if (n != 12) { if (ldraw_commandline_opts.debug_level == 1) printf("Illegal COLOR syntax %d\n",n); break; } zcolor_modify(i,name,inverse_index, (int)m1[0][0], (int)m1[0][1], (int)m1[0][2], (int)m1[0][3], (int)m1[1][0], (int)m1[1][1], (int)m1[1][2], (int)m1[1][3]); } // Intercept the ldconfig.ldr !COLOUR meta command. if (ldlite_parse_colour_meta(s)) break; #else if (strncmp(LinePtr->Comment,"STEP",4) == 0) { // STEP encountered, you may set some flags to enable/disable e.g. drawing // (Note that I have not tested this, but this is the way it is supposed to work :-) } #endif break; case 1: #ifdef USE_OPENGL // NOTE: I could achieve L3Lab speeds if I delay rendering studs till last // then do occlusion tests on the bounding boxes before rendering. // Unfortunately GL_OCCLUSION_TEST_HP is an extension (SUN, HP, ???) // // Other ideas include substituting GLU cylinder primitives for studs. if (LinePtr->PartPtr->IsStud) { if ((ldraw_commandline_opts.F & TYPE_F_STUDLESS_MODE) != 0) break; #ifdef USE_OPENGL_OCCLUSION if ((ldraw_commandline_opts.F & TYPE_F_STUDONLY_MODE) != 0) { M4M4Mul(m1,m,LinePtr->v); if (Occluded(LinePtr->PartPtr, Color, m1)) break; } #endif } #endif M4M4Mul(m1,m,LinePtr->v); #ifdef USE_OPENGL // Negative determinant means its a mirrored stud. if (det < 0) { // For now, we only support Paul Easters logo.dat texture. // For display speed, we really should precalculate an IsLogo flag // and use that here rather than IsStud. if (LinePtr->PartPtr && LinePtr->PartPtr->DatName && !stricmp(LinePtr->PartPtr->DatName, "logo.dat")) { float mirror[4][4] = { {1.0,0.0,0.0,0.0}, {0.0,1.0,0.0,0.0}, {0.0,0.0,-1.0,0.0}, {0.0,0.0,0.0,1.0} }; M4M4Mul(mm,m1,mirror); memcpy(m1,mm,sizeof(m1)); } } // implement nesting level counter. include_stack_ptr++; DrawPart(0,LinePtr->PartPtr,Color,m1); include_stack_ptr--; // Do zStep() after the model, not toplevel parts. #else DrawPart(0,LinePtr->PartPtr,Color,m1); if (IsModel) zStep(0,0); #endif break; case 2: #ifdef USE_OPENGL_OCCLUSION if ((ldraw_commandline_opts.F & TYPE_F_STUDONLY_MODE) && !(PartPtr->IsStud)) break; #endif for (i=0; i<LinePtr->LineType; i++) { M4V3Mul(r,m,LinePtr->v[i]); v3d[i].x=r[0]; v3d[i].y=r[1]; v3d[i].z=r[2]; } render_line(&v3d[0],&v3d[1],Color); break; case 3: #ifdef USE_OPENGL_OCCLUSION if ((ldraw_commandline_opts.F & TYPE_F_STUDONLY_MODE) && !(PartPtr->IsStud)) break; #endif for (i=0; i<LinePtr->LineType; i++) { M4V3Mul(r,m,LinePtr->v[i]); v3d[i].x=r[0]; v3d[i].y=r[1]; v3d[i].z=r[2]; } #ifdef USE_OPENGL // Try to render solid Primitives visibly when in XOR wire mode. if (ldraw_commandline_opts.F & TYPE_F_XOR_PRIMITIVE) { render_line(&v3d[0],&v3d[1],Color); render_line(&v3d[1],&v3d[2],Color); render_line(&v3d[2],&v3d[0],Color); break; } #endif render_triangle(&v3d[0],&v3d[1],&v3d[2],Color); break; case 4: #ifdef USE_OPENGL_OCCLUSION if ((ldraw_commandline_opts.F & TYPE_F_STUDONLY_MODE) && !(PartPtr->IsStud)) break; #endif for (i=0; i<LinePtr->LineType; i++) { M4V3Mul(r,m,LinePtr->v[i]); v3d[i].x=r[0]; v3d[i].y=r[1]; v3d[i].z=r[2]; } #ifdef USE_OPENGL // Try to render solid Primitives visibly when in XOR wire mode. if (ldraw_commandline_opts.F & TYPE_F_XOR_PRIMITIVE) { render_line(&v3d[0],&v3d[1],Color); render_line(&v3d[1],&v3d[2],Color); render_line(&v3d[2],&v3d[3],Color); render_line(&v3d[3],&v3d[0],Color); break; } #endif render_quad(&v3d[0],&v3d[1],&v3d[2],&v3d[3],Color); break; case 5: #ifdef USE_OPENGL_OCCLUSION if ((ldraw_commandline_opts.F & TYPE_F_STUDONLY_MODE) && !(PartPtr->IsStud)) break; #endif for (i=0; i<4; i++) { M4V3Mul(r,m,LinePtr->v[i]); v3d[i].x=r[0]; v3d[i].y=r[1]; v3d[i].z=r[2]; } render_five(&v3d[0],&v3d[1],&v3d[2],&v3d[3],Color); break; } } if (((zDetailLevel == TYPE_PART) && (PartPtr->FromPARTS)) || ((zDetailLevel == TYPE_P) && (PartPtr->FromP))) zStep(-1, 0); }
bool Occluded( const Box2D &bound, float depth, const HOM *hom, int scnWidth, int scnHeight, bool autoFind ) { // initiating case if ( autoFind ) { // find best starting level float area = bound.Area() * 100.f; float bestDif = (float)INFINITE; HOM *curLevel = const_cast<HOM *>( hom ); while ( curLevel != NULL ) { int pixArea = ( ( scnWidth / curLevel->width ) * ( scnHeight / curLevel->height ) ); float curDif = abs( (float)pixArea - area ); if ( curDif < bestDif ) { hom = curLevel; bestDif = curDif; } curLevel = curLevel->prev; } } Box2D box; float denW = (float)( scnWidth / hom->width ); float denH = (float)( scnHeight / hom->height ); box.origin[0] = (float)bound.origin[0] / denW; box.extent[0] = (float)bound.extent[0] / denW; box.origin[1] = (float)bound.origin[1] / denH; box.extent[1] = (float)bound.extent[1] / denH; // test each pixel int nOpaques = 0; int yMin = std::max( (int)( box.origin[1] + .5f ), 0 ); int yMax = std::min( (int)( box.extent[1] + .5f ), hom->height - 1 ); int xMin = std::max( (int)( box.origin[0] + .5f ), 0 ); int xMax = std::min( (int)( box.extent[0] + .5f ), hom->width - 1 ); for ( int y = yMin; y < yMax; ++y ) { for ( int x = xMin; x < xMax; ++x ) { float test = const_cast<HOM *>( hom )->Depth( x, y ); if ( ( const_cast<HOM *>( hom )->Map( x, y ) < TRANSPARENCY_THRESHOLD ) || ( depth < const_cast<HOM *>( hom )->Depth( x, y ) ) ) { return false; } if ( const_cast<HOM *>( hom )->Map( x, y ) > OPACITY_THRESHOLD ) { nOpaques++; } } } int iArea = ( ( xMax - xMin ) * ( yMax - yMin ) ); if ( nOpaques < iArea ) { if ( hom->prev != NULL ) return Occluded( bound, depth, hom->prev, scnWidth, scnHeight, false ); return false; } return true; }