/* * Copyright (c) 1993 Argonaut Software Ltd. All rights reserved. * * $Id: zbmeshe.c 1.19 1995/08/31 16:47:57 sam Exp $ * $Locker: $ * * Mesh rendering to produce points */ #include "zb.h" #include "shortcut.h" #include "blockops.h" #include "brassert.h" static char rscid[] = "$Id: zbmeshe.c 1.19 1995/08/31 16:47:57 sam Exp $"; /* * Clip a line to an arbitary plane eqn. Return true if any part * of the line remains */ STATIC int ClipLineToPlane( struct temp_vertex *in, struct temp_vertex *out, br_vector4 *plane, int cmask) { br_scalar t,tu,tv; int m; br_scalar *usp,*vsp,*wsp; tu =-BR_MAC4( plane->v[0],in[0].comp[C_X], plane->v[1],in[0].comp[C_Y], plane->v[2],in[0].comp[C_Z], plane->v[3],in[0].comp[C_W]); tv =-BR_MAC4( plane->v[0],in[1].comp[C_X], plane->v[1],in[1].comp[C_Y], plane->v[2],in[1].comp[C_Z], plane->v[3],in[1].comp[C_W]); if(tu <= S0) { /* * First vertex is inside clip space */ out[0] = in[0]; if(tv <= S0) { /* * last vertex was as well - return whole line */ out[1] = in[1]; return 1; } /* * Line crosses in to out, truncate to intersection */ t = BR_DIVR(tu,(tu-tv)); usp = in[0].comp; vsp = in[1].comp; wsp = out[1].comp; for(m = cmask ; m ; m >>=1, usp++,vsp++,wsp++) if(m & 1) *wsp = *usp + BR_MUL(t,(*vsp-*usp)); } else { /* * First vertex is outside clip space */ if(tv > S0) /* * last vertex was as well - return false */ return 0; out[1] = in[1]; /* * Line crosses out to in, truncate to intersection */ t = BR_DIVR(tv,(tv-tu)); usp = in[0].comp; vsp = in[1].comp; wsp = out[0].comp; for(m = cmask ; m ; m >>=1, usp++,vsp++,wsp++) if(m & 1) *wsp = *vsp + BR_MUL(t,(*usp-*vsp)); } return 1; } /* * Find out which faces in mesh are towards eye * * Accumulate visiblity counts for edges and vertices, mark * materials, and fill in vertices for edges */ STATIC void ZbEdgeFindVisibleFaces(void) { int f,g; br_face *fp = zb.model->prepared_faces; br_face_group *gp = zb.model->face_groups; br_material *group_material; for(g=0; g < zb.model->nface_groups; g++, gp++) { group_material = gp->material?gp->material:zb.default_material; if(group_material->flags & BR_MATF_TWO_SIDED) { /* * Two sided faces always face the eye */ for(f=0; f < gp->nfaces; f++, fp++) { if(!(fp->flags & 1)) { zb.edge_counts[fp->edges[0]]++; zb.temp_edges[fp->edges[0]].material = group_material; zb.temp_edges[fp->edges[0]].vertices[0] = fp->vertices[0]; zb.temp_edges[fp->edges[0]].vertices[1] = fp->vertices[1]; zb.vertex_counts[fp->vertices[0]]++; zb.vertex_counts[fp->vertices[1]]++; } if(!(fp->flags & 2)) { zb.edge_counts[fp->edges[1]]++; zb.temp_edges[fp->edges[1]].material = group_material; zb.temp_edges[fp->edges[1]].vertices[0] = fp->vertices[1]; zb.temp_edges[fp->edges[1]].vertices[1] = fp->vertices[2]; zb.vertex_counts[fp->vertices[1]]++; zb.vertex_counts[fp->vertices[2]]++; } if(!(fp->flags & 4)) { zb.edge_counts[fp->edges[2]]++; zb.temp_edges[fp->edges[2]].material = group_material; zb.temp_edges[fp->edges[2]].vertices[0] = fp->vertices[2]; zb.temp_edges[fp->edges[2]].vertices[1] = fp->vertices[0]; zb.vertex_counts[fp->vertices[2]]++; zb.vertex_counts[fp->vertices[0]]++; } } zb.face_group_counts[g] = gp->nfaces; } else { /* * Check plane eqn. of every face in group against the eye */ zb.face_group_counts[g] = 0; for(f=0; f < gp->nfaces; f++, fp++) { /* * if Plane_Eqn . Eye <= 0, face is away from eye */ if(fw.vtos_type == BR_VTOS_PERSPECTIVE) { if(BrFVector3Dot(&fp->n,&fw.eye_m) <= fp->d) continue; } else { if(BrFVector3Dot(&fp->n,&fw.eye_m) <= S0) continue; } if(!(fp->flags & 1)) { zb.edge_counts[fp->edges[0]]++; zb.temp_edges[fp->edges[0]].material = group_material; zb.temp_edges[fp->edges[0]].vertices[0] = fp->vertices[0]; zb.temp_edges[fp->edges[0]].vertices[1] = fp->vertices[1]; zb.vertex_counts[fp->vertices[0]]++; zb.vertex_counts[fp->vertices[1]]++; } if(!(fp->flags & 2)) { zb.edge_counts[fp->edges[1]]++; zb.temp_edges[fp->edges[1]].material = group_material; zb.temp_edges[fp->edges[1]].vertices[0] = fp->vertices[1]; zb.temp_edges[fp->edges[1]].vertices[1] = fp->vertices[2]; zb.vertex_counts[fp->vertices[1]]++; zb.vertex_counts[fp->vertices[2]]++; } if(!(fp->flags & 4)) { zb.edge_counts[fp->edges[2]]++; zb.temp_edges[fp->edges[2]].material = group_material; zb.temp_edges[fp->edges[2]].vertices[0] = fp->vertices[2]; zb.temp_edges[fp->edges[2]].vertices[1] = fp->vertices[0]; zb.vertex_counts[fp->vertices[2]]++; zb.vertex_counts[fp->vertices[0]]++; } zb.face_group_counts[g]++; } } } } /* * For each edge, check outcodes, fill in edges and allocate temp_vertices */ STATIC void ZbEdgeFindEdgesAndVertices(void) { int e,j,v,vt; struct temp_edge *tep = zb.temp_edges; br_uint_32 combined_codes; for(e=0; e < zb.model->nedges; e++,tep++) { tep->flag = 0; if(zb.edge_counts[e] == 0) continue; /* * Work out AND/OR of outcodes */ combined_codes = zb.temp_vertices[tep->vertices[0]].outcode | zb.temp_vertices[tep->vertices[1]].outcode; /* * If completely of one edge of view volume (by outcodes) * mark as not visible * continue */ if((combined_codes & OUTCODES_NOT) != OUTCODES_NOT) { zb.vertex_counts[tep->vertices[0]]--; zb.vertex_counts[tep->vertices[1]]--; continue; } tep->flag = TFF_VISIBLE; /* * If any outcode is set - mark as needing clipping and remember combined codes */ if(combined_codes & OUTCODES_ALL) { tep->flag |= TFF_CLIPPED; tep->codes = combined_codes & OUTCODES_ALL; } /* * Edge will be rendered */ } } #if 0 /* * Do per-vertex paramter calculations (intensity, u & v) */ STATIC void ZbEdgeFindVertexParameters(void) { int i; struct temp_vertex *avp; br_vertex *mvp; br_material *matp; /* * Base vertices */ avp = zb.temp_vertices; mvp = zb.model->prepared_vertices; for(i=0; i < zb.model->nprepared_vertices; i++, avp++, mvp++) { /* * Ignore if not visible */ if(zb.vertex_counts[i] == 0) continue; // matp = zb.vertex_materials[i]; #if 1 /* * I am not sure this is necessary */ if(zb.material == NULL) continue; #else ASSERT(zb.material != NULL); #endif fw.surface_fn(mvp, &mvp->n, avp->comp); if(zb.material->flags & BR_MATF_FORCE_Z_0) { avp->v[Z] = 0; avp->comp[C_Z] = BR_SCALAR(0.0); } } } #endif /* * Do per-vertex paramter calculations (intensity, u & v) */ STATIC void ZbEdgeFindVertexParameters(void) { int gv,g,v; struct temp_vertex *avp; br_vertex *vp; br_vertex_group *gp = zb.model->vertex_groups; /* * Base vertices */ avp = zb.temp_vertices; v = 0; for(g=0; g < zb.model->nvertex_groups; g++, gp++) { zb.material = gp->material?gp->material:zb.default_material; if((zb.model->prep_flags & MODUF_VERTEX_GROUPS_MATCH) && (zb.face_group_counts[g] == 0)) { avp += gp->nvertices; v += gp->nvertices; } else { SurfacePerMaterial(zb.material); for(gv=0, vp = gp->vertices ; gv < gp->nvertices; gv++,v++,avp++,vp++) { /* * Ignore if not visible */ if(zb.vertex_counts[v] == 0) continue; fw.surface_fn(vp,&vp->n,avp->comp); } } /* * Handle FORCE_Z_0 */ if(zb.material->flags & BR_MATF_FORCE_Z_0) { avp -= gp->nvertices; for(gv=0; gv < gp->nvertices; gv++,avp++) { avp->v[Z] = 0; avp->comp[C_Z] = BR_SCALAR(0.0); } } } } STATIC void ZbLineClipRender(struct temp_edge *tep, int mask, br_line_fn *renderfn, br_uint_32 convert_mask) { static br_vector4 plane_px = BR_VECTOR4(-1, 0, 0,1); static br_vector4 plane_nx = BR_VECTOR4( 1, 0, 0,1); static br_vector4 plane_py = BR_VECTOR4( 0,-1, 0,1); static br_vector4 plane_ny = BR_VECTOR4( 0, 1, 0,1); static br_vector4 plane_pz = BR_VECTOR4( 0, 0,-1,0); static br_vector4 plane_nz = BR_VECTOR4( 0, 0, 1,1); struct temp_vertex cv0[2],cv1[2]; struct temp_vertex *cp_in = cv0,*cp_out = cv1,*cp_tmp; struct temp_vertex tv[2]; int i,c; cp_in[0] = zb.temp_vertices[tep->vertices[0]]; cp_in[1] = zb.temp_vertices[tep->vertices[1]]; if(tep->codes & OUTCODE_LEFT) { if(ClipLineToPlane(cp_in,cp_out,&plane_px,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } if(tep->codes & OUTCODE_RIGHT) { if(ClipLineToPlane(cp_in,cp_out,&plane_nx,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } if(tep->codes & OUTCODE_TOP) { if(ClipLineToPlane(cp_in,cp_out,&plane_py,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } if(tep->codes & OUTCODE_BOTTOM) { if(ClipLineToPlane(cp_in,cp_out,&plane_ny,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } if(tep->codes & OUTCODE_HITHER) { if(ClipLineToPlane(cp_in,cp_out,&plane_pz,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } if(tep->codes & OUTCODE_YON) { if(ClipLineToPlane(cp_in,cp_out,&plane_nz,mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } #if USER_CLIP /* * User-defined clip plane */ for(c = 0; c < fw.nactive_clip_planes; c++) { if(!(tep->codes & (OUTCODE_USER << c))) continue; if(ClipLineToPlane(cp_in,cp_out, &fw.active_clip_planes[c].screen_plane, mask) == 0) return; cp_tmp = cp_in; cp_in = cp_out; cp_out = cp_tmp; } #endif /* * Re-project vertices */ for(i=0; i< 2; i++) { PROJECT_VERTEX(tv+i,cp_in[i].comp[C_X],cp_in[i].comp[C_Y],cp_in[i].comp[C_Z],cp_in[i].comp[C_W]); UPDATE_BOUNDS(tv[i]); #if !BASED_FIXED ZbConvertComponents((br_fixed_ls *)tv[i].comp,cp_in[i].comp,convert_mask); #else tv[i].comp[C_W]=cp_in[i].comp[C_W]; tv[i].comp[C_I]=cp_in[i].comp[C_I]; tv[i].comp[C_U]=cp_in[i].comp[C_U]; tv[i].comp[C_V]=cp_in[i].comp[C_V]; tv[i].comp[C_R]=cp_in[i].comp[C_R]; tv[i].comp[C_G]=cp_in[i].comp[C_G]; tv[i].comp[C_B]=cp_in[i].comp[C_B]; #endif } renderfn((struct temp_vertex_fixed *)(tv+0),(struct temp_vertex_fixed *)(tv+1)); } STATIC void ZbRenderEdges(void) { br_uint_32 e,i; struct temp_edge *tep = zb.temp_edges; struct zb_material_type *zbmt; struct temp_vertex *tvp; struct temp_vertex_fixed tv[2]; for(e=0; e < zb.model->nedges; e++,tep++) { switch(tep->flag) { case TFF_VISIBLE: if(tep->material->colour_map) zb.texture_buffer = tep->material->colour_map->pixels; if(tep->material->index_shade) zb.shade_table = tep->material->index_shade->pixels; zbmt = tep->material->rptr; #if !BASED_FIXED for(i=0;i<2;i++) { tvp=zb.temp_vertices+tep->vertices[i]; tv[i].v[X]=tvp->v[X]; tv[i].v[Y]=tvp->v[Y]; tv[i].v[Z]=tvp->v[Z]; ZbConvertComponents(tv[i].comp,tvp->comp,zbmt->convert_mask); } zbmt->line(tv+0,tv+1); #else zbmt->line((struct temp_vertex_fixed *)zb.temp_vertices+tep->vertices[0], (struct temp_vertex_fixed *)zb.temp_vertices+tep->vertices[1]); #endif break; case TFF_VISIBLE | TFF_CLIPPED: if(tep->material->colour_map) zb.texture_buffer = tep->material->colour_map->pixels; if(tep->material->index_shade) zb.shade_table = tep->material->index_shade->pixels; zbmt = tep->material->rptr; ZbLineClipRender(tep,zbmt->clip_mask | CM_U | CM_V | CM_I | CM_R | CM_G | CM_B,zbmt->line,zbmt->convert_mask); break; } } } /* * Render a models points to the screen through the current transform */ void ZbMeshRenderEdges(br_actor *actor, br_model *model, br_material *material, br_uint_8 style, int on_screen) { void *scratch; int scratch_size; char *sp,*clear_end; int inside_out; /* * Remember model and material in renderer info. */ zb.model = model; zb.default_material = material; /* * Work out amount of scratch space needed for this model */ scratch_size = SCRATCH_ALIGN(model->nedges * sizeof(*zb.temp_edges)); scratch_size += SCRATCH_ALIGN(model->nedges * sizeof(*zb.edge_counts)); scratch_size += SCRATCH_ALIGN(model->nface_groups * sizeof(*zb.face_group_counts)); scratch_size += SCRATCH_ALIGN(model->nprepared_vertices * sizeof(*zb.vertex_counts)); scratch_size += SCRATCH_ALIGN(model->nprepared_vertices * sizeof(*zb.temp_vertices)); scratch = BrScratchAllocate(scratch_size+SCRATCH_BOUNDARY); /* * Allocate scratch areas */ sp = scratch; zb.vertex_counts = (void *)sp; sp += SCRATCH_ALIGN(model->nprepared_vertices * sizeof(*zb.vertex_counts)); zb.face_group_counts = (void *)sp; sp += SCRATCH_ALIGN(model->nface_groups * sizeof(*zb.face_group_counts)); zb.edge_counts = (void *)sp; sp += SCRATCH_ALIGN(model->nedges * sizeof(*zb.edge_counts)); clear_end = sp; zb.temp_edges = (void *)sp; sp += SCRATCH_ALIGN(model->nedges * sizeof(*zb.temp_edges)); zb.temp_vertices = (void *)sp; /* * Clear vertex and edge counts */ BrBlockFill(scratch, 0, ((clear_end - (char *)scratch) + 3) / 4); /* * Build view_to_model * * Record a flag to say if model is inside out (det of Xfrm is < 0) * * This should be done in traversal so that special cases can be * exploited */ inside_out = BrMatrix34Inverse(&fw.view_to_model,&fw.model_to_view) < S0; /* * Transform eye point into model space */ BrVector3EyeInModel(&fw.eye_m); /* * Process lighting for this model */ SurfacePerModel(); /* * Initialise bounds */ #if BOUNDING_RECTANGLE_CALL zb.bounds[BR_BOUNDS_MIN_X] = zb.bounds[BR_BOUNDS_MIN_Y] = 0x7fffffff; zb.bounds[BR_BOUNDS_MAX_X] = zb.bounds[BR_BOUNDS_MAX_Y] = 0x80000001; #endif /* * Process this model */ ZbEdgeFindVisibleFaces(); ZbTransformVertices(); ZbEdgeFindEdgesAndVertices(); ZbEdgeFindVertexParameters(); ZbRenderEdges(); BrScratchFree(scratch); #if BOUNDING_RECTANGLE_CALL /* * Invoke a callback for bounding rectangle */ if(zb.bounds_call) { br_int_32 int_bounds[4]; zb.bounds[0]-=BR_SCALAR(0.5); zb.bounds[1]-=BR_SCALAR(0.5); zb.bounds[2]+=BR_SCALAR(0.5); zb.bounds[3]+=BR_SCALAR(0.5); CLAMP_POINT_MIN(zb.bounds[0],zb.bounds[1]); CLAMP_POINT_MAX(zb.bounds[2],zb.bounds[3]); int_bounds[0] = ScreenToInt(zb.bounds[0]); int_bounds[1] = ScreenToInt(zb.bounds[1])+fw.output->base_y; // int_bounds[2] = ScreenToInt(zb.bounds[2])-1; // int_bounds[3] = ScreenToInt(zb.bounds[3])+fw.output->base_y-1; // Hawkeye - Add 1 pixel to make sure the narrow lines // give a rect. (1 pixel high or wide.) int_bounds[2] = ScreenToInt(zb.bounds[2]); int_bounds[3] = ScreenToInt(zb.bounds[3])+fw.output->base_y; if((int_bounds[0] <= int_bounds[2]) && (int_bounds[1] <= int_bounds[3]) ) { zb.bounds_call(actor, model, material, NULL, style, &fw.model_to_screen, int_bounds); } } #endif }