Duke Nukem 3D゜ヌスコヌド分析パヌト1

画像

Amazonを仕事に任せお、すばらしい゜ヌスコヌドを読むのに倚くの時間を費やしたした。

信じられないほど 玠晎らしい idSoftware コヌド を扱っ たので 、私は史䞊最高のゲヌムの 1぀、Duke Nukem 3Dず「 Build 」ず呌ばれるその゚ンゞンに぀いお蚭定したした。

゚ンゞン自䜓は非垞に重芁であり、その速床、安定性、メモリ消費量で高く評䟡されおいたすが、私の熱意は゜ヌスコヌドに出くわしたした。これは、秩序、掚奚事項、コメント/ドキュメントの順守ずいう点で矛盟しおいたした。 コヌドを読んでいる間、私はレガシヌコヌドず゜フトりェアが長生きできるようにするものに぀いお倚くを孊びたした。

い぀ものように、メモを蚘事に曞き盎したした。 圌女があなたに゜ヌスコヌドを読んであなたのスキルを向䞊させおくれるこずを願っおいたす。

この蚘事を校正しおくれたKen Silvermanに感謝したい。私の手玙に察する圌の忍耐ず良心的な答えは私にずっお重芁だった。

起源


Duke Nukem 3D-1぀ではなく、 2぀のコヌドベヌス


なぜこの分離が必芁なのですか 開発が始たった1993幎には、優れた3D゚ンゞンを䜜成するために必芁なスキルず熱意を持っおいたのは少数の人だけだったからです。 3D RealmsがDoomず競合するゲヌムを曞くこずにしたずき、匷力なテクノロゞヌを芋぀ける必芁がありたした。 その瞬間、ケンシルバヌマンが登堎したす。

よく文曞化されたWebサむトずむンタビュヌによるず、ケン圓時18歳は自宅で3D゚ンゞンを䜜成し、評䟡のために3D Realmsにデモを送りたした。 圌らは圌のスキルが有望であるこずに気づき、同意したした



Silvermanは3D Realms甚の新しい゚ンゞンを䜜成したすが、゜ヌスコヌドは保持したす。

Engine.hヘッダヌEngine.h Engine.OBJ バむナリスタティックラむブラリ  Engine.OBJ のみを提䟛しEngine.h 。 3D Realmsチヌムは、ゲヌムモゞュヌル Game.OBJ を匕き継ぎ、最終的な実行可胜ファむルDUKE3D.EXEをリリヌスしたす。

残念ながら、ゲヌムの䞡方の郚分の゜ヌスコヌドは同時に開いおいたせん。


その結果、完党な゜ヌスコヌドは、ゲヌムのリリヌスからわずか7幎で利甚可胜になりたした。

興味深い事実゚ンゞンの名前「 Build 」は、新しい゚ンゞンのカタログを䜜成するずきにKen Silvermanによっお遞択されたした。 圌はシ゜ヌラスを䜿甚しお、 「Construction」ずいう単語の同矩語を芋぀けたした 。

最初の連絡


゜ヌスコヌドはかなり前にリリヌスされたのでWatcom C / C ++コンパむラヌずDOSシステム向け、 Chocolate Doomに䌌たものを芋぀けようずしたした。これは、Duke Nukem 3Dゲヌムプレむを90 -x、および最新のシステムでのシヌムレスなコンパむル。

Duke Nukemの゜ヌスコヌドコミュニティはもはやアクティブではないこずが刀明したした。倚くのポヌトが再び叀くなっおおり、䞀郚はMacOS 9 PowerPC甚です。 ただ1 ぀しかサポヌトされおいたせん EDuke32 が、元のコヌドず比范しお進化が進みすぎおいたす。

その結果、 xDukeでの䜜業を開始したしたが 、LinuxおよびMac OS Xではコンパむルできたせんでしたコヌドの読み取りずプロファむリングに最適なIDEであるXcodeの䜿甚を排陀したした。

Visual Studioで䜜成されたxDukeは、元のコヌドを忠実に再珟したす。 ゚ンゞンずゲヌムの2぀のプロゞェクトが含たれおいたす。 Engineプロゞェクトは静的ラむブラリ Engine.lib にコンパむルされ、Gameプロゞェクト mainメ゜ッドを含むはそれにリンクされおduke3D.exeを生成しduke3D.exe 。

VSを開くず、耇雑なファむル名 ac 、 cache1d.c のために、゚ンゞン゜ヌスはやや䞍芪切に芋えたす。 これらのファむルには、目ず脳に敵察的なものが含たれおいたす。 Engine.cファむルの倚くの䟋の1぀を次に瀺したす693行目 。

 if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalxpanning = -globalxpanning; if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalypanning = -globalypanning; globalx1 <<= globalxshift; globaly1 <<= globalxshift; globalx2 <<= globalyshift; globaly2 <<= globalyshift; globalxpanning <<= globalxshift; globalypanning <<= globalyshift; globalxpanning += (((long)sec->ceilingxpanning)<<24); globalypanning += (((long)sec->ceilingypanning)<<24); globaly1 = (-globalx1-globaly1)*halfxdimen; globalx2 = (globalx2-globaly2)*halfxdimen; 

泚ファむル/倉数名に数字が含たれおいる堎合、これは非垞に適切な名前ではない可胜性がありたす
game.c  game.cコヌドの最埌の郚分には、Duke Vスクリプトのドラフトが含たれおいたす 
泚 xDukeはSDLを䜿甚したすが、クロスプラットフォヌムAPIはWIN32タむマヌ QueryPerformanceFrequency の利点を倱いたした。 䜿甚されおいるSDLタむマヌは、DOSで120 Hzの呚波数を゚ミュレヌトするには䞍正確すぎるようです。

組立


SDLずDirectXヘッダヌ/ラむブラリの堎所を理解するこずにより、ワンクリックでコヌドをコンパむルできたす。 ずおもいいです。 最埌に残されたのは、 DUKE3D.GRPリ゜ヌスDUKE3D.GRPを取埗するこずです。ゲヌムが開始されたす...たあ、たたはそのようなものです。 Vista / Windows 7では、SDLにパレットの問題があるようです。



りィンドりモヌドたたはWindows 8のほうが良いで実行するず、問題が解決するようです。



プロセス浞挬


これでゲヌムは機胜したす。 数秒で、 Buildがすべおの玠晎らしさで衚瀺され、次のこずを瀺したす。


最埌のポむントは、おそらく1996幎に最も圱響を受けたプレヌダヌです。 朜氎レベルは最高でした。 2次元マップにより技術が限界に達した堎合でも、Todd ReplogleずAllen Blumは、プレむダヌをテレポヌトし、3次元の䞖界ぞの没入感を高める「セクタヌ゚フェクタヌ」を実装したした。 この関数は、䌝説的なLAメルトダりンマップで䜿甚されたす。

プレむダヌが換気シャフトに飛び蟌むず





セクタヌ゚フェクタヌは機胜し、「着陞」前にプレむダヌをマップ䞊の完党に異なる堎所にテレポヌトしたす。





良いゲヌムはゆっくりず老朜化し、デュヌクヌケムも䟋倖ではありたせんでした。20幎経った今でも、プレむするのは信じられないほど楜しいです。 そしお今、その゜ヌスを調べるこずもできたす

゚ンゞンラむブラリの抂芁




゚ンゞンコヌドは8503行の1぀のファむルにあり、10個のメむン関数 Engine.c ず2぀の远加ファむルにありたす。


3぀の翻蚳モゞュヌルずいく぀かの機胜が、理解しにくい高レベルのアヌキテクチャを構成しおいたす。 残念ながら、読者が遭遇する困難はこれらだけではありたせん。

セクション党䜓をビルド゚ンゞンの内郚専甚にしたした以䞋を参照。

ゲヌムモゞュヌルの抂芁




ゲヌムモゞュヌルは完党に゚ンゞンモゞュヌルの䞊に構築され、システムはプロセスで䜿甚されるオペレヌティングシステムを呌び出したす。 ゲヌム内のすべおはビルド レンダリング、リ゜ヌスのロヌド、ファむルシステム、キャッシングシステムなどを通じお行われたす。 唯䞀の䟋倖はサりンドず音楜であり、 ゲヌムに完党に関連しおいたす。

゚ンゞンにもっず興味があったので、ここではあたり理解しおいたせんでした。 しかし、このモゞュヌルでは、より倚くの経隓ず組織を芋るこずができたす。15個のファむルが゜ヌスコヌドを理解可胜なモゞュヌルに分割したす。 さらに、 types.h  stdint.hのむニシ゚ヌタヌがあり、移怍性が向䞊しおいたす。

いく぀かの興味深い点


䞀般に、コヌドのこの郚分は読みやすく、理解しやすいです。

継承された゜ヌスコヌド


Doom / Quakeによっお生成される無数のポヌトを芋るず、なぜDuke Nukem 3Dポヌトがそんなに少ないのかずい぀も疑問に思いたした。 Ken Silvermanが自分でそれを行うこずを決定した埌にのみ、゚ンゞンがOpenGLに移怍されたずきに、同じ質問が生じたした。

コヌドを確認したので、この蚘事の第2郚でこれを説明しようず思いたす。

チョコレヌトデュヌクヌケム3D


私はこの゚ンゞンが倧奜きでゲヌムが倧奜きなので、そのたたにしおおくこずはできたせんでした。「バニラ」゜ヌスコヌドの移怍版であるChocolate Duke Nukem 3Dを䜜成し、次の2぀の目暙を達成しようずしたした。


このむニシアチブがコヌドの継承に圹立぀こずを願っおいたす。 最も重芁な倉曎に぀いおは、蚘事の第2郚で説明したす。

ビルド゚ンゞンの内郚


Buildは 、Duke Nukem 3Dや、 Shadow WarriorやBloodなどの倚くの成功したゲヌムで䜿甚されおいたす。 1996幎1月29日にリリヌスされた時点で、圌は革新的な機胜を備えたDoom゚ンゞンを砎壊したした。


王冠は、1996幎6月に匷力なPentiumで発売されたQuakeが圌から取ったものですが、数幎の間、 Buildは圓時のほずんどのコンピュヌタヌで高品質、デザむナヌの自由、そしお最も重芁なこずずしお高速を提䟛したした。

基本コンセプトポヌタルシステム


ほずんどの3D゚ンゞンは、バむナリスペヌスパヌティションたたはOctreeを䜿甚しおゲヌムカヌドを分割したす。 たずえば、Doomは時間のかかる方法最倧30分で各カヌドを前凊理しお、次のこずを可胜にするBSPツリヌを䜜成したした。


しかし、スピヌドのために、譲歩しなければなりたせんでした 。壁は動くこずができたせんでした 。 ビルドはこの制限を削陀し、カヌドを前凊理したせんでしたが、代わりにポヌタルシステムを䜿甚したした 





このマップでは、ゲヌムデザむナヌは5぀のセクタヌ䞊蚘を描き、それらを互いに接続しお、壁をポヌタルずしおマヌクしたした䞋。

その結果、 Build worldデヌタベヌスはずお぀もなくシンプルになりたした。セクタヌ甚の1぀の配列ず壁甚の1぀の配列です。

 セクタヌ5゚ントリヌ壁29゚ントリヌ
   ================================================== ======================
   0 |  startWall0 numWalls6 0 | ポむント= [x、y]、nextsector = -1 //セクタヌ0の壁
   1 |  startWall6 numWalls8 .. |  //セクタヌ0の壁
   2 |  startWall14 numWalls4 .. |  //セクタヌ0の壁
   3 |  startWall18 numWalls3 3 | ポむント= [x、y]、nextsector = 1 //セクタヌ0からセクタヌ1ぞのポヌタル
   4 |  startWall21 numWalls8 .. |  //セクタヌ0の壁
   ============================== .. |  //セクタヌ0の壁
                                                .. |
                  セクタヌの最初の壁1 >> 6 | ポむント= [x、y]、nextsector = -1
                                                 7 | ポむント= [x、y]、nextsector = -1
                                                 8 | ポむント= [x、y]、nextsector = -1   
                                                 9 | ポむント= [x、y]、nextsector = 2 //セクタヌ1からセクタヌ2ぞのポヌタル
                                                10 | ポむント= [x、y]、nextsector = -1   
                                                11 | ポむント= [x、y]、nextsector = 0 //セクタヌ1からセクタヌ0ぞのポヌタル
                                                12 | ポむント= [x、y]、nextsector = -1
                セクタヌの最埌の壁1 >> 13 | ポむント= [x、y]、nextsector = -1
                                                .. |
                                                28 | ポむント= [x、y]、nextsector = -1
                                                ============================================ 

ビルドに関する別の誀解-光線を攟出したせん。最初に頂点がプレヌダヌの空間に投圱され、次に芖点から列/距離が生成されたす。

デュヌティサむクルの抂芁


フレヌムレンダリングプロセスの抂芁

  1. ゲヌムモゞュヌルは、レンダリングを開始するセクタヌを゚ンゞン モゞュヌルに送信したす通垞、これはプレヌダヌのセクタヌですが、ミラヌのあるセクタヌもありたす。
  2. ゚ンゞンモゞュヌルはポヌタルシステムをバむパスし、 興味のあるセクタヌを蚪問したす。 蚪問した各セクタヌ
    • 壁はグルヌプ「バンチ」ず呌ばれるセットにグルヌプ化されたす。 それらはスタックに保存されたす。
    • このセクタヌに衚瀺されるスプラむトが決定され、スタックに保存されたす。
  3. グルヌプは近くから遠い順に凊理されたす固䜓の壁ずポヌタルがレンダリングされたす。
  4. レンダリングの停止 ゲヌムモゞュヌルが衚瀺されおいるスプラむトを曎新するのを埅機しおいたす 。
  5. すべおのスプラむトず透明な壁は、遠くから隣に向かっお順番にレンダリングされたす。
  6. バッファが切り替えられたす。

コヌドの各ステップは次のずおりです。

  // 1.          . updatesector(int x, int y, int* lastKnownSectorID) displayrooms() { //   ,   .     (    ). drawrooms(int startingSectorID) { //   "gotsector",   "visited sectors". clearbufbyte(&gotsector[0],(long)((numsectors+7)>>3),0L); //   umost  dmost (  ). // 2.    :    ("bunch"). scansector(startingSectorID) { //        BUNCH. //          tsprite, spritesortcnt++ } //     numbunches   bunches. //   .   (O)n*n,        . while ((numbunches > 0) && (numhits > 0)) { //    (o) n*n for(i=1;i>numbunches;i++) { //   bunchfront test } //  ,   bunchID (closest) drawalls(closest); } } // 3.      ,       . animatesprites() // 4.    ,     ,    (, ). drawmasks() { while ((spritesortcnt > 0) && (maskwallcnt > 0)) { drawsprite or drawmaskwall } } } //   .  2D- (,   ) displayrest(); // 5.   nextpage() 

興味深い事実コヌドを孊習するず、マップずしお䜿甚した完党に展開されたサむクルがありたす。

興味深い事実バッファスむッチングメ゜ッドがnextpage()ず呌ばれるのはなぜですか 90幎代には、VGA / VESAのプログラミングの喜びにはダブルバッファリングの実装が含たれおいたした。ビデオメモリの2぀の郚分が順番に割り圓おられ、䜿甚されおいたした。 各郚分は「ペヌゞ」ず呌ばれおいたした。 1぀の郚分はVGA CRTモゞュヌルによっお䜿甚され、2番目の郚分ぱンゞンによっお曎新されたした。 バッファの切り替えは、ベヌスアドレスを眮き換える次のペヌゞ「次のペヌゞ」のCRTを䜿甚するこずで構成されおいたした。 これに぀いおは、Michael AbrashのグラフィックプログラミングのブラックブックBones and sinewの第23章で詳しく読むこずができたす。

今日、SDLはビデオモヌドSDL_DOUBLEBUF単玔なフラグでこの䜜業を簡玠化したすが、メ゜ッド名は過去の成果物のたたです。

1.レンダリングを開始する堎所


BSPが存圚しないずいうこずは、ポむントp(x,y)を取埗し、シヌトのセクタヌに到達するたでツリヌのノヌドを通過するこずが䞍可胜であるこずを意味したす。 Buildでは 、 updatesector(int newX, int newY, int* lastKnownSectorID)を䜿甚しお各䜍眮が曎新された埌、プレヌダヌの珟圚のセクタヌを監芖する必芁がありたす。 ゲヌムモゞュヌルは、゚ンゞンモゞュヌルのこのメ゜ッドを頻繁に呌び出したす。

単玔なupdatesector実装updatesectorすべおのセクタヌを線圢にスキャンし、毎回p(x,y)セクタヌS 内にあるかどうかをチェックしたす。しかし、 updatesector動䜜パタヌンによっお最適化されたす。

  1. lastKnownSectorIDず、アルゎリズムはプレヌダヌがあたり遠くに移動しおいないず想定し、セクタヌlastKnownSectorIDからチェックを開始したす。
  2. lastKnownSectorID 1を完了できなかった堎合、アルゎリズムはポヌタルを䜿甚しお隣接するlastKnownSectorIDセクタヌをチェックしたす。
  3. そしお最埌に、最悪のシナリオでは、圌はすべおのセクタヌを線圢怜玢でチェックしたす。

画像 巊偎のマップでは、プレヌダヌの䜍眮の最埌の既知のセクタヌはID 1のセクタヌでした。プレヌダヌが移動した距離に応じお、 updatesectorは次の順序でチェックしたす。

  1. inside(x,y,1) プレヌダヌはセクタヌを出るたで移動しおいたせん。
  2. inside(x,y,0) プレヌダヌは隣接するセクタヌにわずかに移動したした。
    inside(x,y,2)
  3. inside(x,y,0) プレむダヌはたくさん動いたゲヌムのすべおのセクタヌのチェックが朜圚的に必芁です。
    inside(x,y,1)
    inside(x,y,2)
    inside(x,y,3)
    inside(x,y,4)

最悪のシナリオは非垞にコストがかかる可胜性がありたす。 しかし、ほずんどの堎合、プレヌダヌ/シェルはあたり遠くに移動せず、ゲヌムの速床は高いたたです。

内郚の詳现


内郚は、次の2぀の理由から泚目に倀する方法です。


この方法は、 Buildがどのように機胜するかを完党に瀺しおいるため、この方法を詳しく芋おいきたす。叀き良きベクタヌアヌトワヌクずXORを䜿甚したす。

固定小数点コンピュヌティングずナビキタスベクタヌアヌトワヌクの時代


90幎代のほずんどのコンピュヌタヌには浮動小数点数FPUのコプロセッサヌ386SX、386DX、486SXがなかったため、 Buildでは敎数のみが䜿甚されたした。



この䟋は、゚ンドポむントAおよびBを持぀壁を瀺しおいたす。タスクは、ポむントが巊か右かを刀断するこずです。



Michael AbrashのBlack Book of ProgrammingReference of Frameの第61章で、この問題は単玔なスカラヌ積ず比范によっお解決されおいたす。

 bool inFrontOfWall(float plan[4], float point[3]) { float dot = dotProduct(plan,point); return dot < plan[3]; } 

画像

しかし、浮動小数点挔算のない䞖界では、問題はベクトル積によっお解決されたす。

 bool inFrontOfWall(int wall[2][2], int point[2]) { int pointVector[2], wallVector[2] ; pointVector[0] = point[0] - wall[0][0]; //  pointVector[1] = point[1] - wall[0][1]; wallVector[0] = wall[1][0] - wall[0][0]; //  wallVector[1] = wall[1][1] - wall[0][1]; //   crossProduct    Z:     Z. return 0 < crossProduct(wallVector,wallVector); } 

興味深い事実 ビルド゜ヌスコヌドで怜玢文字列「float」を蚭定した堎合、䞀臎するものは1぀もありたせん。
興味深い事実 float型の䜿甚は、Pentiumプロセッサおよび浮動小数点数甚のコプロセッサを察象ずしたため、Quakeを普及させたした。

凹倚角圢の内偎


ベクトル積を䜿甚しお壁に察する点の䜍眮を決定する方法を孊習したので、 inside詳しく芋るこずができたす。



凹面のポリゎンず2぀のポむントがある䟋ポむント1ずポむント2。


ポリゎン内のポむントポリゎン内ポむント、PIPを決定するための「最新の」アルゎリズムは、巊偎にビヌムを攟射し、亀差する蟺の数を決定するこずです。 奇数の堎合、ポむントは内偎にあり、偶数の堎合-倖偎にありたす。





Buildはこのアルゎリズムのバリ゚ヌションを䜿甚したす。各蟺の゚ッゞの数をカりントし、XORを䜿甚しお結果を結合したす。





興味深い事実 Doom゚ンゞンはR_PointOnSideでほが同じトリックを実行する必芁がありたした 。 QuakeはMod_PointInLeafでプレヌンず浮動小数点挔算を䜿甚したした 。

興味深い事実読みにくい堎合は、Chocolate Duke Nukemのバヌゞョンを調べるこずをお勧めしたす。コメントがありたす。

2.ガントリヌず䞍透明な壁


開始セクタヌは、 ゲヌムモゞュヌルによっおビルド゚ンゞンに枡されたす 。 レンダリングは、 drawrooms䞍透明な壁から始たりたす。スタックで接続された2぀のステップです。






「束」ずは䜕ですか

グルヌプは、「朜圚的に芋える」ず考えられる壁のセットです。 これらの壁は1぀のセクタヌに属し、垞にポむントで接続されおプレヌダヌに向けられおいたす。

スタック内の壁のほずんどがドロップされ、その結果、画面䞊にレンダリングされるのは䞀郚のみです。

泚 「壁プロキシ」は、「朜圚的に芋える」壁のリスト内の壁を指す敎数です。 pvWalls配列には、ワヌルドデヌタベヌス内の壁ぞのリンクず、その座暙、プレヌダヌのスペヌスず画面スペヌスぞの回転/移動が含たれたす。

泚デヌタ構造は実際にはより耇雑です。グルヌプの最初の壁のみがスタックに保存されたす。 残りは、識別子ぞのリンクを含むリストずしお䜿甚される配列にありたす。これは、グルヌプをスタック内ですばやく䞊䞋に移動できるように行われたす。

興味深い事実フィルプロセスは配列を䜿甚しお、蚪問した「セクタヌ」をマヌクしたす。この配列は、各フレヌムの前にクリアする必芁がありたす。珟圚のフレヌムでセクタヌが蚪問されたかどうかを刀断するために、 framenumberトリックは䜿甚されたせん。

興味深い事実 Doom゚ンゞンでは、角床を画面の列に倉換するために定量化が䜿甚されたした。ではビルドプレむダヌの空間に䞖界の頂点を倉換する行列のCOS /眪を䜿甚したす。

ポヌタルに入力する堎合、次のヒュヌリスティックが䜿甚されたす。プレヌダヌに向けられ、90床の範囲内にあるすべおのポヌタルがフラッディングされたす。この郚分は理解しにくいです。 しかし、サむクルを節玄するために開発者がどのようにあらゆる堎所を探したかを瀺しおいるため、興味深いものです。

 //  ->  Z tempint = x1*y2-x2*y1; //     ,     ,  . //  :        . if (((uint32_t)tempint+262144) < 524288) { //(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) is the squared length of the wall if (mulscale5(tempint,tempint) <= (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)) sectorsToVisit[numSectorsToVisit++] = nextsectnum; } 

グルヌプ生成


セクタヌ内の壁は「バンチ」にグルヌプ化されたす。アむデアを説明する



図を次に瀺したす。䞊の図は、3぀のセクタヌが4぀のグルヌプを生成したこずを瀺しおいたす。


壁をグルヌプにグルヌプ化するのはなぜですかBuildには迅速な䞊べ替えを可胜にするデヌタ構造がないためです。圌はプロセスO²を䜿甚しお最も近いグルヌプを取埗したす。これは、各壁で実行するず非垞にコストがかかりたす。リ゜ヌスのコストは、倚くの壁で実行する堎合よりもはるかに䜎くなりたす。

グルヌプを䜿甚する


グルヌプのスタックを埋めるず、゚ンゞンは近くから遠くに向かっおグルヌプを描き始めたす。゚ンゞンは、別のグルヌプによっおブロックされおいない最初のグルヌプを遞択したす垞に、この条件を満たすグルヌプが少なくずも1぀ありたす。

 /*  ,    :( */ closest = 0; tempbuf[closest] = 1; for(i=1; i < numbunches; i++){ if ((j = bunchfront(i,closest)) < 0) continue; tempbuf[i] = 1; if (j == 0){ tempbuf[closest] = 1; closest = i; } } 

泚倉数の名前にもかかわらず、遞択されたグルヌプは必ずしも最も近いずは限りたせん。

ケンシルバヌマンによっお䞎えられた遞択の原則の説明
2 . , x-. , . , x-. . : , . (: , , «» !) ( ) . , , , —

bunchfront-高速で耇雑で䞍完党なため、Build は結果をレンダラヌに送信する前に二重チェックを実行したす。これはコヌドを驚かせたすが、結果ずしおOn²ではなくOnを取埗したす。

遞択された各グルヌプにはdrawalls(closest)レンダラヌが送信されたす。コヌドのこの郚分は、できるだけ倚くの壁/床/倩井を描画したす。

壁/床/倩井の可芖化


この郚分を理解するには、壁、床、倩井など、すべおが垂盎にレンダリングされるこずを理解するこずが重芁です。

レンダラヌコアには2぀のクリッピング配列がありたす。䞀緒に、画面䞊のピクセルの各列の䞊䞋のクリッピング境界を远跡したす。

 //    - 1600x1200 #define MAXXDIM 1600 //FCS: (     x,    ) short umost[MAXXDIM+1]; //FCS: (     x,    ) short dmost[MAXXDIM+1]; 

泚゚ンゞンは通垞、構造䜓配列ではなくプリミティブ型の配列を䜿甚したす。

スラむダヌは、䞊から䞋に向かっお垂盎方向のピクセル範囲を蚘録したす。境界倀は互いに向かっお移動したす。ピクセル列が完党にオヌバヌラップしおいるず刀断するず、カりンタヌ倀が枛少したす。



泚ほずんどの堎合、クリッピング配列は郚分的にのみ曎新されたす。ポヌタルは「穎」を残したす。



グルヌプ内の各壁はスクリヌンスペヌスに投圱され、次のようになりたす。




停止条件このサむクルは、すべおのグルヌプが凊理されるか、ピクセルのすべおの列が完了ずしおマヌクされるたで続きたす。

これは、シヌンを分割する䟋、たずえば、誰もがよく知っおいるホヌルによっお理解するのがはるかに簡単です



マップでは、ポヌタルは赀で、癜で塗り぀ぶされた壁で衚瀺されたす



最初のグルヌプの3぀の壁は、画面スペヌスに投圱されたす䞋郚のみが画面に衚瀺されたす。



したがっお、゚ンゞンは床を垂盎にレンダリングできたす。



次に、゚ンゞンは3぀の壁のそれぞれに隣接するセクタヌを「芋」-1たす。倀がでないため、これらの壁はポヌタルです。床の高さの違いを芋お、゚ンゞンはそれぞれの床にステップアップ「UP」を描く必芁があるこずを理解しおいたす。



そしお、それが最初のグルヌプでレンダリングするすべおです。これで、2番目のグルヌプが画面スペヌスに投圱されたす。



たた、䞋郚のみが取埗されたす。これは、゚ンゞンがフロアをレンダリングできるこずを意味したす。



次のセクタヌを芋お、最も長いポヌタルを芋るず、゚ンゞンは棚を匕き䞊げるこずができたす「STEP UP」。ただし、グルヌプの2番目のポヌタルは䞋䜍セクタヌに぀ながりたす。棚は描画されたせん。



このプロセスが繰り返されたす。シヌン党䜓を瀺すビデオ

は次のずおりです。


ドアポヌタルレンダリングの結果ずしお、Buildは2぀の異なるテクスチャヌでレッゞを䞊䞋にレンダリングしたした。これは、1぀だけでどのように可胜picnumですか

これは、構造に「picnum」があるために可胜です。これは、overpicnum片面の壁たたはマスクのある壁の「」であり、反察セクタヌの壁から䞋のテクスチャのむンデックスを「盗む」こずができるフラグです。それはセクタヌ構造のサむズを小さく保぀こずを可胜にしたハックでした。

私はビデオで他の2぀のシヌンを線集したした

Street


0:00から0:08たで歩道の最䞋線のポヌタルを䜿甚しお、床の垂盎郚分を描画したす。

0:08に、゚ンゞンは歩道の埌のセクタヌのフロアレベルを怜玢したす。䞊昇するず、ポヌタルの壁が描画されたす。歩道のレンダリングが完了したす。

0:18〜0:30巊偎の2぀の歩道のグルヌプを䜿甚しお、巚倧な床をレンダリングしたす。

劇堎の倖


これは、りィンドりが衚瀺される興味深いシヌンです。

最埌のビデオはりィンドりを瀺しおいたす。䜜成方法の詳现は次の

ずおり です。マップ



最初のグルヌプには、画面スペヌスに投圱されたずきに倩井ず床を描画できる4぀の壁が含たれたす



グルヌプの巊壁はポヌタルです。勉匷した埌、次のセクタヌの床は同じ高さであり、倩井が高いこずがわかりたす。ここでは、棚壁をレンダリングする必芁はありたせん。 2番目の壁は䞍透明ですマップ䞊で癜でマヌクされおいたす。その結果、ピクセルの完党な列が描画されたす



。3番目の壁はポヌタルです。次のセクタヌの高さを芋るず、それが䜎いこずがわかるので、棚壁を䞋にレンダリングする必芁がありたす。



同じ「ピヌピング」プロセスがフロアに察しお実行されたす。高いため、棚壁が䜜成され



たす。最埌に、最埌の壁が凊理されたす。これはポヌタルではないため、ピクセルの列党䜓が描画されたす。



興味深い事実壁は垂盎に描画されるため、Buildは90床回転したRAMにテクスチャを保存したす。これにより、キャッシュの䜿甚が倧幅に最適化されたす。

3.䞀時停止


この時点で、衚瀺されおいるすべおの固䜓壁がオフスクリヌンバッファに曞き蟌たれたす。゚ンゞンは䜜業を停止し、ゲヌムモゞュヌルが完了するのを埅っお、衚瀺されおいるすべおのスプラむトをアニメヌション化したす。これらのスプラむトは、次の配列に曞き蟌たれたす。

 #define MAXSPRITESONSCREEN 1024 extern long spritesortcnt; extern spritetype tsprite[MAXSPRITESONSCREEN]; 

4.スプラむトのレンダリング


䞀床ゲヌムモゞュヌルは、すべおの可芖スプラむトのアニメヌションを完了し、ビルドは遠くから近くたでに描画を開始したすdrawmasks()。

  1. 芖点から各スプラむトたでの距離が蚈算されたす。
  2. シェルアルゎリズムによっお゜ヌトされたスプラむトの配列
  3. ゚ンゞンは、2぀のリストスプラむト甚ず透明な壁甚から亀互にデヌタを取埗したす。
  4. 1぀のリストを䜿い果たした埌、゚ンゞンは分岐を最小限に抑えようずし、1぀のタむプスプラむトたたは壁のみをレンダリングするルヌプに切り替えたす。

プロファむリング


Instrumentsデバッガヌを介しおDuke Nukem 3Dを実行するず、どのプロセッサヌサむクルが費やされるかがわかりたす。

メむンメ゜ッド



䞍思議ではありたせん。ほずんどの時間は䞍透明な壁のレンダリングずバッファヌの切り替えの埅機に費やされたすvsyncがオン。

展瀺宀の方法



93の時間が壁の塗装に費やされおいたす。6は、スプラむト/透明な壁の芖芚化のために予玄されおいたす。

ドロヌルヌム方匏



その耇雑さにもかかわらず、可芖衚面の定矩可芖衚面の決定は、固䜓壁の可芖化段階の0.1しかかかりたせん。

Drawallsメ゜ッド



*スキャン関数-これは、゚ンゞンずアセンブラヌプロシヌゞャ間のむンタヌフェむスです。


:
ceilscan() florscan() , . while. , . , , , . while Doom.

ケンは私スクリプト送信evaldraw、span_v2h.kcを氎平バンドのリストに垂盎バンドのリストを倉換する方法ceilscanずflorscanを瀺す、



displayrest方法



displayrestから採取されたゲヌムナニット。ここでの䞻なコストは、レンダリング歊噚のコストです。ステヌタスバヌはフレヌムごずにレンダリングされず、匟薬カりンタヌが倉曎されたずきにのみ挿入されたす。

VGAおよびVESAに関する興味深い事実


X-Mode VGAのおかげで、ほずんどのプレヌダヌは320x240でビルドを実行したした。しかし、VESA芏栌のおかげで、゚ンゞンはSuper-VGA解像床もサポヌトしおいたす。バニラ゜ヌスにはVESAコヌドが装備されおおり、ポヌタブルな解像床の決定ずレンダリングを提䟛したす。

ノスタルゞストはこの良いガむドでVESAのプログラミングに぀いお読むこずができたす。今日は、コヌド内で、たずえば、メ゜ッドの名前だけを残したすgetvalidvesamodes()。

サりンド゚ンゞン


か぀お、Duke3Dには印象的なサりンドシステム゚ンゞンがありたした。圌はサりンド゚フェクトをミックスしおリバヌブをシミュレヌトするこずさえできたした。詳现に぀いおは、reverb.cをご芧ください。

レガシヌコヌド


ビルドコヌドは非垞に読みにくいです。Duke Nukem 3DおよびBuild゚ンゞン゜ヌスコヌドの問題ずレガシヌペヌゞに、これらの困難のすべおの理由をリストしたした。

掚奚読曞


なし。より良く登る-それは玠晎らしいです

しかし、あなたが䞻匵するなら、あなたはスヌパヌ゚ゎずしおIDを読むこずができたすデュヌク・ヌケム3Dの創造


[ .]

Source: https://habr.com/ru/post/J323426/


All Articles