// convert HSV [0..1] to RGB [0..1] static void HSV2RGB(float h, float s, float v, float &r, float &g, float &b) { #if 1 // convert hue to index and fraction const int bits = 20; int scaled = (xs_FloorToInt(h * (1 << bits)) & ((1 << bits) - 1)) * 6; int i = scaled >> bits; float f = scaled * (1.0f / (1 << bits)) - i; // generate components float p = v * (1 - s); float q = v * (1 - f * s); float t = v * (1 - (1 - f) * s); switch (i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } #else // http://www.xmission.com/~trevin/atari/video_notes.html const float Y = 0.7f, S = 0.7f, theta = float(M_PI) - float(M_PI) * (sim_turn & 63) / 32.0f; float R = Clamp(Y + S * sin(theta), 0.0f, 1.0f); float G = Clamp(Y - (27/53) * S * sin(theta) - (10/53) * S * cos(theta), 0.0f, 1.0f); float B = Clamp(Y + S * cos(theta), 0.0f, 1.0f); #endif }
// render void PlayerOverlaySpecial::Render(unsigned int aId, float aTime, const Transform2 &aTransform) { // get the player Player *player = Database::player.Get(aId); // get the player entity (HACK) unsigned int id = player->GetId(); // get "special" ammo resource (HACK) Resource *specialresource = Database::resource.Get(id).Get(0xd940d530 /* "special" */); if (!specialresource) return; int new_special = xs_FloorToInt(specialresource->GetValue()); // if the special has not changed... if (new_special == cur_special && !wasreset) { // call the existing draw list glCallList(special_handle); return; } // update special cur_special = new_special; // start a new draw list list glNewList(special_handle, GL_COMPILE_AND_EXECUTE); // draw the special ammo icon glColor4f(0.4f, 0.5f, 1.0f, 1.0f); glPushMatrix(); glTranslatef(specialpos.x, specialpos.y, 0.0f); glScalef(4, 4, 1); glCallList(Database::drawlist.Get(0x8cdedbba /* "circle16" */)); glPopMatrix(); // draw remaining special ammo char special[16]; sprintf(special, "x%d", cur_special); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle); glColor4f(0.4f, 0.5f, 1.0f, 1.0f); glBegin(GL_QUADS); float w = 8; float h = -8; float x = specialpos.x + 8; float y = specialpos.y - 0.5f * h; float z = 0; OGLCONSOLE_DrawString(special, x, y, w, h, z); glEnd(); glDisable(GL_TEXTURE_2D); glEndList(); }
void gl_RenderModel(GLSprite * spr) { FSpriteModelFrame * smf = spr->modelframe; // Setup transformation. glDepthFunc(GL_LEQUAL); gl_RenderState.EnableTexture(true); // [BB] In case the model should be rendered translucent, do back face culling. // This solves a few of the problems caused by the lack of depth sorting. // TO-DO: Implement proper depth sorting. if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) { glEnable(GL_CULL_FACE); glFrontFace(GL_CW); } int translation = 0; if ( !(smf->flags & MDL_IGNORETRANSLATION) ) translation = spr->actor->Translation; // y scale for a sprite means height, i.e. z in the world! float scaleFactorX = spr->actor->Scale.X * smf->xscale; float scaleFactorY = spr->actor->Scale.X * smf->yscale; float scaleFactorZ = spr->actor->Scale.Y * smf->zscale; float pitch = 0; float roll = 0; float rotateOffset = 0; float angle = spr->actor->Angles.Yaw.Degrees; // [BB] Workaround for the missing pitch information. if ( (smf->flags & MDL_PITCHFROMMOMENTUM) ) { const double x = spr->actor->Vel.X; const double y = spr->actor->Vel.Y; const double z = spr->actor->Vel.Z; if (spr->actor->Vel.LengthSquared() > EQUAL_EPSILON) { // [BB] Calculate the pitch using spherical coordinates. if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); // Correcting pitch if model is moving backwards if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) { if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; } else pitch = fabs(pitch); } } if( smf->flags & MDL_ROTATING ) { const float time = smf->rotationSpeed*GetTimeFloat()/200.f; rotateOffset = float((time - xs_FloorToInt(time)) *360.f ); } // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the momentum vector pitch. if (smf->flags & MDL_USEACTORPITCH) { double d = spr->actor->Angles.Pitch.Degrees; if (smf->flags & MDL_BADROTATION) pitch -= d; else pitch += d; } if(smf->flags & MDL_USEACTORROLL) roll += spr->actor->Angles.Roll.Degrees; gl_RenderState.mModelMatrix.loadIdentity(); // Model space => World space gl_RenderState.mModelMatrix.translate(spr->x, spr->z, spr->y ); // Applying model transformations: // 1) Applying actor angle, pitch and roll to the model gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0); gl_RenderState.mModelMatrix.rotate(pitch, 0, 0, 1); gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0); // 2) Applying Doomsday like rotation of the weapon pickup models // The rotation angle is based on the elapsed time. if( smf->flags & MDL_ROTATING ) { gl_RenderState.mModelMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); gl_RenderState.mModelMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); gl_RenderState.mModelMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); } // 3) Scaling model. gl_RenderState.mModelMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); // 4) Aplying model offsets (model offsets do not depend on model scalings). gl_RenderState.mModelMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); // 5) Applying model rotations. gl_RenderState.mModelMatrix.rotate(-smf->angleoffset, 0, 1, 0); gl_RenderState.mModelMatrix.rotate(smf->pitchoffset, 0, 0, 1); gl_RenderState.mModelMatrix.rotate(-smf->rolloffset, 1, 0, 0); // consider the pixel stretching. For non-voxels this must be factored out here float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / glset.pixelstretch; gl_RenderState.mModelMatrix.scale(1, stretch, 1); gl_RenderState.EnableModelMatrix(true); gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, spr->actor->GetClass(), nullptr, translation ); gl_RenderState.EnableModelMatrix(false); glDepthFunc(GL_LESS); if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) glDisable(GL_CULL_FACE); }
operator int() const { return xs_FloorToInt(X) + 65536 * xs_FloorToInt(Y); }
// render void PlayerOverlayLevel::Render(unsigned int aId, float aTime, const Transform2 &aTransform) { // get the player Player *player = Database::player.Get(aId); // get the attached entity identifier unsigned int id = player->mAttach; // get level resource Resource *levelresource = Database::resource.Get(id).Get(0x9b99e7dd /* "level" */); if (!levelresource) return; int new_level = xs_FloorToInt(levelresource->GetValue()); float new_part = levelresource->GetValue() - new_level; // if the level has not changed... if (new_part == cur_part && new_level == cur_level && !wasreset) { // call the existing draw list glCallList(level_handle); return; } // update level cur_level = new_level; cur_part = new_part; // start a new draw list list glNewList(level_handle, GL_COMPILE_AND_EXECUTE); // draw level gauge glBegin(GL_QUADS); // background glColor4fv(levelcolor[cur_level]); glVertex2f(levelrect.x, levelrect.y); glVertex2f(levelrect.x + levelrect.w, levelrect.y); glVertex2f(levelrect.x + levelrect.w, levelrect.y + levelrect.h); glVertex2f(levelrect.x, levelrect.y + levelrect.h); // fill gauge glColor4fv(levelcolor[cur_level+1]); glVertex2f(levelrect.x, levelrect.y); glVertex2f(levelrect.x + levelrect.w * cur_part, levelrect.y); glVertex2f(levelrect.x + levelrect.w * cur_part, levelrect.y + levelrect.h); glVertex2f(levelrect.x, levelrect.y + levelrect.h); glEnd(); // draw the level icon glColor4f(0.4f, 0.5f, 1.0f, 1.0f); glPushMatrix(); glTranslatef(levelpos.x, levelpos.y, 0.0f); glScalef(4, 4, 1); glCallList(Database::drawlist.Get(0x8cdedbba /* "circle16" */)); glPopMatrix(); // draw level number char level[16]; sprintf(level, "x%d", cur_level); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle); glColor4f(0.4f, 0.5f, 1.0f, 1.0f); glBegin(GL_QUADS); float w = 8; float h = -8; float x = levelpos.x + 8; float y = levelpos.y - 0.5f * h; float z = 0; OGLCONSOLE_DrawString(level, x, y, w, h, z); glEnd(); glDisable(GL_TEXTURE_2D); glEndList(); }
// render void PlayerOverlayAmmo::Render(unsigned int aId, float aTime, const Transform2 &aTransform) { // get the player Player *player = Database::player.Get(aId); // get the attached entity identifier unsigned int id = player->mAttach; // draw player ammo (HACK) Resource *ammoresource = Database::resource.Get(id).Get(0x5b9b0daf /* "ammo" */); if (!ammoresource) return; // get ammo ratio float new_ammo = 0.0f; const ResourceTemplate &ammoresourcetemplate = Database::resourcetemplate.Get(id).Get(0x5b9b0daf /* "ammo" */); if (ammoresourcetemplate.mMaximum > 0) { new_ammo = ammoresource->GetValue() / ammoresourcetemplate.mMaximum; } // get level int new_level = 1; Resource *levelresource = Database::resource.Get(id).Get(0x9b99e7dd /* "level" */); if (levelresource) { new_level = xs_FloorToInt(levelresource->GetValue()); } // if the lives count has not changed... if (new_ammo == cur_ammo && new_level == cur_level && glIsList(ammo_handle)) { // call the existing draw list glCallList(ammo_handle); return; } // update ammo cur_ammo = new_ammo; cur_level = new_level; // start a new draw list list glNewList(ammo_handle, GL_COMPILE_AND_EXECUTE); glBegin(GL_QUADS); // background glColor4fv(ammocolor[cur_level]); glVertex2f(ammorect.x, ammorect.y); glVertex2f(ammorect.x + ammorect.w, ammorect.y); glVertex2f(ammorect.x + ammorect.w, ammorect.y + ammorect.h); glVertex2f(ammorect.x, ammorect.y + ammorect.h); // fill gauge glColor4fv(ammocolor[cur_level+1]); glVertex2f(ammorect.x, ammorect.y); glVertex2f(ammorect.x + ammorect.w * cur_ammo, ammorect.y); glVertex2f(ammorect.x + ammorect.w * cur_ammo, ammorect.y + ammorect.h); glVertex2f(ammorect.x, ammorect.y + ammorect.h); glEnd(); glEndList(); }
void P_DrawRailTrail(AActor *source, TArray<SPortalHit> &portalhits, int color1, int color2, double maxdiff, int flags, PClassActor *spawnclass, DAngle angle, int duration, double sparsity, double drift, int SpiralOffset, DAngle pitch) { double length = 0; int steps, i; TArray<TrailSegment> trail; TAngle<double> deg; DVector3 pos; bool fullbright; unsigned segment; double lencount; for (unsigned i = 0; i < portalhits.Size() - 1; i++) { TrailSegment seg; seg.start = portalhits[i].ContPos; seg.dir = portalhits[i].OutDir; seg.length = (portalhits[i + 1].HitPos - seg.start).Length(); //Calculate PerpendicularVector (extend, dir): double minelem = 1; int epos; int ii; for (epos = 0, ii = 0; ii < 3; ++ii) { if (fabs(seg.dir[ii]) < minelem) { epos = ii; minelem = fabs(seg.dir[ii]); } } DVector3 tempvec(0, 0, 0); tempvec[epos] = 1; seg.extend = (tempvec - (seg.dir | tempvec) * seg.dir) * 3; length += seg.length; auto player = source->Level->GetConsolePlayer(); if (player) { // Only consider sound in 2D (for now, anyway) // [BB] You have to divide by lengthsquared here, not multiply with it. AActor *mo = player->camera; double r = ((seg.start.Y - mo->Y()) * (-seg.dir.Y) - (seg.start.X - mo->X()) * (seg.dir.X)) / (seg.length * seg.length); r = clamp<double>(r, 0., 1.); seg.soundpos = seg.start + r * seg.dir; seg.sounddist = (seg.soundpos - mo->Pos()).LengthSquared(); } else { // Set to invalid for secondary levels. seg.soundpos = {0,0}; seg.sounddist = -1; } trail.Push(seg); } steps = xs_FloorToInt(length / 3); fullbright = !!(flags & RAF_FULLBRIGHT); if (steps) { if (!(flags & RAF_SILENT)) { auto player = source->Level->GetConsolePlayer(); if (player) { FSoundID sound; // Allow other sounds than 'weapons/railgf'! if (!source->player) sound = source->AttackSound; else if (source->player->ReadyWeapon) sound = source->player->ReadyWeapon->AttackSound; else sound = 0; if (!sound) sound = "weapons/railgf"; // The railgun's sound is special. It gets played from the // point on the slug's trail that is closest to the hearing player. AActor *mo = player->camera; if (fabs(mo->X() - trail[0].start.X) < 20 && fabs(mo->Y() - trail[0].start.Y) < 20) { // This player (probably) fired the railgun S_Sound (mo, CHAN_WEAPON, sound, 1, ATTN_NORM); } else { TrailSegment *shortest = NULL; for (auto &seg : trail) { if (shortest == NULL || shortest->sounddist > seg.sounddist) shortest = &seg; } S_Sound (source->Level, DVector3(shortest->soundpos, r_viewpoint.Pos.Z), CHAN_WEAPON, sound, 1, ATTN_NORM); } } } } else { // line is 0 length, so nothing to do return; } // Create the outer spiral. if (color1 != -1 && (!r_rail_smartspiral || color2 == -1) && r_rail_spiralsparsity > 0 && (spawnclass == NULL)) { double stepsize = 3 * r_rail_spiralsparsity * sparsity; int spiral_steps = (int)(steps * r_rail_spiralsparsity / sparsity); segment = 0; lencount = trail[0].length; color1 = color1 == 0 ? -1 : ParticleColor(color1); pos = trail[0].start; deg = (double)SpiralOffset; for (i = spiral_steps; i; i--) { FParticle *p = NewParticle (source->Level); DVector3 tempvec; if (!p) return; int spiralduration = (duration == 0) ? 35 : duration; p->alpha = 1.f; p->ttl = spiralduration; p->fadestep = FADEFROMTTL(spiralduration); p->size = 3; p->bright = fullbright; tempvec = DMatrix3x3(trail[segment].dir, deg) * trail[segment].extend; p->Vel = tempvec * drift / 16.; p->Pos = tempvec + pos; pos += trail[segment].dir * stepsize; deg += double(r_rail_spiralsparsity * 14); lencount -= stepsize; if (color1 == -1) { int rand = M_Random(); if (rand < 155) p->color = rblue2; else if (rand < 188) p->color = rblue1; else if (rand < 222) p->color = rblue3; else p->color = rblue4; } else { p->color = color1; } p->renderstyle = STYLE_Translucent; if (lencount <= 0) { segment++; if (segment < trail.Size()) { pos = trail[segment].start - trail[segment].dir * lencount; lencount += trail[segment].length; } else { // should never happen but if something goes wrong, just terminate the loop. break; } } } } // Create the inner trail. if (color2 != -1 && r_rail_trailsparsity > 0 && spawnclass == NULL) { double stepsize = 3 * r_rail_trailsparsity * sparsity; int trail_steps = xs_FloorToInt(steps * r_rail_trailsparsity / sparsity); color2 = color2 == 0 ? -1 : ParticleColor(color2); DVector3 diff(0, 0, 0); pos = trail[0].start; lencount = trail[0].length; segment = 0; for (i = trail_steps; i; i--) { // [XA] inner trail uses a different default duration (33). int innerduration = (duration == 0) ? 33 : duration; FParticle *p = JitterParticle (source->Level, innerduration, (float)drift); if (!p) return; if (maxdiff > 0) { int rnd = M_Random (); if (rnd & 1) diff.X = clamp<double>(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 2) diff.Y = clamp<double>(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 4) diff.Z = clamp<double>(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); } DVector3 postmp = pos + diff; p->size = 2; p->Pos = postmp; if (color1 != -1) p->Acc.Z -= 1./4096; pos += trail[segment].dir * stepsize; lencount -= stepsize; p->bright = fullbright; if (color2 == -1) { int rand = M_Random(); if (rand < 85) p->color = grey4; else if (rand < 170) p->color = grey2; else p->color = grey1; } else { p->color = color2; } if (lencount <= 0) { segment++; if (segment < trail.Size()) { pos = trail[segment].start - trail[segment].dir * lencount; lencount += trail[segment].length; } else { // should never happen but if something goes wrong, just terminate the loop. break; } } } } // create actors if (spawnclass != NULL) { if (sparsity < 1) sparsity = 32; double stepsize = sparsity; int trail_steps = (int)((steps * 3) / sparsity); DVector3 diff(0, 0, 0); pos = trail[0].start; lencount = trail[0].length; segment = 0; for (i = trail_steps; i; i--) { if (maxdiff > 0) { int rnd = pr_railtrail(); if (rnd & 1) diff.X = clamp<double>(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 2) diff.Y = clamp<double>(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 4) diff.Z = clamp<double>(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); } AActor *thing = Spawn (source->Level, spawnclass, pos + diff, ALLOW_REPLACE); if (thing) { if (source) thing->target = source; thing->Angles.Pitch = pitch; thing->Angles.Yaw = angle; } pos += trail[segment].dir * stepsize; lencount -= stepsize; if (lencount <= 0) { segment++; if (segment < trail.Size()) { pos = trail[segment].start - trail[segment].dir * lencount; lencount += trail[segment].length; } else { // should never happen but if something goes wrong, just terminate the loop. break; } } } } }
void gl_RenderModel(GLSprite * spr, int cm) { // [BB/EP] Take care of gl_fogmode and ZADF_FORCE_GL_DEFAULTS. OVERRIDE_FOGMODE_IF_NECESSARY FSpriteModelFrame * smf = spr->modelframe; // Setup transformation. gl.MatrixMode(GL_MODELVIEW); gl.PushMatrix(); gl.DepthFunc(GL_LEQUAL); // [BB] In case the model should be rendered translucent, do back face culling. // This solves a few of the problems caused by the lack of depth sorting. // TO-DO: Implement proper depth sorting. if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) { gl.Enable(GL_CULL_FACE); glFrontFace(GL_CW); } int translation = 0; if ( !(smf->flags & MDL_IGNORETRANSLATION) ) translation = spr->actor->Translation; // y scale for a sprite means height, i.e. z in the world! float scaleFactorX = FIXED2FLOAT(spr->actor->scaleX) * smf->xscale; float scaleFactorY = FIXED2FLOAT(spr->actor->scaleX) * smf->yscale; float scaleFactorZ = FIXED2FLOAT(spr->actor->scaleY) * smf->zscale; float pitch = 0; float rotateOffset = 0; float angle = ANGLE_TO_FLOAT(spr->actor->angle); // [BB] Workaround for the missing pitch information. if ( (smf->flags & MDL_PITCHFROMMOMENTUM) ) { const double x = static_cast<double>(spr->actor->velx); const double y = static_cast<double>(spr->actor->vely); const double z = static_cast<double>(spr->actor->velz); // [BB] Calculate the pitch using spherical coordinates. pitch = float(atan( z/sqrt(x*x+y*y) ) / M_PI * 180); } if( smf->flags & MDL_ROTATING ) { const float time = smf->rotationSpeed*GetTimeFloat()/200.f; rotateOffset = float((time - xs_FloorToInt(time)) *360.f ); } if (gl_fogmode != 2 && (GLRenderer->mLightCount == 0 || !gl_light_models)) { // Model space => World space gl.Translatef(spr->x, spr->z, spr->y ); if ( !(smf->flags & MDL_ALIGNANGLE) ) gl.Rotatef(-angle, 0, 1, 0); // [BB] Change the angle so that the object is exactly facing the camera in the x/y plane. else gl.Rotatef( -ANGLE_TO_FLOAT ( R_PointToAngle ( spr->actor->x, spr->actor->y ) ), 0, 1, 0); // [BB] Change the pitch so that the object is vertically facing the camera (only makes sense combined with MDL_ALIGNANGLE). if ( (smf->flags & MDL_ALIGNPITCH) ) { const fixed_t distance = R_PointToDist2( spr->actor->x - viewx, spr->actor->y - viewy ); const float pitch = RAD2DEG ( atan2( FIXED2FLOAT ( spr->actor->z - viewz ), FIXED2FLOAT ( distance ) ) ); gl.Rotatef(pitch, 0, 0, 1); } // [BB] Workaround for the missing pitch information. if (pitch != 0) gl.Rotatef(pitch, 0, 0, 1); // [BB] Special flag for flat, beam like models. if ( (smf->flags & MDL_ROLLAGAINSTANGLE) ) gl.Rotatef( gl_RollAgainstAngleHelper ( spr->actor ), 1, 0, 0); // Model rotation. // [BB] Added Doomsday like rotation of the weapon pickup models. // The rotation angle is based on the elapsed time. if( smf->flags & MDL_ROTATING ) { gl.Translatef(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); gl.Rotatef(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); gl.Translatef(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); } // Scaling and model space offset. gl.Scalef(scaleFactorX, scaleFactorZ, scaleFactorY); // [BB] Apply zoffset here, needs to be scaled by 1 / smf->zscale, so that zoffset doesn't depend on the z-scaling. gl.Translatef(0., smf->zoffset / smf->zscale, 0.); gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, RUNTIME_TYPE(spr->actor), cm, NULL, NULL, translation ); } else { Matrix3x4 ModelToWorld; Matrix3x4 NormalTransform; // For radial fog we need to pass coordinates in world space in order to calculate distances. // That means that the local transformations cannot be part of the modelview matrix ModelToWorld.MakeIdentity(); // Model space => World space ModelToWorld.Translate(spr->x, spr->z, spr->y); if ( !(smf->flags & MDL_ALIGNANGLE) ) ModelToWorld.Rotate(0,1,0, -angle); // [BB] Change the angle so that the object is exactly facing the camera in the x/y plane. else ModelToWorld.Rotate(0,1,0, -ANGLE_TO_FLOAT ( R_PointToAngle ( spr->actor->x, spr->actor->y ) ) ); // [BB] Change the pitch so that the object is vertically facing the camera (only makes sense combined with MDL_ALIGNANGLE). if ( (smf->flags & MDL_ALIGNPITCH) ) { const fixed_t distance = R_PointToDist2( spr->actor->x - viewx, spr->actor->y - viewy ); const float pitch = RAD2DEG ( atan2( FIXED2FLOAT ( spr->actor->z - viewz ), FIXED2FLOAT ( distance ) ) ); ModelToWorld.Rotate(0,0,1,pitch); } // [BB] Workaround for the missing pitch information. if (pitch != 0) ModelToWorld.Rotate(0,0,1,pitch); // [BB] Special flag for flat, beam like models. if ( (smf->flags & MDL_ROLLAGAINSTANGLE) ) ModelToWorld.Rotate(1, 0, 0, gl_RollAgainstAngleHelper ( spr->actor )); // Model rotation. // [BB] Added Doomsday like rotation of the weapon pickup models. // The rotation angle is based on the elapsed time. if( smf->flags & MDL_ROTATING ) { ModelToWorld.Translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); ModelToWorld.Rotate(smf->xrotate, smf->yrotate, smf->zrotate, rotateOffset); ModelToWorld.Translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); } ModelToWorld.Scale(scaleFactorX, scaleFactorZ, scaleFactorY); // [BB] Apply zoffset here, needs to be scaled by 1 / smf->zscale, so that zoffset doesn't depend on the z-scaling. ModelToWorld.Translate(0., smf->zoffset / smf->zscale, 0.); if (!gl_light_models) { gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, RUNTIME_TYPE(spr->actor), cm, &ModelToWorld, NULL, translation ); } else { // The normal transform matrix only contains the inverse rotations and scalings but not the translations NormalTransform.MakeIdentity(); NormalTransform.Scale(1.f/scaleFactorX, 1.f/scaleFactorZ, 1.f/scaleFactorY); if( smf->flags & MDL_ROTATING ) NormalTransform.Rotate(smf->xrotate, smf->yrotate, smf->zrotate, -rotateOffset); if (pitch != 0) NormalTransform.Rotate(0,0,1,-pitch); if (angle != 0) NormalTransform.Rotate(0,1,0, angle); gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, RUNTIME_TYPE(spr->actor), cm, &ModelToWorld, &NormalTransform, translation ); } } gl.MatrixMode(GL_MODELVIEW); gl.PopMatrix(); gl.DepthFunc(GL_LESS); if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) gl.Disable(GL_CULL_FACE); }
void BuildPathingGrid(const int aZoneSize, const int aCellSize) { if (grid_handle) { glCallList(grid_handle); return; } // create a new grid handle grid_handle = glGenLists(1); // create a new list glNewList(grid_handle, GL_COMPILE); // get world boundary const b2AABB &boundary = Collidable::GetBoundary(); // get zone extents int zx0 = xs_FloorToInt(boundary.lowerBound.x / aZoneSize); int zx1 = xs_CeilToInt(boundary.upperBound.x / aZoneSize); int zy0 = xs_FloorToInt(boundary.lowerBound.y / aZoneSize); int zy1 = xs_CeilToInt(boundary.upperBound.y / aZoneSize); // cells per zone int cell_side = aZoneSize / aCellSize; int cell_count = cell_side * cell_side; // get the collision world b2World *world = Collidable::GetWorld(); // create a probe fixture b2PolygonShape probeshape; probeshape.SetAsBox(aCellSize * 0.5f, aCellSize * 0.5f, b2Vec2(aCellSize * 0.5f, aCellSize * 0.5f), 0.0f); b2BodyDef probebodydef; b2Body *probebody = world->CreateBody(&probebodydef); b2FixtureDef probefixturedef; probefixturedef.shape = &probeshape; probefixturedef.isSensor = true; probebody->CreateFixture(&probefixturedef); // for each zone row... for (int zy = zy0; zy < zy1; ++zy) { // for each zone column... for (int zx = zx0; zx < zx1; ++zx) { // get zone boundary b2AABB zone_aabb; zone_aabb.lowerBound.x = float((zx) * aZoneSize); zone_aabb.lowerBound.y = float((zy) * aZoneSize); zone_aabb.upperBound.x = float((zx + 1) * aZoneSize); zone_aabb.upperBound.y = float((zy + 1) * aZoneSize); // draw zone boundary glBegin(GL_LINE_LOOP); glColor4f(1, 1, 1, 1); glVertex2f(zone_aabb.lowerBound.x, zone_aabb.lowerBound.y); glVertex2f(zone_aabb.upperBound.x, zone_aabb.lowerBound.y); glVertex2f(zone_aabb.upperBound.x, zone_aabb.upperBound.y); glVertex2f(zone_aabb.lowerBound.x, zone_aabb.upperBound.y); glEnd(); // initialize cell map unsigned char *cell_map = static_cast<unsigned char *>(_alloca(cell_count)); memset(cell_map, 0xFF, cell_count); // for each cell row for (int row = 0; row < cell_side; ++row) { // for each cell column... for (int col = 0; col < cell_side; ++col) { // probe filter static const b2Filter aFilter; // get fixtures intersecting the cell b2AABB cell_aabb; cell_aabb.lowerBound.x = zone_aabb.lowerBound.x + (col) * aCellSize; cell_aabb.lowerBound.y = zone_aabb.lowerBound.y + (row) * aCellSize; cell_aabb.upperBound.x = zone_aabb.lowerBound.x + (col + 1) * aCellSize; cell_aabb.upperBound.y = zone_aabb.lowerBound.y + (row + 1) * aCellSize; GridQueryCallback callback(aFilter); world->QueryAABB(&callback, cell_aabb); // cell type (0=empty, 1=blocked) cell_map[row * cell_side + col] = callback.mBlocker != NULL; } } // initialize slab map unsigned char *slab_map = static_cast<unsigned char *>(_alloca(cell_count)); memset(slab_map, 0xFF, cell_count); int slab_count = 0; // for each cell row for (int row = 0; row < cell_side; ++row) { // for each cell column... for (int col = 0; col < cell_side; ++col) { // skip assigned spaces if (slab_map[row * cell_side + col] != 0xFF) continue; // cell type unsigned char cell = cell_map[row * cell_side + col]; // allocate a new index unsigned char index = unsigned char(slab_count++); assert(index < 0xFF); // find horizontal extent int c0 = col; int c1 = cell_side; for (int c = c0; c < c1; ++c) { if ((cell_map[row * cell_side + c] != cell) || ((slab_map[row * cell_side + c] != 0xFF) && (slab_map[row * cell_side + c] != index))) { c1 = c; break; } } // find vertical extent int r0 = row; int r1 = cell_side; for (int r = r0; r < r1; ++r) { for (int c = c0; c < c1; ++c) { if ((cell_map[r * cell_side + c] != cell) || ((slab_map[r * cell_side + c] != 0xFF) && (slab_map[r * cell_side + c] != index))) { r1 = r; break; } } } // fill slab for (int r = r0; r < r1; ++r) { for (int c = c0; c < c1; ++c) { slab_map[r * cell_side + c] = index; } } assert(c0 < c1 && r0 < r1); // set slab extents //titleslab[index][0] = c0; //titleslab[index][1] = c1; //titleslab[index][2] = r0; //titleslab[index][3] = r1; b2AABB slab_aabb; slab_aabb.lowerBound.x = zone_aabb.lowerBound.x + c0 * aCellSize; slab_aabb.lowerBound.y = zone_aabb.lowerBound.y + r0 * aCellSize; slab_aabb.upperBound.x = zone_aabb.lowerBound.x + c1 * aCellSize; slab_aabb.upperBound.y = zone_aabb.lowerBound.y + r1 * aCellSize; AddGridSlab(slab_aabb, cell_color[cell]); // skip visited columns col = c1 - 1; } } DebugPrint("zone %d %d slabs %d\n", zx, zy, slab_count); } } // destroy the probe fixture world->DestroyBody(probebody); glEndList(); }