├── Texture.png ├── GitHubPage ├── SwiftOpenGL Logo.png ├── PageImages │ ├── First App.png │ ├── Cocoa Class.png │ ├── New Project.png │ ├── New target.png │ ├── Run Button.png │ ├── Xcode Splash.png │ ├── Manage Schemes.png │ ├── The path icon.png │ ├── App Store search.png │ ├── Document Outline.png │ ├── FirstVertex Group.png │ ├── Make new folder.png │ ├── Naming the scheme.png │ ├── Selecting a file.png │ ├── SwiftOpenGL App.png │ ├── Utilities Panel.png │ ├── Drop onto the View.png │ ├── Identity Inspector.png │ ├── NSOpenGLView Object.png │ ├── Naming the project.png │ ├── Renaming the target.png │ ├── Setting plist path.png │ ├── Attributes Inspector.png │ ├── Choosing the file path.png │ ├── Go to the storyboard.png │ ├── Navigating to a Folder.png │ ├── Saving SwiftOpenGLView.png │ ├── Spotlight Search Xcode.png │ ├── SwiftOpenGLView Naming.png │ ├── The Developer Folder.png │ ├── Xcode App Store page.png │ ├── Selecting the right target.png │ ├── Xcode search on App Store.png │ ├── Drop into the View Controller in the Editor View.png │ └── Click on the Pin button at the bottom right of the editor.png ├── main.css ├── Point_in_the_Right_Direction.html ├── Swift_OpenGL_Setup.html ├── tutorials.html └── Point_in_the_Right_Direction(Setup).html ├── .github └── workflows │ └── swift.yml ├── README.md ├── SwiftOpenGLRefactor ├── Supporting Files │ └── SwiftOpenGLRefactor.entitlements ├── AppDelegate.swift ├── UserInteraction.swift ├── AssetManager.swift ├── GraphicViewController.swift └── GraphicView.swift ├── 01 Beginning ├── ViewController.swift ├── AppDelegate.swift └── SwiftOpenGLView.swift ├── 10 OnTheMove ├── AppDelegate.swift └── MoveViewController.swift ├── 11 OpenGLMVCPt1 ├── AppDelegate.swift ├── SwiftOpenGLViewController.swift ├── SwiftOpenGLObjects.swift └── SwiftOpenGLView.swift ├── 12 OpenGLMVCPt2 ├── AppDelegate.swift ├── SwiftOpenGLViewController.swift ├── SwiftOpenGLViewDelegates.swift └── SwiftOpenGLView.swift ├── index.html ├── 02 FirstVertex └── SwiftOpenGLView.swift ├── 04 ColoredTriangle └── SwiftOpenGLView.swift ├── 03 FirstTriangle └── SwiftOpenGLView.swift ├── 06 LitTriangle └── SwiftOpenGLView.swift ├── 07 PhongTriangle └── SwiftOpenGLView.swift ├── 05 TexturedTriangle └── SwiftOpenGLView.swift ├── 08 StartAnimating └── SwiftOpenGLView.swift └── 09 AnimationNext └── SwiftOpenGLView.swift /Texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/Texture.png -------------------------------------------------------------------------------- /GitHubPage/SwiftOpenGL Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/SwiftOpenGL Logo.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/First App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/First App.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Cocoa Class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Cocoa Class.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/New Project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/New Project.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/New target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/New target.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Run Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Run Button.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Xcode Splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Xcode Splash.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Manage Schemes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Manage Schemes.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/The path icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/The path icon.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/App Store search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/App Store search.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Document Outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Document Outline.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/FirstVertex Group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/FirstVertex Group.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Make new folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Make new folder.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Naming the scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Naming the scheme.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Selecting a file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Selecting a file.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/SwiftOpenGL App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/SwiftOpenGL App.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Utilities Panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Utilities Panel.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Drop onto the View.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Drop onto the View.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Identity Inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Identity Inspector.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/NSOpenGLView Object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/NSOpenGLView Object.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Naming the project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Naming the project.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Renaming the target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Renaming the target.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Setting plist path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Setting plist path.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Attributes Inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Attributes Inspector.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Choosing the file path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Choosing the file path.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Go to the storyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Go to the storyboard.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Navigating to a Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Navigating to a Folder.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Saving SwiftOpenGLView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Saving SwiftOpenGLView.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Spotlight Search Xcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Spotlight Search Xcode.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/SwiftOpenGLView Naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/SwiftOpenGLView Naming.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/The Developer Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/The Developer Folder.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Xcode App Store page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Xcode App Store page.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Selecting the right target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Selecting the right target.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Xcode search on App Store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Xcode search on App Store.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Drop into the View Controller in the Editor View.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Drop into the View Controller in the Editor View.png -------------------------------------------------------------------------------- /GitHubPage/PageImages/Click on the Pin button at the bottom right of the editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuggusMageevers/SwiftOpenGLTutorials/HEAD/GitHubPage/PageImages/Click on the Pin button at the bottom right of the editor.png -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build -v 18 | - name: Run tests 19 | run: swift test -v 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftOpenGLTutorials 2 | Sequential OpenGL targets to be used as tutorials in learning OpenGL with Swift. 3 | 4 | 01. Beginning 5 | 02. FirstVertex 6 | 03. FirstTriangle 7 | 04. ColoredTriangle 8 | 05. TextureTriangle 9 | 06. LitTriangle 10 | 07. PhongTriangle 11 | 08. StartAnimating 12 | 09. AnimationNext 13 | 10. OnTheMove 14 | 11. OpenGLMVCPt1 15 | 12. OpenGLMVCPt2 16 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/Supporting Files/SwiftOpenGLRefactor.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /01 Beginning/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles Schultz on 1/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | // Ver. 1: No changes to project default 9 | 10 | import Cocoa 11 | 12 | class ViewController: NSViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | // Do any additional setup after loading the view. 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /01 Beginning/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles Schultz on 1/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /10 OnTheMove/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OnTheMove 4 | // 5 | // Created by Myles Schultz on 1/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /11 OpenGLMVCPt1/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OpenGLMVCPt1 4 | // 5 | // Created by Myles Schultz on 9/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /12 OpenGLMVCPt2/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OpenGLMVCPt2a 4 | // 5 | // Created by Myles Schultz on 10/5/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftOpenGLRefactor 4 | // 5 | // Created by Myles Schultz on 1/17/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /11 OpenGLMVCPt1/SwiftOpenGLViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OpenGLMVCPt1 4 | // 5 | // Created by Myles Schultz on 9/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | // Ver 3: First step in developing our app to be MVC compliant. Uses 9 | // an IBOutlet to reference the instance of SwiftOpenGLView we 10 | // created in interface builder. 11 | // 12 | 13 | import Cocoa 14 | 15 | 16 | class SwiftOpenGLViewController: NSViewController { 17 | @IBOutlet weak var interactiveView: SwiftOpenGLView! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | // Do any additional setup after loading the view. 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /12 OpenGLMVCPt2/SwiftOpenGLViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OpenGLMVCPt2a 4 | // 5 | // Created by Myles Schultz on 10/5/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | // Ver 4: Incorporates a render delegate to the drive the 9 | // drawing loop of the interactive view. The view 10 | // controller or an dedicated object could accomplish this 11 | // task. 12 | // 13 | 14 | import Cocoa 15 | 16 | 17 | class SwiftOpenGLViewController: NSViewController, RenderDelegate { 18 | @IBOutlet weak var interactiveView: SwiftOpenGLView! 19 | var scenes = [String : Scene]() 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | scenes["Scene"] = Scene(named: "Scene") 25 | interactiveView.renderDelegate = self 26 | } 27 | 28 | func loadScene() { 29 | scenes["Scene"]?.load(into: interactiveView) 30 | } 31 | 32 | func prepareToRender(_ scene: SceneName, for time: Double) { 33 | scenes[scene]!.update(with: Float(time)) 34 | } 35 | 36 | func render(_ scene: SceneName, with renderer: Renderer) { 37 | scenes[scene]!.draw(with: renderer) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/UserInteraction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInteraction.swift 3 | // SwiftOpenGLRefactor 4 | // 5 | // Created by Myles Schultz on 1/23/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | enum UserInput: Hashable, Equatable { 13 | case key(Keyboard) 14 | 15 | enum Keyboard: UInt16 { 16 | case a = 0 17 | case s = 1 18 | case d = 2 19 | case w = 13 20 | } 21 | 22 | var hashValue: Int { 23 | switch self { 24 | case .key(let key): 25 | return key.hashValue &* 65_537 26 | } 27 | } 28 | static func ==(lhs: UserInput, rhs: UserInput) -> Bool { 29 | return lhs.hashValue == rhs.hashValue 30 | } 31 | static func !=(lhs: UserInput, rhs: UserInput) -> Bool { 32 | return lhs.hashValue != rhs.hashValue 33 | } 34 | } 35 | enum Instruction { 36 | case move(Float3) 37 | } 38 | struct InstructionSet { 39 | let target: String 40 | let instruction: Instruction 41 | } 42 | protocol Respondable { 43 | var instructions: [ UserInput : InstructionSet ] { get set } 44 | 45 | mutating func respond(to input: UserInput, at time: Double) 46 | } 47 | protocol Asset { 48 | var name: String { get set } 49 | } 50 | protocol Moveable { 51 | var position: Float3 { get set } 52 | } 53 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/AssetManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssetManager.swift 3 | // SwiftOpenGLRefactor 4 | // 5 | // Created by Myles Schultz on 1/23/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | private struct ObjectGraph { 13 | typealias AssetName = String 14 | 15 | var assetPositions: [ AssetName : Float3 ] = [ "globalCamera" : Float3(x: 0.0, y: 0.0, z: 0.0) ] 16 | 17 | mutating func move(_ asset: AssetName, in direction: Float3, with timeInterval: Float) { 18 | if let position = assetPositions[asset] { 19 | assetPositions[asset] = position.move(direction, over: timeInterval) 20 | print("\(asset) moved to \(assetPositions[asset]!)") 21 | } 22 | } 23 | } 24 | struct AssetManager: Respondable { 25 | typealias AssetName = String 26 | 27 | var instructions: [ UserInput : InstructionSet ] = [ 28 | UserInput.key(.w) : InstructionSet(target: "globalCamera", instruction: Instruction.move(Float3(x: 0.0, y: 0.0, z: 1.0))), 29 | UserInput.key(.a) : InstructionSet(target: "globalCamera", instruction: Instruction.move(Float3(x: -1.0, y: 0.0, z: 0.0))) 30 | ] 31 | var assets: [ AssetName : Asset ] = [ 32 | "globalCamera" : Camera() 33 | ] 34 | private var objectGraph = ObjectGraph() 35 | 36 | mutating func respond(to input: UserInput, at time: Double) { 37 | if let instructionSet = instructions[input] { 38 | switch instructionSet.instruction { 39 | case .move(let direction): 40 | objectGraph.move(instructionSet.target, in: direction, with: Float(time)) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /GitHubPage/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Oxygen'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local('Oxygen Regular'), local('Oxygen-Regular'), url(https://fonts.gstatic.com/s/oxygen/v7/LC4u_jU27qpsdszDEgeU_3-_kf6ByYO6CLYdB4HQE-Y.woff2) format('woff2'); 6 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 7 | } 8 | html{ 9 | font-family: 'Oxygen', sans-serif; 10 | color: #ececf3; 11 | font-size: 16px; 12 | } 13 | html, body { 14 | /* Ensure the page covers the entire view thus giving these parents 15 | an inherent size. This enables children that do not have content 16 | fill empty space */ 17 | height: 100%; 18 | width: 100%; 19 | } 20 | body { 21 | min-height: 100%; 22 | margin: 0; 23 | background: #4f4f56; 24 | } 25 | header { 26 | position: relative; 27 | min-height: 10%; 28 | background: linear-gradient(10deg, #004bff, #4f4f56); 29 | } 30 | main { 31 | position: relative; 32 | min-height: 82%; 33 | background: #1b1b22; 34 | } 35 | footer { 36 | position: relative; 37 | min-height: 8%; 38 | background-color: #4f4f56; 39 | } 40 | a { 41 | color: #639dff; 42 | text-decoration: none; 43 | } 44 | ul { 45 | padding: 0; 46 | line-height: 32px; 47 | list-style-position: inside; 48 | list-style-type: none; 49 | } 50 | #title { 51 | position: relative; 52 | 53 | padding: 4% 8%; 54 | font-size: 42px; 55 | } 56 | #entry { 57 | position: relative; 58 | padding: 1em 1em 5em; 59 | margin: auto; 60 | max-width: 800px; 61 | } 62 | #links { 63 | position: relative; 64 | padding: 1em 1em 2em; 65 | margin: auto; 66 | max-width: 800px; 67 | font-size: 20px; 68 | } 69 | #copyright { 70 | position: absolute; 71 | padding: 1em 0; 72 | left: 50%; 73 | transform: translateX(-50%); 74 | font-size: 16px; 75 | } 76 | -------------------------------------------------------------------------------- /10 OnTheMove/MoveViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OnTheMove 4 | // 5 | // Created by Myles La Verne Schultz on 10/27/15. 6 | // Copyright © 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 2: Initial implementation of user interaction to move through 9 | // the 3D world. AWSD keys move the camera position along the 10 | // x, r, and z planes while mouse movement re-oritents the 11 | // camera direction. 12 | // 13 | 14 | 15 | import Cocoa 16 | 17 | 18 | class MoveViewController: NSViewController { 19 | 20 | @IBOutlet weak var interactiveView: SwiftOpenGLView! 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | // Do any additional setup after loading the view. 26 | 27 | if let view = self.view.subviews[0] as? SwiftOpenGLView { 28 | 29 | interactiveView = view 30 | 31 | } 32 | 33 | } 34 | 35 | override func keyDown(with theEvent: NSEvent) { 36 | 37 | if let keyName = SwiftOpenGLView.KeyCodeName(rawValue: theEvent.keyCode) { 38 | 39 | if interactiveView.directionKeys[keyName] != true { 40 | 41 | interactiveView.directionKeys[keyName] = true 42 | 43 | } 44 | 45 | } else { super.keyDown(with: theEvent) } 46 | 47 | } 48 | 49 | override func keyUp(with theEvent: NSEvent) { 50 | 51 | if let keyName = SwiftOpenGLView.KeyCodeName(rawValue: theEvent.keyCode) { 52 | 53 | interactiveView.directionKeys[keyName] = false 54 | 55 | } else { super.keyUp(with: theEvent) } 56 | 57 | } 58 | 59 | override func mouseDragged(with theEvent: NSEvent) { 60 | 61 | interactiveView.rotateCamera(pitch: Float(theEvent.deltaY), yaw: Float(theEvent.deltaX)) 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/GraphicViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftOpenGLRefactor 4 | // 5 | // Created by Myles Schultz on 1/17/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class ViewController: NSViewController, RenderDelegate { 12 | @IBOutlet weak var graphicView: GraphicView! 13 | var scene = Scene() 14 | var scenes = [String : Scene]() 15 | var sceneLoaded = false 16 | var assetManager = AssetManager() 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | scenes["Scene"] = self.scene 22 | graphicView.renderDelegate = self 23 | } 24 | 25 | override var representedObject: Any? { 26 | didSet { 27 | // Update the view, if already loaded. 28 | } 29 | } 30 | 31 | func loadScene() { 32 | scenes["Scene"]?.load(into: graphicView) 33 | graphicView.scene = "Scene" 34 | sceneLoaded = true 35 | } 36 | func prepareToRender(_ scene: RenderDelegate.SceneName, for time: Double) { 37 | scenes[scene]!.update(with: Float(time)) 38 | } 39 | func render(_ scene: RenderDelegate.SceneName, with renderer: Renderer) { 40 | scenes[scene]!.draw(with: renderer) 41 | } 42 | 43 | override func mouseDown(with event: NSEvent) { 44 | if sceneLoaded == false { 45 | print("Loading scene...") 46 | graphicView.openGLContext?.makeCurrentContext() 47 | scenes["Scene"]?.load(into: graphicView) 48 | graphicView.scene = "Scene" 49 | sceneLoaded = true 50 | } else { 51 | print("Scene already loaded.") 52 | } 53 | } 54 | override func keyDown(with event: NSEvent) { 55 | if let input = UserInput.Keyboard(rawValue: event.keyCode) { 56 | assetManager.respond(to: UserInput.key(input), at: event.timestamp) 57 | } 58 | } 59 | 60 | deinit { 61 | scene.delete() 62 | scenes["Scene"]!.delete() 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /12 OpenGLMVCPt2/SwiftOpenGLViewDelegates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLViewDelegates.swift 3 | // OpenGLMVCPt2a 4 | // 5 | // Created by Myles Schultz on 1/27/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreVideo.CVDisplayLink 11 | 12 | 13 | struct DisplayLink { 14 | let id: CVDisplayLink 15 | let callback: CVDisplayLinkOutputCallback = {(displayLink: CVDisplayLink, inNow: UnsafePointer, inOutputTime: UnsafePointer, flagsIn: CVOptionFlags, flagsOut: UnsafeMutablePointer, displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in 16 | // print("fps: \(Double(inNow.pointee.videoTimeScale) / Double(inNow.pointee.videoRefreshPeriod))") 17 | 18 | let view = unsafeBitCast(displayLinkContext, to: SwiftOpenGLView.self) 19 | view.displayLink?.currentTime = Double(inNow.pointee.videoTime) / Double(inNow.pointee.videoTimeScale) 20 | let result = view.drawView() 21 | 22 | return result 23 | } 24 | var currentTime: Double = 0.0 { 25 | willSet { 26 | deltaTime = currentTime - newValue 27 | } 28 | } 29 | var deltaTime: Double = 0.0 30 | var running: Bool = false 31 | 32 | init?(forView view: SwiftOpenGLView) { 33 | var newID: CVDisplayLink? 34 | 35 | if CVDisplayLinkCreateWithActiveCGDisplays(&newID) == kCVReturnSuccess { 36 | self.id = newID! 37 | CVDisplayLinkSetOutputCallback(id, callback, UnsafeMutableRawPointer(Unmanaged.passUnretained(view).toOpaque())) 38 | } else { 39 | return nil 40 | } 41 | } 42 | 43 | mutating func start() { 44 | if !running { 45 | CVDisplayLinkStart(id) 46 | running = true 47 | } 48 | } 49 | mutating func stop() { 50 | if running == true { 51 | CVDisplayLinkStop(id) 52 | running = false 53 | } 54 | } 55 | } 56 | 57 | protocol RenderDelegate { 58 | func loadScene() 59 | func prepareToRender(_ scene: SceneName, for time: Double) 60 | func render(_ scene: SceneName, with renderer: Renderer) 61 | } 62 | -------------------------------------------------------------------------------- /12 OpenGLMVCPt2/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles Schultz on 10/5/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import OpenGL.GL3 11 | 12 | 13 | enum RenderElementType: UInt32 { 14 | case points = 0 15 | case lines = 1 16 | case triangles = 4 17 | } 18 | protocol Renderer { 19 | func render(_ elementCount: Int32, as elementType: RenderElementType) 20 | } 21 | extension NSOpenGLContext: Renderer { 22 | func render(_ elementCount: Int32, as elementType: RenderElementType) { 23 | glDrawArrays(elementType.rawValue, 0, elementCount) 24 | } 25 | } 26 | final class SwiftOpenGLView: NSOpenGLView { 27 | var scene: SceneName? 28 | var displayLink: DisplayLink? 29 | var renderDelegate: RenderDelegate? 30 | 31 | required init?(coder: NSCoder) { 32 | super.init(coder: coder) 33 | 34 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 35 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAAllRenderers), 36 | NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer), 37 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32, 38 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile), NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core), 39 | 0 40 | ] 41 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 42 | Swift.print("pixelFormat could not be constructed") 43 | return 44 | } 45 | self.pixelFormat = pixelFormat 46 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 47 | Swift.print("context could not be constructed") 48 | return 49 | } 50 | self.openGLContext = context 51 | 52 | // Set the context's swap interval parameter to 60Hz (i.e. 1 frame per swamp) 53 | self.openGLContext?.setValues([1], for: .swapInterval) 54 | 55 | displayLink = DisplayLink(forView: self) 56 | } 57 | 58 | override func prepareOpenGL() { 59 | super.prepareOpenGL() 60 | 61 | glClearColor(0.5, 0.5, 0.5, 1.0) 62 | 63 | renderDelegate?.loadScene() 64 | displayLink?.start() 65 | } 66 | 67 | override func draw(_ dirtyRect: NSRect) { 68 | _ = drawView() 69 | } 70 | 71 | func drawView() -> CVReturn { 72 | guard let context = self.openGLContext else { 73 | print("Could not acquire an OpenGL context") 74 | return kCVReturnError 75 | } 76 | 77 | context.makeCurrentContext() 78 | context.lock() 79 | 80 | if let time = displayLink?.currentTime { 81 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 82 | glEnable(GLenum(GL_CULL_FACE)) 83 | 84 | if let scene = scene { 85 | renderDelegate?.prepareToRender(scene, for: time) 86 | 87 | renderDelegate?.render(scene, with: context) 88 | } 89 | } 90 | 91 | context.flushBuffer() 92 | context.unlock() 93 | 94 | return kCVReturnSuccess 95 | } 96 | 97 | deinit { 98 | displayLink?.stop() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /01 Beginning/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/15/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 1: Draws a black background. 9 | // 10 | 11 | 12 | import Cocoa 13 | import OpenGL.GL3 14 | 15 | 16 | // To optimize the binary created for this class, we'll set the class to final. This tells the 17 | // compiler methods declared in this file are owned by the file and you don't have to worry about 18 | // the potential of a subclass's implementation. In other words, this eliminates some behind the 19 | // scenes stack calls which speeds things up (this is per WWDC 15, so it is probably something 20 | // realized in Swift 2 and not Swift 1.2). It's good practice to mark class you do not intend to 21 | // subclass as final, and methods you do not accessible to other classes, structs, and enums as 22 | // private. 23 | final class SwiftOpenGLView: NSOpenGLView { 24 | 25 | required init?(coder: NSCoder) { 26 | // Allow the super class to initialize it's properties (phase 1 initialization) 27 | super.init(coder: coder) 28 | 29 | // Some OpenGL setup 30 | // NSOpenGLPixelFormatAttribute is a typealias for UInt32 in Swift, but the individual 31 | // attributes are Int's. We have initialize them as Int32's to fit them into an array 32 | // of NSOpenGLPixelFormatAttributes 33 | 34 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 35 | UInt32(NSOpenGLPFAAccelerated), // Use accelerated renderers 36 | UInt32(NSOpenGLPFAColorSize), UInt32(32), // Use 32-bit color 37 | UInt32(NSOpenGLPFAOpenGLProfile), // Use version's >= 3.2 core 38 | UInt32( NSOpenGLProfileVersion3_2Core), 39 | UInt32(0) // C API's expect to end with 0 40 | ] 41 | 42 | // Create a pixel format using our attributes 43 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 44 | Swift.print("pixelFormat could not be constructed") 45 | return 46 | } 47 | self.pixelFormat = pixelFormat 48 | 49 | // Create a context with our pixel format (we have no other context, so nil) 50 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 51 | Swift.print("context could not be constructed") 52 | return 53 | } 54 | self.openGLContext = context 55 | } 56 | 57 | override func prepareOpenGL() { 58 | // Allow the superclass to perform it's tasks 59 | super.prepareOpenGL() 60 | 61 | // Setup OpenGL 62 | 63 | // The buffer will clear each pixel to black upon starting the creation of a new frame 64 | glClearColor(0.0, 0.0, 0.0, 1.0) 65 | 66 | /* other setup here, e.g. gelable() calls */ 67 | 68 | // Run a test render 69 | drawView() 70 | } 71 | 72 | override func draw(_ dirtyRect: NSRect) { 73 | super.draw(dirtyRect) 74 | 75 | // Drawing code here. 76 | 77 | // Call our OpenGL rendering code. 78 | drawView() 79 | } 80 | 81 | private func drawView() { 82 | // Clear out the context before rendering 83 | // This uses the clear color we set earlier (0.0, 0.0, 0.0, 1.0 or black) 84 | // We're only drawing with colors now, but if we were using a depth or stencil buffer 85 | // we would also want to clear those here separated by "logical or" " | " 86 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 87 | 88 | /* Drawing code here */ 89 | 90 | // Tell OpenGL that you're done rendering and it's time to send the context to the screen 91 | glFlush() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SwiftOpenGLRefactor/GraphicView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphicView.swift 3 | // SwiftOpenGLRefactor 4 | // 5 | // Created by Myles Schultz on 1/17/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import OpenGL.GL3 11 | 12 | 13 | protocol RenderDelegate { 14 | typealias SceneName = String 15 | 16 | func loadScene() 17 | func prepareToRender(_ scene: SceneName, for time: Double) 18 | func render(_ scene: SceneName, with renderer: Renderer) 19 | } 20 | 21 | enum RenderElementType: UInt32 { 22 | case points = 0 23 | case lines = 1 24 | case triangles = 4 25 | } 26 | protocol Renderer { 27 | func render(_ elementCount: Int32, as elementType: RenderElementType) 28 | } 29 | extension NSOpenGLContext: Renderer { 30 | func render(_ elementCount: Int32, as elementType: RenderElementType) { 31 | glCall(glDrawArrays(elementType.rawValue, 0, elementCount)) 32 | } 33 | } 34 | extension _CGLContextObject: Renderer { 35 | func render(_ elementCount: Int32, as elementType: RenderElementType) { 36 | glCall(glDrawArrays(elementType.rawValue, 0, elementCount)) 37 | } 38 | } 39 | 40 | final class GraphicView: NSOpenGLView { 41 | typealias SceneName = String 42 | 43 | var scene: SceneName? 44 | var displayLink: DisplayLink? 45 | var renderDelegate: RenderDelegate? 46 | 47 | required init?(coder: NSCoder) { 48 | super.init(coder: coder) 49 | 50 | let attributes: [NSOpenGLPixelFormatAttribute] = [ 51 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAAllRenderers), 52 | NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer), 53 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32, 54 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile), NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core), 55 | 0 56 | ] 57 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attributes) else { 58 | Swift.print("pixelFormat could not be constructed") 59 | return 60 | } 61 | self.pixelFormat = pixelFormat 62 | 63 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 64 | Swift.print("context could not be constructed") 65 | return 66 | } 67 | self.openGLContext = context 68 | 69 | // Set the context's swap interval parameter to 60Hz (i.e. 1 frame per swamp) 70 | self.openGLContext?.setValues([1], for: .swapInterval) 71 | 72 | displayLink = DisplayLink(forView: self) 73 | } 74 | 75 | override func prepareOpenGL() { 76 | super.prepareOpenGL() 77 | 78 | glClearColor(0.5, 0.5, 0.5, 1.0) 79 | 80 | renderDelegate?.loadScene() 81 | displayLink?.start() 82 | } 83 | 84 | override func draw(_ dirtyRect: NSRect) { 85 | _ = drawView() 86 | } 87 | 88 | func drawView() -> CVReturn { 89 | guard let context = self.openGLContext?.cglContextObj else { 90 | print("Could not acquire an OpenGL context") 91 | return kCVReturnError 92 | } 93 | 94 | CGLSetCurrentContext(context) 95 | CGLLockContext(context) 96 | 97 | if let time = displayLink?.currentTime { 98 | glCall(glClear(GLbitfield(GL_COLOR_BUFFER_BIT))) 99 | glCall(glCullFace(GLenum(GL_BACK))) 100 | glCall(glEnable(GLenum(GL_CULL_FACE))) 101 | 102 | if let scene = scene { 103 | renderDelegate?.prepareToRender(scene, for: time) 104 | 105 | renderDelegate?.render(scene, with: context.pointee) 106 | } 107 | } 108 | 109 | CGLFlushDrawable(context) 110 | CGLUnlockContext(context) 111 | 112 | return kCVReturnSuccess 113 | } 114 | 115 | deinit { 116 | displayLink?.stop() 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /GitHubPage/Point_in_the_Right_Direction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swift OpenGL Tutorials 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |

Drawing a Point On Screen

37 |
38 |

Coding a Shader

39 |

Shader's (shader programs) are collections of complied code that run on the GPU. They accept and process data which is used to calculate the individual pixel data (red, green, and blue values) for displaying/printing to the screen, memory, printer or other device. A shader program is made of vertex, fragment, geometry, and you're mac is new enough to code with OpenGL 4.1, tessellation shaders. I find it unfortunate that OpenGL chose to call these individual shader components shaders. I find it makes things confusing, but that's the naming convention.

40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |

Previous

49 |
50 |
51 |
52 |

Next

53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | 71 |
72 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
Swift OpenGL Tutorials
9 |
10 |
11 |
12 |

Welcome to Swift OpenGL Tutorials

13 |

The purpose of these turtorials is to provide a starting point for implementing OpenGL using Swift. OpenGL is an application programming interface (API) that allows for communication with your computer's GPU(s). OpenGL had, for a long time, been used on Mac OS X to drive graphics processing of operating system and user created elements. However, since MacOS, Metal has become the main GPU language of choice. Why learn OpenGL then? OpenGL is a cross-platform language while Metal is not: meaning that OpenGL code on MacOS also works on Windows or Linux platforms. If you plan on writing an a cross-platform app, and reduce the amount of code that you need to write from one system to another, then OpenGL would be more ideal. That brings us to Swift. Work on bring Apple's new programming language to other platforms such as Ubuntu. As the language matures and interest grows, Swift may make it's way to many other platforms as well. Additionaly, while new computers running MacOS have the capability to run Metal through MacOS, there are many older computers processing Mac OS X or MacOS graphical elements through OpenGL. To see if your Mac is capable of running Metal, check out this site for a list of Macs with Metal capable GPU's. Generally, Macs older than 2012 are OpenGL capable only.

14 |

A Little History

15 |

OpenGL has been around for some time (version 1.0 was released in 1992, and you can see the API specification here). The most recent version, as you may have seen on that site, is 4.6 as of 2017. With each version, they have made of number of improvements and modernizations. The most notable, arguably, from version 1.0 to version 2.0 is the addition of the "programmable pipeline": the beginnings of what is thought of today as the modern approach. The pipeline is merely the various steps involved in producing a graphic element by the GPU. Originally this pipeline was "fixed": there was a default implementation which as programmer could pass hints, or data to, but there was little customization compared to what is available now. The programmable pipeline requires the programmer to provide portions of this pipeline (i.e. shaders) in order to produce a result. While this requires more effort from the progammer, it also enables the programmer with greater flexibility and potential for interesting graphic effects. This modernization of OpenGL in 2004 was a little late as Direct3D (Windows proprietary graphics API) had already implemented their programmable pipeline in 2000). Metal is the proprietary API for graphics programming on Apple products and also utilizes a programmable pipeline--though there have been a number of revitalizations in order to reduce the overhead of graphics processing. Though OpenGL has been receiving updates over the years, it's API is showing it's age. In order to keep up with competing graphics languages, DirectX and Metal, Khronos has created Vulkan (OpenGL's cross-platform successor).

16 |

Closing Remarks

17 |

DirectX (Windows only), Vulkan (Windows, Linux, indirect MacOS support through 3rd party API), and Metal (Apple only) all require modern graphics cards. However, OpenGL can run on "older" GPU's and new GPU's, and all major operating systems (Windows, Linux, and MacOS). Portable platforms (Android, and iOS) may harness the capabilities of OpenGL through a lighter weight specification called OpenGLES. OpenGL is still relevent and is also a great place to start learning to work with graphics API's because of it's breadth of accessibility. With all of this in mind, I hope you find these tutorials helpful and enjoyable.

18 |
19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /02 FirstVertex/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/20/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 2: Draws a vertex to the screen with OpenGL shaders 9 | // 10 | 11 | 12 | import Cocoa 13 | import OpenGL.GL3 14 | 15 | 16 | final class SwiftOpenGLView: NSOpenGLView { 17 | 18 | fileprivate var programID: GLuint = 0 19 | fileprivate var vaoID: GLuint = 0 20 | 21 | required init?(coder: NSCoder) { 22 | super.init(coder: coder) 23 | 24 | // OpenGL view setup 25 | // NSOpenGLPixelFormatAttribute is a typealias for UInt32 in Swift and GLbitfield in OpenGL, cast each attribute 26 | // Set the view's PixelFormat and Context to the custom pixelFormat and context 27 | 28 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 29 | UInt32(NSOpenGLPFAAccelerated), 30 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 31 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 32 | UInt32(0) 33 | ] 34 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 35 | Swift.print("pixelFormat could not be constructed") 36 | return 37 | } 38 | self.pixelFormat = pixelFormat 39 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 40 | Swift.print("context could not be constructed") 41 | return 42 | } 43 | self.openGLContext = context 44 | } 45 | 46 | override func prepareOpenGL() { 47 | 48 | super.prepareOpenGL() 49 | 50 | // Setup OpenGL 51 | 52 | glClearColor(0.0, 0.0, 0.0, 1.0) 53 | 54 | programID = glCreateProgram() 55 | 56 | glGenVertexArrays(1, &vaoID) 57 | 58 | // Here we see an example of converting a variable into a pointer to a pointer 59 | // This function from sbennett912 takes an UnsafePointer of a certain type and 60 | // returns an UnsafePointer of the same type: to the compiler, you've created 61 | // a variable that passes as an UnsafePointer> 62 | // As an alternative, you can cast the varible as an UnsafePointer 63 | // i.e. UnsafePointer(variable) 64 | func getPointer (_ pointer: UnsafePointer?)->UnsafePointer? { return pointer } 65 | 66 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 67 | var source = "#version 330 core \n" + 68 | "void main() \n" + 69 | "{ \n" + 70 | " gl_Position = vec4(0.0, 0.0, 0.0, 1.0); \n" + 71 | "} \n" 72 | let vss = source.cString(using: String.Encoding.ascii) 73 | // Here we cast instead of using the getPointer() function 74 | var vssptr = UnsafePointer(vss) 75 | glShaderSource(vs, 1, &vssptr, nil) 76 | glCompileShader(vs) 77 | var compiled: GLint = 0 78 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 79 | if compiled <= 0 { 80 | Swift.print("Could not compile, getting log") 81 | var logLength: GLint = 0 82 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 83 | Swift.print(" logLength = \(logLength)") 84 | if logLength > 0 { 85 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 86 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 87 | Swift.print("Vert Error Log = \(String.init(cString: cLog))") 88 | free(cLog) 89 | } 90 | } 91 | 92 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 93 | source = "#version 330 core \n" + 94 | "out vec4 color; \n" + 95 | "void main() \n" + 96 | "{ \n" + 97 | " color = vec4(1.0, 1.0, 1.0, 1.0); \n" + 98 | "} \n" 99 | let fss = source.cString(using: String.Encoding.ascii) 100 | var fssptr = getPointer(fss) 101 | glShaderSource(fs, 1, &fssptr, nil) 102 | glCompileShader(fs) 103 | compiled = 0 104 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 105 | if compiled <= 0 { 106 | Swift.print("Could not compile, getting log") 107 | var logLength: GLint = 0 108 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 109 | Swift.print(" logLength = \(logLength)") 110 | if logLength > 0 { 111 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 112 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 113 | Swift.print("Frag Error Log = \(String.init(cString: cLog))") 114 | free(cLog) 115 | } 116 | } 117 | 118 | glAttachShader(programID, vs) 119 | glAttachShader(programID, fs) 120 | glLinkProgram(programID) 121 | var linked: GLint = 0 122 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 123 | if linked <= 0 { 124 | Swift.print("Could not link, getting log") 125 | var logLength: GLint = 0 126 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 127 | Swift.print(" logLength = \(logLength)") 128 | if logLength > 0 { 129 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 130 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 131 | Swift.print("Program Error Log: \(String.init(cString: cLog))") 132 | free(cLog) 133 | } 134 | } 135 | 136 | // These shaders are currently being used by the Shader Program, so they are just 137 | // flagged for deletion. They shall be automatically detached and deleted when 138 | // when glDeleteShader() is called on the associated shader program. 139 | glDeleteShader(vs) 140 | glDeleteShader(fs) 141 | 142 | // Run a test render 143 | 144 | drawView() 145 | 146 | } 147 | 148 | override func draw(_ dirtyRect: NSRect) { 149 | super.draw(dirtyRect) 150 | 151 | // Drawing code here. 152 | 153 | drawView() 154 | 155 | } 156 | 157 | fileprivate func drawView() { 158 | 159 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 160 | 161 | glUseProgram(programID) 162 | glBindVertexArray(vaoID) 163 | 164 | glPointSize(40) 165 | glDrawArrays(GLenum(GL_POINTS), 0, 1) 166 | 167 | glBindVertexArray(0) 168 | 169 | glFlush() 170 | } 171 | 172 | deinit { 173 | glDeleteVertexArrays(1, &vaoID) 174 | glDeleteProgram(programID) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /11 OpenGLMVCPt1/SwiftOpenGLObjects.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLObjects.swift 3 | // OpenGLMVCPt1 4 | // 5 | // Created by Myles Schultz on 1/27/18. 6 | // Copyright © 2018 MyKo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Quartz 11 | 12 | 13 | protocol OpenGLObject { 14 | var id: GLuint { get set } 15 | 16 | func bind() 17 | func unbind() 18 | mutating func delete() 19 | } 20 | 21 | struct Vertex { 22 | var position: Float3 23 | var normal: Float3 24 | var textureCoordinate: Float2 25 | var color: Float3 26 | } 27 | struct VertexBufferObject: OpenGLObject { 28 | var id: GLuint = 0 29 | let type: GLenum = GLenum(GL_ARRAY_BUFFER) 30 | var vertexCount: Int32 { 31 | return Int32(data.count) 32 | } 33 | var data: [Vertex] = [] 34 | 35 | mutating func load(_ data: [Vertex]) { 36 | self.data = data 37 | 38 | glGenBuffers(1, &id) 39 | bind() 40 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 41 | } 42 | 43 | func bind() { 44 | glBindBuffer(type, id) 45 | } 46 | 47 | func unbind() { 48 | glBindBuffer(type, id) 49 | } 50 | 51 | mutating func delete() { 52 | glDeleteBuffers(1, &id) 53 | } 54 | } 55 | struct VertexArrayObject: OpenGLObject { 56 | var id: GLuint = 0 57 | 58 | mutating func layoutVertexPattern() { 59 | glGenVertexArrays(1, &id) 60 | bind() 61 | 62 | /* Position */ 63 | glVertexAttribPointer(0, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 44, UnsafePointer(bitPattern: 0)) 64 | glEnableVertexAttribArray(0) 65 | 66 | /* Normal */ 67 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 44, UnsafePointer(bitPattern: 12)) 68 | glEnableVertexAttribArray(1) 69 | 70 | /* Texture Coordinate */ 71 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 44, UnsafePointer(bitPattern: 24)) 72 | glEnableVertexAttribArray(2) 73 | 74 | /* Color */ 75 | glVertexAttribPointer(3, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 44, UnsafePointer(bitPattern:32)) 76 | glEnableVertexAttribArray(3) 77 | } 78 | 79 | func bind() { 80 | glBindVertexArray(id) 81 | } 82 | func unbind() { 83 | glBindVertexArray(id) 84 | } 85 | 86 | mutating func delete() { 87 | glDeleteVertexArrays(1, &id) 88 | } 89 | } 90 | enum TextureSlot: GLint { 91 | case texture1 = 33984 92 | } 93 | struct TextureBufferObject: OpenGLObject { 94 | var id: GLuint = 0 95 | var textureSlot: GLint = TextureSlot.texture1.rawValue 96 | 97 | mutating func loadTexture(named name: String) { 98 | guard let textureData = NSImage(named: NSImage.Name(rawValue: name))?.tiffRepresentation else { 99 | Swift.print("Image name not located in Image Asset Catalog") 100 | return 101 | } 102 | 103 | glGenTextures(1, &id) 104 | bind() 105 | 106 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 107 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 108 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT) 109 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT) 110 | 111 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), (textureData as NSData).bytes) 112 | } 113 | 114 | func bind() { 115 | glBindTexture(GLenum(GL_TEXTURE_2D), id) 116 | } 117 | func unbind() { 118 | glBindTexture(GLenum(GL_TEXTURE_2D), 0) 119 | } 120 | 121 | mutating func delete() { 122 | glDeleteTextures(1, &id) 123 | } 124 | } 125 | enum ShaderType: UInt32 { 126 | case vertex = 35633 /* GL_VERTEX_SHADER */ 127 | case fragment = 35632 /* GL_FRAGMENT_SHADER */ 128 | } 129 | struct Shader: OpenGLObject { 130 | var id: GLuint = 0 131 | 132 | mutating func create(withVertex vertexSource: String, andFragment fragmentSource: String) { 133 | id = glCreateProgram() 134 | 135 | let vertex = compile(shaderType: .vertex, withSource: vertexSource) 136 | let fragment = compile(shaderType: .fragment, withSource: fragmentSource) 137 | 138 | link(vertexShader: vertex, fragmentShader: fragment) 139 | } 140 | 141 | func compile(shaderType type: ShaderType, withSource source: String) -> GLuint { 142 | let shader = glCreateShader(type.rawValue) 143 | var pointerToShader = UnsafePointer(source.cString(using: String.Encoding.ascii)) 144 | glShaderSource(shader, 1, &pointerToShader, nil) 145 | glCompileShader(shader) 146 | var compiled: GLint = 0 147 | glGetShaderiv(shader, GLbitfield(GL_COMPILE_STATUS), &compiled) 148 | if compiled <= 0 { 149 | print("Could not compile shader type: \(type), getting log...") 150 | var logLength: GLint = 0 151 | print("Log length: \(logLength)") 152 | glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength) 153 | if logLength > 0 { 154 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 155 | glGetShaderInfoLog(shader, GLsizei(logLength), &logLength, cLog) 156 | print("\n\t\(String.init(cString: cLog))") 157 | free(cLog) 158 | } 159 | } 160 | 161 | return shader 162 | } 163 | 164 | func link(vertexShader vertex: GLuint, fragmentShader fragment: GLuint) { 165 | glAttachShader(id, vertex) 166 | glAttachShader(id, fragment) 167 | glLinkProgram(id) 168 | var linked: GLint = 0 169 | glGetProgramiv(id, UInt32(GL_LINK_STATUS), &linked) 170 | if linked <= 0 { 171 | print("Could not link, getting log") 172 | var logLength: GLint = 0 173 | glGetProgramiv(id, UInt32(GL_INFO_LOG_LENGTH), &logLength) 174 | print(" logLength = \(logLength)") 175 | if logLength > 0 { 176 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 177 | glGetProgramInfoLog(id, GLsizei(logLength), &logLength, cLog) 178 | print("log: \(String.init(cString:cLog))") 179 | free(cLog) 180 | } 181 | } 182 | 183 | glDeleteShader(vertex) 184 | glDeleteShader(fragment) 185 | } 186 | 187 | func setInitialUniforms() { 188 | let location = glGetUniformLocation(id, "sample") 189 | glUniform1i(location, GLint(GL_TEXTURE0)) 190 | 191 | bind() 192 | glUniform3fv(glGetUniformLocation(id, "light.color"), 1, [1.0, 1.0, 1.0]) 193 | glUniform3fv(glGetUniformLocation(id, "light.position"), 1, [0.0, 2.0, 2.0]) 194 | glUniform1f(glGetUniformLocation(id, "light.ambient"), 0.25) 195 | glUniform1f(glGetUniformLocation(id, "light.specStrength"), 3.0) 196 | glUniform1f(glGetUniformLocation(id, "light.specHardness"), 32) 197 | } 198 | func update(view: FloatMatrix4, projection: FloatMatrix4) { 199 | glUniformMatrix4fv(glGetUniformLocation(id, "view"), 1, GLboolean(GL_FALSE), view.columnMajorArray()) 200 | glUniformMatrix4fv(glGetUniformLocation(id, "projection"), 1, GLboolean(GL_FALSE), projection.columnMajorArray()) 201 | } 202 | 203 | func bind() { 204 | glUseProgram(id) 205 | } 206 | func unbind() { 207 | glUseProgram(0) 208 | } 209 | 210 | func delete() { 211 | glDeleteProgram(id) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /GitHubPage/Swift_OpenGL_Setup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swift OpenGL Tutorials 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |

Create the Project with Xcode

37 |
38 |

Download Xcode

39 |

To follow along with these tutorials, you'll need a Mac 40 | with the latest public version of Xcode (Xcode 6.4 as of 41 | this writing). To get Xcode, open the App Store and 42 | search for Xcode.

43 | App Store Search 44 |

You'll see a bunch of applications, but Xcode should be 45 | the first one if you typed in "xcode" into the search 46 | field. I already have the application downloaded, so 47 | the button says "Open", but if you haven't downloaded 48 | Xcode, the button will say "Get".

49 | Xcode search on App Store 50 |
51 | Xcode App Store page 52 |

You may be prompted to input your AppleID and password 53 | and click "Buy". Remember, the app is free. The app 54 | will download automatically when you "Buy" the app. 55 | Once it has downloaded, you'll find it in your 56 | Applications folder, Launchpad, or by holding down ⌘ 57 | and pressing the spacebar you'll get Spotlight Search 58 | where you can type in "xcode" and press return on the 59 | keyboard.

60 | Spotlight Search Xcode 61 |
62 | Xcode Splash 63 |

The first time you open Xcode, you should see the 64 | splash screen. This is a great starting point to create 65 | new projects or open existing and recent projects. 66 | Create a new project by clicking on "Create New Project" 67 | (I've blurred out my previous projects, but you would 68 | see your previous project at the right of the welcome 69 | screen). While in Xcode, you may open the Welcome 70 | Screen at any time by pressing ⇧⌘ 1 on the 71 | keyboard.

72 | New Project 73 |

The page down menu prompts you to select the type of 74 | project to create. On the left side of the menu, select 75 | Application under OS X. Then select Cocoa Application 76 | from from the icons at the left. Click next. You'll be 77 | prompted for a name, company, etc. The name of our 78 | project is SwiftOpenGL. Leave the Organization name 79 | field blank for now. For Organization Identifier, you 80 | have to put something, so it nothing is there, insert 81 | "com". Click on the language dropdown menu and select 82 | Swift if it is not selected already. Finally, make sure 83 | Use Storyboards is checked and Create Document-Based 84 | Application and Use Core Data are not checked.

85 | Naming the project 86 |

Click next and you'll be asked for a location to save 87 | the project and related files. I recommend using the 88 | Developer folder that should have been created when you 89 | downloaded Xcode. You should be able to find it by 90 | click on the dropdown menu at the top of the menu and 91 | click on Macintosh HD.

92 | Selecting a file 93 |
94 | The Developer Folder 95 |

Select the Developer folder, and click Create. A 96 | whirlwind of computer magic will ensue and you'll be 97 | presented with your first Xcode app! At the top left of 98 | the screen, you'll see a play button icon. This is the 99 | Run button. Click Run and you're application will build 100 | and launch. It's not exciting--just a grey window--but 101 | it'll let you know that you have a working application.

102 | Click the run button 103 |
104 | The window of your first app 105 |

Now that we have a working app, we need to add a view to 106 | our window that will display out OpenGL content.

107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | 116 |
117 |
118 |
119 |

Next

120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | 138 |
139 |
140 |
141 | 142 | 143 | -------------------------------------------------------------------------------- /04 ColoredTriangle/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 8/24/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 4: Draws a triangle where each corner is a different color 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | final class SwiftOpenGLView: NSOpenGLView { 15 | 16 | fileprivate var programID: GLuint = 0 17 | fileprivate var vaoID: GLuint = 0 18 | fileprivate var vboID: GLuint = 0 19 | 20 | required init?(coder: NSCoder) { 21 | super.init(coder: coder) 22 | 23 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 24 | UInt32(NSOpenGLPFAAccelerated), 25 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 26 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 27 | UInt32(0) 28 | ] 29 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 30 | Swift.print("pixelFormat could not be constructed") 31 | return 32 | } 33 | self.pixelFormat = pixelFormat 34 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 35 | Swift.print("context could not be constructed") 36 | return 37 | } 38 | self.openGLContext = context 39 | } 40 | 41 | override func prepareOpenGL() { 42 | 43 | super.prepareOpenGL() 44 | 45 | glClearColor(0.0, 0.0, 0.0, 1.0) 46 | 47 | programID = glCreateProgram() 48 | 49 | // We'll add color information to the position information such that the position is 50 | // listed first with two components (x and y) and the color is listed second with 51 | // three components (r, g, and b). 52 | 53 | let data: [GLfloat] = [-1.0, -1.0, 1.0, 0.0, 0.0, 54 | 0.0, 1.0, 0.0, 1.0, 0.0, 55 | 1.0, -1.0, 0.0, 0.0, 1.0] 56 | 57 | glGenBuffers(1, &vboID) 58 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 59 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 60 | 61 | glGenVertexArrays(1, &vaoID) 62 | glBindVertexArray(vaoID) 63 | 64 | // The first pointer is to the position data -- note the last two arguments: stride 65 | // and offset. 66 | // Stride - number of bytes from the start of one vertex to the start of the next 67 | // In other words, each vertex is made of 5 elements from the array (x, y, r, g, 68 | // and b). Each element is a GLfloat with is 4 bytes of data; therefore, 69 | // 5(4) = 20 bytes 70 | // Offset - number of bytes from the vertex's start address that must be passed by 71 | // before reaching the appropriate data 72 | // The position data starts at the start address; therefore, offset = 0 73 | // The color data starts 2 floats (2(4) = 8 bytes) from the vertex address; 74 | // therefore, offset = 8 75 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 20, UnsafePointer(bitPattern: 0)) 76 | glEnableVertexAttribArray(0) 77 | // The second pointer is to the color data. 78 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 20, UnsafePointer(bitPattern: 8)) 79 | glEnableVertexAttribArray(1) 80 | 81 | // Unbind the VAO so no further changes are made. 82 | glBindVertexArray(0) 83 | 84 | // Adjust the shader attributes. There are two ways to do this, but the simplest is 85 | // the use the layout keyword. This allows us to use simple indicies when defining 86 | // our vertex attributes as you saw above (i.e. 0 and 1 for position and color, 87 | // respectively). The alternative is to use glGetAttribLocation(). It's certainly 88 | // more declarative to use this method so we have a named variable instead of a "magic 89 | // number" like 0 or 1, but it's a more common convention to just use the indices. 90 | // The other caveat is that the function assumes the shader has already been 91 | // compiled--in our case it has not. layout allows us to specifically tell OpenGL 92 | // what index we want for a particular attribute. 93 | // layout (location = 0) means the attribute declared thereafter will be located 94 | // at position 0. It is a little weird that you are forced to use the layout 95 | // keyword to force the convention considering that if you use the functionand 96 | // then print() the result, you'll find that 0 and 1 are assigned as we have them 97 | // below. If you don't use the layout keyword though, you'll get unexpected 98 | // results (perhaps a black screen, at the worst). Don't fight the system, just 99 | // use layout (location = x)! 100 | // 101 | // Notice the addition of two attributes: color and passColor. Color is what is 102 | // passed in from the VBO while passColor is the color that is given to the fragment 103 | // shader. Note that they are both vec3's and not vec2's because they have three 104 | // values, r, g, and b. 105 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 106 | var source = "#version 330 core \n" + 107 | "layout (location = 0) in vec2 position; \n" + 108 | "layout (location = 1) in vec3 color; \n" + 109 | "out vec3 passColor; \n" + 110 | "void main() \n" + 111 | "{ \n" + 112 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 113 | " passColor = color; \n" + 114 | "} \n" 115 | let vss = source.cString(using: String.Encoding.ascii) 116 | var vssptr = UnsafePointer(vss) 117 | glShaderSource(vs, 1, &vssptr, nil) 118 | glCompileShader(vs) 119 | var compiled: GLint = 0 120 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 121 | if compiled <= 0 { 122 | Swift.print("Could not compile vertex, getting log") 123 | var logLength: GLint = 0 124 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 125 | Swift.print(" logLength = \(logLength)") 126 | if logLength > 0 { 127 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 128 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 129 | Swift.print("log = \(String.init(cString: cLog))") 130 | free(cLog) 131 | } 132 | } 133 | 134 | // It is really important to name the in attribute the same as the out attribute from 135 | // the vertex shader--otherwise the connection won't be between them. The name of the 136 | // out attribute does not matter. You just have to have an out and it has to be a 137 | // vec4: we have to account for the alpha component of a color. We can use the same 138 | // syntax for creating the vec4 color as we did the position in the vertex shader 139 | // (i.e. passing passColor for the frist three vertices and 1.0 for the fourth to make 140 | // a complete vec4 141 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 142 | source = "#version 330 core \n" + 143 | "in vec3 passColor; \n" + 144 | "out vec4 outColor; \n" + 145 | "void main() \n" + 146 | "{ \n" + 147 | " outColor = vec4(passColor, 1.0); \n" + 148 | "} \n" 149 | let fss = source.cString(using: String.Encoding.ascii) 150 | var fssptr = UnsafePointer(fss) 151 | glShaderSource(fs, 1, &fssptr, nil) 152 | glCompileShader(fs) 153 | compiled = 0 154 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 155 | if compiled <= 0 { 156 | Swift.print("Could not compile fragement, getting log") 157 | var logLength: GLint = 0 158 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 159 | Swift.print(" logLength = \(logLength)") 160 | if logLength > 0 { 161 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 162 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 163 | Swift.print("log = \(String.init(cString: cLog))") 164 | free(cLog) 165 | } 166 | } 167 | 168 | glAttachShader(programID, vs) 169 | glAttachShader(programID, fs) 170 | glLinkProgram(programID) 171 | var linked: GLint = 0 172 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 173 | if linked <= 0 { 174 | Swift.print("Could not link, getting log") 175 | var logLength: GLint = 0 176 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 177 | Swift.print(" logLength = \(logLength)") 178 | if logLength > 0 { 179 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 180 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 181 | Swift.print("log: \(String.init(cString: cLog))") 182 | free(cLog) 183 | } 184 | } 185 | 186 | glDeleteShader(vs) 187 | glDeleteShader(fs) 188 | 189 | drawView() 190 | 191 | } 192 | 193 | override func draw(_ dirtyRect: NSRect) { 194 | super.draw(dirtyRect) 195 | 196 | // Drawing code here. 197 | 198 | drawView() 199 | 200 | } 201 | 202 | fileprivate func drawView() { 203 | 204 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 205 | 206 | glUseProgram(programID) 207 | glBindVertexArray(vaoID) 208 | 209 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 210 | 211 | glBindVertexArray(0) 212 | 213 | glFlush() 214 | } 215 | 216 | deinit { 217 | glDeleteVertexArrays(1, &vaoID) 218 | glDeleteBuffers(1, &vboID) 219 | glDeleteProgram(programID) 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /03 FirstTriangle/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 8/23/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 3: Draws a triangle to the screen with OpenGL shaders 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | 15 | final class SwiftOpenGLView: NSOpenGLView { 16 | 17 | fileprivate var programID: GLuint = 0 18 | fileprivate var vaoID: GLuint = 0 19 | fileprivate var vboID: GLuint = 0 // The VBO handle 20 | 21 | required init?(coder: NSCoder) { 22 | super.init(coder: coder) 23 | 24 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 25 | UInt32(NSOpenGLPFAAccelerated), 26 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 27 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 28 | UInt32(0) 29 | ] 30 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 31 | Swift.print("pixelFormat could not be constructed") 32 | return 33 | } 34 | self.pixelFormat = pixelFormat 35 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 36 | Swift.print("context could not be constructed") 37 | return 38 | } 39 | self.openGLContext = context 40 | } 41 | 42 | override func prepareOpenGL() { 43 | 44 | super.prepareOpenGL() 45 | 46 | // Setup OpenGL 47 | 48 | glClearColor(0.0, 0.0, 0.0, 1.0) 49 | 50 | programID = glCreateProgram() 51 | 52 | // A triangle is one of three primitives used for drawing in OpenGL: 53 | // points, lines, triangles 54 | // To draw a solid triangle to the screen, we'll need three points. OpenGL defines 55 | // drawing space as being Unit length (Normalized) in positive and negative directions. 56 | // The top of the view is y 1.0, bottom -1.0, left -1.0, right 1.0, etc. 57 | // This is different from the way other Mac API's define screen space: 58 | // the origin x 0.0, y 0.0 is the middle of the view in OpenGL and the bottom left 59 | // of the view in Mac. 60 | // We'll define a triangle that fits these coordinates in an array that we can send to 61 | // a VBO. We don't need to access it later, so we'll define it within the method such 62 | // that upon method completion, the memory will be released. 63 | // We are drawing a two dimensional object, so for right now, we only need to define an 64 | // x and y coordinate. The z and w coordinates will be added in the shader (see below). 65 | 66 | let data: [GLfloat] = [-1.0, -1.0, 0.0, 1.0, 1.0, -1.0] // Three vertices 67 | 68 | // The ampersand is used when passing a variable of type (i.e. a: ) 69 | // You may drop the ampersand if you define the variable as an UnsafeMutablePointer 70 | // a: UnsafeMutablePointer 71 | // We choose the former because we'll be using the form far more than the 72 | // UnsafeMutablePointer form--this makes the code look nicer. 73 | 74 | glGenBuffers(1, &vboID) // Allocate a buffer with the handle 75 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) // Initialize the buffer 76 | 77 | // The next line fills the VBO with the data in our array "data". The first argument is 78 | // the type of VBO--GL_ARRAY_BUFFER is used when we are drawing vertices one after 79 | // another in an array. The alternative is GL_ELEMENT_ARRAY_BUFFER which feeds the 80 | // shader by indices--more on this later. The second argument tells OpenGL how many 81 | // bytes are required (each element in data is a float and there are data.count number of 82 | // elements or 6 elements (4 bytes) = 24 bytes). The third argument is supposed to be a 83 | // a pointer to the data. Swift does not directly expose pointers, but it does allow you 84 | // to pass variable names to UnsafePointer parameters. The fourth argument tells 85 | // tells OpenGL how to optimize memory for drawing. GL_STATIC_DRAW states that the data 86 | // will mostly read from and won't be changed often (it's just a hint to the GPU). 87 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 88 | 89 | glGenVertexArrays(1, &vaoID) 90 | // We have to bind the VAO before we can add a pointer to an attribute 91 | glBindVertexArray(vaoID) 92 | 93 | // Vertex attribute pointers set up connections between VBO data and shader input 94 | // attributes. The first parameter is the index location of the attribute in the shader. 95 | // You see this later, but the first defined attribute is 0, the second is 1, etc. The 96 | // second parameter tells OpenGL how many "pieces" of data are being supplied--we're 97 | // passing a location with an x and why coordinate, or 2 values. The third parameter 98 | // tells OpenGL what type the data is so it knows how many bytes are needed. The fourth 99 | // parameter tells OpenGL if the data needs to be normalized (converted to a value 100 | // between -1.0 and 1.0). We are already doing so in our array so we pass in false. The 101 | // fifth parameter is the stride and tells OpenGL how large a vertex is in bytes. If you 102 | // pass 0, OpenGL assumes the vertex size is the same as the number of pieces of data 103 | // times the data type (here that is 2 * 4bytes = 8). The sixth parameter tells OpenGL 104 | // how many bytes from the vertex's address must be skipped over to reach the data. 0 105 | // tells OpenGL the data is at the start of the vertex's address. 106 | // NOTE: The VAO knows which VBO to access because it is bound when this attribute is 107 | // created. A VAO does not store the VBO handle itself. 108 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, UnsafePointer(bitPattern: 0)) 109 | glEnableVertexAttribArray(0) // Unbind the VAO so no further changes are made. 110 | 111 | // Now we'll add input attributes to the shader. Inputs are marked as in, uniform, or 112 | // Sample2D, Sample3D, etc. They are defined after the shader version, but before the 113 | // main function. Our in attribute is a 2 point vector (vec2). In the main function 114 | // we set the predefined variable gl_Position. Remember that it expects a vec4. We make 115 | // a new vec4 by using our vec2 for the first two arguments and passing in 0.0 for the z, 116 | // and 1.0 for w arguments. 117 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 118 | var source = "#version 330 core \n" + 119 | "in vec2 position; \n" + 120 | "void main() \n" + 121 | "{ \n" + 122 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 123 | "} \n" 124 | let vss = source.cString(using: String.Encoding.ascii) 125 | var vssptr = UnsafePointer(vss) 126 | glShaderSource(vs, 1, &vssptr, nil) 127 | glCompileShader(vs) 128 | var compiled: GLint = 0 129 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 130 | if compiled <= 0 { 131 | Swift.print("Could not compile, getting log") 132 | var logLength: GLint = 0 133 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 134 | Swift.print(" logLength = \(logLength)") 135 | if logLength > 0 { 136 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 137 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 138 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 139 | free(cLog) 140 | } 141 | } 142 | 143 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 144 | source = "#version 330 core \n" + 145 | "out vec4 color; \n" + 146 | "void main() \n" + 147 | "{ \n" + 148 | " color = vec4(1.0, 1.0, 1.0, 1.0); \n" + 149 | "} \n" 150 | let fss = source.cString(using: String.Encoding.ascii) 151 | var fssptr = UnsafePointer(fss) 152 | glShaderSource(fs, 1, &fssptr, nil) 153 | glCompileShader(fs) 154 | compiled = 0 155 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 156 | if compiled <= 0 { 157 | Swift.print("Could not compile, getting log") 158 | var logLength: GLint = 0 159 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 160 | Swift.print(" logLength = \(logLength)") 161 | if logLength > 0 { 162 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 163 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 164 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 165 | free(cLog) 166 | } 167 | } 168 | 169 | glAttachShader(programID, vs) 170 | glAttachShader(programID, fs) 171 | glLinkProgram(programID) 172 | var linked: GLint = 0 173 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 174 | if linked <= 0 { 175 | Swift.print("Could not link, getting log") 176 | var logLength: GLint = 0 177 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 178 | Swift.print(" logLength = \(logLength)") 179 | if logLength > 0 { 180 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 181 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 182 | Swift.print("log: \(String.init(cString: cLog))") 183 | free(cLog) 184 | } 185 | } 186 | 187 | glDeleteShader(vs) 188 | glDeleteShader(fs) 189 | 190 | drawView() 191 | 192 | } 193 | 194 | override func draw(_ dirtyRect: NSRect) { 195 | super.draw(dirtyRect) 196 | 197 | // Drawing code here. 198 | 199 | drawView() 200 | 201 | } 202 | 203 | fileprivate func drawView() { 204 | 205 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 206 | 207 | glUseProgram(programID) 208 | glBindVertexArray(vaoID) // VBO's are used indirectly through VAO's 209 | 210 | // To draw a solid triangle, we pass GL_TRIANGLES as the first argument and 3 as the last 211 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 212 | 213 | glBindVertexArray(0) 214 | 215 | glFlush() 216 | } 217 | 218 | deinit { 219 | glDeleteVertexArrays(1, &vaoID) 220 | glDeleteBuffers(1, &vboID) // All objects must be deleted manually 221 | glDeleteProgram(programID) 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /GitHubPage/tutorials.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 12 | Swift OpenGL Tutorials 13 | 15 | 16 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 34 |
35 |
36 | 49 |
50 |
51 | 53 |
54 | 55 |
56 | 58 |
59 | 62 |
63 | 64 |
65 | 66 | 85 |
86 | 97 |
98 | 109 |
110 | 121 |
122 | 133 |
134 | 145 |
146 | 157 |
158 | 169 |
170 | 181 |
182 | 193 |
194 | 205 |
206 | 217 |
218 | 229 |
230 | 241 | 244 |
245 |
246 |
247 | 249 |
250 |
251 | 260 |
261 |
262 |
263 | 264 | 265 | -------------------------------------------------------------------------------- /06 LitTriangle/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 9/8/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 6: Draws a textured and colored triangle using a single diffuse area light 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | 15 | final class SwiftOpenGLView: NSOpenGLView { 16 | 17 | fileprivate var programID: GLuint = 0 18 | fileprivate var vaoID: GLuint = 0 19 | fileprivate var vboID: GLuint = 0 20 | fileprivate var tboID: GLuint = 0 21 | 22 | required init?(coder: NSCoder) { 23 | super.init(coder: coder) 24 | 25 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 26 | UInt32(NSOpenGLPFAAccelerated), 27 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 28 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 29 | UInt32(0) 30 | ] 31 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 32 | Swift.print("pixelFormat could not be constructed") 33 | return 34 | } 35 | self.pixelFormat = pixelFormat 36 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 37 | Swift.print("context could not be constructed") 38 | return 39 | } 40 | self.openGLContext = context 41 | } 42 | 43 | override func prepareOpenGL() { 44 | 45 | super.prepareOpenGL() 46 | 47 | glClearColor(0.0, 0.0, 0.0, 1.0) 48 | 49 | programID = glCreateProgram() 50 | 51 | //format: x, y, r, g, b, s, t, nx, ny, nz 52 | let data: [GLfloat] = [-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 2.0, -1.0, -1.0, 0.0001, 53 | 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0001, 54 | 1.0, -1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 1.0, -1.0, 0.0001] 55 | 56 | let fileURL = Bundle.main.url(forResource: "Texture", withExtension: "png") 57 | 58 | let dataProvider = CGDataProvider(url: fileURL! as CFURL) 59 | let image = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent) 60 | 61 | let textureData = UnsafeMutableRawPointer.allocate(bytes: 256 * 4 * 256, alignedTo: MemoryLayout.alignment) 62 | 63 | let context = CGContext(data: textureData, width: 256, height: 256, bitsPerComponent: 8, bytesPerRow: 4 * 256, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 64 | 65 | context?.draw(image!, in: CGRect(x: 0.0, y: 0.0, width: 256.0, height: 256.0)) 66 | 67 | glGenTextures(1, &tboID) 68 | glBindTexture(GLenum(GL_TEXTURE_2D), tboID) 69 | 70 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 71 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 72 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT) 73 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT) 74 | 75 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), textureData) 76 | 77 | free(textureData) 78 | 79 | glGenBuffers(1, &vboID) 80 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 81 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 82 | 83 | glGenVertexArrays(1, &vaoID) 84 | glBindVertexArray(vaoID) 85 | 86 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 0)) 87 | glEnableVertexAttribArray(0) 88 | 89 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 8)) 90 | glEnableVertexAttribArray(1) 91 | 92 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 20)) 93 | glEnableVertexAttribArray(2) 94 | 95 | // The fourth attribute, the normal. This adds an addition 12 bytes to the vertex 96 | // The vertex's total byte count is now 40 and the normal may be found at the end 97 | // of the previous attribute, byte 28 98 | glVertexAttribPointer(3, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern:28)) 99 | glEnableVertexAttribArray(3) 100 | 101 | glBindVertexArray(0) 102 | 103 | // The fragment shader is going to need the vertex position and normal, so we pass 104 | // those values on. The vertex shader remains otherwise unchanged. 105 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 106 | var source = "#version 330 core \n" + 107 | "layout (location = 0) in vec2 position; \n" + 108 | "layout (location = 1) in vec3 color; \n" + 109 | "layout (location = 2) in vec2 texturePosition; \n" + 110 | "layout (location = 3) in vec3 normal; \n" + 111 | "out vec3 passPosition; \n" + 112 | "out vec3 passColor; \n" + 113 | "out vec2 passTexturePosition; \n" + 114 | "out vec3 passNormal; \n" + 115 | "void main() \n" + 116 | "{ \n" + 117 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 118 | " passPosition = vec3(position, 0.0); \n" + 119 | " passColor = color; \n" + 120 | " passTexturePosition = texturePosition; \n" + 121 | " passNormal = normal; \n" + 122 | "} \n" 123 | let vss = source.cString(using: String.Encoding.ascii) 124 | var vssptr = UnsafePointer(vss) 125 | glShaderSource(vs, 1, &vssptr, nil) 126 | glCompileShader(vs) 127 | var compiled: GLint = 0 128 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 129 | if compiled <= 0 { 130 | Swift.print("Could not compile vertex, getting log") 131 | var logLength: GLint = 0 132 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 133 | Swift.print(" logLength = \(logLength)") 134 | if logLength > 0 { 135 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 136 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 137 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 138 | free(cLog) 139 | } 140 | } 141 | 142 | // Here is where the bulk of the changes take place and where the actual computing 143 | // change takes place. 144 | // The light source is defined by a color and a position that are the same for every 145 | // fragment. Therefore, we pass them in as uniform variables. 146 | // The normal must be renormalized as it is an interpolated value when passed from the 147 | // vertex shader. 148 | // The light ray incident to this fragment is calculated from the interpolated position 149 | // and the light position. This vector should also be normalized to simplify our 150 | // calculations--as we shall see in a moment. 151 | // The intensity of light upon the fragment, is calculated using the dot product and it's 152 | // equivalent equation |A||B|cos𝛉. Solving for cos𝛉, we get the euqation 153 | // dot(A, B) / length(A) * length(B) 154 | // Because we normalized the normal and the light ray vectors, we can simplify this to 155 | // dot(A, B) 156 | // This value is clamped to a value between 0 and 1 because the calculation may return a 157 | // a negative value. 158 | // The light variable is simple now, but will become complete later. For now it is a 159 | // simple pass on. 160 | // The model color is calculated the same as before. 161 | // Then the final color of the fragment is calculated by combining the light and surface 162 | // colors and added on the alpha value. 163 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 164 | source = "#version 330 core \n" + 165 | "uniform sampler2D sample; \n" + 166 | "uniform vec3 lightColor; \n" + 167 | "uniform vec3 lightPosition; \n" + 168 | "in vec3 passPosition; \n" + 169 | "in vec3 passColor; \n" + 170 | "in vec2 passTexturePosition; \n" + 171 | "in vec3 passNormal; \n" + 172 | "out vec4 outColor; \n" + 173 | "void main() \n" + 174 | "{ \n" + 175 | " vec3 normal = normalize(passNormal); \n" + 176 | " vec3 lightRay = normalize(lightPosition - passPosition); \n" + 177 | " float intensity = dot(normal, lightRay); \n" + 178 | " intensity = clamp(intensity, 0, 1); \n" + 179 | " vec3 light = lightColor * intensity; \n" + 180 | " vec3 surface = texture(sample, passTexturePosition).rgb * passColor; \n" + 181 | " vec3 rgb = surface * light; \n" + 182 | " outColor = vec4(rgb, 1.0); \n" + 183 | "} \n" 184 | let fss = source.cString(using: String.Encoding.ascii) 185 | var fssptr = UnsafePointer(fss) 186 | glShaderSource(fs, 1, &fssptr, nil) 187 | glCompileShader(fs) 188 | compiled = 0 189 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 190 | if compiled <= 0 { 191 | Swift.print("Could not compile fragement, getting log") 192 | var logLength: GLint = 0 193 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 194 | Swift.print(" logLength = \(logLength)") 195 | if logLength > 0 { 196 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 197 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 198 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 199 | free(cLog) 200 | } 201 | } 202 | 203 | glAttachShader(programID, vs) 204 | glAttachShader(programID, fs) 205 | glLinkProgram(programID) 206 | var linked: GLint = 0 207 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 208 | if linked <= 0 { 209 | Swift.print("Could not link, getting log") 210 | var logLength: GLint = 0 211 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 212 | Swift.print(" logLength = \(logLength)") 213 | if logLength > 0 { 214 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 215 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 216 | Swift.print("log: \(String.init(cString: cLog))") 217 | free(cLog) 218 | } 219 | } 220 | 221 | glDeleteShader(vs) 222 | glDeleteShader(fs) 223 | 224 | let sampleLocation = glGetUniformLocation(programID, "sample") 225 | glUniform1i(sampleLocation, GL_TEXTURE0) 226 | 227 | // Before we define our light's attributes, we need to select a program 228 | glUseProgram(programID) 229 | 230 | // Define the parameters for a light. 231 | // We'll start our light definition with a color and a position. 232 | // The color and position contain three components -- use a 3fv uniform 233 | // 3 for three components, f for float, v for variable 234 | // Alternatively, you could use a uniform3f which allows you to send three floats as 235 | // separate parameters 236 | glUniform3fv(glGetUniformLocation(programID, "lightColor"), 1, [1.0, 1.0, 1.0]) 237 | glUniform3fv(glGetUniformLocation(programID, "lightPosition"), 1, [0.0, 1.0, 0.0]) 238 | 239 | drawView() 240 | } 241 | 242 | override func draw(_ dirtyRect: NSRect) { 243 | super.draw(dirtyRect) 244 | 245 | // Drawing code here. 246 | 247 | drawView() 248 | 249 | } 250 | 251 | fileprivate func drawView() { 252 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 253 | 254 | glUseProgram(programID) 255 | glBindVertexArray(vaoID) 256 | 257 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 258 | 259 | glBindVertexArray(0) 260 | 261 | glFlush() 262 | } 263 | 264 | deinit { 265 | glDeleteVertexArrays(1, &vaoID) 266 | glDeleteBuffers(1, &vboID) 267 | glDeleteProgram(programID) 268 | glDeleteTextures(1, &tboID) 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /07 PhongTriangle/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 10/23/15. 6 | // Copyright © 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 7: Draws a textured and colored triangle using a single Phong area light 9 | // 10 | 11 | 12 | import Cocoa 13 | import OpenGL.GL3 14 | 15 | 16 | final class SwiftOpenGLView: NSOpenGLView { 17 | 18 | fileprivate var programID: GLuint = 0 19 | fileprivate var vaoID: GLuint = 0 20 | fileprivate var vboID: GLuint = 0 21 | fileprivate var tboID: GLuint = 0 22 | 23 | required init?(coder: NSCoder) { 24 | super.init(coder: coder) 25 | 26 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 27 | UInt32(NSOpenGLPFAAccelerated), 28 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 29 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 30 | UInt32(0) 31 | ] 32 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 33 | Swift.print("pixelFormat could not be constructed") 34 | return 35 | } 36 | self.pixelFormat = pixelFormat 37 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 38 | Swift.print("context could not be constructed") 39 | return 40 | } 41 | self.openGLContext = context 42 | } 43 | 44 | override func prepareOpenGL() { 45 | 46 | super.prepareOpenGL() 47 | 48 | glClearColor(0.0, 0.0, 0.0, 1.0) 49 | 50 | programID = glCreateProgram() 51 | 52 | //format: x, y, r, g, b, s, t, nx, ny, nz 53 | let data: [GLfloat] = [-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 2.0, -1.0, -1.0, 0.0001, 54 | 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0001, 55 | 1.0, -1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 1.0, -1.0, 0.0001] 56 | 57 | let fileURL = Bundle.main.url(forResource: "Texture", withExtension: "png") 58 | 59 | let dataProvider = CGDataProvider(url: fileURL! as CFURL) 60 | let image = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent) 61 | 62 | let textureData = UnsafeMutableRawPointer.allocate(bytes: 256 * 4 * 256, alignedTo: MemoryLayout.alignment) 63 | 64 | let context = CGContext(data: textureData, width: 256, height: 256, bitsPerComponent: 8, bytesPerRow: 4 * 256, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 65 | 66 | context?.draw(image!, in: CGRect(x: 0.0, y: 0.0, width: 256.0, height: 256.0)) 67 | 68 | glGenTextures(1, &tboID) 69 | glBindTexture(GLenum(GL_TEXTURE_2D), tboID) 70 | 71 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 72 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 73 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT) 74 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT) 75 | 76 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), textureData) 77 | 78 | free(textureData) 79 | 80 | glGenBuffers(1, &vboID) 81 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 82 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 83 | 84 | glGenVertexArrays(1, &vaoID) 85 | glBindVertexArray(vaoID) 86 | 87 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 0)) 88 | glEnableVertexAttribArray(0) 89 | 90 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 8)) 91 | glEnableVertexAttribArray(1) 92 | 93 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 20)) 94 | glEnableVertexAttribArray(2) 95 | 96 | glVertexAttribPointer(3, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern:28)) 97 | glEnableVertexAttribArray(3) 98 | 99 | glBindVertexArray(0) 100 | 101 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 102 | var source = "#version 330 core \n" + 103 | "layout (location = 0) in vec2 position; \n" + 104 | "layout (location = 1) in vec3 color; \n" + 105 | "layout (location = 2) in vec2 texturePosition; \n" + 106 | "layout (location = 3) in vec3 normal; \n" + 107 | "out vec3 passPosition; \n" + 108 | "out vec3 passColor; \n" + 109 | "out vec2 passTexturePosition; \n" + 110 | "out vec3 passNormal; \n" + 111 | "void main() \n" + 112 | "{ \n" + 113 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 114 | " passPosition = vec3(position, 0.0); \n" + 115 | " passColor = color; \n" + 116 | " passTexturePosition = texturePosition; \n" + 117 | " passNormal = normal; \n" + 118 | "} \n" 119 | let vss = source.cString(using: String.Encoding.ascii) 120 | var vssptr = UnsafePointer(vss) 121 | glShaderSource(vs, 1, &vssptr, nil) 122 | glCompileShader(vs) 123 | var compiled: GLint = 0 124 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 125 | if compiled <= 0 { 126 | Swift.print("Could not compile vertex, getting log") 127 | var logLength: GLint = 0 128 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 129 | Swift.print(" logLength = \(logLength)") 130 | if logLength > 0 { 131 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 132 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 133 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 134 | free(cLog) 135 | } 136 | } 137 | 138 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 139 | source = "#version 330 core \n" + 140 | "uniform sampler2D sample; \n" + 141 | // The Light uniform Struct allows us to more convenietly access the light attributes 142 | "uniform struct Light { \n" + 143 | " vec3 color; \n" + 144 | " vec3 position; \n" + 145 | " float ambient; \n" + 146 | " float specStrength; \n" + 147 | " float specHardness; \n" + 148 | "} light; \n" + 149 | "in vec3 passPosition; \n" + 150 | "in vec3 passColor; \n" + 151 | "in vec2 passTexturePosition; \n" + 152 | "in vec3 passNormal; \n" + 153 | "out vec4 outColor; \n" + 154 | "void main() \n" + 155 | "{ \n" + 156 | " vec3 normal = normalize(passNormal); \n" + 157 | " vec3 lightRay = normalize(light.position - passPosition); \n" + 158 | " float intensity = dot(normal, lightRay); \n" + 159 | " intensity = clamp(intensity, 0, 1); \n" + 160 | // viewer is the vector pointing from the fragment to the viewer 161 | " vec3 viewer = normalize(vec3(0.0, 0.0, 0.2) - passPosition); \n" + 162 | // reflect() calculates the reflection vector 163 | // first parameter is the incident ray 164 | // second parameter is the normal 165 | // we do not negate the lightRay because it is already pointing from the surface to the viewer 166 | // negating the vector would cause the reflection vector to point away from the viewer and no 167 | // highlight would seen. 168 | " vec3 reflection = reflect(lightRay, normal); \n" + 169 | // specular is calculated by taking the dot product of the viewer and reflection vectors, 170 | // ensuring those vectors are >=0.0 with max(), and then raising that value by the value 171 | // of hardness to adjust the hardness of the edge of the highlight. 172 | " float specular = pow(max(dot(viewer, reflection), 0.0), light.specHardness); \n" + 173 | // The specular component casts light so it must also be multiplied by the .color component. 174 | " vec3 light = light.ambient + light.color * intensity + light.specStrength * specular * light.color; \n" + 175 | " vec3 surface = texture(sample, passTexturePosition).rgb * passColor; \n" + 176 | " vec3 rgb = surface * light; \n" + 177 | " outColor = vec4(rgb, 1.0); \n" + 178 | "} \n" 179 | let fss = source.cString(using: String.Encoding.ascii) 180 | var fssptr = UnsafePointer(fss) 181 | glShaderSource(fs, 1, &fssptr, nil) 182 | glCompileShader(fs) 183 | compiled = 0 184 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 185 | if compiled <= 0 { 186 | Swift.print("Could not compile fragement, getting log") 187 | var logLength: GLint = 0 188 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 189 | Swift.print(" logLength = \(logLength)") 190 | if logLength > 0 { 191 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 192 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 193 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 194 | free(cLog) 195 | } 196 | } 197 | 198 | glAttachShader(programID, vs) 199 | glAttachShader(programID, fs) 200 | glLinkProgram(programID) 201 | var linked: GLint = 0 202 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 203 | if linked <= 0 { 204 | Swift.print("Could not link, getting log") 205 | var logLength: GLint = 0 206 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 207 | Swift.print(" logLength = \(logLength)") 208 | if logLength > 0 { 209 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 210 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 211 | Swift.print(" log: \(String.init(cString: cLog))") 212 | free(cLog) 213 | } 214 | } 215 | 216 | glDeleteShader(vs) 217 | glDeleteShader(fs) 218 | 219 | let sampleLocation = glGetUniformLocation(programID, "sample") 220 | glUniform1i(sampleLocation, GL_TEXTURE0) 221 | 222 | glUseProgram(programID) 223 | 224 | // Uniforms for the light struct. Each component is accessed using dot notation. 225 | glUniform3fv(glGetUniformLocation(programID, "light.color"), 1, [1.0, 1.0, 1.0]) 226 | glUniform3fv(glGetUniformLocation(programID, "light.position"), 1, [0.0, 1.0, 0.1]) 227 | glUniform1f(glGetUniformLocation(programID, "light.ambient"), 0.25) 228 | glUniform1f(glGetUniformLocation(programID, "light.specStrength"), 1.0) 229 | glUniform1f(glGetUniformLocation(programID, "light.specHardness"), 32) 230 | 231 | drawView() 232 | } 233 | 234 | override func draw(_ dirtyRect: NSRect) { 235 | super.draw(dirtyRect) 236 | 237 | // Drawing code here. 238 | 239 | drawView() 240 | 241 | } 242 | 243 | fileprivate func drawView() { 244 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 245 | 246 | glUseProgram(programID) 247 | glBindVertexArray(vaoID) 248 | 249 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 250 | 251 | glBindVertexArray(0) 252 | 253 | glFlush() 254 | } 255 | 256 | deinit { 257 | glDeleteVertexArrays(1, &vaoID) 258 | glDeleteBuffers(1, &vboID) 259 | glDeleteProgram(programID) 260 | glDeleteTextures(1, &tboID) 261 | } 262 | 263 | } 264 | -------------------------------------------------------------------------------- /05 TexturedTriangle/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 8/30/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 5: Draws a triangle with a texture using CGImage 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | final class SwiftOpenGLView: NSOpenGLView { 15 | 16 | fileprivate var programID: GLuint = 0 17 | fileprivate var vaoID: GLuint = 0 18 | fileprivate var vboID: GLuint = 0 19 | fileprivate var tboID: GLuint = 0 20 | 21 | required init?(coder: NSCoder) { 22 | super.init(coder: coder) 23 | 24 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 25 | UInt32(NSOpenGLPFAAccelerated), 26 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 27 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 28 | UInt32(0) 29 | ] 30 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 31 | Swift.print("pixelFormat could not be constructed") 32 | return 33 | } 34 | self.pixelFormat = pixelFormat 35 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 36 | Swift.print("context could not be constructed") 37 | return 38 | } 39 | self.openGLContext = context 40 | } 41 | 42 | override func prepareOpenGL() { 43 | 44 | super.prepareOpenGL() 45 | 46 | glClearColor(0.0, 0.0, 0.0, 1.0) 47 | 48 | programID = glCreateProgram() 49 | 50 | // This time we'll add texture coordinates to each vertex. When appliced to a model, 51 | // These coordinates are named U and V. When these same coordinates are applied to a 52 | // texture, they are named S and T. I am not really sure why a separate naming 53 | // convention is used, but it is. either way, they values are clamped between 0.0-1.0 54 | // As a side, texture coordinates may be described in four dimensions like position 55 | // and color: s, t, r, and q. However, in GLSL, r is already used for red, so it is 56 | // replaced by p. Thus, in GLSL only, texture coordinates are defined as s, t, p, and q 57 | // We only need two dimensions for our texture--we'll add s and t coordinates to each 58 | // vertex after each color. 59 | 60 | let data: [GLfloat] = [-1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 61 | 0.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 62 | 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0] 63 | 64 | // Now we'll take the time to create the texture data. We're using the Core Graphics 65 | // Framework which allows us to generate and load images that can be placed into bitmaps 66 | 67 | // Get the URL for the texture we added to the TextureTriangle file. 68 | // The NSBundle is used to access files packaged with the app, URL's are preferred 69 | // over paths 70 | let fileURL = Bundle.main.url(forResource: "Texture", withExtension: "png") 71 | 72 | // To get the file's contents into the CGImage, we need a data provider 73 | // The dataProvider is what actually writes the data into the the CGImage from the file 74 | // The second parameter of our CGImageCreate function is for a decode 75 | let dataProvider = CGDataProvider(url: fileURL! as CFURL) 76 | let image = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: false, intent: .defaultIntent) 77 | 78 | // Declare a pointer to a collection of memory that will hold the texture data. 79 | // If you don't use this step, you cannot access the data when you need it 80 | // The space in memory must be allocated before we send it into our context 81 | // we use malloc to reserve a space of memory 256 * 4 bytes wide (4 bytes, 8 bits 82 | // each for r, g, b, and a for 256 pixels), and a height of 256 83 | let textureData = UnsafeMutableRawPointer.allocate(bytes: 256 * 4 * 256, alignedTo: MemoryLayout.alignment) 84 | 85 | // create a context and pass in the textureData UnsafeMutablePointer as the storage location 86 | // for the image data 87 | let context = CGContext(data: textureData, width: 256, height: 256, bitsPerComponent: 8, bytesPerRow: 4 * 256, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 88 | 89 | // Draw the image into the context, this transfers the CGImage data into the 90 | // CGContext 91 | context?.draw(image!, in: CGRect(x: 0.0, y: 0.0, width: 256.0, height: 256.0)) 92 | 93 | // Generate and bind a texture buffer object 94 | glGenTextures(1, &tboID) 95 | glBindTexture(GLenum(GL_TEXTURE_2D), tboID) 96 | 97 | // Set up the parameters regarding how the bounds of the texture are handled 98 | // The min and mag filters tell OpenGL how to handle choosing pixels when the 99 | // the image is made bigger or smaller than it's actual size, we'll just set it 100 | // to linear interplation for now. 101 | // The wrap s and t paramters tell OpenGL what do do when the texture position 102 | // is outside the range of the texture. clamp to edge just continues the color. 103 | // Note that only the min and mag parameters are required, the wrapping parameters 104 | // are not. 105 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 106 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 107 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE) 108 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE) 109 | 110 | // Transfer the bitmap image data to the TBO 111 | // The target is the type of texture, we are using a 2D texture 112 | // The mipmap level is not turned on if you choose 0 113 | // The format we want the texture to be is rgba (GL_RGBA) 114 | // The number of pixels along the width 115 | // The number of pixel along the height 116 | // The number of pixels desired as a border around the texture (we want 0) 117 | // The format of the incoming bitamp data is also rgb (GL_RGBA) 118 | // The type of data coming in is in bytes which are > 0, so unsigned (GL_UNSIGNED_BYTE) 119 | // A pointer to the data itself 120 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), textureData) 121 | 122 | // Free up the textureData space we reserved from earlier. 123 | free(textureData) 124 | 125 | glGenBuffers(1, &vboID) 126 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 127 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 128 | 129 | glGenVertexArrays(1, &vaoID) 130 | glBindVertexArray(vaoID) 131 | 132 | // The first pointer is to the position data -- note the last two arguments: stride 133 | // and offset. 134 | // Stride - number of bytes from the start of one vertex to the start of the next 135 | // In other words, each vertex is made of 5 elements from the array (x, y, r, g, 136 | // and b). Each element is a GLfloat with is 4 bytes of data; therefore, 137 | // 5(4) = 20 bytes 138 | // Offset - number of bytes from the vertex's start address that must be passed by 139 | // before reaching the appropriate data 140 | // The position data starts at the start address; therefore, offset = 0 141 | // The color data starts 2 floats (2(4) = 8 bytes) from the vertex address; 142 | // therefore, offset = 8 143 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 28, UnsafePointer(bitPattern: 0)) 144 | glEnableVertexAttribArray(0) 145 | // The second pointer is to the color data. 146 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 28, UnsafePointer(bitPattern: 8)) 147 | glEnableVertexAttribArray(1) 148 | // The third pointer is to the texture data 149 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 28, UnsafePointer(bitPattern: 20)) 150 | glEnableVertexAttribArray(2) 151 | 152 | // Unbind the VAO so no further changes are made. 153 | glBindVertexArray(0) 154 | 155 | // Add a texture coordinate in and out attribute to pass the coordinates to the 156 | // fragment shader--make sure the set the out att. to the in att. value in main() 157 | // We'll use location 2 for texture coordinates 158 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 159 | var source = "#version 330 core \n" + 160 | "layout (location = 0) in vec2 position; \n" + 161 | "layout (location = 1) in vec3 color; \n" + 162 | "layout (location = 2) in vec2 texturePosition; \n" + 163 | "out vec3 passColor; \n" + 164 | "out vec2 passTexturePosition; \n" + 165 | "void main() \n" + 166 | "{ \n" + 167 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 168 | " passColor = color; \n" + 169 | " passTexturePosition = texturePosition; \n" + 170 | "} \n" 171 | let vss = source.cString(using: String.Encoding.ascii) 172 | var vssptr = UnsafePointer(vss) 173 | glShaderSource(vs, 1, &vssptr, nil) 174 | glCompileShader(vs) 175 | var compiled: GLint = 0 176 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 177 | if compiled <= 0 { 178 | Swift.print("Could not compile vertex, getting log") 179 | var logLength: GLint = 0 180 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 181 | Swift.print(" logLength = \(logLength)") 182 | if logLength > 0 { 183 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 184 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 185 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 186 | free(cLog) 187 | } 188 | } 189 | 190 | // It is really important to name the in attribute the same as the out attribute from 191 | // the vertex shader--otherwise the connection won't be between them. The name of the 192 | // out attribute does not matter. You just have to have an out and it has to be a 193 | // vec4: we have to account for the alpha component of a color. We can use the same 194 | // syntax for creating the vec4 color as we did the position in the vertex shader 195 | // (i.e. passing passColor for the frist three vertices and 1.0 for the fourth to make 196 | // a complete vec4 197 | // The uniform sampler2D is how the texture data gets into the shader. It's a uniform 198 | // because it is the same data every time the fragment is run, whereas the in attributes 199 | // change every time the fragment is run. 200 | // The outColor attribute uses a combination of the texture and color to produce an 201 | // output. The color part we have already seen. The texture() function has two 202 | // arguments: the texture to be used, and the position on the texture. 203 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 204 | source = "#version 330 core \n" + 205 | "uniform sampler2D sample; \n" + 206 | "in vec3 passColor; \n" + 207 | "in vec2 passTexturePosition; \n" + 208 | "out vec4 outColor; \n" + 209 | "void main() \n" + 210 | "{ \n" + 211 | " outColor = texture(sample, passTexturePosition) * vec4(passColor, 1.0); \n" + 212 | "} \n" 213 | let fss = source.cString(using: String.Encoding.ascii) 214 | var fssptr = UnsafePointer(fss) 215 | glShaderSource(fs, 1, &fssptr, nil) 216 | glCompileShader(fs) 217 | compiled = 0 218 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 219 | if compiled <= 0 { 220 | Swift.print("Could not compile fragement, getting log") 221 | var logLength: GLint = 0 222 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 223 | Swift.print(" logLength = \(logLength)") 224 | if logLength > 0 { 225 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 226 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 227 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 228 | free(cLog) 229 | } 230 | } 231 | 232 | glAttachShader(programID, vs) 233 | glAttachShader(programID, fs) 234 | glLinkProgram(programID) 235 | var linked: GLint = 0 236 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 237 | if linked <= 0 { 238 | Swift.print("Could not link, getting log") 239 | var logLength: GLint = 0 240 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 241 | Swift.print(" logLength = \(logLength)") 242 | if logLength > 0 { 243 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 244 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 245 | Swift.print("log: \(String.init(cString: cLog))") 246 | free(cLog) 247 | } 248 | } 249 | 250 | glDeleteShader(vs) 251 | glDeleteShader(fs) 252 | 253 | // Set the Texture Uniform with glGetUniformLocation 254 | // take care to type in "sample" properly otherwise you'll get an error 255 | // Pass in the location and the predefined variable that states which texture slot 256 | // we are filling--there are a total of 30 slots available. 257 | let sampleLocation = glGetUniformLocation(programID, "sample") 258 | glUniform1i(sampleLocation, GL_TEXTURE0) 259 | 260 | drawView() 261 | 262 | } 263 | 264 | override func draw(_ dirtyRect: NSRect) { 265 | super.draw(dirtyRect) 266 | 267 | // Drawing code here. 268 | 269 | drawView() 270 | 271 | } 272 | 273 | fileprivate func drawView() { 274 | 275 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 276 | 277 | glUseProgram(programID) 278 | glBindVertexArray(vaoID) 279 | 280 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 281 | 282 | glBindVertexArray(0) 283 | 284 | glFlush() 285 | } 286 | 287 | deinit { 288 | glDeleteVertexArrays(1, &vaoID) 289 | glDeleteBuffers(1, &vboID) 290 | glDeleteProgram(programID) 291 | glDeleteTextures(1, &tboID) // Delete the TBO 292 | } 293 | 294 | } 295 | -------------------------------------------------------------------------------- /GitHubPage/Point_in_the_Right_Direction(Setup).html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swift OpenGL Tutorials 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |

Setup For Drawing a Point On Screen

37 |
38 |

Now that have a view that allows us to draw to the 39 | screen, let's start drawing something. We'll start 40 | by drawing one vertex to the screen. That may sound 41 | boring, but drawing requires covering several concepts 42 | at once: shaders, shader programs (what artists are 43 | usually referring to when they talk about "shaders"), 44 | buffers (for data, attributes, etc.), and draw commands 45 | (we'll just cover one in this post). All of these 46 | concepts together are the foundations of a rendering 47 | engine: another concept that is thrown around, but has 48 | a rather ambiguous meaning. I've seen it used in 49 | regard to everything from full applications for making 50 | games, to bare bones API's. If you search for the 51 | definition of "rendering engine" on Google, you'll see 52 | a definition from PC Mag stating that a rendering 53 | engine is, essentially, "software that forms the text 54 | and images for display and printing". What we will be 55 | writing here fits that definition quite well.

56 |

Setting Up the Project

57 |

This step is entirely optional, but it allows you to 58 | keep the steps of your code all in one project. Xcode 59 | offers a "Snapshot" feature which you are welcome to 60 | use as well, but this rewrites the files in your 61 | current project to reflect the changes. Instead, we're 62 | going to use Schemes and Targets. Essentially this 63 | makes a new app within your project. This means all of 64 | your code is accessible right when you need it, and 65 | running a particular target (app) is as easy as 66 | selecting the appropriate scheme.

67 |

Before we set up a new scheme, let's work on the one 68 | we have. Every time you make a new scheme, you'll make 69 | a new target, info-plist, app, test target, test 70 | info-plist, app delegate, view controller, image assets 71 | folder, and storyboard. If you duplicate a target, you 72 | reuse the app delegate, view controller, image assets, 73 | and storyboard. In order to tell the difference between 74 | each target, we'll rename the target, info-plist, and 75 | product. Click on SwiftOpenGL at the top of the Project 76 | Navigator. Then in the Editor, click on the Target and 77 | press Return on the keyboard to change the name to 78 | Beginning.

79 | Renaming the Tartget 80 |

Also, rename the Group folder to Beginning as well. 81 | If you don't have a group, select AppDelegate.swift, 82 | ViewController.swift, SwiftOpenGLView.swift, 83 | images.xcassets, main.storyboard, 84 | and Supporting Files. Then right click on the selection 85 | and click on New Group from Selection. Name this group 86 | Beginning. In the Supporting Files folder, change the 87 | name of the Info.plist to Beginning-Info.plist. 88 | Once the name is changed, you need to let Xcode know 89 | what the plist is called now and where it is located. 90 | Before we do that though, let's clean up the project's 91 | folder in Finder. You can go straight to the folder to 92 | selecting any of the files in the Project Navigator 93 | (we'll select Beginning-Info.plist), and 94 | then click on the arrow next to the path name.

95 | Navigating to a Folder 96 |

This will open up the selected file in a Finder window.

97 | Make a New Folder 98 |

Within the SwiftOpenGL project folder, you'll see 99 | AppDelegate.swift, ViewController.swift, 100 | SwiftOpenGLView.swift, image.xcassets, 101 | and Base.proj. Select these items, right 102 | click on them, and click on New Folder with Selection 103 | (6 items). Name this folder Beginning. Close the 104 | window. In Xcode, you'll notice that you're folders are 105 | now red! You have to click on each and set the path 106 | manually. This is why we didn't set the Info.plist 107 | path earlier--we would have had to do that twice! 108 | Click on AppDelegate.swift. In the 109 | Utilities panel, on the File Inspector tab, click on 110 | the Folder icon instead of the arrow icon. This brings 111 | up a page down menu. Navigate to the Beginning folder 112 | where the AppDelegate.swift file is located. 113 | Select AppDelegate.swift, and click Choose.

114 | The Path Icon 115 | Choosing the File Path 116 |

Repeat this for ViewController.swift, 117 | SwiftOpenGLView.swift, and images.xcassets 118 | as well as the group folder Beginning (if you miss this 119 | one, look at post 1.2). For the Info.plist, 120 | click on the SwiftOpenGL project at the top of the 121 | Project Navigator. Then click on Build Settings. 122 | Search for "info.plist". Under Packaging 123 | you'll see see Info.plist File. To the right of that is 124 | the plist file path. Double click on the path that's 125 | there and change it to Beginning/Beginning-info.plist 126 | and press Return on the keyboard.

127 | Setting the Plist Path 128 |

Now we'll change the name of the scheme. At the top 129 | left of the window, to the right of the Stop button is 130 | the Scheme dropdown menu. Click on it and then click on 131 | Manage Schemes... at the bottom.

132 | Manage Schemes 133 |

Again, you see some of the future targets that we're 134 | going to create in the image above. The Manage Schemes 135 | menu lists all of the available schemes. You'll only 136 | have one target. Click on it and press Return on the 137 | keyboard. Rename the scheme as Beginning. Click close.

138 | Naming the Scheme 139 |

Test your app to make sure it still runs. If it does, 140 | you're set to move on to create the next target. Again, 141 | click on the SwiftOpenGL project in the Project Navigator. 142 | In the Editor, at the bottom left of the window, click 143 | on the "+".

144 | New Target 145 |

You'll see the same menu you saw when you created the 146 | project. Make sure Application under the OS X section 147 | is selected. Then select Cocoa Application and click 148 | Next. On the next window, the only change new thing 149 | here, is the Project field. There's only one option 150 | here: SwiftOpenGL. Set the Product Name as FirstVertex 151 | and all the other fields will be the same. Click Finish. 152 | Delete the FirstVertex Test target and its plist.

153 |

In the Project Navigator, make a new group of the 154 | FirstVertex's AppDelegate.swift, 155 | ViewController.swift, images.xcassets, 156 | main.storyboard, and Info.plist. 157 | You can also make a group called Supporting Files out 158 | the Info.plist, if you like.

159 | FirstVertex Group 160 |

Rename the Info.plist as FirstVertex-Info.plist 161 | as you see at the bottom of the image above. Now open 162 | a Finder window to location of these files like we did 163 | for the last target. Create a new folder in the 164 | SwiftOpenGL project folder called FirstVertex. Now 165 | you'll have Beginning and FirstVertex in your project 166 | folder. Move all of the FirstVertex files into the 167 | FirstVertex folder like you did for the Beginning target 168 | files.

169 |

Back in Xcode, set the paths for each file like we did 170 | before (remember to set the path for the 171 | FirstVertex-Info.plist through Build Phases). 172 | Change the name of the scheme for FirstVertex by 173 | clicking Manage Schemes... in the Scheme dropdown. Test 174 | the app by running it.

175 |

If you're app runs, you're ready to set up the UI just 176 | like the last post. I won't go back through that process, 177 | except for one detail. I want to remind you to select 178 | the right target when you create a file for the 179 | SwiftOpenGLView. Make sure you save the file to 180 | FirstVertex, select the FirstVertex group, and select 181 | the FirstVertex target, and not Beginning.

182 | Selecting the Right Target 183 |

Now you have two working targets. Make sure the scheme 184 | is set to FirstVertex and SwiftOpenGLView.swift 185 | is selected before you jump into writing the first 186 | version of the render engine.

187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |

Previous

196 |
197 |
198 |
199 |

Next

200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | 218 |
219 |
220 |
221 | 222 | 223 | -------------------------------------------------------------------------------- /08 StartAnimating/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 10/24/15. 6 | // Copyright © 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 8: Draws a simple animation using NSTimer 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | 15 | final class SwiftOpenGLView: NSOpenGLView { 16 | 17 | private var programID: GLuint = 0 18 | private var vaoID: GLuint = 0 19 | private var vboID: GLuint = 0 20 | private var tboID: GLuint = 0 21 | 22 | // The NSTimer for animating. 23 | private var timer = Timer() 24 | 25 | required init?(coder: NSCoder) { 26 | super.init(coder: coder) 27 | 28 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 29 | UInt32(NSOpenGLPFAAccelerated), 30 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 31 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 32 | UInt32(0) 33 | ] 34 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 35 | Swift.print("pixelFormat could not be constructed") 36 | return 37 | } 38 | self.pixelFormat = pixelFormat 39 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 40 | Swift.print("context could not be constructed") 41 | return 42 | } 43 | self.openGLContext = context 44 | 45 | } 46 | 47 | override func prepareOpenGL() { 48 | 49 | super.prepareOpenGL() 50 | 51 | glClearColor(0.0, 0.0, 0.0, 1.0) 52 | 53 | programID = glCreateProgram() 54 | 55 | //format: x, y, r, g, b, s, t, nx, ny, nz 56 | let data: [GLfloat] = [-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 2.0, -1.0, -1.0, 0.0001, 57 | 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0001, 58 | 1.0, -1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 1.0, -1.0, 0.0001] 59 | 60 | let fileURL = Bundle.main.url(forResource: "Texture", withExtension: "png") 61 | 62 | let dataProvider = CGDataProvider(url: fileURL! as CFURL) 63 | let image = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent) 64 | 65 | let textureData = UnsafeMutableRawPointer.allocate(bytes: 256 * 4 * 256, alignedTo: MemoryLayout.alignment) 66 | 67 | let context = CGContext(data: textureData, width: 256, height: 256, bitsPerComponent: 8, bytesPerRow: 4 * 256, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 68 | 69 | context?.draw(image!, in: CGRect(x: 0.0, y: 0.0, width: 256.0, height: 256.0)) 70 | 71 | glGenTextures(1, &tboID) 72 | glBindTexture(GLenum(GL_TEXTURE_2D), tboID) 73 | 74 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 75 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 76 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT) 77 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT) 78 | 79 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), textureData) 80 | 81 | free(textureData) 82 | 83 | glGenBuffers(1, &vboID) 84 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 85 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 86 | 87 | glGenVertexArrays(1, &vaoID) 88 | glBindVertexArray(vaoID) 89 | 90 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 0)) 91 | glEnableVertexAttribArray(0) 92 | 93 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 8)) 94 | glEnableVertexAttribArray(1) 95 | 96 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 20)) 97 | glEnableVertexAttribArray(2) 98 | 99 | glVertexAttribPointer(3, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern:28)) 100 | glEnableVertexAttribArray(3) 101 | 102 | glBindVertexArray(0) 103 | 104 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 105 | var source = "#version 330 core \n" + 106 | "layout (location = 0) in vec2 position; \n" + 107 | "layout (location = 1) in vec3 color; \n" + 108 | "layout (location = 2) in vec2 texturePosition; \n" + 109 | "layout (location = 3) in vec3 normal; \n" + 110 | "out vec3 passPosition; \n" + 111 | "out vec3 passColor; \n" + 112 | "out vec2 passTexturePosition; \n" + 113 | "out vec3 passNormal; \n" + 114 | "void main() \n" + 115 | "{ \n" + 116 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 117 | " passPosition = vec3(position, 0.0); \n" + 118 | " passColor = color; \n" + 119 | " passTexturePosition = texturePosition; \n" + 120 | " passNormal = normal; \n" + 121 | "} \n" 122 | let vss = source.cString(using: String.Encoding.ascii) 123 | var vssptr = UnsafePointer(vss) 124 | glShaderSource(vs, 1, &vssptr, nil) 125 | glCompileShader(vs) 126 | var compiled: GLint = 0 127 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 128 | if compiled <= 0 { 129 | Swift.print("Could not compile vertex, getting log") 130 | var logLength: GLint = 0 131 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 132 | Swift.print(" logLength = \(logLength)") 133 | if logLength > 0 { 134 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 135 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 136 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 137 | free(cLog) 138 | } 139 | } 140 | 141 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 142 | source = "#version 330 core \n" + 143 | "uniform sampler2D sample; \n" + 144 | // The Light uniform Struct allows us to more convenietly access the light attributes 145 | "uniform struct Light { \n" + 146 | " vec3 color; \n" + 147 | " vec3 position; \n" + 148 | " float ambient; \n" + 149 | " float specStrength; \n" + 150 | " float specHardness; \n" + 151 | "} light; \n" + 152 | "in vec3 passPosition; \n" + 153 | "in vec3 passColor; \n" + 154 | "in vec2 passTexturePosition; \n" + 155 | "in vec3 passNormal; \n" + 156 | "out vec4 outColor; \n" + 157 | "void main() \n" + 158 | "{ \n" + 159 | " vec3 normal = normalize(passNormal); \n" + 160 | " vec3 lightRay = normalize(light.position - passPosition); \n" + 161 | " float intensity = dot(normal, lightRay); \n" + 162 | " intensity = clamp(intensity, 0, 1); \n" + 163 | // viewer is the vector pointing from the fragment to the viewer 164 | " vec3 viewer = normalize(vec3(0.0, 0.0, 0.2) - passPosition); \n" + 165 | // reflect() calculates the reflection vector 166 | // first parameter is the incident ray 167 | // second parameter is the normal 168 | // we do not negate the lightRay because it is already pointing from the surface to the viewer 169 | // negating the vector would cause the reflection vector to point away from the viewer and no 170 | // highlight would seen. 171 | " vec3 reflection = reflect(lightRay, normal); \n" + 172 | // specular is calculated by taking the dot product of the viewer and reflection vectors, 173 | // ensuring those vectors are >=0.0 with max(), and then raising that value by the value 174 | // of hardness to adjust the hardness of the edge of the highlight. 175 | " float specular = pow(max(dot(viewer, reflection), 0.0), light.specHardness); \n" + 176 | // The specular component casts light so it must also be multiplied by the .color component. 177 | " vec3 light = light.ambient + light.color * intensity + light.specStrength * specular * light.color; \n" + 178 | " vec3 surface = texture(sample, passTexturePosition).rgb * passColor; \n" + 179 | " vec3 rgb = surface * light; \n" + 180 | " outColor = vec4(rgb, 1.0); \n" + 181 | "} \n" 182 | let fss = source.cString(using: String.Encoding.ascii) 183 | var fssptr = UnsafePointer(fss) 184 | glShaderSource(fs, 1, &fssptr, nil) 185 | glCompileShader(fs) 186 | compiled = 0 187 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 188 | if compiled <= 0 { 189 | Swift.print("Could not compile fragement, getting log") 190 | var logLength: GLint = 0 191 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 192 | Swift.print(" logLength = \(logLength)") 193 | if logLength > 0 { 194 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 195 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 196 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 197 | free(cLog) 198 | } 199 | } 200 | 201 | glAttachShader(programID, vs) 202 | glAttachShader(programID, fs) 203 | glLinkProgram(programID) 204 | var linked: GLint = 0 205 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 206 | if linked <= 0 { 207 | Swift.print("Could not link, getting log") 208 | var logLength: GLint = 0 209 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 210 | Swift.print(" logLength = \(logLength)") 211 | if logLength > 0 { 212 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 213 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 214 | Swift.print(" log: \n\t\(String.init(cString: cLog))") 215 | free(cLog) 216 | } 217 | } 218 | 219 | glDeleteShader(vs) 220 | glDeleteShader(fs) 221 | 222 | let sampleLocation = glGetUniformLocation(programID, "sample") 223 | glUniform1i(sampleLocation, GL_TEXTURE0) 224 | 225 | glUseProgram(programID) 226 | 227 | // Uniforms for the light struct. Each component is accessed using dot notation. 228 | glUniform3fv(glGetUniformLocation(programID, "light.color"), 1, [1.0, 1.0, 1.0]) 229 | glUniform3fv(glGetUniformLocation(programID, "light.position"), 1, [0.0, 1.0, 0.1]) 230 | glUniform1f(glGetUniformLocation(programID, "light.ambient"), 0.25) 231 | glUniform1f(glGetUniformLocation(programID, "light.specStrength"), 1.0) 232 | glUniform1f(glGetUniformLocation(programID, "light.specHardness"), 32) 233 | 234 | drawView() 235 | 236 | // Now that the pipeline is set, we'll start the timer. 237 | // First, tell the context how often to look for a new frame. A value of 1 indicates 238 | // our tartget is 60 frames per second 239 | self.openGLContext?.setValues([1], for: .swapInterval) 240 | 241 | // This line may also be placed in init(_:), but it makes more sense to create and add 242 | // it to the loop when were ready to start animating. No sense in wasting computation time. 243 | // Time interval is the time until the timer fires: 0.001 = firing in 1/1000 of a second 244 | // Target is the object that will call a mthod once the timer fires, self indcates this 245 | // method is in SwiftOpenGLView. 246 | // Selector is the method that is to be called when the timer fires. In Swift, a string 247 | // literal of the method name may be passed. 248 | // UserInfo allows you to add additional parameters to the timer that may be retrieved 249 | // for use in the selector 250 | // Repeats indicates if this timer is to fire continuously at the interval specified 251 | // in the timerInterval parameter. true indicates we want to continue firing 252 | self.timer = Timer(timeInterval: 0.001, target: self, selector: #selector(SwiftOpenGLView.redraw), userInfo: nil, repeats: true) 253 | 254 | // Once the timer is created, we need to add it to the default run loop and the event 255 | // Essentially the default loop is for general application loop, while the event loop 256 | // is for firing during events like dragging the view around and clicking within the view 257 | RunLoop.current.add(self.timer, forMode: RunLoopMode.defaultRunLoopMode) 258 | RunLoop.current.add(self.timer, forMode: RunLoopMode.eventTrackingRunLoopMode) 259 | 260 | } 261 | 262 | // The function to be called when the timer fires. 263 | // display() is a function owned by NSView that recalls the lockFocus, drawRect(_:), 264 | // and unlockFocus of the subview and each subview. 265 | // The SwiftOpenGLView.drawRect(_:) calls drawView() as part of it's definition 266 | @objc func redraw() { 267 | 268 | self.display() 269 | 270 | } 271 | 272 | override func draw(_ dirtyRect: NSRect) { 273 | super.draw(dirtyRect) 274 | 275 | // Drawing code here. 276 | 277 | drawView() 278 | 279 | } 280 | 281 | private func drawView() { 282 | 283 | // To make the animation visible, we'll change the background color over time. 284 | // CACurrentMediaTime() returns the amount of time since the app started. 285 | // sin() is applied to this value and then a float value is made from it. 286 | // glClearColor() takes four floats to create an rgba color. We have not activated 287 | // blending, so no matter what value we pass here is ignored. 288 | let value = Float(sin(CACurrentMediaTime())) 289 | glClearColor(value, value, value, 1.0) 290 | 291 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 292 | 293 | glUseProgram(programID) 294 | glBindVertexArray(vaoID) 295 | 296 | glDrawArrays(GLenum(GL_TRIANGLES), 0, 3) 297 | 298 | glBindVertexArray(0) 299 | 300 | glFlush() 301 | } 302 | 303 | deinit { 304 | // Stop and the timer and remove it from the run loop. 305 | self.timer.invalidate() 306 | glDeleteVertexArrays(1, &vaoID) 307 | glDeleteBuffers(1, &vboID) 308 | glDeleteProgram(programID) 309 | glDeleteTextures(1, &tboID) 310 | } 311 | 312 | } 313 | -------------------------------------------------------------------------------- /09 AnimationNext/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 10/24/15. 6 | // Copyright © 2015 MyKo. All rights reserved. 7 | // 8 | // Ver. 9: Draws a simple animation using CVDisplayLink 9 | // 10 | 11 | import Cocoa 12 | import OpenGL.GL3 13 | 14 | 15 | final class SwiftOpenGLView: NSOpenGLView { 16 | 17 | fileprivate var programID: GLuint = 0 18 | fileprivate var vaoID: GLuint = 0 19 | fileprivate var vboID: GLuint = 0 20 | fileprivate var tboID: GLuint = 0 21 | 22 | fileprivate var data = [GLfloat]() 23 | 24 | // The CVDisplayLink for animating. Optional value initialized to nil. 25 | fileprivate var displayLink: CVDisplayLink? 26 | 27 | required init?(coder: NSCoder) { 28 | super.init(coder: coder) 29 | 30 | // We'll use double buffering this time (one buffer is displayed while the other is 31 | // calculated, then we swap them. 32 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 33 | UInt32(NSOpenGLPFAAccelerated), 34 | UInt32(NSOpenGLPFADoubleBuffer), 35 | UInt32(NSOpenGLPFAColorSize), UInt32(32), 36 | UInt32(NSOpenGLPFAOpenGLProfile), UInt32(NSOpenGLProfileVersion3_2Core), 37 | UInt32(0) 38 | ] 39 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 40 | Swift.print("pixelFormat could not be constructed") 41 | return 42 | } 43 | self.pixelFormat = pixelFormat 44 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 45 | Swift.print("context could not be constructed") 46 | return 47 | } 48 | self.openGLContext = context 49 | 50 | // Set the context's swap interval parameter to 60Hz (i.e. 1 frame per swamp) 51 | self.openGLContext?.setValues([1], for: .swapInterval) 52 | 53 | } 54 | 55 | override func prepareOpenGL() { 56 | 57 | super.prepareOpenGL() 58 | 59 | glClearColor(0.0, 0.0, 0.0, 1.0) 60 | 61 | programID = glCreateProgram() 62 | 63 | //format: x, y, r, g, b, s, t, nx, ny, nz 64 | data = [-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 2.0, -1.0, -1.0, 0.0001, 65 | 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0001, 66 | 1.0, -1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 1.0, -1.0, 0.0001] 67 | 68 | let fileURL = Bundle.main.url(forResource: "Texture", withExtension: "png") 69 | 70 | let dataProvider = CGDataProvider(url: fileURL! as CFURL) 71 | let image = CGImage(pngDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent) 72 | 73 | let textureData = UnsafeMutableRawPointer.allocate(bytes: 256 * 4 * 256, alignedTo: MemoryLayout.alignment) 74 | 75 | let context = CGContext(data: textureData, width: 256, height: 256, bitsPerComponent: 8, bytesPerRow: 4 * 256, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 76 | 77 | context?.draw(image!, in: CGRect(x: 0.0, y: 0.0, width: 256.0, height: 256.0)) 78 | 79 | glGenTextures(1, &tboID) 80 | glBindTexture(GLenum(GL_TEXTURE_2D), tboID) 81 | 82 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR) 83 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR) 84 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT) 85 | glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT) 86 | 87 | glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, 256, 256, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), textureData) 88 | 89 | free(textureData) 90 | 91 | glGenBuffers(1, &vboID) 92 | glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID) 93 | glBufferData(GLenum(GL_ARRAY_BUFFER), data.count * MemoryLayout.size, data, GLenum(GL_STATIC_DRAW)) 94 | 95 | glGenVertexArrays(1, &vaoID) 96 | glBindVertexArray(vaoID) 97 | 98 | glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 0)) 99 | glEnableVertexAttribArray(0) 100 | 101 | glVertexAttribPointer(1, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 8)) 102 | glEnableVertexAttribArray(1) 103 | 104 | glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern: 20)) 105 | glEnableVertexAttribArray(2) 106 | 107 | glVertexAttribPointer(3, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 40, UnsafePointer(bitPattern:28)) 108 | glEnableVertexAttribArray(3) 109 | 110 | glBindVertexArray(0) 111 | 112 | let vs = glCreateShader(GLenum(GL_VERTEX_SHADER)) 113 | var source = "#version 330 core \n" + 114 | "layout (location = 0) in vec2 position; \n" + 115 | "layout (location = 1) in vec3 color; \n" + 116 | "layout (location = 2) in vec2 texturePosition; \n" + 117 | "layout (location = 3) in vec3 normal; \n" + 118 | "out vec3 passPosition; \n" + 119 | "out vec3 passColor; \n" + 120 | "out vec2 passTexturePosition; \n" + 121 | "out vec3 passNormal; \n" + 122 | "void main() \n" + 123 | "{ \n" + 124 | " gl_Position = vec4(position, 0.0, 1.0); \n" + 125 | " passPosition = vec3(position, 0.0); \n" + 126 | " passColor = color; \n" + 127 | " passTexturePosition = texturePosition; \n" + 128 | " passNormal = normal; \n" + 129 | "} \n" 130 | let vss = source.cString(using: String.Encoding.ascii) 131 | var vssptr = UnsafePointer(vss) 132 | glShaderSource(vs, 1, &vssptr, nil) 133 | glCompileShader(vs) 134 | var compiled: GLint = 0 135 | glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled) 136 | if compiled <= 0 { 137 | Swift.print("Could not compile vertex, getting log") 138 | var logLength: GLint = 0 139 | glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength) 140 | Swift.print(" logLength = \(logLength)") 141 | if logLength > 0 { 142 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 143 | glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog) 144 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 145 | free(cLog) 146 | } 147 | } 148 | 149 | let fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER)) 150 | source = "#version 330 core \n" + 151 | "uniform sampler2D sample; \n" + 152 | "uniform struct Light { \n" + 153 | " vec3 color; \n" + 154 | " vec3 position; \n" + 155 | " float ambient; \n" + 156 | " float specStrength; \n" + 157 | " float specHardness; \n" + 158 | "} light; \n" + 159 | "in vec3 passPosition; \n" + 160 | "in vec3 passColor; \n" + 161 | "in vec2 passTexturePosition; \n" + 162 | "in vec3 passNormal; \n" + 163 | "out vec4 outColor; \n" + 164 | "void main() \n" + 165 | "{ \n" + 166 | " vec3 normal = normalize(passNormal); \n" + 167 | " vec3 lightRay = normalize(light.position - passPosition); \n" + 168 | " float intensity = dot(normal, lightRay); \n" + 169 | " intensity = clamp(intensity, 0, 1); \n" + 170 | " vec3 viewer = normalize(vec3(0.0, 0.0, 0.2) - passPosition); \n" + 171 | " vec3 reflection = reflect(lightRay, normal); \n" + 172 | " float specular = pow(max(dot(viewer, reflection), 0.0), light.specHardness); \n" + 173 | " vec3 light = light.ambient + light.color * intensity + light.specStrength * specular * light.color; \n" + 174 | " vec3 surface = texture(sample, passTexturePosition).rgb * passColor; \n" + 175 | " vec3 rgb = surface * light; \n" + 176 | " outColor = vec4(rgb, 1.0); \n" + 177 | "} \n" 178 | let fss = source.cString(using: String.Encoding.ascii) 179 | var fssptr = UnsafePointer(fss) 180 | glShaderSource(fs, 1, &fssptr, nil) 181 | glCompileShader(fs) 182 | compiled = 0 183 | glGetShaderiv(fs, GLbitfield(GL_COMPILE_STATUS), &compiled) 184 | if compiled <= 0 { 185 | Swift.print("Could not compile fragement, getting log") 186 | var logLength: GLint = 0 187 | glGetShaderiv(fs, GLbitfield(GL_INFO_LOG_LENGTH), &logLength) 188 | Swift.print(" logLength = \(logLength)") 189 | if logLength > 0 { 190 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 191 | glGetShaderInfoLog(fs, GLsizei(logLength), &logLength, cLog) 192 | Swift.print(" log = \n\t\(String.init(cString: cLog))") 193 | free(cLog) 194 | } 195 | } 196 | 197 | glAttachShader(programID, vs) 198 | glAttachShader(programID, fs) 199 | glLinkProgram(programID) 200 | var linked: GLint = 0 201 | glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked) 202 | if linked <= 0 { 203 | Swift.print("Could not link, getting log") 204 | var logLength: GLint = 0 205 | glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength) 206 | Swift.print(" logLength = \(logLength)") 207 | if logLength > 0 { 208 | let cLog = UnsafeMutablePointer.allocate(capacity: Int(logLength)) 209 | glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog) 210 | Swift.print(" log: \n\t\(String.init(cString: cLog))") 211 | free(cLog) 212 | } 213 | } 214 | 215 | glDeleteShader(vs) 216 | glDeleteShader(fs) 217 | 218 | let sampleLocation = glGetUniformLocation(programID, "sample") 219 | glUniform1i(sampleLocation, GL_TEXTURE0) 220 | 221 | glUseProgram(programID) 222 | 223 | // Uniforms for the light struct. Each component is accessed using dot notation. 224 | glUniform3fv(glGetUniformLocation(programID, "light.color"), 1, [1.0, 1.0, 1.0]) 225 | glUniform3fv(glGetUniformLocation(programID, "light.position"), 1, [0.0, 1.0, 0.1]) 226 | glUniform1f(glGetUniformLocation(programID, "light.ambient"), 0.25) 227 | glUniform1f(glGetUniformLocation(programID, "light.specStrength"), 1.0) 228 | glUniform1f(glGetUniformLocation(programID, "light.specHardness"), 32) 229 | 230 | /* Now that the OpenGL pipeline is defined, declare a callback for our CVDisplayLink. 231 | There are three ways to do this: declare a function, declare a computed property, 232 | or pass in a closure to CVDisplayLinkSetOutputCallback(). Using each requires 233 | subtle changes in the CVDisplayLinkSetOutputCallback()'s argument list. 234 | */ 235 | let displayLinkOutputCallback: CVDisplayLinkOutputCallback = {(displayLink: CVDisplayLink, inNow: UnsafePointer, inOutputTime: UnsafePointer, flagsIn: CVOptionFlags, flagsOut: UnsafeMutablePointer, displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in 236 | 237 | /* The displayLinkContext in CVDisplayLinkOutputCallback's parameter list is the 238 | view being driven by the CVDisplayLink. In order to use the context as an 239 | instance of SwiftOpenGLView (which has our drawView() method). We need to use 240 | unsafeBitCast() to cast this context as a SwiftOpenGLView. 241 | */ 242 | unsafeBitCast(displayLinkContext, to: SwiftOpenGLView.self).drawView() 243 | 244 | // We are going to assume that everything went well, and success as the CVReturn 245 | return kCVReturnSuccess 246 | } 247 | 248 | /* Grab the a link to the active displays, set the callback defined above, and start 249 | the link. An alternative to a nested function is a global function or a closure 250 | passed as the argument--a local function (i.e. a function defined within the 251 | class) is NOT allowed. The 252 | UnsafeMutableRawPointer(unmanaged.passUnretained(self).toOpaque()) passes a 253 | pointer to an instance of SwiftOpenGLView. UnsafeMutableRawPointer is a new type 254 | Swift 3.0 that 255 | */ 256 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) 257 | CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())) 258 | CVDisplayLinkStart(displayLink!) 259 | 260 | // Test render 261 | drawView() 262 | 263 | } 264 | 265 | override func draw(_ dirtyRect: NSRect) { 266 | super.draw(dirtyRect) 267 | 268 | // Drawing code here. 269 | 270 | drawView() 271 | 272 | } 273 | 274 | fileprivate func drawView() { 275 | 276 | // Grab a context, make it the active context for drawing, and then lock the focus 277 | // before making OpenGL calls that change state or data within objects. 278 | guard let context = self.openGLContext else { 279 | // Just a filler error 280 | Swift.print("oops") 281 | return 282 | } 283 | 284 | context.makeCurrentContext() 285 | CGLLockContext(context.cglContextObj!) 286 | 287 | // To make the animation visible, we'll change the background color over time. 288 | // CACurrentMediaTime() returns the amount of time since the app started. 289 | // sin() is applied to this value and then a float value is made from it. 290 | // glClearColor() takes four floats to create an rgba color. We have not activated 291 | // blending, so no matter what value we pass here is ignored. 292 | let value = Float(sin(CACurrentMediaTime())) 293 | glClearColor(value, value, value, 1.0) 294 | 295 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 296 | 297 | glUseProgram(programID) 298 | glBindVertexArray(vaoID) 299 | 300 | glDrawArrays(GLenum(GL_TRIANGLES), 0, GLsizei(data.count)) 301 | 302 | glBindVertexArray(0) 303 | 304 | // glFlush() is replaced with CGLFlushDrawable() and swaps the buffer being displayed 305 | CGLFlushDrawable(context.cglContextObj!) 306 | CGLUnlockContext(context.cglContextObj!) 307 | } 308 | 309 | deinit { 310 | // Stop the display link. 311 | CVDisplayLinkStop(displayLink!) 312 | glDeleteVertexArrays(1, &vaoID) 313 | glDeleteBuffers(1, &vboID) 314 | glDeleteProgram(programID) 315 | glDeleteTextures(1, &tboID) 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /11 OpenGLMVCPt1/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles Schultz on 9/30/16. 6 | // Copyright © 2016 MyKo. All rights reserved. 7 | // 8 | // Ver. 11: Part 1 of MVC design. Vector and Matrix related 9 | // code removed to separate files. This marks the first 10 | // step in creating the model of the framework. 11 | // 12 | 13 | import Cocoa 14 | import OpenGL.GL3 15 | 16 | 17 | final class SwiftOpenGLView: NSOpenGLView { 18 | // Replace the previous properties with out Shader, VertexArrayObject 19 | // VertexBufferObject, and TextureBufferObject types. We don't have 20 | // to worry about crashing the system if we initialize them right 21 | // away because the OpenGL code will be initiated later in 22 | // `prepareOpenGL`. 23 | // Also decalre the data array to be of type [Vertex] 24 | fileprivate var shader = Shader() 25 | fileprivate var vao = VertexArrayObject() 26 | fileprivate var vbo = VertexBufferObject() 27 | fileprivate var tbo = TextureBufferObject() 28 | fileprivate var data = [Vertex]() 29 | 30 | fileprivate var previousTime = CFTimeInterval() 31 | 32 | // Make the view and projection matrices of type `FloatMatrix4` 33 | fileprivate var view = FloatMatrix4() 34 | fileprivate var projection = FloatMatrix4() 35 | 36 | // The CVDisplayLink for animating. Optional value initialized to nil. 37 | fileprivate var displayLink: CVDisplayLink? 38 | 39 | // In order to recieve keyboard input, we need to enable the view to accept first responder status 40 | override var acceptsFirstResponder: Bool { return true } 41 | 42 | required init?(coder: NSCoder) { 43 | super.init(coder: coder) 44 | 45 | // We'll use double buffering this time (one buffer is displayed while the other is 46 | // calculated, then we swap them. 47 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 48 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAAccelerated), 49 | NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer), 50 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32, 51 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile), NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core), 52 | 0 53 | ] 54 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 55 | Swift.print("pixelFormat could not be constructed") 56 | return 57 | } 58 | self.pixelFormat = pixelFormat 59 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 60 | Swift.print("context could not be constructed") 61 | return 62 | } 63 | self.openGLContext = context 64 | 65 | // Set the context's swap interval parameter to 60Hz (i.e. 1 frame per swamp) 66 | self.openGLContext?.setValues([1], for: .swapInterval) 67 | } 68 | 69 | override func prepareOpenGL() { 70 | super.prepareOpenGL() 71 | 72 | glClearColor(0.0, 0.0, 0.0, 1.0) 73 | 74 | // Fill the data array with our new `Vertex` type. We'll also take 75 | // this opportunity to make a new 3D mesh. 76 | data = [ 77 | Vertex(position: Float3(x: -1.0, y: -1.0, z: 1.0), /* Front face 1 */ 78 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 79 | textureCoordinate: Float2(x: 0.0, y: 0.0), 80 | color: Float3(x: 1.0, y: 0.0, z: 0.0)), 81 | Vertex(position: Float3(x: 1.0, y: -1.0, z: 1.0), 82 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 83 | textureCoordinate: Float2(x: 1.0, y: 0.0), 84 | color: Float3(x: 0.0, y: 0.0, z: 1.0)), 85 | Vertex(position: Float3(x: 1.0, y: 1.0, z: 1.0), 86 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 87 | textureCoordinate: Float2(x: 1.0, y: 1.0), 88 | color: Float3(x: 0.0, y: 1.0, z: 0.0)), 89 | 90 | Vertex(position: Float3(x: 1.0, y: 1.0, z: 1.0), /* Front face 2 */ 91 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 92 | textureCoordinate: Float2(x: 1.0, y: 1.0), 93 | color: Float3(x: 0.0, y: 1.0, z: 0.0)), 94 | Vertex(position: Float3(x: -1.0, y: 1.0, z: 1.0), 95 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 96 | textureCoordinate: Float2(x: 0.0, y: 1.0), 97 | color: Float3(x: 1.0, y: 1.0, z: 1.0)), 98 | Vertex(position: Float3(x: -1.0, y: -1.0, z: 1.0), 99 | normal: Float3(x: 0.0, y: 0.0, z: 1.0), 100 | textureCoordinate: Float2(x: 0.0, y: 0.0), 101 | color: Float3(x: 1.0, y: 0.0, z: 0.0)), 102 | 103 | Vertex(position: Float3(x: 1.0, y: -1.0, z: 1.0), /* Right face 1 */ 104 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 105 | textureCoordinate: Float2(x: 0.0, y: 0.0), 106 | color: Float3(x: 0.0, y: 0.0, z: 1.0)), 107 | Vertex(position: Float3(x: 1.0, y: -1.0, z: -1.0), 108 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 109 | textureCoordinate: Float2(x: 1.0, y: 0.0), 110 | color: Float3(x: 1.0, y: 1.0, z: 0.0)), 111 | Vertex(position: Float3(x: 1.0, y: 1.0, z: -1.0), 112 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 113 | textureCoordinate: Float2(x: 1.0, y: 1.0), 114 | color: Float3(x: 0.0, y: 1.0, z: 1.0)), 115 | 116 | Vertex(position: Float3(x: 1.0, y: 1.0, z: -1.0), /* Right face 2 */ 117 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 118 | textureCoordinate: Float2(x: 1.0, y: 1.0), 119 | color: Float3(x: 0.0, y: 1.0, z: 1.0)), 120 | Vertex(position: Float3(x: 1.0, y: 1.0, z: 1.0), 121 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 122 | textureCoordinate: Float2(x: 0.0, y: 1.0), 123 | color: Float3(x: 0.0, y: 1.0, z: 0.0)), 124 | Vertex(position: Float3(x: 1.0, y: -1.0, z: 1.0), 125 | normal: Float3(x: 1.0, y: 0.0, z: 0.0), 126 | textureCoordinate: Float2(x: 0.0, y: 0.0), 127 | color: Float3(x: 0.0, y: 0.0, z: 1.0)), 128 | 129 | Vertex(position: Float3(x: 1.0, y: -1.0, z: -1.0), /* Back face 1 */ 130 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 131 | textureCoordinate: Float2(x: 0.0, y: 0.0), 132 | color: Float3(x: 1.0, y: 1.0, z: 0.0)), 133 | Vertex(position: Float3(x: -1.0, y: -1.0, z: -1.0), 134 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 135 | textureCoordinate: Float2(x: 1.0, y: 0.0), 136 | color: Float3(x: 0.0, y: 0.0, z: 0.0)), 137 | Vertex(position: Float3(x: -1.0, y: 1.0, z: -1.0), 138 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 139 | textureCoordinate: Float2(x: 1.0, y: 1.0), 140 | color: Float3(x: 1.0, y: 0.0, z: 1.0)), 141 | 142 | Vertex(position: Float3(x: -1.0, y: 1.0, z: -1.0), /* Back face 2 */ 143 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 144 | textureCoordinate: Float2(x: 1.0, y: 1.0), 145 | color: Float3(x: 1.0, y: 0.0, z: 1.0)), 146 | Vertex(position: Float3(x: 1.0, y: 1.0, z: -1.0), 147 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 148 | textureCoordinate: Float2(x: 0.0, y: 1.0), 149 | color: Float3(x: 0.0, y: 1.0, z: 1.0)), 150 | Vertex(position: Float3(x: 1.0, y: -1.0, z: -1.0), 151 | normal: Float3(x: 0.0, y: 0.0, z: -1.0), 152 | textureCoordinate: Float2(x: 0.0, y: 0.0), 153 | color: Float3(x: 1.0, y: 1.0, z: 0.0)), 154 | 155 | Vertex(position: Float3(x: -1.0, y: -1.0, z: -1.0), /* Left face 1 */ 156 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 157 | textureCoordinate: Float2(x: 0.0, y: 0.0), 158 | color: Float3(x: 0.0, y: 0.0, z: 0.0)), 159 | Vertex(position: Float3(x: -1.0, y: -1.0, z: 1.0), 160 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 161 | textureCoordinate: Float2(x: 1.0, y: 0.0), 162 | color: Float3(x: 1.0, y: 0.0, z: 0.0)), 163 | Vertex(position: Float3(x: -1.0, y: 1.0, z: 1.0), 164 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 165 | textureCoordinate: Float2(x: 1.0, y: 1.0), 166 | color: Float3(x: 1.0, y: 1.0, z: 1.0)), 167 | 168 | Vertex(position: Float3(x: -1.0, y: 1.0, z: 1.0), /* Left face 2 */ 169 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 170 | textureCoordinate: Float2(x: 1.0, y: 1.0), 171 | color: Float3(x: 1.0, y: 1.0, z: 1.0)), 172 | Vertex(position: Float3(x: -1.0, y: 1.0, z: -1.0), 173 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 174 | textureCoordinate: Float2(x: 0.0, y: 1.0), 175 | color: Float3(x: 1.0, y: 0.0, z: 1.0)), 176 | Vertex(position: Float3(x: -1.0, y: -1.0, z: -1.0), 177 | normal: Float3(x: -1.0, y: 0.0, z: 0.0), 178 | textureCoordinate: Float2(x: 0.0, y: 0.0), 179 | color: Float3(x: 0.0, y: 0.0, z: 0.0)), 180 | 181 | Vertex(position: Float3(x: -1.0, y: -1.0, z: 1.0), /* Bottom face 1 */ 182 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 183 | textureCoordinate: Float2(x: 0.0, y: 0.0), 184 | color: Float3(x: 1.0, y: 0.0, z: 0.0)), 185 | Vertex(position: Float3(x: -1.0, y: -1.0, z: -1.0), 186 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 187 | textureCoordinate: Float2(x: 1.0, y: 0.0), 188 | color: Float3(x: 0.0, y: 0.0, z: 0.0)), 189 | Vertex(position: Float3(x: 1.0, y: -1.0, z: -1.0), 190 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 191 | textureCoordinate: Float2(x: 1.0, y: 1.0), 192 | color: Float3(x: 1.0, y: 1.0, z: 0.0)), 193 | 194 | Vertex(position: Float3(x: 1.0, y: -1.0, z: -1.0), /* Bottom face 2 */ 195 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 196 | textureCoordinate: Float2(x: 1.0, y: 1.0), 197 | color: Float3(x: 1.0, y: 1.0, z: 0.0)), 198 | Vertex(position: Float3(x: 1.0, y: -1.0, z: 1.0), 199 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 200 | textureCoordinate: Float2(x: 0.0, y: 1.0), 201 | color: Float3(x: 0.0, y: 0.0, z: 1.0)), 202 | Vertex(position: Float3(x: -1.0, y: -1.0, z: 1.0), 203 | normal: Float3(x: 0.0, y: -1.0, z: 0.0), 204 | textureCoordinate: Float2(x: 0.0, y: 0.0), 205 | color: Float3(x: 1.0, y: 0.0, z: 0.0)), 206 | 207 | Vertex(position: Float3(x: -1.0, y: 1.0, z: 1.0), /* Top face 1 */ 208 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 209 | textureCoordinate: Float2(x: 0.0, y: 0.0), 210 | color: Float3(x: 1.0, y: 1.0, z: 1.0)), 211 | Vertex(position: Float3(x: 1.0, y: 1.0, z: 1.0), 212 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 213 | textureCoordinate: Float2(x: 0.0, y: 1.0), 214 | color: Float3(x: 0.0, y: 1.0, z: 0.0)), 215 | Vertex(position: Float3(x: 1.0, y: 1.0, z: -1.0), 216 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 217 | textureCoordinate: Float2(x: 1.0, y: 1.0), 218 | color: Float3(x: 0.0, y: 1.0, z: 1.0)), 219 | 220 | Vertex(position: Float3(x: 1.0, y: 1.0, z: -1.0), /* Top face 2 */ 221 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 222 | textureCoordinate: Float2(x: 1.0, y: 1.0), 223 | color: Float3(x: 0.0, y: 1.0, z: 1.0)), 224 | Vertex(position: Float3(x: -1.0, y: 1.0, z: -1.0), 225 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 226 | textureCoordinate: Float2(x: 0.0, y: 1.0), 227 | color: Float3(x: 1.0, y: 0.0, z: 1.0)), 228 | Vertex(position: Float3(x: -1.0, y: 1.0, z: 1.0), 229 | normal: Float3(x: 0.0, y: 1.0, z: 0.0), 230 | textureCoordinate: Float2(x: 0.0, y: 0.0), 231 | color: Float3(x: 1.0, y: 1.0, z: 1.0)) 232 | ] 233 | 234 | // This is where OpenGL initialization is going to happen. Also of our 235 | // OpenGLObject's may now safely run their initialization code here. 236 | 237 | // Load the texture--make sure you have a texture named "Texture" in your 238 | // Assets.xcassets folder. 239 | tbo.loadTexture(named: "Texture") 240 | 241 | // Load our new data into the VBO. 242 | vbo.load(data) 243 | 244 | // Now we tell OpenGL what our vertex layout looks like. 245 | vao.layoutVertexPattern() 246 | 247 | // Define our view and projection matrices 248 | view = FloatMatrix4().translate(x: 0.0, y: 0.0, z: -5.0) 249 | projection = FloatMatrix4.projection(aspect: Float(bounds.size.width / bounds.size.height)) 250 | 251 | // Declare our Vertex and Fragment shader source code. 252 | let vertexSource = "#version 330 core \n" + 253 | "layout (location = 0) in vec3 position; \n" + 254 | "layout (location = 1) in vec3 normal; \n" + 255 | "layout (location = 2) in vec2 texturePosition; \n" + 256 | "layout (location = 3) in vec3 color; \n" + 257 | "out vec3 passPosition; \n" + 258 | "out vec3 passNormal; \n" + 259 | "out vec2 passTexturePosition; \n" + 260 | "out vec3 passColor; \n" + 261 | "uniform mat4 view; \n" + 262 | "uniform mat4 projection; \n" + 263 | "void main() \n" + 264 | "{ \n" + 265 | " gl_Position = projection * view * vec4(position, 1.0); \n" + 266 | " passPosition = position; \n" + 267 | " passNormal = normal; \n" + 268 | " passTexturePosition = texturePosition; \n" + 269 | " passColor = color; \n" + 270 | "} \n" 271 | let fragmentSource = "#version 330 core \n" + 272 | "uniform sampler2D sample; \n" + 273 | "uniform struct Light { \n" + 274 | " vec3 color; \n" + 275 | " vec3 position; \n" + 276 | " float ambient; \n" + 277 | " float specStrength; \n" + 278 | " float specHardness; \n" + 279 | "} light; \n" + 280 | "in vec3 passPosition; \n" + 281 | "in vec3 passNormal; \n" + 282 | "in vec2 passTexturePosition; \n" + 283 | "in vec3 passColor; \n" + 284 | "out vec4 outColor; \n" + 285 | "void main() \n" + 286 | "{ \n" + 287 | " vec3 normal = normalize(passNormal); \n" + 288 | " vec3 lightRay = normalize(light.position - passPosition); \n" + 289 | " float intensity = dot(normal, lightRay); \n" + 290 | " intensity = clamp(intensity, 0, 1); \n" + 291 | " vec3 viewer = normalize(vec3(0.0, 0.0, 0.2) - passPosition); \n" + 292 | " vec3 reflection = reflect(lightRay, normal); \n" + 293 | " float specular = pow(max(dot(viewer, reflection), 0.0), light.specHardness); \n" + 294 | " vec3 light = light.ambient + light.color * intensity + light.specStrength * specular * light.color; \n" + 295 | " vec3 surface = texture(sample, passTexturePosition).rgb * passColor; \n" + 296 | " vec3 rgb = surface * light; \n" + 297 | " outColor = vec4(rgb, 1.0); \n" + 298 | "} \n" 299 | 300 | // Pass in the source code to create our shader object and then set 301 | // it's uniforms. 302 | shader.create(withVertex: vertexSource, andFragment: fragmentSource) 303 | shader.setInitialUniforms() 304 | 305 | // We'll deal with this guy soon, but for now no changes here. 306 | let displayLinkOutputCallback: CVDisplayLinkOutputCallback = {(displayLink: CVDisplayLink, inNow: UnsafePointer, inOutputTime: UnsafePointer, flagsIn: CVOptionFlags, flagsOut: UnsafeMutablePointer, displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in 307 | unsafeBitCast(displayLinkContext, to: SwiftOpenGLView.self).drawView() 308 | 309 | return kCVReturnSuccess 310 | } 311 | 312 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) 313 | CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())) 314 | CVDisplayLinkStart(displayLink!) 315 | } 316 | 317 | override func draw(_ dirtyRect: NSRect) { 318 | drawView() 319 | } 320 | 321 | fileprivate func drawView() { 322 | guard let context = self.openGLContext else { 323 | Swift.print("oops") 324 | return 325 | } 326 | 327 | context.makeCurrentContext() 328 | context.lock() 329 | 330 | let time = CACurrentMediaTime() 331 | 332 | let value = Float(sin(time)) 333 | previousTime = time 334 | 335 | glClearColor(GLfloat(value), GLfloat(value), GLfloat(value), 1.0) 336 | 337 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 338 | glEnable(GLenum(GL_CULL_FACE)) 339 | 340 | // Bind the shader and VAO 341 | shader.bind() 342 | vao.bind() 343 | 344 | // reset any uniforms we want to update. We're going to fix this later, 345 | // but for fight now, we'll leave the camera out so we can adjust it's position 346 | glUniform3fv(glGetUniformLocation(shader.id, "light.position"), 1, [value, 2.0, 2.0]) 347 | shader.update(view: view, projection: projection) 348 | 349 | // "The moment we've all been waiting for", was ask OpenGL to draw our 350 | // scene. Make sure that you adjust the `count` parameter or else you 351 | // won't see anything more than one triangle. We'll use our `data` 352 | // property's `count` property, but cast it into an Int32 which is 353 | // what glDraw* is expecting. 354 | glDrawArrays(GLenum(GL_TRIANGLES), 0, Int32(data.count)) 355 | 356 | // Unbind the shader. 357 | shader.unbind() 358 | 359 | context.flushBuffer() 360 | context.unlock() 361 | } 362 | 363 | deinit { 364 | CVDisplayLinkStop(displayLink!) 365 | // Don't forget to be a good memory manager and delete what we don't 366 | // need anymore. 367 | shader.delete() 368 | vao.delete() 369 | vbo.delete() 370 | tbo.delete() 371 | } 372 | } 373 | --------------------------------------------------------------------------------