static void CalcPlane (SlopeWork &slope, secplane_t &plane) { FVector3 pt[3]; long j; slope.x[0] = slope.wal->x; slope.y[0] = slope.wal->y; slope.x[1] = slope.wal2->x; slope.y[1] = slope.wal2->y; if (slope.dx == 0) { slope.x[2] = slope.x[0] + 64; slope.y[2] = slope.y[0]; } else { slope.x[2] = slope.x[0]; slope.y[2] = slope.y[0] + 64; } j = DMulScale3 (slope.dx, slope.y[2]-slope.wal->y, -slope.dy, slope.x[2]-slope.wal->x); slope.z[2] += Scale (slope.heinum, j, slope.i); pt[0] = FVector3(slope.dx, -slope.dy, 0); pt[1] = FVector3(slope.x[2] - slope.x[0], slope.y[0] - slope.y[2], (slope.z[2] - slope.z[0]) / 16); pt[2] = (pt[0] ^ pt[1]).Unit(); if ((pt[2][2] < 0 && plane.c > 0) || (pt[2][2] > 0 && plane.c < 0)) { pt[2] = -pt[2]; } plane.a = fixed_t(pt[2][0]*65536.f); plane.b = fixed_t(pt[2][1]*65536.f); plane.c = fixed_t(pt[2][2]*65536.f); plane.ic = fixed_t(65536.f/pt[2][2]); plane.d = -TMulScale8 (plane.a, slope.x[0]<<4, plane.b, (-slope.y[0])<<4, plane.c, slope.z[0]); }
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) { framebuffer = fb; mCurrentPortal = NULL; mMirrorCount = 0; mPlaneMirrorCount = 0; mLightCount = 0; mAngles = FRotator(0,0,0); mViewVector = FVector2(0,0); mCameraPos = FVector3(0,0,0); mVBO = NULL; gl_spriteindex = 0; mShaderManager = NULL; glpart2 = glpart = gllight = mirrortexture = NULL; }
void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * subsec) { ADynamicLight *light; float frac, lr, lg, lb; float radius; float out[3] = { 0.0f, 0.0f, 0.0f }; // Go through both light lists FLightNode * node = subsec->lighthead; while (node) { light=node->lightsource; if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != self) && !(light->lightflags&LF_DONTLIGHTACTORS)) { float dist; // This is a performance critical section of code where we cannot afford to let the compiler decide whether to inline the function or not. // This will do the calculations explicitly rather than calling one of AActor's utility functions. if (Displacements.size > 0) { int fromgroup = light->Sector->PortalGroup; int togroup = subsec->sector->PortalGroup; if (fromgroup == togroup || fromgroup == 0 || togroup == 0) goto direct; DVector2 offset = Displacements.getOffset(fromgroup, togroup); dist = FVector3(x - light->X() - offset.X, y - light->Y() - offset.Y, z - light->Z()).LengthSquared(); } else { direct: dist = FVector3(x - light->X(), y - light->Y(), z - light->Z()).LengthSquared(); } radius = light->GetRadius(); if (dist < radius * radius) { dist = sqrtf(dist); // only calculate the square root if we really need it. frac = 1.0f - (dist / radius); if (frac > 0 && GLRenderer->mShadowMap.ShadowTest(light, { x, y, z })) { lr = light->GetRed() / 255.0f; lg = light->GetGreen() / 255.0f; lb = light->GetBlue() / 255.0f; if (light->IsSubtractive()) { float bright = FVector3(lr, lg, lb).Length(); FVector3 lightColor(lr, lg, lb); lr = (bright - lr) * -1; lg = (bright - lg) * -1; lb = (bright - lb) * -1; } out[0] += lr * frac; out[1] += lg * frac; out[2] += lb * frac; } } } node = node->nextLight; } gl_RenderState.SetDynLight(out[0], out[1], out[2]); modellightindex = -1; }
int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer) { if (num_lines == 0) return -1; // Find bounding box and median of the lines FVector2 median = FVector2(0.0f, 0.0f); FVector2 aabb_min, aabb_max; auto &maplines = Level->lines; aabb_min.X = (float)maplines[mapLines[lines[0]]].v1->fX(); aabb_min.Y = (float)maplines[mapLines[lines[0]]].v1->fY(); aabb_max = aabb_min; for (int i = 0; i < num_lines; i++) { float x1 = (float)maplines[mapLines[lines[i]]].v1->fX(); float y1 = (float)maplines[mapLines[lines[i]]].v1->fY(); float x2 = (float)maplines[mapLines[lines[i]]].v2->fX(); float y2 = (float)maplines[mapLines[lines[i]]].v2->fY(); aabb_min.X = MIN(aabb_min.X, x1); aabb_min.X = MIN(aabb_min.X, x2); aabb_min.Y = MIN(aabb_min.Y, y1); aabb_min.Y = MIN(aabb_min.Y, y2); aabb_max.X = MAX(aabb_max.X, x1); aabb_max.X = MAX(aabb_max.X, x2); aabb_max.Y = MAX(aabb_max.Y, y1); aabb_max.Y = MAX(aabb_max.Y, y2); median += centroids[mapLines[lines[i]]]; } median /= (float)num_lines; if (num_lines == 1) // Leaf node { nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0])); return (int)nodes.Size() - 1; } // Find the longest axis float axis_lengths[2] = { aabb_max.X - aabb_min.X, aabb_max.Y - aabb_min.Y }; int axis_order[2] = { 0, 1 }; FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) }; std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); // Try sort at longest axis, then if that fails then the other one. // We place the sorted lines into work_buffer and then move the result back to the lines list when done. int left_count, right_count; for (int attempt = 0; attempt < 2; attempt++) { // Find the sort plane for axis FVector2 axis = axis_plane[axis_order[attempt]]; FVector3 plane(axis, -(median | axis)); // Sort lines into two based ib whether the line center is on the front or back side of a plane left_count = 0; right_count = 0; for (int i = 0; i < num_lines; i++) { int line_index = lines[i]; float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane; if (side >= 0.0f) { work_buffer[left_count] = line_index; left_count++; } else { work_buffer[num_lines + right_count] = line_index; right_count++; } } if (left_count != 0 && right_count != 0) break; } // Check if something went wrong when sorting and do a random sort instead if (left_count == 0 || right_count == 0) { left_count = num_lines / 2; right_count = num_lines - left_count; } else { // Move result back into lines list: for (int i = 0; i < left_count; i++) lines[i] = work_buffer[i]; for (int i = 0; i < right_count; i++) lines[i + left_count] = work_buffer[num_lines + i]; } // Create child nodes: int left_index = -1; int right_index = -1; if (left_count > 0) left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer); if (right_count > 0) right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer); // Store resulting node and return its index nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index)); return (int)nodes.Size() - 1; }
bool gl_GetSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out, line_t *line, int side) { ADynamicLight *light; float frac, lr, lg, lb; float radius; bool changed = false; out[0]=out[1]=out[2]=0; for(int j=0;j<2;j++) { // Go through both light lists FLightNode * node = subsec->lighthead[j]; while (node) { light=node->lightsource; //if (!light->owned || light->target == NULL || light->target->IsVisibleToPlayer()) { if (!(light->flags2&MF2_DORMANT) && (!(light->flags4&MF4_DONTLIGHTSELF) || light->target != self)) { float dist = FVector3(FIXED2FLOAT(x - light->x), FIXED2FLOAT(y - light->y), FIXED2FLOAT(z - light->z)).Length(); radius = light->GetRadius() * gl_lights_size; if (dist < radius) { frac = 1.0f - (dist / radius); if (frac > 0) { if (line != NULL) { if (P_PointOnLineSide(light->x, light->y, line) != side) { node = node->nextLight; continue; } } lr = light->GetRed() / 255.0f * gl_lights_intensity; lg = light->GetGreen() / 255.0f * gl_lights_intensity; lb = light->GetBlue() / 255.0f * gl_lights_intensity; if (light->IsSubtractive()) { float bright = FVector3(lr, lg, lb).Length(); FVector3 lightColor(lr, lg, lb); lr = (bright - lr) * -1; lg = (bright - lg) * -1; lb = (bright - lb) * -1; } out[0] += lr * frac; out[1] += lg * frac; out[2] += lb * frac; changed = true; } } } } node = node->nextLight; } } // Desaturate dynamic lighting if applicable if (desaturation>0 && desaturation<=CM_DESAT31) { float gray=(out[0]*77 + out[1]*143 + out[2]*37)/257; out[0]= (out[0]*(31-desaturation)+ gray*desaturation)/31; out[1]= (out[1]*(31-desaturation)+ gray*desaturation)/31; out[2]= (out[2]*(31-desaturation)+ gray*desaturation)/31; } return changed; }
bool GLSprite::CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp) { const auto &HWAngles = di->Viewpoint.HWAngles; if (actor != nullptr && (actor->renderflags & RF_SPRITETYPEMASK) == RF_FLATSPRITE) { Matrix3x4 mat; mat.MakeIdentity(); // [MC] Rotate around the center or offsets given to the sprites. // Counteract any existing rotations, then rotate the angle. // Tilt the actor up or down based on pitch (increase 'somersaults' forward). // Then counteract the roll and DO A BARREL ROLL. FAngle pitch = (float)-Angles.Pitch.Degrees; pitch.Normalized180(); mat.Translate(x, z, y); mat.Rotate(0, 1, 0, 270. - Angles.Yaw.Degrees); mat.Rotate(1, 0, 0, pitch.Degrees); if (actor->renderflags & RF_ROLLCENTER) { float cx = (x1 + x2) * 0.5; float cy = (y1 + y2) * 0.5; mat.Translate(cx - x, 0, cy - y); mat.Rotate(0, 1, 0, - Angles.Roll.Degrees); mat.Translate(-cx, -z, -cy); } else { mat.Rotate(0, 1, 0, - Angles.Roll.Degrees); mat.Translate(-x, -z, -y); } v[0] = mat * FVector3(x2, z, y2); v[1] = mat * FVector3(x1, z, y2); v[2] = mat * FVector3(x2, z, y1); v[3] = mat * FVector3(x1, z, y1); return true; } // [BB] Billboard stuff const bool drawWithXYBillboard = ((particle && gl_billboard_particles) || (!(actor && actor->renderflags & RF_FORCEYBILLBOARD) //&& di->mViewActor != nullptr && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD)))); const bool drawBillboardFacingCamera = gl_billboard_faces_camera; // [Nash] has +ROLLSPRITE const bool drawRollSpriteActor = (actor != nullptr && actor->renderflags & RF_ROLLSPRITE); // [fgsfds] check sprite type mask uint32_t spritetype = (uint32_t)-1; if (actor != nullptr) spritetype = actor->renderflags & RF_SPRITETYPEMASK; // [Nash] is a flat sprite const bool isFlatSprite = (actor != nullptr) && (spritetype == RF_WALLSPRITE || spritetype == RF_FLATSPRITE); const bool useOffsets = (actor != nullptr) && !(actor->renderflags & RF_ROLLCENTER); // [Nash] check for special sprite drawing modes if (drawWithXYBillboard || drawBillboardFacingCamera || drawRollSpriteActor || isFlatSprite) { // Compute center of sprite float xcenter = (x1 + x2)*0.5; float ycenter = (y1 + y2)*0.5; float zcenter = (z1 + z2)*0.5; float xx = -xcenter + x; float zz = -zcenter + z; float yy = -ycenter + y; Matrix3x4 mat; mat.MakeIdentity(); mat.Translate(xcenter, zcenter, ycenter); // move to sprite center // Order of rotations matters. Perform yaw rotation (Y, face camera) before pitch (X, tilt up/down). if (drawBillboardFacingCamera && !isFlatSprite) { // [CMB] Rotate relative to camera XY position, not just camera direction, // which is nicer in VR float xrel = xcenter - vp->X; float yrel = ycenter - vp->Y; float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); float counterRotationDeg = 270. - HWAngles.Yaw.Degrees; // counteracts existing sprite rotation float relAngleDeg = counterRotationDeg + absAngleDeg; mat.Rotate(0, 1, 0, relAngleDeg); } // [fgsfds] calculate yaw vectors float yawvecX = 0, yawvecY = 0, rollDegrees = 0; float angleRad = (270. - HWAngles.Yaw).Radians(); if (actor) rollDegrees = Angles.Roll.Degrees; if (isFlatSprite) { yawvecX = Angles.Yaw.Cos(); yawvecY = Angles.Yaw.Sin(); } // [fgsfds] Rotate the sprite about the sight vector (roll) if (spritetype == RF_WALLSPRITE) { mat.Rotate(0, 1, 0, 0); if (drawRollSpriteActor) { if (useOffsets) mat.Translate(xx, zz, yy); mat.Rotate(yawvecX, 0, yawvecY, rollDegrees); if (useOffsets) mat.Translate(-xx, -zz, -yy); } } else if (drawRollSpriteActor) { if (useOffsets) mat.Translate(xx, zz, yy); if (drawWithXYBillboard) { mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees); } mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees); if (useOffsets) mat.Translate(-xx, -zz, -yy); } else if (drawWithXYBillboard) { // Rotate the sprite about the vector starting at the center of the sprite // triangle strip and with direction orthogonal to where the player is looking // in the x/y plane. mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees); } mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center v[0] = mat * FVector3(x1, z1, y1); v[1] = mat * FVector3(x2, z1, y2); v[2] = mat * FVector3(x1, z2, y1); v[3] = mat * FVector3(x2, z2, y2); } else // traditional "Y" billboard mode { v[0] = FVector3(x1, z1, y1); v[1] = FVector3(x2, z1, y2); v[2] = FVector3(x1, z2, y1); v[3] = FVector3(x2, z2, y2); } return false; }
void GLWall::SetupLights() { if (RenderStyle == STYLE_Add && !glset.lightadditivesurfaces) return; // no lights on additively blended surfaces. // check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.) switch (type) { case RENDERWALL_FOGBOUNDARY: case RENDERWALL_MIRRORSURFACE: case RENDERWALL_COLOR: return; } float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2}; Plane p; lightdata.Clear(); p.Set(&glseg); /* if (!p.ValidNormal()) { return; } */ FLightNode *node; if (seg->sidedef == NULL) { node = NULL; } else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) { node = seg->sidedef->lighthead; } else if (sub) { // Polobject segs cannot be checked per sidedef so use the subsector instead. node = sub->lighthead; } else node = NULL; // Iterate through all dynamic lights which touch this wall and render them while (node) { if (!(node->lightsource->flags2&MF2_DORMANT)) { iter_dlight++; DVector3 posrel = node->lightsource->PosRelative(seg->frontsector); float x = posrel.X; float y = posrel.Y; float z = posrel.Z; float dist = fabsf(p.DistToPoint(x, z, y)); float radius = node->lightsource->GetRadius(); float scale = 1.0f / ((2.f * radius) - dist); FVector3 fn, pos; if (radius > 0.f && dist < radius) { FVector3 nearPt, up, right; pos = { x, z, y }; fn = p.Normal(); fn.GetRightUp(right, up); FVector3 tmpVec = fn * dist; nearPt = pos + tmpVec; FVector3 t1; int outcnt[4]={0,0,0,0}; texcoord tcs[4]; // do a quick check whether the light touches this polygon for(int i=0;i<4;i++) { t1 = FVector3(&vtx[i*3]); FVector3 nearToVert = t1 - nearPt; tcs[i].u = ((nearToVert | right) * scale) + 0.5f; tcs[i].v = ((nearToVert | up) * scale) + 0.5f; if (tcs[i].u<0) outcnt[0]++; if (tcs[i].u>1) outcnt[1]++; if (tcs[i].v<0) outcnt[2]++; if (tcs[i].v>1) outcnt[3]++; } if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) { gl_GetLight(seg->frontsector->PortalGroup, p, node->lightsource, true, lightdata); } } } node = node->nextLight; } dynlightindex = GLRenderer->mLights->UploadLights(lightdata); }
void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out) { FDynamicLight *light; float frac, lr, lg, lb; float radius; out[0] = out[1] = out[2] = 0.f; // Go through both light lists while (node) { light=node->lightsource; if (light->ShouldLightActor(self)) { float dist; FVector3 L; // This is a performance critical section of code where we cannot afford to let the compiler decide whether to inline the function or not. // This will do the calculations explicitly rather than calling one of AActor's utility functions. if (Level->Displacements.size > 0) { int fromgroup = light->Sector->PortalGroup; int togroup = portalgroup; if (fromgroup == togroup || fromgroup == 0 || togroup == 0) goto direct; DVector2 offset = Level->Displacements.getOffset(fromgroup, togroup); L = FVector3(x - (float)(light->X() + offset.X), y - (float)(light->Y() + offset.Y), z - (float)light->Z()); } else { direct: L = FVector3(x - (float)light->X(), y - (float)light->Y(), z - (float)light->Z()); } dist = (float)L.LengthSquared(); radius = light->GetRadius(); if (dist < radius * radius) { dist = sqrtf(dist); // only calculate the square root if we really need it. frac = 1.0f - (dist / radius); if (light->IsSpot()) { L *= -1.0f / dist; DAngle negPitch = -*light->pPitch; DAngle Angle = light->target->Angles.Yaw; double xyLen = negPitch.Cos(); double spotDirX = -Angle.Cos() * xyLen; double spotDirY = -Angle.Sin() * xyLen; double spotDirZ = -negPitch.Sin(); double cosDir = L.X * spotDirX + L.Y * spotDirY + L.Z * spotDirZ; frac *= (float)smoothstep(light->pSpotOuterAngle->Cos(), light->pSpotInnerAngle->Cos(), cosDir); } if (frac > 0 && (!light->shadowmapped || screen->mShadowMap.ShadowTest(light, { x, y, z }))) { lr = light->GetRed() / 255.0f; lg = light->GetGreen() / 255.0f; lb = light->GetBlue() / 255.0f; if (light->IsSubtractive()) { float bright = (float)FVector3(lr, lg, lb).Length(); FVector3 lightColor(lr, lg, lb); lr = (bright - lr) * -1; lg = (bright - lg) * -1; lb = (bright - lb) * -1; } out[0] += lr * frac; out[1] += lg * frac; out[2] += lb * frac; } } } node = node->nextLight; } }
void FUE1Model::LoadGeometry() { FMemLump lump, lump2; const char *buffer, *buffer2; lump = Wads.ReadLump(mDataLump); buffer = (char*)lump.GetMem(); lump2 = Wads.ReadLump(mAnivLump); buffer2 = (char*)lump2.GetMem(); // map structures dhead = (d3dhead*)(buffer); dpolys = (d3dpoly*)(buffer+sizeof(d3dhead)); ahead = (a3dhead*)(buffer2); // detect deus ex format if ( (ahead->framesize/dhead->numverts) == 8 ) { averts = NULL; dxverts = (dxvert*)(buffer2+sizeof(a3dhead)); } else { averts = (uint32_t*)(buffer2+sizeof(a3dhead)); dxverts = NULL; } weaponPoly = -1; // set counters numVerts = dhead->numverts; numFrames = ahead->numframes; numPolys = dhead->numpolys; numGroups = 0; // populate vertex arrays for ( int i=0; i<numFrames; i++ ) { for ( int j=0; j<numVerts; j++ ) { UE1Vertex Vert; if ( dxverts != NULL ) { // convert padded XYZ16 Vert.Pos = FVector3(dxverts[j+i*numVerts].x, dxverts[j+i*numVerts].z, (float)-dxverts[j+i*numVerts].y); } else { // convert packed XY11Z10 Vert.Pos = FVector3(unpackuvert(averts[j+i*numVerts],0), unpackuvert(averts[j+i*numVerts],2), -unpackuvert(averts[j+i*numVerts],1)); } // push vertex (without normals, will be calculated later) verts.Push(Vert); } } // populate poly arrays for ( int i=0; i<numPolys; i++ ) { UE1Poly Poly; // set indices for ( int j=0; j<3; j++ ) Poly.V[j] = dpolys[i].vertices[j]; // unpack coords for ( int j=0; j<3; j++ ) Poly.C[j] = FVector2(dpolys[i].uv[j][0]/255.f,dpolys[i].uv[j][1]/255.f); // compute facet normals for ( int j=0; j<numFrames; j++ ) { FVector3 dir[2]; dir[0] = verts[Poly.V[1]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos; dir[1] = verts[Poly.V[2]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos; Poly.Normals.Push((dir[0]^dir[1]).Unit()); } // push polys.Push(Poly); } // compute normals for vertex arrays // iterates through all polys and compute the average of all facet normals // from those who use this vertex. not pretty, but does the job for ( int i=0; i<numFrames; i++ ) { for ( int j=0; j<numVerts; j++ ) { FVector3 nsum = FVector3(0,0,0); for ( int k=0; k<numPolys; k++ ) { if ( (polys[k].V[0] != j) && (polys[k].V[1] != j) && (polys[k].V[2] != j) ) continue; nsum += polys[k].Normals[i]; } verts[j+numVerts*i].Normal = nsum.Unit(); } } // populate poly groups (subdivided by texture number and type) // this method minimizes searches in the group list as much as possible // while still doing a single pass through the poly list int curgroup = -1; UE1Group Group; for ( int i=0; i<numPolys; i++ ) { // while we're at it, look for the weapon triangle if ( dpolys[i].type&PT_WeaponTriangle ) weaponPoly = i; if ( curgroup == -1 ) { // no group, create it Group.P.Reset(); Group.numPolys = 0; Group.texNum = dpolys[i].texnum; Group.type = dpolys[i].type; groups.Push(Group); curgroup = numGroups++; } else if ( (dpolys[i].texnum != groups[curgroup].texNum) || (dpolys[i].type != groups[curgroup].type) ) { // different attributes than last time // search for existing group with new attributes, create one if not found curgroup = -1; for ( int j=0; j<numGroups; j++ ) { if ( (groups[j].texNum != dpolys[i].texnum) || (groups[j].type != dpolys[i].type) ) continue; curgroup = j; break; } // counter the increment that will happen after continuing this loop // otherwise it'll be skipped over i--; continue; } groups[curgroup].P.Push(i); groups[curgroup].numPolys++; } // ... and it's finally done mDataLoaded = true; }