All public snippets from the HMN GitLab
This commit is contained in:
commit
e29ec6c1ca
|
@ -0,0 +1,63 @@
|
|||
Useful FFMPEG commands:
|
||||
|
||||
Turn video into images, 1 image per 5 seconds:
|
||||
ffmpeg -i Day_11_part_1.mp4 -vf fps=1/5 -start_number 0 %06d.png
|
||||
|
||||
Turn images into a slideshow:
|
||||
ffmpeg -framerate 30 -i %06d.png -c:v libx264 -r 30 -pix_fmt yuv420p Timelapse.mp4
|
||||
|
||||
Turn 1 image into a 3 second video:
|
||||
ffmpeg -framerate 1/3 -i 6.png -c:v libx264 -r 30 -pix_fmt yuv420p 6.mp4
|
||||
|
||||
Extract audio
|
||||
ffmpeg -i Day07c.mp4 -c:a copy -vn audio.aac
|
||||
|
||||
Extract video:
|
||||
ffmpeg -i Day07c.mp4 -c:v copy -an video.mp4
|
||||
|
||||
Mux video and audio back together:
|
||||
ffmpeg -i Timelapse.aac -i Timelapse.mp4 -c:v copy -c:a copy -bsf:a aac_adtstoasc TimelapseNew.mp4
|
||||
|
||||
Mux like before, but also stop encoding when the shortest file runs out
|
||||
ffmpeg -framerate 60 -i %06d.png -c:v libx264 -r 60 -pix_fmt yuv420p -i audiofile.mp3 -c:a copy -filter_complex " [1:0] apad " -shortest vidya.mp4
|
||||
|
||||
Concat simple:
|
||||
ffmpeg -f concat -i a.txt Timelapse.aac
|
||||
|
||||
Concat advanced:
|
||||
ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
|
||||
ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
|
||||
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4
|
||||
|
||||
Copy a 58m part of a video, starting 2h33m20s in to another video:
|
||||
ffmpeg -ss 02:33:20 -i Day07c.mp4 -t 00:58:00 -c:a copy -c:v copy -bsf:a aac_adtstoasc 3.mp4
|
||||
|
||||
# Quality settings
|
||||
|
||||
echo Default quality (-crf 23) - 2041kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -pix_fmt yuv420p quality_23_default.mp4
|
||||
|
||||
echo Lossless - 5444kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -preset veryslow -qp 0 -pix_fmt yuv420p lossless.mp4
|
||||
# If you care just about the quality and are going to upload it to YT,
|
||||
# you could use -preset veryfast or -preset ultrafast to still get lossless, but without as long a wait
|
||||
|
||||
echo Default quality, animation - 1828kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -pix_fmt yuv420p quality_23_default_anim_tuned.mp4
|
||||
|
||||
echo Lossless, animation - 5447kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -preset veryslow -qp 0 -pix_fmt yuv420p lossless_anim_tuned.mp4
|
||||
|
||||
echo Quality level 22 - 2239kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -crf 22 -pix_fmt yuv420p quality_22.mp4
|
||||
|
||||
echo Quality level 22, animation - 1998kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -crf 22 -pix_fmt yuv420p quality_22_anim_tuned.mp4
|
||||
|
||||
echo Quality level 18 - 3069kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -crf 18 -pix_fmt yuv420p quality_18.mp4
|
||||
|
||||
echo Quality level 18, animation - 2740kb/s avg
|
||||
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -crf 18 -pix_fmt yuv420p quality_18_anim_tuned.mp4
|
||||
|
||||
For more quality settings, see: https://trac.ffmpeg.org/wiki/Encode/H.264
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
if __name__ == '__main__':
|
||||
|
||||
target = '3.14159263'
|
||||
# target = '3.14159263589'
|
||||
digits = len(target) - 2
|
||||
power = pow(10, digits)
|
||||
target = int(float(target) * power)
|
||||
|
||||
result = 0
|
||||
divisor = -1
|
||||
step = 0
|
||||
count = -1
|
||||
count2 = 0
|
||||
subtract = False
|
||||
|
||||
close = False
|
||||
r4 = 0
|
||||
|
||||
while (not close):
|
||||
count = count + 1
|
||||
divisor = divisor + 2
|
||||
|
||||
step = 1 / divisor
|
||||
if subtract == True:
|
||||
result = result - step
|
||||
subtract = False
|
||||
else:
|
||||
result = result + step
|
||||
subtract = True
|
||||
|
||||
r4 = result * 4
|
||||
|
||||
if count2 == 100000:
|
||||
print("Step: %10d | Fraction: 1/%10d = %1.12f | Result: %1.12f | Result * 4: %1.12f" % (count, divisor, step, result, r4))
|
||||
count2 = 0
|
||||
|
||||
if int(r4 * power) == target:
|
||||
close = True
|
||||
|
||||
count2 = count2 + 1
|
||||
|
||||
print("Step: %10d | Fraction: 1/%10d = %1.12f | Result: %1.12f | Result * 4: %1.12f" % (count , divisor, step, result, r4))
|
|
@ -0,0 +1,23 @@
|
|||
/* This file is automatically generated.
|
||||
It defines a symbol `__stub_FUNCTION' for each function
|
||||
in the C library which is a stub, meaning it will fail
|
||||
every time called, usually setting errno to ENOSYS. */
|
||||
|
||||
#ifdef _LIBC
|
||||
# error Applications may not define the macro _LIBC
|
||||
#endif
|
||||
|
||||
#define __stub_bdflush
|
||||
#define __stub_chflags
|
||||
#define __stub_fattach
|
||||
#define __stub_fchflags
|
||||
#define __stub_fdetach
|
||||
#define __stub_getmsg
|
||||
#define __stub_gtty
|
||||
#define __stub_lchmod
|
||||
#define __stub_putmsg
|
||||
#define __stub_revoke
|
||||
#define __stub_setlogin
|
||||
#define __stub_sigreturn
|
||||
#define __stub_sstk
|
||||
#define __stub_stty
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from datetime import datetime, date, timedelta
|
||||
import sys
|
||||
# this is optional, to autolink mentioned urls if format --html is chosen
|
||||
# if you don't have it, also comment out line 96
|
||||
from bleach import linkify
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#print(sys.argv)
|
||||
if len(sys.argv) < 2 or (len(sys.argv) > 1 and sys.argv[1] == '-h'):
|
||||
print("Usage: %s <logname> [offset-in-hours] [--html]" % sys.argv[0])
|
||||
exit()
|
||||
elif len(sys.argv) >= 2:
|
||||
filename = sys.argv[1]
|
||||
offset = 0
|
||||
elif len(sys.argv) >= 3:
|
||||
filename = sys.argv[1]
|
||||
offset = int(sys.argv[2])
|
||||
if len(sys.argv) >= 4 and sys.argv[3] == '--html':
|
||||
formatting = 'html'
|
||||
else:
|
||||
formatting = 'plain'
|
||||
|
||||
today = date.today()
|
||||
delta = timedelta(hours=offset)
|
||||
nickcolor = {}
|
||||
log = []
|
||||
colors = ['red', 'blue', 'green', 'yellow', 'teal', 'purple']
|
||||
max_nick = 0
|
||||
with open(filename, 'r') as f:
|
||||
for line in f:
|
||||
when = line[:15]
|
||||
when_parsed = datetime.strptime(when, '%b %d %H:%M:%S').replace(year=today.year)
|
||||
when_est = when_parsed + delta
|
||||
what = line[15:]
|
||||
if what[1] == '<':
|
||||
nick_start = 2
|
||||
nick_end = what.find('>')
|
||||
nick = what[nick_start:nick_end].strip()
|
||||
if not nick in nickcolor:
|
||||
nickcolor[nick] = colors[0]
|
||||
colors = colors[1:] + [colors[0]]
|
||||
|
||||
if len(nick) > max_nick:
|
||||
max_nick = len(nick)
|
||||
|
||||
what = what[nick_end + 2:].strip('\n')
|
||||
|
||||
log.append(dict(when=when_est, nick=nick, what=what))
|
||||
|
||||
elif what[1] == '*':
|
||||
nick_start = 3
|
||||
nick_end = what.find('\s')
|
||||
what = what[nick_start:nick_end].strip()
|
||||
nick_start = 0
|
||||
nick_end = what.find(' ')
|
||||
nick = what[nick_start:nick_end].strip()
|
||||
if not nick in nickcolor:
|
||||
nickcolor[nick] = colors[0]
|
||||
colors = colors[1:] + [colors[0]]
|
||||
|
||||
if len(nick) > max_nick:
|
||||
max_nick = len(nick)
|
||||
|
||||
what = what[nick_end + 1:].strip('\n')
|
||||
|
||||
log.append(dict(when=when_est, nick=nick, what=what, action=True))
|
||||
|
||||
if formatting == 'html':
|
||||
fmt1 = "<tr><td class=\"dateline\">%s</td><td class=\"nick %s\">%s</td><td class=\"chat %s\">%s</td>"
|
||||
fmt2 = "<tr><td class=\"dateline\">%s</td><td colspan=\"2\">* <span class=\"nick %s\">%s</span> <span class=\"chat %s\">%s</span></td>"
|
||||
print("""<html>
|
||||
<head>
|
||||
<title>%s</title>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.nick { font-weight: bold; text-align: right; }
|
||||
table { width: 100%%; }
|
||||
tr > td { width: 10%%; }
|
||||
tr > td+td { width: 10%%; }
|
||||
tr > td+td+td { width: 80%%; }""" % filename)
|
||||
for nick, color in nickcolor.items():
|
||||
print(".%s { color: %s; }" % (nick, color))
|
||||
print(".%s a { color: %s; font-weight: bold; }" % (nick, color))
|
||||
|
||||
print("""
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
""")
|
||||
thisnick = ''
|
||||
for line in log:
|
||||
line['what'] = linkify(line['what'])
|
||||
if not 'action' in line.keys():
|
||||
if thisnick == line['nick']:
|
||||
displaynick = u'▲'
|
||||
else:
|
||||
displaynick = line['nick']
|
||||
print(fmt1 % (line['when'], line['nick'], displaynick, line['nick'], line['what']))
|
||||
else:
|
||||
print(fmt2 % (line['when'], line['nick'], line['nick'], line['nick'], line['what']))
|
||||
|
||||
thisnick = line['nick']
|
||||
print("""
|
||||
</table>
|
||||
</body>
|
||||
</html>""")
|
||||
else:
|
||||
fmt = "%%s | %%%ds | %%s" % (max_nick)
|
||||
for line in log:
|
||||
if not 'action' in line.keys():
|
||||
print(fmt % (line['when'], line['nick'], line['what']))
|
||||
else:
|
||||
print("%s | * %s %s" % (line['when'], line['nick'], line['what']))
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
;FFMETADATA1
|
||||
major_brand=isom
|
||||
minor_version=512
|
||||
compatible_brands=isomiso2avc1mp41
|
||||
encoder=Lavf57.41.100
|
||||
|
||||
title=Handmade Hero Day 317 - Alpha Blending Multiple Render Targets
|
||||
|
||||
[CHAPTER]
|
||||
TIMEBASE=1/1
|
||||
START=0
|
||||
END=66
|
||||
title=Start
|
||||
|
||||
[CHAPTER]
|
||||
TIMEBASE=1/1
|
||||
START=66
|
||||
END=176
|
||||
title=Run the game and show our current situation with render targets
|
||||
|
||||
[CHAPTER]
|
||||
TIMEBASE=1/1
|
||||
START=176
|
||||
END=368
|
||||
title=handmade_entity.cpp: Make UpdateAndRenderEntities() create two different ClipRects for the alpha and normal floors
|
||||
|
||||
[CHAPTER]
|
||||
TIMEBASE=1/1
|
||||
START=368
|
||||
END=413
|
||||
title=Run the game and see that the alpha stuff is gone
|
|
@ -0,0 +1,86 @@
|
|||
#!/bin/zsh
|
||||
|
||||
#Default settings
|
||||
DLART="0"
|
||||
DLPRE="0"
|
||||
DLSRC="0"
|
||||
DLVID="1"
|
||||
RATE="800K"
|
||||
|
||||
# My environment settings
|
||||
SRCDIR="${HOME}/Software/src/Handmade Hero" # the directory to save Casey's files
|
||||
VIDDIR="${HOME}/Media/Videos/YouTube/Handmade Hero" # the direct to save the videos
|
||||
OWLURL="" # the url in the email from Molly@sendowl.com
|
||||
|
||||
# Casey's files
|
||||
HMHSRC_001_099="file?product_id=168514"
|
||||
HMHSRC_100_199="file?product_id=79802"
|
||||
HMHART="file?product_id=93897"
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: dlhmh [option]"
|
||||
echo
|
||||
echo " -art - disable art"
|
||||
echo " +art - enable art"
|
||||
echo " -pre - disable pre-stream video"
|
||||
echo " +pre - enable pre-stream video"
|
||||
echo " -src - disable source"
|
||||
echo " +src - enable source"
|
||||
echo " -vid - disable main stream"
|
||||
echo " +vid - enable main stream"
|
||||
echo
|
||||
echo " -fs - download at full-speed"
|
||||
echo " -h|--help - outputs this message"
|
||||
exit
|
||||
}
|
||||
|
||||
# Argument parsing
|
||||
while [[ $1 ]]; do
|
||||
case "$1" in
|
||||
'-art') DLART='0';;
|
||||
'+art') DLART='1';;
|
||||
'-pre') DLPRE='0';;
|
||||
'+pre') DLPRE='1' ; echo "Downloading the pre-stream video is not yet implemented\nExiting..." ; exit;;
|
||||
'-src') DLSRC='0';;
|
||||
'+src') DLSRC='1';;
|
||||
'-vid') DLVID='0';;
|
||||
'+vid') DLVID='1';;
|
||||
'-fs') RATE="9G";;
|
||||
'-h'|'--help') usage ;;
|
||||
*) echo "${0##*/}: No such option, mate!\n" ; usage ; exit 127 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Downloading the art assets
|
||||
if [[ "$DLART" == "1" ]]; then
|
||||
wget -O "${SRCDIR}/handmade_hero_assets.zip" "${OWLURL}/${HMHART}"
|
||||
fi
|
||||
|
||||
# Downloading the pre-stream video
|
||||
if [[ "$DLPRE" == "1" ]]; then
|
||||
#NOTE(matt): This is where the pre-stream downloading code will go
|
||||
fi
|
||||
|
||||
# Downloading the source code
|
||||
if [[ "$DLSRC" == "1" ]]; then
|
||||
if [[ ! -e "${SRCDIR}/handmade_hero_source_001-099.zip" ]]; then
|
||||
wget -O "${SRCDIR}/handmade_hero_source_001-099.zip" "${OWLURL}/${HMHSRC_001_099}"
|
||||
fi
|
||||
wget -O "${SRCDIR}/handmade_hero_source_100-199.zip" "${OWLURL}/${HMHSRC_100_199}"
|
||||
fi
|
||||
|
||||
# YouTube playlists:
|
||||
# https://www.youtube.com/user/handmadeheroarchive/playlists
|
||||
|
||||
#youtube-dl -o "${VIDDIR}/04. Game Architecture/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY6v0eFYcyiH7twEh1UF2Lxw"
|
||||
#youtube-dl -o "${VIDDIR}/05. Asset Pipeline/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY6JVkAI8elXUJ-83HY706AQ"
|
||||
#youtube-dl -o "${VIDDIR}/06. Rendering/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY40lfaaowTqIs_dKNgOXR5Q"
|
||||
|
||||
# Download all of the videos from the last 4 days ago into the HMH base directory
|
||||
# NOTE(matt): Maybe figure out a way to download them to playlists while still
|
||||
# keeping the '4 days ago' thing
|
||||
if [[ "$DLVID" == "1" ]]; then
|
||||
youtube-dl -i -r $RATE -f 137+140 --download-archive "${VIDDIR}/.dlarchive" -o "${VIDDIR}/%(title)s-%(id)s.%(ext)s" --dateafter "$(date +%Y%m%d -d'8 days ago')" "https://www.youtube.com/user/handmadeheroarchive"
|
||||
fi
|
|
@ -0,0 +1,39 @@
|
|||
! ----------------------------------------------------------------------------
|
||||
! file: ~/.Xresources-softlo
|
||||
! author: Miblo - http://www.miblodelcarpio.co.uk/
|
||||
! modified: 2013-11-10
|
||||
! vim:fenc=utf-8:nu:ai:si:et:ts=4:sw=4:ft=xdefaults:
|
||||
! ----------------------------------------------------------------------------
|
||||
|
||||
*background: #000000
|
||||
*foreground: #FFFFFF
|
||||
*fading: 40
|
||||
*fadeColor: #000000
|
||||
*cursorColor: #33FF33
|
||||
*pointerColorBackground:#99FF33
|
||||
*pointerColorForeground:#000000
|
||||
|
||||
!! blacks: Black / Dark grey
|
||||
*color0: #000000
|
||||
*color8: #3D3D3D
|
||||
!! reds: Super Meat Boy red / Light brilliant red
|
||||
*color1: #BA0001
|
||||
*color9: #FF3333
|
||||
!! greens: Light brilliant green / Light brilliant chartreuse green
|
||||
*color2: #33FF33
|
||||
*color10: #99FF33
|
||||
!! yellows: Light brilliant orange / L—event yellow
|
||||
*color3: #FF9933
|
||||
*color11: #FEF697
|
||||
!! blues: Light brilliant blue / Light brilliant azure
|
||||
*color4: #3333FF
|
||||
*color12: #3399FF
|
||||
!! magentas: Light brilliant violet / Light brilliant magenta
|
||||
*color5: #9933FF
|
||||
*color13: #FF33FF
|
||||
!! cyans: Light brilliant cyan / Very light cyan
|
||||
*color6: #33FFFF
|
||||
*color14: #99FFFF
|
||||
!! whites: Cosmic latte / White
|
||||
*color7: #FFF8E7
|
||||
*color15: #FFFFFF
|
|
@ -0,0 +1,41 @@
|
|||
! ----------------------------------------------------------------------------
|
||||
! file: ~/.Xresources
|
||||
! author: Miblo - http://www.miblodelcarpio.co.uk/
|
||||
! modified: 2012-06-15
|
||||
! vim:fenc=utf-8:nu:ai:si:et:ts=4:sw=4:ft=xdefaults:
|
||||
! ----------------------------------------------------------------------------
|
||||
|
||||
#include ".Xresources-softlo"
|
||||
|
||||
Xcursor.theme: oxy-neon
|
||||
|
||||
XTerm*vt100.faceName: inconsolata
|
||||
XTerm*vt100.faceSize: 9
|
||||
|
||||
URxvt*termName: rxvt-256color
|
||||
!URxvt.letterSpace: -1
|
||||
URxvt.font: xft:inconsolata:size=9,\
|
||||
xft:Liberation Mono,\
|
||||
xft:IPAPMincho,\
|
||||
xft:symbola
|
||||
URxvt.boldFont: xft:inconsolata:bold:size=9,\
|
||||
xft:Liberation Mono,\
|
||||
xft:IPAPMincho,\
|
||||
xft:symbola
|
||||
URxvt*externalBorder: 0
|
||||
URxvt*internalBorder: 0
|
||||
URxvt*saveLines: 4096
|
||||
URxvt.scrollBar: false
|
||||
!URxvt.tabbed.tabbar-fg: 15
|
||||
!URxvt.tabbed.tabbar-bg: 0
|
||||
!URxvt.tabbed.tab-fg: 15
|
||||
!URxvt.tabbed.tab-bg: 4
|
||||
|
||||
!URxvt.perl-ext: default,clipboard,tabbed,url-select,keyboard-select
|
||||
URxvt.perl-ext: default,clipboard,url-select,keyboard-select
|
||||
URxvt.keysym.M-u: perl:url-select:select_next
|
||||
URxvt.keysym.M-Escape: perl:keyboard-select:activate
|
||||
URxvt.keysym.M-s: perl:keyboard-select:search
|
||||
URxvt.url-select.launcher: firefox
|
||||
URxvt.url-select.underline: true
|
||||
URxvt*boldMode: false
|
|
@ -0,0 +1,477 @@
|
|||
a 0 512
|
||||
a 1 128
|
||||
r 0 640
|
||||
a 2 128
|
||||
f 1
|
||||
r 0 768
|
||||
a 3 128
|
||||
f 2
|
||||
r 0 896
|
||||
a 4 128
|
||||
f 3
|
||||
r 0 1024
|
||||
a 5 128
|
||||
f 4
|
||||
r 0 1152
|
||||
a 6 128
|
||||
f 5
|
||||
r 0 1280
|
||||
a 7 128
|
||||
f 6
|
||||
r 0 1408
|
||||
a 8 128
|
||||
f 7
|
||||
r 0 1536
|
||||
a 9 128
|
||||
f 8
|
||||
r 0 1664
|
||||
a 10 128
|
||||
f 9
|
||||
r 0 1792
|
||||
a 11 128
|
||||
f 10
|
||||
r 0 1920
|
||||
a 12 128
|
||||
f 11
|
||||
r 0 2048
|
||||
a 13 128
|
||||
f 12
|
||||
r 0 2176
|
||||
a 14 128
|
||||
f 13
|
||||
r 0 2304
|
||||
a 15 128
|
||||
f 14
|
||||
r 0 2432
|
||||
a 16 128
|
||||
f 15
|
||||
r 0 2560
|
||||
a 17 128
|
||||
f 16
|
||||
r 0 2688
|
||||
a 18 128
|
||||
f 17
|
||||
r 0 2816
|
||||
a 19 128
|
||||
f 18
|
||||
r 0 2944
|
||||
a 20 128
|
||||
f 19
|
||||
r 0 3072
|
||||
a 21 128
|
||||
f 20
|
||||
r 0 3200
|
||||
a 22 128
|
||||
f 21
|
||||
r 0 3328
|
||||
a 23 128
|
||||
f 22
|
||||
r 0 3456
|
||||
a 24 128
|
||||
f 23
|
||||
r 0 3584
|
||||
a 25 128
|
||||
f 24
|
||||
r 0 3712
|
||||
a 26 128
|
||||
f 25
|
||||
r 0 3840
|
||||
a 27 128
|
||||
f 26
|
||||
r 0 3968
|
||||
a 28 128
|
||||
f 27
|
||||
r 0 4096
|
||||
a 29 128
|
||||
f 28
|
||||
r 0 4224
|
||||
a 30 128
|
||||
f 29
|
||||
r 0 4352
|
||||
a 31 128
|
||||
f 30
|
||||
r 0 4480
|
||||
a 32 128
|
||||
f 31
|
||||
r 0 4608
|
||||
a 33 128
|
||||
f 32
|
||||
r 0 4736
|
||||
a 34 128
|
||||
f 33
|
||||
r 0 4864
|
||||
a 35 128
|
||||
f 34
|
||||
r 0 4992
|
||||
a 36 128
|
||||
f 35
|
||||
r 0 5120
|
||||
a 37 128
|
||||
f 36
|
||||
r 0 5248
|
||||
a 38 128
|
||||
f 37
|
||||
r 0 5376
|
||||
a 39 128
|
||||
f 38
|
||||
r 0 5504
|
||||
a 40 128
|
||||
f 39
|
||||
r 0 5632
|
||||
a 41 128
|
||||
f 40
|
||||
r 0 5760
|
||||
a 42 128
|
||||
f 41
|
||||
r 0 5888
|
||||
a 43 128
|
||||
f 42
|
||||
r 0 6016
|
||||
a 44 128
|
||||
f 43
|
||||
r 0 6144
|
||||
a 45 128
|
||||
f 44
|
||||
r 0 6272
|
||||
a 46 128
|
||||
f 45
|
||||
r 0 6400
|
||||
a 47 128
|
||||
f 46
|
||||
r 0 6528
|
||||
a 48 128
|
||||
f 47
|
||||
r 0 6656
|
||||
a 49 128
|
||||
f 48
|
||||
r 0 6784
|
||||
a 50 128
|
||||
f 49
|
||||
r 0 6912
|
||||
a 51 128
|
||||
f 50
|
||||
r 0 7040
|
||||
a 52 128
|
||||
f 51
|
||||
r 0 7168
|
||||
a 53 128
|
||||
f 52
|
||||
r 0 7296
|
||||
a 54 128
|
||||
f 53
|
||||
r 0 7424
|
||||
a 55 128
|
||||
f 54
|
||||
r 0 7552
|
||||
a 56 128
|
||||
f 55
|
||||
r 0 7680
|
||||
a 57 128
|
||||
f 56
|
||||
r 0 7808
|
||||
a 58 128
|
||||
f 57
|
||||
r 0 7936
|
||||
a 59 128
|
||||
f 58
|
||||
r 0 8064
|
||||
a 60 128
|
||||
f 59
|
||||
r 0 8192
|
||||
a 61 128
|
||||
f 60
|
||||
r 0 8320
|
||||
a 62 128
|
||||
f 61
|
||||
r 0 8448
|
||||
a 63 128
|
||||
f 62
|
||||
r 0 8576
|
||||
a 64 128
|
||||
f 63
|
||||
r 0 8704
|
||||
a 65 128
|
||||
f 64
|
||||
r 0 8832
|
||||
a 66 128
|
||||
f 65
|
||||
r 0 8960
|
||||
a 67 128
|
||||
f 66
|
||||
r 0 9088
|
||||
a 68 128
|
||||
f 67
|
||||
r 0 9216
|
||||
a 69 128
|
||||
f 68
|
||||
r 0 9344
|
||||
a 70 128
|
||||
f 69
|
||||
r 0 9472
|
||||
a 71 128
|
||||
f 70
|
||||
r 0 9600
|
||||
a 72 128
|
||||
f 71
|
||||
r 0 9728
|
||||
a 73 128
|
||||
f 72
|
||||
r 0 9856
|
||||
a 74 128
|
||||
f 73
|
||||
r 0 9984
|
||||
a 75 128
|
||||
f 74
|
||||
r 0 10112
|
||||
a 76 128
|
||||
f 75
|
||||
r 0 10240
|
||||
a 77 128
|
||||
f 76
|
||||
r 0 10368
|
||||
a 78 128
|
||||
f 77
|
||||
r 0 10496
|
||||
a 79 128
|
||||
f 78
|
||||
r 0 10624
|
||||
a 80 128
|
||||
f 79
|
||||
r 0 10752
|
||||
a 81 128
|
||||
f 80
|
||||
r 0 10880
|
||||
a 82 128
|
||||
f 81
|
||||
r 0 11008
|
||||
a 83 128
|
||||
f 82
|
||||
r 0 11136
|
||||
a 84 128
|
||||
f 83
|
||||
r 0 11264
|
||||
a 85 128
|
||||
f 84
|
||||
r 0 11392
|
||||
a 86 128
|
||||
f 85
|
||||
r 0 11520
|
||||
a 87 128
|
||||
f 86
|
||||
r 0 11648
|
||||
a 88 128
|
||||
f 87
|
||||
r 0 11776
|
||||
a 89 128
|
||||
f 88
|
||||
r 0 11904
|
||||
a 90 128
|
||||
f 89
|
||||
r 0 12032
|
||||
a 91 128
|
||||
f 90
|
||||
r 0 12160
|
||||
a 92 128
|
||||
f 91
|
||||
r 0 12288
|
||||
a 93 128
|
||||
f 92
|
||||
r 0 12416
|
||||
a 94 128
|
||||
f 93
|
||||
r 0 12544
|
||||
a 95 128
|
||||
f 94
|
||||
r 0 12672
|
||||
a 96 128
|
||||
f 95
|
||||
r 0 12800
|
||||
a 97 128
|
||||
f 96
|
||||
r 0 12928
|
||||
a 98 128
|
||||
f 97
|
||||
r 0 13056
|
||||
a 99 128
|
||||
f 98
|
||||
r 0 13184
|
||||
a 100 128
|
||||
f 99
|
||||
r 0 13312
|
||||
a 101 128
|
||||
f 100
|
||||
r 0 13440
|
||||
a 102 128
|
||||
f 101
|
||||
r 0 13568
|
||||
a 103 128
|
||||
f 102
|
||||
r 0 13696
|
||||
a 104 128
|
||||
f 103
|
||||
r 0 13824
|
||||
a 105 128
|
||||
f 104
|
||||
r 0 13952
|
||||
a 106 128
|
||||
f 105
|
||||
r 0 14080
|
||||
a 107 128
|
||||
f 106
|
||||
r 0 14208
|
||||
a 108 128
|
||||
f 107
|
||||
r 0 14336
|
||||
a 109 128
|
||||
f 108
|
||||
r 0 14464
|
||||
a 110 128
|
||||
f 109
|
||||
r 0 14592
|
||||
a 111 128
|
||||
f 110
|
||||
r 0 14720
|
||||
a 112 128
|
||||
f 111
|
||||
r 0 14848
|
||||
a 113 128
|
||||
f 112
|
||||
r 0 14976
|
||||
a 114 128
|
||||
f 113
|
||||
r 0 15104
|
||||
a 115 128
|
||||
f 114
|
||||
r 0 15232
|
||||
a 116 128
|
||||
f 115
|
||||
r 0 15360
|
||||
a 117 128
|
||||
f 116
|
||||
r 0 15488
|
||||
a 118 128
|
||||
f 117
|
||||
r 0 15616
|
||||
a 119 128
|
||||
f 118
|
||||
r 0 15744
|
||||
a 120 128
|
||||
f 119
|
||||
r 0 15872
|
||||
a 121 128
|
||||
f 120
|
||||
r 0 16000
|
||||
a 122 128
|
||||
f 121
|
||||
r 0 16128
|
||||
a 123 128
|
||||
f 122
|
||||
r 0 16256
|
||||
a 124 128
|
||||
f 123
|
||||
r 0 16384
|
||||
a 125 128
|
||||
f 124
|
||||
r 0 16512
|
||||
a 126 128
|
||||
f 125
|
||||
r 0 16640
|
||||
a 127 128
|
||||
f 126
|
||||
r 0 16768
|
||||
a 128 128
|
||||
f 127
|
||||
r 0 16896
|
||||
a 129 128
|
||||
f 128
|
||||
r 0 17024
|
||||
a 130 128
|
||||
f 129
|
||||
r 0 17152
|
||||
a 131 128
|
||||
f 130
|
||||
r 0 17280
|
||||
a 132 128
|
||||
f 131
|
||||
r 0 17408
|
||||
a 133 128
|
||||
f 132
|
||||
r 0 17536
|
||||
a 134 128
|
||||
f 133
|
||||
r 0 17664
|
||||
a 135 128
|
||||
f 134
|
||||
r 0 17792
|
||||
a 136 128
|
||||
f 135
|
||||
r 0 17920
|
||||
a 137 128
|
||||
f 136
|
||||
r 0 18048
|
||||
a 138 128
|
||||
f 137
|
||||
r 0 18176
|
||||
a 139 128
|
||||
f 138
|
||||
r 0 18304
|
||||
a 140 128
|
||||
f 139
|
||||
r 0 18432
|
||||
a 141 128
|
||||
f 140
|
||||
r 0 18560
|
||||
a 142 128
|
||||
f 141
|
||||
r 0 18688
|
||||
a 143 128
|
||||
f 142
|
||||
r 0 18816
|
||||
a 144 128
|
||||
f 143
|
||||
r 0 18944
|
||||
a 145 128
|
||||
f 144
|
||||
r 0 19072
|
||||
a 146 128
|
||||
f 145
|
||||
r 0 19200
|
||||
a 147 128
|
||||
f 146
|
||||
r 0 19328
|
||||
a 148 128
|
||||
f 147
|
||||
r 0 19456
|
||||
a 149 128
|
||||
f 148
|
||||
r 0 19584
|
||||
a 150 128
|
||||
f 149
|
||||
r 0 19712
|
||||
a 151 128
|
||||
f 150
|
||||
r 0 19840
|
||||
a 152 128
|
||||
f 151
|
||||
r 0 19968
|
||||
a 153 128
|
||||
f 152
|
||||
r 0 20096
|
||||
a 154 128
|
||||
f 153
|
||||
r 0 20224
|
||||
a 155 128
|
||||
f 154
|
||||
r 0 20352
|
||||
a 156 128
|
||||
f 155
|
||||
r 0 20480
|
||||
a 157 128
|
||||
f 156
|
||||
r 0 20608
|
||||
a 158 128
|
||||
f 157
|
||||
r 0 20736
|
||||
a 159 128
|
||||
f 158
|
||||
r 0 20864
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# packs multiple images (bmp/png/...) into ico file
|
||||
# width and height of images must be <= 256
|
||||
# pixel format of images must be 32-bit RGBA
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
import os
|
||||
from PIL import Image # https://python-pillow.org/
|
||||
|
||||
def pack(output, inp):
|
||||
count = len(inp)
|
||||
|
||||
with open(output, "wb") as f:
|
||||
f.write(struct.pack("HHH", 0, 1, count))
|
||||
offset = struct.calcsize("HHH") + struct.calcsize("BBBBHHII")*count
|
||||
|
||||
for i in inp:
|
||||
size = os.stat(i).st_size
|
||||
img = Image.open(i)
|
||||
w = 0 if img.width == 256 else img.width
|
||||
h = 0 if img.height == 256 else img.height
|
||||
f.write(struct.pack("BBBBHHII", w, h, 0, 0, 1, 32, size, offset))
|
||||
offset += size
|
||||
|
||||
for i in inp:
|
||||
f.write(open(i, "rb").read())
|
||||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser(description="pack multiple images into ico file")
|
||||
ap.add_argument("-o", "--out", help="output file")
|
||||
ap.add_argument("input", type=str, nargs='+', help="input images")
|
||||
args = ap.parse_args()
|
||||
pack(args.out, args.input)
|
|
@ -0,0 +1,773 @@
|
|||
// example how to set up D3D11 rendering
|
||||
|
||||
// set to 0 to create resizable window
|
||||
#define WINDOW_WIDTH 1280
|
||||
#define WINDOW_HEIGHT 720
|
||||
|
||||
// do you need depth buffer?
|
||||
#define WINDOW_DEPTH 1
|
||||
|
||||
// do you need stencil buffer?
|
||||
#define WINDOW_STENCIL 0
|
||||
|
||||
// use sRGB or MSAA for color buffer
|
||||
// set 0 to disable, or 1 to enable sRGB
|
||||
// typical values for MSAA are 2, 4, 8, 16, ...
|
||||
// when enabled, D3D11 cannot use more modern lower-latency flip swap effect on Windows 8.1/10
|
||||
// instead you can use sRGB/MSAA render target and copy it to non-sRGB window
|
||||
#define WINDOW_SRGB 0
|
||||
#define WINDOW_MSAA 0
|
||||
|
||||
// do you need vsync?
|
||||
#define WINDOW_VSYNC 1
|
||||
|
||||
// keep this enabled when debugging
|
||||
#define USE_DEBUG_MODE 1
|
||||
|
||||
#define COBJMACROS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma comment (lib, "user32.lib")
|
||||
#pragma comment (lib, "dxgi.lib")
|
||||
#pragma comment (lib, "d3d11.lib")
|
||||
#pragma comment (lib, "dxguid.lib")
|
||||
|
||||
#define SAFE_RELEASE(release, obj) if (obj) release##_Release(obj)
|
||||
|
||||
#define LOG_AND_RETURN_ERROR(hr, msg) do \
|
||||
{ \
|
||||
if (FAILED(hr)) \
|
||||
{ \
|
||||
LogWin32Error(hr, msg); \
|
||||
return hr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float x, y;
|
||||
float r, g, b;
|
||||
};
|
||||
|
||||
static const struct Vertex vertices[] =
|
||||
{
|
||||
{ 0.0f, 0.5f, 1.f, 0.f, 0.f },
|
||||
{ 0.5f, -0.5f, 0.f, 1.f, 0.f },
|
||||
{ -0.5f, -0.5f, 0.f, 0.f, 1.f },
|
||||
};
|
||||
|
||||
// You can compile shader to bytecode at build time, this will increase startup
|
||||
// performance and also will avoid dependency on d3dcompiler dll file.
|
||||
// To do so, put the shader source in shader.hlsl file and run these two commands:
|
||||
|
||||
// fxc.exe /nologo /T vs_4_0_level_9_0 /E vs /O3 /WX /Zpc /Ges /Fh d3d11_vshader.h /Vn d3d11_vshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl
|
||||
// fxc.exe /nologo /T ps_4_0_level_9_0 /E ps /O3 /WX /Zpc /Ges /Fh d3d11_pshader.h /Vn d3d11_pshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl
|
||||
|
||||
// then set next setting to 1
|
||||
#define USE_PRECOMPILED_SHADERS 0
|
||||
|
||||
#if USE_PRECOMPILED_SHADERS
|
||||
#include "d3d11_vshader.h"
|
||||
#include "d3d11_pshader.h"
|
||||
#else
|
||||
#pragma comment (lib, "d3dcompiler.lib")
|
||||
static const char d3d11_shader[] =
|
||||
"struct VS_INPUT \n"
|
||||
"{ \n"
|
||||
" float2 pos : POSITION; \n"
|
||||
" float3 col : COLOR0; \n"
|
||||
"}; \n"
|
||||
" \n"
|
||||
"struct PS_INPUT \n"
|
||||
"{ \n"
|
||||
" float4 pos : SV_POSITION; \n"
|
||||
" float3 col : COLOR0; \n"
|
||||
"}; \n"
|
||||
" \n"
|
||||
"PS_INPUT vs(VS_INPUT input) \n"
|
||||
"{ \n"
|
||||
" PS_INPUT output; \n"
|
||||
" output.pos = float4(input.pos.xy, 0.f, 1.f); \n"
|
||||
" output.col = input.col; \n"
|
||||
" return output; \n"
|
||||
"} \n"
|
||||
" \n"
|
||||
"float4 ps(PS_INPUT input) : SV_Target \n"
|
||||
"{ \n"
|
||||
" return float4(input.col, 1.f); \n"
|
||||
"} \n";
|
||||
#endif
|
||||
|
||||
// is window visible?
|
||||
// if this is 0 you can skip rendering part in your code to save time
|
||||
static int render_occluded;
|
||||
|
||||
static IDXGISwapChain* render_swapchain;
|
||||
static ID3D11Device* render_device;
|
||||
static ID3D11DeviceContext* render_context;
|
||||
static ID3D11DeviceContext1* render_context1;
|
||||
static ID3D11RenderTargetView* render_window_rtview;
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
static ID3D11DepthStencilView* render_window_dpview;
|
||||
#endif
|
||||
static HANDLE render_frame_latency_wait;
|
||||
|
||||
static ID3D11RasterizerState* render_raster_state;
|
||||
static ID3D11DepthStencilState* render_depthstencil_state;
|
||||
static ID3D11BlendState* render_blend_state;
|
||||
static ID3D11PixelShader* render_pixel_shader;
|
||||
static ID3D11VertexShader* render_vertex_shader;
|
||||
static ID3D11InputLayout* render_input_layout;
|
||||
static ID3D11Buffer* render_vertex_buffer;
|
||||
|
||||
static void LogWin32Error(DWORD err, const char* msg)
|
||||
{
|
||||
OutputDebugStringA(msg);
|
||||
OutputDebugStringA("!\n");
|
||||
|
||||
LPWSTR str;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
|
||||
err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&str, 0, NULL))
|
||||
{
|
||||
OutputDebugStringW(str);
|
||||
LocalFree(str);
|
||||
}
|
||||
}
|
||||
static void LogWin32LastError(const char* msg)
|
||||
{
|
||||
LogWin32Error(GetLastError(), msg);
|
||||
}
|
||||
|
||||
static HRESULT FatalDeviceLostError()
|
||||
{
|
||||
MessageBoxW(NULL, L"Cannot recreate D3D11 device, it is reset or removed!", L"Error", MB_ICONEXCLAMATION);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// called when device & all d3d resources needs to be released
|
||||
// can happen multiple times (e.g. after device is removed/reset)
|
||||
static void RenderDestroy()
|
||||
{
|
||||
if (render_context)
|
||||
{
|
||||
ID3D11DeviceContext_ClearState(render_context);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(ID3D11Buffer, render_vertex_buffer);
|
||||
SAFE_RELEASE(ID3D11InputLayout, render_input_layout);
|
||||
SAFE_RELEASE(ID3D11VertexShader, render_vertex_shader);
|
||||
SAFE_RELEASE(ID3D11PixelShader, render_pixel_shader);
|
||||
SAFE_RELEASE(ID3D11RasterizerState, render_raster_state);
|
||||
SAFE_RELEASE(ID3D11DepthStencilState, render_depthstencil_state);
|
||||
SAFE_RELEASE(ID3D11BlendState, render_blend_state);
|
||||
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
SAFE_RELEASE(ID3D11DepthStencilView, render_window_dpview);
|
||||
#endif
|
||||
SAFE_RELEASE(ID3D11RenderTargetView, render_window_rtview);
|
||||
SAFE_RELEASE(ID3D11DeviceContext, render_context);
|
||||
SAFE_RELEASE(ID3D11Device, render_device);
|
||||
SAFE_RELEASE(IDXGISwapChain, render_swapchain);
|
||||
|
||||
render_context1 = NULL;
|
||||
render_frame_latency_wait = NULL;
|
||||
}
|
||||
|
||||
// called any time device needs to be created
|
||||
// can happen multiple times (e.g. after device is removed/reset)
|
||||
static HRESULT RenderCreate(HWND wnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
D3D_FEATURE_LEVEL level;
|
||||
|
||||
// device, context
|
||||
{
|
||||
UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
|
||||
#if USE_DEBUG_MODE
|
||||
flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
|
||||
if (FAILED(hr = D3D11CreateDevice(
|
||||
NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, NULL, 0, D3D11_SDK_VERSION,
|
||||
&render_device, &level, &render_context)))
|
||||
{
|
||||
if (FAILED(hr = D3D11CreateDevice(
|
||||
NULL, D3D_DRIVER_TYPE_WARP, NULL, flags, NULL, 0, D3D11_SDK_VERSION,
|
||||
&render_device, &level, &render_context)))
|
||||
{
|
||||
LOG_AND_RETURN_ERROR(hr, "D3D11CreateDevice failed");
|
||||
}
|
||||
}
|
||||
|
||||
hr = ID3D11DeviceContext_QueryInterface(render_context, &IID_ID3D11DeviceContext1, &render_context1);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// using ID3D11DeviceContext1 to discard render target
|
||||
ID3D11DeviceContext_Release(render_context);
|
||||
render_context = (ID3D11DeviceContext*)render_context1;
|
||||
}
|
||||
}
|
||||
|
||||
// swap chain
|
||||
{
|
||||
IDXGIFactory* factory;
|
||||
if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory, &factory)))
|
||||
{
|
||||
LOG_AND_RETURN_ERROR(hr, "CreateDXGIFactory failed");
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC desc =
|
||||
{
|
||||
.BufferDesc =
|
||||
{
|
||||
#if WINDOW_SRGB
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
|
||||
#else
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
#endif
|
||||
.RefreshRate =
|
||||
{
|
||||
.Numerator = 60,
|
||||
.Denominator = 1,
|
||||
},
|
||||
},
|
||||
.SampleDesc =
|
||||
{
|
||||
#if WINDOW_MSAA
|
||||
.Count = WINDOW_MSAA,
|
||||
#else
|
||||
.Count = 1,
|
||||
#endif
|
||||
.Quality = 0,
|
||||
},
|
||||
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
.OutputWindow = wnd,
|
||||
.Windowed = TRUE,
|
||||
};
|
||||
|
||||
#if !WINDOW_SRGB && !WINDOW_MSAA
|
||||
// Windows 10 and up
|
||||
desc.BufferCount = 2;
|
||||
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
|
||||
if (FAILED(IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain)))
|
||||
{
|
||||
// Windows 8.1
|
||||
desc.BufferCount = 2;
|
||||
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
||||
desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
|
||||
hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain);
|
||||
}
|
||||
#else
|
||||
hr = E_FAIL;
|
||||
#endif
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// older Windows
|
||||
desc.BufferCount = 1;
|
||||
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
desc.Flags = 0;
|
||||
hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain);
|
||||
LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::CreateSwapChain failed");
|
||||
}
|
||||
|
||||
if (desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)
|
||||
{
|
||||
IDXGISwapChain2* swapchain2;
|
||||
if (SUCCEEDED(hr = IDXGISwapChain_QueryInterface(render_swapchain, &IID_IDXGISwapChain2, &swapchain2)))
|
||||
{
|
||||
// using IDXGISwapChain2 for frame latency control
|
||||
render_frame_latency_wait = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
|
||||
IDXGISwapChain2_Release(swapchain2);
|
||||
}
|
||||
}
|
||||
|
||||
hr = IDXGIFactory_MakeWindowAssociation(factory, wnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
|
||||
LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::MakeWindowAssociation failed");
|
||||
|
||||
IDXGIFactory_Release(factory);
|
||||
}
|
||||
|
||||
// rasterizer state
|
||||
{
|
||||
D3D11_RASTERIZER_DESC desc =
|
||||
{
|
||||
.FillMode = D3D11_FILL_SOLID,
|
||||
.CullMode = D3D11_CULL_BACK,
|
||||
.FrontCounterClockwise = FALSE,
|
||||
.DepthBias = 0,
|
||||
.DepthBiasClamp = 0,
|
||||
.SlopeScaledDepthBias = 0.f,
|
||||
.DepthClipEnable = TRUE,
|
||||
.ScissorEnable = FALSE,
|
||||
.MultisampleEnable = WINDOW_MSAA > 0,
|
||||
.AntialiasedLineEnable = FALSE,
|
||||
};
|
||||
|
||||
hr = ID3D11Device_CreateRasterizerState(render_device, &desc, &render_raster_state);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRasterizerState failed");
|
||||
}
|
||||
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
// depth & stencil state
|
||||
{
|
||||
D3D11_DEPTH_STENCIL_DESC desc =
|
||||
{
|
||||
.DepthEnable = WINDOW_DEPTH ? TRUE : FALSE,
|
||||
.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL,
|
||||
.DepthFunc = D3D11_COMPARISON_LESS,
|
||||
.StencilEnable = FALSE, // if you need stencil, set up it here
|
||||
.StencilReadMask = 0,
|
||||
.StencilWriteMask = 0,
|
||||
};
|
||||
|
||||
hr = ID3D11Device_CreateDepthStencilState(render_device, &desc, &render_depthstencil_state);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilState failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
// blend state
|
||||
{
|
||||
D3D11_BLEND_DESC desc =
|
||||
{
|
||||
.AlphaToCoverageEnable = FALSE,
|
||||
.IndependentBlendEnable = FALSE,
|
||||
.RenderTarget =
|
||||
{
|
||||
{
|
||||
.BlendEnable = FALSE,
|
||||
.SrcBlend = D3D11_BLEND_SRC_ALPHA,
|
||||
.DestBlend = D3D11_BLEND_INV_SRC_ALPHA,
|
||||
.BlendOp = D3D11_BLEND_OP_ADD,
|
||||
.SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA,
|
||||
.DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA,
|
||||
.BlendOpAlpha = D3D11_BLEND_OP_ADD,
|
||||
.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
hr = ID3D11Device_CreateBlendState(render_device, &desc, &render_blend_state);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBlendState failed");
|
||||
}
|
||||
|
||||
#if !USE_PRECOMPILED_SHADERS
|
||||
UINT shader_flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS
|
||||
#if USE_DEBUG_MODE
|
||||
| D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
|
||||
#else
|
||||
| D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// vertex shader & input layout
|
||||
{
|
||||
D3D11_INPUT_ELEMENT_DESC layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(struct Vertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(struct Vertex, r), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
|
||||
ID3DBlob* code = NULL;
|
||||
const void* vshader;
|
||||
size_t vshader_size;
|
||||
|
||||
#if USE_PRECOMPILED_SHADERS
|
||||
vshader = d3d11_vshader;
|
||||
vshader_size = sizeof(d3d11_vshader);
|
||||
#else
|
||||
ID3DBlob* error;
|
||||
hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL,
|
||||
"vs", "vs_4_0_level_9_0", shader_flags, 0, &code, &error);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
const void* data = ID3D10Blob_GetBufferPointer(error);
|
||||
size_t size = ID3D10Blob_GetBufferSize(error);
|
||||
char msg[1024];
|
||||
lstrcpynA(msg, data, (int)size);
|
||||
msg[size] = 0;
|
||||
OutputDebugStringA(msg);
|
||||
ID3D10Blob_Release(error);
|
||||
LOG_AND_RETURN_ERROR(hr, "D3DCompile vs failed");
|
||||
}
|
||||
vshader = ID3D10Blob_GetBufferPointer(code);
|
||||
vshader_size = ID3D10Blob_GetBufferSize(code);
|
||||
#endif
|
||||
|
||||
hr = ID3D11Device_CreateVertexShader(render_device, vshader, vshader_size, NULL, &render_vertex_shader);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SAFE_RELEASE(ID3D10Blob, code);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateVertexShader failed");
|
||||
}
|
||||
|
||||
hr = ID3D11Device_CreateInputLayout(render_device, layout, _countof(layout),
|
||||
vshader, vshader_size, &render_input_layout);
|
||||
SAFE_RELEASE(ID3D10Blob, code);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateInputLayout failed");
|
||||
}
|
||||
|
||||
// pixel shader
|
||||
{
|
||||
ID3DBlob* code = NULL;
|
||||
const void* pshader;
|
||||
size_t pshader_size;
|
||||
|
||||
#if USE_PRECOMPILED_SHADERS
|
||||
pshader = d3d11_pshader;
|
||||
pshader_size = sizeof(d3d11_pshader);
|
||||
#else
|
||||
ID3DBlob* error;
|
||||
hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL,
|
||||
"ps", "ps_4_0_level_9_0", shader_flags, 0, &code, &error);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
const void* data = ID3D10Blob_GetBufferPointer(error);
|
||||
size_t size = ID3D10Blob_GetBufferSize(error);
|
||||
char msg[1024];
|
||||
lstrcpynA(msg, data, (int)size);
|
||||
msg[size] = 0;
|
||||
OutputDebugStringA(msg);
|
||||
ID3D10Blob_Release(error);
|
||||
LOG_AND_RETURN_ERROR(hr, "D3DCompile ps failed");
|
||||
}
|
||||
pshader = ID3D10Blob_GetBufferPointer(code);
|
||||
pshader_size = ID3D10Blob_GetBufferSize(code);
|
||||
#endif
|
||||
|
||||
hr = ID3D11Device_CreatePixelShader(render_device, pshader, pshader_size, NULL, &render_pixel_shader);
|
||||
SAFE_RELEASE(ID3D10Blob, code);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreatePixelShader failed");
|
||||
}
|
||||
|
||||
// vertex buffer
|
||||
{
|
||||
D3D11_BUFFER_DESC desc =
|
||||
{
|
||||
.ByteWidth = sizeof(vertices),
|
||||
.Usage = D3D11_USAGE_IMMUTABLE,
|
||||
.BindFlags = D3D11_BIND_VERTEX_BUFFER,
|
||||
};
|
||||
|
||||
D3D11_SUBRESOURCE_DATA data =
|
||||
{
|
||||
.pSysMem = vertices,
|
||||
};
|
||||
|
||||
hr = ID3D11Device_CreateBuffer(render_device, &desc, &data, &render_vertex_buffer);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBuffer failed");
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// called when device is reset or removed, recreate it
|
||||
static HRESULT RecreateDevice(HWND wnd)
|
||||
{
|
||||
RenderDestroy();
|
||||
HRESULT hr = RenderCreate(wnd);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
RenderDestroy();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// called when window is resized
|
||||
static HRESULT RenderResize(HWND wnd, int width, int height)
|
||||
{
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (render_window_rtview)
|
||||
{
|
||||
ID3D11DeviceContext_OMSetRenderTargets(render_context, 0, NULL, NULL);
|
||||
ID3D11RenderTargetView_Release(render_window_rtview);
|
||||
render_window_rtview = NULL;
|
||||
}
|
||||
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
if (render_window_dpview)
|
||||
{
|
||||
ID3D11DepthStencilView_Release(render_window_dpview);
|
||||
render_window_dpview = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
UINT flags = render_frame_latency_wait ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
|
||||
HRESULT hr = IDXGISwapChain_ResizeBuffers(render_swapchain, 0, width, height, DXGI_FORMAT_UNKNOWN, flags);
|
||||
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR)
|
||||
{
|
||||
if (FAILED(RecreateDevice(wnd)))
|
||||
{
|
||||
return FatalDeviceLostError();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::ResizeBuffers failed");
|
||||
}
|
||||
|
||||
ID3D11Texture2D* window_buffer;
|
||||
hr = IDXGISwapChain_GetBuffer(render_swapchain, 0, &IID_ID3D11Texture2D, &window_buffer);
|
||||
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::GetBuffer failed");
|
||||
|
||||
hr = ID3D11Device_CreateRenderTargetView(render_device, (ID3D11Resource*)window_buffer, NULL, &render_window_rtview);
|
||||
ID3D11Texture2D_Release(window_buffer);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRenderTargetView failed");
|
||||
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc =
|
||||
{
|
||||
.Width = width,
|
||||
.Height = height,
|
||||
.MipLevels = 1,
|
||||
.ArraySize = 1,
|
||||
.Format = (WINDOW_STENCIL || ID3D11Device_GetFeatureLevel(render_device) < D3D_FEATURE_LEVEL_10_0)
|
||||
? DXGI_FORMAT_D24_UNORM_S8_UINT : DXGI_FORMAT_D32_FLOAT,
|
||||
.SampleDesc =
|
||||
{
|
||||
#if WINDOW_MSAA
|
||||
.Count = WINDOW_MSAA,
|
||||
#else
|
||||
.Count = 1,
|
||||
#endif
|
||||
.Quality = 0,
|
||||
},
|
||||
.Usage = D3D11_USAGE_DEFAULT,
|
||||
.BindFlags = D3D11_BIND_DEPTH_STENCIL,
|
||||
};
|
||||
|
||||
ID3D11Texture2D* depth_stencil;
|
||||
hr = ID3D11Device_CreateTexture2D(render_device, &desc, NULL, &depth_stencil);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateTexture2D failed");
|
||||
|
||||
hr = ID3D11Device_CreateDepthStencilView(render_device, (ID3D11Resource*)depth_stencil, NULL, &render_window_dpview);
|
||||
ID3D11Texture2D_Release(depth_stencil);
|
||||
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilView failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
D3D11_VIEWPORT viewport =
|
||||
{
|
||||
.TopLeftX = 0.f,
|
||||
.TopLeftY = 0.f,
|
||||
.Width = (float)width,
|
||||
.Height = (float)height,
|
||||
.MinDepth = 0.f,
|
||||
.MaxDepth = 1.f,
|
||||
};
|
||||
ID3D11DeviceContext_RSSetViewports(render_context, 1, &viewport);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// called at end of frame
|
||||
static HRESULT RenderPresent(HWND wnd)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if (render_occluded)
|
||||
{
|
||||
hr = IDXGISwapChain_Present(render_swapchain, 0, DXGI_PRESENT_TEST);
|
||||
if (SUCCEEDED(hr) && hr != DXGI_STATUS_OCCLUDED)
|
||||
{
|
||||
// DXGI window is back to normal, resuming rendering
|
||||
render_occluded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!render_occluded)
|
||||
{
|
||||
hr = IDXGISwapChain_Present(render_swapchain, WINDOW_VSYNC, 0);
|
||||
}
|
||||
|
||||
if (hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED)
|
||||
{
|
||||
if (FAILED(RecreateDevice(wnd)))
|
||||
{
|
||||
return FatalDeviceLostError();
|
||||
}
|
||||
|
||||
RECT rect;
|
||||
if (!GetClientRect(wnd, &rect))
|
||||
{
|
||||
LogWin32LastError("GetClientRect failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderResize(wnd, rect.right - rect.left, rect.bottom - rect.top);
|
||||
}
|
||||
}
|
||||
else if (hr == DXGI_STATUS_OCCLUDED)
|
||||
{
|
||||
// DXGI window is occluded, skipping rendering
|
||||
render_occluded = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::Present failed");
|
||||
}
|
||||
|
||||
if (render_occluded)
|
||||
{
|
||||
Sleep(10);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (render_context1)
|
||||
{
|
||||
ID3D11DeviceContext1_DiscardView(render_context1, (ID3D11View*)render_window_rtview);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// this is where rendering happens
|
||||
static void RenderFrame()
|
||||
{
|
||||
if (!render_occluded)
|
||||
{
|
||||
if (render_frame_latency_wait)
|
||||
{
|
||||
WaitForSingleObjectEx(render_frame_latency_wait, INFINITE, TRUE);
|
||||
}
|
||||
|
||||
#if WINDOW_DEPTH || WINDOW_STENCIL
|
||||
ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, render_window_dpview);
|
||||
ID3D11DeviceContext_OMSetDepthStencilState(render_context, render_depthstencil_state, 0);
|
||||
ID3D11DeviceContext_ClearDepthStencilView(render_context, render_window_dpview, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
|
||||
#else
|
||||
ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, NULL);
|
||||
#endif
|
||||
|
||||
// clear background
|
||||
FLOAT clear_color[] = { 100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f };
|
||||
ID3D11DeviceContext_ClearRenderTargetView(render_context, render_window_rtview, clear_color);
|
||||
|
||||
// draw a triangle
|
||||
const UINT stride = sizeof(struct Vertex);
|
||||
const UINT offset = 0;
|
||||
ID3D11DeviceContext_IASetInputLayout(render_context, render_input_layout);
|
||||
ID3D11DeviceContext_IASetVertexBuffers(render_context, 0, 1, &render_vertex_buffer, &stride, &offset);
|
||||
ID3D11DeviceContext_IASetPrimitiveTopology(render_context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ID3D11DeviceContext_VSSetShader(render_context, render_vertex_shader, NULL, 0);
|
||||
ID3D11DeviceContext_PSSetShader(render_context, render_pixel_shader, NULL, 0);
|
||||
ID3D11DeviceContext_RSSetState(render_context, render_raster_state);
|
||||
ID3D11DeviceContext_OMSetBlendState(render_context, render_blend_state, NULL, ~0U);
|
||||
ID3D11DeviceContext_Draw(render_context, _countof(vertices), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_CREATE:
|
||||
if (FAILED(RenderCreate(wnd)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DESTROY:
|
||||
RenderDestroy(wnd);
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_SIZE:
|
||||
if (FAILED(RenderResize(wnd, LOWORD(lparam), HIWORD(lparam))))
|
||||
{
|
||||
DestroyWindow(wnd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcW(wnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show)
|
||||
{
|
||||
WNDCLASSEXW wc =
|
||||
{
|
||||
.cbSize = sizeof(wc),
|
||||
.lpfnWndProc = WindowProc,
|
||||
.hInstance = instance,
|
||||
.hIcon = LoadIconA(NULL, IDI_APPLICATION),
|
||||
.hCursor = LoadCursorA(NULL, IDC_ARROW),
|
||||
.lpszClassName = L"d3d11_window_class",
|
||||
};
|
||||
|
||||
if (!RegisterClassExW(&wc))
|
||||
{
|
||||
LogWin32LastError("RegisterClassEx failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
int width = CW_USEDEFAULT;
|
||||
int height = CW_USEDEFAULT;
|
||||
|
||||
DWORD exstyle = WS_EX_APPWINDOW;
|
||||
DWORD style = WS_OVERLAPPEDWINDOW;
|
||||
|
||||
if (WINDOW_WIDTH && WINDOW_HEIGHT)
|
||||
{
|
||||
style &= ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
|
||||
|
||||
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
|
||||
if (!AdjustWindowRectEx(&rect, style, FALSE, exstyle))
|
||||
{
|
||||
LogWin32LastError("AdjustWindowRectEx failed");
|
||||
style = WS_OVERLAPPEDWINDOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = rect.right - rect.left;
|
||||
height = rect.bottom - rect.top;
|
||||
}
|
||||
}
|
||||
|
||||
HWND wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"D3D11 Window",
|
||||
style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height,
|
||||
NULL, NULL, wc.hInstance, NULL);
|
||||
if (!wnd)
|
||||
{
|
||||
LogWin32LastError("CreateWindow failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
MSG msg;
|
||||
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (msg.message == WM_QUIT)
|
||||
{
|
||||
break;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderFrame();
|
||||
|
||||
if (FAILED(RenderPresent(wnd)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,561 @@
|
|||
// example how to set up modern OpenGL context with fallback to legacy context
|
||||
|
||||
// set to 0 to create resizable window
|
||||
#define WINDOW_WIDTH 1280
|
||||
#define WINDOW_HEIGHT 720
|
||||
|
||||
// do you need depth buffer?
|
||||
#define WINDOW_DEPTH 1
|
||||
|
||||
// do you need stencil buffer?
|
||||
#define WINDOW_STENCIL 0
|
||||
|
||||
// use sRGB for color buffer
|
||||
#define WINDOW_SRGB 1
|
||||
|
||||
// do you need multisampling?
|
||||
// to disable set to 0, to enable set to 2, 4, 8, 16, ...
|
||||
#define WINDOW_MSAA 4
|
||||
|
||||
// do you need vsync?
|
||||
#define WINDOW_VSYNC 1
|
||||
|
||||
// keep this enabled when debugging
|
||||
#define USE_DEBUG_MODE 1
|
||||
|
||||
// replace this with your favorite assert() implementation
|
||||
#include <assert.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
|
||||
#include "glext.h" // download from https://www.opengl.org/registry/api/GL/glext.h
|
||||
#include "wglext.h" // download from https://www.opengl.org/registry/api/GL/wglext.h
|
||||
|
||||
// https://www.opengl.org/registry/specs/ARB/wgl_extensions_string.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/wgl_create_context.txt
|
||||
// https://www.opengl.org/registry/specs/EXT/wgl_swap_control.txt
|
||||
// https://www.opengl.org/registry/specs/EXT/wgl_swap_control_tear.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/multisample.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/debug_output.txt
|
||||
|
||||
#pragma comment (lib, "gdi32.lib")
|
||||
#pragma comment (lib, "user32.lib")
|
||||
#pragma comment (lib, "opengl32.lib")
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
#define GL_CHECK(x) do \
|
||||
{ \
|
||||
x; \
|
||||
GLenum err = glGetError(); \
|
||||
assert(err == GL_NO_ERROR); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define GL_CHECK(x) x
|
||||
#endif
|
||||
|
||||
// called after context is set up (only once)
|
||||
// for example, load GL extensions here & set up GL state
|
||||
static void RenderInit(void)
|
||||
{
|
||||
GL_CHECK( glClearColor(100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f) );
|
||||
}
|
||||
|
||||
// called before render is destroyed
|
||||
static void RenderDone(void)
|
||||
{
|
||||
}
|
||||
|
||||
// called when window is resized
|
||||
static void RenderResize(unsigned width, unsigned height)
|
||||
{
|
||||
GL_CHECK( glViewport(0, 0, width, height) );
|
||||
}
|
||||
|
||||
// called every frame before swapping buffers
|
||||
static void RenderFrame(void)
|
||||
{
|
||||
GL_CHECK( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) );
|
||||
|
||||
// use legacy GL for drawing to display a triangle
|
||||
glBegin(GL_TRIANGLES);
|
||||
glColor3f(1.f, 0.f, 0.f);
|
||||
glVertex2f(0.f, 0.5f);
|
||||
glColor3f(0.f, 1.f, 0.f);
|
||||
glVertex2f(0.5f, -0.5f);
|
||||
glColor3f(0.f, 0.f, 1.f);
|
||||
glVertex2f(-0.5f, -0.5f);
|
||||
GL_CHECK( glEnd() );
|
||||
}
|
||||
|
||||
static void LogWin32Error(const char* msg)
|
||||
{
|
||||
OutputDebugStringA(msg);
|
||||
OutputDebugStringA("!\n");
|
||||
|
||||
DWORD err = GetLastError();
|
||||
|
||||
LPWSTR str;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
|
||||
err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&str, 0, NULL))
|
||||
{
|
||||
OutputDebugStringW(str);
|
||||
LocalFree(str);
|
||||
}
|
||||
}
|
||||
|
||||
static HGLRC CreateOldOpenGLContext(HDC dc)
|
||||
{
|
||||
PIXELFORMATDESCRIPTOR pfd =
|
||||
{
|
||||
.nSize = sizeof(pfd),
|
||||
.nVersion = 1,
|
||||
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
|
||||
PFD_DOUBLEBUFFER | (WINDOW_DEPTH ? 0 : PFD_DEPTH_DONTCARE),
|
||||
.iPixelType = PFD_TYPE_RGBA,
|
||||
.cColorBits = 24,
|
||||
.cDepthBits = (WINDOW_DEPTH ? 24 : 0),
|
||||
.cStencilBits = (WINDOW_STENCIL ? 8 : 0),
|
||||
};
|
||||
|
||||
int format = ChoosePixelFormat(dc, &pfd);
|
||||
if (!format)
|
||||
{
|
||||
LogWin32Error("ChoosePixelFormat failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!DescribePixelFormat(dc, format, sizeof(pfd), &pfd))
|
||||
{
|
||||
LogWin32Error("DescribePixelFormat failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SetPixelFormat(dc, format, &pfd))
|
||||
{
|
||||
LogWin32Error("SetPixelFormat failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HGLRC rc = wglCreateContext(dc);
|
||||
if (!rc)
|
||||
{
|
||||
LogWin32Error("wglCreateContext failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!wglMakeCurrent(dc, rc))
|
||||
{
|
||||
LogWin32Error("wglMakeCurrent failed");
|
||||
wglDeleteContext(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
static void APIENTRY OpenGLDebugCallback(
|
||||
GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar *message, const void* user)
|
||||
{
|
||||
OutputDebugStringA(message);
|
||||
OutputDebugStringA("\n");
|
||||
if (severity >= GL_DEBUG_SEVERITY_LOW_ARB && severity <= GL_DEBUG_SEVERITY_HIGH_ARB)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int StringsAreEqual(const char* src, const char* dst, size_t dstlen)
|
||||
{
|
||||
while (*src && dstlen-- && *dst)
|
||||
{
|
||||
if (*src++ != *dst++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (dstlen && *src == *dst) || (!dstlen && *src == 0);
|
||||
}
|
||||
|
||||
static HGLRC CreateOpenGLContext(HDC dc)
|
||||
{
|
||||
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
|
||||
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
|
||||
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
|
||||
int wgl_ARB_multisample = 0;
|
||||
int wgl_ARB_framebuffer_sRGB = 0;
|
||||
int wgl_EXT_swap_control_tear = 0;
|
||||
|
||||
HWND wnd = CreateWindowExW(0, L"STATIC", L"Dummy Window", WS_OVERLAPPED,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
|
||||
if (wnd)
|
||||
{
|
||||
HDC dc = GetDC(wnd);
|
||||
if (dc)
|
||||
{
|
||||
HGLRC rc = CreateOldOpenGLContext(dc);
|
||||
if (rc)
|
||||
{
|
||||
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
|
||||
(void*)wglGetProcAddress("wglGetExtensionsStringARB");
|
||||
if (wglGetExtensionsStringARB != (void*)0 &&
|
||||
wglGetExtensionsStringARB != (void*)1 &&
|
||||
wglGetExtensionsStringARB != (void*)2 &&
|
||||
wglGetExtensionsStringARB != (void*)3 &&
|
||||
wglGetExtensionsStringARB != (void*)-1)
|
||||
{
|
||||
const char* ext = wglGetExtensionsStringARB(dc);
|
||||
if (ext)
|
||||
{
|
||||
const char* start = ext;
|
||||
for (;;)
|
||||
{
|
||||
while (*ext != 0 && *ext != ' ')
|
||||
{
|
||||
ext++;
|
||||
}
|
||||
|
||||
size_t length = ext - start;
|
||||
if (StringsAreEqual("WGL_ARB_pixel_format", start, length))
|
||||
{
|
||||
wglChoosePixelFormatARB = (void*)wglGetProcAddress("wglChoosePixelFormatARB");
|
||||
}
|
||||
else if (StringsAreEqual("WGL_ARB_create_context", start, length))
|
||||
{
|
||||
wglCreateContextAttribsARB = (void*)wglGetProcAddress("wglCreateContextAttribsARB");
|
||||
}
|
||||
else if (StringsAreEqual("WGL_EXT_swap_control", start, length))
|
||||
{
|
||||
wglSwapIntervalEXT = (void*)wglGetProcAddress("wglSwapIntervalEXT");
|
||||
}
|
||||
else if (StringsAreEqual("WGL_ARB_framebuffer_sRGB", start, length))
|
||||
{
|
||||
wgl_ARB_framebuffer_sRGB = 1;
|
||||
}
|
||||
else if (StringsAreEqual("WGL_ARB_multisample", start, length))
|
||||
{
|
||||
wgl_ARB_multisample = 1;
|
||||
}
|
||||
else if (StringsAreEqual("WGL_ARB_framebuffer_sRGB", start, length))
|
||||
{
|
||||
wgl_ARB_framebuffer_sRGB = 1;
|
||||
}
|
||||
else if (StringsAreEqual("WGL_EXT_swap_control_tear", start, length))
|
||||
{
|
||||
wgl_EXT_swap_control_tear = 1;
|
||||
}
|
||||
|
||||
if (*ext == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ext++;
|
||||
start = ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
wglDeleteContext(rc);
|
||||
}
|
||||
ReleaseDC(wnd, dc);
|
||||
}
|
||||
DestroyWindow(wnd);
|
||||
}
|
||||
|
||||
HGLRC rc = NULL;
|
||||
|
||||
if (wglCreateContextAttribsARB && wglChoosePixelFormatARB)
|
||||
{
|
||||
int attrib[32];
|
||||
int* p = attrib;
|
||||
|
||||
*p++ = WGL_DRAW_TO_WINDOW_ARB; *p++ = GL_TRUE;
|
||||
*p++ = WGL_ACCELERATION_ARB; *p++ = WGL_FULL_ACCELERATION_ARB;
|
||||
*p++ = WGL_SUPPORT_OPENGL_ARB; *p++ = GL_TRUE;
|
||||
*p++ = WGL_DOUBLE_BUFFER_ARB; *p++ = GL_TRUE;
|
||||
*p++ = WGL_PIXEL_TYPE_ARB; *p++ = WGL_TYPE_RGBA_ARB;
|
||||
*p++ = WGL_COLOR_BITS_ARB; *p++ = 24;
|
||||
|
||||
if (WINDOW_DEPTH)
|
||||
{
|
||||
*p++ = WGL_DEPTH_BITS_ARB;
|
||||
*p++ = 24;
|
||||
}
|
||||
if (WINDOW_STENCIL)
|
||||
{
|
||||
*p++ = WGL_STENCIL_BITS_ARB;
|
||||
*p++ = 8;
|
||||
}
|
||||
if (WINDOW_SRGB && wgl_ARB_framebuffer_sRGB)
|
||||
{
|
||||
*p++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
|
||||
*p++ = GL_TRUE;
|
||||
}
|
||||
if (WINDOW_MSAA && wgl_ARB_multisample)
|
||||
{
|
||||
*p++ = WGL_SAMPLE_BUFFERS_ARB;
|
||||
*p++ = 1;
|
||||
*p++ = WGL_SAMPLES_ARB;
|
||||
*p++ = WINDOW_MSAA;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
int format;
|
||||
UINT formats;
|
||||
if (!wglChoosePixelFormatARB(dc, attrib, NULL, 1, &format, &formats) || formats == 0)
|
||||
{
|
||||
LogWin32Error("wglChoosePixelFormatARB failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
PIXELFORMATDESCRIPTOR pfd =
|
||||
{
|
||||
.nSize = sizeof(pfd),
|
||||
};
|
||||
|
||||
if (!DescribePixelFormat(dc, format, sizeof(pfd), &pfd))
|
||||
{
|
||||
LogWin32Error("DescribePixelFormat failed");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SetPixelFormat(dc, format, &pfd))
|
||||
{
|
||||
LogWin32Error("SetPixelFormat failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
int ctx[] =
|
||||
{
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
|
||||
#if USE_DEBUG_MODE
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
||||
#endif
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0,
|
||||
};
|
||||
|
||||
rc = wglCreateContextAttribsARB(dc, NULL, ctx);
|
||||
if (!rc)
|
||||
{
|
||||
LogWin32Error("wglCreateContextAttribsARB failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!wglMakeCurrent(dc, rc))
|
||||
{
|
||||
LogWin32Error("wglMakeCurrent failed");
|
||||
wglDeleteContext(rc);
|
||||
rc = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WINDOW_MSAA && wgl_ARB_multisample)
|
||||
{
|
||||
GL_CHECK(glEnable(GL_MULTISAMPLE_ARB));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
OutputDebugStringA("Failed to create modern OpenGL context, retrying with legacy context!\n");
|
||||
rc = CreateOldOpenGLContext(dc);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
{
|
||||
if (WINDOW_VSYNC && wglSwapIntervalEXT)
|
||||
{
|
||||
if (!wglSwapIntervalEXT(wgl_EXT_swap_control_tear ? -1 : 1))
|
||||
{
|
||||
LogWin32Error("wglSwapIntervalEXT failed");
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
const GLubyte* ext;
|
||||
GL_CHECK( ext = glGetString(GL_EXTENSIONS) );
|
||||
if (ext)
|
||||
{
|
||||
const GLubyte* start = ext;
|
||||
for (;;)
|
||||
{
|
||||
while (*ext != 0 && *ext != ' ')
|
||||
{
|
||||
ext++;
|
||||
}
|
||||
|
||||
size_t length = ext - start;
|
||||
if (StringsAreEqual("GL_ARB_debug_output", (const char*)start, length))
|
||||
{
|
||||
PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB =
|
||||
(void*)wglGetProcAddress("glDebugMessageCallbackARB");
|
||||
GL_CHECK( glDebugMessageCallbackARB(OpenGLDebugCallback, NULL) );
|
||||
GL_CHECK( glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) );
|
||||
break;
|
||||
}
|
||||
|
||||
if (*ext == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ext++;
|
||||
start = ext;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct Win32Context
|
||||
{
|
||||
HDC dc;
|
||||
HGLRC rc;
|
||||
};
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
struct Win32Context* ctx = (void*)GetWindowLongPtr(wnd, GWLP_USERDATA);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_CREATE:
|
||||
ctx = ((CREATESTRUCTW*)lparam)->lpCreateParams;
|
||||
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)ctx);
|
||||
|
||||
if (!(ctx->dc = GetDC(wnd)))
|
||||
{
|
||||
LogWin32Error("GetDC failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(ctx->rc = CreateOpenGLContext(ctx->dc)))
|
||||
{
|
||||
ReleaseDC(wnd, ctx->dc);
|
||||
return -1;
|
||||
}
|
||||
RenderInit();
|
||||
return 0;
|
||||
|
||||
case WM_DESTROY:
|
||||
if (ctx->rc)
|
||||
{
|
||||
RenderDone();
|
||||
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
wglDeleteContext(ctx->rc);
|
||||
}
|
||||
if (ctx->dc)
|
||||
{
|
||||
ReleaseDC(wnd, ctx->dc);
|
||||
}
|
||||
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_SIZE:
|
||||
RenderResize(LOWORD(lparam), HIWORD(lparam));
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcW(wnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show)
|
||||
{
|
||||
WNDCLASSEXW wc =
|
||||
{
|
||||
.cbSize = sizeof(wc),
|
||||
.lpfnWndProc = WindowProc,
|
||||
.hInstance = instance,
|
||||
.hIcon = LoadIconA(NULL, IDI_APPLICATION),
|
||||
.hCursor = LoadCursorA(NULL, IDC_ARROW),
|
||||
.lpszClassName = L"opengl_window_class",
|
||||
};
|
||||
|
||||
if (!RegisterClassExW(&wc))
|
||||
{
|
||||
LogWin32Error("RegisterClassEx failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
int width = CW_USEDEFAULT;
|
||||
int height = CW_USEDEFAULT;
|
||||
|
||||
DWORD exstyle = WS_EX_APPWINDOW;
|
||||
DWORD style = WS_OVERLAPPEDWINDOW;
|
||||
|
||||
if (WINDOW_WIDTH && WINDOW_HEIGHT)
|
||||
{
|
||||
style &= ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
|
||||
|
||||
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
|
||||
if (!AdjustWindowRectEx(&rect, style, FALSE, exstyle))
|
||||
{
|
||||
LogWin32Error("AdjustWindowRectEx failed");
|
||||
style = WS_OVERLAPPEDWINDOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = rect.right - rect.left;
|
||||
height = rect.bottom - rect.top;
|
||||
}
|
||||
}
|
||||
|
||||
struct Win32Context ctx;
|
||||
HWND wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"OpenGL Window",
|
||||
style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height,
|
||||
NULL, NULL, wc.hInstance, &ctx);
|
||||
if (!wnd)
|
||||
{
|
||||
LogWin32Error("CreateWindow failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
MSG msg;
|
||||
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (msg.message == WM_QUIT)
|
||||
{
|
||||
break;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderFrame();
|
||||
|
||||
if (!SwapBuffers(ctx.dc))
|
||||
{
|
||||
LogWin32Error("SwapBuffers failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
// example how to set up modern OpenGL context with fallback to legacy context
|
||||
// compile: gcc xlib_opengl.c -lX11 -lGL -o xlib_opengl
|
||||
|
||||
// set to 0 to create resizable window
|
||||
#define WINDOW_WIDTH 1280
|
||||
#define WINDOW_HEIGHT 720
|
||||
|
||||
// do you need depth buffer?
|
||||
#define WINDOW_DEPTH 1
|
||||
|
||||
// do you need stencil buffer?
|
||||
#define WINDOW_STENCIL 0
|
||||
|
||||
// use sRGB for color buffer
|
||||
#define WINDOW_SRGB 1
|
||||
|
||||
// do you need multisampling?
|
||||
// to disable set to 0, to enable set to 2, 4, 8, 16, ...
|
||||
#define WINDOW_MSAA 4
|
||||
|
||||
// do you need vsync?
|
||||
#define WINDOW_VSYNC 1
|
||||
|
||||
// keep this enabled when debugging
|
||||
#define USE_DEBUG_MODE 1
|
||||
|
||||
// replace this with your favorite assert() implementation
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
#include "glext.h" // download from https://www.opengl.org/registry/api/GL/glext.h
|
||||
#include "glxext.h" // download from https://www.opengl.org/registry/api/GL/glxext.h
|
||||
|
||||
// https://www.opengl.org/registry/specs/ARB/glx_create_context.txt
|
||||
// https://cgit.freedesktop.org/mesa/mesa/plain/docs/specs/MESA_swap_control.spec
|
||||
// https://www.opengl.org/registry/specs/SGI/swap_control.txt
|
||||
// https://www.opengl.org/registry/specs/EXT/swap_control.txt
|
||||
// https://www.opengl.org/registry/specs/EXT/glx_swap_control_tear.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/multisample.txt
|
||||
// https://www.opengl.org/registry/specs/ARB/debug_output.txt
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
#define GL_CHECK(x) do \
|
||||
{ \
|
||||
x; \
|
||||
GLenum err = glGetError(); \
|
||||
assert(err == GL_NO_ERROR); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define GL_CHECK(x) x
|
||||
#endif
|
||||
|
||||
// called after context is set up (only once)
|
||||
// for example, load GL extensions here & set up GL state
|
||||
static void RenderInit(void)
|
||||
{
|
||||
GL_CHECK( glClearColor(100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f) );
|
||||
}
|
||||
|
||||
// called before render is destroyed
|
||||
static void RenderDone(void)
|
||||
{
|
||||
}
|
||||
|
||||
// called when window is resized
|
||||
static void RenderResize(unsigned width, unsigned height)
|
||||
{
|
||||
GL_CHECK( glViewport(0, 0, width, height) );
|
||||
}
|
||||
|
||||
// called every frame before swapping buffers
|
||||
static void RenderFrame(void)
|
||||
{
|
||||
GL_CHECK( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) );
|
||||
|
||||
// use legacy GL for drawing to display a triangle
|
||||
glBegin(GL_TRIANGLES);
|
||||
glColor3f(1.f, 0.f, 0.f);
|
||||
glVertex2f(0.f, 0.5f);
|
||||
glColor3f(0.f, 1.f, 0.f);
|
||||
glVertex2f(0.5f, -0.5f);
|
||||
glColor3f(0.f, 0.f, 1.f);
|
||||
glVertex2f(-0.5f, -0.5f);
|
||||
GL_CHECK( glEnd() );
|
||||
}
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
static void APIENTRY OpenGLDebugCallback(
|
||||
GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar *message, const void* user)
|
||||
{
|
||||
fprintf(stderr, "%s\n", message);
|
||||
if (severity >= GL_DEBUG_SEVERITY_LOW_ARB && severity <= GL_DEBUG_SEVERITY_HIGH_ARB)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int StringsAreEqual(const char* src, const char* dst, size_t dstlen)
|
||||
{
|
||||
while (*src && dstlen-- && *dst)
|
||||
{
|
||||
if (*src++ != *dst++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (dstlen && *src == *dst) || (!dstlen && *src == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Display* display = XOpenDisplay(NULL);
|
||||
if (!display)
|
||||
{
|
||||
fprintf(stderr, "XOpenDisplay failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
int glx_ARB_create_context = 0;
|
||||
int glx_ARB_multisample = 0;
|
||||
int glx_ARB_framebuffer_sRGB = 0;
|
||||
int glx_SGI_swap_control = 0;
|
||||
int glx_MESA_swap_control = 0;
|
||||
int glx_EXT_swap_control = 0;
|
||||
int glx_EXT_swap_control_tear = 0;
|
||||
|
||||
const char* glx = glXQueryExtensionsString(display, DefaultScreen(display));
|
||||
if (!glx)
|
||||
{
|
||||
fprintf(stderr, "glXQueryExtensionsString failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* start = glx;
|
||||
for (;;)
|
||||
{
|
||||
while (*glx != 0 && *glx != ' ')
|
||||
{
|
||||
glx++;
|
||||
}
|
||||
|
||||
size_t length = glx - start;
|
||||
if (StringsAreEqual("GLX_ARB_create_context", start, length))
|
||||
{
|
||||
glx_ARB_create_context = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_ARB_multisample", start, length))
|
||||
{
|
||||
glx_ARB_multisample = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_ARB_framebuffer_sRGB", start, length))
|
||||
{
|
||||
glx_ARB_framebuffer_sRGB = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_SGI_swap_control", start, length))
|
||||
{
|
||||
glx_SGI_swap_control = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_MESA_swap_control", start, length))
|
||||
{
|
||||
glx_MESA_swap_control = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_EXT_swap_control", start, length))
|
||||
{
|
||||
glx_EXT_swap_control = 1;
|
||||
}
|
||||
else if (StringsAreEqual("GLX_EXT_swap_control_tear", start, length))
|
||||
{
|
||||
glx_EXT_swap_control_tear = 1;
|
||||
}
|
||||
|
||||
if (*glx == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
glx++;
|
||||
start = glx;
|
||||
}
|
||||
}
|
||||
|
||||
int attr[32];
|
||||
int* p = attr;
|
||||
*p++ = GLX_X_VISUAL_TYPE; *p++ = GLX_TRUE_COLOR;
|
||||
*p++ = GLX_DOUBLEBUFFER; *p++ = True;
|
||||
*p++ = GLX_RED_SIZE; *p++ = 8;
|
||||
*p++ = GLX_GREEN_SIZE; *p++ = 8;
|
||||
*p++ = GLX_BLUE_SIZE; *p++ = 8;
|
||||
*p++ = GLX_DEPTH_SIZE; *p++ = WINDOW_DEPTH ? 24 : 0;
|
||||
*p++ = GLX_STENCIL_SIZE; *p++ = WINDOW_STENCIL ? 8 : 0;
|
||||
if (WINDOW_SRGB && glx_ARB_framebuffer_sRGB)
|
||||
{
|
||||
*p++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
|
||||
*p++ = True;
|
||||
}
|
||||
if (WINDOW_MSAA && glx_ARB_multisample)
|
||||
{
|
||||
*p++ = GLX_SAMPLE_BUFFERS_ARB;
|
||||
*p++ = 1;
|
||||
*p++ = GLX_SAMPLES_ARB;
|
||||
*p++ = WINDOW_MSAA;
|
||||
}
|
||||
*p++ = 0;
|
||||
|
||||
int count;
|
||||
GLXFBConfig* config = glXChooseFBConfig(display, DefaultScreen(display), attr, &count);
|
||||
if (!config || count == 0)
|
||||
{
|
||||
fprintf(stderr, "glXChooseFBConfig failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
XVisualInfo* info = glXGetVisualFromFBConfig(display, config[0]);
|
||||
if (!info)
|
||||
{
|
||||
fprintf(stderr, "glXGetVisualFromFBConfig failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Window root = DefaultRootWindow(display);
|
||||
|
||||
Colormap cmap = XCreateColormap(display, root, info->visual, AllocNone);
|
||||
if (!cmap)
|
||||
{
|
||||
fprintf(stderr, "XCreateColormap failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
XSetWindowAttributes swa;
|
||||
swa.colormap = cmap;
|
||||
swa.event_mask = StructureNotifyMask;
|
||||
|
||||
Window window = XCreateWindow(
|
||||
display, root, 0, 0,
|
||||
WINDOW_WIDTH ? WINDOW_WIDTH : 800,
|
||||
WINDOW_HEIGHT ? WINDOW_HEIGHT : 450,
|
||||
0, info->depth, InputOutput, info->visual,
|
||||
CWColormap | CWEventMask, &swa);
|
||||
if (!window)
|
||||
{
|
||||
fprintf(stderr, "XCreateWindow failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WINDOW_WIDTH && WINDOW_HEIGHT)
|
||||
{
|
||||
XSizeHints* hints = XAllocSizeHints();
|
||||
if (!hints)
|
||||
{
|
||||
fprintf(stderr, "XAllocSizeHints failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
hints->flags |= PMinSize | PMaxSize;
|
||||
hints->min_width = hints->max_width = WINDOW_WIDTH;
|
||||
hints->min_height = hints->max_height = WINDOW_HEIGHT;
|
||||
XSetWMNormalHints(display, window, hints);
|
||||
XFree(hints);
|
||||
}
|
||||
}
|
||||
Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1);
|
||||
|
||||
XStoreName(display, window, "OpenGL window");
|
||||
XMapWindow(display, window);
|
||||
|
||||
GLXContext ctx = NULL;
|
||||
if (glx_ARB_create_context)
|
||||
{
|
||||
int cattr[] =
|
||||
{
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
|
||||
#if USE_DEBUG_MODE
|
||||
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
|
||||
#endif
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
None,
|
||||
};
|
||||
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
|
||||
(PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB(
|
||||
(const GLubyte*)"glXCreateContextAttribsARB");
|
||||
ctx = glXCreateContextAttribsARB(display, config[0], 0, True, cattr);
|
||||
if (!ctx)
|
||||
{
|
||||
fprintf(stderr, "Failed to create modern OpenGL context, trying legacy context!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
ctx = glXCreateContext(display, info, NULL, GL_TRUE);
|
||||
}
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
fprintf(stderr, "glXCreateContext failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!glXMakeCurrent(display, window, ctx))
|
||||
{
|
||||
fprintf(stderr, "glXMakeCurrent failed!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WINDOW_VSYNC)
|
||||
{
|
||||
if (glx_EXT_swap_control)
|
||||
{
|
||||
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
|
||||
(PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB(
|
||||
(const GLubyte*)"glXSwapIntervalEXT");
|
||||
glXSwapIntervalEXT(display, window, glx_EXT_swap_control_tear ? -1 : 1);
|
||||
}
|
||||
else if (glx_MESA_swap_control)
|
||||
{
|
||||
PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA =
|
||||
(PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddressARB(
|
||||
(const GLubyte*)"glXSwapIntervalMESA");
|
||||
if (glXSwapIntervalMESA(1))
|
||||
{
|
||||
fprintf(stderr, "glXSwapIntervalMESA failed!\n");
|
||||
}
|
||||
}
|
||||
else if (glx_SGI_swap_control)
|
||||
{
|
||||
PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI =
|
||||
(PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB(
|
||||
(const GLubyte*)"glXSwapIntervalSGI");
|
||||
if (glXSwapIntervalSGI(1))
|
||||
{
|
||||
fprintf(stderr, "glXSwapIntervalSGI failed!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_DEBUG_MODE
|
||||
const GLubyte* ext;
|
||||
GL_CHECK( ext = glGetString(GL_EXTENSIONS) );
|
||||
if (ext)
|
||||
{
|
||||
const GLubyte* start = ext;
|
||||
for (;;)
|
||||
{
|
||||
while (*ext != 0 && *ext != ' ')
|
||||
{
|
||||
ext++;
|
||||
}
|
||||
|
||||
size_t length = ext - start;
|
||||
if (StringsAreEqual("GL_ARB_debug_output", (const char*)start, length))
|
||||
{
|
||||
PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB =
|
||||
(PFNGLDEBUGMESSAGECALLBACKARBPROC)glXGetProcAddressARB((const GLubyte*)"glDebugMessageCallbackARB");
|
||||
GL_CHECK( glDebugMessageCallbackARB(OpenGLDebugCallback, NULL) );
|
||||
GL_CHECK( glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) );
|
||||
break;
|
||||
}
|
||||
|
||||
if (*ext == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ext++;
|
||||
start = ext;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (WINDOW_MSAA && glx_ARB_multisample)
|
||||
{
|
||||
GL_CHECK( glEnable(GL_MULTISAMPLE_ARB) );
|
||||
}
|
||||
|
||||
RenderInit();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (XPending(display))
|
||||
{
|
||||
XEvent event;
|
||||
XNextEvent(display, &event);
|
||||
|
||||
if (event.type == ConfigureNotify)
|
||||
{
|
||||
RenderResize(event.xconfigure.width, event.xconfigure.height);
|
||||
}
|
||||
else if (event.type == ClientMessage)
|
||||
{
|
||||
if ((Atom)event.xclient.data.l[0] == WM_DELETE_WINDOW)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderFrame();
|
||||
|
||||
glXSwapBuffers(display, window);
|
||||
}
|
||||
|
||||
RenderDone();
|
||||
|
||||
glXMakeCurrent(display, None, NULL);
|
||||
}
|
||||
glXDestroyContext(display, ctx);
|
||||
}
|
||||
XDestroyWindow(display, window);
|
||||
}
|
||||
XFreeColormap(display, cmap);
|
||||
}
|
||||
XFree(info);
|
||||
}
|
||||
XFree(config);
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue