Q:

Build a Semi-Zigzag

You will be given a positive integer N as input. Your task is to build a Semi-Zigzag, of N sides, each of length N. Since it is relatively hard to clearly describe the task, here are some examples:

N = 1:

O

N = 2:

O
 O O

N = 3:

O       O
 O     O
  O O O

N = 4:

O           O O O O
 O         O
  O       O
   O O O O

N = 5:

O               O O O O O
 O             O         O
  O           O           O
   O         O             O
    O O O O O               O

N = 6:

O                   O O O O O O
 O                 O           O
  O               O             O
   O             O               O
    O           O                 O
     O O O O O O                   O O O O O O

N = 7:

O                       O O O O O O O                       O
 O                     O             O                     O
  O                   O               O                   O
   O                 O                 O                 O
    O               O                   O               O
     O             O                     O             O
      O O O O O O O                       O O O O O O O

A larger test case with N = 9

As you can see, a Semi-Zigzag is made of alternating diagonal and horizontal lines, and it always begins with a top-left to bottom right diagonal line. Take note that the characters on the horizontal lines are separated by a space.
Rules

You may choose any non-whitespace character instead of O, it may even be inconsistent.
You may output / return the result as a String or as a list of Strings, each representing one line.
You may have a trailing or leading newline.
Default Loopholes apply.
You can take input and provide output by any standard mean.
If possible, please add a testing link to your submission. I will upvote any answer that shows golfing efforts and has an explanation.
This is code-golf, so the shortest code in bytes in every language wins!

A:

Charcoal, 24 bytes
ＦＮ«↶§7117ι×⁺#× ﹪ι²⁻Ｉθ¹»#

Try it online!
-5 thanks to Neil.
AST:
Program
├Ｆ: For
│├Ｎ: Input number
│└Program
│ ├↶: Pivot Left
│ │└§: At index
│ │ ├'7117': String '7117'
│ │ └ι: Identifier ι
│ └Print
│  └×: Product
│   ├⁺: Sum
│   │├'#': String '#'
│   │└×: Product
│   │ ├' ': String ' '
│   │ └﹪: Modulo
│   │  ├ι: Identifier ι
│   │  └2: Number 2
│   └⁻: Difference
│    ├Ｉ: Cast
│    │└θ: Identifier θ
│    └1: Number 1
└Print
 └'#': String '#'

A:

Python 2, 157 153 bytes
n=input()
o,s=q='O '
def p(k,t=q*n+s*(4*n-6)):print(t*n)[k*~-n:][:n*3/2*~-n+1]
p(2)
for i in range(n-2):p(0,i*s+s+o+s*(4*n-7-2*i)+o+s*(2*n+i-2))
n>1>p(5)

Try it online!

n*3/2*~-n+1 is the width of each line: ⌊3n/2⌋ · (n−1) + 1 characters.
The string q*n+s*(4*n-6) represents the top and bottom rows. If we repeat it and slice [2*(n-1):] we get the top row; if we slice [5*(n-1):] we get the bottom row. Hence the definition of p and the calls to p(2) and p(5). But since we need the repetition and line-length slicing for all other lines anyway, we reuse p in the loop.
The i*s+s+o+… is just a boring expression for the middle rows.
n>1>p(5) will short-circuit if n≯1, causing p(5) to not get evaluated. Hence, it’s shorthand for if n>1:p(5).

A:

Mathematica, 126 125 121 112 104 89 86 bytes
(m=" "&~Array~{#,#^2-#+1};Do[m[[1[i,#,-i][[j~Mod~4]],j#-#+i+1-j]]="X",{j,#},{i,#}];m)&

# is the input number for an anonymous function (ended by the final &).
m=" "&~Array~{#,#^2-#+1}; makes a matrix of space characters of the right size by filling an array of given dimensions #,#^2-#+1 with the outputs of the constant anonymous function "output a space" " "&.
Do[foo,{j,#},{i,#}] is a pair of nested do loops, where j ranges from 1 to # and inside of that i ranges from 1 to #.
m[[1[i,#,-i][[j~Mod~4]],j#-#+i+1-j]]="X" sets the the corresponding part of the matrix to be the character X based on j and i. The -i uses negative indexing to save bytes from #-i+1. (I forgot to write Mod[j,4] as j~Mod~4 in the original version of this code.) Jenny_mathy pointed out that we can use the modular residue to index into the list directly (rather than using Switch) to save 9 bytes, and JungHwan Min pointed out that we didn't need to use ReplacePart since we can set a part of an array and that 1[i,#,-i][[j~Mod~4]] uses the odd behavior and generality of [[foo]] to save bytes over {1,i,#,-i}[[j~Mod~4+1]]
Since meta established that a list of characters is a string (as JungHwan Min pointed out) we don't need to map any function across the rows of the matrix of characters since it's already a list of "string"s.

You can test this out in the Wolfram Cloud sandbox by pasting code like the following and hitting Shift+Enter or the numpad Enter:
(m=" "&~Array~{#,#^2-#+1};Do[m[[1[i,#,-i][[j~Mod~4]],j#-#+i+1-j]]="X",{j,#},{i,#}];m)&@9//MatrixForm

