diff --git a/convert.py b/convert.py new file mode 100644 index 0000000..e13ef6c --- /dev/null +++ b/convert.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +"""Convert Quake 3 tutorial HTML dumps (from archive.org) to clean Markdown.""" + +import os +import re +import shutil +from pathlib import Path +from bs4 import BeautifulSoup, NavigableString, Tag + +BASE = Path(__file__).parent / "html_dumps_from_archive_org" +OUT = Path(__file__).parent / "leveldk.co.uk" +IMAGES = OUT / "images" + +# (source html number, output stem, full title) +TUTORIALS = [ + (2, "tutorial-01", "Basic Room"), + (3, "tutorial-02", "2-Point Clipping, Brush and Patch Work"), + (4, "tutorial-03", "Detail and Structural Brushes, Level Layout"), + (5, "tutorial-04", "Advanced Patch Work, Lighting, Stairs and Ramps"), + (6, "tutorial-05", "Stuff with Holes, Liquids, Rocks and Terrain"), + (7, "tutorial-06", "Lighting the Level – Advanced Techniques"), + (8, "tutorial-07", "Compiling, Creating .pk3 Files"), + (9, "tutorial-08", "Converting the Level for CTF"), + (10, "tutorial-09", "Extra Entities"), +] + +# File extensions and patterns to wrap in backticks +_CODE_EXTS = r"(?:pk3|bsp|map|arena|aas|ase|shader|zip|cfg|bat|htm|txt|jpg|png|md3)" +_FILE_RE = re.compile( + r"(? str: + """Wrap console commands and filenames in backticks.""" + # Wrap slash-prefixed console commands: /sv_pure 0, /devmap, etc. + text = _CMD_RE.sub(r"`\1`", text) + # Wrap filenames with known Q3/mapping extensions + text = _FILE_RE.sub(r"`\1`", text) + return text + + +def fix_encoding(text: str) -> str: + """Fix Windows-1252 curly quotes / dashes that survived as latin-1 bytes.""" + replacements = { + "\u2018": "'", # left single quotation mark + "\u2019": "'", # right single quotation mark (also apostrophe) + "\u201c": '"', # left double quotation mark + "\u201d": '"', # right double quotation mark + "\u2013": "--", # en dash + "\u2014": "--", # em dash + "\u2026": "...",# ellipsis + "\u00e2\u0080\u0099": "'", # UTF-8 misread artefact + # Raw windows-1252 bytes that sometimes survive + "\x91": "'", "\x92": "'", + "\x93": '"', "\x94": '"', + "\x96": "--", "\x97": "--", + } + for bad, good in replacements.items(): + text = text.replace(bad, good) + return text + + +def element_to_markdown(el, first_heading_seen: list) -> str | None: + """ + Convert a single BeautifulSoup element to a Markdown string. + Returns None to signal 'skip this element'. + first_heading_seen is a mutable list used as a flag. + """ + if not isinstance(el, Tag): + return None + if el.name != "p": + return None + + # --- Image paragraph (check before blank-text check, img has no text) --- + img = el.find("img") + if img: + src = img.get("src", "") + # Strip the "level_tutoral_N_files/" prefix, keep just the filename + filename = Path(src).name + return f"![{filename}](images/{filename})" + + # --- Blank / whitespace-only paragraphs --- + raw_text = el.get_text(separator=" ").strip() + if not raw_text or raw_text == "\xa0": + return None # skip spacer + + # --- Skip the archive.org "Back to index" link paragraph --- + link = el.find("a") + if link and "archive.org" in link.get("href", ""): + return None + + # --- Figure caption paragraph: starts with "fig." --- + if re.match(r"fig\.", raw_text, re.IGNORECASE): + return f"*{fix_encoding(raw_text)}*" + + # --- Heading paragraph:

...

--- + align = el.get("align", "") or el.get("style", "") + is_centered = "center" in align + bold = el.find("b") + if is_centered and bold and bold.get_text(strip=True): + heading_text = bold.get_text(separator=" ").strip() + heading_text = re.sub(r"\s+", " ", heading_text) + heading_text = fix_encoding(heading_text) + if not first_heading_seen: + first_heading_seen.append(True) + return f"# {heading_text}" + else: + return f"## {heading_text}" + + # --- Regular paragraph: collect inline markup --- + parts = [] + for child in el.children: + if isinstance(child, NavigableString): + # Normalise inline whitespace (HTML source has bare newlines) + parts.append(re.sub(r"[\r\n]+", " ", str(child))) + elif child.name == "b": + inner = child.get_text(separator=" ").strip() + if inner: + parts.append(f"**{inner}**") + elif child.name == "i": + inner = child.get_text(separator=" ").strip() + if inner: + parts.append(f"*{inner}*") + elif child.name == "a": + # Strip archive.org URLs; just keep link text + parts.append(child.get_text(separator=" ")) + elif child.name == "u": + inner = child.get_text(separator=" ") + parts.append(inner) + elif child.name == "img": + pass # handled above at paragraph level + else: + parts.append(child.get_text(separator=" ")) + + # Ensure a space separates a closing *...* marker from the next word. + # Do this at the parts level (before joining) to avoid mistakenly adding + # space after an *opening* marker like team_CTF_*colour*flag. + normalized: list[str] = [] + for part in parts: + if (normalized + and normalized[-1].endswith(("*", "**")) + and part + and part[0].isalpha()): + normalized.append(" ") + normalized.append(part) + parts = normalized + + para = "".join(parts).strip() + para = fix_encoding(para) + para = apply_inline_code(para) + # Collapse excessive whitespace + para = re.sub(r"[ \t]+", " ", para) + para = re.sub(r"\n+", " ", para) + if not para: + return None + return para + + +def convert_tutorial(num: int, stem: str, title: str) -> None: + html_path = BASE / f"level_tutoral_{num}.html" + files_dir = BASE / f"level_tutoral_{num}_files" + + # Read bytes and let BS detect encoding from meta charset + raw = html_path.read_bytes() + soup = BeautifulSoup(raw, "html.parser", from_encoding="windows-1252") + + body = soup.find("body") + if not body: + print(f" WARNING: no found in {html_path.name}") + return + + # --- Copy images --- + if files_dir.exists(): + for img_file in files_dir.glob("*.jpg"): + shutil.copy2(img_file, IMAGES / img_file.name) + + # --- Build Markdown --- + lines = [] + first_heading = [] + + for el in body.children: + md = element_to_markdown(el, first_heading) + if md is not None: + lines.append(md) + + # If the page title wasn't in the HTML headings, prepend it + if not first_heading: + lines.insert(0, f"# {title}") + + # --- Navigation footer --- + idx = next(i for i, t in enumerate(TUTORIALS) if t[0] == num) + prev_link = f"[← Tutorial {idx}]({TUTORIALS[idx - 1][1]}.md)" if idx > 0 else "" + next_link = f"[Tutorial {idx + 2} →]({TUTORIALS[idx + 1][1]}.md)" if idx < len(TUTORIALS) - 1 else "" + nav_parts = [p for p in [prev_link, "[Index](index.md)", next_link] if p] + nav = " | ".join(nav_parts) + lines.append("") + lines.append("---") + lines.append(nav) + + # Write output + out_path = OUT / f"{stem}.md" + out_path.write_text("\n\n".join(lines) + "\n", encoding="utf-8") + print(f" Written {out_path.name} ({len(lines)} blocks)") + + +def build_index() -> None: + descriptions = { + 2: "Creating a brush, selecting textures, changing views, selecting faces, adding a start point, saving and compiling.", + 3: "2-point clipping, endcaps and bevels, making a curved hallway, texturing patches.", + 4: "Detail and structural brushes, level layout, triggers and traps, adding geometry, .ase models.", + 5: "Advanced patch manipulation, lighting tips, stairs and ramps including spiral stairs.", + 6: "Nodraw and surfaces with holes, custom textures and shaders, alphaMod volumes, lava/liquids, rocks and terrain.", + 7: "Advanced lighting techniques, light styles, clip brushes and bot optimisation, hint brushes, clusterportals.", + 8: "Compiling the map, finishing the level, creating a .pk3, adding music, .arena files, levelshots and batch files.", + 9: "Converting the level for CTF: team_CTF spawn, player and flag entities.", + 10: "Extra entities: func_door, func_rotating, func_bobbing, func_train, func_plat, areaportals, target_speaker, triggers.", + } + + lines = [ + "# Quake 3 Level Design Tutorials", + "", + "A series of tutorials covering the construction of a complete Quake 3 Arena level " + "for both Deathmatch and Capture the Flag, using GTKRadiant 1.4.", + "", + "All map files and media are available in `dk_lmtut.pk3`.", + "", + "## Tutorials", + "", + ] + for i, (num, stem, title) in enumerate(TUTORIALS, 1): + desc = descriptions.get(num, "") + lines.append(f"- **[Tutorial {i}: {title}]({stem}.md)** — {desc}") + + out = OUT / "index.md" + out.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f" Written index.md") + + +def main() -> None: + IMAGES.mkdir(parents=True, exist_ok=True) + OUT.mkdir(parents=True, exist_ok=True) + + for num, stem, title in TUTORIALS: + print(f"Converting tutorial {num} → {stem}.md …") + convert_tutorial(num, stem, title) + + print("Building index …") + build_index() + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/leveldk.co.uk/images/alpha1.jpg b/leveldk.co.uk/images/alpha1.jpg new file mode 100644 index 0000000..cdcb5be Binary files /dev/null and b/leveldk.co.uk/images/alpha1.jpg differ diff --git a/leveldk.co.uk/images/alpha2.jpg b/leveldk.co.uk/images/alpha2.jpg new file mode 100644 index 0000000..7d37699 Binary files /dev/null and b/leveldk.co.uk/images/alpha2.jpg differ diff --git a/leveldk.co.uk/images/alpha3.jpg b/leveldk.co.uk/images/alpha3.jpg new file mode 100644 index 0000000..1f37297 Binary files /dev/null and b/leveldk.co.uk/images/alpha3.jpg differ diff --git a/leveldk.co.uk/images/alpha4.jpg b/leveldk.co.uk/images/alpha4.jpg new file mode 100644 index 0000000..2943683 Binary files /dev/null and b/leveldk.co.uk/images/alpha4.jpg differ diff --git a/leveldk.co.uk/images/alpha5.jpg b/leveldk.co.uk/images/alpha5.jpg new file mode 100644 index 0000000..4d998d8 Binary files /dev/null and b/leveldk.co.uk/images/alpha5.jpg differ diff --git a/leveldk.co.uk/images/alpha6.jpg b/leveldk.co.uk/images/alpha6.jpg new file mode 100644 index 0000000..cf78b30 Binary files /dev/null and b/leveldk.co.uk/images/alpha6.jpg differ diff --git a/leveldk.co.uk/images/angle.jpg b/leveldk.co.uk/images/angle.jpg new file mode 100644 index 0000000..e504433 Binary files /dev/null and b/leveldk.co.uk/images/angle.jpg differ diff --git a/leveldk.co.uk/images/ap1.jpg b/leveldk.co.uk/images/ap1.jpg new file mode 100644 index 0000000..ca2c0fb Binary files /dev/null and b/leveldk.co.uk/images/ap1.jpg differ diff --git a/leveldk.co.uk/images/ap2.jpg b/leveldk.co.uk/images/ap2.jpg new file mode 100644 index 0000000..cdcfac9 Binary files /dev/null and b/leveldk.co.uk/images/ap2.jpg differ diff --git a/leveldk.co.uk/images/arch1.jpg b/leveldk.co.uk/images/arch1.jpg new file mode 100644 index 0000000..8df1a6c Binary files /dev/null and b/leveldk.co.uk/images/arch1.jpg differ diff --git a/leveldk.co.uk/images/arch2.jpg b/leveldk.co.uk/images/arch2.jpg new file mode 100644 index 0000000..34c78e7 Binary files /dev/null and b/leveldk.co.uk/images/arch2.jpg differ diff --git a/leveldk.co.uk/images/arch3.jpg b/leveldk.co.uk/images/arch3.jpg new file mode 100644 index 0000000..6f6b7a0 Binary files /dev/null and b/leveldk.co.uk/images/arch3.jpg differ diff --git a/leveldk.co.uk/images/ase1.jpg b/leveldk.co.uk/images/ase1.jpg new file mode 100644 index 0000000..5e29a14 Binary files /dev/null and b/leveldk.co.uk/images/ase1.jpg differ diff --git a/leveldk.co.uk/images/ase2.jpg b/leveldk.co.uk/images/ase2.jpg new file mode 100644 index 0000000..f5fb71b Binary files /dev/null and b/leveldk.co.uk/images/ase2.jpg differ diff --git a/leveldk.co.uk/images/b1.jpg b/leveldk.co.uk/images/b1.jpg new file mode 100644 index 0000000..fce6a13 Binary files /dev/null and b/leveldk.co.uk/images/b1.jpg differ diff --git a/leveldk.co.uk/images/b2.jpg b/leveldk.co.uk/images/b2.jpg new file mode 100644 index 0000000..c9d4a80 Binary files /dev/null and b/leveldk.co.uk/images/b2.jpg differ diff --git a/leveldk.co.uk/images/b3.jpg b/leveldk.co.uk/images/b3.jpg new file mode 100644 index 0000000..06500c4 Binary files /dev/null and b/leveldk.co.uk/images/b3.jpg differ diff --git a/leveldk.co.uk/images/b4.jpg b/leveldk.co.uk/images/b4.jpg new file mode 100644 index 0000000..c8ea131 Binary files /dev/null and b/leveldk.co.uk/images/b4.jpg differ diff --git a/leveldk.co.uk/images/b5.jpg b/leveldk.co.uk/images/b5.jpg new file mode 100644 index 0000000..a1d34c8 Binary files /dev/null and b/leveldk.co.uk/images/b5.jpg differ diff --git a/leveldk.co.uk/images/b6.jpg b/leveldk.co.uk/images/b6.jpg new file mode 100644 index 0000000..feb605f Binary files /dev/null and b/leveldk.co.uk/images/b6.jpg differ diff --git a/leveldk.co.uk/images/b7.jpg b/leveldk.co.uk/images/b7.jpg new file mode 100644 index 0000000..d5a32c4 Binary files /dev/null and b/leveldk.co.uk/images/b7.jpg differ diff --git a/leveldk.co.uk/images/b8.jpg b/leveldk.co.uk/images/b8.jpg new file mode 100644 index 0000000..5734d04 Binary files /dev/null and b/leveldk.co.uk/images/b8.jpg differ diff --git a/leveldk.co.uk/images/basecolour.jpg b/leveldk.co.uk/images/basecolour.jpg new file mode 100644 index 0000000..079e9dc Binary files /dev/null and b/leveldk.co.uk/images/basecolour.jpg differ diff --git a/leveldk.co.uk/images/bob.jpg b/leveldk.co.uk/images/bob.jpg new file mode 100644 index 0000000..a219a7b Binary files /dev/null and b/leveldk.co.uk/images/bob.jpg differ diff --git a/leveldk.co.uk/images/botjp.jpg b/leveldk.co.uk/images/botjp.jpg new file mode 100644 index 0000000..25d586e Binary files /dev/null and b/leveldk.co.uk/images/botjp.jpg differ diff --git a/leveldk.co.uk/images/botroam.jpg b/leveldk.co.uk/images/botroam.jpg new file mode 100644 index 0000000..4d803f5 Binary files /dev/null and b/leveldk.co.uk/images/botroam.jpg differ diff --git a/leveldk.co.uk/images/but.jpg b/leveldk.co.uk/images/but.jpg new file mode 100644 index 0000000..fe80bfe Binary files /dev/null and b/leveldk.co.uk/images/but.jpg differ diff --git a/leveldk.co.uk/images/cave1.jpg b/leveldk.co.uk/images/cave1.jpg new file mode 100644 index 0000000..b93455e Binary files /dev/null and b/leveldk.co.uk/images/cave1.jpg differ diff --git a/leveldk.co.uk/images/cave10.jpg b/leveldk.co.uk/images/cave10.jpg new file mode 100644 index 0000000..08121bb Binary files /dev/null and b/leveldk.co.uk/images/cave10.jpg differ diff --git a/leveldk.co.uk/images/cave11.jpg b/leveldk.co.uk/images/cave11.jpg new file mode 100644 index 0000000..392754a Binary files /dev/null and b/leveldk.co.uk/images/cave11.jpg differ diff --git a/leveldk.co.uk/images/cave12.jpg b/leveldk.co.uk/images/cave12.jpg new file mode 100644 index 0000000..97eaa8c Binary files /dev/null and b/leveldk.co.uk/images/cave12.jpg differ diff --git a/leveldk.co.uk/images/cave2.jpg b/leveldk.co.uk/images/cave2.jpg new file mode 100644 index 0000000..6663960 Binary files /dev/null and b/leveldk.co.uk/images/cave2.jpg differ diff --git a/leveldk.co.uk/images/cave3.jpg b/leveldk.co.uk/images/cave3.jpg new file mode 100644 index 0000000..8a77b4b Binary files /dev/null and b/leveldk.co.uk/images/cave3.jpg differ diff --git a/leveldk.co.uk/images/cave4.jpg b/leveldk.co.uk/images/cave4.jpg new file mode 100644 index 0000000..9a8ab28 Binary files /dev/null and b/leveldk.co.uk/images/cave4.jpg differ diff --git a/leveldk.co.uk/images/cave5.jpg b/leveldk.co.uk/images/cave5.jpg new file mode 100644 index 0000000..31b2c57 Binary files /dev/null and b/leveldk.co.uk/images/cave5.jpg differ diff --git a/leveldk.co.uk/images/cave6.jpg b/leveldk.co.uk/images/cave6.jpg new file mode 100644 index 0000000..39ffd97 Binary files /dev/null and b/leveldk.co.uk/images/cave6.jpg differ diff --git a/leveldk.co.uk/images/cave7.jpg b/leveldk.co.uk/images/cave7.jpg new file mode 100644 index 0000000..3bd7053 Binary files /dev/null and b/leveldk.co.uk/images/cave7.jpg differ diff --git a/leveldk.co.uk/images/cave8.jpg b/leveldk.co.uk/images/cave8.jpg new file mode 100644 index 0000000..e60eeb4 Binary files /dev/null and b/leveldk.co.uk/images/cave8.jpg differ diff --git a/leveldk.co.uk/images/cave9.jpg b/leveldk.co.uk/images/cave9.jpg new file mode 100644 index 0000000..f4897ca Binary files /dev/null and b/leveldk.co.uk/images/cave9.jpg differ diff --git a/leveldk.co.uk/images/clip1.jpg b/leveldk.co.uk/images/clip1.jpg new file mode 100644 index 0000000..868715b Binary files /dev/null and b/leveldk.co.uk/images/clip1.jpg differ diff --git a/leveldk.co.uk/images/clip2.jpg b/leveldk.co.uk/images/clip2.jpg new file mode 100644 index 0000000..ec74f42 Binary files /dev/null and b/leveldk.co.uk/images/clip2.jpg differ diff --git a/leveldk.co.uk/images/clip3.jpg b/leveldk.co.uk/images/clip3.jpg new file mode 100644 index 0000000..531d077 Binary files /dev/null and b/leveldk.co.uk/images/clip3.jpg differ diff --git a/leveldk.co.uk/images/closebox.jpg b/leveldk.co.uk/images/closebox.jpg new file mode 100644 index 0000000..23a6e4a Binary files /dev/null and b/leveldk.co.uk/images/closebox.jpg differ diff --git a/leveldk.co.uk/images/cluster.jpg b/leveldk.co.uk/images/cluster.jpg new file mode 100644 index 0000000..d971634 Binary files /dev/null and b/leveldk.co.uk/images/cluster.jpg differ diff --git a/leveldk.co.uk/images/cluster2.jpg b/leveldk.co.uk/images/cluster2.jpg new file mode 100644 index 0000000..ff4843a Binary files /dev/null and b/leveldk.co.uk/images/cluster2.jpg differ diff --git a/leveldk.co.uk/images/ctflayout.jpg b/leveldk.co.uk/images/ctflayout.jpg new file mode 100644 index 0000000..fa6056f Binary files /dev/null and b/leveldk.co.uk/images/ctflayout.jpg differ diff --git a/leveldk.co.uk/images/detail1.jpg b/leveldk.co.uk/images/detail1.jpg new file mode 100644 index 0000000..35b0f3f Binary files /dev/null and b/leveldk.co.uk/images/detail1.jpg differ diff --git a/leveldk.co.uk/images/detail2.jpg b/leveldk.co.uk/images/detail2.jpg new file mode 100644 index 0000000..2f759be Binary files /dev/null and b/leveldk.co.uk/images/detail2.jpg differ diff --git a/leveldk.co.uk/images/detail3.jpg b/leveldk.co.uk/images/detail3.jpg new file mode 100644 index 0000000..f67530a Binary files /dev/null and b/leveldk.co.uk/images/detail3.jpg differ diff --git a/leveldk.co.uk/images/detail4.jpg b/leveldk.co.uk/images/detail4.jpg new file mode 100644 index 0000000..8f24dcf Binary files /dev/null and b/leveldk.co.uk/images/detail4.jpg differ diff --git a/leveldk.co.uk/images/detail5.jpg b/leveldk.co.uk/images/detail5.jpg new file mode 100644 index 0000000..8ace989 Binary files /dev/null and b/leveldk.co.uk/images/detail5.jpg differ diff --git a/leveldk.co.uk/images/dome1.jpg b/leveldk.co.uk/images/dome1.jpg new file mode 100644 index 0000000..5044bcb Binary files /dev/null and b/leveldk.co.uk/images/dome1.jpg differ diff --git a/leveldk.co.uk/images/dome2.jpg b/leveldk.co.uk/images/dome2.jpg new file mode 100644 index 0000000..cd15905 Binary files /dev/null and b/leveldk.co.uk/images/dome2.jpg differ diff --git a/leveldk.co.uk/images/dome3.jpg b/leveldk.co.uk/images/dome3.jpg new file mode 100644 index 0000000..b5c7621 Binary files /dev/null and b/leveldk.co.uk/images/dome3.jpg differ diff --git a/leveldk.co.uk/images/dome4.jpg b/leveldk.co.uk/images/dome4.jpg new file mode 100644 index 0000000..9d9b5a7 Binary files /dev/null and b/leveldk.co.uk/images/dome4.jpg differ diff --git a/leveldk.co.uk/images/dome5.jpg b/leveldk.co.uk/images/dome5.jpg new file mode 100644 index 0000000..0814c40 Binary files /dev/null and b/leveldk.co.uk/images/dome5.jpg differ diff --git a/leveldk.co.uk/images/door.jpg b/leveldk.co.uk/images/door.jpg new file mode 100644 index 0000000..fe19979 Binary files /dev/null and b/leveldk.co.uk/images/door.jpg differ diff --git a/leveldk.co.uk/images/dp1.jpg b/leveldk.co.uk/images/dp1.jpg new file mode 100644 index 0000000..56bbf4b Binary files /dev/null and b/leveldk.co.uk/images/dp1.jpg differ diff --git a/leveldk.co.uk/images/dp2.jpg b/leveldk.co.uk/images/dp2.jpg new file mode 100644 index 0000000..35c28c1 Binary files /dev/null and b/leveldk.co.uk/images/dp2.jpg differ diff --git a/leveldk.co.uk/images/dp3.jpg b/leveldk.co.uk/images/dp3.jpg new file mode 100644 index 0000000..d2f9fff Binary files /dev/null and b/leveldk.co.uk/images/dp3.jpg differ diff --git a/leveldk.co.uk/images/dp4.jpg b/leveldk.co.uk/images/dp4.jpg new file mode 100644 index 0000000..ec55fcb Binary files /dev/null and b/leveldk.co.uk/images/dp4.jpg differ diff --git a/leveldk.co.uk/images/dp5.jpg b/leveldk.co.uk/images/dp5.jpg new file mode 100644 index 0000000..cb52763 Binary files /dev/null and b/leveldk.co.uk/images/dp5.jpg differ diff --git a/leveldk.co.uk/images/drawbrush.jpg b/leveldk.co.uk/images/drawbrush.jpg new file mode 100644 index 0000000..c534cde Binary files /dev/null and b/leveldk.co.uk/images/drawbrush.jpg differ diff --git a/leveldk.co.uk/images/drawwalls.jpg b/leveldk.co.uk/images/drawwalls.jpg new file mode 100644 index 0000000..fb33d34 Binary files /dev/null and b/leveldk.co.uk/images/drawwalls.jpg differ diff --git a/leveldk.co.uk/images/ecap1.jpg b/leveldk.co.uk/images/ecap1.jpg new file mode 100644 index 0000000..b21b0ac Binary files /dev/null and b/leveldk.co.uk/images/ecap1.jpg differ diff --git a/leveldk.co.uk/images/ecap2.jpg b/leveldk.co.uk/images/ecap2.jpg new file mode 100644 index 0000000..aa31e67 Binary files /dev/null and b/leveldk.co.uk/images/ecap2.jpg differ diff --git a/leveldk.co.uk/images/ecap3.jpg b/leveldk.co.uk/images/ecap3.jpg new file mode 100644 index 0000000..0740bdb Binary files /dev/null and b/leveldk.co.uk/images/ecap3.jpg differ diff --git a/leveldk.co.uk/images/ecap4.jpg b/leveldk.co.uk/images/ecap4.jpg new file mode 100644 index 0000000..45ee0b6 Binary files /dev/null and b/leveldk.co.uk/images/ecap4.jpg differ diff --git a/leveldk.co.uk/images/ecap5.jpg b/leveldk.co.uk/images/ecap5.jpg new file mode 100644 index 0000000..bbc23b8 Binary files /dev/null and b/leveldk.co.uk/images/ecap5.jpg differ diff --git a/leveldk.co.uk/images/ecaptjunc1.jpg b/leveldk.co.uk/images/ecaptjunc1.jpg new file mode 100644 index 0000000..7dd948c Binary files /dev/null and b/leveldk.co.uk/images/ecaptjunc1.jpg differ diff --git a/leveldk.co.uk/images/ecaptjunc2.jpg b/leveldk.co.uk/images/ecaptjunc2.jpg new file mode 100644 index 0000000..0b82c95 Binary files /dev/null and b/leveldk.co.uk/images/ecaptjunc2.jpg differ diff --git a/leveldk.co.uk/images/entitiesandteamed.jpg b/leveldk.co.uk/images/entitiesandteamed.jpg new file mode 100644 index 0000000..83b8fa0 Binary files /dev/null and b/leveldk.co.uk/images/entitiesandteamed.jpg differ diff --git a/leveldk.co.uk/images/flags.jpg b/leveldk.co.uk/images/flags.jpg new file mode 100644 index 0000000..adefb2a Binary files /dev/null and b/leveldk.co.uk/images/flags.jpg differ diff --git a/leveldk.co.uk/images/hint.jpg b/leveldk.co.uk/images/hint.jpg new file mode 100644 index 0000000..e4e8197 Binary files /dev/null and b/leveldk.co.uk/images/hint.jpg differ diff --git a/leveldk.co.uk/images/hole1.jpg b/leveldk.co.uk/images/hole1.jpg new file mode 100644 index 0000000..9d0b12b Binary files /dev/null and b/leveldk.co.uk/images/hole1.jpg differ diff --git a/leveldk.co.uk/images/hole2.jpg b/leveldk.co.uk/images/hole2.jpg new file mode 100644 index 0000000..9a15620 Binary files /dev/null and b/leveldk.co.uk/images/hole2.jpg differ diff --git a/leveldk.co.uk/images/holes1.jpg b/leveldk.co.uk/images/holes1.jpg new file mode 100644 index 0000000..3a6c3b9 Binary files /dev/null and b/leveldk.co.uk/images/holes1.jpg differ diff --git a/leveldk.co.uk/images/holes2.jpg b/leveldk.co.uk/images/holes2.jpg new file mode 100644 index 0000000..b9adb4f Binary files /dev/null and b/leveldk.co.uk/images/holes2.jpg differ diff --git a/leveldk.co.uk/images/holes3.jpg b/leveldk.co.uk/images/holes3.jpg new file mode 100644 index 0000000..dd764f7 Binary files /dev/null and b/leveldk.co.uk/images/holes3.jpg differ diff --git a/leveldk.co.uk/images/infopdm.jpg b/leveldk.co.uk/images/infopdm.jpg new file mode 100644 index 0000000..b067fa5 Binary files /dev/null and b/leveldk.co.uk/images/infopdm.jpg differ diff --git a/leveldk.co.uk/images/initialplayer.jpg b/leveldk.co.uk/images/initialplayer.jpg new file mode 100644 index 0000000..b49d772 Binary files /dev/null and b/leveldk.co.uk/images/initialplayer.jpg differ diff --git a/leveldk.co.uk/images/interm.jpg b/leveldk.co.uk/images/interm.jpg new file mode 100644 index 0000000..9bbc15b Binary files /dev/null and b/leveldk.co.uk/images/interm.jpg differ diff --git a/leveldk.co.uk/images/jp1.jpg b/leveldk.co.uk/images/jp1.jpg new file mode 100644 index 0000000..e5e6b72 Binary files /dev/null and b/leveldk.co.uk/images/jp1.jpg differ diff --git a/leveldk.co.uk/images/jp2.jpg b/leveldk.co.uk/images/jp2.jpg new file mode 100644 index 0000000..1b9545b Binary files /dev/null and b/leveldk.co.uk/images/jp2.jpg differ diff --git a/leveldk.co.uk/images/lava.jpg b/leveldk.co.uk/images/lava.jpg new file mode 100644 index 0000000..4c25b04 Binary files /dev/null and b/leveldk.co.uk/images/lava.jpg differ diff --git a/leveldk.co.uk/images/light1.jpg b/leveldk.co.uk/images/light1.jpg new file mode 100644 index 0000000..896260b Binary files /dev/null and b/leveldk.co.uk/images/light1.jpg differ diff --git a/leveldk.co.uk/images/light2.jpg b/leveldk.co.uk/images/light2.jpg new file mode 100644 index 0000000..5cfc708 Binary files /dev/null and b/leveldk.co.uk/images/light2.jpg differ diff --git a/leveldk.co.uk/images/light3.jpg b/leveldk.co.uk/images/light3.jpg new file mode 100644 index 0000000..2b11022 Binary files /dev/null and b/leveldk.co.uk/images/light3.jpg differ diff --git a/leveldk.co.uk/images/location.jpg b/leveldk.co.uk/images/location.jpg new file mode 100644 index 0000000..fdde4af Binary files /dev/null and b/leveldk.co.uk/images/location.jpg differ diff --git a/leveldk.co.uk/images/move.jpg b/leveldk.co.uk/images/move.jpg new file mode 100644 index 0000000..2df5b25 Binary files /dev/null and b/leveldk.co.uk/images/move.jpg differ diff --git a/leveldk.co.uk/images/nodraw.jpg b/leveldk.co.uk/images/nodraw.jpg new file mode 100644 index 0000000..b0aa3a0 Binary files /dev/null and b/leveldk.co.uk/images/nodraw.jpg differ diff --git a/leveldk.co.uk/images/nodrop.jpg b/leveldk.co.uk/images/nodrop.jpg new file mode 100644 index 0000000..f658a97 Binary files /dev/null and b/leveldk.co.uk/images/nodrop.jpg differ diff --git a/leveldk.co.uk/images/null.jpg b/leveldk.co.uk/images/null.jpg new file mode 100644 index 0000000..04d301d Binary files /dev/null and b/leveldk.co.uk/images/null.jpg differ diff --git a/leveldk.co.uk/images/oldroom.jpg b/leveldk.co.uk/images/oldroom.jpg new file mode 100644 index 0000000..32a6c04 Binary files /dev/null and b/leveldk.co.uk/images/oldroom.jpg differ diff --git a/leveldk.co.uk/images/patchc.jpg b/leveldk.co.uk/images/patchc.jpg new file mode 100644 index 0000000..3d5fb1e Binary files /dev/null and b/leveldk.co.uk/images/patchc.jpg differ diff --git a/leveldk.co.uk/images/patchc2.jpg b/leveldk.co.uk/images/patchc2.jpg new file mode 100644 index 0000000..b4b1b04 Binary files /dev/null and b/leveldk.co.uk/images/patchc2.jpg differ diff --git a/leveldk.co.uk/images/patchc3.jpg b/leveldk.co.uk/images/patchc3.jpg new file mode 100644 index 0000000..df9bd0c Binary files /dev/null and b/leveldk.co.uk/images/patchc3.jpg differ diff --git a/leveldk.co.uk/images/patchsquarec.jpg b/leveldk.co.uk/images/patchsquarec.jpg new file mode 100644 index 0000000..6d93acd Binary files /dev/null and b/leveldk.co.uk/images/patchsquarec.jpg differ diff --git a/leveldk.co.uk/images/patchtrim.jpg b/leveldk.co.uk/images/patchtrim.jpg new file mode 100644 index 0000000..15aca36 Binary files /dev/null and b/leveldk.co.uk/images/patchtrim.jpg differ diff --git a/leveldk.co.uk/images/patchtrimverts.jpg b/leveldk.co.uk/images/patchtrimverts.jpg new file mode 100644 index 0000000..78ef621 Binary files /dev/null and b/leveldk.co.uk/images/patchtrimverts.jpg differ diff --git a/leveldk.co.uk/images/patchverts.jpg b/leveldk.co.uk/images/patchverts.jpg new file mode 100644 index 0000000..9c3dbab Binary files /dev/null and b/leveldk.co.uk/images/patchverts.jpg differ diff --git a/leveldk.co.uk/images/patchverts2.jpg b/leveldk.co.uk/images/patchverts2.jpg new file mode 100644 index 0000000..dddb1c9 Binary files /dev/null and b/leveldk.co.uk/images/patchverts2.jpg differ diff --git a/leveldk.co.uk/images/plat.jpg b/leveldk.co.uk/images/plat.jpg new file mode 100644 index 0000000..fd16ea5 Binary files /dev/null and b/leveldk.co.uk/images/plat.jpg differ diff --git a/leveldk.co.uk/images/r1.jpg b/leveldk.co.uk/images/r1.jpg new file mode 100644 index 0000000..d46fb11 Binary files /dev/null and b/leveldk.co.uk/images/r1.jpg differ diff --git a/leveldk.co.uk/images/r2.jpg b/leveldk.co.uk/images/r2.jpg new file mode 100644 index 0000000..441aa2a Binary files /dev/null and b/leveldk.co.uk/images/r2.jpg differ diff --git a/leveldk.co.uk/images/ramp1.jpg b/leveldk.co.uk/images/ramp1.jpg new file mode 100644 index 0000000..b98c6cb Binary files /dev/null and b/leveldk.co.uk/images/ramp1.jpg differ diff --git a/leveldk.co.uk/images/ramp2.jpg b/leveldk.co.uk/images/ramp2.jpg new file mode 100644 index 0000000..85f7ef6 Binary files /dev/null and b/leveldk.co.uk/images/ramp2.jpg differ diff --git a/leveldk.co.uk/images/ramp3.jpg b/leveldk.co.uk/images/ramp3.jpg new file mode 100644 index 0000000..e554010 Binary files /dev/null and b/leveldk.co.uk/images/ramp3.jpg differ diff --git a/leveldk.co.uk/images/ramp4.jpg b/leveldk.co.uk/images/ramp4.jpg new file mode 100644 index 0000000..02e2260 Binary files /dev/null and b/leveldk.co.uk/images/ramp4.jpg differ diff --git a/leveldk.co.uk/images/ramp5.jpg b/leveldk.co.uk/images/ramp5.jpg new file mode 100644 index 0000000..a8e7147 Binary files /dev/null and b/leveldk.co.uk/images/ramp5.jpg differ diff --git a/leveldk.co.uk/images/respawn.jpg b/leveldk.co.uk/images/respawn.jpg new file mode 100644 index 0000000..307f52f Binary files /dev/null and b/leveldk.co.uk/images/respawn.jpg differ diff --git a/leveldk.co.uk/images/rotate.jpg b/leveldk.co.uk/images/rotate.jpg new file mode 100644 index 0000000..a69ac9a Binary files /dev/null and b/leveldk.co.uk/images/rotate.jpg differ diff --git a/leveldk.co.uk/images/saveas.jpg b/leveldk.co.uk/images/saveas.jpg new file mode 100644 index 0000000..0574b25 Binary files /dev/null and b/leveldk.co.uk/images/saveas.jpg differ diff --git a/leveldk.co.uk/images/select.jpg b/leveldk.co.uk/images/select.jpg new file mode 100644 index 0000000..c272061 Binary files /dev/null and b/leveldk.co.uk/images/select.jpg differ diff --git a/leveldk.co.uk/images/select2.jpg b/leveldk.co.uk/images/select2.jpg new file mode 100644 index 0000000..b225b39 Binary files /dev/null and b/leveldk.co.uk/images/select2.jpg differ diff --git a/leveldk.co.uk/images/selectcaulk.jpg b/leveldk.co.uk/images/selectcaulk.jpg new file mode 100644 index 0000000..3bbe1e8 Binary files /dev/null and b/leveldk.co.uk/images/selectcaulk.jpg differ diff --git a/leveldk.co.uk/images/selectcaulkb.jpg b/leveldk.co.uk/images/selectcaulkb.jpg new file mode 100644 index 0000000..6e23b3e Binary files /dev/null and b/leveldk.co.uk/images/selectcaulkb.jpg differ diff --git a/leveldk.co.uk/images/selface.jpg b/leveldk.co.uk/images/selface.jpg new file mode 100644 index 0000000..70922cf Binary files /dev/null and b/leveldk.co.uk/images/selface.jpg differ diff --git a/leveldk.co.uk/images/seltex.jpg b/leveldk.co.uk/images/seltex.jpg new file mode 100644 index 0000000..4bea39d Binary files /dev/null and b/leveldk.co.uk/images/seltex.jpg differ diff --git a/leveldk.co.uk/images/shadow.jpg b/leveldk.co.uk/images/shadow.jpg new file mode 100644 index 0000000..b75849a Binary files /dev/null and b/leveldk.co.uk/images/shadow.jpg differ diff --git a/leveldk.co.uk/images/speaker.jpg b/leveldk.co.uk/images/speaker.jpg new file mode 100644 index 0000000..dac4920 Binary files /dev/null and b/leveldk.co.uk/images/speaker.jpg differ diff --git a/leveldk.co.uk/images/spst1.jpg b/leveldk.co.uk/images/spst1.jpg new file mode 100644 index 0000000..cb5fb14 Binary files /dev/null and b/leveldk.co.uk/images/spst1.jpg differ diff --git a/leveldk.co.uk/images/spst2.jpg b/leveldk.co.uk/images/spst2.jpg new file mode 100644 index 0000000..3c5a8c8 Binary files /dev/null and b/leveldk.co.uk/images/spst2.jpg differ diff --git a/leveldk.co.uk/images/spst3.jpg b/leveldk.co.uk/images/spst3.jpg new file mode 100644 index 0000000..058aa54 Binary files /dev/null and b/leveldk.co.uk/images/spst3.jpg differ diff --git a/leveldk.co.uk/images/spst4.jpg b/leveldk.co.uk/images/spst4.jpg new file mode 100644 index 0000000..911c814 Binary files /dev/null and b/leveldk.co.uk/images/spst4.jpg differ diff --git a/leveldk.co.uk/images/spst5.jpg b/leveldk.co.uk/images/spst5.jpg new file mode 100644 index 0000000..3698c41 Binary files /dev/null and b/leveldk.co.uk/images/spst5.jpg differ diff --git a/leveldk.co.uk/images/stairs.jpg b/leveldk.co.uk/images/stairs.jpg new file mode 100644 index 0000000..b51965f Binary files /dev/null and b/leveldk.co.uk/images/stairs.jpg differ diff --git a/leveldk.co.uk/images/tjunc1.jpg b/leveldk.co.uk/images/tjunc1.jpg new file mode 100644 index 0000000..2e58967 Binary files /dev/null and b/leveldk.co.uk/images/tjunc1.jpg differ diff --git a/leveldk.co.uk/images/tjunc2.jpg b/leveldk.co.uk/images/tjunc2.jpg new file mode 100644 index 0000000..6c4f940 Binary files /dev/null and b/leveldk.co.uk/images/tjunc2.jpg differ diff --git a/leveldk.co.uk/images/tp1.jpg b/leveldk.co.uk/images/tp1.jpg new file mode 100644 index 0000000..74baee0 Binary files /dev/null and b/leveldk.co.uk/images/tp1.jpg differ diff --git a/leveldk.co.uk/images/tp2.jpg b/leveldk.co.uk/images/tp2.jpg new file mode 100644 index 0000000..27168e9 Binary files /dev/null and b/leveldk.co.uk/images/tp2.jpg differ diff --git a/leveldk.co.uk/images/tr1.jpg b/leveldk.co.uk/images/tr1.jpg new file mode 100644 index 0000000..3613610 Binary files /dev/null and b/leveldk.co.uk/images/tr1.jpg differ diff --git a/leveldk.co.uk/images/tr2.jpg b/leveldk.co.uk/images/tr2.jpg new file mode 100644 index 0000000..3cd080b Binary files /dev/null and b/leveldk.co.uk/images/tr2.jpg differ diff --git a/leveldk.co.uk/images/tr3.jpg b/leveldk.co.uk/images/tr3.jpg new file mode 100644 index 0000000..2f208c1 Binary files /dev/null and b/leveldk.co.uk/images/tr3.jpg differ diff --git a/leveldk.co.uk/images/trapdoor.jpg b/leveldk.co.uk/images/trapdoor.jpg new file mode 100644 index 0000000..f347366 Binary files /dev/null and b/leveldk.co.uk/images/trapdoor.jpg differ diff --git a/leveldk.co.uk/images/trapdoor2.jpg b/leveldk.co.uk/images/trapdoor2.jpg new file mode 100644 index 0000000..8d38f86 Binary files /dev/null and b/leveldk.co.uk/images/trapdoor2.jpg differ diff --git a/leveldk.co.uk/images/tris1.jpg b/leveldk.co.uk/images/tris1.jpg new file mode 100644 index 0000000..e01ae11 Binary files /dev/null and b/leveldk.co.uk/images/tris1.jpg differ diff --git a/leveldk.co.uk/images/tris2.jpg b/leveldk.co.uk/images/tris2.jpg new file mode 100644 index 0000000..2b7b01f Binary files /dev/null and b/leveldk.co.uk/images/tris2.jpg differ diff --git a/leveldk.co.uk/images/tris3.jpg b/leveldk.co.uk/images/tris3.jpg new file mode 100644 index 0000000..07fd2d9 Binary files /dev/null and b/leveldk.co.uk/images/tris3.jpg differ diff --git a/leveldk.co.uk/images/tris4.jpg b/leveldk.co.uk/images/tris4.jpg new file mode 100644 index 0000000..b3ccdfa Binary files /dev/null and b/leveldk.co.uk/images/tris4.jpg differ diff --git a/leveldk.co.uk/images/tris5.jpg b/leveldk.co.uk/images/tris5.jpg new file mode 100644 index 0000000..973998c Binary files /dev/null and b/leveldk.co.uk/images/tris5.jpg differ diff --git a/leveldk.co.uk/images/walltemplate.jpg b/leveldk.co.uk/images/walltemplate.jpg new file mode 100644 index 0000000..cc25dc1 Binary files /dev/null and b/leveldk.co.uk/images/walltemplate.jpg differ diff --git a/leveldk.co.uk/images/walltemplatecorner.jpg b/leveldk.co.uk/images/walltemplatecorner.jpg new file mode 100644 index 0000000..d2757fd Binary files /dev/null and b/leveldk.co.uk/images/walltemplatecorner.jpg differ diff --git a/leveldk.co.uk/images/wsbitz.jpg b/leveldk.co.uk/images/wsbitz.jpg new file mode 100644 index 0000000..496a04d Binary files /dev/null and b/leveldk.co.uk/images/wsbitz.jpg differ diff --git a/leveldk.co.uk/index.md b/leveldk.co.uk/index.md new file mode 100644 index 0000000..fa7da42 --- /dev/null +++ b/leveldk.co.uk/index.md @@ -0,0 +1,17 @@ +# Quake 3 Level Design Tutorials + +A series of tutorials covering the construction of a complete Quake 3 Arena level for both Deathmatch and Capture the Flag, using GTKRadiant 1.4. + +All map files and media are available in `dk_lmtut.pk3`. + +## Tutorials + +- **[Tutorial 1: Basic Room](tutorial-01.md)** — Creating a brush, selecting textures, changing views, selecting faces, adding a start point, saving and compiling. +- **[Tutorial 2: 2-Point Clipping, Brush and Patch Work](tutorial-02.md)** — 2-point clipping, endcaps and bevels, making a curved hallway, texturing patches. +- **[Tutorial 3: Detail and Structural Brushes, Level Layout](tutorial-03.md)** — Detail and structural brushes, level layout, triggers and traps, adding geometry, .ase models. +- **[Tutorial 4: Advanced Patch Work, Lighting, Stairs and Ramps](tutorial-04.md)** — Advanced patch manipulation, lighting tips, stairs and ramps including spiral stairs. +- **[Tutorial 5: Stuff with Holes, Liquids, Rocks and Terrain](tutorial-05.md)** — Nodraw and surfaces with holes, custom textures and shaders, alphaMod volumes, lava/liquids, rocks and terrain. +- **[Tutorial 6: Lighting the Level – Advanced Techniques](tutorial-06.md)** — Advanced lighting techniques, light styles, clip brushes and bot optimisation, hint brushes, clusterportals. +- **[Tutorial 7: Compiling, Creating .pk3 Files](tutorial-07.md)** — Compiling the map, finishing the level, creating a .pk3, adding music, .arena files, levelshots and batch files. +- **[Tutorial 8: Converting the Level for CTF](tutorial-08.md)** — Converting the level for CTF: team_CTF spawn, player and flag entities. +- **[Tutorial 9: Extra Entities](tutorial-09.md)** — Extra entities: func_door, func_rotating, func_bobbing, func_train, func_plat, areaportals, target_speaker, triggers. diff --git a/leveldk.co.uk/tutorial-01.md b/leveldk.co.uk/tutorial-01.md new file mode 100644 index 0000000..abfcadd --- /dev/null +++ b/leveldk.co.uk/tutorial-01.md @@ -0,0 +1,95 @@ +# Basic Room Tutorial + +For the purpose of these tutorials I have used GTKRadiant 1.4 + +OK let's start with a few fundamentals. As mappers or level designers we are attempting to create visually interesting 3d worlds that are also fun to play in! + +It is important to be aware of some issues relating to the Q3 game engine that will impact upon your building. The 3d world is rendered as triangles (or tris) by Q3. If the number of triangles drawn at any one time starts to get very high, the game will slow down. Although more powerful computers can cope with higher tris numbers it is still advisable to try to keep tris at around 8000 at any one time. More about tris and how to control what is being rendered later. The other important point to consider at the moment is the prevention of 'leaks'. Leaks are when your world has a hole in it through to the void outside. Your level must be completely contained and sealed off from the outside otherwise you will create problems for your self later on. Good brushwork will help prevent leak spots in the first place, but GTK also has a handy feature called pointfile which can be found under the file menu. If during compile it is reported that your map has a leak you can click on pointfile. This draws a red line that shows you where the whole in the outside of the map can be found, making fixing the problem much easier. + +Right, let's do some building! Firstly open up the editor. Then you will need to select a texture to draw with. It is good practice to draw objects in the caulk texture. Caulk is invisible in game, but also solid so it has the effect of not adding to what Quake 3 has to draw on the screen at any one time and also preventing the inside of your world leaking through to the void outside. + +![selectcaulk.jpg](images/selectcaulk.jpg) + +If your common texture folder doesn't have all the textures I have in mine, don't worry. Place your mouse pointer over the pink caulk texture and left click. A red border should appear around the texture: + +![selectcaulkb.jpg](images/selectcaulkb.jpg) + +Now we're ready. In the 2d window, the large black one in my set up, we are going to create a 'brush'. Brushes are the blocks from which the worlds we create a made. + +You will notice the 2d window is covered in grid lines. The grid is your friend. Brushes are measured in 'units'. You can alter grid size by pressing the number keys on your keyboard. 1 equals grid size 1, 4 equals grid size 4, I'm sure you get the idea. The grid size dictates the number of units a brush is drawn out by with each move. As a general rule it's best to keep the grid size as large as possible. For the purpose of this tutorial we are going to use 4, so hit the number 4. To give an idea of scale a player in Q3 is 56 units high. + +On the 2d window place your mouse pointer and left click drag the pointer across the work area whilst holding the left mouse button down. A brush should have now been created! If you use the arrow keys you can fly around the 3d window (above the texture window) and inspect your creation! By placing your mouse pointer against the side of the shape and pressing and holding the left mouse button you will see how you can stretch out or shrink the brush. To deselect the brush hit Esc, to select it again place the mouse pointer on it press shift and left click. + +To change views in the 2d window press Ctrl and Tab together. The window will cycle through top/down, front/back and left/right views. Try moving the selected brush in all views and watching how it alters in the 3d window. + +Right, let's make a first room: + +![drawbrush.jpg](images/drawbrush.jpg) + +Notice the blue thick grid lines? That's a block. Don't worry too much about what it's for just now but it is worth pointing out that it is good practice to keep areas aligned to blocks. You can see the blocks from the view menu. Go to view, show, show blocks. + +Draw out a brush in the top down (yx) view 1088 by 1088 units. That's going to be our floor. In the zx or zy view adjust the thickness of the floor to about 24 units. + +Now for some walls: + +![drawwalls.jpg](images/drawwalls.jpg) + +In the top down view draw out a brush that fits exactly along the edge of the floor area. Make sure the brushes are right up againt each other. Use your mouse wheel or insert and delete to zoom in and out and right click and drag in the 2d window to move your work area around. This way you can inspect what you're doing very closely. In the side or front view pull up the wall brush to 192 units. Make sure the bottom edge of the wall sits against the top edge of the floor, no overlapping it at all. + +With the brush selected, come back to the top down view and hit the space bar. This clones the brush. At the top of the screen there will be some buttons. Starting from the left move your mouse slowly along until you come to the one that says z axis rotate, now press that one. The selected brush will rotate 90 degrees clockwise. Place your mouse over the centre of the brush and hold the left mouse button. Now move the brush into the position shown above. Note how the corners of the wall do not overlap. Point and press shift left click at the first wall brush and then holding shift point and left click at the second. Both brushes will now be selected. Clone both, rotate as we did before and move into position. + +Now for the ceiling. + +![closebox.jpg](images/closebox.jpg) + +In zx or zy view select the floor brush then clone it. Move it into position over the floor brush. Now we can use the handy z-window, the thin window on the left. Place your mouse pointer in the middle of the selected brush and drag upwards. Stop when the ceiling is in place. Deselect. Flip through all views to make sure everything is aligned....then we're almost done! + +As I said before the caulk texture is invisible in the game. Not a lot of point in creating a room we can't see, so the next step is to paint or texture the insides of the area. As rule of thumb, only texture what will be seen by the players in game. This way we can save valuable tris. + +Instead of shift and left click to select sides of a brush press Ctrl shift and left click. Often this is more easily done in the 3d window. To select multiple sides press shift Ctrl Alt and left click. You will discover you can drag your mouse across several brush faces and select them all very quickly. + +![selface.jpg](images/selface.jpg) + +Having selected the face we want, lets add a texture + +![seltex.jpg](images/seltex.jpg) + +From the texture menu load up gothic_floor. Select the face of our floor brush then click on the largerblock3b4 texture. The caulk will change and the brush will now be textured. Now load the gothic_block textures and texture the inside faces with the killblock texture. Now open the skies texture folder. See how some of the textures have white borders. That means these textures have shader scripts. In game these textures will have a special effect (in this case they are skies). Select the inside face of the ceiling brush then texture it with hellskybright. A useful texturing technique is after you have textured one face use Ctrl shift middle click to apply the texture with the same alignment to other faces. + +Nearly there! + +![infopdm.jpg](images/infopdm.jpg) + +We need to add a player starting point for the game. Right click in the 2d window. A menu will appear, scroll down to info and select info_player_deathmatch. + +A pink box will appear. Flip to top view and move the start point to the middle of our room, then flip to side view and move it so that it sits just resting on the floor. + +With the start point still selected hit n. + +![angle.jpg](images/angle.jpg) + +Press the 90 on the little box of squares. This rotates the starting point so that the player begins facing toward that direction. It's not really a problem in a large room but you don't want to spawn in game facing into a wall! + +Time to save the map. + +Go to file, then save as and enter a name. + +![saveas.jpg](images/saveas.jpg) + +Now to compile, go to bsp and select Q3Map2(test) BSP --meta --vis --light --fast --filter. + +Exit and fire up Q3. Drop the console by pressing the key above Tab and type: + +`/sv_pure` 0 + +(sv_pure 0 turns off pure server, else your level will not run) + +`/devmap` yourmapname + +where yourmapname is the name of your map without the .map extention + + + +--- + +[Index](index.md) | [Tutorial 2 →](tutorial-02.md) diff --git a/leveldk.co.uk/tutorial-02.md b/leveldk.co.uk/tutorial-02.md new file mode 100644 index 0000000..b1c0d9f --- /dev/null +++ b/leveldk.co.uk/tutorial-02.md @@ -0,0 +1,174 @@ +# 2 point clipping, brush work and basic patch work + +To begin we will cover the basics of clipping brushes. Clipping, or cutting brushes is the primary means of shaping geometry available to the level designer and therefore is perhaps the most important tool to get to grips with. + +As with most functions in the editor it is best to keep the grid snap as high as possible, once again keeping thing neatly aligned to the grid will save a lot of frustration. Lets get underway then. As illustrated in fig.1, draw out a brush. Size here isn't really an issue. + +![clip1.jpg](images/clip1.jpg) + +*fig. 1* + +With the brush selected hit x to start the clipper tool or press the clipper button on the tool bar (15th from left) + +Click once on the top left hand corner and then once on the bottom right. Your brush should now look like the one in fig.2 + +![clip2.jpg](images/clip2.jpg) + +*fig.2* + +Now you have 2 choices, hit x to cancel the clip or enter to accept. If you hit enter your brush will look like the one below in fig. 3 + +![clip3.jpg](images/clip3.jpg) + +*fig.3* + +Easy isn't it! You can clip through multiple brushes stacked on top of each other too. Remember though, keep thing aligned to the grid! + +Lets talk about using the clipper tool for building levels tidily and for optimal performance in game. Imagine you want to build a wall with a hole through it. Well you could do it as shown in fig. 4 below + +![hole1.jpg](images/hole1.jpg) + +*fig.4* + +The green dots are vertices. To bring up vertex editing mode select the brushes and hit v. Don't worry about what vertex editing does just at the moment we'll come to that later. There are a few reasons why fig.4 is poor brush work. When textured you will have textured faces hidden under other brushes. This is called over draw. Over draw is wasteful. In game Q3 will be drawing faces the player could never see. In a large map over draw can slow things down considerably. The other reason fig.4 is bad goes back to those green dots, or vertices, or verts. Remember the world in game is rendered as triangles or tris. The verts in fig.4 link together to form tris. Compare the number of verts in fig.5 to those in fig.4. + +*Fig.5 represent good brush work. With the corners cut on an +angle (or mitred ) those angled face can be left textured in caulk and so +not drawn in game. This eliminates over draw. Also we have cut the number of +verts from 12 to 8. Big deal you might say, but in a large map every triangle +counts!* + +![hole2.jpg](images/hole2.jpg) + +*fig.5* + +It is important that brushes meet corner to corner, rather than a corner meeting along the edge of another brush. Where ever you end with a brush corner you get a vert, this vert has to meet another in game. Therefore if a corner meets the edge of another brush that brush will be split into more triangles. Figs 6 and 7 below show how our hole would be correctly incorporated into a simple corridor. Avoiding these t-junction errors takes some time but will help complicated levels run smoothly, so learning how to build for optimal performance from the word go is helpful. + +We will return to the subject of t-junction errors a little later when looking at using patches. + +![tjunc1.jpg](images/tjunc1.jpg) + +*fig.6* + +![tjunc2.jpg](images/tjunc2.jpg) + +*fig.7* + +Lets look at using curves or *patches.* + +Using curves to good effect can really make your level stand out but there are some things to be aware off. Firstly they are not solid like brushes, this means that you can't seal the world off from the void with them. If the the patch forms part of your outer wall you will need to fit  caulk brushes behind it to stop leaks (see fig.15) Use the caulk texture as it isn't drawn in game, hence eliminating over draw and any chance of z-fighting (where more than 1 texture is drawn in the same place, causing an ugly mess in game). Secondly, patches use up a lot of tris, very quickly. Having a lot of patches can cause your map to run slowly! + +Let's begin then. + +Flip to y/x view. Draw out a brush. Here I've gone 192 units square. Go to the curve menu, select Endcap as in fig.8 below. + +![ecap1.jpg](images/ecap1.jpg) + +*fig.8* + +The brush will transform into an endcap. Keep it selected, rotate through the x axis 3 times (4th button from the left) and switch to z/x view. The endcap should look like the one in fig.9 + +![ecap2.jpg](images/ecap2.jpg) + +*fig.9* + +Incorporating a curved archway like this into rectangular brushwork might be tricky...lucky for us we have the cap function. Select the endcap, go to curve menu and select 'Cap Selection'....as in fig.10 + +![ecap3.jpg](images/ecap3.jpg) + +*Fig.10* + +This brings up the cap menu as shown in fig.11. Although we have an endcap, select inverted bevel, not inverted endcap. The verts in inverted endcaps do not line up well with surrounding brushes and cause t-junction errors. Unlike t-junction errors with brushes, those caused by patches cause tiny holes that can be seen in game. These are known as *sparklies.* + +![ecap4.jpg](images/ecap4.jpg) + +*fig.11* + +*Fig.12 below shows a capped endcap. Selecting inverted bevel +will only cap 1 side, so you will need to clone and rotate the caps, then move +into position.* + +![ecap5.jpg](images/ecap5.jpg) + +*fig.12* + +The surrounding geometry is important when building the curve into your brushwork. Fig.13 below shows poor brush work. See how there are two brushes making up the right hand side of the arch. This will cause t-junction errors and therefore sparklies. Fig.13 shows the correct way to do it. Don't worry about texturing your new geometry yet, we'll come back to that in the next section. + +![ecaptjunc1.jpg](images/ecaptjunc1.jpg) + +*fig.13* + +![ecaptjunc2.jpg](images/ecaptjunc2.jpg) + +*fig.14* + +Using bevels. + +I'm sure you have seen curved corridors in Q3. Here's how it's done. In fig.15 I have pre-built a right angled corridor. I have textured it all, apart from the corner. Remember patches are not solid, so as stated earlier it is necessary to place solid caulk brushes behind them. I have left the floor and ceiling as caulk because we are going to use patches there also. + +![b1.jpg](images/b1.jpg) + +*fig.15* + +In fig.16 below we are getting ready to create a curved wall section. Draw out a brush the size of the area you want and in the desired texture, then go to the curve menu and select bevel. + +![b2.jpg](images/b2.jpg) + +*fig.16* + +The brush will become a curve. You will need to rotate it through the z-axis and move it into position....but wait it's all wire-frame! This is because the bevel in back to front. With the bevel selected press Ctrl i to invert it. Move the patch so it is exactly in place in the corner with the end resting right up against the ends of the textures wall brushes. Now to fit the texture. With the bevel selected hit shift s, this brings up the patch properties window (shown in fig. 17). Press natural. Does the patch texture now align with the wall texture? If not try rotating it 180 degrees by using the rotate step option. + +![b3.jpg](images/b3.jpg) + +*fig.17* + +*Fig. 18 below shows another bevel being created to match the +trim along the bottom of the wall. If we made one big bevel for the corner it +would result in t-junction errors where the two brushes that make up each wall +met the single edge of the bevel. Therefore it is necessary to stack bevels so +that the edge of the bevel is exactly the same height as the brush it joins. The +most simple method of creating the 2nd bevel is to clone the first and resize it +the same way you would resize a brush, then move it into place. With the bevel +selected click on the new texture and use patch properties to fit it as before.* + +![b4.jpg](images/b4.jpg) + +*fig.18* + +*Fig. 19 below shows another more advanced approach of altering +the shape of a patch. Select it, then hit v to bring up vertex mode. By clicking +over the little green dots you can select one or more vert and drag it into the +required position. When vertex editing patches press Ctrl g after each move to +ensure the verts are aligned to the grid.* + +![b5.jpg](images/b5.jpg) + +*fig.19* + +Right, what about the floor and ceiling? We could just texture the visible sides of the brush, but the portion behind the bevel would never be seen, so this is wasteful. We are going to cap the bevel and place the cap directly on top of the caulk brush. Select the texture you want, select the bevel. Choose to cap the selection, then choose bevel (fig.20) + +![b6.jpg](images/b6.jpg) + +*fig.20* + +Deselect the bevel and top patch, or press Tab until you have selected the bottom cap. Delete this cap. Select the top cap (you may need to press Alt 6 to hide the caulk texture) and bring up the patch properties window (fig. 21). Hit cap to align the texture on the cap (You do this for aligning the texture on the caps we made in the earlier section also) + +![b7.jpg](images/b7.jpg) + +*fig.21* + +Clone the cap, retexture it as floor, invert it and position it on the floor, then align the texture as above + +If you turn off caulk (Alt 6) your hall way will now look like the one below in fig. 22. + +![b8.jpg](images/b8.jpg) + +*fig.22* + +In the next tutorial we will cover detail and structural brushes, laying out an entire level and fitting geometry into the room we made in the first tutorial. + + + +--- + +[← Tutorial 1](tutorial-01.md) | [Index](index.md) | [Tutorial 3 →](tutorial-03.md) diff --git a/leveldk.co.uk/tutorial-03.md b/leveldk.co.uk/tutorial-03.md new file mode 100644 index 0000000..7c58ed7 --- /dev/null +++ b/leveldk.co.uk/tutorial-03.md @@ -0,0 +1,246 @@ +# Detail and Structural brushes, laying out the level (including triggers and traps) and adding geometry to the map. + +Please note that this section of the series is long and contains much information. If you are a novice mapper take your time and work through the concepts discussed slowly and carefully. + +N.B. Since the advent of the skip shader the use of the hint brush has altered slightly from the method described in Bill Brook's superb tutorial. Exactly how the approach has altered will be covered later but rest assured the important points made regarding where to apply the brushes and why, continue to remain valid. + +We will now return to the simple box map we made in the first tutorial. + +Detail and structural brushes, so what exactly are they and how should we use them? Structural brushes are solid, they block vis and seal the world off from the void, detail brushes are solid in game, but as far as the compiler and game are concerned they do not block vis, nor can they seal the world off from the void. To make a brush detail, select it and press Ctrl m, to make a detail brush structural select it the press Ctrl shift s. By default all brushes are structural. Why should we worry about all this? Well, a complicated map could take a long time to compile vis. We can shorten the time it takes by making all the outer brushes that touch the void structural, and everything else that isn't needed for blocking vis into detail. If we make our rooms and corridors out of nicely rectangular structural brushes applying hint brushes is also much easier as the splits created by the hint brushes are not effected by detail brushes. + +Below are two images of the room we are going to build, one shows the geometry with detail brushes, models and patches all turned on, the second shows the room with everything but structural brushes filtered off. + +N.B. toggle details on and off Ctrl d + +toggle structural on and off shift Ctrl d + +toggle patches on and off  Ctrl p + +![detail1.jpg](images/detail1.jpg) + +![detail2.jpg](images/detail2.jpg) + +Remember how in the first tutorial it was stressed how brushes shouldn't over lap? Well, with detail and structural brushes we can or even must break that rule! Here is another shot of the same room with detail and structural brushes both turned on. See how the caulk structural brushes are drawn on top of the detail brushes in many places? This doesn't matter, caulk isn't drawn in game. + +![detail3.jpg](images/detail3.jpg) + +Examine the corridor below. The complex shape is all detail brushes. Buried in the walls and floor are simplified caulk structural brushes brushes. Vis only considers the simplified brushes. Because the caulk brushes are never drawn it is not necessary to worry about t-junction errors either. Structural brushes made from caulk can be slapped down quickly. Only the detail brushes with textured  faces, or structural brushes with textured faces need to be optimised in the way discussed in tutorial 2. + +![detail4.jpg](images/detail4.jpg) + +![detail5.jpg](images/detail5.jpg) + +It is important that the caulk brushes do not stick out into the world, if they do and can be seen in game they may create visual distortion. + +Having talked about *how* we are going to build now lets talk about *what.* The next stage in the building of our map is to come up with a layout. Deciding a layout depends on many things. In this case theme will not be an issue. In order to make the process as straight forward as possible I'm going for standard id Gothic, although later we will be using some custom textures and shaders. There are certain factors in layout we can not ignore. The level must be built with vis blocking in mind from the out set. The scale must be correct, average corridors would be 192 units in height and width. Let the texture sets be your guide, wall top and bottom trims are 32 or 64 units high, wall textures are 128 units high. The level must be playable for humans *and* bots. The level must be appealing and attractive whilst running smoothly and fun to play. Leading on from fun to play, there must be a high degree of connectivity between areas, with out losing control of vis. The classic Death Match level forms a closed circuit, whilst a CTF map consists of 2 normally identical bases with all routes in and out leading through a central 'choke point'. + +The brief for this level was to design a Death Match level that could easily be turned into a CTF map later. Hmmm. Not exactly mutually compatible, but lets get started! + +To avoid problems later on it is vital to rough out a layout first. This is normally textured, roughly lit and items placed. Then the level is compiled and played. This is known as an alpha build. Problems can be identified and corrected before details have been added and fiddly brush work begun. This is what we are going to do next. + +Lets get to it then! The following are some in game shots from the alpha build. Fig.1 is the room we created in the first tutorial with some slight alteration that we can come to in a moment. + +![alpha6.jpg](images/alpha6.jpg) + +*fig.1* + +*Fig.2 below is another shot of our first room. I dropped the +centre of the floor area because I had an idea about a shallow pool of water +there. The holes in the floor at the top and bottom of the image give access up +from the lower areas.* + +![alpha5.jpg](images/alpha5.jpg) + +*fig.2* + +*Fig.3 below is  a shot of the lower area. I thought the +lower areas could both be turned into natural rock, at least in part, as if our +Gothic structure were built into or on a bed of rock. I liked the idea of +natural organic rock and worked stone in contrast as well. You can't see it from +this shot but there is an exit via jump pad up to the centre area (the first +room we made) via the square holes shown in fig. 2 and 1. There is a lower area +on both sides of the map. As only one side of the map has a 'base' at the +moment, the exit from one room will be provided by teleporter and will most +likely contain a power up you will need to make a special trip to grab. Both +rooms will have a swirling fog death pit.* + +![alpha4.jpg](images/alpha4.jpg) + +*fig.3* + +*Fig.4 shows a view out over what I called the base area. As I +had to come up with a level that would suit CTF as well as Death Match the +second area I built off of the first area would double as a flag base. The door +way in the centre of the back wall leads down to the lower area pictured in Fig. +3. The trough in the floor is there to be filled up with lava flowing beneath +some grates.* + +![alpha3.jpg](images/alpha3.jpg) + +*fig.4* + +Below in fig.5 is another view of our base room, access is from corridors on each side of the area that lead to the centre area and also with some difficulty, via the drop from the centre area and up from the lowest level. At the moment avoiding the jump pad in the drop down to the lower level is difficult but that can be worked in. Plenty of opportunity for split level game play here. Remember to think in 3 dimensions when planning a level, back/forward, right/left and up/down! The first time I actually walked about in this room I realised the ceiling was far too low, so had to adjust the head clearance! + +![alpha2.jpg](images/alpha2.jpg) + +*fig.5* + +One of the corridors connecting the base to the centre area is shown if fig.6. Note the right angled kink in the middle, a classic vis blocking design. + +![alpha1.jpg](images/alpha1.jpg) + +*fig.6* + +To alpha layout is included in the sample map .pk3, along with all the custom media used in the levels. + +If you open the map file and have a look around you will see many diamond shaped objects floating around in the air. These are point light ents (short for entities). Light ents act as sources of light. In Q3 light can be emitted from surfaces, such as skies, flames or textured brushes through the use of shader scripts (more on that later). The alternative method of  applying light to your level is through light ents. Perhaps *alternative* is the wrong word as in fact in nearly all cases levels are lit with a mix of shader based lighting and light ents. We have already used the entity menu in order to place an info_player_deathmatch in the level. Right click in the 2d window to bring up the entity menu once again. Scroll down to Light and select. A new light ent will appear along with a little box asking you to enter a value. As a rule of thumb lower values tend to be better in enclosed spaces, by lower I mean anything from 10 to 100. Higher numbers are fine but be careful. If lights are too bright textures become washed out and ugly, also if you place lights too close to walls, ceiling or floors this can result in spotting, where an obvious 'pool' of light appears with no apparent source. The value of lights can be adjusted at a latter date by selecting the ent and pressing n to bring up the entity editing window. In the main window look for the word (or *key*) *'light'*. To the right of *light* is a number value. Click on light and you will see the word light appear in the key text box and a number value appears in the value text box. Change the number and enter. GTK 1.4 shows the radius of emitted lights when a light ent is selected. This is quite a useful new tool, although not on a par with the real time lighting of Doom 3 in GTK 1.5. To change the colour of light emitted from a light ent, select it and press k. A colour chart will appear. Click on the colour you want and then OK. The ent should have changed colour. A word of warning, strong, highly coloured lighting looks terrible. Use coloured lights with care, to emphasis light emitted from flame for example, but let *subtlety* be your guide. + +The other points of interest in the alpha build are jump pads, teleporter and the pits of death. Lets look at each in turn. + +Firstly Jump Pads. In order to move from a lower level to a higher level quickly in Q3A jump pads are often used instead of stairs. To make a jump pad a trigger is needed to push the player into the air and a target is needed to indicate the direction of the push. Fig.7 shows the trigger. Select the common texture folder, then click on the yellow and white trigger texture. Draw out a brush from the trigger texture that sits on and above the floor, then with the brush still selected drop the entity menu (right click in the 2d window). Scroll down to trigger, then select trigger_push. Deselect. Your brush is now a trigger_push entity. The jump pad now requires a target. Some where in the 2d window, with nothing selected right click above the trigger brush. Scroll down to target and select target_position. Oddly enough, don't use target_push. This entity will still work, but will cause the game to look for a missing sound file. The green cube shown selected in fig.8 should now appear. + +![jp1.jpg](images/jp1.jpg) + +*fig.7* + +Finally we have to link the 2 entities. Select the trigger first then select the target without deselecting the trigger. Press Ctrl and K, or drop the selection menu and select 'connect entities'. A line should now be joining the trigger brush to the target entity. Deselect. That was the easy part. The hardest part of making a jump pad is moving the target so that the player lands neatly in the correct place and adjusting the trigger so that the push begins at the right moment. Having got a jump pad that works well for the human players it then needs to be tweaked for bot (computer controlled players) play, but thats a whole new topic. For the sake of realism make sure you place a jump pad texture or model underneath the trigger brush. + +![jp2.jpg](images/jp2.jpg) + +*fig.8* + +Next let make a teleporter. Teleporters are useful for moving player around a level where otherwise a physical route isn't possible to avoid dead-ends or for strategic reasons. Examine fig.9. Ignore the trigger brush on the floor, that's part of the death pit. The trigger brush we need is the selected one. Draw out a brush made from the common/trigger texture and then drop the entity menu once again. This time select trigger_teleport. Deselect. Go to the part of the map you would like the player directed to and right click above that area. + +![tp1.jpg](images/tp1.jpg) + +*fig.9* + +Scroll down to misc and select misc_teleporter_dest. A flat orange cube should appear like the one pictured in fig.10. Just as with the info_player_deathmatch entity, you can press n to adjust the angle at which the player will appear facing. Deselect the entity. Return to the trigger brush and select it, travel back to your target ent and select that as well. Press Ctrl and K and a line should now join the 2 entities. Place a teleporter shader or model (don't worry if you don't know how, we will cover this topic later)inside the trigger and move the target around, and you finished. + +![tp2.jpg](images/tp2.jpg) + +*fig.10* + +Sometimes you may want to include a trap in your level. Be careful not to over use traps, most multiplayer game players find falling into swirling pits of toxic fog, or tumbling into space, rather annoying. That said a well placed trap can make powerful items much more dangerous to obtain or deliberately slow down play for strategic reasons. To make a death pit, complete with screaming sounds (like the fog pits or dropping into space), use the following method. + +To kill the player we are going to draw out a brush made out of common/trigger (in my sample map the brush is 64 units deep), drop the entity menu and select trigger/trigger_hurt. Press n to bring up the entity window. In key box type dmg, in the value box type 9999, then enter and Esc. (Shown in fig.11) + +![dp5.jpg](images/dp5.jpg) + +Somewhere above the trgger_hurt draw out another trigger brush, this time select trigger_multiple from the entity list. Deselect the brush and any where close by it in the 2d window drop the entity list. Scroll to target then target speaker. A green cube should appear, then hit n. The entity window appears and type *noise* in the key box and  **falling1.wav* in the value (shown in fig.12). Enter and Esc. Deselect the ent. Select the trigger, then the speaker, then hit Ctrl and k. A line should now link the trigger and target. + +*fig.11* + +![dp4.jpg](images/dp4.jpg) + +*fig.12* + +We need to make sure that if the player falls into the pit they are going to stay there and meet a sticky end. At the very top of our pit draw out another brush from the trigger texture and convert it to a trigger_multiple(fig.14). Right click in the 2d window to bring up the entity list again, this time select target and then target_remove_powerups (fig.13). Link the two entities in the same fashion as above, make sure you select the trigger before the target. + +![dp3.jpg](images/dp3.jpg) + +*fig.13* + +![dp2.jpg](images/dp2.jpg) + +*fig.14* + +Finally, we need to ensure items don't get left at the bottom of the pit (or space). Open the common texture folder and select the blue and white nodrop texture. Stick a large nodrop textured brush all over the bottom of the pit and the problem of dropped items will disappear(fig.15). The only thing left to do is to stick a fog brush in the hole so that players know not to fall in. From textures select sfx, then select hellfog. Fill the entire pit, stopping a little way from the top, with a brush made from the fog texture. Don't worry if your fog passes through some solid brushes and make sure the sides and bottom of the brush are buried inside the surrounding floor and walls brushes. Fog must only have one side that a player can enter through, otherwise when we come to compile our level the compiler will spit warnings at us. + +![dp1.jpg](images/dp1.jpg) + +*fig.15* + +Lets talk about the changes I've made our original room(fig.16 and figs 1 and 2) + +![oldroom.jpg](images/oldroom.jpg) + +*fig.16* + +The main body of the room remains the same rectangular shape. The central floor area has dropped as I'd thought about having a sunken floor area with a water splash there. I've added areas on each side of the room behind the side walls. The player will never be able to get there but I like geometry that goes into spaces players can not. To me this gives a sort of realism to a level, the illusion of a world outside the arena. The top of the room has extended upwards considerably in order to accommodate the structures I have in mind. When building in a vertical direction, especially if the area is to be surrounded in sky, always bear in mind if it would be possible to see that area from another. There is a horrid sky bug in Q3 that causes visual anomalies  (partially rendered geometry) if one area surrounded by sky is viewed through another sky textured brush. If you are lucky enough that the two areas are in different bsp splits and never considered visible from each other then the problem does not arise, but it's better to be safe than sorry. The two ends of the map will both have twin corridors linking to base areas in the CTF version, for FFA, only one end will have corridors. Again, the area above the corridors extends back from the room to allow room for some decorative architecture. The two holes in the floor give access up from the areas below, and with some difficulty, access down from this area. + +Time to play our map. The main thing left to do for the alpha test is to throw some items around. Items are placed in the map in the same manner as the info_player_deathmatch ent we added earlier. Right click in the 2d window over the area you want to insert your item. Select the item from the entity menu. Hit n to bring up the entity editing window if you want to adjust the angle or any other of the entity's properties. Various keys and values can be set, depending on the entity in question. In the entity editing window information and notes are given for each entity, regarding what can be added or changed. Apart from changing the angles of the player spawn-points the only change I made to the basic items was to team the Mega Health with the Quad Damage. In this way one item will spawn, after it has been taken the next item will appear. This is done by giving both item the key *team* and then the same value, in this case, *1*. Both items are then placed in there spawning position. + +Placing items is something of an art. More powerful items should be placed in such a way as to make there collection more difficult or more dangerous. Health must be sufficient but not over done, the same applies to ammo and armour. Avoid placing powerful weapons, armour and health in a cluster. Players should move around and be given little opportunity to camp, encouraging good circulation in a map assists with good bot play too. Try to avoid placing ammo to close to its weapon. More powerful weapons could maybe have ammo limited to ensure better balance. Try to balance areas/routes around the level to ensure that equal advantage and disadvantage can be found in any direction. Don't think you have to include all the weapons and power ups, only use those that seem appropriate to the level. Lastly, if possible try to play the alpha level with some real folks. Bot play is going to be poor at this stage. Although the alpha build will give some indication of how bots cope navigating the map, much is still to be done in this department. In the alpha stage we are looking at layout and scale, connectivity and flow and item placement and game-play. + +Our sample map lacks in the layout department. This design fault is intentional as the level needs to be simple, and because of the requirement to be able to convert it to a CTF level fairly easily. Time to move on then! + +Lets start to add some architecture! + +![arch3.jpg](images/arch3.jpg) + +*fig.17* + +![arch2.jpg](images/arch2.jpg) + +*fig.18* + +![arch1.jpg](images/arch1.jpg) + +*fig.19* + +Figure 17 to 19 above show examples of the type of gothic architecture that we are going to add to the level. Inspiration can come from other maps, real life, research or your imagination. It's a good idea to carry a sketch pad to scribble down ideas when they come, or a camera to capture some object or place. + +In the case of this map, we are sticking with id-style gothic architecture that the standard texture sets fit so well. Let's begin with the courtyard walls. + +![walltemplate.jpg](images/walltemplate.jpg) + +*fig.20* + +Flat walls are boring. Try to avoid large flat areas, where textures obviously repeat. Take the scale from the texture sets, a base border of 32 units, a wall of 128 units and a top border of 32 units, gives us a 192 unit high barrier. The selected wall section in fig.20 above is the template for the wall that surrounds the courtyard area. We could have made it 192 units high and with a flat, vertical surface. That would have been boring. The base border is angled, as is the upper brick section, resulting in a much more interesting shape. The angled brushes are created by clipping brushes into the required shapes, as previously discussed. As before, all brushes are made from the common/caulk texture and then textured on the visible faces. All of these internal details are turned into 'detail' brushes by pressing Ctrl and m whilst they are selected. + +![walltemplatecorner.jpg](images/walltemplatecorner.jpg) + +*fig.21* + +Figure 21 above shows how the wall sections can be fitted at the corners. Joints should be mitred to ensure the best results. You can select a group of brushes and clip across them all to ensure uniform cuts. Keep the grid as high as possible to minimise imprecise cutting. Note: if you make a wrong cut, Ctrl and z undoes the action. Some other important clipping shortcuts are Shift and Enter which reverses the cut selection and Ctrl and Enter which selects both sides of the cut. + +Figures 22 and 23 below show how verts should line up between the curved patch that makes the arch and the surrounding brush work. Remember brushes should meet at corners and vertices of patches should meet the vertices of the brushes they touch, otherwise 'sparklies', or t-junction errors will occur in game. + +![patchverts.jpg](images/patchverts.jpg) + +*fig.22* + +![patchverts2.jpg](images/patchverts2.jpg) + +*fig.23* + +Figure 24 below shows how a concentric trim has been added to the bevel cap. Trims help add solidity to a level. Edges become more defined which can create a 'finished' look to geometry. On architecture that players interact with trims can help visual assessment of 3d space. The bevel cap has been vertex edited to reduce it by 8 units in height and width. Don't forget to re-naturalise the cap texture. The trim texture should be selected, a brush drawn out and turned into a simple patch mesh. Adjust the size of the mesh so that it is more or less the same length as the breadth of the cap and 8 units wide. Vertex edit the mesh as shown below, in order to bend it around the cap (which in fig.24 is also selected for the purpose of the screen shot). Make sure the texture is orientated to face outwards, Ctrl i, if it is not. Naturalise the texture on the patch and you're done. See fig.25. + +![patchtrimverts.jpg](images/patchtrimverts.jpg) + +*fig.24* + +![patchtrim.jpg](images/patchtrim.jpg) + +*fig.25* + +Sometimes when you add patch trims even though vertices align perfectly in the editor, in game holes or tears appear when the geometry is rendered. These are known as LOD (Level of Detail) cracks. These errors are caused by the Q3 code itself in the way it calculates the drawing of curves. Small LOD cracks can be stitched by the game, but some will be quite visible. Luckily there is a solution. Through the use of Ydnar's mighty Q3map2 it is possible to convert compiled .map files into .ase model files. As models are rendered differently by Q3, they do not suffer LOD cracking. Fig.26 and Fig.27 show 2 areas where .ase models have been used to solve this problem. Fig.26 shows an endcap with patch mesh trim that cracked, Fig.27 shows a group of vertex edited square cylinders that also exhibited this problem. I should point out that using square cylinders can be expensive in tris. Often other geometry gets embedded inside part of a cylinder or square cylinder, wasting tris. Z-fighting will tend not to occur in game as the player can not see inside the cylinders. Using 4 (or more or less) patch meshes to do the same job as a square cylinder would be much less wasteful in valuable tris, as the patches could be edited so that  no overdraw occurs. In certain places I would choose a square cylinder over patches as in spite of their problems when drawn in game they have a lovely smooth and rounded appearance to their edges, which patches do not have. + +![ase1.jpg](images/ase1.jpg) + +*Fig.26* + +![ase2.jpg](images/ase2.jpg) + +*Fig.27* + +How are .ase models created then? Well, it's not that complicated. Select the geometry you want to convert into a model. Copy it. Save the map and open a new map. Paste the copied geometry into the new map. Slap a caulk box around the geometry and throw in an info_player_deathmatch entity. Select the entire map and move it so that the centre of the geometry is somewhere close to 0, 0, 0 on the grid. This is necessary as the model control point is always created at 0,0,0. If you don't move the geometry the result can cause a leak if you insert a model into a map with it's control point in the void. + +Save the map, with an appropriate name, I normally use something like *mymap_tallarchase.* Open Quake toolkit or Q3map2 toolz, or what ever front end compiler you use. Compile with: + +bsp -meta -patchmeta -subdivisions 6 + +Then compile the new .bsp (not the .map!) with: + +bsp -convert + +You have now created your first .ase model. I normally move the model into a *mymapname* folder inside the models/mapobjects/ folder. + +Open your original map and delete the old geometry. Insert the model by dropping the entity menu and selecting misc_model. In the window that pops up, change the file type from .md3 to .ase, then select your model. Move the model into position. You can rotate it in any direction (using the angle keys in the entity editing window, and/or by setting the key *angles* with a value *n,n,n* for pitch, roll and yaw. GTK 1.5 will allow you to free rotate models around any axis), just as with an .md3, this is the other benefit of creating and using models. It is not normally a good idea to rotate brush or patch work, but with models there is no problem. All map models are non-solid. You will need to place a playerclip brush over the top of them to make models solid, or weapclip if you want weapon impacts as well. Cut the clip brushes into more or less the correct shape, but don't be overly fussy. Try to keep clipping simple. It is possible to auto-clip models by setting a key of *spawnflags* and a value of 6, however I strongly recommend NOT going down that route as the load placed upon your CPU increases hugely once weapons start getting fired into your models. If you want explosions, use simple weapclip brushes. You will need to set the key *spawnflags* and a value of 4. This will allow your model to be lightmapped (cast and receive shadows), it's also often a good idea to set a key of *_lightmapscale* and a value of 0.125. This sets a high resolution lightmap on the model, resulting in much sharper shadows. + + + +--- + +[← Tutorial 2](tutorial-02.md) | [Index](index.md) | [Tutorial 4 →](tutorial-04.md) diff --git a/leveldk.co.uk/tutorial-04.md b/leveldk.co.uk/tutorial-04.md new file mode 100644 index 0000000..78e99af --- /dev/null +++ b/leveldk.co.uk/tutorial-04.md @@ -0,0 +1,140 @@ +# More patch manipulation, Lighting tips, Stairs and Ramps + +In previous tutorials we have covered basic patch manipulation and touched on more advanced techniques. In this section we will look at more approaches to using patches. The merits and draw backs to using square cylinders (draw out a brush, with it highlighted, drop the curve menu, select *more cylinders*, then *square cylinder*) have been discussed previously. In fig.1 a square cylinder is being used to create a flying buttress style of support. A brush has been clipped at an angle to form the top of the buttress. The brush is a few units wider than the square cylinder we are going to manipulate. Create a square cylinder  and rotate using the tool bar buttons and move into roughly the correct position. Engage vertex mode (press v). Click on the verts (or drag a selcction box and select a groups of verts) you want to move and manipulate the cylinder into the position shown below. Make sure the cylinder is a little narrower than the support it ends in and the brush forming the top of the buttress. Don't worry about the cylinder entering the brush, that will not be seen in game. Press shift s to bring up the patch texturing inspector and hit natural to align the texture. Heavily manipulated patches can warp detailed textures, so it is generally better to keep the textures you select for your patches fairly simple. + +![patchsquarec.jpg](images/patchsquarec.jpg) + +*fig.1* + +Let's get more adventurous. We are going to create a hole. We could simply cut a square drop down to the level below. Details are everything though, so rather than a square or rectangular hole we are going to create a smooth rounded hole, with a rounded, smooth top (see fig.4) Firstly we need to create the hole. Remember verts need to meet verts, so the brushes need to be mitred in such away that the corners of the brushes meet the corner of where the bevel caps will be (see fig.2). Create a cylinder to fill the hole. Select the floor texture and cap the cylinder with bevel caps, move and clone so that the caps are in place, then align the cap textures. Now reselect the cylinder. Switch to yx (top down) view. Vertex edit to reduce the width of the cylinder by as many units as required. Now for the tricky bit. From side on view, move the top row of verts above the level of the floor then move the middle row of verts up to the floor level (a good map makers tip is to select the geometry you want to work with, hit Ctrl i to invert the selection, then h to hide selected. Now only the geometry you need to work on is visible, making working in detail far easier). In turn, now select each of the top verts and pull them out sideways so they sit level with the inside edge of the caps. Now select each on the middle row verts and pull them up, as shown in fig.3. Reselect the top verts and pull them down to the floor level, so they meet the caps perfectly (again, as shown in fig.3). When you've completed, don't forget to naturalise the texture. + +![patchc3.jpg](images/patchc3.jpg) + +*fig.2* + +![patchc2.jpg](images/patchc2.jpg) + +*fig.3* + +![patchc.jpg](images/patchc.jpg) + +*fig.4* + +In several places around the level you may have noticed sharp, dramatic shadows being cast upon the surrounding geometry. Unlike newer games, Q3 has a static lightmap. Shadows are not cast in real time, they are pre-calculated during the compile of the level. If you run a level in devmap mode (type `/devmap` *mymapname*) then type `/r_lightmap` 1 at the console, you will see the level's lightmap. The geometry, apart from non-lightmapped shaders, will be white, with the shadows, light and colour burnt into it clearly visible. `/r_lightmap` 0 returns the map to normal. This lightmap is rendered onto the textures you apply to your geometry, so in a way, all textures in Q3 are blended shaders. Just because Q3 doesn't have dynamic (real time) lighting (how to fake dynamic lighting is covered later in this tutorial), doesn't mean that it can not rival the newest games in terms of realism and beauty. So how can sharp shadows be cast? The key is the use and positioning of your light sources, whether that be, shader or entity in origin. Try to shine light through interestingly shaped geometry, where it can be cast on to an appropriate surface. In fig.5 the area under the central archway is shown. I have placed a light ent inside the little lamp brush model I made, so that light is cast through the lamp, falling onto the curved arch ceiling. If you were to compile the map with the arrangement I described, the result would be disappointing. No crisp, clear shadows would be present. So how is it done? The final thing to do is to increase the resolution of the affected geometry's lightmap. Select the geometry, drop the entity menu. First select ungroup entity if patch groups are chosen, the select func_group. Bring up the entity editing window (press n) then add the key _lightmapscale, and a value less than 1. 0.125 is very Hi Res. Be aware that many Hi Res lightmaps will bump up the size of your .bsp considerably. + +![shadow.jpg](images/shadow.jpg) + +*fig.5* + +A final word on shadows. Q3 isn't that good at rendering curved shadows or angled shadows. You can end up with ugly jagged shadows if the light is emitted from or hits geometry at certain angles. Changing light sources can help, also making affected geometry into func_group entities and assigning keys of _rc or _cs and a value of 0 is a useful trick. The _rc key prevents shadows being cast on to geometry, and _cs prevents it from casting shadows. + +Let's return to more advanced patch work. In the base area ceiling I have constructed a dome with a patch of sky visible at the top (fig.6). It is those little details that perhaps most players wont even notice that set the look and feel of a level. Interesting ceiling work, just as interesting floor details are what sets a map apart from the crowd. So how is the dome created? Select your texture. It is important that texture is not detailed or busy, as some distortion is unavoidable. + +![dome1.jpg](images/dome1.jpg) + +*fig.6* + +We are going to make a quarter of the dome at a time. Firstly create a bevel. Then naturalise the texture (fig.7). The dimensions of the bevel don't really matter. The important point is to keep things square. In the sample map the quarter section of the edited dome bevel is 184 units wide and long, and 104 units high. It's best to align textures now, as naturalising heavily vertex edited patches can really mess things up. Now bring up vertex edit mode. + +![dome2.jpg](images/dome2.jpg) + +*fig.7* + +Vertex edit the bevel so that it looks like the image in fig.8 below. + +![dome3.jpg](images/dome3.jpg) + +*fig.8* + +Next create a simple patch mesh textured in a contrasting texture. This can be vertex edited to fit around the bottom edge of the bevel, then naturalised.. In top down view, select the ceiling texture again and create a new bevel that follows the contour of the outer edge of the patch mesh. Now select cap/bevel cap. Keep one of the caps and delete the bevel and the other cap. If necessary move the cap into position, then align the cap texture. Your quarter dome section should now look like the image in fig.9. In the image below both the simple patch mesh and the bevel cap are selected. + +![dome4.jpg](images/dome4.jpg) + +*fig.9* + +Nearly there. Now lets create a square cylinder, arrange it in such away that it sits with the top edge of our dome section halfway through it. Then vertex edit the cylinder to fit around the top of the bevel, as shown in fig.10 below. Naturalise the texture on the cylinder. + +![dome5.jpg](images/dome5.jpg) + +*fig.10* + +Select the whole section and clone it (spacebar). Rotate the clone 90 degrees and position it next to the first section, so that verts meet up exactly. Select everything, clone again, rotate and move into position. Align textures as necessary, and we're finished. Your dome should look like the one depicted in fig.6. + +Remember patches are not solid, that is they can not seal your map from the void. You will need to place caulk brushes on the outside of your dome to prevent leaks. + +We have previously talked about how to create a jump pad, using trigger_push and target_position entities. Sometimes you might decide that actual stairs or ramps are called for, either because the height the player needs to scale is minimal, or for aesthetic or game play reasons. Normally stairs are 8 or 12 units high. Any thicker than 12 units are players will have to start jumping to climb, quite apart from the fact that the stairs will appear out of scale. Normally levels in a map are 128 or 256 units apart, this tends to be dictated by the size of the textures. In order for the stairs to correctly meet the level they are intended for, the height to be scaled must be divisible by the height of the stairs. + +![stairs.jpg](images/stairs.jpg) + +*fig.11* + +*Fig.11 above shows the correct construction of a small flight of +stairs. Each stair is 8 units thick and 16 units across the top. Notice how the +steps are clipped on an angle so that they sit perfectly on top of the wedge +shaped brush beneath them that is not selected. Stairs should be made in this +way in order to avoid over draw. All the unseen faces of the stairs have the +common/caulk texture applied to them.* + +Some times you might decide that a ramp fits the style of your map, rather than using stairs. In the sample map there is a ramp leading from the base area to the cave area. In this case the height travelled is 192 units up and down whilst 336 units in a forward direction (fig.12) + +![ramp1.jpg](images/ramp1.jpg) + +*fig.12* + +There are some draw backs to using ramps rather than stairs. Players using stairs give out sound cues to other players as they ascend or descend the steps, this doesn't happen with ramps. The other problem is that when stepping off a ramp players will experience 'ramp bounce', where they are bumped in an upwards direction just a little. The solution to both these problems is to create some invisible steps out of player clip (fig.13). The common/clip textures block the player or bots, or both. Clip is used to smooth the player's passage through the world so that they do not become snagged on geometry. Clip can also be used to prevent players or bots from reaching areas you don't want them to get to. Further details on the use of clips will be covered in a further tutorial section. Each of the clip stairs are 8 units high and 12 units across the top. The stairs will be invisible in-game, and the ramp will appear as a normal ramp. + +![ramp2.jpg](images/ramp2.jpg) + +*fig.13* + +A quick word on the remaining geometry around the ramp. The curved ceiling is an endcap, vertex edited to follow the angle of the ramp (fig.14). The endcap sits on top of two brushes, again cut on the same angle as the ramp. The brushes are 64 units in height at the top and bottom of the ramp (fig.15). + +![ramp3.jpg](images/ramp3.jpg) + +*fig.14* + +![ramp4.jpg](images/ramp4.jpg) + +*fig.15* + +Square edges can be boring. So the inside edge of the brushes have been angled downwards. Whilst clipping the brush into this shape would be possible, there is an easier way to achieve this effect. Select one brush and hit v to bring up vertex editing mode. Carefully click on the top inside corner vertex of the brush and it will turn blue (fig.16). Sometimes it is actually easier to select an individual vert in the 3d window, although caution should be used when doing this as it is easy to lose control of the direction things get moved in. Back in the 2d window carefully drag down the vert 8 units. Keep an eye on the 3d window to check you have moved the corner in the correct direction. It is possible to move verts around in the 3d window, but again very great care is needed. Select the corner vert at the bottom of the brush and drag that down 8 units as well. After vertex editing brushes go to plugins on the tool bar, then Bob's toolz, then select brush clean up. This will repair any brushes damaged by vertex editing, as can sometimes happen. + +![ramp5.jpg](images/ramp5.jpg) + +*fig.16* + +Feel like being creative? How about some spiral stairs? See fig. 17 below (note the stairs are blue in the 2d window, this is because I made them into a func_group entity for ease of moving about).  In the base room there are two flights of spiral stairs, one ascending each side of the room. These stairs are open, but they could very easily have been enclosed in round tower or such like. + +![spst1.jpg](images/spst1.jpg) + +*fig.17* + +So how do we set about creating a spiral stair case? First we need a template or guide to assist in the clipping of brushes. A bevel is created to act as a guide. The dimension of the bevel depend very much on the size of the stair case you want to create. The stairs in the sample map are actually 2 groups, each 128 units square and rising 80 units. The bevel created was, therefore, 128 units square (fig.18). It was necessary for the stairs to rise 80 units, each step was 8 units high, meaning there needed to be 10 steps within the 128 unit square. At the wide end, therefore, each step would be roughly (although not exactly) 12 units in width. An exactly matching  width for each step at the wide end does not matter as 1 or 2 unit difference will not be noticed in game. The important points are to keep the stairs a consistent height and within the 128 unit square. + +![spst2.jpg](images/spst2.jpg) + +*fig.18* + +Now we are ready to make the first step. Draw out an 8 unit high brush, 12 units deep, that sits on the floor. Drop the grid to 2 and zoom in (mouse wheel, or use insert/delete). You are going to clip the first step so that it approximates the step shown in fig.19. The edge of the first step will not require clipping, but clip the back edge from the back, outside edge, to the inner aspect of the stair case, as shown. Draw out another brush. Clip the outer edge so that one side meets the out side of the bottom step, and the other side follows fairly closely the line of the bevel. Check the step at its widest point is still roughly 12 units wide, then clip the step on an angle to the inner aspect of the stair case. + +![spst3.jpg](images/spst3.jpg) + +*fig.19* + +Now we have to move the step into position, change the 2d view and raise the new step up so that it's bottom edge meets the top of the lower step, as shown in figs. 20 and 21. + +![spst4.jpg](images/spst4.jpg) + +*fig.20* + +![spst5.jpg](images/spst5.jpg) + +*fig.21* + +Now you have to repeat the process for each of the remaining 8 steps. Then delete the guiding bevel. Texture the steps as required. I turned my steps into a func_group and cloned the steps, the repositioned them so as to form a single flight of steps rising 160 units and turning 180 degrees. In the sample map I placed a cylinder as a central column for the spiral stair case to rotate around, adding a sense of solidity and realism, rather than leaving the stairs floating in space. The cylinder is placed so that the inner aspects of the stairs are very slightly inside, again, in-game, no z-fighting will be seen, so this causes us no real problem. As with all internal geometry the stairs are made into detail brushes, as they play no part at all in controlling vis. + + + +--- + +[← Tutorial 3](tutorial-03.md) | [Index](index.md) | [Tutorial 5 →](tutorial-05.md) diff --git a/leveldk.co.uk/tutorial-05.md b/leveldk.co.uk/tutorial-05.md new file mode 100644 index 0000000..3f32a9d --- /dev/null +++ b/leveldk.co.uk/tutorial-05.md @@ -0,0 +1,130 @@ +# Stuff with holes in...using nodraw + +As discussed in a previous section of these tutorials, Q3 uses shader scripts to create special effects with textures. In very simple terms textures are blended together or manipulated in some way to create the required results. The Q3 Shader Manual offers the definitive guide to writing shaders, and in a later section of this series we will cover how to add your own custom shaders and textures to maps. Sometimes using a shader to achieve an effect is far easier than creating complicated brushwork. One of the best examples of this is grates or fences. Examine fig.1 below. Shaders are identified in GTK by a white border around the texture and [] around the texture name. Remember the image in the editor is only a representation as far as shaders are concerned. The grate shader used below looks solid in the editor, only in game are the transparent parts visible. Because of this it is necessary to have some idea of what each shader does before you can use them. + +![holes1.jpg](images/holes1.jpg) + +*fig.1* + +To create the grate over the lava with brushes would be complicated. Instead a shader has been applied to the top face of a brush made out of common/nodraw (fig.2) + +![holes2.jpg](images/holes2.jpg) + +*fig.2* + +The common/nodraw texture is used where you don't require certain faces of a brush to be drawn in game. Nodraw must not be confused with caulk, it is not solid and does not block vis, in game it does not exist. Because nodraw is not solid a playerclip brush needs to be placed over the grate, to stop players and bots falling through (fig.3). + +![holes3.jpg](images/holes3.jpg) + +*fig.3* + +An alternative approach to achieve the same result would be to simply texture one side of a playerclip brush with the grate shader. Some shaders for textures with transparent parts allow light to pass through and will cast interesting shadows on surrounding geometry. By assigning a _lightmapscale value to surrounding geometry it is possible to sharpen the shadows and make them far clearer. This can be seen on the far wall in fig.1. + +![nodraw.jpg](images/nodraw.jpg) + +*fig.4* + +Above in fig.4 is another example of a use for nodraw. Only the outward facing side of the flag is required. The flag shader has transparent areas rather like the grate shader. + +![lava.jpg](images/lava.jpg) + +*fig.5* + +Just a mention about the lava placed in the floor trench under the grate (fig.5). Lava is a liquid, so far as Q3 is concerned. It is placed in your map in the same way as water, or any other liquid. Bury the sides of your lava in the surrounding brushes, or apply the nodraw texture. Lava automatically inflicts damage on the player or bot if the hapless individual should be careless enough to fall in. The lava textures can be found in the liquids folder. Each lava texture emits a certain amount of light. In the sample map it is these light emitting qualities, shining up through the grate, that were of primary importance. + +## Getting custom textures and shaders into your map and how to create terrain areas + +Sometimes you may feel it necessary to use your own, or somebody else's textures or shaders in your levels, rather than re-using the standard id texture sets. In order to create the rocky terrain included in the sample map, it is necessary to add new shaders and textures to our collection. Creating textures and shaders is a separate series of tutorials in itself and will not be covered here, but rather how to get your custom content into the game. If you use somebody else's artwork you must remember to give credit in the final build of your .pk3 (how to create a .pk3 is covered later). As individual custom content is not available to all Q3 players, it is necessary to include those files for distribution with your map. A level with a high amount of new textures and shaders can quickly increase the final file size of your .pk3. Balancing the size of your final file with the quest for an original look is something most mappers have to do. One of the most often made mistakes by new mappers is to fail to include custom material. Missing textures in game are rendered as black and white squares. + +So how is it possible to get new textures into Q3? Quite simply, locate the texture folder under the baseq3 folder. Into the texture folder place a new folder. It is good practice to name the folder *mymapname*, where *mymapname* is the name of your level. Into this you place the textures you want to use in the level. The exact details concerning Q3 textures will not be discussed here, other than to state that they must be either .jpg or .tga. By placing all your textures in one folder, you make it less likely that something will be missed out of the .pk3. Inevitably textures are drawn from several folders, but it helps to be organised. + +New shaders are added to the scripts folder, which can also be located in the baseq3 folder. A shader is a .txt file, renamed so that the file extension reads .*shader* rather than .*txt.* A word of caution about shaders: Do not edit existing shader files, by doing this you will mess things up. If you want to tinker with a shader script, copy and paste it into a new file, and rename it. In order for the new shader to be found by GTK you need to look for a file called *`shaderlist.txt`* in the scripts folder. Open this file and add the name of your shader to the end of it (with out the .*shader* extension), then save the file. + +The latest build of the map compiler (Ydnar's Q3map2) has many useful features. One of these features is the inclusion of a texture blending feature. By using a shader script for the textures it is possible to achieve a gradual blend between textures. The blend is controlled by brushes textured in a special alphaMod shader. The alphaMod shaders and associated textures are distributed with Q3map2, with instructions on where to put them. The gradual blending together of textures is ideal for reproducing realistic rock or terrain features, where mineral or organic material blends naturally into itself, with no abrupt beginning or end. Fig.1 below shows the cave area in the sample map, demonstrating blended rock textures. In this section of the tutorial how to quickly create brushes that replicate rock will be covered, and also how to use the alphaMod brushes to control the texture blending. + +![cave1.jpg](images/cave1.jpg) + +*fig.1* + +![cave2.jpg](images/cave2.jpg) + +*fig.2* + +*Fig.2 above shows the cave section modelled in GTK, with the +rest of the map filtered out. The image in fig.1 shows natural looking rock, so +what's with all the green and black squares? That's simply the 'in-editor' +image. Where more than one texture blend shader (each shader offers  the +opportunity to blend two textures) is used, it makes life easier to use simple +editor images, rather than detailed textures.* + +![cave3.jpg](images/cave3.jpg) + +*fig.3* + +The cave started out as a single wall template, pictured in fig.3 above. The next step is to alter the template to make it appear more organic. + +![cave4.jpg](images/cave4.jpg) + +*fig.4* + +So far in this series of tutorials we have used the clipper tool to carve brushes into shapes. To create our rock wall the easiest approach is to use edge manipulation. Select one brush of the template (manipulating all 3 brushes at the same time will have strange results), face on, as in fig.4, left click and hold Ctrl on the left hand side of the brush. Drag the mouse left in a straight line, the brush should 'warp' and follow the mouse drag, then drag downwards. In the above image the brush has been dragged 8 units to the left and 16 units down. Now repeat the same process with the remaining 2 brushes. Next flip to top down view. Select 1 brush, and Ctrl left mouse button and drag the brush left again. Now pull the left edge forward, as shown in fig.5. Don't panic if the warping goes wrong, Ctrl Z is the 'undo' short cut. Repeat the manipulation on each brush. + +![cave5.jpg](images/cave5.jpg) + +*fig.5* + +Clone the wall section and place it next to the first section, as shown in fig.6 + +![cave6.jpg](images/cave6.jpg) + +*fig.6* + +Manipulate the new brushes so that they join together as depicted in fig.7. The left hand top edges need pulling up level with the top right hand edges of the original brushes. From top down view, the left hand side of the new brushes can be pulled back so that they are level with the right hand side of the original brushes. When you come to fill in the floor area, in most cases its best to keep things flat, cut the floor brushes around the angled walls so that there is no overdraw, edge manipulate or clip the bottom of the brushes straight to make things easier for yourself. Be careful though to keep verts (the brush corners) on the grid, otherwise aligning brushes gets very messy, if not impossible. + +![cave7.jpg](images/cave7.jpg) + +*fig.7* + +You may be thinking those angles look very sharp, not at all like the smooth edges of real rock. Another feature of Q3map2 is 'Phong shading'. This is a handy tool to be able to use. Phong shading applies soft shadows to geometry, with the effect of making  sharp angles looking rounded. To utilise phong shading, it can be applied to the entire map by setting a key and value during the light compile stage, or as done for the sample map, by adding the line: + +*q3map_shadeAngle 179* + +to the shader script for the rock textures. The value 179 is the amount of shadow applied. + +Let's turn our attention to the cave roof. The section shown in fig.8 demonstrates how brushes should be cut to follow the contours of the front of the  top edges of the wall brushes. The centre of the top wall brushes is lower than the edges. This means the centre of the roof brushes also needs to be pulled down in order to achieve a perfect fit. To pull down the edge of those brushes vertex editing can be used. This is shown in fig.9. Select the brushes and press v to engage vertex editing mode. Carefully select the verts you want to move and drag them down into position. A ceiling or roof that undulates, varies in height, helps achieve that 'natural' look, rather than having the roof meeting walls at 90 degrees. + +![cave8.jpg](images/cave8.jpg) + +*fig.8* + +![cave9.jpg](images/cave9.jpg) + +*fig.9* + +*Fig.10 shows where the wall section have met at the corners of +the room. Generally it would be a good idea to avoid a right angled corner, as +this would not often occur in real caves. That said, in this case it was +necessary to fit the cave into a certain area, so a right angled corner was +unavoidable. The corner brushes have been clipped and vertex edited into +position. As mentioned before, it is always a good idea to run brush clean-up +after vertex editing brushes.* + +![cave10.jpg](images/cave10.jpg) + +*fig.10* + +![cave11.jpg](images/cave11.jpg) + +*fig.11* + +One last job to do. In order to prevent players getting snagged on our rocky out crops and to make the way simplified for bots it is necessary to fit  clip brushes over the top of our cave. This is shown in fig.12, where the rest of the map has been hidden from view. The orange and white clip brushes are weapclip. Not only does weapclip block players and bots, but also causes weapon fire to impact. By having a simplified cave area made out of weapclip, passage is made easier for players and bots and collision calculations are made easier for the computer. + +![cave12.jpg](images/cave12.jpg) + +*fig.12* + + + +--- + +[← Tutorial 4](tutorial-04.md) | [Index](index.md) | [Tutorial 6 →](tutorial-06.md) diff --git a/leveldk.co.uk/tutorial-06.md b/leveldk.co.uk/tutorial-06.md new file mode 100644 index 0000000..301be2c --- /dev/null +++ b/leveldk.co.uk/tutorial-06.md @@ -0,0 +1,144 @@ +# Lighting the level, more advanced techniques + +In a previous section using light entities has been discussed. Light can be emitted from entities or from surfaces textured in certain shaders, such as sky, lights, or some lava or fog. + +In order to avoid ugly 'spotting', where strange pools of light appear, do not place light ents too close to geometry. More light ents, with lower light values, generally give better results than single entities with high values. Try to use a sky that suits the look and feel of your level. Experiment with different skies to try to find the correct brightness and sun angle, choose a texture that gives you the required brightness and the most interesting shadows. + +Newer games, such as Doom 3, have real time or dynamic lighting. Lights flicker on and off and cast moving shadows that resemble those in the real world. This is not possible in Quake 3, but the remarkable Q3map2 does give us the option of 'faking' dynamic lighting through shader trickery. + +Although the images in fig.1 and 2 can not depict dynamic lighting in a still picture, both these areas use this technique. The flaming torches would emit a flickering light, so it seems reasonable to try to recreate that effect. Be careful in the use of dynamic lighting, as with all shaders, over use impacts upon performance in terms of a loss of frame rate. Also, any one surface must not receive light from any more than 4 dynamic light sources. + +So how is it done? + +![light1.jpg](images/light1.jpg) + +*fig.1* + +![light2.jpg](images/light2.jpg) + +*fig.2* + +A light ent is created or selected in the normal way. Bring up the entity editing window and add the key *_style* and then a value of your choice, normally somewhere between 1 and 12, in this case 10 was selected, see fig.3 below. Some experimentation is required to achieve the look that is required. + +![light3.jpg](images/light3.jpg) + +*fig.3* + +Fairly simple so far? Things get a little more complicated now. When the map is compiled Q3map2 generates a custom shader and texture folder, used in game to achieve the flickering light and shadow effects. The texture folder, which will have the same name as the map, is placed automatically in the maps folder and the shader, named *`q3map2_mymapname.shader`*, is placed in the scripts folder. Both the texture folder and the shader must be included in the .pk3 for distribution, just like other custom map content. Each time the level is compiled the texture folder and shader are updated, so be careful to include the current versions of the textures and shader with the .bsp (the compiled map). + +Sometimes it may be necessary or desirable to have the light source some distance from the area where you require the light to fall. This 'spot light' effect can be achieved by targeting a light entity at a target_position or an info_null entity (fig.4). Create the light ent, then create the info_null (under *info*, in the entity menu). Select the light, then the info_null, next press Ctrl k to connect the entities. Unfortunately GTK can not show lighting effect in real time, so it is necessary to compile the map in order to observe the effect you have created. Sometimes it may take 3 or 4 attempts to achieve the result you want, adjusting the strength of the light source and the distance and angle from the info_null. + +![null.jpg](images/null.jpg) + +*fig.4* + +## Applying the clip brush and optimising the level for bots + +The use of the clip brush has been touched on before. Clips are used to simplify geometry, make models appear solid or prevent players and bots from reaching areas they should not. There are 3 kinds of clip brush used: *Common/playerclip*, which blocks players and bots, *common/botclip*, which blocks only bots, and *weapclip*, which blocks players, bots and weapon fire. + +To smooth passage through the level, playerclip is used extensively, encasing architecture completely within in smooth simple geometric shapes. If an examination of the sample map is made, it will be seen that almost an entire level is formed by clip brushes, nearly every surface is covered by a clip brush of one type or another (see fig.1 below). + +![clip1.jpg](images/clip1.jpg) + +*fig.1* + +A good test for where to apply clip brushes is to compile the level as a test, and run around the walls in the map. If you become snagged at any point, that area requires smoothing out with the clip brush. It is perfectly fine to overlap clip brushes. They are not visible in game, so optimising clips in the same way that is required for rendered surfaces is not necessary. In fact, the more simplified clip brushes are kept, the better, in most cases. + +![clip2.jpg](images/clip2.jpg) + +*fig.2* + +Watch the bots play the level. Examine areas they have trouble with, and add common/botdonotenter  brushes (tells bots not to enter a particular area) or more botclip. Bots tended to get stuck to the right of the little flight of steps shown in fig.3. A simple remedy for this was to place a triangular shaped clip brush next to the stairs, so that as the bots exit the top of the spiral stairs, they are funnelled round to the front of the troublesome stairs. + +Often bots will not act in what would seem like a logical manner at all. Some areas may not be visited or certain jump pads ignored. The solution is tweaking, tweaking and more tweaking (fig.6). Bots do not like to use jump pads where 'air control' is required. Step onto the trigger and see where you end up. The jump pad should place you neatly at your target level, if you need to move in the air, bots will struggle, or ignore the jump pad. If the path connecting the trigger to it's target passes through any solid geometry, this also can cause bots to reject the jump pad. Try adjusting the size of the trigger brush, this can also influence whether or not bots will use the pad. Sometimes bots will refuse to visit a certain area. This can be a result of small clusters, more about that later. Ensure the ignored area is clipped and smoothed, so there is nothing that might be confusing the bots. The items you place in a map will encourage bots to circulate, just as real players. Try to scatter items, like a paper trail, for the bots to follow. Apart from at jump or launch pads, bots will not go for suspended items. Having an understanding of individual bot behaviour can be helpful. One particular bot may handle certain aspects of a map better than others, but this is largely a case of trial and error. Knowing bot preferences for weapons or items is a definate help. Placing a Super Shot Gun in an area in a map where Bones is included as a bot, will almost certainly encourage at least one bot to visit that area! If, despite your best efforts, bots still refuse to enter an area, or use a corridor, try inserting an item_botroam or two (fig.7). Item_botroams are bot only entities, they can be given a key of*weight*, and a numerical value. The higher the value of the key set, the more the attraction to the bot. Much care is needed when using botroams. Play can very easily become unbalanced if you set the value too high, or not alter in the slightest, if you set it too low. Unfortunately item_botroam entities are always on, there is no respawn time after a bot has collected them, this can cause constant revisiting of the entity's location. Placing an item_botroam on a jump pad trigger sometimes causes bot to repeatedly bounce up and down like they were on a trampoline. Setting a high value to begin with is generally the best approach, start with 400. Much recompiling will be required, but then adjust the value until bots act reasonably sensibly. Try to use item_botroams as a last resort, if all else fails, rather than as a first course of action. + +![clip3.jpg](images/clip3.jpg) + +*fig.3* + +Occasionally very small clusters are formed by the .bspc. These can confuse bots, who will refuse to use the offending regions. There are a number of approaches to solving this problem. Try clipping and clusterportal placement. If no improvement is achieved try one of the following 'hacks': + +1. Place a large clusterportal brush, or several touching clusterportal brushes inside the region you want to eliminate the cluster. This can sometimes confuse the .bspc and force it to incorporate the area into another larger cluster. + +2. Use a targeted entity to connect the clusters. If you don't require the entity for game play reasons, then place it in such away as players and bots can not get to it. + +![cluster.jpg](images/cluster.jpg) + +*fig.4* + +![cluster2.jpg](images/cluster2.jpg) + +*fig.5* + +![botjp.jpg](images/botjp.jpg) + +*fig.6* + +![botroam.jpg](images/botroam.jpg) + +*fig.7* + +Finally, remember that optimising the level for bots takes time and practice. You will have to recompile the map many times until you achieve the result you want. It is, in all but a very few cases, something of a cop-out to decide that a map is not intended for bots. With effort and time most levels can be tweaked and polished until bots offer at the very least a reasonably enjoyable match. If bot play is ignored, chances are that your level will not remain on the hard drives of many people for very long at all, and all your hard work will have been for nothing. + +## Tweaking vis control + +Although fairly highly detailed, the sample maps have been built with vis blocking in mind. The main areas are separated by solid vis blocking structures to prevent too much of the entire level being drawn at any one time. In a previous tutorial problems resulting from the 'sky bug' were discussed. If more than one area occurs in the same vis leaf node, it is possible to observe partially rendered geometry from one region through the sky brushes of another. Building with this in mind it should be possible to prevent this from happening, however a small error was made and it was possible to see part of the base area roof from the centre area. Although annoying at the time, it is useful to discuss the solution here as it directly effects vis control. Firstly, however, let us take a look at hint brushes, the reasons we need them in the sample map, and where they are placed. + +There are 3 important console commands to use in the relation to vis control. In each case the level must be loaded in */devmap* mode. Type a 1 after the command to toggle on, and the command again followed by 0 to toggle off. The console commands are: + +*/r_showtris*, this draws the level as polygons being rendered by Q3. + +*/r_speeds*, this shows a readout of the number of polygons being rendered. + +*/r_lockpvs*, this culls areas of the map outside the leaf node that you occupy at the time of enabling. + +These commands are essential for deciding where vis control requires a little tweaking. */r_showtris* in particular is extremely useful for evaluating where areas of the map are being drawn that the player should not be able to see. Fig.1 below shows a view from the base area, looking back towards the centre area. The mass of tris in the centre of the image shows where a considerable portion of the middle of the map is being rendered. From this position it should not be possible to see any of the central area, so unnecessary strain is being placed upon the computer. Don't worry about some of the shots being in full bright mode, as compiling light is not required in order to assess vis. + +![tris4.jpg](images/tris4.jpg) + +*fig.1* + +Having identified a problem with vis, how can the situation be improved? If you have read Bill Brook's article then you will already know the answer. We are going to use the hint texture to create artificial splits in the bsp tree. Brushes textured in the hint texture are structural, but invisible and non-solid. A common misconception is that hint brushes block vis themselves. This is incorrect. The job they do is to manipulate the vis calculation process by creating portals that otherwise would not exist. Where the vis process calculates that 2 portals can not see each other, culling will occur in game. This is a simple theory, yet sometimes much harder in practice to achieve. Fig.2 below shows the corridor that needs attention. All the detail brushes are filtered out as they have no effect upon vis. In the 2d window, examine the 2 selected brushes, marked A and B. The faces of the 2 brushes are cut on EXACTLY the same angle and meet at the corner of the corridor where it is intersected by a wall. Both brushes are made out of the common/skip texture, with only the angled faces textured in the common/hint texture. Common/skip is the one advance not covered by Bill Brook's tutorial. Skip informs the compiler to ignore any part of the brush that is textured by it. In this way only the faces textured as hint will cause portals avoiding unnecessary portal creation, as would happen with the original method. The down side of using common/skip is that only GTK 1.5 will filter skip/hint brushes out when you try to filter hint brushes from the map. Renaming the skip shader *hint_skip*, is one way around this. As both skip and hint are compiler only shaders this will cause no problems with missing textures in game. If the angled faces of the 2 hint brushes are not on the EXACT same angle their effect will be lost. Both brushes must end right up against other structural brushes, and they must meet right at the corner of the corridor. The compiler will calculate that the portal created by brush A could not see the portal created by brush B, and vice versa, therefore culling the map beyond. + +![hint.jpg](images/hint.jpg) + +*fig.2* + +*Fig.3 depicts the same corridor as in fig.1, but this time after +the hint brushes have been placed. Notice now how the central area of the map is +no longer being drawn, hence saving valuable tris. If you are very observant, +you may well notice that the frame rate indicator in the top right hand side of +the screen shows more or less the same number. This doesn't mean hinting has had +no effect, but rather is a result of a maximum frame rate being set.* + +![tris5.jpg](images/tris5.jpg) + +*fig.3* + +Moving on to the central area of the map, fig.4 shows the cave area below being drawn at the same time as the region we are currently in. This problem will be dealt with in a slightly different way. Remember the sky bug problem? The base area is part of the same potentially visible set as the central area. Vis starts by splitting the map into 1024 by 1024 unit sections. This is known as the *block size*. An obvious and simple solution would be to reduce the dimensions of the block size. By splitting the map into smaller sections to calculate vis, it becomes less likely that another area becomes visible from somewhere else. To adjust the block size, select any brush that is not part of a group and press n to bring up the entity handling window. The entity selected is called *worldspawn*. Worldspawn is used for global effects on the level. Add the key *_blocksize* and a numerical value of 512, or 256 or 128. It should be mentioned that the smaller the size, the longer the vis process will take, but considering the default setting of 1024 was compiling vis in under a second, that's something we can live with. For very large terrain maps vis could be speeded up by actually increasing _blocksize values, but of course this may result in adverse effect upon performance. + +![tris3.jpg](images/tris3.jpg) + +*fig.4* + +The image shown in fig.6 is the same area as in fig.4 above, but with _blocksize reduced to 256. So why can _blocksize not be used to tweak vis by itself? Do we really need to spend time playing about with hint brushes? + +The answer is yes. Look at fig.5, this is the same corridor as in fig.1 and 3. There are no hints placed in this version of the map, vis control has only been altered by reducing _blocksize. Compare how the central area is being rendered in fig.6, as opposed to fig.3 that shows the level compiled with hints in place and the _blocksize at the default 1024. + +![tris1.jpg](images/tris1.jpg) + +*fig.5* + +So apart from solving the sky bug problem has reducing the _blocksize had any other effect? Look at fig.6 and compare it to fig.4, notice how the cave area is no longer visible. + +![tris2.jpg](images/tris2.jpg) + +*fig.6* + +Through a combination of approaches, the most basic of which is good level planning and map making, vis is effectively controlled and the rendering of unnecessary tris avoided. Levels be far more highly detailed and still able to run fairly smoothly if similar care is taken. + + + +--- + +[← Tutorial 5](tutorial-05.md) | [Index](index.md) | [Tutorial 7 →](tutorial-07.md) diff --git a/leveldk.co.uk/tutorial-07.md b/leveldk.co.uk/tutorial-07.md new file mode 100644 index 0000000..cfe2116 --- /dev/null +++ b/leveldk.co.uk/tutorial-07.md @@ -0,0 +1,89 @@ +# Compiling the map + +Compilation of the map involves 3 stages: BSP, VIS and Light. We will return to this a little later. The bot file, or .aas, is compiled using the bspc.exe. The .aas is compiled from the .bsp, therefore it is necessary to compile the .map first. Any changes made to the map, and hence the .bsp will require you to recompile the bspc, or else the bot file will not function. Without a functioning .aas file bot play will not take place. + +Below is a copy of my `q3map.bat`, for compiling with Q3map2 and the bspc, with the switches I wanted set for both programs: + +"C:\Program Files\Quake III Arena\GtkRadiant\q3map_2.5.16_win32_x86\q3map2.exe" -meta -fs_basepath "C:\Program Files\Quake III Arena" "C:\Program Files\Quake III Arena\baseq3\maps\`tut.map`" "C:\Program Files\Quake III Arena\GtkRadiant\q3map_2.5.16_win32_x86\q3map2.exe" -vis -saveprt -fs_basepath "C:\Program Files\Quake III Arena" "C:\Program Files\Quake III Arena\baseq3\maps\`tut.map`" "C:\Program Files\Quake III Arena\GtkRadiant\q3map_2.5.16_win32_x86\q3map2.exe" -light -fast -faster -patchshadows -gamma 1.4 -samples 2 -fs_basepath "C:\Program Files\Quake III Arena" "C:\Program Files\Quake III Arena\baseq3\maps\`tut.map`" "C:\Program Files\Quake III Arena\GtkRadiant\bspc.exe" -optimize -forcesidesvisible -bsp2aas "C:\Program Files\Quake III Arena\baseq3\maps\`tut.map`" @echo off C: + +The paths will need to be edited for your installation, switches and your map name. + +To run the compiled map in Q3, drop the console and type: *`/sv_pure` 0*, press enter, then type, */map* or */devmap* followed by *yourmapname*, where *yourmapname* is the name of your map. You should not add the .bsp extension to the map name. + +Let's start with the bsp process. In order to avoid max visibility errors and the process failing, always compile with the option *-meta* set. + +Next, is the Vis stage. Never set *-fast*, this is no longer necessary if you build your levels using structural and detail brushes correctly. In the past Vis could take a very long time to calculate, so the  *-fast* option (where the map is divided into the default blocksize and no further calculations are made) offered a quick alternative for test compiles. Now, even with a very large map, Vis should not take long to compile. No further switches need to be set. + +The final stage of the process is the compiling of light, where the lightmap is calculated. Depending on the map and the switches you choose, this can take no time at all, or many hours. Always set *-fast*, this speeds up the process and seems to have no detrimental effect upon the final result. Another good idea is to set *-patchshadows*, which allows patches to cast shadows, and also improves the look of shadows that they receive. Setting *-gamma* with a value of somewhere between 1 and 2.2 helps increase the overall brightness of the level. If there are any over bright spots try setting *-compensate 4* as well. The *-samples* switch helps tidy up the edges of shadows, in the above example the value of 2 has been set. Compiling the light stage with these options is fairly quick. Setting the *-bounce* switch with a high value, say of 8 will seriously slow things down. By using the *-bounce* option you enable radiosity, this has the effect of smoothing light that strikes surfaces and blending shadow. The effects achieved through using *-bounce* can be remarkable, but the pay off is the massively increased compile times. + +The best way to learn about the different options offered by Q3map2, apart from research, is to use it. Experiment and find a look that you feel suits your level. + +Finally, let's look at the bspc options. Generally these are far fewer than the options available for Q3map2. If you compile the BSP stage with*-meta*, which you should always do, you must set the *-forcesidesvisible*, or else the bots will hardly play at all. Set the *-bsp2aas* switch so that the .bsp is used to generate an .aas file, and always set the *-optimize* switch or else the .aas file could outstrip the size of the .bsp file. + +That's about it. If the map has a leak, this will be reported during the compile stage. In GTK, go to *File*, then select *pointfile...*, this generates a red line that leads to the leak spot that requires fixing. + +## Finishing the level, creating the .pk3 and tying up loose ends + +So what else is there to do before the map making process is complete? Some finishing touches would include adding a message that gets shown on the screen as the map loads and adding some music to the level itself. A starting position is required for spectators and for the final scoreboard. If the level is intended for team play some location identifying messages are needed to keep track of your team. + +Let's discuss each of the above in turn. To display a start up message and add music to the level it is necessary to add more  keys and values to *worldspawn* (fig.1). The key *music* and a value that points to the music file adds the level music. To create a start up message add the key *message* where the value is the message you want shown. + +![wsbitz.jpg](images/wsbitz.jpg) + +fig1 + +The location messages are done by placing *target_location* entities (fig.2). Try to position the entity centrally in an area, players location will be identified by the *target_location* to which they are closest. It is possible, if desired, to set the colour of the printed message, using the key *count* and a value as described in the entity's notes. One word of caution, over use of this entity increases the information sent and received by computers when playing online or over a network. Judicial use is recommended. + +![location.jpg](images/location.jpg) + +*fig.2* + +The spectator spawn point and end level position is indicated by an *info_player_intermission* entity (fig.3). A map can only contain a single *info_player_intermission*. Rotate using the angle and angles keys as required and try to position in a particularly interesting setting. + +![interm.jpg](images/interm.jpg) + +*fig.3* + +For distribution compiled levels, and all the custom content that they require, are placed inside a .pk3 file. This is simply a .zip file that has been renamed. Make sure that in your computer settings you have not hidden file extensions, as this will result in problems renaming files. The zip contains folders, the exact same name as those found in the baseq3 folder (maps, scripts, textures, etc). Into these folders go the compiled map, the bot file and any custom content. When placed in the baseq3 folder, Q3 will read the map directly from the .pk3. To avoid confusion, when it comes to paking up a map it is often easier to create a folder named after your map (personally I usually place it on the desktop, but that's up to you). Inside the folder create the folders needed, these will always include:*maps, scripts* and*levelshots*. More on what goes in these folders in a moment*.* If you have used custom content that too must be included in its base folder (e.g. *textures*, with a folder inside named *mymapname*, or *models/mapobjects/mymapmodels*). Only include content that is required for the map, as mentioned previously, models get 'baked' into the .bsp, so do not need to be included, but their textures and any shaders do. The .pk3 containing the sample maps have to following folder structure: + +*Maps* + +*Scripts* + +*Textures* + +*Models*, with a folder inside named *mapobjects* + +*Levelshots* + +These folders are added to a zip files and then renamed as a .pk3. As a matter of choice I use Winzip to create zip files, but any compression utility that can create .zip files will do. + +Let's discuss what exactly goes in each folder in turn: + +In the *maps* folder are placed the .bsp and .aas files. In the sample .pk3 the .bsp and .aas for both the FFA and CTF versions have been included. The source .map files are not normally required, but because they support this series of tutorials their inclusion is essential in this case. There are also 2 folders containing a number of .tga art files. These folders were generated by Q3map2 as part of its computation of dynamic lighting effects. In order for in game textures effected by dynamic lights to be shown, these folders must be included. + +The*scripts* folder contains a .txt file, giving information about the levels and thanking those people who have assisted. The folder also contains the shader scripts used by the level or required for custom models. Remember if you pulled the shader from another map or source, you must include the entire script, not just the portion of it that may be applicable to this level. In short, if you use somebody else's shader, DO NOT EDIT IT! When writing shaders to use, cutting and pasting to another shader script is fine as long as credit is given, but changing the content of an existing script will effect the way the shader works in other levels that use the same script. Also contained in the *scripts* folder are 2 .arena files. An .arena file is a renamed .txt file. For the level or levels to appear in the Skirmish Menu in Q3, each must have its own .arena file named *`mymapname.arena`*, where my map name is the EXACT name of the .bsp for that level. The sample text from the `dk_lmtutctf.arena` follows: + +/////////////////////////////// ///killer pandas///////// /////////////////////////////// ///by dONKEY/////// /////////////////////////////// /////////////////////////////// { map "dk_lmtutctf" bots "major grunt doom bones visor razor" longname "killer pandas-ctf" capturelimit "8" type "ctf" } + +The `dk_lmtutffa.arena` contains the following text: + +{ map "dk_lmtutffa" bots "major grunt doom bones" longname "killer pandas-FFA" fraglimit 25 type "ffa tourney team" } + +The .arena files give required information, regarding bots to be loaded, map name, frag or capture limit, and game type to be set. + +Into the *textures* folder go any custom textures used, placed in side their folders. A common mistake is to fail to include textures from the *`mapmedia.pk3`*, which came with GTK and was not distributed with the original game. The confusion arises because the textures in the *`mapmedia.pk3`* will be included in the standard id texture sets. The importance of testing the level in a clean install of Q3 can not be stressed enough. + +The *Models* folder contains any custom model textures, as already mentioned, in a normal .pk3 do not include the .md3 or .ase files. The sample .pk3 does contain the actual models as they are required by the editor. Normally models go inside a *mapobjects* folder inside the *models* folder. Inside the *mapobjects* folder each model or relating texture should be placed into its own folder. + +The image shown as the map loads is placed into the *levelshots* folder. Do not make the image too large. A 512 by 512 pixel .jpg is normal. If the image is excessively big the over all size of the .pk3 increases with little benefit and the skirmish menu can start to lag. A useful trick for taking good screen shots is to turn off the gun and all 2d in game icons, *`/cg_drawgun` 0* and  *`/cg_draw2d` 0* entered in the console will have the desired result. The same commands followed by 1 will restore the gun and icons. + +Most mappers have 2 installs of Q3. One contains all the collected custom content, the other only the original .pk3 files that came with the game and its subsequent updates. Always run the new .pk3 in the clean version of quake. If you have missed any textures or shaders out of the .pk3, this will highlight the problem. The console reports errors. Drop the console and type */condump* *`mymapname.txt`*, locate the *`mymapname.txt`* file in your Q3 directory and open it. The console read out that you have just saved will inform you of what the game can not find. Add the missing files to the .pk3. + +In order to distribute the .pk3 it is common practice to place the file inside a zip file. Not only will this help reduce the upload/download times by shrinking the file size a little, but will avoid the problem of some hosts considering the .pk3 a suspect file type. + + + +--- + +[← Tutorial 6](tutorial-06.md) | [Index](index.md) | [Tutorial 8 →](tutorial-08.md) diff --git a/leveldk.co.uk/tutorial-08.md b/leveldk.co.uk/tutorial-08.md new file mode 100644 index 0000000..1997142 --- /dev/null +++ b/leveldk.co.uk/tutorial-08.md @@ -0,0 +1,71 @@ +# Converting the level for CTF + +At several points throughout this series of tutorials it has been mentioned that the map is intended to be converted to a Capture The Flag Level. This section deals with that process. + +There are some utilities available that are supposed to automate the cloning and rotating of levels in order create the archetypal symmetrical CTF level. This article will discuss how to complete that process by hand. From the outset, the important of saving the map under different names at points throughout the process can not be stressed enough. GTK does not enjoy moving and rotating large sections of a map and might well give up and crash at various points. Fig.1 below, shows the mirrored level we are intending to create. + +![ctflayout.jpg](images/ctflayout.jpg) + +*fig.1* + +Make certain you have nothing filter off. Before we make a mass selection, select and hide geometry on the existing base side that already exists in the area we are intending to place the cloned base. Delete the closed door ways that blocked of the un-built routes in the FFA version of the map.Draw out a large brush over the existing base (fig.2), then press the select inside button on the tool bar. Press i to invert the selection, then h to hide. Tidy up the parts of the base we want to select, hiding each brush that we do not want to select. When only the parts of the base we require are left, press i to select all (fig.3) and un-hide the rest of the map. Now for the tricky bit, hit the space bar to clone the base. Switch to top down view, if you haven't already. Rotate the selection 180 degrees. Hopefully GTK will not have crashed at this point. + +![select.jpg](images/select.jpg) + +*fig.2* + +![select2.jpg](images/select2.jpg) + +*fig.3* + +Zoom in and move the selection carefully into place (fig.4). This is by no means an easy task, take your time and use a lot of care. I would tend to suggest moving the cloned base away from the rest of the map and saving the level as a temporary name. This way if you make a mistake, selecting the base a second time will be straight forward using the select inside tool. + +![move.jpg](images/move.jpg) + +*fig.4* + +Having cloned and repositioned the base you will discover that the models, although having moved, have not rotated. You will need to go through the new area and rotate each model, using the entity editing window (fig.5). This may seem like a daunting task, but it actually does not take as long as you might think. + +![rotate.jpg](images/rotate.jpg) + +*fig.5* + +Now we have 2 neutral coloured bases. Not much good for red and blue teamed CTF games. Blue and red versions of the textures used, or suitable replacements will need to be applied (fig.6). Again, selecting an area by the select inside button, then using texture find/replace is a fast way of doing this. The only long winded part of the process in the sample map was needing to make 2 new .ase models for the bases as it is not possible to retexture the neutral coloured models like normal brushes. To add to the overall effect, the colour of the base light entities should also be changed to reflect the teams colour. Target_location messages should also be altered. + +![basecolour.jpg](images/basecolour.jpg) + +*fig.6* + +CTF games have specific game type entities. The most obvious new entity is the team_CTF_*colour* flag. This is the team flag entity (fig.7), and should be placed in such away that its attack and defence may lead to exciting and epic confrontations.  Instead of  info_player_deathmatch entities, use team_CTF_*colour* spawn (fig.8) or  team_CTF_*colour* player (fig.9). The first entity is the initial spawn point when a player enters the game, the second is the subsequent re-spawn point. Strategic placing of initial and subsequent spawn point is vital for good game play. + +![flags.jpg](images/flags.jpg) + +*fig.7* + +![initialplayer.jpg](images/initialplayer.jpg) + +*fig.8* + +![respawn.jpg](images/respawn.jpg) + +*fig.9* + +The level may need some tweaking that for FFA play was not necessary. The large botclip brushes caused a small problem if the flag were dropped by a bot, by placing nodrop brushes over the top this problem was solved (fig.10). + +![nodrop.jpg](images/nodrop.jpg) + +*fig.10* + +Finally, the item load will need to be adjusted from the FFA set to those more suitable for CTF. There is no longer any need for the teleporter as both cave areas must be the same. Try to balance item load so that both teams can have access to weapons and armour. More powerful weapons or armour can be placed in the centre area in order to promote aggressive play. In place of the armour and mega health in the cave area some items have been teamed. Entities are given the key team and a team value, in fig.11 below the value 2 has been selected. These items will spawn in rotation, offering some variation to tactics and game play. + +![entitiesandteamed.jpg](images/entitiesandteamed.jpg) + +*fig.11* + +A final word. CTF games are best suited to real players (although if you optimise your level for bots well enough they should give you a reasonable contest). If possible, beta test the level against humans in order to get a real feel for how well the level plays. + + + +--- + +[← Tutorial 7](tutorial-07.md) | [Index](index.md) | [Tutorial 9 →](tutorial-09.md) diff --git a/leveldk.co.uk/tutorial-09.md b/leveldk.co.uk/tutorial-09.md new file mode 100644 index 0000000..c75194f --- /dev/null +++ b/leveldk.co.uk/tutorial-09.md @@ -0,0 +1,93 @@ +# Entities not included in the main maps + +After completing the main maps and the supporting tutorials I was aware that a number of entities had not been covered. In this article the entities func_rotating, func_bobbing, func_door (including areaportals), func_train and func_plat will be examined, in addition to using triggers and traps. Open `dk_lmtutmap2.map` for examples of the entities that are going to be discussed. Remember the shortcut Ctrl Alt and left click to select func_ entities. + +Let's begin with func_door entities (fig.1). Doors can be single or multiple brushes, they can open in one direction, or brushes can move in different directions. If you want the doors to open in different directions (classic double doors moving in opposite directions away from each other), they must be separate door entities but teamed together. Create the brushes that you intend to convert into a func_door. Select all the door brushes and drop the entity menu, select func, then func_door. Then bring up the entity editing window and allocate an angle for the door to open. If you so desire you can set the speed for the door, the amount of damage a door takes before it is triggered open, or whether or not it acts as a crusher and inflicts damage. You may want to target a func_button at it or some other trigger to cause the door to open. More on this later, as we discuss other entities. In the small entity map there are two doors, each given the key team and the value 1. As a result the doors open and close together as a single entity. + +![door.jpg](images/door.jpg) + +*fig.1* + +In some cases it is useful to cull what is drawn behind a door in order to control vis. This is never a perfect solution for multiplayer games, as the map can lag as the doors open and tris climbs steeply. In order to cull behind a door, or set of doors, an areaportal is placed inside the door entity. Draw a thin brush (8 units thick) made of skip, that completely fills the area filled by the door. Texture one side of the brush with the common/areaportal texture (fig.3). The areaportal will only work if each area of the map is completely sealed off from the next area with areaportals. As the doors open and close the areaportal brush is triggered and the far side of the func_door entity is culled. + +![ap1.jpg](images/ap1.jpg) + +*fig.2* + +![ap2.jpg](images/ap2.jpg) + +*fig.3* + +Next func_rotating. In the example used in the accompanying map, the func_rotating entity as visible in game is a model. The entities discussed in this article could all be created with either a model or with brushes. The common/origin texture is used to specify the centre of the entity, around which it rotates along an axis set in the entity editing window (fig.4). Entities must be created from at least one solid brush, so when using a model you must also use a small player clip brush as part of the entity. Draw out a small brush out of common/origin, and a small playerclip brush. Select both and drop the entity menu, select func, then func_rotating. Edit the properties as required. In order to use a model as an entity, select both the model and the newly created func_rotating, then press Ctrl k to target the model at the func_rotating (fig.5). You should always target the model at the entity or a strange bug may occur where the model does not get baked into the .bsp on compile as normal. + +![r1.jpg](images/r1.jpg) + +*fig.4* + +![r2.jpg](images/r2.jpg) + +*fig.5* + +Targeting entities can be done by hand, so to speak, if necessary. The entity to be targeted **at** another needs a *targetname* key, in the above example *t2*. The entity targeted **by** another, requires a *target* key and a value, in this case *t2*. + +Func_bobbing entities can have any number uses. In the example map, 3 little crates are bobbing up and down gently in the water. The effect helps to give a sense of realism to the water brush. Once again, create and select the brush or brushes you want to create the entity from. This time select func_bobbing from the entity menu. Adjust the properties in the editing window (fig.6). Each of the 3 func_bobbing entities in the example map have a different value for the *phase* key, so that each bobs up and down at a slightly different rate, rather than at the same time. A general requirement for each of the entities covered is to experiment until you achieve the results you want. + +![bob.jpg](images/bob.jpg) + +*fig.6* + +Normally jump pads or steps are used for ascending levels in Quake 3. At times, however, it may be useful to use a platform that rises and falls. Platforms can be triggered by another entity (in this case a button), or will work automatically when a player steps aboard. Create and select the brush or brushes you intend to create the entity from and select func_plat. Bots can be rather stupid about running underneath platforms. This can be easily avoided by including as part of the entity a player or bot clip brush that at the platform's highest point completely fills the space below to the ground. Edit the entity's properties. Platforms are drawn in the editor in the highest, raised position, but spawn in game at the lower point. A slow rising platform can be used to reach more powerful items quite effectively. Adjust the *height* key value so that the platform sits neatly on the floor in its lowest position (subtract the thickness of the visible part of the func_plat from the total height from the top of the entity to the ground). The most significant problem with func_plat entities is the absence of sounds in the original distribution of Quake 3. To this end, replacement sounds must always be included with any map that uses this entity. Examine the sample .pk3. It contains a *sound* folder with a *movers* folder within and a *plats* folder inside that. Contained within the *plats* folder are the *pt1_start* and *pt1_end* .wav files looked for by the game. + +![plat.jpg](images/plat.jpg) + +*fig.7* + +In the map demonstrating the entities, the platform is triggered by the pushing of a button. A func_button is created by 1 or more brushes, in the same way as the other entities discussed. The*angle* key needs to be set, so that the entity moves in the desired direction when touched (fig.8). To target the entity at the func_plat, select the button, then the platform, then press Ctrl and k. Alternatively, set the *target* and *targetname* by the method outlined above. + +![but.jpg](images/but.jpg) + +*fig.8* + +An alternative method of creating a platform-like entity is to use a func_door. The trap door under the Rocket Launcher in the demonstrator map was created using a func_door, that is triggered to drop as the weapon is taken. The *angle* key is set with a value of -2 (fig.9) so that the door drops downward. As the door moves its own length (minus a *lip),* an unseen caulk brush is included as the underside of the entity. Edit the value of the *speed* key to suit your requirements. + +![trapdoor.jpg](images/trapdoor.jpg) + +*fig.9* + +To trigger the trap by taking the Rocket Launcher, target the weapon at the func_door entity. + +![trapdoor2.jpg](images/trapdoor2.jpg) + +*fig.10* + +Atmosphere can be added to the level by including sound effects. Select a target_speaker from the entity menu, then bring up the editing window (fig.11). The *noise* key allows a value to be set that points to the sound file, in this case *sound/dk_lmtut/evilwind.wav*, remember every custom media must be included in the .pk3. Many .wav files can dramatically increase the size of the file, so caution is recommended. Spawnflags can be set, in this case, *looped_on*, or *spawnflags 1*. The sound will play continually, getting louder as a player gets closer to the entity. + +![speaker.jpg](images/speaker.jpg) + +*fig.11* + +Finally, let us look at a func_train entity. This is perhaps the least often used of all the entities available in Quake 3. Firstly a path needs to be set by placing some *path/path_corner* entities from the entity menu (fig.12). Adjust the angles and target the entities in the order you with the train to follow and any other keys and values that you may feel appropriate for the level (fig.13). A line with arrows should correctly pass from entity to entity in the direction the train should pass. + +![tr1.jpg](images/tr1.jpg) + +*fig.12* + +![tr2.jpg](images/tr2.jpg) + +*fig.13* + +Create a brush or group of brushes, with the common/origin brush in the centre, select func_train from the entity menu. Adjust the keys and values, once again in the entity editing window. Select the entity and then the first path_corner entity and press Ctrl k to link the 2 (fig.14). The train will spawn in game at the path_corner that it was targeted at. + +![tr3.jpg](images/tr3.jpg) + +*fig.14* + +That brings us to the end of our journey through Quake 3 level design. I sincerely hope that some of the thought and ideas expressed in this series has been of some benefit. Go forth a create many new worlds, in which you can meet your friends and blow them apart with a large gun. ;) + +dONKEY + + + +--- + +[← Tutorial 8](tutorial-08.md) | [Index](index.md)