Introduction

Initial Setup

For this project I have chosen C++ (C with classes flavor) as the language just because I want to get more comfortable with this language but I could have gone with something different.

First thing first. I need a platform layer that allows me to render a window and gives me a buffer that is used to render pixels on the window.

I’m using MacOS and Clang++ to compile my code. But I will add a platform layer for both MacOS and Windows.

MacOS Platform Layer

I wanted to keep this layer as simple as I’m not interested on this part right now. I just want to get started as fast as possible. I have a simple Objective-C file that creates a window and draws pixels with a tick function that runs every frame.

#import <Cocoa/Cocoa.h>

// TODO: I will create this next
#include "renderer.h"

// TODO: We don't have this yet but we will create it in the next section
static Renderer gRenderer;

@interface RasterizerView : NSView
@end

@implementation RasterizerView

- (BOOL)acceptsFirstResponder {
  return YES; // Required to receive keyboard events
}

- (void)drawRect:(NSRect)dirtyRect {
  if (!gRenderer.ready)
    return;

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef ctx = CGBitmapContextCreate(
      gRenderer.pixels, gRenderer.width, gRenderer.height, 8,
      gRenderer.width * 4, colorSpace,
      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);

  CGImageRef image = CGBitmapContextCreateImage(ctx);
  CGContextDrawImage([[NSGraphicsContext currentContext] CGContext], dirtyRect,
                     image);

  CGImageRelease(image);
  CGContextRelease(ctx);
  CGColorSpaceRelease(colorSpace);
}

@end

@interface AppDelegate : NSObject <NSApplicationDelegate> {
  NSWindow *window;
  RasterizerView *view;
  NSTimer *timer;
}
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
  InitTiming();

  const int w = 800, h = 600, pixelSize = 1;

  // Internal Resolution: scaled 4x to 800x600
  // const int w = 200, h = 150, pixelSize = 4;

  // INFO: Initializes the renderer
  gRenderer = Renderer_Create(w, h, pixelSize);


  // INFO: Creates the window frame 800x600
  NSRect frame =
      NSMakeRect(100, 100, gRenderer.windowWidth, gRenderer.windowHeight);
  window = [[NSWindow alloc]
      initWithContentRect:frame
                styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
                  backing:NSBackingStoreBuffered
                    defer:NO];

  view = [[RasterizerView alloc] initWithFrame:frame];
  [window setContentView:view];
  [window setTitle:@"CPU Rasterizer"];
  [window makeKeyAndOrderFront:nil];

  // Start render loop
  timer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 60.0
                                           target:self
                                         selector:@selector(tick)
                                         userInfo:nil
                                          repeats:YES];
}

- (void)tick {
  // TODO: My render call goes here

  [view setNeedsDisplay:YES];
}

- (void)applicationWillTerminate:(NSNotification *)notification {
  Renderer_Destroy(&gRenderer);
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:
    (NSApplication *)sender {
  return YES;
}

@end

int main() {
  @autoreleasepool {
    NSApplication *app = [NSApplication sharedApplication];
    [app setActivationPolicy:NSApplicationActivationPolicyRegular];

    AppDelegate *delegate = [[AppDelegate alloc] init];
    [app setDelegate:delegate];
    [app activateIgnoringOtherApps:YES];
    [app run];
  }
  return 0;
}

The previous snippet creates a window using Objective-C and the Cocoa Framework.

I don’t have too much knowledge building MacOS applications but this should get me started. The code creates an 800x600 pixels window and has a tick function that I can use to render my objects.

Now I just need to compile and run the executable created. For this I have created a simple Makefile that allows me to run some make commands:

APP_NAME = Rasterizer
BUILD_DIR = ./bin
RESOURCES_DIR = resources
C_FILES = ./src/*.cpp ./src/*.mm
CFLAGS = -Wall -g -O0 -std=c++17

APP_DEFINES:=
APP_INCLUDES:= -I/usr/local/include -L/usr/local/lib -framework Cocoa -Wl,-rpath,/usr/local/lib

all: build copy_resources

build:
	mkdir -p $(BUILD_DIR)
	clang++ $(CFLAGS) $(C_FILES) -o $(BUILD_DIR)/$(APP_NAME) $(APP_INCLUDES)

copy_resources:
	cp -r $(RESOURCES_DIR) $(BUILD_DIR)/

clean:
	rm -rf $(BUILD_DIR)

When I run the make command, the executable named Rasterizer is added to the ./bin folder. I can now just run in with ./bin/Rasterizer and an empty window should appear.

Now that the platform layer is done I can move on to the core part of the project.

Windows Platform Layer

TODO

Squared Wave SVG