Function generator design
Many function generators are designed with specs similar to yours:


variable frequency
amplitude of output independent of frequency
square, triangle, sine selectable.


 An often-used block diagram starts with a triangle-wave oscillator. If an op-amp is used for this oscillator, it could be a Schmitt type integrator. Output is frequency-independent amplitude that satisfies the triangle output requirement. Frequency can be varied by feeding the integrator with variable current source.

Square wave output is easily derived from the triangle wave with a comparator chip. Again, comparator output amplitude is independent of frequency. While an op-amp makes an inferior comparator, it is possible.

Sine output is derived from the triangle wave, with a network of diodes/resistors to approximate a linear piece-wise sinusoidal wave. This method also yields a constant amplitude independent of frequency. An op-amp could be used as a summing amplifier that adds together currents from the diode-resistor network.



A modern approach used an arbitrary waveform generator (all digital) to generate just about any wave shape you desire. A digital-to-analog converter is the last stage of this process: again, it supplies output whose amplitude is independent of frequency.
A square wave clock source increments through multiple steps of one cycle. A small counter generates addresses to a RAM or ROM where numerical (signed binary?) discrete waveform samples are selected - these feed an analog-to-digital converter. However, these stages are digital and don't meet your op-amp requirement.